AI 加速开发流水线实战:Spec-Driven + GitHub Actions + Claude Code
用 Issue label(sdd:*)驱动开发流程,AI 执行 auto 步骤、人类把关 gate 步骤,交替自动与人工验证;branch 从 staging 分出并 PR 回 staging。
完整中文译文
面临的问题
每条开发流水线都会遇到相同的手工瓶颈。有人写 spec,有人 review,有人实现,有人开 Pull Request(PR),有人再 review。每次交接都是一次暂停、一段上下文丢失、一句"明天再看吧"。然而,在我所在的小组里,我们决定用一种不同的方式来解决这个问题。我们把这条流程的大部分环节变成了自动化流水线,其中一个 AI Agent(Claude Code)执行那些重复性的步骤,而人类只介入那些真正需要分析的节点。最终,这条 pipeline 运行在 GitHub 内部,由 Issue 上的 labels 全面驱动,遵循 Spec-Driven Development(Spec Kit)模型。我当时想:为什么不聊聊这次转型呢?所以这篇文章就是这次转型的"方法论",包括那些让 pilot 卡住的错误,以及我们仍在调整的部分。
设计黄金法则
规则表述起来很简单,但在实践中很强大:Issue 上的 sdd:* label 是唯一的事实来源。GitHub Project 的 board 只是视图。这解决了一个经典的自动化问题。当"真实"状态存在于一个任何人都可以随时编辑的地方(board),而自动化试图从中推断该做什么时,就会产生竞态条件和不一致状态。把 label 作为唯一的事实来源后,board 就变成了一个窗口:好看、有用,但可以丢弃。核心映射是 1 card = 1 spec = 1 branch:
- Card:通过特定模板("Spec card")打开的 Issue
- Spec:由 Agent 生成的
specs/NNN-slug/文件夹 - Branch:
NNN-slug,从 staging 分出,PR 回到 staging
把 card 在列之间移动,实际上就是换 label。每次换 label 都会通过 issues: labeled 事件触发 workflow。
设计最重要的一点
并不是每个步骤都是自动的。每个 label 都携带一个 type,决定行为:
在实践中
流程在自动步骤和人工验证步骤之间交替。Agent 生成 spec,但人类在生成计划之前先验证。Agent 生成计划和任务,但人类在实现运行之前批准。Agent 实现,但人类在部署之前 review PR。这种交替直接回答了任何资深工程师在批准这类系统之前会问的问题:如果 Agent 决策失误怎么办?答案是,它永远不会在没有人验证上一步的情况下独自决策到足以造成损害的程度。
一次自动执行的解剖
当应用了 auto 类型的 label 时,主 workflow 运行一个明确定义的序列:
- 读取 label 配置,解析阶段并导出必要的属性
- 确保 card 分支存在,以 idempotent 方式从 staging 创建
- checkout 到该分支
- 组合 Agent 的 prompt,将基础指令和该步骤的特定指令拼接
- 通过 GitHub Action 执行 Agent(Claude Code)
- 如果有文件变更,commit 并 push
- 如果步骤需要打开 PR,按惯例以 draft 形式打开
- 在 card 上评论摘要
- 自动推进 label,触发级联的下一阶段
失败时,锁定 card 并评论带错误信息的执行链接。
有一个细节在实践中带来了很大区别:某些步骤声明需要生成 artifact,即新的或修改过的文件。如果步骤运行了但仓库中没有发生任何变化,job 会故意失败,因为 Agent 可能会有 false positive——什么都没写但返回成功——这被视为静默失败。
让 pilot 卡住的那些错误
1. 由 Issue 事件触发的 Workflow 只能从 default 分支触发
如果你在 feature 分支上编辑 workflow,它不会触发。对流水线的所有 workflow 调整都需要到达主分支才能生效。这是"为什么不运行"的第一个原因。
2. 静默的假成功
如果没有在 Agent 执行时配置正确的权限模式,它会以受限权限运行:命令没有写入任何文件,但执行仍然返回成功。CI 绿灯、board 推进,但实际上什么都没做。防御措施是 artifact 要求机制:如果步骤应该生成文件但没有生成,job 会明确失败,而不是放行。
3. Agent 执行后 git 认证会断开
执行 Agent 的 action 会重新配置 runner 中 git 的认证方式,导致后续的 git push 因认证错误而失败。这里的修复方法是使用显式认证的 URL 来 push,而不是依赖 runner 的默认配置。
4. GitHub Actions 的默认 token 不会触发其他 workflow
这是 GitHub 有意设计的防循环保护。如果你用默认 token 更换 label,不会触发监听该事件的 workflow。实际上 board 停止同步,级联步骤直接中断,没有任何可见的错误。这种情况的解决方案是使用专用的 personal access token 来处理需要这种级联的操作。
5. 默认 token 也不能打开 Pull Request
这是 GitHub Actions 对仓库的直接限制:默认情况下,它没有创建或批准 PR 的权限。同样,通过专用 token 解决。
6. 用于列出或查看 PR 的 GraphQL 命令要求 token 没有的组织范围权限
解决方案是将这些特定命令换成等效的 REST 调用,后者要求更简单的权限范围。这些问题单独看都不复杂。放在一起,就形成了那种如果你不知道它们存在,就会在第一周就让一个有前景的 pilot 搁浅的摩擦。
安全:将 Issue 内容视为不可信数据
Issue 的正文和标题可供任何有仓库访问权限的人编辑,而这些内容会进入 Agent 的 prompt。从定义上来说,这就是 prompt injection 的攻击面。因此应用的缓解措施包括:
- 私有仓库,控制谁能应用 label
- 明确指示 Agent 将 card 内容视为数据,而不是命令:不进行交互式提问、不切换分支、不执行步骤范围之外的操作
- 每次执行时生成一个随机分隔符,避免通过 workflow 环境变量注入命令
这并不能完全消除风险。任何 label 理论上都可以触发一个有写权限的 job。对于当前内部使用的阶段,这个残留风险是有意识接受的。接下来 hardening 的一步是更细粒度的权限白名单,规定谁可以触发每个步骤。
运行了一个半月
这条流水线已经运行了一个半月,持续处理每周 4 到 6 个 spec。这里重要的是要说明,这不是概念验证,而是真实的、重复的使用。在写这篇文章的时候,有一个经验值得分享:一开始,spec 的范围写得太大。实际效果是 Agent 生成的实现往往不能 100% 覆盖指定的范围——范围太大,一次引导执行无法完整捕获。在这种情况下,纠正的方法不是修改流水线的引擎,而是修改流程的起点:更小、更精简的 spec,更好地界定范围。工作单元的粒度对 Agent 的重要性,就和人类团队在划分 sprint 时一样——这是我们仍在精炼的事情。
如果从头开始
好的,你问我:如果需要全部重做会怎样?
首先,我会从第一天就设计好 spec 粒度的验证,而不是在实践中发现大 spec 会产生不完整实现。以及,保持自动步骤和人工验证步骤交替进行的决策完全不变:正是这个选择使得在不放弃真正重要节点控制权的前提下信任流水线成为可能。
如果你正在考虑构建类似的东西,我最实用的建议是:从一开始就假设 Agent 会在并非成功的情况下返回成功,并为此构建你自己的安全网。如果有什么秘诀的话,那就是不要只依赖执行的退出码。