首页
学习
活动
专区
圈层
工具
发布

Claude Code 与 Codex Memory 机制详解

Claude Code 与 Codex Memory 机制详解

最近在疯狂学习 Agent 开发,把一些学习笔记整理成文章与大家分享。

这次聊聊 Claude Code 与 Codex 的 Memory。主要看几个问题:记忆如何组织以及记忆如何读写

Claude Code 基于年初泄露源码,以及本文采用的 2.1.263 Memory 系统提示词。后台提取与 Dream 的实现只能作为旧版本参考。Codex 基于rust-v0.153.4源码。

两套设计都有一个很直观的思路:Markdown 组织记忆,渐进式披露这样每次对话都能找到过去的经验,也不用把全部历史重新塞进上下文,实现跨 session 记忆。

文章最后,我会介绍结合这两套思路,为DeepSeek Harness开发的dsh-memory插件。它用 Markdown 作为记忆载体,实现了简单的渐进式披露,并在LoCoMo上完成了 eval。欢迎大家使用并 Star:https://github.com/hr98w/dsh-memory。

Codex

感谢 Codex 是开源的,个人认为比 Claude Code 更有学习价值,我就把它放到了前面。

#系统提示词

Codex 的系统提示词中和 Memory 直接相关的是两部分:

记忆读取规则什么时候要查记忆,先查哪里,什么时候需要验证旧信息,以及用了记忆以后如何标注来源

•memory_summary.md则提供用户偏好、常用经验和记忆导航。源码会把注入内容裁到约 2,500 Token,完整的MEMORY.md和历史摘要不会一起自动加载。(后面会详细讲讲记忆的组织与渐进式披露)

#记忆文件与渐进式披露

Codex 的记忆根目录是$CODEX_HOME/memories/,一般就是~/.codex/memories/:

memories/

├── memory_summary.md

├── MEMORY.md

├── raw_memories.md

├── rollout_summaries/

│   └── <会话摘要>.md

├── skills/

│   └── <流程名称>/SKILL.md

└── extensions/ad_hoc/notes/

  └── <临时修改笔记>.md

这些名字看起来有点接近,可以按职责理解:

整体是遵循渐进式披露:一开始系统提示词里只有memory_summary.md,随后是按需进行加载,获取更详细的记忆文件。

#记忆的两阶段写入

先看整条路径:

新 turn 触发后台尝试

筛选符合条件的历史 thread

Phase 1:提取,写入 SQLite

Phase 2:同步中间文件,计算变化

整合 Agent 更新长期记忆

新 turn 只是触发一次尝试,还要检查开关、会话类型、数据库和额度等条件。后台异步执行,主对话不用等它完成。

Phase 1:提取会话记忆

默认会考虑最近 10 天内更新、且至少空闲 6 小时的符合条件的历史会话,排除触发后台任务的当前 thread。

对选中的 thread,程序读取磁盘 rollout,也就是持久化的会话记录/session,过滤部分注入内容和非目标记录,并在模型请求前后做敏感信息脱敏。

随后发起一次无工具的结构化提取请求,返回三个字段:

{

"raw_memory": "值得复用的知识、偏好和失败教训",

"rollout_summary": "这个会话做了什么、结果如何、有哪些证据",

"rollout_slug": "简短的会话标识"

}

成功后,结果先进入 SQLite 的一阶段结果中stage1_outputs。

Phase 2:整理多段会话记忆

这个阶段首先会把 1 阶段生成的记忆从 SQLite 读取出来,保存到文件中。

接着,Codex 用记忆目录里的 Git 记录计算记忆变化:哪些材料新增了,哪些更新了,哪些来源已经消失。需要整合时,把 diff 和文件位置交给内部 Agent。

这个 Agent 再按需读取材料,更新MEMORY.md、memory_summary.md,必要时把重复出现的操作经验整理成 skill。

第二阶段不会重新阅读全文原始会话,它只会读取 1 阶段的产物,以及访问原本的记忆系统,进行整理沉淀,处理多个 session 之间的关联记忆。

第二阶段成功后默认冷却 6 小时。这表示一段时间内不重复整合,不表示有一个每 6 小时准时运行的定时器。

用户主动修改记忆

主 Agent 还有一条明确的修改入口:临时笔记,也就是 ad-hoc notes。

例如用户说:“更新一下,以后这个项目使用 pnpm。”主 Agent 会把修改要求追加到:

extensions/ad_hoc/notes/<时间戳>-<简短名称>.md

二阶段发现这些文件变化,再把修改要求整合进长期记忆。

#记忆的读取

写入之后,主 Agent 依旧使用很普通的文件检索方式。

