开源 Agent 套件 ECC 怎么组织 281 个技能:放置、写法与适配
本文基于 ECC 仓库 commit 591ab5c(2026-07-29)梳理,该项目仍在高频迭代,具体行为以仓库 https://github.com/affaan-m/ECC 最新代码与文档为准。
技能数量一旦过百,真正的难题就不再是”这条技能写得好不好”,而是”它有资格进仓库吗、装的时候会不会把上下文撑爆、换个工具还能不能用”。 ECC 这个开源套件把这三个问题拆成了三份可执行的政策文件,外加一个只管结构、不管内容的 CI 校验器。它的取向很明确:机器只守边界,内容好坏交给人。
先说清本篇和站内几篇的分工。Claude Code 技能机制 讲的是技能这个功能本身怎么用,MCP 扩展框架 讲协议层怎么接外部工具,Claude Code subagent 实践 讲任务怎么分给不同角色——那三篇是通用方法论。本篇不重复方法论,只做一件事:把 ECC 这个真实仓库的技能治理拆开,看一个已经膨胀到 281 个技能的项目,是靠哪几份文件、哪几个脚本把秩序守住的。你可以当场 clone 下来逐条核对。
一、先分清技能、agent、命令这三样东西
ECC 仓库根目录下 skills/ 有 281 个技能目录,agents/ 有 67 个 agent,commands/ 有 94 个命令。这三个数字放在一起,第一个要回答的问题就是:为什么同一件事要有三种载体。
docs/SKILL-DEVELOPMENT-GUIDE.md 开头给了一张对照表,把五类组件的用途和激活方式并排列出来:技能是知识仓库,靠上下文自动激活;agent 是任务执行器,靠显式委派;命令是用户动作,靠 /command 触发;hook 是自动化,靠事件触发;rule 是常驻约束,一直生效。文档里对技能的定性很克制——技能是”被动知识”(passive knowledge),Claude Code 在相关时去引用它,它自己不会主动跑。
这个定性决定了后面所有政策的形状。既然技能是被引用的知识,那它的价值就取决于两件事:能不能在该出现的时候被拉进上下文,以及被拉进来之后占多少预算。ECC 的技能治理,基本上就是围绕这两件事展开的。上下文这一侧的通用取舍可以参考 Agent 上下文预算怎么定,这里只谈 ECC 具体怎么做。
| 组成部分 | 它负责什么 | 对应仓库位置 | 你什么时候会碰到它 |
|---|---|---|---|
| 策展技能 | 随仓库分发的知识模块本体 | skills/<skill-name>/SKILL.md | 想给团队加一条共用规范 |
| 放置政策 | 定义四类技能各自的落点与是否分发 | docs/SKILL-PLACEMENT-POLICY.md | 拿不准新技能该写在哪 |
| 写作指南 | SKILL.md 的字段、结构、长度上限 | docs/SKILL-DEVELOPMENT-GUIDE.md | 第一次动手写技能 |
| 来源政策 | 从外部项目搬想法进来的规矩 | docs/skill-adaptation-policy.md | 想把别人的提示词包搬过来 |
| 结构校验 | 查目录、查空文件、查 frontmatter | scripts/ci/validate-skills.js | CI 报出 WARN 或 ERROR |
| 安装清单 | 把技能切成模块并声明可装的目标工具 | manifests/install-modules.json | 新写的技能装不上 |
| 安装画像 | 把模块组合成几档预设 | manifests/install-profiles.json | 决定一次装多少 |
| 出处元数据 | 自动产出技能的来源字段定义 | schemas/provenance.schema.json | 写自动学习出来的技能 |
| 跨工具适配 | 各执行环境的可移植性与支持状态 | docs/architecture/cross-harness.md | 想在别的编码工具上用 |
| 健康度报表 | 技能成功率、失败聚类、版本历史 | scripts/skills-health.js | 想知道哪条技能在退化 |
二、技能该放哪:四类落点,只有一类进仓库
docs/SKILL-PLACEMENT-POLICY.md 是这套政策里最硬的一份。它把技能分成四类,每类有固定的根路径,以及一个是否随仓库分发的结论:
- Curated(策展):路径
skills/,随仓库分发,不需要出处文件。 - Learned(学到的):路径
~/.claude/skills/learned/,不进仓库、不分发,必须有出处文件。 - Imported(导入的):路径
~/.claude/skills/imported/,不进仓库、不分发,必须有出处文件。 - Evolved(演化的):路径
~/.claude/homunculus/evolved/skills/(全局)或projects/<hash>/evolved/skills/(按项目),不进仓库、不分发,出处从来源继承。
这条边界的关键在于:安装清单只引用策展路径。也就是说,一条技能哪怕在你本机跑得再好,只要它是机器从会话里提炼出来的,它就永远不会出现在别人的安装包里,除非有人把它重写成策展技能提交上去。
Learned 这一类由持续学习机制产出——一个是 evaluate-session 钩子(它挂在会话停止事件上,一轮活干完了才去回看这段会话有没有值得沉淀的东西),一个是用户主动敲的 /learn 命令。默认落点可以通过 skills/continuous-learning/config.json 里的 learned_skills_path 改。这个配置文件里同时还写了它盯哪几类模式(错误解决、用户纠正、绕行方案、调试技巧、项目专属)和忽略哪几类(简单笔误、一次性修复、外部 API 问题)。
出处文件叫 .provenance.json,跟 SKILL.md 放在同一个技能目录里。schema 在 schemas/provenance.schema.json,四个字段全是必填:source(来源,可以是 URL、路径或标识符)、created_at(ISO 8601 时间戳)、confidence(0 到 1 的数)、author(谁或什么东西产出的)。验证入口在 scripts/lib/skill-evolution/provenance.js 的 validateProvenance。
这里有个诚实的现状值得指出:政策文档自己的实施路线图里,第 2 条写的是”给学到的技能的写入路径加上出处校验,让新产生的技能总是带上 .provenance.json”——也就是说,这个约束目前是政策要求,写入端的强制还在计划中。对照 commands/learn.md 也能看出这层落差:它给的输出格式是 ~/.claude/skills/learned/[pattern-name].md 这样一个单文件,而政策要的是一个目录,里面装 SKILL.md 加出处文件。你如果照命令文档的写法产出,健康度脚本按目录扫的时候是认不到的。
三、SKILL.md 怎么写:一半是格式,一半是判断
写作指南给的最小结构很简单:一个技能一个目录,目录里 SKILL.md 必需,examples/ 和 references/ 可选。frontmatter 只有两个必填字段——name(小写连字符)和 description(一行,同时用于列表展示和自动激活),可选的有 origin、tags、version。
不过实际仓库里的写法并不统一。skills/agentic-engineering/SKILL.md 的开头是这样:
---
name: agentic-engineering
description: Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing.
metadata:
origin: ECC
---
来源标记被套进了 metadata 里,而写作指南和 docs/architecture/cross-harness.md 讲的都是顶层 origin。仓库里两种写法并存,而且嵌套那一种才是绝大多数技能实际采用的形态。这不是笔误,而是校验器压根不看这一项——它对 frontmatter 只关心 name 有没有写、以及 description 是不是写成了合法的行内形式,origin 在哪一层它一概不问。你自己维护一套技能库的时候,这就是个提醒:没被脚本查的字段,长期一定会漂——文档写得再清楚也拦不住,因为文档不会在提交时拦人。
比格式更值得抄的是判断部分。指南反复强调技能要”聚焦”,并且给了一张好/坏对照:react-hook-patterns 好过 react,postgresql-indexing 好过 databases,pytest-fixtures 好过 python-testing,nextjs-app-router 好过 nextjs。理由不难想——描述越宽,自动激活时越容易在不该出场的时候被拉进来,白白吃掉上下文。
内容写法上,指南把 “When to Activate” 这一节标成对自动激活最关键的一节,要求写得具体到场景级;正文推崇”给例子不给论断”(把”要正确处理异步错误”换成一段真能跑的带 response.ok 判断的代码),要求带反面模式、带清单、带决策树。篇幅上给了硬数字:典型 200 到 500 行,最多 800 行。
四、CI 只守结构:WARN 和 ERROR 的分界线画在哪
scripts/ci/validate-skills.js 的文件头注释把它管的范围写得很死,一共四条:每个子目录必须有 SKILL.md;SKILL.md 非空;frontmatter 若存在,必须声明 name;description 必须用行内标量,不能用字面量块标量(| / |- / |+)。
第四条的理由写在注释里——块标量会保留内部换行,把以 description 为键的扁平表渲染打断。这是个很典型的”数据格式选择影响下游渲染”的坑,值得记一笔。
分级方式更值得琢磨:结构问题(缺失或空的 SKILL.md)永远是错误,frontmatter 问题默认只是警告,要靠 --strict 参数或者 CI_STRICT_SKILLS=1 环境变量才升级成错误退出。注释里写了原因:让存量数据缺陷可以在 CI 之外慢慢清,而不是一上来就把流水线卡红。
作用域也划得很清楚——只管仓库里的 skills/,学到的、导入的、演化的三个根一律不碰。skills/ 目录不存在时直接 exit 0。另一个校验器 scripts/ci/validate-install-manifests.js 管的是反面:清单里声明的所有 paths 必须在仓库里真实存在,缺一个就报错,没有”可选路径”这种网开一面。
把这两条合起来看,ECC 的取向就清楚了:脚本只保证”结构不烂、清单不指空”,技能写得对不对、例子能不能跑,机器一概不管。 代码例子的验证是写在指南里让作者自己跑的,npx tsc --noEmit、python -m py_compile、go build ./examples/... 这一类。至于”这条技能该不该进来”,交给 docs/skill-adaptation-policy.md 里那五个评审问题去问人:这是 ECC 里一个真实可复用的面,还是只是别人工具的说明书?名字还配得上现在的形态吗?仓库里是不是已经有技能覆盖了大半行为?引进的是一个概念,还是别人的产品身份?一个不知道上游项目的用户看得懂这个技能是干嘛的吗?
五、281 个技能怎么装:模块、画像、目标工具
技能不是一股脑安装的。manifests/install-modules.json 把它们切成了模块,每个模块带一组字段:id、kind、paths、targets、dependencies、defaultInstall、cost、stability。比如 framework-language 是最大的一块(框架与语言类技能),workflow-quality 管质量工作流,还有 security、agentic-patterns、devops-infra、research-apis、business-content、operator-workflows、media-generation 等等。
默认值这一项要单看:技能类模块里,只有 workflow-quality 的 defaultInstall 是 true,其余全是 false。这跟 scripts/lib/harness-adapter-compliance.js 里 Claude Code 那条记录的风险备注是同一个意思——不要默认加载全部技能,钩子保持可选且可检视。cost 字段的取值是 light / medium / heavy,stability 是 stable / beta,这两个字段就是给你做安装决策用的。
manifests/install-profiles.json 再把模块组合成档位:minimal 是低上下文的基础组合,明确不带 hook 运行时;core 加上 hooks;developer 是给大多数人写业务代码用的默认档;另有 security、research、opencode 等。opencode 这一档的描述里直接写明”故意不含 hooks-runtime,要用请显式加 --modules hooks-runtime”。
跨工具这一层,docs/architecture/cross-harness.md 给的定位是”ECC 是可复用的工作流层,各个执行环境只是执行面”。它的可移植性表格里,技能这一行的共享源就是 skills/*/SKILL.md,适配层则分别是各家的插件、.agents/skills、Cursor 的技能副本、OpenCode 的插件配置。文档里那句判断挺准:SKILL.md 是最可移植的单元,因为它绝大部分内容是指令、约束和工作流形状,不依赖某个运行时。
真正不可移植的是钩子。scripts/lib/harness-adapter-compliance.js 里定义了四种支持状态,含义写在 COMPLIANCE_STATES 里:Native(能直接安装或验证)、Adapter-backed(有薄适配层,但各家能力不等)、Instruction-backed(文件能给,但对方没有 ECC 需要的运行时钩子面来做强制)、Reference-only(有参考价值,但没有直接安装器)。Codex 那条记录就是 Instruction-backed,风险备注写得很直白:除非有原生钩子面,否则把 hook 当作政策文本看待。想清楚钩子机制本身的可以看 Claude Code hooks 用法,这里只强调一点——跨工具复用技能可行,跨工具复用强制力不可行。
六、边界与代价:这套设计明确放弃了什么
放弃了自动学习成果的可分发性。技能一旦是机器从会话里提炼出来的,它就被钉死在用户目录,不进版本控制、不被 CI 校验、团队之间也不会自动流通。想让它变成资产,只有一条路:有人重新把它写成策展技能提交上去。这是拿”可复现”换”不失控”,代价是每个人本机那份经验大概率会烂在本地。
放弃了内容层面的质量保证。校验器只查结构,你可以提交一条完全说错的技能,只要目录和 frontmatter 合规就能过 CI。指南和评审问题都写得不错,但它们是给人看的规范,不是能自动执行的门。
放弃了跨工具的行为一致。可移植性表格里,规则与指令那一行的现状写的是”支持,但各家并不完全相同”;命令那一行写的是”支持,但命令语义各有差异”;会话那一行直接标 Alpha。你不能假设同一份技能在两个工具里表现一样。
以及它明确不管的几件事:不管技能内容对不对,不管例子能不能编译,不管技能之间的语义冲突(两条描述接近的技能同时被激活该听谁的,这几份政策文件里没有给出仲裁办法),也不管你装完之后上下文实际涨了多少——cost 字段只是个粗粒度标签。
还有一层要如实说清的代价:这类套件会往你的机器里写东西。安装会在用户目录或项目目录下铺文件,hook 运行时会挂到会话事件上,MCP 那一层会连外部进程。ECC 在这件事上的处理是把默认值收紧——记忆库的 MCP 服务端参考配置放在 mcp-configs/mcp-servers.json,故意不进默认的 .mcp.json,文档给的理由是不让安装悄悄多出一个可写的上下文面,也不让你白白付它的工具 schema 成本。默认值收紧不等于没风险,装之前把 install-modules.json 里对应模块的 paths 和 targets 看一遍,你才知道文件会落到哪。
七、上手与避坑清单
别上来就装 developer 档。 会踩是因为档位名字听着像”给开发者的默认选择”,实际它把框架语言、数据库、编排都拉进来了,技能一多,每次会话的上下文占用就压不住。避法是先用 minimal 跑一周,缺什么再用 --modules 单点加,或者用 --skills 按 ID 装单条技能。
新写的技能记得挂进安装清单。 会踩是因为 validate-skills.js 只查 skills/ 下的目录结构,它不会反过来问”这个技能有没有被清单引用”。结果就是 CI 全绿、技能躺在仓库里,但任何人装都装不到。避法是提交时同步改 manifests/install-modules.json,并且跑一遍清单校验确认路径对得上。
description 千万别写成多行块标量。 会踩是因为描述写长了,顺手用 | 换行更好看。而这一条默认只报 WARN,CI 照样绿,问题要等到技能列表渲染错乱时才暴露。避法是本地跑校验时带 --strict,把 frontmatter 警告直接当错误对待。
技能名和描述别起太宽。 会踩是因为自动激活靠的就是 description 匹配,一条叫 react 的技能会在任何沾边的任务里冒出来抢上下文。避法是按指南那张对照表收窄到具体子领域,并且把 “When to Activate” 写成可判定的场景列表,而不是一句”写前端代码时”。
学到的技能别提交进仓库。 会踩是因为它就在你 home 目录里躺着,看着跟策展技能长得一模一样,顺手 git add 很自然。避法是记住那条边界——出处文件是学到的和导入的技能的标记物,看到 .provenance.json 就说明这东西不该进仓库。政策路线图里的第 5 条正是要把这件事写进贡献文档,说明它确实是个常见误操作。
从别的项目搬技能过来时,先答那五个问题。 会踩是因为直接复制粘贴最省事,但搬进来的往往是别人的产品身份而不是可复用的能力。来源政策给的规则是:拿走底层的想法、工作流和结构,适配到当前项目的安装面和校验流程,去掉不必要的外部品牌、依赖假设和上游框架。它还有一条依赖底线——不要做一个主要作用是”劝用户去装并信任某个未经审查的第三方包”的技能。
别指望钩子在所有工具上都生效。 会踩是因为技能能装过去,就容易默认整套约束也跟着过去了。避法是先查 harness-adapter-compliance.js 里对应那条记录的状态:只要写的是 Instruction-backed,那边的钩子就只是文字,强制力得靠别的手段补。
收尾:一份自查清单
如果你要把这套思路搬到自己的技能库上,逐条对一遍:新技能有没有明确的落点分类,机器产出的东西是不是被挡在版本库外,出处元数据有没有强制字段并且写入端真的在写,校验脚本管的是结构还是内容(以及你有没有把这条边界告诉贡献者),有没有一个默认值足够保守的安装档,跨工具时哪些东西是可移植的、哪些只是文字。
想继续往下读,顺序建议是:docs/SKILL-PLACEMENT-POLICY.md 定边界,scripts/ci/validate-skills.js 看边界怎么被脚本兑现,manifests/install-modules.json 看 281 个技能怎么被切开分发,最后 docs/architecture/cross-harness.md 看这套东西离开原生环境之后还剩多少。四份读完,你大概能判断出自己的项目该抄哪一段、该跳过哪一段。
项目采用 MIT 许可证,仓库在 https://github.com/affaan-m/ECC 。它迭代得很快,本文写到的路径和字段随时可能变,动手前请以仓库当下的代码和文档为准。
本文属于 ECC 开源 Agent 套件专题(共 40 篇,从选择性安装一直拆到内部机制)。同一批还写了另一种取向的项目——不给你资产、只给你纪律的 superpowers 方法论专题。