LangGraph节点的并行化处理

在大模型应用向多智能体协作、复杂任务拆解演进的当下,LangGraph作为专注于构建状态ful、可交互智能体系统的框架,其并行节点执行机制成为突破性能瓶颈的关键。多智能体系统中,大量独立任务(如多源数据检索、并行分析处理)若按线性顺序执行,会因等待单个任务完成而浪费资源,显著降低响应速度。而LangGraph的并行化能力,通过Fan-out(多输出,又称为扇出)与Fan-in(多输入,又称为扇入)的核心模式,让多个节点在同一时间步(step)同步运行,再将结果高效汇聚,从根本上提升了复杂任务的执行效率。

本节将聚焦这一核心技术,从基础概念到实战落地,层层拆解并行执行的实现逻辑与关键细节。首先通过线性执行与并行执行的对比示例,直观呈现并行化的优势与潜在问题------当多个节点同时更新同一状态时,易出现状态冲突报错。随后深入解析解决方案:利用Python的Annotated类型提示搭配Reducer函数,定义状态合并规则,解决并行更新的冲突问题。

4.2.1 LangGraph中的并行化实战

LangGraph中的并行执行是指多个节点在同一时间步内同时运行,而非遵循先后顺序依次执行,其核心依托 Fan-out与Fan-in两种模式实现。

  • Fan-out指一个节点的输出会同步分发至多个并行节点,让这些节点各自独立开展任务。
  • Fan-in则是待多个并行节点执行完成后,将它们的输出集中汇聚到同一个节点,实现结果的整合与后续处理。
1. 一个简单的顺序执行示例

下面是我们实现的一个简单的顺序执行示例:

#顺序执行代码

from pydantic import BaseModel

class AgentState(BaseModel):

node_state:str

class ReturnNodeValue:

def init(self, node_secret: str):

self._value = node_secret

def call(self, state: AgentState):

print(f"Adding {self._value} to {state.node_state}")

return {"node_state": self._value}

from langgraph.graph import StateGraph,MessagesState,START,END,add_messages

graph_builder = StateGraph(AgentState)

graph_builder.add_node("a", ReturnNodeValue("I'm A"))

graph_builder.add_node("b", ReturnNodeValue("I'm B"))

graph_builder.add_node("c", ReturnNodeValue("I'm C"))

graph_builder.add_node("d", ReturnNodeValue("I'm D"))

graph_builder.set_entry_point("a")

graph_builder.add_edge("a", "b")

graph_builder.add_edge("b", "c")

graph_builder.add_edge("c", "d")

graph_builder.add_edge("d", END)

graph = graph_builder.compile()

if name == 'main':

replys = graph.stream(input={"node_state":""})

for reply in replys:

print(reply)

print("----------------------")

以上代码中,我们顺序对实例化节点进行了处理,这段代码展示了LangGraph中顺序执行(Sequential Execution)的基础实现,核心是构建一个按固定顺序依次执行的节点流程。

其中,ReturnNodeValue类是一个可调用类(通过实现__call__方法,让类的实例可以像函数一样被调用),用于定义节点的执行逻辑。

  • 每个节点实例化时传入一个字符串(如"I'm A"),作为该节点的"标识"。
  • call 方法接收当前状态state,执行两个核心操作:(1)打印日志:显示当前节点正在将自身标识添加到现有状态中。(2)返回状态更新:通过字典{"state": self._value}告诉LangGraph,需要将AgentState中的node_state字段更新为当前节点的标识。

输出结果如下:

Adding I'm A to

{'a': None}


Adding I'm B to

{'b': None}


Adding I'm C to

{'c': None}


Adding I'm D to

{'d': None}


可以看到,上面代码的流程严格按a→b→c→d的顺序执行,每个节点必须等待前一个节点完成后才会启动。每个节点执行时,node_state会被更新为当前节点的标识(如a执行后node_state变为"I'm A",b 执行后变为"I'm B"等),体现了"顺序覆盖"的特点。

也就是说,这段代码的核心是展示LangGraph中顺序执行的基础模式:通过定义状态结构、节点逻辑,再用边连接节点形成固定顺序的流程,最终实现节点按先后顺序依次执行,前一个节点的输出(状态更新)会作为后一个节点的输入。这种模式适合任务之间有依赖关系、需要严格按步骤执行的场景。

