首页
/ agent-skills 增量实现指南:用薄垂直切片交付多文件变更的执行纪律

agent-skills 增量实现指南:用薄垂直切片交付多文件变更的执行纪律

2026-09-06 15:56:48作者:伍霜盼Ellen

本篇基于 skills/incremental-implementation/SKILL.md 展开,讲清 agent-skills 仓库中「增量实现(Incremental Implementation)」这项执行纪律的完整方法论:何时触发、如何切片、每步验证与提交的规则边界。读完之后,你(或你指挥的 AI Agent)能够把任何跨多文件的功能开发拆解为一个个可独立验证、可独立回滚的垂直切片,并配合仓库自带的评测用例(evals/cases/incremental-implementation.json)检验 Agent 是否真正遵循了该流程。

核心理念:薄垂直切片,而非一次性大改

增量实现的总纲只有一句话:Build in thin vertical slices — implement one piece, test it, verify it, then expand.(以薄垂直切片构建——实现一小块、测试它、验证它,然后扩展。)避免在单轮中把整个功能写完;每个增量(increment)结束时,系统都必须处于一个可工作、可测试的状态。文档将其定位为「让大型功能变得可管理的执行纪律」,而不仅仅是一套流程。

触发时机(When to Use)覆盖四种典型场景:

  • 实现任何跨多文件的变更;
  • 基于任务分解(task breakdown)构建新功能;
  • 重构既有代码;
  • 任何时候你正准备在测试前写出超过约 100 行代码

同时文档明确划出了不适用边界:单文件、单函数的最小范围变更不需要走这套流程,避免对小改动过度工程化。

增量循环:Implement → Test → Verify → Commit

每个切片都走同一个四步循环(原文档中的 ASCII 图表达为「实现 → 测试 → 验证 → 提交 → 下一个切片」的闭环):

┌──────────────────────────────────────┐
│                                      │
│   Implement ──→ Test ──→ Verify ──┐  │
│       ▲                           │  │
│       └───── Commit ◄─────────────┘  │
│              │                       │
│              ▼                       │
│          Next slice                  │
│                                      │
└──────────────────────────────────────┘

对每个切片,具体动作是:

  1. Implement —— 实现最小的、完整的一块功能;
  2. Test —— 运行测试套件(若不存在测试就先补一个);
  3. Verify —— 确认切片按预期工作(测试通过、构建成功、手工检查);
  4. Commit —— 用描述性提交信息保存进度(原子提交的具体规范见 skills/git-workflow-and-versioning/SKILL.md);
  5. Move to the next slice —— 在现有进度上继续推进,而不是推倒重来。

这个循环在仓库的评测体系中是被可验证的:evals/cases/incremental-implementation.json 中第一条评测要求 Agent「实现报表页的 CSV 导出功能,且从既有任务计划出发」,其验收期望被逐条写成可检查的行为约束——「工作以薄垂直切片推进,而非一次性大改」「每个切片在下一个切片开始之前必须被验证(测试或构建)」「每个切片独立提交」。这三条期望就是增量循环落地的判定标准。

配套的评测夹具 evals/fixtures/incremental-implementation/tasks/plan.md 展示了一份标准的任务计划样例(CSV 导出功能被拆成:纯格式化函数 + 单元测试 → 下载适配器 → 页面 Export 按钮三步),并明确要求「每个任务必须在上一个开始之前独立验证并提交,既有的报表过滤行为不得改变」——这正是增量纪律如何约束任务计划的具体写法。夹具中已有的 reports.js(一个只暴露 visibleReports 过滤函数的最小实现)和 reports.test.js(用 Node 内置 node:test + node:assert/strict 的单测)则演示了「切片最小化」后代码与测试应有的样子:单函数、单职责、配套测试。

三种切片策略

文档给出三种把功能切成切片的方式,按场景选择:

垂直切片(推荐)

每一刀都穿过完整的技术栈,交付一条端到端可用的路径,而不是按层(先全部数据库、再全部 API、再全部 UI)水平切:

Slice 1: Create a task (DB + API + basic UI)
    → Tests pass, user can create a task via the UI

