☰
Agent技能体系设计:从Prompt膨胀到可维护的Skill层
2026/9/25 12:45:02 网站建设 项目流程

过去三个月,我一直在跟"agent-skills"这四个字较劲。起因是自己维护的Agent项目越来越难维护:Prompt里堆了十几个技能说明,模型的调用准确率不升反降,每次改一个技能都像拆地雷。后来我把所有零散能力抽成了一套独立的技能层,走了不少弯路,也踩了不少坑。这篇文章不铺垫概念,直接把这套体系的拆分思路、落地细节和翻车经历完整过一遍。如果你正在做Agent相关应用,或者正准备把一堆提示词和工具调用整理成可维护的结构,这篇应该能给到不少参考。

1. 先想清楚:agent-skills 到底在治什么病

1.1 从 Prompt 越堆越臃肿开始说起

最早期我的Agent其实没有技能层的概念,所有能力都写在System Prompt里:"你可以整理会议纪要""你可以生成周报""你可以调用日历API"……当只有三五个能力时这个方案还能转,但随着业务加码,Prompt很快膨胀到接近两万字。这时候你会发现几个非常典型的症状:模型开始出现"指令失忆"——你让它同时记住十几个技能的使用条件和输出规则,它总是记了后面忘前面;上下文窗口被大量静态描述占用,真正跟用户对话相关的上下文反而被挤掉了;每次修改一个技能描述,都可能影响其他技能被识别的概率,回归问题防不胜防。

后来我在一个模型可观测性工具里看到了一组数据:当System Prompt里技能说明超过十个之后,技能召回率从92%掉到了76%,误触发率还翻倍。那段时间团队正好在做基准测试,我用同样的测试集反复跑了好几轮,三个模型无一例外,技能相关的指令一多,整体表现就是往下掉。这个数据说服了我:必须把"能力说明"从"主对话上下文"里剥离出去。这就是我最初想做agent-skills的动因。不是跟风,是被问题逼出来的。

1.2 Skill、Tool、Plugin、Workflow 的真实边界

先给结论:我不觉得Skill和Tool是按代码量划分的,核心区别在于决策点的多少。一个Tool可以是一个简单的函数,输入输出固定,没有任何内部逻辑分支,比如"查询天气""获取当前时间"。而一个Skill通常包含多个步骤、多个工具调用路径,甚至内置判断逻辑,比如"整理会议纪要"可能要经过转写、摘要、待办提取、按模板排版四步,中间还要判断会议里有没有出现待办事项。再比如"规划出差行程"这个Skill,内部会去查天气、查交通、安排会议时间、生成行程单,任何一个环节出了问题都要有对应的兜底逻辑,这明显不是单个Tool能覆盖的。

Workflow是确定性的流程编排,定义好节点之后每次跑都一样;Skill则可能内部有一个松散的流程,但允许模型根据输入做一定程度的自主决策,它向下可以复用多个Tool或子流程。Plugin更像是一种安装和分发形态,Skill可以被打包进Plugin里。如果在做项目时遇到"不知道用哪种形式"的情况,我的建议是:把单个确定性动作定义成Tool,把需要决策和编排的动作定义成Skill,把固定顺序、不允许模型自由发挥的流程定义成Workflow。

这套边界在实际协作里特别重要。边界不清晰会导致团队内部口径混乱——有人把"生成周报"写成Tool,有人写成了Workflow,等到系统里同类能力大量出现时,维护成本会指数级上升。项目初期就把这些名词定义清楚,后面能省下大量沟通成本。

1.3 一套技能体系的核心组成

定完边界,再说agent-skills体系的组成。我理解中一个完整的Skill包含四部分:技能声明、操作说明、执行资产、执行器。

  • 技能声明(Manifest):给模型看的"入口",包含技能名称、用途、触发条件、输入输出Schema。它决定模型什么时候调用这个技能,是技能层的脸面。
  • 操作说明(Instructions):技能被触发后注入给模型的详细指令,可以理解为一本使用手册,只有在被调用时才加载。
  • 执行资产(Assets):技能依赖的脚本、模板、知识库切片、外部API配置等。
  • 执行器(Runtime):负责加载、校验、运行Skill的宿主代码,通常对接Agent框架。

