第一章回答了什么信息值得长期保存。接下来,我们沿着一段历史任务的处理过程,看看 Codex 如何把它转化为能够在未来使用的记忆。
Codex 不会在任务结束时直接改写长期记忆。后台管线启动后,系统先从历史记录中找出符合条件的任务,再逐个提取其中值得保留的信息。提取结果不会立即写入最终文件,而是先进入状态数据库,等待全局整理。
整理阶段会把多项候选记忆放在一起,处理重复、冲突和已经发生变化的信息,最后更新本地记忆文件(Memory)。单项提取彼此独立,可以有限并发;全局整理会修改同一组文件,因此同一时刻只能有一个整合任务执行。状态数据库负责保存候选结果和管线进度,本地 Memory 目录负责保存整理后的长期产物。
一、后台管线的启动与历史任务筛选
Memory 生成不是等当前任务关闭以后才进行的一次总结。根任务提交一轮包含用户输入的消息后,Codex 会尝试在后台启动记忆管线。当前回答继续生成,记忆处理不会阻塞这次交互。
这条管线并非每次都能启动。Memory 生成功能必须处于启用状态,当前任务不能是临时任务或子 Agent,并且系统需要能够访问状态数据库。当前正在执行的根任务只负责唤醒后台工作,它本身会被排除在本轮候选之外。因此,用户刚刚输入的内容不会在同一轮里立刻成为长期记忆。
/memories 提供了两个相互独立的控制项。「生成记忆」决定任务能否在以后参与记忆提取,最终记录为任务的记忆模式(memory_mode);「使用记忆」决定新任务是否读取已经存在的 Memory。关闭使用不会停止后台生成,关闭生成也不会删除已有的长期记忆。
后台管线在发起模型请求以前还会检查 Codex 的 rate limit。剩余额度低于配置门槛时,本轮提取和整理都会停止。清理过期记录不需要调用模型,可以先执行。若额度查询失败,管线仍会继续运行,避免一次监测故障长期阻断记忆生成。
thread 与 rollout 分别记录什么
系统扫描历史任务时会遇到两个容易混淆的概念。
任务线程(thread)表示一项逻辑上的 Codex 任务。它有稳定标识,并关联更新时间、工作目录、来源、模型和当前状态等信息。用户在界面中看到的一项任务,通常对应一个 thread。
任务运行记录(rollout)是该 thread 在磁盘上持续追加的执行记录。它包含用户消息、Agent 回复、工具调用与结果、会话元数据和执行事件,更接近一份按时间写入的运行日志,而不是整理好的对话稿。后续的记忆提取 Agent 真正读取的是 rollout。
状态数据库保存 thread 的元数据以及 rollout 的文件位置,但不复制完整运行记录。原始 rollout 仍保存在会话存储目录,也不属于 ~/.codex/memories/。这形成了第一道边界:rollout 保存历史证据,状态数据库负责寻找和调度任务,本地 Memory 文件保存整理后的长期知识。
哪些历史任务会进入提取流程
后台管线不会读取全部历史任务。只有同时满足以下条件的任务,才会进入记忆提取流程:
- 由用户直接发起,尚未归档且不是空任务。
- 记忆模式处于启用状态。
- 不是当前任务,且已达到最短空闲时间,未超过最长回溯期限。
- 尚未完成过提取,或在上次成功提取后产生了新内容。
记忆模式也可能在任务执行过程中改变。如果启用了外部上下文保护,使用 Web Search、工具搜索或特定外部工具的任务会被标记为「受外部上下文影响(polluted)」,从而失去记忆提取资格。这个标记只表示任务混入了外部来源,并不表示其中的内容错误。
通过上述筛选后,符合条件的历史 rollout 才会交给记忆提取 Agent。
二、单任务记忆的提取与候选结果入库
单任务记忆提取(Phase 1)每次分析一份 rollout,并为这项历史任务生成候选记忆。不同 rollout 之间互不依赖,因此可以同时提取。具体的并发限制和重复处理保护,将在后文的后台运行保障中统一说明。
提取 Agent 会看到哪些内容
rollout 保存了一项任务的完整运行记录,但记忆提取 Agent 只会看到其中与任务过程有关的部分。
系统会保留用户真正输入的任务内容、Agent 对任务的回复、工具调用与结果,以及 Agent 之间的通信。这些记录可以还原任务目标、执行过程和实际结果。
由 Codex 运行环境注入的开发者指令(developer message)不会进入提取请求。这类消息用于规定 Agent 应当怎样工作,并不是当前任务产生的事实或用户偏好。模型推理、上下文压缩记录和内部运行信息也会被排除。AGENTS.md 与 Skill 说明即使出现在用户上下文中,也会被单独删除,因为它们是系统已有的规则,不是当前任务产生的新信息。
工具结果仍会保留。测试结果、命令反馈和错误信息可以证明任务实际发生了什么;其中夹带的外部指令只能作为数据处理,不能被当成用户要求。
发送给模型以前,系统会遮盖记录中的密钥、令牌等秘密信息。rollout 过长时,内容还会根据模型的上下文容量进行裁剪,同时保留记录的开头和结尾。
这些程序规则只限定记忆提取 Agent 能够参考哪些历史记录。记录中的内容是否值得成为长期记忆,仍由提取 Agent 判断。
一次提取会产生什么
记忆提取 Agent 根据处理后的 rollout 生成 3 项结构化结果。
- 候选记忆正文(
raw_memory)记录从这项任务中提炼出的偏好、事实、操作经验和判断依据,等待全局整理。 - 任务运行摘要(
rollout_summary)概括任务目标、实际结果、验证证据和可复用结论。以后需要核对细节时,可以先读取这份摘要,不必重新打开完整 rollout。 - 任务摘要文件标识(
rollout_slug)为摘要文件提供简短的主题名称。存储层会把它与时间和短哈希组合成文件名,方便读者从文件名辨认任务内容;它本身不保存记忆。
如果这份 rollout 没有达到记忆提取门槛,Agent 可以把 3 项结果都留空。程序会检查候选记忆正文和任务运行摘要,其中任何一项为空,这次作业就记为「成功但无输出(succeeded no output)」,不会保存空的候选记忆。任务摘要文件标识不参与有效性判断;它缺失时,存储层会生成备用文件名。
模型完成提取后,系统会再次遮盖 3 项输出中的秘密信息。这样做不能证明模型从未接触敏感内容,却能降低秘密被写进持久化候选结果和本地文件的风险。
状态数据库保存的不是最终记忆
有效结果不会直接进入 MEMORY.md,而是先写入状态数据库中的候选结果表(stage1_outputs)。每个 thread 保存一份当前有效的提取结果,同时记录来源更新时间和生成时间。
这里的「候选」表示内容尚未经过跨任务整理,并不意味着数据库是一块随时可以丢弃的临时缓存。状态数据库是持久化的中间层。除了候选正文和任务摘要,它还记录使用次数(usage_count)、最近使用时间(last_usage)、作业状态、租约、重试次数、最近错误以及处理水位。
这些状态让后台工作能够跨越应用重启继续运行。worker 中途失去响应后,租约到期可以由其他 worker 接管;一次提取失败也不会被误记为完成。如果同一个 thread 过去存在候选结果,而最新提取变成空结果,旧结果会被删除,并触发后续的全局整理。
经过 Phase 1,一份 rollout 已经转化成数据库中的候选记忆,但它还不是未来任务会直接读取的长期记忆。
三、候选记忆的选择与整理输入生成
状态数据库可能保存多项尚待整理的候选记忆。Codex 不会把它们全部交给整合 Agent,而是先确定本轮真正需要处理的内容。
系统会排除长期未使用或来源过旧的记录,再按照使用次数、最近使用或更新的时间以及来源的新近程度排序,只选择配置允许的前 N 项。使用次数更高的候选会获得更高优先级,因此 Memory 是否在后续任务中被使用,确实会影响它以后参与全局整理的机会。
如果一项任务此前已经参与过全局整理,后来又被标记为「受外部上下文影响」,系统会安排新一轮整理。下一轮选择会排除这项任务,再由整合 Agent 根据输入变化更新长期记忆文件。
数据库中的候选记忆是持久化状态。经过规则筛选后得到的「本轮需要整理的候选记忆」,只表示一次全局整理的输入范围。
选择顺序与文件写入顺序也不相同。系统先按相关字段确定哪些候选入选,再按照稳定的 thread 标识顺序生成文件。即使使用次数发生变化,只要入选内容没有改变,文件也不会因为排序波动而反复重排。
本轮选中的内容会被同步成两类输入文件:
- 原始候选记忆合集(
raw_memories.md)汇总入选记录的raw_memory,供整合 Agent 查看本轮有哪些内容需要处理。 - 任务运行摘要目录(
rollout_summaries/)为每项入选任务保存一份rollout_summary,并附带 thread 标识、原始 rollout 路径、工作目录、更新时间和 Git 分支等回溯信息。
这两类文件不是最终长期记忆。它们只是把数据库中的结构化结果转换成整合 Agent 能够读取和比较的本地输入。某项候选不再入选时,对应摘要会从目录中移除,raw_memories.md 也会重新生成。新增、修改和删除都会成为下一阶段需要处理的变化。
四、全局记忆的整合与本地文件更新
全局记忆整合(Phase 2)同时查看多项候选结果。整合 Agent 需要识别重复内容,处理相互冲突或已经变化的事实,并把仍然有效的信息组织进同一套本地文件。
为什么这一阶段只能有一个写入者
Phase 1 可以并发,因为不同请求处理不同的 rollout,结果写入不同的数据库记录。Phase 2 修改的是同一个 Memory 目录。如果两个整合 Agent 同时更新 MEMORY.md,其中一个可能覆盖另一个刚完成的合并、删除或冲突处理。
因此,系统会在读取本轮候选和修改目录以前领取全局租约。同一时刻只有一个整合任务能够取得写入权。运行时间较长时,整合任务会通过心跳续租;一旦失去所有权,它就不能把自己的结果登记为成功。
整合 Agent 可以读取候选记忆、任务摘要和已有 Memory,并在 Memory 根目录内更新产物。它不能修改原始 rollout,也不会把自己的内部任务再次送进记忆生成流程。实现还关闭了 Memory、协作 Agent、Apps、Plugins 和 MCP 等能力,减少递归调用及外部状态进入整理过程的机会。
在 Codex 管理的沙箱中,整合 Agent 只能写入 Memory 根目录,并且不能访问网络。如果父任务明确使用外部权限配置,或者关闭了 Codex 管理的沙箱,实现会保留父任务的选择。因此,「整合 Agent 在任何运行模式下都绝对不能联网」并不成立。
Git 基线如何支持增量整理
状态数据库能够判断候选来源是否更新,却不能仅凭时间水位判断本地输入文件是否真的发生了变化。Codex 因此在 Memory 目录中维护一份内部 Git 基线,记录上一次成功整合后的文件状态。
系统取得全局租约后,先把本轮候选同步到 raw_memories.md 和 rollout_summaries/,再与 Git 基线比较。若文件没有变化且现有核心产物仍然有效,本轮可以直接完成,不调用整合模型。若出现新增、修改或删除,系统会把有大小上限的差异写入临时文件 phase2_workspace_diff.md,让整合 Agent 先了解这次发生了什么变化。
数据库水位和 Git 基线解决不同问题。水位负责记录后台管线处理到了哪个来源版本,Git 基线负责判断本地整理输入发生了哪些实际变化。候选来源时间发生变化时,生成的内容可能完全相同;某项候选被撤回时,Git 差异又能直接呈现相应文件的删除。
整合 Agent 退出后,系统还要检查 MEMORY.md 是否存在,并确认 memory_summary.md 符合规定格式。只有产物有效、Agent 已经关闭,而且全局租约仍属于当前 worker,系统才会更新 Git 基线和数据库中的完成状态。失败运行不会推进成功基线,后续重试仍能看到未处理的变化。
模型调用和失败如何受到控制
Codex 没有为 Memory 维护一笔精确的货币预算,它主要限制模型调用数量和单次输入规模。
Phase 1 每轮只领取有限数量的 rollout。一条后台管线最多同时执行 8 个提取请求,状态数据库还会限制所有后台管线正在执行的提取任务总数。前者限制一次启动产生的并发,后者防止多次启动叠加后突破全局上限。rollout 过长时,系统最多使用提取模型有效输入窗口的 70%,为系统指令和模型输出预留空间。
Phase 2 只整理有限数量的候选记忆。长期未使用的记录会失去资格,本地输入文件没有变化时则跳过整合模型。两个阶段也可以分别选择不同模型和推理强度。
为了避免重复提取,系统领取一份 rollout 时,会在状态数据库中登记由哪条后台管线处理,并设置到期时间。这就是处理租约(lease)。租约有效期内,其他后台管线不能重复领取;处理过程意外中断后,租约到期,这份 rollout 可以重新进入处理流程。
Phase 1 和 Phase 2 都有延迟重试和重试次数限制。新的 rollout 内容可以刷新单任务提取的处理机会。整合 Agent 异常退出、产物缺失、Git 基线更新失败或租约丢失,都会留下失败状态,不会被当成一次成功整理。
这些措施无法消除模型成本,也不保证每轮后台工作都能完成。它们限制了单轮调用规模,并避免部分失败污染已经确认的长期产物。
本地 Memory 目录保存什么
状态数据库负责协调后台管线,整理后的长期内容则保存在 ~/.codex/memories/。下面的目录树同时列出了整理输入、长期产物和运行设施:
~/.codex/memories/
├── .git/ # 上一次成功整合后的内部文件基线
├── raw_memories.md # 本轮选中的候选记忆合集
├── rollout_summaries/ # 本轮入选任务的运行摘要
│ └── <时间>-<短哈希>-<主题>.md # 单项任务摘要及回溯信息
├── MEMORY.md # 整理后的详细长期记忆和检索入口
├── memory_summary.md # 新任务优先读取的紧凑记忆摘要
├── skills/ # 从长期经验中整理出的可复用流程
│ └── <skill-name>/
│ ├── SKILL.md # 流程入口与使用说明
│ ├── scripts/ # 可选的辅助脚本
│ ├── templates/ # 可选的模板
│ └── examples/ # 可选的示例
├── extensions/ # 手动笔记或其他记忆来源的扩展入口
│ ├── ad_hoc/
│ │ ├── instructions.md # 解释如何处理手动补充内容
│ │ └── notes/ # 等待整合的补充记录
│ └── <other-extension>/
│ ├── instructions.md # 扩展来源的处理说明
│ └── resources/ # 可选的扩展资源
└── phase2_workspace_diff.md # 整理期间使用的临时文件,成功后删除
这些文件不能一概视为「记忆正文」。raw_memories.md 和 rollout_summaries/ 是从状态数据库同步出来的整理输入;MEMORY.md 是详细的长期记忆,并在需要时指向任务摘要;memory_summary.md 更短,用来告诉新任务记忆中有哪些主题以及应当继续查找什么;skills/ 保存已经稳定到可以复用的操作流程。
.git/ 和 phase2_workspace_diff.md 属于运行设施,不会作为用户记忆注入模型。extensions/ 为手动笔记或其他记忆来源提供入口,是否出现以及包含什么内容取决于实际启用的扩展。
至此,一项历史任务已经经过筛选、单任务提取和全局整理,转化为保存在本地的长期记忆。下一章关注相反的方向:新的任务启动后,系统会读取哪些文件,又会以什么顺序把这些记忆带入模型上下文。