前一阵和团队里的同学聊到一个很有意思的现象大家都在用大模型做Agent但真正拉开体验差距的往往不是模型本身多聪明而是Agent能稳定调用哪些能力——这就是我理解的agent-skills也就是智能体的技能库。有人把技能全写在系统提示词里有人做了一大堆工具却没人管编排有人上线第一天效果惊艳第二周就崩得莫名其妙。这篇文章我想从工程化角度聊聊技能到底是什么、一套技能库该怎么搭、哪些坑我替大家先踩过了。先说结论技能不是一段提示词也不是一个API它是把“某个可复用的任务场景”封装为指令、工具、输入输出规范和评测标准的结构化单元。有了它Agent才从“随机应变”变成“有条理地办事”。这篇文章会分享我对技能库的完整拆解以及实际落地时的设计与复盘适合正在做Agent产品、或者准备把原型做成可维护系统的团队参考。1. Agent为什么时灵时不灵只靠提示词的三个天花板很多初级方案喜欢把所有能力都塞进系统提示词里让大模型“自由发挥”。这么做在Demo阶段看不出问题但一进入真实业务很快就会遇到三个无法回避的天花板。1.1 没有“可复用的操作”每次运行都在即兴发挥提示词的描述是自然语言自然语言天生就存在歧义。你今天写“请整理日报并输出汇总”模型可能输出三段式文字明天换个模型版本它又输出一张表格再过两天它甚至把原始数据全部打印一遍。问题不在于模型变笨了而在于你根本没有给它一个稳定的操作模板。“可复用的操作”意味着同样的输入在同样的约束下应该得到同样结构的结果。这就像餐厅的后厨不能每次都由主厨临场发明菜谱必须有一本标准作业指导书放多少油、多大火、出锅装盘用什么盘子。编写技能包本质就是把“做这件事的经验”固化成这份作业指导书。1.2 工具调用全凭临场发挥缺少约束与兜底工具调用是Agent落地的关键动作但裸奔式的工具调用非常危险。模型可能传错参数可能把单位搞混可能在工具返回异常时假装一切正常更常见的是它选了错误的工具去做事。我见过一个很典型的案例一个问答型Agent同时绑定了“查天气”“查日历”“发邮件”三个工具用户问“明天适合开会吗”它直接调了查天气还一本正经地给出了“有雨所以别出门开会”的结论。工具本身没错错在Agent没有一套规则去确认“这种请求到底该走哪条技能路线”。技能库能把工具调用放进一个“壳”里先校验参数再执行再校验返回最后决定是否把结果交给模型。这个壳比模型的自律可靠得多。1.3 效果不可回滚、不可评估改一句提示词全乱套提示词改动的副作用是最让人崩溃的你为了修复A场景加了一句约束结果B场景全挂了。因为提示词是一个整体的、稠密的语言场任何局部修改都可能影响全局的语义分布。没有评测体系时你根本说不清“这一版到底比上一版好在哪里”。技能库的模式则完全不同技能是独立版本化的。每个技能自带评测断言改动只影响该技能的调用路径上线前可以针对该技能的样例集做回归。这看起来是个很小的工程习惯却决定了你的Agent系统能不能长期活下去。2. 一个Skill里到底装了什么五个核心组成部分要设计一套技能库先得明确单个技能的结构。我给内部定义了一套五个组成部分的格式实践下来足够覆盖绝大多数业务场景元信息、指令模板、工具算子、输入输出Schema、评测断言。2.1 元信息与触发条件告诉路由“什么时候该用你”元信息包括技能的标识、名称、一句话描述、适用场景、优先级和禁用场景。这些字段的主要消费者不是大模型而是路由选择器。很多团队会忽略一个细节技能描述要面向“匹配任务”而不是面向“用户解释”。比如一个技能叫“账单查询”描述写成“查询用户的历史账单”就太弱了更有效的描述是“当用户询问某段时间内的消费金额、订单明细、扣费原因时使用优先于其他财务类技能”。前者描述了能力后者定义了触发边界。触发条件写得好路由准确率直接上一个台阶。2.2 指令模板让模型“照着抄”而不是“自由发挥”指令模板是一段结构化的自然语言它规定了完成该技能必须遵循的步骤顺序、输出格式和禁止事项。设计模板的关键是“步骤化清单化”。取消订阅我常用的写法是# 技能日报汇总 ## 步骤 1. 读取原始数据字段包括日期、渠道、订单数、成交金额。 2. 按渠道聚合生成总计行。 3. 若任一渠道成交金额为0在备注列标注“无成交”。 4. 输出Markdown表格禁止输出原始数据全文。 ## 输出格式 | 渠道 | 订单数 | 成交金额 | 备注 |这套模板的价值在于把不确定性约束在步骤和格式里模型不需要猜只需要执行。你不需要把模板写得像法典一样长覆盖关键路径和异常分支即可。2.3 依赖的工具算子把动作收敛到可执行单元技能要落地必须绑定一个或多个工具算子。算子不是随便一个函数就行它需要满足三个条件入参可校验、执行可重试、结果可观察。入参校验会在调用前拦截错误参数避免模型把“2024-13-45”这样的值喂给接口执行可重试意味着网络抖动时可以自动恢复结果可观察意味着调用日志里有完整轨迹出了问题能追溯。这里有个实践经验把粗粒度的API拆成细粒度的算子。比如“发送营销邮件”这个技能不要绑定一个“全量营销接口”而是绑定“创建邮件任务”“追加收件人”“触发发送”“查询发送状态”四个算子。技能用编排串联它们每一步的异常都能精确定位。2.4 输入输出Schema先约定契约再实现逻辑输入输出Schema是技能库里的“合同”它定义了该技能接收什么结构化参数、返回什么结构化结果。比如“账单查询”技能的输入是{userId, startDate, endDate, 分页信息}输出是{订单列表, 汇总统计, 查询耗时}。有了Schema路由选择器可以校验模型抽出来的参数是否完整执行引擎可以把上游结果直接映射给下游技能评测模块也能基于输出字段做断言。检查清单可以这样写必填字段是否有默认值兜底输出字段是否有明确的类型定义日期、金额这类关键字段是否统一格式化失败场景是否有独立的错误码返回2.5 评测断言技能自动化巡逻的底层支撑没有断言的技能你无法知道它是否正常工作。评测断言是一个可执行的脚本它检查技能执行结果是否符合预期。简单的断言可以是“输出包含表头”“返回值字段userCount存在”复杂的断言可以是“生成的SQL经过解释器执行后返回的行数与预期一致”。用JSON示例帮助理解{ skill_id: bill_query, cases: [ { input: {userId: u123, startDate: 2025-01-01, endDate: 2025-01-31}, expected: { has_header: true, total_amount_gte: 0, channels_not_empty: true } } ] }只要把这类断言纳入CI每次技能代码变更都会自动跑一遍样例集问题在发布前就能暴露。3. 技能库落地框架注册中心、路由选择器与执行循环的分工单个技能设计好了接下来是整套系统的骨架。这一节我会介绍我推荐的落地架构以及每个模块背后的取舍理由。3.1 整体架构四层各司其职一套可取用的技能库分四层注册中心维护所有技能的元数据、版本号、状态可用/灰度/下线。路由选择器接收用户请求决定调用哪个技能。执行引擎负责加载技能模板、组装上下文、调用工具算子、收集结果。反馈回路把执行结果和用户反馈写回日志供评测和优化使用。注册中心的核心是“一份技能清单一份状态表”。清单告诉路由有哪些技能可选状态表控制技能是否能被命中。灰度中的技能只对特定比例流量生效下线中的技能直接不参与路由。3.2 路由选择器为什么用“规则模型”双通道纯粹让大模型做技能路由表面聪明但实际上不可控纯粹用规则匹配又覆盖不了语义泛化。最稳的方案是双通道规则通道高频、确定性强的请求维护一张关键词和正则表直接命中技能。比如“查余额”三个字不需要模型出场。模型通道规则未命中或置信度低时把技能元信息列表交给模型做语义匹配返回技能ID和置信度。两个通道的结果会合并打分规则通道命中则优先规则未命中则采用模型通道二者冲突时记录日志并走仲裁规则。双通道的好处是廉价路径承担大部分流量聪明路径只处理模糊地带二者互补。3.3 让大模型与工具协同工作的执行循环技能执行不是一次性生成而是“规划→执行→观察→再规划”的循环。我一般用类似下面的伪代码描述这个动作def run_skill(request, skill_id): skill registry.load(skill_id) # 从注册中心加载技能定义 args selector.extract_params(request, skill.input_schema) history [] for step in skill.steps: plan llm.plan(step, history) # 当前步骤的目标拆解 result execute_tool(plan.tool, plan.params) # 调用算子 history.append(result) if not validate(result, skill.assertions): return error(result) # 断言不通过直接失败并上报 return format_output(history[-1])每一步都做一次断言校验而不是等最后统一校验。这个习惯能帮你把错误定位到具体步骤而不是只看到一句“任务失败”。执行循环里真正的难点是上下文管理每轮工具结果都很长全塞给模型会爆窗口。我的做法是只注入“本轮需要的字段摘要”原始结果存日志需要时再查。3.4 版本化与上下文管理容易忽视、影响最大的两个细节技能库的版本管理比普通代码更严格。技能是自然语言写的行为差异肉眼不可见必须给每个技能加版本号、变更说明、生效范围。线上出问题时可以把路由回退到旧版本而不是紧急改描述再发布。上下文管理方面建议每个技能声明自己的“最小上下文清单”。例如账单查询只需要“用户当前会话的账户ID时间范围”邮件撰写需要“用户称呼历史邮件风格”。执行引擎只组装清单里声明的字段不把整段对话记录灌进去。上下文越精简模型跑偏的概率越低成本也越低。4. 我在技能库踩过的四个经典坑完整复盘再好的架构设计也必须在真实数据面前低头。下面这四类问题是我在设计、上线和运营技能库过程中反复遇到的每一条都值得展开讲。4.1 坑一选择器把A技能当B技能用了现象用户问“我这个月花了多少钱”系统没有命中“账单查询”反而命中了“支出分析”。两者都能处理消费类问题但前者查明细后者做趋势输出逻辑完全不同。排查链路我先去看路由日志发现模型通道给出的置信度两个技能都很高A技能0.72B技能0.69最终取了A。再去看技能元信息问题清楚了两个技能的描述都写了“查询消费”“统计支出”语义空间几乎重叠模型根本无法区分。修复方案分三步改写描述明确区分触发边界。A技能描述增加“返回原始订单列表、不含同比环比”B技能描述增加“按月份聚合、输出趋势结论”。给A技能补充负样本示例。“当用户只想看明细时优先选择本技能”这类正例没用需要直接写明“当用户询问趋势或环比时请勿使用本技能”。在后处理环节加校验如果路由返回的两个技能置信度差小于0.05且它们共享某个“冲突标签”则触发二次问答澄清。这个坑的核心教训是技能描述的作用对象是“路由分类器”不是“终端用户”。写得越接近分类器的判别逻辑路由越准。4.2 坑二上下文越滚越大窗口直接超限现象某技能刚上线时响应正常运行半个月后频繁报“上下文长度超限”且这类报错都出现在对话轮数较多的会话里。排查链路我查看执行日志发现每次执行循环都会把“上一轮完整工具输出”附加到上下文里。单次工具输出可能10KB循环10次就是100KB多轮会话叠加后迅速顶爆窗口。更隐蔽的问题是很多无效字段比如接口返回的调试信息也被无差别注入模型真正用到的可能只是其中的3个字段。解决方案在工具算子层提供“摘要化输出”工具不只返回原始结果还返回一个精简字段版本默认只注入精简版。执行引擎增加上下文预算机制。每个技能设定最大上下文上限超出后自动压缩历史步骤把旧步骤替换为“该步骤已完成结论摘要xxx”。对超长文档类工具比如“读取用户手册”采用按需检索的方式不一次性注入全文而是先检索相关段落再注入段落内容。场景类比这就像你让助手去仓库找一份材料你不会要求他把整个仓库的货架清单背下来只需要他找到那一份文件并带回来。上下文管理的本质就是做减法。4.3 坑三技能之间抢活优先级打架现象系统上线了“订餐推荐”和“营养咨询”两个技能用户问“晚上吃什么”两个技能都被路由命中。订餐推荐只推餐厅营养咨询只讲热量用户获得的回答被切成两半体验很差。排查链路我打开注册中心发现两个技能的“优先级”字段都是默认值0触发标签也有重叠。路由选择器拿到并列结果后直接用“先到先得”策略处理没有冲突仲裁逻辑。修复方案为技能定义显式优先级。操作型技能如“创建订单”“发送消息”优先级高于咨询型技能垂直场景技能高于通用技能。引入“互斥标签”。比如“消费决策类”下挂了订餐推荐、商品对比、优惠查询三个技能互斥同一请求只能命中一个。当冲突无法解决时路由选择器不直接硬选而是向用户呈现“你更想要哪种帮助”两个选项。这个坑的根因不是模型能力而是技能库没有声明技能间的依赖与互斥关系。技能不能只是孤立的条目它们必须参与一张关系网谁覆盖谁、谁优先于谁、谁不能同时被调用。4.4 坑四新版技能上线后老案例大面积失败现象某技能平时成功率85%某次更新后成功率掉到60%用户投诉明显上升。回滚后成功率恢复正常再发布第二次更新又在相同位置翻车。排查链路问题不在实现在发布流程。第一次上线时我只用新版本跑了几个新写的正例没有跑该技能的完整历史回归集。更新里改动了输出格式从“宽表”改成“长表”但旧的评测断言依然期望“表头包含渠道和金额”前后矛盾。解决方案所有技能发布必须携带回归报告。跑一遍历史全部样例报告里必须有“成功数/失败数/失败case的差异摘要”三维信息。引入影子模式新版本技能在线上同步执行但不影响真实用户只把结果写入日志对比新旧版本输出差异。差异超过阈值就自动暂停灰度。评测断言跟随版本演进。如果新版本有意改变输出格式评测断言必须同步更新并且要在变更说明里写清楚“本次变更影响输出结构”。经过了这四次踩坑我把技能库的迭代纪律定为任何技能变更必须走“离线回归—影子对比—灰度放量—全量上线”四步缺一步都视为违规发布。5. 给技能库做“体检”测试分层、衡量指标与版本演进技能库不是搭好就能躺平的系统。它需要持续的测试、度量和迭代否则会像老旧代码库一样腐化。5.1 三层测试单技能、跨技能、回归测试体系至少分三层单技能测试只验证一个技能内部的指令遵循度和格式正确度。它解决“这个技能单独跑是否是好的”的问题。跨技能测试验证多个技能协作时是否产生冲突或信息丢失。典型场景是“先查订单再发起退款”两个技能接力时参数能否正确传递、异常能否正确传播。回归测试是技能库的保险丝。每改一次技能全量历史case都要重跑。这个地方最容易偷懒但绝对不能省。你甚至可以给回归测试加一个硬性要求覆盖率低于90%时禁止发布。5.2 三个核心指标命中率、指令遵循率、任务完成率衡量技能库健康度的指标很多我长期盯的是三个路由命中率用户请求中能正确路由到某个技能的比例。过低意味着很多请求在“裸奔”没有技能兜底。指令遵循率命中技能后模型是否成功执行了指令模板里的关键步骤。比如模板要求“必须输出备注列”模型输出里没有就是一次遵循失败。任务完成率用户意图是否被端到端满足需要有明确的完成信号。这个指标最难定义但最有价值。这三个指标可以做成每周简报。我建议在路由日志、执行日志、用户反馈三个数据源之间做交叉分析路由对了但执行失败大概率是模板或工具问题路由错了但执行成功大概率是描述和样例问题。5.3 技能生命周期淘汰、拆分、合并技能库一定会有冗余。当两个技能的路由命中模式高度重合时可以考虑合并为一个用分支处理不同细节当一个技能承担了多类完全不同的任务时应该拆分成两个独立技能让每个技能保持语义单一。判断拆分的标准很朴素看一个技能的评测样例集里是否存在“彼此语义差异超过50%”的组。如果有说明功能聚合过度模型很难在一个技能模板里同时控制两种行为。判断合并的标准也朴素两个技能的指令模板超过70%内容相同且路由经常选错那就合并统一入口再内部区分。淘汰机制同样重要。每个季度扫描一次技能库把“近30天命中数为0且近60天命中数低于5”的技能标记为待下线。没人用的技能留在库里只会徒增路由选择器的负担还会成为模型误判的噪音源。5.4 把每一次失败都变成一条新的评测样例运维阶段最有价值的习惯是每当线上出现一条失败case就把它沉淀为该技能的回归样例。这条case不需要等到优化后再加入——它会立刻出现在回归集里成为下一次技能变更必须通过的门槛。这个机制的效果很惊人。我见过一个团队用这个办法第一个月收集了80条失败case第二个月收集了30条第三个月只剩零星几条。表面上看是bug越来越少实际上是那个技能已经进化成一套自带“免疫记忆”的实体。所谓成熟的技能库不是所有case都成功而是成功和失败的边界越来越清晰。我个人在运维中最深的体会是技能库需要的是一个“持续归因”的习惯。每次失败都去回答三个问题——是路由选错了还是指令没执行对或者工具返回格式不符合预期。把这三个问题的答案写进新的评测断言里技能就永远不会在同一个坑里摔第二次。