这四部分相互独立又缺一不可。声明决定了触发面,说明书决定了执行质量,资产是能力基础,执行器解决的是"怎么被安全地跑起来"。日常开发中,我们团队对一份技能改动最多的往往是声明和说明书,执行器和资产相对稳定,因为前者是"对外接口",一点语义变化都会影响调用;后者一旦稳定下来,就尽量不做大改。下一章重点讲我具体怎么设计这四部分。

2. Skill 的设计框架:我在项目里沉淀的拆分方法

2.1 触发面:什么时候该被调用

Skill声明里的description是整个技能系统里最重要的文本。很多项目的技能召回率低,问题不在模型,而在描述写得不讲人话。我见过大量类似"帮助用户进行会议纪要整理"这种描述——它太抽象了,模型很难判断什么场景算"帮助",什么情况不用。后来我沉淀了一套三段式写法:触发条件+典型输入+必做动作。

举个例子,这是我从项目里截取的一个技能声明描述:

触发条件:当用户提供会议录音转写文本、会议笔记,或者明确要求"把这次讨论整理成纪要"时触发 典型输入:会议原始转写/笔记、参会人列表、可能的议题背景 必做动作:提取关键决议、拆解待办事项、标注负责人和截止时间、按既定模板输出会议纪要

描述里尤其要写清楚"哪些情况不要触发"。我会在每条description后面加一段"不适用场景",例如"仅闲聊时不需要触发""如果用户只是问如何做纪要,请不要调用执行,而是直接回答方法"。加了负例之后,误触发率往往立竿见影地下降。之前我把"写周报"技能的负例写成"如果用户只是抱怨工作太累,不要自动生成周报",加上这句话之前,这个技能经常在负面情绪场景中被误触发,三个月里误触发日志占了一整页。

还有一个小技巧:把description写成一个可检索的短文本索引,而不是完整说明书。这样动态加载时,Router可以先通过向量检索或关键词匹配候选技能,只有选中的技能才把完整说明书送入上下文。这个优化能把"触发判断"的token开销降一个数量级,尤其适合技能数量多的中大型项目。

2.2 执行面:代码优先还是提示词优先

每个Skill内部可以选择纯提示词执行、纯代码执行、或代码+提示词混排。我建议按任务性质来选,而不是按团队技术栈来选。

执行类型适用场景优点缺点
纯提示词型内容理解、文本重写、结构化摘要、创意生成实现快、迭代快、适合"读懂语义"的任务输出不稳定,对格式和精度要求高的场景容易翻车
纯代码型调用API、文件解析、数据清洗、规则计算完全可控、可测试、结果稳定需要写大量代码,改动成本高
混合型大多数真实业务技能先用代码处理结构化部分,再用模型处理语义部分链路更长,需要定义清晰的"代码处理完交给模型什么数据"

我自己的默认策略是:能用代码解决的永远不要丢给模型。比如解析日期、合并列表、转换格式,这些交给模型不仅贵而且不可靠。模型只负责"理解"和"生成"的部分。有一次我优化一个报表生成技能,把日期标准化从提示词挪到了Python里,成功率从82%提到了98%,单次执行成本降了差不多一半。这就是代码优先的直观收益。

但纯提示词型也并非一无是处。在快速验证阶段,先用提示词把流程跑通,确认业务逻辑没有硬伤后再逐步替换成代码,是一种很高效的演进路径。别一上来就追求完美工程化,先让业务跑起来,再谈稳定性。

2.3 契约面:输入输出 Schema 的严谨程度决定稳定性

Skill本质上是模型与世界交互的中间层。中间层最怕的就是"接口不清晰"。模型传参经常丢字段、给错类型,如果你不做校验,后面的代码就要处理各种脏数据;如果你校验太死,模型一旦传错就直接报错,用户体感很差。我的策略是八个字:宽松入参,严格出参。

