资讯详情

Agent技能体系从零搭建:设计、调度与生产落地的避坑指南

📅 2026/10/8 11:23:08 | 华诺云谱 👁 阅读
Agent技能体系从零搭建:设计、调度与生产落地的避坑指南
1. 前置结论我从零搭建了一套技能体系先沉淀踩坑认知两年前我第一次尝试给聊天机器人叠能力的时候以为会做某件事就是往提示词里塞一段描述让大模型自由发挥。结果上线第一周就被现实教育了同样一句帮我查物流用户换个说法就解析失败昨天还能准确调用的接口今天上下文多了几轮闲聊模型就开始一本正经编造数据。后来我意识到真正缺的不是更长的提示词而是一层结构化的能力封装——这就是 agent-skills 这个概念落到工程上要解决的事情。我先给一个明确的前置结论给急着抄作业的朋友代理技能不是提示词模板的升级版它是一套可复用、可组合、可校验的能力单元。一个完整的技能至少包含五个要素——标识与版本号、触发条件、输入参数契约、内部执行逻辑、失败兜底策略。我在项目里验证过的路径是先拆原子技能再组复合技能调度层完全代码化模型只负责意图识别和参数抽取。下面所有经验都来自实际跑过的流程不涉及任何没验证过的花把势。顺便说清楚适合谁来读这篇文章。如果你是正在把 LLM 应用从聊天玩具推向能干活的生产工具的开发者这篇能帮你拆解怎么把模糊需求变成技能单元如果你已经在用 LangChain、Semantic Kernel 或者自研框架做 Agent这篇更多在讲技能库设计背后的取舍而不是某个框架现成 API 的堆砌。2. 为什么提示词塞不进可靠执行技能与提示词的本质边界先把最容易混淆的一个概念掰开——技能和提示词到底差在哪。很多人觉得给模型写清楚当用户询问运费时请根据重量和目的地计算这就是一个技能了。实际跑起来你会发现模型收到运费两个字就开始自由发挥它可能把体积也算进去可能忘记你写了八遍的折扣规则可能在费率表选择上自作聪明。提示词擅长的是知道有这么回事不擅长的是每一步都按固定规则执行。而技能的核心恰恰是后者把确定的事情从模型的临场发挥里剥离出来。我自己比较喜欢用一个比方提示词像给实习生口头交代工作你说了注意安全、看清楚再填但是不是真做到了全看对方状态技能像给实习生一张带校验的操作单每一步填什么、不满足什么条件就停、失败走哪个分支全写死在流程里。大模型的强项是理解模糊意图、生成自然语言回复弱项是严格遵守多步约束、执行精确计算、在长上下文里保持规则不漂移。技能体系就是扬长避短让模型做调度员让代码做执行者。拆开看提示词和技能在执行可靠性上有几个明显的差异维度。第一个是参数约束。提示词里你写请根据用户输入提取城市模型传回来一个杭州还是Hangzhou还是浙江省杭州市全看它心情技能里有 JSON Schema 顶着字段缺失、类型错误、格式不符直接在校验层被拦下。第二个是执行路径。提示词方案里每个动作都是模型现场决策今天心情好先查库存再查价格明天可能就反过来技能方案里你把查询顺序写死模型进来只要输入、等结果。第三个是错误处理。提示词方案里接口返回异常模型可能顺着话头编一个查到了预计明天到出来技能方案里错误对象明确携带状态码和下一步动作模型就算要安抚用户也没有瞎编的空间。这个边界想清楚之后你自然会得出一个结论能用技能封装的事情尽量不要留给提示词临场发挥。那哪些事情适合封装成技能我的筛选标准很简单——你确定要做什么、做法也基本确定的就封装用户意图本身模糊、需要多轮澄清判断的才留给模型自由对话。前者比如查库存、算运费、创建工单、解析某个固定格式的文档后者比如帮我处理一下售后这种需求它本身不是一个动作而是一串动作的编排。实际做的时候还有一个反向注意点不要把什么都封装成技能。有人会把给用户发送欢迎语这种纯模板输出也做成技能结果技能数量爆炸模型在工具列表里翻得眼花缭乱调度准确率不升反降。我的经验是纯文本生成、不需要外部依赖、规则固定到一句话就能说清的事情留在提示词里就够了。技能只有在你需要它调用服务、操作数据、执行计算、承载状态的时候才真正有价值。3. 技能库设计原子技能与复合技能的组合逻辑打定主意要做技能体系之后第一个绕不开的问题是技能库怎么组织。很多人上来就写一个 handle_refund 大函数把查订单、算退款、发起退款全塞进去。结果用户只是问一句我订单到哪了你没法复用里面那段查订单的逻辑只能新写一个查物流的技能两段逻辑大量重复。等业务规则变了你得同时改好几个地方而且每改一次都可能引入不一致。我后来用起来效果最好的组织方式是先拆原子技能再组复合技能。原子技能的粒度控制在一个动作、一个输出比如查订单 get_order_info、算退款金额 calc_refund_amount、发起退款 issue_refund、发通知 notify_user每一个都是独立的、单一职责的、可以被其他场景复用的。复合技能则是一个完整的用户意图比如 handle_refund 就是把这几个原子技能按固定顺序编排起来。这样做的好处最直接体现在复用性上get_order_info 能被查状态、查物流、售后处理好几个场景共用calc_refund_amount 可以单独做单元测试以后某个业务规则变了只改一个技能不影响其他调用链。组合逻辑里有一个容易被低估的设计点复合技能要显式声明子技能的调用顺序和终止条件而不要让模型自由决定下一步。比如 handle_refund 的流程是先查订单、再算金额、再发起退款任何一个步骤失败就终止返回错误。这套顺序是业务规则不需要模型临场推理。我在项目里倾向于把这类编排逻辑直接写死在代码层不让模型参与决定只在描述里留一个例外空间的说明——如果订单状态已经是已退款直接返回提示不要重复发起退款。代码层控制主流程描述层处理少量例外双管齐下最稳。技能粒度怎么把握这是团队讨论最多的问题。太粗了组合困难太细则代理每做一个动作要跳多个环节Token 开销和执行延迟双双上升。我给一个参考标准原子技能的输入输出足够简单人类能在一分钟内说清楚它传什么、返回什么复合技能对应一个完整业务诉求用户一句话能描述清楚。比如查订单是原子技能处理退货是复合技能。在真实项目里你可以先用这个标准拆跑一段时间再看调用数据调整。技能库里每个技能除了执行逻辑还必须有完整的元数据。元数据不只是给文档用的更是给代理的调度层和评估系统用的。我维护的元数据项主要包括四类适用场景描述、限制条件、依赖关系、版本号。限制条件尤其是重中之重比如 calc_refund_amount 要声明仅适用于已完成支付且未超过退货期限的订单get_order_info 要声明只读不写。调度层靠这些元数据判断当前输入是否匹配这个技能评估系统靠这些元数据自动生成测试用例版本号则在线上事故回滚时候救命这个我后面细说。输入输出 Schema 我建议直接用 JSON Schema别自己发明格式。原因有三个一是校验工具生态成熟随手就能做运行时校验二是主流模型的 function calling 接口基本都解析 JSON Schema三是团队协作时 JSON Schema 的自描述性远好于注释。写 Schema 时有一条铁律参数名要跟着业务语义走别用缩写。模型是靠参数名猜字段语义的你写 user_id 它知道填用户ID写 uid 它就可能把订单号也填进来。这个坑我踩过模型把 refund_id 填给 order_id 就是因为我两个字段名太像。4. 技能注册与调度落地从接口定义到运行时校验的完整管线设计搞清楚了接着聊落地实现。我先跑一个实际项目里的典型流程把技能注册进代理不管用哪个框架本质都是三件事定义接口、暴露给模型、运行时校验。下面是一个库存管理场景的简化例子我用 Pydantic 写接口定义因为可以直接生成 JSON Schema 给模型用。from pydantic import BaseModel, Field class StockQuery(BaseModel): sku: str Field(description商品SKU编号必填例如 SKU-10086) warehouse: str | None Field(defaultNone, description仓库代码可省略例如 W-HZ-01) class StockLevel(BaseModel): sku: str warehouse: str available: int inbound: int Field(description在途入库数量无则为0) class RestockOrder(BaseModel): sku: str quantity: int Field(gt0, le1000, description补货数量1到1000之间) warehouse: str reason: str | None Field(defaultNone, description补货原因说明) async def get_stock_level(q: StockQuery) - StockLevel: # 实际项目里这里查库存服务 return StockLevel(skuq.sku, warehouseq.warehouse or W-HZ-01, available42, inbound17) async def create_restock_order(o: RestockOrder) - dict: # 实际项目里这里写工单系统 return {order_id: RO-2025-0001, sku: o.sku, quantity: o.quantity, status: pending}写这套定义的时候有几个细节直接被线上事故教育过。字段 description 必须写业务边界写商品SKU编号必填例如 SKU-10086和只写SKU编号给模型的信息量完全不同。枚举值直接写清楚别让模型在头脑里翻答案warehouse 如果不是开放输入就写死可选列表。数值字段带范围约束quantity 限定 1 到 1000能挡掉大量匪夷所思的幻觉参数。这些约束在开发阶段看来是多写两行字上了生产你会知道它们帮你挡了多少脏数据。接口定义好之后第二件事是暴露给模型。主流做法有两种一种是把函数定义塞进工具列表让模型走 function calling另一种是把技能描述写进上下文让模型输出结构化 JSON 再由解释器执行。我推荐前者因为 function calling 自带输出约束模型产生的工具参数会被强制对齐到函数签名比自由输出 JSON 的幻觉空间小得多。框架通常会帮你把上面的 Pydantic 模型自动转换成工具定义你真正要花心思的是写 tool description。工具描述里有一个常被忽略的点第一句话就写明何时不该用。模型做意图选择时负向说明往往比正向说明更有效。比如 create_restock_order 的描述要写仅当用户明确要求创建补货订单时使用。如果用户只是询问缺货情况不要调用此技能请使用 get_stock_level。后期评估数据会告诉你这一句话能把混淆场景的准确率拉高一大截。第三件事是运行时校验。别信任模型一定传了合法参数它经常会传错。技能入口必须加校验层校验不过时返回可读的错误信息而不是直接抛异常。校验通过之后技能内部还要有防御逻辑查不到数据、第三方接口超时、权限不足都要返回结构化错误码。下面这段调度层的伪代码说明我做校验与兜底的基本思路。async def call_skill(model_decision): skill_name model_decision[skill] arguments model_decision[parameters] if skill_name not in skill_registry: return {status: error, code: SKILL_NOT_FOUND, message: 未定义的技能} skill skill_registry[skill_name] try: validated skill.input_schema.validate(arguments) except ValidationError as e: return {status: error, code: INVALID_PARAM, detail: str(e)} try: result await skill.execute(validated) return {status: ok, result: result} except SkillExecutionError as e: return {status: error, code: e.code, message: e.message}调度层一定要和业务逻辑隔离。我项目里把调度层抽成独立模块不管底层接 GPT、Claude 还是本地开源模型调度逻辑都不改。这样每次模型厂商切换代价只是工具生成的格式适配。这件事收益很大调度层代码可以单独写单元测试不依赖任何模型厂商的在线服务。注册表设计还要提一点别用魔法字符串硬编码。我用一个 registry 类启动时扫描被 skill 装饰器标记的函数自动收集名字、描述、Schema 和 handler。装饰器把元数据写在实现旁边技能和它的实现放一起版本不会漂移。注册表还要记得维护 semantic_version 字段调度层把技能版本写进每次调用的日志。线上出问题时没有版本号根本没法回滚有了版本号才能做灰度流量对比。5. 技能评估与边界约束让代理知道什么不能做技能库建起来了但如果只靠单技能测试通过就敢上线后面基本要吃大亏。技能本身执行正确和代理在真实对话里调对了技能是两码事。我遇到过单技能命中率接近满分、集成之后整段对话一塌糊涂的项目——用户问A代理先去调了B用户嘴上说查库存代理把补货单给创建了。技能评估不能只看单点命中还要看调度准确率和边界敏感性。测试集一定要覆盖三类场景缺一不可。正向场景是用户输入明确指向某技能代理必须调用它混淆场景是用户输入表面像A技能、实际是B技能代理不能被表面词汇带走负向场景是用户输入与任何技能都不匹配代理必须拒绝调用回退到通用对话或追问澄清。负向场景最容易被忽略但它恰恰最能体现技能系统的价值。我测试集里专门放了很多看似能调用但实际不该调的输入比如你们客服电话多少它和售后技能沾边但只该走通用问答不该触发任何技能调用。评估指标我分三层来看每一层都有用。第一层是技能调用正确率测试集里每个样本标注期望技能跑完代理比对输出算准确率和召回率。第二层是参数解析正确率单独看传给技能的参数对不对这个指标极其重要但经常被忽略。用户说查一下杭州仓库的库存代理调对了 get_stock_level但参数里没有仓库字段只有 SKU结果就不对。第三层是交互回归表现把一批真实对话回放给代理看整段对话完成度这能捕捉到多轮测试发现不了的意图漂移、调用顺序错乱、同一技能重复触发。评估发现问题之后怎么改我总结了一个规律模型调度出错十有八九是技能描述有歧义。描述不要堆功能点要写清楚做什么、不做什么、什么时候用、什么时候不用。描述的质量和代码质量一样重要它就是投喂给模型的接口文档。有个例子我记得很清楚一个叫 get_latest_news 的技能描述只写获取最新资讯用户在对话里问你们公司最近有什么活动吗代理就调了它返回一堆驴唇不对马嘴的新闻。后来改成获取站内公告和行业新闻资讯。仅当用户明确使用新闻、资讯、公告、最新消息等词时调用。涉及公司活动、优惠促销等话题不要调用此技能应使用 get_promotion_info混淆场景准确率从 71% 升到了 94%。边界约束这块还有一个权限维度的坑。技能是能力载体但并不是所有技能都该对普通用户开放。创建补货单、发起退款这种写操作和查库存这种读操作在权限模型里必须严格分开。调度层收到写操作意图时要检查会话身份必要时让用户做一次明确的二次确认。这道防线放在技能层比放在模型提示词里可靠得多。模型提示词随时会被系统升级覆盖技能层的代码逻辑不会。6. 生产环境里踩过的坑五个案例与对应补救方案最后这部分讲实战中踩过的坑。技能系统上线之后问题不会出现在单元测试里而是出现在真实流量的角落里。我按踩坑频率排序把印象最深的五个事故和补救方案写出来每一条都是拿线上数据换的。第一个坑是上下文污染导致技能误触发。代理在长对话里前面的内容会影响后面的工具调用判断。最典型的是用户先聊退货后又聊别的模型在话题早已切换之后仍坚持调用售后技能。排查下来有两个原因一是技能描述的适用场景写得太宽让模型认为旧话题仍相关二是上下文窗口太长早期对话的残留信号压过了当前意图。补救办法有两个一是做意图快照——每次工具调用后重新编码最近一条用户消息、重新做意图判断不让历史对话直接决定技能选择二是给技能加运行时限制比如一次会话内最多触发次数和话题相关性阈值。这两个限制把误触发率直接降了三分之一。第二个坑是技能参数里的幻觉值。模型在参数解析上会凭空想象超出预期。有个案例让我印象很深用户问帮我查一下上周五的销售数据代理调了 get_sales_report参数 sales_date 传的是2025-04-01但当天是 4 月 18 日上周五应该是 4 月 11 日。模型没有做日期计算而是从上下文随便找了个像日期的值硬塞进去。这种问题靠校验层挡不住因为格式和类型都合法。我的补救办法是凡涉及日期、金额、数量这类需要计算的参数不让模型直接填而是让技能内部用解析器处理。定义技能参数时用 date_expr 这种表达式字段技能内部调 date parser 解析把计算责任从模型身上移走准确性立刻上了一个台阶。第三个坑是技能描述被系统提示词覆盖。模型能力升级或系统提示词模板调整后技能描述里的优先级可能被静默稀释。比如你写了查库存优先用 get_stock_level但系统提示词里有一句用户投诉时优先安抚情绪并推荐查询库存模型就可能绕过技能层直接生成一段建议性回复。这种问题很阴险它不会让系统突然崩掉而是效果缓慢下滑你很难定位到是哪次改动导致的。我的处理办法是把技能优先写进调度层硬代码用户请求与技能触发条件匹配时强制要求调用技能并限制模型的自由文本输出。不要让可调用也可自由发挥两种选项同时存在二选一才可控。第四个坑是工具返回结果太长挤爆上下文。技能执行后的原始结果往往包含大量无关字段比如查库存返回了内部服务报文、带分页信息的完整列表。模型拿到这些数据后Token 大头都耗在无关信息上后续对话质量急剧下降。补救办法很粗暴但有效所有技能输出都定义一个给模型看的精简视图只保留回答用户问题所必需的核心字段。查订单的模型视图只包含订单号、状态、商品名称、发货地址其余一律丢弃或折叠。有了这层精简推理速度变快答案也更准因为模型的注意力不会被无关信息带偏。第五个坑是复合技能的错误处理没有传递进度。复合技能调了三个原子技能中间某个失败代理收到错误后不知道前面执行到哪一步就可能编造成功消息回复用户。补救办法是所有技能错误都以结构化错误码返回错误信息包含当前进度和下一步可执行动作。比如退款创建失败原因是支付系统超时当前订单尚未扣款请重试或联系人工处理。有了明确的进度信息模型即便要生成安抚话术也不会凭空造一个已退款出来。除了这五个高频坑我再补一个通用建议给所有技能调用加链路追踪。每条调用无论成功失败都要记录时间戳、参数快照、返回摘要、调度模型版本、技能版本。上生产之后这是一切排查的起点。我用最简单的结构化日志方案trace_id 贯穿每次调用配合查询面板按 trace_id 检索成本不高但线上事故复盘时能省掉几个小时手工翻日志。说回团队协作层面技能库不是一次性交付物它需要持续运营。我现在的习惯是每周一次技能体检看每个技能的调用次数、失败率、平均耗时、参数校验失败率。调用量极低的技能反思是不是描述引导不够失败率高的定位是内部逻辑问题还是参数解析问题参数校验失败率高的基本都能追查到是描述有歧义。把技能当成产品来运营而不是当成代码来维护这才是 agent-skills 能持续产生价值的关键。技能库建完不是终点你要跟着真实交互不停调优——粒度、边界、描述、组合方式都在变。这个迭代节奏才是这个方向真正考验人的地方。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