Agent Skills 实战第三课:从规格到工单,别再按前后端拆任务

第二课结束后,"取消订阅"已经被重新定义成两个明确动作:停止续订和立即终止。适用范围、状态边界、异常场景也经过了逐项确认。

现在是不是可以直接让 Agent 写代码了?

还差一步。聊天里形成的共识,需要先变成一份能验收的规格,再拆成一组能够独立交付的工单。否则团队很容易回到熟悉的老路:后端一张票、前端一张票、测试一张票。每个人都有任务,但在最后一张票完成前,没有任何一张票能单独证明需求已经可用。

第三课只解决两个连续动作:/to-spec 把已经谈清楚的需求收束成规格,再用 /to-tickets 把规格拆成可演示、可验证、有真实依赖关系的纵向工单。

先把两个 skill 的边界分清

它们都在处理"已经知道要做什么"的工作,但职责完全不同:

text 复制代码
/to-spec      把当前对话和代码库理解整理成一份规格
/to-tickets   把规格拆成一组可独立交付的纵向工单

/to-spec 不负责继续追问需求。发现关键问题还没有答案时,应该退回第二课继续 /grill-with-docs,而不是一边写规格一边让 Agent 自己补答案。

/to-tickets 也不负责重新设计方案。它应该忠实使用规格里的用户故事、实现决定、测试边界和 out of scope,把工作切成下一阶段可以执行的单位。

完整链路是:

text 复制代码
grill-with-docs -> to-spec -> to-tickets -> implement -> code-review

第三课处在理解与实现之间。这里切得好不好,会直接决定后面的人和 Agent 能不能并行推进,也决定每个上下文是否能独立完成一块工作。

运行前先检查三个前提

第一,第一课已经通过 /setup-matt-pocock-skills 配置 issue tracker 和 ready-for-agent 标签。两个 skill 都会把产物发布到 tracker,没有配置时,它们只能猜。

第二,需求已经经过讨论。业务术语、核心范围和关键取舍都已确定。如果需求负责人还会对"停止续订到底是什么意思"给出不同答案,就不该进入 /to-spec

第三,Agent 可以读取相关仓库。规格和工单要使用项目自己的领域语言,也要尊重现有 ADR。脱离代码库生成的规格,往往会提出项目里根本不存在的模块边界。

准备好后,在 Agent 对话框运行:

text 复制代码
/to-spec

如果当前对话不是第二课的原会话,要先把完整讨论或已经确认的材料带进来。/to-spec 只综合它拿得到的上下文,不会替你找回丢失的决定。

/to-spec 的第一步,不是马上写文档

它会先探索仓库,读取领域词汇和相关 ADR,然后确认一个常被忽略的问题:这次功能应该从哪里测试?

skill 把这个观察边界叫作 testing seam。可以把 seam 理解为一个稳定的公开入口:测试从这里发起操作,再从外部可观察结果判断行为是否正确。

对"停止续订"来说,候选 seam 可能有:

  • 直接测试内部的 setCancelFlag() 函数;
  • 调用公开的订阅服务接口;
  • 通过 HTTP API 发起停止续订并查询订阅状态;
  • 从页面完成整条用户操作。

/to-spec 的规则很明确:优先已有 seam,优先更高层的 seam,数量越少越好,理想状态是整个改动只需要一个主要 seam。

这不是说所有功能都必须写端到端浏览器测试,而是提醒团队:不要为了方便测试,给每个内部函数都开一个口子。高层 seam 更接近用户行为,也允许内部实现持续重构而不必同步改测试。

Agent 必须先把建议的 seam 展示出来,让人确认符合团队预期,然后才开始写规格。这是 /to-spec 唯一必要的确认点,不等于重新启动一轮需求访谈。

一份合格的 spec 应该包含什么

/to-spec 使用固定结构,把聊天中的共识变成后续可以引用的工程输入。

Problem Statement:先说用户遇到了什么问题

