计算机王头像

164.Agent-LangGraph-更新状态(修改检查点)

摘要:本文围绕 LangGraph 中基于检查点(Checkpoint)的状态更新与时间旅行能力展开,重点讲解 graph.update_state() 的传值机制与参数含义。文章先说明上一节代码中 input 只能传 None 的局限,再结合官方文档与检查点记录截图,演示如何从指定节点(如节点 2)开始执行,并解释更新后会在旧检查点基础上创建新的分支而非覆盖原记录。随后介绍 config、values、as_node 三个参数的作用,以及如何借助 update_state 实现人机交互式的草稿修改。最后给出完整的 Python 示例代码,涵盖状态结构定义、归约函数(reducer)绑定、线性流程构建、检查点历史查看与分叉执行,帮助读者掌握在历史快照上修改状态并继续运行的实践方法。

内容参考于:图灵AI大模型全栈

如下图红框是上一节的代码,它的input传入的是None,这个input它没办法传值,只能传None,如果想传值接着往下看

LangChain官网说明

Use time-travel - Docs by LangChain

它传值是用的 graph.update_state() 这个函数,如下图是一个检查点记录的内容,现在要从2开始执行

它会变成下图的样子,黑色的是原本旧的检查点,它会在节点2开始创建一个新的分支(检查点),旧的检查点还是会保存,就是说它并不是在原本的检查点里改的,它是创建出新的检查点分支

它的参数

config:线程id(用来指定要更新哪一个检查点列表),还可以传递一个检查点id(checkpoint_id)

values:它是用来更新状态的值,它的值会传递给归并函数(reducer),如果没有设置归并函数那么会覆盖

as_node:通过它可以知道把更新的内容传递给谁

它可以进行人机交互,就是我们给用户一个草稿,如果用户觉得草稿不满意,可以编辑草稿

代码说明:

如下图红框,它的逻辑 就是从创建任务开始执行,然后再执行更新任务,然后再执行准备任务

如下图运行的效果图:注意它是倒着的,start是在最后一个

如下图红框,我们通过 update_state 对检查点进行了修改,可以通过它进行人机交互,用户通过对节点输入新的内容,然后我们通过 update_state 更新到节点对应的检查点里

代码

# ============================================================
# 模块导入
# ============================================================
from langgraph.graph import StateGraph, START, END  # 状态图构建器、流程起止标记
from langgraph.checkpoint.memory import InMemorySaver  # 内存版检查点持久化器
from typing_extensions import TypedDict  # 结构化字典类型定义
from typing import Annotated  # 类型元数据注解,用于绑定状态归约规则
from operator import add  # Python标准运算符,此处作为列表拼接的归约函数

# ============================================================
# 定义全局任务状态结构
# ============================================================
class TaskState(TypedDict):
    task_id: str       # 任务唯一标识
    title: str         # 任务标题
    assignee: str      # 任务负责人
    priority: int      # 优先级数值
    # 【核心机制】评论字段使用Annotated绑定add归约规则
    # 原理:任何节点返回的comments列表,不会覆盖原有列表,而是自动拼接追加
    # 效果:所有节点产生的评论会按执行顺序累加到总评论列表中
    comments: Annotated[list, add]
    status: str        # 任务当前状态

# ============================================================
# 业务节点定义
# ============================================================
def create_task(state: TaskState):
    """节点1:创建任务,初始化任务状态和第一条评论"""
    return {
        "status": "进行中",  # 更新状态为进行中
        # 因为有add归约规则,这个单元素列表会自动追加到总评论列表
        "comments": [f"任务 '{state['title']}' 已创建,分配给 {state['assignee']}"]
    }

def update_task(state: TaskState):
    """节点2:更新任务状态"""
    return {
        "status": "已更新",
        # 同样走归约规则,追加新评论
        "comments": ["任务状态已更新"]
    }

def plan_task(state: TaskState):
    """节点3:标记任务准备完成"""
    return {
        "status": "已准备",
        "comments": [f"任务 '{state['title']}' 已准备"]
    }

# ============================================================
# 构建线性任务流程
# ============================================================
workflow = StateGraph(state_schema=TaskState)
# 注册三个业务节点
workflow.add_node("create", create_task)
workflow.add_node("update", update_task)
workflow.add_node("complete", plan_task)

