知识点思维导图
40 个知识节点
Tool 与 Function Calling(04) - 多工具选择、路由与并行调用
读完后,你应能完成以下任务:
- 绘制“Tool 与 Function Calling(04) - 多工具选择、路由与并行调用 / 工具注册表:所有工具登记在一处”的关键对象与数据流,解释“每项包含:名字、给模型看的说明、触发条件、参数怎么抽、怎么执行。”,并用源码位置、日志或 Trace 标注证据。
- 为“Tool 与 Function Calling(04) - 多工具选择、路由与并行调用 / 意图路由:把问题分发给对的工具”设计正常与异常输入,验证“有了注册表,还需要一个「路由器」:拿到用户问题,决定该用表里哪个工具。”,输出首个偏差位置与回归测试结果。
- 实现“Tool 与 Function Calling(04) - 多工具选择、路由与并行调用 / 真实项目里,选工具是模型干的”的最小代码或配置,检验“自己判断该调哪个、传什么参数(这正是 28 篇讲的)。”,输出命令、结果与 Diff,并说明不适用边界。
一句话目标:读完你能用「注册表 + 路由」组织多个工具,让加工具不改主逻辑,并知道工具一多时模型选错怎么办。
一、与进阶篇的分工
本篇保留为多工具 Agent 基础:重点讲工具选择和编排。 进阶实践请读 55《实现 mini cursor》、57《高德 MCP + 浏览器 MCP》、75《图编排引擎》, 它们会进一步处理命令安全、外部工具复用和图状态编排。
二、多工具选择、路由与并行调用的真实应用场景
你的客服 Agent 一开始只有一个工具:查订单。 后来产品要加:算报销、建工单、查物流、发通知……工具从 1 个涨到 10 个。
如果你用 if-else 硬写——「问到订单就查订单, 问到报销就算报销, 问到工单就建工单」——这堆判断会越堆越长, 加一个工具就改一次主流程, 改着改着就乱了。 这是典型的「该用配置表的地方用了硬编码」。
多工具 Agent 的核心,就是把工具管理从「写死的分支」变成「一张可扩展的注册表」。 加工具 = 往表里加一行,主逻辑一个字不用动。 前端写路由表、写菜单配置,是同一个思路。
三、工具注册表:所有工具登记在一处
把每个工具的「身份信息」集中登记成一张表, 每项包含:名字、给模型看的说明、触发条件、参数怎么抽、怎么执行。
这张表是整个系统的「单一事实来源」。 加一个查物流的工具,就往表里加一项,不碰任何分发逻辑。 这就是开闭原则——对扩展开放,对修改关闭。
四、意图路由:把问题分发给对的工具
有了注册表,还需要一个「路由器」:拿到用户问题,决定该用表里哪个工具。
选中工具后,用它自带的 extract 抽参数,再用 run 执行。
整个流程是「路由 → 抽参数 → 执行」,对所有工具一视同仁,不为某个工具写特例。
这里有个必须有的分支:route 返回 None——没有任何工具匹配。
这时不能硬塞一个工具去调,而要兜底:转人工,或直接回复「这个我处理不了」。
没有这个兜底,Agent 遇到能力范围外的问题就会乱调工具。
五、真实项目里,选工具是模型干的
demo 里 route 用关键词匹配模拟路由。
真实项目里这一步交给模型的 Function Calling:把注册表里每个工具的 name 和 desc 作为 schema 发给模型,
模型读说明,
自己判断该调哪个、传什么参数(这正是 28 篇讲的)。
但骨架完全一样:注册表 + 路由 + 参数抽取 + 兜底。
无论路由是关键词还是模型,加工具都只动注册表。
模型选工具时,
desc 写得清不清楚直接决定它选得准不准——这也是为什么 28 篇强调 description 是模型决策的依据,
不是注释。
六、工具再多一层,就是多 Agent 路由
当工具数量继续增长,光靠一个 Agent 直接选工具会变难。 企业项目常见做法是分层:
- 主 Agent 先判断大类:知识库、订单、账单、技术支持、报告生成。
- 子 Agent 只拿自己领域内的工具和上下文。
- 工具执行结果回到主 Agent,由它统一汇总。
这样拆不是为了“分角色聊天”,而是为了减少上下文、降低工具混淆、隔离权限。 MCP 这类共享服务可以把检索、记忆、Prompt 模板、外部工具统一暴露给不同 Agent; 进阶方案可继续阅读“工程基础”模块的《进阶:多 Agent 与 MCP 工程化》。
七、工程上真正会踩的坑(本篇独有)
- 用 if-else 硬编码工具分发。加一个工具改一次主流程,分支越堆越乱。用注册表,加工具只动数据不动逻辑。
- 没有「无匹配」兜底。问题落在所有工具能力之外时,如果不兜底,Agent 会硬挑一个工具乱调。
route返回 None 的分支必须处理。 - 工具太多,模型选不准(工具混淆)。工具一多,几个功能相近的工具模型容易选错。两个办法:一是把
desc写得更有区分度,明确「什么时候用我、什么时候别用我」;二是工具特别多时分组或分层路由,先粗分大类再选具体工具,别一次把几十个工具全丢给模型。 - 工具的参数抽取和 schema 不一致。注册表里说要
customer_id,抽参数时却抽成customerId,执行时对不上。schema、抽取、执行三处的字段名必须严格统一。
八、一句话面试答法
多工具 Agent 怎么组织? 核心是「注册表 + 路由」:所有工具登记在一张表里,每项含名字、给模型看的说明、参数抽取和执行函数;路由根据用户意图选出该用哪个工具。加工具只往注册表加一行,主逻辑不动。真实项目里选工具交给模型的 Function Calling,模型靠工具的 description 判断,所以描述要写得有区分度。还要有「无匹配工具」的兜底,以及防止工具太多导致模型选错——办法是写清描述或分层路由。
九、动手实践:30 多工具 Agent
用工具注册表 + 意图路由,把不同意图的问题分发给不同工具。 加工具只需往注册表加一项,不改分发主逻辑。
9.1 在线运行
零依赖,纯标准库。
9.2 预期输出
问:帮我查下客户 C1001 的订单
路由:命中【lookup_orders】(查询客户订单),参数 {'customer_id': 'C1001'}
结果:客户 C1001 的订单:2 笔订单,其中 1 笔退款中
问:打车 38 餐补 50 能报销多少
路由:命中【calc_reimbursement】(计算报销总额),参数 {'amounts': [38.0, 50.0]}
结果:共 2 笔费用,可报销合计 88.0 元
问:系统登录失败,建个工单
路由:命中【create_ticket】(创建工单),参数 {'title': '系统登录失败,建个工单'}
结果:已创建工单 T-1001:系统登录失败,建个工单
问:今天天气怎么样
路由:无匹配工具 -> 转人工或直接回复无法处理
四个不同意图的问题分别路由到三个工具,最后一个没有匹配工具时走兜底,不硬调。
9.3 代码↔概念对应
| 概念 | 在 main.py 哪里 |
|---|---|
| 工具注册表 | TOOL_REGISTRY |
| 每个工具:名字/说明/关键词/参数抽取/执行 | TOOL_REGISTRY 里每一项 |
| 意图路由(选哪个工具) | route |
| 参数抽取 | 每个工具的 extract |
| 工具执行 | 每个工具的 run |
| 无匹配兜底 | run 里 if tool is None |
9.4 说明
这个 demo 的路由用关键词匹配模拟。
真实项目里这一步交给模型的 Function Calling:把注册表里每个工具的 name 和 desc 作为 schema 发给模型,
模型读说明自己决定调哪个、传什么参数(见 Tool 与 Function Calling(01)《Function Calling 与 Tool Calling》)。
但「注册表 + 路由 + 参数抽取 + 兜底」这个骨架不变。
加一个新工具时,
你只往 TOOL_REGISTRY 加一项,
route 和 run 一行都不用动——这就是注册表设计的价值。
9.5 可运行源码:多工具 Agent
main.py
十、总结
- 工具注册表:所有工具登记在一处:每项包含:名字、给模型看的说明、触发条件、参数怎么抽、怎么执行。
- 意图路由:把问题分发给对的工具:有了注册表,还需要一个「路由器」:拿到用户问题,决定该用表里哪个工具。
- 真实项目里,选工具是模型干的:自己判断该调哪个、传什么参数(这正是 28 篇讲的)。
- 工具再多一层,就是多 Agent 路由:主 Agent 先判断大类:知识库、订单、账单、技术支持、报告生成。 -> 子 Agent 只拿自己领域内的工具和上下文。 -> 工具执行结果回到主 Agent,由它统一汇总。
- 工程上真正会踩的坑(本篇独有):route 返回 None 的分支必须处理。
- 一句话面试答法:核心是「注册表 + 路由」:所有工具登记在一张表里,每项含名字、给模型看的说明、参数抽取和执行函数;
学完自测
选择所有正确答案;提交后逐项核对判断依据。