跳转到内容

长任务不漂移:用会话分段、handoff 和 checkpoint 管住 Claude Code 上下文

难度阅读时间最后验证作者
进阶15 分钟2026-07-11LearnPrompt 编辑部

你已经能让 Claude Code 完成小任务了:改一两个文件、跑个测试、看一下 diff,都没问题。可一旦任务变成 “先调查 bug,再规划修复,再实现并验证”,事情就开始变味:

  • 前面明明说过“先别改文件”,后面还是顺手动了三个不相关文件。
  • 早期已经定义好的验收标准,被后半程的新猜测覆盖掉了。
  • 同一个会话聊到很长时,你开始分不清现在是在继续推进,还是在脏上下文里重复试错。

这不是简单的“提示词写得不够好”。更常见的根因是:你把探索、计划、实现、验收全挤在同一条会话里, 却没有决定下一步究竟要保留什么状态。长任务稳定性的核心,不是让 Claude 多记一点,而是把会话操作 当成工程边界来用。

读完这篇,你应该能稳定完成三件事:

  1. 分清 continuecompactclearresumefork、checkpoint 各自保留的是什么状态。
  2. 把一个真实 bug 任务拆成 Stage A 只读探索Stage B 干净实现,中间冻结一份 handoff。
  3. 拒绝一份“不含 acceptance command 的 handoff”,避免第二阶段回到“看起来修好了”的主观判断。

本文不会做三件事:

  • 不展开多 agent。
  • 不把一次 Showcase 写成模型能力排名。
  • 不宣称 resumecompact 或 checkpoint 自动保证质量。

官方 best-practices 把问题说得很直接:Claude 的 context window 会很快装满,而性能会随着装满而退化。 这个窗口里不只有你的消息,还包括读过的文件、跑过的命令输出、载入的规则和 memory。会话越长,系统越需要 清旧输出、做摘要压缩;可一旦压缩发生,早期那些真正重要的边界就更容易被弱化。

所以长任务的失稳,常常不是“模型不会写代码”,而是下面这三件事没有分开:

混在一起的环节常见后果真正缺的不是
探索和实现混在一条消息里还没搞清症状就直接改“更聪明一点”
验收标准只存在对话里改到后面忘了什么叫完成“多提醒几次”
会话变脏后仍继续追加历史同一 bug 越修越散“再继续聊一轮”

官方 how-claude-code-works 也给出同一个机制视角:Claude Code 的 agentic loop 本质上是 gather context → take action → verify results。如果你不给它明确分段,探索时读进来的噪声、 失败时刷出来的长日志、实现时的中途尝试,都会一起回流到后面的 verify 阶段。

真正的问题因此变成了:下一步我需要保留的是完整历史、压缩后的结论,还是只带走一份冻结 handoff?

先分清:会话、上下文、checkpoint 各管什么

标题为“先分清:会话、上下文、checkpoint 各管什么”的章节

这几个词经常被混着说,但它们不是同一层。

会话是你和 Claude 的一条持续工作流。官方 sessions 文档说明,Claude Code 会把会话持续保存在本地, 因此你可以:

  • claude --continue 接上当前目录最近一次会话;
  • claude --resume/resume 选回旧会话;
  • --fork-session/branch 复制历史到一个新的 session ID。

会话回答的是:“我要不要继续这条工作流?”

上下文是当前这次推理真正看见的材料。即使是同一个 session,上下文也会随着消息、文件、命令输出而变化, 并且在接近上限时被压缩。/compact 管的是这一层:它还留在同一 session,只是把历史替换成摘要,释放空间。

上下文回答的是:“Claude 现在脑子里还装着哪些细节?”

checkpoint 回答的是:“我是否要撤回刚才那段编辑或会话状态?”

官方 checkpointing 文档给出的边界很重要:

  • 可以 restore code、restore conversation,或两者一起恢复;
  • 也可以针对局部消息做 summarize;
  • 不能追踪 bash 命令造成的文件修改,所以它不是 git 替代品。

这意味着 checkpoint 适合撤销刚才的风险编辑,或者把一段旁支讨论压缩掉;它不等于“从此以后一定安全”。

如果把这些操作当成命令清单,很容易记混。更稳妥的问法是:我要保留什么状态?

你真正想保留的东西更合适的操作为什么不是别的
完整工作流和完整历史,只是被打断了continue / resume你要的是延续,不是清洗
同一工作流,但历史已经太长compact你仍想留在同一 session,只是压缩噪声
旧会话已经脏了,只想带走结论clear 后喂新的 handoff继续追加只会放大脏上下文
保留原调查,同时试另一条实现路线fork原 session 不动,新 session 带走历史分叉
撤回刚才的高风险编辑或错误尝试checkpoint / rewind这是回退,不是开新工作流

