Appearance
运行中的信息放哪里:Conversation、Checkpoint 与 Short-Term Memory
前置:Run、Session 和 Durable State 到底各保存什么
核心问题:为什么 Conversation History 不能代替 Checkpoint,Short-Term Memory 又应该保存哪些“对模型有用但不是权威事实”的内容?
一个长 Run 已经运行 40 轮。完整 conversation 很大,刚完成一次 checkpoint,同时仍有“用户要求只能改 src/ 目录”这样的短期约束。恢复后系统必须知道哪些来自历史、哪些来自 checkpoint、哪些必须继续放进 context。
Mental Model
text
Conversation Items ──────┐
│ selection / summary
▼
Short-Term Memory
│
▼
Model Context
Runtime State ── at consistent boundary ──▶ Checkpoint
│
└─ resumeConversation 与 Short-Term Memory 更接近“认知连续性”;Checkpoint 属于“执行连续性”。
Conversation History
完整 conversation 可以保存:
text
user message
assistant message
model tool call
normalized tool result
handoff item
approval input保存完整历史不等于每轮都把完整历史送给模型。下一轮 ModelRequest 只应拿到当前任务真正需要的部分;选择、裁剪和压缩属于 ContextManager 的职责。
Checkpoint
Checkpoint 不是“把当前 Python 对象随手 json.dump() 一份”。这里不重复 Module 09 的 checkpoint 机制,只强调它在状态语义上的职责:保存一个逻辑一致、能够恢复执行的边界。
真正的 Checkpoint 至少要回答:
- 它属于哪个 Run / Task 和哪个 state version;
- 哪些 Node / Tool attempt 已经提交;
- 哪些 pending write / approval 尚未完成;
- resume 后应该从哪里继续;
- 相关 execution record / artifact reference 在哪里。
🔍 源码对照:Checkpoint 与 Transcript 是相邻但不同的持久化事实
完整源码与通用分析见 09-06 Checkpoint / Pause / Resume。这里不重复代码;这里关注状态分层:Checkpoint 是可恢复的 execution fact,Transcript / Conversation 是相邻但不同的持久化材料,不能互相替代。
源码说明
这里可以把三个容易混淆的概念放在同一张图里:conversation-state 代表交互状态,system-prompt-assembly 决定其中哪些信息进入当前 context,而 turn-memory 负责另一条 memory maintenance 路径。Checkpoint 则回答“Runtime 怎么恢复”,它不能由 conversation history 自动替代。
工程边界
Memory 被放进 system prompt 的具体方式是产品选择;短期 memory 也不必一定实现成独立 store。关键是语义边界,不是存储介质名称。
Shadow Harness 对照
- Context 引用 Conversation/Memory 的 projection,不直接把 store 当 request。
- Checkpoint 记录 execution state;ShortTermMemory 记录未来推理可能有用但非 authoritative 的信息。
验证
删掉 short-term memory 仍应能恢复一个 WAITING Run;删掉 checkpoint 则不能仅凭聊天记录声称安全 resume。
Short-Term Memory
Short-Term Memory 是 thread/session 范围内、为了当前连续交互而保留的工作状态。它通常包括 conversation history,但可以比 message history 更丰富:
- 当前用户输入;
- recent messages;
- 当前任务 summary;
- uploaded artifact references;
- 当前 thread 的 temporary facts。
text
trim
filter
summarize
externalize artifacts最小实现 / 数据结构
注意 ConversationStore 和 CheckpointStore 可以由同一个 PostgreSQL 实现,但接口仍然分离。
python
class ConversationStore(Protocol):
async def append(self, session_id: str, items: list[ConversationItem]) -> None: ...
async def list(self, session_id: str, before: int | None = None, limit: int = 100) -> list[ConversationItem]: ...
class ShortTermStateStore(Protocol):
async def load(self, thread_id: str) -> ThreadState: ...
async def save(self, thread_id: str, state: ThreadState) -> None: ...
class CheckpointStore(Protocol):
async def put(self, cp: Checkpoint) -> None: ...
async def get(self, checkpoint_id: str) -> Checkpoint: ...
async def latest(self, thread_id: str) -> Checkpoint | None: ...在 Shadow Harness 中怎么落地
Shadow Harness 中至少保留三个独立 contract:ConversationStore 负责不可随意等同于 Context 的历史 items;CheckpointStore 负责一致恢复点;ShortTermStateStore 负责 thread-scoped working state。它们可以共享 PostgreSQL,但不能共享同一个无语义 save_state() 接口。
工程取舍
实践中常见组合是:完整/可追溯历史 + 精简 working representation + 周期性 checkpoint。
三者解决不同问题:完整历史方便审计和重新计算;working representation 控制每轮 Context 成本;checkpoint 则保证进程退出后可以恢复执行。不要为了节省 token 删除唯一事实,也不要因为数据库保留了全部历史,就把全部历史重新塞回模型。
Failure Modes
conversation history = short-term memory = context
症状: 系统把持久化 history、thread working state 和本轮 Context 设计成同一个列表。
为什么危险: 三者目标不同:history 追求历史连续/审计,short-term state 追求 thread 连续性,Context 追求本轮高价值 token。
处理: ConversationStore 保存历史;thread state/checkpointer 保存短期状态;ContextManager 从两者选择本轮输入。
checkpoint 每一步都保存全部大型对象
症状: 每个 Tool/Node 后完整复制大型 messages、artifacts 或 graph state。
为什么危险: Checkpoint 频率和 payload 大小共同决定 write amplification、恢复 latency 与 retention cost。
处理: 只保存恢复所需 state;大型 artifact 外部化;需要时用 delta/event + periodic snapshot。
summary 覆盖原始历史
症状: Compaction 后只保留 summary,原始 conversation/tool result 被覆盖或删除。
为什么危险: Summary 是有损表示,会 drift;没有原始证据就无法重新 grounding、审计或纠错。
处理: durable archive 与 working summary 分开;summary 带 source/version,并允许重新读取原始 items/artifacts。
crash 只靠模型重新“回忆”
症状: 服务重启后把旧消息重新喂给模型,让模型猜哪些步骤已经完成。
为什么危险: 模型无法证明外部副作用和 commit boundary,容易产生 duplicate Tool calls。
处理: 使用 Checkpoint + execution records 恢复确定性执行事实;模型只参与后续语义决策。
如何验证
| Case | 断言 |
|---|---|
| conversation history = short-term memory = context | 构造 1000-turn history,断言存储仍完整,而单次 ModelRequest 严格受 token budget 控制。 |
| checkpoint 每一步都保存全部大型对象 | 长 Run 中记录 checkpoint 大小与写延迟,断言不会每步复制全部历史。 |
| summary 覆盖原始历史 | 故意生成遗漏关键 constraint 的 summary,断言仍可从原始 history/artifact 恢复。 |
| crash 只靠模型重新“回忆” | 在 remote Tool success/local commit 前强杀进程,resume 后断言已成功动作不重复。 |
关联源码
自测
- Conversation 里写着“测试通过”,但 checkpoint 没有对应 execution record。恢复时应该信哪一个?为什么?
- Short-term memory 与 checkpoint 都可能跨 Turn 存在,它们的 authority 和恢复用途有什么不同?