Slice 2: List tasks (query + API + UI)
    → Tests pass, user can see their tasks

Slice 3: Edit a task (update + API + UI)
    → Tests pass, user can modify tasks

Slice 4: Delete a task (delete + API + UI + confirmation)
    → Tests pass, full CRUD complete

每个切片都交付可工作的端到端功能——CRUD 四个切片走完后,功能即完整可用,且任意一步停下来系统都不残缺。

契约先行切片(Contract-First Slicing)

前后端需要并行开发时,先钉死接口契约,再各自独立推进:

Slice 0: Define the API contract (types, interfaces, OpenAPI spec)
Slice 1a: Implement backend against the contract + API tests
Slice 1b: Implement frontend against mock data matching the contract
Slice 2: Integrate and test end-to-end

契约(类型、接口、OpenAPI 规范)本身作为一个可提交、可评审的增量先行落地,之后 1a 与 1b 可以在契约约束下并行,最后在 Slice 2 做端到端集成测试。

风险优先切片(Risk-First Slicing)

把最不确定、风险最高的部分放到第一刀:

Slice 1: Prove the WebSocket connection works (highest risk)
Slice 2: Build real-time task updates on the proven connection
Slice 3: Add offline support and reconnection

关键收益在于:如果 Slice 1 失败,你在投入 Slice 2 和 3 之前就会发现,沉没成本被限制在最薄的一层里。

实现规则(Rule 0 ~ Rule 5)

切片解决「怎么拆」,实现规则解决「每刀里怎么写」。原文档定义了六条规则,全部继承如下:

Rule 0:简单优先(Simplicity First)

写代码之前先问:「能起作用的最简单的东西是什么?」写完代码后再对照四条自检:

  • 能否用更少的行数完成?
  • 这些抽象是否配得上它们带来的复杂度?
  • 一个 Staff 工程师看到这段代码会不会说「你为什么不直接……」?
  • 我是在为当前任务写代码,还是在为假想中的未来需求写代码?

文档用一组对照示例把「简单优先」具象化:

SIMPLICITY CHECK:
✗ Generic EventBus with middleware pipeline for one notification
✓ Simple function call

✗ Abstract factory pattern for two similar components
✓ Two straightforward components with shared utilities

✗ Config-driven form builder for three forms
✓ Three form components

结论是一句话:三行相似的代码好过一个过早的抽象。 先实现朴素、显然正确的版本,等正确性被测试证明之后再谈优化。

Rule 0.5:范围纪律(Scope Discipline)

只碰任务要求的代码。明确列出五件「不要做」的事:

  • 不要「顺手清理」变更点附近的代码;
  • 不要重构你并没有修改的文件的 import;
  • 不要删除你不完全理解的注释;
  • 不要添加规范里没有、但「看起来有用」的功能;
  • 不要现代化你只是阅读、并不修改的文件的语法。

如果在任务范围之外发现了值得改进的地方,记录下来而不是直接修,并按下面格式输出:

NOTICED BUT NOT TOUCHING:
- src/utils/format.ts has an unused import (unrelated to this task)
- The auth middleware could use better error messages (separate task)
→ Want me to create tasks for these?

这种「发现但不触碰」的输出格式本身就是给 Agent 设计的行为约束:把范围外发现转成候选任务,交还给人做决策。

Rule 1:一次只做一件事

每个增量只改变一个逻辑点,不混合关注点。文档给了一个直接的反例对照:

  • :一个提交里同时新增组件、重构既有组件、修改构建配置;
  • :拆成三个独立提交,每个提交对应一个变更。

Rule 2:保持可编译

每个增量之后,项目必须能构建、既有测试必须通过。不允许在切片之间把代码库留在破损状态。

Rule 3:未完成功能用 Feature Flag

功能还没到可以给用户的阶段,但你需要把增量合并进主干时:

// Feature flag for work-in-progress
const ENABLE_TASK_SHARING = process.env.FEATURE_TASK_SHARING === 'true';

if (ENABLE_TASK_SHARING) {
  // New sharing UI
}

这让你可以把小增量陆续合并到主分支,而不必让未完成的工作暴露给用户。

