资讯动态

Agent技能化改造:从工具函数到技能注册与编排的工程实践

发布时间:2026/10/28 7:12:15 来源:尧图企业网站定制
Agent 技能化改造一直是个热门话题但真正把 agent-skills 落到生产环境的人大多经历过一段工具调用一时爽排错火葬场的时期。我自己的项目从最早硬编码几十个 function call到后来重构为完整的技能注册与编排体系中间踩过的坑、推倒重来的设计比写业务代码的时间多得多。这篇内容就当是给自己的项目做一次复盘也希望能给正在做 Agent 技能层设计的各位一些参考。先说结论agent-skills 不是简单地把工具函数注册给模型就完事它是一整套关于如何让模型正确理解、选择、执行、编排能力的工程方法论。1. 为什么智能体需要技能而不是工具函数1.1 从一次失败的天气查询说起我最早做 Agent 原型时参考网上大部分教程的做法——定义几个 JSON Schema塞进 OpenAI 的functions参数。当时跑通了一个 Demo能查天气、能设闹钟感觉很顺利。但放到真实业务场景里问题立刻炸开用户问明天去杭州出差适合穿什么衣服我注册的天气查询函数有两个参数city和date。模型确实调用了这个函数但传参是city杭州、date2025-07-15假设今天 7 月 14 日。看起来没错可返回的温度是 22°C~28°C模型最终回答建议穿短袖——杭州夏季室内外温差大、公共场所冷气足一件单薄短袖其实不够。问题不在模型而在于我的工具函数只解决了获取原始数据没有解决如何针对用户意图做更完整的推理。这催生了我对技能的重新理解工具函数是能力的最小原子但不该是 Agent 暴露给模型的最小单元。1.2 技能是目标导向的行为封装我后来定义的技能必须包含三个层次意图匹配层这个技能解决什么问题、在什么场景下被唤起参数与依赖层需要什么输入、依赖哪些其他技能或数据源执行与反馈层如何执行、失败后的兜底方式、结果如何翻译成自然语言回答举上面穿衣服的例子我不该直接暴露天气查询而是暴露穿衣建议技能。这个技能内部封装两步先调用天气数据源拿温度和降水概率再结合季节、地域温差、用户出行场景做一层规则判断或模型二次推理最后输出建议。这就是工具函数和技能的差别技能是为某个完整任务目标服务的它可以内部串联多个基础能力并且承担一小部分决策逻辑。1.3 从模型视角理解技能的价值如果你只从工程视角看技能和工具函数的代码差异无非是多包了一层。但站在模型的角度差别非常大。模型的推理过程是逐 token 进行的它的注意力是有限资源。当一个模型面对 30 个扁平注册的 function它需要先从所有 function 里挑一个或几个再组织参数。但如果你把 30 个函数归类、封装成 8 个技能每个技能的描述是什么情况下用它、它内部会做什么、最终返回什么形态的结果模型的决策路径就从30 选 1简化成8 选 1准确率显然会提升。更重要的一点扁平工具列表经常导致模型把多个步骤的复杂任务拆解成连续多次工具调用中间的上下文开销和错误风险都成倍增加。而技能化封装后一个复杂任务变成一次调用后续执行逻辑在引擎内部完成。这既减少了模型的推理负担也降低了系统整体延迟和出错概率。2. 技能的结构化表达从自由文本到可校验的 Schema2.1 技能描述是给模型看的产品说明书技能化改造初期我犯过一个典型错误把技能描述写得很工程师化里面全是技术术语比如调用 travel_advisor_service 获取目的地 рекомендации。结果模型经常学不会在正确的时机使用技能。后来我请教了一位做 NLP 的老前辈他一句话点醒我你写的描述是给模型看的说明书不是给代码看的技术文档。模型理解自然语言你要用场景化语言描述而不是接口文档语言。现在我的技能描述模板是这样的技能名字简短、语义化动词开头What做什么一句话说明技能产出什么结果When何时用两到三句话描述典型触发场景覆盖常见用户表达When not何时不用明确排除易混淆但不应使用该技能的场景Input输入需求需要模型从对话中提取的关键实体/参数Output输出约定返回给用户的回答风格、结构以我之前做的会议纪要技能为例它的描述片段是技能名称: generate_meeting_minutes What: 根据会议的原始转写文本生成结构化会议纪要包含议题、决议、待办事项。 When: 用户说把刚才的会议整理成纪要帮我记一下这次讨论的重点会议记录发我一份或检测到会议结束后的整理诉求。 When not: 如果用户只是询问会议时间、会议地点不要使用该技能。如果会议尚未发生不应调用应告知用户需要会后整理。 Input: 需要从对话或上下文中获取会议转写全文或关键片段若缺失应主动向用户索要。 Output: 严格按照 议题-讨论过程-决议-责任人-截止日期 五段式输出。这一段文字看起来简单但对模型行为的影响非常大。改完描述后会议纪要技能在盲测数据上的触发准确率从 72% 提升到 91%。原因是之前描述里写的是提取并总结对话内容生成 JSON 格式数据模型理解的目标是产出结构化数据而不是生成一份用户能直接阅读的纪要。2.2 参数设计的最少必需原则技能参数设计遵循一条核心原则能推断的绝不要求显式传入能合并的绝不分拆。很多 Agent 工程师是从 Django 写 CRUD 转过来的习惯把参数设计成 RESTful 风格——id、type、status 各种字段一应俱全。但技能参数是给模型看的模型需要从对话里抽取实体进行映射参数越多抽取出错率越高。我设计参数时用了一个三重问法这个参数会被执行器真的用到吗如果只是作为透传值传到下一个工具那说明技能拆分有问题模型能稳定地从对话中抽取这个参数吗抽不出来的参数宁可设计成可选兜底逻辑参数能否合并成更高层的语义对象比如城市日期可以合并为出行计划对象尽管后端会拆开用但模型只需要理解一个输入同样以穿衣建议技能为例第一版我定义了city、date、occasion场合、activity活动类型四个参数。实际测试中模型经常从出差里推断出occasion商务、activity会议但这两个字段后端根本用不上它们的真实作用是帮助模型理解用户需求然后由技能内部的规则模块根据季节数据做判断。所以最终版本我把参数削成两个destination目的地城市和travel_date出行日期occasion和activity降级为可选提示项不列入必传参数。2.3 输出 Schema 的稳定性大于一切技能的输出格式一旦被 Agent 主流程消费就必须保持高度稳定。我在项目里统一使用 JSON 作为技能输出格式每个技能的输出 Schema 都有严格的 JSON Schema 定义并加上了版本号管理。为什么这么较真因为技能的输出会被上游的 Agent 编排器读取并决定下一步动作。如果某个技能今天输出{status: success, data: ...}明天改成{code: 0, result: ...}编排器里的判断逻辑就全乱了。我在项目里吃过这个亏——一个第三方技能包升级后把原有的orders字段名改成了order_list导致整个订单同步流程静默失败了两天直到用户投诉才发现。现在我的做法是所有技能输出必须通过 JSON Schema 校验不通过直接标记技能调用失败Schema 变更必须走版本升级流程旧版本至少并行运行一个迭代周期技能内部字段命名遵循统一规范状态用status数据主体用data错误信息用message关联资源用resource_id3. 技能注册与发现的工程实践3.1 技能注册中心一份集中式的技能清单技能多了以后最怕的就是哪哪都有技能定义但谁也说不清一共多少个。我第一版技能代码是散在项目各个目录里的有的技能写在services/skill_weather.py有的写在handlers/skill_calendar.py。结果到后期想盘点能力时只能靠人肉 grep还经常漏掉。重构后我加了一个技能注册中心——本质上是一份集中式的技能清单包含skills: - name: generate_meeting_minutes version: 2.1.0 enabled: true owner: team_productivity entrypoint: skills.meeting_notes.execute schema_ref: schemas/meeting_minutes_output.json tags: [meeting, productivity, text_summary] timeout_ms: 8000 retry_count: 2这份清单的核心作用有三点技能入口统一编排器只认注册中心里的entrypoint不关心技能代码在哪个目录版本与归属清晰每个技能有明确的负责人、版本号出现问题能快速找到维护方启停可控线上如果某个技能异常可以在注册中心直接enabled: false摘除不等发版3.2 技能发现Agent 主流程如何找到正确的技能注册中心只是让技能被管理起来真正难的是让 Agent 主流程在运行时找到该用的技能。我的方案是把技能发现分成两个阶段第一阶段基于意图的粗筛当用户请求进来首先跑一个轻量的意图分类器把请求映射到一至三个候选技能域。这一步我用的是 embedding 相似度 关键词规则的双路召回。Embedding 负责语义泛化比如用户说帮我总结一下上午开会聊的内容能匹配到会议纪要技能关键词规则负责精准触发比如用户说会议纪要直接命中。粗筛的目的是把大技能库缩小到一个小候选集降低后续精确匹配的干扰。第二阶段基于 LLM 的精确选择对粗筛得到的候选技能把它们的描述拼接起来连同用户请求一起发给 LLM让模型选出最合适的技能并填充参数。这一步我在 prompt 里做了严格的输出约束SYSTEM_PROMPT 你是技能调度器负责从候选技能中选择最合适的技能。 候选技能如下 {skills_schema} 用户请求如下 {user_request} 你必须仅输出 JSON格式为 {skill_name: ..., parameters: {...}} 如果不确定用户意图选择 skill_name 为 fallback_ask_clarify。 这里有个非常关键的细节给 LLM 的候选技能描述必须保留原始场景描述不能只给技能名和参数列表。一旦精简了描述模型的精确选择准确率会下降一截因为它缺少了When use / When not use这类关键的决策上下文。3.3 兜底策略找不到技能时怎么办技能发现系统不可能做到 100% 覆盖。用户可能提出超出技能库范围的请求也可能是技能描述写得不够好导致模型没召回。我的兜底策略分三级一级兜底把请求交给一个通用的问答技能由模型直接基于自身知识回答同时明确告知用户这项能力我没有专用的工具以下回答基于通用知识二级兜底主动向用户澄清需求而不是强行瞎猜。这个策略对应上面的fallback_ask_clarify技能三级兜底记录失败样本定期人工分析。如果某类请求频繁走兜底分支说明技能库里缺了一个本该有的技能或者现有技能的描述覆盖不足三级兜底里最容易被忽略的是向用户澄清。不少 Agent 产品发现模型选不出技能时会让模型硬答往往答得又空又错。宁可多问一句您是想查天气还是想规划行程让用户明确意图也好过硬着头皮给错误答案。4. 技能编排从单个技能到复合任务4.1 为什么要引入编排层如果你的 Agent 只是做回答天气查询余额这类单技能任务确实不需要编排层。但真实业务里绝大多数请求都是复合的帮我把下周的会议安排生成一份简报发给所有参会人并同步到群里。这个请求里至少包含三件事读取日历条目、生成简报内容、发送消息。如果把它们封装成一个巨大的会议简报推送技能也不是不行但技能的复用性会变得很差——读取日历本身就值得单独成为一个技能它能服务提醒我下午的会帮我看看周五有没有空档等更多场景。所以我引入了一个独立的编排层它根据用户复杂请求生成一条技能调用链。这里的核心问题是编排器怎么决定技能的先后顺序和数据流向4.2 编排策略顺序执行、条件分支与并行我目前实现的编排器支持三种技能执行策略顺序执行技能 A 的输出作为技能 B 的输入适合强依赖场景。比如查询订单状态技能输出订单号查询物流轨迹技能需要使用这个订单号。数据流是线性的执行顺序不能颠倒。条件分支根据某个技能的输出结果决定下一步走哪条分支。比如检查库存技能返回in_stock: false则走生成缺货通知分支否则走创建订单分支。条件分支的逻辑放在编排器的流程定义里而不写死在技能内部。并行执行多个技能互不依赖时可以并发跑。比如获取用户画像获取本周消费记录获取优惠券列表三者没有依赖关系可以同时发起。并行能显著降低整体响应时间我实测一个三技能并行的场景总耗时从串行的 2.8 秒降到了 1.1 秒。编排器的流程定义我是用 YAML 描述的结构大致如下workflow: meeting_brief_and_notify steps: - id: fetch_calendar skill: get_calendar_events params: time_range: next_week - id: generate_brief skill: generate_meeting_minutes params: source: {{steps.fetch_calendar.result}} depends_on: [fetch_calendar] - id: notify_group skill: send_group_message params: content: {{steps.generate_brief.result}} group_id: {{context.param.group_id}} depends_on: [generate_brief]为了让编排器能正确填充参数每个技能暴露的输入输出接口必须遵守统一协议。技能之间的数据传递我统一用 JSON path 表达式引用避免写一坨get_result()[0][data][items]这种脆弱代码。4.3 人工编排 vs 模型自动编排这里想聊一个偏思辨但工程上很重要的问题编排流程到底应该人肉写在 YAML 里还是让模型在运行时自动生成调用链我的测试结论是对稳定性要求高的生产场景优先人工编排对探索性、开放性的场景可以让模型自动编排但必须有护栏。人工编排的好处是流程可控、错误可预期、问题可复现。我把常用的 20 条流程预先定义好覆盖公司业务的 80% 场景。剩下 20% 长尾场景我让模型自由编排但设置了两个硬性限制技能调用次数上限一次自由编排最多调用 5 个技能超过视为失败转入人工兜底通道敏感操作白名单涉及对外发送消息、修改数据、删除资源的技能即使被编排器选中也必须经过二次确认有人可能觉得人工编排 YAML 太重不如全让模型来。我理解这个倾向但真实生产里你无法接受模型随机把两个不相干技能串在一起还自信满满地告诉用户已完成。4.4 编排失败的回滚机制技能编排执行到一半某个技能失败了是整个流程直接放弃还是尝试恢复我目前的策略是分情况处理可重试型网络超时、服务临时不可用直接重试该技能最多重试两次可降级型如生成简报技能失败但原始会议记录已拿到可以直接把原文附上发给用户并注明简报生成为降级输出不可恢复型如关键技能 (比如用户身份鉴权) 失败立即终止流程向用户说明失败原因并保证不留下半截脏数据回滚逻辑中最麻烦的是副作用已产生的场景。比如发送消息技能已经发出去了但后面的生成简报技能失败了用户收到消息但没有简报。我在实际项目里的处理是这不属于技术回滚而需要做业务补偿——在失败分支里调用一个发送补充说明技能告知用户简报生成失败以下是关键内容摘要完整版稍后补发。5. 技能化改造的实测心得与避坑指南5.1 参数幻觉模型自信地传错了值技能化改造上线后我观察到一个高频问题模型有时候会脑补参数值。用户只说帮我查下上海的天气模型会把date参数填充为当前日期——这个还算合理。但有时用户说告诉我要不要带伞模型居然给city填了深圳因为上下文里早前出现过在深圳出差的表述。这类错误排查起来特别费劲因为参数值是合法的技能也正确执行了就是结果不符合用户隐含预期。我的应对之策是引入参数确认机制当技能检测到关键参数不是用户显式提供而是模型推断得到的执行前主动向用户确认一次。这个机制只对高成本或高风险技能开启——比如订机票发起支付给外部系统发通知——低风险的查询类技能不做确认以免过度打扰用户。5.2 技能描述更新后要重新跑回归测试技能描述是对模型行为影响最大的单一因素没有之一。我在项目里每次改技能描述都会重新跑一遍该技能的回归测试集——事先准备 100 条典型用户请求统计技能触发准确率、参数抽取完整率、输出格式合法率。有次我把生成会议纪要技能描述里加了一句如果会议时长低于 5 分钟直接返回一句话摘要结果触发准确率从 91% 掉到 84%。原因是模型对低于 5 分钟这个条件产生了困惑开始在是否为会议场景的判断上犹豫。回滚描述后准确率恢复。这个教训让我建立了严格的管理流程技能描述变更必须走 review 回归测试不能因为改几个字就随手提交上线。准确率对比表格摘录自内部测试集技能描述版本触发准确率参数完整率输出合法率生成会议纪要v1接口文档风格72%65%88%生成会议纪要v2场景化描述91%83%96%生成会议纪要v3新增时长分支84%79%93%穿衣建议v1四参数全必传68%55%91%穿衣建议v2最少必传参数87%81%95%5.3 技能之间的隐性冲突技能库到了一定规模会出现隐性冲突。两个技能描述里对同一个场景都有何时使用的描述模型就会陷入两难。我在项目里遇到过一个真实案例查询附近的餐厅和规划周末行程两个技能在用户问周末去哪吃这个请求上发生了冲突。前者认为应该找出餐厅后者认为应该输出完整行程。模型时而选前者时而选后者结果完全不一致。这个问题的解法是回归到技能描述层面在查询附近的餐厅的When not use里明确写上如果用户意图是规划包含餐厅在内的完整行程应使用规划周末行程技能而非本技能同时在规划周末行程技能描述里增加如果用户只关注餐饮优先返回餐厅推荐而不需完整行程。技能冲突排查起来很隐蔽因为单个技能测试都是通过的只有在组合场景里才暴露。我现在每两周做一次跨技能的路由冲突分析用一个专门的数据集覆盖常见易混淆场景防止某个技能改描述后悄悄越界。5.4 线上监控技能层的可观测性设计技能化改造后我有一个很深的感触技能层必须有自己的可观测性体系不能只依赖框架层面的日志。目前我在每个技能入口和出口各埋一个关键点记录四类数据调用链路从用户请求到技能选型到执行到反馈完整 trace参数快照模型抽取后的参数原样记录包含数据脱敏后的版本用于事后回溯参数幻觉问题选型置信度模型/排序器给出的候选技能打分看低分被选中是不是高失误率来源执行耗时分位数每个技能的 P50/P95/P99 耗时敏感技能单独告警有了这些数据再遇到线上问题我能很快定位是技能没被选上参数抽错了技能执行报错反馈格式异常这四种情况中的哪一种排查效率大幅提升。举一个具体例子有段时间用户反馈设置提醒不生效。从链路 trace 上看用户请求成功触发了创建提醒技能参数也正常但最后的反馈话术误导了用户——技能执行成功但模型回答已为你设置提醒实际上提醒的触发时间被技能内部的时区转换逻辑调整成了 UTC 时间和用户理解的中国时间差了 8 小时导致提醒晚了两小时才触发。这种问题不加链路监控根本查不出来。6. 未来的扩展方向技能的市场化与跨 Agent 共享沿着 agent-skills 这条路继续走我目前比较看好的方向是跨 Agent、跨组织的技能共享。每个团队积累的技能资产完全可以像能力插件一样被其他 Agent 实例动态加载。前提是技能定义标准化做到位——Schema 版本、描述规范、输入输出协议、安全边界全部对齐。我自己的项目已经做了一个轻量版的内部技能市场团队里任何人贡献的新技能经过 review 后可以上架共享。其他项目组的 Agent 可以通过技能清单发现并按其声明的接口进行调用互相之间不需要共享业务代码只通过技能注册中心与执行协议交互。这个模式跑通后各项目组的重复建设明显减少新的业务场景往往有一半以上的能力可以直接从技能市场里点菜。当然跨 Agent 共享的技能涉及数据权限隔离问题不是简单把技能注册中心公开就能做好的。我的下一步工作是给技能市场接入统一的权限网关让技能在执行用户请求时能够识别调用方身份并做细粒度数据权限控制。回到最开始的结论agent-skills 做得好不好核心看三点——技能描述是否让模型明确决策、参数设计是否减轻模型负担、编排层是否让复合任务稳定执行。这三点我都有过实际教训对应地给出了具体设计和优化方法希望能给同行们省下一些试错成本。技能层的架构本身不复杂复杂的全在细节里。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