想象你经营着一家小餐馆,明天晚上要承办一场百人婚宴。如果所有菜都让主厨一个人按顺序做——先炒这盘,待会儿再炒下一盘——客人吃完前菜大概要等到天亮。聪明的做法是:把菜单拆成几十份小任务,多位帮厨各占一个灶台同时开火,菜品陆续出锅后统一送到传菜口,由主厨核对摆盘再端上桌。负责“拆菜单”的是 Map,多位厨师同时开工是“并行执行”,最后收拢菜品的是 Reduce。 LangGraph 的并行节点就是这套“数字化后厨”。它把一个大任务拆成多个互不依赖的子任务,让它们在同一轮里并发执行,再按规则合并成最终结果。你会发现,LangGraph 里的 Channel 是后厨公告板,Send 指令是派单条,super-step 是整轮齐步走的号令,而并发调度就是灶台排班表。读完这篇文章,你就能理解一个 Agent 如何从“做完一件事再做下一件”,进化成“同时忙活一堆事,最后合成一个漂亮答案”。
对 AI 从业者来说,这是让 Agent 从“单线程苦力”升级为“任务调度主管”的关键能力。掌握并行节点的并发执行模型,你就能在检索、批处理、多方案对比等场景里把吞吐量提升数倍,这也是高级 Agent 框架面试中绕不开的硬核考点。
可视化演示
展示 Map-Reduce 并行执行模型:Map 阶段分发任务到多个 Worker 并发执行,Reduce 阶段合并结果。以及并发控制与错误处理策略。
本节配有交互动效(进入视口后自动加载,约 3KB)。
Map-Reduce 并行流程 Input:任务分发入口节点 Map:多个Worker并发执行子任务 Reduce:合并所有Worker结果 总耗时 ≈ max(T_worker_i) + T_reduce Map阶段并行执行,Reduce阶段合并结果
Map并行拆分:任务扇出
婚宴筹备最紧张的时刻,是主厨在开工前把厚厚一沓菜单摊开,飞快地勾画出每个帮厨该负责的菜品。这个“拆菜单、派活”的动作,对应 LangGraph 里的 Map 阶段。入口节点收到一个输入后,并不直接处理它,而是把它拆成许多互不依赖的子任务,每个子任务带着自己的一份“小菜单”(独立的状态快照)流向不同的执行分支。
在 LangGraph 中,这个“派单”由一个叫 Send 的指令完成。你可以把 Send 想象成一张张派单小票:每张票上写着一个子任务的函数和数据。更巧妙的是,这个小票可以动态开具——Map 阶段根据输入内容决定开几张票、每张票的数据结构是什么。这就像主厨看到客人有海鲜过敏备注,临时把某道菜拆成“免海鲜”版单独派一张单,不需要事先列好所有情况。
因为我们想让每位帮厨全神贯注炒自己的菜,所以每个子任务都拥有独立的子状态。它们可能共享一份“总菜单”(全局配置),但各自的进度、结论都写在自己的小本子上,互不干扰。这样设计的好处是:任何两个分支之间不会因为抢同一支笔而打架,后续的并发执行才有安全的基础。当一个入口节点调用 send() 生成多个任务时,LangGraph 会把这些分支当作同一个父节点的多个“分身”,并发窗口会同时调度它们。
1. 子任务与并发窗口的比值:N 是待处理的子任务数,M 是允许同时执行的并发窗口(比如线程池大小)。
2. 上取整:⌈·⌉ 表示向上取整。7 个子任务、3 个并发窗口,需要 3 轮而不是 2 轮,因为最后一轮哪怕只有 1 个任务也得执行完。
3. 总时间构成:轮数乘以单任务耗时得到执行时间,再加上 Map 拆分开销和 Reduce 合并开销,就得到整体耗时。
这个公式一眼就能看出并行收益来自哪里:如果 N 很大而 M 很小,时间主要花在排队轮次上;如果 M 足够覆盖 N,一轮就能跑完,剩下就是拆与合的固定开销。主厨看完各灶台的完成情况后,判断的正是这个“怎么排班最省时间”的问题。
\displaystyle T = \left\lceil \frac{N}{M} \right\rceil \times \tau + t_{\text{map}} + t_{\text{reduce}}并行执行机制:Super-step 同步
派单结束后,后厨进入最热闹的环节:六口灶台同时开火,油锅滋滋作响。每一位帮厨都不知道旁边的人在炒什么,只专心做好手上的菜。LangGraph 的并行执行机制与此惊人地相似——多个并行分支同时处于 running 状态,每个分支只处理自己的子状态,彼此之间不需要直接通信。
但热闹背后必须有秩序。LangGraph 采用了一种叫 “super-step“(超级步)的同步模型:每一轮被唤醒的节点全部执行完毕后,框架才会推进到下一轮。你可以把它理解为后厨的”齐步走”口令——所有灶台火候到点同时起锅,不会出现某个灶台还在炒、另一位已经乱入下一轮的情况。任何依赖上一步结果的节点,都必须等这一轮所有并行分支归档之后才能被唤醒,这正是数字化后厨和真实后厨一样需要的纪律。
那各位帮厨做完菜,如何把自己的成果“登记在案”?答案是 Channel——后厨公告板。每个并行分支执行结束后,会把结果写到指定的 Channel 上。如果多个分支同时往同一个 Channel 写入,就由 Channel 的写入规则决定如何处置:有些规则是“保留最后一个值”,这相当于公告板上最后贴上的一张便利贴盖住了之前的;有些规则是“追加成列表”,像把每位厨师的作品单依次钉在公告板下面。写入规则的协调,保证了所有分支对状态的修改以可预期的方式合并。
super-step 的同步保证换来的是状态一致性:读到的每个值都代表“上一轮所有分支的最终结果”,不会出现读到一半的脏数据。正如后厨的秩序保证了每道菜都是完整出锅——虽然六口灶台同时开火,但传菜口永远只接收已经完成的菜品。
- 输入集合X 是待处理的所有输入元素,N 是元素的个数。比如 100 份订单、50 个城市的天气数据。
- 处理函数f 是每个并行分支要执行的节点逻辑。所有分支使用同一个 f,只是输入数据不同,就像所有帮厨遵循同一套菜谱但各自炒不同的菜。
- 输出集合Y 是所有分支结果的有序或无序集合。这个集合就是后续 Reduce 阶段的“半成品仓库”。
Reduce结果合并:扇入汇聚
帮厨们陆续熄火,端着成品走向传菜口。此时的主厨不再下锅炒菜,而是站在摆盘区,把一盘盘菜按宴席顺序排列、核对、装饰。这个“收拢”动作就是 LangGraph 的 Reduce 阶段:它把所有并行分支的输出汇聚到一个节点上,按照预订规则整合成最终结果。
LangGraph 里的 Reduce 有两种实现思路。第一种是“站着收”——设置一个 Reducer 节点,它等所有并行分支完成后被唤醒,拿到完整的输出列表,做排序、筛选、聚合这类重量级操作。另一种是“边收边合”——依靠 Channel 的写入规则增量合并,比如统计型 reducer 每收到一个值就累加一次,不用等全部结束就已有部分结果。选择哪种,取决于你要的是“全部到齐再算总分”还是“边收分边记录”。
这里最关键的概念是 Reducer 函数本身。它定义了如何把多条结果合并成一个:是拼接、求和、加权平均,还是取置信度最高的那条?自定义合并策略的灵活性,让并行模式不只适用于同质化任务。你可以让一个分支写 SQL 查询结果、另一个分支写向量检索结果,最后在 Reduce 阶段用加权融合把它们变成统一的推荐列表——像主厨把冷菜、热菜、甜点编排成完整的婚宴菜单。
主厨在传菜口每收一道菜就勾掉菜单上的一项,全部的菜到齐才能开席——Reduce 就是这样一道“验收关”。它保证了并行分支各自为战的成果,最终被组织成一个有序、完整的整体交付给下游节点。
- 并行输出y_1 到 y_N 是 Map 阶段各个分支的输出,它们此时全部就绪,等待合并。
- 合并运算⊕ 不是固定的加号,而是你自定义的归并逻辑——可以是字符串拼接、数值累加、字典更新或打分排序。
- 最终结果z 是所有输出按照合并运算顺序作用后的最终值。合并运算的结合性很重要:
(a⊕b)⊕c应当等于a⊕(b⊕c),这样并行合并(两两合并再合并)才不会出乱子。
并发调度控制
只有六口灶台的后厨,却接到了二十道菜的任务。主厨必须做出选择:一口气全塞给帮厨,灶台不够、案板不够、调味料也会抢破头。真正聪明的调度是“分批上灶”——每轮只推六道菜,做完一轮再上新菜。LangGraph 的并发调度控制,处理的就是这个“灶台排班”问题。
LangGraph 底层通常跑在线程池或异步执行器上,max_workers 之类的参数直接决定了并发窗口的大小。并发窗口不是越大越好:窗口太大,大量分支同时抢占内存和回调栈,反而让单任务变慢;窗口太小,任务排队时间变长,并行红利所剩无几。实用的策略是让窗口大小接近执行器的核心并发度,就像六口灶台的后厨,派七道菜不如派六道菜节奏更稳。
除了窗口大小,LangGraph 还允许你控制“分批派单”的节奏。Map 阶段可以不是一次性生成全部子任务,而是每轮生成一部分,等上一批完成后再继续派下一批——这对应 Send API 的连续调用。你可以把它想成主厨不会一次性把所有菜单都拍到帮厨面前,而是按上菜节奏一盘一盘地派。配合 recursion limit(递归深度上限),这种动态分批调度能避免“任务爆炸”:在循环或递归场景中,只要还有剩余配额就继续派单,配额耗尽则优雅终止。
调度的艺术是“有限资源做最多的事”。当你在生产环境里调并行度时,那个最合适的窗口值往往要通过压测来找——正如主厨知道六口灶台最稳,但偶尔也会根据客流量临时调整到五口或七口,核心原则是不让任何一口灶台闲太久,也不让任何一位帮厨手忙脚乱。
- 忙碌资源M_busy 是同时处于执行状态的并发任务数量,M 是并发窗口总量。
- 执行与等待τ 是任务实际执行的平均耗时,t_wait 是任务排队等待的平均耗时。总占用时间就是两者之和乘上 M。
- 利用率实际干活的时间占比。窗口太小利用率低,因为 M_busy 很小;窗口太大利用率也可能低,因为资源争抢导致 τ 变大、t_wait 变长。
错误处理策略
热闹的后厨也会出事:一位帮厨火候没看住,把红烧肉烧糊了。这时候整桌宴席怎么办?是让这位帮厨重做一份,还是干脆取消这道菜,让宴席继续?LangGraph 并行执行的错误处理,本质上就是这些预案的工程化实现。
默认行为是“整桌停下”:只要任何一个并行分支抛出异常,整个 super-step 立刻终止,图进入失败状态。这听起来很残酷,但你换位思考一下——主厨无法确定其他灶台的结果是否会被这道糊菜影响,与其端上一桌东拼西凑的菜,不如让宴席停下来统一处理。不过,LangGraph 也提供了“重做这一道”的选项:给节点配置重试策略(retry policy),设置最大重试次数和退避间隔。这就像帮厨被允许再炒一次,第二次心里更有底,成功率自然更高。
还有更进阶的降级方案:在 Reduce 阶段手动捕获部分分支的结果,即使某个分支失败,也把其他成功分支的结果做合并输出。这种“部分降级”像是主厨说:“红烧肉没了,但其他菜照常上,我们用备用的梅菜扣肉顶上。”它最适合那些“部分成功仍有价值”的场景,比如多源检索时一个数据库挂了,其他数据库的结果依然可以用来回答问题。
这个公式揭示了一个反直觉的真相:并行不是“人多力量大”,而是“人多摊上事的概率也大”。十个人各端一杯水,总有一个人可能手抖。所以优秀的 Agent 工程师在设计并行图时,总会在关键分支上加重试和降级策略,避免一场婚宴因为一道菜毁于一旦。
- 单分支成功每个分支的成功率为 p,失败率为 1-p。
- 所有分支同时成功N 个独立分支全部成功的概率是各自的成功率相乘。
- 整体失败概率用 1 减去“全部成功”的概率,得到至少一个分支失败的概率。当 N 逐渐增大时,即使单分支成功率很高,整体失败率也会显著上升。
适用场景案例
不是每道菜都适合让多位厨师同时做——佛跳墙需要文火慢炖,中途加人反而添乱。判断一个任务是否适合 LangGraph 并行节点,核心标准是看它能不能被拆成“互不依赖的平行工序”。如果子任务之间需要频繁交换中间结果,或者每一步都依赖上一步的输出,那就不该上并行,老老实实串行反而更可靠。
适合的场景长什么样?第一类是批处理数据:把一份包含一千篇文档的语料库拆成十个片段,每个片段独立做摘要,最后合并成一份总报告。这像宴席上的凉菜拼盘——每盘独立制作,最后摆在一起。第二类是多源检索:同一个查询同时发给多个知识库或搜索引擎,收集所有结果后做排序融合,能显著缩短用户等待时间。第三类是子 Agent 规划:让多个子 Agent 分别调研市场、竞品、成本,最后汇总成商业分析报告,每个子 Agent 都是独立动作、独立产出。
但也有明显的局限性。如果所有分支都要竞争同一份全局状态,频繁写入引起冲突,状态一致性就难以保证;如果任务之间存在隐形依赖——比如分支 A 需要分支 B 中途的一个数值才能继续——硬塞进并行只会得到错误结果。判断的方式一句话:能不能把任务画成“一棵树”而不是“一条链”?树可以并行修剪枝干,链只能逐节推进。掌握了这个判断标准,你就能在合适的场景里放心地开足火力。
主厨深知:哪怕后厨有三十口灶台,如果菜单设计本身必须一道菜接一道菜地炖,那并行也救不了宴席的进度。你能做的,是把可并行的部分尽量拆细、摊平,让每一口灶台都有活干,同时接受“总有些步骤快不起来”的现实。懂取舍,才是用好并行节点的最高境界。
- 串行占比1 - p_par 是任务中无法并行的部分比例,比如数据加载、Reduce 合并的固定开销。
- 并行部分的加速p_par 除以并发窗口 M,表示并行部分被压缩后的时间占比。
- 总加速比原始耗时 1 除以加速后的总耗时占比。当 M 趋于无穷大时,加速比趋近于 1/(1-p_par),这就是并行收益的上限。
AI 应用场景
多源情报并行检索 Agent
阶段:数据获取与融合
解决的问题:传统检索 Agent 串行访问多个数据源,每次查询要等待前一个接口返回才发起下一个请求,用户等待时间是所有数据源延迟的总和。使用 LangGraph 并行节点后,所有数据源同时发起检索,总延迟从“累加”变成“最大”,且 Reduce 阶段可以统一去重排序。
具体案例:某金融风控 Agent 需要同时查询企业工商信息、司法涉诉、舆情新闻和黑名单库四个数据源。用 Map 阶段拆出 4 个查询分支并行执行,每个分支独立调用一个 API,Reduce 阶段按企业名称关联聚合,生成统一的风险画像。原来 4 秒的串行等待降到 1.2 秒,用户体感提升明显。
批量文档智能审查管道
阶段:任务分解与批量处理
解决的问题:合同审查、论文查重、代码审计等场景中,需要处理大量文档。传统做法是逐篇送审,GPU 和大模型算力闲置而耗时线性增长。并行节点将文档按批次拆分成多个子任务,每个子任务独立调用大模型做要点抽取,Reduce 阶段汇总成审查报告并标注风险等级。
具体案例:某法律科技公司的合同审查 Agent 一次接收 50 份合同,Map 阶段按合同类型动态分组派单,每组分配独立的大模型调用并发执行,最后合并所有审查意见并按风险分值排序输出。处理吞吐量提升约 4 倍,而成本几乎不变。
多模型方案投票决策 Agent
阶段:推理与决策
解决的问题:关键决策场景中单一模型可能偏差较大,需要多个模型/策略并行出方案再投票。并行节点让不同模型在各自分支里独立推理,互不污染上下文,Reduce 阶段做加权投票或置信度融合,输出最优方案。相比串行逐模型评估,等待时间缩短为原来的一半以下。
具体案例:某医疗辅助诊断 Agent 同时调用 3 个专科模型判断影像报告,Map 阶段并行分发,Reduce 阶段按“至少两票一致”的规则输出结论,并附上分歧提示给医生复核。
Python 演示
# -*- coding: utf-8 -*-"""LangGraph Map-Reduce 并发执行模拟:多城市空气质量查询与汇总"""from concurrent.futures import ThreadPoolExecutor, as_completedimport time def fetch_city_aqi(city): time.sleep(0.3) aqi = sum(ord(ch) for ch in city) % 100 return {"city": city, "aqi": aqi} class CityAqiPipeline: def __init__(self, cities, workers): self._cities = cities self._workers = workers self._results = [] def map_phase(self): with ThreadPoolExecutor(max_workers=self._workers) as pool: futures = [pool.submit(fetch_city_aqi, c) for c in self._cities] for fut in as_completed(futures): self._results.append(fut.result()) def reduce_phase(self): total = sum(item["aqi"] for item in self._results) best = max(self._results, key=lambda item: item["aqi"]) avg = total / len(self._results) return {"best_city": best["city"], "best_aqi": best["aqi"], "avg_aqi": avg} if __name__ == "__main__": cities = ["北京", "上海", "广州", "深圳", "成都", "杭州"] pipe = CityAqiPipeline(cities, workers=3) start = time.time() pipe.map_phase() summary = pipe.reduce_phase() print("汇总结果:", summary) print("总耗时: %.2f 秒" % (time.time() - start))
workers 参数可以直观感受并发窗口对耗时的影响。from concurrent.futures import ThreadPoolExecutor, as_completed引入线程池工具,相当于 LangGraph 底层的执行器。with ThreadPoolExecutor(max_workers=self._workers) as pool:创建并发窗口为_workers的线程池,超过窗口的任务会排队等待,这就是“灶台排班”。[pool.submit(fetch_city_aqi, c) for c in self._cities]对每个城市提交一个异步任务,对应 Map 阶段的 Send 派单。for fut in as_completed(futures):每完成一个任务就取一次结果,对应 LangGraph 在 super-step 内持续收集分支输出。self._results.append(fut.result())把每个并行分支的输出收集到列表,对应 Channel 的追加式写入规则。total = sum(item["aqi"] for item in self._results)Reduce 阶段的第一部分:将所有子结果累加,对应自定义 reducer 的求和逻辑。best = max(self._results, key=lambda item: item["aqi"])用max取最优城市,对应“取最大值”风格的自定义合并策略。with ThreadPoolExecutor(...) as pool使用上下文管理器确保所有线程资源在 Map 阶段结束后自动释放,防止资源泄漏。time.sleep(0.3)模拟真实网络请求的耗时,让并行效果可视化地体现为总耗时的显著下降。
常见误区
误区我以为 Map 阶段就是最开始把所有数据切成碎片,然后一次性全部送进并行分支里。如果有一千个子任务,那就一千个分支同时跑。
正解LangGraph 的 Map 阶段更像持续派单的传菜口,而不是一次性倒出的进货仓。你可以用 Send 分批生成子任务,每轮只生成当前并发窗口能处理的量,上一批完成后再派下一批。配合递归限制,可以优雅控制任务总量,防止“任务爆炸”吞掉内存。
为何常见:很多人把 Map-Reduce 理解成 Hadoop 的全量切片模型,忽略了 LangGraph 作为图执行引擎的动态派单能力,误以为分支数等于并发数。
误区既然各分支都在同一个图里,我想让它们共享一个全局计数器或缓存字典,直接读写就行了吧?
正解并行分支直接读写共享可变状态会产生竞态条件,导致结果不可复现。LangGraph 的正确做法是:共享信息通过 Channel 写入,由 Channel 的 reducer 规则决定如何合并多个分支的写入。分支内部修改自己的子状态,跨分支的沟通只能通过图的“公告板”完成,而不能直接抓对方的变量。
为何常见:把单线程编程习惯直接搬进并发模型,忽略了“同时读写”这个在并发世界里最危险的信号。
误区总觉得把 `max_workers` 调大,分支全都拥挤地同时跑,整体速度肯定更快。于是我看到 100 个子任务就把并发窗口调到 50。
正解并发窗口过大会引发资源竞争:线程切换开销、内存占用量暴涨、单个任务变慢,甚至触发执行器的排队雪崩。实际工程中,最合适的窗口值通常接近硬件核数或下游 API 的并发上限,需要通过压测确定。阿姆达尔定律也提醒你:串行部分的耗时不会因为窗口变大而消失。
为何常见:直觉上“更多就是更快”,忽略了真实世界里调度开销和资源瓶颈的存在,这也解释了为什么并行加速比总是低于理论值。
误区有一个分支查询超时了,没关系,其他分支的结果还是好的,让它们继续跑,最后把好的结果汇总出来不就行了?
正解LangGraph 的默认行为是任一分支抛出异常都会终止整个 super-step,图转入失败状态。要容忍部分失败,你必须显式设计降级方案:在分支内捕获异常并返回“失败标记”,或者在 Reduce 阶段过滤掉失败结果。否则,一个分支的错误会像停电一样让整桌宴席停下来。
为何常见:把微服务架构里“单点故障隔离”的经验套用到图执行引擎,忽略了 super-step 的同步屏障语义——一轮内任何一个分支失败都让“齐步走”无法收场。
误区既然并行这么好,我把 Agent 里所有节点都套上 Map-Reduce,是不是整体响应时间都能大幅缩短?
正解只有子任务互不依赖时并行才安全。如果一个分支的输出是另一个分支的输入,或者多个分支需要频繁读写同一份状态,强行并行会引入竞态和一致性灾难。判断标准是“能否画成树”:树状结构适合并行,链状依赖只能串行。把可并行的部分并行,把必须串行的部分老老实实排队,才是正确姿势。
为何常见:被并行的收益诱惑,忽略了任务间的依赖结构分析。贪多求快,反而因状态冲突和重试机制把平均耗时拖得更长。
面试题
Q1. LangGraph 中 Map 阶段的核心职责是什么?
- A.等待所有并行分支完成后汇总结果
- B.把单个输入拆成多个子任务并派发← 正确答案
- C.把子任务的输出整合为单个结果
- D.为每个子任务分配专属执行线程
答案:B。Map 阶段负责“拆菜单”——把输入拆成多个子任务并派发出去,对应选项 B。A 和 C 描述的是 Reduce 阶段的职责,D 误解了 Map 的职责,LangGraph 不直接管理线程分配,线程由底层执行器控制。
Q2. LangGraph 中“super-step 同步”的含义是什么?
- A.所有节点执行完才进入下一轮← 正确答案
- B.单个快节点执行完就立即推进
- C.每轮只允许一个节点执行
- D.节点之间直接共享内存状态
答案:A。super-step 是“齐步走”——当前轮所有被唤醒的并行分支全部执行完毕,框架才推进到下一轮,对应选项 A。B 描述的是异步流水线,C 是串行执行,D 是共享内存通信,都不符合 LangGraph 的状态同步模型。
Q3. 下列哪个场景最适合使用 LangGraph 并行节点?
- A.一个子任务的输出是另一任务的输入
- B.多个分支共享同一个可变计数器
- C.批量文档各自独立做摘要← 正确答案
- D.每个分支需要上一步的中间结果
答案:C。并行节点的前提是子任务互不依赖。批量文档各自独立做摘要天然是“树”状结构,适合并行,对应 C。A 和 D 是链式依赖,B 是共享可变状态的危险设计,都不适合。
Q4. 多个并行分支同时写同一个 Channel,LangGraph 靠什么决定结果形态?
- A.后写的分支自动覆盖先写分支
- B.Channel 的 reducer 写入规则决定← 正确答案
- C.所有写入按字典序自动排列
- D.每个分支单独分配独立 Channel
答案:B。Channel 的写入行为由它的 reducer 规则决定——可以是“保留最后写入”,也可以是“追加成列表”或自定义聚合,对应 B。A 只是其中一种规则而非通用行为,C 和 D 是真实并不存在的机制。
Q5. 关于并发窗口与实际执行速度的关系,哪一项描述正确?
- A.并发窗口越大,执行速度一定越快
- B.并发窗口大小对执行速度没有影响
- C.窗口过大会导致资源竞争拖慢速度← 正确答案
- D.并发窗口只在单核机器上发挥作用
答案:C。并发窗口过大会引入线程切换开销、内存争抢和排队雪崩,反而拖慢整体速度,对应 C。A 忽略了资源瓶颈,B 明显错误,D 误解了并发窗口的作用机制。
Q6. LangGraph 中 Reduce 阶段自定义合并策略的典型做法是什么?
- A.强制所有分支输出格式完全相同
- B.只能使用框架内置的求和规则
- C.编写 Reducer 函数定义合并逻辑← 正确答案
- D.在 Map 阶段提前合并所有输出
答案:C。自定义合并策略就是编写 Reducer 函数,定义如何把多个输出合并成最终结果(如加权平均、置信度投票),对应 C。A 太死板(自定义合并允许不同格式),B 忽略了框架的扩展性,D 把 Reduce 的职责错安在 Map 头上。
Q7. 如果一个并行分支抛出异常,LangGraph 的默认行为是什么?
- A.该分支重试三次后继续执行
- B.只跳过失败分支并保留其他结果
- C.整个 super-step 终止并进入失败← 正确答案
- D.其他分支的结果自动回滚到上轮
答案:C。默认行为是任一分支异常都会终止整个 super-step,图进入失败状态,对应 C。A 重试需要显式配置 retry policy,B 部分降级需要手动设计,D 回滚不是默认行为,只有显式配置状态持久化才可能实现。
Q8. 单个分支成功率 p=0.95,并行分支数 N=10,整体至少一个分支失败的概率约为多少?
- A.0.95
- B.0.50
- C.0.40← 正确答案
- D.0.05
答案:C。用公式 1-(1-p)^N 计算:1-(0.95)^10 ≈ 1-0.598 = 0.402,约等于 0.40,对应 C。A 和 D 分别把单分支成功率误当成总体指标,B 是粗估的错误值。并行分支越多,整体失败概率越高。
Q9. 下列哪些措施可以有效提升 LangGraph 并行执行的整体稳定性?
- A.给关键分支配置重试策略← 正确答案
- B.为分支设置超时保护← 正确答案
- C.任意扩大并发窗口到硬件极限
- D.在 Reduce 阶段做部分降级处理← 正确答案
答案:ABD。重试策略能让暂时失败的分支自动补偿,超时保护防止卡死分支拖住整轮,部分降级让部分成功仍有价值,三者都是稳定性手段,对应 A、B、D。C 盲目扩大窗口反而可能因资源竞争引发更多错误。
Q10. 关于 LangGraph Map-Reduce 模式,下列说法正确的有哪些?
- A.Map 阶段可以通过 Send 动态分批派单← 正确答案
- B.super-step 保证同轮分支结果一致可见← 正确答案
- C.定制 reducer 可以合并异构分支输出← 正确答案
- D.并行加速比可以随窗口无限线性增长
答案:ABC。Send 支持动态分批派单(A 正确),super-step 的同步屏障保证结果一致性(B 正确),自定义 reducer 可以混合不同结构的数据(C 正确)。D 错误,阿姆达尔定律指出加速比存在上限,受串行部分比例约束。