随笔

最大化发挥 GPT Plus 账号价值:一次子任务执行与验收实验(下)

复盘 Phonics Quest 升级实验中的真实派发链路:Codex 子任务如何通过 DeepSeek provider 执行,子任务怎样被 AGENTS 约束,为什么实现和验收要拆开,哪些候选必须被拦住,以及这次节约成本的效果和差距。

2026-07-27 CodexDeepSeekcc-taskmulti-agent实践复盘

上一篇讨论的是系列主旨:最大化发挥 GPT Plus 账号价值,不是让高阶 GPT 主控模型做完所有事,而是让它把主要额度用在任务拆解、边界判断、结果回收和最终裁决上。

这一篇回到具体实验。

实验落地在一个很小的 Web 工具:给四年级学生使用的自然拼读小游戏 Phonics Quest。它不是复杂系统,但很适合做第一次多 agent 拆解实验,因为它的边界清楚,失败成本低,又足够覆盖真实小工具升级中会遇到的问题。

本次实验采用 Codex 子任务 agent 通过 --provider 机制选择第3方底层模型,任务仍然运行在 Codex 的 agent 框架里。准备验证的是:

当主控 agent 把任务派发给一个使用 DeepSeek provider 的 Codex 子任务后,子任务的交付边界是否清晰,结果是否可回收,验收是否能把错误候选拦住。

本次试验使用的任务指派工具是 cc-task。在这套流程里,cc-task 负责创建子任务、隔离 worktree、调用指定 Codex provider,并把 final-messageeventspatchstderr 等产物回收到主控可检查的位置。(本次试验中创建并迭代的任务指派工具 cc-task,已发布到公开仓库 julia3356/codex-execution-backend-kit。)

从只读 review 开始

实验没有一开始就让使用 DeepSeek provider 的 Codex 子任务改代码。

第一步是只读 review:让它读取小游戏,按照教学适配目标提出改进意见,但明确要求不要修改任何文件。

这个任务完成后,主控回收了产物:

final-message.md
events.jsonl
stderr.log
git-status.txt
diff-stat.txt
changes.patch

结果是可接受的:

  • status 是 completed。
  • exit code 是 0。
  • stderr.log 为空。
  • git-status.txt 为空。
  • diff-stat.txt 为空。
  • changes.patch 为空。

这一步证明,Codex 子任务可以在 DeepSeek provider 下完成一个只读任务,并且执行结果能够被主控完整回收。它没有解决所有问题,但证明了任务指派器的基本闭环:任务可派发,过程可记录,结果可审查。

子任务必须变成可验收的交付

只读 review 之后,第一阶段实现任务被收窄成六条:

  • 增加 CVC 短元音预热关。
  • 给关卡增加中文名和中文解释。
  • 增强规则卡片与答错反馈,让四年级学生知道“为什么”。
  • 修复 level-count 显示为“当前关/总关数”。
  • 只修改 index.htmlREADME.md
  • 不引入依赖,不修改 Git 配置,不提交,不推送。

这不是普通的“优化一下”式任务,而是子任务被定义成一个可验收的交付:输入是什么,允许改哪里,不能做什么,完成后要看哪些产物。只有这样,使用 DeepSeek provider 的 Codex 子任务才是执行 agent,而不是另一个自由发挥的主控。这是最大化 Plus 账号价值的前提。主控 agent 必须掌握控制权,必须把任务拆到可以回收、可以验收、可以拒绝。

第一次实现:模型完成了任务,也暴露了底线

第一轮实现任务完成后,这个底层使用 DeepSeek 的 Codex 子任务生成了候选 patch。

从功能目标看,它确实尝试做了事:增加教学适配、补中文解释、调整反馈。但人工 review 时发现了一个阻塞问题:候选 index.html 的 JavaScript 语法会失败。而原因是中文教学文案里直接写了未转义的英文双引号,例如“类似"哎"”这样的字符串。如果这个文本正好落在双引号包裹的 JS 字符串里,脚本就会被提前截断。

这不是文案风格问题,而是运行级错误。更重要的是,子任务自己的验证没有发现它。它做了类似 git diff --check 的表层检查,却没有真正证明页面脚本可解析。

这次失败其实暴露了子任务执行过程中的风险控制挑战:

  • 模型自述不能替代验证。
  • git diff --check 不能替代运行级检查。
  • 实现候选必须先被回收,再由主控或独立验收任务检查。
  • 候选失败时,主工作区不能被污染。

