2026年8月20日 1 分钟阅读

从一份 YAML 规格到多套 AI 编码规则:用 NAEOS 把架构约束前置到生成之前

tinyash 0 条评论

AI 编码 Agent 最常见的失控方式,并不是不会写函数,而是每个会话都要重新猜一遍:模块边界在哪里、服务如何依赖、哪些端口已被占用、这次改动是否破坏既有架构。把这些信息散落在 README、口头约定和若干提示词里,短期很轻便;当仓库跨语言、多人维护、同时使用 Claude Code、Cursor 与 Codex 时,约束会逐渐漂移。

NAEOS 是一个用 Go 实现、采用 Apache-2.0 许可证的规格驱动工程平台。它的切入点不是再包装一个聊天窗口,而是把 YAML/JSON 规格解析、规范化并构造成内部工程模型,再执行校验、调度、生成和面向 AI 工具的上下文编译。对团队而言,重点是让规格成为可审查、可版本控制的输入,而不是让每个 Agent 各自维护一份“项目理解”。

为什么先写规格,而不是先写 Prompt

Prompt 适合表达一次任务,但不擅长保存结构性事实。例如,“认证模块不应依赖 HTTP 网关”“服务端口不得重复”“API 依赖 auth 模块”这些规则应当被机器验证,而不是依靠下一次对话仍然记得。NAEOS README 描述的主流水线依次包括 Parser、Normalizer、Resolver、统一工程模型 NEIR、Validator、基于 DAG 的 Scheduler 与 Generator。

这条链路有两个实际价值。第一,变量引用、跨文件引用和模块关系先在规格阶段处理,Agent 接收到的是更稳定的上下文。第二,校验失败会在生成代码或交给 Agent 之前暴露;例如循环依赖、端口冲突和模块边界问题,不必等到代码评审才发现。

它也不意味着 YAML 取代设计讨论。规格只能表达团队已经做出的决定;领域模型、异常策略和接口取舍仍需要人来判断。更稳妥的用法是把高频、可检查的结构性决策沉淀进去,而不是试图把所有业务语义塞成一份巨型配置。

一个最小可运行的起点

官方 Quick Start 展示了从源码构建 CLI、再以 spec.yaml 驱动流程的方式。下面的例子沿用其公开字段结构,刻意保持很小:一个认证模块、一个 HTTP 网关,以及网关对认证模块的依赖。

project: checkout-service
modules:
  - name: auth
    path: ./auth
  - name: gateway
    path: ./gateway
    dependencies: [auth]
services:
  - name: gateway
    kind: http
    port: 8080
architecture:
  pattern: hexagonal
generation:
  languages: [go, typescript]

先克隆并构建,然后初始化工作区、验证规格,再运行完整流水线:

git clone https://github.com/NAEOS-foundation/naeos.git
cd naeos
go build ./cmd/naeos/
naeos init
naeos validate --input-file spec.yaml
naeos run --input-file spec.yaml

这里先执行 validate 是有意的。生成器的输出再漂亮,也不应掩盖输入定义中的冲突。把这一步放进 CI 后,架构约束能够像单元测试一样在合并前被检查。对于复杂仓库,还可以用 naeos diff 比较规格变化;这比从生成后的大量代码差异中反推架构变化更直接。

如何把同一份模型交给不同 Agent

多 Agent 协作的麻烦之一,是工具各自有不同的规则文件约定。NAEOS 的 AI 编译器将 NEIR 转换为不同目标的指令文件:README 列出的目标包括 GitHub Copilot 的 .github/copilot-instructions.md、Claude Code 的 CLAUDE.md、Cursor 的 .cursorrules、Gemini CLI 的 .gemini/CONFIG.md,以及 Codex 和 OpenCode 使用的 AGENTS.md

官方示例使用下面的命令,把规格编译为 OpenCode 目标的 AI 上下文:

naeos context --input-file spec.yaml
naeos ai compile --input-file spec.yaml --target opencode