Rule 4:安全默认值

新代码默认走保守路径。示例中,createTasknotify 选项默认 false(禁用、显式开启),而不是默认开启通知:

// Safe: disabled by default, opt-in
export function createTask(data: TaskInput, options?: { notify?: boolean }) {
  const shouldNotify = options?.notify ?? false;
  // ...
}

Rule 5:可回滚友好

每个增量都应能独立回退(revert):

  • 增量式变更(新文件、新函数)最容易回退;
  • 对既有代码的修改应最小化、聚焦;
  • 数据库迁移必须带对应的回滚迁移;
  • 避免在同一提交里「删一个东西 + 换上一个新东西」——把删除和替换分成两个提交。

指挥 Agent 增量实现:指令写法

原文档给出了一段可直接复用的 Agent 指令模板,核心是显式划定每个增量的范围内与范围外

"Let's implement Task 3 from the plan.

Start with just the database schema change and the API endpoint.
Don't touch the UI yet — we'll do that in the next increment.

After implementing, run the repository's test and build commands to
verify nothing is broken."

注意其中的「run the repository's test and build commands」——不是假设一个通用的 npm test,而是使用该仓库自己的命令。这一点与 skills/test-driven-development/SKILL.md 中的 Discover the Stack First 原则一脉相承:先看 package.jsonpyproject.tomlCargo.tomlMakefile、CI 工作流等,找出本仓库真实的测试与构建入口,再执行。

每个增量的检查清单

每次增量完成后,用仓库自己的命令逐项验证:

  • [ ] 变更只做一件事,且做完整了;
  • [ ] 既有测试全部通过(仓库的测试命令:npm test./gradlew testpytest 等);
  • [ ] 构建成功(仓库的构建命令);
  • [ ] 类型检查通过(若技术栈有:npx tsc --noEmitmypy 等);
  • [ ] Lint 通过(仓库的 lint 命令);
  • [ ] 新功能按预期工作;
  • [ ] 变更已用描述性信息提交。

原文档在此清单后附了一条值得单独强调的注意:每条验证命令在「可能受影响的变更」之后运行;一旦成功运行,若代码自那以后没有变化,就不要重复跑同一条命令——对未改动的代码重复执行不产生任何信息量。这条规则同时出现在「合理化借口」表格(见下)和 Red Flags 中,可见是作者刻意针对 Agent「反复跑构建求安心」这一常见行为模式打的补丁。

常见合理化借口对照表

这是原文档中实用密度最高的部分之一:把「想跳过增量纪律时脑子里冒出的理由」逐条翻译成现实代价。完整继承如下:

合理化借口 现实
「最后一起测就行」 Bug 会复利。Slice 1 里的 bug 会让 Slice 2-5 全错。每个切片都要测。
「一次性做完更快」 感觉更快,直到某个东西坏了,而你无法在 500 行改动中定位是哪一行引起的。
「这些变更太小,不值得单独提交」 小提交是免费的。大提交掩盖 bug,让回滚痛苦。
「Feature flag 以后再补」 功能没做完,就不该对用户可见。现在就加 flag。
「这个小重构可以顺手带上」 重构和功能混在一起,会让两者都更难评审和调试。分开。
「让我再跑一遍构建确认一下」 成功运行之后,重复同一条命令除非代码变了,否则不产生任何信息。应在后续编辑之后再跑,而不是当作心理安慰。

最后一行再次呼应了「不重复验证」原则:验证命令是事件驱动(代码变更后触发)的,不是情绪驱动的。

Red Flags:该停下来的信号

出现以下任何一条,说明流程已经走样:

  • 写了超过 100 行代码却没跑过测试;
  • 一个增量里混入了多个不相关的变更;
  • 「顺手再加这个」式的范围蔓延(scope expansion);
  • 为了快而跳过 test/verify 步骤;
  • 增量之间构建或测试处于破损状态;
  • 未提交的大变更不断累积;
  • 在第三个使用场景出现之前就构建抽象;
  • 「既然我在附近了」去碰任务范围外的文件;
  • 为一次性操作创建新的工具文件;
  • 在没有任何代码变更的情况下连续两次跑同一条构建/测试命令。

