Yoda
参考Agent 设计指南上下文

记忆系统(Auto Memory / Memories)

CC 用「明文 markdown 目录 + 行内即时写入 + 后台 fork 提取」做记忆;Codex 用「SQLite 任务队列 + 两阶段离线蒸馏」做记忆——在线 vs 离线两种范式

记忆系统(Auto Memory / Memories)

结论

CC 的记忆是在线、文件优先、默认开启的:每个 git repo 对应 ~/.claude/projects/<slug>/memory/ 目录,MEMORY.md 索引(截断 200 行 / 25KB)每会话注入,系统提示词内含完整的「两步保存协议」让主 agent 边干活边用 Write/Edit 工具写记忆;turn 结束后还有一个共享 prompt cache 的后台 fork agent(extractMemories)兜底补漏。Codex 的记忆是离线、数据库驱动、默认关闭(实验特性)的:rollout 结束后由后台任务做 stage-1 提取(每线程产出 raw_memory + rollout_summary,写入 $CODEX_HOME/memories_1.sqlite),再由 phase-2 全局 consolidation agent(带租约/水位线并发控制)把原始记忆蒸馏到 $CODEX_HOME/memories/ 文件工作区(memory_summary.md / MEMORY.md 注册表 / skills / rollout_summaries);读路径是把 memory_summary.md(截 2500 token)嵌入 developer instructions,并暴露 memories.list/read/search/add_ad_hoc_note 工具。另外 CC 还有一套独立的会话内记忆(SessionMemory:后台 fork 维护当前会话笔记,用于压缩保真),不要与跨会话 auto memory 混淆。

研究问题

  • 记忆存哪、谁写、何时写(自动 vs 手动)?
  • 召回机制:什么内容进上下文、截断策略?
  • 污染/过期控制?

各 Agent 设计与实现

Claude Code

来源:重构源码 src_2026-03-31(memdir/、services/extractMemories/、services/SessionMemory/、services/compact/)。[一手源码]

① Auto memory(跨会话,默认开)

  • 位置getAutoMemPath() 解析顺序为:
    1. CLAUDE_COWORK_MEMORY_PATH_OVERRIDE env(SDK/Cowork 全路径重定向,paths.ts:152-166);
    2. settings autoMemoryDirectory故意排除 projectSettings——防止恶意 repo 把记忆目录指到 ~/.ssh 获得写权限豁免,memdir/paths.ts:174-186 安全注释;路径还要过 validateMemoryPath 的反 ~-展开/UNC/根目录校验,paths.ts:109-149);
    3. 默认 ~/.claude/projects/<sanitized-git-root>/memory/,用 findCanonicalGitRoot 让同 repo 的所有 worktree 共享一份记忆(paths.ts:200-206,引 issue #24382)。
  • 开关:优先级链 CLAUDE_CODE_DISABLE_AUTO_MEMORY env → CLAUDE_CODE_SIMPLE(--bare)→ CCR 无持久存储 → settings autoMemoryEnabled → 默认开启(paths.ts:30-56 doc comment 原文列举)。
  • 写入(主路径,行内)loadMemoryPrompt()memdir/memdir.ts:419-506)把行为指令注入系统提示词 memory 段:四类型记忆分类(user/feedback/project/reference)、「两步保存」协议——先写独立主题文件(带 frontmatter),再在 MEMORY.md 加一行指针;明确 "MEMORY.md is an index, not a memory"(memdir.ts:218-234)。harness 预先 ensureMemoryDirExists 并在 prompt 里声明目录已存在,省掉模型的 mkdir 试探(memdir.ts:116-117, 129-147)。
  • 写入(兜底,后台)services/extractMemories/extractMemories.ts 文件头注释:在每个完整 query loop 结束(最终响应无 tool call)时经 stopHooks 触发,用 runForkedAgent 完美 fork 主对话、共享父级 prompt cache 提取持久记忆;若主 agent 本 turn 已写过记忆则跳过该区间(hasMemoryWritesSince)。受 GrowthBook gate tengu_passport_quail 控制(paths.ts:69-78)。
  • 召回MEMORY.md 作为 AutoMem 类型进 getMemoryFiles() 与 CLAUDE.md 同通道注入(claudemd.ts:979-992),截断 MAX_ENTRYPOINT_LINES = 200 / MAX_ENTRYPOINT_BYTES = 25_000(memdir.ts:35-38),超限追加 WARNING 文本教模型自己整理索引(memdir.ts:94-102)。主题文件不进上下文,模型按需 Read。可选 tengu_coral_fern gate 注入「搜索过去上下文」指南(Grep memory 目录 → 兜底 grep 会话 .jsonl 转写,memdir.ts:375-407)。
  • 过期控制:靠 prompt 约定("Update or remove memories that turn out to be wrong or outdated"、查重后再写,memdir.ts:215-216, 232-233),无代码级 TTL。
  • 变体:TeamMem(团队共享目录 memory/team/,组织同步);KAIROS 助手模式改为 append-only 日志 logs/YYYY/MM/YYYY-MM-DD.md + 夜间 /dream 蒸馏(memdir.ts:318-370)。

