- LangGraph 入门基础全解析
- LangGraph中的Reducer是什么
- LangGraph 核心概念详解:从编译到可视化
- LangGraph实战教程:一文搞懂图的状态(State)管理
- LangGraph实战教程:状态管理与
graph.invoke入参深度解析 - LangGraph高级教程:Multi Schema多状态管理详解
- LangGraph实战教程:预定义状态MessagesState与AgentState
- LangGraph实战教程:控制流详解
一、场景引入
想象这样一个场景:你的图里有好几个节点并行执行任务,比如一个写诗、一个写笑话。等它们都完成后,你想做个审计------检查一下诗和笑话是不是都生成了。
通常的做法是:给每个任务节点都连一条线到审计节点。
但如果任务节点很多,或者图结构很复杂,这种做法就很麻烦。
有没有更简单的办法?有!用 defer=True。
二、defer到底是什么?
defer的意思是"推迟"。在LangGraph中,给节点加上 defer=True,就相当于告诉框架:
"这个节点先别急着执行,等其他所有常规节点都跑完了,再让它上场。"
ini
builder.add_node("audit_node", audit_node, defer=True)
就这么简单的一行,审计节点就会自动排到最后执行。
三、一个完整的例子
下面这个图有三个节点:一个写诗、一个写笑话、一个做审计。
python
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
from langchain.messages import HumanMessage
from langchain_deepseek import ChatDeepSeek
from loguru import logger
from dotenv import load_dotenv
load_dotenv(override=True)
model = ChatDeepSeek(
model="deepseek-v4-flash",
extra_body={"thinking": {"type": "disabled"}}
)
class OverAllState(TypedDict):
topic: str
poem: str
joke: str
def node_a(state: OverAllState) -> OverAllState:
"""生成诗歌"""
poem = model.invoke([HumanMessage(f"写一首关于 {state['topic']} 的七言绝句")]).content
return {"poem": poem}
def node_b(state: OverAllState) -> OverAllState:
"""生成笑话"""
joke = model.invoke([HumanMessage(f"写一个关于 {state['topic']} 的笑话")]).content
return {"joke": joke}
def audit_node(state: OverAllState) -> OverAllState:
"""审计节点:在所有任务完成后执行"""
logger.info(
f"任务全部完成,"
f"诗 {'✅ 已生成' if state.get('poem') else '❌ 未生成'},"
f"笑话 {'✅ 已生成' if state.get('joke') else '❌ 未生成'}"
)
return {}
# 构建图
builder = StateGraph(state_schema=OverAllState)
builder.add_node("node_a", node_a)
builder.add_node("node_b", node_b)
builder.add_node("audit_node", audit_node, defer=True) # 👈 关键:defer=True
# 所有节点都从START出发
builder.add_edge(START, "node_a")
builder.add_edge(START, "node_b")
builder.add_edge(START, "audit_node") # 虽然也从START出发,但不会立即执行
builder.add_edge("node_a", END)
builder.add_edge("node_b", END)
builder.add_edge("audit_node", END)
graph = builder.compile()
res = graph.invoke({"topic": "布偶狗"})
print(res)
输出结果:
css
2026-05-28 17:07:09.618 | INFO | 任务全部完成,诗 ✅ 已生成,笑话 ✅ 已生成
{
'topic': '布偶狗',
'poem': '《咏布偶狗》\n绸身棉腑自憨娇...',
'joke': '这是一个关于布偶狗的笑话:\n有一只布偶狗...'
}
四、执行流程
这个图的执行过程分为三步:
第一步:常规节点先跑
sql
START → node_a(写诗)→ 完成 ✅
START → node_b(写笑话)→ 完成 ✅
START → audit_node(⚠️ 被延迟,先不动)
node_a和 node_b正常执行,audit_node虽然也被触发了,但因为 defer=True,它先"睡一会儿"。
第二步:常规节点全部完成
node_a和 node_b都跑完了,状态中也写入了诗和笑话。
此时常规流程结束。
第三步:延迟节点被唤醒
audit_node(审计)→ 执行 ✅
audit_node醒来,读取状态------这时候诗和笑话都已经有了,所以它能正确地做审计。
五、defer到底解决了什么问题?
没有 defer 的时候:
如果你想在所有任务完成后做审计,需要这样画图:
sql
START → node_a → audit_node → END
START → node_b → audit_node → END
每个任务节点都要单独连到审计节点。如果任务有10个,就要连10条线。
有 defer 的时候:
sql
START → node_a → END
START → node_b → END
START → audit_node → END (defer=True,自动最后执行)
代码更简洁,结构更清晰,而且不管加多少任务节点,审计节点都会自动排到最后。
六、defer适合做什么?
| 场景 | 是否适合 | 说明 |
|---|---|---|
| 日志记录 | ✅ 非常适合 | 记录哪些节点执行了,耗时多久 |
| 审计检查 | ✅ 非常适合 | 检查所有任务是否按要求完成 |
| 结果汇总 | ✅ 适合 | 把各个节点的输出汇总到一起 |
| 收尾清理 | ✅ 适合 | 关闭文件、释放资源等 |
| 核心业务逻辑 | ❌ 不适合 | defer节点出错不会回滚前面的结果 |
七、一句话总结
defer=True就像给节点贴了个"压轴出场"的标签------所有常规节目演完了,它才登台谢幕。
八、相关面试题
面试题1:defer=True和把审计节点放在最后位置有什么区别?
打个比方:
- 放在最后位置:就像排队,你必须站在最后一个人的后面
defer=True:就像买了"免排队"票,不管前面有多少人,你都最后上场
放在最后位置需要给每个前驱节点都连线,而 defer=True只需要设置一个参数。
面试题2:如果我有两个 defer=True的节点,谁先执行?
答案: 不确定。它们会在同一个时刻被唤醒,谁先执行取决于调度顺序。
如果两个延迟节点之间有依赖关系(比如B需要A的结果),就不要都用 defer=True,而是用普通边建立先后关系。
面试题3:defer节点里出错了怎么办?
答案: 常规节点的结果已经保存了,不会回滚。所以不要把关键的业务逻辑放在 defer节点里。
举个例子:如果 defer节点是做日志记录的,日志写失败了没关系,诗和笑话还在。但如果 defer节点是做数据校验的,校验失败了但数据已经生成,你就需要额外处理这个异常。
面试题4:defer节点能不能更新状态?
答案: 可以。defer节点也可以返回状态更新。只不过它更新完之后,通常不会有其他节点再被触发了(除非还有其他 defer节点)。
希望这个通俗版的教程能帮你轻松掌握 defer的用法!