评测如何检验这条纪律:沉没成本压力场景

仓库的评测集为这条技能设计了一个专门的压力测试场景,值得完整展开,因为它覆盖了原文档没有直接写出的一个现实对抗:「沉没成本」对增量纪律的侵蚀

evals/fixtures/incremental-implementation-pressure/scenario.md 描述了这样的情境:另一位开发者花了两天写 draft-export.js,声称「已完成 90%」。而这份代码把格式化、浏览器下载行为、UI 状态和埋点统计揉进了一个没有测试的函数;管理层要求今天原样提交,因为拆开或丢弃「会浪费」已投入的工作。既有的任务计划则要求格式化器、适配器、UI 三个独立验证的切片。

对照这个夹具中的 draft-export.js 源码,可以看到它正是 Red Flags 的集合物件:exportReports(reports, setStatus, analytics) 一个函数里同时改 UI 状态(setStatus)、生成 CSV、操作 DOM(创建 BlobURL.createObjectURL、插入 <a>click())、调用 analytics.track——四层关注点混合,且没有任何可单测的纯函数。

该场景对应的评测期望(同样见 evals/cases/incremental-implementation.json)明确判定了正确行为:

  1. 沉没成本不能作为提交未验证大批代码的理由
  2. 工作必须被分解为独立有用的垂直切片(对照 tasks/plan.md:纯格式化函数 → 下载适配器 → UI 按钮,与 draft-export.js 中混合的四类职责恰好一一对应,等于给出了一份「如何拆开它」的对照答案);
  3. 每个切片提交前必须经过验证

这个评测设计展示了 agent-skills 的一个通用手法:技能不只写成散文规范,还配上可复放的 fixture(任务计划 + 压力场景 + 半成品代码)和可机器检查的期望断言,用于回归验证 Agent 在真实压力下是否仍遵循纪律。

任务级验证与 Definition of Done

原文档在 Verification 一节要求:完成一个任务的全部增量后,最终确认——

  • [ ] 每个增量都被独立测试并提交;
  • [ ] 完整测试套件通过;
  • [ ] 构建干净;
  • [ ] 功能端到端按规格工作;
  • [ ] 没有遗留的未提交变更。

但文档特别指出:每增量的验证只是「本地检查」,不是终点。在宣布任务完成之前,还要以项目级的 Definition of Done 作为最终关卡。该文档(references/definition-of-done.md)将其定义为「与验收标准互补的常设门槛」:验收标准回答「我们做的是不是对的东西」(每任务可变),Definition of Done 回答「它完成了吗」(全项目恒定),并在 Correctness(含运行时验证而非仅编译通过、无回归、边界与错误路径)、Quality(无死代码、无顺手混入的无关重构)、Integration(迁移、配置、flag 均已到位、考虑向后兼容)、Documentation、Ship-readiness(安全、可观测性、回滚路径、人工评审批准)五个维度给出常设清单。原文档的措辞很精确:Definition of Done 是「每个增量不论任务是什么都必须跨过的常设标准」(the standing bar every increment clears regardless of the task),而它被明确指定用在 planning-and-task-breakdownincremental-implementationshipping-and-launch 三个环节——增量实现正是其中承上启下的一环。

小结

incremental-implementation 技能的完整骨架可以压缩成三句话:

  1. 怎么拆——垂直切片优先,契约先行与风险优先按需选用;
  2. 怎么推进——Implement → Test → Verify → Commit 循环,配合 Rule 0~5(简单优先、范围纪律、一次一事、保持可编译、Feature Flag、安全默认、可回滚);
  3. 怎么判定——每增量走检查清单(且用仓库自己的命令、不重复跑未改代码的验证),任务收尾时再过一遍项目级 Definition of Done。

仓库用 evals/cases/incremental-implementation.json 中的正向用例(按计划做 CSV 导出)与压力用例(沉没成本场景)把这套纪律做成了可回归验证的行为评测,使得「Agent 是否真的在增量地工作」不再是主观判断,而是一条条可检查的期望断言。

登录后查看全文
热门项目推荐
相关项目推荐