换句话说,第三方 provider 可以承担执行型 token,但主控的判断权依然要慎重地掌握在最强的模型,甚至是人的手中。

为什么要拆成“执行”和“验收”两阶段

第一次失败前,流程其实沿用了模型给出的规划路径:让一个子任务同时完成执行和验收。这次它既要生成候选 patch,又要检查交付物质量。失败以后,我做了一个调整:执行和验收拆成两个阶段,并分别设计目标和边界。

执行任务
  只负责生成候选 patch,不 apply,不提交。

验收任务
  只负责读取候选产物,检查范围、语法、需求满足度和风险,不修改源码。

主控 agent
  读取执行和验收产物,做最终 accept / revise / reject 判断。

这个拆分不是为了形式上更复杂。它有两个重点作用。

第一,避免同质化判断。

如果同一个模型既负责实现,又负责验收,它很容易沿着自己的实现思路证明自己是对的。哪怕不是故意,它也可能漏掉自己生成的风险。

本次还没有真正引入第二个模型做验收,但通路已经留出来了。下一步可以尝试:

  • Codex + DeepSeek provider 做执行,另一个 provider 或模型做验收。
  • 第三方 provider 做执行,本地小模型做机械检查。
  • 本地规则先跑语法和路径门禁,再交给 GPT 主控做最终判断。

这让主控体系可以引入异质验收,而不是让一个模型自写自证。

第二,让验收模块可以独立迭代。

这和软件工程中的隔离很像:执行模块只负责按任务边界生成候选,验收模块专门负责检查候选。而且,验收一旦独立出来,就可以把每次失败沉淀成自己的规则,而不反向污染执行环节。

比如这次实验里,语法错误可以沉淀成内联脚本解析检查;patch 污染可以沉淀成 patch 文件头检查;验收 agent 越界可以沉淀成“验收任务不得修改源码”的身份规则。这些经验都进入验收模块,下一次验收更强,但执行任务仍然只需要专注于交付候选 patch。

这才是做“执行/验收”拆分的长期价值:它既给不同模型交叉检查留出通路,也让验收规则形成一个可持续增强的循环。

验收任务也必须被约束

拆出验收任务以后,并不意味着验收 agent 就天然可靠。

在一次验收任务中,它发现了一个问题:JS 引用了 level-total,但 HTML 没有对应元素,运行时会报错。与此同时,它又漏掉了另一个更严重的字符串引号语法错误。这说明验收任务也不能只写成“请 review 一下”。它必须有切入场景的明确检查项,如:

  • 读取 git-status.txt
  • 读取 diff-stat.txt
  • 检查 changes.patch 的文件头。
  • 检查 patch 是否只包含允许文件。
  • 检查 stderr.log
  • 对单页 Web 工具做内联脚本语法检查。

后续将内联脚本语法检查补进门禁:从 index.html 提取 <script> 内容,用 JS parser 或 new Function() 做解析检查。对这种单页工具来说,这是很低成本但很有效的验收。

这一步节约的是主控模型反复试错的 token,但不是主控最终判断的 token。

一次用户标注:播放规则应该讲中文

接下来又实验了一次对具体用户反馈问题的响应。

页面里有一个按钮叫“播放规则”。但点击以后,规则说明是英文朗读。

这个设计不符合目标用户使用习惯,模型自己在设计时没有发现,但是真实的人很快就揪出了这个设计 bug:单词本身应该用英文发音,但规则解释面向中国小学生,则应该用中文讲清楚。

这个需求将派发给子任务,验证对于宽泛描述型的问题,能否被清晰地进行任务交接并完成可靠交付:

  • 问题明确:规则播报语言错了。
  • 修改范围明确:主要是 index.html 里的语音逻辑和规则文案。
  • 验收明确:规则播报用中文,单词朗读仍用英文。
  • 风险较低:候选失败可以丢弃。

主控 agent 先定位原因:所有关卡的 rule 都是英文,通用 speak() 又强制使用 en-US,所以“播放规则”必然英文播报。

然后它派发执行任务,让底层使用 DeepSeek 的 Codex 子任务把语音路径拆开:

  • “听单词”继续使用英文 voice。
  • “播放规则”使用中文规则文本和中文 voice。

第一轮候选看似满足需求,但 patch 被运行时产物污染,混进了 changes.patchdiff-stat.txtfinal-message.mdgit-status.txt 等报告文件。

这种 patch 不能 apply。因为它不再是业务代码修改,而是把任务运行过程也写进了候选变更。

