
把大模型“调教”成能干活的智能体agent-skills 技能化设计实战笔记做了快两年大模型应用开发我有一个很深的体会现在市面上讨论 Agent 的文章十篇里有八篇在讲怎么搭框架、怎么接模型、怎么写 ReAct 循环但真正让 Agent 从“演示能用”变成“生产能打”的往往是那些不起眼的细节——模型在什么情况下该调用哪个工具、调用的参数怎么传、传错了怎么纠偏、多个工具之间怎么配合。这些细节往深了走就会触及一个核心概念技能Skills。最近我在折腾一个内部项目代号就叫 agent-skills目标很直接把模型的能力封装成一套可复用的“技能包”让智能体像搭积木一样组合出复杂行为。折腾了小两个月踩了不少坑也总结了一些比较实用的设计思路。这篇就把我的完整思考、实现方案和排坑记录分享出来给正在做 Agent 应用开发、尤其是被“模型什么都会但什么都做不好”困扰的朋友一个参考。如果你刚接触这个概念也不用担心我会从最基础的“为什么要技能化”讲起一步步拆到具体实现。1. 核心思路拆解为什么 Agent 必须“技能化”而不是只会“调用工具”1.1 一个扎心的现实大模型什么都会但什么都做不精细先说一个我反复遇到的场景。早期我做 Agent 原型的时候喜欢把能力全部押在提示词上“你现在是一个数据分析助手你可以读取 CSV 文件、计算统计量、生成图表、输出 Markdown 报告……”模型确实听懂了回答得也头头是道。但一到了实际执行问题就全跑出来了让它读取一个 50MB 的 CSV它居然想用 Python 代码一行行读然后自己“脑补”分析让它算个相关系数矩阵它写出来的 pandas 代码经常列名对不上更头疼的是连续对话时间一长模型会“忘记”自己有哪些能力甚至开始一本正经地胡说八道给出根本不存在的函数名。这不是大模型笨而是我在设计上就偷懒了。大模型本质上是一个“文本预测器”它对“能力”的理解全部来自上下文中的描述。如果你只是用自然语言告诉它“你可以做什么”它在执行时就会按照“文本续写”的逻辑去自由发挥。自由发挥就意味着不可控不可控就意味着没法用。换句话说在 Agent 架构里语言描述是一回事实际执行是另一回事中间缺的恰恰是一层“将意图映射到确定性代码”的桥梁。1.2 技能的本质给模型一套“标准动作”而不是一段“自由描述”我在 agent-skills 项目里做的第一件事就是把“告诉模型你能做什么”改成“给模型一套标准动作”。每个技能本质上是一个最小可执行的原子能力单元它有固定的名字、明确的输入输出、可校验的参数约束、以及配套的示例。模型要做的不是“理解”这个技能该怎么实现而是“选择”当前场景该激活哪个技能然后把它需要的参数提取出来剩下的交给确定性的代码去执行。打个比方这就好比你去餐厅点菜。如果服务员只是说“我们这里什么菜都会做”那厨师的发挥就全凭心情了你可能等来一盘黑暗料理。但如果菜单上每道菜都有编号、主料、辅料、口味说明你只需要报编号后厨就知道该怎么做。技能就是这个“菜单编号”它把大模型从“厨师”的位置上解放出来让它专心做好“点菜员”的角色。技能化带来的直接好处有三个。第一是确定性同样的输入技能的执行结果永远一致不会因为模型状态波动而飘忽第二是可测试性每个技能可以独立做单元测试像验证普通函数一样验证它的输入输出是否符合预期第三是可复用性一个写得好的技能可以被多个 Agent、多个场景反复调用不需要每次重新“教”模型一遍。这三个特性正是 Agent 从“玩具”走向“工程”的分水岭。1.3 方案选型的取舍为什么不做“全自动编排”而是“半自动技能路由”在技能化的落地路线上我一开始其实纠结过两条路。一条是现在很多 Agent 框架主打的“全自动编排”模型自己决定调用顺序、自己决定何时终止人类只负责给一个最终目标。另一条是我后来实际采用的“半自动技能路由”预先定义好技能拓扑模型在允许的技能集合里决策但关键的流程节点由代码控制。全自动编排的诱惑力很大demo 做出来特别炫你喊一句“帮我把上个月的销售数据分析了并做个可视化”它自己一步步调用工具、自己检查中间结果、自己生成图表整个流程丝滑得像个老手。但放进生产环境后我遇到了几个劝退的问题首先是不可预期性模型在长任务里偶尔会“自创”步骤比如在分析销售数据时突然调了一个天气查询技能逻辑链断了整个任务失败其次是成本爆炸全自动编排意味着模型要在每个步骤都做决策一次复杂任务的 token 消耗是固定流程的几倍甚至十几倍。所以最终我选了半自动路线把“流程”和“技能”分开。流程由代码定义技能由模型选择。模型只负责在流程节点上决策“这一步用哪个技能、传什么参数”至于任务分几步、步骤之间怎么衔接全部由预先编排好的逻辑控制。这样既保留了智能体的灵活性又不会让失控的代价变得难以接受。项目跑到现在这个选择被证明是性价比最高的。2. 技能结构与分类体系从“零散函数”到“可演进的技能库”2.1 技能的解剖一个合格技能包必须具备的五个要素把概念想清楚之后接下来的问题就是具体设计了。一个技能在 agent-skills 里到底是什么样的数据结构经过几轮迭代我沉淀了一个比较稳定的五段式结构每个技能包都包含元信息、触发描述、参数模式Parameter Schema、执行逻辑、示例对Few-shot Examples。元信息好理解就是技能的唯一标识、版本号、所属领域、标签。这一部分看似简单但有一个坑我踩过版本号必须纳入运行时的路由判断逻辑否则老版本技能有 bug 时你没有办法单独下线它只能把整个 Agent 灰度回滚。触发描述是整个技能包里对模型最重要的部分它决定了模型“什么时候该想起”这个技能。这里我强烈建议不要写抽象的能力说明而应该写“场景触发词 排除条件”。比如“当用户要求对表格数据进行统计计算且数据行数超过 50 行时调用 dataframe_analyze 技能单条数据的简单计算不需要调用技能直接回答即可”。这种写法能大幅降低模型的误召率。参数模式其实就是 JSON Schema它定义了技能调用时需要传入哪些字段、每个字段的类型和取值范围。这一层是技能化与普通工具调用的最大区别普通工具调用往往由模型自由发挥参数技能化则强制模型按照 Schema 提取参数不合规的调用直接抛出校验异常。执行逻辑就是技能内部的具体实现了这里可以是 Python 函数、Shell 脚本、SQL 查询甚至是对另一个服务的 HTTP 请求。示例对是给模型参考的“标准答案”我的经验是每个技能给 3-5 组覆盖典型场景的输入输出示例比在系统提示词里写一百行“你要怎么做”管用得多。2.2 技能分类信息获取型、计算分析型、操作执行型、生成创造型给技能设计好结构之后我在整理现有技能时发现如果不对技能做分类管理技能库一旦超过二十个路由准确率就会急剧下降因为模型在决策时面对的选择太多了。后来我把技能按照“动作性质”归成了四个大类分类本身也参与路由决策。第一类是信息获取型技能负责从指定数据源拉取信息例如查数据库、读文件、调 API。这类技能的关键设计点是输出格式标准化因为后续步骤要拿到结构化的结果才能继续处理。第二类是计算分析型技能负责处理数据、做统计、执行算法这类技能的关键点是参数约束要严格输入数据格式不规范时宁可报错也不要去猜。第三类是操作执行型技能负责触发实际操作比如发送邮件、创建工单、执行部署这类技能的安全设计最重要必须加授权校验和操作确认不能模型一调就真干了。第四类是生成创造型技能负责调用模型完成文本生成、代码生成、创意设计这类技能与其他三类有本质区别它的输入输出都是自然语言校验难度最大需要额外的质量评估环节。在实际的项目里这四类技能通常不是孤立使用的它们会按照一定的拓扑关系组合成“工作流”。比如一个“竞品动态监控 Agent”的工作流是信息获取型技能去爬取竞品官网更新 → 计算分析型技能对比版本差异 → 生成创造型技能撰写分析摘要 → 操作执行型技能把摘要推送到钉钉群。每一类技能有自己专属的调试策略和数据规范分开管理会清爽很多。2.3 影响范围评估技能库的规模决定了 Agent 能力的边界设计技能分类体系时我顺带做了一次影响范围评估结论也成了后来项目迭代的重要依据。技能库并不是越大越好而是要看模型在一次决策中实际需要面对的技能候选空间有多大。做过一组对照实验技能库 5 个时路由准确率可以达到 95% 以上技能库增加到 20 个时准确率会掉到 88% 左右超过 30 个后准确率就跌破 80% 了。这个数据说明无脑堆技能数量不如给技能做分层路由全局模型只面对十几个粗粒度的技能组进组之后再细粒度匹配具体技能。这个发现直接影响了我对“agent-skills 到底能做多大”的预期。它本来是我一个人的内部工具库现在架构上变成了“技能分组 路由决策 执行层”的三层结构每一层都可以按需扩展。单就我们团队正在跑的数智员工试点项目来看目前接了 14 个技能三个信息获取型、四个计算分析型、两个操作执行型、五个生成创造型每个技能都在这套标准结构下独立开发、独立测试、独立部署。这个规模下Agent 的日常任务完成率比没有技能化之前提升了将近一倍。3. 实操过程与核心环节实现从零搭建一套技能化 Agent3.1 第一件事先把技能注册中心建起来别急着写技能很多朋友做 Agent 的时候习惯先去写工具函数写完了再一股脑塞给模型。我在 agent-skills 里没有这么做而是先花了一天时间搭建了一个非常轻量的技能注册中心。这个注册中心的核心是一个 Python 装饰器加一个服务端登记表每一个技能在被定义时自动向登记表写入元信息、参数 Schema、以及版本号。代码结构大概是这样的# skill_registry.py from dataclasses import dataclass, field from typing import Callable, Any, Dict import inspect import json dataclass class SkillMetadata: name: str version: str category: str description: str trigger_keywords: list parameter_schema: dict function: Callable field(reprFalse) class SkillRegistry: def __init__(self): self._skills: Dict[str, SkillMetadata] {} self._versions: Dict[str, str] {} def register(self, name, version, category, description, trigger_keywords, parameter_schema): def decorator(func): self._skills[name] SkillMetadata( namename, versionversion, categorycategory, descriptiondescription, trigger_keywordstrigger_keywords, parameter_schemaparameter_schema, functionfunc ) self._versions[name] version return func return decorator def get(self, name: str): return self._skills[name] def list_skills(self): return {name: skill.parameter_schema for name, skill in self._skills.items()} registry SkillRegistry()别小看这个注册中心它解决了我之前遇到的一个隐性问题当技能数量多起来之后模型系统提示词里的技能描述必须动态生成。每次新增或更新技能系统提示词跟着变化如果没有注册中心这个中间层你在提示词工程和技能代码之间就得手动同步早晚会出乱子。有了注册中心之后系统提示词里那一大段技能列表就是从 registry.list_skills() 自动渲染出来的更新技能时提示词同步更新彻底杜绝了“技能已上线但模型不知道”的问题。注册中心建好之后技能的执行接口我也做了统一规范所有技能函数的第一个参数必须是 **kwargs 或一个 dict避免模型传参时跟位置参数混淆返回值必须是 dict且至少包含 success 和 data 两个字段。这个约定换来的是路由层的极度简单——它只管拿参数、调函数、取返回值完全不用关心每个技能内部是什么也不用把异常逻辑散落在各处。统一契约的威力在后续接入可视化工具、日志系统时体现得特别明显。3.2 第二件事设计模型决策接口用 Function Calling 接收结构化指令技能注册中心有了接下来就要解决“模型怎么调用技能”的问题。我选择的方式是基于 OpenAI 的 Function Calling 协议也就是把每个技能的参数 Schema 直接透传给模型让模型在对话过程中产出结构化的函数调用指令。这里的优势在于Function Calling 输出的参数是 JSON 格式的天然结构化模型很少会给出格式错误的内容省去了解析自然语言再实体提取的麻烦。但直接透传裸 Schema 是不够的。在真实对话里模型经常会发出“空调用”或者“试探性调用”比如用户只是问“你们能不能做数据分析”模型就傻乎乎地调用了数据分析技能。为了应对这种情况我在技能 Schema 的描述字段上做了一次细化加工除了说明“这个技能是什么”还强制加上“在什么场景下应该调用/不应该调用”。这一步极其有效我实测下来加了否定描述之后空调用率至少下降了 60%。例如“analyze_data”这个技能的描述我最终改成了这样{ name: analyze_data, description: 对用户提供的结构化数据执行统计分析与计算。当用户给出数据表格、CSV 内容或多条数据记录并明确要求分析、统计、对比时调用。如果用户只是问通用性的数据分析方法论或数据量很小且可以直接心算不要调用此技能。, parameters: { type: object, properties: { data: { type: string, description: 需要分析的数据内容保持原始格式。 }, analysis_type: { type: string, enum: [descriptive, correlation, trend, comparison], description: 分析类型。 } }, required: [data, analysis_type] } }决策接口的另一半是温度设置。发现技能调用相关的推理线程温度必须设低我统一设了 0.1这样模型在判断“要不要调用、调用哪个、传什么参数”时会更保守不会天马行空。相反在生成创造型技能内部调用模型做文本创作时才把温度调到 0.7 甚至更高。温度参数在 Agent 里应该按执行阶段拆分而不是全局一套设置这是我调了很久才悟出来的。3.3 第三件事实现技能路由与结果回填把“调用了”变成“调用对了”决策接口把模型的调用请求接进来之后路由层要做三件事校验、执行、回填。校验过程使用了 jsonschema 库对模型传出的参数做严格校验。这一层常被新手忽略但它的价值在出错时才能体现出来如果不校验就硬着头皮执行模型传错一个字段名技能内部可能就崩了而且报错信息往往莫名其妙加了校验后你可以很优雅地返回一条“参数缺失analysis_type”的消息给模型让模型自己修正再重新调用。执行环节看起来简单就是把参数塞进技能函数跑一趟。但有一个容易被忽略的细节技能之间可能互相调用尤其是信息获取型技能的结果往往需要喂给计算分析型技能。所以我在路由层保留了“技能依赖声明”的字段每个技能可以声明自己的前置依赖条件比如 analyze_data 依赖 fetch_db_data 的输出。当模型直接要求 analyze_data 但上下文里没有 fetch_db_data 的执行结果时路由层会先自动触发 fetch_db_data再把数据拼装进 analyze_data 的参数里。结果回填是另一个决定 Agent 体验的细节。执行完技能拿到结果后我会把结果以“系统消息”的形式追加到对话上下文里让模型基于这个结果生成面向用户的回复。这里的关键是要控制回填的文本量如果技能返回的是一张 500 行的表你不能全量塞给模型否则上下文窗口直接爆炸。实践中我总结了一套回填压缩规则——数据量超过一定阈值时只回填统计摘要加前 10 行预览并附一句提示“完整数据请调用 get_full_result 获取”。这些规则的代码示例在项目里非常琐碎但每一个都是在真实失败的教训里磨出来的。3.4 第四件事沉淀一套四步排查法让 Agent 在出错后能自我修复AGI 走向实用的前提是 Agent 会自己纠错。与其追求一次调用就成功不如先想好失败后的恢复路径。我在实际项目中按下面的四步顺序让 Agent 处理技能调用的失败第一步检查参数错误。使用 jsonschema 解析模型返回的 JSON如果发现缺少必要字段就把缺的字段名和原因返回给模型让它重新生成一次。这一步能解决大约 80% 的“看起来调用了但没调对”问题。第二步检查技能本身是否异常。如果参数没问题技能内部抛异常了那就要区分是数据问题还是逻辑问题。数据问题的处理是自动重试一次并尝试做数据格式转换逻辑问题则需要返回错误码让模型换一个技能或者换一种策略。第三步检查是否选错了技能。这一步是最难排查的往往要回溯日志看模型当时的决策上下文。我开发的这套机制是当执行后出现明显的“结果与意图不匹配”比如用户要趋势分析却调用了描述统计就把这个错误样本记录到本地 eval 数据集里后续用这批数据做路由模型测试。第四步向用户澄清。如果前三步都没能解决问题就别装了直接让模型对用户说“这个问题我当前的处理能力还不太够你可以尝试换个方式描述”不要让模型硬编一个错误的答案。这种“坦诚”策略在用户体验上反而比强行给错答案要好很多因为它至少保住了信任感。这四步法的核心逻辑是“越修越窄”从技术层逐步过渡到产品层。每步都有明确的退出条件不会陷入死循环。我在代码里加了一个 max_retry 计数器超过三次就直接转人工澄清防止模型在某个技能调用上无限重试浪费 token。4. 上下文管理和提示词架构决定 Agent 上限的“暗线”4.1 动态技能选择别让你的 Agent 被“能力列表”冲昏头脑提示词架构在技能化 Agent 里是个容易被低估的模块。我在第一版实现里把全部技能的名字和描述一股脑放进了系统提示词里结果前面提到过技能一多模型决策准乱。后来我在提示词层引入了“动态技能选择”而不是静态大列表。动态技能选择的核心是一个前置的粗筛模型调用。在每轮用户输入到达时先由模型判断输入最可能涉及哪些技能组然后把系统的提示词裁剪成只包含这些技能组对应的技能包描述。比如用户说“帮我查一下上个月的销售额”粗筛模型会打上【数据获取】【数据计算】【可视化】三个组的标签提示词里就只放与这三个组相关的 8 个技能描述而不是全部 30 个。这个机制带来的效果非常明显路由的准确率重新回到了 90% 以上单次对话的 token 消耗降低了大半模型推理时也更“专注”。这个方案的代价是要多一次模型调用但以当前的模型成本来看这点开销相对整体任务的消耗几乎可以忽略。更关键的是粗筛模型本身也可以做小动作比如它可以在返回组标签时带上置信度低于阈值的就直接问用户“你是想看报表还是想写报告”避免瞎猜。消息越窄、目标越明确Agent 的稳定度越高。在这个问题上我的感受是“少即是多”。4.2 技能执行日志的格式化可观测性是调优 Agent 的唯一抓手Agent 的调试体验和传统编程完全不同你很难用断点调试来定位“为什么模型刚才没调用技能”。我花了不少时间在这个问题上最终靠的是一套严格的技能执行日志规范。每个技能调用必然留下几条结构化日志触发前上下文快照、模型生成的原始调用指令、参数校验结果、技能执行耗时与结果摘要、用户可见回复。这些日志统一输出为 JSON Lines 格式落在本地的 logs 目录里方便检索和分析。日志格式化的最大好处是可以把 Agent 的决策过程“回放”。我复盘时常用这个流程从用户问题开始一步步回溯模型拿到的是什么上下文、看到了哪些技能描述、最终生成了什么调用指令。很多时候“模型为什么要那么做”一目了然。有一次我排查一个 Agent 总是选错分析类型的 bug回放日志后发现上下文里靠前位置残留了前一轮任务的系统消息模型被带偏了。切掉冗余上下文并加了一个输入长度限制之后问题立刻消失。这类问题在非结构化的日志里可能要排查几个小时但在规范化的调用链日志里几分钟就能定位。所以如果你的 Agent 项目还没有日志系统我建议立刻补上。不要等出问题再补Agent 的出错本来就是概率性的没有日志就意味着你只能靠猜和碰运气。4.3 上下文窗口中技能输出的权重管理一视同仁不行的除了技能选择上下文管理还有一个很多人没意识到的问题技能输出的内容在后续对话里的“影响力”是不同的。比如 execute_sql 技能返回了一个表格的 JSON 结构这个 JSON 里有 200 个字段的元数据。在后续生成报告时模型如果看到这一大坨原始 JSON很容易被无关字段“带偏”写出的报告抓不住重点。我采用的方案是对技能输出做“重要性标记”。每类技能在返回时附带一个 summary 字段和一个 detail 字段summary 是模型生成报告时需要的主要信息放在上下文里detail 是供其他技能或用户按需获取的完整数据不进主上下文。例如 execute_sql 的 summary 只保留表结构、行数、前三条样例记录detail 保留完整查询结果。这样做之后上下文瘦身了模型更难被无关信息干扰。Agent 的“注意力”是一种稀缺资源应该花在决策最相关的地方这跟人开会是一样的道理——会议室里只有跟议题相关的人会议效率才会高。关联到影响范围评估技能输出权重管理的效果是可以量化的。在我记录的一组对照中同样场景下不加 summary/detail 分离时Agent 生成报告的相关性评分大概在 0.71加入分离后提升到了 0.86。这个提升不是来自提示词调优而是单纯靠上下文管理做到的说明这种结构性的优化比话术层面的优化更有杠杆效应。5. 踩坑实录与排查技巧那些文档里不会告诉你的真实教训5.1 模型坚持调用已下线技能注册过期技能导致的幽灵调用有一次我在版本迭代时下线了一个旧技能但没有同步更新注册中心的反向索引结果发现模型在对话里偶尔还会尝试调用这个已经不存在的技能。排查了很久才发现原因是大模型的系统提示词是从注册中心动态渲染的但历史对话消息里的函数定义仍然是旧的模型会参考历史消息中的既有格式依然输出旧技能的调用。这看起来是个蠢问题但它非常隐蔽也很容易复现。解决方案是给注册中心加上“技能启停状态”的枚举并在渲染动态系统提示词时不仅过滤已下线的技能还要维护一份“最近已下线技能列表”。把这个列表也一并写进系统提示词明确提示模型“以下技能不可用不要调用”。加了这个之后幽灵调用彻底消失了。这里也印证了一个经验Agent 的“记忆污染”是真实存在的模型会看历史消息里的工具定义而不是只依赖最新系统提示词。5.2 技能调通但效果拉胯典型的“调用成功≠任务完成”如果说上面那个问题是显性的机制缺陷那技能调通但效果差就是一个更隐蔽的质量问题了。我一度很困惑技能本身写了单测跑起来没有异常返回的数据结构也对但用户实际体验就是觉得 Agent 并不聪明。后来我在日志里仔细看才发现问题不在技能执行上而在于模型传给技能的参数经常是“形式正确但语义偏差”。例如用户问“对比这周和上周的订单量变化”模型确实调用了 compare_trend 技能但传进去的字段名把“本周订单数”写成了“total_orders_for_this_week_v2”技能接收后虽然校验通过但内部逻辑拿到的数据压根对不上。解决办法是给技能参数增加“别名纠偏”逻辑。在参数 Schema 的 description 里写明每个字段可能的别名说法同时在技能内部加一层映射解析把常见变体名规范化到标准字段。这个写法确实显得有点笨但在跟大模型打交道时不这么干你就等着被它坑。“模型按它理解的意思传参而不会严格阅读你的字段设计文档”这个认知越早建立越好。5.3 多技能并行调用与竞态冲突同一技能被重复执行的幂等问题有段时间我给技能路由加了并行调用能力本意是加快任务处理速度结果引入了一个新的麻烦模型在上下文里多次表达了同一个意图导致路由层对同一个技能提交了多次并行的调用请求。技能本身没有做幂等保障比如一个发送通知的技能被同时调了两次用户就收到了两条重复的消息。虽然概率不高但每次出现都很尴尬。解决方案是给每个技能加“幂等键”Idempotency Key在路由层用一个 Redis 计数器做去重同一会话中同一技能并且参数哈希一致时第二次调用直接返回第一次的结果不再真正执行。这其实是后端系统设计的常见做法只不过在 Agent 场景下被重新踩了一遍。吃一堑长一智任何有副作用的操作型技能幂等设计必须前置事后补救会痛苦到怀疑人生。5.4 调试时的模型固执模型宁可用自创函数也不调用可用技能最后记录一个特别恼人的问题。有时候我明明已经在系统提示词里把一个技能描述写得很清楚了但模型在收到用户请求后宁愿自己编一个函数名去“回答”——比如用户问“能不能帮我查一下天气”模型不调用我的 get_weather 技能而是直接胡写了一个 get_temparature_today 函数返回了一个感觉上是值的东西。这种问题主要集中在复杂多轮对话的中间轮次发生模型“走神”了。我的排查思路有两个。第一个思路是看生成采样参数如果 temperature 较高模型的自由发挥倾向会增加技能路由线程的温度我已经降到 0.1但有时候上下文里的历史消息太多模型仍然会飘。第二个思路是检查技能描述是否太抽象把“查天气”写成了“获取气象数据”模型的联想就容易跑偏。我发现把技能描述从名词性描述改成“当用户想……时使用”的句式误用率会显著下降。总而言之与其说这是一个技术问题不如说这是一个 Prompt 与模型心理模型的适配问题需要不厌其烦地用具体场景磨。6. 性能评估与效果复盘技能化改造前后数据发生了什么变化6.1 三个核心指标任务完成率、路由准确率、平均处理时延项目推进到阶段性验收的时候我专门整理了一份技能化改造前后的对照评估跟踪的指标主要是三个任务完成率、路由准确率、平均处理时延。任务完成率按端到端的效果判定路由准确率按“是否选对技能且参数正确”判定平均处理时延从用户提问到最终结果回复统计。改造前的基线是传统“提示词驱动原生工具调用”的 Agent。当时的任务完成率只有 61%也就是说十次请求里有四次是拿不到可用的结果的。路由这件事几乎没有概念因为模型经常不按规矩调用工具有一搭没一搭。平均处理时延反而很低因为模型经常直接“编答案”不怎么调用函数。改造后的数据变化比较明显任务完成率提升到了 84%路由准确率在动态技能选择的加持下达到 90% 出头平均处理时延因为多了一次粗筛模型调用和更严格的执行涨了约 0.8 秒但换来的是结果质量的质变。0.8 秒的延迟完全可以在产品体验上接受毕竟用户等来的是靠谱的答案而不是一本正经的胡话。6.2 失败案例分析一次典型的“意图偏移”是如何被日志揪出来的我还特意留了一个失败样本做复盘。当时用户的问题是“帮我看看 A/B 两个方案在过去一周的转化率差异并告诉我哪个更好。”理想的分析链是fetch_metrics 获取数据compare_rates 做显著性检验最后生成结论。但真实执行是模型直接调用了 generate_report 技能把分析步骤一股脑丢给文本生成模型去编。结果显示报告里有大段的文字描述但没有任何真实数据支撑转化率数字全是编的。回放日志之后定位的根因有两个。第一技能描述中的“generate_report”字段过于宽泛让模型误以为它是一个全能型技能可以在没有数据的情况下生成报告。第二缺少一项前置强制依赖generate_report 技能声明了“需要输入前置数据摘要如果没有则返回错误”但这个依赖声明在配置时被我忽略了。修复方式是拆分技能职责写报告只能基于已有数据不承担数据获取职能同时给 generate_report 加上“禁止在无可信数据情况下直接输出KPI数值”的约束描述。这类问题在设计上是可以提前预防的我的教训是技能职责必须足够原子化粒度太粗就会给模型留下“偷懒”和“幻觉”的空间。6.3 后续规划技能库的多元化和跨场景复用agent-skills 这套方案目前的形态已经能支撑日常使用但离我理想的状态还有距离。接下来我计划做两件比较有杠杆的事。第一件事是完善技能库的“跨场景复用”能力把一个在数据分析场景下沉淀好的技能直接迁移到另一个场景比如运营报表时只替换数据源连接参数和输出格式模板不改技能内部核心逻辑。这就需要在技能包结构里把业务相关的配置和逻辑相关的代码彻底分离我目前正在抽一个 configs 目录专门放这类环境差异配置。第二件事是把技能评估从手工标记推进到自动化回归。当前的路由准确性评估还是靠人工攒 eval 集成本高且覆盖不足。我准备引入一套基于 LLM-as-a-judge 的评估管线给定历史对话和正确技能命中序列让大模型对当前路由结果打分再把低分样本沉淀为新的训练与调优素材。这套机制打通后技能库的迭代周期就可以从两周缩短到两三天真正支撑起“技能也是产品”的定位。写在最后的个人体会如果你现在也要做 Agent 技能化我最大的建议是先不要急着写一堆技能而是先把技能注册、路由、日志和评估这套“骨架”撑起来。技能本身是业务资产会越积累越多但骨架决定了这些资产能不能被高效地组织和使用。我在这套系统中投入的前几周几乎全在处理骨架而没怎么碰具体业务技能但正是那段时间的积累让后续的开发变快了。另外一点体会是Agent 技能化和传统工具调用的关键差异在“设计边界”。传统编程里调用方总是知道自己在做什么Agent 场景下调用方是模型它总是猜着来。你所有的设计都得围绕“怎么减少猜测空间”来做技能描述用具体样例参数校验用严格 Schema输出格式统一标准化失败时按字典序排查。每一步都在压缩不确定性压缩到最后模型的选择自然就对了。agent-skills 这个项目到今天还在不断迭代中但它已经从一个“实验性玩具”变成了我日常工作中离不开的基础设施。希望这篇分享能给你一些启发少走一些我走过的弯路。如果你也在做类似的事情欢迎在实践中多记录多复盘把属于你自己的“技能库”一点点养大。