我最近遇到一个问题:Codex 里的 session 越来越长,执行时也显得越来越重。但是任务本身还没有结束,旧 session 里又积累了不少决策和进度,直接新开一个似乎会“失忆”。
我的任务主要靠两样东西维持:一个是 /goal,另一个是本地的 项目目标与进度基线.md。问题就变成了:怎样换到一个更干净的 session,同时又不丢掉真正有用的信息?
先说结论:
不要迁移完整聊天历史,而要迁移已经确认的事实、决策、进度、验证结果和下一步。
具体做法是:让旧 session 生成一份可复查的交接文件,更新项目进度基线,然后新建一个完全独立的 chat,由它从仓库文件恢复现场。这样迁移的是项目状态,而不是旧模型的全部思考过程。
#先分清 session、chat 和几种“继续”
为了方便,本文仍把一段长期对话称为 session。当前官方文档在桌面应用里主要使用 chat,CLI 的命令说明里仍能看到 session。它们在本文讨论的工作流里都指向同一个问题:Codex 下一轮工作要从哪里恢复上下文。
resume、fork、/compact 和新 chat 看起来都能解决“对话太长”,但它们保留的东西并不一样。
| 操作 | 对原聊天历史的处理 | 适合什么 | 不适合什么 |
|---|---|---|---|
resume | 重新加载原 session,保留原有历史 | 中断后继续同一条工作链 | 想摆脱旧 transcript 的负担 |
fork | 把当前或已有 chat 复制到一个新 chat | 保留现场,尝试另一种方案 | 只想迁移结论,不想复制历史 |
/compact | 用摘要替换较早的上下文,释放窗口 | 长任务中途临时整理上下文 | 彻底摆脱旧 session |
| 新 chat | 不执行恢复或复制,从新的聊天上下文开始 | 通过仓库文件重新接手任务 | 依赖旧聊天里尚未落盘的信息 |
/goal | 把持久目标附着在当前 chat 上 | 有明确终点和验证循环的长任务 | 代替跨 chat 的项目状态文件 |
官方的 Developer commands 对这些边界写得很清楚:resume 会恢复保存的聊天并保留原历史,fork 会克隆当前聊天,/compact 则把早期内容替换成摘要。桌面应用里的 /fork、/compact 和 /goal 可以在 Slash commands 中确认。
所以,如果目标只是“刚才断了,接着干”,用 resume 没问题;如果想保留现有上下文,探索另一条实现路径,用 fork;如果上下文只是暂时吃紧,可以先 /compact。但如果旧聊天已经长到想真正重新开始,新 chat 才是最干净的入口。
在桌面应用中,可以点击 New chat,也可以使用 Cmd/Ctrl + N 或 Cmd/Ctrl + Shift + O。当前快捷键以官方的 桌面端命令文档 为准。
#新 chat 为什么还能接上旧任务
因为一个长期项目真正需要继承的,并不是聊天的每一句话。
旧 session 里通常混着四类内容:
- 已经确认的目标、约束和设计决策;
- 当前代码、工作区和测试结果;
- 搜索过程、失败尝试和临时推理;
- 已经被后续结论取代的旧计划。
新 session 真正需要的是前两类,以及第三类中仍然会影响下一步的部分。至于几十次搜索命中、重复解释和已经放弃的方案,继续携带反而会让接手者分不清什么才是当前事实。
这和工程里的交接班很像。下一班不需要重看整晚的监控录像,但必须知道事故是否结束、排除了哪些原因、哪些操作已经验证,以及下一步应该观察什么。
因此,仓库需要承担一部分“外部记忆”。
#把长期状态分成四层
我会把长期任务的状态拆成下面四层:
| 文件 | 保存什么 | 更新频率 |
|---|---|---|
AGENTS.md | 仓库级稳定规则、命令、安全边界和验收要求 | 规则真正变化时 |
项目目标与进度基线.md | 当前目标、checkpoint、已完成事项、待办和风险 | 每个重要 checkpoint |
docs/agent-handoffs/LATEST.md | 上一个 session 留给下一个 session 的压缩交接 | 每次迁移 session 前 |
PLANS.md | 阶段计划、里程碑和较长的实施拆解 | 计划变化时 |
这里最容易犯的错误,是把所有内容都塞进 AGENTS.md。它适合保存稳定规则,不适合记录“今天改到哪了”。官方的 AGENTS.md 文档 也把它定位为持久的项目指令,并支持从仓库根目录到子目录逐层覆盖。
换句话说:
AGENTS.md回答“在这个仓库里应该怎样工作”;- 进度基线回答“这个项目现在走到哪里”;
- handoff 回答“上一个 session 刚做完什么、接下来先看什么”;
PLANS.md回答“这一阶段准备怎样完成”。
职责分开之后,新 session 不需要猜哪段信息是否过期。
#第一步:让旧 session 只做交接
准备迁移时,不要再让旧 session 顺手多修一个问题。它最后的任务应该是核对现场和生成交接。
下面这段提示词可以直接使用:
请只做 session 交接,不要继续实现业务功能。
目标:把当前 session 中仍然有效的上下文压缩并落到仓库文件,
让一个不读取旧聊天历史的新 Codex chat 可以继续工作。
请先读取并核对:
- AGENTS.md,以及当前目录下可能生效的嵌套 AGENTS.md
- 项目目标与进度基线.md
- PLANS.md(如果存在)
- 本轮实际修改过的相关文件
- git status --short
- git branch --show-current
然后:
1. 创建或更新 docs/agent-handoffs/LATEST.md;
2. 更新 项目目标与进度基线.md 中的当前目标和 checkpoint;
3. 不要修改业务代码。
交接文件必须包含:
- 本轮 session 的目标;
- 已确认的关键设计决策及理由;
- 已修改文件和当前工作区状态;
- 已完成事项与未完成事项;
- 已运行的验证命令及结果;
- 尚未运行的验证;
- 阻塞、风险和证据缺口;
- 已经否定、不要重复尝试的方案;
- 下一位 agent 启动后的第一步;
- 需要优先阅读的文件。
完成后只汇报更新了哪些状态文件,不要继续开发。
这份提示词有两个关键点。
第一,交接前先读 Git 状态和实际文件,而不是只凭聊天记忆写总结。聊天里可能还说着“准备修改”,但工作区中可能已经改完,也可能根本没有落盘。
第二,要求记录“不要重复做的事”。一个失败方案如果只存在于旧聊天里,新 session 很可能会再走一遍。
#第二步:检查交接是否能独立成立
交接文件不是“旧 session 的总结作文”,而是新 session 的启动输入。至少要能回答下面几个问题:
- 当前唯一目标是什么?
- 到什么状态才算完成?
- 哪些结论已经被代码、命令或文档验证?
- 工作区有没有未提交修改?
- 下一步应该读哪个文件、执行哪个命令?
- 哪些操作有风险,必须先停下来确认?
如果 handoff 里写的是“继续完成之前讨论的方案”,这份交接就失败了。新 session 没有“之前”。
更好的写法是:
## 当前 checkpoint
认证中间件已经切换到新的 token 校验入口,但刷新流程尚未迁移。
## 已验证
- `pnpm test auth` 通过;
- 登录和过期 token 用例通过;
- 尚未运行完整构建。
## 下一步
先阅读 `src/auth/refresh.ts` 和对应测试,确认刷新 token 是否仍调用旧入口。
完成修改后运行 `pnpm test auth` 与 `pnpm build`。
即使完全看不到旧对话,这段信息也能让下一位 agent 开始工作。
#第三步:用新 chat 恢复现场
确认交接已经落盘后,再创建新 chat。不要 resume,也不要从旧聊天 fork,因为这两种操作的目的本来就是保留原现场。
新 chat 的第一条消息可以这样写:
请接手当前仓库任务,但不要依赖任何旧聊天历史。
开始工作前必须读取:
1. AGENTS.md,以及当前任务目录下可能生效的嵌套 AGENTS.md;
2. 项目目标与进度基线.md;
3. docs/agent-handoffs/LATEST.md;
4. PLANS.md(如果存在)。
然后执行只读检查:
- git status --short
- git branch --show-current
- 阅读 handoff 指向的相关文件
先总结你恢复出的:
- 当前目标和完成条件;
- 已完成事项;
- 当前 checkpoint;
- 未完成事项与下一步;
- 工作区状态;
- 风险、阻塞和未运行的验证。
如果文件之间冲突,不要自行拼凑结论。指出冲突、文件日期和 Git 证据,
再给出你建议采用的基线。
多步任务先进入 plan mode。确认计划前不要修改业务文件。
这里故意要求先做只读检查。新 session 的第一反应不应该是“根据摘要继续写代码”,而是确认摘要和仓库当前状态仍然一致。
#第四步:在新 chat 重新建立 goal
/goal 很适合有明确终点、验证循环和停止条件的长期任务。官方的 Goal mode 指南 建议在目标里说明要先读取的文件、怎样验证进度,以及什么时候停止。
但 /goal 是当前 chat 的持久目标,不是整个仓库的项目数据库。换了新 chat,先恢复文件状态,再重新设置目标:
/goal 基于 `项目目标与进度基线.md` 和
`docs/agent-handoffs/LATEST.md` 继续推进当前任务。
每个 checkpoint 后:
1. 更新进度基线;
2. 运行文件中约定的验证命令;
3. 记录通过、失败和未运行的检查;
4. 遇到目标冲突、破坏性操作或缺少关键输入时暂停。
当进度基线中的完成条件全部满足,并且要求的验证通过后停止。
如果目标本身还没有收敛,可以先用 /plan 把范围和验收条件谈清楚,再设置 /goal。当前桌面应用的 Slash commands 文档也把这条路径作为推荐用法。
#把迁移变成固定循环
一次迁移不难,难的是半年后仍然知道哪个文件可信。可以把日常工作固定成下面的循环:
读取规则与进度基线
↓
执行当前 checkpoint
↓
运行验证并记录结果
↓
更新进度基线
↓
需要换 chat 时生成 handoff
↓
新 chat 重新读取并核对仓库
每个 checkpoint 不必写成长篇日报。有效的进度记录只需要说明:做了什么、证据是什么、还剩什么、下一步是什么。
LATEST.md 也不必无限增长。它表示最近一次交接,历史交接可以按日期归档,例如:
docs/
agent-handoffs/
LATEST.md
2026-07-28-codex-session-handoff.md
如果项目没有长期追溯需求,只保留 LATEST.md 也可以。关键不是文件数量,而是新 session 能找到唯一、明确且足够新的入口。
#什么时候不需要这样做
这套方法不是每次对话都要执行。
如果只是关掉终端后继续昨天的同一个任务,resume 更直接;如果要保留当前上下文,验证一个替代设计,fork 更合适;如果聊天仍然可靠,只是上下文窗口暂时紧张,先 /compact 就够了。
只有当你明确想摆脱旧 transcript,同时项目又不能从头开始时,才值得做“交接落盘 → 新 chat 恢复”。
还要注意:本文没有证明新 chat 一定能带来多少 CPU、内存或响应时间改善。它能确定减少的是继承的聊天历史,并把长期状态变成可检查的仓库文件。至于实际性能,还会受到模型、工具调用、仓库规模和当前应用版本影响。
#总结
长 session 的问题,不只是上下文太多,更重要的是项目状态和聊天历史混在了一起。
resume 负责继续原 session,fork 负责复制现场探索分支,/compact 负责在原聊天里压缩上下文。它们都有用,但都不是“只带结论重新开始”。
真正可持续的做法,是把稳定规则放进 AGENTS.md,把当前目标和 checkpoint 放进进度基线,把最近一次交接放进 LATEST.md。然后开一个新 chat,让它先读文件、核对 Git,再重新建立 /goal。
聊天可以结束,项目状态不能只活在聊天里。