不要写"系统缺少 cancellation API"。这是实现视角。

更好的表达是:"个人付费用户无法自行停止下个周期续费,只能联系客户支持,处理慢且容易产生非预期扣款。"

问题陈述要让团队知道为什么值得做,而不是提前暗示某个技术方案。

Solution:从用户视角描述解决方案

例如:"用户可以在账户账单页停止续订,继续使用权益到当前周期结束,并看到明确的失效日期。"

这里描述交付形态,不展开文件、类名和代码实现。

User Stories:把可观察行为列完整

每条用户故事使用统一格式:

text 复制代码
As an <actor>, I want a <feature>, so that <benefit>

对应中文可以写成:

text 复制代码
1. 作为个人付费用户,我希望停止下个周期续费,以免再次扣款。
2. 作为已停止续订的用户,我希望看到权益结束日期,以便安排后续使用。
3. 作为改变决定的用户,我希望在周期结束前恢复续订,以便继续使用服务。
4. 作为客服人员,我希望看到用户的续订状态,以便准确回复咨询。

用户故事应该覆盖正常路径、权限差异和关键状态,不是为了凑数量,而是给后续切票和验收提供完整行为清单。

Implementation Decisions:只记录已经确定的技术决定

这里可以写模块职责、接口契约、Schema 变化、系统交互和架构选择,但不要放具体文件路径或大段代码。文件会移动,规格不应该因此过期。

例如:

  • Billing 是订阅续订状态的唯一所有者;
  • 停止续订只影响下个周期,不撤销已经发起的扣款;
  • Account 通过领域事件获取状态变化,不直接修改 Billing 数据;
  • 第一版只覆盖个人付费套餐。

如果之前用原型验证过状态机、Schema 或类型设计,而且一段短代码比文字更准确,可以保留"决定最密集"的片段,并明确注明来自 prototype。除此之外,不要把工作代码贴进规格。

Testing Decisions:提前锁定公开行为

这一节要写清:什么算好测试、从哪个 seam 观察、哪些模块参与,以及仓库里有没有同类测试可以参考。

例如:"通过现有订阅 HTTP API 测试公开行为,验证状态、权益结束日期和重复请求结果;不直接断言内部事件处理器被调用次数。"

Out of Scope:明确这次不做什么

Out of scope 不是可有可无的尾注,而是控制范围最有效的一节。

例如:第一版不处理企业合同、不支持立即终止、不自动退款、不改造历史账单。以后这些内容可以进入新规格,但不能在当前工单里被"顺手加上"。

Further Notes:只放确实需要继续携带的信息

外部系统限制、上线窗口、迁移提醒等不适合前面章节的信息可以放这里。没有就留空,不要为了让文档显得完整堆背景材料。

/to-spec 做完后会发生什么

规格生成后,会发布到第一课配置好的 issue tracker,并自动应用 ready-for-agent 标签,不需要再跑一次 triage。

发布前至少做一次人工检查:

  1. 有没有把未确认的猜测写成既定事实?
  2. User Stories 是否覆盖第二课确认的主要场景?
  3. Testing Decisions 是否测试公开行为,而不是内部实现?
  4. Out of Scope 是否足够明确?
  5. 文档是否使用 CONTEXT.md 中的统一术语?

这五项通过后,才进入拆票。

从 spec 到 tickets,最容易犯的错是横向拆分

运行 /to-tickets 时,可以直接使用当前对话,也可以附上 spec 文件、issue 编号或 URL。传入引用后,Agent 应读取完整正文和评论,而不是只看标题。

很多团队看到一个跨层功能,会自然拆成:

text 复制代码
票 1:新增数据库字段
票 2:开发停止续订 API
票 3:开发账单页按钮
票 4:补测试

这叫横向切片。每张票只完成一个技术层。数据库字段交付后,用户什么也看不到;API 完成后,仍然无法走通业务;测试票还要等所有实现结束。四张票看似可以分给四个人,实际上形成了一条隐藏的集成长链。