假设下一次进入项目,用户问:“上次数据库迁移是怎么处理的?”读取路径大致是:

已注入的 memory_summary.md

搜索 MEMORY.md 里的项目名与迁移关键词

打开相关 rollout summary 或 skill

必要时沿 rollout_path 核查原始会话

这里读原始会话的是主任务 Agent,直接进行Agentic Search

系统提示词还给了一个轻量检索预算:先做大约 4—6 步快速查找,通常只打开 1—2 个相关文件,没有命中就停止。它不要求每次先扫完整个记忆目录。

如果问题本身就很完整,比如简单翻译、改一句话、一行命令,可以直接跳过记忆。

#记忆的引用与遗忘

Codex 还有一个比较有意思的设计:模型引用用了记忆以后,会提供来源,LLM 会生成如下的信息,但是用户并不可见,codex 的程序读到后,会修改 SQLite 中的 usage_count 字段。

MEMORY.md:10-15|note=[migration workflow]

00000000-0000-4000-8000-000000000001

citation_entries记录文件、行号和用途,方便追溯;rollout_ids记录这些知识来自哪些 thread,用于更新使用次数。

这些 count 次数会影响后续第二阶段的材料选择:

• 先看新鲜度。默认保留最近 30 天内使用过的来源;从未被引用的,则按来源会话更新时间判断。

• 再排优先级。引用次数多的靠前,相同时再比较最近使用时间等信息。

• 取配置数量内的来源结果,供下一轮整合使用。

usage_count决定有限名额里的顺序,last_usage影响来源是否仍在保留期内。一个历史上很常用的来源,如果长期不再被引用,也可能过期。

把整个流程连起来,就能看出设计的闭环:

会话经验 提取 整合 按需读取 引用来源

                   └──── 使用记录影响保留 ────┘

Claude Code

Claude Code 的整理主要基于旧源码。auto-memory 在较新提示词里依旧存在,但自动提取、相关性召回和 Dream 是否仍按旧代码运行,我个人并不确定,大家可以仅作为参考。

先把几个名字分开:CLAUDE.md和 rules 用来放明确的指令与约定;auto-memory 用来保存对话中值得长期保留的信息。

#CLAUDE.md

CLAUDE.md也被 Claude 描述为记忆系统的一部分。它通常由人维护,或者由人指示 Agent 编辑,不走后面要讲的 auto-memory 自动提取流程。

用户、项目、子目录都可以有自己的文件,组织也可以统一下发:

启动时,会沿目录树读取当前目录及父级的相关文件;子目录里的文件,则在读取该目录下的文件时按需加载。

这些内容会一起进入上下文,更具体的内容通常排在后面。它没有配置文件那样严格的逐字段覆盖规则,所以最好不要让不同层级的要求互相矛盾。

rules 和CLAUDE.md类似,只是把规则按主题拆成了独立文件,方便管理。比如测试规范放testing.md,接口规范放api.md。

如果带有paths,规则只在读取匹配文件时加载。例如.claude/rules/api.md:

---

paths:

- "src/api/**/*.ts"

---

接口需要校验输入,并使用统一的错误响应格式。

没有paths的规则会在启动时加载,和顶层.claude/CLAUDE.md一样作为常驻的项目指令。这个拆分既方便维护,也能让只适用于某个模块的要求按需进入上下文。

#auto-memory

这部分是我理解中的狭义 agent memory。auto-memory 的文件组织很简单:

~/.claude/projects/<项目标识>/memory/

├── MEMORY.md

├── user-background.md

├── feedback-real-db.md

└── project-release.md

MEMORY.md是索引,详细内容放在单独的 Markdown 文件里

记忆分为四种类型:

按本文采用的新提示词,一个文件保存一个事实。下面是一条假设的反馈记忆:

---

name: integration-tests-use-real-db

description: 数据库集成测试使用真实数据库

metadata:

type: feedback

---

数据库集成测试应连接真实数据库。

Why:曾出现 mock 测试通过,但真实数据库迁移失败的情况。

How to apply:涉及数据库行为时,使用真实实例验证。

除了记录“做什么”,还留下“为什么”和“什么时候适用”。下一次遇到类似任务,Agent 才不至于把一条局部经验无限推广。

MEMORY.md索引里只保留一个入口:

- [数据库集成测试](integration-tests-use-real-db.md) — 使用真实数据库验证迁移与查询行为

记忆写入

首先,主 Agent 自己就能写。

用户说“记住,这个项目的集成测试要用真实数据库”,Agent 可以通过 Write/Edit 写入事实文件,再更新MEMORY.md。提示词会要求它先检查已有记忆,尽量更新已有条目,避免同一件事重复保存。

