60天企业 AI 实战训练营 · 第 7 周 · Agent 工程

多 Tool 路由
串并行调用

一个工具查一件事,一条链回答一个业务问题 —— 让 Agent 学会组合多个数据源

Day 33 讲师 · 包学斌 2026 · 09 含实操 · 请自带电脑
user 这单能退吗? router 规则→分类→LLM 选一条链 order payment policy merge 路由决定"走哪条链" 依赖决定"串还是并" 合并决定"答案可不可信"

按 → 或 空格键 翻页 · F 全屏

今天这堂课解决什么问题

课程目标

01
选对工具,也选对"链"
从"选一个工具"升级到"选一条工具链":规则兜底、意图分类粗筛、LLM 决策收口。
02
分清串行与并行
有依赖必须串行并短路,无依赖才能并行;延迟不是"加起来",而是"最长那条"。
03
合并多源结果,保留证据
字段对齐、去重、排序、标注来源与口径;冲突不掩盖,由权威源裁决。
它在 60 天课程中的位置
Day 32 · Function Calling 与 Tool 设计已完成
Day 33 · 多 Tool 路由、串并行调用今天
Day 34 · Memory、Session 与上下文管理下一课
Day 35 · Agent Planning 与 Human-in-the-loop本周
Day 36 · 身份、权限、RBAC 与数据安全下周
昨天学会造零件,今天学会组装:一个业务问题往往要三四个数据源才能回答。
互动 · 举手投票 + 点击选择

用户问"这单能退吗",Agent 的第一步应该是什么?

凭直觉点一个 —— 这决定了你的编排结构

点一个选项,看看它意味着什么 →
01
Part 01

先选对路
再谈跑得快

路由:规则兜底、分类粗筛、LLM 收口 —— 三层配合最稳

三层路由 意图分类 路由日志
Part 01 · 全景

一个业务问题,往往要四步才能回答

点一次走一步 —— 注意:路由发生在最前面,不是边做边想

① 理解
问题 + 用户 + 上下文
→
② 路由
选工具链
→
③ 执行
串行 / 并行
→
④ 合并
证据 + 结论
路由层
决定"该查哪些数据源、按什么顺序"。错了,后面再快也白搭。
执行层
串行保证依赖,并行压缩延迟;失败要短路、要能降级。
合并层
字段对齐、冲突裁决、保留来源;答案才可解释、可回查。
路由不是"选一个工具",是选一条有依赖关系的执行计划。
Part 01 · 机制

模型凭什么选这个工具?

三个依据
① Description 的语义匹配
问题里的"退"和工具描述里的"退款/退货/取消"能不能对上。
② 参数的适配度
用户给了订单号但没有金额,写工具就缺必填参数,不该选。
③ 上下文与用户身份
当前页面是订单详情?用户是客服还是财务?决定工具子集。
三层路由,从上到下
① 规则命中
关键词 / 正则 / 页面上下文;0ms、确定、可解释。覆盖 60–70% 常见意图。
最快
② 意图分类
小模型或向量相似度,把问题分到"查订单/退款/对账"等域,再给该域的工具集。
较省
③ LLM 决策
前两层没命中,或需要多工具组合时,才把候选工具交给大模型选。
最灵活
只用规则的代价:维护成本高、新问法必挂;只用 LLM 的代价:慢、贵、不可预测。组合才稳。
Part 01 · 规模

一次给模型 100 个工具,它一定选不准

10–20
单次决策的工具上限
超过 20 个,先做粗筛:按意图/域/权限把候选缩到 5–8 个,再交给 LLM。
工具语义重叠 = 模型犹豫
路由日志至少记三件事:命中哪一层、候选有哪些、最终选了什么。
症状 1:选择准确率随工具数下降
10 个以内通常稳定;到 40 个以上,"该选 A 却选 B"显著上升。不是模型笨,是候选空间的区分度不够。
症状 2:同义工具互相抢
get_order / query_order / fetch_order —— 三个都在,模型只能随机。
解法:先粗筛,再精选
意图分类 → 候选工具 5–8 个 → LLM 决策;工具注册中心按角色/租户下发子集,顺带做权限隔离。
route: {layer: "rule", intent: "refund", tools: ["check_refund_policy", "get_order_status", "get_payment_record"], confidence: 0.92}
互动 · 快问快答

三句话,测你有没有真懂路由

