Skip to content

C2|Memories:默关、读写分离,不替代 AGENTS.md ​

先别把跨会话记忆当成第二份项目说明书 ​

C1 刚把 AGENTS.md 钉成「进 git 的短政策」。下一件错事是打开记忆开关,把「我喜欢怎么写测试」写进 MEMORY.md,再把 API key、内网主机名一并记住。然后以为 /memories 只影响这一窗聊天,仓库克隆到别人机器上政策还在。

判断先说清楚:Memories 是本机、跨 thread 的用户侧笔记,默认关;读和写是两套开关、两套 crate。 它住在 CODEX_HOME/memories/,不进 git,不替代 AGENTS.md,也不是 ExecPolicy。政策进仓库说明书;偏好和「上次怎么做成的」才进记忆。


1. 总闸默认关,目录是可读的 Markdown ​

feature 键是 memories(Feature::MemoryTool),阶段 Stable,default_enabled: false(features/src/lib.rs:1106-1110)。打开方式是 [features] memories = true,或 codex features enable memories(B5)。总闸关着,后面的 use_memories / generate_memories 再怎么 true 都不跑流水线(memories/write/src/start.rs:34-37)。

读路径 crate 把根目录定成 codex_home.join("memories")(memories/read/src/lib.rs:13-15)。写路径按版本还可以落到 memories / memories_v2。文件夹契约在 consolidation 模板里写死了(memories/write/templates/memories/consolidation.md:20-29):

文件角色
memory_summary.md导航层,应始终短、可检索
MEMORY.md手册条目,靠 grep 按需读
raw_memories.mdPhase 1 产出的临时合并稿,给 Phase 2 吃
rollout_summaries/按 thread 切的摘要
skills/、extensions/扩展记忆,不是项目 Skill 正本

这是 渐进披露:摘要指路,正文点名再读。和 D1 Skills、B2 tool_search 同一哲学。打开 use_memories 时,配置层会把 memories 根加进 helper 可读根(core/src/config/mod.rs:4046-4048),让模型能用工具读这些 md,而不是每轮把全部笔记灌进 prompt。注入侧若走 fragment,MemoryContextFragment 仍有硬顶:约 8900 字节(core/src/context/memory.rs:8-42),对齐 B4「单条有界」。

没有「默关 + 本机目录」会怎样?新安装就开始从所有历史里抽偏好,写进一份会进备份的文件;或者把记忆当仓库文件提交,克隆等于泄露上一个作者的账号习惯。记忆属于 CODEX_HOME,和 B5 的身份根是同一棵树。


2. 读和写必须分开拧:use_memories vs generate_memories ​

MemoriesConfig 上两个 bool(config/src/types.rs:333-338)。feature 打开之后,它们的默认值都是 true(:356-357)——所以 总闸关、子开关默认开:你一 enable memories,读和写都亮。若只想读旧笔记、不再从新会话抽取,关 generate_memories;若只想后台写、当前模型先别读,关 use_memories。

crate 边界也按这个切(memories/README.md:10-16):

  • codex-memories-read:注入、引用解析、读用量遥测。不依赖写流水线。
  • codex-memories-write:Phase 1 / 2、落盘、脱敏、workspace diff。

/memories 打开的就是这两颗旋钮外加 Reset(tui/src/slash_command.rs:143,memories_settings_view.rs:80-95)。文案比「只改当前 chat」更精确:

  • Use memories:「Applied at next thread」——读开关写进用户 config.toml,下一轮 thread 才生效。
  • Generate memories:「Current thread included」——写开关同样进用户配置,当前 thread 也会被纳入后续抽取。
  • Reset:清本地记忆文件和摘要,现有 thread 不动。

落盘走 build_memory_settings_edits → memories.use_memories / memories.generate_memories(tui/src/config_update.rs:137-147),reload_user_config: true。这是用户层配置,不是 thread-local overlay。Generate 把当前 thread 算进后续抽取;Use 要等下一个 thread。

没有读写分离会怎样?关掉「别读我的笔记」会连后台抽取一起停;或者 Reset 误删会话 JSONL。读是给未来 agent 的可见性,写是对过去 rollout 的蒸馏,寿命和风险都不一样。


3. 后台两阶段:抽取,然后整合——而且经常故意不跑 ​

流水线在 root session 启动时异步拉起(start.rs:20-37,README :29-38)。先过总闸,再过:

跳过条件代码
ephemeral 会话start.rs:34
feature 关同左
子代理 / 非 rootsource.is_non_root_agent()
StateDb 没有start.rs:60-62(B5:索引不可用则整条记忆链停)
额度不够guard::rate_limits_ok:ChatGPT 后端、Codex limit 剩余低于 min_rate_limit_remaining_percent(默认 25%,types.rs:53);额度数字来自 codex_backend_client::Client(memories/write/src/guard.rs:1),跟 A1 的 PlanType、E3 的额度共享读的是同一只后端 client
会话还太新min_rollout_idle_hours 默认 6 小时(:52)
不是交互来源只 claim INTERACTIVE_SESSION_SOURCES(phase1.rs:130-144)
整合 agent 自己Phase 2 子代理 ephemeral,并强制 generate_memories = false、use_memories = false、关掉 MemoryTool(phase2.rs:301-314)

没有单独的「少于 N 条消息就不抽」常量。短会话、空会话的出口是 Phase 1 的 succeeded_no_output(README :71-75):模型认为没什么可记,不算失败,也不写进手册。另有年龄窗(默认 10 天)和每次启动最多 claim 2 条 rollout(types.rs:50-51),防止开机把历史全煮一遍。

Phase 1 对着单条 rollout 抽结构化记忆:raw_memory、rollout_summary、可选 slug;并行有上限;lease 防重复(phase1.rs:51-55,README :40-76)。送进模型之前先过滤 item、再 redact_secrets(phase1.rs:396-422)。产出进 StateDb,不是直接改 MEMORY.md。

Phase 2 拿全局锁,把选中的 stage-1 产出 sync 成 raw_memories.md 和 rollout_summaries/,算 git-style diff,若有变更才 spawn 内部整合子代理(README :79-120)。这份 diff 不是手写的字符串比较:memories/write/src/workspace.rs 直接调 codex_git_utils::ensure_git_baseline_repository / diff_since_latest_init,把 memories 根当成一个小 git 仓库,GitBaselineDiff 写成 phase2_workspace_diff.md 给整合子代理先读——复用的是 E2/E6 也在用的同一个 git-utils crate,不是 memories 自己再发明一套 diff 算法。这个子代理:AskForApproval::Never、工作区只写 memories 根、无网络、关 collab / plugins / apps(phase2.rs:308-330)。它是工人,不是又一个会记笔记的聊天窗口——所以必须把记忆开关关掉,否则会递归抽取自己。

没有「启动时后台、两阶段、大量跳过」会怎样?每句用户话都同步调模型写笔记,额度被吃光;子代理的内部独白变成「用户偏好」;整合和抽取抢同一份 MEMORY.md。跳过不是功能残缺,是这条管道的安全阀。


4. 密钥要脱敏,记忆仍不是保险箱 ​

Phase 1 序列化 rollout、结构化输出的 raw_memory / rollout_summary / slug,都走 codex_secrets::redact_secrets(phase1.rs:422,phase1_output.rs:49-56)。v2 抽取证据同样先 redact 再进 prompt(rollout_input.rs:16-17)。测试名就叫 preserves_human_evidence_and_redacts_storage。

这只能挡住 已知形态的密钥。记忆文件仍是 CODEX_HOME 下的明文 Markdown,备份、同步盘、Reset 之前的副本都看得到。不该进记忆的:token、内网主机、客户数据。该进 ExecPolicy 禁止的命令,也不该写成「我记得用户让我 sudo」。脱敏是写路径上的补丁,不是加密存储。

没有这一步会怎样?一次 export OPENAI_API_KEY=... 的调试会话,会进 raw_memories.md,再被 Phase 2 写进手册,之后每个打开 use_memories 的 thread 都能读到。


5. 不替代 AGENTS.md,也不替代 ExecPolicy ​

对照 C1:

AGENTS.mdMemories
默认发现链有文件就加载(未信任除外)feature 关
进 git应该不应该
作用域项目、从 root 到 cwd本机用户、跨 thread
读法每轮 fragment 差量注入摘要指路 + 工具读文件
该放测试命令、禁区、验证个人偏好、重复踩坑、可复用流程线索
不该放密钥、长教程项目政策、硬拦截

把「这个仓库必须 just test -p X」写进 Memories:别人 clone 没有你的 CODEX_HOME,政策蒸发。把「我喜欢简洁回复」写进 AGENTS.md:每个贡献者的 agent 都变成你的口吻。硬拦截继续走 B3。C4 的 Personality 才改说话方式。

Phase 2 模板说记忆是为了让未来 agent 少问、少试、少踩雷(consolidation.md:8-14),不是为了让它拥有一份第二政策。手册条目应当指向「去哪读详情」,而不是把 AGENTS.md 再抄一遍。


6. 结语:带走一句话 ​

Memories 是本机默关的跨会话笔记:读和写分开拧,后台两阶段蒸馏 rollout,密钥会脱敏但仍不是政策层——项目约束继续写在 AGENTS.md,永远不许做的事继续写在 ExecPolicy。