将经典城市模拟游戏 SimCity 转化为一个可供多 AI 代理并行操作的实验平台,是当前 AI 系统架构中的一个有趣挑战。本文以 Hallucinating Splines 项目为例,阐述如何通过 REST API 网关与状态机设计,构建一个支持多 AI 代理并行决策、状态同步的 headless 城市模拟服务,并提供可直接落地的工程参数与监控清单。
核心观点:状态机是协调并行决策与状态同步的枢纽
在单机游戏中,状态管理相对简单;但在多 AI 代理并行的云端环境中,核心矛盾在于如何让多个独立的决策流(AI 代理)安全、有序地操作同一个不断演进的游戏状态。一个直观但低效的做法是采用全局锁,但这会严重限制并发性。Hallucinating Splines 的架构给出了一个优雅的答案:将每个模拟城市实例封装为一个独立的状态机,并由 Cloudflare Durable Objects 承载。
每个 Durable Object 是一个强一致性的、单线程的 JavaScript 运行时环境,它持有一个完整的 HeadlessGame 实例(即城市模拟状态)。所有对该城市的 API 请求(如建造、推进时间)都被路由到同一个 Durable Object 中顺序处理。这天然构成了一个状态机:外部 “事件”(API 请求)触发内部 “动作”(游戏引擎操作),并驱动游戏状态转移。这种设计确保了单个城市内部状态的线性一致性,完全避免了并发冲突。
证据:从 API 端点到架构实现
Hallucinating Splines 的公开文档与代码库清晰展示了这一架构。其 REST API 提供了四个核心端点:创建 API 密钥、创建城市、执行动作(如 build_coal_power)、推进时间(以月为单位)。每个城市拥有唯一 ID,所有针对该城市的操作都通过 Authorization 头进行鉴权后执行。
在架构层面,项目明确划分为三层:
- 引擎层(
src/):基于 micropolisJS 改造的 headless 模拟引擎,确定性运行,无任何浏览器依赖。 - API 网关 / 服务层(
worker/):使用 Cloudflare Workers 和 Hono 框架构建。关键设计在于,每个城市对应一个 Durable Object,城市元数据(如名称、种子、创建时间)存储在 D1(SQLite)数据库中,而地图快照等大型状态则序列化后存入 R2 对象存储。 - 展示层(
site/):Astro 构建的静态站点,用于展示城市画廊和排行榜。
这种架构使得 “状态” 有了明确的归属和生命周期。正如其文档所述,“每个城市都有自己的 Durable Object 持有活的 HeadlessGame 实例”,这为状态机的实现提供了基础设施保障。
可落地参数与工程清单
基于上述分析,要构建一个类似的可供 AI 代理操作的模拟环境,以下是一份可落地的参数配置与工程监控清单。
1. 状态机与 API 网关关键参数
- Durable Object 单实例并发数:保持为 1。这是保证状态一致性的基础,所有请求进入该对象的队列顺序执行。
- 状态快照频率:建议每推进游戏时间 12 个月 或每处理 10 个有效动作 后,自动将完整游戏状态序列化并持久化到 R2(或类似对象存储)。这平衡了性能与故障恢复粒度。
- 动作队列深度监控:为每个城市的 Durable Object 设置队列长度指标。当平均队列深度持续 >5 时告警,可能表明该 AI 代理决策频率过高或单个动作处理超时。
- API 密钥与资源配额:
- 每个密钥同时活跃城市数上限:5 个(仿照 Hallucinating Splines)。
- 密钥不活动失效时间:14 天。对于长期实验,需设计定时 “心跳” 请求。
- 单城市动作频率限制:例如,每秒最多 2 个 建造 / 规划类动作,防止模拟超速。
2. 并行决策实现模式
多 AI 代理的 “并行” 体现在不同城市之间,而非单个城市内部。架构需支持:
- 批量城市创建与初始化:API 应支持通过单个请求(含种子列表)批量创建多个城市,并返回 ID 列表,供不同的 AI 代理认领。
- 全局状态聚合查询:提供
/v1/summary类端点,快速返回所有城市的元数据(如人口、预算、评分),供监控或元决策使用,避免轮询每个城市。 - 事件驱动的代理通知(可选):当城市达到特定状态(如破产、人口超过 10 万)时,可通过 Webhook 通知外部协调器,以触发新的 AI 决策流程。
3. 容错与监控清单
- 必监控指标:
- Durable Object 激活 / 解激活速率。
- 各 API 端点(
/actions,/advance)的 P95/P99 延迟。 - R2 快照存储的成功率与延迟。
- 按城市统计的 “游戏年 / 动作” 比率,识别异常活跃或停滞的模拟。
- 故障回滚策略:
- 由于模拟是确定性的,任何错误都可以通过重置到上一个已知好的快照(通过种子 + 快照时间点) 来回滚。应在 API 中提供
POST /v1/cities/{id}/rollback端点,支持回滚到指定时间戳。 - 对于因 AI 代理错误指令导致的 “坏状态”(如连续建造导致破产),应设计 “市长任期” 概念,允许在不停用城市的情况下,将控制权移交给另一个 AI 代理,并从当前状态继续。
- 由于模拟是确定性的,任何错误都可以通过重置到上一个已知好的快照(通过种子 + 快照时间点) 来回滚。应在 API 中提供
4. 为 AI 代理优化的 API 设计
- 结构化观察空间:
GET /v1/cities/{id}/observation应返回一个高度结构化的 JSON,包含地图瓦片编码、需求指数(住宅 / 商业 / 工业)、关键统计(资金、人口、污染)、可用动作空间等,直接作为强化学习的环境状态。 - 动作验证与建议:在执行动作前,提供
GET /v1/cities/{id}/buildable?action=zone_industrial端点,返回所有可执行该动作的坐标列表,减少无效请求。 - 确定性保证:在创建城市时传入的
seed参数,必须保证在任何服务器、任何时间点都能重现完全相同的初始地图和随机数序列。
总结与展望
通过 REST API 网关将 SimCity 这类复杂模拟游戏封装为服务,其价值远超创建一个 “游戏服务器”。它提供了一个高度可控、可观测、可重复的复杂系统实验床。AI 代理可以在此学习城市规划、多目标优化、长期决策,甚至多个代理之间可以尝试协作或竞争策略。
Hallucinating Splines 的实践证明了利用现代云原生服务(如 Cloudflare Durable Objects)可以较低成本构建此类系统。下一步的演进方向可能包括:引入更细粒度的权限控制让不同代理操作同一城市的不同区域;增加更丰富的外部事件模拟(如灾难、经济波动);以及提供标准化的 OpenAI Gym 或 PettingZoo 接口,使其无缝接入主流强化学习框架。
最终,这类架构的目标是模糊游戏模拟与 AI 训练环境之间的界限,让经典游戏引擎焕发新生,成为驱动 AI 智能体进化的数字沙盒。
资料来源
- Hallucinating Splines 官方文档 (https://hallucinatingsplines.com/docs)
- Hallucinating Splines GitHub 仓库架构说明 (https://github.com/andrewedunn/hallucinating-splines)