项目来源:mewamew/my_ai_town · 相关视频:AI小镇发布啦!【B站AI创造公开赛】
AI 参与说明(Agent:Codex):本文由 Codex 阅读项目公开源码、README、发布记录、GitHub Actions 与 Godot 官方文档,辅助分析技术方案和玩法的匹配。源码分析固定于提交
43ef1d13f973cf90f0868209d0c1e3492d406312,资料整理于 2026-10-07;选型与改进建议是本文的工程判断。运行记录:模型gpt-6.1-sol,reasoning effortultra,执行入口 Codex Desktop,提供方openai,运行时 CLI 版本0.162.0-alpha.2(不代表桌面 App 版本)。
对于现在这款可观察、可化身进入的像素风居民生活与社交模拟,Godot + GDScript + LLM 是匹配的方案,继续使用现有引擎有充分理由。 它的优势来自明确分工:Godot 负责空间、表现和交互,LLM 提出居民的行为决定,游戏自己的世界系统维护事实并执行规则。
“是不是最好的匹配”要分两层看:引擎与当前玩法高度匹配;LLM 与开放式社交、故事生成匹配,但未必适合承担所有日常模拟。 如果目标变成严格可预测的经营数值、即时动作反应,或多人共享的持续在线小镇,就需要调整模拟或部署方案。仅把 Godot 换成 Unity,不能自动解决模型延迟、调用成本与玩法目标的问题。
这篇文章是Steam 独立游戏开发技术方案的具体案例:从一个真实项目检查“游戏类型决定技术选择”如何落到源码中。
先认识几个贯穿全文的名称。
| 英文术语 | 中文名称 | 简要解释 |
|---|---|---|
| Godot | Godot | 提供场景、图形、输入、物理和界面的游戏引擎 |
| GDScript | GDScript | 本项目编写游戏规则与交互的脚本语言 |
| LLM | 大语言模型 | 根据输入的居民背景、记忆和环境生成行为决定或对话 |
| Agent | 智能体 | 本文指具有独立状态、记忆和决策流程的居民程序 |
| World | 世界层 | 本文沿用源码 world/ 的职责划分,指维护事实和结算行动的游戏系统 |
| wake_packet | 唤醒数据包 | 项目发送给居民决策流程的当前环境、事件和行动结果 |
| HTTPRequest | HTTP 请求节点 | Godot 用于异步发送网络请求的节点 |
| BYOK | 自带密钥 | 玩家配置自己的模型服务 API Key,调用由该服务账户承担 |
它实际是什么类型的游戏
项目 README 将其描述为由 LLM 驱动居民生活的单机像素小镇模拟。源码支持进一步细分:以居民生活与社交为中心、允许玩家观察和干预的模拟沙盒,包含职业、生产和服务系统。 固定版本 README
| 玩家或居民的行为 | 已核实的实现 | 对类型判断的意义 |
|---|---|---|
| 观察居民 | 跟随角色,查看状态、关系、记忆与内心观察入口 | 居民的变化与社会关系本身就是观看对象 |
| 化身进入小镇 | observer 与化身模式切换,读取方向输入,实际移动并与世界交互 | 玩家可以在空间中探索,体验超出聊天页面 |
| 干预生活 | 发布公告、改变天气、与附近居民交谈 | 玩家通过改变条件,观察居民如何回应 |
| 居民日常 | 能量、饱腹、社交与独处需求随时间变化 | 居民具有持续生活状态,不只在被点击时回复 |
| 工作与生产 | 捕鱼、花园地块、研究项目,以及工作任务、货物和职业服务 | 有生产与组织元素,但不等于玩家主导的传统经营循环 |
对应源码:ResidentActionMenu、化身移动、天气与公告入口、生活需求、生产状态。
因此,不能因为场景里有商店、花园和工作,就直接把它看成“建造设施 → 获得利润 → 扩大小镇”的经营游戏。本次审查的核心界面与运行逻辑,尚不足以确认这种玩家主导的循环。它更适合用“我改变了一个条件,居民接下来怎样生活和相处”来理解。
技术方案:Godot 客户端中的模拟与模型调用
The project declares Godot 4.7 with the GL Compatibility renderer, and its resident presentation uses CharacterBody2D. project.godot · ResidentCharacterBody
不要把配置中的 Jolt Physics 字样当成 3D 玩法证据。实际审查到的是 2D 地图、2D 角色与像素风表现;地图底图由 Sprite2D 加载并使用 nearest filtering,也不能据此说地图一定通过 TileMap 实现。TownBase 的底图加载
| 层次 | 本项目的方案 | 负责的事情 |
|---|---|---|
| 场景与交互 | Godot、GDScript、2D 节点和 UI | 地图、角色表现、化身移动、室内外空间、摄像机、菜单 |
| 模拟规则 | TownWorldRuntime 与多个 World 模块 | 时间、居民状态、需求、行动、关系、职业与生产 |
| 空间移动 | AStar2D 路网、World 路径采样、角色表现路径 | 计算路线,结算实际位置,再表现移动过程 |
| 居民决策 | 自有 Agent 协议、Prompt 编译和决定校验 | 组装上下文,要求模型返回结构化决定,检查数据 |
| 模型传输 | HTTPRequest 与多个 ModelProvider | 连接云端服务或本地模型服务,解析响应与错误 |
| 记忆与存档 | JSON 文件、证据队列、会话保存协调 | 保留居民记忆、世界证据和可恢复的会话状态 |
这是从源码归纳的职责表,不是项目依赖了六套独立框架。沿着默认客户端调用路径,Godot 可以直接请求配置的模型 endpoint;本地 Ollama 与 LM Studio 的预设也已存在,不要求先部署项目专属服务端。模型请求 · 本地服务配置
多数适配器采用 OpenAI-compatible 请求方式,但不能把所有供应商都写成同一种协议:例如 Ollama Cloud 适配器还支持原生 /api/chat 路径。模型接口最终必须提供项目需要的 JSON 决定;兼容聊天请求格式,不代表每个模型都能可靠遵守这个行为协议。OllamaCloudModelProvider
最关键的匹配:LLM 提议,World 决定事实
The LLM proposes a structured decision; the World validates and commits the action against its current state. 决定校验 · World 提交
下图展示决定通过校验后的成功路径。
flowchart TB
A["World 当前状态<br/>与已确认事件"] --> B["wake_packet<br/>与居民记忆"]
B --> C["Prompt 编译<br/>与 LLM 请求"]
C --> D["DecisionExecution<br/>检查结构化决定"]
D --> E["World 检查当前条件"]
E --> F["执行行动<br/>并结算状态"]
F --> H["已确认事件<br/>与 action_results"]
H --> I["更新居民证据与记忆"]
图中省略了队列、预取和详细失败分支。World 已消费的拒绝会留下权威回执,部分未消费的拒绝会进入回退;过期请求的回包直接忽略。Provider 调用或决定校验失败的重试,另按错误类型判断。回退行动也必须经过 World 提交。Gateway 提交与拒绝处理 · 失败重试 · 连续性回退
例如,模型想让居民去找另一位居民谈话,返回时目标可能已经离开。项目会在执行前重新检查条件;目标离开等情况可能导致重新观察或拒绝,模型的文字不能直接把一次未发生的谈话写成事实。TownAgentDecisionActionRuntime
这个边界特别适合 AI 社会模拟:性格、意图和表达可以开放生成,地点、距离、资源、行动完成与否仍由规则结算。如果这两者混在一起,居民说“我送到了”就可能被误当成真的送达,后续记忆和工作都会失去依据。
世界推进与模型等待分开
World 由 RefCounted 对象承载,不依赖某个可见角色节点保存唯一状态。TownRuntime 每帧调用 World 的 advance(delta),World 再按游戏分钟结算需求、行动与社会事务。TownWorldContract · TownRuntime · TownWorldAdvanceRuntime
居民模型决策走待处理队列,由唤醒条件、行动边界等驱动,并非每个渲染帧都发起一次 LLM 请求。Gateway 居民决策流程的并发上限为 6,并为化身对话预留 1 个位置;本地模型的决策流程上限为 3,其中普通居民最多占 2 个位置。选择器统计在途 decision_id,包括准备和记忆整理阶段;其他独立模型请求应另行统计。队列与并发常量 · 请求挑选
Godot’s HTTPRequest is asynchronous, but a single node must not serve simultaneous requests. The project creates a request node for each dispatched HTTP operation. Godot 4.7 HTTPRequest · 项目的请求节点创建
异步传输让游戏继续运行,但 Prompt 组装、JSON 处理和世界结算仍有本地计算成本,不能因为请求异步就推断所有工作都在后台线程。项目另外检查 session_epoch、当前 decision_id 和 Gateway 的 generation,用于丢弃已失效或重复返回的结果。ResidentRuntime · Gateway
记忆保留的是证据与理解
项目把当前意图与已完成的事情分开:准备替换行动时,先记录 pending intent;收到 World 的事件和 action_results 后,才摄入相应证据。记忆整理把旧摘要与新证据交给模型,再检查结构与连续性。意图记录 · 证据摄入 · MemoryOrganizer
Current memory retrieval uses structured entries, ranking rules, and text anchors; the inspected implementation does not use embeddings or a vector database. 排序规则 · 锚点匹配
这对规模有限的小镇是合理起点:人物、地点和任务往往有明确标识,先用可解释的检索可以减少依赖。若长期游玩后出现遗漏相关往事、同义表达难以命中等问题,再用实际失败案例评估是否增加 embeddings;“有 Agent”本身不是引入向量数据库的理由。
Godot 为什么合适,何时才值得换方案
Godot provides a dedicated 2D renderer and physics engine, with built-in support for animation, tilemaps, and navigation. Godot 4.7:Introduction to 2D · 2D navigation overview
项目已经把引擎能力用于实际玩法:角色是 CharacterBody2D,化身会进行物理移动,居民有室内外空间与路线,摄像机可以跟随居民。其 AStar2D 路网生成的 minutePositions 用于 World 的位置结算,presentationPath 用于视觉移动;可见动画和权威位置并非同一份状态。路线生成 · 移动结算 · 角色表现
以下比较是基于产品目标的工程建议,不是性能实测排名。
| 目标 | 更合适的起点 | 原因与代价 |
|---|---|---|
| 继续做可探索的 2D 单机小镇,保留化身、地图与角色行为 | 保留 Godot | 当前场景与规则已经围绕这些体验工作;迁移会重做大量交互,无法消除模型等待 |
| 体验主要是居民观察、对话和事件时间线,优先链接即开 | Web 界面,按空间需求加入 Phaser 或 Canvas | 更符合网页分发目标;需重新处理客户端存储、模型接入和空间表现 |
| 团队已有成熟 C# / Unity 工作流,或目标改成更复杂的 3D 角色与资源生产 | 比较 Unity 与现有方案 | 既有经验和生产工具可能抵消迁移成本;仅因 AI 代码复杂而换引擎,收益不足 |
| 多人共同干预同一个持续在线的小镇 | 服务端维护权威 World,客户端负责表现 | 持续运行、并发修改和成本控制成为部署问题;Godot 仍可作为客户端 |
| 主要挑战是精确资源配置、利润与生产效率 | 规则驱动日常模拟,LLM 只处理对话与特殊事件 | 减少模型波动影响数值平衡,保留居民表达的变化 |
因此,当前最有价值的调整大多在“哪些事需要 LLM”,而不是“用哪个引擎”。寻路、饥饿变化、工作进度和货物转移有明确规则;人物动机、谈话与社会反应更能发挥模型作用。
技术成立之后,还需要一个可感知的玩法循环
实现很多系统,不等于玩家已经拥有持续游玩的理由。 观察型沙盒可以没有强制胜负,但仍需要让玩家看懂变化、形成期待,并找到值得再次介入的机会。
从已实现的观察、公告、天气和化身互动入口出发,一个适合验证的循环是:玩家提出问题或改变条件 → 居民作出反应 → World 记录实际后果 → 玩家理解关系和生活如何变化 → 再次尝试。这里的循环是本文建议,不是声称项目已经设计了相应关卡。
例如,可用以下三种体验分别检验,而不必同时扩张成大型经营游戏:
- 观察实验:改变一次天气或公告后,读者能否从世界日志和居民行为看出谁受到了影响、为什么改变计划?
- 化身关系:玩家与同一居民反复接触后,对方能否根据已发生的互动表现出可辨认的态度变化?
- 有限目标:如果希望增加挑战,可围绕一个工作任务或一场社会事件设置目标,让玩家的行动产生明确后果。目标可以是短期情境,不必引入庞大的经济系统。
更适合这种类型的质量判断是“变化是否有因果、是否能被玩家理解”,而不是单看居民说了多少句话。独立居民记忆、权威事件和日志,正好为这种反馈提供基础;是否已经达到好玩的程度,需要实际游玩验证。
下一步优先解决什么
1. 用请求数据衡量等待与成本
并发上限只能限制负载,不能保证模型返回及时。应分别记录请求到达队列的时间、派发时间、完成时间、被接受或失效的结果,以及实际 token usage。项目 Provider 已将响应中的 usage 写入 diagnostics,可作为采集入口。usage 记录
一个假设算例:15 位居民,每位每个真实分钟触发 4 次基础行为决定,总计每分钟 60 次。若每次平均占用决策槽 8 秒,要避免持续积压,平均需要约 60 ÷ 60 × 8 = 8 个决策槽。这超过 Gateway 的上限 6;若有位置预留、重试和额外记忆整理,普通居民可用容量还会更少。这些数字不是项目实测,也不是项目固定的唤醒频率,只用于说明为什么应先减少无必要调用,再评估提高并发。
建议先采集“每个真实分钟调用数、队列等待、化身对话等待、回退比例、输入与输出 token 数”。快速模拟、普通模拟和玩家对话应分别统计;游戏时间加速并不意味着云端模型能按同样倍数加速。
2. 验证回退能否维持生活连续性
Gateway 的 MAX_DECISION_ATTEMPTS 为 2,并按失败情况决定是否重试;最后可以采用继续当前行动、选择可用活动、待着或结束对话等回退。重试判断 · 回退活动选择
这说明项目已经考虑模型失败,不能笼统说断网后所有居民必然卡死。但这种回退也不等于完整的离线 Agent:它能维持一些行动连续性,无法自动提供和正常模型同等的社交推理、故事与对话。
可执行的验收方式是:让测试 Provider 返回超时、错误 JSON 和迟到结果,分别检查居民是否继续合理活动、错误是否可解释、旧结果是否被丢弃、恢复后能否重新决策。使用本地模型时,还要检查模型服务未启动和本地推理排队的情况。
3. 保持事实、记忆与存档对齐
会话保存由协调器处理 World 候选、Agent 存档与最终 manifest,并带有事务 gate、journal 和恢复流程。它不是把所有跨文件状态通过一次 rename 就完成原子提交。保存入口 · Agent 与 World 提交 · manifest 发布
适合继续验证的场景是:居民正在等待模型时保存;恢复后旧请求返回;居民任务已经完成但摘要整理尚未完成;保存写入中断后重新启动。通过标准应围绕实际事实:没有重复完成任务、没有用旧记忆覆盖新状态、没有把计划当成完成记录。
4. 先收紧模块职责,再考虑迁移语言
当前 TownWorldRuntime.gd 已拆分为多个模块,仓库还有限制主文件指标和模块私有访问增长的架构检查。本次运行检查得到:主文件 2,412 行、211 个函数、44 个顶层状态,运行模块存在 128 次 world._成员 直接访问。检查脚本
这些数量不能单独证明设计好坏,但说明它已经是一套有维护成本的模拟系统。继续明确状态归属、用稳定接口减少模块对私有状态的依赖,比仅改写成 C# 更直接。若性能测量指向某个热路径,再针对那个部分优化;不能从代码行数推断 GDScript 已成为运行瓶颈。
下载版本、Steam 与再利用边界
仓库已有公开 Pre-release:v0.1.0-beta.5 发布于 2026-08-20,提供 Windows x86_64、macOS universal、Android arm64 包及 SHA256SUMS。这个 Release 对应 e31f342 开头的历史提交,与本文审查的 43ef1d1 主线快照不同,不能把本文全部源码能力视作该下载包已经包含的能力。
当前发布 workflow 会验证、导出并创建 draft prerelease;审查的 workflow 中没有 Steam 上传步骤。若继续作为 Steam 独立游戏推进,应另行验收桌面导出、输入体验、存档兼容和模型配置流程,再按需要接入 Steam 功能。release-build.yml · Steam 选型与发布路径
BYOK 适合愿意配置模型的体验者,也让不同模型成为可选择的变量;若面向普通付费玩家,API Key 配置、服务余额、模型能力差异都会影响首次体验。若改成开发者承担推理费用,应单独设计服务端凭据、额度和长期运营方案,不能把服务方密钥放进发行客户端。
项目当前会把玩家密钥存入本地加密文件,密码由项目标识与设备标识派生;这不是操作系统 Keychain 托管。TownProviderCredentialStore · 密码派生
源码公开可查看,但本快照没有根 LICENSE;README 明确说明第三方资源按各自许可使用,项目代码与自有素材的统一许可仍在整理。可以研究架构,不能据此假定整个仓库已经授予统一的商用或再分发许可。README:授权说明
如何复现本文的一项源码核查
下面的 shell 命令需要 Git、Python 3 和网络;检查脚本只读取源码与基线,不需要 Godot、API Key 或模型调用。使用固定提交避免后续更新改变统计结果。
git clone https://github.com/mewamew/my_ai_town.git my-ai-town-review
cd my-ai-town-review
git checkout --detach 43ef1d13f973cf90f0868209d0c1e3492d406312
python3 -B tools/guards/world_runtime_architecture_check.py --check
本次在该提交运行检查的实际输出为:
TownWorldRuntime 架构棘轮通过:lines=2412 functions=211 states=44 private_accesses=128。
仓库同一提交的 formal-validation 运行记录 显示成功;其 Agent suite 显式设置 AI_TOWN_PROVIDER_TEST_NO_NETWORK=1,这是自动回归证据,不能用来证明真实模型的延迟、费用和长期对话质量。formal-validation.yml
本次未启动游戏或测量帧率、模型等待与游玩体验。相关视频仅核验公开简介与项目关联,未播放视频或取得字幕。本文能支持的结论是:当前技术职责与当前游戏类型相符,下一步应优先验证玩法反馈和模型运行质量,再根据目标变化决定是否调整方案。