前面我们已经学习了 Checkpoint、get_state_history()、Replay 和 Fork。
这一篇继续看一个更实用的问题:
如果同一个 SuperStep 中,一个节点执行成功,另一个节点执行失败,修复后恢复运行时,成功节点还会不会重新执行?
LangGraph 中:已经成功的任务结果可以被检查点保存,恢复时直接复用。
1. 一个节点失败后,LangGraph 保存了什么?
假设图结构是:
START
|
node_change_topic
|
+——> node_poem
|
+——> node_joke
|
v
node_output
|
END
其中 node_poem 和 node_joke 属于同一个 SuperStep。
为了模拟失败,可以故意让:
def node_joke(state):
raise Exception("人为中断")
第一次执行时:
node_change_topic 执行成功
↓
node_poem 和 node_joke 并行执行
↓
node_poem:成功
node_joke:失败
↓
图中断
这时查看历史检查点:
history = list(
graph.get_state_history(config)
)
可以看到对应的 tasks 中:
PregelTask(
name="node_poem",
error=None,
result={
"poem": "…"
}
)
而失败节点:
PregelTask(
name="node_joke",
error="Exception('人为中断')",
result=None
)
所以这里可以理解为:
result
=
节点已经成功产生的结果
error
=
节点执行失败时记录的异常
2. 为什么 node_poem 不需要重新执行?
虽然 node_poem 和 node_joke 在同一个 SuperStep 中运行,但其中一个失败,并不会让另一个已经成功的结果全部丢失。
检查点已经知道:
node_poem
成功
result 已保存
node_joke
失败
error 已保存
因此恢复时,LangGraph 可以:
复用 node_poem.result
+
重新执行 node_joke
而不是:
node_poem 再执行一次
node_joke 再执行一次
这对包含大模型调用、API 请求、数据库查询等昂贵操作的工作流尤其重要。
3. 修复 BUG 后如何恢复?
先把 node_joke 修好:
def node_joke(state):
topic = state["topic"]
joke = model.invoke(
[
HumanMessage(
f"写一个关于 {topic} 的笑话"
)
]
).content
return {
"joke": joke
}
然后重新编译图:
new_graph = builder.compile(
checkpointer=checkpointer
)
这里最重要的是:
必须继续使用之前那个 Checkpointer。
因为历史 Checkpoint 就保存在它里面。
如果重新创建:
checkpointer = InMemorySaver()
那就是一个新的空存储器,原来的历史也就找不到了。
恢复时:
res = new_graph.invoke(
None,
config={
"configurable": {
"thread_id": "123"
}
}
)
这里:
input=None
表示:
不再提供新的输入
而是从当前 thread_id 已保存的状态继续
实际恢复结果中:
node_joke
重新执行
node_output
继续执行
node_poem
没有重新执行
最终输出中的诗仍然是第一次 node_poem 成功时生成的内容,说明它的结果被复用了。
4. 失败恢复和 Replay 有什么区别?
这两个概念看起来很像,但目的不同。
Replay:
我主动找到一个历史 Checkpoint
然后从那里重新执行
例如:
graph.invoke(
None,
old_checkpoint.config
)
而失败恢复是:
上一次执行因为异常中断
↓
修复 BUG
↓
继续使用原来的 Checkpointer
↓
通过相同 thread_id 恢复
↓
接着未完成的任务继续执行
可以简单记:
Replay
重新走一次
失败恢复
上次没走完,现在接着走
它还在解决:
任务执行到了哪里,以及哪些计算已经完成。
网硕互联帮助中心



评论前必须登录!
注册