
智能代理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% 基本不能上线。第二关参数提取测试。对每一条语句检查模型填出的参数是否正确。重点看三类错误率漏填、错填、多填无关字段。比如用户说“把上周的周报发给我”模型不应该自作主张填一个 Week2024-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 收到 statusfailed 和可读原因后才有机会通过补充信息、调用替代技能的方式自动恢复。如果你的技能失败后没有结构化原因反馈Agent 只能陷入死循环。5. 技能质量评估与协作落地5.1 技能质量评估指标体系技能上了生产不能只看“看起来能用”要建立持续监控的指标体系。我常用的指标有四个指标定义关注时机意图命中率正确选中技能的比例描述修改后参数完整率必填参数成功提取的比例参数设计调整后执行成功率技能内部流程正常完成的比例依赖服务变更时用户反馈满意率后续消息中负面表达的比例持续监控这四个指标在线监控时如果发现意图命中率下降先查最近的描述变更记录。之前有一次我们只是把描述中一个关键词从“PDF 文件”改成“文档”结果命中率掉了 15 个点因为模型原来学到的触发信号变了。改技能描述一定要谨慎每次都要回滚预案。5.2 技能库的组织与协作规范最后当技能数量上了规模协作规范比技术更重要。我的团队内部有两条硬规矩第一任何技能必须有 owner负责人负责维护描述、参数、测试用例和线上指标第二技能改动必须走评审流程不只评审代码还要评审描述文本因为在技能体系里描述就是产品逻辑的一部分。技能目录的组织也推荐按业务域分层。比如 /domain/crm /domain/erp /common/notify 这种结构公共技能放 /common 下业务技能放各自域下。这样既方便复用也能在做领域隔离时谁也不影响谁。最后再分享一个小技巧定期做一次技能清理大会。把半年内没人调用的技能全部下线归档把调用率高的技能找出来做一次描述规范化。这是我到目前为止对团队帮助最大的一个动作比调任何模型参数都见效快。