对话模式选择图:左侧比较 continue、compact、clear、fork 各自保留的状态,右侧展示 explore、冻结 handoff、fresh implement、verify 的两阶段流水线 图注:左边先问“我要保留什么状态”,再选 continue、compact、clear 或 fork;右边说明真正让长任务稳定的,不是一直续聊,而是把探索结果冻结成 handoff,再在新进程里只拿 repo + handoff 做实现与验证。

读图时先看左边,再看右边:

  1. 左边四个框不是命令优先级,而是四种任务关系。
  2. 绿色 continue / resume 和蓝色 compact 都留在原工作流里,区别只在于要不要压缩历史。
  3. 橙色 clear + 新 prompt 说明的是“丢掉脏上下文,但不丢掉结论”;因此必须先有 handoff。
  4. 粉色 fork 不是清空,而是复制历史另开分支,适合比较两种实现路线。
  5. 右边的流程线故意把 Stage A 与 Stage B 拆开,强调第二阶段输入应该尽量少,只保留 repo 与冻结 handoff。

这张图要传达的结论只有一个:不是所有“继续”都该继续原上下文,也不是所有“重开”都该从零开始解释。

把一次真实 bug 修复拆成 Explore → Handoff → Fresh Implement

标题为“把一次真实 bug 修复拆成 Explore → Handoff → Fresh Implement”的章节

官方推荐“先探索,再计划,再编码”,但真正在长任务里起作用的,是你有没有把探索结论固定下来。 本文 Showcase 用一个本地、零依赖的 slugify fixture 演示这件事。

Stage A 的目标不是修复,而是冻结结论。它做的只有三类事:

  1. 读仓库事实:package.jsonsrc/slugify.jstests/run-tests.mjs
  2. 运行基线测试,确认症状。
  3. 写出 handoff,明确第二阶段可以改什么、怎么验收、还有哪些风险。

Stage A 的实际基线结果被归档在 research/articles/advanced-conversation-patterns/showcase/results/stage-a-summary.txt。核心症状只有一句:

FAIL collapses punctuation gaps into one hyphen expected=rock-roll actual=rock--roll

这句话的价值,不在于它“说明模型看懂了”,而在于它把问题压成了一个可传递的工程事实:验收前,当前仓库确实失败。

本文没有把 handoff 写成大而空的摘要,而是强制它回答五个问题:

字段为什么必须有
symptom第二阶段要修什么
evidence为什么相信这个症状是真的
allowed_files第二阶段不该随手扩大范围
acceptance.commands怎样把“修好了”变成机械信号
risks哪些边界仍需要人盯着

正例 handoff 在 showcase/handoff/handoff-good.json。它把验收命令冻结为 node tests/run-tests.mjs, 并且把允许修改范围锁到 src/slugify.js。这两条信息一起,才足以让第二阶段在干净上下文里直接执行。

Stage B 用的是全新进程,并且把 fixture 复制到系统临时目录运行。它只吃两样输入:

  • 仓库副本;
  • Stage A 冻结的 handoff。

这一步很关键。因为你要证明的不是“我还能回想起前面聊过什么”,而是“前面的探索结论已经足够让另一个干净进程完成最小修复”。

正例结果归档在 showcase/results/positive-run.txt。它先重放基线失败,再只改 src/slugify.js, 最后重跑验收命令通过。这里没有任何“模型认为已经完成”的措辞,只有三个工程事实:

  1. 基线退出码是失败;
  2. 只改了允许文件;
  3. 验收命令退出码变成 0。

为什么缺 acceptance command 的 handoff 必须被拒绝

标题为“为什么缺 acceptance command 的 handoff 必须被拒绝”的章节

这是本文最想强调的一条硬边界。

很多团队会写探索总结,但总结里只有“我觉得问题大概在这里”。这种 handoff 看起来比没有强,可它并不能真正 支撑第二阶段,因为它没有定义完成条件。没有 acceptance command,就没有下面这条闭环:

改动 → 运行检查 → 读取 pass/fail → 决定是否停止

负例 handoff 存在 showcase/handoff/handoff-missing-acceptance.json。它写了症状、证据、允许文件, 却故意漏掉 acceptance.commands。Stage B 的 deterministic gate 会直接拒绝,而不是猜一个命令继续往下做。

