ChatGPT、Codex实战:长任务为什么容易跑偏?从任务拆分到Checkpoint的6层控制

ChatGPT、Codex实战:长任务为什么容易跑偏?从任务拆分到Checkpoint的6层控制

Codex越来越强以后,一个很明显的变化是:

我们开始敢把更大的任务交给Agent。

以前可能只是:

帮我写一个函数。

后来变成:

帮我修复这个Bug。

再往后是:

把认证模块重构一下,补齐测试,然后确保现有接口行为不变。

甚至:

把整个项目从旧技术栈迁移到新技术栈,持续工作直到所有测试通过。

问题也随之发生变化。

短任务失败时,我们通常还能很快找到原因。

但长任务最麻烦的情况不是直接失败。

而是:

它一直在工作,但正在慢慢偏离最初目标。

你可能看到Codex:

第一小时还在解决核心问题;

第二小时开始处理边缘问题;

后面又顺手重构几个模块;

测试失败以后继续扩大修改范围;

最后产生了大量代码。

看起来Agent非常努力。

但回头检查时却发现:

最初真正需要解决的问题,反而没有形成一个清晰、可验证的闭环。

这就是Long-Horizon Agent真正困难的地方。

OpenAI目前对Codex长任务的描述已经非常明确:关键变化不只是模型“变聪明”,而是Agent能够在更长时间内维持执行循环,通过计划、修改代码、运行测试、观察结果、修复失败,再继续下一轮。Codex的长任务能力依赖的也不是一个巨大的Prompt,而是整个Agent Loop以及仓库、文件、日志、Diff、Worktree等外部状态。

所以:

长任务不是“一个更大的Prompt”。

它更接近:

一个持续运行的小型工程系统。

如果还是按照“一句话交代需求,然后等Agent自己做完”的方式使用,任务越长,失控概率通常越高。


一、首先要理解:长任务真正困难的是“状态”,不是代码量

假设有两个任务。

Task A

修改Login.tsx中的按钮文案。

即使Codex产生一点偏差,问题也很容易发现。

因为整个任务可能只有:

读取文件 ↓ 修改一行 ↓ 检查Diff ↓ 完成

状态非常少。

但换成:

Task B

重构认证系统,保持现有API兼容,迁移Token逻辑,补齐测试,并确保登录、注册、刷新Token都能正常运行。

现在状态开始增加:

认证架构 当前API行为 兼容要求 Token逻辑 登录测试 注册测试 刷新测试 历史Bug 新实现 回滚策略

而且每一次修改都会产生新的状态。

例如:

阶段1:修改Token生成 ↓ 阶段2:测试登录 ↓ 发现Refresh Token异常 ↓ 修改Cookie ↓ 注册测试开始失败 ↓ 继续修改公共认证逻辑

此时Agent真正需要维护的已经不是:

“我要写什么代码?”

而是:

“整个项目现在处于什么状态?”

这也是为什么OpenAI在长任务实践中把“外部化状态”列为Agent保持长期连贯的重要因素,包括仓库文件、文档、Worktree、输出以及测试结果等。

因此长任务的第一个原则应该是:

不要让任务状态只存在于模型脑子里。


二、第一层控制:一个长任务只能有一个Root Goal

这是最容易被忽略的一点。

例如有人给Codex:

重构登录模块,同时升级React,解决TypeScript错误,优化页面性能,再把测试覆盖率提高一些。

看起来这是一个“大任务”。

实际上里面至少包含:

Goal A:登录重构 Goal B:依赖升级 Goal C:类型修复 Goal D:性能优化 Goal E:测试增强

这不是一个Goal。

这是一个:

Backlog。

如果Agent连续执行几个小时,它必须不断判断:

现在最优先处理哪个目标?

而不同目标甚至可能相互冲突。

例如:

React升级导致类型变化;

类型变化影响登录重构;

登录重构又让原来的测试失效。

最后因果关系越来越复杂。

OpenAI目前对Codex/goal的建议也非常明确:适合长任务的目标应该“比普通Prompt大,但比开放式Backlog小”,要明确Agent要实现什么、不应该修改什么、如何验证以及什么时候停止;并明确不建议把一组互不相关的工作塞进同一个Goal。

所以第一步应该先定义:

Root Goal

例如:

完成认证模块重构,在不改变现有API行为的前提下,使登录、注册和Token刷新测试全部通过。

这才是一个真正可执行的Goal。

其他事情:

升级React 优化页面性能 整理UI

暂时全部排除。

不是永远不做。

而是:

不属于当前Goal。


三、第二层控制:把Goal拆成Milestone,而不是拆成几十个Todo

确定Goal以后,下一步很多人会犯另一个错误:

