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

# 向后兼容性

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

软件在生产环境中需要变更。新需求、缺陷修复和重构最终都会落到你的图代码中。由于 LangGraph 针对已有线程的[持久化](/oss/javascript/langgraph/persistence)状态运行最新部署的图，你提交的每个更改实际上都是相对于现有检查点的向后兼容 API 变更。

与将运行固定到其启动时代码版本的工作流引擎不同，LangGraph 会立即将最新的图应用到*每个*线程，包括新线程和从检查点恢复的线程。这很方便：缺陷修复无需仪式即可传播到正在进行的对话和智能体。但这也意味着你必须考虑每个更改如何与在先前版本代码下启动的运行交互。

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

1. [技术兼容性](#technical-compatibility)：最常见的；新代码必须仍能加载并执行现有状态。
2. [业务兼容性](#business-compatibility)：不太常见的；即使代码已更改，现有运行也应继续遵循旧的业务逻辑。
3. [非确定性](#non-determinism)：仅适用于[函数式 API](/oss/javascript/langgraph/functional-api)。

<Tip>
  有关运行时默认支持哪些图拓扑和状态更改的简短摘要，请参阅[图迁移](/oss/javascript/langgraph/graph-api#graph-migrations)。本页面的其余部分涵盖了当更改超出支持范围时可以应用的模式。
</Tip>

## 技术兼容性

技术兼容性相当于微服务中的 API 破坏性变更。这里的"API"是你的图代码与[检查点](/oss/javascript/langgraph/persistence#checkpointer-libraries)已经为现有线程持久化的数据之间的契约。当线程恢复时，LangGraph 反序列化保存的状态，按名称将其分派到节点，并期望节点返回符合状态模式的值。

常见的技术兼容性破坏：

* **重命名或删除节点**，而线程在该节点处暂停或即将进入该节点，例如在 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 处或通过仍路由到旧名称的检查点条件边。恢复时，LangGraph 无法按保存的名称找到节点，运行失败。[恢复运行的起始点](/oss/javascript/langgraph/durable-execution#starting-points-for-resuming-workflows)是执行停止处的节点开始位置，因此缺少的节点无处可恢复。
* **重命名或删除状态键**，而旧检查点仍包含该键或下游节点仍在读取它。
* **收紧状态字段**，例如将 `Optional` 字段改为必需、缩窄类型，或添加没有默认值的新必需字段。现有检查点将无法满足新模式。

边拓扑本身*不会*持久化在检查点中。在仍存在的节点之间添加、删除或重新路由边对正在进行的线程是安全的。根据[图迁移](/oss/javascript/langgraph/graph-api#graph-migrations)摘要，唯一能破坏已中断线程的拓扑更改是重命名或删除节点。

### 推荐模式

* 将新状态字段标记为可选（`z.string().optional()` 或 `.nullish()`），以便旧检查点仍能验证通过。
* 将删除视为弃用：在模式上保留该字段至少一个清空周期，以便现有检查点继续加载。
* 通过先添加后删除的方式重命名：在旧的字段或节点旁添加新的，在弃用窗口期间同时写入或路由到两者，然后在没有正在进行的线程依赖旧的时删除它。
* 使用[时间旅行](/oss/javascript/langgraph/use-time-travel)和 [`graph.getState`](https://reference.langchain.com/javascript/classes/_langchain_langgraph.pregel.Pregel.html#getState) 在部署到预生产环境之前，抽检现有线程与新代码的兼容性。

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

在删除节点、重命名状态键或进行旧线程无法容忍的其他更改之前，你需要知道是否有线程当前停留在你即将删除的代码版本上。LangGraph 本身不维护线程状态的搜索索引，因此答案取决于你的图运行在哪里。

**如果你部署到 [LangSmith](/langsmith/deployment)。** 使用智能体服务器的线程搜索按状态筛选。`status` 字段接受 `idle`、`busy`、`interrupted` 和 `error`，因此你可以批量查询 `interrupted` 或 `busy` 线程，可选地使用元数据过滤器进行缩小。请参阅[按线程状态筛选](/langsmith/use-threads#filter-by-thread-status)和[列出线程](/langsmith/use-threads#list-threads)。

**LangGraph 运行的任何地方。** 使用 [LangSmith 追踪](/oss/javascript/langgraph/observability)来监控生产中哪些节点正在被进入和退出。这是节点或状态字段在任何活跃代码路径中不再可达的最可靠信号。

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

* [`graph.getState(config)`](https://reference.langchain.com/javascript/classes/_langchain_langgraph.pregel.Pregel.html#getState) 返回最新的检查点，包括线程暂停在哪个节点以及任何待处理的中断。
* [`graph.getStateHistory(config)`](https://reference.langchain.com/javascript/classes/_langchain_langgraph.pregel.Pregel.html#getStateHistory) 返回该线程的完整时间顺序检查点列表。

如有疑问，请保留已弃用的节点或字段，直到智能体服务器线程列表和追踪都显示不再有活动。

## 业务兼容性

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

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

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

推荐的模式是在线程启动时在状态上记录相关的*行为版本*，然后使用[条件边](/oss/javascript/langgraph/graph-api#conditional-edges)进行分支：

旧线程在 `triage` 之后恢复时从其保存的状态中读取 `flow_version`（或回退到 v1 默认值）并跳过 `policy_check`。新线程从 `intake` 开始，被标记为 `flow_version=2`，并运行新路径。一旦所有 v1 线程完成，你可以删除版本标志和条件边。

此模式仅在你在线程启动时设置版本*之前*需要版本化的任何分支时才有效。稍后设置意味着现有线程在需要时不会有它。

## 非确定性

此类别仅适用于[函数式 API](/oss/javascript/langgraph/functional-api)。[图 API](/oss/javascript/langgraph/graph-api) 在恢复时从节点边界重新进入，因此节点代码不会像 Temporal 风格的工作流那样从函数开头"重放"。

相比之下，函数式 API 在运行恢复时从头开始重放 `@entrypoint` 的主体，使用缓存的 [`@task`](https://reference.langchain.com/javascript/langchain-langgraph/index/task) 结果来跳过已完成的工作。两种更改会破坏此模型：

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

有关更深入的示例处理，请参阅函数式 API 指南中的[确定性](/oss/javascript/langgraph/functional-api#determinism)和[常见陷阱](/oss/javascript/langgraph/functional-api#common-pitfalls)。

如果你需要对有正在进行运行的 `@entrypoint` 进行重大代码更改，最安全的选项是：

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

***

<div className="source-links">
  <Callout icon="terminal-2">
    [将这些文档连接](/use-these-docs)到 Claude、VSCode 等工具，通过 MCP 获取实时答案。
  </Callout>

  <Callout icon="edit">
    [在 GitHub 上编辑此页面](https://github.com/langchain-ai/docs/edit/main/src/oss/langgraph/backward-compatibility.mdx) 或 [提交问题](https://github.com/langchain-ai/docs/issues/new/choose)。
  </Callout>
</div>
