智能代理(Agent)这几年是真火,但火归火,真正能把 Agent 从“聊天机器人”推向“能独立干活”的,靠的还是底层那一层不起眼的积木——技能。我经常跟团队说:Agent 跑不通,十有八九不是模型不够聪明,而是技能层设计得太糙。这次围绕 “agent-skills” 这个标题,把我自己从零搭建技能体系、给 Agent 装手装脚的过程做一个完整复盘,包括技能怎么描述、参数怎么设计、状态怎么管理、测试怎么做、上线后怎么排查,一次性讲透。
这个内容适合谁?一种是正打算把 Agent 从 Demo 推向生产的开发者,另一种是已经上了 Agent 但发现“模型偶尔聪明、经常抽风”的团队。看完你能得到一套可以直接抄作业的技能开发规范,外加一堆我踩出来的坑。
1. 内容整体设计与思路拆解
1.1 为什么“技能”是 Agent 落地的第一道门槛
很多人最开始做 Agent,想法很简单:把大模型接上提示词,再给它几个函数,它就能像人一样干活。结果一测试就发现,模型要么压根不调用工具,要么调用了但参数传得乱七八糟。这问题不在模型,在于你给模型提供的不是“技能”,而是一堆孤立的函数签名。
技能和普通工具函数的区别,我可以打个比方:工具函数像是一个新员工手里的螺丝刀,技能则是这个员工的一整套标准化动作。比如“处理一封客户投诉邮件”,技能里不只有“调用邮件接口”这个动作,还包括了如何判断邮件情绪、如何确认是否属于投诉范畴、如何起草回复、如何归档、事后如何标记跟进状态。也就是说,技能是把目标、触发条件、调用路径、参数约束、执行步骤、输出规范、边界条件和失败处理打包成一个完整的可复用单元。
所以 Agent 落地的第一道门槛,就是能不能把业务需求拆成“模型可理解、可调用、可执行、可验证”的技能单元。拆不好,后面评估、调优、扩展全都难办。
1.2 技能拆分的核心方法论:从业务流到技能图
我习惯做技能拆分时先画一张技能图。这张图不是流程图,而是“目标——技能——子技能”的树状结构。
拿一个最常见的场景举例:做一个“会议助手 Agent”。粗看只需要一个技能“处理会议”,但真正落地上线,你会发现要拆成多个子技能:提取会议要素(时间、地点、参会人、议题)、生成会议纪要、整理待办事项、根据待办时间戳提醒用户、检索历史会议记录。每个子技能对应一个独立的技能文件,模型在收到用户请求时,先理解意图,再决定调用哪个或哪几个技能,必要时串联执行。
拆技能有个原则:粒度适中。太粗,模型不容易理解触发边界,容易误用;太细,模型在意图路由时选择困难,调用链条一长就出错。我自己的经验是:一个技能应该对应一个“完整但单一”的任务目标,执行时间控制在几秒到半分钟内,输入输出边界清晰。
1.3 为什么现在这个时间点必须认真沉淀 Agent Skills
如果你去看今年主流模型平台在 Agent 方向的动作,不难发现一个共同趋势:都在推“技能市场”。底层逻辑很直接——大家发现模型能力再强,面对真实世界的长尾任务,光靠内置知识根本不够,必须让 Agent 能动态获取、加载、执行外部技能。
另一个原因是可组合性。技能如果设计得好,不同业务线之间可以像乐高块一样复用。我在团队里见过最典型的案例:数据团队做了“SQL 查询生成”技能,后来财务团队做“报表自动核对”Agent,直接复用这个技能,只换了数据源认证和输出格式校验部分,开发时间从两周压缩到两天。
这个时间点,谁能先把技能体系沉淀下来,谁后面建 Agent 就像搭积木,否则永远在从零开始。
2. 核心细节解析与实操要点
2.1 技能描述(Skill Description)怎么写模型才愿意听
技能描述是整个技能文件里最重要、也最容易被写砸的地方。很多人把描述写成给开发者看的功能说明书,比如“提供邮件发送服务”。但模型读描述是为了做两件事:第一,判断当前任务和这个技能是否匹配;第二,判断任务里的关键信息能不能填进这个技能的参数。所以描述的核心是“触发条件”,不是“功能清单”。
我总结了一个描述公式:
这个技能适用于什么场景(用户意图特征)
它执行什么动作(结果导向)
它不适用于什么场景(负面排除,非常重要)
关键参数有哪些,什么格式(模型填参的必要提示)
举个例子,“发送邮件”技能的描述,如果只写“发送邮件”,模型在处理“帮我把报价单发给王总”时,往往搞不清楚收件人“王总”应该查通讯录还是直接写在收件人参数里。好的描述应该是:当用户希望把内容发送给特定联系人时使用本技能。支持通过联系人姓名匹配通讯录,匹配不到时返回候选列表供用户确认。如果用户只是表达发送意愿但缺少收件人或正文,应主动调用信息补充子技能,不要自作主张生成地址。
描述写好之后,一定要做意图路由测试,拿真实用户语句去跑一遍,看看模型能不能准确选中这个技能。选不准,别急着骂模型,先回头看描述。
2.2 参数设计:输入输出的边界和约束才是关键
参数设计不好,直接导致技能执行阶段翻车。我在实操中总结出来的经验,用一句话说就是:参数不是给后端程序看的,是给模型填的。所以每个字段都要考虑模型有没有能力从对话里正确提取。
首先参数数量要克制。我见过一个技能给模型开了 15 个参数,结果模型每次都在里面瞎猜三四个无关字段。除非必要,参数控制在五到七个以内,可选参数尽量少,必要时用“根据上下文自动获取”的方式,不要让模型猜测业务侧才有权限知道的信息。
其次是格式约束。模型天然倾向于填自然语言字符串,比如日期字段用户说“周五”,模型可能直接填“周五”而不是具体的日期解析结果。所以日期、枚举类型字段,必须在描述里明确格式,甚至给一个示例。我当时做过一个报销系统 Agent,字段“费用类型”允许值只有【交通、餐饮、住宿、办公、其他】,因为描述里没写清楚,模型给填了“打车费”,后端校验直接抛异常。后来在描述里加了示例,准确率从 62% 提到了 95%。
输出边界也很重要。技能的输出不只是给用户看的一句话,更应该是“结构化的结果 + 友好的解释”。我会让每个技能统一返回一个包含 status、result、message、next_actions 的对象。这样上层 Agent 既能拿结构化数据做后续决策,又能拿到一条给用户的自然语言消息。统一输出格式,对后面做多技能编排、结果拼接、失败重试都有极大帮助。
2.3 状态管理与技能链(Skill Chaining)的衔接技巧
单技能跑通之后,必然要面对多技能配合的问题。比如“处理一封邮件并创建待办”,首先要调用“邮件解析技能”,再根据解析结果创建待办,中间还可能需要“联系人查询技能”来补全任务负责人。
这里最核心的实践经验是:不要在单个技能内部硬编码调用其它技能,而是通过返回结构化结果和 next_actions,让上游的 Agent 来做路由决策。技能之间保持解耦,每个技能只干自己的事,最终由 Agent 编排层决定下一步调用谁。
状态管理方面,我强烈建议引入一次会话内的技能调用上下文(Context Buffer)。每次技能执行完,把关键状态字段追加进去,比如“当前处理对象是邮件ID 2837”“已确认费用类型为住宿”。这样即使模型本轮忘了,也能从上下文里捞回信息,减少重复问答。
3. 实操过程与核心环节实现
3.1 从零实现一个“邮件摘要 + 自动归档”技能包
用一个具体例子把上述内容串起来。我以“邮件摘要 + 自动归档”技能包为演示,这是目前我做过所有技能里面最简单、但最适合讲清全流程的一个。
先做技能拆分。这个场景拆成三个子技能:fetch_email(根据邮件ID或主题关键词拉取邮件正文)、summarize_email(对邮件正文做结构化要点抽取)、archive_email(标记归档并移动到指定文件夹)。三个子技能按顺序执行,合成一个完整流程。
然后写技能文件。每个子技能用如下结构组织:
name: fetch_email description: > 当用户需要查看、处理、总结某封邮件时,先调用本技能获取邮件全文。 支持按邮件ID精确获取,也支持按主题关键词模糊搜索,搜索结果超过5条时返回邮件列表并等待用户选择。 本技能不负责生成摘要,不负责回复邮件。 input: email_id: type: string required: false description: 六位数字邮件ID,示例:283745 keyword: type: string required: false description: 主题关键词,用于模糊搜索 output: status: string message: string data: content: 邮件全文 sender: 发件人 datetime: 收件时间写完之后,用一组测试用例验证模型能不能正确填参。比如用户说“帮我看看昨天那封来自财务部的邮件”,模型应该填 keyword=财务部,而不是瞎编一个 email_id。这里的关键是,描述里的“本技能不负责生成摘要”这一段起了作用,它把模型的其他猜测挡掉了。
3.2 技能测试与回归:如何验证技能在不同模型上的表现
技能开发完不能直接上生产,先过三关测试。
第一关,意图路由测试。准备至少五十条典型用户语句,覆盖正常表达、模糊表达、混合意图表达三类。跑完后统计技能命中率,低于 90% 基本不能上线。
第二关,参数提取测试。对每一条语句,检查模型填出的参数是否正确。重点看三类错误率:漏填、错填、多填无关字段。比如用户说“把上周的周报发给我”,模型不应该自作主张填一个 Week=2024-W40,除非当前日期信息在上下文里明确提供。
第三关,全链路回归测试。模拟一个真实会话,连续调用多个技能,验证状态传递是否正常、结果是否一致。
我建议这套测试用脚本固化下来。技能迭代的时候,改动描述或参数后跑一遍全量回归,防止“修好一个用例打断了另外三个”。我自己就是把测试用例写成一个 JSONL 文件,跑的时候逐条灌进对话流程,最后汇总准确率报告。
3.3 技能版本管理与热更新机制
技能和代码一样,一定要做版本管理。我见过的常见事故是不管版本,直接在线上改描述文件,导致同一批请求里模型行为不一致,一会儿走新逻辑一会儿走旧逻辑。
我的做法是:每个技能文件头部加 version 字段,改动后递增版本号,并存到独立的 Git 仓库。上线时通过发布系统确认当前生效版本,线上只引用发布锁定的版本。另外至少保留上一版本,以便出现问题时秒级回滚。
热更新需要谨慎。我建议遵循“灰度优先”原则:先切 10% 流量到新版本,观察一两个小时,如果命中率、耗时、错误率没有恶化,再逐步扩到全量。技能更新时,关注的不只是功能本身,还有调用耗时的变化——因为描述变长可能导致推理时间上升,对这个要心里有数。
4. 常见问题与排查技巧实录
4.1 模型就是不调用技能怎么办
这个问题我遇到太多次了。用户表达很明确,技能也匹配,但模型就是不给 function call,非要用自己的知识硬答。
首先排查描述是否清晰。如果技能描述里全是功能名词而不是触发条件,模型很容易把它忽略。其次是上下文里是否有更抢眼的指令,比如系统提示词里说了“你是一个知识助手”,模型倾向直接回答。建议检查一下系统提示词有没有弱化工具角色。
还有一种情况是技能参数太复杂,模型在犹豫之后选择了放弃调用。如果连续多次不调用,可以降低参数复杂度,或者把技能描述中的触发示例增强,明确告诉模型“遇到这种情况必须调用”。
4.2 技能执行结果不稳定:同一个输入,不同回答
不稳定主要来自两个源头:第一是模型参数(特别是 temperature),技能调用时建议把 temperature 控制在 0 到 0.2,别让模型在 JSON 里自由发挥。第二是输入上下文波动,比如用户前一轮说的信息和后一轮冲突,模型犹豫时在不同技能间横跳。
排查技巧是:把完整对话轨迹里模型每轮的中间推理、工具调用选择记录下来。我用的方法是给每次调用打 traceID,记录完整的 prompt(包括技能文件内容)、模型输出、最终执行结果。出问题时拿 traceID 反查,看模型到底在我哪个环节“跑偏了”。
4.3 多技能冲突与召回混淆
技能库大了以后,召回是必然问题。比如同时有“发送邮件”和“发送微信消息”两个技能,用户说“发消息给张总”,模型可能两个都选中,或者选一个错误的。
解决思路有两个:一是在描述里做互斥声明,比如“本技能特指微信消息发送,若用户提到邮件请调用 send_email 技能”;二是引入技能路由前置模型,先用一个轻量模型做意图分类,只把候选的三到五个技能传给主模型去选,降低干扰。我们上线超过十个技能后,就采用了第二种方案。
4.4 技能内错误处理:不能把异常裸奔给用户
技能执行失败时,绝不能只抛个“执行失败”就完事。用户听到这种回复没用。我的规范是:技能内部要捕获异常,转化成“可解释的失败”,并给出建议动作,比如“邮件发送失败:收件人邮箱不合法,请检查后再试”或“会议纪要生成失败:音频转写超时,建议重新上传文件”。
这不仅是体验问题,更是数据结构问题。上层 Agent 收到 status=failed 和可读原因后,才有机会通过补充信息、调用替代技能的方式自动恢复。如果你的技能失败后没有结构化原因反馈,Agent 只能陷入死循环。
5. 技能质量评估与协作落地
5.1 技能质量评估指标体系
技能上了生产,不能只看“看起来能用”,要建立持续监控的指标体系。我常用的指标有四个:
| 指标 | 定义 | 关注时机 |
|---|---|---|
| 意图命中率 | 正确选中技能的比例 | 描述修改后 |
| 参数完整率 | 必填参数成功提取的比例 | 参数设计调整后 |
| 执行成功率 | 技能内部流程正常完成的比例 | 依赖服务变更时 |
| 用户反馈满意率 | 后续消息中负面表达的比例 | 持续监控 |
这四个指标在线监控时,如果发现意图命中率下降,先查最近的描述变更记录。之前有一次我们只是把描述中一个关键词从“PDF 文件”改成“文档”,结果命中率掉了 15 个点,因为模型原来学到的触发信号变了。改技能描述一定要谨慎,每次都要回滚预案。
5.2 技能库的组织与协作规范
最后,当技能数量上了规模,协作规范比技术更重要。我的团队内部有两条硬规矩:第一,任何技能必须有 owner(负责人),负责维护描述、参数、测试用例和线上指标;第二,技能改动必须走评审流程,不只评审代码,还要评审描述文本,因为在技能体系里描述就是产品逻辑的一部分。
技能目录的组织也推荐按业务域分层。比如 /domain/crm /domain/erp /common/notify 这种结构,公共技能放 /common 下,业务技能放各自域下。这样既方便复用,也能在做领域隔离时谁也不影响谁。
最后再分享一个小技巧:定期做一次技能清理大会。把半年内没人调用的技能全部下线归档,把调用率高的技能找出来做一次描述规范化。这是我到目前为止,对团队帮助最大的一个动作,比调任何模型参数都见效快。