跳到主要内容

文档索引

在以下地址获取完整的文档索引:https://docs.langchain.org.cn/llms.txt

在进一步探索之前,请使用此文件发现所有可用页面。

生产环境中的软件需要迭代。新的需求、错误修复和重构最终都会落实到你的图(graph)代码中。由于 LangGraph 使用最新部署的图代码来运行针对现有线程的持久化状态,因此你发布的每一次更改,对于现有检查点(checkpoints)而言,实际上都是一种向后兼容的 API 变更。 与那些将运行绑定到其启动时代码版本的流程引擎不同,LangGraph 会立即将最新的图代码应用到每一个线程中,无论是新线程还是从检查点恢复的线程。这非常方便:错误修复无需繁琐流程即可传播到运行中的对话和智能体中。但这也意味着你必须考虑每一次更改如何与在上一个代码版本下启动的运行进行交互。 需要注意三类兼容性问题,按你可能遇到的顺序排列如下:
  1. 技术兼容性:最常见的问题;新代码必须能够加载并针对现有状态(State)执行。
  2. 业务兼容性:较少见;现有运行应继续遵循旧的业务逻辑,即使代码已经更改。
  3. 非确定性:仅适用于函数式 API
关于运行时默认支持哪些图拓扑和状态更改的简要总结,请参阅图迁移。本页其余部分介绍了当更改超出支持范围时可以采用的模式。

技术兼容性

技术兼容性相当于微服务中 API 的破坏性变更。这里的“API”是指你的图代码与检查点库为现有线程所持久化数据之间的契约。当线程恢复时,LangGraph 会反序列化已保存的状态,按名称将其分发给节点,并期望该节点返回符合状态模式的值。 常见的技术破坏情况:
  • 重命名或删除节点,此时线程正暂停在该节点或即将进入该节点(例如在 interrupt 处,或通过仍指向旧名称的检查点条件边)。在恢复时,LangGraph 找不到该名称的节点,导致运行失败。恢复运行的起点是执行停止所在的节点开头,因此如果节点丢失,运行将无处恢复。
  • 重命名或删除状态键(State key),而旧检查点仍包含该键,或者下游节点仍需读取该键。
  • 收紧状态字段,例如将 Optional 字段变为必需,缩小类型范围,或添加新的无默认值的必需字段。现有的检查点将无法满足新的架构。
边拓扑本身不会在检查点中持久化。在现有节点之间添加、删除或重定向边对于运行中的线程是安全的。根据图迁移的总结,唯一会导致中断线程崩溃的拓扑更改是重命名或删除节点。
  • 将新状态字段添加为 NotRequired(或 Optional[...] = None),以便旧检查点仍然有效。
    from typing import NotRequired
    from typing_extensions import TypedDict
    
    class State(TypedDict):
        messages: list
        summary: NotRequired[str]
    
  • 将删除视为弃用。即使没有节点读取该字段,也要在状态中保留定义至少一个流转周期,以便现有检查点继续加载。
  • 通过先添加后删除的方式进行重命名。在旧字段或节点旁边添加新的,在弃用期内同时写入或路由到两者,一旦确认没有任何运行中的线程依赖旧版本,再将其移除。
  • 保持节点函数对未知键的容错性。TypedDict 在运行时会忽略多余的键,因此除非节点显式读取缺失的键,否则来自旧代码版本的残留状态不会引发错误。
  • 在部署到生产环境之前,使用时间旅行graph.get_state 在预发布环境中对现有线程进行抽查。

检测运行中的线程

在删除节点、重命名状态键或进行其他旧线程无法容忍的更改之前,你需要知道是否还有线程正停留在你即将丢弃的代码版本上。LangGraph 本身不会维护线程状态的搜索索引,因此答案取决于你的图运行位置。 如果你部署到 LangSmith 使用 Agent Server 的线程搜索来按状态过滤。status 字段接受 idlebusyinterruptederror,因此你可以批量查询 interruptedbusy 线程,并可选择通过元数据过滤器进一步缩小范围。请参阅按线程状态过滤列出线程 在任何 LangGraph 运行的地方。 使用 LangSmith 追踪来监控生产环境中哪些节点正在被进入或退出。这是确认节点或状态字段在任何活动代码路径中是否仍然可达的最可靠信号。 当你已经拥有一个 thread_id 时。 直接检查该线程: 如有疑问,请保留已弃用的节点或字段,直到 Agent Server 线程列表和追踪显示对其不再有任何活动为止。

