尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Agent技能选择不准确?元技能机制实现智能路由

Agent技能选择不准确?元技能机制实现智能路由 1. 从一次选错技能事故说起Agent 到底难在哪我这几天在调试一个多技能 Agent 时遇到了一个非常窘迫的场面用户发来一句很简单的请求——帮我把上周的销售数据整理成周报然后邮件发给团队。当时这个 Agent 已经接入了数据分析、周报生成、邮件发送、日程管理等十几个技能理论上这个请求完全在能力范围之内结果它干了件让我哭笑不得的事它直接调用了邮件发送技能试图把还没生成的周报先发出去。这个场景不是个例。凡是做过 Agent 开发的人几乎都会在某个阶段遇到类似的尴尬——Agent 明明什么都会却总是关键时刻选错。问题的根源在哪里很多人一开始觉得是模型理解能力不够换更强的模型就好了。但后来我发现真正的问题往往是工程问题Agent 在面临多个技能时缺少一个明确的决策机制来回答一个看似简单的问题——我到底该用哪个技能如果你只是给 Agent 接了两三个技能这个问题还不明显因为模型随便一猜也能猜对。但当技能数量超过十个、二十个尤其是有一些功能边界模糊、名称相近的技能时决策质量直接决定了 Agent 好不好用。这也是为什么元技能 using-agent-skills这个机制在 Agent 工程化里越来越被重视。这篇文章我会把手上的实践内容完整摊开从技能选择问题的本质、using-agent-skills 元技能的设计逻辑到运行时的完整链路、工程落地细节再到实际调试中踩过的一堆坑一次讲清楚。适合正在做 Agent 开发、被技能选择不准折磨的工程师也适合想理解 Agent 架构设计思路的产品和技术同学。2. 技能管理为什么会成为 Agent 开发的瓶颈2.1 把所有技能塞进提示词是最省事也最糟糕的方案早期我做 Agent 的姿势非常简单粗暴把所有技能的描述、参数、使用说明全部写进 system prompt然后让模型自己看着办。二个技能的时候挺好使五个技能的时候勉强能用等到接入了十个以上技能问题就全来了。首先是 token 消耗问题。每个技能描述如果写 200 到 300 字十个技能就是两三千 token而且这些 token 是每次请求都要带的。你以为这是小事在高频调用场景下这直接影响成本也直接影响响应速度。更要命的是上下文窗口是有限的你把技能描述占掉一大截留给对话历史和工具返回结果的空间就变少了Agent 的记忆和理解能力都会跟着下降。其次是注意力稀释问题。这跟人很像——你面前摆着 50 种工具每种工具都配了一份详细说明书你在紧急情况下反而不知道该拿哪个。Transformer 结构的注意力机制也是类似的技能描述越多模型对每个技能的注意力权重就越容易被分散。尤其是在两个技能功能相近的时候比如生成周报和生成日报模型经常会混淆。最核心的问题在于你把技能选择这件事完全交给了模型自由发挥但模型本质上是在做自由联想而不是在做结构化决策。它没有一个明确的决策路径、判断标准、兜底策略全靠对 prompt 指令的模糊理解。2.2 技能选择的本质一个结构化的路由决策问题如果把 Agent 的技能调用类比成一个服务后台你会立刻想明白每个技能就是一个微服务而 Agent 的决策环节就是 API 网关。网关的核心职责是路由——根据请求的特征把流量分发到对应的服务上。没有哪个正规系统会让客户端自己决定我该调用哪个后端服务那中间一定有一个路由层。Agent 的技能选择也是同样的道理。用户说了一句帮我把销售数据整理成周报发给团队这背后实际上隐含了几个子任务先要取数、再要整理分析、生成报告、最后发送。Agent 需要理解用户意图、拆分任务、然后决定调用哪个技能来完成每一步。而 using-agent-skills 这个元技能就是在这个层面发挥作用。它不是直接干活儿的普通技能而是管着其他技能如何被调度的技能——我把它类比成技能的技能。普通技能回答的是我能做什么元技能回答的是在当前情境下应该调用哪个技能来做。2.3 硬编码规则和向量检索为什么不能完全替代元技能有人可能会说那我不用元技能我写一套 if-else 规则来做路由或者用向量检索做技能匹配不也行吗这些方案我全都试过可以做补充但很难做主力。硬编码规则的问题是僵化。用户表达意图的方式太多了把数据发出去 发个邮件给团队 通知大家一下可能是同一个意图你不可能把所有的说法都枚举完。一旦出现规则覆盖不到的表述路由就直接失败了。而且规则一多维护成本极高每接一个新技能都要改路由逻辑很容易改出 bug。向量检索是另一个常见思路把用户请求和技能描述分别做 Embedding然后算相似度选最接近的技能。这个方案对单一意图 技能边界清晰的场景表现不错但一旦请求是复合意图或多步任务相似度匹配的结果就很不稳定。比如帮我把上周的销售数据整理成周报然后邮件发给团队这句话向量上可能和邮件发送技能的相似度最高因为邮件这个词出现在句尾且语义权重较大分析类的意图反而被它盖过去了。元技能的路由方式和这两种都不一样它不是靠规则或相似度来做匹配而是把技能选择本身当成一次模型推理任务。它会先列出所有可用的技能选项再把用户的当前请求、对话历史、上下文信息一并交给模型让模型分析后输出用哪个技能、为什么、置信度多少。这一步看似只是加了一次模型调用但它的设计价值在于把该用哪个技能从模糊的潜意识行为变成了一次显式的、可观测的、可调试的推理过程。3. using-agent-skills 元技能的设计逻辑与核心机制3.1 为什么叫元技能一场职责分离的架构革命元这个词在计算机领域并不陌生元数据、元编程、元学习……它的核心含义都是关于某某的东西。那么元技能就是关于技能的东西。在一个配置了 using-agent-skills 的 Agent 架构里技能被分成了两个层次。底层是普通技能负责执行具体的操作比如调用天气 API、查询数据库、生成图表上层是元技能负责回答一个更高维度的问题面对当前的用户请求应该调用哪个底层技能或者是否需要把多个技能编排组合起来。这个设计最大的价值是把做什么和怎么做分开了。用户问今天适合洗车吗普通技能是查询天气和查询洗车指数这种具体动作而元技能负责理解为这个问题需要先获取当前位置和当天的天气数据再结合洗车条件做判断因此应该调用获取地理位置和查询天气两个技能并串联执行。如果没有这个决策层你要么把所有逻辑压在模型的一次性输出上要么就得写一堆硬编码流程。有了元技能每个普通技能只需要管好自己的一亩三分地复杂场景由元技能来编排。3.2 技能注册表Agent 的百宝箱清单要让元技能做决策前提是它得知道有哪些技能可以用。所以工程上第一步是把所有技能整理成一个结构化的注册表通常用 JSON Schema 或 YAML 格式维护。我的实践里每个技能条目至少包含这样几个字段技能名称、功能描述这个描述会直接影响模型的选择判断后面我会专门讲怎么写、输入参数、输出类型、调用权限级别、依赖关系。下面是一个简化的例子skills: - name: query_sales_data description: 查询销售数据支持按时间段、地区、产品线筛选返回结构化销售记录 parameters: date_range: string region: string product_line: string output_type: json_array permission_level: read_only tags: [data, sales, analysis] - name: generate_report description: 根据数据生成周报或月报支持自定义模板和图表输出 Markdown 或 PDF 格式报告 parameters: report_type: string data_source: string format: string output_type: document permission_level: normal tags: [report, document] - name: send_email description: 发送邮件给指定收件人仅支持文本和附件无法直接生成报告内容 parameters: to: string subject: string body: string attachments: array output_type: boolean permission_level: sensitive_action tags: [mail, notify]这份注册表就是 Agent 的百宝箱清单元技能在每次做路由决策时会把注册表里的技能名称和描述作为候选选项传入模型。注意这里有一个关键点传给元技能的并不是全部技能的所有细节而是经过压缩的摘要信息具体参数细节等到真正调用目标技能时再加载。3.3 核心思路把技能选择从潜意识行为变成显式推理我觉得理解 using-agent-skills 最关键的一点是要想明白它和你不加元技能直接把技能列表扔给模型有什么本质区别。不加元技能时技能选择是模型在生成回复过程中的一个隐式动作它可能在思考过程中觉得用户想发邮件就直接调用了 send_email。整个过程是不可观测的你只能看到最终结果很难知道模型到底基于什么做了选择。加了元技能后技能选择变成了一个独立的显式推理步骤。模型的输出不再直接是动作而是先输出一段结构化的决策结果比如用户请求隐含了数据收集、报告生成和邮件发送三个子任务我计划先调用 query_sales_data 获取数据再调用 generate_report 生成周报最后调用 send_email 发送整体置信度 0.85。这个变化的工程意义太重要了。因为一旦决策结果是显式输出的就意味着你可以做日志、可以监控、可以调试、可以设置阈值拦截。哪一步决策错了你翻日志就能定位置信度低的时候你可以让 Agent 主动向用户确认而不是闷头乱来。这种可观测性是 Agent 能够从 demo 走向生产环境的必要条件。4. 运行时拆解一次技能路由的完整链路4.1 第一步意图预处理与候选技能筛选我要强调一个很容易被忽视的细节using-agent-skills 元技能虽然是做路由决策的但它在运行时的第一步并不是直接把所有技能都丢给模型让它选而是先做一个候选技能粗筛。为什么因为如果 Agent 接入了 50 个技能每个技能的描述平均 150 字光技能列表就是 7500 token这一大堆信息直接塞进上下文模型不仅处理慢注意力也会被稀释选错的概率反而更高。我的做法是分两级第一级用轻量级规则或向量检索从技能注册表里先筛出 3 到 5 个最可能的候选技能第二级才把候选技能列表连同用户请求、对话历史一起交给元技能做精细化决策。比如用户说帮我查一下北京明天天气顺便把结果发我邮箱粗筛阶段通过关键词和向量匹配可能筛出查询天气和发送邮件两个技能元技能只需要在这两个候选里做判断而不是在 50 个技能里大海捞针。粗筛本身就是利用技能描述中的 keywords 或 embedding 相似度来做的可以做得很轻量。4.2 第二步元技能触发与决策提示词构造元技能的触发有两种主要方式。一种是把 using-agent-skills 注册成一个普通工具模型在觉得当前请求可能需要调用技能的时候主动发起调用另一种是在 Agent 的主循环中设定一个规则如果用户请求不是简单闲聊且检测到潜在的执行意图就先进入元技能决策流程。我实际项目中更倾向于第一种方式让它更贴近工具调用的自然模式。下面是一个简化的元技能提示词模板供参考你现在是一个技能调度系统。用户有一个请求你需要从候选技能中选择最合适的技能来执行。 候选技能列表 {{ 候选技能内容的 JSON 列表 }} 用户当前请求 {{ user_query }} 对话历史摘要 {{ conversation_summary }} 请按以下步骤分析 1. 判断用户请求的核心目标是单一技能还是多个技能的组合 2. 对每个候选技能评估它与请求的匹配程度高/中/低 3. 如果匹配度最高的技能存在歧义说明你的判断依据 4. 输出 JSON 格式的决策结果包含 selected_skills 数组和 confidence 字段 只在 confidence 低于 0.6 时返回 need_clarification 为 true这里有一个值得注意的设计细节提示词里明确要求模型先把候选技能的匹配程度梳理一遍再输出决策结果。这个先分析后输出的步骤看起来多此一举但实际上能显著提高决策质量。因为如果直接让模型选一个技能输出它容易偷懒跳判断如果要求它先评估每个候选技能等于强制它进入一次推理过程决策的稳定性会好很多。4.3 第三步模型返回结构化决策与执行元技能决策的输出是一段 JSONAgent 运行时先解析它。比如模型可能返回{ selected_skills: [query_sales_data, generate_report, send_email], plan: 先查询销售数据再生成周报最后发送邮件, confidence: 0.85, need_clarification: false }解析之后Agent 就知道自己该做什么了先调用 query_sales_data 拿到数据再把数据传给 generate_report 生成周报最后把生成的周报作为附件传给 send_email。每个技能调用后都会返回结果结果会继续作为上下文传入下一轮决策直到整条链跑完。这里要特别说明一点using-agent-skills 元技能输出的是技能的编排计划而不是替 Agent 做完所有事。具体的参数填充、技能调用、结果处理仍然由 Agent 主循环来完成。元技能的职责边界非常清晰——只管选哪个、按什么顺序不管具体怎么做。4.4 小节从触发到决策到执行一次完整的路由链路走完本质上就是在 Agent 和技能的众多 API 之间加了一层决策网关。这层网关让 Agent 不用再靠猜来选择技能也让工程师有了一个可以集中管理路由逻辑的位置。后面我再详细展开工程落地时那些更容易踩坑的地方。5. 工程落地技能描述、上下文压缩与安全控制5.1 怎么写技能描述模型才不容易选错技能描述是决定元技能决策质量的重要因素也是很多人容易忽略的地方。你可能觉得描述写得越详细越好但实际并非如此。描述太短模型无法理解技能的边界描述太冗余又会挤占上下文、干扰决策。我的建议是每条技能描述控制在 100 到 200 字之间并且必须包含四类信息技能的明确动作动词开头、输入的必要条件、输出的形式、不适合使用该技能的反例场景。举一个反例这是我早期项目里写得很差的技能描述send_email: 发送邮件功能这种描述不仅毫无区分度还会让模型觉得凡是跟沟通相关的事情都可以用它。我后来改成了这样send_email: 向指定收件人发送电子邮件支持文本内容和附件需要明确的收件人地址、主题和正文通常用于用户要求发邮件通知发送报告/文件时不适合用于生成内容本身如果用户请求帮我写一封邮件但未要求发送应优先选择邮件草稿生成技能这个描述里明确了触发条件发邮件通知发送文件、前提条件需要收件人地址等、以及一个重要的反例生成内容≠发送内容。加上了反例之后我项目里的误调用率明显下降。这个思路适用于所有技能描述值得刻意练一练。5.2 上下文压缩当用户请求变成小作文用户发来的请求有时候非常长尤其是包含大段背景说明的场景。如果直接把完整请求丢给元技能token 消耗大而且很多无关细节会干扰路由判断。我的处理方式是做一层请求摘要先让模型把用户请求的核心意图浓缩成两三句话再传给元技能做决策。这实际上是在决策之前多了一层轻量级的意图理解虽然多花了一次调用但换来的是更稳定的路由结果在长文本场景下非常值得。对话历史同样需要处理。如果 Agent 和用户已经来回聊了很多轮把全部历史都传给元技能显然不现实。我的做法是用一个滑动窗口只保留最近 5 到 8 轮的对话摘要并特别标注出用户在这一轮请求之前的上下文里提到过的关键约束条件比如之前说的是按华东区的数据这类信息。5.3 权限分级危险技能必须设置高置信度门槛Agent 的技能并不都是无害的。发送邮件、删除数据、对外发布内容这类技能一旦误调用就可能造成严重问题。所以在 using-agent-skills 的决策链路里应该引入权限分级机制。我的项目里把技能分成了三个等级只读类技能、普通操作类技能、敏感操作类技能。元技能在输出决策时对于敏感操作类技能的 confidence 要求会更高——比如只有 confidence 大于 0.85 才允许直接执行否则必须向用户确认。除此之外对于敏感技能还可以额外加一条规则即使元技能选中了它也需要做一次二次校验把用户请求的关键信息提取出来验证是否与技能参数对应。比如用户要求删除 2023 年之前的数据那么校验层需要确认技能参数里确实包含了时间范围2023 年之前并且数据范围没有被错误扩大。这层校验可以在很大程度上避免 Agent会错意导致的不可逆操作。6. 实测中的坑技能选择失败的三类典型场景与排查路径上面说了那么多设计思路和规范但真正到了实际运行你会发现理论上没问题和实际上不出错完全是两回事。我在调试过程中踩过不少坑下面把最有代表性的三类问题连同排查路径一起写出来。6.1 场景一该用技能 A却鬼使神差用了技能 B这个场景最让人头疼因为它不是完全不会选而是偶尔选错。发生频率最高的位置往往是两个功能边界有重叠的技能之间。比如我之前项目里的生成周报和生成数据分析报告——周报本质上也是数据分析报告的一种两个技能在功能上大面积重叠模型经常搞混。排查这类问题我会先翻日志里元技能的中间分析部分看看模型自己是怎么写的判断依据。比如它能清楚地写出来用户要求生成一周维度的报告因此选择 generate_report 周报技能——那问题就不在决策逻辑而在技能划分本身。两个技能边界模糊再聪明的模型也没法稳定区分。解决方法有两种要么合并技能让一个技能同时支持周报和分析报告两种模式要么在技能描述里把边界说得更死——明确周报是数据分析报告的一种仅当用户明确提到周报/日报时使用本技能。6.2 场景二元技能完全不被触发Agent 直接胡说八道另一种更隐蔽的问题是Agent 在需要技能时压根就没有进入元技能决策流程直接凭自己想象回答用户。比如用户问帮我查一下上海今天的气温Agent 直接说上海今天 23 度——但实际上它并没有调用任何天气查询技能这个温度完全是模型编出来的。这种幻觉式回答是 Agent 应用里非常危险的因为回答看起来结构完整、语气笃定用户很难分辨是真实查到的还是编的。排查路径从两层入手第一层检查元技能的注册方式——当 using-agent-skills 被注册为工具时模型确实有一个工具调用的概率但概率不是 100%。对于明显需要实时或外部数据的请求需要在提示词层面做更强的指令约束比如明确告诉模型当用户请求中包含查询、搜索、获取等需要外部数据的动作时你必须先调用技能调度工具禁止直接凭已有知识回答。第二层检查模型的基础能力——有些较小的模型在多轮对话中会忘记去调用工具这种情况下要么升级模型要么降低路由触发的复杂程度。6.3 场景三元技能反复被触发陷入无意义的循环还有一种比较闹心的情况元技能触发后返回了一个技能计划Agent 照着执行执行完却发现结果不满足用户请求于是又回到元技能重新决策然后选出了同样的技能再次执行再次失败……整个流程陷入死循环。有一次我排查了很久才发现循环的根源是一个技能的实现 buggenerate_report 依赖数据查询结果但因为参数传递不对数据查询函数返回了空数组生成报告时直接报错Agent 重新决策时又没有意识到错误原因出在参数上于是又选了同一个技能。这类问题的治理要从两个方向下手一是给每个技能调用加上次数上限和错误反馈机制技能执行失败时把失败原因作为上下文传给下一轮决策让元技能能做出不同的选择二是给整个执行流程设置一个最大重试次数超过后强制叫停向用户道歉并说明当前状态而不是无限循环浪费 token。7. 最后再分享几个实战心得项目跑了几个版本之后我对 using-agent-skills 元技能这个机制有了更实际的理解。如果你正在设计 Agent 的技能路由层有几条经验我觉得值得参考。第一条元技能不要追求大而全宁可让路由链路分两级甚至三级也不要试图一次决策解决所有问题。第一级粗筛用轻量级方法快速收敛候选集第二级再交给模型精细决策这个分层思路在技能数量多时几乎是必须的。第二条技能描述的质量远比数量重要。我见过一些项目为了能力丰富而不停加技能结果技能之间互相打架路由准确率反而下滑。与其加一堆边界模糊的技能不如把核心技能打磨到清晰、稳定。第三条给每次元技能决策留 trace 日志。调用时间、候选技能列表、模型中间分析、最终决策、置信度、实际执行结果全都记录下来。这会让后续排障轻松很多也能积累出用于优化技能描述的真实数据。做一个会选技能的 Agent本质上不是让模型更聪明而是让你的系统更有序。元技能这套机制就是把选择这件事从玄学变成工程让每一步都有据可查、有迹可循。
返回列表