
说明本文讨论的是 Agent 工具集的划分方式属于工程架构话题不涉及具体模型版本与价格。AI 领域版本迭代极快凡涉及版本号、价格、可用性请以你阅读时的官方页面为准。文中代码为结构示意请按自己的技术栈调整后再上生产。一、工具数涨上来之后先崩的是选择准确率第一个版本的 Agent 通常只有三五个工具那段时间的体验是最好的模型几乎每次都选对参数也基本不填错。加了半年功能工具数爬到三十个上下用户的反馈变成了感觉变笨了——但模型没换提示词也没大改。变化发生在工具集本身。模型每次决策时要在同一个请求里、同一层上下文中从 N 个候选里挑出一个。候选变多的同时候选之间的描述也变长了而这两件事是同时消耗注意力的。延迟和 token 消耗的上涨是肉眼可见的真正的损失却在另一处选择准确率掉了不报错只是答错。把变笨这个体感拆开落到工程上其实只有三种症状处置方向完全不同症状表面现象常见根因先查哪里选错工具该查订单却去读了用户资料两个工具的名字与描述语义重叠混淆矩阵里的成对误选该调不调明明有工具可用模型凭记忆直接作答关键工具被淹没在过长的列表里工具列表顺序与分组参数填串工具选对了参数用了另一个工具的字段名多个工具参数名相似但含义不同参数命名的一致性这三种症状里参数填串最容易误导人。它看起来像模型的参数生成能力问题实际往往是设计问题两个工具都收一个叫id的参数一个指订单号一个指用户号模型在切换工具时把上一轮的参数名顺手带了过来。修法不是加提示词是让两个参数名不再长得一样。还有一个反直觉的地方误选不是均匀分布的而是成对出现的。如果你在三十个工具上做统计会发现绝大部分错误集中在少数几对语义相邻的工具上比如按订单号查和按用户查该用户的订单。这意味着只看一个总体的选择正确率几乎没有诊断价值——它只会告诉你变差了不会告诉你哪一对在互相污染。真正该盯的是一张混淆矩阵横轴是期望工具纵轴是实际调用的工具看非对角线上哪几格在持续累积。定性地说工具集规模和选择准确率的关系大致是五到十个工具时区分度最好到二十个上下语义相邻的工具开始互相抢答三十个以上且没有分组时模型会开始出现系统性的偏向——它会反复偏爱列表中靠前、描述更长的那个工具哪怕语义并不匹配。这个偏向一旦出现靠调提示词是压不住的只能靠重新划分工具集。二、拆细的代价与合并的代价面对误选最本能的两条路是拆得更细和合并成一个。这两条路都有效但代价不一样而且代价不发生在同一层。2.1 拆细的三种代价第一是上下文占用。每个工具的描述、每个参数的说明都要进上下文。把一个更新记录拆成改状态“改名称”改标签三个工具等于把一份描述变成了三份而且这三份里有大量重复的公共部分。第二是语义重叠带来的歧义。拆出来的两个工具如果只差一个维度模型在这两者之间做选择靠的就是概率了。典型情形是按订单号查订单和按用户查订单列表——对模型来说用户说帮我看下那笔订单这两者都说得通。拆细本身不会消除歧义它只是把歧义从工具内部挪到了工具之间。第三是后端接口碎片化。一个业务动作被拆成多个工具意味着你的适配层要为每个碎片单独维护鉴权、错误分类和超时策略。三个碎片就是三份配置任何一份漏配都会变成线上一个只在特定路径上出现的怪问题。2.2 合并的三种代价第一是一个工具承担多种语义。模型拿到这个工具后要先在心里做一次路由用户这句话对应我这几个语义里的哪一个这次路由是隐式的你看不见也没法给它加判据。第二是参数变成 union。一个payload字段要承载多种形状校验只能做到很浅的一层——你能校验它是对象但很难校验当 action 是 a 时 fields 必须有哪些键。校验一浅错误就被推迟到执行阶段才暴露。第三是失败语义模糊。调用失败了你不知道是哪种意图失败。是改状态的权限不够还是改名称的目标不存在这两种情况该给用户看完全不同的东西但合并之后它们长一个样。2.3 取舍怎么落地把两边的代价摆在一起判断标准其实就三条边界能否用一句话说清、失败能否分类、参数能否单义。判断维度宁可拆细宁可合并理由两个动作的失败处置是否相同不同 → 拆相同 → 合失败语义不同时合并后无法分流参数集合是否有交集无交集 → 拆高度重叠 → 合无交集的参数硬塞进一个工具必然变 union权限边界是否一致不一致 → 拆一致 → 合权限差异不适合放在同一个工具里调用频率高频单独 → 拆都低频 → 合高频工具单独暴露减少无关上下文是否有二次判断无 → 拆有 → 合合并会强迫模型在参数里做一次隐式路由这张表用起来有个顺序先看失败处置和权限边界这两条是硬约束再看参数交集和调用频率这两条是优化项。硬约束冲突时必须拆哪怕承受上下文变长的代价优化项冲突时优先合并因为少一个工具就少一份歧义。三、按动作还是按资源拆合的方向定下来之后下一个问题更具体按什么维度切。3.1 两种划分法的差别按资源划分第一层是名词用户、订单、工单每个资源下面挂一组读写操作。这种切法映射清晰和后端的数据模型一一对应实现成本最低。按动作划分第一层是动词而且是业务动词不是 CRUD 动词。“推进订单到下一状态”关闭这个工单是动作更新订单字段不是它是资源视角的抽象。两者的差别在模型侧体验很明显资源划分下模型要先想这事涉及哪个资源再想对资源做什么动作划分下模型只需要判断用户想干的是哪件事。任务导向的对话里后者更接近模型的推理路径。3.2 各自适用资源划分适合面向内部系统的通用助手——调用者本身就熟悉数据模型问题是开放式的探索比如找出所有符合条件的记录。这时候资源边界即语义边界不容易歧义。动作划分适合面向具体业务的流程助手——任务是有终点的比如把这个工单结掉。这时候语义边界是业务动作不是数据表。两种都不适合的情形是混合第一层一半工具按资源命名一半按动作命名。这种清单对模型最不友好因为它在两种切法之间没有稳定的映射只能靠逐条读描述来判断而这恰恰是它最不擅长的。3.3 一个不需要技术的判定方法切分是否合理有个不依赖任何工具的检验法把工具清单拿去给一个完全不了解你业务的人看让他用一句话说清任意两个工具的边界。说不清的那一对就是有问题的切分。这个检验之所以有效是因为不了解业务的人只能靠名字和描述判断边界而模型判断工具的依据本质上也是名字和描述。你团队里那个人之所以觉得边界清楚是因为他脑子里装着没写进清单的上下文——这些上下文模型拿不到。下面这份清单就是典型的反面例子工具清单按后端接口逐个暴露未整理 - get_user_info # 传 user_id返回用户资料 - fetch_user_profile # 传 user_id返回用户资料 - query_member # 传 member_id返回用户资料 - update_user_status # 改用户状态 - set_member_state # 改用户状态 - close_ticket # 关闭工单前三个工具对模型来说几乎是同一个东西区别只写在文档里、没写在名字里第四个和第五个同理。让不了解业务的人说出get_user_info 和 fetch_user_profile 有什么不同他答不上来——模型也答不上来。四、分组、命名与按阶段暴露切分维度确定后还有一层可以做的事不改变工具本身改变工具被呈现的方式。4.1 命名空间前缀给每个工具加一个固定的前缀把资源域或动作域编码进名字里。order_lookup、order_update、ticket_close、ticket_escalate这样的命名模型在语义层面就能把工具归堆。前缀的价值在于它把隐性分组变成显性token。即使模型不会真的按前缀做分组统计前缀也会在注意力层面形成一个可复用的模式让语义相邻的工具更容易被一起理解、更容易被区分开。需要注意前缀必须一致且互斥要么统一用资源_动作要么统一用动作_资源不要混。混用会让前缀失去信息量——一旦order_lookup和lookup_order同时存在前缀就不再是稳定的信号了。4.2 按阶段暴露比命名更强的手段是按任务阶段动态收窄工具集。一次任务通常是有阶段的先侦查和检索再生成草稿最后写入或提交。这三个阶段需要的工具几乎不重叠把它们全程一起暴露等于让模型在每个阶段都要排除掉三分之二的无关选项。做法是把工具按阶段分组只在进入某个阶段时注入那一组阶段典型工具暴露策略目的侦察 / 检索搜索、查询、读取任务开始即暴露提供信息面压缩其他组生成 / 组装模板、草稿、校验检索出结果后暴露让模型专注组合而非挑工具写入 / 提交更新、创建、关闭草稿确认后暴露缩小副作用类工具的可见窗口按阶段暴露还有一个额外好处它天然降低了该调不调的概率。当写入组只在最后一步出现时模型不会在早期阶段误触发一个不可逆的操作。4.3 动态工具集怎么和上下文配合这里有个容易忽略的取舍收窄工具集能省上下文但切换本身也有成本。每次切换阶段工具列表变了模型需要重新建立我能做什么的心智模型。如果阶段切得太碎切换成本可能超过收窄带来的收益。一个可参考的密度是单次注入的工具数控制在十个以内阶段数控制在三到四个。十个以内是让模型在候选之间做选择时保持区分度三到四个是因为再多阶段之间的判据就会开始重叠出现这个阶段到底算侦察还是算生成的争议。# 工具分组声明按任务阶段组织按需注入上下文# 注意 expose_when 描述的是进入条件不是排斥条件TOOL_GROUPS{recon:{# 阶段一只读侦察enter:task_start,tools:[kb_search,order_lookup,user_profile_read],},draft:{# 阶段二生成草稿enter:after:recon,tools:[draft_reply,render_template,check_policy],},apply:{# 阶段三写入提交enter:after:draft,tools:[order_update,ticket_close],},}deftools_for(stage:str)-list:返回某个阶段应当注入的工具名。stage 由编排层在状态机里推进。names[]forspecinTOOL_GROUPS.values():rulespec[enter]ifruletask_startorrulefafter:{stage}:names.extend(spec[tools])returnnames这段示意里最关键的约定是阶段由编排层推进不由模型自己声明。让模型说我现在进入写入阶段了等于让一个概率组件去控制副作用窗口的开关那是把风险放在了最难验证的地方。五、参数收敛把隐式约定提到显式参数工具边界切对了仍然会有参数层面的混淆。这一层的问题最隐蔽工具被正确选中但参数是错的而错误的表现是一个看起来正常的失败。5.1 万能参数的问题最常见的形态是万能参数——一个工具收一个宽泛的字典参数把多种意图塞进去# 收敛前一个万能参数语义靠字符串约定defupdate_record(record_id:str,payload:dict): payload 的形状由 action 决定 {action: status, value: done} {action: rename, to: 新名称} {action: tag, tags: [vip]} 模型必须先在这个字典里做一次隐式路由再选字段名。 ...# 收敛后动作成为工具边界取值集合固定参数名各自单义defset_record_status(record_id:str,status:str):# status 只允许有限取值超出直接判为参数错误ifstatusnotin{pending,doing,done}:raiseValueError(funsupported status:{status})...defrename_record(record_id:str,new_name:str):...deftag_record(record_id:str,tags:list):...收敛前后模型的责任变了收敛前它要在一个字典里做二次判断这个判断没有任何外部校验收敛后它只需要在三个工具里选一个选错会被工具选择的评测集直接抓到。5.2 三个收敛动作第一把隐式上下文提到显式参数。很多工具依赖当前用户“当前会话”当前租户这类从上下文里推断的东西。让它保持隐式模型既看不见也控制不了它出错时也无法归因。把它变成显式参数虽然看起来冗余但换来的是可观测。第二把取值受限的字符串显式化。状态、类型、级别这类字段取值集合是有限的。把它写进参数约束里模型收到一个不合法的值时会立刻被拒绝而不是带着错误值走到下游业务逻辑里。第三把高频组合固定下来。如果某三个参数在八成调用里都是一起出现的固定组合可以考虑把它提升为一个独立工具。这不是为了好看是为了减少模型每次都要重复生成的那部分内容。5.3 收敛的边界收敛不是越多越好。判断该不该继续拆看一件事拆出来的新工具是否有独立的失败语义。如果两个工具永远同时成功、同时失败它们的拆分就没有信息增益只是把上下文撑长了。另一个停止信号是参数数量。一个工具的显式参数超过六到七个之后模型填错的概率会明显上升——它开始漏填、或者把相似名称的参数互换。这时候的正确动作不是继续加参数而是回到工具边界那一层重新切。完整版资料清单本文用到的工具清单模板与选择评测用例都整理在里面了扫码即可获取六、工具的版本演进工具不是一次设计完就固定的。业务在变工具就要变而工具一旦变正在跑的任务就可能被影响。6.1 新增字段怎么加新增可选参数的代价最低只要新参数有合理的默认值老的任务路径不受影响。真正危险的是新增必填参数——它会让所有既有的调用方式立刻失效包括那些已经写进评测集的用例。所以判断标准是这个参数能不能给一个对所有历史场景都成立的默认值。能就加可选不能就说明它其实是另一个工具而不是同一个工具的新参数。6.2 废弃参数怎么退参数不能直接删。删除会让调用直接报错而报错发生在生产流量上。稳妥的做法是三步走先标记废弃并保留行为同时返回一个提示观察一段时间确认没有调用方再用最后才真正移除。6.3 同名工具语义变更怎么标最需要警惕的情况是工具名不变、语义变了。比如一个更新订单的工具从允许任意状态跳转改成只允许相邻状态迁移。这两者的名字一模一样参数也一模一样唯一变的是内部规则。任何依赖旧语义的调用方都不会收到任何提示直到某个任务在运行时失败。处理这种变更只有一条可靠路径换版本号并且让旧版本显式不可用。让旧版本继续默默工作、行为却变了是最坏的情况——它把所有风险推迟到了最不可控的时刻。变更类型是否影响既有调用推荐做法观测重点新增可选参数不影响直接加给默认值默认值命中率新增必填参数全部影响优先拆成新工具是否有历史场景无默认值废弃参数部分影响标记 → 观察 → 移除废弃参数的实际调用量同名语义变更隐性影响升版本旧版本拒绝旧版本的调用来源工具的元数据应该把这些状态一并记录下来让这个工具现在处于什么版本、有哪些参数在退场成为可查询的事实而不是散在某个人的记忆里⚠️ 代码待验证tool:order_updateversion:2added_params:-name:confirm_modetype:stringvalues:[auto,manual]default:autodeprecated_params:-name:forcesince:v2replacement:confirm_moderemove_after:v3semantic_change:-since:v2note:v1 允许任意状态跳转v2 起只允许相邻状态迁移七、怎么验证粒度是否合适前面六章都是设计判断判断对不对需要验证。验证的手段不是跑几个例子看看而是一个专门的工具选择评测集。7.1 评测集怎么构成最实用的构成是每个工具五条正例加三条易混负例。五条正例用来确认这个工具能被选中三条负例用来确认它不会被抢答——负例要专门挑那些看起来该用这个工具、实际该用另一个的输入。易混负例是这个评测集里最值钱的部分。正例通常一遍就过真正暴露设计问题的是负例如果某个工具的负例持续被它自己抢走说明它和相邻工具的边界还有重叠。用例类型数量构造方法失败说明什么正例每工具 5 条换句式、换指代、换省略工具的语义覆盖有缺口易混负例每工具 3 条从相邻工具的正例改写而来两个工具的边界有重叠无工具用例全局 5–10 条用不需要工具的问题模型滥用工具或过度保守第三类无工具用例容易被忽略。它测的是另一个方向的错误模型是不是在没必要的时候也去调工具。这类误调同样消耗延迟和配额但不会被任何工具级别的统计捕捉到。评测用例文件的结构建议把期望工具和易混对象都写进去这样一次运行就能直接产出混淆对⚠️ 代码待验证suite:tool_selection_v1cases:-id:lookup_positive_01query:上个月那笔订单发货了没有expect_tool:order_lookupkind:positive-id:lookup_hardneg_01query:客户资料里的备注提到过发货时间吗expect_tool:user_profile_readkind:hard_negativeconfusable_with:[order_lookup]-id:no_tool_01query:你们一般几天发货expect_tool:nullkind:no_tool7.2 线上误选率怎么观测评测集覆盖的是你想到的场景线上覆盖的是你没想到的。两者要分开看。线上不需要专门埋点但需要在工具调用记录里保留两个字段这一次调用的期望语义和实际调用的工具。前者通常拿不到所以退而求其次的做法是记录调用之后的结果状态——一次调用如果紧接着被同一个任务用另一个工具重试、或者调用后任务直接失败那就是误选的强信号。把评测结果和线上信号放在一起看能得到一张可追踪的清单⚠️ 代码待验证{run:2026-09-30T10:00:00Z,suite:tool_selection_v1,total:64,wrong_tool:5,no_call:2,arg_error:1,confusion_pairs:[{expected:order_lookup,called:user_profile_read,count:3},{expected:user_profile_read,called:order_lookup,count:2}]}7.3 什么时候该调整粒度有了这两组数据调整的触发条件就可以写清楚而不是拍脑袋。三个可用的判据第一某个工具的正例长期被别的工具抢走。这说明它的语义没有被自己的描述和名字承载住先改名字和分组无效再拆。第二某对工具的混淆数占了全部误选的多数。说明这两个工具的边界不清该做的是合并或重新切分而不是继续给它们各加描述。第三无工具用例误调率偏高。说明工具集暴露得太宽该收窄注入范围而不是改任何单个工具。这三条判据的价值在于它们指向了不同的修法第一条修名字第二条修边界第三条修暴露策略。如果只有一个总体的正确率数字你只能知道该修不知道该修哪一层。完整版资料清单本文用到的工具清单模板与选择评测用例都整理在里面了扫码即可获取附表 A关键取舍一览本文涉及的所有工程判断集中在这里方便按需回看。取舍本文结论判断依据位置工具变多后先看什么选择准确率不是延迟选择错误不报错延迟是可测的第一章误选怎么统计看混淆矩阵不看总体正确率误选成对集中总体值无诊断力第一章拆细的适用条件失败语义或权限边界不同这两条是硬约束第二章合并的适用条件参数高度重叠且都低频减少候选数就减少歧义第二章按什么维度切内部助手按资源流程助手按动作贴合各自的推理路径第三章切分是否合理的检验让不了解业务的人一句话说清边界模型也只有名字和描述可用第三章命名前缀必须一致且互斥混用会让前缀失去信号价值第四章什么时候按阶段暴露任务有明确阶段且工具不重叠收窄候选比调提示词更直接第四章单次注入工具数十个以内阶段三到四个超过后区分度下降、切分争议上升第四章阶段由谁推进编排层不由模型声明副作用窗口不该交给概率组件第四章万能参数怎么处理拆成显式工具取值受限消除工具内的隐式二次判断第五章隐式上下文怎么处理提到显式参数不可见就无法归因第五章参数拆到哪停新工具须有独立失败语义无信息增益的拆分只撑长上下文第五章新增必填参数优先拆成新工具必填会让历史场景全部失效第六章同名语义变更升版本旧版本拒绝静默变行为风险最大第六章评测集怎么配比每工具 5 正例 3 易混负例负例才能暴露边界重叠第七章无工具用例全局 5–10 条测滥用与过度保守第七章该改名字还是改边界按误选形态分流三种判据对应三种修法第七章附表 B术语速查表术语含义工具粒度一个工具承担的业务语义范围越细语义越窄选择准确率模型在候选工具中选中正确工具的比例混淆对两个语义相邻、互相抢答的工具组合混淆矩阵期望工具与实际调用工具的交叉计数表万能参数用单个宽泛参数承载多种意图的写法参数收敛把隐式约定与宽泛取值改成显式、受限的参数命名空间前缀工具名中表示资源域或动作域的统一前缀按阶段暴露只在任务特定阶段注入对应工具组动态工具集随任务状态变化的工具清单非固定全集易混负例看似该用某工具、实际应用另一工具的评测用例无工具用例本不需要调用工具的问题用于测滥用语义变更工具名与参数不变但内部行为改变误选率线上观察到错误工具被选中的比例写在最后这篇用到的资料写这篇文章时我把几个 Agent 项目里的工具清单和选择误例都对了一遍顺手整理成几份配套的东西大模型学习路线图从 LLM 基础到 Agent 开发各阶段该学什么、用什么资料大模型全套教程按主题分好的视频与文档清单大模型实战好书24 本附每本适合的阶段资料是我自己整理的放在下面这个码上扫码即可获取添加时备注「大模型」优先通过。拿到之后建议先看学习路线图那一份先定位自己在哪个阶段再决定学什么比一上来就啃框架效率高得多。