业务兼容性

有时更改在技术上是有效的(每个现有的检查点仍然可以加载,每个节点仍然可以解析),但新图的含义与旧图不同。新行为对于新线程来说是正确的,但你不希望将其追溯性地应用于在旧逻辑下启动的线程。 例如,假设你的图运行流程为 intake → triage → respond,并且你决定在 triagerespond 之间插入一个名为 policy_check 的新步骤:
  • 已经通过 triage 的线程应继续直接执行 respond(旧流程)。
  • 新线程应运行完整的全新流程。
推荐的模式是在线程启动时在状态中记录相关的行为版本,然后使用条件边进行分支处理。
from typing import NotRequired
from typing_extensions import TypedDict

from langgraph.graph import END, START, StateGraph


class State(TypedDict):
    request: str
    flow_version: NotRequired[int]
    response: NotRequired[str]


def intake(state: State) -> dict:
    # Stamp new threads with the current flow version. Existing threads
    # that resume past `intake` keep whatever value was already saved.
    return {"flow_version": state.get("flow_version", 2)}


def triage(state: State) -> dict: ...
def policy_check(state: State) -> dict: ...
def respond(state: State) -> dict: ...


def after_triage(state: State) -> str:
    if state.get("flow_version", 1) >= 2:
        return "policy_check"
    return "respond"


builder = StateGraph(State)
builder.add_node("intake", intake)
builder.add_node("triage", triage)
builder.add_node("policy_check", policy_check)
builder.add_node("respond", respond)
builder.add_edge(START, "intake")
builder.add_edge("intake", "triage")
builder.add_conditional_edges("triage", after_triage, ["policy_check", "respond"])
builder.add_edge("policy_check", "respond")
builder.add_edge("respond", END)

graph = builder.compile()
triage 后恢复的旧线程会从保存的状态中读取 flow_version(或回退到 v1 默认值)并跳过 policy_check。新线程从 intake 开始,被标记为 flow_version=2,并运行新路径。一旦所有 v1 线程完成,你就可以移除版本标志和条件边。 此模式仅在你在线程启动时(在任何需要版本控制的分支之前)设置版本时才有效。稍后设置意味着现有线程在需要它时不会有此标记。

非确定性

此类别仅适用于函数式 API图 API 在恢复时会在节点边界重新进入,因此节点代码不会像 Temporal 风格的工作流那样从函数开头“重放”。 相反,函数式 API 在运行恢复时会从开头重放 @entrypoint 的主体,使用缓存的 @task 结果来跳过已完成的工作。以下两类更改会破坏此模型:
  • 添加、删除或重新排序 @task 调用或 interrupt 调用(发生在恢复点之前)。LangGraph 通过重放中的位置来匹配缓存结果和恢复值,因此改变位置会导致错误的缓存值被重放给不同的调用。
  • @task 之外引入非确定性操作,例如 time.time()random.random() 或内联在入口点主体中的网络调用。在重放时,这些操作产生的值与第一次运行时不同,这可能会改变控制流。
关于更深入的探讨和示例,请参阅函数式 API 指南中的确定性常见陷阱 如果你需要对有运行中任务的 @entrypoint 进行非琐碎的代码更改,最安全的选择是:
  • 在部署更改之前,让运行中的任务自然结束。
  • 将任何新逻辑包装在新的 @task 中,以便其结果独立进行检查点处理。
  • langgraph.json 中为新行为注册一个新的入口点,并将新线程路由到该入口点。

© . This site is unofficial and not affiliated with LangChain, Inc.