写一张巨大的Todo List。

例如:

修改auth.ts 修改token.ts 修改cookie.ts 修改login.ts 修改register.ts 修改router.ts 修改tests 检查类型 运行lint 运行build ……

问题是:

这只是操作列表。

它没有告诉Agent:

完成到哪一步,应该停下来检查一次?

真正适合Agent的任务拆分不是:

File-Based。

而应该更加接近:

State-Based。

例如认证系统重构,可以拆成:

Milestone 1

建立当前行为Baseline。

完成标准:

记录现有登录测试 记录注册测试 记录Token刷新测试 记录API返回行为

Milestone 2

迁移Token逻辑。

完成标准:

Token单元测试通过 旧API签名保持不变

Milestone 3

迁移登录流程。

完成标准:

登录测试通过 错误码行为不变

Milestone 4

迁移注册与刷新Token。

完成标准:

相关测试全部通过

Milestone 5

最终验证。

完成标准:

完整测试 Lint Build 最终Diff

现在Agent执行的不再是:

把一大堆文件改完。

而是:

把系统从State A推进到State B。

这才是Milestone真正的意义。

OpenAI公开的长时任务实践也采用类似方式:先冻结项目目标、约束、交付物以及“Done when”,然后让Codex生成基于Milestone的计划,并在每一个阶段运行测试、Lint和类型检查。


四、第三层控制:每个Checkpoint必须有Evidence

Milestone解决的是:

任务怎么分。

但还缺一个关键问题:

怎么知道这个阶段真的结束了?

很多Agent任务的问题就在这里。

Codex可能说:

Milestone 2完成。

但“完成”可能只是:

代码已经修改

而真正应该要求的是:

代码修改 + 测试通过 + Diff合理 + 没有新增失败

这就是:

Checkpoint。

可以把Checkpoint理解成每个阶段之间的“门”。

只有满足条件,才能进入下一个阶段。

例如:

Milestone 2: 迁移Token逻辑

它的Checkpoint可以写成:

[ ] Token相关单元测试通过 [ ] API输出保持兼容 [ ] 没有修改数据库Schema [ ] Build成功 [ ] Diff仅涉及预期模块

只要有一项失败:

不要进入Milestone 3。

先解决当前阶段。

这会带来一个非常大的好处:

错误不会一直向后传播。

否则很容易出现:

阶段1留下问题 ↓ 阶段2建立在错误状态上 ↓ 阶段3继续产生新修改 ↓ 阶段4测试全面爆炸

到了最后,你根本不知道最早从哪里开始出错。

OpenAI针对长任务的Goal机制同样强调“verifiable stopping condition”和validation loop,并建议定义能够证明进度的命令或产物,让Codex按照Checkpoint工作并保持简短的进度记录。

所以长任务真正可靠的结构应该是:

Plan ↓ Milestone ↓ Checkpoint ↓ Evidence ↓ Next Milestone

而不是:

Plan ↓ 不停修改 ↓ 最后一次性测试

五、第四层控制:把Progress写进文件,不要全部留在对话里

上一篇我们讨论了Context Pollution。

长任务里还有一个更进一步的问题:

即使Context没有明显污染,

执行状态本身也应该外部化。

例如建立:

PLAN.md STATUS.md DECISIONS.md

它们并不是为了写漂亮文档。

而是承担不同状态。


PLAN.md

保存相对稳定的计划:

Goal Milestone 1 Milestone 2 Milestone 3 Constraints Done When

它回答:

我们准备怎么完成任务?


STATUS.md

保存当前执行状态:

Current Milestone:2 Completed: - Baseline完成 - Token生成逻辑迁移 Current Issue: Refresh Token测试失败 Next: 检查Cookie设置

它回答:

现在做到哪里了?


DECISIONS.md

保存已经确定的重要决策:

Decision 01: 继续保持原API返回结构。 Reason: 避免影响现有客户端。 Decision 02: 不升级JWT依赖。 Reason: 当前问题与依赖版本无关。

它回答:

为什么之前这样决定?

这三个文件解决的是一个非常现实的问题:

如果Agent运行几个小时以后进行了Context Compaction,或者中间发生大量对话,

它仍然可以重新读取这些文件,快速恢复到:

Current Project State。

OpenAI在长任务案例中明确把这种“durable project memory”作为最重要的技术之一:把spec、plan、constraints和status写入Markdown文件,让Codex可以反复读取,以减少漂移并保持稳定的Done定义。

所以长任务里:

文档不是任务结束后的记录。

它可以直接成为:

Agent运行时的外部记忆。


六、第五层控制:并行任务必须隔离,不要多个Agent抢同一个Workspace

