Ch16: Codex 记忆——AI 驱动的批量整合
Part 4: 生产级 Coding Agent 记忆系统 前置章节:Ch15: Claude Code 记忆——索引驱动的实时学习 后续章节:Ch17: OpenClaw 记忆——Pre-Compaction Flush 与 Dreaming
16.1 一句话定位
Codex 是 OpenAI 的 Coding Agent,采用两阶段异步 AI pipeline 做记忆管理,以”批量整合”为哲学——不追求每次对话都写记忆,而是在 session 结束后统一提取、定期整合,用全局共享的记忆空间服务所有项目。
16.2 日常类比:期末考试复习法
Claude Code 像一个勤奋的学生,每节课后都整理笔记——当天学的当天记,第二天上课前翻笔记。优点是笔记新鲜,缺点是每节课多花 15 分钟整理,而且笔记可能很碎片(同一主题分散在多天的笔记里)。
Codex 像另一个学生,期末考试前才整理笔记——平时上课只听讲不整理,考前一周把所有笔记拿出来,重新分类、合并、去掉重复的、补充缺失的,做成一份精炼的复习提纲。优点是整理效率高(一次性处理所有内容),缺点是考前之前的知识可能已经忘了一半,而且临时重要信息可能来不及整合。
两种方法都能通过考试,但适用场景不同:
| 场景 | 课后整理(Claude Code) | 考前整理(Codex) |
|---|---|---|
| 每天学新东西 | ✅ 当天记住 | ⚠️ 要等整合才知道 |
| 突发重要信息 | ✅ 立即记忆 | ❌ 可能遗漏 |
| 长期积累 | ⚠️ 笔记碎片化 | ✅ 整合后更精炼 |
| 整理成本 | 高(每天花时间) | 低(集中花一次) |
类比的边界
| 考前复习类比 | 对应物 | 类比失效之处 |
|---|---|---|
| 课堂笔记 | rollout 原始数据 | 笔记是人写的,rollout 是 AI 执行的完整轨迹 |
| 复习提纲 | MEMORY.md + memory_summary.md | 复习提纲是静态的,Codex 的记忆会持续更新 |
| 考前一周 | Phase2 整合窗口 | 考前复习是确定时间点,整合由全局锁和冷却期控制 |
| 分类整理 | Phase2 consolidation agent | 人工分类比 AI 分类更可靠 |
16.3 Phase1 详解:从 Rollout 提取结构化记忆
Phase1 是记忆的采集阶段——从每个 session 的执行轨迹(rollout)中提取有价值的信息,存入数据库等待整合。
16.3.1 触发时机与并发
Phase1 在 root session 启动时并行执行,不是串行等待:
Root Session 启动
│
├─ Phase1(concurrency=8)──── 并行处理 8 个 rollout
│ ├─ Rollout A → 结构化 JSON
│ ├─ Rollout B → 结构化 JSON
│ ├─ ...
│ └─ Rollout H → 结构化 JSON
│
└─ 主对话正常进行(不等待 Phase1 完成)
concurrency=8 意味着最多同时处理 8 个历史 rollout。这个数字的选取有讲究:
- 太小(如 1):提取速度慢,积累的 rollout 处理不完
- 太大(如 32):API 并发压力大,可能触发限流
- 8 是在延迟和成本之间的经验平衡点
16.3.2 Reasoning=Low:轻量推理降成本
Phase1 提取时使用 reasoning=Low 的模型配置:
| 配置 | 含义 | 为什么 |
|---|---|---|
| reasoning=Low | 最少推理步骤,快速输出 | Phase1 只做信息提取,不需要复杂推理 |
| 对比 reasoning=Medium | Phase2 整合时使用 | 整合需要判断哪些记忆该合并、哪些该淘汰 |
| 对比 reasoning=High | 用户核心任务使用 | 核心任务需要深度推理 |
成本差异显著:Low 的 token 消耗可能只有 High 的 1/3,而 Phase1 处理量大(每个 rollout 都要提取),省下来的成本很可观。
16.3.3 多层内容过滤
不是 rollout 里的所有内容都值得提取。Phase1 有多层过滤器:
第一层:移除 developer 消息
Developer 消息是系统注入的指令(类似 system prompt),包含通用规则和工具描述。这些信息:
- 是静态的,每次 session 都一样
- 已经在 AGENTS.md 和 CLAUDE.md 中有对应
- 提取到记忆中是纯冗余
第二层:移除 AGENTS.md 和 skill 定义
这些是项目配置,不是从执行中学到的知识:
- AGENTS.md 内容可以用
Read工具随时获取 - Skill 定义是固定的,不需要记忆
- 提取它们会浪费记忆空间
第三层:保留环境信息
环境信息虽然不是”学到的知识”,但对跨 session 复现问题很重要:
- 操作系统版本(macOS 27.0.0)
- 安装的工具版本(node 20.11.0, pnpm 9.1.0)
- 目录结构和权限
- 环境变量(脱敏后)
第四层:保留 InterAgent 通信
Codex 支持多 agent 协作(子 agent、评审 agent 等),agent 之间的通信包含有价值的协作知识:
- 子 agent 发现的问题(如”测试用例 X 失败”)
- 评审 agent 的建议(如”这个函数应该拆分”)
- agent 间的任务分工和依赖关系
16.3.4 Secret 脱敏
在保留环境信息的同时,必须脱敏(sanitize)secret:
# 脱敏规则示例
rules = {
"API_KEY=sk-abc123..." → "API_KEY=[REDACTED]",
"token=eyJhbGci..." → "token=[REDACTED]",
"password=mysecret" → "password=[REDACTED]",
"DATABASE_URL=postgres://user:pass@host" → "DATABASE_URL=postgres://[REDACTED]@host",
}
脱敏策略是宁可过度也不遗漏——任何看起来像 secret 的值都替换,即使可能误伤非敏感的字符串。这是因为在记忆泄露场景下,secret 暴露的代价远大于丢失一条非敏感信息的代价。
16.3.5 存入 SQLite stage1_outputs
提取结果存入 SQLite 数据库的 stage1_outputs 表:
CREATE TABLE stage1_outputs (
id INTEGER PRIMARY KEY,
session_id TEXT NOT NULL, -- 来源 session
rollout_id TEXT NOT NULL, -- 来源 rollout
category TEXT NOT NULL, -- 记忆类别
content TEXT NOT NULL, -- 结构化 JSON
created_at TIMESTAMP DEFAULT NOW,
usage_count INTEGER DEFAULT 0 -- 被引用次数
);
usage_count 字段是 citation 正反馈循环的关键——我们会在 16.5 节详述。
16.4 Phase2 详解:AI 驱动的批量整合
Phase2 是记忆的整合阶段——把 Phase1 提取的碎片记忆合并成精炼的长期记忆。这是 Codex 记忆系统的核心,也是最复杂的部分。
16.4.1 全局锁与冷却期
Phase2 不是随时运行的,有全局锁 + 6 小时冷却期:
时间线:
─── Phase2 整合 ─── 锁定 6h ─── Phase2 整合 ─── 锁定 6h ───
↑ ↑
合并记忆 合并记忆
生成 MEMORY.md 生成 MEMORY.md
为什么需要冷却期?
- 防止频繁整合浪费算力:整合一次可能消耗数十万 token(处理 256 条记忆 + 多轮推理),6 小时内跑两次是浪费
- 给新记忆积累时间:刚提取的记忆可能还不完整,等 6 小时后更多 session 的记忆入库,整合效果更好
- 避免和用户任务争资源:整合是后台任务,不应该占用用户正在使用的 API 配额
16.4.2 输入选择:usage_count + recency 排序
Phase2 不处理所有 stage1_outputs,而是按优先级选 top-256:
优先级分数 = α × usage_count + β × recency_score
其中:
usage_count:该记忆被 Phase2 输出引用的次数(被引用越多越重要)recency_score:该记忆的创建时间距现在越近分数越高(近期记忆更可能相关)α + β = 1:两个权重的和为 1,具体值根据经验调整
为什么选 256? 这是输入 token 预算的约束——256 条记忆,每条平均 200 tokens,约 51200 input tokens,加上整合 prompt 本身,刚好在 GPT-4 级模型的上下文窗口内留出足够的输出空间。
16.4.3 隔离沙箱执行
Phase2 的整合在隔离沙箱中运行,有三个限制:
| 限制 | 说明 | 原因 |
|---|---|---|
| 无网络 | 整合 agent 不能访问网络 | 防止记忆整合过程中访问外部资源,确保整合只基于已有记忆 |
| 无 MCP | 不能调用 MCP 工具 | MCP 工具可能有副作用(如修改文件、发消息),整合过程不应有副作用 |
| 无人工审批 | 整合结果不需要人工确认 | 整合是后台自动化流程,如果需要人工审批就无法 24/7 运行 |
沙箱的意义是纯粹——整合 agent 只做一件事:分析输入记忆,输出整合结果。没有外部干扰,没有副作用,结果可预测。
16.4.4 Consolidation Agent(reasoning=Medium)
整合由一个专门的 consolidation agent 执行,使用 reasoning=Medium 配置:
- 为什么不是 Low? 整合需要判断记忆的重要性、发现矛盾、决定合并策略——这些需要中等程度的推理
- 为什么不是 High? 整合不需要解决复杂的编程问题,Medium 的推理深度足够,且成本更低
Consolidation agent 的典型工作流:
输入:top-256 条 stage1 记忆
│
├─ 第 1 轮:按主题分组(技术栈、用户偏好、项目约定、bug 历史...)
├─ 第 2 轮:组内合并(重复的合并,矛盾的标记,过时的淘汰)
├─ 第 3 轮:跨组关联("偏好 pnpm" 和 "pnpm 的 CI 配置" 应该关联)
└─ 输出:整合后的长期记忆
16.4.5 三种输出
Phase2 整合后产生三种输出:
1. MEMORY.md——长期记忆索引
和 Claude Code 的 MEMORY.md 类似,是记忆文件的索引。但 Codex 的 MEMORY.md 是全局共享的——不限于单个项目,服务所有项目。
# Memory Index (Codex Global)
## Technical Preferences
- [pnpm-over-npm](memory/pnpm-over-npm.md): 全局偏好 pnpm
- [typescript-strict](memory/typescript-strict.md): 偏好 TypeScript strict mode
## Project Knowledge
- [nextjs-app-router](memory/nextjs-app-router.md): Next.js App Router 经验
- [trpc-patterns](memory/trpc-patterns.md): tRPC 常用模式
## Common Pitfalls
- [ci-cache-issues](memory/ci-cache-issues.md): CI 缓存导致的各种坑
2. memory_summary.md——精炼摘要
这是全量注入上下文的文件,包含所有记忆的精炼摘要——不是完整内容,而是压缩后的关键信息。典型格式:
# Memory Summary
## User Preferences
- Uses pnpm (not npm/yarn)
- Prefers TypeScript strict mode
- Dark theme, vim keybindings
## Technical Knowledge
- Next.js App Router: use Server Components by default, client components need 'use client'
- tRPC: always define input/output schemas with Zod
- CI: GitHub Actions, cache node_modules but not .next/
## Pitfalls to Avoid
- Don't use var (always const/let)
- pnpm store path differs from npm, CI needs PNPM_HOME
- Next.js 14: not all App Router features are stable, check docs
3. skills/ 目录——提取的技能
整合过程中可能发现可复用的操作模式,提取为 skill(类似 Claude Code 的 skill 系统):
skills/
├── debug-ci.md ← "如何调试 CI 失败"
├── setup-nextjs.md ← "Next.js 项目初始化清单"
└── review-checklist.md ← "code review 检查要点"
16.5 读取路径:全量注入 + Citation 正反馈
16.5.1 memory_summary.md 全量注入
和 Claude Code 不同,Codex 的记忆读取不做检索——memory_summary.md 全量注入到 developer instruction 中:
Developer Instruction
│
├── 系统指令(工具描述、安全规则...)
├── AGENTS.md(项目配置)
└── memory_summary.md(记忆摘要,全量注入)
为什么全量注入? 因为 memory_summary.md 已经是 Phase2 整合后的精炼摘要,信息密度高、噪音少,全量注入的成本可控。如果 Claude Code 的记忆文件是”具体的书”,Codex 的 memory_summary.md 就是”复习提纲”——提纲本身很薄,可以全量带上。
16.5.2 Citation 正反馈循环
这是 Codex 独有的机制,也是其记忆系统的亮点:
记忆 A 被整合到 memory_summary.md
│
├─ Codex 在后续 session 中使用了记忆 A 的内容
├─ Phase1 检测到使用(citation)
├─ usage_count += 1
└─ 下次 Phase2 排序时,记忆 A 的优先级更高
│
└─ 更可能被保留在 top-256 输入中
正反馈循环:被引用的记忆 → usage_count 增加 → 优先级升高 → 更可能被整合 → 更可能被引用 → …
这形成了自然选择——有用的记忆被反复引用,优先级越来越高;没用的记忆逐渐被新记忆挤出 top-256,自然淘汰。
16.5.3 和硬截断的对比
| 淘汰机制 | 代表 | 优点 | 缺点 |
|---|---|---|---|
| 硬截断 | Claude Code 200 文件上限 | 简单可预测 | 可能截断有用的记忆 |
| Citation 驱动柔性淘汰 | Codex usage_count 排序 | 自适应,有用的自然保留 | 需要”引用”才能证明有用 |
| 手动管理 | 用户自己删除 | 精确控制 | 人力成本高 |
Citation 驱动的柔性淘汰本质是用使用数据代替人为判断——让市场(使用频率)决定哪些记忆值得保留。局限是”冷启动”问题:新写入的记忆还没有被引用过,usage_count=0,可能在第一次 Phase2 排序时就被淘汰。
16.6 资源让步机制:API 余量守卫
Codex 的记忆生成(Phase1 + Phase2)不是无限运行的——当 API 余量紧张时,记忆生成主动让路给核心任务。
16.6.1 触发条件
if (api_remaining_quota < 25%):
pause_memory_generation()
log("Memory generation paused: API quota below 25%")
当 API 余量低于 25% 时:
- 暂停 Phase1:不再从新 rollout 提取记忆,已有的 stage1_outputs 留在数据库等后续处理
- 暂停 Phase2:如果整合正在进行,完成当前步骤后暂停,不启动新整合
- 恢复条件:API 余量回升到 25% 以上时自动恢复
16.6.2 为什么是 25%?
- 10% 太低:API 余量到 10% 时可能已经被其他并发请求消耗完,来不及暂停
- 50% 太高:一半以上时间都在暂停记忆生成,记忆积累速度太慢
- 25% 是经验值:足够余量保证核心任务完成,同时不会过度限制记忆生成
16.6.3 让步的优先级链
核心任务(用户直接请求)
> 记忆读取(memory_summary.md 注入)
> Phase2 整合
> Phase1 提取
优先级从高到低:
- 核心任务永远不暂停——用户说”帮我修这个 bug”,不管 API 余量多少都执行
- 记忆读取只在极端情况暂停(如余量 < 5%)
- Phase2 整合在余量 < 25% 时暂停
- Phase1 提取最优先暂停——因为它最不紧急,延迟几小时提取无影响
16.7 和 Claude Code 的四个关键差异
16.7.1 差异一:实时 vs 批量写入
| 维度 | Claude Code | Codex |
|---|---|---|
| 写入时机 | 每轮对话后(extractMemories) | Session 结束后(Phase1) |
| 写入粒度 | 单条记忆 | 整个 rollout |
| 写入延迟 | <2s(异步 fire-and-forget) | 分钟级(并发处理 8 个 rollout) |
| 信息完整性 | 可能遗漏(extractMemories 可能判断失误) | 更完整(rollout 包含完整轨迹) |
| 实时性 | 上一轮学的下一轮就能用 | 要等 Phase2 整合后才能用 |
核心权衡:实时写入保证不丢短期信息,批量写入保证整合质量更高。
16.7.2 差异二:索引 + LLM 筛选 vs 全量注入
| 维度 | Claude Code | Codex |
|---|---|---|
| 注入方式 | MEMORY.md 索引 + Sonnet 选 5 个文件 | memory_summary.md 全量注入 |
| 注入内容 | 完整的记忆文件内容 | 压缩后的精炼摘要 |
| 选择精度 | 高(LLM 语义筛选) | N/A(全量) |
| 上下文消耗 | 低(只占 5×4KB=20KB) | 中(摘要可能 30-50KB) |
| 漏检风险 | 有(5 个名额可能不够) | 无(全量在场) |
| 噪音风险 | 低(精选的) | 中(摘要可能含不相关条目) |
核心权衡:索引 + LLM 筛选精度高但漏检风险存在,全量注入无漏检但精度依赖整合质量。
16.7.3 差异三:项目隔离 vs 全局共享
| 维度 | Claude Code | Codex |
|---|---|---|
| 记忆作用域 | 项目级(.claude/memory/) |
全局级(跨项目共享) |
| 跨项目复用 | 需要手动在全局目录配置 | 自动共享 |
| 项目污染风险 | 低(严格隔离) | 中(项目 A 的约定可能误用于项目 B) |
| 记忆总量 | 200 文件/项目 | top-256 全局 |
| 团队协作 | 项目记忆可 git 共享 | 全局记忆是个人知识库 |
核心权衡:项目隔离避免污染但复用受限,全局共享复用方便但需要更精细的整合来区分通用知识和项目特定知识。
16.7.4 差异四:硬截断 vs Citation 驱动柔性淘汰
| 维度 | Claude Code | Codex |
|---|---|---|
| 容量限制 | 200 文件硬上限 | top-256 排序输入 + 柔性淘汰 |
| 淘汰机制 | 超限不写入(或 autoDream 合并腾空间) | usage_count 排序,低分自然淘汰 |
| 新记忆命运 | 受限于剩余空间 | 和老记忆公平竞争(但 usage_count=0 初始劣势) |
| 稳定性 | 高(200 个文件始终可用) | 中(高频使用的记忆稳定,低频的可能被淘汰) |
核心权衡:硬截断简单可预测但可能截断有用记忆,柔性淘汰自适应但冷启动劣势可能导致新记忆被过早淘汰。
16.8 批量整合的失败模式
批量整合不是银弹,有明确的失败模式:
16.8.1 短期信息遗漏
Phase1 提取和 Phase2 整合之间有延迟(最短 6 小时冷却期),期间的重要信息可能丢失:
场景:用户在 session A 中说”这个 API 的认证方式改了,用新 token”,Phase1 提取了这条信息,但 Phase2 还没整合。用户在 session B 中问”怎么调这个 API”,Codex 用的是旧的认证方式——因为新信息还没整合到 memory_summary.md。
Claude Code 不会出现这个问题:session A 中 extractMemories 实时写入了记忆,session B 中 Sonnet 语义筛选会选中这条记忆。
16.8.2 整合质量的不稳定性
Phase2 的整合质量依赖 consolidation agent 的推理能力:
- 过度合并:两条看似相似但实际有重要差异的记忆被合并,丢失细节
- 误判过时:一条仍然有效的记忆被判定为”过时”而淘汰
- 遗漏关联:两条应该关联的记忆(如”偏好 pnpm”和”pnpm 的 workspace 配置”)没有被整合 agent 发现
16.8.3 Citation 的马太效应
正反馈循环可能导致马太效应(富者越富):
- 一条记忆偶然被多次引用 → usage_count 很高 → 优先级更高 → 更容易出现在 top-256 → 更容易被引用 → …
- 另一条同样重要但偶然没被引用的记忆 → usage_count 低 → 被淘汰 → 永远无法被引用 → …
这和搜索引擎的”富者越富”问题类似——热门页面因为排名高所以更热门,新页面很难获得曝光。
16.8.4 全局共享的污染风险
全局记忆可能在不同项目间产生污染:
- 在项目 A 中学到的约定(如”用 CommonJS”)被错误地应用到项目 B(项目 B 用 ESM)
- memory_summary.md 中的信息没有项目标签,Codex 可能无法判断某条记忆的适用范围
16.9 成本模型对比
16.9.1 Codex 记忆成本
| 阶段 | 触发频率 | 单次 token 消耗 | 单次成本 | 日成本(估) |
|---|---|---|---|---|
| Phase1 提取 | 每 session 启动 | ~2000 tokens (reasoning=Low) | ~$0.006 | ~$0.03 |
| Phase2 整合 | 每 6h 一次 | ~100K tokens (reasoning=Medium) | ~$0.3 | ~$1.2 |
| 记忆读取 | 每 session 启动 | ~10K tokens (注入摘要) | ~$0.03 | ~$0.15 |
| 合计 | ~$1.38/天 |
16.9.2 Claude Code 记忆成本(对比)
| 阶段 | 触发频率 | 单次 token 消耗 | 单次成本 | 日成本(估) |
|---|---|---|---|---|
| 语义筛选 | 每轮对话 | ~1200 tokens | ~$0.006 | ~$0.6 |
| extractMemories | 每轮无工具响应 | ~500 tokens | ~$0.0075 | ~$0.375 |
| autoDream | 每 24h 一次 | ~50K tokens | ~$0.15 | ~$0.15 |
| 合计 | ~$1.125/天 |
16.9.3 成本结构差异
- Codex 的成本集中在 Phase2(批量整合是最贵的操作),而 Phase1 很便宜
- Claude Code 的成本分散在日常操作(语义筛选 + extractMemories),而 autoDream 相对便宜
- 总体:两者日成本在同一量级,但 Codex 有更明显的”脉冲”特征(每 6h 一个大消耗),Claude Code 更平滑
16.10 Phase1 过滤的深层逻辑
回到 Phase1 的多层过滤,理解其深层设计意图:
16.10.1 为什么要移除 developer 消息?
Developer 消息包含系统级指令,是”规则”而非”知识”。记忆系统存的是知识(从执行中学到的),不是规则(系统预设的)。混淆两者会导致:
- 记忆空间被静态规则占满(规则不变化,但每次 rollout 都重复出现)
- 整合 agent 试图”优化”规则(不该优化的东西)
16.10.2 为什么要保留 InterAgent 通信?
InterAgent 通信是 Codex 多 agent 协作的副产品,包含单 agent 视角看不到的知识:
- 子 agent 可能发现了主 agent 忽略的问题
- 评审 agent 的建议可能影响未来的编码决策
- agent 间的分工模式本身就是可复用的知识
如果过滤掉 InterAgent 通信,就丢失了协作学习的价值——记忆系统变成了单 agent 的个人笔记,而不是团队的知识库。
16.10.3 Secret 脱敏的不可逆性
脱敏是不可逆的——一旦 API_KEY=sk-abc123 被替换为 API_KEY=[REDACTED],原始值永远丢失。这是刻意的:
- 即使数据库被泄露,secret 也无法恢复
- 整合 agent 不可能”意外”把 secret 写入 MEMORY.md
- 唯一的代价是:如果确实需要记忆某个 secret 的格式特征(如”key 以 sk- 开头”),需要在脱敏前提取格式信息
16.11 章节小结
Codex 的记忆系统是一个AI 驱动的批量整合方案,核心设计可以概括为三句话:
- 异步提取:Phase1 在 session 启动时并行从 rollout 提取结构化 JSON(concurrency=8, reasoning=Low),多层过滤确保信噪比,secret 脱敏确保安全,存入 SQLite 等待整合。
- 批量整合:Phase2 在 6h 冷却期后,从 top-256 输入中由 consolidation agent(reasoning=Medium)在隔离沙箱中合并,输出 MEMORY.md + memory_summary.md + skills/。
- 全量注入 + Citation 循环:memory_summary.md 全量注入 developer instruction,被引用的记忆 usage_count 递增形成正反馈循环,实现柔性淘汰。
和 Claude Code 的核心差异在于哲学:Claude Code 是”实时学习,每刻都记”,Codex 是”批量整合,定期复习”。前者不丢短期信息但碎片化,后者整合质量高但可能遗漏短期重要信息。两者不是优劣关系,而是设计空间中不同位置的权衡选择。
下一章我们将看到 OpenClaw 的第三种方案——不是实时也不完全是批量,而是在上下文压缩的临界点做记忆保存,这引出了一个独家的创新:Pre-Compaction Flush。
导读目录:README.md 上一章:Ch15: Claude Code 记忆——索引驱动的实时学习 下一章:Ch17: OpenClaw 记忆——Pre-Compaction Flush 与 Dreaming