这并不保证所有 Agent 的行为完全一致。模型能力、工具权限和提示词层仍然不同;生成的规则文件更像共同的“工程宪法”。实践中应把这些生成物纳入检查:确认目标文件是否被正确写入、是否覆盖了团队手写规则、Agent 是否真的在对应仓库根目录读取它。若已有人工维护的 AGENTS.md,先在分支中比较内容,而不要直接覆盖主分支文件。

把规格校验接入日常交付

真正让规格发挥作用的,不是偶尔手动运行一次,而是让它进入提交后的固定反馈环。一个可行的顺序是:开发者先修改 spec.yaml 与代码;CI 先调用 naeos validate --input-file spec.yaml;通过后才运行语言自身的测试、静态检查和构建。这样,规格检查负责结构性不变量,测试负责行为正确性,二者不互相替代。

发生需求变更时,建议先审查规格 diff,再审查实现 diff。比如新增一个服务,不只要看新增了哪些 handler,还应确认其端口、依赖方向和架构模式是否被明确写入。对于临时实验,可以在独立规格文件中试验,避免把未经确认的结构混进主规格。NAEOS README 还列出 watchmigratedocgentest 等命令;这些能力可以按团队成熟度逐步引入,没必要一次把整条工具链都设成发布前门槛。

在多人协作中,规则生成也应是可复现的。建议固定 NAEOS 的版本或在升级时单独提交生成物变化,并在 CI 中检查工作区是否因编译而产生未提交的规则文件。否则,不同成员使用不同版本编译,可能在没有业务改动时得到不同的 CLAUDE.mdAGENTS.md。如果团队不希望将生成物提交到仓库,也应明确由哪一个流水线步骤生成,以及 Agent 实际运行的目录能否拿到这些文件。

另一个容易忽略的边界是“规格的粒度”。太粗的规格只能重复 README,太细的规格会把实现细节锁死,反而增加维护成本。通常优先描述模块、服务、依赖、公开接口、部署约束和安全边界;函数内部的局部实现仍交给代码、测试和评审。每次架构讨论结束后,只把已经稳定且能被验证的结论写入规格,能避免配置成为另一种噪声。

对现有项目的迁移,也不必从全仓库建模开始。可以挑选一个变更频繁、依赖关系容易出错的服务作为试点,先只建模块、服务和端口三类事实;让 validate 在一两个迭代中发现真实问题,再决定是否补充更复杂的引用、策略或生成配置。评估时关注的不是生成了多少文件,而是架构变更是否更早被发现、不同 Agent 的规则是否更一致、审查者能否更快理解依赖变动。若这三项没有改善,就应缩减规格范围或调整工作流,而不是继续增加字段。

规格语言的边界与维护策略

README 列出的规格语言 v2 支持 ${var} 变量插值、$env{VAR} 环境变量解析、$ref{path} 跨引用与 $include{file} 多文件组合。多文件组合尤其适合把稳定的基础架构与环境差异分开:共享模块和依赖放在基础规格,开发/测试环境再通过独立文件补充端口或部署参数。密钥本身不要写进规格;即使支持环境变量解析,也应只引用变量名,并通过 CI 的密钥管理注入值。

避免两种反模式。其一是把生成结果当作唯一真相:生成代码仍应经过测试、评审和安全检查。其二是让规格与实现脱节:服务拆分或依赖改变时,只改代码不改规格,最终会把“可验证上下文”变成过期文档。解决方式不是增加更多说明文字,而是把规格变更和代码变更绑定到同一个 PR,并为 naeos validate 设置明确的失败门槛。

NAEOS 还提供 MCP Server、上下文包、文档生成、迁移和 watch 等能力;是否采用应以团队的规格维护成本为准。若项目模块少、生命周期短,一份简洁的 AGENTS.md 可能足够。若同一系统需要持续面对多语言生成、多种 Agent 与架构审查,先把不变量写成可验证规格,通常比不断补 Prompt 更能降低协作噪声。

最后,规格文件本身也需要像代码一样被审查:名称是否清晰、依赖是否必要、环境变量是否只引用不泄露值、生成目标是否符合仓库实际工具。把这些问题放在变更发生时处理,能把 Agent 协作从“反复解释上下文”转为“共同遵守可检查的约束”。

相关链接

发表评论

你的邮箱地址不会被公开,带 * 的为必填项。