2. 并行执行的代码

对以上代码进行修正,将顺序执行改为并行执行,代码如下:

from pydantic import BaseModel

class AgentState(BaseModel):

node_state:str

class ReturnNodeValue:

def init(self, node_secret: str):

self._value = node_secret

def call(self, state: AgentState):

print(f"Adding {self._value} to {state.node_state}")

return {"node_state": self._value}

from langgraph.graph import StateGraph,START,END

graph_builder = StateGraph(AgentState)

graph_builder.add_node("a", ReturnNodeValue("I'm A"))

graph_builder.add_node("b", ReturnNodeValue("I'm B"))

graph_builder.add_node("c", ReturnNodeValue("I'm C"))

graph_builder.add_node("d", ReturnNodeValue("I'm D"))

并行流程:a 输出到 b和c,然后输入 d

graph_builder.add_edge(START, "a")

graph_builder.add_edge("a", "b") # a → b

graph_builder.add_edge("a", "c") # a → c(并行)

graph_builder.add_edge("b", "d") # b → d

graph_builder.add_edge("c", "d") # c → d

graph_builder.add_edge("d", END)

graph = graph_builder.compile()

if name == 'main':

replys = graph.stream(input={"node_state":""})

for reply in replys:

print(reply)

print("---------")

我们先从执行的构建图来看:

START → a → b → d → END

c → d

从设计上看,节点a执行完成后,同时触发b和c两个节点(而非按顺序执行),这两个节点在同一时间步并行运行。而节点b和c都执行完成后,才会共同触发节点d(d需等待b和c全部结束),最终d执行完成后到达END。

输出结果如下:

Adding I'm A to

{'a': {'node_state': "I'm A"}}


Adding I'm B to I'm A

Adding I'm C to I'm A

{'b': {'node_state': "I'm B"}}


{'c': {'node_state': "I'm C"}}

langgraph.errors.InvalidUpdateError: At key 'node_state': Can receive only one value per step. Use an Annotated key to handle multiple values.

For troubleshooting, visit: https://docs.langchain.com/oss/python/langgraph /errors/INVALID_CONCURRENT_GRAPH_UPDATE

此时结果报错,这是由于同一个时间步(step)内,b和c都试图更新state这个键。LangGraph不知道如何合并这两个更新,所以抛出InvalidUpdateError错误。

4.2.2 并行化实战中Reduce的妙用

前面尝试通过LangGraph实现并行执行时,最终因状态更新冲突导致执行失败。其核心原因在于:并行节点(如b和c)处于同一时间步,且同时对AgentState中的同一个状态字段node_state发起更新,LangGraph无法自动判断如何合并这些并发更新,进而引发冲突。

要解决这一问题,关键在于为状态字段指定Reducer机制------通过定义明确的状态合并规则,让框架知道如何处理多个并行节点的更新请求。前文讲解状态更新时提到,add_messages本质是一种列表拼接式的Reducer工具,可将多个并行更新的结果有序拼接成列表,恰好适配当前场景。

我们重新定义AgentState,为node_state字段绑定add_messages作为Reducer,具体代码如下:

from langgraph.graph import add_messages

from pydantic import BaseModel

from typing import Annotated,List

class AgentState(BaseModel):

node_state:Annotated[List,add_messages]

这里我们将node_state字段指定为Annotated[List, add_messages]类型,其中add_messages作为Reducer工具,能自动将多个并行节点的更新结果有序拼接成列表,让框架清晰知晓如何合并并发更新,从而顺利规避冲突,保障并行执行的正常推进。完整的并行代码如下:

from langgraph.graph import add_messages

from pydantic import BaseModel

from typing import Annotated,List

class AgentState(BaseModel):

node_state:Annotated[List,add_messages]

class ReturnNodeValue:

def init(self, node_secret: str):

self._value = node_secret

def call(self, state: AgentState):

print(f"Adding {self._value} to {state.node_state}")

return {"node_state": self._value}

from langgraph.graph import StateGraph,START,END

