第十三章:日志、恢复与结果未知
13.1 聊天历史与执行账本关注不同事实
聊天历史便于继续交流,执行账本则还要记录调用身份、开始、结果、权限与验收。有的工程将它们组织在同一会话系统中,有的使用多个存储层。选择什么格式之前,先列出恢复需要回答的问题。
至少需要知道:哪项输入已接纳,哪份模型回复已完整保存,哪些调用尚未执行,哪些动作开始了但没有结果,以及最后的完成是否有验收支撑。只保存用户和助手文字,通常不足以回答这些问题。
13.2 关键顺序:先保存意图,再执行
对于可能有副作用的工具,先持久保存调用意图,再进入执行。如果先执行再记“开始”,进程在中间崩溃后,日志甚至无法说明曾经尝试过这项操作。
但保存意图仍不能把外部世界和本地日志变成一个原子事务。下面三个时刻必须区分:意图落盘、外部动作生效、结果落盘。中间可能发生任意故障。
| 恢复时的证据 | 可以确认 | 不可以直接确认 |
|---|---|---|
| 已有完整调用,尚无开始 | 宿主还未记录进入执行 | 外部环境没有被其他人改变 |
| 有开始,没有结果 | 曾进入可能执行的阶段 | 动作一定没发生或一定已发生 |
| 有结果 | 有已记录结算 | 外部状态以后永远不变 |
13.3 unknown 比猜测更有价值
随书恢复器发现开始事件缺少结果时,会补充 unknown 结果并停止会话,要求核查实际状态。它不会自动重放写操作,也不让模型根据文字猜测是否已经成功。
这种保守处理有明确成本:连只读调用中断也会停下来,且实验没有自动人工裁决接口。它牺牲便利性,换取一个容易理解的恢复语义。生产实现可以根据幂等性、外部事务 ID 或可查询状态做更细处理,但每项恢复策略都需要额外证据。
13.4 追加日志还需要版本和写入所有权
JSONL 便于逐条追加,却不自动解决半行写入、多个写入者和格式升级。实验使用序号与哈希链检测意外损坏,遇到缺少换行的尾部显式失败,并通过独占锁文件约束合作写入者。
哈希链不是防恶意篡改的认证机制:能修改整份文件的人也可能重算链。锁文件也不是分布式租约;进程硬退出会留下锁,必须先确认所有者已停止,再按受控流程恢复。不要写一个“超过几秒就删锁”的规则来假装解决进程身份问题。
DeepSeek 的 JSONL generation 模块展示了生产系统面对稳定读取与迁移时更多的工作。Pi 的条目树则提醒我们,恢复还可能包含分支选择与上下文重建,而不仅是读回所有行。DeepSeek Harness · generation.ts L253–267 Pi · session-manager.ts L574–588
13.5 恢复对话不恢复磁盘
回到旧消息,不会自动回滚文件、数据库或网络动作。恢复时应核对工作区身份和当前文件状态;如果系统承诺恢复文件,应另有版本控制或快照机制,并说明其覆盖范围。
练习:某付款工具返回前断网,应该再调用一次吗?仅根据“没看到结果”不能确定。需要服务端幂等键或查询交易状态,否则应进入核查流程。文件写入规模虽小,也遵循相同的因果问题。