主控 agent 拒绝了第一轮候选,并重新派发第二轮执行任务,把门禁写得更硬:

  • 候选 worktree 里只能出现 M index.html
  • changes.patch 只能包含 index.html 一个文件头。
  • 不允许在项目根创建交付文件。
  • 交付内容只能由 cc-task runtime 自动采集。

第二轮候选才达到要求:

  • 只修改 index.html
  • “播放规则”改为中文播报。
  • “听单词”继续英文朗读。
  • 各关卡均有中文规则。
  • 中英文 voice 分别选择,并支持安全回退。
  • git diff --check 与内联 JS 语法检查通过。

这次修复说明,Codex 框架下的第三方 provider 可以完成力所能及的小实现。但它的交付必须被主控回收并验收,不能直接进入主线。

AGENTS.md 控制的是子任务身份

这次实验还暴露了另一个关键问题:子任务能不能稳定执行,很大程度上取决于它读到的 AGENTS.md 是否写给了正确的 agent。

最初的项目约束里混了两类内容:

  • 主控 agent 如何派发 cc-task、读取 artifact、做最终裁决。
  • 被 cc-task 调起的子任务 agent 应该如何执行实现任务或验收任务。

这两类规则不能混在一起。

主控 agent 需要理解的是:

  • 什么时候应该派发执行任务。
  • 什么时候应该派发验收任务。
  • 什么时候要读取 artifact。
  • 什么时候可以建议 apply。
  • 什么时候必须 reject。

而子任务 agent 需要理解的是:

  • 这次任务角色是执行任务,还是验收任务。
  • 如果是执行任务,就只生成候选 patch。
  • 如果是验收任务,就只检查候选,不修改源码。
  • 不提交,不推送,不改 Git 配置。
  • 不把运行时报告写进业务工作区。

所以约束应该被拆开。

父级 AGENTS.md 面向主控 agent,写“执行任务 + 验收任务”的两阶段调度。子项目 AGENTS.md 面向 cc-task 子任务 agent,只描述它在某一次被派发任务中应该承担的角色。

这个文档修改必须坚持身份定位清晰。AGENTS 不是普通说明书,它会直接影响 agent 的行为边界。子任务读到的约束越清楚,主控 agent 的拆解和回收能力越稳定。

这次节约了什么,也没节约什么

从整体成本看,这次节约的是高阶 GPT 主控模型在执行细节上的消耗。

节约的是:

  • 让 Codex 子任务里的第三方 provider 承担部分文件阅读和局部实现。
  • 让子任务生成候选 patch 和 final message。
  • 让验收任务承担第一轮检查。
  • 让主控少陷入重复试错。

没有节约、也不应该节约的是:

  • 主控对任务边界的设计。
  • 主控对候选 patch 的最终判断。
  • 主控对失败样本的归因。
  • 主控对 AGENTS 和流程约束的沉淀。

这和最初预期有一致的地方:主控模型确实可以把大量执行型 token 转移出去。

也有明显差距:第三方 provider 不是稳定可靠的“自动完成器”。它需要更硬的边界、更明确的验收、更清晰的身份约束。

所以这套方案当前还不是全自动省成本系统,而是一个低成本、可审查、可逐步强化的任务委派通道。

本次实验留下的规则

第一,子任务必须是可验收交付,不是开放式讨论。

第二,执行任务和验收任务要拆开:一方面为以后使用不同模型交叉验收留出通路,另一方面让验收规则可以独立迭代,不影响执行环节。

第三,验收必须检查真实产物,不能只听 final message。

第四,AGENTS.md 必须写给正确身份的 agent。主控规则和子任务规则混在一起,会削弱主控的拆解能力。

第五,主控要保留最终裁决权。外部模型可以产出候选,不能绕过主控 apply。

结论

这次实验尝试证明一件事:在 Codex 主控、cc-task 隔离、artifact 回收、AGENTS 身份约束和两阶段验收的组合下,基于 --provider 接入的第三方底层模型可以承担一部分执行型工作,从而让 GPT Plus 主控模型把额度用在更高价值的判断上。

这也是该方案值得继续试验的理由。

多 agent 不是让更多模型同时干活,而是让不同模型承担不同责任。主控负责拆解和裁决,执行 agent 负责候选交付,验收 agent 负责挑错,artifact 负责把过程留下来。

下一步可以试验异质验收:让执行和验收真正落到不同模型上,观察是否能降低同质化漏判。同时把路径检查、patch 文件头检查、脚本语法检查这类机械门禁固化进验收模块,让它持续吸收失败经验,而不是每次都消耗主控模型去重复判断。