跳转到内容

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 状态机:

Terminal window
# 提取 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"
}

状态机原理

  1. 初始 fm=0
  2. 第一次遇到 ---fm=1(进入 frontmatter)
  3. 第二次遇到 ---fm=2(进入正文)
  4. fm>=2 时输出所有行

局限性:不支持嵌套 YAML 结构(如 services: 列表)。对于需要列表字段的工具,转换器通常只提取简单字段(name, description)。

命名转换(Slugify)

Terminal window
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/无需转换,直接复制
AntigravitySKILL.md~/.gemini/antigravity/skills/{slug}/添加 antigravity 特定 frontmatter(risk, source, date_added)
Gemini CLISKILL.md~/.gemini/extensions/agency-agents/skills/{slug}/最小 frontmatter(仅 name + description)
Cursor.mdc.cursor/rules/添加 Cursor 规则头(alwaysApply: false)
AiderCONVENTIONS.md项目根目录所有 Agent 合并为单个文件
Windsurf.windsurfrules项目根目录所有 Agent 合并为单个文件
OpenCode.md.opencode/agents/项目级 .md 文件
OpenClawSOUL.md + AGENTS.md + IDENTITY.md~/.openclaw/agency-agents/{agent}/三分解:将 Agent 定义拆分为灵魂/行为/身份
Kimi Codeagent.yaml + prompt.md~/.config/kimi/agents/{slug}/YAML agent 规范 + 独立 prompt 文件
Qwen Code.md.qwen/agents/项目级 SubAgent 文件

合并型工具(Aider / Windsurf)

Aider 和 Windsurf 不支持单个 Agent 文件,而是读取单个大文件(CONVENTIONS.md / .windsurfrules)。转换器需要:

  1. 遍历所有 Agent 源文件
  2. 提取每个 Agent 的 body(去掉 frontmatter)
  3. 按类别拼接,插入分隔符
  4. 生成单个大文件

潜在问题:合并后的文件可能非常大(160+ Agent × 平均 5KB ≈ 800KB),超过某些工具的上下文限制。

三分解工具(OpenClaw)

OpenClaw 的 workspace 将 Agent 定义分解为三个文件:

文件内容来自源文件的
SOUL.mdAgent 的身份、个性、核心驱动力Identity & Memory + Vibe
AGENTS.mdAgent 的行为能力、工作流、规则Core Mission + Critical Rules + Workflow
IDENTITY.mdAgent 的自我认知、记忆、经验Identity & Memory + Experience

问题与规避

Frontmatter 边界误判

问题:正文中的 ---(Markdown 分隔线)被解析器误认为 frontmatter 结束。

对策:使用状态机(fm 计数器),只对前两个 --- 做特殊处理。

嵌套 YAML 支持

问题:源文件中的 services: 列表等嵌套 YAML 结构无法用简单的 get_field 提取。

对策

  • 对需要完整 YAML 的工具,使用 yq 或 Python yaml
  • 对只需要简单字段的工具,保持 awk 实现(更简单、零依赖)

合并文件的上下文超限

问题:Aider/Windsurf 的合并文件可能超过工具的上下文窗口。

对策

  • 按类别分组生成多个合并文件(如 CONVENTIONS-engineering.md
  • 或在转换时添加截断逻辑,仅保留核心定义

设计取舍

Bash 转换器 vs Python/Node 转换器

方案优势代价
Bash零运行时依赖、任何环境开箱即用代码量大、错误处理有限
PythonYAML 库完善、错误处理丰富需要 Python 运行时
Node.jsJSON/YAML 处理方便需要 Node.js 运行时

推荐:面向 CLI 工具的转换器用 Bash(零依赖);需要复杂 YAML 处理的用 Python。

原生兼容 vs 格式转换

方案优势代价
原生兼容(如 Claude Code 直接读 .md)零转换、源文件即最终文件仅适用于少数工具
格式转换支持更多工具需要维护转换脚本

推荐:对原生兼容的工具直接复制;对需要特定格式的工具使用转换管道。


参考来源