所谓宽松入参,是指当模型传入的参数不完整时,不立刻失败,而是允许Skill内部主动补全。比如生成周报时模型可能只传了"这周做了API重构",Skill可以通过上下文查询或模板默认值补全其他字段。严格出参则是指Skill返回给上层的数据必须符合Schema,字段缺失要显式补空,类型要严格转换,不能让上层消费方去猜。

下面是一个技能参数声明的JSON Schema示例:

{ "name": "weekly_report", "description": "生成结构化周报,供后续渠道分发", "parameters": { "type": "object", "properties": { "accomplishments": { "type": "array", "items": {"type": "string"}, "description": "本周完成的事项列表" }, "blockers": { "type": "array", "items": {"type": "string"}, "description": "遇到的问题,没有则传空数组" }, "plan": { "type": "array", "items": {"type": "string"}, "description": "下周计划" } }, "required": ["accomplishments", "blockers", "plan"] } }

注意required字段。我见过不少项目把required设置为全部必填,结果模型经常因为缺字段而报错;也有项目完全不加required,导致输出经常缺核心字段。正确姿势是只把"没有就无法继续"的字段设置为必填,其他字段用默认值兜底。在实现时,我会为每个Skill写一个独立的校验函数,入参阶段做"软化处理",出参阶段则严格按照Schema序列化,任何类型不一致都在这一步修正。

2.4 记忆面:Skill 的上下文隔离策略

上下文隔离是agent-skills里最容易被忽略的一环。很多Agent框架默认会把每个步骤的中间结果都保留在全局上下文里,导致执行一个技能后,上下文里堆满了一大段临时内容。这些问题一开始不明显,技能数量上来之后就会变成性能杀手。

我的做法是三层隔离。第一层:Skill内部产生的中间处理结果默认不写回全局上下文,只有最终输出通过Schema校验后才返回给主Agent。第二层:如果确实需要在多个Skill之间共享状态,我会定义一个State通道,显式声明哪些数据可共享,不使用隐式传递。第三层:每个Skill执行时都有独立的临时工作目录,目录名带Skill ID,避免文件覆盖。

这套隔离策略直接改变了系统的可维护性。以前改一个技能可能意外影响另一个技能,现在技能之间除非显式对接,否则完全独立,可以放心并行开发和测试。团队成员后来也习惯了"每个技能自带状态、但状态默认私有"的写法,跨技能传参必须走接口而非改全局变量,代码整洁度提升了一个档次。

3. 从能用到好用:演进过程中踩过的四个坑

3.1 坑一:Skill 描述里的隐含前提在换模型后全部失效

第一次规模性翻车发生在我把主模型从Claude切到GPT-4o的时候。切完之后,过去跑得好好的技能调用突然变得各种不听话:该触发的技能不触发,不该触发的反而触发。我最开始怀疑是温度参数问题,调了半天没用。后来逐条用固定的测试集跑两个模型的对比,才发现真正的问题在描述本身。

我的Skill描述里充满了"默认模型应该懂"但模型未必懂的内容。比如我当时写"当用户提到整理一下时,可以默认调用会议纪要技能",Claude能理解这是"整理"的宽泛语境,但GPT-4o更倾向于字面匹配"整理"这个词。也就是说,我把自己团队的"常识"当成了模型的常识。这类问题在单一模型上很难暴露,因为长期调试下来,描述已经被优化得和那个模型的脾气很匹配;可一旦换模型,所有隐性默契全部失效。

修复方案很直接:把所有隐含前提显式化。每个技能描述里增加"触发边界"字段,明确写出该触发与不该触发的正反例。一共改了四十多处描述,改完之后两个模型的触发准确率都稳定在了95%以上。这里也反映出一个原则:技能描述不应该为任何一个具体模型定制,应该尽量写成"人类也能看明白的说明书"——越客观、越具体,跨模型的鲁棒性越强。

3.2 坑二:多个 Skill 同时响应导致的任务竞争