归档结果 showcase/results/negative-run.txt 的核心行是:

REJECT missing acceptance.commands[0]

这不是吹毛求疵,而是避免第二阶段退化成“看起来像修好了”。官方 best-practices 强调的也是同一个逻辑: Claude 需要一个可运行的验证门。你可以把命令写得很小,例如一条单测;但不能没有。

这套做法和 continue、compact、clear、resume、fork 怎么配合

标题为“这套做法和 continue、compact、clear、resume、fork 怎么配合”的章节

真正工作时,你通常不会每次都从零设计。更实际的搭配方式如下:

场景 1:任务没变,只是暂时离开终端

标题为“场景 1:任务没变,只是暂时离开终端”的章节

continueresume。这时你保留的是整条 workstream,没必要人为切断历史。

先考虑 compact。你仍想保留工作流,只是需要把长日志和旁支讨论压缩掉。注意它只能减噪,不能证明摘要里的结论没错。

场景 3:前面聊脏了,但探索结论已经够清楚

标题为“场景 3:前面聊脏了,但探索结论已经够清楚”的章节

这是本文最推荐使用 handoff 的场景。先冻结 handoff,再 clear,然后在新的 prompt 里只喂:

  • 仓库;
  • handoff;
  • 允许文件范围;
  • acceptance command。

这时你不是“从零开始”,而是“丢掉噪声,只保留结论”。

场景 4:你想保留原调查,再试另一种实现路线

标题为“场景 4:你想保留原调查,再试另一种实现路线”的章节

fork。例如原会话已经摸清 bug,但你想比较“修 regex”与“拆成多个 helper”的两种方案。fork 的好处是:

  • 原调查历史还在;
  • 新路线有新 session ID;
  • 失败后可以回看原路线,而不是把两种实现混成一条历史。

这时用 checkpoint / rewind,而不是 clearclear 解决的是上下文污染,不负责撤销磁盘上的当前编辑; checkpoint 则恰好负责回退代码、会话或两者。官方同时提醒:bash 改动不在它的追踪范围内,所以高风险操作仍要配合 git。

这套方法不是银弹,至少有四条边界必须讲清楚。

如果 handoff 变成一份长报告,第二阶段同样会被噪声淹没。真正值得冻结的是执行契约,而不是完整聊天记录。

压缩了上下文,不代表错误结论自动消失。如果前面的调查本身就错了,摘要只会把错得更短。

resume 是续写,fork 是复制。原历史脏,两者都会继承脏历史。需要干净实现时,还是要靠 handoff + 新上下文。

本文负例只证明“没有 acceptance command 应被拒绝”;它不证明“任何 acceptance command 都足够”。真正上线前, 你仍要审查命令本身是否覆盖了目标行为。

下面这份模板比“写一段总结”更有执行价值,因为它把第二阶段真正需要的东西单列出来了:

{
"task": "用最小改动修复 bug",
"symptom": "当前行为错在哪里",
"evidence": [
"复现命令",
"最关键的一行失败输出"
],
"allowed_files": [
"只允许改动的文件"
],
"acceptance": {
"commands": [
"一条最快验收命令"
],
"expected_exit_code": 0
},
"risks": [
"不要顺手扩大范围",
"仍未覆盖的边界"
]
}

真正给 Claude Code 用时,你可以把它放进新 prompt 里,再加一句明确的话:

只根据仓库和这份 handoff 实现。不要重新探索无关文件;只改 allowed_files;
完成后运行 acceptance.commands,并报告真实退出码与剩余风险。

这比“继续刚才那个思路修一下”稳得多,因为它把第二阶段需要继承的内容收缩到了最小。

拿你自己仓库里一个最近修过的 bug,做一遍下面这套演练:

  1. 用计划模式或只读提示,先做 Stage A,只读探索并写出 handoff。
  2. 强迫自己把 allowed_filesacceptance.commands 写完整。
  3. 在一个干净会话里重新开始,只喂 handoff,不复制整段历史。
  4. 如果第二阶段还需要重新问一堆“问题到底是什么”,说明 handoff 不合格。

完成标准不是“修好了”,而是同时满足三点:

  • 第二阶段没有扩大文件范围;
  • 验收命令真实运行并返回可解释的退出码;
  • 你能清楚说出这次为什么该 clear、为什么不该 continue,或者为什么应该 fork

官方文档支撑当前产品行为与会话语义;本文 Showcase 只证明本地 deterministic 流程隔离。橙皮书作为二手主题地图保留署名与链接,其仓库 README 声明许可为 CC BY-NC-SA 4.0。