其次,泄露的源码还有回合结束后的后台提取:

主 Agent 完成本轮回答

stopHooks 检查提取条件

extractMemories 检查尚未处理的消息范围

fork 一个 Agent 提取长期信息

写入事实文件并维护索引

它受编译开关、服务端配置和会话条件控制,不是每个 turn 都无条件运行,个人并不确定这个功能还是否存在

记忆读取

默认路径就是渐进式披露:

加载 MEMORY.md 索引

主 Agent 判断哪些记忆相关

Read 对应 Markdown

文件内容作为工具结果进入上下文

索引让 Agent 知道“这里有什么”,详细文件回答“具体是什么”。旧源码对索引注入有长度限制,其中一个限制是前 200 行;超过限制的内容不会全部进入上下文,原文件本身不会被裁掉。

旧源码还有一条默认关闭的试验功能,开关叫tengu_moth_copse。它会扫描记忆文件的文件名、description 等元信息,再把候选清单和用户问题交给模型,让模型选出最多 5 个相关文件。

用户问题 + 记忆文件元信息

模型选择相关文件

读取正文

包装进 <system-reminder>

提供给主 Agent

预取是异步的,主 Agent 不会停下来等它。每次工具执行后,程序检查结果是否准备好,准备好就注入,否则等同一次请求的下一次循环再检查。请求结束时会取消尚未完成的预取,因此不能直接说它一定会留到下一轮用户对话。

召回内容还带有根据文件修改时间生成的新鲜度提示,提醒模型这条记忆可能已经过时。新提示词也明确要求:记忆提到文件、函数或配置时,使用前要核查它们是否仍然存在。

#Dream

记忆不断写入以后,还会出现重复、矛盾和过期。Dream 处理的就是这些问题。

旧源码里,它是一次由模型完成的记忆整理任务,大致分成四步:

1. 浏览索引、已有记忆和近期线索,了解目前记了什么。

2. 收集需要补充或修正的信息,必要时搜索历史会话。

3. 合并重复主题,修正矛盾,把相对日期变成明确日期。

4. 精简索引,把详细内容移到主题文件,清理失效入口。

它最后修改的仍然是这套 Markdown 文件。

普通 auto-dream 会在回合结束后检查是否需要运行。旧源码的默认条件包括:距离上次整理至少 24 小时,并且至少有 5 个其他会话文件在此后被更新。这些阈值可以配置,也要满足功能开关等条件。

这部分蛮像刚才讲过的 Codex 记忆整理机制

站在巨人的肩膀上:dsh-memory

看完这两套机制,我想先把其中简单、可解释的部分用起来,于是为 DeepSeek Harness 做了dsh-memory

我设计了两种车次:Global 保存跨项目偏好,Workspace 保存当前项目的事实与反馈。文件都在本地:

$DSH_HOME/memory/

├── GLOBAL.md

└── workspaces/<工作区标识>/

  ├── MEMORY.md

  └── <具体记忆>.md

每次请求先提供GLOBAL.md和当前 Workspace 的MEMORY.md。Agent 看到相关入口,再使用已有的文件工具打开正文。其他 Workspace 的记忆不会一起提供给它。

在线写入通过专门的工具memory_update完成。模型提出记什么、怎么改,采用专门的记忆工具来保证格式的正确。

这个分工是我比较看重的:模型负责理解信息,程序/工具负责把文件和索引写对。

历史 Session 也可以整理。目前由用户手动触发,选择已结束且稳定的 Session,交给独立整理 Agent 检查并提出修改,它借鉴了 Codex 的会话整理思路(也可以说是类似 claude code 的 dream),但没有完整复刻 Codex 的两阶段后台调度。

实现之后,我用 LoCoMo-10 进行了 Eval,结果如下,得分比我想象中要好的多

而且不得不说,跑测评真是实打实的费钱

如果你也在用 DeepSeek Harness,欢迎试用 https://github.com/hr98w/dsh-memory ,提 Issue,也欢迎 Star。

写在最后

Claude Code 与 Codex 都采用了 markdown 组织记忆,主 Agent 写入 + 子 Agent 辅助整理的方式,并且通过 Agentic Search 的方式进行渐进式披露读取。

mem0, openviking 这样的记忆系统固然跑分很高,但是很重,有额外的开销,简单的 markdown + agentic search 就可以实现很高的分数,并且随着基模增强,这样的 memory 机制是会自动进步的。

在记忆系统形成范式前,markdown + agentic search 可能就是目前的记忆范式。

  • 发表于:
  • 原文链接https://page.om.qq.com/page/OYug5PnL1xWh-oUd05JImtqQ0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

相关快讯

领券