同名 format 保证字节级相同输出:一条被写进注释的契约

2026-08-09

在 agency-agents 这个仓库里,最容易被跳过、但信息密度最高的地方不是 README,是 tools.json 里那个叫 _note 的字段。它不是给程序读的,是仓库作者写给后来维护者的一段话。其中一句定下了整套转换体系的地基:同一个 format 名,保证渲染出字节级相同的输出

这句话听着像实现细节,但它直接决定三件事:你排查一个转换问题要查几份内容、你能不能只装某一个 agent、以及为什么有的工具装完像没装。这篇就把这条契约拆开讲,顺带把和它并列的另一个维度 installKind 说清楚——这两个字段经常被混着看,混了就会得出错误结论。

一、契约本身:format 管的是”渲染出什么内容”

先把 agency-agents 的链路摆正。它本质上是一批 markdown 文件:每个 agent 一个 .md,带 YAML frontmatter 加提示词正文,共 255 个 agent 分布在 17 个 division(2026-08-09 核对)。这批源文件本身不能直接给各家工具用,所以仓库自带两个脚本:scripts/convert.sh 把源 markdown 渲染成各工具认识的格式、写进 integrations/scripts/install.sh 再把 integrations/ 里的产物拷到每个工具对应的配置目录。

tools.json 就是这条链路的登记表,目前登记了 16 种工具。每一条至少要有 idlabelkebabformatinstallKinddest 这几个字段——缺任意一个,scripts/check-tools.sh 会让检查失败。

其中 format 的含义,就是 _note 定下的那条契约:它是一个渲染器的名字,两个工具能共用同一个 format,前提是它们渲染出来的文件完全一样。不是”差不多”,不是”结构类似”,是字节级相同。

16 种工具,但 format 名只有 14 个不同的值——因为有两处共用:

kebablabelformatinstallKind用户级目标路径
claude-codeClaude Codeidentityper-agent.claude/agents/{slug}.md
copilotGitHub Copilotidentityper-agent.copilot/agents/{slug}.md.github/agents/{slug}.md
osaurusOsaurusskill-mdper-agent.osaurus/skills/{slug}/SKILL.md
antigravityAntigravityskill-mdper-agent.gemini/config/skills/{slug}/SKILL.md

identity 这个名字顾名思义:原样输出,不做格式改写。也就是说 Claude Code 和 GitHub Copilot 拿到的,就是源 agent markdown 本身。skill-md 则是 Osaurus 和 Antigravity 共用的技能文件格式。

这里有个很关键的读法:这两行的 format 相同,dest 却完全不同。契约管的是”渲染出什么内容”,不管”拷到哪里去”。落点是 dest 的事,和 format 没关系。Copilot 那行更极端——一个 format、一个 agent,但用户级目标写了两个目录。

二、由此可以做的第一个判断:什么时候能合并同类项

这条契约的实用价值,在于它把”要查几份东西”这个问题变成了确定的。

假设你在 Claude Code 里发现某个 agent 的正文渲染得不对劲,你想知道 Copilot 那边是不是同一个毛病。按契约,两边 format 都是 identity,产出的文件内容按定义就是一样的,所以内容层面的问题只需要查一份——去看源 agent markdown 和 convert.shidentity 的处理,不用两边分别读一遍。

反过来,如果两边表现不一致,那么按契约推,问题就不在 format 渲染这一层,而在别处:可能是落点写错了目录(Copilot 有两个目标目录),可能是环境变量覆盖了路径(install.sh 注释里列了 CLAUDE_CONFIG_DIRCOPILOT_AGENT_DIR 等一串变量,它们在硬编码默认值之前被检查),也可能是工具自己读取规则的差异。这一步的价值是排除法:契约成立时,同 format 的内容差异不该存在,出现了就说明你找错层了。

skill-md 同理。Osaurus 和 Antigravity 的 SKILL.md 内容按契约一致,差别只在一个落在 .osaurus/skills/{slug}/、一个落在 .gemini/config/skills/{slug}/

三、format 与 installKind 是两个正交维度

_noteinstallKind 定义为安装机制,并且特意说明它是”上游真相,对每个消费方都成立”。它和 format 管的完全是两件事:format 管内容长什么样,installKind 管产物是几个、什么形态。

三种取值:

  • per-agent —— 每个 agent 渲染出一个文件或一个目录。装 255 个 agent 就是 255 份产物。16 种工具里绝大多数是这一类。
  • roster —— 所有 agent 合并成一个文件。Aider 的 CONVENTIONS.md(format 为 aider-conventions)和 Windsurf 的 .windsurfrules(format 为 windsurf-rules)属于这一类。
  • plugin —— 一个构建出来的产物,不能按 agent 渲染成字符串,在所有消费方都只能走 CLI。Hermes 是唯一一个,format 为 hermes-router-plugin,落点是 .hermes/plugins/agency-agents-router

判断依据在这里:如果你在找”为什么 Aider 里没法只装某一个 agent”,答案不在 format,在 installKind: roster——它的产物形态就不是一 agent 一文件,而是全员合并成一份。而 Hermes 的 plugin 意味着连 README 里那个官方 app 都装不了,只能走命令行。

所以碰到”我只想装两三个 agent”这类需求,正确的看法顺序是:先看 installKind(决定颗粒度能不能做到),再看 dest(决定装到哪),最后才轮到 format(决定内容长什么样)。反过来看就会得出”是不是格式不支持”这种错误结论。

四、契约保证内容一致,不保证”装完就生效”

这是最需要泼冷水的一条。字节级相同是一个关于渲染输出的承诺,它不承诺任何关于”这份文件在目标工具里会不会起作用”的事。

