news 2026/9/28 15:51:58

60个工具下Agent挑花眼?工具路由与动态检索三招解决

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
60个工具下Agent挑花眼?工具路由与动态检索三招解决

六十个工具堆在 Agent 面前的时候,问题不是它“不知道选哪个”,而是它开始乱选、反复横跳、甚至干脆不干活。这段时间我在折腾一个内部办公助手,把各类接口从 PDF 处理、表格解析、定时任务、图片压缩到会议纪要全挂上去,前前后后凑了 61 个工具。结果原本 10 秒能跑完的 PDF 转图片任务,变成了模型先在十几个相似工具之间反复推理,然后调了一个参数格式完全不对的接口,报错后又重试一遍,最后告诉我“暂不支持”。不是模型变笨了,是工具选择的上下文压力把模型逼疯了。

这个现象在很多 Agent 项目里都藏着。工具少的时候,Function Calling 几乎是指哪打哪,工具一多,模型要在一个超长的系统提示词里做分类、排序、纠错,难度完全不亚于让一个人在一本 500 页的产品手册里找一句话。这篇文章就把我在 60+ 工具场景下踩过的坑、拆过的方案、调过的参数一次性说透。不管你是刚入门搭第一个 Agent,还是已经维护着几十个工具的线上服务,下面这套“工具路由 + 动态检索 + 描述重构”的组合拳应该都能给你些参考。

1. 六十个工具,Agent 为什么会挑花眼

1.1 一个再常见不过的现场

先说个我实际遇到的例子。当时系统里有几个功能上有重叠的工具,比如convert_pdf_to_png、pdf_to_image、image_converter,还有office_doc_convert。从设计者的角度,这些工具各自服务的文件类型和场景略有差别,但对于模型来说,它们就是“把 PDF 变成图片”的三张几乎一样的脸。用户问“帮我把这个合同的 PDF 转成图片发我”,Agent 在那轮推理里先分析了三个工具的描述,接着又比较了半天参数,最后调用了pdf_to_image,但传参用的是convert_pdf_to_png的字段格式,直接报校验错误。

这背后的问题就是:当工具数量从 10 个涨到 60 个,模型面对的不再是“该不该调用工具”,而是“60 个候选里哪个才是最佳选项”。Function Calling 的原理说到底就是让大模型在给定工具列表上做条件生成,模型要把用户 query 和工具描述做语义匹配,还要在 JSON Schema 里构建出合法的参数。60 个工具全部塞进 context,等于让模型在巨大的候选空间里同时做检索、分类、信息抽取和格式生成,任何一个环节出错,表现就是乱调、假调、拒调。

1.2 工具注册表膨胀对推理的三重压力

第一重压力是上下文窗口被工具描述占了太多。一个工具若写了 name、description、parameters,平均要吃掉 150 到 300 个 token,60 个工具加起来就是 9000 到 18000 个 token,这些 token 还集中在系统提示词里。模型做推理的时候,每轮都要重新“读”一遍这堆描述,能留给对话历史和中间推理的注意力就更少了,反应速度和准确率一起下降。

第二重压力是长上下文里的注意力稀释。业界有大量“lost in the middle”研究,模型对长文本中间部分的记忆和理解明显弱于开头和结尾。按我注册工具的习惯,新建的工具往往追加在提示词末尾,老工具挤在中间,结果老工具慢慢成了“隐形人”,明明早就挂好的image_resize被模型忘得干干净净。测试时我试过把某个高频工具的描述放在不同位置,放中间时调用率掉了将近四成,这数据相当吓人。

第三重压力是相似工具之间的语义干扰。工具多了,名称和描述难免撞车,模型在生成 tool_name 时,logits 分数会被这些相似项拉平,最终选出来的不一定是最合适的,而是描述和当前 query 表面重合度最高的。它会把“生成 PDF”和“编辑 PDF”搞混,会把“定时提醒”和“日程查询”搞混,本质上不是模型能力不行,是工具描述本身没有给到足够强的区分度。

2. 工具过多导致的典型故障表现

2.1 幻觉调用与参数错乱

幻觉工具调用是最常见也最头疼的问题,表现形式就是模型调用了工具,可这个工具的入参和真实 schema 对不上。比如send_email明明只接受to、subject、body,模型却自作主张加了个cc,或者把attachments传成字符串而不是数组。工具少的时候,模型还能靠例子里的模式兜住,工具一多,参数模板在上下文里被其他工具的描述冲散,生成 JSON 时就容易张冠李戴。