1. 工具越多,Agent 能力越强?
候选越多,区分度越低。10–20 个是甜点区,超过就粗筛。
不是
2. 能并行就一定比串行快?
有依赖的并行会读到旧数据;并行度太高还会打挂下游。快的前提是"无依赖 + 有上限"。
不一定
3. 路由这件事,交给 LLM 就够了?
LLM 慢、贵、不可预测;常见意图用规则,模糊意图用分类,复杂组合才交给 LLM。
不够
路由的目标不是"最聪明",是在可接受的成本下,稳定选对。
实操 ① · 8 分钟 + 讲评 3 分钟

给六个工具写一张路由表

1
打开 demo/router_lab.py。六个工具已注册:订单、支付、库存、政策、退款、发票。
2
补全 RULES:为每个意图写 2–3 个关键词或正则,覆盖最常见的问法。
3
跑 15 条问句,看规则层能命中多少;未命中的会落到"意图分类"模拟器,再不行才交给 LLM。
4
看输出的路由日志:每条记录命中层、候选工具、置信度。找出至少一条"规则误伤"。
08:00
验收三条
□ 规则层命中 ≥ 10/15
□ 未命中能落到下一层,不是直接报错
□ 日志能回答"为什么选它"
讨论:"帮我把上周那个单退了"——规则该命中还是该交给 LLM?为什么?
02
Part 02

该串就串
该并就并

依赖关系决定拓扑,拓扑决定延迟与正确性

串行 + 短路 并行 fan-out DAG
Part 02 · 串行

有依赖,就必须串行 —— 而且要会"短路"

get_order
拿到 order_id / 金额
→
get_payment
用 order_id 查支付
→
check_policy
用支付时间判定期
→
create_refund
写操作 + 审批
上一步的输出是下一步的输入
没有 order_id 就查不了支付;没有支付时间就算不出"是否在 7 天无理由期内"。
任一步失败,立即短路
订单不存在 → 直接返回,不要继续查支付、查政策。无意义调用既慢又增加故障面。
错误要带"停在哪一步"
回填给模型的是"订单存在,但支付记录缺失",而不是笼统的"查询失败"。
给整条链设总超时
每一步 2 秒,四步串行可能 8 秒;用户等不了。总预算 + 单步超时,两层都要。
Part 02 · 并行

无依赖,就扇出去 —— 延迟是"最长那条"

三个互不依赖的查询
订单状态、支付记录、库存占用 —— 都只需要 order_id,谁也不等谁。
扇出 → 汇合(fan-out / fan-in)
同时发起,全部完成后汇合;任何一个失败,按策略决定"整体失败"还是"部分降级"。
并行度要有上限
10 个并发打同一个 WMS,可能直接触发限流;用信号量把并发压到 3–5。
写操作不要盲目并行
同一订单的"退款"和"取消"并行,结果取决于谁先落库 —— 这是数据错乱,不是提速。
# 串行:300 + 400 + 200 = 900ms r1 = get_order(oid) r2 = get_payment(oid) r3 = get_inventory(oid) # 并行:max(300,400,200) ≈ 400ms r1, r2, r3 = await asyncio.gather( get_order(oid), get_payment(oid), get_inventory(oid), )
省下来的 500ms 是什么?
是用户不关页面、客服不重复问、Agent 不超时的概率。延迟是体验,也是成本。
Part 02 · 判定

串还是并?看这四行就够了

互不依赖,都是只读
订单状态、支付记录、库存、政策 —— 同时查,延迟取最大值。
并行
后一步需要前一步的输出
先拿 order_id 才能查支付;先有支付时间才能判退款期限。
串行
混合依赖:部分可并、部分要串
先并行拉三份证据,再串行做规则判断;用 DAG 表达,不要手写嵌套回调。
DAG
写操作 / 有副作用的动作
串行 + 审批 + 幂等;同一业务对象的写操作尤其不能并行。
串行 + 审批
反模式 1
把有依赖的查询并行,读到旧状态。
反模式 2
串行链无短路,错到最后才发现。
反模式 3
并行度不设上限,打挂下游。
实操 ② · 10 分钟 + 讲评 4 分钟

把串行链改成 DAG,量一次延迟

