想象一家餐厅的晚高峰。如果只有一位主厨从头到尾独自完成每道菜——先做凉菜,再煲汤,再炒热菜,最后烤肉——客人要等两小时。现实中餐厅怎么做?主厨把订单拆开:凉菜台、汤锅台、热炒台、烤箱台同时开工,最后统一摆盘上桌。更妙的是,主厨心里有谱:汤要提前煲、烤肉要先腌制,这些是“依赖”;灶台就那几个,不能一口气接十桌单;某道菜炒砸了,其他菜照常上,绝不整桌重做。 AI Agent 调用工具,面临同样的选择。串行调用:问完天气再查航班,查完航班再比酒店,一次一问,耗时是所有工具耗时的累加。并行工具调用:把无依赖的多个查询同时发出去,总时延从“总和”变成“最慢的那一个”。这也是实时问答、多源数据聚合、并行信息检索等场景里 Agent 都在用的“并行术”。 这篇文章,我们就用这家餐厅的运转逻辑,把并行工具调用的依赖分析、并行识别、并发控制、结果合并、异常隔离,一桌一桌讲清楚。
学会并行工具调用,你的 Agent 响应时延能降 50%-80%。在实时问答、语音交互、多源聚合这些“秒回”场景里,这是从“能用”迈向“好用”的分水岭。
目录
可视化演示
展示并行工具调用三大机制:DAG依赖图识别可并行工具、串行vs并行时间对比、以及异常隔离与部分失败容错处理策略。
本节配有交互动效(进入视口后自动加载,约 3KB)。
DAG依赖图:自动识别可并行工具 构建工具调用有向无环图分析依赖 无依赖关系的工具自动标记为可并行 并行组内工具同时执行,有依赖的串行 DAG分析是并行调用的基础。
并行基础:总时延从“求和”到“取最大”
餐厅里如果只有一位厨师,晚上八点同时进来三桌客人,他只能一桌一桌做。这在信息技术里叫“串行”——一次只做一件事。坏处显而易见:总耗时等于每道菜耗时之和。Agent 也一样:一个请求要依次调用“天气查询→航班查询→酒店比价→门票预订”四个工具时,一次问答的时延就是四段网络往返的简单叠加,用户等得心焦。
主厨很快发现,凉菜和烤肉互不干扰,完全可以同时做。放到 Agent 里,工具调用之间也分两种:有依赖的(后一个需要前一个的输出)和无依赖的(各问各的)。无依赖的一组调用,可以组成一个“批次”同时发出。这就是并行工具调用的出发点:把等待时间重叠起来。
串行总时延是累加,并行总时延是取最大值。前提是这些调用彼此独立、互不读取对方的中间结果。实际收益受“最慢的那个工具”限制——就像一桌菜里烤鸭要四十分钟,凉菜再快也得等烤鸭上桌才算上齐。
回到餐厅:主厨拿到订单的第一步,不是埋头做菜,而是先判断哪些菜能“同时下锅”。能同时下锅的越多,整桌上齐越快;但只要有一道炖四十分钟的硬菜,整桌就快不过它。并行术的第一课,就是认清“总和”与“最慢”之间的天壤之别。
- 单个工具耗时 \(t_i\)第 i 个工具从发出到返回的完整时间,包含网络往返与工具自身处理时间,是时延计算的基本单位。
- 串行求和 \(\sum\)串行时工具一个接一个,后一个只能干等前一个结束,总时间等于各耗时机械相加。
- 并行取最大 \(\max\)并行时所有工具同时开始,批次完成时刻由最慢者决定,其他工具在它执行期间已经返回并空闲。
依赖分析:先做哪道菜,心里要有谱
不是所有菜都能同时做。熬高汤的汤锅台要先于借用高汤的热炒台开工;烤肉要先腌制两小时。主厨心里那张“先后关系图”,就是 Agent 的依赖图。工具 A 的输出是工具 B 的输入时,B 必须等 A 完成,强行并行只会让 B 拿到空参数而报错。
依赖图用有向边表达“谁等谁”:节点是工具,边表示数据流向。有了这张图,还要做拓扑排序——给每个工具排一个合法执行顺序,保证被依赖方永远排在依赖方前面。排序之后,同一层的工具互不依赖,天然适合放进同一个并行批次。
排序过程中一旦发现环路——A 需要 B、B 又需要 A——就说明任务描述自相矛盾,没有合法执行顺序。工程上的处理是直接报错或人工介入拆环,而不是让它进入“互相死等”的空转。分层分组的技巧在于:层号越小越先执行,同一层并行。
主厨心里没有这张“先后图”,就会把没腌好的肉塞进烤箱。Agent 心里没有依赖图,就会把需要航班号的余票查询和航班查询一起发出去,然后收到一堆“参数缺失”的报错。依赖分析,是并行术的地基。
- 依赖边 \(i \to j\)一条从工具 i 指向工具 j 的边,表示 tool_j 的输入引用了 tool_i 的输出,这是构建依赖图的原材料。
- 分层号 \(\text{level}\)把工具按依赖深度编号,没有前驱的层号为 1,其余工具的层号 = 所有前驱最大层号 + 1。
- 严格大于 \(\gt\)每条边都必须满足 level(j) > level(i),保证被依赖方先执行完,所有并行调度都不允许违反这条约束。
并行识别:找出能同时下锅的菜
高汤没熬好,热炒台就闲着吗?不——主厨会先把不需要高汤的菜安排给热炒台。把工具分成“现在就能做”和“必须等别人”两组,这个筛选动作,就是并行识别。
识别的方法很直观:检查工具两两之间有没有依赖路径。所谓路径,不光是“直接依赖”,还包括“间接依赖”——A 的输出喂给 B、B 的输出喂给 C,那么 A 和 C 之间就存在一条可达路径,C 不能和 A 并行。只有两个工具双向都不可达,它们才是真正意义上的无依赖,可以安全地放进同一批次。
工程上,这一步通常在调用前做静态预分析:把整个工具调用图扫一遍,标记出所有无依赖对。参数独立性是另一个必要条件——即使两个工具在依赖图上没连边,如果它们读写同一个共享状态(比如同时写同一个用户缓存),也要小心并发冲突。预分析的结果就是一张“可并行矩阵”,运行时直接查表分组,省去每次调度的重复计算。
主厨扫一眼菜单,就能报出“这三道可以同时做,那两道得等汤好”。Agent 的并行识别做得也是同样的事:在动手前把“能同时下锅的菜”圈出来,剩下的任务自然沉淀到后面的批次。识别得越准,时间重叠得越多。
- 并行判定 \(\text{Para}(i, j)\)定义工具 i 与工具 j 能否放进同一个并行批次同时执行。
- 可达路径 \(\text{path}\)依赖图中“顺着边走几步能到对方”的关系,间接依赖也是依赖,必须一并计入。
- 双向不可达 \(\nexists\)两个方向都找不到路径,才说明两者互不等待,是真正可以同时下锅的“菜”。
并发控制:灶台有限,排队等候
厨房里灶台只有六个。同时开工的任务超过六个,帮厨互相撞车、煤气不够、出菜口堆满盘子。主厨定下的“最多同时开 N 个灶”,就是并发度上限。Agent 调用外部 API 同样有隐形的灶台:QPS 限制、连接池大小、下游服务的负载能力。
实现并发控制最常用的工具是信号量(Semaphore):一个计数器记录当前正在执行的工具数,达到上限时,新任务进入等待队列,等某个工具完成后让出一个“灶位”再放行。排队策略可以简单粗暴(先来先服务),也可以带优先级(关键路径上的工具插队)。
资源预算同样重要:内存、网络连接、第三方额度都是有限的。总预算除以单次调用开销,就能估算出一个安全的并发上限——宁可在队列里多等 50 毫秒,也不要一次性把下游打挂。并发度为 k 时,总耗时存在一个理论下界,它由“最慢工具”和“总工作量均摊”共同决定。
主厨不会因为客人催单就把二十道菜同时丢进六个灶台——那只会得到一片焦糊和更大的混乱。Agent 也一样:并发不是越多越好,而是“在资源允许的范围内,让每一个灶台都不闲着”。排队不是退步,而是对整体稳定性的保护。
- 并发度 \(k\)同一时刻最多执行的工具数,对应“灶台总数”,是调度器的硬约束。
- 最慢工具下限 \(\max_i t_i\)无论怎么调度,最慢的那个工具始终是单点下限——它不结束,批次就不结束。
- 工作量均摊 \(\sum t_i / k\)把全部时长的工作量平摊到 k 个“工位”上,工位越少这个下界越高。
- 取两者较大者 \(\max\)真正的理论最低时延不会低于这两个下界中的任何一个,是评估调度优劣的标尺。
结果合并:出菜口按桌号摆盘
各工位完工时间不同——烤箱的烤肉先出炉,凉菜反而后做好?出菜口不能谁先到谁上桌,必须按桌号、按菜单顺序摆盘。Agent 的并行调用也一样:先返回的未必是第一个请求的结果,必须按原始序号重新排列,否则就会把“机票价格”填进“天气温度”的位置。
这就是乱序完成问题。并行发出四个请求后,它们各自走各自的网络路径,完成顺序完全是“谁先到家谁先敲门”。正确处理方式是:给每个工具调用挂一个序号或请求 ID,返回时按 ID 放进对应的格子,而不是按到达时间的先后追加进数组。
合并阶段还要做去重与聚合:多个工具可能返回重复数据(比如两次检索命中同一篇文章),先按主键去重;然后按业务规则聚合排序,比如把机票、酒店、景点按总预算排成一张比价表。最终交付给调用方的结果,必须保持与发出调用时一致的顺序,这叫顺序一致性——让上层代码感觉不到并行的存在,只享受并行的速度。
主厨在出菜口做的事,从来不是“谁先出锅谁上桌”,而是“每一桌的菜按菜单顺序齐活”。结果合并就是 Agent 的出菜口:外面看起来秩序井然,里面早已千帆竞发。
- 结果集合 \(\{r_i\}\)第 i 个工具调用返回的内容,每个结果都携带着自己的调用序号。
- 重排映射 \(\pi\)完成顺序五花八门,需要一个“序号 → 结果”的映射表来归位,而不是按完成时间排队。
- 按原始序号输出最终返回时严格恢复发出时的顺序,保证调用方看到的结果次序与请求次序一致。
异常隔离:一道菜失败,不连坐整桌
蒸锅台的火候没控制好,一条鱼蒸老了。主厨不会把整桌菜都倒掉——他会给这桌客人补一道菜,其他菜照常上。厨房里任何工位的失误,都不能连坐整桌。这就是异常隔离:并行调用中,单个工具失败不应该拖垮整个批次。
现实中最常见的故障是慢和挂:某个第三方 API 迟迟不返回,把整个批次的完成时刻无限拉长。对策是超时熔断——为每个工具设定等待上限 \(\tau\),超时就掐断这次调用并标记失败;连续失败多次后直接熔断,短时间内不再请求该工具,防止它继续拖累整体。降级补偿则是失败后的兜底:用缓存数据、默认值或占位文案填上失败的位置。
部分失败是并行场景的常态。四个调用成功三个,我们就返回三个真实结果加一个降级结果,而不是把整批结果全部作废。比起串行模式“一个出错全链报废”,并行加隔离让系统有了优雅降级的能力——用户至少拿到大部分答案,而不是一句冷冰冰的“服务错误”。
主厨的哲学是:一道菜失误是局部事故,整桌餐是整体承诺。异常隔离让 Agent 在部分工具失灵时依然能“把菜上齐”——用降级替代失败,用超时替代无限等待。这种韧性,正是生产环境里并行调用比串行调用更难也更有价值的地方。
- 实际耗时 \(t_i\)工具的真实执行时长,网络挂起时可能趋于无穷,必须给它上“闹钟”。
- 超时阈值 \(\tau_i\)为每个工具设定的等待上限,超过即终止等待并进入失败处理流程。
- \(\min(t_i, \tau_i)\) 截断把“无限等待”截断成“有界等待”,这是熔断的核心动作。
- \(\max_i\) 聚合完成时刻批次完成时间由最晚结束者决定,超时保证了它不会无限晚。
工程实践:把并行术跑进生产
晚高峰的餐厅,主厨把这套“同时开工、出菜口统一摆盘、单菜失败不连坐”的流程跑顺之后,翻台率翻了一倍。实时 Agent 也一样:把并行术嵌进架构,让用户产生“秒回”的体验。我们来盘点三个最典型的落地场景。
第一个是实时问答。语音助手被问到“明天去上海出差要带伞吗?顺便订最早航班”,串行查询日历、天气、航班三个工具要 3 秒以上,用户早就打断重问了;并行发出后 1.2 秒内全部返回,合并成一句话回答。第二个是多源数据聚合查询:旅行规划 Agent 拿到“杭州 3 日游”请求,同时拉取航班、酒店、景点、天气四个渠道,按“总预算”统一排序输出比价表。第三个是并行信息检索(RAG 多路召回):同步检索 Elasticsearch、向量库、Wiki API 三路,去重后交给 LLM 生成答案;某一检索源超时自动降级为两路结果,整体体验不受影响。
衡量这套并行术的价值,最直接的指标是加速比——改造前的串行耗时除以改造后的并行耗时。工程上的经验值通常在 2 到 5 倍之间,具体取决于无依赖工具的数量和耗时分布。别忘了配合可观测性:把每个工具的耗时、失败率、排队时间都记下来,持续优化“最慢的那道菜”。
餐厅的晚高峰不会消失,但主厨的调度术让每一桌都能按时上菜。Agent 的请求洪峰也不会消失,但并行工具调用让每一次问答都快人一步。把这张“并行术”的流程图挂在架构图上,你的 Agent 就有了从“能用”到“好用”的底气。
- 串行基线 \(T_{\text{串行}}\)改造前逐个调用工具的耗时总和,是衡量提速的参照系。
- 并行耗时 \(T_{\text{并行}}\)采用依赖分组、并发控制、结果合并后,整个批次的实际总耗时。
- 加速比 \(S\)两者之比大于 1 表示变快;2 就是快一倍,5 就是快四倍,是汇报优化成果时最有力的数字。
AI 应用场景
场景一:实时语音问答助手
阶段:Agent 工具编排与并行调度
解决的问题:语音交互要求首响时间低于 2 秒,串行调用日历、天气、航班三个工具要 3 秒以上,用户明显感到迟钝甚至直接打断。并行发出后将总时延压缩到最慢工具的水平,给“秒回”体验留出余量。
具体案例:智能音箱被问到“明天去上海出差要带伞吗?订最早航班”。Agent 先做依赖分析,确认三个查询互不依赖,于是同时调用日历 API 确认行程、天气 API 查降水概率、航班 API 查最早班次,最后按原始顺序合并成一句话回答:“明天上海有雨,请带伞;最早航班是 7:20,预计 9:00 到达。”
场景二:多源数据聚合的旅行比价
阶段:并发控制与结果合并
解决的问题:旅行规划需要聚合机票、酒店、景点、天气四类数据,串行查询要四次网络往返,且一旦中间某个渠道超时整个流程卡死。并行加隔离让四个渠道同时返回,任何一个超时都只降级那一项。
具体案例:用户说“帮我规划杭州三天两晚的行程,预算 3000 元”。Agent 同时请求航班比价 API、酒店预订 API、景点门票 API、天气 API,四路返回后去重合并,按“交通 + 住宿 + 门票”总价排序,输出三套方案让用户挑选。全程耗时从串行的 4 秒降到 1.5 秒。
场景三:RAG 并行多路检索
阶段:并行信息检索与异常隔离
解决的问题:单库检索命中率不够,多路召回若串行执行会让问答时延成倍增长;更重要的是,某一路检索源宕机不应让整个问答失败。并行检索加超时降级是标准解法。
具体案例:企业知识库问答机器人收到“今年报销新规是什么”,同时检索 Elasticsearch 文档库、向量库相似段落、公司 Wiki API 三路。向量库响应超时 800ms 被熔断降级,剩余两路结果去重后交给 LLM 生成答案,用户无感知地拿到完整回答。
Python 演示
import timefrom concurrent.futures import ThreadPoolExecutor, as_completed def fetch_weather(city): time.sleep(0.5) # 模拟 0.5s 网络耗时 return "sunny 25C" def fetch_flight(city): time.sleep(1.2) # 模拟 1.2s 网络耗时 return "flight avg 1280 CNY" def fetch_hotel(city): time.sleep(0.8) # 模拟 0.8s 网络耗时 return "hotel min 320 CNY" def fetch_sight(city): time.sleep(0.4) # 模拟 0.4s 网络耗时 return "sights: West Lake, Lingyin" TOOLS = { "weather": fetch_weather, "flight": fetch_flight, "hotel": fetch_hotel, "sight": fetch_sight,} def call_sequential(city, tool_names): results = {} t0 = time.time() # 串行:一个接一个调用,总耗时是各工具耗时之和 for name in tool_names: results[name] = TOOLS[name](city) return results, time.time() - t0 def call_parallel(city, tool_names, max_workers=4): results = {} t0 = time.time() # 线程池:最多 max_workers 个工具同时执行(对应并发度 k) with ThreadPoolExecutor(max_workers=max_workers) as pool: # 同时提交所有无依赖的工具调用,future 上挂上工具名 futures = {pool.submit(TOOLS[name], city): name for name in tool_names} # 谁先完成谁先被处理,完成顺序与提交顺序无关 for fut in as_completed(futures): name = futures[fut] try: results[name] = fut.result() except Exception as exc: # 异常隔离:单个失败只记录该工具,不影响其他结果 results[name] = "failed: " + str(exc) return results, time.time() - t0 def merge_by_order(results, tool_names): # 结果合并:按原始调用顺序重排,而不是按完成顺序 parts = [] for name in tool_names: parts.append(name + "=" + results[name]) return " | ".join(parts) if __name__ == "__main__": city = "Hangzhou" tool_names = ["weather", "flight", "hotel", "sight"] seq_res, seq_cost = call_sequential(city, tool_names) par_res, par_cost = call_parallel(city, tool_names) print("sequential:", merge_by_order(seq_res, tool_names)) print("parallel: ", merge_by_order(par_res, tool_names)) print("seq cost %.2fs vs par cost %.2fs" % (seq_cost, par_cost)) print("speedup: %.2fx" % (seq_cost / par_cost))
with ThreadPoolExecutor(max_workers=max_workers) as pool:创建“灶台”线程池,同时最多跑 max_workers 个工具,这就是并发度上限。futures = {pool.submit(TOOLS[name], city): name for name in tool_names}把四个无依赖的工具一次性提交进线程池,每个 future 挂上自己的工具名,对应并行识别后的“同批派遣”。for fut in as_completed(futures):谁先完成谁先被收割,直观体现“乱序完成”——完成顺序与提交顺序无关。results[name] = fut.result()按 future 上挂的工具名把结果放进对应格子,天然实现“按名归位”。
常见误区
误区并行能省时间,那我干脆把 Agent 里所有工具调用都丢进并发池,响应一定最快。
正解有依赖的调用绝对不能并行——先查航班列表,才能查某航班余票;先算好总价,才能发起支付。盲目并行轻则参数缺失报错,重则拿到错误答案。正确做法是先做依赖分析,只把无依赖的工具子集组成批次,有依赖的按拓扑顺序分批执行。
为何常见:只看到并行省时的直观收益,忽略了“后一个工具需要前一个工具的输出”这个最基本的数据流约束。
误区反正要多快好省,把 max_workers 调到 100,所有工具一瞬间全发出去,总时延肯定趋近于零。
正解并发受三重约束:外部 API 的速率限制会直接拒绝超量请求、线程上下文切换消耗 CPU、下游服务负载过高会超时雪崩。并发开过头,大量请求被限流重试,反而比串行更慢。工程上通常取 3-8 个并发,配合信号量限流和排队策略。
为何常见:把“并行”等同于“无限并行”,忽略了每个真实服务都有吞吐上限这个物理事实。
误区我并行发出四个请求,哪个先返回,结果数组里就把它放在哪个位置,反正是同时发的。
正解完成顺序和调用顺序没有任何关系——网络延迟、服务端负载都会让结果乱序到达。必须给每个调用挂序号或请求 ID,返回时按 ID 归位,最后按原始调用顺序输出,否则就会把“机票价格”填进“天气温度”的位置,张冠李戴。
为何常见:串行时代完成顺序天然等于调用顺序,思维惯性没有切换到“乱序完成”的新模型。
误区并行调用中有一个工具抛异常,整体就应该 catch 后重试,或者直接告诉用户“服务出错”。
正解正确的做法是部分失败隔离:失败的那一项用降级结果兜底(缓存值、默认值、占位文案),其余成功结果照常使用;只有关键路径上的工具失败才考虑整组失败。重试必须配合熔断,连续失败多次就停手,防止无限重试把下游彻底打爆。
为何常见:程序员习惯了数据库事务“全有或全无”的思维,忘了读多写少的 Agent 场景大多能容忍部分信息缺失。
面试题
Q1. 串行调用 n 个独立工具,总时延大约是?
- A.各工具耗时之和← 正确答案
- B.最慢工具的耗时
- C.所有工具的平均耗时
- D.最快工具耗时的两倍
答案:A。串行就是排队,一个做完才做下一个,总时延等于所有工具耗时求和。选 B 混淆了串行与并行——并行才看最慢的那个。类比餐厅:一道菜一道菜做,一桌菜的时间就是所有菜的时间相加。
Q2. 并行调用 n 个相互独立的工具,整个批次的时延大约等于?
- A.接近最慢工具的耗时← 正确答案
- B.等于全部工具耗时的和
- C.等于所有工具耗时的差
- D.接近最快工具的两倍
答案:A。并行时所有工具同时开始,批次完成时刻由最慢者决定,其余工具在它执行期间已经返回。这是“总和”与“最大值”的区别,也是并行术最核心的收益来源。
Q3. 工具 A 的输出是工具 B 的输入,正确的执行方式是?
- A.同批并行执行 A 与 B
- B.先执行 A 再执行 B← 正确答案
- C.只执行 B 跳过 A
- D.先执行 B 再执行 A
答案:B。存在数据依赖就必须按拓扑序执行:先产出再消费。选 A 会让 B 拿不到输入直接报错;选 D 顺序颠倒必然出错。类比厨房:必须先熬高汤,才能做用高汤的菜,两件事不能同时下锅。
Q4. 依赖关系通常用什么结构表达并进行排序校验?
- A.有向无环图加拓扑排序← 正确答案
- B.单向链表加顺序遍历
- C.哈希表做键值映射
- D.最小堆做优先级队列
答案:A。依赖关系天然是“谁等谁”的有向关系,用有向图建模,用拓扑排序求出合法执行顺序,同时能发现环路。链表表达不了多对多依赖,哈希表管不了顺序,堆只适合按优先级出队,都不匹配。
Q5. 并发度为 k 时,总工作量均摊到每个工位的理论下界是?
- A.总工作量除以并发度 k← 正确答案
- B.总工作量乘以并发度 k
- C.最慢工具耗时除以并发度 k
- D.所有工具耗时之差的总和
答案:A。k 个“灶台”同时工作,理想情况下总工作量被均匀分摊,下界是总耗时之和除以 k。这是并行调度的理论下限之一,另一个下界是最慢工具的耗时,实际时延不会低于两者中的较大者。
Q6. 以下哪些情况会显著削弱并行调用的收益?
- A.工具之间存在长依赖链← 正确答案
- B.并发上限被压得很低← 正确答案
- C.单工具耗时远超其他工具← 正确答案
- D.工具返回结果全部完全相同
答案:ABC。长依赖链迫使任务只能逐层串行;并发上限低会让大量任务排队;单个工具耗时独占鳌头时,并行相比串行几乎没有提速。D 选项与耗时无关,不影响并行收益。
Q7. 并行调用返回结果乱序,应按什么恢复正确顺序?
- A.按原始调用序号进行重排← 正确答案
- B.按返回时刻先后进行排序
- C.按工具名称字典序拼接
- D.按结果内容长度进行排序
答案:A。每个工具调用在发出时就挂上序号,返回后按序号归位,最终按原始调用顺序输出。完成时刻、名称、内容长度都与调用语义无关,按它们排序会把结果张冠李戴。
Q8. 关于超时熔断与异常隔离,下列说法正确的是?
- A.超时把无限等待截成有界等待← 正确答案
- B.单个工具失败不影响其余结果← 正确答案
- C.失败后应无限次地自动重试
- D.失败工具可用降级结果兜底← 正确答案
答案:ABD。超时阈值保证批次不会因一个慢调用而无限拖延;异常隔离让单点失败不连坐整批;降级结果(缓存、默认值)兜住失败的位置。无限重试会打爆下游,必须配合熔断在连续失败后停手。
Q9. 并发度 k=3,6 个耗时相同的任务,理论最短完成时间约为串行的?
- A.三分之一← 正确答案
- B.二分之一
- C.六分之一
- D.三分之二
答案:A。6 个任务分到 3 个工位,每工位 2 个任务,并行耗时是串行的 2/6 = 1/3。类比餐厅:三个灶台同时开火,六道菜分两轮就能出齐。
Q10. 并行工具调用的工程实践包含哪些关键环节?
- A.依赖分析后分组并行调度← 正确答案
- B.并发度限流与排队等待← 正确答案
- C.结果按请求序号合并← 正确答案
- D.所有工具强制串行重试
答案:ABC。完整流程是:先画依赖图分组合并调度,再用信号量限制并发、超限排队,最后按请求序号把乱序结果重排输出。D 选项与并行术背道而驰——并行失败后的重试应该针对单个失败工具做熔断降级,而不是强制整批串行。