我遇到过一次特别离谱的,Agent 要查一个订单状态,居然去调了create_invoice,还煞有介事地传了一整套开票参数。之所以这么选,是因为工具描述里都有“order”这个词,模型在做语义匹配时把“查”理解成了“建”。这类错误在单次对话里很难通过重试解决,因为模型每次重新审视工具列表时,面对的还是同样的六十份描述、同样的模糊匹配压力。

应对这种幻觉调用,光靠 prompt 里写“请仔细选择工具”完全无效,模型的高概率 token 已经落错位置了。我在实践中更认可的做法是给 Agent 配一个可编程的“校验层”,在 Function Calling 返回之后、真正执行之前,用一个轻量级规则引擎检查参数 schema 是否合法,非法就直接打回重生成,而不是拿着坏参数去调真实接口。很多框架自带的“tool validation”就是干这个的,但默认开关往往没打开。

2.2 试探性绕路与拒绝执行

工具多了以后,Agent 会表现得特别“怂”。遇到一个任务,它先调 A 工具失败,再试 B 工具,又失败,然后就开始自言自语“这个任务无法完成”,主动放弃。真实场景里,一个“把 Excel 表格里的数据提取出来并生成图表”的任务,明明excel_reader和chart_generator两个工具都现成,Agent 却先去调了document_intelligence,因为它的描述里有“document”“data”这些词,结果返回的格式不对,又绕去调csv_parser,最后还是没拼出图表。

这类试探性绕路的根源是缺少一条快速失败的信号。工具多了,模型不知道哪个能赢,于是只能采用一种“试错”策略,用越来越高的 token 成本去换取一个不确定的结果。更可怕的是这种失败会“传染”,模型在连续失败后会怀疑整个系统的能力,出现“tool execution terminated due to error”之后直接拒绝继续干活。

我后来在系统里加了一个“工具失败原因反馈”机制,把失败的 JSON、校验错误的具体字段名、HTTP 状态码全部塞回给模型,并附一句“请换一个工具或者修正参数”。这个简单的做法把最终任务完成率从 67% 拉到了 81%,效果立竿见影。对比模型自己反复试探,不如给它一个明确的路障提示。

2.3 上下文爆炸带来的成本飙升

工具多了还有一笔隐性账单:token 成本。60 个工具,每个哪怕只算 180 token,那就是 10800 token 的基础开销。很多 Agent 框架每轮对话都会重新注入系统提示词,也就是说,用户哪怕只问一句“几点开会”,这 10800 token 也要一进一出算两遍费用。如果模型因为选择困难再来回推理几轮,成本轻松翻倍。

我做过一个粗略统计,采用动态工具注入之前,单次完整任务的平均 token 消耗大概是 28000,其中 40% 都砸在工具描述和无效试探上。改用动态注入之后,每轮进上下文的工具只有 8 到 12 个,单次任务平均 token 掉到 15000 左右,成本降了差不多一半,响应时间也从 15 秒压到了 6 秒。对于高频调用场景,这笔账算下来非常可观。

所以在工具多起来的早期就要有成本意识,不能把所有工具一股脑全塞进去。工具注册表不应该是“模型每轮必须阅读的图书馆目录”,而应该像一个“检索式抽屉”,只把最近用到的、和当前任务语义相关的几把工具推到前台。

3. 我的三个落地解法:路由、检索、合并

3.1 工具分组与子代理路由

第一个解法是给六十个工具按领域分层,而不是拉平成一个巨型列表。我按业务模块把工具分成四组:文档处理组、数据分析组、沟通协作组、系统管理组。然后设定了一个“总调度 Agent”,它不做具体执行,只负责根据用户请求选择进入哪个子代理,再由子代理在其专属工具集内执行任务。这样一来,真正进入模型推理上下文的工具数量从 60 个降到了每个分组内的 8 到 15 个。

