Agent 定义的多工具格式转换
Agent 定义的多工具格式转换
学习目标
读完本章后,你将能够:
- 理解统一 Agent 定义格式(Markdown + YAML frontmatter)与各工具特定格式的映射关系
- 设计跨工具的格式转换管道
- 处理 YAML frontmatter 解析的边界情况
- 在自己的项目中实现多工具兼容
前置知识
- YAML frontmatter 语法
- Bash 脚本基础
核心概念
统一源格式的设计动机
AI 编码助手市场高度碎片化——Claude Code、Cursor、Aider、Gemini CLI 等工具各自有不同的 Agent/Skill/Rule 文件格式。如果为每个工具维护独立的源文件,会导致:
- 更新不同步:修改一个 Agent 的能力需要同步到 N 个文件
- 版本混乱:不同工具的 Agent 定义可能出现功能差异
- 维护成本高:N 个工具 × M 个 Agent = N×M 个文件
解决方案:维护一份统一的源文件(Markdown + YAML frontmatter),通过转换管道生成各工具特定格式。
转换管道架构
YAML Frontmatter 解析模式
转换器的核心是从源文件中提取元数据和正文。纯 Bash 实现使用 awk 状态机:
# 提取 frontmatter 中的单个字段get_field() { local field="$1" file="$2" awk -v f="$field" ' /^---$/ { fm++; next } fm == 1 && $0 ~ "^" f ": " { sub("^" f ": ", ""); print; exit } ' "$file"}
# 提取正文(frontmatter 之后的内容)get_body() { awk 'BEGIN{fm=0} /^---$/{fm++; next} fm>=2{print}' "$1"}状态机原理:
- 初始
fm=0 - 第一次遇到
---→fm=1(进入 frontmatter) - 第二次遇到
---→fm=2(进入正文) fm>=2时输出所有行
局限性:不支持嵌套 YAML 结构(如 services: 列表)。对于需要列表字段的工具,转换器通常只提取简单字段(name, description)。
命名转换(Slugify)
slugify() { echo "$1" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//'}将人类可读的 Agent 名称转换为 kebab-case 文件名:
"Frontend Developer"→"frontend-developer""AI Engineer"→"ai-engineer"
各工具格式映射表
| 工具 | 输出格式 | 安装位置 | 转换特点 |
|---|---|---|---|
| Claude Code | 原生 .md | ~/.claude/agents/ | 无需转换,直接复制 |
| GitHub Copilot | 原生 .md | ~/.github/agents/ + ~/.copilot/agents/ | 无需转换,直接复制 |
| Antigravity | SKILL.md | ~/.gemini/antigravity/skills/{slug}/ | 添加 antigravity 特定 frontmatter(risk, source, date_added) |
| Gemini CLI | SKILL.md | ~/.gemini/extensions/agency-agents/skills/{slug}/ | 最小 frontmatter(仅 name + description) |
| Cursor | .mdc | .cursor/rules/ | 添加 Cursor 规则头(alwaysApply: false) |
| Aider | CONVENTIONS.md | 项目根目录 | 所有 Agent 合并为单个文件 |
| Windsurf | .windsurfrules | 项目根目录 | 所有 Agent 合并为单个文件 |
| OpenCode | .md | .opencode/agents/ | 项目级 .md 文件 |
| OpenClaw | SOUL.md + AGENTS.md + IDENTITY.md | ~/.openclaw/agency-agents/{agent}/ | 三分解:将 Agent 定义拆分为灵魂/行为/身份 |
| Kimi Code | agent.yaml + prompt.md | ~/.config/kimi/agents/{slug}/ | YAML agent 规范 + 独立 prompt 文件 |
| Qwen Code | .md | .qwen/agents/ | 项目级 SubAgent 文件 |
合并型工具(Aider / Windsurf)
Aider 和 Windsurf 不支持单个 Agent 文件,而是读取单个大文件(CONVENTIONS.md / .windsurfrules)。转换器需要:
- 遍历所有 Agent 源文件
- 提取每个 Agent 的 body(去掉 frontmatter)
- 按类别拼接,插入分隔符
- 生成单个大文件
潜在问题:合并后的文件可能非常大(160+ Agent × 平均 5KB ≈ 800KB),超过某些工具的上下文限制。
三分解工具(OpenClaw)
OpenClaw 的 workspace 将 Agent 定义分解为三个文件:
| 文件 | 内容 | 来自源文件的 |
|---|---|---|
SOUL.md | Agent 的身份、个性、核心驱动力 | Identity & Memory + Vibe |
AGENTS.md | Agent 的行为能力、工作流、规则 | Core Mission + Critical Rules + Workflow |
IDENTITY.md | Agent 的自我认知、记忆、经验 | Identity & Memory + Experience |
问题与规避
Frontmatter 边界误判
问题:正文中的 ---(Markdown 分隔线)被解析器误认为 frontmatter 结束。
对策:使用状态机(fm 计数器),只对前两个 --- 做特殊处理。
嵌套 YAML 支持
问题:源文件中的 services: 列表等嵌套 YAML 结构无法用简单的 get_field 提取。
对策:
- 对需要完整 YAML 的工具,使用
yq或 Pythonyaml库 - 对只需要简单字段的工具,保持 awk 实现(更简单、零依赖)
合并文件的上下文超限
问题:Aider/Windsurf 的合并文件可能超过工具的上下文窗口。
对策:
- 按类别分组生成多个合并文件(如
CONVENTIONS-engineering.md) - 或在转换时添加截断逻辑,仅保留核心定义
设计取舍
Bash 转换器 vs Python/Node 转换器
| 方案 | 优势 | 代价 |
|---|---|---|
| Bash | 零运行时依赖、任何环境开箱即用 | 代码量大、错误处理有限 |
| Python | YAML 库完善、错误处理丰富 | 需要 Python 运行时 |
| Node.js | JSON/YAML 处理方便 | 需要 Node.js 运行时 |
推荐:面向 CLI 工具的转换器用 Bash(零依赖);需要复杂 YAML 处理的用 Python。
原生兼容 vs 格式转换
| 方案 | 优势 | 代价 |
|---|---|---|
| 原生兼容(如 Claude Code 直接读 .md) | 零转换、源文件即最终文件 | 仅适用于少数工具 |
| 格式转换 | 支持更多工具 | 需要维护转换脚本 |
推荐:对原生兼容的工具直接复制;对需要特定格式的工具使用转换管道。