1
打开 demo/dag_lab.py。先跑 sequential(),记下总耗时。
2
补全 parallel_fetch():用 asyncio.gather 同时拉订单、支付、库存。
3
补全 check_refund():它依赖前三份结果,必须放在 gather 之后。
4
跑两次,对比总耗时;再打开"注入延迟"开关,模拟支付接口慢 2 秒,看总延迟由谁决定。
10:00
验收三条
□ 并行版本明显快于串行
□ check_refund 在 gather 之后执行
□ 慢接口不会拖垮其他分支的结果
思考:如果支付接口必须 2 秒,你还能做什么让用户"感觉快"?
03
Part 03

合并不是
字符串拼接

对齐、去重、排序、留来源 —— 冲突不掩盖,由权威源裁决

统一结构 来源标注 冲突裁决
Part 03 · 合并

四步,把四份结果变成一份证据

① 对齐
字段映射
order_no / orderId / 订单号 统一成 order_id;金额统一币种与精度。
② 去重
同一实体只留一份
按主键去重;重复的物流节点保留最新时间戳。
③ 排序 / 评分
按业务优先级
权威源优先、时间新的优先、置信度高的优先;规则独立成函数并测试。
④ 留来源
每个字段可回查
记录 source、as_of、trace_id;回答里能点回原始系统。
{ "order_id": "A102", "status": "SHIPPED", "status_source": "ERP", // 权威源 "status_conflict": "WMS=待揽收", // 不掩盖冲突 "as_of": "2026-09-20T10:00:00+08:00", "sources": ["ERP:order", "WMS:logistics", "CRM:customer"], "trace_id": "tr_9f21" }
Part 03 · 冲突

多源数据打架,才是常态

口径冲突
"销售额"到底含不含税?
ERP 含税、BI 不含税;两个数都"对",但回答里必须写清口径,否则业务之间永远吵。
时点冲突
库存是实时还是 T+1?
WMS 实时 120 件,数仓快照 150 件;回答要带 as_of,不能把昨天的数说成"现在"。
主数据冲突
"苏州甲公司"是哪个客户?
CRM 名称、ERP 编码、发票抬头三套;先查主数据,统一到客户编码再合并。
裁决三原则
① 权威源优先(订单状态看 ERP,物流节点看 WMS);② 时间新的优先,但必须标 as_of;③ 无法裁决就并列呈现,让用户/人工判断。
最坏的做法
取平均值、取第一个、或者让 LLM"综合判断"——前两个是数据错误,后者是把裁决责任推给一个不承担后果的模型。
Part 03 · 反模式

别把三段 JSON 拼成字符串,丢给模型"总结"

✗ 拼字符串
prompt = f""" 订单:{order_json} 支付:{payment_json} 物流:{logistics_json} 请综合判断能不能退款。 """
来源丢失:答错了不知道是哪份数据错
冲突被模型"和稀泥"
token 爆炸,长字段挤掉关键信息
✓ 结构化合并 + 证据
facts = merge_sources( order, payment, logistics) verdict = policy.evaluate(facts) answer = llm.explain( facts=facts, verdict=verdict, require_citations=True)
判断由代码/规则做,模型只负责解释
每个结论带 source + as_of
合并逻辑可单测、可回归
模型擅长"解释证据",不擅长"裁决事实"。把这两件事分开。
实操 ③ · 10 分钟 + 讲评 5 分钟

合并三份数据,处理一次冲突

1
打开 demo/merge_lab.py。三份数据分别是 ERP 订单、WMS 物流、CRM 客户。
2
补全 merge():字段对齐、按主键去重、按权威源裁决状态冲突。
3
输出必须包含 status_source、status_conflict、as_of 三个字段。
4
再跑一次"口径冲突"用例:含税/不含税销售额,验证你的合并函数不会悄悄取平均。
10:00
验收四条
□ 三个来源字段齐全
□ 冲突被记录而不是被覆盖
□ 裁决结果可解释
□ 输出结构统一,下游可直接消费
关键:合并函数是纯函数,输入固定、输出固定 —— 这样才可单测。
04
Part 04

给编排
装护栏

最大步数、循环检测、重试、降级、预算 —— 让 Agent 正确地失败

max_steps 循环检测 降级 trace
Part 04 · 护栏

没有护栏的 Agent,只是更快地跑偏

1
最大步数
一次任务最多 8–12 步;超了强制总结或转人工,不无限循环。
2
循环检测
同一个工具 + 同一组参数出现两次,直接判环,回填错误而不是再调。
3
重试策略
只重试瞬时错误(超时/限流);参数错、权限拒绝永不重试。
4
降级回答
库存查不到就说明"库存数据暂不可用",不要编一个数字,也不要整单失败。
5
预算
token、时间、工具调用次数三个预算;任一超限,停止并给出已完成部分。
6
全链路 trace
每一步的 step_id、工具、参数、结果、耗时、parent_span_id —— 可回放。
目标不是"永不失败",是失败时说得清、退得回、不造成额外损失。
Part 04 · 度量

多工具任务,要这样量

指标 1
任务成功率
端到端答对,而不只是某一步调通。目标 ≥ 90%。
指标 2
平均步数
步数越多越贵越慢;异常升高通常意味着路由或循环出了问题。
指标 3
P95 延迟
看尾部,不看平均;并行改造的效果主要在这里体现。
指标 4
单任务成本
token + 工具调用 + 下游资源;优化要有账可算。
{ "trace_id": "tr_9f21", "steps": [ {"step":1, "tool":"get_order", "ms":312}, {"step":2, "tool":"get_payment", "ms":401, "pg":"g1"}, {"step":3, "tool":"get_inventory", "ms":188, "pg":"g1"}, {"step":4, "tool":"check_policy", "ms":12}, {"step":5, "status":"answered"} ], "total_ms": 913, "tokens": 2410 }
Golden Set 也要覆盖"组合"
不只看单工具选择,要看"多工具任务"是否走对链:订单+支付+政策、库存+采购+交期。
失败要分类
路由错、参数错、下游错、合并错、生成错 —— 五类分开统计,否则永远不知道该修哪。
实操建议:先加 trace,再谈优化。没有 trace 的"感觉变快了",下周就会被打回原形。
Part 04 · 复盘 · 互动讨论

三大血泪坑

1
把有依赖的查询并行
订单还没查到就并行查支付,读到空结果;或者退款和取消同时执行,最终状态看谁先落库。
2
所有错误都重试
参数错重试一百次还是错,还把下游打挂;只有超时/限流才值得退避重试。
3
没有最大步数
Agent 在"查不到 → 换关键词 → 再查不到"之间循环,token 烧完、用户等崩,最后给一句"抱歉"。
课堂讨论 · 2 分钟
"让 Agent 自己决定每一步串行还是并行,不是更智能吗?"你同意吗?
答案:不同意,拓扑应由代码决定
依赖关系是业务事实,不是模型灵感。让 LLM 决定拓扑,等于把正确性交给概率。正确分工:LLM 选"要哪些证据",代码决定"按什么顺序取"。
总结

今天你带走了什么

路由
三层:规则 → 意图分类 → LLM
路由
单次候选 10–20 个,超了先粗筛
路由
日志记:命中层、候选、最终选择
串并行
有依赖串行 + 短路;无依赖并行 + 限流
串并行
延迟 = 最长分支,不是各段之和
串并行
写操作不并行,顺序 + 幂等 + 审批
合并
对齐 / 去重 / 排序 / 留来源
合并
冲突不掩盖,权威源裁决并标 as_of
合并
判断归代码,解释归模型
护栏
max_steps / 循环检测 / 预算
护栏
只重试瞬时错误,其余立即失败
护栏
trace 先行:每一步可回放
"路由决定方向,拓扑决定代价,合并决定可信。"
课后作业 · 明日预告

画一张你们业务的多工具执行图

作业 1 · 工具链拓扑图
选一个真实业务问题(能否退款/能否发货/客户风险),画出需要哪些工具、哪些能并行、哪些必须串行、失败如何短路。
作业 2 · 合并规则说明
列出至少两个数据源之间的字段冲突(口径/时点/主数据),写明权威源、裁决规则和 as_of 策略。
作业 3 · 阅读
复习 OpenAPI 的 operationId 与并行调用;预习"会话状态":Redis 的 TTL、key 设计与隔离。
明日预告 · Day 34
Memory、Session 与上下文管理
Agent 能查多个系统了,但它记不住"刚才说的那单"——更危险的是,它把聊天历史当成了业务状态。明天讲清楚:什么该记、记在哪、什么时候必须忘。
讲师 · 包学斌

谢谢 · Q&A —— 实操问题随时在群里 @ 包学斌

Day 33 · 多 Tool 路由、串并行调用 · 包学斌
1 / 25
本站由 2AIGC Pages 托管 · 30 秒发布你的网站我也要建站