这方案的原理是分治。总调度模型面对的是一个 5 选 1 甚至 4 选 1 的小分类问题,分类精度远高于 60 选 1;子代理模型面对的是自己领域内的小工具列表,任务目标清晰,干扰项大幅减少。我用的是 OpenAI 的 Assistants 风格子代理,其实用 LangGraph 的 subgraph 或 Dify 的工作流节点都能实现,关键是“路由决策”和“工具全集”要做物理隔离,不能只是逻辑分组后还堆在同一个 system prompt 里。

实际调试中我给总调度 Agent 写了一份极小但明确的路由表,比如“凡是涉及 PDF、Word、图片格式转换的,一律走文档处理代理;凡是涉及数据库查询、报表统计的,一律走数据分析代理”。路由描述越具体,模型选错分组的概率越低。这个方案上线后,工具调用准确率从 58% 提到了 79%,是最立竿见影的一步。

3.2 语义检索动态注入工具

第二个解法是给工具建索引,然后按用户 query 动态召回相关工具。做法很简单:把所有工具的 name、description、关键参数说明汇总成文本,用向量模型(我用的text-embedding-3-small)生成工具向量,存到本地向量库(比如 Chroma、LanceDB 甚至就是 NumPy 数组)里。每次用户发起请求时,把用户的 query 也向量化,做一次余弦相似度检索,只把 top_k 个工具注入系统提示词。

这个方案的核心逻辑是“不在推理时检索,而在注册时索引”。工具描述是静态的,提前向量化没有任何成本;query 向量化只需要多调一次 embedding API,耗时可以忽略。关键是 top_k 和相似度阈值要调试得当。我默认取 top_k=10,相似度阈值 0.3。测试中发现阈值太严会漏召回到常用工具,太松又会让无关工具混进来。有一类特殊情况是用户问“你有哪些能力”,query 和任何一个工具都不太像,这种就触发一个兜底机制:不做检索,直接把全部工具按分组目录发给用户。

这个方案还解决了“工具顺序敏感性”问题。动态检索后,最相关的工具永远排在最前面,模型一眼就能看到最高频的选项。即使某次召回的 top_k 里混入一两个不相干的工具,排在后面的位置也不至于干扰模型产生错误调用。实测下来,动态注入在相似工具较多、query 意图明确的场景下效果最好。如果你对 embedding 成本敏感,也可以考虑用 BM25 关键词召回做平替,体感差距不大。

3.3 描述重写与复合工具合并

第三个解法是对工具描述本身做瘦身和合并。很多工具描述写得像产品说明书,堆满了设计词汇、动词、同义词,模型根本抓不住重点。我给工具模板定了一个硬性要求:description 必须包含三个部分——这个工具干什么、大概适用什么输入、典型触发词。比如把convert_pdf_to_png描述写成“将 PDF 文件转为 PNG 图片。输入 PDF 路径,输出图片路径。常见于合同扫描件、纸质文件的图片化需求。”模型看到这个描述,匹配成本极低。

同时把功能重叠的工具合并成复合工具。比如原系统里resize_image、crop_image、rotate_image三个小工具,合并成一个image_editor,通过action参数区分 resize/crop/rotate。这样工具数量减少,每个工具的 schema 变大了一点,但总 token 开销明显下降,且模型在做“选工具”时只需要选一个而不是三个。合并的原则是“执行逻辑可以收敛在同一服务里”,如果两个工具的底层实现完全不同,合并反而会让参数校验复杂化,不建议硬并。

这里有一个比较反直觉的细节:工具名要起得足够口语化。fetch_latest_stock_prices和get_stock_data对工程师来说都能理解,但模型对短小、动词开头的名称更敏感,我后来统一把工具名改成了get_stock_prices、convert_pdf_to_png这类最直白的格式,调用准确率又提了几个点。工具名不是变量名,不需要追求语义长度,越接近自然语言越好。

还有一个容易被忽略的点:写描述时避免过度使用否定句。比如“此工具不用于查询天气”这种描述,模型的注意力会被“查询天气”吸引走,反而更容易误调用。要正向引导,写“此工具用于查询航班动态”,而不是“此工具不处理天气”。这在模型眼里就是两条完全不同的注意力路径。

4. 实操记录与参数调优

4.1 工具路由模块的搭建记录

