上一篇说的是规则:依据要写成文档。这篇说过程:发生了什么,要一条条记下来。
两件事合起来才是完整的白盒——规则回答"依据是什么",记录回答"发生了什么"。
为什么不能改
推演系统里最危险的操作是"改状态"。
城市人口少了 3000,直接 UPDATE 一下?短期省事,长期灾难。等你想问"这 3000 人是怎么没的",会发现没人记得:是迁出的、统计口径变了,还是某个循环里多减了一次?状态被覆盖,现场就没了。
屿城沙盘的规矩是:任何变动都作为一条新记录追加进事件序列,已有记录永不修改、永不删除。
每条记录写四要素:
- 谁触发的(哪个 agent、哪条规则、哪个系统模块);
- 改了什么(哪个变量、改前改后的值);
- 什么时候(推演的第几个时间步,逻辑时钟,不是墙上时钟);
- 为什么(上游引用——这条变动是响应哪条变动而来的)。
几个视图,一份记录
城市状态有经济、文化、心理等好几个视图,听起来像好几套数据。其实它们全部从同一份事件记录里查出来:记录里只有一种东西——事件条目。经济视图是把与资源、交易相关的条目聚合出来,心理视图是把与情绪、压力相关的条目聚合出来。视图是查询,不是存储。
这个设计有个很实用的性质:几个视图永远不可能互相矛盾。
经济视图说"居民变富了"、心理视图说"居民更焦虑了",这在现实里完全成立;但如果两套视图各记各的,你就要怀疑是不是数据没对上。从同一份记录派生时,两个结论来自同一批事件,矛盾只可能是真实现象,不可能是系统 bug。
回放与复算
有了"规则版本 + 完整记录",回放就不是录屏,而是重演:
- 回放:从记录的任意时间点开始逐条重放,看城市状态怎么一步步演化,像拖进度条看一座城市的历史;
- 复算:用同一版本规则和同一随机种子从头跑一遍,记录应该逐条一致。不一致,说明引擎引入了非确定性,那是必须修掉的 bug。
复算这条我们直接拿来当回归测试:每次引擎升级,先跑历史推演复算,对不齐就不许发布。
成本与取舍
只增不改意味着记录只会变长。我们的应对办法:
- 条目结构化压缩存储,热数据在内存、冷数据落盘;
- 支持快照点——不必每次从第 0 步回放,从最近的快照起步即可;
- 快照本身也写进记录,生成依据同样可查。
最后
只增不改的记录,是把"系统的记忆"从易变的状态变成不可变的历史。历史不能改,只能追加——推演结论能被信任,靠的就是这一点。
下一篇聊多智能体:我们怎么基于清华 AgentSociety 框架,让白盒居民、人格居民和黑盒对照组住在同一座城市里。