当Codex能够运行多个Agent以后,一个非常自然的想法是:

那我干脆同时开三个Agent。

例如:

Agent A:重构认证 Agent B:补测试 Agent C:修TypeScript

理论上效率提高3倍。

实际如果三个Agent直接修改同一个Workspace,很容易产生:

A修改auth.ts ↓ B基于旧auth.ts写测试 ↓ C又修改auth.ts类型 ↓ 三个任务开始互相覆盖

这种问题不是模型能力问题。

而是:

共享可变状态冲突。

Codex App目前采用独立Thread和Git Worktree支持并行任务。OpenAI官方文档说明,Worktree可以让多个独立Codex聊天在同一个项目中并行工作而不互相干扰,每个Worktree拥有独立的仓库文件副本,同时共享Git元数据。

所以更稳定的并行结构应该是:

Repo ├── Worktree A │ └── Auth Refactor │ ├── Worktree B │ └── Test Expansion │ └── Worktree C └── Type Fix

而不是:

一个Workspace ↓ 三个Agent一起改

这里其实出现了一个很重要的Agent工程原则:

Context Isolation解决认知冲突。

Worktree Isolation解决代码状态冲突。

这两种隔离缺一不可。

OpenAI目前的Codex App本身也把Agent放在项目下的独立线程中运行,并内置Worktree支持,让多个Agent能够在同一个仓库里工作而不直接影响彼此的代码状态。


七、第六层控制:任务方向变化时,不要硬着头皮继续原计划

长任务还有一个很常见的问题:

计划已经失效,但Agent仍然沿着原计划继续执行。

例如最开始认为:

登录Bug来自Token刷新。

于是Plan是:

Milestone 1 重构Token Milestone 2 修改Refresh Milestone 3 更新Login

执行Milestone 1以后却发现:

真正Root Cause是:

Cookie SameSite配置错误

这时候最差的处理方式就是:

既然Plan已经写了,那还是继续执行吧。

正确做法应该是:

Re-plan。

明确记录:

Original Hypothesis: Token刷新错误 Status: 已排除 New Root Cause: Cookie SameSite Plan: 停止Token重构 重新生成后续Milestone

这叫:

Course Correction。

长任务真正的优势不是:

永远按照最初计划执行。

而是:

发现现实和计划不一致以后,能够保持已有有效成果,同时调整后续方向。

OpenAI对当前Codex长时任务能力的描述也特别强调,它能够在多步骤执行中接受中途纠偏,而不必因为方向调整就完全重置整个运行。

因此:

Plan不是合同。

它只是当前最合理的路线。

真正不能变化的是:

Goal和Verification Criteria。

只要Goal没有改变,

Plan完全可以变化。


八、什么时候应该用Goal,什么时候只需要普通Task?

并不是所有任务都需要搞成Long-Horizon Workflow。

一个简单判断方法是:

普通Task

例如:

修复这个TypeScript类型错误。

可能:

5—20分钟 一个模块 一个明确错误 一次验证

没有必要建立复杂计划。


Medium Task

例如:

重构订单状态逻辑并补测试。

可能需要:

2—4个Milestone 多个文件 阶段验证

这时候可以使用:

PLAN + Checkpoint

Long-Horizon Task

例如:

技术栈迁移

大型代码重构

多模块系统改造

长时间实验与优化

此时才真正需要:

Root Goal + Milestone + Checkpoint + External State + Worktree + Evidence

OpenAI目前对/goal的定位也是用于拥有明确成功条件和验证循环的长时间编码任务,例如大型重构、迁移、实验和持续迭代,而不是一组松散、无关的小需求。

所以Agent工程并不是:

所有任务都复杂化。

而是:

任务越长,治理结构越完整。


九、一个真正适合Codex长任务的模板

假设现在要执行:

重构认证系统。

不要只写:

帮我把认证模块重构好。

可以改成:

【Root Goal】 重构认证模块, 保持现有API兼容, 最终所有认证相关测试通过。 【Non-Goals】 不升级框架版本。 不修改数据库Schema。 不重构无关业务模块。 【Milestone 1】 建立Baseline。 验证: - 登录测试 - 注册测试 - Refresh Token测试 - Build 【Milestone 2】 迁移Token模块。 Checkpoint: - Token单元测试通过 - API行为不变 - Diff仅涉及认证模块 【Milestone 3】 迁移登录和注册流程。 Checkpoint: - 登录测试通过 - 注册测试通过 【Milestone 4】 完整验证。 Checkpoint: - 全部认证测试通过 - Build通过 - Lint无新增问题 【Execution Rules】 每完成一个Milestone: 1. 更新STATUS; 2. 总结修改; 3. 保存验证结果; 4. 再进入下一阶段。 如果发现原计划错误: 停止继续扩张修改范围, 更新Root Cause, 重新规划剩余Milestone。 【Done When】 所有目标行为验证通过, 最终Diff完成Review, 不存在未说明的失败项。

