update_state与resume

interrupt 停住之后,人可能不只想点「通过」——还要改字段(修正收件人、拒绝并写原因)。update_state 经 Saver put 再拍一枚 checkpoint(source=updatenext 通常不动);Command(resume=…)get_tuple 续跑。

图 1 update_state 复印一张新快照;resume 再打开它(对应 §4.1)


1. 定位

维度 内容
角色 暂停态下的 state 修补与恢复
输入 → 输出 update_state(values) / Command(resume=x) → 继续图
核心 API graph.update_stateCommand
依赖 LangChain

2. 图拓扑

节点表

节点名 职责 读 State 写 State
draft 生成草稿 draft
review 读人工意见 draft, feedback final

边表

目标 类型
START draft 固定
draft review 固定
review END 固定

interruptinterrupt_before=["review"]


3. invoke 生命周期

1
2
3
4
5
1. draft → draft="v1"
2. interrupt 在 review 前暂停
3. update_state({"feedback": "改用 v2 标题"})
4. invoke(Command(resume={"approved": True}))
5. review 读 feedback + resume → final="v2标题|ok"

4. 原理

4.1 update_state 如何写进 Saver

不是原地改上一枚 checkpoint,而是:

  1. get_tuple(cfg) 取出当前暂停态(含 next=("review",))。
  2. values 按 reducer 合并进 channels。
  3. put 新 idmetadata.source="update",parent 指向旧 id;默认 不改 next
  4. get_state(cfg)get_tuple 应看到新 feedbacknext 仍是 review

as_node="review" 会假装这次写入来自该节点(影响后续边与 writes 归属),调试用。

4.2 resume 如何再读出来

invoke(Command(resume=...), cfg)get_tuple 加载 update 后的最新快照,从 next 继续。本篇 mock 用 state 字段 feedback 承载人工输入;Command(resume=...) 的 payload 也可经 interrupt() 注入(依版本)。

少了第 3 步的 put → resume 读不到人工改动。
第 4 步换 thread_id → 另一棵空树,暂停态丢失。


5. 最小可运行示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from typing import TypedDict

from langgraph.checkpoint.memory import InMemorySaver
from langgraph.graph import StateGraph, START, END
from langgraph.types import Command


class State(TypedDict):
draft: str
feedback: str
final: str


def draft_node(_: State) -> dict:
return {"draft": "v1"}


def review_node(state: State) -> dict:
fb = state.get("feedback") or "none"
return {"final": f"{state['draft']}|{fb}"}


builder = StateGraph(State)
builder.add_node("draft", draft_node)
builder.add_node("review", review_node)
builder.add_edge(START, "draft")
builder.add_edge("draft", "review")
builder.add_edge("review", END)

cp = InMemorySaver()
graph = builder.compile(checkpointer=cp, interrupt_before=["review"])
cfg = {"configurable": {"thread_id": "rev-1"}}

graph.invoke({"draft": "", "feedback": "", "final": ""}, cfg)
graph.update_state(cfg, {"feedback": "approved-by-human"})
out = graph.invoke(Command(resume=True), cfg)
print(out["final"]) # v1|approved-by-human

6. 执行追踪

步骤 draft feedback final
draft 后 v1 “” “”
update_state 后 v1 approved-by-human “”
resume 后 v1 approved-by-human v1|approved-by-human

重要配置参数

参数(API 名) 类型 / 默认值 功能说明 作用与影响 参考起点 / 常用范围 配置指导
update_state(config, values) dict get_tuple 后合并再 put 新 id 默认不改 next feedback 字段 先 get_state 确认
Command(resume=...) Any 再 invoke:get_tuple 后从 next 须同 thread True / dict 与 interrupt 成对
get_state(config) snapshot 确认 update 已落盘 看 values 与 next 审批后 再 resume
as_node str,可选 假装写入属于某节点 改 writes 归属 调试 慎用生产
compile(checkpointer=) 必填 两步都打同一 Saver 无则 update 失败 InMemorySaver 生产换 DB
thread_id str update 与 resume 同一把钥匙 换 ID 丢暂停态 不变 全程同一 cfg

7. 易踩坑

  1. 未 update_state 直接 resume:review 读不到人工输入。
  2. 换 thread_id resume:找不到暂停 checkpoint。
  3. update_state 后改 next:需高级 API;一般只改 values。

小结

  • update_state = 再 put 一枚 source=update 的新 checkpoint,next 通常不动。
  • resume = get_tuple 最新快照后从 next 继续。
  • interrupt_before 组合实现可编辑 HITL。

参考链接

-------------本文结束感谢您的阅读-------------