/to-tickets 要求使用 tracer bullet,也就是纵向切片:每张票都沿着一条窄路径穿过必要的 Schema、API、UI 和测试层,完成后可以独立演示或验证。

同一个需求,怎样拆成纵向工单

"停止续订"可以拆成下面这样的行为切片。

工单 1:个人月度订阅用户可以停止续订

它交付一条最窄的完整路径:符合条件的用户在账单页停止续订,刷新后看到"将在某日结束",API 与持久化状态一致,并有公开行为测试。

这张票会跨越多个技术层,但完成时已经有用户价值,也能独立验证。

工单 2:用户可以在周期结束前恢复续订

它交付逆向状态迁移:已停止续订但权益仍有效的用户可以恢复,页面和 API 都显示最新状态。

如果恢复续订依赖工单 1 定义的状态和入口,那么它被工单 1 阻塞。这个依赖是真实的,不是因为团队习惯"按顺序做"。

工单 3:客服可以查看停止续订状态

客服后台能看到是否续订、权益结束日期以及状态更新时间。如果它只读取已有公开状态,并不依赖恢复续订,那么它可能只被工单 1 阻塞,而不需要等待工单 2。

这样拆完后,工单 2 和工单 3 会同时进入可开工队列。只要共同的 blocker 完成,两张票就能并行。

每张 ticket 必须回答三个问题

/to-tickets 在发布前会先展示编号列表,每张票都要有:

  • Title:简短、使用领域语言的标题;
  • Blocked by:真正阻止它开工的其他工单;
  • What it delivers:这张票完成后,哪个端到端行为可以工作。

然后 Agent 会询问粒度是否合适、阻塞关系是否真实,以及是否需要合并或继续拆分。团队确认前不应该直接发布。

课堂上可以对每张票追问:

text 复制代码
完成后能展示什么?
不依赖未完成的其他实现,能否验证?
是否能放进一个全新的 Agent 上下文完成?
它被谁阻塞,为什么不完成那个前置就无法开工?

任何一个问题答不清,说明切片还需要调整。

阻塞关系不是排期偏好

"我想先做 A 再做 B"不是 blocking edge。"没有 A,B 在技术上无法开始或无法独立保持正确"才是。

所有 blocker 都完成的工单组成当前 frontier,也就是此刻真正可以拿走执行的工作集合。

text 复制代码
Ticket 01 ──> Ticket 02
         └──> Ticket 03

初始 frontier:Ticket 01
Ticket 01 完成后:Ticket 02 + Ticket 03

阻塞边写得准确,多个 Agent 才能安全并行。把所有票强行串成一条线,会浪费并行能力;遗漏真实依赖,则会让两个上下文同时修改尚未稳定的契约。

发布到不同 tracker,工单内容不变

如果第一课配置的是本地 Markdown,skill 会在下面的位置为每张票创建独立文件:

text 复制代码
.scratch/<feature-slug>/issues/
├── 01-<slug>.md
├── 02-<slug>.md
└── 03-<slug>.md

编号按依赖顺序排列,blocker 先发布。每个文件包含 What to buildBlocked byStatus: ready-for-agent 和验收条件。不会把所有票塞进一个大文件。

如果使用 GitHub、Linear 等真实 tracker,就一张 ticket 对应一个 issue,并尽量使用平台原生 blocking 或 sub-issue 关系;平台不支持时,在 Blocked by 中写引用。skill 同样会应用 ready-for-agent 标签。

来源是已有父 issue 时,只建立引用,不关闭也不修改父 issue。

宽范围重构是纵向切片的例外

有些改动无法切成可独立上线的用户行为。例如重命名一个被上千处调用的共享字段,任何一批调用点单独修改都会让 CI 失败。

这类 wide refactor 不要硬套 tracer bullet,而应使用 expand -> migrate -> contract:

text 复制代码
Expand:新旧形式并存,现有调用不受影响
Migrate:按包或目录分批迁移,每批保持 CI 绿色
Contract:所有迁移完成后删除旧形式

Expand 是各迁移票的 blocker,Contract 被所有迁移票阻塞。如果迁移批次连单独保持绿色都做不到,可以共享 integration branch,再由最终的 integrate-and-verify 工单统一保证绿色。

这个例外很重要。工程方法不是把规则机械套在所有工作上,而是知道规则在哪种风险形态下失效。

怎么判断工单拆得好不好

可以用下面的检查表评审 /to-tickets 输出:

检查项 合格表现 不合格表现
用户价值 完成后有一条行为可展示 只完成数据库、API 或 UI 某一层
独立验证 自己带验收条件和必要测试 等"最后统一补测试"
上下文大小 一个 fresh context 可以完成 包含多个互不相关的大目标
领域语言 标题和描述使用 glossary 术语 使用含糊或临时技术叫法
阻塞关系 只声明真实技术依赖 把期望顺序当 blocker
范围控制 对应 spec 的 user story 偷带 out-of-scope 工作
发布顺序 blocker 先创建,引用可追溯 先发布下游导致依赖无法引用

工单不是越多越专业。拆分的目标是让每一张都能独立向前走,不是把一项工作切成更多管理对象。

常见误区

/to-spec 又开始问一轮需求问题。 说明输入还没准备好,或者 Agent 偏离了 skill。回到 /grill-with-docs 解决决定,再让 /to-spec 只负责综合。

testing seam 选了内部函数。 先找已有公开入口,再往更高层移动。测试应该保护行为,不应该锁死实现。

spec 写满具体文件路径。 模块职责可以写,易变化的文件路径和工作代码不要写。它们会让规格很快失效。

ticket 还是"前端一张、后端一张"。 用"完成后用户能看到什么"重新切片,让必要层在一张窄票里闭环。

所有 ticket 都互相阻塞。 逐条检查,没有前置就真的无法开工吗?如果只是习惯顺序,删除这条 edge。

工单还没评审就直接发布。 /to-tickets 明确要求先 quiz 用户。粒度和 blocker 经过批准后再落到 tracker。

把宽范围重构切成大量无法保持绿色的小票。 改用 expand、migrate、contract,让兼容状态承接迁移过程。

第三课真正训练的是"切出可以完成的工作"

规格的价值,不是把聊天写得更正式,而是把问题、行为、技术决定、测试边界和范围固定下来。

工单的价值,也不是让看板更满,而是让一个人或一个 Agent 拿到之后,能够在独立上下文中交付一条可验证行为。

先 spec,再 tickets;先确认 seam,再写测试决定;先拆纵向价值,再画真实依赖。做到这三点,第四课的 /implement + /tdd 才能真正做到拿一张票、清一个上下文、完成一条路径。

相关推荐
AI大模型-小雄1 小时前
ChatGPT Plus 够不够用?从5种开发场景判断是否需要 Pro
chatgpt·ai编程·开发工具·codex·chatgpt plus·chatgpt pro
canonical_entropy2 小时前
Mission Driver:Loop Engineering 的一种通用参考实现
后端·aigc·ai编程
周末程序猿2 小时前
技术总结|十分钟了解PageIndex
llm·aigc·ai编程
战场小包2 小时前
世界杯结束了,我用 AI 造了平行宇宙,这次结局你写
前端·人工智能·ai编程
AINative软件工程5 小时前
MCP Server 权限边界工程实践:OAuth、最小权限与工具沙箱,别让 Agent 拿到整台机器
架构·llm·ai编程
深念Y5 小时前
AI编程Agent工具定义对比分析
agent·ai编程·开源项目·工具·tool·hermes·ccsiwtch
东小西13 小时前
第8篇:《白嫖社区生态:一行配置接入GitHub的MCP Server,AI直接读我的代码仓库》
openai·ai编程