这段Prompt真正重要的不是它比较长。

而是它给Agent建立了:

Goal ↓ Boundary ↓ Milestone ↓ Checkpoint ↓ Evidence ↓ Done

这已经不是普通Prompt Engineering。

它更接近:

Agent Execution Protocol。


十、为什么“任务拆分”最终解决的不是效率,而是可恢复性?

很多人认为拆任务主要是为了:

让Codex一次少做一点。

这只说对了一部分。

真正更重要的是:

Recovery。

假设一个任务运行6小时。

如果它是一个连续的大流程:

Start ↓ …………………… ↓ Failure

最后失败时,你可能不知道:

从哪里重新开始?

但如果任务是:

Milestone 1 ✓ ↓ Milestone 2 ✓ ↓ Milestone 3 ✓ ↓ Milestone 4 ✕

问题就非常简单:

从Milestone 4恢复。

前面三个阶段不需要重新证明。

所以好的任务拆分带来的最大价值其实是:

Failure Localization。

能够明确知道:

错误发生在哪个阶段。

进一步还会带来:

Resume Capability。

能够从最近一个可信Checkpoint继续。

这和数据库事务、分布式任务以及CI Pipeline的设计思想非常接近。

真正可靠的系统从来不会假设:

整个长流程永远不会失败。

而是提前设计:

失败以后怎么恢复。

Agent也一样。


十一、Codex长任务最终需要的是一个“闭环系统”

把前面6层放在一起,就可以得到一个完整结构:

Root Goal ↓ Milestone ↓ Checkpoint ↓ External State ↓ Isolated Execution ↓ Verification ↓ Evidence ↓ Next Milestone

如果失败:

Failure ↓ Identify Checkpoint ↓ Update State ↓ Re-plan ↓ Resume

这时候Agent就不再是:

接受一个Prompt以后一直生成下去。

而是在运行一个:

有目标、有状态、有检查点、有恢复能力的工程循环。

这其实也是OpenAI目前对Codex长时工作的核心描述:Agent通过计划、实现、运行工具、观察结果、修复错误并继续迭代的闭环工作,而不是依赖一次性生成;项目状态、验证反馈和可中途调整的执行循环共同维持长任务的连贯性。


从Context Engineering再往前一步,是Task Engineering

上一篇我们讨论:

Context Engineering。

解决的是:

Agent应该持续知道什么?

这一篇进一步解决的是:

Task Engineering。

它关注:

Agent应该怎样持续完成一个复杂目标?

两者结合以后,Codex长任务的基本框架开始变得清楚:

Environment ↓ Permission ↓ Context ↓ Goal ↓ Milestone ↓ Checkpoint ↓ Verification ↓ Evidence

这也是为什么Agent越来越强以后,开发者真正需要学习的东西反而越来越不像:

怎么写一个漂亮Prompt。

而更像:

怎么设计一个可执行系统。

因为模型能够连续工作几分钟以后,Prompt非常重要。

模型能够连续工作几小时甚至更长以后,

真正决定结果的开始变成:

任务有没有边界;

目标有没有冻结;

状态有没有外部化;

阶段有没有Checkpoint;

并行任务有没有隔离;

每一步有没有验证;

失败以后能不能恢复。

这些东西共同决定:

Agent到底是在“长时间工作”,还是在“长时间地逐渐跑偏”。


最后

Codex长任务容易跑偏,真正的问题通常不是:

Agent不能连续工作。

而是:

我们仍然用短任务的方法管理长任务。

一句Prompt交代目标。

然后期待Agent连续工作几个小时。

中间不断追加要求。

最后再一次性检查结果。

这种方式在任务越来越长以后一定会变得脆弱。

更稳定的方式应该是:

一个Root Goal 拆成多个Milestone 每个Milestone都有Checkpoint 每个Checkpoint都有Evidence 状态持续写入外部文件 并行任务通过Thread和Worktree隔离 发现方向错误立即Re-plan 最后按照明确Done条件结束

当这套结构形成以后,

Codex真正发生的变化不是:

它一次能做更多事情。

而是:

它能够在一个可控制、可验证、可恢复的系统里持续工作。

从这个角度看,未来AI编程真正重要的能力可能不是:

Prompt Engineering。

甚至也不只是Context Engineering。

而是:

Task Engineering。

因为Agent越能长时间自主工作,

人类真正需要设计的就越不是下一句话。

而是:

整个任务应该怎样运行。