graph_builder = StateGraph(AgentState)

graph_builder.add_node("a", ReturnNodeValue("I'm A"))

graph_builder.add_node("b", ReturnNodeValue("I'm B"))

graph_builder.add_node("c", ReturnNodeValue("I'm C"))

graph_builder.add_node("d", ReturnNodeValue("I'm D"))

并行流程:a输出到b和c,然后输入d

graph_builder.add_edge(START, "a")

graph_builder.add_edge("a", "b") # a → b

graph_builder.add_edge("a", "c") # a → c(并行)

graph_builder.add_edge("b", "d") # b → d

graph_builder.add_edge("c", "d") # c → d

graph_builder.add_edge("d", END)

graph = graph_builder.compile()

if name == 'main':

replys = graph.stream(input={"node_state":""})

for reply in replys:

print("---------")

此时输出如下:

Adding I'm A to [HumanMessage(content='', additional_kwargs={}, response_metadata={}, id='bc75b85f-ea1b-40ef-aa5d-3c32ba9173fc')]


Adding I'm B to [HumanMessage(content='', additional_kwargs={}, response_metadata={}, id='bc75b85f-ea1b-40ef-aa5d-3c32ba9173fc'), HumanMessage(content="I'm A", additional_kwargs={}, response_metadata={}, id='583f933a-e985-45b4-8a6c-9de9710ba188')]

Adding I'm C to [HumanMessage(content='', additional_kwargs={}, response_metadata={}, id='bc75b85f-ea1b-40ef-aa5d-3c32ba9173fc'), HumanMessage(content="I'm A", additional_kwargs={}, response_metadata={}, id='583f933a-e985-45b4-8a6c-9de9710ba188')]---------


Adding I'm D to [HumanMessage(content='', additional_kwargs={}, response_metadata={}, id='bc75b85f-ea1b-40ef-aa5d-3c32ba9173fc'), HumanMessage(content="I'm A", additional_kwargs={}, response_metadata={}, id='583f933a-e985-45b4-8a6c-9de9710ba188'), HumanMessage(content="I'm B", additional_kwargs={}, response_metadata={}, id='98879729-67c9-4d1a-b048-6223f5dcdd4e'), HumanMessage(content="I'm C", additional_kwargs={}, response_metadata={}, id='22cbc05d-7732-46a7-951a-7668aa44ddad')]


可以看到,这里我们使用并行执行代码成功规避了状态更新冲突,输出结果清晰呈现了预期效果:节点a先执行,其结果被添加到初始状态的node_state列表中;随后节点b和c在同一时间步并行运行,二者读取到的是相同的a执行后的状态,且各自的输出通过add_messages Reducer有序拼接至node_state;待b和c均执行完成后,节点d启动,读取到包含初始状态、a、b、c所有结果的完整列表并完成自身输出的拼接。整个过程中,node_state以HumanMessage列表形式存储所有并行节点的更新结果,add_messages有效实现了并发更新的有序合并,彻底解决了之前的冲突问题,确保了"a扇出至b、c并行执行,再共同扇入至d"的并行流程顺畅推进。

相关推荐
006_2 小时前
python 全球多语言情感分析-模型版
人工智能·自然语言处理
DO_Community2 小时前
如何使用DigitalOcean Gradient 平台上的无服务器推理
人工智能·aigc·ai编程·ai推理
ws2019072 小时前
花城聚智:2026广州新能源汽车技术与热管理展为何成产业升级关键节点?
大数据·人工智能·科技·物联网·汽车
康康的AI博客2 小时前
向量数据库选型指南:AI 数据底座怎么选不踩坑
数据库·人工智能
happyprince2 小时前
2026年03月26日全球AI前沿动态
人工智能
YoanAILab2 小时前
AI 推理系统架构怎么选?图像生成与文本生成的分层选型思路(ComfyUI / Dify / vLLM / Triton)
人工智能·系统架构·comfyui·dify·vllm·ai工程
林森见鹿2 小时前
OpenClaw 飞书 ACP 会话配置详解
人工智能
Alter12302 小时前
华为吴辉:AI正在重构生产系统,“大增量时代”已经到来
人工智能·华为·重构