> ## Documentation Index
> Fetch the complete documentation index at: https://langchain-docs-zh.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 向后兼容性

> 在生产环境中更新 LangGraph 图代码，而不会中断正在运行的运行。

软件需要在生产环境中进行变更。新的需求、错误修复和重构最终都会落实到你的图代码中。由于 LangGraph 对最新部署的图执行已[持久化](https://langchain-docs-zh.mintlify.site/langchain/oss/langgraph/persistence.md)的现有线程状态，你发布的每个变更都实际上是对现有检查点的向后兼容 API 变更。

与将运行固定到其启动时代码版本的流程引擎不同，LangGraph 会立即将最新图应用于*每个*线程，包括新线程和从检查点恢复的线程。这很方便：错误修复可以无缝传播到正在进行的对话和智能体中。这也意味着你必须推理每个变更如何与在旧代码版本下启动的运行交互。

需要关注的兼容性问题分为三类，大致按你遇到的顺序排列：

1. [技术兼容性](#技术兼容性)：最常见；新代码必须仍能加载和执行现有状态。
2. [业务兼容性](#业务兼容性)：不太常见；即使代码已更改，现有运行也应继续遵循旧业务逻辑。
3. [非确定性](#非确定性)：仅适用于[功能 API](https://langchain-docs-zh.mintlify.site/langchain/oss/langgraph/functional-api.md)。

> [!TIP]
>
> 有关运行时默认支持的图拓扑和状态变更的简要总结，请参阅[图迁移](https://langchain-docs-zh.mintlify.site/langchain/oss/langgraph/graph-api.md#图迁移)。本页其余部分涵盖了当变更超出支持范围时可应用的模式。

## 技术兼容性

技术兼容性相当于微服务中的 API 断开变更。"API"是指你的图代码与现有线程通过[检查点](https://langchain-docs-zh.mintlify.site/langchain/oss/langgraph/checkpointers.md#检查点库)持久化的数据之间的契约。当线程恢复时，LangGraph 反序列化保存的状态，将其分派给指定名称的节点，并期望节点返回符合状态模式的数据。

常见的技术中断：

* **重命名或删除节点**时线程暂停在该节点或即将进入该节点，例如在@\[`interrupt`]处或通过仍路由到旧名称的检查点条件边。恢复时，LangGraph 无法通过保存的名称找到节点，运行失败。恢复运行的起点是执行停止的节点开始处，因此缺失节点无处恢复。
* **重命名或删除状态键**，而旧检查点仍包含或下游节点仍读取。
* **收紧状态字段**，例如将`Optional`字段设为必需、缩小类型或添加无默认值的必需新字段。现有检查点将无法满足新模式。

边拓扑本身*不会*在检查点中持久化。在仍然存在的节点之间添加、删除或重新路由边对正在运行的线程是安全的。根据[图迁移](https://langchain-docs-zh.mintlify.site/langchain/oss/langgraph/graph-api.md#图迁移)的总结，唯一可能中断已中断线程的拓扑变更是指定或删除节点。

### 推荐模式

:::python

* 将新状态字段作为`NotRequired`（或`Optional[...] = None`）添加，以便旧检查点仍验证：

  ```python theme={null}
  from typing import NotRequired
  from typing_extensions import TypedDict

  class State(TypedDict):
      messages: list
      summary: NotRequired[str]  # [!code ++]
  ```

* 将删除视为弃用。即使没有节点读取，也要在状态上定义字段至少一个排空周期，以便现有检查点继续加载。

* 通过*添加后删除*重命名。将新字段或节点与旧字段或节点并置，双写或路由到两者进行弃用窗口，然后一旦确认没有正在运行的线程依赖旧版本，就删除旧版本。

* 保持节点函数对未知键容忍。`TypedDict`在运行时忽略额外键，因此旧代码版本遗留的状态不会引发异常，除非节点显式读取缺失键。

* 使用[时间旅行](https://langchain-docs-zh.mintlify.site/langchain/oss/langgraph/use-time-travel.md)和@\[`graph.get_state`]\[get\_state]在发布前在暂存部署中检查现有线程与新代码。

:::

:::js

* 将新状态字段标记为可选（`z.string().optional()`或`.nullish()`），以便旧检查点仍验证。
* 将删除视为弃用：在模式上保留字段至少一个排空周期，以便现有检查点继续加载。
* 通过添加后删除重命名：将新字段或节点与旧字段或节点并置，双写或路由到两者进行弃用窗口，然后一旦没有正在运行的线程依赖旧版本，就删除旧版本。
* 使用[时间旅行](https://langchain-docs-zh.mintlify.site/langchain/oss/langgraph/use-time-travel.md)和@\[`graph.getState`]\[get\_state]在发布前在暂存部署中检查现有线程与新代码。

:::

### 检测正在运行的线程

在删除节点、重命名状态键或进行其他现有线程无法容忍的变更之前，你想知道是否有任何线程当前停驻在你即将丢弃的代码版本上。LangGraph 本身不维护线程状态的搜索索引，因此答案取决于你的图运行位置。

**如果你部署到[LangSmith](https://docs.langchain.com/langsmith/deployment)。** 使用 Agent 服务器线程搜索按状态过滤。`status`字段接受`idle`、`busy`、`interrupted`和`error`，因此你可以批量查询`interrupted`或`busy`线程，可选地使用元数据过滤器缩小范围。参见[按线程状态过滤](https://docs.langchain.com/langsmith/use-threads#filter-by-thread-status)和[列出线程](https://docs.langchain.com/langsmith/use-threads#list-threads)。

**在 LangGraph 运行的任何地方。** 使用[LangSmith 跟踪](https://langchain-docs-zh.mintlify.site/langchain/oss/langgraph/observability.md)监控生产中正在进入和退出的节点。这是最可靠的信号，表明节点或状态字段在任何活动代码路径中不再可达。

**当你已有`thread_id`。** 直接检查该单个线程：

:::python

* @\[`graph.get_state(config)`]\[get\_state] 返回最新检查点，包括线程暂停的节点和任何待处理的中断。
* @\[`graph.get_state_history(config)`]\[get\_state\_history] 返回线程的完整时间序列检查点列表。

:::

:::js

* @\[`graph.getState(config)`]\[get\_state] 返回最新检查点，包括线程暂停的节点和任何待处理的异常。
* @\[`graph.getStateHistory(config)`]\[get\_state\_history] 返回线程的完整时间序列检查点列表。

:::

当不确定时，保留已弃用的节点或字段，直到 Agent Server 线程列表和跟踪显示其不再有任何活动。

## 业务兼容性

有时一个变更在技术上有效（所有现有检查点仍然可以加载，所有节点仍然可以解析），但新图的意义与旧图不同。新行为对新的线程是正确的，你不想将其回溯应用到在旧逻辑下启动的线程。

例如，假设你的图运行 `intake → triage → respond`，你决定在 `triage` 和 `respond` 之间插入一个新的 `policy_check` 步骤：

* 已经通过 `triage` 的线程应直接继续到 `respond`（旧流程）。
* 新线程应运行完整的新流程。

推荐的模式是在线程启动时记录相关的 *行为版本*，然后使用 [条件边](https://langchain-docs-zh.mintlify.site/langchain/oss/langgraph/graph-api.md#条件边缘) 根据它进行分支：

:::python

```python theme={null}
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](https://langchain-docs-zh.mintlify.site/langchain/oss/langgraph/functional-api.md) 和 [**任务**](https://langchain-docs-zh.mintlify.site/langchain/oss/langgraph/functional-api.md#任务) 或 @\[`interrupt`] 调用在 [图 API](https://langchain-docs-zh.mintlify.site/langchain/oss/langgraph/graph-api.md) **节点** 内部。普通的图 API **节点** [从节点函数开始重新运行](https://langchain-docs-zh.mintlify.site/langchain/oss/langgraph/graph-api.md#重执行与幂等性)；设计副作用以幂等性进行，但你不需要保留任务调用顺序，除非你在该 **节点** 中使用 **任务** 或 @\[`interrupt`]。

功能 API **入口点** 编译为单个 **节点**，当运行恢复时，它从头开始重播入口点主体，使用缓存的 @\[`@task`]\[任务] 结果来跳过已经完成的工作。两种类型的变更会破坏此模型：

* **添加、删除或重新排序`@task`调用或 @\[`interrupt`]调用**，这些调用在恢复点之前。LangGraph 通过调用在重播中的位置来匹配缓存的 结果和恢复值，因此改变该位置可能导致错误的缓存值被重播到不同的调用上。
* **在`@task`外部引入非确定性操作**，例如 `time.time()`、`random.random()` 或在入口点主体中内联的网络调用。在重播时，它们产生的值与第一次运行时不同，这可能会改变控制流。

有关更深入的处理和示例，请参阅功能 API 指南中的 [确定性](https://langchain-docs-zh.mintlify.site/langchain/oss/langgraph/functional-api.md#确定性) 和 [常见陷阱](https://langchain-docs-zh.mintlify.site/langchain/oss/langgraph/functional-api.md#常见陷阱)。

如果你需要对具有进行中运行的 `@entrypoint` 进行非平凡的代码更改，最安全的选项是：

* 让进行中的运行在部署更改之前耗尽。
* 将任何新逻辑包装在一个新的 `@task` 中，以便其结果独立地进行检查点。
* 在 `langgraph.json` 中注册一个新入口点，使用新的图名，以新的行为路由新线程。

***

> 官方原文：[src/oss/langgraph/backward-compatibility.mdx](https://github.com/langchain-ai/docs/blob/main/src/oss/langgraph/backward-compatibility.mdx) · 非官方翻译，译文由 AI 生成，以英文原文为准。


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.