② 手动记忆 = CLAUDE.md。当前版本里「让 Claude 记住某事」默认落 auto memory,想进 CLAUDE.md 需明说或经 /memory 编辑(docs/memory.md:390 [一手文档])。早期的 # 前缀快捷键在本源码快照和现行文档中均不存在(已检索 startsWith('#') 全量调用点,无记忆相关命中)[一手源码+推断]。

③ Session memory(会话内,勿混淆)services/SessionMemory/sessionMemory.ts:1-5 头注释——后台 fork 子代理周期性维护一份当前会话的 markdown 笔记,路径 {projectDir}/{sessionId}/session-memory/utils/permissions/filesystem.ts:259-267);sessionMemoryCompact.ts 在压缩时用它替代纯摘要,保留 10K–40K token 的尾部消息(DEFAULT_SM_COMPACT_CONFIG,sessionMemoryCompact.ts:58-62)。提取模板可被 ~/.claude/session-memory/prompt.md 自定义(services/SessionMemory/prompts.ts:108-114)。它的生命周期是单会话,属于压缩保真机制而非长期记忆。

④ 用户可见性与审计/memory 命令列出本会话加载的全部 CLAUDE.md/rules 文件、提供 auto memory 开关与目录入口;界面上的 "Writing memory" / "Recalled memory" 提示对应模型对 memory 目录的 Write/Read(docs/memory.md:380-390 [一手文档])。子代理也可拥有独立 memory 目录(tools/AgentTool/agentMemory.ts,经 isAgentMemoryPath 参与路径鉴权,utils/memoryFileDetection.ts:115-120)。值得注意的安全细节:Bash 命令若试图直接操作 session-memory / 会话转写 .jsonl,会被 detectSessionFileType(memoryFileDetection.ts:40-59)识别并走特殊权限路径——记忆文件在 CC 中是被权限系统单独建模的资源类别。

Codex CLI