第二个坑出现在技能数量超过十五个之后。用户说了一句"把这次会议纪要按照模板发给项目经理",结果系统同时命中了"会议纪要整理"和"消息发送"两个技能。两个技能各自执行完毕,主流程却不知道该用哪个结果,进程直接卡死。日志里看到的是两条技能链路同时在自己跑,互不相让。

排查时我还以为是Router有问题,后来把触发日志拉出来,发现两个技能的description都写了"当用户需要生成并发送XXX时触发",语义范围叠得很厉害。根因是每个技能只站在自己的角度描述适用范围,没有顾及全局的职责划分。单独看哪个描述都没错,放在一起就冲突了。

解法分两步。第一步在Router层加消歧逻辑:如果命中多个候选技能,先做一次LLM判断,输出"应该由哪个技能主导、其他技能是否作为子步骤参与"。第二步是给每个技能的描述增加"协作边界",主动声明"如果任务同时涉及生成和发送,本技能只负责生成,发送交给messaging技能"。这样等于把跨技能的冲突前置到了描述层,从源头上减少竞争。实施之后,类似的多技能冲突从每周七八次降到了几乎为零。

3.3 坑三:全局注入导致上下文被无关技能吃掉

第三个坑是我自己设计失误,不是模型的锅。当时为了让模型"随时准备好"调用任何技能,我把所有Skill的完整说明书都注入到了系统提示里。二十个技能,每个上千字,加起来接近两万字。表面上看是"模型什么都会",实际上上下文预算被静态文本占掉了一大半,用户对话稍长一点,模型就开始忘事,响应质量肉眼可见地下降。

我用日志统计了一下token分布:系统提示占每次请求token量的42%,其中真正被激活的技能相关token不到10%。也就是说,绝大多数技能说明只是躺在上下文中当摆设,还白花钱。这个数据出来之后,团队内部立刻达成共识:不能再"全量常驻"了。

修复思路就是动态加载。我在执行器里加了一层"技能索引":系统提示里只放每个技能的一句话摘要,当模型决定调用某个技能时,再把完整说明书放进上下文。这样上下文占用砍掉了将近七成,单次请求的成本也降了。动态加载是个老思路,但在Agent技能体系里它不是优化项,而是必须项。不过动态加载也不是没有代价,它要求Router本身足够准,如果索引摘要写得不清楚,模型可能在第一步就选错技能,反而比全量注入更糟。所以我会在技能索引摘要上花与完整描述同样多的精力。

3.4 坑四:动态注册技能时的命名与路径冲突

最后一个坑跟技能注册机制有关。为了支持团队多人并行开发技能包,我设计了一个动态注册目录,所有技能包按约定的目录结构上传后会自动加载。上线后没过多久就出了事故:两个技能包都带了一个名为utils.py的工具文件,后注册的包把先注册的覆盖了,导致前一个技能运行时各种奇怪报错。

排查过程其实不难,难在定位心态。第一反应以为是文件系统权限问题,查了很久没有头绪;后来打开加载器的日志,发现注册顺序与文件哈希变更完全对应,才意识到是覆盖问题。根因是加载器做合并时没有做命名空间隔离,两个目录里的同名文件被当成同一个模块处理了。

修复方案有三点。一是每个技能一个独立命名空间,相关工具函数和临时文件都放进以技能ID命名的目录,不允许跨目录引用。二是所有注册到全局的工具函数名统一加技能前缀,比如meeting_summary__generate_digest。三是注册阶段做冲突检测,发现同名工具或同名关键文件立即报错,而不是默默覆盖。经过这次教训,我把"宁可启动失败也不悄悄出错"定为技能注册的原则,这类隐性覆盖风险在协作开发中比想象中常见得多。

4. 质量闭环:我用什么方式持续衡量和打磨这套体系

4.1 评估集的设计:既要覆盖也要防过拟合

技能系统做到后面,最大的挑战不是实现而是回归。每次改一个描述或调整一个执行器,都可能影响几十个技能的表现。我后来建了一套比较轻量的回归评估集,核心思路是给每个技能准备正例和反例各十条左右,用它们组合成一批测试会话,每次改动后跑一遍,对比各项指标变化。

