# 8. 短期记忆迭代
# 8.1 原理
短期记忆是当前任务的工作记忆,包括近期消息、当前计划、工具结果、打开的文件、任务状态、失败尝试和中间执行产物。它追求当前相关性,不要求跨 Session 永久保存。
短期记忆常用三层:
- 原始近期窗口:最近 N 轮,保持细节和 ToolUse/ToolResult 配对。
- 结构化工作状态:goal、plan、todo、modified files、pending approvals、active agents。
- 滚动摘要:较旧步骤的结论、失败原因和未完成事项。
迭代时不是简单 append:工具的大输出要裁剪,重复观察要折叠,无效尝试要总结,关键状态要结构化提升,防止重要信息被中间日志淹没。
# 8.2 源码对照
Claude Code 的 Session Memory 可直接作为压缩摘要,并在已知 lastSummarizedMessageId 时只保留其后的消息;恢复场景如果边界未知,会使用 Session Memory 但保留全部消息,避免误删:sessionMemoryCompact.ts:506、sessionMemoryCompact.ts:562。
Codex 的 ContextManager/History 维护当前模型可见历史,并对 FunctionCall 输出按策略截断;Rollout 是持久日志,当前 Prompt History 是从日志和世界状态派生的模型视图,两者不能混为一谈:history.rs:204、history.rs:438。
# 8.3 伪代码
function updateWorkingMemory(event): // 用新事件增量更新当前任务的短期工作记忆
rawRecent.append(event) // 保留近期原始事件,以便模型看到必要细节
structuredState.reduce(event) // 同步更新计划、文件、审批和 Agent 等结构化投影
if event is ToolResult and event.size > perToolBudget: // 单个工具结果超出独立预算时先做局部治理
rawRecent.replace(event, truncateWithHeadTailAndHandle(event)) // 保留头尾、摘要和完整内容句柄,替换超大原文
if tokenEstimate(rawRecent) > recentBudget: // 近期窗口总量超过短期记忆预算时触发滚动压缩
prefix = chooseSafePrefixWithoutBreakingToolPairs(rawRecent) // 选择不拆断 ToolUse/ToolResult 的安全旧前缀
rollingSummary = summarize(rollingSummary, prefix) // 将旧摘要与本次前缀合并成新滚动摘要
rawRecent.remove(prefix) // 摘要成功后从原始近期窗口移除已覆盖事件
# 8.4 面试题
问:短期记忆和上下文窗口是什么关系?
短期记忆是逻辑状态;上下文窗口是一次模型请求的物理 Token 容量。短期记忆可以部分驻留在结构化状态、文件或数据库中,按需投影到窗口。
# 8.5 原理深化:Working Memory 是事件事实的可丢弃投影
短期记忆不能与聊天数组画等号。更准确的模型是:Rollout/Transcript 保存不可变事件,Reducer 从事件构造 Goal、Plan、文件集合、待审批项等结构化状态,Context Builder 再把“近期原文 + 结构化状态 + 滚动摘要”转换成当前 Step 的 Prompt。这个模型可见视图可以重算、裁剪甚至丢弃,但原始事件记录和关键终态不能因窗口不足而被改写。
Codex History::for_prompt 在发送前才执行规范化:为没有输出的调用补齐结果、移除孤立输出、按模型能力剥离不支持的图片或音频,并对 FunctionCall 输出应用截断策略:history.rs:141、history.rs:325、history.rs:438。这说明“存储什么”和“本次给模型看什么”是两个阶段;如果为了省 Token 直接破坏持久历史,恢复与审计都会丢失证据。
Working Memory 更新还应满足单调性:ToolCall 的终态不能被旧进度覆盖,已确认的文件修改不能因摘要遗漏而消失,待审批项只有收到明确决策才能出队。滚动摘要只是低成本检索层,不是状态机;真正影响调度和恢复的字段必须结构化保存。模型可以总结“接下来要测试”,但只有 Task/Plan Reducer 才能把相应节点从 pending 迁移为 running。
← 7. 长期记忆迭代 9. 上下文压缩机制 →