来源:codex-rs @ b89ce9astate/memories/write/ext/memories/。[一手源码]

  • 特性开关Feature::MemoryTool,key "memories"Stage::Experimentaldefault_enabled: false,菜单文案 "Allow Codex to create new memories from conversations and bring relevant memories into new conversations",公告推 /memories 命令(features/src/lib.rs:847-856)。另有 Chronicle feature(lib.rs:864-868)处于 UnderDevelopment,本快照未见实质实现接线 [一手源码]。
  • 存储双层:任务/状态层是 $CODEX_HOME/memories_1.sqlitestate/src/lib.rs:83 MEMORIES_DB_FILENAME),由 StateRuntime 带独立 migrator 打开(state/src/runtime.rs:126-131, 214-226);内容层是 $CODEX_HOME/memories/ 文件工作区。
  • 写路径 = 两阶段离线流水线memories/write/src/phase1.rsphase2.rs;数据模型 state/src/model/memories.rs):
    • Stage 1:扫描已结束且闲置足够久的 rollout(min_rollout_idle_hours,注释建议 >12h),按线程 claim 任务(Stage1JobClaimOutcome:Claimed / SkippedUpToDate / SkippedRunning / SkippedRetryBackoff / SkippedRetryExhausted——完整的租约+退避状态机),把 rollout 喂给 extract_model 产出结构化 JSON { raw_memory, rollout_summary, rollout_slug }(phase1.rs:50-61),写库前过 redact_secrets(phase1.rs:20)。
    • Phase 2:全局唯一锁 + input_watermark 水位线(Phase2JobClaimOutcome,memories.rs:54-68),spawn 一个 consolidation agent 整理 memory 工作区(prepare_memory_workspace / memory_workspace_diff,phase2.rs:10-15),重建 raw_memories 文件并同步 rollout_summaries。
    • 资源保护:min_rate_limit_remaining_percentmax_rollouts_per_startupmax_rollout_age_days 等配额全在 [memories] config(config/src/types.rs:259-292)。
  • 读路径build_memory_tool_developer_instructions()$CODEX_HOME/memories/memory_summary.md,截断到 2500 token 后填入 read_path.md 模板注入 developer instructions(ext/memories/src/prompts.rs:27-51)。模板本身是一份精细的「检索决策边界」prompt:何时跳过记忆、何时默认查、4-6 步检索预算、目录布局(memory_summary.md → MEMORY.md 注册表 → skills/ → rollout_summaries/ jsonl)(ext/memories/templates/memories/read_path.md [一手源码])。工具面:memories 命名空间下 list(上限 2000 条)/search(上限 200 条)/read(默认 20K token)/add_ad_hoc_noteext/memories/src/lib.rs:11-22)——ad_hoc_note 是唯一的会话内主动写入口,其余写入全部离线。
  • 污染控制disable_on_external_context(别名 no_memories_if_mcp_or_web_search)——线程一旦接触外部上下文(MCP/网搜)就标记 memory_mode = "polluted",不参与记忆生成(types.rs:263-265);这是对 prompt-injection 进长期记忆的工程化防御,CC 无对等机制。
  • 运维codex debug clear-memories(隐藏命令,cli/src/main.rs:236-238, 1913-1915)清空记忆库;codex doctor 输出包含 memories DB 完整性检查("memories DB ... integrity ok",cli/src/doctor/output.rs:1387-1395)。

自动 vs 手动小结:Codex 的会话内手动入口只有 add_ad_hoc_note 工具与 /memories 管理命令;记忆主体完全由后台流水线自动生成,用户不被期望直接编辑 sqlite 或工作区文件。CC 则相反——明文目录鼓励用户直接增删改,自动提取只是兜底。两种取向分别对应「记忆是系统资产」与「记忆是用户资产」两种产品哲学 [推断]。

差异矩阵

