别让 Agent 把需求写成一团散文:Yamlet 用可验证规格把实现前的分歧提前暴露
让编码 Agent 接手一个功能时,最昂贵的返工往往不是它把某行代码写错了,而是它从一段看似完整的自然语言里选中了错误的解释。比如“上传文件后发送邮件”这句话,究竟要限制格式吗?失败后是否重试?收件人从哪里来?这些边界若没有落到可检查的契约中,Agent 很容易快速产出一大段能运行、却不符合真实意图的代码;人工审查也只能在改动已经扩散后再逐项猜测。
Yamlet 是一个面向 Agent 的轻量规格工具。它不是让团队再维护一套庞大的流程平台,而是把一个组件的契约、需求和验收条件放进单个 .yamlet.yaml 文件,再由 CLI 负责写入、编号和校验。项目采用 MIT 许可证,提供 macOS 与 Linux 的独立二进制,也提供 Claude Code 插件。它的核心价值不在“再发明一种 YAML”,而在于把模糊需求变成可机械验证、可投影为测试的中间产物。
为什么提示词不该同时承担规格书职责
提示词很适合表达方向,却不擅长维护长期不变的边界。一次对话中,Agent 能理解“只允许 TLS SMTP”;下一次它可能又把它解释为“优先 TLS,必要时回退”。当需求、约束和实现建议混在同一段文本里,审查者还得先区分哪些是不可违反的条件,哪些只是例子。
Yamlet 的做法是给每个组件一份最小但有结构的描述。顶层必须包含系统标识、主题、摘要、描述、影响范围、对外面,以及非空的需求列表。若该组件需要声明接口,可以用 exposes 描述名称、意图、输入和可选输出;如果它是组合组件,则以 components 列出子规格,再用 connections 显式连接输入输出。
这带来一个很实际的好处:接口引用可以被双向检查。规格里写了 {input.recipient},就应当能在契约输入中找到它;反过来,声明了却从未使用的输入或输出也会被发现。对于由多个 Agent 分别实现的模块,这比在聊天记录里追踪“那个字段是不是后来改名了”可靠得多。
用 EARS 模式压缩歧义,而不是堆更多形容词
Yamlet 的验收条件采用 EARS 风格。每条需求有 RQ-N 标识、描述和非空的验收条件列表;每条条件有 AC-N 标识、模式、必要子句及 shall 断言。不同模式要求不同的触发语义:常驻规则使用 ubiquitous,状态约束使用 state 加 while,事件触发使用 event 加 when,异常路径则可用 unwanted 加 if。
例如,邮件投递组件的目标不必写成“尽可能安全、稳定地发送邮件”。更可审查的表达是:在传输通道不满足 TLS 时,组件不得提交邮件;当有效投递请求到达时,组件应使用已声明的 SMTP 端点提交消息。前者把“安全”和“稳定”留给读者猜,后者给出了条件、动作和可验证的结果。
Yamlet 还要求带占位符的条件附带示例表,并让每一行绑定所有占位符。这一点很重要。若规格写着“对每个 {n} 重试”,却没有说明 {n} 是次数、收件人还是延迟,Agent 仍然会有多个合理但不一致的实现。把示例与占位符绑定,能在生成代码前先消灭这一类空洞的精确感。
一个适合现有仓库的落地流程
Yamlet 的定位不是取代业务代码仓库,而是为“准备让 Agent 动手”的阶段增加一道轻量闸门。官方 README 给出的安装方式如下;Homebrew 6 及以上版本需要先信任第三方 tap,才能避免安装过程中等待交互确认。
brew tap RicardoMonteiroSimoes/yamlet brew trust --tap RicardoMonteiroSimoes/yamlet brew install yamlet yamlet --version
安装之后,建议先选一个边界清楚的小功能,而不是把整个系统一次性翻译成规格。例如为“上传 PDF 后归档并邮件通知”建立组件规格:先明确对外输入、输出和影响范围;再把传输安全、格式限制、失败行为拆成独立需求;最后为每条需求写出事件或状态条件。不要让 Agent 直接手工编辑 YAML 和猜编号——Yamlet 的设计是由 CLI 负责序列化与 ID,这能避免多人或多 Agent 修改时出现编号冲突和格式漂移。
完成草稿后,优先运行验证,再让编码 Agent 读取已通过的规格。README 所列的 yamlet verify 会按照规则目录检查规格;yamlet tests 则把验收条件投影为 Gherkin .feature 文件。后者生成的是特征文件,不会替你虚构 step definitions,因此它不是“按一下就有测试覆盖率”,而是把验收语言与测试骨架同步,留下由项目本身实现步骤的明确边界。
yamlet verify specs/email_service.yamlet.yaml yamlet tests specs
这两步适合放进 PR 检查:第一步阻止不合法或引用不完整的规格进入主线,第二步让测试目录随通过的规格重新生成。需要注意,生成特征文件并不等价于业务行为已被验证;它只能保证“被写下的验收条件”进入了后续测试流程。接口集成、真实 SMTP 行为和权限策略仍需由项目测试与环境验证覆盖。
组合规格让依赖关系也能被审查
单组件规格解决“这个模块应当做什么”,组合规格则解决“模块之间怎样连”。在 components 中为成员规格起别名并给出路径后,connections 使用“接收端:来源端”的形式描述连线。Yamlet 会解析成员的 exposes,校验端点、方向和完整性;每个成员输入都应被接线,组合层自身的需求则保留给无法由一条连线表达的整体约束。
这特别适合 Agent 容易跨边界修改的场景。假设上传模块输出 pdf_file,邮件模块接受附件输入,组合规格显式写出二者关系后,Agent 不该再靠猜测在两个实现间临时增加隐式字段。遇到架构讨论时,还可以用 yamlet graph 输出结构图:默认格式为 Graphviz DOT,也支持稳定的 JSON 图模型,便于将规格结构交给可视化或审查工具。
yamlet graph specs/pdf_archiver.yamlet.yaml | dot -Tsvg > diagram.svg yamlet graph specs --format=json
第一条命令中的 dot 属于 Graphviz,而不是 Yamlet 自带二进制;在 CI 中使用前应显式安装该依赖。对于目录级图,项目建议把有关联的规格放在同一目录,或对单个根规格使用递归展开,避免图只显示局部而误以为依赖已经完整。
Claude Code 插件不是自动正确性开关
Yamlet 还打包了 yamlet-skills Claude Code 插件,包含作者引导、契约挑战、验收条件挑战、验证和测试投影等技能。安装入口是官方仓库,而不是任意第三方 marketplace:
/plugin marketplace add RicardoMonteiroSimoes/Yamlet /plugin install yamlet-skills@yamlet
其中两个“挑战”步骤值得保留。项目的工作流把契约初始化前和每项需求提交前视为单向节点:一旦写入,后续不再将其作为可随意编辑的草稿。因此,先让独立审查角色提出阻塞问题,再由人决定如何裁决,通常比让同一个 Agent 一边写规格一边宣布“已经充分考虑”更稳健。
不过,任何规格工具都无法替团队替代决策。Yamlet 能检查字段、引用、模式和结构,不能判断“重试三次是否符合业务成本”,也不能从不存在的产品规则中推出数据保留期限。它最适合的使用方式是:人负责选择不可妥协的边界,Agent 协助把边界结构化,工具负责把结构错误尽早拦下。
何时值得引入,何时不必强上
如果任务只有一次性脚本、接口稳定且需求已经由严格类型或现成测试表达,额外维护规格文件未必划算。相反,下面几种情形很适合先试用 Yamlet:多个 Agent 在同一功能上接力;需求经常被口头补充;模块间接口容易漂移;团队希望把“验收语言”系统性地投影为测试骨架。
起步时别追求覆盖全仓库。挑一个过去经常返工的流程,用一份组件规格写清接口与两三条关键验收条件,先让 yamlet verify 成为实现前的固定检查。只要它能让一次“Agent 做得很快但方向错了”的返工提前暴露,规格文件的维护成本就有了明确回报。
相关链接