从Prompt到可执行规格:用GitHub Spec Kit构建需求契约 最近我在整理一个内部服务的技术方案时被一件事卡了很久需求评审会上大家对着 Prompt 说“挺好的很具体”可真要进入开发阶段后端、前端、测试拿到的却是同一段自然语言每个人理解出来的接口参数、异常分支、返回结构完全对不上。后来我把一套“Prompt 到可执行规格”的流程引入团队核心工具就是 GitHub Spec Kit。它不是那种炫酷的“一句话生成整个系统”的代码生成器而是把人类语言描述转成机器能解析、能校验、能进 CI 的规格文件。这篇文章我想把整个思路、实际步骤和踩过的坑都记下来给正在为“需求到开发”衔接发愁的朋友做个参考。1. 为什么需要“可执行的规格”1.1 Prompt 不是交付物很多人现在习惯用 Prompt 写需求确实比一大段 Word 文档高效。但 Prompt 本质上是自然语言自然语言有天然的优势表达灵活、门槛低、人人都能参与。可一旦进入工程化协作它的缺点就非常明显歧义、上下文缺失、无法自动校验。举个例子你说“用户注册后要发通知”。这里的“通知”指什么是站内信、邮件还是短信发送失败要不要回滚注册事务重试几次这些细节在 Prompt 里往往是模糊的。开发同学只能自己猜猜错了返工猜对了算运气。Prompt 适合用来“讨论需求”但从来不是“交付物”。一个可交付的需求必须能被测试用例覆盖能被代码实现能在 Review 时逐行确认。而这就是规格Spec要做的事情。1.2 规格为什么必须“可执行”传统规格文档最常见的问题是停留在人类阅读层面。写了 50 页接口文档最后测试同学还得手动核对字段类型、必填项、取值范围前端同学还得对着文档手写 TypeScript 类型后端同学还要在代码里重新维护一遍校验逻辑。文档和代码一旦出现偏差很难及时发现。可执行规格简单来说就是一种“既能给人看也能给机器跑”的中间产物。常见的形态包括 OpenAPI接口描述、JSON Schema数据结构描述、Gherkin行为用例描述等。它们有明确的语法规则可以被程序解析也能直接挂到 CI 里做契约测试。GitHub Spec Kit 做的事情就是把你的 Prompt 翻译成这类可执行规格。你可以把它理解成一个“需求翻译官”输入是自然语言输出是结构化的 YAML、JSON、测试骨架。1.3 解决的痛点从口头共识到代码契约我在团队里推这套工具之前最大的痛点是“口头共识”。开会时大家对着白板说“这个接口返回用户信息和订单列表”每个人都点头但没人知道订单列表是嵌套在用户对象里还是平铺在顶层接口是返回单个对象还是数组登录态怎么传Spec Kit 逼着你在 Prompt 阶段把话说完整。因为如果 Prompt 描述不完整生成的规格文件就不会完整。当你看到自己写的 Prompt 被翻译成一份 OpenAPI 时那种“原来我说得这么模糊”的感觉会特别强烈。这个过程本身就能帮助团队提高需求表达质量。2. 认识 GitHub Spec Kit 的核心设计2.1 输入输出都很“GitHub 原生”Spec Kit 是围绕 GitHub 生态设计的输入一般是 Markdown 文件输出是 YAML 或 JSON两者都是纯文本天然适配 Git 的 diff 和 Code Review 流程。这一点非常重要。以往很多建模工具会把结果存在私有数据库或专用 GUI 工程里团队成员要查看规格还得装一个客户端。而在 Spec Kit 的工作流里规格文件就是一个普通仓库里的普通文件。你可以提交 PR在 PR 里看到这次需求变更影响了哪些接口定义、哪些字段被改成了必填、哪些状态码新增了。Reviewer 可以直接在规格文件的 diff 上留 comment。这套流程的核心理念是规格也是代码也应该纳入版本管理。2.2 生成的是分层规格不是单个文件Spec Kit 不是简单地把 Prompt 改写成一段 YAML而是生成一组相互关联的规格文件。我理解它的输出主要分四层第一层是接口层使用 OpenAPI 描述路径、方法、参数、响应状态码。这一层解决“有哪些接口、怎么调用”的问题。第二层是数据层使用 JSON Schema 描述对象结构、字段类型、必填项、枚举范围、正则约束。这一层解决“数据长什么样、怎样算合法”的问题。第三层是行为层使用 Gherkin 一类的 Given-When-Then 用例描述业务规则和异常分支。这一层解决“系统在什么条件下应该做什么事”的问题。第四层是约束层可以自定义一些规则文件比如“所有密码字段不得出现在响应里”“所有时间字段必须使用 UTC”等生成后会嵌入到校验脚本中。这四层结合基本覆盖了一个模块从接口定义到验收测试的完整链路。2.3 为什么值得选择“规格先行”有人会问先写规格多一道工序不如直接从 Prompt 生成代码来得爽。我一开始也这么觉得直到试了一次从 Prompt 直接生成代码的项目才发现问题非常大生成的代码虽然是能跑的但它缺少契约。后端的 Controller 和前端的数据模型可能是两套逻辑两边各自生成了不同的字段名。代码生成得越快返工的风险反而越高。Spec Kit 走的是另一个路线先强制生成规格规格经过 Review 确认后再基于规格去写实现。这样前后端看到的是同一份契约。你可以先把规格文件提交到仓库然后后端按 OpenAPI 实现接口前端按 JSON Schema 生成类型定义测试按行为用例写自动化用例。每个人都对着同一份规格干活自然不容易跑偏。3. 实操从一个真实需求开始3.1 初始化项目以我用的这个版本为例安装方式很简单npm install -g github/spec-kit安装完成后创建一个空目录并初始化mkdir spec-demo cd spec-demo spec-kit init --project user-service初始化命令会生成一个默认的目录结构. ├── specs │ ├── openapi │ ├── schemas │ └── behaviors ├── prompts │ └── templates └── spec-kit.config.ymlprompts目录放原始需求描述和模板specs目录放生成的规格文件spec-kit.config.yml是项目级配置。我第一次看到这个目录就觉得很舒服它把“需求原文”和“规格产物”放在同一个仓库里方便溯源。3.2 编写第一条 PromptSpec Kit 对 Prompt 的格式没有魔法要求就是你平时写需求描述的方式但描述越结构化生成的规格越准确。我在一个示例项目里写了这样的 Prompt创建一个用户注册接口 - 请求方式POST /users - 请求体字段 - email字符串必填必须符合邮箱格式 - password字符串必填长度至少 8 位且必须包含数字 - nickname字符串可选长度 2-20 个字符 - 注册成功后返回 HTTP 201 - 响应体包含 id、email、nickname、created_at - created_at 使用 ISO8601 格式时区为 UTC - 如果 email 已存在返回 409 错误 - 注册成功后需要发送一封欢迎邮件如果邮件服务不可用不能影响注册接口的响应注意我在这里使用了“必须”“可选”“如果……返回……”这类带约束性的表达后面生成出来的规格就会在这些地方体现得非常明确。3.3 生成规格文件执行生成命令spec-kit generate ./prompts/register.md --out ./specs命令结束之后specs/openapi目录下会出现一个register.openapi.yaml。我截取其中关键部分给你看openapi: 3.0.3 info: title: User Register version: 1.0.0 paths: /users: post: summary: 用户注册 requestBody: required: true content: application/json: schema: $ref: #/components/schemas/RegisterRequest responses: 201: description: 注册成功 content: application/json: schema: $ref: #/components/schemas/RegisterResponse 409: description: 邮箱已存在同时specs/schemas下会生成对应的 JSON Schema{ type: object, properties: { email: { type: string, format: email }, password: { type: string, minLength: 8, pattern: .*[0-9].* }, nickname: { type: string, minLength: 2, maxLength: 20 } }, required: [email, password] }specs/behaviors下还会有对应的行为用例Scenario: 注册成功 Given 用户请求 POST /users And 请求体包含合法的 email 和 password When 接口执行成功 Then 返回状态码 201 And 响应体包含 created_at 字段且格式为 ISO8601 Scenario: 邮箱已存在 Given 用户请求 POST /users And email 在系统中已存在 When 接口执行 Then 返回状态码 409看到这儿你会发现Spec Kit 把一句“创建用户注册接口”扩展成了四个层次的规格产物。这些文件可以直接交付给后端、前端、测试每个人都拿到了一份结构化的契约。3.4 把规格纳入版本管理和 CI有了规格文件下一步就是让它参与协作。我的习惯是开一个新分支把所有生成的文件提交然后发起 PR。在 PR 的 Review 界面里能看到某一行从minLength: 6改成了minLength: 8也能看到新增了一个 409 响应分支。这种 diff 级的 Review比看十几个聊天记录里“这个字段改一下”要清晰得多。更进一步的用法是把它接入 CI。比如在 GitHub Actions 里跑一个校验任务name: spec-check on: [pull_request] jobs: validate: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - run: npm install -g github/spec-kit - run: spec-kit validate ./specsspec-kit validate会检查生成的 OpenAPI 和 JSON Schema 语法是否合法、引用是否存在、行为用例的步骤是否完整。如果 Prompt 改了但规格没同步CI 就会红逼着团队保持需求和规格的一致。4. 关键配置与高级用法4.1 不同框架适配Spec Kit 支持多种实现框架的适配参数。在生成命令里可以加--frameworkspec-kit generate ./prompts/register.md --out ./specs --framework spring指定框架之后生成的规格文件会带上对应的框架注释和代码片段方便后端直接复制。比如使用spring生成的时候OpenAPI 文件的每个操作下面会多一个x-spring-controller扩展字段用来说明 Controller 类名和方法名使用fastapi时则会在 Schema 层生成 Python 的 type hint。我建议第一次使用时不要指定框架先看纯规格是否满足需求再加入框架适配。因为框架适配本质上是把实现细节提前注入到契约里如果团队技术栈还不稳定过早绑定会让规格文件的可移植性变差。4.2 用规则文件控制生成细节实际项目里光靠 Prompt 里的自然语言约束还不够。比如你希望所有接口的响应都包含统一的request_id字段总不能在每个 Prompt 里都写一遍。Spec Kit 提供了规则文件在spec-kit.config.yml里指定rules: - file: ./rules/basic.ymlbasic.yml可以这样写response: required_fields: - request_id field_type: request_id: string forbid_fields: - password - token生成规格时Spec Kit 会自动把这些规则合并进 JSON Schema并且如果有字段冲突会在终端输出 warning。我遇到过好多次就是因为规则文件里写了forbid_fields: password结果 Prompt 里把密码放进了响应体直接被拦下来。4.3 检测 Prompt 中的约束冲突自然语言最容易出现前后矛盾。比如在 Prompt 开头写了“created_at 使用 ISO8601 格式”后面又写“返回 created_at: 2024-01-01”这就是冲突。Spec Kit 有一个 conflict detection 模式spec-kit generate ./prompts/register.md --out ./specs --detect-conflicts它会将抽取出的约束做逻辑比对。比如一个是format: date-time一个是example: 2024-01-01后者只能匹配date那么就会输出类似这样的提示[conflict] created_at: schema format is date-time, but example value 2024-01-01 does not include time part这个功能很实用。以前人肉 Review 文档时很容易漏掉这种细节现在机器帮我们在生成阶段就发现了。4.4 Prompt 模板复用如果你经常写同一类需求可以把稳定的部分做成模板。prompts/templates目录下可以放一个base.user.md所有接口必须遵守以下约定 - 响应时间字段统一命名为 created_at、updated_atUTC 时间 - 列表接口必须支持分页参数 page 和 page_size - 所有错误响应必须包含 code 和 message 字段然后在正式 Prompt 开头写一句请基于 base.user.md 模板生成本次规格。Spec Kit 会把模板内容和当前 Prompt 合并相当于在需求描述阶段就应用了团队规范。这比让每个人背诵规范再手写进 Prompt 可靠得多。5. 常见问题与排查技巧5.1 生成的规格太泛缺少细节最常见的反馈是同样一段描述别人写的能生成详细的 openapi我怎么生成出来就只有一个空壳原因几乎都是 Prompt 本身缺少约束。Spec Kit 是“按需取义”它不会凭空猜测你没说的规则。你说“用户登录”它就生成一个最简的 POST /login你说“用户登录用户名或手机号都可作为账号密码错误连续五次锁定账号”它才知道要生成两个字段的枚举、一个锁定状态、一个重试次数上限。我的建议是写 Prompt 时逼自己做三件事明确动作与路径比如 “POST /users/login”明确关键字段的格式和边界比如 “password 长度 6-20 位且必须包含字母和数字”明确异常分支比如 “账号锁定后返回 423并且响应体带 lock_time 字段”如果你确实不知道某个模块该怎么描述可以先用 Spec Kit 内置的示例 Prompt 练习看看它生成的规格长什么样反向学会怎么提需求。5.2 规格与实现代码不一致规格文件生成之后如果实现代码没有跟进很快规格就会和现实脱节。 Spec Kit 提供了一个验证命令可以把实现代码和规格做比对spec-kit verify ./specs --source ./src它会扫描后端代码中的路由定义和 OpenAPI 中的路径进行匹配扫描前端的 TypeScript 类型定义和 JSON Schema 中的字段做对照。一旦发现路径缺失、字段类型不一致、时间格式不对就会返回错误列表。我第一次跑这个命令时发现有两个接口的路径写错了一个多了个/v1前缀一个少了末尾的/。这种问题在测试阶段容易被忽略但规格验证阶段就能直接暴露。5.3 Prompt 中不小心带上敏感信息这是我在团队推行时非常警惕的问题。Spec Kit 会调用外部的语言模型做解析和生成如果 Prompt 里包含数据库连接串、内部 IP、生产环境的 Key那等于把敏感信息发送给了第三方。好一点的版本会提供--scan-secrets参数在发送前检查本地 Prompt 是否有疑似密钥的模式比如sk-开头、password结尾、IP 地址、私钥块等。建议在任何团队协作中都开启这个参数并且写进 CIspec-kit generate ./prompts/register.md --out ./specs --scan-secrets更重要的是在源头上避免不要把真实环境变量放到 Prompt 里用环境变量占位符代替。5.4 大仓库性能问题与增量生成当你的prompts目录里有几百个 Markdown 文件时每次全量生成会非常慢。我遇到过的最极端情况是 200 多个接口描述跑了十几分钟CI 直接超时。解决方案是启用增量模式spec-kit generate ./prompts --out ./specs --incremental增量模式下Spec Kit 会比较源文件和输出文件的修改时间只处理发生变化的 Prompt。如果是第一次接入存量仓库建议先把所有 Prompt 批量生成一次之后再打开增量模式平时的提交速度就会赶到秒级。5.5 多团队协作时的命名冲突多个团队共用一个仓库时很容易出现两个模块都叫User然后生成的 Schema 在合并的时候互相覆盖。如果想避免这种问题可以在配置里开启命名空间specification: prefix: true这样会默认在生成的规格文件名前加上项目名前缀比如user-service_register.openapi.yaml。虽然文件名长了一点但避免了文件相互覆盖也让 CI 的错误定位更准确。5.6 版本升级带来的生成结果漂移Spec Kit 更新频率不算低如果团队里有人升级了全局工具版本生成的规格可能跟你之前跑出来的 diff 很大。我建议在项目根目录固化版本最简单的方式是在package.json里把工具作为 devDependency 保存而不是全局安装。npm install --save-dev github/spec-kit0.4.2然后在 CI 和本地都用npx spec-kit保证所有人跑的是同一个版本。这样能最大程度避免“我本地生成的规格跟 CI 不一样”这种尴尬问题。我在实际使用中最大的体会是工具本身并不复杂复杂的是让团队接受“需求需要被结构化”。Spec Kit 提供了一个很好的“翻译层”把日常的 Prompt 变成工程化的规格但它不会替你把需求想清楚。如果你写的 Prompt 模糊出来的规格一定也模糊只是模糊得看起来更正式而已。所以我的建议是先用小模块试水把一两个接口从 Prompt 到规格、到 CI 校验完整跑通再逐步扩大范围。等团队成员都习惯了“先写清楚约束再进入开发”这套节奏你会发现返工真的少了很多。最后再分享一个小技巧规格文件也一定要参加 Code Review而且最好每次由不同岗位的人来看——后端关注接口可不可实现前端关注字段够不够用测试关注异常分支全不全。三个视角一对齐需求里的坑基本就在评审阶段被填平了。