路由模块我用的是最朴素的方式:一个RouteDecision函数,接收用户 query,返回目标分组名。一开始想用模型去判定路由,测试发现多一层模型调用就多一层延迟和误差,后来改成了“关键词规则优先 + 模型兜底”的混合模式。规则层用一组词表做粗筛,比如 query 里含“PDF/转换/图片”就直发文档处理组;规则没命中时再调模型做一次轻量分类,选分组而不是选工具,分类难度低,准确率很高。

这套逻辑的代码骨架大致是这样:

def route_to_group(query: str) -> str: rules = { "document": ["pdf", "word", "图片", "转换", "ocr", "ppt"], "data": ["表格", "报表", "统计", "sql", "数据库", "图表"], "communication": ["邮件", "会议", "日程", "通知", "提醒"], "system": ["日志", "部署", "监控", "配置", "权限"], } for group, keywords in rules.items(): if any(k in query for k in keywords): return group # 兜底:让轻量模型做分组 llm_resp = call_model(f"请将用户请求分入以下组别: ...") return parse_group(llm_resp)

规则层的目的是拦截绝大多数明确意图,模型兜底只处理模糊 query。这样既保证速度,又不会因为规则太死板而漏掉新说法。测试时注意一点:关键词不能写得过长,过长的词容易互相覆盖,比如“图片”命中文档组,“图表”命中数据组,如果 query 是“将图片数据做成图表”,两个规则都命中,此时要靠规则优先级或模型兜底来仲裁。

路由模块上线后,我统计了 200 条真实用户请求,规则层直接命中约七成,剩下三成走模型兜底,总路由准确率在 94% 以上。这个结果说明,路由决策不需要很聪明的模型,关键是决策空间要小、规则要清晰,把复杂留给子代理执行层。

4.2 语义检索模块的参数选择

动态工具检索这块,我重点调了三个参数:embedding 模型、top_k、阈值。embedding 模型从text-embedding-3-small换到text-embedding-3-large之后,相似度排序确实更合理,但延迟和成本上去了,单次任务多那几十毫秒还能忍,成本增加则要考虑调用量。如果每天几千次调用,还是 small 版本更划算,中文场景下 small 的排序质量也够用。

top_k 我做了几轮对比,结果如下:

top_k工具调用准确率平均响应时间上下文占用
572.1%5.1s约 1.5k token
1084.6%6.3s约 2.6k token
1583.9%8.0s约 3.8k token
全部(60)58.3%16.2s约 11k token

top_k=10 是最稳的点,top_k=15 准确率反而略降,因为混入了一些低相关的工具干扰判断。这个结果也再次印证了“少即是多”:编造一个不存在的参数。这类问题最有效的办法是在执行前端加 JSON Schema 校验,用项目里现成的jsonschema库或者自己写一个字段核对函数,一次校验不过就打回重新调用。具体可以这么处理:捕获模型输出的 JSON,逐个字段和注册表里的 schema 做对比,遇到缺失字段或多余字段就返回一个错误描述并把“上次失败原因”附加到下一次 prompt 里,让模型自己修正。加了这层防护后,参数错误率从 16% 降到 4%,是投入产出比最高的一个修复。

第三种现象:工具“看见了却不去用”。常见于用户用口语化表达提出需求,比如“帮我把这个搞成文件发出去”,工具列表里明明有create_file_and_send,但模型没识别出来,反而说“我无法完成发送操作”。这是描述和用户语言之间存在语义鸿沟。解决办法是在工具描述里补充一个“触发场景”字段,用两三个用户原话级别的例子来标注,比如“当用户说把内容做成文件/发出去/存档时使用”。这比增加工具数量更有效——工具不是不够用,是描述不够贴近真实口语。

5.2 几条测出来的经验

最后分享几条我在实战中验证过的通用经验,谈不上系统方法论,但每一条都是拿失败换来的。

第一,工具的“分层”比“分类”更重要。分类只是把工具打上标签,分层则是把决策树搭好,让模型永远面对一个小候选集。我最后给所有工具都分配了层级号:L1 是路由 Agent,L2 是分组子代理,L3 是工具本体。任何一次工具调用都只能发生在 L3,而 L3 的候选集永远不超过 15 个。这套层级设计不挑框架,LangGraph、Semantic Kernel、自研 loop 都能套用。