正例是"这个技能应该被准确触发并成功执行的场景",反例是"看起来有点像但不该触发的场景"。比如会议纪要技能的反例可以是:用户说"帮我把会议推迟到明天"——会议相关,但没有纪要整理需求,所以不该触发。反例的价值比正例更大,因为大部分系统的问题不在召回不足,而在误触发太多。我见过很多团队把评估集做得特别复杂,但反例占比极低,结果线上误触发率始终压不下来。

还要防过拟合。模型很聪明,如果你的评估集长期固定不变,它的表现会慢慢"记住"这些用例,导致测试分数虚高。我每两周会从真实对话日志里捞10到20条新的用户消息作为盲测用例,不提前告诉模型,跑完再看命中情况。这种盲测数据比精心构造的测试集更能反映线上真实水平。目前这套评估通过定时任务每天凌晨自动跑,早上起来看报告就行。

4.2 三个运行指标:调用准确率、执行成功率、上下文损耗率

我用来衡量技能体系健康度的指标不多,就三个,监控面板上也只放这三条曲线。

指标定义健康标准典型问题信号
调用准确率该调用的技能是否被调用、不该调用的有没有误触发90%以上误触发增多时优先查描述边界
执行成功率技能被调用后,是否产出符合Schema的合法结果95%以上执行成功率低时优先查执行体和资产质量
上下文损耗率单次技能调用中,总token消耗与"实际被模型有效使用部分"的差距控制在合理预算内损耗率升高说明技能加载过于臃肿

关于上下文损耗率,我需要解释一下。这不是一个标准名词,是我在项目里自己定的一个观测口径:把每次技能调用涉及的系统提示、技能说明书、知识库内容、历史对话等都算进消耗,再看最终输出对这个上下文的使用程度。比如一个技能加载了很长的知识库文档,但模型只用了其中一段,损耗率就偏高。关注这个指标能反过来帮助收敛技能设计——说明书能短则短,知识库能精简就精简。

这三个指标要放在一起看。单独看调用准确率没有意义,因为一个系统如果什么都不调用,触发准确率可能也高;执行成功率高但调用准确率低,说明技能没有在正确的场景使用;上下文损耗率虽然重要,但也不能为了降损耗牺牲执行质量。三者的平衡点需要根据具体业务反复调,没有一套万能参数。

4.3 迭代节奏:我如何决定一个 Skill 是重构还是删除

技能体系和代码一样,需要持续维护,不是上线就完了。我给自己定了一套比较简单的决策规则,用数据而不是感觉来判断。

第一,看调用频次。过去三个月调用次数少于五次的技能,基本上是一个"伪需求"技能,我会先并入更高频技能,如果三个月后还是没有调用,直接删除。第二,看成功率趋势。如果某个技能调用次数很多,但执行成功率长期低于85%,优先重构执行体,尤其是把里面"提示词负责的部分"逐步替换成代码。第三,看误触发情况。如果某个技能经常被错误触发,问题一般不在执行体,而在触发描述,优先改description而不是改代码。第四,看技能间关联度。如果两个技能在日志里频繁同时触发,我倾向于合并成一个技能,或者显式定义它们的协作关系,而不是放任它们各自为战。

每个技能上线时我都会在它的Manifest里写清楚版本号和负责人。后续任何改动都走"新增版本号"而不是原地覆盖,这样即使某个版本在线上出问题,也能快速回滚到上一个稳定版本。技能系统的演进不可能一次成型,它更像是搭积木——先把积木的种类和接口定清楚,再一块块替换、删除、重组。到后期你会发现,真正让你Agent能力上限变高的,不一定是某个单点技能的惊艳表现,而是这套技能集合能否稳定地、低成本地协作。

最后再补充一个我做技能设计时的习惯:每次新建一个技能,我都会先写一份"一页纸说明"给一个完全不了解这个项目的人看,如果对方看完能复述出"什么情况该用、什么情况不该用、输出大概长什么样",我才把它转写成技能描述。这个习惯帮我挡掉了不少设计漏洞。希望这套从设计到迭代的经验,能让你在做类似方向时少踩几个坑。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询