将经典城市模拟游戏 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 头进行鉴权后执行。

在架构层面,项目明确划分为三层:

  1. 引擎层(src/:基于 micropolisJS 改造的 headless 模拟引擎,确定性运行,无任何浏览器依赖。
  2. API 网关 / 服务层(worker/:使用 Cloudflare Workers 和 Hono 框架构建。关键设计在于,每个城市对应一个 Durable Object,城市元数据(如名称、种子、创建时间)存储在 D1(SQLite)数据库中,而地图快照等大型状态则序列化后存入 R2 对象存储。
  3. 展示层(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. 容错与监控清单

  • 必监控指标
    1. Durable Object 激活 / 解激活速率。
    2. 各 API 端点(/actions, /advance)的 P95/P99 延迟。
    3. R2 快照存储的成功率与延迟。
    4. 按城市统计的 “游戏年 / 动作” 比率,识别异常活跃或停滞的模拟。
  • 故障回滚策略
    • 由于模拟是确定性的,任何错误都可以通过重置到上一个已知好的快照(通过种子 + 快照时间点) 来回滚。应在 API 中提供 POST /v1/cities/{id}/rollback 端点,支持回滚到指定时间戳。
    • 对于因 AI 代理错误指令导致的 “坏状态”(如连续建造导致破产),应设计 “市长任期” 概念,允许在不停用城市的情况下,将控制权移交给另一个 AI 代理,并从当前状态继续。

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 智能体进化的数字沙盒。


资料来源

  1. Hallucinating Splines 官方文档 (https://hallucinatingsplines.com/docs)
  2. Hallucinating Splines GitHub 仓库架构说明 (https://github.com/andrewedunn/hallucinating-splines)