摘要
AI 编码代理的主要风险不是单行代码写错,而是在目标、规格和边界尚未确认时,以远高于人工评审速度的节奏持续扩大改动。本文建立一套从 SDD(规格驱动开发) 到 Plan、Goal、ExecPlan 的工作框架,并用 Loop 把探索、规划、执行、验证和人工评审串成受控闭环。
SDD 规定“应该构建什么、什么才算正确”;Plan 负责只读调查和下一小步的方案取舍;Goal 负责在批准范围内完成一个可验证的交付;ExecPlan 负责管理多个小时、多个里程碑和多个 Goal 的长任务;Loop 则规定每轮何时验证、何时停止、是否继续。ExecPlan 是 SDD 与 Loop 在多阶段 Agent 工作中的一种应用,不是二者的替代品。
本文给出四种工作选择:问题清楚时直接修改并验证;影响面不清时使用 Plan-only;一个 PR 可完成时使用受控 Goal;跨多个里程碑且结果可机器验证、容易回滚时才使用 ExecPlan。核心治理原则是:路线图不直接授权执行,发现不等于授权,超预算或出现架构分歧必须暂停,每个批次都要有范围、非目标、验证证据、人工检查点和自然终点。
最终目标不是让 Agent 自主完成更多代码,而是让每轮交付都能回答:改了什么、为什么这样改、证据是什么、还剩什么风险、谁批准继续。
总框架:SDD 与 Loop
本文把 SDD(Spec-Driven Development,规格驱动开发) 与 Loop(受控反馈循环) 作为理解 Plan、Goal 和 ExecPlan 的上层框架。
SDD:先定义应该构建什么
SDD 的核心不是先写一份很长的需求文档,而是把实现前提变成可检查的规格。规格至少应说明:
- 目标行为和用户可观察结果;
- 范围、非目标和兼容约束;
- 输入、输出、接口和状态变化;
- 错误处理、安全边界和不可逆操作;
- 验收标准、测试证据和未解决问题。
对 AI 编码代理而言,SDD 解决的是方向和契约问题:代理不能只根据一句“重构一下”自行推断产品意图,也不能把测试通过当作规格已经满足。Plan、Goal 和 ExecPlan 都应引用或补齐这些规格,但它们本身不是 SDD 的同义词。
Loop:控制如何推进
Loop 解决的是执行过程中的反馈与授权问题:
探索 → 规划 → 人工批准 → 执行 → 验证 → 评审 → 停止或进入下一轮
每轮都要有独立的范围、预算、验证证据和停止点。新发现可以改变下一轮的 Plan,但不能因为 Agent 已经发现了旁支问题,就自动获得继续修改的授权。Loop 的目标是缩短“证据到决策”的反馈周期,而不是让 Agent 连续产生更多未审查代码。
四者的关系
SDD:定义应该构建什么、必须满足什么约束
↓
ExecPlan:组织多阶段工作如何按里程碑推进
↓
Plan:决定下一小步怎么做,以及为什么这样做
↓
Goal:在批准范围内完成这一小步
↓
Loop:执行、验证、评审,并决定是否继续
因此,ExecPlan 是 SDD 和 Loop 在多阶段 Agent 工作中的一种应用形态,但不是 SDD 或 Loop 本身:
| 概念 | 主要回答的问题 |
|---|---|
| SDD | 应该构建什么,什么才算满足要求? |
| ExecPlan | 多个阶段如何按顺序、带证据地完成? |
| Plan | 下一小步怎么做,方案取舍是什么? |
| Goal | 这一小步的可交付结果和边界是什么? |
| Loop | 做完后如何验证、评审并决定是否继续? |
如果一份 ExecPlan 只有步骤清单,没有目标规格、验收标准和停止条件,它不是完整的 SDD 实践;如果它没有执行后的验证、评审和人工继续决策,它也没有真正实现 Loop。
本文核心观点
- AI 速度不能直接等同于交付速度。 交付速度受评审、验证和决策吞吐约束。
- Plan 与 Goal 共同实现受控 Loop:Plan 定义路径与证据,Goal 定义结果与边界,评审决定下一轮。 Roadmap 不应直接变成自治执行 Goal。
- 默认采用短工作流,例外采用长自治。 可预定义路径的任务不需要开放式 Agent。
- 小批量是 AI 工程的控制面。 它降低错误累积、沉没成本和人工认知负载。
- 测试通过不是整体正确。 还要证明范围、兼容性、可评审性和真实运行行为。
- 发现不是授权。 代理发现旁支问题后应记录并停止扩张,由人决定是否形成新 Goal。
- 大型重构必须拆分。 每个 Goal 都应独立验证、评审和回滚。
- Loop 迭代的是认知与交付,不是未审查的代码量。 每轮都应有独立的批准、验证和停止点。
1. 新手快速上手
先用一句话理解
- SDD:先把目标行为、约束和验收标准写成可检查的规格。
- Plan:先只读调查,弄清问题和方案,暂不改代码。
- Goal:在已经批准的范围内,完成一件可验证的小事。
它们合起来形成一个受控 Loop:规格确认 → 发现与规划 → 人工批准 → 有边界地执行 → 验证与评审 → 再决定下一步。新手只要记住:先确认什么才算正确;不确定时再 Plan;方案确认后,用 Goal 执行一个小切片;完成后回到人工评审,而不是让 Agent 自行继续。
先确认规格,再开始 Plan
Plan 不是用来替代需求澄清的。开始只读调查前,至少要写出一版轻量 SDD:
- 目标行为:用户或调用方最终能观察到什么;
- 非目标:本次明确不处理什么;
- 约束:兼容性、安全、性能、权限和不可逆操作边界;
- 验收:哪些测试、指标或产物可以证明完成。
规格不完整时,Plan 的任务是识别缺口并提出问题,而不是擅自补齐产品决策。规格确认后,Plan 才能围绕真实目标调查调用链、候选方案和最小切片。
Plan:先弄清楚,再决定是否动手
当你不确定问题在哪里、会影响哪些文件,或者需要在多个方案中做选择时,先进入 Plan 模式。此时要求 Codex 阅读代码、解释现状、给出方案,但不要修改文件。
一个简单的 Plan 请求可以这样写:
只读分析这个问题,不修改文件。
请说明:
1. 问题现在出现在哪个流程;
2. 可能影响哪些模块或文件;
3. 推荐怎样最小化修改;
4. 需要哪些测试来验证。
输出方案后停止,等待我确认。
Plan 的作用是帮助你做决定,不代表代码已经完成。发现前提不对时,继续讨论或重新规划即可。
Goal:一次只完成一件小事
当方案已经确认,且你能明确说明“完成后应该看到什么结果”时,再创建 Goal。一个好的 Goal 至少包含五件事:
- 目标:要实现什么可观察结果;
- 范围:哪些文件或模块可以改,哪些不能改;
- 验证:哪些测试、构建或产物必须通过;
- 边界:最多改多少文件、花多长时间,遇到什么情况必须停;
- 结束:当前事项完成后停止,不自动开始下一项。
示例:
仅修复视频页面切换策略后不生效的问题。
- 只修改策略选择和视频会话创建相关代码;
- 不改公共接口、视觉行为和其他页面;
- 补充对应测试,并运行定向测试;
- 如果需要修改无关模块或无法确定兼容性,停止并报告;
- 验证通过后停止,等待评审。
不要把“完成整个重构”“顺便优化相关代码”写成 Goal。这类目标太大,既难验证,也会让代理不断扩大工作范围。
一个最简单的使用流程
- 先问自己:问题清楚吗? 不清楚,先 Plan。
- 阅读 Plan: 只批准其中一个最小、可评审的修改。
- 创建 Goal: 写清目标、范围、验证和停止条件。
- 检查结果: 看 diff 和测试证据;满意后再考虑下一件事。
执行中发现的新问题,先记录下来。除非不处理就无法完成当前 Goal,否则不要让代理顺手继续修改。
什么时候不必使用 Plan 或 Goal?
- 只是解释代码、查询状态:直接提问即可。
- 只有一行配置、根因明确的小修复:可以直接修改并验证。
- 跨多个模块、需要多次评审的大型重构:拆成多个 Plan 和 Goal,不要用一个 Goal 包办。
先小后大
第一次使用时,选择一个预计 30 分钟内可以完成、测试方式明确的小问题。先建立一次受控闭环,再逐步扩大任务规模。
客户端差异
并非所有 Codex 客户端都有独立的 Goal 按钮。没有该功能时,可以把 Goal 的目标、范围、验证和停止条件直接写进任务提示词或 Issue。治理原则不变。
2. Plan、Goal 与相关载体的职责
AI 可以快速生成代码,但评审、验证和决策仍由人完成。如果代理持续扩大范围,代码产出越快,团队积累的评审压力和返工风险就越大。
因此,SDD、Plan 和 Goal 的治理共同解决一个核心问题:让 AI 在明确规格和授权边界内自主执行,而不是让它自行决定正确性或整个项目接下来要做什么。
从 Loop 的角度看,Plan 和 Goal 不是两个孤立命令,而是同一控制系统的不同阶段:
| Loop 阶段 | Plan/Goal 中的实现 | 人的职责 |
|---|---|---|
| 规格 | SDD 定义目标行为、约束和验收标准 | 确认需求、风险和非目标 |
| 观察 | Explore 与 Plan 收集调用链、测试和兼容面证据 | 判断问题定义和证据是否充分 |
| 选择 | Plan 给出候选切片、取舍和预算 | 批准一个切片,明确非目标 |
| 行动 | Goal 在已批准范围内修改 | 授权范围、权限和风险上限 |
| 验证 | Goal 运行分层测试并报告实际 diff | 判断证据是否足以接受交付 |
| 学习与继续 | Review 记录结果、风险与 Follow-ups | 决定停止、重新 Plan 或开启下一 Goal |
因此,Loop 追求的是更快、更可靠地缩短“证据到决策”的反馈周期,而不是让 Agent 连续执行更多步骤。
各类文档分别负责什么?
| 载体 | 主要作用 | 不应该承载什么 |
|---|---|---|
| SDD / 规格 | 目标行为、约束、接口和验收标准 | 具体执行进度和临时探索结果 |
AGENTS.md | 项目规则、常用命令、仓库导航 | 当前任务进度和临时实现细节 |
| Roadmap / Epic | 长期方向、阶段和依赖 | 对 Agent 的无限执行授权 |
| Plan | 调查结果、实施路径、风险和验证方式 | 未经批准的新任务清单 |
| Goal | 一个可验证结果及其范围、预算和停止条件 | 整个项目路线图或“持续优化” |
| PR / CL(代码评审单) | 人工评审、合并和回滚单元 | 多个互不相关的改动主题 |
| ADR / Decision Log | 关键决策、备选方案和理由 | 机械执行步骤 |
可以把它们理解成:
Roadmap:最终要去哪里
SDD:什么结果才算正确
Plan:下一小步怎么走
Goal:这一步做到什么程度才算完成
ExecPlan:多个小步如何按里程碑推进
PR:人如何检查和接收这一步
ExecPlan:多小时工作的执行契约
ExecPlan 可以理解为面向复杂任务的可执行计划文档。它不是把更多待办事项堆在一起,而是把目标、现状、方案、里程碑、预算、风险、验证证据和停止条件放到同一份可持续更新的文档中。OpenAI 的 PLANS.md 实践强调:计划应自包含、可维护,记录关键决策和发现,并让每个里程碑都能独立验证和恢复。OpenAI: Using PLANS.md for multi-hour problem solving
ExecPlan 与本指南中的载体关系如下:
| 载体 | 关系 |
|---|---|
| Roadmap / Epic | 提供长期方向,不直接授权执行 |
| Plan | 为一个切片调查问题并提出方案 |
| Goal | 在批准范围内完成一个可交付结果 |
| ExecPlan | 当任务跨越多个小时或多个里程碑时,持续管理多个受控 Goal |
| PR / CL | 承载每个里程碑的评审、合并和回滚 |
因此,ExecPlan 不应把多个 Goal 变成一个无限自治任务。正确关系是:
ExecPlan(总执行契约)
→ 里程碑 1:Plan → 批准 → Goal → 验证 → 评审
→ 里程碑 2:Plan → 批准 → Goal → 验证 → 评审
→ 里程碑 3:Plan → 批准 → Goal → 验证 → 评审
ExecPlan 的最小字段
# ExecPlan: <任务名称>
## 目标与非目标
- 目标:<可观察结果>
- 非目标:<明确不处理的内容>
## 当前状态
- 已确认的调用链、文件、测试和外部依赖
- 未确认的前提与待补证据
## 方案与取舍
- 推荐方案及原因
- 放弃的替代方案及原因
## 里程碑
1. <一个可独立验证和回滚的切片>
2. <一个可独立验证和回滚的切片>
## 每个里程碑的执行约束
- In scope / Out of scope
- 文件、时间、diff 和新抽象预算
- 测试、构建和人工检查点
- 完成条件、暂停条件和回滚方式
## 决策日志
- 日期、决策、证据、影响
## 发现与 Follow-ups
- 新发现及其证据
- 不属于当前范围的后续事项,不自动执行
## 最终验收
- 行为结果
- 测试与构建证据
- 实际 diff 和未完成风险
ExecPlan 的执行规则
- 先建立计划,再开始修改。 先读取代码、测试和约束;计划未经确认时不执行高风险动作。
- 按里程碑推进。 每个里程碑都要有独立 diff、验证结果和回滚路径。
- 计划是活文档。 新证据可以更新计划,但不能静默扩大范围;影响方向、接口或预算时必须回到人工批准点。
- 发现不是授权。 旁支问题进入
Follow-ups,除非当前目标无法完成,否则不顺手修改。 - 完成即停止。 一个里程碑完成后报告结果,等待人工决定是否开始下一个里程碑。
- 验证必须可追溯。 记录命令、测试结果、diff、日志或其他证据,不用“看起来没问题”替代验收。
什么时候使用 ExecPlan
只有在以下条件基本满足时,才考虑多小时 ExecPlan:
- 结果可以由测试、指标或确定性产物验证;
- 环境隔离,执行不会覆盖未提交工作;
- 每个里程碑可以独立提交、回滚和恢复;
- 外部写入、删除、迁移和权限变化有人工门禁;
- 墙钟时间、最大重试次数、diff 和文件预算明确;
- 任务不是开放式架构探索或需要频繁产品判断。
遗留系统“全面重构”、视觉品味调整、缺少回归保护的业务迁移,不适合直接交给 ExecPlan。
ExecPlan 提示词示例
先不要修改文件。请为这个多阶段任务生成 ExecPlan。
要求:
- 先调查现状、调用链、测试和兼容面;
- 拆成可独立验证、提交和回滚的里程碑;
- 为每个里程碑写清范围、非目标、文件/时间/diff 预算;
- 写出验证命令、完成条件、暂停条件和回滚方式;
- 单独列出决策日志和不自动执行的 Follow-ups。
输出 ExecPlan 后停止,等待批准。执行期间只推进当前已批准的里程碑,
超预算、发现新架构分歧或需要范围外修改时立即暂停。
Plan 与 ExecPlan 的根本区别
Plan 和 ExecPlan 都发生在修改代码之前,但解决的问题不同:
| 对比项 | Plan | ExecPlan |
|---|---|---|
| 核心问题 | 下一步应该怎么做? | 多阶段工作如何持续、可控地做完? |
| 主要产物 | 一份候选方案和一个建议切片 | 一份持续维护的执行契约 |
| 时间跨度 | 通常一个短任务或一个 Goal | 多小时、多个里程碑或多个 Goal |
| 是否执行 | 默认只读,输出后等待批准 | 批准后可按里程碑推进,但每个里程碑仍需验证和评审 |
| 关注重点 | 调查、影响面、方案取舍 | 里程碑、预算、状态、决策日志、证据和恢复 |
| 结束条件 | 方案清楚,能创建一个受控 Goal | 所有批准里程碑完成,或明确停止并交接 |
| 变化处理 | 发现新事实后重新规划 | 更新活文档,但不得静默扩大范围 |
| 失败恢复 | 重新做一次 Plan | 从最近一个已验证里程碑恢复 |
可以用一个简单判断来区分:
Plan 决定“做哪一小步以及为什么”;ExecPlan 管理“多小步如何按顺序、带证据地完成”。
Plan 的输出通常应该在这里停下:
只读调查 → 形成方案 → 选择一个切片 → 等待批准
ExecPlan 则把多个受控循环串起来,但不取消每一轮的人工边界:
ExecPlan
→ Plan 里程碑 1 → Goal 1 → 验证 → Review
→ Plan 里程碑 2 → Goal 2 → 验证 → Review
→ Plan 里程碑 3 → Goal 3 → 验证 → Review
因此,ExecPlan 不是“自动批准后续工作的超级 Plan”,也不是把整个 Roadmap 改名为计划文件。它必须保留每个里程碑的批准点、停止点和回滚点。
三种场景的连续选择
从任务开始到交付,可以按下面的路径选择工作方式:
- 只读回答:问题清楚,只需要解释代码、查询状态或给出建议,不需要修改。
- Plan-only:问题位置、影响面或实现方案不清楚;只读调查,输出方案后停止。
- 单次 Goal:方案已确定,范围小,能在一个 PR 或一个审查批次中完成。
- 多个 Plan + Goal:跨模块、跨 PR 或需要多轮架构决策,但每一步都能短周期完成。
- ExecPlan:工作预计持续多个小时,存在多个依赖里程碑,但每个里程碑都能独立验证、提交、回滚和恢复。
选择逻辑可以压缩为:
是否需要调查?
否 → 直接修改并验证
是 → Plan-only
↓ 方案是否已经确定?
否 → 继续 Plan 或请求人工决策
是 → 一个批次能否完成?
是 → Goal
否 → 多个 Plan/Goal
↓ 是否跨小时且可机器验证?
是 → ExecPlan
否 → 保持人工驱动的短批次
什么时候 Plan 足够
以下任务通常只需要 Plan,不需要 ExecPlan:
- 一个明确 bug 的根因定位;
- 一个 API 字段或配置项的修改;
- 单模块内的小型重构;
- 只需补充一组回归测试的修复;
- 需要在两种实现方案中做选择,但最终只会执行一个小切片。
Plan 的完成标准是“可以安全地批准下一步”,而不是“把未来所有工作都写完”。如果 Plan 已经能够给出准确文件、明确行为、测试和回滚方式,就应停止继续拆解,创建一个小 Goal。
什么时候需要升级为 ExecPlan
满足以下特征中的多数时,才考虑 ExecPlan:
- 任务会跨越多个独立 PR 或多个工作阶段;
- 前一阶段的结果会决定后一阶段的路径;
- 需要持续记录决策、发现和未决问题;
- 预计执行时间超过一个普通 Goal 的预算;
- 每个阶段都有自动化测试、指标或确定性产物;
- 可以建立隔离工作区和明确的恢复点;
- 任务不是开放式架构探索,而是路径虽长、验收仍然明确。
不应因为任务“看起来很大”就直接使用 ExecPlan。开放式遗留系统重构、视觉品味判断、频繁变化的产品需求和没有可靠回归测试的迁移,仍应拆成多个由人驱动的 Plan/Goal。
ExecPlan 如何承接 Plan
ExecPlan 的第一版不是凭空写出的,而是由一次只读 Plan 产生:
- Plan 调查代码、调用链、测试、兼容面和风险。
- 人工从 Plan 中批准第一个里程碑,并确认非目标和预算。
- ExecPlan 将后续里程碑、依赖和验收标准记录下来,但不自动授权执行。
- 当前 Goal 完成后,写入实际结果、diff、测试证据和新发现。
- 人工决定是继续执行 ExecPlan 的下一个里程碑,还是回到 Plan 重新评估。
这保证了 ExecPlan 既有全局上下文,又不会跳过局部批准。尤其当实际代码与初始假设不一致时,正确动作是更新 Plan 或暂停,而不是让 Agent 自己“顺着发现继续做”。
一个完整例子:播放器迁移
只用 Plan 的阶段:
只读审计播放器入口、Native/Exo 两条路径、状态所有权和现有测试。
请判断是否可以定义统一播放器接口,并给出第一个最小切片。
不要修改文件,输出后停止。
Plan 可能得出:先增加契约测试,再让 Native 路径接入接口;暂不迁移 Exo、Fragment 和策略选择。
用 Goal 执行第一个切片:
Goal:定义最小 VideoPlayer 契约并为 Native 路径补充测试。
In scope:接口文件、Native adapter、契约测试。
Out of scope:Exo、Fragment、策略选择、旧入口删除。
预算:最多 5 个生产文件,约 300 行逻辑 diff,执行 60 分钟。
完成:契约测试和 Native 定向测试通过,构建不变。
停止:发现需要修改策略选择或公开接口时暂停。
升级为 ExecPlan 的情况:
当 Native adapter、Exo adapter、会话策略、行为迁移和旧入口删除需要连续多个里程碑时,再创建 ExecPlan。此时 ExecPlan 负责维护顺序、依赖、总体验收和决策日志;每个阶段仍然分别使用 Plan 和 Goal。
把计划写成长文不等于 ExecPlan
以下写法看起来详细,实际上仍然不是 ExecPlan:
- 只有“待完成事项”,没有每个里程碑的完成证据;
- 把所有未来工作列出来,却没有预算和人工批准点;
- 发现新问题后直接追加到当前执行列表;
- 只有最终验收,没有中间可回滚状态;
- 写了“持续优化”“完成全部重构”等没有自然终点的目标。
真正的 ExecPlan 必须能够在任意里程碑暂停,并回答:
当前完成了什么?依据是什么?下一步为什么是它?谁批准继续?如果不继续,系统能否安全停在这里?
根据任务选择工作方式
| 任务情况 | 推荐方式 |
|---|---|
| 解释代码、查询状态 | 直接只读回答 |
| 根因明确的小修复 | 单次修改并验证 |
| 需求不清或影响范围未知 | Plan-only,输出方案后停止 |
| 路径明确、一个 PR 可完成 | 创建有边界的 Goal |
| 跨模块或多周重构 | Roadmap 拆成多个 Plan/Goal |
| 多小时且可机器验证、易回滚 | 满足准入条件后使用 ExecPlan |
默认优先选择短、可预测的工作流。只有成功路径无法预先确定,同时结果又能可靠验证时,才考虑提高 Agent 的自治程度。Anthropic: Building effective agents
3. 失控案例:为什么“大 Goal”会失败
下面的案例来自一次经过脱敏的媒体模块重构。原始目标同时包含代码清理、测试补齐、模块文档、三个功能域重构、双播放器迁移、设备验收和旧代码删除。
它看起来有顺序、有验收,也强调渐进兼容,但本质上仍是一个 Epic:需要多个 PR、多次架构决策和多轮人工验收,不应该直接作为一个 Goal 执行。
错误 Goal 原文:把整个 Epic 装进一个 Goal
下面保留经过模块名脱敏的原始 Goal。它不是推荐模板,而是用于说明“大而完整”为什么不等于“可执行”。
标题: SampleGallery 渐进式可维护性重构目标
原则:
- 功能完全兼容、渐进替换
- 保持 XML / ViewBinding,不迁移 Compose
- 新增 Kotlin + StateFlow 状态层和端口适配层
清理与基线:
- 盘点不可达代码、重复资源、大块注释、动态资源访问
- 逐批清理且每批构建验证
- 补齐媒体过滤、损坏文件、定位、旋转、续播、倍速、USB、
删除/导出、播放限制、语音和硬键回归
模块契约:
- 建立根、browser、main、data、assistant、wallpaper 的 AGENTS.md
- 记录职责、接口、状态所有权、依赖、测试入口和禁止项
Browser:
- 定义 BrowserUiState / BrowserAction / BrowserEffect
- 分离会话、导航、全屏/轮播、删除/导出、图片编辑
- 将会话状态从 GalleryConstant 迁入生命周期状态持有者
Video:
- 定义统一 VideoPlayer 能力接口
- 实现 NativeVideoPlayer 与 ExoVideoPlayer
- 增加开发配置策略,默认 Native,会话内固定,不自动回退
- 抽出播放状态机和音频焦点、持久化、手势、语音/硬键、
HVAC、系统属性、设备安全信号等端口或协调器
- 双策略验收后删除 VideoFragment2 旧入口
Picture:
- 分离装载/缩放、旋转、退出位置、轮播、运行限制
- 保持壁纸入口和原有交互兼容
交付顺序:
- 清理与测试基线
- 模块契约
- Browser 状态层
- Video 双策略
- Picture
- 删除旧代码和死资源
全局验收:
- 生产源码以 1000 行为目标阈值
- JVM / Robolectric 覆盖各类状态和系统事件
- 两套播放器执行同一组契约测试
- 每阶段运行模块单测、assembleDebug、lint
- 每阶段执行包含 USB、设备信号、音频焦点等设备清单
这个 Goal 同时包含代码清理、回归建设、文档契约、Browser 架构迁移、Video 双实现迁移、Picture 拆分、设备验收和旧代码删除。它应该被保存为 Roadmap,再拆成多个 Goal。
错误 Plan 原文:完成一步后自动追加下一步
执行过程中没有先形成一份完整、静态、等待批准的 Plan,而是从局部实现开始,随后不断改写 Plan。
Plan 快照 A:从 Video seek 局部切片开始
1. [completed] 审计两种策略 seek 手势状态与调用时序
2. [in_progress → completed] 迁移共同 seeking 状态到手势端口
3. [pending] 补齐双策略回归、契约并完成构建验证
这个切片本身可以成立,但没有文件数、diff、时间、新抽象和构建次数预算。
Plan 快照 B:完成 Video 后自动转入 Picture
1. [completed] 验证视频 seek 端口迁移的完整模块门禁
2. [completed] 审计 Picture 状态与 Fragment 责任边界
3. [completed] 提取下一项 Picture 纯状态并补充回归验证
4. [in_progress] 继续分批清理与最终验收
Video 小目标完成后没有停止评审,而是把 Picture 自动接入同一个 Goal。“继续分批清理与最终验收”也没有可判定终点。
Plan 快照 C:继续追加 Browser 微抽象
更新 1:
- [completed] 提取 Browser 硬键重复计数端口并验证
- [completed] 提取 Browser 系统栏回调保护端口并验证
- [in_progress] 继续分批清理与最终验收
更新 2:
- [in_progress] 提取 Browser pager interaction 端口
- [pending] 继续分批清理与最终验收
此时,“发现一个可提取状态就创建一个端口”已经替代人工架构选择。Plan 也从执行前的授权契约,退化成了没有终点的进度流水账。
失控是怎样发生的?
| 阶段 | 实际行为 | 应有控制动作 |
|---|---|---|
| 启动 | 把整个重构路线图创建为持久 Goal | 只做 Plan-only,先验证前提并拆分 PR |
| 局部实现 | 完成 Video 小切片后继续进入 Picture | 完成一个切片即停止并评审 |
| 发现问题 | 发现待删除入口仍是活跃入口 | 停止执行,返回 Plan 修正路线图 |
| 范围扩张 | 不断发现并提取新的 Browser 微抽象 | 记录到 Follow-ups,不自动实施 |
| 验证 | 多次构建和测试通过 | 仍需检查范围、兼容性和真实设备行为 |
| 用户介入 | 数小时后因改动过大被人工叫停 | 应由时间、文件数或 diff 门禁更早停止 |
为什么测试通过仍然不算成功?
局部测试和构建只能证明部分代码可以运行,不能证明:
- 累计 diff 仍然适合人工评审;
- 新增抽象确实有必要;
- 未授权模块没有被修改;
- 旧入口可以安全删除;
- 设备、系统事件和真实交互保持兼容;
- 临时兼容层将来会被删除。
因此,测试绿色是完成条件之一,不是范围正确和设计正确的替代品。
根因
- Roadmap 被当成 Goal:长期方向没有被拆成单次交付。
- Plan 变成开放 Backlog:新发现被自动追加并实施。
- 没有人工检查点:跨模块和架构决策未经重新批准。
- 没有硬预算:时间、文件数、diff 和新抽象数量都无上限。
- 没有自然终点:局部完成后,Agent 继续领取下一项任务。
正确做法
第一轮只做只读调查:
只读审计示例模块的 Browser、Video 和 Picture 结构,不修改文件。
验证播放器真实入口、现有测试和外部兼容面。
输出按 PR 拆分的路线图,并为第一个切片给出文件范围、预算、
完成条件和回滚方式。输出后停止,等待批准。
人工确认调查结果后,只批准一个小 Goal,例如“定义最小播放器接口和契约测试,不迁移页面、不改变运行路径”。这个 Goal 完成并通过评审后,才为下一步重新进入 Plan。
案例结论
问题不在于 Agent 做得不够多,而在于团队把一个多周 Epic 交给了没有预算、没有批准点、没有自然终点的 Goal。
4. 标准工作流:SDD、Plan 与 Goal 构成受控 Loop
flowchart LR
S[SDD<br/>定义规格与验收] --> E[Explore<br/>只读取证]
E --> P[Plan<br/>定义一个交付批次]
P --> A{人工批准?}
A -->|否| P
A -->|是| X[Execute<br/>受预算约束]
X --> V[Verify<br/>分层验证]
V --> R{是否满足完成条件?}
R -->|否且范围内| X
R -->|发现扩展/超预算| T[停止并报告]
R -->|是| H[Review<br/>人工评审]
H --> D{人工决定<br/>是否开启下一轮?}
D -->|是| E
D -->|否| N[结束当前工作]
阶段 A:SDD,先定义正确性
先写清目标行为、非目标、兼容约束和验收标准。规格可以很短,但必须足以判断候选方案是否满足需求。若关键需求、权限边界或不可逆操作仍不明确,应在这里暂停并请求决策。
阶段 B:Explore,只读探索
输出必须是证据而不是代码:
- 当前调用链和状态所有权;
- 外部兼容面;
- 已有测试覆盖与缺口;
- 候选切片及其收益、风险和文件影响;
- 对任务前提的验证结果。
此阶段禁止修改生产代码。若发现“待删除旧入口实际上仍被调用”,应先修正路线图,而不是继续实现原计划。
阶段 C:Approve,批准一个批次
由人选择一个切片,确认:
- 目标和非目标;
- 允许修改的模块;
- diff 与时间预算;
- 公开接口和兼容要求;
- 测试层级;
- 停止条件。
阶段 D:Execute,受预算执行
代理只执行已批准 Plan。旁支问题进入 Follow-ups,不得顺手修改。
阶段 E:Review,完成即停止
交付仅包含:
- 改动摘要;
- 行为证据和测试结果;
- 实际 diff 规模;
- 未执行项目与风险;
- 建议的下一个 Goal,但不自动启动。
这条路径才是本文所说的 Loop:每轮从 SDD 规定的正确性出发,经由证据和 Plan,进入受限 Goal,最后以验证和人工评审结束。Loop 的闭环点在人工是否批准下一轮,而不是 Agent 完成局部实现后自动领取新任务。这样既保留了“观察结果、调整路径、继续推进”的迭代能力,也让每次范围扩张、架构取舍和风险接受都有明确责任人。
5. 预算与强制停止条件
以下数值是本文结合上述资料给出的团队默认值建议,不是 OpenAI、Google 或 DORA 的强制标准。团队应按代码库和风险校准。
5.1 默认批次预算
| 项目 | 默认值 | 升级条件 |
|---|---|---|
| 代理执行时间 | 45–90 分钟 | 超过 90 分钟必须人工续期 |
| 生产文件 | ≤ 5 个 | 跨模块或公开接口变更需预批 |
| 总文件 | ≤ 10 个 | 生成文件可单独计算 |
| 人工可读逻辑 diff | 约 100–300 行 | 接近 1000 行必须拆分或预先获批 |
| 新抽象 | ≤ 1 个 | 第二个抽象进入后续 Goal |
| 模块跨度 | 1 个主模块 | 适配层可作为显式例外 |
| 完整构建 | 每个批准批次 1 次 | 中间使用定向测试 |
| Goal | 1 个评审结果 | 禁止“以及顺便” |
5.2 必须停止的条件
- 预计或实际超过任一预算;
- 任务前提被证伪;
- 需要修改未授权模块、公开接口、Manifest、持久化键或外部协议;
- 需要新增第二种架构抽象或第三方依赖;
- 发现用户已有改动与当前任务冲突;
- 关键行为没有可执行回归保护;
- 测试环境连续两次因同一外部原因失败;
- 出现多个同样合理、但取舍会影响长期架构的方案;
- 需要删除旧路径、资源或数据;
- 人工验收是唯一完成证据。
预算不是 KPI
预算的作用是触发重新决策,不是鼓励代理通过压缩代码、减少测试或隐藏变更来“达标”。
6. 分层验证:先定向,后全量
| 层级 | 何时运行 | 示例 |
|---|---|---|
| L0 静态检查 | 每次编辑后 | 引用搜索、格式、git diff --check |
| L1 定向测试 | 每个逻辑小步 | 单个状态处理器、接口或适配器契约 |
| L2 模块编译/测试 | 批次实现稳定后 | testDebugUnitTest、模块编译 |
| L3 完整构建/lint | 批次交付前一次 | assembleDebug、lint 报告 |
| L4 集成/设备验证 | PR 候选或发布前 | USB、音频焦点、车机信号、前后台 |
如果 L3 存在已知上游阻断,必须记录报告路径与根因,并明确它不能证明什么;不得为了变绿而修改范围外配置。
7. 大型重构如何拆分
Fowler 的 Branch by Abstraction 允许旧实现与新实现通过同一抽象并存,并要求系统在迁移期间始终可构建、可运行。Martin Fowler: Branch By Abstraction
当接口存在多个消费者时,Parallel Change 将迁移拆成 Expand、Migrate、Contract 三阶段,每阶段都可发布和验证;但如果 Contract 不执行,系统可能比迁移前更复杂。Martin Fowler: Parallel Change
媒体模块示例拆分
以下是路线图,不是一个 Goal,也不是一个可以直接执行的 ExecPlan。它需要先形成 SDD 和总体 ExecPlan,再按里程碑分别经过 Plan 与 Goal:
- Discovery Goal:只读盘点播放器入口、兼容接口和现有回归,不改代码。
- Test Baseline Goal:只增加 Native 当前行为测试,不重构生产代码。
- Contract Goal:定义最小
VideoPlayer能力接口和一组契约测试,不迁移 Fragment。 - Native Adapter Goal:让现有 Native 路径通过接口,默认行为不变。
- Exo Adapter Goal:让现有 Exo 路径通过同一组契约测试,不改策略选择。
- Session Strategy Goal:仅实现进入会话时固定策略及无自动回退。
- One Workflow Goal:一次迁移一个行为,例如续播,完成后停下评审。
- Contract Goal:证据证明旧入口无消费者后,单独删除旧代码和资源。
每个 Goal 都应拥有独立 diff、测试证据和回滚路径。不要把八个 Goal 重新包装成一个“多里程碑自治任务”,除非满足第 9 节的长任务准入条件。
8. 可复制提示词模板
8.1 Plan-only:先规划,不改代码
模式:Plan-only。禁止修改任何文件。
背景:<业务背景和当前问题>
目标:为 <单一结果> 形成一个可评审执行计划。
先确认规格:
- 目标行为:<用户/调用方可观察结果>
- 非目标:<本次不处理的内容>
- 约束:<兼容、安全、性能、权限或不可逆操作边界>
- 验收:<测试、指标或确定性产物>
请先读取:
- <文件/模块>
- <测试>
- <外部接口>
输出必须包含:
1. 当前数据流与控制流证据;
2. 任务前提是否成立;
3. 2–3 个切片方案及取舍;
4. 推荐方案涉及的准确文件;
5. 行为兼容面、测试与回滚;
6. 预计文件数、逻辑 diff 和耗时;
7. 哪些决策必须由我批准。
停止条件:输出计划后停止,等待批准;不得开始实现。
8.2 受控 Goal:一个 PR 级交付
Goal:<可观察、可验证的单一结果>
规格依据:
- 目标行为:<本次要满足的 SDD 结果>
- 验收标准:<可执行的测试或产物>
In scope:
- <允许修改的行为和文件>
Out of scope:
- 不处理 <相邻问题>
- 不修改 <模块/公开接口/资源/依赖>
兼容约束:
- <Intent、广播、偏好键、API、UI 等>
预算:
- 最长 60 分钟;
- 最多 5 个生产文件、10 个总文件;
- 约 300 行人工可读逻辑 diff;
- 最多新增 1 个抽象。
验证:
- 每步运行 <定向测试>;
- 交付前运行 <模块测试/构建>;
- 人工验证项只列清单,不声称完成。
强制停止:
- 前提不成立、需要跨范围修改、超预算或存在架构分歧时停止;
- 完成当前 Goal 后停止,不自动开始下一项。
交付格式:完成 / 验证 / 未做与原因 / 风险。
8.3 只读审计 Goal
Goal:验证 <旧入口/资源/依赖> 是否可以删除。
权限:只读,不修改代码、不删除文件。
证据:Manifest、直接引用、反射/动态资源、生成代码、测试、构建配置、运行入口。
完成条件:给出“可删除 / 不可删除 / 证据不足”结论,并列出证据路径。
停止条件:报告完成即停止。
8.4 超预算续期
不要继续修改。请报告:
- 已完成与未完成;
- 当前 diff 规模;
- 超预算原因;
- 最小可交付截点;
- 回退到批准范围的方法;
- 继续执行所需的新预算和新增风险。
等待我明确批准后再继续。
9. 多小时 ExecPlan 的准入条件
OpenAI 的 ExecPlan 适合复杂、多小时任务,但其前提是计划自包含、持续维护、每个里程碑独立可验证,并记录发现与决策。OpenAI: Using PLANS.md
ExecPlan 的准入顺序应是:先确认 SDD 规格,再用 Plan 验证路径和依赖,最后才批准 ExecPlan 的里程碑。没有稳定的目标行为、验收标准或回滚边界时,ExecPlan 只能记录不确定性,不能替代需求决策。
只有以下项目全部满足,才适合无人值守多小时运行:
- 目标结果可通过测试、指标或确定性产物机器验证;
- 执行环境隔离,不会覆盖未提交工作;
- 每个里程碑都可独立提交、回滚和恢复;
- 所有破坏性和外部动作均需人工批准;
- 路径虽长,但不是架构偏好探索;
- 预算、最大重试次数和墙钟超时明确;
- 代理可以访问真实运行日志或等价观测;
- 有自动化评审或强测试,而非只靠编译通过;
- 人工接受可能产生的 diff 规模;
- 完成后不会自动开启旁支优化。
不适合的典型任务:遗留系统开放式“全面重构”、视觉品味调整、缺少回归的业务流程迁移、需要频繁产品判断的工作。
10. 参考资料与观点来源
- OpenAI, How OpenAI uses Codex:大型改动先 Ask/Plan;推荐约一小时或数百行的明确任务;提示词应类似高质量 issue。
- OpenAI, Using PLANS.md for multi-hour problem solving:ExecPlan 的自包含、活文档、里程碑、决策日志和可验证结果要求。
- OpenAI, Harness engineering: leveraging Codex in an agent-first world:深度拆解、人类 QA 瓶颈、仓库知识地图和机械化架构约束。
- OpenAI, Codex-maxxing for long-running work:可验证 Goal、持续循环中的人类最终决定权。
- Anthropic, Building effective agents:工作流与 Agent 的区别、优先简单可组合模式、自治的成本与错误累积风险。
- Google Engineering Practices, Small CLs:一个自包含变更、小变更的评审与回滚优势、约 100/1000 行的经验参考。
- DORA, Working in small batches:小批量、快速反馈、INVEST,以及其在 AI 采用中的风险对冲作用。
- Martin Fowler, Branch By Abstraction:通过抽象让新旧实现并存并渐进迁移。
- Martin Fowler, Parallel Change:Expand–Migrate–Contract 兼容演进模式及未完成 Contract 的风险。
- Martin Fowler, Ship / Show / Ask:根据不确定性选择直接交付、展示或先讨论;重大方向应尽早交流。
一句话原则
让 AI 自主决定“怎么完成一个已批准的小目标”,不要让它自主决定“接下来整个系统还应该改什么”。