维度Claude CodeCodex CLI
默认状态开启(v2.1.59+,docs/memory.md:329)关闭(Experimental feature memories
存储明文 markdown 目录(每 repo 一份)SQLite(memories_1.sqlite)+ $CODEX_HOME/memories/ 文件区(全局一份)
作用域per-project(git root 归一,worktree 共享)全局 CODEX_HOME(read_path 模板用关键词路由到 workspace)
写入时机在线:主 agent 行内随时写 + turn 末后台 fork 兜底离线:rollout 闲置 >12h 后两阶段批处理;会话内仅 ad_hoc_note
写入者主模型自己(共享上下文与 cache)独立 extract_model / consolidation_model(可单独配模型)
召回MEMORY.md 索引每会话注入(200 行/25KB 截断),主题文件按需 Readmemory_summary.md(2500 token 截断)注入 developer instructions + 专用 list/search/read 工具
截断/预算200 行 / 25KB 索引上限summary 2500 token;read 20K token;search 200 条
污染防御无代码级机制(靠 prompt 约定 + 用户可编辑明文)disable_on_external_context 标记 polluted 线程
过期处理prompt 约定模型自行更新/删除max_unused_days / max_rollout_age_days 代码级筛选
用户审计/memory 面板 + 直接编辑 markdown/memories 命令 + codex debug clear-memories
并发控制无需(单会话写者)租约 + ownership_token + 水位线(多进程安全)

最小复现

# CC:确认 auto memory 目录与索引
ls ~/.claude/projects/*/memory/ 2>/dev/null
# 会话中说 "remember that I prefer pnpm",结束后检查 MEMORY.md 出现指针行
# 预期目录结构(docs/memory.md:360-366):
#   MEMORY.md            # 索引,每会话注入
#   debugging.md         # 主题文件,按需 Read
#   api-conventions.md

# CC:验证记忆按 canonical git root 归一(worktree 共享)
cd < repo worktree> && claude -p "what is your memory directory path?"
# 应与主 repo 的 memory 目录一致

# CC:关掉 auto memory 验证系统提示词 memory 段消失(行为验证)
CLAUDE_CODE_DISABLE_AUTO_MEMORY=1 claude -p "do you have a persistent memory directory? answer yes/no"

# Codex:开启实验特性并检查产物
# config.toml: [features] memories = true
ls ~/.codex/memories_1.sqlite ~/.codex/memories/ 2>/dev/null
sqlite3 ~/.codex/memories_1.sqlite ".tables"

Harness 接入建议(Yoda 实践)

  1. 记忆面板的数据源是确定的:CC 读 ~/.claude/projects/<slug>/memory/(slug 由 canonical git root 派生)即可全量展示/编辑——明文 markdown 是 Yoda 最友好的格式;Codex 读 ~/.codex/memories/memory_summary.md + MEMORY.md 展示,sqlite 仅用于显示流水线状态(多少 rollout 待提取/已蒸馏)。
  2. 校验规则:CC 的 MEMORY.md >200 行或 >25KB → 标红(超出部分根本不会进上下文,源码 memdir.ts:35-38 是硬截断);索引行 >150 字符 → 提示(违反两步协议)。Codex 的 memory_summary.md > 2500 token → 提示会被截断。
  3. 多 runtime 记忆隔离意识:两家记忆互不可见。Yoda 若做「跨 runtime 记忆同步」,可行方案是把 CC 的 MEMORY.md 摘要写进共享 AGENTS.md/CLAUDE.md(项目提示词通道两边都读),而不是去碰 Codex 的 sqlite——后者 schema 无兼容承诺(文件名都带版本号 _1)。
  4. SDK 场景:CC 自定义 system prompt 会丢失 memory 指令;设 CLAUDE_COWORK_MEMORY_PATH_OVERRIDE 可在自定义 prompt 下重新注入 memory-mechanics 段(QueryEngine.ts:310-319 注释明确该 env var 即 opt-in 信号)——Yoda 托管会话目录时正好用这个口子把记忆重定向到任务工作区。
  5. 踩坑:CC 记忆按 git root 归一,同一目录从 repo 内/外启动会落到不同记忆目录;Codex 记忆生成要求 rollout 闲置(默认建议 >12h),演示场景里「为什么没生成记忆」大概率是 idle 门槛 + feature 默认关。

失效条件

  • CC tengu_moth_copse(skipIndex:MEMORY.md 不再注入系统提示词,改由 findRelevantMemories 预取走 attachment,claudemd.ts:1142-1151)转正——召回模型从「静态索引」变「相关性检索」
  • CC tengu_passport_quail(extractMemories 后台提取)全量 / 下线
  • Codex memories feature 转 GA 或 Chronicle 落地(可能替换两阶段流水线)
  • Codex memories_1.sqlite 升版本号(schema 迁移)

参考资料

  • CC 源码:src_2026-03-31/memdir/{memdir,paths}.tsservices/extractMemories/extractMemories.tsservices/SessionMemory/sessionMemory.tsservices/compact/sessionMemoryCompact.tsutils/claudemd.ts:979-992
  • CC 文档:claude-code-docs/docs/memory.md(Auto memory 全节)
  • Codex 源码:codex/codex-rs/features/src/lib.rs:847-856state/src/{lib,runtime}.rsstate/src/model/memories.rsmemories/write/src/{phase1,phase2}.rsext/memories/src/{lib,prompts}.rsext/memories/templates/memories/read_path.mdconfig/src/types.rs:259-300

On this page