# 定义纯线性执行链路:START → create → update → complete → END
workflow.add_edge(START, "create")
workflow.add_edge("create", "update")
workflow.add_edge("update", "complete")
workflow.add_edge("complete", END)

# 绑定检查点持久化器
# 【机制】图每跑完一个节点,就自动把当前全量状态存为一个不可变的Checkpoint快照
checkpointer = InMemorySaver()
graph = workflow.compile(checkpointer=checkpointer)

# ============================================================
# 第一次完整执行流程
# ============================================================
# 配置会话标识thread_id:同一thread_id下的所有检查点归为一组,不同会话完全隔离
config = {"configurable": {"thread_id": "task_001"}}

# 传入初始任务状态,从START跑完整条链路
result = graph.invoke({
    "task_id": "T001",
    "title": "开发注册功能",
    "assignee": "张三",
    "priority": 1,
    "comments": [],  # 初始评论为空列表
    "status": "待分配"
}, config)

print("=== 任务创建完成 ===")
print(f"状态: {result['status']}")
print("评论:", result['comments'])

# ============================================================
# 查看历史检查点列表
# 【执行后会生成4个快照】
# 1. 初始状态快照:START刚启动,next=("create",)
# 2. create节点跑完:next=("update",) (我们后面要做分叉的目标点)
# 3. update节点跑完:next=("complete",)
# 4. complete节点跑完:next=() 流程结束
# ============================================================
history = list(graph.get_state_history(config))
for i, checkpoint in enumerate(history):
    print("\n--------------------------------")
    print("Checkpoint:", i + 1)
    print("checkpoint_id:", checkpoint.config["configurable"]["checkpoint_id"])
    # 长文本截断展示,避免控制台输出过长
    strvalue = str(checkpoint.values)
    if len(strvalue) > 200:
        print("当前 State:", strvalue[:200])
    else:
        print("当前 State:", strvalue)
    print("下一步执行:", checkpoint.next)

# ============================================================
# 找到分叉起点:create节点刚执行完,下一步准备跑update节点的快照
# 匹配条件:next == ("update",) —— 代表当前快照状态是create跑完的结果,下一步计划执行update
# ============================================================
before_joke = next(s for s in history if s.next == ("update",))

# ============================================================
# 【核心API演示】update_state:修改历史检查点,生成分支
# 【机制说明】
# 1. 不是直接修改原来的旧快照(旧快照永远保留,不会被覆盖)
# 2. 而是在指定的快照位置,应用传入的状态更新,生成一个全新的分叉快照
# 3. 更新内容会走State定义里的归约规则:comments字段是追加,不是覆盖整个列表
# 4. 返回值是新生成的分叉快照的config,后续invoke就基于这个新快照跑
# 【典型场景】人工介入纠错、审批通过后修改状态、节点执行结果不对时中途修改参数重跑
# ============================================================
fork_config = graph.update_state(
    before_joke.config,  # 要基于哪个历史快照做分叉
    {
        # 注意:传入的是单元素列表,因为有add归约规则
        # 不是把原来的comments整个替换成这条内容,而是把这条追加到原有评论后面
        "comments": ["任务,已经开发完成"]
    }
)

# ============================================================
# 从修改后的分叉快照继续执行
# 【执行效果】
# 不会重跑前面的create节点,直接从next指向的update节点开始跑:
# 1. update节点执行:追加"任务状态已更新"评论
# 2. complete节点执行:追加"任务已准备"评论
# ============================================================
final_result = graph.invoke(None, fork_config)

print("\n\n")
print("#" * 70)
print("查看使用update_state之后的全部检查点")
print("#" * 70)

# 再次拉取历史检查点
# 【机制】新执行产生的检查点会追加到历史里,原来的旧时间线快照全部保留
# 相当于在原来的执行链上,多了一条改过评论的新时间线分支
new_history = list(graph.get_state_history(config))

for i, checkpoint in enumerate(new_history):
    print("\n--------------------------------")
    print("Checkpoint:", i + 1)
    print("checkpoint_id:", checkpoint.config["configurable"]["checkpoint_id"])
    strvalue = str(checkpoint.values)
    if len(strvalue) > 200:
        print("当前 State:", strvalue[:200])
    else:
        print("当前 State:", strvalue)
    print("下一步执行:", checkpoint.next)


求求了

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/qq_36301061/article/details/169732062

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--