一名玩家在集市里把一盏油灯递给守卫,转身走进西侧巷道,三分钟后回来,守卫对这盏灯毫无印象,巷口的一个摊位还凭空消失了。排查下来,模型并没有“胡说”,它只是各说各话:对话模块以为灯在守卫手里,背包模块以为灯还在玩家身上,地图模块在重新加载分区时把摊位当成了可以回收的临时对象。三个模块各自维护了一份世界的样子,谁也没错,世界却裂开了。
PA真人游戏世界智能研究组在设计PA真人AI的世界状态层时,第一个决定并不是选哪个模型,而是规定一条纪律:世界里发生的任何事,只在一个地方有权威记录,其他模块看到的都是它的投影。这就是常说的“唯一事实源”(Single Source of Truth)。至于语言模型怎样从聊天走向完整的世界模型,可以先读《一个真正的AI游戏大模型为什么不能只会聊天》,本文只讨论其中最底层的一块:状态到底怎样存、怎样读、怎样改。
实体:先把“世界里有什么”说清楚
世界状态里的东西统一叫实体(Entity)。研究组把它们归为六类:NPC、玩家、地图区块、物品、任务、关系。关系单独成类是一个有意的取舍,因为“守卫欠玩家一个人情”既不属于守卫,也不属于玩家,硬塞进任一方的属性里,另一方读取时就会出现信息不对称。每个实体有稳定的编号、类型、版本号和一组受模式(Schema)约束的字段,模型可以在文本里随便称呼它,但在状态里它只有一个编号。
entity: item#lamp_0142
type: item
owner: npc#guard_07 // 谁持有,只能有一个
location: region#market_a
flags: [lit, quest_related]
version: 18 // 每次提交递增
last_event: evt#88213 // 指向使这一版生成的事件
entity: relation#guard_07-player_2
kind: favor_owed
strength: 0.4 // 示例参数
source_event: evt#88213
每个状态都能追到造成它的事件,而不是“模型说的”。
模型只读视图,只提提案
语言模型在这套结构里没有写权限。它拿到的是一份“视图”(View),也就是为当前任务裁剪出来的只读快照,输出的是“提案”(Proposal),比如“守卫接过油灯并表示感谢,建议登记一条人情关系”。提案交给规则引擎,引擎依次检查:这个实体是否存在,前置条件是否满足,数值是否越界,与已有事实是否冲突。通过的才会被提交,产生新版本的实体并写入事件日志;被拒的会带着原因退回,模型可以改写后再提交,或者干脆把这次拒绝写成一句合理的台词。
这样分工之后,几类常见事故会自然消失。模型说“守卫把灯扔进了河里”,但河边并没有守卫的行动能力,提案被拒;模型在同一轮里两次转移同一件物品,第二次因为版本号过期被拒。
事件日志与快照:可回放才算可信
状态可以直接覆盖,也可以只追加事件、再从事件推出当前状态。研究组的方案是两者并用:事件日志只增不改,定期生成整体快照,快照相当于“存档点”。示例参数:每200条事件或每5分钟游戏时间存一次快照,保留最近三份。任何一次异常,都可以取最近快照、重放其后的事件,逐条对照是哪一步让世界变了样。
这也让“撤销”与“回滚”变成合理的操作。玩家投诉“我的灯被吞了”,工作人员不需要猜测,直接查看灯的实体在哪个版本发生了转移,由哪个提案触发。与之相邻的思路,生存类玩法里如何让因果可复盘,在生存游戏的世界规则一文里有更具体的展开。
多人同步:冲突在提交处解决
两名玩家几乎同时去拿同一把钥匙,两个客户端都在本地看到“拿到了”,这是多人游戏里最典型的冲突。做法是让所有写操作汇入同一个提交点,按版本号判定先后:先到的提案提交成功,实体版本从7变成8;后到的提案带着旧的版本号7,会被拒绝并收到最新状态,客户端据此回滚本地表现。
玩家之间的可见性也在同一层处理。某个秘密只有一名玩家知道,就在实体上标注可见范围,视图生成时按接收者过滤,模型不会“无意间”把它说给另一个人。
分区与视图裁剪:上下文是预算,不是仓库
整个世界不可能一次塞给模型。地图被切成分区,玩家附近的分区处于活跃状态,远处的只保留摘要;每个模块拿到的视图也不同,如下表所示。
| 模块 | 读取的视图 | 可提交的提案 | 示例上限 |
|---|---|---|---|
| 对话 | 说话者、听者、二者关系、当前区块摘要 | 关系变动、口头承诺 | 约30个实体 |
| 任务 | 任务图、相关NPC、关键物品 | 任务状态迁移 | 约40个实体 |
| 地图事件 | 活跃分区、资源与天气 | 生成、移除、移动实体 | 约60个实体 |
| 玩家行动 | 背包、位置、邻近可交互物 | 使用物品、移动 | 约25个实体 |
裁剪规则不复杂,难的是漏掉什么。研究组的经验是宁可让模块通过“查询接口”按需追加读取,也不要预先塞满。不同任务由哪种模型、在哪里执行,则属于另一个层面,可参见模型路由一文。
这一层还没有好办法的地方
两件事目前依然是取舍而不是答案。其一,模式约束越严,模型越难表达意外的创意,比如玩家把灯挂在桥栏上当路标,模式里没有“悬挂”这个状态时怎么办;现在的折中是允许提案携带一个待审核的“自由备注”,由后续规则版本决定是否升级为正式字段。其二,多人跨地域的延迟会让“先到先得”变得不那么公平,需要在体验层面另外补偿。
把世界状态做成一个不会说话、也不会撒谎的底座,语言模型才有资格去讲故事。栏目里的其他内容可以从PA真人AI大模型栏目继续阅读。