
从 AGENTS.md、会话上下文到两阶段长期记忆管道拆清 Codex 如何提取、整合、遗忘和取用记忆并给出一个可运行的 Python 最小实现。很多 AI 编程助手都有这个毛病这一轮像老同事下一轮像新实习生。你刚教完项目偏好、命令习惯、踩坑记录开个新会话又得重讲。问题不只是模型上下文不够长而是记忆没有变成一条可维护的管道。Claude Code 更依赖人手写CLAUDE.mdCodex 的新方向是把“什么值得记、什么时候忘、用到后如何反馈”拆成后台流程。官方codex-rs/memoriesREADME 已把这条链分成 read path 和 write path记忆能力仍属于实验特性通常需要在配置里显式开启。# ~/.codex/config.toml[features]memories true1. 先分层不要把所有东西塞进一个文件Codex 的记忆不是一份超大的备忘录而是三层东西各管各的AGENTS.md静态规则。比如代码风格、目录约定、团队偏好。当前会话上下文这一轮刚说过什么、工具刚返回什么。长期记忆从旧会话里提炼出来、未来还会改变行为的经验。这一步很重要。AGENTS.md已经会按工作目录重新注入如果再被长期记忆管道反复抽取只会制造重复和污染。from pathlib import Path def collect_agents(start: str) - str: cur Path(start).resolve() files [] for parent in [cur, *cur.parents]: doc parent / AGENTS.md if doc.exists(): files.append(doc.read_text(encodingutf-8)) return \n.join(reversed(files)) print(collect_agents(.))2. Phase 1先从旧会话里捞候选长期记忆最难的不是“写摘要”而是判断什么信息未来还值钱。Codex 的写入路径会在根会话启动时检查多层条件不是临时会话、记忆功能已开启、不是子代理、状态库可用然后后台处理旧 rollout。Phase 1 做的事很像粗筛读取旧会话过滤掉AGENTS.md、skill 注入这类系统上下文再提取 raw memory、rollout summary 和索引信息。它不应该把历史里的“指令文本”当成新系统指令执行只能当数据处理。def should_extract(session: dict) - bool: checks [ not session.get(ephemeral, False), session.get(memory_enabled, False), not session.get(is_sub_agent, False), session.get(state_db_ok, False), ] return all(checks) case {ephemeral: False, memory_enabled: True, is_sub_agent: False, state_db_ok: True} print(should_extract(case)) # True3. Phase 2整合比提取更关键Phase 1 只得到零散候选。真正决定记忆质量的是 Phase 2拿到一批候选后在受控工作区里合并、去重、排序并删除已经不值得保留的条目。这里最值得借鉴的是 Selection Diff新选中的进added继续保留的进retained上轮有但本轮不再保留的进removed。很多记忆系统只会追加最后一定变成垃圾堆。Codex 把“遗忘”做进流程这才像长期记忆。selection_diff { added: [用户偏好 Python 3.11 示例], retained: [回答默认用中文], removed: [一次性调试路径 /tmp/foo], } for name, items in selection_diff.items(): print(f{name}: {len(items)})4. 读取先看总览再按需翻细节memory_summary.md不应该变成“每次把所有历史都塞进 prompt”。更合理的做法是先给模型一个总览让它知道有什么可查如果问题真的需要旧信息再去更细的MEMORY.md、skills 或 rollout summaries 里找。Codex 的 read path 还有一个闭环回答如果引用了某段记忆系统可以从 citation 里回写使用次数和最近使用时间。常用记忆下次更容易被保留长期没用的记忆会被淘汰。from datetime import datetime, timezone usage {usage_count: 3, last_usage: None} usage[usage_count] 1 usage[last_usage] datetime.now(timezone.utc).isoformat() print(usage[usage_count])最小实现下面这份脚本不依赖外部 API。它用 SQLite 模拟历史会话Phase 1 抽取偏好Phase 2 去重整合最后把memory_summary.md注入下一轮问题。import sqlite3 from pathlib import Path ROOT Path(.) DB ROOT / memory_demo.db OUT ROOT / memory_summary.md conn sqlite3.connect(DB) cur conn.cursor() cur.execute(CREATE TABLE IF NOT EXISTS sessions(id INTEGER PRIMARY KEY, content TEXT)) cur.execute(CREATE TABLE IF NOT EXISTS raw_memories(id INTEGER PRIMARY KEY, memory TEXT UNIQUE)) cur.execute(DELETE FROM sessions) cur.execute(DELETE FROM raw_memories) sessions [ 用户说以后代码示例默认用 Python 3.11。, 用户说回答尽量简洁先给结论再展开。, 用户又提到代码示例请继续使用 Python 3.11。, ] cur.executemany(INSERT INTO sessions(content) VALUES(?), [(s,) for s in sessions]) conn.commit() rows list(cur.execute(SELECT content FROM sessions)) for (content,) in rows: if Python 3.11 in content: cur.execute( INSERT OR IGNORE INTO raw_memories(memory) VALUES(?), (代码示例默认用 Python 3.11,), ) if 先给结论 in content or 简洁 in content: cur.execute( INSERT OR IGNORE INTO raw_memories(memory) VALUES(?), (回答风格先结论后展开保持简洁,), ) conn.commit() memories [row[0] for row in cur.execute(SELECT memory FROM raw_memories ORDER BY id)] summary # Memory Summary\n\n \n.join(f- {m} for m in memories) OUT.write_text(summary, encodingutf-8) question 请给我一个 SQLite 示例。 prompt OUT.read_text(encodingutf-8) \n\n用户问题 question print(prompt) print(\n预期效果回答应先给简洁结论并使用 Python 3.11 示例。) conn.close()预期输出# Memory Summary - 代码示例默认用 Python 3.11 - 回答风格先结论后展开保持简洁 用户问题请给我一个 SQLite 示例。 预期效果回答应先给简洁结论并使用 Python 3.11 示例。边界说明第一当前会话压缩不等于长期记忆。压缩只是在一个 thread 里延长上下文寿命长期记忆则来自旧会话的后台抽取和整合。第二AGENTS.md是静态规则层不应被长期记忆反复学习。它已经会随工作目录进入上下文。第三网络搜索、外部 MCP、一次性调试输出这类临时信息容易污染长期记忆。工程上要给它们打标必要时直接禁止进入 Phase 2。第四源码仍在快速变化。本文重点讲设计模式具体模型名、默认扫描窗口、文件路径上限等参数发布时应以 OpenAI Codex 仓库当前实现为准。def should_persist(source: str, polluted: bool) - bool: if source in {agents_md, skill_injection}: return False if polluted: return False return True print(should_persist(chat, pollutedFalse)) # True结尾如果只看表面Codex 的记忆像是自动版CLAUDE.md。往里拆它真正有价值的是一条闭环先分层再提取再整合再按需读取最后用使用反馈决定下一轮保留什么。对做 agent 的团队来说别急着先写一个“永久记忆文件”。先问四个问题哪些信息不能从代码和文档实时推导哪些信息可能污染长期偏好哪些记忆应该被忘掉模型用到某段记忆后系统能不能记录这次使用这四个问题答清楚记忆系统才不会从助手变成负担。