• 微信:WANCOME
  • 扫码加微信,提供专业咨询
  • 服务热线
  • 13215191218
    13027920428

  • 微信扫码访问本页
desc-2
环企首页

LangGraph 中外部控制修改图状态

LangGraph 提供了多种方式从外部(即图执行之外)修改图的状态,这对于实现“人在回路”(Human-in-the-Loop)、状态恢复、调试等功能至关重要。

📌 核心概念

LangGraph 的状态管理基于持久化检查点机制。图在执行过程中会保存状态快照(checkpoint),外部控制就是通过更新、恢复或修改这些检查点来实现的。

1️⃣ 基本状态访问和修改

获取当前状态

# 执行后获取最终状态
result = graph.invoke(initial_state, config)

# 或者单独获取状态快照
state_snapshot = graph.get_state(config)
print(state_snapshot.values)  # 当前状态值
print(state_snapshot.next)    # 下一个要执行的节点

更新状态(从外部注入)

使用 graph.update_state() 可以在图执行过程中注入新状态:

# 更新状态
graph.update_state(
    config,                    # 线程配置
    {"messages": [HumanMessage(content="人工干预")]},  # 要更新的状态
    as_node="human_input"      # 模拟从哪个节点更新
)

2️⃣ 人在回路干预(Human-in-the-Loop)

中断执行,等待人工输入

在图编译时设置中断点:

from langgraph.graph import StateGraph, START, END

# 编译时设置中断节点
graph = builder.compile(
    checkpointer=checkpointer,
    interrupt_before=["assistant"],  # 在 assistant 前中断
    # 或 interrupt_after=["tool_node"]  # 在工具节点后中断
)

外部干预流程

# 第1步:执行图,会在中断点停止
thread_config = {"configurable": {"thread_id": "abc123"}}

for event in graph.stream(
    {"messages": [HumanMessage(content="计算3+5")]},
    config=thread_config,
    stream_mode="values"
):
    print(event)

# 第2步:图会停在中断点,你可以检查和修改状态
state = graph.get_state(thread_config)
print("当前状态:", state.values)
print("下一个节点:", state.next)  # 应该是 ['assistant']

# 第3步:从外部修改状态
graph.update_state(
    thread_config,
    {"messages": [HumanMessage(content="修改后的输入")]},
    as_node="human_input"
)

# 第4步:继续执行
for event in graph.stream(None, config=thread_config, stream_mode="values"):
    print(event)

3️⃣ 使用 Command 进行精细控制

LangGraph 提供了 Command 对象来实现更复杂的控制流:

from langgraph.types import Command

def human_feedback_node(state):
    # 等待人工输入
    user_input = get_user_input()  # 外部获取输入
    return Command(
        update={"messages": [HumanMessage(content=user_input)]},
        goto="assistant"  # 指定下一个节点
    )

Command 的常用参数

疫苗 疫苗 疫苗update 疫苗要合并到状态中的数据 疫苗goto 疫苗指定下一个要执行的节点 疫苗resume 疫苗恢复中断并传入值
参数作用

4️⃣ 状态版本回滚和时间旅行

利用检查点机制,你可以回到历史的任何状态:

# 获取所有历史检查点
history = list(graph.get_state_history(config))
for i, state in enumerate(history):
    print(f"步骤 {i}: {state.values}")

# 回到上一个状态
previous_state = history[1]  # index 0 是最新状态
config = previous_state.config

# 从该状态重新执行
for event in graph.stream(None, config=config):
    print(event)

5️⃣ 完整示例:人工审批流程

from typing import Annotated, Literal
from langgraph.graph import StateGraph, START, END, add_messages
from langgraph.checkpoint.memory import MemorySaver

class ApprovalState(TypedDict):
    messages: Annotated[list, add_messages]
    approved: bool
    feedback: str

def assistant(state: ApprovalState):
    response = llm.invoke(state["messages"])
    return {"messages": [response]}

def human_approval(state: ApprovalState) -> Literal["assistant", "END"]:
    # 这个节点实际上会被中断,由外部决策
    if state.get("approved", False):
        return "assistant"
    return END

# 构建图
builder = StateGraph(ApprovalState)
builder.add_node("assistant", assistant)
builder.add_node("human_approval", human_approval)
builder.add_edge(START, "assistant")
builder.add_edge("assistant", "human_approval")
builder.add_conditional_edges("human_approval", human_approval)

# 编译时设置中断
memory = MemorySaver()
graph = builder.compile(checkpointer=memory, interrupt_before=["human_approval"])

# 执行
config = {"configurable": {"thread_id": "approval_001"}}

# 第一次执行,会在 human_approval 前停止
for event in graph.stream(
    {"messages": [HumanMessage(content="生成报告")]},
    config=config
):
    print(event)

# 外部获取状态,询问用户
state = graph.get_state(config)
print("等待审批的内容:", state.values["messages"][-1].content)

# 用户审批
user_approved = input("是否批准?(y/n): ")

# 外部更新状态
graph.update_state(
    config,
    {"approved": user_approved.lower() == "y"},
    as_node="human_approval"
)

# 继续执行
if user_approved.lower() == "y":
    for event in graph.stream(None, config=config):
        print(event)

📊 方法对比

疫苗 疫苗 疫苗 疫苗graph.get_state() 疫苗获取当前状态 疫苗调试、检查进度 疫苗graph.update_state() 疫苗注入/修改状态 疫苗人工干预、纠正错误 疫苗interrupt_before/after 疫苗设置中断点 疫苗审批流程、数据校验 疫苗Command 疫苗动态控制流向 疫苗分支选择、条件路由 疫苗状态历史回滚 疫苗回到过去状态 疫苗调试、错误恢复
方法用途适用场景

⚠️ 重要注意事项

  1. 需要持久化存储:要支持状态修改和历史查询,必须传入 checkpointer 参数编译图
  2. thread_id 是必需的:每个会话(线程)需要有唯一的 thread_id 才能保存状态
  3. 状态合并规则:使用 update_state 时,新增的状态会按照 Reducer 规则(如 add_messages)合并,而非直接替换
  4. as_node 的意义:模拟状态是从哪个节点发起的更新,影响后续图的执行逻辑
  5. stream(None) 恢复执行:中断后继续执行时,调用 graph.stream(None, config) 即可

💎 总结

外部控制修改图状态的核心方法是:

  • get_state(config):获取当前状态
  • update_state(config, update, as_node):注入新状态
  • interrupt_before/after:设置中断点
  • 状态历史回滚:回到任意历史状态

这为构建需要人工审批、错误恢复、动态修正的复杂 Agent 系统提供了强大的支持。