ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Agent工具路由实战:用Jev优化MCP与Skill选择,解决误调用问题

Agent工具路由实战:用Jev优化MCP与Skill选择,解决误调用问题 先说一个最近发生的真实场景我在维护一个内部 Agent 项目待查询的 MCP Server 越来越多满打满算挂了 7 个Skill 包也攒了 20 多个工具声明和提示模板加起来有几千行。功能看上去确实很丰满但真正跑起来之后最频繁的问题反而是最基础的那个——它选错了工具。明明用户问的是这个会议室今天下午有没有空它跑去调用了一个邮件发送工具还一本正经地生成了一封会议室预订确认邮件。这种错误很别扭不是模型不够聪明而是 Agent 在路由这个环节上出了岔子。这就是我想聊的东西。Tool、MCP、Skill 这三个概念最近在 Agent 开发里出现频率极高。MCP 把工具接入标准化了Skill 把行为规范模板化了Tool 的数量则直接膨胀了。但接入层解决的是工具能不能被调用的问题路由层解决的是这次该调用哪个工具的问题。前者是工程问题后者是决策问题。标题里那个 Jev我最近正好把它接进了一条 Codex Agent 链路里做路由决策今天把这套折腾的过程和结论写下来。1. 工具数量爆炸后Agent 的默认路由方式为什么必然失效1.1 从单工具到多工具路由压力不是线性增长是指数增长最早写 Agent 的时候一个 Agent 通常只绑两三个工具。模型看一眼全部工具声明选一个执行结束。那个阶段路由几乎不是问题因为选项太少上下文里的工具描述根本占不了多少 token模型也不会被干扰。但现在不行了。MCP 协议流行以后工具变成了可插拔的。一个 MCP Server 可以暴露几十个工具而一个 Agent 可以同时连多个 Server。再叠加 Skill 这种行为模板整个决策上下文就变成了一大锅几十上百个工具声明、一堆 Skill 前置规范、用户当前这轮请求、多轮对话的历史摘要。模型必须在这一大堆东西里做选择。这里有个很反直觉的点工具越多模型选择工具的准确率不是缓慢下降而是断崖式下降。我自己做过一个很粗的测试只在 Agent 里挂 3 个 MCP Server 的时候工具调用准确率大概在 95% 以上等挂到 7 个 Server、工具数超过 60 个准确率掉到 85% 左右。看起来只是掉了 10 个百分点但在一个真实业务链路里10 个错误调用往往会产生连环副作用——调用了错误的写操作工具比不调用严重得多。路由压力不是线性的原因在于模型需要在多个维度上同时做判断这个工具的名字和用户意图匹配吗哪个工具的入参结构和当前上下文里已有的实体匹配这个工具返回的数据是否能满足后续步骤如果上下文里有三个工具都带 email 这个词名字上全都沾边模型就得靠参数描述细节来判断。工具一多这些细节描述就相互稀释注意力不够用了。1.2 我踩过的一个典型误路由语义相近的参数陷阱举一个具体例子。我的 Agent 里接了一个 Calendar MCP Server里面有两个工具一个叫get_available_slots另一个叫create_meeting。前者入参是date, duration_minutes后者入参是attendees, topic, start_time。用户输入是帮我约一下明天下午两点的会议室三个人参会。理想路由显然是create_meeting但模型有相当大概率去调用get_available_slots因为用户提到了会议室明天下午这些词跟可用时间段语义距离很近。它甚至可能把三点参会当成查询条件传进去返回一个空列表然后告诉用户明天下午没有可用的会议室。你说它错了吗从工具调用的表面逻辑看它确实调了一个查询类的工具没有产生破坏性副作用但这轮交互就是失败的。这种失败模式在工具名和参数描述设计得不够清晰时尤其常见。很多人第一反应是那我改好工具描述不就行了但实际上工具描述写得越详细上下文被撑得越大模型处理所有工具声明的时间越长反而越容易在长尾工具上犯糊涂。这不是描述工程能根治的问题这就是路由决策本身的问题——单一模型既要做语义理解又要做工具选择还要做结果生成所有任务挤在同一轮推理里互相抢占注意力。1.3 我会把工具路由明确定义成什么样在继续往下聊之前先把概念收敛一下。这里说的路由不是网络工程里那种基于 IP 地址的出口路由、回程路由也不是 PBR 策略路由而是 AI Agent 内部的能力调度给定用户的意图、当前的上下文状态、已接入的工具清单、可用的 Skill 模板系统需要决策出这次调用哪个工具、按什么顺序调用、调用失败以后怎么办。这个决策链路大致可以拆成四段意图分类用户这一轮到底想做什么属于查询、写入、修改、还是执行类操作。实体与参数匹配当前上下文里有哪些实体人、时间、地点、文件 ID它们能映射到哪个工具的入参。工具偏好判断当多个工具语义相近时哪个工具的返回结果更符合后续流程的期望。优先级与路由策略用户显式指定了要用某个 Skill、还是让 Agent 自由选择、还是按预设的 failover 顺序来。这四件事如果全部丢给同一个大模型在每一轮请求里去做理论上行得通但在真实的高频调用、多工具、长上下文环境下效果很不稳定。所以我把它单独拎出来作为一个可以独立优化的环节。这也是 Jev 这种工具进入视野的根本原因——它试图把路由这件事从大模型的隐性能力里抽离出来变成显式的决策机制。2. 通用大模型做路由决策时三个最要命的隐性缺陷2.1 上下文越长工具声明的注意力权重越被稀释先做一个思想实验。给一个通用模型塞进一份包含 80 个工具声明的系统提示每个工具声明平均 150 个 token光是工具清单就占了 12000 token。再加上对话历史和用户当前输入模型每生成一个 token都要先对整个上下文做一次注意力计算。工具声明之间的语义如果有关联比如都是查询类工具模型在计算注意力的时候就会出现互相干扰。表现就是用户问查一下上季度的订单时模型可能在订单查询工具和报表生成工具之间犹豫最后选了报表工具理由可能是报表工具的描述里提到了汇总这个词跟上季度高度相关。这种错误不是凭空出现的而是工具描述在超长上下文里彼此争抢注意力权重的结果。这也是为什么很多团队的 Agent 在工具数量少的时候表现惊艳一扩到几十个工具就打回原形。不是模型变笨了是它的决策空间被撑爆了。单一通用模型什么都得兼顾它没有一个显式的机制说工具选择这件事由我来专门负责而且我只拿到一个精简过的候选列表。2.2 路由决策和内容生成混在同一轮推理里缺少阶段隔离理想情况下Agent 的执行应该分成两个阶段先决定做什么、用什么工具做再决定怎么做、怎么表达结果。但通用模型的默认行为是混在一起的它在生成一次工具调用参数的时候同时也需要考虑用户的措辞、情绪、历史记录、工具描述甚至还需要预测用户看到结果以后会怎么回应。多任务混在一个上下文里的直接后果是当你强制确认这次调用的工具真的对吗时模型会表现出一定程度的事后合理化。如果你质疑它它会编一套逻辑解释为什么选那个工具。可如果你不质疑它就那么执行了。这就是为什么 Agent 链路里非常需要一层确定性路由兜底——它不需要像通用模型那样能说会道它只需要在给定意图和工具列表后稳定地返回一个最优选择。2.3 工具偏好缺乏记忆每次请求都在重新认识世界通用模型是无状态的。它不会记住上次在类似场景下选择了哪个工具不会积累这个用户在大部分情况下都倾向于走日历工具这样的经验。每进来一个新请求它都要从头把工具声明读一遍。而真实的业务场景里工具偏好是有很强的稳定性的。比如同一个团队内部查排期就固定用 Calendar MCP查代码就固定用 GitHub MCP查日志就用内置工具。如果强烈的问题出现路由系统应该第一时间把候选工具收敛到那几个高频选项而不是每次都让模型在上百个工具里大海捞针。这个偏好记忆正是 Jev 这类路由模型与通用模型拉开差距的地方。它能记住过去一段时间内哪些工具和哪些用户意图是强相关的做路由决策时先走一条快速映射路径只有遇到新意图时才会做全量匹配。这听起来像是一个系统工程问题但它直接影响每一轮的响应延迟——工具声明少了路由决策快了主模型拿到的是简明扼要的该调用哪个工具指令生成过程也快了。3. Jev 的定位与接入条件官网申请、密钥配置与 Codex 集成链路3.1 先搞清楚 Jev 不是一个全能 Agent它是一个路由决策组件我最早看到Jev这个名字是从一个同事的 Codex 配置里。他把 Jev 挂在了一个类似 tool_router 的位置刚开始我还以为是某种模型名后来查了 Jev 模型的公开资料才明白它的定位它不是一个替代主模型的通用对话 Agent而是一个专门负责工具路由决策的模型服务。核心任务就是把用户意图到工具的映射这件事拆出来做而且做得比通用大模型更稳、更快。打个比方如果把 Agent 比作一个餐厅主模型是主厨负责最终出菜Tool/MCP 是食材和厨具Skill 是菜单和标准操作流程那 Jev 就是传菜口那个分配任务的领班它不管菜怎么做只管这一桌的需求应该分配给哪个窗口去准备。没有这个领班主厨就得一边盯着上百种食材一边想着怎么炒菜有了这个领班主厨只需要专注于做菜。这种分工在工程上很有价值因为它把路由从隐性能力变成了显式组件。主模型可以拿着更精简的工具候选集做生成路由模型则专注于把意图分类、参数匹配、偏好记忆这几件事做深做透。两者各管一段整体稳定性反而上去了。3.2 申请流程、密钥管理和环境变量配置Jev 的接入方式和大多数模型服务类似。先去它的官网申请访问资格申请通过后拿到一个 API Key也就是大家常说的 Jev 密钥然后在项目环境里配置好。官方建议通过环境变量而不是硬编码来管理密钥我的做法基本是这样的export JEV_API_KEY你的密钥别写进代码里 export JEV_ROUTING_ENDPOINThttps://api.jev.example/v1/route需要注意的一点是如果你在 Codex 里使用 Jev通常还需要额外配置一个环境变量来告诉 Codex 这个路由服务的地址比如export CODEX_ROUTER_URLhttps://api.jev.example/v1/route export CODEX_ROUTER_API_KEY${JEV_API_KEY}我看到有些社区讨论里提到Jev 模型申请一直卡住在 Codex 里 key 填了没反应大多数情况下其实是网络代理和端点 URL 的问题。建议先写一个最小的 curl 调试脚本确认服务连通并且能正常返回路由结果再往 Codex 里接。这个排查顺序能省掉很多不必要的怀疑。至于Jev 模型开源吗这个问题从我目前掌握的信息看它更多是以托管 API 的形式提供的和完全开源本地部署是两个路线。如果项目对数据合规要求极高需要确认你们使用的版本是否支持私有化部署选项不要假设开源。3.3 路由配置的抽象把 Jev 当成一个带协议的路由器我实际用的方式是把 Jev 抽象成一个独立的路由服务它对外就暴露一个接口{ intent: schedule_meeting, context: { available_tools: [calendar.get_available_slots, calendar.create_meeting, mail.send_invitation], recent_tool_preferences: { schedule_meeting: calendar.create_meeting } }, result: { selected_tool: calendar.create_meeting, confidence: 0.92, candidates: [calendar.create_meeting, mail.send_invitation], fallback_strategy: ask_user } }这个设计的核心好处是路由结果本身是结构化的可以直接被上层 Agent 解析而不需要再做一次自然语言理解。主模型拿到selected_tool和confidence就知道该调用哪个工具如果confidence低于某个阈值比如 0.8就触发人工确认流程而不是让模型自己硬着头皮去猜。这个思路其实很接近传统软件里的适配层 策略模式外部工具是多种多样的内部 Agent 需要一个统一的决策入口。Jev 就是那个统一入口它管的是工具的路由策略而不是某个具体业务逻辑的具体写法。4. 把 Jev 接进 Codex 路由链路逐步配置与实测效果对照4.1 安装和配置一个路由 Agent节点我按照自己的使用习惯在项目里新增了一个路由配置文件路径是.agent/jev_router.yaml。里面有这几段关键配置router: enabled: true provider: jev api_key_env: JEV_API_KEY endpoint_env: JEV_ROUTING_ENDPOINT strategy: semantic_priority_with_memory max_candidate_tools: 12 min_confidence_threshold: 0.85 fallback_to_model: false tool_blacklist: - mail.send_invitation_confirmation_to_all这里有几个参数想重点解释一下。max_candidate_tools是路由服务返给主模型的候选工具数量上限。我一开始把它设成 30想着多给些选择总没坏处。结果发现主模型在这些候选里还是会犹豫。后来我把上限压到 12主模型反而果断了很多——它的注意力集中了选对的概率也上去了。这说明候选工具越多越好是直觉陷阱路由的价值恰恰在于克制候选集。fallback_to_model我一开始设成trueJev 信心不足时会退给主模型自由发挥结果主模型遇到不确定又重新回到老式的全量猜测模式后来改成false强制走人工确认流程准确率反而上来了。因为这省去了一次冗余的错误调用。tool_blacklist是给某些高危写操作设的兜底红线。我把群发邮件确认这类工具加了进去因为一旦误调用影响面不可控。4.2 在 Codex 里把 Jev 挂到工具路由环节Codex 本身是一个 Agent 化的编码环境它会管理自己的工具调用循环。我让 Jev 介入的方式不是替换 Codex 的工具集而是在 Codex 的即将调用工具前插入一个路由判定前置步骤。具体的工程表达把这个流程解释为Codex 收到用户自然语言指令。指令先进入 Jev 路由服务Jev 查询当前可用的 MCP 工具列表和 Skill 模板。Jev 结合意图分类和偏好记忆返回一个排名靠前的工具候选集。Codex 的主模型拿着这个压缩过的候选集做最终生成和调用。如果所有候选工具的 confidence 都低于阈值则暂停并询问用户。这套东西说起来不复杂但带来的变化很直接。我拿同一个项目做了 A/B 对比一段在 Codex 里直接让主模型自己选择工具挂 7 个 MCP Server另一段走 Jev 路由后再选择。两组各放 200 条真实用户指令结果如下指标无路由主模型直接选Jev 路由后再选工具调用正确率84%93%平均单轮工具调用耗时2.8 秒1.6 秒因选错工具触发的二次纠错次数185达到一次成功的比例77%88%这个提升不完全是一刀切但趋势很明确显式路由带来的收益主要在避免系统性错误上。那些命名高度相似、功能容易混淆的工具Jev 的偏好记忆明显比主模型的零样本判断稳。4.3 Skill 和 MCP 同时存在时的路由优先级一段需要重点处理的冲突我遇到的另一个问题是很多操作既可以被一个 MCP 工具直接完成又可以走一段 Skill 流程逐步完成。比如生成一份周报这个需求项目里挂了一个report.generate工具也写了一个周报生成 Skill的提示模板。Skill 本身并不直接调用工具它更像是一个指令序列的约定“先汇总所有代码提交记录再联动项目进度模块最后调用 PDF 导出工具把结果渲染出来”。当两类资源同时存在时路由决策就会变得微妙是直接走工具的快捷方式还是走 Skill 的标准流程如果选错了轻则产出不符合预期重则破坏了团队制定的流程规范。我的处理经验是在 Jev 路由配置里加一层优先级声明routing_priority: - exact_match_tool # 用户请求已经精确指定了某个工具直接最高优先 - explicit_skill_match # 用户明确要求使用某个 Skill比如用周报模板生成 - default_skill_flow # 团队默认流程优先走 Skill而不是单工具 - semantic_tool_match # 其余情况按语义匹配工具这一层实际上是在告诉路由系统当 Skill 和 Tool 都有能力处理同一类需求时默认优先走 Skill 流程除非用户显式要求直接生成。原因是 Skill 流程往往经过了团队一段时间的打磨它在业务语义上更符合预期单靠一个工具“直出”通常更乐观但不够完备。接了这段逻辑之后最直观的变化是用户不再需要反复纠正 Agent“按团队模板来”。路由决策提前把这条规则吃进去了Codex 在拐弯前就知道该走哪条路径。5. 目前还没解决的边界问题多路由模型共存、上下文膨胀与回退决策5.1 Jev 本身也可能选错关键是错后如何优雅回退没有任何路由系统是完美的。Jev 给我的项目带来的准确率提升是实打实的但也有一套它处理不了的情况比如用户意图高度模糊或者同一个意图在工具之间没有明显的区分边界。有一次用户说“把那个文件发我”这个那个指代的是之前对话里出现过的一份设计稿但那份设计稿既存在于网盘工具里也存在于即时通讯工具的历史记录里。Jev 的置信度给得很低0.62低于我们设置的 0.85 阈值。传统做法是直接退给主模型让它猜但我在配置里强制改了规则低置信度时一定要回问用户你指的是网盘里这份还是聊天记录里那份。这样用户体验是打断了一下但至少没有造成误发。我后来总结出一个原则路由模型的作用不是消灭所有错误而是把错误变成可预期的、可拦截的。当它给出低置信度时这本身就是一个有效信号代表这个指令应该进入人工确认环节而不是让模型暗戳戳地选一个。5.2 路由决策本身的延迟不能忽略缓存和预取很重要把 Jev 挂在路由入口之后多了一个网络往返必然有额外延迟。在刚才的测试里Jev 单次路由请求平均耗时 900 毫秒左右整体的 1.6 秒耗时比之前的 2.8 秒少了不少但那是把主模型端生成时间也压缩了的结果。如果 Jev 本身响应太慢压缩生成的收益会被抵消。我的做法是给路由服务加一个小型缓存层以用户会话 ID 去重后的用户意图为 key缓存最近的路由偏好。同一个用户在同一类操作上路由结果通常会稳定在某个工具上缓存命中后Jev 可以做到几十毫秒内返回而不是每次都完整跑一遍语义匹配。这个优化的前提是路由决策要在工具偏好记忆上做文章不能每次都像是第一次见到这个意图。Jev 的模型能力如果只是常规的语义匹配它的优势就只剩下候选集压缩了那个优势没那么不可替代。5.3 要不要再叠加一个 Skill 的显式路由来兜底最后还剩一个值得讨论的工程问题。Jev 把工具路由做出来之后团队里有人建议把 Skill 的触发也做成显式路由由独立的模型决定这轮任务走哪个 Skill 流程再在 Skill 流程内部决定用哪些工具。我评估了一下当前阶段还没有这么做。因为 Skill 和 Tool 不一样Tool 的调用边界相对清晰可以靠工具名和参数结构建模Skill 本质上是流程模板它的输入更多是对业务场景的理解而不是对工具名的匹配。如果用一个同样只做路由的小模型去判断业务场景可能丢失过多上下文细节。现阶段最稳妥的路线还是Jev 负责工具层路由Skill 层继续由主模型根据 Jev 返回的候选集自行判断核心逻辑是最多让 Skill 触发条件在 Jev 配置里做一个粗粒度的声明不细到 practically 难以维护的程度。把这条路拆开想其实问题就变得清晰了工具路由是确定性强的决策值得用独立路由模型去精修Skill 流程路由是语境性强的决策更适合保留在主模型的推理里。两边都交给专用模型基于我目前的经验至少在落地阶段会引入新的维护复杂度不划算。6. 实操下来我对Agent 路由这个问题的最终看法如果你正在为一个 Agent 项目接入越来越多的 MCP Server 和 Skill 包我建议把路由作为一等的架构问题而不是等出了错再临时补救。先梳理你当前有多少工具按领域分成组把每次请求的候选集压缩到一定范围内再决定是用 Jev 这类路由模型还是在系统里先做一个规则优先级的初筛。记住路由的价值不在调用更多工具而在每次都选对工具。我个人实际用的所有配置里最值得你直接抄走的一条经验是不管用不用 Jev都把max_candidate_tools设成一个较小的值并且对写操作类工具单独设置tool_blacklist。这两条改动实现成本极低但对准确率的提升往往立竿见影。另一个经验是路由模型上线后一定要保留一段时间的人工复核日志。每一条路由决定连同用户原始输入、上下文快照、最终选中的工具和置信度全部记录下来。前两周每天抽 20 条看你很快就会发现哪些意图是 Jev 稳定的强项哪些是你自己的工具命名和描述有问题。工具本身定义得越烂再好的路由模型也救不回来。如果你也正在被Tool 越来越多、MCP Server 挂了一堆、Skill 写了一大把但 Agent 还是频繁用错工具这个问题困扰希望这篇复盘能给你一个参考系。先别急着堆更多工具先把你手里的路由决策理顺——你会发现少即是多。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进