外观
Dolores
约 1773 字大约 6 分钟
2026-06-05
"These violent delights have violent ends."
Dolores 是一个完全由 LLM 驱动的自治 NPC 小镇——NPC 何时开口、做什么、去哪里,全部由它自己基于感知与推理、通过自选的工具来决定。没有导演指令、没有脚本桥段、没有"现在轮到你说话"的提示。系统只提供三样东西:诚实的感知、忠实的记忆、一组精心设计的工具,然后让开,把决策交还给模型。
它是 GameJam 项目 MAIGameVar 的全新继任者:代码不复用,但那些用代价换来的设计知识(多智能体的视角改写、感知切片、Web 导出的坑、host-authoritative + append-only)会被带过来。
一个核心区别
| MAIGameVar(故事内核) | Dolores(自治内核) | |
|---|---|---|
| 谁决定一个动作 | 客户端 / 触发器 | NPC 自己(选一个工具) |
| LLM 的输出 | 固定的 {action, speech} | 一次工具调用(也许说话、也许移动、也许什么都不做) |
| system prompt | 命令式("现在你说话") | 描述式(你是谁 / 你感知到什么 / 你有哪些工具) |
| 玩家离开时 | 世界冻结 | 小镇照常运转 |
核心命题:从"表演一句台词"到"自己决定下一步"
前作 MAIGameVar 是一个导演驱动的回合渲染器:外部决定谁/何时/说什么,LLM 只负责把那一句"演"出来。对于线性、被编排的叙事,这是正确的设计,它也确实交付了。
Dolores 把它反转成一个 感知 → 决策 → 行动 的循环。关键不在于"完全不要 system prompt"(模型总得知道自己是谁、有哪些工具),而在于 prompt 的性质翻转:
- 删掉所有命令。 没有"现在轮到你发言",没有听众模式的导演指令,没有锁死的脚本台词。任何替 NPC 做决定的东西都被移除。
- 行为契约降格为护栏。 它从"行为脚本"收缩成"边界":别出戏、别替别人说话、只能通过工具行动。它约束,但不指挥。
- prompt 只剩三类内容:你是谁(来自 YAML 的稳定身份)、你此刻感知到什么(动态注入)、你能调用哪些工具(schema)。决策由模型的能力产生,而不是由 prompt 指定。
至高原则:prompt 是描述性的,而非命令性的——agency 属于 NPC。 任何"替 NPC 做决定"的注入都是 bug,无论它把 demo 衬托得多好看。
不变量:红线与仲裁顺序
自治这件事很容易在边界处被悄悄破坏,所以项目用一套"红线 + 违反后果 + 守护测试"的账本来钉住它:
- Append-only:世界事件与观察只追加、永不编辑;物体状态是事件日志上的一次投影(左折叠)。
- Peer routing:当一个 agent 说话时,历史按它的视角改写(别人 →
user,带「名字」:前缀 + 硬性的"你只是 X"约束),否则多智能体会互相串味、替别人说话。 - 感知切片即感知系统本身:agent 只能看到/听到它的半径与位置允许的东西,按事件发生那一刻评估。
- 易逝 vs 持久分离:感知是易逝的;只有 agent 自己的
note或自动的observe才会写进记忆流。 - 行为契约是护栏,不是脚本。
- Host-authoritative:整个仿真跑在后端,客户端只是视图。
当两条红线在边界处打架时,靠固定的仲裁顺序裁决:命题(描述式 prompt / agency)> 数据完整性 > 多智能体正确性 > 感知诚实 > 易逝-持久分离 > host-authoritative > 其它工程洁癖。
工具是牢笼,不是外挂
一个反直觉但关键的立场:工具是用来"约束"NPC 的,不是用来"赋能"的。 工具把 NPC 限制在世界内合理的动作里,模型永远只能请求你声明过的工具,而后端是唯一的闸门。"有意义的限制"本身就是玩法的内核,而不是缺陷。
它想成为什么样的"游戏"
诚实地说,内核证明(M0–M6)只是 proof,不是 product。但它最终想长成的乐趣类型是想清楚的:
不是俄罗斯方块那种即时型乐趣,而是 Dwarf Fortress / RimWorld / Crusader Kings 那种故事生成型乐趣——乐趣在于事后值得复述的那个故事。慢热,需要你先在意这个世界。LLM 在这里唯一不可替代的贡献,是把机制层发生的事件,翻译成有人格、可记忆、值得复述(可截图、可剪辑)的角色与桥段:DF 的故事靠玩家脑补,而这里的 NPC 能带着动机和记忆真的演出来。
由此引出的产品框架:
- 双线成长:玩家成长 → 更能帮村子、赢得村民支持;村子成长 → 玩家得到更多资源。互相喂养。
- 玩家与 NPC 共用同一套框架:玩家就是
agent_id: player,进同一个工具注册表 / peer-router,区别只在驾驶员(人 vs LLM)。白送一个动态——玩家要和会成长、会争地位的自治 NPC 竞争村长之位。 - 数值是笼子,不是进度条:NPC 的数值是能力边界,不是它们去刷的目标;成长从"活出性格"涌现。
- 村民支持、村长之位都是涌现的:从 NPC 真实的记忆与处置里长出来,而不是好感度心心条或系统授予——当上村长是影响力与责任,不是对 NPC 的控制权。
- 世界不为玩家暂停,且姿态务实:现在仍以验证为主。
世界
第一个可玩切片是一个边境小村(致敬《无限世界》):温暖、有生活感、好奇,底下藏着一股"他们只是程序吗?"的暗流。村庄级的魔法、边境的怪物,以及周期性的边境危机——危机给世界带来真实的赌注与结局。
技术栈与进度
- 栈:Python + FastAPI(仿真后端)+ Godot(视图),host-authoritative。
- M0 已完成:世界骨架(append-only 事件日志 + 仿真时钟 + 状态投影 + 第一个区域)跑在 headless 后端上。
- 下一步 M1:function-calling probe——整个内核都押在"LLM 能稳定地选对工具"这一个前提上,所以先把它验证清楚,其余都从这个答案展开。
- 里程碑是闸门,不是建议:每一关的命题检查不过,就不开下一关。
- 定位:已从一次 GameJam 升级为个人长期项目。
延伸思考
这套"分层架构"(村民 = 小的世界原生模型,主神 = 最强的真实世界模型)背后,其实还牵着一串更发散的问题:世界的丰富度,是不是它能孕育的智能的硬上限?LLM 到底有没有真正的驱动力和好奇心,还是只是"演"出来的?