convert.sh 里 Cursor 的转换规则(cursor-mdc)就是现成例子。它渲染出的 .mdc 文件 frontmatter 是这样:

---
description: ${description}
globs: ""
alwaysApply: false
---
${body}

alwaysApply: falseglobs: ""写死在转换脚本里的。也就是说转换产物默认不会自动生效,需要在 Cursor 里按需引用。装完发现”没反应”,多半就是撞上了这一条,而不是转换出了错——从 format 契约的角度看,这份文件渲染得完全正确。

Codex 的 codex-toml 是另一种取舍。它每个 agent 写一个 TOML,只填最小必需的三个字段:

name = "..."
description = "..."
developer_instructions = "<整个正文>"

源码注释解释了为什么用 TOML 的 basic string:为了把源正文里的控制字符安全编码,避免产出非法 TOML。这同样是渲染层的正确性保证,和”Codex 会怎么用这份 TOML”是两回事。

opencode 的 opencode-md 则在 frontmatter 里多加了一个 mode: subagent,颜色值由 resolve_opencode_color() 把命名色解析成十六进制。三个 format 各自都在”字节级相同”的约束下工作,但对最终行为的影响天差地别。

五、这条契约靠什么守住

写在注释里的约定,如果没人执行,很快就会漂移。agency-agents 把”配置漂移”当成了明确的敌人,scripts/check-tools.sh 就是守 tools.json 这条线的:它要求 tools.json 必须与 install.shALL_TOOLSconvert.sh 的转换器集合保持一致,任何条目缺 id / label / kebab / format / installKind / dest 就失败。

同一套思路在 division 侧也有:check-divisions.sh 要求 division 列表与磁盘目录、convert.shlint-agents.sh 里的 AGENT_DIRS 数组、以及工作流的路径过滤器一致,不一致就让构建失败。check-agent-originality.sh 里还有个值得抄的做法——division 列表直接读 divisions.json,而不是在脚本里硬编码一份拷贝,源码注释写明了理由:硬编码的字面量会悄悄跟目录漂移,读文件则不可能。

对读者的直接推论是:要查某个工具当前的 formatinstallKind 或落点,回 tools.json 现查,不要从别的工具类推,也不要照抄本文的表格——本文只列了与共用 format 直接相关的那几行,其余行按上游更新而变。

六、契约管不到的三件事

_note 自己也划了边界,写作时要如实带出来:

第一,渲染器覆盖面是消费方自己的事,由 format 推导。tools.json 只登记”这个工具用哪个渲染器”,至于某个消费方(比如 README 里那个官方 app,或某条 CLI)实际实现了哪些渲染器,是那一侧的问题。

第二,目录本身不携带任何 app 发布状态。 tools.json 里有一条,不等于任何一个 app 已经把它做出来了。看到某个工具在表里,只能说明这份登记表里有它。

第三,格式正确不等于内容好用。 仓库的另一道关 lint-agents.sh 检查的是结构:frontmatter 必须存在且含 name / description / color 三个必填字段(缺了报 ERROR),推荐章节 Identity / Core Mission / Critical Rules 缺失只报 WARN 不会失败,正文词数少于 50 也只是 WARN。check-agent-originality.sh 检查的是重复度:用实体中性化后的 8 词 shingle 重叠率打分,默认 ORIGINALITY_FAIL 为 40、ORIGINALITY_WARN 为 20(均可用环境变量覆盖),源码注释给出的库内校准结论是同一对之间最差相似度约 1.5%、中位数 0%,所以双位数就是强异常。这两道关加起来管的仍然是结构与重复度,不评估提示词写得好不好用。

七、动手之前先看一眼计划

如果你要基于以上判断真的去装,install.sh 有两个开关值得先用:

./scripts/install.sh --list tools
./scripts/install.sh --tool claude-code --division engineering --dry-run

--list [tools|teams|agents] 是列出后退出;--dry-run 只打印计划、不写任何东西。往 ~/.claude/agents/ 这类共享目录一次性写两百多个文件之前,先用 --dry-run 看一眼计划,是这个仓库对新手最有价值的一个开关。想收窄范围就用 --tool / --division / --agent / --agents-file 这几个选择器组合,全空即全装。(以上为按官方参数语义组合的示例,未逐项实测,以官方文档与 ./scripts/install.sh --help 的实际输出为准。)

还有一个容易踩的坑和 dest 有关:install.sh 的注释里,opencode、cursor、aider、windsurf 这四个写的是当前目录,其余多为家目录。所以执行命令前先确认你人在哪个目录——这决定了你是给某个项目装还是给整个账户装。平台方面脚本注释写的是 Linux、macOS(需要 bash 3.2 及以上)、Windows 的 Git Bash 或 WSL;Windows 用户不要在 PowerShell 或 cmd 里直接跑这个 .sh

回到开头那句话。format 的字节级相同契约,本身只是一行注释,但它把”内容”和”落点”、“内容”和”形态”这两组容易混淆的东西彻底切开了。看懂它之后,遇到问题你至少知道该往哪一层找——这比记住 16 个工具的路径有用得多。

延伸阅读


本文依据 agency-agents 官方仓库(github.com/msitarzewski/agency-agents)的 README、 tools.jsondivisions.jsonscripts/ 下的安装与校验脚本整理,核对日 2026-08-09。 本文内容为仓库源码与文档口径,我们没有安装或运行过其中任何 agent。 目录、脚本与安装路径随上游更新而变动,请以仓库最新内容与 ./scripts/install.sh --help 的实际输出为准。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。