做 agent 开发我最大的感受是skill 这个东西改起来比写起来难。写 skill 是从零到一照着模板拼一拼能跑就算成功改 skill 却是在一个已经能工作的系统上动刀动不好不仅没变强反而把原来能用的能力也搞坏了。最近读了一篇叫 SkillFocus 的论文它提出的观点非常朴素但特别戳痛点先把能力拆清楚再决定 skill 怎么改。核心意思是任何 skill 的改动都应该建立在一张“能力地图”之上而不是靠感觉、靠临时拍脑袋。这篇文章我会结合自己的实操把 SkillFocus 的方法拆开讲透顺便聊聊怎么用这套方法解决 agent skill 迭代中最常见的一堆问题。如果你正在维护一个 agent 项目或者正在写属于自己的 skill 插件这篇文章应该能帮你少踩几个坑。1. 为什么“改 skill”之前必须先拆能力1.1 一个典型的改 skill 翻车现场我维护过一个信息整理类的 agent里面有个 skill 叫“会议纪要生成”。因为用户反馈纪要里的行动项经常提取不全我直接往 skill 的 prompt 里塞了一大段关于“行动项”的说明还加了几个示例。改完第一轮测试发现行动项提取确实比之前全了但原来能正常识别的“讨论结论”却开始出错经常把闲聊内容也当成结论输出。当时我第一反应是“模型抽风了”折腾了半天才发现问题出在我自己身上我往 skill 里加内容的时候没有明确加在哪一层也没有划定边界结果所有相关行为都被新加的内容影响了。这个场景做 agent 开发的朋友应该都不陌生。一个 skill 在运行时会同时处理多种输入、产出多种输出它们共享同一份 prompt 和同一批上下文。你为了加强 A 能力加进去的话术很可能会把 B 能力带偏。更麻烦的是这种带偏还不容易发现因为你的测试用例往往只覆盖了你关心的 A 场景B 场景得等用户吐槽才知道。1.2 SkillFocus 论文的核心主张SkillFocus 这篇论文解决的就是这个痛点。它的核心主张是agent 的每一个 skill 都应该有一个明确的能力定义这个定义不是一句话描述而是三层结构化信息——行为层、知识层、边界层。每次改动之前先把你想要改的 skill 的三层画出来然后明确这次改动会影响到哪一层其他层要不要同步调整。这个思路听起来简单但绝大多数 agent 开发者没有做到。我在各个技术社区看别人分享 skill 修改经验几乎都在说“prompt 加什么词”“few-shot 怎么给”很少有人先回答“这个 skill 当前能力边界是什么”。SkillFocus 把这个问题拎出来等于告诉我们你之所以越改越乱是因为你根本不知道自己在改什么。行为层、知识层、边界层这三层就是帮你搞清楚“自己在改什么”的工具。2. 三层能力地图把 skill 拆成可管理的结构2.1 行为层把 skill 当成一个有输入输出的函数任何 skill 在运行时本质上都是一个函数接收输入产生输出可能调用工具。行为层要写清楚的就是这个 skill 的输入是什么输出是什么在什么条件下被激活激活后调用哪些工具怎样算成功怎样算失败用代码来类比行为层就是这个函数的签名和异常处理。我经常用一段伪代码来画行为层比如skill: 会议纪要生成 trigger: 用户提供会议录音或文字稿并要求生成纪要 input: 原始会议内容可选指定格式 output: 结构化纪要结论、行动项、负责人、截止时间 tools: 文本解析工具、日期识别工具 failure: 输入文本太短无法识别会议主题时返回提示并请求补充你可能会觉得这有点繁琐但画出来之后你才能回答“这个 skill 到底能不能改”。比如前面说的翻车案例如果我先画了行为层就会看到原版的行为输出除了行动项还有“结论”和“讨论过程摘要”而我改的时候只盯着“行动项提取不全”这一个输入输出对忽略了其他输出项导致其他输出被新增内容干扰。行为层的意义是让你意识到每一次 skill 改动都是在改一个多输入多输出的映射关系而不是在改一段文字。2.2 知识层skill 的“肉”也是改动最频繁的地方知识层包含 skill 需要参考的领域知识、示例、术语表、few-shot 样本等。比如一个“代码审查 skill”知识层里应该有你们团队的编码规范、常见 bug 模式、安全注意事项。改知识层是最容易的因为往里塞内容立竿见影这也是为什么大多数人改 skill 都喜欢从这一层下手。但难点在于知识层的知识是相互关联的。你新加一条规则可能会和已有规则冲突。我在实践中会为知识层建一张表每条知识用一行记录标明它影响的行为、依赖的其他知识、可能的冲突面。比如加一条“禁止使用 eval”它影响的行为是“代码执行建议”依赖的是“安全规范”冲突面是“某些用户可能希望快速验证表达式”。这样改知识层之前你就能判断这条新知识会不会把其他行为带歪。另外知识层不是越满越好。塞进去的知识如果和当前场景无关反而会稀释模型对重点规则的注意力。SkillFocus 强调的是“必要知识”而不是“全部知识”。我见过有人为了让翻译 skill 更专业把一百多条行业术语全塞进去结果常见句子的翻译质量反而下降了因为模型被大量低频术语干扰了。2.3 边界层比“能做什么”更重要的是“不该做什么”边界层定义 skill 的能力范围包括不该触发的场景、不该回答的内容、不该调用的工具、低于某个置信度时不该硬答。大部分 agent 开发者在写 skill 时几乎不关注边界层但 SkillFocus 论文里特别强调边界层是防止 agent 失控的第一道防线。举个安全相关的例子一个“翻译 skill”如果输入里出现敏感词它应该停下来询问用户怎么处理而不是尝试解释这个词。再比如一个“数据分析 skill”它不应该在没有授权的情况下尝试调用外网访问工具。边界层的本质是给 agent 的能力画一个围栏。我们做 agent 安全时很多问题本质上不是模型不行而是 skill 的边界层没写清楚导致模型在模糊场景下自由发挥。边界层也要避免写得太“死”。边界不是禁止一切灵活性而是明确哪些情况属于“能力范围之外”。比如翻译 skill 可以规定“如果输入中超过 30% 的内容是代码则不执行翻译直接返回原文并提示用户。”这就是一条有弹性、可执行的边界规则而不是笼统地写“不要翻译代码”。3. 动手实操用三层地图给 skill 做一次能力体检3.1 能力体检的四步流程先别急着改任何 skill先按下面四步做一次能力体检列出当前 agent 用到的所有 skill 清单。不要凭记忆去框架配置里一个个找出来。为每个 skill 画三层地图。可以用表格也可以用刚才的伪代码格式重点是让地图完整覆盖行为、知识、边界。标注问题类型。我习惯分三类空白区该有能力但没有、模糊区能力边界不清楚容易和其他 skill 重叠、冲突区多个 skill 对同一场景的反应互相矛盾。输出优先级排序。先解决冲突区再补空白区最后收紧模糊区。因为冲突区会让 agent 行为随机风险最大。注意这一步不用追求一次把所有 skill 都画完。你可以只挑用户反馈最多的两三个 skill 开始画完地图之后再做后续判断。慢慢来能力地图是沉淀资产画完的以后都能复用。3.2 体检表模板与示例我每次准备改 skill 的时候都会先填下面这个表格填完就能发现自己之前想改的方向是不是对的Skill 名称行为层定义知识层内容边界层定义问题类型改动建议会议纪要生成输入会议稿输出结构化纪要会议角色、行动项关键词、结论识别规则不输出逐字稿不解释会议背景冲突区调整知识层中“行动项”与“结论”的区分规则翻译助手输入源语言文本输出译文术语表、风格要求、常见语序规则不翻译代码不解释敏感词模糊区补充“遇到代码片段保持原样”的规则代码审查输入代码段输出审查意见编码规范、常见 bug 模式、安全注意事项不直接修改代码不执行代码空白区新增一个独立的“安全漏洞扫描”skill填完这个表你会发现自己原以为的改动方向可能并不是最佳方案。比如我想提升“翻译助手”的术语准确性但填表后发现它的模糊区是“代码和文本混排时处理方式不一致”那么这次改动的重点就不只是术语而是边界层规则。3.3 如何判断“拆清楚”了判断标准很简单如果有人拿着你的三层地图不需要看 skill 原始 prompt就能准确说出这个 skill 的职责和能力边界。另外当你要修改 skill 时能够明确指出这次改的是哪一层以及会影响到哪些行为。如果说不出来说明还没拆清楚先不要动手。我自己还有一个附加判断拿一个典型的测试用例问自己“这个用例会不会被地图里描述的边界规则拦截”。如果答案是“可能会”说明边界还不够明确。如果答案是“不会因为……”并且这个理由能在图上找到对应条目那说明拆得足够清楚了。4. 能力拆清之后skill 改动的四个落地动作4.1 拆分和合并什么时候该动结构能力体检之后如果发现一个 skill 的行为层有多个差异很大的输入输出对就应该考虑拆分。比如“文档处理 skill”同时承担“格式转换”和“内容摘要”这两个行为输入输出都不一致合并在一起只会让 prompt 越来越长触发冲突越来越多。拆开之后用户需要摘要时只加载摘要 skill需要转换格式时只加载转换 skill上下文也被精简了。反过来如果两个 skill 的知识层高度重叠且行为层也类似就应该合并。比如“用户反馈分析”和“评论情绪判断”底层都依赖情感词库合并成一个“文本情绪分析”skill 可以减少重复上下文也方便统一维护情感知识。拆分和合并的原则不是“职责单一”或者“粒度越小越好”而是“行为差异足够大才拆知识重叠足够高才合”。4.2 改写 prompt 时锁定“能力锚点”能力锚点是指那些已经被验证稳定、用户反馈良好的能力。在改写 skill 时先找出锚点并在新版本中保持锚点的行为绝对不变。具体做法是用行为层定义来标记锚点比如“输出结构化纪要”是锚点那么不管知识层怎么改都不能改变输出格式。锚点需要固化下来变成可执行的测试。比如“输入包含三句话的会议稿输出必须包含‘结论’字段”“输入翻译请求输出必须保留原文的换行格式”。把这些断言写成简单的测试脚本放到 skill 的测试目录里每次改动后跑一遍。我自己的项目里每个 skill 都对应一组锚点测试运行 agent 框架自带的回归命令就能自动验证。锚点测试没过说明这次改动动到了根子上必须回滚。4.3 处理 skill 之间的冲突触发优先与互斥规则Agent 框架中经常会有多个 skill 同时被触发的情况。SkillFocus 的思路是冲突不在运行时解决而是在设计期解决。具体操作在能力地图中标注依赖关系和冲突关系。比如“会议纪要生成”依赖“时间识别”工具“日程安排”也依赖“时间识别”但两者通常不会同时触发因为输入场景不同。对于可能同时触发的情况要明确优先级。例如“生成代码”和“解释代码”如果同时满足触发条件应该先执行“生成代码”因为用户更倾向于先看到结果再听解释。在 skill 定义中加入规则字段比如name: generate_code priority: 10 conflict_with: explain_code如果在 agent 框架的编排层看到conflict_withharness 就会在处理完高优先级 skill 后重新评估低优先级 skill 是否还需要触发。这里顺便说下 harness 和 agent 的区别harness 是调度容器负责加载 skill、管理上下文、执行工具调用agent 是决策实体。能力拆解的结果最终要交给 harness 来编排才能减少冲突。很多 agent 项目出问题就是因为 skill 定义里没有这些规则harness 只能随机选一个执行。4.4 回归验证用对比实验确认改动是进化还是退化改完 skill 后不能只靠一两个用例判断好坏。我至少准备三类用例锚点用例检验之前稳定的能力有没有被破坏、目标用例这次想改进的场景、对抗用例以前容易翻车的场景。分别跑新旧版本记录通过率和输出质量评分。跑测试时要注意一件事顺序。模型对输入顺序有一定敏感性如果你的目标用例总是排在前面可能只是位置带来的偏差。把用例的顺序打乱多跑几轮然后取平均分。如果目标用例提高了但对抗用例下降了说明利弊参半需要继续调整如果锚点用例挂了必须回滚。我在实践里遇到最多的情况是目标用例提升了 30%对抗用例下降了 50%这种改动看似“有效”实际净收益是负的。用表记录一下用例类型旧版本通过率新版本通过率影响判断锚点用例100%60%严重退化回滚目标用例40%90%目标达成对抗用例80%30%出现新的误伤需重新设计5. 几个容易踩的坑和我的排查思路5.1 拆解后发现能力全是缺口怎么办体检结果可能很惨所有 skill 都有一堆问题。这时候千万不要全改。按优先级排序先改影响用户最频繁的一两个其余记录到 backlog。因为一次性改动太多你根本定位不了问题。我踩过这个坑连续改了五个 skill结果 agent 整体表现下降最后只能靠 git 回滚白忙了两天。正确做法是把体检表当成产品需求池每次迭代只取出一个“冲突区”或“空白区”问题来解决。改完、测完、发布观察几天确认没有新问题再动下一个。这个过程很慢但很稳。5.2 改完 skill 后 agent 反而变笨了先查这三个地方第一检查是否动了能力锚点。这是最常见的翻车原因改着改着顺手改了一个输出格式导致下游依赖全部失效。第二检查知识层新增内容是否与已有规则冲突。比如你加了一条“遇到不确定的回答要坦诚说明”但原有规则里有一条“所有回答必须给出确定性结论”这两条直接打架。第三检查边界层是否被意外放宽。为了修复“漏答”问题让 skill 在低置信度时也给出答案结果开始瞎编这是典型的边界放宽导致的退化。我的经验是90% 的“变笨”发生在边界层。你想要一个更宽容的行为结果把禁区也开放了。修复方式不是放宽边界而是增加一个分支处理低置信度场景。比如边界规则写成“如果置信度低于 60%返回‘我需要更多信息’并列出缺失项”而不是“无论什么都给出答案”。5.3 避免“过度拆解”导致 skill 碎片化有时候拆得太细一个功能变成了五个 skill结果上下文里塞满了 skill 描述反而干扰模型判断。SkillFocus 的拆解原则很明确行为层差异足够大才拆知识层重叠足够高才合。不要为了追求“职责单一”而把强耦合的能力拆开。我遇到过一个小型 agent早期只有一个“数据处理 skill”后来为了“结构清晰”拆成了“数据清洗”“数据转换”“数据校验”三个 skill。结果很多任务同时需要这三个能力模型每次都要加载三段描述上下文开销变大而且这三个 skill 的触发条件经常互相踩。最后又合并回去了。我的经验是如果两个 skill 经常需要同时被调用且知识层共享超过 50%就不要再拆了。6. 结合 SkillFocus 再看 agent skill 开发的几个现实问题6.1 skill 编码和框架的标准化当前各框架对 skill 的定义、加载方式、触发机制都有差异。有的用 markdown 文件有的用 yaml有的用 json。SkillFocus 提供的是方法论不绑定具体格式。你可以把三层地图抽象成约定的字段填到自己的 skill 模板里。例如在 skill 的 yaml 头部加入behavior: | input: ... output: ... trigger: ... tools: [...] knowledge: | - rule1 - rule2 boundary: | - forbidden1 - forbidden2这样做的好处是哪怕你换了一个 agent 框架只要把这三个字段翻译成新框架支持的格式能力定义就能保留下来。现在市面上有各种 agent 框架各有各的 skill 加载方式但底层对“能力边界”的需求是一致的。提前用统一格式记录能力地图能减少以后迁移框架时的返工成本。6.2 能力拆解和 agent 安全边界层和 agent 安全是直接挂钩的。在我做的 agent 项目里很多安全问题其实是“能力越界”导致的一个负责数据分析的 skill 被诱导去执行了系统命令。为了防这个问题我在边界层里明确写了“禁止执行任何系统命令”同时在 harness 层做白名单拦截。SkillFocus 的思路让我意识到安全不只是模型层、框架层的事skill 层也必须有自己的边界声明。把边界层写到 skill 里harness 做强制校验是双保险。Skill 文件里的边界声明相当于“意图”harness 的白名单相当于“能力”。如果 skill 的意图里写着禁止执行系统命令但 harness 白名单里却允许了那实际上还是不安全。所以我的建议是skill 编写者负责声明边界harness 配置者负责执行校验两边都要定期对齐。6.3 能力地图要跟着 skill 一起迭代每次改动 skill都要更新它的三层地图并保留旧版本。就像代码合入前要先更新注释一样能力地图就是 skill 的注释。这样后续维护时你能看到能力演进的轨迹为什么这个 skill 加了这条边界规则上次是因为什么拆分了行为有了这些记录后续维护者不用靠猜。我甚至会把能力地图直接写到 skill 文件的注释里。这样一来新同事接手项目时打开一个 skill 文件就能看到它的完整能力定义不用去翻文档、查历史记录。这一步做起来很简单但坚持下来后agent 项目的可维护性提升非常明显。最后分享一个我在实际项目里的小技巧每次改完 skill除了更新能力地图顺手补一条 changelog。格式不用复杂就写“这次改了哪一层、为什么改、影响了哪个行为、验证用例有哪些”。等过了三个月回头看你会发现这几行字比任何口头说明都管用。SkillFocus 给我的启发也就在这里它不是告诉你“怎么让 agent 变强”而是告诉你“怎么让自己知道你改了什么”。想清楚这一点skill 的持续进化才真正开始。
阅读完成 · 觉得有帮助?