Agent Skill 原理与工程实践(十九):Skill 之间如何避免职责重叠
Agent Skill 原理与工程实践(十九):Skill 之间如何避免职责重叠
上一章讨论了多个 Skill 的组合与调用。组合能够把复杂任务拆成若干专业能力,但也会引入一个新问题:多个 Skill 都认为任务属于自己。
大纲给出的反例是:
1 | |
它看起来简单,实际上边界过于宽泛。系统遇到下面任何请求,都可能触发它:
- 帮我判断这次延迟异常属于哪个方向;
- 分析调度延迟;
- 检查内存抖动;
- 定位 I/O 瓶颈;
- 把已有结论整理成报告。
这些任务的目标、输入、工具、判断逻辑和输出都不相同。更合理的结构是:
1 | |
本章要解决的核心问题是:怎样让这五个 Skill 既能协作,又不会抢任务、重复工作或互相覆盖结论。
1. 什么叫“职责重叠”
职责重叠不是两个 Skill 恰好使用了同一个工具,也不是它们都处理同一份 trace。真正的重叠是:
对同一个输入和同一个阶段,多个 Skill 都拥有相同的决策权或交付责任,而系统没有明确的优先级与边界。
例如:
1 | |
第二个 Skill 名字更具体,但 description 仍然声称可以完成整个性能根因分析。结果可能是:
- 两个 Skill 同时触发;
- 总 Skill 绕过专业 Skill 自己分析;
- 专业 Skill 越界输出整体根因;
- 两者对最终结果拥有竞争性的控制权。
职责重叠通常发生在五个维度:
1 | |
仅靠换一个 Skill 名字,不能消除这些重叠。
2. 官方资料给出的两条底层原则
2.1 OpenAI:只有契约发生实质变化时才拆分
OpenAI 官方文档建议从一个职责清晰的 Agent 开始,只有在所有权、指令、工具面、审批策略、模型或输出形式确实不同时,才拆出新的 specialist。
这条原则可以直接迁移到 Skill:
1 | |
拆分的依据不是文件大小,也不是团队人数,而是契约是否改变。
2.2 Anthropic:description 决定发现,必须测试共存
Anthropic 官方说明,Skill 的 name 和 description 会用于判断是否触发;企业实践还特别要求测试 coexistence,即新增 Skill 是否因为 description 太宽而“抢走”已有 Skill 的触发。
因此,职责边界不是藏在 SKILL.md 正文深处的一句说明。边界必须首先体现在 metadata 中,因为模型在触发前通常只看得到 metadata。
3. 用“五维职责契约”定义一个 Skill
为避免重叠,可以给每个 Skill 建立五维职责契约:
1 | |
以 scheduler-analysis 为例:
1 | |
只有五个维度都明确,边界才不只是口号。
4. 不推荐的“大而全 performance Skill”
假设存在下面的 Skill:
1 | |
它的问题不只是“大”,而是同时占用了多个职责:
| 动词 | 隐含职责 |
|---|---|
| analyze | 深入领域分析 |
| find root causes | 跨域归因与最终判断 |
| recommend fixes | 方案设计 |
| generate reports | 报告生成 |
几乎所有性能类请求都能匹配它。即使再增加 scheduler-analysis,模型仍无法稳定判断应该选择谁。
对应的 SKILL.md 往往还会不断膨胀:
1 | |
最终它既是 router,又是所有 specialist,还是 reporter。这样的 Skill 难以独立测试,也无法与新能力安全共存。
5. 推荐的职责拆分
5.1 performance-triage:分类,不做深度分析
职责:
- 验证性能问题的基础输入;
- 识别症状与初始信号;
- 判断需要调用哪些领域 Skill;
- 给出分析优先级;
- 记录证据缺口。
不负责:
- 宣布某个领域已经是最终根因;
- 执行深入调度、内存或 I/O 分析;
- 生成正式报告。
核心输出:
1 | |
5.2 scheduler-analysis:只拥有调度领域结论
负责 run queue、调度等待、wakeup latency、CPU contention、migration 和 affinity 等证据。
它可以说:
1 | |
但不应直接说:
1 | |
后一句是跨领域最终归因,需要综合其他分析。
5.3 memory-analysis:只拥有内存领域结论
负责 page fault、reclaim、swap、OOM、内存压力、分配热点和 NUMA 等证据。
遇到 I/O wait 时,只返回 cross_domain_signal,不接管 I/O 分析。
5.4 io-analysis:只拥有 I/O 领域结论
负责设备延迟、队列深度、吞吐、block I/O、文件系统等待等证据。
它不把等待期间观察到的 CPU 行为直接解释为调度根因。
5.5 report-generator:组织已有结论,不创造新结论
负责:
- 按模板组织内容;
- 统一术语和格式;
- 保留证据引用、置信度、冲突与未知项;
- 生成面向不同读者的报告。
不负责:
- 自己重新分析原始 trace;
- 填补缺失证据;
- 把假设升级成事实;
- 在冲突结论中擅自选边。
这一条非常重要:格式化职责与分析职责必须分开。
6. 使用“名词 + 动作 + 阶段”检查边界
可以用三个问题检查每个 Skill:
1 | |
| Skill | 名词 | 动作 | 阶段 |
|---|---|---|---|
| performance-triage | 性能事件 | 分类、路由 | 分析前 |
| scheduler-analysis | 调度证据 | 深度分析 | 专业分析 |
| memory-analysis | 内存证据 | 深度分析 | 专业分析 |
| io-analysis | I/O 证据 | 深度分析 | 专业分析 |
| report-generator | 已验证发现 | 组织、呈现 | 分析后 |
如果两个 Skill 在这三列都相同,就应进一步检查是否需要合并。
如果只在名词上不同,但动作和阶段相同,它们可能是并列 specialist;如果名词相同但动作或阶段不同,它们可能是顺序工作流中的不同 Skill。
7. Description 必须同时写清“做什么”和“什么时候用”
7.1 容易冲突的写法
1 | |
它没有说明:
- 分析哪个领域;
- 在什么阶段触发;
- 需要什么前提;
- 与其他性能 Skill 有何区别。
7.2 边界更清晰的写法
1 | |
1 | |
1 | |
Description 中应尽量包含:
1 | |
不过也不要堆成完整工作流。它是路由摘要,而不是 SKILL.md 正文。
8. 正向触发、负向触发与模糊区
每个 Skill 都应准备三类样例。
8.1 Should Trigger
1 | |
8.2 Should Not Trigger
1 | |
8.3 Ambiguous
1 | |
负样例不是附属材料。对于一组相邻 Skill,负样例往往比正样例更能说明边界。
9. 设计 Skill Boundary Matrix
当 Skill 数量增加时,建议维护职责矩阵:
| 能力 | triage | scheduler | memory | io | report |
|---|---|---|---|---|---|
| 验证事件基本输入 | Owner | Consumer | Consumer | Consumer | Consumer |
| 选择分析领域 | Owner | Signal only | Signal only | Signal only | None |
| 调度深度分析 | Route | Owner | None | None | None |
| 内存深度分析 | Route | None | Owner | None | None |
| I/O 深度分析 | Route | None | None | Owner | None |
| 跨域证据合并 | Owner/Orchestrator | Contributor | Contributor | Contributor | Consumer |
| 最终报告格式 | None | None | None | None | Owner |
| 最终答复 | Orchestrator | Contributor | Contributor | Contributor | Draft only |
建议使用明确角色,而不是简单打勾:
1 | |
如果同一行出现多个 Owner,就存在潜在职责冲突,除非系统明确规定优先级或共同决策协议。
10. 决策权必须唯一
工作步骤可以共享,最终决策权不能含糊。
例如多个 Skill 都可以读取时间窗口,但以下权力应有唯一 owner:
- 谁决定需要哪些领域分析;
- 谁对调度领域结论定级;
- 谁决定整体根因;
- 谁批准执行有副作用的诊断;
- 谁对最终用户答复负责。
可以用 Decision Rights 表达:
1 | |
这比在各个 Skill 中分别写“必要时自行决定”可靠得多。
11. 共享工具不等于共享职责
scheduler-analysis、memory-analysis 和 io-analysis 可能都需要:
- 读取 trace;
- 计算统计量;
- 画时间序列图;
- 查询系统元数据。
这不代表它们职责重叠。Tool 是执行手段,Skill 是领域工作流。
真正要控制的是:
1 | |
例如三个 Skill 都可使用只读 trace parser,但只有 io-analysis 能把 block queue latency 解释为 I/O 领域证据。
对于有副作用的工具,应进一步缩小权限:
1 | |
权限边界能够防止“职责相邻”演变成“动作互相覆盖”。
12. 输出所有权比输入范围更重要
多个 Skill 可以读取同一个输入,但最好不要竞争同一个最终输出。
推荐让输出形成分层:
1 | |
每个输出都有:
- 唯一 Schema;
- 唯一生产者或明确优先级;
- 清晰消费者;
- 版本和验证规则。
如果两个 Skill 都输出 performance_report_v1,就要解释为什么需要两个生产者。若无法解释,通常说明职责重叠。
13. Triage 与 Analysis 最容易混淆
Triage 的目标是回答:
1 | |
Analysis 的目标是回答:
1 | |
二者的深度、成本和置信度都不同:
| 属性 | Triage | Analysis |
|---|---|---|
| 输入 | 症状、摘要、轻量指标 | 领域数据与完整工件 |
| 目标 | 分类和优先级 | 验证或否定领域假设 |
| 成本 | 低 | 中到高 |
| 输出 | candidate domains | evidence-backed finding |
| 是否给最终根因 | 否 | 只给领域结论 |
如果 triage 开始执行深度分析,它会与 specialist 重叠;如果 specialist 又负责决定是否应该调查自己,就容易出现自我选择偏差。
14. Analysis 与 Report Generation 也要分开
报告 Skill 最容易发生的越界是“为了让报告完整而补充推断”。
例如输入只有:
1 | |
report-generator 不应把它润色成:
1 | |
它应忠实表达:
1 | |
报告生成器的质量标准是忠实、清晰、结构化,不是独立推理出更多事实。
15. 出现重叠时,合并还是继续拆分
15.1 应该合并的信号
- 两个 Skill 的正向触发样例高度相同;
- 它们使用相同工作流和工具;
- 输出 Schema 相同;
- 一个任务经常同时触发二者,但无法说明先后关系;
- 独立 Eval 无法分别定义成功标准;
- 维护者总需要同步修改两个 Skill。
15.2 应该保持分离的信号
- 领域知识显著不同;
- 工具、权限或安全策略不同;
- 输入输出契约不同;
- 可独立复用;
- 可并行执行;
- 有不同维护 owner;
- 任一 Skill 可以替换而不改变另一方。
15.3 应该增加 Router 的信号
- Skill 本身边界清晰,但用户请求经常模糊;
- 多数任务只需要其中一个 Skill;
- 判断依据来自轻量信号;
- 直接同时触发所有 Skill 成本过高。
注意:增加 Router 不能修复两个内部职责完全相同的 Skill。先解决边界,再解决路由。
16. 重叠并非总能消除:定义仲裁规则
现实中有些边界天然交叉,例如内存回写会引发 I/O,I/O 等待又会改变调度行为。这类情况不应假装完全独立,而应定义交叉协议。
1 | |
交叉协议包含:
1 | |
这样能够承认系统耦合,同时保持职责清晰。
17. 用优先级掩盖重叠为什么不够
一种常见做法是:
1 | |
优先级可以暂时解决选择问题,但无法解决:
- 两者内部工作流仍然重复;
- 新增 Skill 后优先级链继续变长;
- 用户措辞稍变就可能落入错误分支;
- 被低优先级 Skill 仍占用 metadata 和维护成本。
优先级只适合处理合理的边界接触,不适合替代职责设计。
18. Skill Registry:把职责边界当作系统资产
当 Skill 超过少量几个时,建议维护注册表:
1 | |
Anthropic 的企业 Skill 指南也建议注册 purpose、owner、version、dependencies 和 evaluation status。注册表让职责冲突可以在设计和评审阶段被发现,而不是上线后靠错误触发暴露。
19. Coexistence Eval:专门测试 Skill 是否互相抢任务
独立 Eval 只能证明某个 Skill 单独可用,不能证明它加入 Skill 集合后仍能正确触发。
19.1 构造对比样例
1 | |
19.2 构造边界样例
1 | |
边界样例不一定只有一个 Skill,但必须预先定义主职责、辅助职责和调用顺序。
19.3 关键指标
1 | |
20. 一套职责重叠审查流程
第一步:建立清单
列出所有 Skill 的 name、description、输入、输出、工具、阶段和 owner。
第二步:两两比较
对每一对 Skill 检查:
1 | |
第三步:标记重叠类型
1 | |
第四步:选择处理方式
- 缩窄 description;
- 增加前置条件和排除项;
- 调整输出所有权;
- 分离 triage、analysis 和 report 阶段;
- 建立 router;
- 合并重复 Skill;
- 废弃旧 Skill。
第五步:运行共存测试
既测试应该触发,也测试不应触发和模糊边界。
第六步:监控真实行为
记录模型实际选择了什么、是否重复调用、是否发生人工纠正,再据此调整,而不是只凭设计者直觉。
21. 重构实例:从 performance 到五个 Skill
21.1 重构前
1 | |
问题:所有职责在一个边界内,任何局部能力都无法复用。
21.2 第一次错误拆分
1 | |
虽然文件变多了,但每个 description 仍写着“分析性能并找出根因”,职责仍然重叠。
21.3 正确拆分
1 | |
21.4 组合关系
1 | |
拆分后的关键不是目录更整齐,而是每个节点都有独立的触发、判断权和交付物。
22. 可直接复用的 Boundary Contract 模板
1 | |
对相邻 Skill 同时使用这一模板,很容易看出冲突位置。
23. 常见反模式
23.1 名字具体,description 宽泛
scheduler-analysis 的 description 却写“解决所有性能问题”。模型主要依赖 description 触发,具体名字救不了宽泛职责。
23.2 所有 Skill 都能生成最终报告
这会造成多个“最终版本”。应让分析 Skill 输出 finding,让报告 Skill 生成草稿,由 Orchestrator 拥有最终答复。
23.3 每个 Skill 都可以自由调用所有其他 Skill
会形成隐式环路和控制权漂移。应使用允许调用列表、调用深度和 return rule。
23.4 用“必要时”掩盖边界
例如“必要时进行其他领域分析”。谁判断必要、分析到什么程度都不明确。应改成结构化 cross_domain_signal 并交回 Orchestrator。
23.5 新增 Skill,却不回归已有触发
新增一个 description 更宽的 Skill,可能让旧 Skill 的触发率突然下降。每次新增或修改都要运行 coexistence regression。
23.6 拆分过细
如果每个 Skill 只有一个普通步骤,且没有独立契约、复用价值或安全边界,拆分只会增加路由噪声。OpenAI 官方也提醒,过早拆分会增加 Prompt、trace 和审批面。
24. 官方资料带来的工程启示
24.1 OpenAI:先定义最小而清晰的 specialist
一个 specialist 应拥有清楚的任务、约束、工具和输出;只有在所有权、工具、政策或输出确实变化时,才增加新的 specialist。
24.2 OpenAI:路由描述要短而具体
handoff description 的作用是让路由者知道何时选择该能力。对应到 Skill,就是不要用“通用助手”“性能处理”之类抽象描述。
24.3 Anthropic:metadata 是触发边界的第一现场
模型利用 name 和 description 判断何时加载 Skill。正文里的边界如果没有反映到 description,可能在 Skill 被读取之前就已经选错。
24.4 Anthropic:必须评测 Coexistence
官方企业指南明确把“新 Skill 的 description 太宽,抢走已有 Skill 的触发”列为失败示例,并建议在隔离测试之外进行共存测试。发生冲突时,应合并重叠 Skill 或缩窄 description。
25. 本章最终模型
1 | |
避免职责重叠的关键不是让 Skill 彼此完全不知道对方,而是做到:
1 | |
26. Skill 专项复盘
26.1 两个 Skill 使用相同 Tool,是否说明重叠?
不一定。共享执行手段很正常,要检查它们的领域解释权、工作阶段和输出所有权是否相同。
26.2 Description 中是否应该写“不做什么”?
对容易与相邻 Skill 混淆的能力,应该写简短排除项;但不要把 metadata 写成完整规则手册。
26.3 如何判断两个 Skill 应合并?
如果它们的触发、流程、工具、输出和成功标准长期相同,并且无法构造稳定的边界 Eval,就应优先合并。
26.4 谁负责跨领域最终根因?
由明确的 Orchestrator 或 correlation Skill 负责。各领域 Skill 只对自己的 finding 负责。
26.5 如何证明职责边界有效?
测量 Trigger Precision、Trigger Recall、Over-trigger Rate、重复工作率、输出所有权冲突、共存回归和人工纠正次数。
27. 本章实践
将一个宽泛的 performance Skill 重构为大纲中的五个 Skill:
- 为每个 Skill 写出 Purpose、Trigger、Scope、Authority 和 Deliverable;
- 为五个 Skill 分别编写互不竞争的 description;
- 建立 Skill Boundary Matrix;
- 为每项关键决策指定唯一 owner;
- 给每个 Skill 定义唯一输出 Schema;
- 定义 scheduler、memory 与 I/O 的交叉信号协议;
- 各准备三条 should-trigger、should-not-trigger 与 ambiguous 样例;
- 运行 pairwise coexistence 测试;
- 检查新增 Skill 是否抢走旧 Skill 的触发;
- 把 purpose、owner、version、dependency 和 eval status 记录进 Skill Registry。
下一章将严格按照大纲进入第六阶段 Context Engineering:
第 20 章:如何控制 Skill 上下文大小。