第二,失败信息要还给模型,不要吞掉。很多 Agent 项目在工具执行异常后只返回“Error”二字,等于让模型在黑暗中摸索。我在异常处理器里统一做结构化输出,包含错误类型、参数校验明细、本地排查建议。模型看到“参数target_format只允许 png/jpg,你传的是 webp”后,常常能自己纠正并重新调用成功。错误信息越具体,模型的自愈能力越强,这在工具多的场景下尤其重要。

第三,定期清理和合并工具应该是常态。工具数量增长是自然趋势,但每加一个新工具,都要回头看看有没有旧的可以合并、下线。我给自己定了条规矩:工具总数超过 40 就必须做一轮“减重”,优先合并 action 维度的工具。把time相关的所有工具合并成一个datetime_tool,把文件操作合并成一个file_ops,这比让 Agent 去 60 个名字里找一个更现实。

第四,不要迷信“更聪明的模型会自动处理更多工具”。即使换成更强的大模型,60 个工具全量注入后的准确率可能高一点,但 token 成本和响应延迟依然是硬伤。工具架构的优化在任何模型上都成立,正确率提升和成本下降是同时发生的,值得投入时间做这件事。

回头再看“Agent 给到六十个工具开始挑花眼”这个问题,我现在的感受是:60 个工具本身不是坏事,坏的是把它们一股脑塞进同一个模型上下文里。工具系统设计得像人的工具箱,常用的伸手就够到,不常用的锁在抽屉里,有标签、有隔层、有使用说明。别奢求模型自己在 60 个工具里选出最佳答案,把问题拆成路由、检索、校验三个环节,每一层只做一点点简单的判断,整体效果就会稳定很多。如果你也正在被工具膨胀折磨,先从给工具分组开始,这可能是整个优化里最简单也最有效的一步。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 15:51:41

AI编码助手实战:融合代码问答与任务执行的Agent设计

做 AI 編碼助手,最常見的誤區是把它做成一個「會說話的搜索引擎」。用戶問「這個報錯什麼意思」,它答得頭頭是道;用戶問「那你幫我改一下、跑一下、把任務排上」,它就啞火了。羲和(XiheAgent)這個項目的出發…

作者头像 李华
网站建设 2026/9/28 15:50:51

从零构建Servlet+JDBC点餐系统:MVC分层、事务与连接池实战

简介:这份压缩包是一套基于MVC架构的JavaWeb点餐系统完整项目,适合用作毕业设计、课程设计或Servlet与JDBC入门实战练习。项目从前台点餐到后台管理,覆盖用户注册登录、菜品分类展示、购物车与订单提交、订单管理等功能模块,通过M…

作者头像 李华
网站建设 2026/9/28 15:50:44

Jev模型源码解析:不生成文字的轻量级单token预测器

前两天我在 Hacker News 上刷到一个节奏感很强的项目:发布 3 天,直接登顶首页第一,标题写着“不生成一个字的模型”。我本来以为又是那种噱头拉满的 AI 玩具,点进 GitHub 之后反而越看越上头。Jev 这个项目和我想象的不太一样&…

作者头像 李华
网站建设 2026/9/28 15:50:33

容器冷启动优化:Agent服务快照恢复实战,从35秒到1秒

我最近被一个 Agent 服务的冷启动坑得够呛。团队把一个大模型 Agent 框架打包进容器,加上 Python 依赖、几个本地 embedding 模型文件,镜像轻松超过 1.5GB。每次弹性扩容或发布新版本,新容器要经历拉镜像、解压、初始化框架、加载模型这一整套…

作者头像 李华
网站建设 2026/9/28 15:50:05

RK628F MIPI转HDMI黑屏排查实战:从I2C到固件到4K时序

最近在调一块RK3588方案的板卡,外接的显示输出就是一颗RK628F桥接芯片,作用是把SoC的MIPI DSI输出转成HDMI,接到4K显示器上。从拿到样板到屏幕真正点亮,中间黑屏了将近一周。这类方案在初期出现黑屏太正常了——RK628F不是你焊上去…

作者头像 李华
网站建设 2026/9/28 15:49:26

舌苔识别检测系统:基于深度学习的细粒度分类与GUI实现

简介:一套基于深度学习的舌苔识别检测鉴定系统,面向计算机相关专业正在准备毕业设计的学生,也适合需要项目实战练习的学习者,可作为毕业设计、课程设计或期末大作业。资源提供完整的Python源码、论文文档和GUI界面,覆盖…

作者头像 李华