LangGraph Python 最佳实践调研报告
- 日期:2026-06-11
- 范围 :仅 LangGraph Python(TypeScript / JavaScript 不在范围内)
- 基准版本 :
langgraph 1.2.4(PyPI,2026-06-02 发布,要求 Python ≥ 3.10) - 配套生态版本 :
langgraph-checkpoint 4.1.1、langgraph-checkpoint-postgres 3.1.0、langgraph-checkpoint-sqlite 3.1.0、langgraph-prebuilt 1.1.0、langgraph-sdk 0.4.2、langgraph-cli 0.4.28、langchain-core 1.4.x - 来源 :官方文档(docs.langchain.com / reference.langchain.com)、LangChain 博客、Anthropic / OpenAI 官方文档、PyPI 元数据、GitHub Releases、
deep-research-synthesis.md事实底座;并经多轮审查闭环。 - 适用读者:需要把 LangGraph Python 投入生产或选型评估的工程师、架构师与 AI Platform 团队。
- 不覆盖:LangGraph TypeScript / JavaScript 完整差异矩阵、A2A / MCP / 多模态 / Computer Use / Voice 等高级专题(按 v2 大纲属阶段二范围)。
目录
- 第 1 章:执行摘要与决策框架
- 第 2 章:LangGraph 核心设计思想
- 第 3 章:Graph 设计规范
- 第 4 章:状态持久化、恢复与 HITL
- 第 5 章:流式输出与前端集成
- 第 6 章:工具调用与 Agent 构建
- 第 7 章:多智能体架构
- 第 8 章:测试与质量保障
- 第 9 章:可观测性
- 第 10 章:性能、成本与可靠性
- 第 11 章:生产化部署与平台选型
- 第 12 章:安全、合规与多租户
- 附录 A:版本与迁移注意事项(Python)
- 附录 B:代码片段库
- 附录 C:参考资料与术语表
- 质量审查说明
- 已知限制 / 待补项
第 1 章:执行摘要与决策框架
1. 解决什么问题(LangGraph 的核心价值)
LangGraph 官方把自己定位为「a low-level orchestration framework and runtime for building, managing, and deploying long-running, stateful agents」------构建、管理、部署长期运行、有状态的 agent 所需的底层编排框架与运行时。它不抽象 prompt 也不提供"全家桶"架构,而是把 agent 编排所必需的底层能力(持久化执行、可恢复、人机协同、流式、可观察、生产部署)交给开发者自己组装。
【官方能力】来源: https://docs.langchain.com/oss/python/langgraph/overview · 2026-06 抓取 · langgraph 1.2.4
LangChain 官方在 1.0 公告里也重申这一定位:LangGraph 是「low-level agent orchestration framework, giving developers durable execution and fine-grained control to run complex agentic systems in production」。文档统一为 python/javascript 合一站点("无破坏性变更"为 alpha 公告原文转述,GA 公告待补,参见附录 A.3)。
【官方能力】来源: https://www.langchain.com/blog/langgraph-v1 · 2025-09-02 · langgraph 1.0.0a1
1.1 LangGraph 解决什么(官方能力矩阵)
| 能力 | 官方表述 | 适用对象 | 引用 |
|---|---|---|---|
| Durable execution | 失败后从断点恢复,长期任务可暂停/续跑 | 任何需要"明天还能继续今天的工作流"的场景 | persistence 文档 |
| Human-in-the-loop | 任意节点可暂停、检查、修改状态 | 审批、编辑、回滚、人工补充上下文 | interrupts 文档 |
| Memory | 短期工作记忆 + 长期持久记忆(Store) | 多轮对话、个性化、跨会话上下文 | memory 文档 |
| Streaming | values/updates/messages/custom/checkpoints/tasks/debug(Python < 3.11 环境下 get_stream_writer 在 async 节点/工具中不可用,需直接传 writer) |
前端实时展示、调试 | streaming 文档 |
| LangSmith 集成 | 路径可视化、状态转移、运行时指标 | 生产可观测性 | overview 文档 |
| 生产部署 | Platform 三层(Cloud / Self-Hosted Lite / Self-Hosted Enterprise) | 多租户、长任务、高可用 | LangSmith Deployment 文档 |
| Multi-agent | Supervisor / Swarm / Subgraph / handoff | 多角色协作 | multi-agent 文档(ch7 展开 langgraph-supervisor / langgraph-swarm) |
【官方能力】来源: https://docs.langchain.com/oss/python/langgraph/overview · 2026-06 抓取 · langgraph 1.2.4
1.2 LangGraph 不解决什么(重要边界)
- 不替代 LangChain 的模型/工具/检索抽象:工具本身(搜索、SQL、HTTP、RAG)属于 LangChain / 第三方库;LangGraph 只做"如何调用这些工具并保留状态"。
- 不提供 prompt 模板或架构模式:它不规定"ReAct"或"Plan-Execute",这些是社区/业务模式。
- 不替代数据库 / 队列 / 工作流引擎:持久化(Postgres/Redis)由 checkpointer 后端提供,但 LangGraph 不管你的业务库结构。
- 不提供开箱可用的多租户、鉴权、计费:这些属于 Platform/Server 的能力,但业务侧的权限模型仍要自己设计。
- 对简单单轮调用不必要:官方明确建议"刚入门 agent 时,先用 LangChain prebuilt agents"。
【官方能力】来源: https://docs.langchain.com/oss/python/langgraph/overview · 2026-06 抓取 · langgraph 1.2.4
1.3 适用 / 不适用场景三问
在引入 LangGraph 之前,先自问三个问题(推断依据:见第 2 章概念图与第 3 章模式):
| 问题 | 答案偏「否」 | 答案偏「是」 |
|---|---|---|
| Q1:可恢复执行? 任务是否会跨进程、跨实例、跨日恢复?是否需要从断点续跑? | 用普通 Python 脚本 / LangChain Runnable | 必须 LangGraph + checkpointer |
| Q2:HITL? 是否有"必须人审批 / 编辑 / 回滚"的节点?是否需要暂停 N 小时等用户? | 自动 agent loop 即可 | LangGraph interrupt() + Command(resume=...) |
| Q3:多步骤多分支多智能体? 是否有显式分支、循环、子图?是否有 agent 间路由 / handoff? | LangChain LCEL 链式已够 | LangGraph StateGraph + add_conditional_edges / Command / Send |
【推测性建议】经验法则:三个问题里只要有 1 个为「是」 ,LangGraph 都值得考虑;2 个以上为「是」 ,几乎可以确定应该用 LangGraph 而非传统 Chain/AgentExecutor。边界依据 :单一强需求(仅 Q1 或仅 Q2 或仅 Q3)场景下,LangChain LCEL / create_agent(langchain 1.x)已能覆盖;2 个以上为「是」说明业务已超出 Chain 抽象的能力半径------这与 LangChain overview 建议"先 prebuilt agents、再升级 LangGraph"是同一口径。
2. 推荐做法(决策路径)
2.1 选型决策树
你打算构建什么?
│
├── 单轮 LLM 调用 / 简单链(prompt → 模型 → 输出)
│ └─► 不要用 LangGraph
│ 直接用 LangChain `create_agent` / LCEL 链;或裸调 chat model
│
├── 多轮对话 + 短期记忆 + 工具调用
│ │
│ ├── 无持久化 / 不用从断点恢复
│ │ └─► LangChain `create_agent`(harness + middleware 已够)
│ │ 来源: https://docs.langchain.com/oss/python/langchain/agents
│ │
│ ├── 需要跨会话恢复 / 人审 / 长期记忆
│ │ └─► LangGraph(自托管 + Postgres / Sqlite checkpointer)
│ │ 或 LangGraph Platform(如果不想自己运维)
│ │
│ └── 多智能体协作 / 显式分支
│ └─► LangGraph(StateGraph + Command + Send / Subgraph)
│
├── 长期 / 异步 / 跨日任务
│ └─► 几乎必选 LangGraph(durable execution 是核心)
│
└── 选 OpenAI Agents SDK / CrewAI / AutoGen?
└─► 见下文 1.2.2 选型矩阵
【推测性建议】依据:LangChain 官方 overview 明确建议"先 LangChain prebuilt agents"再升级到 LangGraph;持久化能力是 LangGraph 的差异化锚点。
2.2 选型矩阵(LangGraph vs 主要替代)
| 维度 | LangGraph | LangChain Chain / create_agent |
OpenAI Agents SDK | CrewAI | AutoGen v0.4+ |
|---|---|---|---|---|---|
| 图范式 | StateGraph 显式节点/边(Command/Send) | LCEL ` | ` 链式 + 函数式 agent loop | 轻量原语,Python-first 串联 | Flow + Crew 双层抽象 |
| 一等公民持久化 | ✅ Checkpointer(Memory/Sqlite/Postgres) | ✅ 可传 checkpointer= 到 create_agent |
✅ Sessions(SQLite/SQLAlchemy/Redis/Mongo/Encrypted/Dapr) | ⚠️ Flow state 默认内存;需自接 storage adapter | ⚠️ 需自接 / Studio 集成 |
| 跨断点恢复(durable) | ✅ Durability modes (exit/async/sync) + super-step 粒度 | ❌ 默认无 | ✅ Sessions 可恢复,但粒度在 session 而非 super-step | ❌ 文档未强调 | ❌ |
| 任意节点可中断 | ✅ interrupt() + Command(resume=...) |
⚠️ 仅支持 agent 层 middleware | ✅ human_in_the_loop/ 内建模块 |
⚠️ 有限 | ⚠️ |
| 工具调用 | ✅ ToolNode + 任意 Python callable | ✅ | ✅ | ✅ | ✅ |
| 多智能体编排 | ✅ Supervisor / Swarm / Subgraph / handoff | ⚠️ 需自实现 | ✅ Handoffs / Agents-as-tools | ✅ Crew | ✅ Team / RoutedAgent |
| 状态隔离 / 共享 | 显式 State schema + 子图隔离 | 单 state | Session 隔离 | Flow/Crew 分层 | AgentId + Runtime |
| 可观察性 | LangSmith(原生集成) | LangSmith | OpenAI Tracing 套件 | 自有 trace | Studio / 第三方 |
| 跨模型 | ✅ 任何 chat model | ✅ 任何 chat model | ⚠️ 偏 OpenAI 模型 | ✅ | ✅ |
| 沙箱 / 隔离执行 | ❌ 自接 | ❌ 自接 | ✅ Sandbox agents | ❌ | ⚠️ Docker Runtime |
| 学习曲线 | 中高(需理解图/状态/检查点) | 低 | 低 | 中 | 中高 |
| 适用场景 | 长期 / 多分支 / 多智能体 / HITL | 短链路 / 单轮 / 简单 RAG | OpenAI 全家桶、轻量多 agent | 创意/角色协作 | 学术 / 实验性多 agent |
【官方能力】LangGraph 能力:https://docs.langchain.com/oss/python/langgraph/overview · 2026-06 抓取 · 1.2.4
【官方能力】LangChain agents:https://docs.langchain.com/oss/python/langchain/agents · 2026-06 抓取 · langchain 1.x
【官方能力】OpenAI Agents SDK sessions:https://openai.github.io/openai-agents-python/sessions/ · 2026-06 抓取
【官方能力】OpenAI Agents SDK 主索引:https://openai.github.io/openai-agents-python/ · 2026-06 抓取
【官方能力】CrewAI:https://docs.crewai.com/en/introduction · 2026-06 抓取
【官方能力】AutoGen v0.4:https://microsoft.github.io/autogen/stable/user-guide/core-user-guide/framework/agent-and-agent-runtime.html · 2026-06 抓取
【推测性建议】矩阵汇总由编者横向比对各官方文档后归纳;选型建议非任何一家官方背书。
关键差异速记(事实+判断):
- 持久化是 LangGraph 差异化最深的能力 (事实+判断)。LangGraph 提供 durability modes(exit/async/sync)+ super-step 粒度的可恢复;OpenAI Agents SDK 也有 Sessions 级恢复(同一 session 实例续跑),但粒度在 session 而非 super-step;AutoGen / CrewAI 默认需自接或第三方。LangChain
create_agent自 langchain 1.x 起也支持checkpointer=。把持久化称为"LangGraph 唯一不可替代"是过度归并;准确说法是"粒度与生产级 durability 上 LangGraph 更深"。 - 图范式 vs 链范式(事实+判断)。LangGraph 显式建模为有向图(StateGraph + add_edge / add_conditional_edges / Command / Send),可表达任意分支/循环/并行;其他框架更偏 Python-first 函数式串联。要"任意分支+子图"是 LangGraph 的强项,但要"轻量"则 OpenAI Agents SDK / CrewAI 更快上手。
- HITL 能力 (事实)。LangGraph 的
interrupt()+Command(resume=...)是编译期支持的细粒度机制;OpenAI Agents SDK 有内建模块但粒度偏 agent 整体;其他框架 HITL 多为自实现。
2.3 何时升级到 LangGraph Platform
LangGraph Platform 是 LangChain 商业化的部署方案,按项目 v2 大纲口径分为三层(按"谁运维基础设施"区分;与 LangSmith Deployment 文档原词 Cloud / Standalone server / Self-hosted 的对应关系见附录 A.4):
| 模式 | 谁运维 | 是否含 LangSmith 控制面 | 数据面 / 持久化 | 适用 |
|---|---|---|---|---|
| Cloud | LangChain | LangChain 托管(含完整控制面) | LangChain 托管 | 中小团队 / 不想运维 / Plus 计划起 |
| Self-Hosted Lite | 你(Docker / Compose / K8s) | 无控制面(仅 LangGraph Server/Studio) | 你管 Postgres / Redis | 已有基础设施 / 单租户 / 不想引入 LangSmith 控制面 |
| Self-Hosted Enterprise | 你(K8s) | 你部署完整 LangSmith 控制面 + 数据面 | 你管全部 | 强合规 / 多租户 / 私有云(Enterprise 计划) |
【官方能力】来源: https://docs.langchain.com/langsmith/deployment · 2026-06 抓取 · langgraph 1.2.4
【推测性建议】上表「适用」列由编者归纳;官方文档原文用 no control plane 描述 Standalone、用 Run the full LangSmith platform, including the control plane and data plane, in your cloud 描述 Self-Hosted,未直接给"适用对象"建议。
升级信号(推测性建议,基于社区与官方部署经验):
- 你已经在生产跑 LangGraph,但多副本 + 队列 + Cron + 长任务需要单独搭一套 → Self-Hosted Lite(v2 命名;官方文档称 Standalone server)。
- 团队没有 SRE 资源也不希望学习 K8s/Postgres 调优 → Cloud。
- 强合规要求全部数据留在自己 VPC,且需要 SSO/审计/多租户控制面 → Self-Hosted Enterprise。
- 仍在 PoC 阶段或单实例开发 → 不用 Platform,直接
pip install langgraph+ 本地 Sqlite/Postgres。
3. 不推荐做法(选型反模式)
- 把 LangGraph 当 Chain 用 (事实+判断)。如果只是
prompt | model | parser三步链,引入 LangGraph 反而增加认知负担。官方 overview 建议"先 LangChain prebuilt agents"。来源: https://docs.langchain.com/oss/python/langgraph/overview - 在不需要持久化的场景上 checkpointer 。短期单轮脚本上 Postgres 是过度工程;本地开发用
InMemorySaver即可。来源: https://docs.langchain.com/oss/python/langgraph/persistence - 为追求"新潮"用多智能体。多智能体大幅增加状态/权限/成本复杂度;能单体解决的不要先拆多 agent。社区共识。
- 闭眼选 Platform Cloud 。如果数据合规要求"不能出公司网络",选 Cloud 是直接踩雷。来源: https://docs.langchain.com/langsmith/deployment
- 用 LangGraph 替代工作流引擎 / 队列 。LangGraph 的"调度"是图执行,不是工业级 job scheduler(cron 属于 Platform 能力而非 OSS)。来源: https://docs.langchain.com/langsmith/deployment
- 用 LangGraph 替代数据库事务。Graph state 不是事务系统;业务关键状态仍要走业务库 + 补偿。社区共识。
- 在 0.x 老版本上写新代码 。当前稳定线是 1.2.x(2026-06-02);v1.0 已从 0.6.x 跨越发布(GA 公告原文待补,参见附录 A.3);早期 0.x 文档和 API 大量过时。来源: https://pypi.org/project/langgraph/
4. 最小代码示例(决策框架不是 API 字典)
本节用最小代码表达决策点,不替代完整 API。完整代码片段库应放在附录 B(与本调研规划一致)。
4.1 最简单 LangGraph(用于判断"我到底要不要用")
依赖:langgraph>=1.2,<2,Python ≥ 3.10
python
# 决策目的:验证"我连最小例子都觉得重"时 → 不要用 LangGraph
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
class State(TypedDict):
msg: str
def echo(state: State) -> dict:
return {"msg": state["msg"].upper()}
g = StateGraph(State)
g.add_node("echo", echo)
g.add_edge(START, "echo")
g.add_edge("echo", END)
print(g.compile().invoke({"msg": "hello"}))
# {'msg': 'HELLO'}
【官方能力】来源: https://github.com/langchain-ai/langgraph(README minimal example)· 2026-06 抓取 · 1.2.4
g.compile()是图执行前的必经步骤:它做基本结构校验(orphaned nodes 等),并把checkpointer/interrupt_before/store等 runtime args 打包;不调用compile()就直接invoke()会抛AttributeError/GraphRecursionError(取决于缺失项)。
4.2 决策:是否需要 checkpointer?
python
# 决策目的:如果你压根不调用 checkpointer,LangGraph 与普通 Chain 几乎等价
from langgraph.checkpoint.memory import InMemorySaver # 开发/单进程测试用;生产用 SqliteSaver/PostgresSaver
checkpointer = InMemorySaver() # 开发/单进程测试
# checkpointer = SqliteSaver.from_conn_string("checkpoints.db") # 本地持久化
# checkpointer = PostgresSaver.from_conn_string(DB_URL) # 生产
app = g.compile(checkpointer=checkpointer)
cfg = {"configurable": {"thread_id": "user-123"}}
print(app.invoke({"msg": "hi"}, config=cfg))
print(app.invoke({"msg": "again"}, config=cfg)) # 同 thread,可恢复
【官方能力】来源: https://docs.langchain.com/oss/python/langgraph/persistence · 2026-06 抓取 · 1.2.4
判断 :如果你的产品形态是"一次请求一次响应",上面
app.invoke不带thread_id就够用;不需要 LangGraph。
5. 检查清单(第 1 章)
- 已确认需求满足"Q1 可恢复 / Q2 HITL / Q3 多步骤多分支多智能体"中至少 1 项
- 短期 / 单轮场景下未引入 LangGraph(改用 LangChain
create_agent或裸 chat model) - 多智能体仅在"单体方案已确认失败"时才启用
- 已对比 LangGraph / LangChain / OpenAI Agents SDK / CrewAI / AutoGen 选型矩阵
- 已明确持久化后端(InMemory / Sqlite / Postgres / Platform)
- 决定自托管 OSS 还是 Platform(Cloud / Standalone / Self-Hosted Enterprise)
- 数据合规要求已对齐 Platform 部署模式
- 未使用 langgraph ≤ 0.6.x 老 API
- 已记录决策依据(事实链接 + 团队判断),便于未来回溯
第 2 章:LangGraph 核心设计思想
1. 解决什么问题(从"链范式"到"图范式"的范式转换)
传统 LangChain 链(prompt | model | parser)和 AgentExecutor 是链范式 :每一步由前一步的结果驱动,状态隐藏在闭包或 memory 对象里,恢复、HITL、并行、复杂分支都需要额外的胶水代码。LangGraph 的核心思想是用显式的有向图(StateGraph)代替隐式的链,把状态、节点、边、终止条件、恢复点全部升格为一等公民。
【官方能力】来源: https://docs.langchain.com/oss/python/langgraph/overview · 2026-06 抓取 · 1.2.4
【官方能力】v1.0 公告:https://www.langchain.com/blog/langgraph-v1 · 2025-09-02 · 1.0.0a1
为什么需要"图"(事实+判断):
- 可恢复性需要明确的"步"边界(super-step 边界),而链式抽象没有这种天然分块。
- 可中断性需要"任意节点暂停后能续跑"的语义,链式只能"从头再来"或"传一个 callback"。
- 多分支 / 循环 / 并行在图里是一等原语(conditional edge / Command / Send),在链里是 if/else 与 for 循环的拼接。
2. 推荐做法(核心概念图)
下面这些概念是 LangGraph 文档反复出现的"必知词":
| 概念 | 官方定义 | 角色 | 引用 |
|---|---|---|---|
| State | 一组在图执行期间维护的字段(TypedDict / Pydantic / dataclass) | 图的"上下文";每步开始时读取、结束时返回部分更新 | graph-api 文档 |
| StateGraph | 显式声明状态、节点、边的图对象 | 你组装图的容器;compile() 之后才能执行 |
graph-api 文档 |
| compile | 校验结构并打包成 Runnable;配置 checkpointer / breakpoints / store | 图执行入口(必经步骤) | graph-api 文档 |
| Node | 接收 state 返回部分 state update 的函数(同步/异步/Runnable) | 原子工作单元 | graph-api 文档 |
| Edge | 节点之间的连接;可以是 normal / conditional | 控制流 | graph-api 文档 |
| Conditional Edge | 由路由函数决定下一个节点的边 | 动态分支 | graph-api 文档 |
| Command | 同时更新 state 与路由到目标节点(update / goto / graph / resume) |
节点内"边+状态"合并操作 | graph-api 文档 |
| Send | 从条件边返回的对象,携带不同 state 到同一节点 | dynamic parallelism(map-reduce) | graph-api 文档 |
| Checkpointer | 实现 BaseCheckpointSaver 的对象,按 thread_id 落盘 checkpoint |
持久化 / 恢复 / HITL | persistence 文档 |
| Thread | 一次持续交互/运行的标识(thread_id) |
状态隔离的"会话"边界 | persistence 文档 |
| Checkpoint | 某 super-step 边界的 state 快照 | 恢复 / Time Travel / 审计 | persistence 文档 |
| Durability modes | exit / async / sync:checkpoint 落盘策略 |
性能 vs 可靠性的旋钮 | persistence 文档 |
| Runtime | 图执行的容器;负责调度、流式、中断、并发 | 与 graph 解耦的运行时 | overview 文档 |
| interrupt() | 节点内调用,暂停图执行并把 payload 暴露给调用方 | HITL 主机制 | interrupts 文档 |
| Command(resume=...) | 唯一作为 invoke/stream 输入的 Command 变体 |
续跑 / 喂入人工输入 | interrupts 文档 |
| Functional API | @entrypoint + @task 装饰器写工作流 |
不想写图但想用 checkpointer/HITL 的折中 | functional-api 文档 |
| Time Travel / Replay / Fork | 从历史 checkpoint 重放(replay)或 fork 出新分支 | 调试 / 审计 / A/B 试错 | use-time-travel 文档 |
【官方能力】graph-api:https://docs.langchain.com/oss/python/langgraph/graph-api · 2026-06 抓取 · 1.2.4
【官方能力】persistence:https://docs.langchain.com/oss/python/langgraph/persistence · 2026-06 抓取 · 1.2.4
【官方能力】interrupts:https://docs.langchain.com/oss/python/langgraph/interrupts · 2026-06 抓取 · 1.2.4
【官方能力】memory:https://docs.langchain.com/oss/python/langgraph/memory · 2026-06 抓取 · 1.2.4
【官方能力】streaming:https://docs.langchain.com/oss/python/langgraph/streaming · 2026-06 抓取 · 1.2.4
【官方能力】functional-api:https://docs.langchain.com/oss/python/langgraph/functional-api · 2026-06 抓取 · 1.2.4
【官方能力】use-time-travel:https://docs.langchain.com/oss/python/langgraph/use-time-travel · 2026-06 抓取 · 1.2.4
【官方能力】overview:https://docs.langchain.com/oss/python/langgraph/overview · 2026-06 抓取 · 1.2.4
2.1 State / Node / Edge / Runtime / Checkpointer 概念图
┌────────────────────────────────────────────┐
│ StateGraph(编译期) │
│ ┌──────┐ add_edge ┌──────┐ │
│ │ Node ├─────────────►│ Node │ │
│ │ A │ │ B │ │
│ └─┬────┘ └──┬───┘ │
│ │ add_conditional_edges │ │
│ │ 路由 fn(state) │ │
│ ▼ ▼ │
│ ┌──────┐ Command ┌──────┐ │
│ │ Node │ (update+ │ Node │ │
│ │ C │ goto) │ D │ │
│ └──────┘ └──────┘ │
│ │
│ Send(node, state_i) × N → 并行 fan-out │
└────────────────────────────────────────────┘
│ compile()
▼
┌────────────────────────────────────────────┐
│ CompiledGraph(运行时)+ Checkpointer │
│ - invoke / stream / ainvoke / astream │
│ - interrupt() 暂停 │
│ - Command(resume=...) 续跑 │
│ - get_state / update_state │
│ - durability: exit / async / sync │
└────────────────────────────────────────────┘
│ thread_id
▼
┌────────────────────────────────────────────┐
│ Checkpoint 存储 │
│ (InMemory / Sqlite / Postgres / Platform) │
│ - thread, checkpoint, pending_writes │
│ - 可回放 / 可审计 / 可恢复 │
└────────────────────────────────────────────┘
【官方能力】整合自 persistence / graph-api / interrupts 三份官方文档(链接见上)
2.2 与 LangChain Chain / AgentExecutor 的核心差异
| 维度 | LangChain Chain / create_agent |
LangGraph |
|---|---|---|
| 状态表示 | 隐式(闭包 / memory 对象 / LCEL 变量) | 显式 State schema(TypedDict / Pydantic) |
| 控制流 | ` | ` 链式 + 函数式 |
| 持久化 | create_agent 支持 checkpointer=(langchain 1.x) |
一等公民;多种内置后端 |
| 中断粒度 | agent 整体 / middleware | 节点内任意位置(interrupt()) |
| 多步分支 | 需自实现 | add_conditional_edges / Command 原生 |
| 并行 | 需 asyncio.gather | Send 原生 |
| Time Travel | 需自实现 | get_state_history + update_state 原生 |
| 可视化 | 弱 | Studio / get_graph().draw_* |
| 学习曲线 | 低 | 中高 |
【官方能力】LangChain agents:https://docs.langchain.com/oss/python/langchain/agents · 2026-06 抓取 · langchain 1.x
【官方能力】LangGraph overview:https://docs.langchain.com/oss/python/langgraph/overview · 2026-06 抓取 · 1.2.4
2.3 与 OpenAI Agents SDK / CrewAI / AutoGen 的关键差异
聚焦四件事:持久化 / 图范式 / 可中断 / 可观察性。
| 维度 | LangGraph | OpenAI Agents SDK | CrewAI | AutoGen v0.4+ |
|---|---|---|---|---|
| 持久化 | Checkpointer + Thread + Checkpoint + durability modes;super-step 粒度 | Sessions(SQLite/SQLAlchemy/Redis/Mongo/Encrypted/Dapr);同 session 实例可恢复,粒度在 session | ⚠️ Flow state 默认内存;需自接 storage adapter | 需自接 / Studio |
| 图范式 | 显式 StateGraph + Command + Send + Subgraph | Python-first 串联(Agent / Handoffs / Guardrails) | Flow + Crew 双层 | Core API(RoutedAgent + Runtime) |
| 可中断 | 任意节点 interrupt() + Command(resume=...);支持多中断映射 |
内建 human_in_the_loop/ 模块 |
有限 | 有限 |
| 可观察性 | LangSmith(原生)/ Studio | OpenAI Tracing | 自有 trace | Studio + 第三方 |
| 跨模型 | 任意 chat model | 偏 OpenAI | 任意 | 任意 |
【官方能力】OpenAI Agents SDK sessions:https://openai.github.io/openai-agents-python/sessions/ · 2026-06 抓取
【官方能力】OpenAI Agents SDK 主索引:https://openai.github.io/openai-agents-python/ · 2026-06 抓取
【官方能力】CrewAI:https://docs.crewai.com/en/introduction · 2026-06 抓取
【官方能力】AutoGen:https://microsoft.github.io/autogen/stable/user-guide/core-user-guide/framework/agent-and-agent-runtime.html · 2026-06 抓取
【推测性建议】跨框架对比由编者按各官方文档归纳,并非任何一家官方背书的对比。
3. 不推荐做法(核心设计反模式)
- 把 LLM 调用 + 工具执行 + 业务逻辑 + 格式化塞进一个 node(社区共识)。节点职责模糊,无法测试、无法定位问题;详见第 3 章。
- 用全局可变对象 / 闭包携带状态 。破坏 Graph 的"state 是单一事实来源"原则;持久化时不可序列化。来源: https://docs.langchain.com/oss/python/langgraph/persistence
- 路由函数包含副作用 (HTTP、写库、随机数)。破坏纯函数假设,无法测试、无法 Time Travel。来源: https://docs.langchain.com/oss/python/langgraph/graph-api
- 同时混用 Command 与 add_edge 路由同一节点 。官方明确警告:节点级路由二选一,否则会出现未定义行为。来源: https://docs.langchain.com/oss/python/langgraph/graph-api
- 把 Graph state 当作业务数据库。Graph state 是执行上下文,不是事务系统;业务关键数据走业务库 + 补偿。社区共识。
- 静态 interrupt 误用为 HITL 。官方明确"static interrupts (
interrupt_before/interrupt_after) are not recommended for human-in-the-loop workflows",仅用于调试断点。来源: https://docs.langchain.com/oss/python/langgraph/interrupts - 混淆 Functional API 与 Graph API 的边界 。两者可以混用但 checkpoint 语义不同:Functional API 在
@task边界落盘,Graph API 在 super-step 边界落盘。来源: https://docs.langchain.com/oss/python/langgraph/functional-api - 把
BaseMessage直接塞入 state 而不使用 reducer 。会出现 ID 冲突、消息重复;官方 1.2.2 修复了"id=None的 BaseMessage 写入 DeltaChannel 时分配稳定 ID"------证明这种坑真实存在。来源: https://github.com/langchain-ai/langgraph/releases/tag/v1.2.2
4. 最小代码示例(表达核心概念)
不重复 README 的最小例子。给出概念对位的最小骨架。
依赖:langgraph>=1.2,<2,Python ≥ 3.10
python
"""
最小骨架:覆盖 5 个核心概念
- State: TypedDict
- Node: 普通函数,返回增量
- Edge: 静态边
- Conditional Edge: 纯函数路由
- Checkpointer: InMemorySaver(同 thread 可恢复)
"""
from typing import TypedDict, Literal
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import InMemorySaver
class State(TypedDict):
query: str
draft: str
quality: int # 0-10
def generate(state: State) -> dict:
return {"draft": f"[draft of {state['query']}]", "quality": 5}
def refine(state: State) -> dict:
return {"draft": state["draft"] + " (refined)", "quality": 8}
def route_after_generate(state: State) -> Literal["refine", "__end__"]:
return "refine" if state["quality"] < 7 else "__end__"
g = StateGraph(State)
g.add_node("generate", generate)
g.add_node("refine", refine)
g.add_edge(START, "generate")
g.add_conditional_edges("generate", route_after_generate) # 纯函数路由
g.add_edge("refine", END)
app = g.compile(checkpointer=InMemorySaver())
cfg = {"configurable": {"thread_id": "t1"}}
print(app.invoke({"query": "hi", "draft": "", "quality": 0}, config=cfg))
# 同 thread_id 再 invoke,能从上次 checkpoint 续跑
【官方能力】来源: https://docs.langchain.com/oss/python/langgraph/graph-api + https://docs.langchain.com/oss/python/langgraph/persistence · 2026-06 抓取 · 1.2.4
5. 检查清单(第 2 章)
- 团队理解 State 是"图的单一事实来源",不引入隐式状态
- 已选择 Graph API(StateGraph)还是 Functional API(@entrypoint/@task),并明确混用边界
- 路由函数全部为纯函数(无 I/O、无随机、无时间)
- 节点职责单一(LLM / 工具 / 纯函数 / IO 各自分开)
- 节点返回增量 state update,不就地修改
- 持久化后端已确定(InMemory/Sqlite/Postgres/Platform)
-
thread_id命名规则已规范(按用户/会话/业务键) - 已用
get_state/get_state_history验证 checkpoint 序列符合预期 - 已禁用 static interrupt 充当 HITL;HITL 一律走
interrupt()+Command(resume=...) - 与其他 Agent 框架(OpenAI Agents SDK / CrewAI / AutoGen)的边界已写明
第 3 章:Graph 设计规范
1. 解决什么问题(State / Node / Edge 的"工程契约")
第 2 章讲图范式是什么;本章讲写一个能上生产的图,应该遵守哪些契约。契约之所以重要(事实+判断):LangGraph 提供了 6--7 种官方核心 API(StateGraph / Functional API / Command / Send / interrupt / checkpointer / store),每种都有自己的边界与坑;没有规范,多人协作时会出现"看起来能跑、线上爆雷"的情况。
2. 推荐做法
2.1 State schema 定义规范
| 选择 | 适用 | 不适用 | 引用 |
|---|---|---|---|
| TypedDict | 简单、内部使用、追求零依赖 | 需要运行时校验 / 嵌套类型 | graph-api 文档 |
Pydantic v2 (BaseModel) |
外部输入、需要校验、复杂嵌套 | 性能敏感、追求最简 | graph-api 文档(隐含) |
| dataclass | 已有 dataclass 重度代码库 | 需要 reducer / Annotated 字段 | graph-api 文档 |
| MessagesState | 纯 chat 场景,已自动带 messages: Annotated[list, add_messages] reducer |
非 chat 业务 | prebuilt 模块 |
Reducer / Annotated 规则(事实):
- 默认 reducer 是"覆盖"。需要"追加"时使用
Annotated[list[T], operator.add]或官方add_messagesreducer。 add_messages会自动处理 message ID 冲突:同 ID 的 message 会被覆盖,不同 ID 追加(行为符合 OpenAI/Anthropic 协议)。- 1.2.2 起,
id=None的 BaseMessage 在写入 DeltaChannel checkpoint 之前会自动分配稳定 ID,避免重复历史。来源: https://github.com/langchain-ai/langgraph/releases/tag/v1.2.2
State 粒度建议(推测性建议+社区经验):
- 小:避免在 state 里塞大文件、DataFrame、整个对话历史。超大 state 会拖慢 checkpoint 序列化。
- 稳定:字段含义一旦发布就少改;版本演进要走"新字段 + 旧字段 deprecated"模式。
- 可序列化 :所有字段必须 JSON 可序列化(
interrupt()文档明确要求)。 - 可演进:新字段必须有默认值;老字段删除要走 deprecation 周期。
- 职责清晰:区分私有 state、输入 state、输出 state;不要把临时变量塞进主 state。
【官方能力】来源: https://docs.langchain.com/oss/python/langgraph/persistence · 2026-06 抓取 · 1.2.4
【官方能力】interrupts 序列化要求:https://docs.langchain.com/oss/python/langgraph/interrupts · 2026-06 抓取 · 1.2.4
【社区经验】State 粒度的"小 / 稳定 / 可序列化"为社区共识,无单一权威来源;可参考 GitHub Discussion 与 LangChain 博客。
2.2 节点职责规范
四类节点,单一职责:
| 节点类型 | 职责 | 例子 | 反例 |
|---|---|---|---|
| LLM 节点 | 仅负责调用 LLM 并解析输出 | def call_llm(state): return {"messages": [llm.invoke(...)]} |
在 LLM 节点里同时写数据库 |
| 工具节点 | 通过 ToolNode 包装工具执行 |
g.add_node("tools", ToolNode([search, sql])) |
在工具里塞 LLM 决策 |
| 纯函数节点 | 业务逻辑(验证、转换、决策) | def validate(state): return {"is_valid": ...} |
纯函数里调外部 HTTP |
| IO 节点 | 外部副作用(HTTP、DB、文件) | def save_to_db(state): ... |
IO 节点里塞 LLM 解析 |
【官方能力】ToolNode 由 langgraph.prebuilt 提供;add_node 接受 sync/async/Runnable。来源: https://github.com/langchain-ai/langgraph(README)· 2026-06 抓取 · 1.2.4
节点输入输出约定(官方+社区共识):
- 只返回增量 :节点返回
dict必须只包含"想更新的字段";不要回传整个 state。 - 不要原地修改 state :从
state["messages"].append(...)会绕过 reducer;应返回{"messages": [new_msg]}。 - 可重入 / 幂等 :节点可能被 retry / re-execute(interrupt 文档明确:节点恢复时从节点开头重跑 )。interrupt 前的副作用必须幂等。来源: https://docs.langchain.com/oss/python/langgraph/interrupts
2.3 路由函数规范(必须纯函数 / 可测试 / 可观测)
纯函数(事实+判断):
python
# 错误:路由里有副作用
def bad_route(state):
log_to_db(state) # 副作用
return "next_node"
# 正确:路由只读 state
def good_route(state) -> Literal["a", "b"]:
return "a" if state["score"] > 0.5 else "b"
可测试(社区经验):路由函数应可被纯单元测试------给定 state 字典,断言 next node 名字。
可观测(社区经验):路由决策应进入 LangSmith trace / 自有日志,避免"为什么走到 B 节点"的鬼故事。
来源: https://docs.langchain.com/oss/python/langgraph/graph-api("routing function" 章节)· 2026-06 抓取 · 1.2.4
2.4 控制流规范
| 场景 | 推荐 | 不推荐 | 引用 |
|---|---|---|---|
| 简单静态分支 | add_edge |
写一个返回固定值的 conditional edge | graph-api 文档 |
| 基于 state 的动态分支 | add_conditional_edges + 纯函数 |
把分支逻辑写进 node 内部 | graph-api 文档 |
| 同时更新 state + 路由 | Command(update=..., goto=...) 返回 |
用 conditional edge 返回一个更新后的 state dict | graph-api 文档 |
| map-reduce / fan-out | Send(node, state_subset) 列表 |
asyncio.gather 自实现并行 | graph-api 文档 |
| HITL 续跑 | Command(resume=...) 作为 invoke/stream 输入 |
把人工输入塞进 state dict | interrupts 文档 |
Command 与 conditional edge 的边界(事实,官方明确):
- 「If you only need to route without updating state, use conditional edges instead.」
- 「For each node, use either Command or static edges to route to the next nodes, not both.」
来源: https://docs.langchain.com/oss/python/langgraph/graph-api · 2026-06 抓取 · 1.2.4
Send 用于 dynamic parallelism(事实):
python
from langgraph.types import Send
def fanout(state: OverallState):
return [Send("process_one", {"x": s}) for s in state["items"]]
g.add_conditional_edges("prepare", fanout)
用途:map-reduce、批量工具调用、并行子任务。LangGraph 会把每条 Send 视为独立的"分支"并发执行。
2.5 循环与终止
recursion_limit 默认 1000 步 (事实,官方:1.0.6 起;当前 1.2.4 仍生效)。来源: https://docs.langchain.com/oss/python/langgraph/graph-api · 2026-06 抓取 · 1.2.4
业务终止条件必须明确(推测性建议,社区共识):
- 在节点内显式终止 :让
router_fn返回"__end__",不要依赖"达到 limit 就停"。 - 给循环加计数器 :state 里加
iteration_count: int,超过阈值强制goto到"放弃 / 报告失败"节点。 - 避免"看似能终止但不会"的死循环 :自审
if state["x"]: goto A; else: goto B是否所有分支都会前进。 - 超时 / 重试边界 :节点级
RetryPolicy(详见 https://docs.langchain.com/oss/python/langgraph/graph-api#retry-policies,本调研未展开错误处理章节)。
2.6 节点幂等性与可重试性
为什么必须幂等(事实,官方明确):
interrupt()节点恢复时从节点开头重跑 ,不是从 interrupt 行继续。来源: https://docs.langchain.com/oss/python/langgraph/interrupts- Functional API 同理:"A task that started but did not finish may run again on that resume, so design side effects to be idempotent."来源: https://docs.langchain.com/oss/python/langgraph/functional-api
- 网络抖动 / 进程崩溃时,未完成的 super-step 会从 checkpoint 续跑,已成功的节点可能再次执行。
幂等设计要点(社区共识 + 官方约束):
- 先写 state,再做副作用:通过 reducer / state 决定"是否要执行",避免重复发邮件、重复扣款。
- 副作用前要查:在 IO 节点开头检查"是否已执行过"(基于 state 标志位或业务唯一键)。
- LLM 调用天然幂等 (同输入同输出),但有副作用的工具(HTTP POST、扣款、发邮件)必须自己保证。
- ToolNode + 自定义工具 :把
idempotency_key暴露给工具,由业务实现去重。
2.7 错误处理(本章仅涉及节点级规范;后续章节展开)
- 可恢复 vs 不可恢复分类(推测性建议):网络瞬时错误、限流可重试;数据校验失败、参数错误不可重试。
- RetryPolicy 在节点级声明(langgraph 标准接口,详见 https://docs.langchain.com/oss/python/langgraph/graph-api#retry-policies),不写裸
try/except吃掉错误。 - 错误必须进入 state 或 trace(社区共识):不要让节点失败后只 print 一行日志;要用 state 字段 / LangSmith metadata 留下证据。
3. 不推荐做法(Graph 设计反模式)
- State 字段过多、嵌套过深 (社区共识)。State 是图的灵魂;太大会拖慢序列化、破坏 checkpoint 性能。来源: https://docs.langchain.com/oss/python/langgraph/persistence
- 在 state 里塞 PII / secret(事实+判断)。State 会被 checkpoint 落盘,泄露面=DB 泄露面;PII/secret 走专门存储(KMS / Vault)。社区共识。
- 在 state 里塞不可序列化对象 (事实,interrupt 文档明确要求 JSON 可序列化)。来源: https://docs.langchain.com/oss/python/langgraph/interrupts
- 节点内 from-state-AND-mutate-state。把 state 当 mutable container 会绕过 reducer、破坏可重入。社区共识。
- 路由函数访问网络 / 数据库 / 时间 (事实+判断)。破坏纯函数假设,无法单元测试,无法 Time Travel。来源: https://docs.langchain.com/oss/python/langgraph/graph-api
- Graph 循环没有显式终止条件 (事实+判断)。即使有
recursion_limit,业务循环必须有自己的"我决定停"条件。来源: https://docs.langchain.com/oss/python/langgraph/graph-api - 同一节点同时用
Command和add_edge路由 (事实,官方明确禁止)。来源: https://docs.langchain.com/oss/python/langgraph/graph-api - 静态 interrupt 充当 HITL (事实,官方明确不推荐)。来源: https://docs.langchain.com/oss/python/langgraph/interrupts
- 裸
try/except包interrupt()(事实,官方不推荐 bare try/except,可能吞掉暂停异常)。注意:带显式 re-raise 的合理封装不在禁止之列------interrupt依赖特殊异常机制,bare 形式的try/except会破坏语义。来源: https://docs.langchain.com/oss/python/langgraph/interrupts interrupt()payload 含函数 / 不可序列化对象 (事实,官方明确禁止)。来源: https://docs.langchain.com/oss/python/langgraph/interrupts- 节点副作用不可重入 (事实,官方明确要求幂等)。来源: https://docs.langchain.com/oss/python/langgraph/interrupts + https://docs.langchain.com/oss/python/langgraph/functional-api
- 把 LangGraph 当 job scheduler / 业务数据库。它不替代 Temporal / Airflow / Postgres。社区共识。
- 手动拼接
from langchain_core.messages import ...而不使用add_messagesreducer 。会出现消息重复 / ID 冲突。来源: https://github.com/langchain-ai/langgraph/releases/tag/v1.2.2
4. 最小代码示例
依赖:langgraph>=1.2,<2,Python ≥ 3.10
4.1 State schema + Reducer 最小骨架
python
"""
覆盖:TypedDict / Annotated reducer / add_messages / 私有/输出 state
"""
from typing import TypedDict, Annotated, Literal
import operator
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.checkpoint.memory import InMemorySaver
class State(TypedDict):
# 短期:消息历史,用 add_messages reducer
messages: Annotated[list, add_messages]
# 业务:追加式 trace
trace: Annotated[list[str], operator.add]
# 私有:路由决策(每步覆盖即可)
next_action: str
# 终止标志
done: bool
# 业务循环计数器(防止 should_continue 始终返回 "call_llm" 时死循环)
iteration_count: int
MAX_ITER = 3
def call_llm(state: State) -> dict:
return {
"messages": [{"role": "assistant", "content": "hello"}],
"trace": ["call_llm"],
"iteration_count": state.get("iteration_count", 0) + 1,
}
def should_continue(state: State) -> Literal["call_llm", "__end__"]:
if state.get("done"):
return "__end__"
if state.get("iteration_count", 0) >= MAX_ITER: # 业务终止守卫
return "__end__"
return "call_llm"
def finalize(state: State) -> dict:
return {"done": True, "trace": ["finalize"]}
g = StateGraph(State)
g.add_node("llm", call_llm)
g.add_node("finalize", finalize)
g.add_edge(START, "llm")
g.add_conditional_edges("llm", should_continue)
g.add_edge("finalize", END)
g.add_edge("finalize", "llm") # finalize → llm 形成循环,由 should_continue 终止
app = g.compile(checkpointer=InMemorySaver())
【官方能力】来源: https://docs.langchain.com/oss/python/langgraph/graph-api + https://docs.langchain.com/oss/python/langgraph/persistence · 2026-06 抓取 · 1.2.4
4.2 Command + Send 最小骨架
python
"""
覆盖:Command(update=..., goto=...); Send 并行; 中断续跑
"""
from typing import TypedDict, Literal
from langgraph.graph import StateGraph, START
from langgraph.types import Command, Send
from langgraph.checkpoint.memory import InMemorySaver
class State(TypedDict):
items: list[str]
results: list[str]
def fanout(state: State):
# Send 用于 map-reduce 风格 fan-out
return [Send("process", {"item": i, "results": []}) for i in state["items"]]
def process(payload: dict) -> Command:
return Command(
update={"results": [f"processed:{payload['item']}"]},
goto="join",
)
def join(state: State) -> Command[Literal["__end__"]]:
# 注意:Command 返回需带 goto 类型注解
return Command(update={}, goto="__end__")
g = StateGraph(State)
g.add_node("process", process)
g.add_node("join", join)
g.add_conditional_edges(START, fanout)
g.add_edge("join", "__end__")
app = g.compile(checkpointer=InMemorySaver())
【官方能力】来源: https://docs.langchain.com/oss/python/langgraph/graph-api · 2026-06 抓取 · 1.2.4
4.3 interrupt + Command(resume) 最小骨架
python
"""
覆盖:节点内 interrupt() + 续跑;多 interrupt 映射
"""
from langgraph.types import interrupt, Command
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import InMemorySaver
from typing import TypedDict
class State(TypedDict):
needs_review: bool
text: str
def draft(state: State) -> dict:
return {"text": "drafted", "needs_review": True}
def review(state: State) -> dict:
# interrupt 会暂停整个图;payload 给调用方;resume 值作为返回值
decision = interrupt({"prompt": "Approve?", "content": state["text"]})
return {"text": state["text"] + ("" if decision else "_REJECTED")}
g = StateGraph(State)
g.add_node("draft", draft)
g.add_node("review", review)
g.add_edge(START, "draft")
g.add_edge("draft", "review")
g.add_edge("review", END)
app = g.compile(checkpointer=InMemorySaver())
cfg = {"configurable": {"thread_id": "u1"}}
# 第一次:会停在 interrupt;stream_events 模式下 stream.interrupted=True
# 第二次:用 Command(resume=True) 续跑
app.invoke({"needs_review": False, "text": ""}, config=cfg)
app.invoke(Command(resume=True), config=cfg)
【官方能力】来源: https://docs.langchain.com/oss/python/langgraph/interrupts · 2026-06 抓取 · 1.2.4
5. 检查清单(第 3 章)
- State schema 已选定类型(TypedDict / Pydantic v2 / dataclass / MessagesState)
- State 字段全部 JSON 可序列化、无 PII / secret
- 追加式字段均带 reducer(
Annotated[list, operator.add]或add_messages) - 节点只返回增量 state update,不就地修改
- 节点按 LLM / 工具 / 纯函数 / IO 四类拆分,职责单一
- 路由函数全部为纯函数(无 I/O / 无随机 / 无时间)
- 路由函数有单元测试覆盖主要分支
- 路由决策进入 LangSmith / 自有日志
- 仅在"需要同时更新 state + 路由"时用 Command;其余用 conditional edge
- 同一节点不混用 Command 与 add_edge
- 动态并行用 Send 列表,不用 asyncio.gather 自实现
- HITL 一律用
interrupt()+Command(resume=...),不用 static interrupt -
interrupt()payload 仅含 JSON 可序列化数据,不套 try/except - 业务循环有显式终止条件(state 计数器 / 业务终态);不仅依赖
recursion_limit - 节点副作用幂等(LLM 调用天然幂等;业务 IO 必须自保证)
- 错误进入 state / trace / log,未被静默吞掉
- 节点级
RetryPolicy用于可恢复错误(限流、网络瞬时) - 至少有一组"给定输入 → 期望路径 → 期望终态"的集成测试
第 4 章:状态持久化、恢复与 HITL
4.1 Checkpointer 选型:MemorySaver / SqliteSaver / PostgresSaver
解决什么问题
LangGraph 把 graph 编译时绑定的 BaseCheckpointSaver 实现作为「运行态恢复点」的总入口。线程(thread_id)下每次 super-step 完成都会生成一个 checkpoint,没有 checkpointer,HITL / Time Travel / 断线恢复全部不可用。选型的核心问题在「持久性 + 多 worker 并发安全 + 部署形态」三者之间的权衡。
来源:LangGraph 持久化官方概念页 官方
- https://docs.langchain.com/oss/python/langgraph/persistence (2026-05 抓取)
- PyPI
langgraph-checkpoint==4.1.1Release notes 官方:https://pypi.org/project/langgraph-checkpoint/ - PyPI
langgraph-checkpoint-postgres==3.1.0(2026-05-12)官方:https://pypi.org/project/langgraph-checkpoint-postgres/ - PyPI
langgraph-checkpoint-sqlite==3.1.0(2026-05-12)官方:https://pypi.org/project/langgraph-checkpoint-sqlite/
推荐做法
- 开发/单进程测试 :用
langgraph.checkpoint.memory.InMemorySaver。别名 :MemorySaver在源码中以MemorySaver = InMemorySaver # Kept for backwards compatibility形式保留,未正式标 deprecated ,旧代码可继续 import;新代码建议直接用InMemorySaver。来源:官方源码 https://github.com/langchain-ai/langgraph/blob/main/libs/checkpoint/langgraph/checkpoint/memory/**init**.py (2026-05 抓取) - 本地小型持久化(单机桌面、本地脚本) :用
SqliteSaver(同步)或AsyncSqliteSaver(异步),数据库可以是:memory:或磁盘.db文件。 - 生产(多 worker / 跨节点 / 跨进程) :用
PostgresSaver/AsyncPostgresSaver,首次部署需要checkpointer.setup()建表;底层默认走 Psycopg 3。 - 完全托管 :直接使用 LangSmith Deployment / Agent Server,它内建托管 checkpointer(含跨副本恢复)。官方
- https://docs.langchain.com/langsmith/deployment (2026-05 抓取)
- Durability 模式 (v1.0+ 引入):在
invoke/stream时传入durability参数,只在生产模式关心 :"exit":仅在执行退出时持久化(最低延迟,但崩溃可能丢状态)"async":下一步运行的同时持久化(折衷)"sync":下一步开始前完成持久化(最安全,吞吐最低)
来源:官方 https://docs.langchain.com/oss/python/langgraph/persistence
- 安全加固 :Postgres 后端默认用 msgpack 反序列化;高敏场景应启用
LANGGRAPH_STRICT_MSGPACK=true或显式指定 allowed modules。官方 https://pypi.org/project/langgraph-checkpoint-postgres/
不推荐做法
- ❌ 不要把
InMemorySaver用在生产 :源码里delete_thread/ 序列化都跑在 Pythondefaultdict上,进程退出即丢;并且aget_tuple等异步方法其实是 sync 代理,事件循环会被序列化 CPU 工作阻塞 ------意味着「看上去是 async」但并发收益为 0。官方源码 https://github.com/langchain-ai/langgraph/blob/main/libs/checkpoint/langgraph/checkpoint/memory/**init**.py - ❌ 不要让多个 worker 共享同一个
InMemorySaver实例:跨进程的内存对象无法同步,state 视图必然发散。 - ❌ 不要把 Checkpointer 当成「长期记忆」 ------它的生命周期是 thread 绑定的,跨 thread 不能查询;跨 user 持久化请用
BaseStore(见 4.2)。官方 https://docs.langchain.com/oss/python/langgraph/memory
最小代码示例(langgraph 1.2.x)
python
# 依赖:langgraph>=1.2、langgraph-checkpoint>=4.1
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import InMemorySaver
class State(TypedDict):
input: str
output: str
def node_a(state: State):
return {"output": f"Processed: {state['input']}"}
builder = StateGraph(State)
builder.add_node("node_a", node_a)
builder.add_edge(START, "node_a")
builder.add_edge("node_a", END)
graph = builder.compile(checkpointer=InMemorySaver())
config = {"configurable": {"thread_id": "thread-1"}}
print(graph.invoke({"input": "hello", "output": ""}, config))
Postgres 版(仅替换 import / 构造):
python
# 依赖:langgraph-checkpoint-postgres>=3.1
from langgraph.checkpoint.postgres import PostgresSaver
checkpointer = PostgresSaver.from_conn_string("postgresql://user:pwd@host/db")
checkpointer.setup() # 首次必须
graph = builder.compile(checkpointer=checkpointer)
检查清单
- ☐ 选型是否匹配部署形态:单进程 / 多 worker / 托管?
- ☐ 是否调用过
PostgresSaver.setup()?是否把setup()放入部署管线? - ☐ 是否启用
LANGGRAPH_STRICT_MSGPACK/ 显式 allowlist? - ☐ 生产图是否传
durability参数并明确选择档位? - ☐ 是否把
InMemorySaver误用到生产或测试套件之外的临时集群? - ☐ Thread key 是否避免写入 PII 原文(用不可逆的 user_id hash)?
- ☐ 是否在节点函数里做了幂等(resume 时节点会从头跑)?
- ☐ 是否在 dump / 备份时排除消息中的敏感字段(见 4.4)?
4.2 长期记忆:BaseStore / InMemoryStore / namespace / 向量化 / TTL
解决什么问题
短时记忆(thread 内的消息历史)只服务一次会话。跨会话、跨 user 的事实 / 偏好 / 反馈 必须放到一个独立于 thread 的存储里。LangGraph 通过 langgraph.store.base.BaseStore 抽象提供「跨 thread」的命名空间读写 + 可选语义检索。
来源:官方 https://docs.langchain.com/oss/python/langgraph/memory (2026-05 抓取)
推荐做法
-
存储三类记忆 :
类型 含义 Agent 例子 Semantic 事实、用户属性 "用户偏好短句回答" Episodic 历史经历、few-shot 样本 "上次调 SQL 出错,用户纠正..." Procedural 行为规则、系统提示 "反思后更新的 system prompt" 来源:官方 同上 -
组织方式 :
- Profile 模式:单条 JSON 文档不断合并/覆盖(适合稳定结构化用户档案)
- Collection 模式:多条窄粒度文档 + 语义检索(适合更细颗粒的事实)
-
namespace / key :用 tuple 形式
("user_id", "category")作 namespace,键是文档 ID;等价于「文件夹 + 文件名」。 -
写入时机 :
- 热路径(agent 实时写入):一致性最好但每次调用都增加延迟与复杂度
- 后台任务(独立 task 周期写入):不影响主链路,但需要明确刷写频率
-
向量化检索 :在
InMemoryStore(index={"embed": embed_fn, "dims": N})中传入 embedding 函数;后续store.search(namespace, query="...")自动返回 top-k。 -
生产 BaseStore 后端(待验证/需自建) :langgraph 1.2.x 在 PyPI 上没有与
langgraph-checkpoint-postgres对应的官方langgraph-store-postgres包 ------BaseStore 的生产持久化方案在社区中仍以 InMemoryStore + 外挂方案为主 ,或使用 LangSmith Deployment 内建 store。待补 生产 BaseStore 后端的成熟度 :- 官方 LangSmith Deployment 提供托管 store(thread-scoped TTL + cross-thread KV),但不是开源可自部署的 Postgres store 包。来源:官方 https://docs.langchain.com/langsmith/deployment
- 社区方案(自建 Postgres / Redis / SQLite + 自实现
BaseStore接口)需要在团队内做轻量封装,不是开箱即用 。推测 - 不要用
langgraph-checkpoint-postgres替代 BaseStore :checkpoint 包只实现BaseCheckpointSaver接口,与BaseStore接口(put/get/search/delete)不兼容;强行混用会导致类型错误。
-
TTL 生命周期 :
- Checkpoint:生命周期由 thread 决定;LangSmith Deployment 提供 thread-level TTL(过期清空 thread 的所有 checkpoint)。
- BaseStore 记录 :没有内建 TTL 字段 ;需要应用层定时清理(
store.delete(namespace, key))或部署侧 LRU。 - 二者不可混淆:thread TTL 不会清空 BaseStore 记录;删除 thread 也不会自动清空 store。来源:官方 https://docs.langchain.com/langsmith/deployment
不推荐做法
- ❌ 不要把长期记忆塞进 Checkpointer :Checkpointer 的粒度是 thread,没有跨 thread 查询能力,强行用 checkpoint 做大记忆会让
get_state_history爆炸。 - ❌ 不要把
InMemoryStore的index=...用作安全过滤 :它只做相似度排序,不替代显式filter={"user_id": ...}这类 hard filter------否则会发生「跨用户记忆泄露」。推测 基于机制推断 - ❌ 不要在节点里同步写 store 不做超时:长期记忆写入应该是可丢弃的副作用,失败时宁可丢掉也别阻塞主链路。
最小代码示例
python
# 依赖:langgraph>=1.2、langgraph-checkpoint>=4.1
from langgraph.graph import StateGraph, START, END
from langgraph.store.memory import InMemoryStore
from langgraph.checkpoint.memory import InMemorySaver
# 假定的 embedding 函数(实际用 langchain 的 embedder)
def embed(texts: list[str]) -> list[list[float]]:
return [[0.0] * 8 for _ in texts]
store = InMemoryStore(index={"embed": embed, "dims": 8})
namespace = ("user-42", "profile")
store.put(namespace, "preferences", {"rules": ["用户偏好短句"]})
# 语义检索
hits = store.search(namespace, query="回答风格", limit=3)
for doc in hits:
print(doc.key, doc.value, doc.score)
在 compile 时把 store 注入图(节点用 runtime.store 访问):
python
graph = builder.compile(checkpointer=InMemorySaver(), store=store)
来源:官方 https://docs.langchain.com/oss/python/langgraph/memory
检查清单
- ☐ Checkpointer 与 BaseStore 边界是否清晰:跨 thread → store;thread 内 → checkpoint?
- ☐ namespace 是否用稳定 user id(不可逆)做第一段?
- ☐ 是否在语义检索前用 hard filter(
user_id/tenant_id)收紧? - ☐ 是否给 store 写入做了超时 + 降级(写入失败是否回退到默认行为)?
- ☐ 是否给「profile 模式」设置版本号,避免新旧字段混在一起读?
- ☐ 是否在生产使用 DB-backed store(Postgres / 后续 store 后端;当前官方未提供,需自建或用 LangSmith 托管)?
- ☐ 删除/退订场景下是否同步清理 store(GDPR / 用户删除)?
4.3 状态序列化、版本演进、敏感数据处理
解决什么问题
LangGraph 用 msgpack + 自定义 serde 把 State 持久化到 checkpoint/store。生产环境关心三类问题:
- 跨版本兼容:升级 langgraph 后旧 checkpoint 还能不能读?
- 自定义类型:Pydantic v2 / dataclass / TypedDict 都能进 state 吗?
- 敏感数据:PII / 凭据落盘前如何处理?
推荐做法
- State 类型首选 :
TypedDict(最轻)/dataclass/ Pydantic v2 model;valuesstream mode 在 1.1+ 会把 Pydantic/dataclass 自动 coerce 成正确类型。来源:官方 https://docs.langchain.com/oss/python/langgraph/streaming - 跨版本兼容 :
langgraph-checkpoint维护了 envelopelc:2之类的版本号;高安全场景启用LANGGRAPH_STRICT_MSGPACK=true,避免任意对象通过 envelope revival 复活。官方源码 https://github.com/langchain-ai/langgraph/blob/main/libs/checkpoint/ - 敏感数据 :
- 在写入 checkpoint 前主动 redact(不要在事后清理)
- 用单独的
secretsnamespace 存到 Key Vault / 外部,state 里只放引用 - 加密静态数据:Postgres 后端可以走透明加密 / 列加密
不推荐做法
- ❌ 不要在 state 里放原始 token / 密码 / 私钥------它们会随 checkpoint 落盘,且
get_state_history能直接读到。 - ❌ 不要让 state 持有不可序列化 对象(数据库连接、文件句柄、open socket);langgraph-checkpoint 用 msgpack + 自定义 serde 持久化,与 Python pickle 协议不直接相关 ------判定标准是「能否被 msgpack / 自定义 serde 序列化」,不是「能否被 pickle」。来源:官方源码 https://github.com/langchain-ai/langgraph/blob/main/libs/checkpoint/langgraph/checkpoint/memory/**init**.py
- ❌ 不要把大文件直接塞 state------pg 单行 / msgpack 都有限制;外链 + 存路径/哈希更稳。推测
最小代码示例
python
from typing_extensions import TypedDict
from pydantic import BaseModel
class UserProfile(BaseModel):
name: str
redacted_email: str # 写入前先在节点里脱敏
class State(TypedDict):
profile: UserProfile
notes: str
检查清单
- ☐ state 字段是否全为可序列化类型?
- ☐ 是否对 PII 做了 redact / 哈希后再写入 state?
- ☐ 是否启用
LANGGRAPH_STRICT_MSGPACK? - ☐ 升级 langgraph 大版本前是否做过 checkpoint 读回烟测?
- ☐ 是否有删除/退订路径:thread 删除 + store 清空 + checkpoint 清理?
4.4 Human-in-the-Loop:interrupt / resume / Command / 审批
解决什么问题
Agent 在执行中可能做出不可逆动作(写库、付款、调用外部 API)。HITL 要求在关键决策点暂停 graph,等人类批准/编辑后再继续 。LangGraph 用 interrupt() + Command(resume=...) 模式实现。
来源:官方 https://docs.langchain.com/oss/python/langgraph/interrupts (2026-05 抓取)
推荐做法
- 三件套 :必须同时具备 checkpointer、configurable
thread_id、JSON-serializable 负载。 - 基本模式 :
- 在节点中调用
value = interrupt(payload)暂停,state 自动持久化 - 外部系统通过
graph.invoke(Command(resume=value), config)恢复 - 节点会从头重启 (不是从
interrupt后继续),interrupt()的返回值就是resume的值
- 在节点中调用
- 多种工作流 :
- Approve/Reject:传
True/False给Command(resume=...) - Review & Edit:人类修改 LLM 输出再继续
- 工具内审批:把审批逻辑放进
@tool函数里 - 输入校验:
while True: ans = interrupt("..."); if valid: break - 多分支并行:用 GraphOutput.interrupts 列表的索引映射到不同 resume 值(Python 原生 API,不是 SDK 投影)。
- Approve/Reject:传
- interrupt 在 Python 端的检测 :
graph.invoke(...)在 1.0+ 返回GraphOutput对象;遇到 interrupt 时out.interrupts非空。- stream v2 chunk 形态说明 :v2 chunk 字典结构是
{"type": str, "ns": tuple, "data": ...},没有next字段 。next是graph.get_state(config)返回的StateSnapshot上的字段(语义为「下一个待执行节点 tuple」),不是 stream chunk 的字段。 - 在
astream中检测 interrupt 的正确方式:- 走
stream_mode="checkpoints"模式订阅,写入事件存储后由 watcher 读StateSnapshot.next判断是否被挂起; - 或 走
stream_mode="updates"+stream_mode="custom"组合 + 业务层在invoke后读GraphOutput.interrupts; - 或 监听 astream 中含
__interrupt__键的 chunk(旧 v1 风格,v2 中已被GraphOutput.interrupts取代)。
- 走
- 通过
updates模式观察node_name的 state delta 只能辅助判断节点级进度,不能直接判定是否被 interrupt 挂起。 - 不要在 Python 端用 SDK 投影 :
stream.interrupted/stream.interrupts/stream.output是@langchain/langgraph-sdk(JS SDK)包装对象属性,不是CompiledStateGraph原生属性。
- Subgraph interrupt :子图里的
interrupt在 resume 时,父节点和子图节点都会从头重启------这是个对幂等性的硬要求。
不推荐做法
- ❌ 不要把
interrupt()包进 try/except:它靠抛异常暂停,捕获会导致暂停失败。 - ❌ 不要重排
interrupt调用顺序 :resume 时按索引匹配,乱序会错位。官方 同上 - ❌ 不要在
interrupt之前做非幂等副作用(写库、发邮件、扣款)------节点会从头跑,副作用会重复执行。 - ❌ 不要用静态断点
interrupt_before/interrupt_after做生产 HITL :官方明确说它只用于调试。 - ❌ 不要在 payload 里传非 JSON-serializable 对象(函数、类实例、生成器)。
最小代码示例
python
# 依赖:langgraph>=1.2
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.types import Command, interrupt
class State(TypedDict):
plan: str
approved: bool
def propose(state: State):
plan = "向 user-1 发起退款 $50"
# 关键决策点:暂停,等人类批准
ok = interrupt({"question": "是否批准此操作?", "plan": plan})
return {"plan": plan, "approved": bool(ok)}
builder = StateGraph(State)
builder.add_node("propose", propose)
builder.add_edge(START, "propose")
builder.add_edge("propose", END)
graph = builder.compile(checkpointer=InMemorySaver())
cfg = {"configurable": {"thread_id": "t-1"}}
# 第一次:走到 interrupt 暂停
graph.invoke({"plan": "", "approved": False}, cfg)
# 人类批准后:传 Command(resume=True) 恢复
graph.invoke(Command(resume=True), cfg)
来源:官方 https://docs.langchain.com/oss/python/langgraph/interrupts
检查清单
- ☐ 是否每个
interrupt前后的副作用都做过幂等(写库加幂等键、邮件发送前先查重)? - ☐ 是否避免用
try/except包裹interrupt? - ☐ payload 是否都是 JSON-serializable?
- ☐ 是否在多分支并行时显式 map
interrupt.id → resume? - ☐ 是否避免使用
interrupt_before/after做生产审批? - ☐ 子图 interrupt 时父节点是否做了幂等?
- ☐ 是否有
interrupted=True时的 UI 反馈(人类需要看到「等你审批」)?
4.5 Time Travel 与 Fork:get_state_history / update_state / 分叉调试
解决什么问题
Production agent 跑偏了,要从中间某个 checkpoint 重放,或改完 state 后分叉出新分支。LangGraph 把每次 super-step 都落盘成 checkpoint,于是:
get_state_history(config)→ 倒序列出所有 checkpointupdate_state(config, values)→ 基于当前 state 改值,生成新 checkpoint(不改旧的)- 用
checkpoint_id拿到历史 config,可以graph.invoke(None, past_config)重放
来源:官方 https://docs.langchain.com/oss/python/langgraph/persistence
推荐做法
-
StateSnapshot 字段 :
values/next/config/metadata/created_at/parent_config/tasks。 -
在历史中定位 :
pythonhistory = list(graph.get_state_history(config)) step1 = next(s for s in history if s.metadata["step"] == 1) done = next(s for s in history if s.next == ()) -
Fork 分支 :拿到旧 config 后
graph.invoke(None, past_config)会从该点继续。update_state再写一份新值就分叉了。 -
审计/调试 UI :把
history暴露给 UI,做「时光机」交互。
不推荐做法
- ❌ 不要在生产 hot path 上调
get_state_history()------它会拉全量 history。 - ❌ 不要把
update_state看作「改旧 checkpoint」,它总是新增。 - ❌ 不要在 fork 后继续依赖原
thread_id不变,如果业务上想区分两条分支建议换 thread_id。
最小代码示例
python
cfg = {"configurable": {"thread_id": "t-1"}}
history = list(graph.get_state_history(cfg))
target = history[-2] # 倒数第二个
# 重放
graph.invoke(None, target.config)
# 分叉:改值
graph.update_state(cfg, {"output": "手动覆盖"})
来源:官方 https://docs.langchain.com/oss/python/langgraph/persistence
检查清单
- ☐ 是否暴露
get_state_history给内部调试 UI? - ☐ fork 后是否换
thread_id区分新旧分支? - ☐
update_state写入是否走 RBAC / 审计日志? - ☐ 是否避免在 hot path 上拉全量 history?
- ☐ 是否在 history 中清理敏感字段(避免 PII 留存)?
4.6 HITL 的 timeout / escalation 策略(参考架构 / 推测性建议)
本节定位 :LangGraph 官方文档未给出 HITL timeout / escalation 的统一推荐实现;本节内容为参考架构 与推测性建议,非官方能力。生产落地需结合 job scheduler / worker 协调做自建。
解决什么问题
人类可能永不审批:请假、系统宕机、通知丢失。无限等待的 interrupt = 内存泄漏 / 任务挂死。需要给每个 interrupt 配超时与升级路径。
参考架构(流程式,不给完整代码)
- 记录 pending interrupt(两条路径,二选一) :
- 路径 A(推荐 / 解耦) :业务层在
graph.invoke(...)拿到GraphOutput后立即读out.interrupts,把{thread_id, run_id, interrupt_payload, created_at}写入外部事件存储(Kafka / Redis Stream / Postgres)。注意 :GraphOutput是invoke的同步返回值,不会"持久化"或被 watcher 直接读;必须由业务代码主动落库。 - 路径 B(流式 / 单进程) :在
astream(stream_mode=["updates", "checkpoints"], version="v2")流中过滤含__interrupt__键的 chunk (v1 风格)或走checkpoints模式订阅StateSnapshot,由流处理函数在内存/Redis 中维护 pending 集合。 - 两条路径都需要把 pending 记录落入持久存储(否则进程崩溃会丢失状态)。
- 路径 A(推荐 / 解耦) :业务层在
- Watcher 扫描:独立 worker 周期扫描「pending 时长 > SLA」的 case;建议多实例用 leader election(Redis lock / Postgres advisory lock)避免重复 resume。
- 注入 resume :watcher 调用
graph.invoke(Command(resume={"approved": False, "reason": "timeout"}), cfg)走自动拒绝路径;或调用Command(resume={"escalate_to": "manager_id"})走升级路径。 - 审计:resume 事件进入审计日志(who/when/why),与 GraphCheckpoint 关联。
- 幂等要求 :每个 resume 必须幂等------节点会从头跑,副作用要靠幂等键保证不重复。
推测性建议(待补 / 自建)
- 超时不应当默认自动通过:会绕过安全网。生产推荐默认自动拒绝 + 升级。
- 避免
threading.Timer触发 resume:跨进程失效。把超时放外部 worker / sidecar。 - 并行审批做 fan-in:不要假设一次 interrupt 只等一个人。
不推荐做法
- ❌ 不要在 LangGraph 内部用
threading.Timer触发 resume------会跨进程失效。把超时机制放外面(worker / edge / sidecar)。推测 - ❌ 不要把"超时自动通过"作为默认------会绕过安全网。
- ❌ 不要用
asyncio.wait_for(graph.ainvoke(...))来"截断" interrupt------该调用并不能识别 graph 内部被 interrupt 挂起,超时只是把外层协程结束,graph 仍在 wait resume 状态;需要靠独立 watcher 注入 resume。 - ❌ 不要假设一次 interrupt 只可能等一个人------并行审批时要做 fan-in。
最小代码示例(仅作 watcher 形态示意,非生产级)
python
# 伪代码:watcher 周期扫描 pending interrupt 并注入 timeout resume
import asyncio
from langgraph.types import Command
async def timeout_watcher(graph, store, cfg, *, sla_seconds: int = 300):
while True:
await asyncio.sleep(10)
# 1. 从事件存储拉 pending interrupt
for rec in store.pending_interrupts(older_than=sla_seconds):
# 2. 拿分布式锁避免多 watcher 重复 resume
if await store.acquire_resume_lock(rec.run_id):
# 3. 注入 timeout resume(自动拒绝 / 升级由业务决定)
graph.invoke(Command(resume={"approved": False, "reason": "timeout"}),
{"configurable": {"thread_id": rec.thread_id}})
store.mark_resumed(rec.run_id, by="timeout_watcher")
来源:推测 基于 LangGraph interrupt 语义 + 一般分布式系统模式;社区方案未找到统一最佳实践,待补 。建议参考 Temporal / Inngest 等工作流引擎的 SLA 模式,但不要直接照搬------LangGraph 的 HITL 与这些系统的中断模型不同。
检查清单
- ☐ 每个 interrupt 是否配了 SLA 超时?
- ☐ 超时后是自动拒绝还是自动升级?
- ☐ 超时事件是否进入可观测体系(pending 时长分布)?
- ☐ 超时 watcher 是否多实例 + 分布式锁(避免单点 / 重复 resume)?
- ☐ 自动通过的策略是否需要二次审批才允许?
- ☐ resume 副作用是否幂等(写库加幂等键)?
第 5 章:流式输出与前端集成
版本锚点:
langgraph==1.2.4(2026-06-02 PyPI latest);Python ≥ 3.10。本章主线 :LangGraph Python 原生
CompiledStateGraph.stream/astream/astream_events的输出语义、协议与生命周期;前端消费(React / Next.js / JS SDK / SSE / WebSocket)只作为消费边界小节,不构成 Python API 主线 。所有 JS SDK 表述均明确标注「非 Python 主线 」。资料类型标记:官方 = 官方文档 / GitHub / 官方博客;社区 = 社区博客 / 实战;推测 = 基于机制推断的最佳实践。
5.1 stream 模式全景:values / updates / messages / events / custom
解决什么问题
一个 graph 跑起来有 6 件事可能被订阅:每步完整 state、每步增量、按 token 的模型输出、自定义业务事件、checkpoint 事件、任务开始/结束事件。LangGraph Python 通过 stream_mode 参数让你按需订阅。
来源:官方 https://docs.langchain.com/oss/python/langgraph/streaming (2026-05 抓取)
| 模式 | 输出 | 典型用途 |
|---|---|---|
values |
每步完整 state | 调试、最终回显 |
updates |
每步 state 增量 | 节点级进度 |
messages |
(token_chunk, metadata) 2-tuple |
token 流式输出 |
custom |
get_stream_writer() 自定义 |
业务事件 |
checkpoints |
checkpoint 事件 | 持久化可见性 |
tasks |
任务开始/结束 | 进度条 |
debug |
checkpoints + tasks + 额外 metadata | 调试 |
Python 原生输出形态(langgraph ≥ 1.1) :传
version="v2"后,所有 stream_mode 统一 返回StreamPart字典,键为type/ns/data。type取值与stream_mode一一对应(values/updates/messages/custom/checkpoints/tasks/debug);ns是 namespace tuple,根图为(),子图为非空 tuple,可用于父子事件隔离;data是各 mode 的载荷。来源:官方 https://docs.langchain.com/oss/python/langgraph/streaming
推荐做法
- 同时订阅多个 mode:
stream_mode=["updates", "custom"],按chunk["type"]分发到不同 UI 区。 messages模式 +metadata["langgraph_node"]过滤出具体节点的 token。custom模式 +get_stream_writer():在节点/工具里推任意事件(loading 状态、文件 ID、引用来源)。- 启用
subgraphs=True后按 v2ns字段拆解 root / 子图事件------多 agent 必看。 - 升
1.0+后invoke返回GraphOutput对象,含.value与.interrupts属性(取代 v1 的__interrupt__字典)。来源:官方 同上
不推荐做法
- ❌ 不要用
messages当业务事件总线:它专给 LLM token 用,业务事件请走custom。 - ❌ 不要在同一调用里既传
subgraphs=True又在 v1__interrupt__解析(v1 API 已被GraphOutput取代)。官方 同上 - ❌ Python < 3.11 下
astream+ 异步 LLM 调ainvoke时必须显式传RunnableConfig------否则 event 不会回流。官方 同上 - ❌ Python < 3.11 下不要在 async 节点里用
get_stream_writer(),改用writer: StreamWriter形参。
最小代码示例
python
# 依赖:langgraph>=1.2
from langgraph.graph import StateGraph, START, END
from typing_extensions import TypedDict
class State(TypedDict):
topic: str
draft: str
def writer_node(state: State):
from langgraph.config import get_stream_writer
w = get_stream_writer()
w({"stage": "thinking", "topic": state["topic"]})
return {"draft": f"Draft about {state['topic']}"}
builder = StateGraph(State)
builder.add_node("writer", writer_node)
builder.add_edge(START, "writer")
builder.add_edge("writer", END)
graph = builder.compile()
for chunk in graph.stream(
{"topic": "ice cream", "draft": ""},
stream_mode=["updates", "custom"],
version="v2",
):
if chunk["type"] == "updates":
for node, delta in chunk["data"].items():
print(f"node={node} delta={delta}")
elif chunk["type"] == "custom":
print(f"event: {chunk['data']}")
来源:官方 https://docs.langchain.com/oss/python/langgraph/streaming
检查清单
- ☐ 前端拿到的是 v2
StreamPart吗?是否按type分发? - ☐ 是否订阅了
custom模式来推送业务事件? - ☐ 多 agent / 子图是否启用
subgraphs=True+ 用ns隔离? - ☐
messages模式是否按metadata.langgraph_node过滤到正确 UI 区? - ☐ Python 版本是否低于 3.11?若是,是否显式
ainvoke(..., config)? - ☐ 是否避免在
messages模式推业务事件? - ☐ 客户端是否有 reconnect / buffer 策略(防丢 chunk)?
5.2 token streaming vs 状态/事件 streaming
解决什么问题
两类实时反馈有本质差异:
- token streaming :连续小增量,关心吞吐 / 首字延迟 / 打字机效果
- 状态/事件 streaming :离散事件,关心可重放 / 顺序一致 / 边界清晰
把两类混用会丢帧、漏状态。
推荐做法
- token 走 SSE(HTTP 长连接、文本流、自带断点续传友好)。
- 状态/事件 走 WebSocket 或 SSE 都可------但需要明确的事件协议(参考下文 5.5)。
- 前端 UI:打字机区只消费
messages模式;侧栏进度条只消费updates模式;toast / 业务提示只消费custom模式。 - 协议层 :v2
StreamPart已经统一信封,前端只需要做"按 type 分发"。官方
不推荐做法
- ❌ 不要把 token 推上
custom模式------失去 LangChain 的messages元数据(langgraph_node、tags),做节点级过滤会很麻烦。 - ❌ 不要把"完整 state 重发"当 token 流------浪费带宽,前端 diff 成本也高。
- ❌ 不要混用两个 stream mode 而不做 v2 适配------v1 / v2 数据结构不一致,并发解析会出 bug。
最小代码示例
- 见 5.1 最小代码(已经体现
messages与custom分发)。 - 前端伪代码(React 消费形态示意,具体端点路径与后端实现以 5.3 为准):
tsx
// 配合 v2 + SSE(路径与 5.3 后端对齐:/api/runs/{run_id}/stream)
const es = new EventSource(`/api/runs/${runId}/stream`);
es.onmessage = (ev) => {
const part = JSON.parse(ev.data);
switch (part.type) {
case "messages": appendToken(part.data); break;
case "updates": renderProgress(part.data); break;
case "custom": showToast(part.data); break;
}
};
来源:社区 React SSE 模式 + 官方 LangGraph v2 协议 + 5.3 后端实现
检查清单
- ☐ 是否把 token 流和事件流在协议层分开?
- ☐ 前端是否按
type字段做单一分发器(避免到处写 if/else)? - ☐ 是否对 token 流做节流 / buffering(避免 60 fps 重渲染)?
- ☐ 状态流是否带
seq/ 序号,丢包时可请求重放?
5.3 Python 后端的 SSE/WebSocket 适配(前端消费边界,非 Python 主线)
本节定位 :本节说明如何把 LangGraph Python
graph.stream/astream输出 暴露 到网络协议层(SSE / WebSocket)。前端 React 组件、JS SDK、Next.js / Edge Runtime 不属于 LangGraph Python API;仅作为消费 Python 输出的客户端形态简述,便于后端设计协议时对齐客户端期望。
解决什么问题
- SSE:单向、HTTP 友好、自动重连、防火墙穿透好。适合 token 流、状态流。
- WebSocket:双向、低延迟。适合协作、typing 指示、双向审批回传。
- 后端封装原则 :把 v2
StreamPart字典原样 序列化为 SSEdata:帧(JSON),让客户端按type单一分发;不要在后端再加一层 envelope。 - Edge Runtime 注意点:Vercel Edge 函数有响应时长上限(官方默认 30s),不适合长连接;LLM 调用也不该放 Edge(CPU 限制、原生模块不可用)。社区 Vercel Edge runtime limits:https://vercel.com/docs/functions/edge-functions/limitations (2026-05 抓取)
来源:
- 官方 https://docs.langchain.com/langsmith/deployment --- LangSmith Deployment 提供标准 SSE 端点
- 社区 https://vercel.com/docs/functions/edge-functions/limitations --- Edge runtime 限制
推荐做法
- 生产部署优先用 LangSmith Deployment / Agent Server,自带标准化 SSE 端点(不需自己实现 v2 envelope 协议)。
- 本地开发 用 Python
FastAPI/Starlette暴露 SSE endpoint,GET而非POST(EventSource仅支持 GET)。流程:先POST /api/runs拿到run_id,再GET /api/runs/{run_id}/stream订阅。 - 双向审批 / resume :HTTP
POST /api/runs/{run_id}/resume提交Command(resume=...),再 GET 新 stream 看 resume 后的输出。 - 不要把 LLM 调用放 Vercel Edge function(超时、CPU 限制、缺 native 依赖);放 Node runtime 或独立后端服务。社区 同上 Vercel Edge limits
- JS / TS 前端 (非 Python 主线,仅消费边界 ):可用
@langchain/langgraph-sdk==1.9.21(2026-05 抓取)的Client客户端 +client.threads.stream({ transport: "sse" | "websocket" });React 侧通过useStream()hook(@langchain/langgraph-sdk/react)订阅thread.messages/thread.values/thread.interrupts等 lazy projection。来源:官方
不推荐做法
- ❌ 不要把 Stream response 放到 Vercel Edge function 里------Edge 有时长上限。
- ❌ 不要在前端用
fetch().then(r => r.body)模拟 SSE------错误处理、断点续传都难做,直接用EventSource或eventsource-parser库。 - ❌ 不要在多 tab 间共享一个
EventSource------每个 tab 独立订阅,避免 backpressure 互相影响。 - ❌ 不要在后端 SSE 输出里再加一层 envelope 协议------v2
StreamPart已经是统一格式。
最小代码示例(Python 后端 SSE,正确 GET + run_id)
python
# 依赖:fastapi、uvicorn、langgraph
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from pydantic import BaseModel
from langgraph.types import Command
import json
import uuid
app = FastAPI()
COMPILED = build_graph() # 你的 graph
RUNS: dict[str, dict] = {} # 演示用内存索引,生产请用 Redis/Postgres
class StartReq(BaseModel):
input: dict
thread_id: str
class ResumeReq(BaseModel):
resume: dict
@app.post("/api/runs")
def start_run(req: StartReq):
run_id = str(uuid.uuid4())
RUNS[run_id] = {"thread_id": req.thread_id, "input": req.input}
return {"run_id": run_id}
@app.get("/api/runs/{run_id}/stream")
def stream_run(run_id: str):
rec = RUNS[run_id]
cfg = {"configurable": {"thread_id": rec["thread_id"]}}
def gen():
for chunk in COMPILED.stream(
rec["input"], cfg, stream_mode=["updates", "custom"], version="v2"
):
yield f"data: {json.dumps(chunk)}\n\n"
return StreamingResponse(gen(), media_type="text/event-stream")
@app.post("/api/runs/{run_id}/resume")
def resume_run(run_id: str, req: ResumeReq):
rec = RUNS[run_id]
cfg = {"configurable": {"thread_id": rec["thread_id"]}}
# 演示用同步调用;生产建议改为 async def + await COMPILED.ainvoke(...)
# (FastAPI 同步 def 会阻塞事件循环,对 LLM 长时间调用不友好)
COMPILED.invoke(Command(resume=req.resume), cfg)
return {"ok": True}
来源:社区 FastAPI SSE 模式(参考 https://fastapi.tiangolo.com/advanced/custom-response/#using-streamingresponse 2026-05 抓取)+ 官方 LangGraph v2 stream 输出
最小代码示例(React 端,仅消费边界参考)
tsx
// 非 Python 主线;展示 useStream 消费端典型形态
// 依赖:@langchain/langgraph-sdk@1.9.21、react 18/19
import { Client } from "@langchain/langgraph-sdk";
import { useStream } from "@langchain/langgraph-sdk/react";
const client = new Client({ apiUrl: "http://localhost:2024" });
function AgentView({ assistantId }: { assistantId: string }) {
const thread = client.threads.stream({ assistantId, transport: "sse" });
const stream = useStream({ client: thread, assistantId });
return (
<div>
{stream.messages.map(m => <p key={m.id}>{m.content}</p>)}
{stream.toolCalls.map(t => <span key={t.id}>running {t.name}...</span>)}
</div>
);
}
ThreadStream 支持 transport: "sse" | "websocket" | AgentServerAdapter(自定义);连接懒启动------可在首次 I/O 前配置订阅。官方 https://github.com/langchain-ai/langgraphjs/blob/main/libs/sdk/docs/streaming.md
检查清单
- ☐ SSE endpoint 是否用 GET(与
EventSource兼容),start/resume 走 POST? - ☐ 后端是否把 v2
StreamPart原样 JSON 序列化到data:帧,没加额外 envelope? - ☐ 是否避免把流放到 Vercel Edge Runtime?
- ☐ 自实现 SSE 时是否处理
Last-Event-ID头(断点续传)? - ☐ WebSocket 是否有心跳 / reconnect 策略?
- ☐ resume / interrupt 回传是否走独立 HTTP 端点(不复用 SSE 通道)?
5.4 多并发流隔离与子图事件 namespace
解决什么问题
一个 UI 上可能同时跑多个 agent 实例:左侧规划、右侧执行、底部评估。后端要么是单连接多 thread,要么是多连接。两类事件不能混------否则 UI 状态错乱。
推荐做法
- 每个 agent 实例独占一个
thread_id+ 一个 SSE 连接(或 WS 连接)。 - 在 Graph 端启用
subgraphs=True,v2 输出中用ns字段区分根图 / 子图事件。 - 前端按
thread_id维护独立的 buffer;按ns区分父子事件。 - 服务端按
thread_id隔离状态------这是 LangGraph checkpointer 的天然优势。
不推荐做法
- ❌ 不要把多 thread 事件塞进同一个
values流------分不清谁是谁。 - ❌ 不要在客户端按"先后顺序"推断归属------并发场景会出错。
- ❌ 不要忽略
ns字段------多 agent 协作时父子事件是关键调试线索。
最小代码示例
见 5.1 中的 v2 输出 ns: () 注释;多 agent 部署时按 thread_id 隔离。
检查清单
- ☐ 是否每个会话/任务有独立
thread_id? - ☐ 前端是否按
thread_id隔离 buffer? - ☐ v2 模式是否解析
ns区分父子图? - ☐ 是否避免在同一连接上合并多 thread 输出?
5.5 断流恢复(resumable stream)、message_id / tool_call_id 对齐
解决什么问题
网络抖动、用户刷新、长时间断网。客户端断开后能否从上次位置继续 ,并且事件能与 LLM 输出的 message_id / tool_call_id 对齐,决定了能不能"无缝"恢复。
推荐做法
- SSE 断点续传(应用层协议) :服务端在每条
data:帧前携带id:字段(event_id),客户端用浏览器原生EventSource+Last-Event-ID头自动重连。Last-Event-ID是 W3C SSE 规范能力,不是 LangGraph Python 内建 ------LangGraph 端只产生StreamPart字典,由后端 SSE 适配层补id:字段。 - 消息对齐(Python 侧原语) :
stream_mode="messages"模式下,每个 token chunk 的id字段与最终写入messagesreducer 的消息id一致;tool_call_id由 LangChain 工具调用框架保证唯一性。来源:官方 https://docs.langchain.com/oss/python/langchain/models - 持久化兜底 :长时间断网不必续传,直接查 thread 历史
graph.get_state(config),从最近 checkpoint 拉取再继续。 - 不要混用消息 id 和 event id :前者是 LangGraph 内部消息标识(用于 reducer 合并),后者是 SSE 帧标识(用于断点续传),二者独立。
不推荐做法
- ❌ 不要用「重新调用
invoke+ 拿新 run_id」当断线恢复------会得到完全不同的 session,token 计费也翻倍。 - ❌ 不要自己造
message_id------和 LangChain 的id冲突,UI 拼接会乱。 - ❌ 不要在客户端做"按顺序拼接"对齐------并发工具调用一来就错。
- ❌ 不要把 LangGraph Python
event_id当成 LangGraph 原生字段------graph.stream不产生该字段;它属于 Agent Server / JS SDK 协议层。
最小代码示例(Python 后端 SSE,message_id / tool_call_id 透传 + SSE id 帧)
python
import json, uuid
# ... 接续 5.3 的 FastAPI SSE 流 ...
def gen():
for chunk in COMPILED.stream(
input, cfg, stream_mode="messages", version="v2"
):
if chunk["type"] == "messages":
token, meta = chunk["data"]
# Python 原生:meta 含 langgraph_node;token.id 是消息级 id
sse_id = str(uuid.uuid4()) # SSE 帧 id 由应用层生成
yield f"id: {sse_id}\ndata: {json.dumps({'id': token.id, 'delta': token.content})}\n\n"
来源:官方 https://docs.langchain.com/oss/python/langgraph/streaming + W3C SSE 规范(EventSource 协议)
检查清单
- ☐ SSE 帧是否带
id:字段启用Last-Event-ID断点续传? - ☐
id:是 SSE 帧 id(应用层生成)还是 token id(Python 原生)?二者是否解耦? - ☐ token 的
id是否和最终 messageid一致(LangChain 框架保证)? - ☐ tool_call 的
tool_call_id是否与ToolMessage.tool_call_id对齐? - ☐ 长时间断线时是否回退到
get_statecheckpoint?
5.6 中断、错误、恢复时的事件协议设计
解决什么问题
graph 可能因 interrupt 暂停、可能因错误失败、可能因 resume 继续。前端必须能区分这三种状态。
推荐做法(按 API 层级分离)
- Python 原生层(stream/astream) :用 v2
StreamPart字典的type字段区分模式;updates模式里如果next == ()表示已结束;checkpoints/tasks模式暴露执行进度。invoke()在 1.0+ 返回GraphOutput对象,含.value与.interrupts属性。 - Python 原生层(astream_events) :传
version="v2"(默认)返回 LangChain 风格事件(on_chain_start/on_llm_stream/on_tool_end等);传version="v1"仍是旧版__interrupt__风格,v1 视为弃用。来源:官方 https://docs.langchain.com/oss/python/langgraph/event-streaming - interrupt 检测 :节点里调
interrupt()抛 GraphInterrupt 异常;Python 端通过 GraphOutput.interrupts 拿 ,不要在订阅流里硬找特定 event 名。 - 错误传播 :工具异常默认经
ToolNode.handle_tool_errors(详见 6.2/6.7)回灌为ToolMessage(status="error");非工具异常会从astream中以异常形式抛出。 - resume 模式 :resume 后新开 stream (
graph.stream(Command(resume=...), cfg)),不在旧 stream 上追加。
不推荐做法
- ❌ 不要把 JS SDK 投影(
stream.interrupted/stream.interrupts/stream.output)当作 Python 原生属性------它们是@langchain/langgraph-sdk包装的对象属性。 - ❌ 不要混用 v1
__interrupt__与 v2GraphOutput------v1 已弃用。 - ❌ 不要在订阅流中"猜"是否被 interrupt------直接看
GraphOutput.interrupts或走checkpoints模式。 - ❌ 不要在同一条 stream 上从 paused → resumed 连续订阅------resume 后新开 stream 更可靠。
最小代码示例(Python 原生 interrupt 检测)
python
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.types import Command, interrupt
class State(TypedDict): x: int
def node(state: State):
ok = interrupt({"ask": "approve?"})
return {"x": int(bool(ok))}
g = StateGraph(State).add_node("n", node)
g.add_edge(START, "n").add_edge("n", END)
app = g.compile(checkpointer=InMemorySaver())
cfg = {"configurable": {"thread_id": "t-1"}}
# Python 1.0+ 风格
out = app.invoke({"x": 0}, cfg)
if getattr(out, "interrupts", None):
print("pending:", out.interrupts)
out2 = app.invoke(Command(resume=True), cfg)
print("final:", out2.value if hasattr(out2, "value") else out2)
来源:官方 https://docs.langchain.com/oss/python/langgraph/interrupts + https://docs.langchain.com/oss/python/langgraph/streaming
检查清单
- ☐ 是否在 Python 端用
GraphOutput.interrupts检测中断(不依赖 SDK 投影)? - ☐ 是否在
astream_events中传version="v2"(v1 弃用)? - ☐ 工具异常是否走
ToolNode.handle_tool_errors通道(详见 6.2)? - ☐ resume 后是否新开 stream(不复用旧 stream)?
第 6 章:工具调用与 Agent 构建
版本锚点:
langgraph==1.2.4;langchain==1.x(LangChain 1.0+ 主线)。LangChain 1.0+ 文档主推
langchain.agents.create_agent(built on LangGraph),作为新代码的"快速 Agent"路径;原langgraph.prebuilt.create_react_agent仍可使用 ,适合"想直接控制 ReAct 循环节点/边"或"必须用 LangGraph prebuilt API"的场景。两者不互斥 ,但默认状态 schema 与返回结构不同 ,混用需注意。参考:
6.1 create_agent(LangChain)vs create_react_agent(LangGraph prebuilt)vs 自定义 StateGraph
解决什么问题
新团队 90% 场景是"模型 + 工具 + ReAct 循环",写一堆节点/边是过度设计。但生产中总会出现:
- 多个 LLM 调用节点(router / planner / executor)
- 多状态 schema
- 强 HITL
- 子图复用
需要一个清晰的选型边界。
三方对比(截至 langgraph 1.2.x / langchain 1.x)
| 维度 | langchain.agents.create_agent |
langgraph.prebuilt.create_react_agent |
自定义 StateGraph |
|---|---|---|---|
| 入口模块 | langchain.agents |
langgraph.prebuilt |
langgraph.graph |
| 推荐来源 | LangChain 1.0+ 主推 | LangGraph 文档保留 | 高级用法 |
| 状态 schema | LangChain AgentState(messages + 内部字段) |
LangGraph MessagesState |
完全自定 |
| 返回值 | result["messages"] + result["structured_response"] |
result["messages"] |
自定 |
| HITL 支持 | 通过 checkpointer + interrupt 子协议 |
通过 checkpointer + interrupt |
完全控制 |
| 适合 | 单 Agent、模型 + 工具的快速搭建 | 想直接用 LangGraph prebuilt API 的团队 | 多 Agent / 复杂流程 |
| 主要风险 | 内部结构演进快,版本间 schema 可能微调 | 文档主推让位,但 API 仍稳定 | 维护成本高 |
来源:
- 官方 https://docs.langchain.com/oss/python/langchain/agents
- 官方 LangGraph prebuilt 仍可 import;具体用法见 LangGraph 概念页(2026-05 抓取)
推荐做法
-
默认(2026 主流) :用
langchain.agents.create_agent。pythonfrom langchain.agents import create_agent from langgraph.checkpoint.memory import InMemorySaver agent = create_agent( model="openai:gpt-5.4", tools=[search, calc], system_prompt="You are a helpful assistant.", checkpointer=InMemorySaver(), ) -
用
create_react_agent的合理场景 :- 团队已基于
langgraph.prebuilt标准化导入;想统一从langgraph而非langchain引入 Agent。 - 需要直接拿
CompiledStateGraph二次扩展(用create_agent得到的对象再.with_config(...)也是CompiledStateGraph,但内部节点命名 / schema 略有不同)。 - 与 LangGraph 多 agent 子图生态(如
langgraph-supervisor)对齐时。
- 团队已基于
-
落到自定义 StateGraph 的信号 :
- 你需要 2 个以上 LLM 调用节点(planner/executor/verifier)
- 你需要自定义 state 字段(非 messages)
- 你需要子图复用 / 多 agent 协作
- 你需要复杂 HITL(多分支审批)
不推荐做法
- ❌ 不要为了"灵活"一上来就自定义 StateGraph------等真有需求再改。
- ❌ 不要在
create_agent里塞大量业务逻辑到 system_prompt------那是 RAG / few-shot 该管的事。 - ❌ 不要混用
create_agent与create_react_agent在同一 thread / 同一 checkpoint schema 上------两者底层 GraphState 字段不完全相同(如create_agent可能有内部structured_response字段),混用会导致get_state读回字段缺失。待补 具体 schema 差异需读两方源码确认;本结论为机制推断。
最小代码示例(自定义 StateGraph + ToolNode + tools_condition)
python
# 依赖:langgraph>=1.2、langchain-core
from typing import Annotated, TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.prebuilt import ToolNode, tools_condition
from langgraph.checkpoint.memory import InMemorySaver
from langchain_openai import ChatOpenAI
from langchain_core.messages import BaseMessage
from langgraph.graph.message import add_messages
class State(TypedDict):
messages: Annotated[list[BaseMessage], add_messages]
def call_model(state: State):
llm = ChatOpenAI(model="gpt-4o").bind_tools(tools)
return {"messages": [llm.invoke(state["messages"])]}
builder = StateGraph(State)
builder.add_node("agent", call_model)
builder.add_node("tools", ToolNode(tools))
builder.add_edge(START, "agent")
builder.add_conditional_edges("agent", tools_condition)
builder.add_edge("tools", "agent")
graph = builder.compile(checkpointer=InMemorySaver())
来源:官方 https://docs.langchain.com/oss/python/langchain/agents + 官方 LangGraph prebuilt
检查清单
- ☐ 是否先尝试
create_agent路径再考虑自定义? - ☐ 真的需要多节点 / 多状态吗?有没有「过度设计」?
- ☐ 自定义 StateGraph 时是否使用
ToolNode+tools_condition而不是手写边? - ☐ state schema 是否明确(
messages用add_messagesreducer)? - ☐ 是否避免在同一 codebase 混用
create_agent和旧create_react_agent?
6.2 ToolNode / tools_condition / 结构化输出
解决什么问题
- ToolNode :LangGraph 内置的"批量执行工具 + 把结果包成
ToolMessage"节点。 - tools_condition :根据 LLM 最后一条消息是否含
tool_calls决定走tools还是END。 - 结构化输出:让 LLM 输出符合 schema 的 JSON(用于 parser / 路由 / 严格表单)。
推荐做法
-
ToolNode 默认行为(langgraph prebuilt 1.2.x) :
handle_tool_errors默认值是_default_handle_tool_errors(一个 callable),不是简单的True。该默认值会捕获「工具调用参数错误」(模型给错参数)并以ToolMessage回灌 ,但不会捕获「工具执行时抛出的异常」 ------执行异常会沿 GraphBubbleUp 机制上抛 到 graph。True:捕获所有错误,统一以ToolMessage+ 默认错误模板回灌str:捕获所有错误,回灌自定义字符串Exception 类 / 元组:只捕获指定异常类型callable[..., str]:自定义错误处理函数False:禁用错误处理,异常直接传播
来源:官方源码 https://github.com/langchain-ai/langgraph/blob/main/libs/prebuilt/langgraph/prebuilt/tool_node.py (2026-05 抓取)
-
生产推荐 :把
handle_tool_errors显式设为True或自定义 callable,不要依赖默认值------执行期异常在默认下会冒泡,可能让 graph 整个挂掉。 -
结构化输出三选一 :
场景 推荐 模型需要主动调外部动作 tool calling 最终回复要解析为 Pydantic / TypedDict with_structured_output兼容老模型 / 只想约束"是 JSON" json_mode(要在 prompt 里说明 schema)来源:官方 https://docs.langchain.com/oss/python/langchain/models -
校验 :用
include_raw=True在 Pydantic 校验失败时拿到原始消息 + 错误,便于降级。 -
强制选择 :
tool_choice="any"或tool_choice="tool_name"在路由场景有用。
不推荐做法
- ❌ 不要把
with_structured_output用在"模型需要先调工具再答"的场景------它不支持工具调用。 - ❌ 不要在 prompt 里堆 JSON 示例然后用
json_mode------改用with_structured_output更稳。 - ❌ 不要把
ToolNode的handle_tool_errors设成False------除非你想让工具异常直接 raise 终止 graph。 - ❌ 不要假设
ToolNode默认会捕获所有工具异常------它只捕获「调用参数错误」 ,执行期异常默认会冒泡(见上文 ToolNode 默认行为)。
最小代码示例
python
from pydantic import BaseModel
from langchain_openai import ChatOpenAI
class Answer(BaseModel):
summary: str
confidence: float
llm = ChatOpenAI(model="gpt-4o")
structured = llm.with_structured_output(Answer)
out = structured.invoke("Summarize: ...")
print(out.summary, out.confidence)
来源:官方 https://docs.langchain.com/oss/python/langchain/models
检查清单
- ☐ 工具异常是否走
ToolMessage(status="error")自愈? - ☐ 结构化输出是否根据场景选 tool calling /
with_structured_output/json_mode? - ☐ 是否在 prompt 里说明 schema 而不是用裸
json_mode? - ☐ 是否给
with_structured_output加include_raw=True拿到降级路径? - ☐ 是否合理使用
tool_choice强制工具?
6.3 工具描述与命名规范(质量第一杀手)
解决什么问题
工具描述直接决定 LLM 调用准确率。最常见的 agent 失败原因不是模型、不是 prompt,是工具描述烂。
推荐做法
- 命名规范 :
- 动词开头:
get_weather/search_orders/create_invoice(不要weather/search) - 命名空间避免冲突:
jira_create_issue/github_create_pr(多服务场景) - 拼写一致:snake_case 全局统一
- 动词开头:
- 描述三段式 :
- What:做什么(一句话)
- When:何时调用、典型场景
- Edge cases:失败 / 空结果 / 需要确认
- 参数描述 :每个参数都要
description+ 类型;可选值用enum限制。 - 示例 :在 description 里给 1-2 个输入输出示例(Few-shot 提升准确率)。
不推荐做法
- ❌ 不要让 description 一句话敷衍------模型会瞎选。
- ❌ 不要让 description 重复函数名------浪费 token。
- ❌ 不要在 description 里夹带私货("如果用户没问天气就拒绝")------这是 system prompt 该管的事。
- ❌ 不要忽略参数描述------LLM 拿不到字段语义就会瞎填。
最小代码示例
python
from langchain_core.tools import tool
@tool
def get_weather(location: str, unit: str = "celsius") -> str:
"""Get the current weather for a given location.
Use this tool when the user asks about current weather, temperature,
or conditions in a city. Do NOT use for forecasts beyond 24h.
Args:
location: City name, e.g. "San Francisco".
unit: "celsius" or "fahrenheit". Defaults to celsius.
"""
...
来源:官方 https://docs.langchain.com/oss/python/langchain/models + 社区 通用经验
检查清单
- ☐ 工具名是否动词开头、snake_case?
- ☐ description 是否三段式(What / When / Edge cases)?
- ☐ 每个参数是否都有
description+ 类型? - ☐ 是否避免在 description 里夹带 system prompt 类的指令?
- ☐ 是否在 CI / 评测中加"工具选择准确率"指标?
- ☐ 命名空间是否避免冲突(多服务场景)?
6.4 大工具集的处理:检索 / 路由 / 去重 / 合并
解决什么问题
当工具数量 > 30 时,LLM 选错概率显著上升;> 100 时基本不可用。需要把工具集拆成可索引的小集合。
推荐做法
- 工具检索(Tool Retrieval) :
- 把工具 description 嵌入向量库;用户 query 来时只取 top-k 工具
- 框架:
langchain-mcp-adapters配合语义搜索 /langgraph内置tools_condition不够,需要上自定义 router
- 路由(Router Agent) :
- 一个 LLM 先决定"该用哪类工具",再实例化对应的子 agent
- 子图模式:把"专业 agent"做成子图
- 去重:相似 description 的工具在 store 端合并("get_user" vs "fetch_user")。
- 合并 / 抽象 :把"get_user_id + get_user_profile"合并为
get_user(id_or_name)。 - 动态加载:MCP 场景下按需从 server 拉工具而不是一次性全注册。
不推荐做法
- ❌ 不要给单 agent 直接塞 50+ 工具------选错率会爆炸。
- ❌ 不要靠 description 长度比拼"重要性"------LLM 没有偏好权重。
- ❌ 不要在 prompt 里 hard-code "如果你看到 X 工具就用 Y"------用 router 替代。
最小代码示例(语义检索挑工具)
python
# 伪代码:把工具 description 嵌入向量库
def select_tools(query: str, k: int = 5):
hits = vector_store.similarity_search(query, k=k)
return [tool_registry[hit.metadata["name"]] for hit in hits]
来源:推测 + 社区 通用模式;LangGraph 本身没有官方"tool retriever"组件,待补具体库名。
检查清单
- ☐ 工具数量是否超过 30?是否启用 router / 检索?
- ☐ 工具 description 是否去重(避免相似工具混在一起)?
- ☐ 是否合并语义相近的工具?
- ☐ 是否在评测中测量"工具选择 top-1 准确率"?
6.5 MCP(Model Context Protocol)集成
解决什么问题
MCP 是 Anthropic 推动的"tool server 标准协议",让 agent 能调用任何实现 MCP 的工具源。langchain-mcp-adapters 把 MCP 工具桥接到 LangChain / LangGraph。
来源:官方 https://github.com/langchain-ai/langchain-mcp-adapters (3.6k stars,2026-05 抓取)
推荐做法
-
单 server(stdio) :
pythonfrom mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client from langchain_mcp_adapters.tools import load_mcp_tools from langchain.agents import create_agent server_params = StdioServerParameters(command="python", args=["/path/to/math_server.py"]) async with stdio_client(server_params) as (read, write): async with ClientSession(read, write) as session: await session.initialize() tools = await load_mcp_tools(session) agent = create_agent("openai:gpt-4.1", tools) result = await agent.ainvoke({"messages": "what's (3+5) x 12?"}) -
多 server(混合 transport) :
pythonfrom langchain_mcp_adapters.client import MultiServerMCPClient client = MultiServerMCPClient({ "math": {"command": "python", "args": ["math_server.py"], "transport": "stdio"}, "weather": {"url": "http://localhost:8000/mcp", "transport": "http", "headers": {"Authorization": "Bearer ..."}}, }) tools = await client.get_tools() agent = create_agent("openai:gpt-4.1", tools)transport 取值说明 (langchain-mcp-adapters 当前版本):
"stdio"/"sse"/"http"/"streamable_http"。"http"是当前 README 默认值,"streamable_http"是 streamable HTTP transport 的语义更准确的别名(两者在最新版本中均可用)。来源:官方 https://github.com/langchain-ai/langchain-mcp-adapters/blob/main/README.md (2026-05 抓取) -
错误自愈 :MCP 工具经
langchain-mcp-adapters加载后由ToolNode统一处理;ToolNode默认对"调用参数错误"做ToolMessage(status="error")回灌(详见 6.2 / 6.7),让 agent 自愈。 -
Web 服务场景用 HTTP transport,避免 stdio 跨进程;stdio 仅适合用户本机应用。
不推荐做法
- ❌ 不要在 web server 上用 stdio transport------MCP 官方明确 stdio "designed primarily for user-machine applications"。
- ❌ 不要把 MCP 工具"包装"成 LangChain 工具时再手动执行------
load_mcp_tools已自带适配。 - ❌ 不要在长连接里复用
ClientSession但忘记重连------MCP session 有生命周期。
最小代码示例
见上。
检查清单
- ☐ MCP 工具加载是用
load_mcp_tools而不是手写适配? - ☐ Web 场景用 HTTP transport,本地工具用 stdio?
- ☐ 多 server 是否用
MultiServerMCPClient而不是多次 stdio_client? - ☐ MCP 工具错误是否走自愈路径?
- ☐ HTTP transport 是否带 auth header?
- ☐ 是否给 MCP session 设置超时 / 重连?
6.6 并行工具调用:parallel_tool_calls 与状态合并风险
解决什么问题
当 LLM 一次返回多个 tool_calls(如"查 5 个城市的天气"),ToolNode 默认会并行执行。这带来:
- 性能提升(少一轮 LLM)
- 状态合并顺序:结果会按
tool_call_id回到 messages,但写入顺序可能并发 - 部分失败:5 个里 2 个失败如何处理?
推荐做法
- 开启
parallel_tool_calls=True(多数 provider 默认开)。 - 工具实现幂等(多次调用副作用相同)。
- 工具内部避免共享可变状态(数据库连接池、计数器等)。
- 失败策略:让
handle_tool_errors=True把错误回灌,agent 自己决定重试。 - 高一致性场景:手动关
parallel_tool_calls=False,串行更可预测。官方 https://docs.langchain.com/oss/python/langchain/models
不推荐做法
- ❌ 不要在工具里维护进程内可变状态(计数器、缓存)------并发调用会撞车。
- ❌ 不要假设
tool_calls一定按 LLM 输出顺序执行------并发场景顺序未定义。 - ❌ 不要把"全部成功才继续"逻辑硬编码进 tool------让 LLM 决定。
最小代码示例
python
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o")
# 默认 parallel_tool_calls=True;高一致场景关掉
llm_no_parallel = llm.bind_tools(tools, parallel_tool_calls=False)
来源:官方 https://docs.langchain.com/oss/python/langchain/models
检查清单
- ☐ 工具实现是否幂等?
- ☐ 工具是否共享可变状态(如果有,关闭并行)?
- ☐ 并行失败是否走
ToolMessage自愈? - ☐ 状态 reducer 是否能处理乱序
ToolMessage?
6.7 工具调用错误处理:分类、可重试、用户可见、静默忽略
解决什么问题
工具调用错误谱系很广:
- 网络抖动 → 可重试
- 4xx 鉴权/参数 → 不可重试,让模型改
- 5xx 临时 → 限流退避后重试
- 业务错误(库存不足)→ 透传给用户
- 数据未找到 → 静默返回空,让 LLM 自己换 query
推荐做法
- 模型级重试(langchain-openai 1.x 实测) :
ChatOpenAI(max_retries=None, timeout=None)是源码默认值,没有内置重试与超时。- 想要重试时显式传
max_retries(生产推荐 2-6 次,受 provider rate limit 限制)。 - 旧文档中流传的"默认 6 次"是 langchain 0.x 历史行为 ;langchain 1.x 实际已变更。务必显式设置 。
来源:官方源码 https://github.com/langchain-ai/langchain/blob/master/libs/partners/openai/langchain_openai/chat_models/base.py (2026-05 抓取)
- 工具级重试 :用
tenacity(生产场景推荐),或包装一个BaseTool子类实现def _run(self, ...)+ 内部重试;BaseTool本身不带重试,需要自实现。 - 分类处理 :
- 网络/5xx → 用 tenacity 指数退避重试
- 4xx → 立即
ToolMessage(status="error", ...) - 业务错误 → 同样以
error回灌,但 description 里写明语义
- 限流:429 → 指数退避
- 用户可见 :高风险操作(付款、删除)必须先
interrupt(),不靠工具错误处理。 - 结构化输出失败 :
with_structured_output(..., include_raw=True)拿原始 + error。
不推荐做法
- ❌ 不要在工具里直接
raise让 graph 崩溃------应该用ToolMessage(status="error")让 agent 继续。 - ❌ 不要对所有错误一律重试------4xx 鉴权/参数错误重试无意义。
- ❌ 不要在生产里
try/except: pass静默吞错------可观测性没了。 - ❌ 不要假设
ChatOpenAI默认带重试------1.x 显式置空,需要主动设max_retries。 - ❌ 不要假设
BaseTool自带重试------它没有,需要用 tenacity 或子类包装。
最小代码示例
python
import tenacity
from langchain_core.tools import tool
@tool
def call_external_api(payload: dict) -> str:
"""Call an external API. Retries on 5xx."""
@tenacity.retry(
stop=tenacity.stop_after_attempt(3),
wait=tenacity.wait_exponential(multiplier=1, min=1, max=10),
retry=tenacity.retry_if_exception_type(requests.HTTPError),
reraise=True,
)
def _do():
r = requests.post(URL, json=payload, timeout=10)
r.raise_for_status()
return r.text
return _do()
来源:官方 https://docs.langchain.com/oss/python/langchain/models + 社区 tenacity 模式(参考 https://tenacity.readthedocs.io/en/latest/index.html 2026-05 抓取)
检查清单
- ☐ 模型级
max_retries/timeout是否合理? - ☐ 工具异常是否走
ToolMessage(status="error")自愈? - ☐ 4xx / 5xx 是否分类处理?
- ☐ 高风险操作是否走
interrupt而不是错误处理? - ☐ 错误是否进入可观测体系(trace / log)?
- ☐ 是否避免静默吞错?
章节内引用一览
待补 / 待验证
- Time Travel / Subgraph 官方概念页
https://docs.langchain.com/oss/python/langgraph/time-travel/subgraphs在抓取时返回 SPA 错误页(页面 noindex、无内容)------可能文档站已迁移;建议从 GitHub examples 补查。本章中 4.5、5.4 引用了其核心 API(get_state_history/update_state/subgraphs=True),来自 LangGraph Pythonpersistence主概念页的同源描述,不构成主结论矛盾。 - 生产 BaseStore DB-backed 后端 :langgraph 1.2.x 在 PyPI 没有开源的
langgraph-store-postgres包 ;生产路径以 LangSmith Deployment 托管 store 或自建 BaseStore 实现为主。Checkpoint-postgres 不能替代 BaseStore(接口不兼容)。待补 --- 已降级为"待验证/需自建"。 - 大工具集检索组件 暂无 LangGraph 官方"tool retriever"------目前都是社区方案(LlamaIndex / 自研),未做产品级选型推荐。
- HITL timeout / escalation 缺少 LangGraph 官方推荐路径(社区方案多)。本章 4.6 已降级为"参考架构/推测性建议",并显式标注"非官方能力"。
- create_agent 与 create_react_agent 状态 schema 差异:6.1 已标 待补------具体字段差异需读两方源码确认。
- m-03(
@langchain/langgraph-sdkuseStream完整 API) :本节只展示最小消费示例;完整ResolveStreamInterface类型 /StreamPart协议细节见 官方 https://github.com/langchain-ai/langgraphjs/blob/main/libs/sdk/src/react/stream.tsx (2026-05 抓取)。
章节分层摘要(官方能力 / 社区经验 / 推测建议)
第 4 章:状态持久化、恢复与 HITL
- 官方已确认能力 :Checkpointer 三档选型(
InMemorySaver/SqliteSaver/PostgresSaver)、Durability 模式(exit/async/sync)、LANGGRAPH_STRICT_MSGPACK安全加固、interrupt()+Command(resume=...)HITL 三件套、Time Travel(get_state_history/update_state/replay)、BaseStore跨 thread KV + 语义检索。 - 社区/部署经验 :生产推荐
PostgresSaver+setup();InMemorySaver多 worker 不可用。 - 推测/待验证 :生产 BaseStore DB-backed 后端(官方未提供,PyPI 无
langgraph-store-postgres);HITL timeout / escalation 完整实现(官方无推荐路径);subgraph interrupt 父节点幂等要求(机制推断)。
第 5 章:流式输出与前端消费边界
- 官方已确认能力(Python 原生) :
stream/astream的 v2StreamPart字典({type, ns, data});stream_mode7 个取值(values/updates/messages/custom/checkpoints/tasks/debug);get_stream_writer()自定义事件;subgraphs=True父子图隔离;GraphOutput.interrupts(1.0+)作为 Python 原生 interrupt 检测。 - 官方已确认能力(部署/消费层) :LangSmith Deployment 自带标准 SSE 端点;FastAPI
StreamingResponse自实现 SSE。 - 社区/部署经验 :SSE 帧
id:+Last-Event-ID实现断点续传(W3C SSE 规范,非 LangGraph 原生);Vercel Edge 不能跑长连接(30s 上限)。 - 推测/待验证 :JS SDK
stream.interrupted/stream.interrupts/stream.output投影是 SDK 包装而非 Python 原生(已显式标注)。
第 6 章:工具调用与 Agent 构建
- 官方已确认能力 :
create_agent(LangChain 1.x,built on LangGraph)作为新代码主推;create_react_agent(LangGraph prebuilt)仍可 import;ToolNode7 个 handle_tool_errors 形态;with_structured_output三种 method(json_schema/function_calling/json_mode);MCP 适配(langchain-mcp-adapters)4 种 transport(stdio/sse/http/streamable_http);parallel_tool_calls控制并行。 - 社区/部署经验:工具描述三段式(What / When / Edge cases)+ Few-shot 示例;tenacity 工具级重试;4xx 不可重试 / 5xx 指数退避。
- 推测/待验证 :大工具集检索(无官方组件,自研 router / LlamaIndex);
create_agent↔create_react_agentschema 差异(需读源码确认,标 待补);ChatOpenAI在 langchain 1.x 已取消默认 6 次重试 (默认None),旧文档以"默认 6 次"流传属历史信息。
调研元信息
- 覆盖版本 :
langgraph==1.2.4(2026-06-02 PyPI latest)、langgraph-checkpoint==4.1.1、langgraph-checkpoint-postgres==3.1.0/langgraph-checkpoint-sqlite==3.1.0(均 2026-05-12 发布)、langchain-openai1.x 主线(ChatOpenAI 默认值核实于 2026-05)、@langchain/langgraph-sdk==1.9.21(2026-05 抓取,仅作消费边界参考)。 - 覆盖范围:仅 LangGraph Python(按用户要求);JS SDK / Agent Server / React 仅作消费边界小节标注。
- 来源类型分布:官方文档/源码约 75%、官方博客 ≈ 5%、社区/推测 ≈ 20%。
- 章节完整性:第 4 章 6 小节;第 5 章 6 小节;第 6 章 7 小节------每节五段结构齐全;新增「章节分层摘要」对齐官方能力 / 社区经验 / 推测建议三类。
第 7 章:多智能体架构
7.1 解决什么问题
单智能体(single agent)在以下场景会撞墙:
- 工具集异质、权限边界不同(只读检索 agent vs 写库 agent)。
- 不同子任务需要不同的 system prompt / 模型 / 工具集。
- 需要清晰的责任切分、独立的 trace / 评估 / 失败重试单元。
- 长期任务里需要把"已完成的子任务"作为可命名的能力复用(subgraph)。
LangGraph 提供的多智能体能力核心是 subgraph 组合 + Command(handoff) :subgraph 既可以"被当成节点"嵌入,也可以通过 Command(goto=..., graph=Command.PARENT) 主动跳回父图------这是从单智能体到多智能体的最小可用拼图。
【官方】"A subgraph is a graph that is used as a node in another graph." ---
docs.langchain.com/oss/python/langgraph/use-subgraphs(资料日期 2026-06 抓取;对应 LangGraph ≥1.0)。
7.2 推荐做法
- 从"supervisor + 工具型 subagent"入手,而不是一上来就搭 swarm。 supervisor 模式(一个路由节点调度多个 worker)是官方示例最常见的范式,控制流清晰,失败可定位(langgraph-supervisor 0.0.31;更新日期 2025-11-19,【官方】)。
- 状态共享走"显式 schema 映射",而不是"全部塞进一个大状态"。 子图和父图 schema 不一致时,封装一个"适配节点"显式做 in/out 映射;schema 一致时直接
add_node("sub", sub_compiled)(Subgraph 模式,【官方】)。 - handoff 用
Command(goto=..., graph=Command.PARENT),避免静态边 + Command 同源混用。 Command 是官方推荐的"同时改状态 + 跳转"原语(Command & handoff,"Command" 章节,【官方】)。 - subgraph 持久化按"作用域"选三种模式之一 (【官方】来源:docs.langchain.com/oss/python/langgraph/use-subgraphs "Persistence modes" 章节,2026-06 抓取),并约束 checkpoint 行为:
compile(checkpointer=None):每次调用全新状态。父图的 checkpointer 不会跨调用恢复子图历史。推荐作为默认值,子图视为"短任务工具"。compile(checkpointer=True):子图状态跨同一 thread 持久化。并发风险限定:当父图对同一子图实例的"同一 thread_id / 同一 checkpoint namespace"发起并发调用时会出现读写竞争;如必须并发,应走不同 thread_id 或不同子图实例。compile(checkpointer=False):纯函数行为,无 checkpoint、无 interrupt/resume 能力、无状态恢复。仅用于无状态 helper 子图。
- 复用多智能体:先考虑 prebuilt,再考虑自研。
langgraph-supervisor0.0.31(2025-11-19)和langgraph-swarm0.1.0(2025-12-04)是官方/官方维护方发布的开箱即用包,覆盖 80% 的 supervisor / handoff 场景(【官方】)。 - 跨图 / 跨框架协作:A2A 是候选协议层标准,不是已落地的 LangGraph 集成。 A2A v1.0.1(2026-05-28)是 Linux Foundation 下的开放标准,JSON-RPC 2.0 over HTTPS;可
pip install a2a-sdk。【待验证】 LangGraph Python 侧官方桥接 / adapter 在本次资料范围内未找到,生产前需自行验证 SDK、认证、streaming、工具调用、trace 串联能力。
7.3 不推荐做法
- 所有子图共用一个"巨型 shared state"。 失去边界 → 任何子任务的 schema 变更都要全量回归。
- multi-agent 当成"性能优化手段"使用。 多一层 LLM 调度就会多一次延迟和 token;如果一个 ReAct agent 能搞定,就不要拆。
- 动态 handoff 路由里塞副作用(写库、发邮件)。 handoff 本身可能因为 retry / interrupt-resume 重跑。
- per-thread subgraph 跨调用并发。 会出现 race condition / 状态错乱(限定条件:共享同一 thread_id / checkpoint namespace 时)。可规避:使用不同 thread_id,或每次调用
compile(checkpointer=True)得到新实例。 - swarm 用于"严格分阶段、可审计"的业务流程。 Swarm 去中心化控制流难以回放和审计,金融/医疗等强监管场景慎用。
- 用
Command(goto)的同时再add_edge(from, to)。 双重入口会让某些路径被绕过,导致跳过的节点状态缺失。
7.4 最小代码示例
依赖:
langgraph>=1.0、langgraph-supervisor==0.0.31、langgraph-swarm==0.1.0、Python ≥3.10。资料日期 2026-06-11。
7.4.1 Supervisor(prebuilt)
python
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent
from langgraph.checkpoint.memory import InMemorySaver
from langgraph_supervisor import create_supervisor
model = ChatOpenAI(model="gpt-4o")
def add(a: float, b: float) -> float:
"""返回两个数的和。"""
return a + b
def search(query: str) -> str:
"""对查询做关键词检索并返回文本。"""
return f"Search result for: {query}"
math_agent = create_react_agent(
model=model,
tools=[add],
name="math_expert",
prompt="Use math tools for calculations.",
)
research_agent = create_react_agent(
model=model,
tools=[search],
name="research_expert",
prompt="Use search for research questions.",
)
workflow = create_supervisor(
[research_agent, math_agent],
model=model,
prompt="Route research to research_expert and calculations to math_expert.",
output_mode="last_message", # "last_message" | "full_history"
)
app = workflow.compile(checkpointer=InMemorySaver())
# 资料来源:PyPI langgraph-supervisor 0.0.31(2025-11-19)
7.4.2 自研 Supervisor(Command + handoff)
python
from typing import Literal
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.types import Command
class TeamState(TypedDict):
request: str
active_agent: str
result: str
def supervisor(state: TeamState) -> Command[Literal["research_agent", "writing_agent"]]:
if "research" in state["request"]:
return Command(update={"active_agent": "research_agent"}, goto="research_agent")
return Command(update={"active_agent": "writing_agent"}, goto="writing_agent")
def research_agent(state: TeamState): return {"result": "research result"}
def writing_agent(state: TeamState): return {"result": "writing result"}
builder = StateGraph(TeamState)
builder.add_node("supervisor", supervisor)
builder.add_node("research_agent", research_agent)
builder.add_node("writing_agent", writing_agent)
builder.add_edge(START, "supervisor")
# 不要同时 add_edge("supervisor", ...) 与 Command(goto=...)
builder.add_edge("research_agent", END)
builder.add_edge("writing_agent", END)
graph = builder.compile()
# 资料来源:docs.langchain.com/oss/python/langgraph/graph-api("Command" 章节,2026-06 抓取)
7.4.3 Subgraph:父图节点内 invoke(schema 不同)
python
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
class ChildState(TypedDict):
child_text: str
class ParentState(TypedDict):
parent_text: str
def child_step(state: ChildState) -> dict:
return {"child_text": "child saw: " + state["child_text"]}
child_builder = StateGraph(ChildState)
child_builder.add_node("child_step", child_step)
child_builder.add_edge(START, "child_step")
child_builder.add_edge("child_step", END)
child_graph = child_builder.compile()
def parent_calls_child(state: ParentState) -> dict:
result = child_graph.invoke({"child_text": state["parent_text"]})
return {"parent_text": result["child_text"]}
parent_builder = StateGraph(ParentState)
parent_builder.add_node("call_child", parent_calls_child)
parent_builder.add_edge(START, "call_child")
parent_builder.add_edge("call_child", END)
# 资料来源:[docs.langchain.com/oss/python/langgraph/use-subgraphs](https://docs.langchain.com/oss/python/langgraph/use-subgraphs) "Call a subgraph inside a parent node" 段落(【官方】,2026-06 抓取)
7.4.4 Swarm(prebuilt)骨架
python
from langgraph_swarm import create_swarm, create_handoff_tool
# 1) 用 create_agent(name=..., system_prompt=..., tools=...) 建好各 agent
# 2) 给每个 agent 加 create_handoff_tool(agent_name=<peer>)
# 3) create_swarm([alice, bob], default_active_agent="Alice")
# 4) workflow.compile(checkpointer=InMemorySaver()) # 多轮需要 checkpointer
# 资料来源:PyPI langgraph-swarm 0.1.0(2025-12-04,【官方】)
7.4.5 Agent-as-tool / Graph-as-tool 最小代码示例
依赖:
langgraph>=1.0、Python ≥3.10。资料日期 2026-06-11。【官方】
python
# === Agent-as-tool: 把编译好的子图当 tool 暴露给父 agent ===
from langchain_core.tools import tool
from langgraph.prebuilt import create_react_agent
def lookup_order(order_id: str) -> str:
"""查询订单状态。"""
return f"order {order_id}: shipped"
# 子 agent 自己是一个可被复用的"能力"
order_agent = create_react_agent(
model="openai:gpt-4o-mini",
tools=[lookup_order],
name="order_agent",
prompt="查询订单。",
).compile()
# 父 agent 通过 Tool 把"调用子图"封装成单步工具
@tool
def ask_order_agent(question: str) -> str:
"""把订单相关问题转交给订单子 agent。"""
out = order_agent.invoke({"messages": [{"role": "user", "content": question}]})
return out["messages"][-1].content
parent = create_react_agent(
model="openai:gpt-4o",
tools=[ask_order_agent],
prompt="你是调度者,订单问题转给 order_agent 工具。",
).compile()
# 资料来源:LangGraph multi-agent 概念页 docs.langchain.com/oss/python/langgraph/multi-agent(【官方】)
python
# === Graph-as-tool: 父图把子图作为节点直接嵌入,控制流和状态共享 ===
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
class Shared(TypedDict):
question: str
order_answer: str
def router(state: Shared):
return {"order_answer": f"routed: {state['question']}"}
# 把已编译的子图作为节点加入父图
parent_builder = StateGraph(Shared)
parent_builder.add_node("order_subgraph", order_agent) # 共享 messages / 共享 schema
parent_builder.add_node("router", router)
parent_builder.add_edge(START, "order_subgraph")
parent_builder.add_edge("order_subgraph", "router")
parent_builder.add_edge("router", END)
parent = parent_builder.compile()
# 资料来源:docs.langchain.com/oss/python/langgraph/use-subgraphs("Add a compiled subgraph directly as a node",【官方】)
7.5 Agent-as-tool vs Graph-as-tool 选型对比
【官方】对比的语义层来自 LangGraph subgraph / multi-agent 概念页;【社区】建议来自 prebuilt 仓库 README 与社区博客。资料日期 2026-06-11。
| 维度 | Agent-as-tool | Graph-as-tool(subgraph-as-node) |
|---|---|---|
| 调用方式 | 父 agent 通过 tool 调用,子图作为"黑盒能力" | 子图作为父图的节点参与边/条件路由 |
| 状态边界 | 显式:tool 输入输出是字符串/字典,状态完全封装 | 隐式:父/子图可共享 state key,或通过 wrapper 节点适配 |
| Trace 粒度 | 一次 tool call 一次 span,嵌套浅 | 父图与子图在同一 super-step,可看到节点级联 |
| 复用成本 | 低------任何能传 tool 的 agent(ReAct、Custom)都能调 | 中------需要把子图编译并按 schema 嵌入 |
| 测试成本 | 把"调子图"当成一个副作用,mock 掉 @tool 函数即可 |
需要独立编译子图后单独测 + 父图集成测 |
| 适用场景 | 子能力相对独立、调用频次低、希望解耦 | 子能力需要访问父图 state、需要在主流程里流转 |
| 不适用 | 需要子任务结果参与后续路由 / 状态更新 | 子任务纯函数性质、状态无需共享 |
【社区】经验补充:高频小模型子任务(关键词提取、路由、归一化)走 Agent-as-tool 减少主 agent 上下文噪声;需要长上下文累积 / 状态演进的多步子任务走 Graph-as-tool。
7.6 经典模式:Plan-and-Execute
模式定义 (【社区】/【推测】基于 LangChain 官方博客与 langgraph 归档 notebook):Planner 把用户目标拆成有序步骤,Executor 逐步执行、Replanner 根据上一步结果修正后续计划。典型循环是 plan → execute → replan → ... → final,可显著降低单次 LLM 推理的复杂度。
LangGraph Python 实现骨架 (基于 LangChain 原始 plan-and-execute 设计与 langgraph.func 范式,资料来源:langchain-ai/langgraph 归档 examples/plan-and-execute/plan-and-execute.ipynb;新 docs 教程区未抓取到对应可运行页面 ,原始路径仅作历史参考,【待验证】 ;可参考 LangChain 原始 plan-and-execute 论文 / langchain-ai/langgraph 旧 README 了解模式骨架):
python
from typing_extensions import TypedDict, List
from langgraph.graph import StateGraph, START, END
from langgraph.prebuilt import create_react_agent
class PlanState(TypedDict):
objective: str
plan: List[str] # 计划步骤
past_steps: List[tuple] # (step, result)
response: str
def plan_step(state: PlanState) -> PlanState:
# 调 LLM 把 objective 拆成步骤;空 plan 表示直接给出 final
...
def execute_step(state: PlanState) -> PlanState:
# 拿 plan[0] 调一个 ReAct agent 执行
...
def replan_step(state: PlanState) -> PlanState:
# 调 LLM 决定:是否结束(return END) / 给出新 plan
...
builder = StateGraph(PlanState)
builder.add_node("planner", plan_step)
builder.add_node("executor", execute_step)
builder.add_node("replanner", replan_step)
builder.add_edge(START, "planner")
builder.add_edge("planner", "executor")
builder.add_edge("executor", "replanner")
# 条件边:replanner 返回 END 时收尾
builder.add_conditional_edges("replanner", lambda s: "end" if s.get("response") else "executor",
{"end": END, "executor": "executor"})
graph = builder.compile()
适用 :多步、需要先规划再执行的任务(研究、代码生成、数据分析)。
不适用:单步可完成、每步强依赖前一步输出的流式对话。
7.7 经典模式:Reflection / Reflexion
Reflection(自我反思) (【社区】Shinn et al., 2023;LangGraph 归档 notebook 名为 reflection.ipynb,新 docs 教程区未抓取到对应页面 ,【待验证】):Generator 产出回答,Critique 节点给出反馈,循环重写直到 Critique 通过或达到最大轮次。
python
# 概念骨架(基于官方 Graph API 习语)
class ReflectionState(TypedDict):
draft: str
critique: str
revisions: int
def generate(s): return {"draft": llm_generate(s["topic"])}
def critique(s): return {"critique": llm_critique(s["draft"])}
def should_continue(s) -> str:
return "end" if (s["critique"].startswith("PASS") or s["revisions"] >= 3) else "revise"
builder = StateGraph(ReflectionState)
builder.add_node("generate", generate)
builder.add_node("critique", critique)
builder.add_node("revise", lambda s: {"draft": llm_revise(s["draft"], s["critique"]),
"revisions": s["revisions"] + 1})
builder.add_edge(START, "generate")
builder.add_edge("generate", "critique")
builder.add_conditional_edges("critique", should_continue,
{"end": END, "revise": "revise"})
builder.add_edge("revise", "critique")
Reflexion 在 Reflection 基础上加入"长期记忆":critique 不仅修改当前 draft,还把"经验"写回外部 BaseStore,下次同类任务时检索使用。LangGraph 的 store.put(namespace, key, value) + store.search(...) 是官方推荐实现方式(资料来源:docs.langchain.com/oss/python/langgraph/persistence,"Memory Store" 章节,【官方】)。
适用 :生成质量受 LLM 自检能力明显提升的任务(代码、SQL、长文写作)。
不适用:对延迟极敏感、或 critique LLM 与 generator LLM 能力相当(反思不带来增益)的场景。
7.8 检查清单
- 是否在 supervisor 模式下使用了官方
langgraph-supervisorprebuilt,而非手撸 handoff? - 父图 / 子图的 schema 关系是否明确(共享 / 映射 / 私有)?
- handoff 路由函数是否是纯函数(不读时钟、不写外部状态)?
- 是否避免了 per-thread subgraph 的并发调用?
-
Command(goto=...)与同源add_edge是否互斥? - 是否对 multi-agent 链路设计了 trajectory 评估("是否按预期顺序走对节点")?
- 是否所有 subagent 都有独立
name,便于 trace / log 检索? - 跨进程 / 跨语言 / 跨框架协作时是否考虑 A2A 而非自研 RPC?------ 并已确认本项目实际需求
- 是否在引入 multi-agent 前先量化了"延迟 / token / 调试成本"的额外开销?
- Agent-as-tool vs Graph-as-tool:是否对每个子能力明确选了合适边界?独立能力用 tool,强耦合多步子任务用 subgraph-as-node
- Plan-and-Execute:是否对多步任务显式拆"规划/执行/再规划"三段,且对 replan 终止条件有显式判断?
- Reflection / Reflexion:critique 与 generate 是否复用同一 LLM?是否给反思轮次设上限防爆?是否把经验回写 store?
第 8 章:测试与质量保障
8.1 解决什么问题
LangGraph 应用的测试痛点集中在:
- 节点级纯逻辑(reducer、路由、tool wrapper)需要快速、确定性的单元测试。
- 整图行为需要集成 / E2E 测试,且要在 HITL、resume、multi-agent 等长链路场景里复现。
- LLM 输出非确定性让"断言最终文本"几乎不可行,必须借助温度/seed/snapshot/mock/judge 体系。
- 生产回归要回答"升级 LangGraph / 换模型后旧 case 是否还过",需要 replay 工具与 golden dataset。
官方 docs.langchain.com/oss/python/langgraph/test 明确给出三种测试模式:(1) 端到端图执行、(2) 单节点直接调用(graph.nodes[name].ainvoke(...),绕开 checkpointer)、(3) Partial execution(用 update_state(as_node=...) + interrupt_after 模拟中间状态)。
8.2 推荐做法
- Reducer、路由、纯函数节点------直接当普通 Python 测,不引入 LangGraph。 用
pytest的parametrize覆盖空、异常、并发更新等边界(LangGraph 测试文档,【官方】)。reducer 与节点副作用必须幂等------推荐用 idempotency key 、upsert 、read-before-write 三种模式;测试应覆盖"同一节点被 replay / interrupt-resume / 并发更新重复调用"三种重复路径。 - 节点级 LLM 调用------使用
graph.nodes[name].ainvoke(...)直调节点 + mock 模型。 官方测试文档明确"bypasses any checkpointers, which is useful for unit testing individual node logic without running the full graph flow"(同源)。 - 整图集成测试------每个用例 fresh
InMemorySaver/ fresh 编译。 避免 checkpoint 串味(persistence 文档,【官方】)。 - HITL / resume 测试------三步法 :(a) 触发
interrupt→ (b)Command(resume=...)→ © 断言 state / messages(interrupts 文档 "Resuming" 段落,【官方】)。 - LLM 断言 走"LLM-as-Judge + 少量 few-shot"组合:openevals 提供
CORRECTNESS_PROMPT/CONCISENESS_PROMPT等 prebuilt 评分器;create_llm_as_judge是 Python SDK 主入口(openevals 集成 与 LLM-as-Judge 指南,【官方】)。 - Replay / 回放 :用历史 thread 的
thread_id+checkpoint_id在新版图上invoke(None, config=...)复跑。常见两种用法:- 重放到指定 checkpoint:以该 checkpoint 的 state 作为起点重放。
- fork 后继续 :先
get_state({thread_id, checkpoint_id})拿快照 →update_state(new_config, values=...)写入新分支 →invoke(None, new_config)在新分支继续。 - 对比方式:直接断言
result["messages"][-1].content == golden、或对结构化输出跑 judge 评分 / 跑 trajectory evaluator。 - ⚠️ Replay 重新执行 checkpoint 之后的节点(不是只读 cache) ------会产生 LLM 计费与 tool 重放风险;final checkpoint 无 next 节点时 replay 是 no-op,可用于"跑通即止"的快速健康检查。
- 缓解方式:mock LLM provider (如 FakeListChatModel、录制 fixture)、使用 idempotent tool 、限定重放节点为纯计算 。Replayer 必须评估 LLM 成本与外部副作用,否则在 CI 中可能造成不可控费用。
(资料来源:docs.langchain.com/oss/python/langgraph/persistence "Time Travel" 章节,【官方】,2026-06-11 抓取)
- 多轮对话测试 :使用
run_multiturn_simulation配create_llm_simulated_user生成完整会话轨迹,再用 trajectory evaluator 评分(multi-turn-simulation,【官方】)。 - Mock 模型确定性 (【官方】与【社区】综合,资料来源:langchain_core.language_models.fake_chat_models):
- 优先使用
FakeListChatModel(from langchain_core.language_models.fake_chat_models import FakeListChatModel),给定循环响应列表。 seed并非所有 provider 都支持(OpenAI、Anthropic、Google 部分模型支持,vLLM/Ollama 部分支持;如不确定应直接 mock 整个模型或用 VCR/录制 fixture)。- temperature=0 在多数 provider 上能让单次响应更接近确定,但对多轮、对 prompt 改动仍不稳定------需配合录制 fixture / golden response 兜底。
- 优先使用
- Eval-as-code / Eval gate :把评估脚本纳入 CI;LangSmith 提供
client.aevaluate(...),可对单次 commit 设通过阈值。upload_results=False可用于本地 smoke(本地评估 与 pytest 集成,【官方】)。 - Property-based testing :reducer、状态合并函数用 Hypothesis 自动探索边界(参考资料:Hypothesis 官方文档)。
8.3 不推荐做法
- 断言 LLM 输出原文字符串。 一次温度抖动就崩。改用结构化 schema + LLM-as-Judge。
- 在测试中调用真实第三方 API(OpenAI / 数据库)。 慢、贵、不可重现;用 mock / VCR / 录制 fixture。
- 测试间共享 checkpointer 状态。 必须每个测试 fresh
InMemorySaver()。 interrupt周围用try/except时需谨慎。interrupt内部通过抛特殊异常暂停;通常应避免将interrupt包裹在 bareexcept中,并核对当前 LangGraph 版本的恢复语义(interrupts 文档)。注:该说法在不同 LangGraph 版本的实现细节上可能存在差异,deep-research 事实底座未将其列为绝对化建议;如需在 try/except 中使用,请用具体异常类型而非 bare except。- 用静态
interrupt_before/interrupt_after写 HITL 流程。 官方明确写"Static interrupts are not recommended for human-in-the-loop workflows. Use theinterruptfunction instead." - 节点内有 pre-
interrupt的副作用。 resume 时节点从头跑,副作用会重复发生------官方"Rules of Interrupts" 第 4 条。 - 在多轮测试里硬编码"应回答什么"。 用 trajectory + outcome evaluator 替代 turn-level 断言。
- 凭单个 LLM 评分做生产门禁。 LLM-as-judge 自身有 bias / 不稳定------必须配 few-shot 校准 + Pairwise + 人审回流。
- 线上回归靠"线上肉眼观察"。 必须把线上 trace 回灌到离线 replay 流程。
- reducer / 路由依赖全局计数器 / 随机数 / 时间戳。 这三类隐式状态会让 reducer 在 replay / interrupt-resume / 并发更新时表现不一致,破坏幂等性。
- 把 Replay 当成"只读 cache"使用。 Replay 会重新执行 checkpoint 之后的节点(包含 LLM 调用与 tool 副作用),不是只读快照;详见 8.2.6 警示。
8.4 最小代码示例
依赖:
langgraph>=1.0、langsmith>=0.3.4、pytest>=8、openevals、hypothesis。资料日期 2026-06-11。
8.4.1 Reducer 单元测试 + Property-based
python
# 资料来源:[Hypothesis 官方文档](https://hypothesis.readthedocs.io/),2026-06-11 抓取(【官方】)
from hypothesis import given, strategies as st
from operator import add
from typing import Annotated
from typing_extensions import TypedDict
class State(TypedDict):
items: Annotated[list[int], add]
@given(st.lists(st.integers(), min_size=0, max_size=50))
def test_add_reducer_preserves_order_and_total(items: list[int]) -> None:
"""验证 list-concat reducer(operator.add)保持顺序、保留全部元素、长度守恒。"""
s: State = {"items": []}
s["items"] = add(s["items"], items) # 模拟节点返回部分更新
assert len(s["items"]) == len(items)
assert s["items"] == items # 顺序保持(list concat 的关键性质)
assert sum(s["items"]) == sum(items) # 元素守恒
# 若要测"交换律"性质,应改用满足交换律的 reducer,例如 set union:
# from typing import Annotated
# class State2(TypedDict):
# tags: Annotated[set[str], lambda a, b: a | b]
# @given(st.sets(st.text()), st.sets(st.text()))
# def test_union_reducer_is_commutative(x, y):
# assert (x | y) == (y | x)
8.4.2 单节点直调(绕开 checkpointer)
python
# 资料来源:[docs.langchain.com/oss/python/langgraph/test](https://docs.langchain.com/oss/python/langgraph/test) "Testing Individual Nodes"(【官方】,2026-06 抓取)
import pytest
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
class S(TypedDict):
my_key: str
def node1(s: S) -> dict: return {"my_key": s["my_key"] + "1"}
def node2(s: S) -> dict: return {"my_key": s["my_key"] + "2"}
g = StateGraph(S)
g.add_node("node1", node1)
g.add_node("node2", node2)
g.add_edge(START, "node1")
g.add_edge("node1", "node2")
g.add_edge("node2", END)
graph = g.compile()
@pytest.mark.asyncio
async def test_node2_in_isolation():
out = await graph.nodes["node2"].ainvoke({"my_key": "x"})
assert out["my_key"] == "x2"
8.4.3 HITL 三步法
python
# 资料来源:[docs.langchain.com/oss/python/langgraph/interrupts](https://docs.langchain.com/oss/python/langgraph/interrupts) "Resuming" 段落(【官方】,2026-06 抓取)
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.types import interrupt, Command
from langgraph.checkpoint.memory import InMemorySaver
class S(TypedDict):
my_key: str
def human_review(state: S) -> dict:
answer = interrupt("approve?")
return {"my_key": answer}
g = StateGraph(S)
g.add_node("human_review", human_review)
g.add_edge(START, "human_review")
g.add_edge("human_review", END)
checkpointer = InMemorySaver()
graph = g.compile(checkpointer=checkpointer)
cfg = {"configurable": {"thread_id": "t1"}}
# step 1: triggers interrupt
result = graph.invoke({"my_key": "x"}, cfg)
assert "__interrupt__" in result # v1 风格;v2 见 streaming 文档
# step 2: resume
result2 = graph.invoke(Command(resume="approved"), cfg)
assert result2["my_key"] == "approved"
8.4.4 LLM-as-Judge(openevals)
python
from openevals.llm import create_llm_as_judge
# pip install openevals
judge = create_llm_as_judge(
prompt="Is the answer factually correct? Question: {inputs}\nAnswer: {outputs}",
model="openai:o3-mini",
feedback_key="correctness",
)
# 调用 judge(带 reference_outputs 与 few-shot examples)
result = judge(
inputs={"question": "法国的首都是哪里?"},
outputs={"answer": "巴黎。"},
reference_outputs={"answer": "巴黎(Paris)。"},
# few-shot 校准示例(每个 example 是 dict 列表)
few_shot_examples=[
{"inputs": {"question": "X"}, "outputs": {"answer": "Y"},
"score": 1.0, "comment": "答得对"},
],
)
print(result) # {'score': ..., 'comment': ...}
# 资料来源:[docs.langchain.com/langsmith/openevals](https://docs.langchain.com/langsmith/openevals) "LLM-as-judge SDK" 章节
# 稳定性治理见 8.5「LLM-as-Judge 稳定性治理」段
8.4.5 Pytest + LangSmith 集成
python
# pip install "langsmith[pytest]"
import pytest
from langsmith import testing as t
@pytest.mark.langsmith(output_keys=["d"])
def test_cd(c: int, d: int):
result = 2 * c
t.log_outputs({"d": result})
assert result == d
# LANGSMITH_TEST_SUITE="X" pytest tests/ → 自动建数据集 + 实验
# 资料来源:docs.langchain.com/langsmith/pytest("Writing Tests with Fixtures")
8.4.6 Replay(用历史 checkpoint 复跑 / fork 后继续)
python
# 资料来源:[docs.langchain.com/oss/python/langgraph/persistence](https://docs.langchain.com/oss/python/langgraph/persistence) "Time Travel" 章节(【官方】,2026-06 抓取)
thread_id = "old-t1"
# 1) 列出历史 checkpoint
history = list(graph.get_state_history({"configurable": {"thread_id": thread_id}}))
target = next(h for h in history if h.next == ("node_after_target",)) # 例:找下一步为某节点的快照
checkpoint_id = target.config["configurable"]["checkpoint_id"]
# 2) 复放到指定 checkpoint(state 作为起点重放)
replay_config = {"configurable": {"thread_id": thread_id, "checkpoint_id": checkpoint_id}}
graph.invoke(None, config=replay_config)
# 3) Fork 后继续:在新分支上修改 state 再 invoke
fork_config = {"configurable": {"thread_id": f"{thread_id}-fork-1"}}
graph.update_state(
fork_config,
values={"messages": [...]}, # 注入修改
as_node="node_before_target", # 标记注入位置
)
result = graph.invoke(None, config=fork_config)
assert result["messages"][-1].content == golden_or_evaluator(result)
注:复跑新版本图、同一
thread_id时,LangGraph 会沿用旧 checkpoint 兼容未变更部分;如已删除 / 重命名节点,参见 docs.langchain.com/oss/python/langgraph/graph-api "Graph migrations" 章节。trace 字段(thread_id/run_id/checkpoint_id)在不同 LangGraph 版本间的兼容性,参见 9.X.1 关联模型说明。
8.4.7 Multi-turn 模拟
python
from langsmith.evaluation import run_multiturn_simulation, create_llm_simulated_user
# user = create_llm_simulated_user(system_prompt=..., model=...)
# result = run_multiturn_simulation(app, user, max_turns=5, thread_id="t1")
# 资料来源:docs.langchain.com/langsmith/multi-turn-simulation
8.5 评估体系分层(离线 / 在线 / CI gate)
【官方】在线评估章节明确写到"Multi-turn evaluators run once per completed thread, not once per trace",并区分 run-level 与 thread-level evaluator(docs.langchain.com/langsmith/online-evaluations-llm-as-judge 与 online-evaluations-multi-turn)。【官方】本地评估章节给出
upload_results=False模式(docs.langchain.com/langsmith/local)。资料日期 2026-06 抓取。
| 维度 | 离线评估(Offline) | 在线评估(Online) | CI 评估(Eval-as-code gate) |
|---|---|---|---|
| 数据来源 | Golden dataset / 历史 thread 回灌 / 合成数据 | 线上真实 trace | Golden dataset(小子集) + 录制 fixture |
| 触发时机 | 主动跑:模型升级、prompt 改动、LangGraph 升级后 | 自动:trace 写入后按采样率触发 | 每次 PR / 每次 merge |
| 指标 | Final response、Trajectory、Single-step | 同上 + 业务 KPI(first contact resolution 等) | 关键不变量:trajectory 顺序、tool 选择、cost 上限 |
| 工具链 | LangSmith client.aevaluate、openevals、pytest plugin |
LangSmith Online Evaluators(run-level / thread-level) | pytest + @pytest.mark.langsmith、upload_results=False smoke |
| 失败处理 | 人工 review、重写 golden、调整 prompt | 自动告警 / 留样 / 路由回退 | 阻断 merge、生成失败报告 |
| 风险 | 离线过拟合到固定数据集 | 采样偏差、judge 自身不稳 | CI 跑全套 LLM 调用既慢又贵 |
| 适用 | 重大变更前 / 周期回归 | 7×24 持续监控 | 每次提交 |
关键不变量(Invariants) :每个层级都应断言至少一条"不依赖 LLM 评分"的事实------例如"必须调用工具 X"、"trajectory 中不能出现节点 Y"、"token 用量 < N"。这是 Eval-as-code gate 与 LLM-judge 体系混用时的安全网。
LLM-as-Judge 的稳定性治理(【官方】与【社区】综合):
- 至少 5--10 条 few-shot 校准样本;few-shot 可来自 LangSmith 的人审反馈回路(自动追加 few-shot)。
- Pairwise 比较("A 答案比 B 答案更好吗")比 absolute score 稳定。
- 同一 feedback_key 不复用于 run-level 和 thread-level(官方明确不推荐)。
- 对关键 judge,应设"双 judge 投票"或"judge + 启发式规则"双轨。
8.6 检查清单
- Reducer / 路由 / 纯函数节点是否都有"非 LangGraph 上下文"的纯 Python 单元测试?
- LLM 调用节点是否用
graph.nodes[name].ainvoke(...)隔离 + mock 模型? - 整图测试是否每个用例 fresh
InMemorySaver? - HITL / resume 流程是否覆盖 trigger →
Command(resume=...)→ 终态 三步? - 是否使用结构化 schema + LLM-as-Judge 替代"断言字符串相等"?
- judge prompt 是否经过 few-shot 校准、是否有 Pairwise 兜底?
- 是否记录了 golden dataset + replay 流程,能在升级 LangGraph / 换模型时一键回归?
-
interrupt是否被裸except包裹?pre-interrupt副作用是否幂等? - 多智能体 / multi-turn 是否用 trajectory evaluator 而非 turn-level 断言?
- Eval 脚本是否纳入 CI,并设了 fail-fast 阈值("Eval gate")?
- 是否对每个评估层(离线 / 在线 / CI)定义了数据来源、触发时机、指标、失败处理?
- 是否至少有一条"非 LLM"的不变量断言(trajectory 顺序 / 工具白名单 / 成本上限)?
第 9 章:可观测性
9.1 解决什么问题
LangGraph 应用在生产里必须回答四类问题:
- 哪个 run 慢 / 贵 / 失败? ------ 需要 trace 级别的延迟、token、错误聚合。
- 某个用户问题为什么返回错? ------ 需要把 thread → run → checkpoint → node → tool 一路串起来。
- 业务侧成功率是多少? ------ 需要把 trace 反馈成业务指标(首次解决率、人工介入率等)。
- 数据怎么脱敏、成本怎么控? ------ 需要 PII 匿名化、采样、OTLP 转发。
OSS 能力 vs 平台服务边界 (deep-research 事实底座列为"证据不足",本节给出语言降级,最终读者需结合自身部署与合规要求验证):
- OSS 自带能力 (随
langgraph包提供,无附加费用):StateGraph/Command路由、persistence(InMemorySaver/ SQLite / Postgres)、subgraph trace namespace、streaming(values/updates/messages等 stream mode)、graph.get_state/update_stateAPI。- 平台服务能力(需 LangSmith SaaS 或自托管 LangSmith):在线 LLM-as-Judge evaluator、在线 multi-turn 评估、trace 聚合看板、dataset 与 experiment 一体化管理、retention 策略、access control、按租户的 cost attribution。
- 混合使用时的边界:上送平台即视为出域;如需 PII / 合规隔离,应自建或选 OSS 后端(Phoenix / Langfuse / Opik),详见 9.X.2 选型表。
官方 docs.langchain.com/oss/python/langgraph/observability 把 LangGraph 的可观测性默认建在 LangSmith 上,并提供 OpenInference(Phoenix)、OTel OTLP 与 Langfuse / Opik 等社区方案的可选路径。
9.2 推荐做法
-
统一开关走环境变量 :
LANGSMITH_TRACING=true+LANGSMITH_API_KEY是 LangSmith 路径;自托管/开源替换时切到 OTel 端点(LangGraph observability,【官方】)。 -
采样与零保留分级 :用
LANGSMITH_TRACING_SAMPLING_RATE=0.1做全局降采样;用tracing_context(project_name=...)给敏感租户切到独立项目 + 单独保留策略(sample-traces,【官方】)。边界提示 :按租户切 project 是"按租户观测"的起点;retention / access control / 成本归属属平台侧配置,deep-research 事实底座将"LangGraph Platform / 自托管多租户架构"列为证据不足 ,最终读者需结合自身部署与合规要求验证,不应将tracing_context(project_name=...)误读为"完整多租户隔离方案"。调用形式:pythonfrom langsmith import tracing_context # 完整 import 见 9.4.1 with tracing_context(project_name="tenant-acme", tags=["acme"]): graph.invoke(inputs, config={"configurable": {"thread_id": "t-1"}}) -
每个 invoke 带 thread_id + tags + metadata :thread_id 串联多轮;tags / metadata 进入 trace 过滤维度(LangSmith Observability 与 LangGraph observability metadata 段,【官方】)。
-
节点级日志走 LangSmith 的
tracing_context,不要把 LLM 输入 / 输出打到 stdout------既不脱敏也不可聚合。 -
隐私脱敏用
create_anonymizer+ 正则 (如 SSN 模式\b\d{3}-?\d{2}-?\d{4}\b→<ssn>),attach 到Client后通过LangChainTracer应用到 graph(LangGraph observability "Anonymizing Sensitive Data",【官方】)。覆盖范围与限制见 9.X.4 节。 -
第三方开源 / 自托管 OTel 路径:
- Phoenix (Arize) :装
openinference-instrumentation-langchain,调phoenix.otel.register(project_name=..., auto_instrument=True),无需改 LangGraph 代码(【社区/官方】)。 - Langfuse :
pip install langfuse+from langfuse.langchain import CallbackHandler(v3 SDK 路径)→config={"callbacks": [handler]};短任务需get_client().shutdown()flush(Langfuse 集成,【社区】)。 - Opik (Comet) :
OpikTracer+track_langgraph(app),可在节点内通过@track装饰器手动埋点(Opik LangGraph 集成,【社区】)。
- Phoenix (Arize) :装
-
OTLP 导出到 LangSmith(官方支持,详见 9.X.3 A 类);纯 OTel Collector 路径需自行接 instrumentation + tracer provider,官方未直接给出最小示例。 端点默认
https://api.smith.langchain.com/otel/,自托管 LangSmith 追加/api/v1/otel;Headers 需带x-api-key=...与Langsmith-Project=...。导出到任意第三方 OTel 后端(Jaeger / Tempo / Datadog 等)属 9.X.3 B 类,依赖 community instrumentation(evaluate-with-opentelemetry,【官方】;纯 OTel Collector 路径属【待验证】)。 -
subgraph trace 命名空间 :父图用
subgraphs=True拿嵌套事件,namespace 元组("parent_node:<task_id>", "child_node:<task_id>")区分层级。资料来源:docs.langchain.com/oss/python/langgraph/streaming(LangGraph ≥1.1 的 v2 输出格式)。 -
业务指标落到 trace 反馈 :成功率、checkpoint 命中率、retry 率、人工介入率通过
client.create_feedback(...)写入 LangSmith dataset 反馈,可与 trace id 关联做漏斗分析。 -
Eval-as-code 同步进入 trace :评估脚本用
client.aevaluate(...),可指定 dataset 关联到 trace;设阈值后即可在 CI 阻断。
9.3 不推荐做法
- 在生产里关掉 trace 节省 token。 故障定位成本远高于 trace 成本。
- 不设采样,把每条 trace 全量上报。 单次大流量场景下成本不可控。
- 把 LLM 输入 / 原始 PII 直接写应用日志。 不可聚合、不可脱敏、合规风险。
- 多智能体场景不开
subgraphs=True。 嵌套 trace 没法看。 - 业务指标不与 trace 关联。 失败归因只能靠"目测"。
- 同 feedback_key 复用于 run-level 和 thread-level online evaluator。 官方明确不推荐;评分会互相覆盖。
- 多轮 thread 评估开过短 idle time。 人还没回完,evaluator 就已触发。资料来源:
docs.langchain.com/langsmith/online-evaluations-multi-turn。 - observability 当成"装上 SDK 就行"。 必须把 trace、metric、log 三个维度对齐业务事件。
9.4 最小代码示例
依赖:
langgraph>=1.0、langsmith>=0.3、openinference-instrumentation-langchain、langfuse>=3、opik、Python ≥3.10。资料日期 2026-06-11。
9.4.1 LangSmith(官方托管)
python
import os
os.environ["LANGSMITH_TRACING"] = "true"
os.environ["LANGSMITH_API_KEY"] = "lsv2_..."
os.environ["LANGSMITH_PROJECT"] = "prod-agent"
# 多租户采样:按租户路由到不同 project
from langsmith import tracing_context
with tracing_context(project_name="tenant-acme", tags=["acme"]):
graph.invoke(inputs, config={"configurable": {"thread_id": "t-1"}})
# 资料来源:docs.langchain.com/oss/python/langgraph/observability
9.4.2 Phoenix (Arize) --- OpenInference 自动埋点
python
# pip install openinference-instrumentation-langchain
from phoenix.otel import register
tracer_provider = register(project_name="my-llm-app", auto_instrument=True)
# 之后所有 LangGraph / LangChain 调用自动 span 上报
# 资料来源:arize.com/docs/phoenix/integrations/python/langgraph/langgraph-tracing(2026-06 抓取)
9.4.3 Langfuse --- v3 SDK
python
from langfuse import get_client
from langfuse.langchain import CallbackHandler
langfuse = get_client() # 自动读 LANGFUSE_SECRET_KEY/PUBLIC_KEY/BASE_URL
handler = CallbackHandler()
graph.invoke(inputs, config={"callbacks": [handler], "configurable": {"thread_id": "t-1"}})
# 短任务退出前:get_client().shutdown()
# 资料来源:langfuse.com/docs/integrations/langchain("Migration v2 → v3" 章节)
9.4.4 Opik (Comet)
python
from opik.integrations.langchain import OpikTracer
from opik import track_langgraph
tracer = OpikTracer()
graph_tracked = track_langgraph(graph, project_name="prod-agent")
graph_tracked.invoke(inputs) # 不再需要每次传 callbacks
# 节点内可用 @track;async 需 extract_current_langgraph_span_data
# 资料来源:comet.com/docs/opik/integrations/langgraph
9.4.5 OTLP 直接导出到任意 OTel 后端
python
import os
os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = "https://otel-collector.internal:4317"
os.environ["OTEL_EXPORTER_OTLP_HEADERS"] = "x-api-key=lsv2_...,Langsmith-Project=exp-123"
# LangGraph / LangChain 自动用 OTel 导出器
# 资料来源:docs.langchain.com/langsmith/evaluate-with-opentelemetry
9.4.6 业务反馈写入
python
from langsmith import Client
client = Client()
client.create_feedback(
run_id=trace_id,
key="first_contact_resolution",
score=1,
comment="user resolved in 1 turn",
)
# 资料来源:docs.langchain.com/langsmith/online-evaluations-llm-as-judge
9.5 检查清单
- 是否有统一的 trace 开关(env var 或
tracing_context),且支持按租户 / 标签路由 project? - 是否设了全局采样(
LANGSMITH_TRACING_SAMPLING_RATE)或条件采样? - 是否对 PII 走
create_anonymizer脱敏,且 thread_id 已脱敏? - multi-agent / subgraph trace 是否开启
subgraphs=True? - 是否至少有 1 个自托管 / OTel 路径可回退(避免单一供应商锁定)?
- 业务指标(成功率、人工介入率、retry 率、checkpoint 命中率)是否通过
create_feedback进入 trace? - 是否有 trace id / run id / thread id / checkpoint id 的串联约定,可一键定位用户问题?
- 多轮 thread evaluator 的
idle_time是否设得大于典型用户回复时长? - 是否区分了 LangSmith 商业能力 vs 开源/OTel 能力,并明确哪些依赖必须付费?
- 短任务 / serverless 环境是否在退出前调用了 flush(
get_client().shutdown()等)? - 是否对所有第三方后端(Phoenix / Langfuse / Opik / Helicone)做过 PII 脱敏一致性评估?
- 节点日志是否只记"标识 + 结构化状态 diff + 异常类型",不记完整 messages / 工具原始返回?
9.X 附加小节(trace 关联模型 / 后端选型 / OTLP 分类 / 脱敏范围)
以下为可观测性补充说明,统一保留【官方】/【社区】/【推测】标记。
9.X.1 trace 关联模型:thread / run / checkpoint / node / tool
【官方】来源:docs.langchain.com/oss/python/langgraph/persistence "Checkpoint" 章节与 docs.langchain.com/oss/python/langgraph/observability;【官方】stream v2 格式:docs.langchain.com/oss/python/langgraph/streaming。资料日期 2026-06 抓取。
关联模型:
tenant / project
└── thread_id ← 用户/会话维度;同一会话多轮共享
└── run_id ← 一次 invoke/stream 调用维度(LangSmith run)
└── checkpoint_id ← 每个 super-step 一个 snapshot
├── node_name
├── tool_call_id
└── input_state / output_state / writes
字段定义:
| 字段 | 来源 | 维度 | 作用 |
|---|---|---|---|
thread_id |
用户传入 config.configurable.thread_id |
会话 | 串联多轮;支持 time travel 与 fork |
run_id |
LangSmith 自动分配 | 一次调用 | 与 LangSmith trace 一一对应 |
checkpoint_id |
checkpointer 自动 | 每个 super-step | 时间旅行、fork、回放 |
langgraph_step |
node 内 config["metadata"]["langgraph_step"] |
super-step 序号 | 排查循环/recursion |
langgraph_node |
自动 | 节点 | 节点级 trace 与日志 |
checkpoint_ns |
自动 | 子图 namespace | 多层 subgraph 区分 |
节点变更与 replay 兼容性 :见 [8.4.6 注](#8.4.6 注)。节点被重命名或删除会破坏同一
thread_id的 replay 兼容性,trace 字段本身(thread_id/run_id/checkpoint_id/checkpoint_ns)在 LangGraph 主版本间保持稳定。
节点日志应记录什么 (【官方】+【社区】综合,避坑参考:docs.langchain.com/oss/python/langgraph/interrupts "Rules of Interrupts" 强调"side effects before interrupt must be idempotent"):
| 类别 | 记录 | 不记录 |
|---|---|---|
| 标识 | thread_id、run_id、checkpoint_id、node_name、step |
--- |
| 上下文 | messages[-1].type、工具白名单命中、模型 id |
完整 messages 原文(成本 + 脱敏风险) |
| 状态 | 节点前后 state diff(key 级别,structured) | 整个 state 序列化 |
| 结果 | token 用量、延迟、tool call 名称 | 工具原始返回值(大对象) |
| 异常 | 异常类型 + 消息 + stack id | 完整 traceback(含可能的 PII) |
使用 v2 streaming 拿状态 diff / 事件流 (docs.langchain.com/oss/python/langgraph/streaming "v2 Unified Output Format",LangGraph ≥1.1):
python
for part in graph.stream(inputs, stream_mode=["updates", "checkpoints"], version="v2"):
if part["type"] == "updates":
for node_name, state_update in part["data"].items():
log_diff(thread_id, node_name, state_update)
elif part["type"] == "checkpoints":
cp = part["data"]
log_checkpoint(thread_id, cp["config"]["configurable"]["checkpoint_id"])
在 LangSmith / OTel span metadata 中落这些字段:
python
config = {
"configurable": {"thread_id": "t-1"},
"tags": ["prod", "tenant:acme", "agent:order_agent"],
"metadata": {
"tenant_id": "acme",
"feature_flag": "new_router",
"expected_trajectory": ["router", "order_agent", "responder"],
},
}
graph.invoke(inputs, config=config)
# 这些 tag/metadata 全部会进入 LangSmith trace / OTel span attribute
# 资料来源:docs.langchain.com/oss/python/langgraph/observability "Metadata and Tags" 段
9.X.2 第三方可观测性后端选型表
选型参考:【官方】LangSmith 与各后端集成页(资料日期 2026-06 抓取);【社区】运营经验。
部署形态:☁️ 商业 SaaS / 🛠️ 自托管 OSS / 两者皆可。
| 后端 | 定位 | 部署 | LangGraph Python 接入方式 | 优点 | 缺点 | 成熟度 |
|---|---|---|---|---|---|---|
| LangSmith | LangChain 系官方一体化 trace + eval + dataset | ☁️ | LANGSMITH_TRACING=true + LANGSMITH_API_KEY 或 from langsmith import Client |
官方维护;与 LangGraph 兼容最完整;在线 / 离线 eval 一体 | 商业 SaaS 收费;数据出域 | 高(官方) |
| Phoenix(Arize) | OSS 可观测性 + 评估 | 🛠️ | pip install openinference-instrumentation-langchain + phoenix.otel.register(project_name=..., auto_instrument=True) |
开源;OpenInference 标准;本地可跑 | 与 LangGraph 集成需经过 OpenInference 间接层 | 高(社区/官方) |
| Langfuse | OSS LLM 可观测性 + eval | ☁️🛠️ | from langfuse.langchain import CallbackHandler + config={"callbacks": [...]} |
易部署;JS/TS 与 Python 对等;支持 session / user 维度 | 短任务需 get_client().shutdown() flush |
高(社区) |
| Opik(Comet) | LLM eval + trace | ☁️🛠️ | OpikTracer + track_langgraph(app) |
节点内 @track 装饰器;eval 与实验管理 |
节点 async 需 extract_current_langgraph_span_data |
中-高(社区) |
| Helicone | LLM gateway + 观测 | ☁️🛠️ | 间接接入:OpenAI-compatible proxy 替换 base_url | 对调用级成本 / 延迟聚合强 | 【待验证】 无 LangGraph 专用集成页面 | 中(社区) |
Helicone 接入参考(【推测】基于社区博客与 Helicone 文档对 OpenAI-compatible proxy 的描述):
pythonfrom langchain_openai import ChatOpenAI llm = ChatOpenAI( model="gpt-4o-mini", base_url="https://oai.helicone.ai/v1", api_key=os.environ["HELICONE_API_KEY"], default_headers={"Helicone-Auth": f"Bearer {os.environ['HELICONE_API_KEY']}"}, ) # 之后 LangGraph / LangChain 自动通过 base_url 把请求经 Helicone
成熟度分级依据(用于解释上表"成熟度"列四档差异):
- 高(官方):LangSmith 由 LangChain 官方维护,与 LangGraph 同源;文档 / 集成 / 版本兼容最完整。
- 高(社区/官方):Phoenix(Arize)由 OpenInference 标准支撑,Arize 与 LangGraph 团队均维护集成示例,跨 LLM 框架兼容强。
- 中-高(社区):Langfuse / Opik 在社区有大量生产案例,但与 LangGraph 的耦合需经 Callback / Tracer 抽象,少数边界(如 async context 传播)需自处理。
- 中(社区):Helicone 定位为 LLM gateway,未直接对接 LangGraph;接入路径经 OpenAI-compatible proxy 间接完成,trace 串联与状态 diff 需在业务侧补齐。
9.X.3 OTLP 导出:分两类讲清
【官方】OTel 文档明确给出"导出到 LangSmith"端点(docs.langchain.com/langsmith/evaluate-with-opentelemetry)。【官方】LangChain/LangGraph 对纯 OTel Collector(无 LangSmith)端到端配置未直接给出最小示例,属【待验证】。
A 类:导出到 LangSmith(官方支持)
python
import os
os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = "https://api.smith.langchain.com/otel/"
os.environ["OTEL_EXPORTER_OTLP_HEADERS"] = (
f"x-api-key={os.environ['LANGSMITH_API_KEY']},Langsmith-Project={experiment_id}"
)
# 自托管 LangSmith 把 endpoint 改成 https://<self-hosted>/api/v1/otel
B 类:导出到纯 OTel Collector / Jaeger / Tempo(社区/待验证)
要在非 LangSmith 后端拿完整 trace,至少需要以下三类组件(【社区】综合):
- Instrumentation :
openinference-instrumentation-langchain(Phoenix 维护)或opentelemetry-instrumentation-langchain(社区)。 - Tracer provider + Exporter :
trace.set_tracer_provider(...)+BatchSpanProcessor(OTLPSpanExporter(...)),endpoint 指向 OTel Collector。 - Span processor 注入 :把
LangChainTracer或 OTel span handler 通过config={"callbacks": [...]}传给图调用;或使用tracing_context包裹。
⚠️ 建议参考 openinference-instrumentation-langchain 示例后做端到端验证。
9.X.4 隐私脱敏:覆盖范围与限制
【官方】来源:docs.langchain.com/oss/python/langgraph/observability "Anonymizing Sensitive Data" 章节。【社区】补充:跨后端脱敏一致性需业务侧自行实现。
| 字段 | 是否被 LangSmith create_anonymizer 处理 |
备注 |
|---|---|---|
| LLM inputs / outputs | ✅ 走 LangChainTracer callback 时被处理 | 默认仅 SSN 之类内置正则 |
| Tool args / tool returns | ✅ 同上 | 自定义工具返回值需在 tool 内部先脱敏 |
| Tags | ❌ 不会自动处理 | 业务侧控制(建议不写 PII) |
| Metadata | ❌ 不会自动处理 | 业务侧控制(建议不写 PII) |
thread_id |
❌ 不会自动处理 | 业务侧传入时建议用 hash |
| Phoenix / Langfuse / Opik 路径 | ❌ 不共享 | 各后端需独立配置脱敏 |
关键限制 :anonymizer 是客户端侧的正则替换;只能防"trace 文本里出现 PII",无法防"工具内部把 PII 写到数据库"等业务层泄漏。
第 10 章:性能、成本与可靠性
10.1 解决什么问题
生产环境的 LLM 链路必须同时回答四类问题:
- 上下文无限增长:长对话、长期任务会撑爆模型 context window,导致单次调用变慢、变贵、甚至失败。
- 算力与延迟成本:单次 LLM 调用占整个请求延迟 80% 以上,浪费一次重算 = 多花数秒 + 几美分到几美元。
- 外部 provider 不稳定:OpenAI / Anthropic / Bedrock 都可能 5xx 限流,必须能在 provider 失败时降级、重试、切换。
- 资源竞争:多用户、多租户并发会互相饿死;需要 token 预算、并发上限、QPS 限流。
官方能力矩阵在 2025/2026 年发生重要变化:LangGraph 1.1 引入 stream v2,1.2 引入 DeltaChannel 增量持久化、StreamWriter 参数等。本章基于这些能力展开。
10.2 推荐做法
10.2.1 上下文窗口控制:四种官方机制
官方 LangGraph 提供四种独立可叠加的机制(来源:docs.langchain.com/oss/python/langgraph/add-memory,2026-06):
RemoveMessage物理删除:从 state 中抹去消息,对应 checkpointer 持久化也会被裁剪。trim_messages临时裁剪:裁剪后送入 LLM 的列表,但 state 中仍保留全部消息。- Summarize 压缩:保留近期 + 早期摘要。
- Custom filter:按角色、tool_call_id、长度自定义筛选。
四者关系:state 是真相 ,trim_messages 只决定「这次送什么给 LLM」,RemoveMessage 决定「持久化中保留什么」。
官方 DeltaChannel(langgraph>=1.2,beta API )只持久化增量 delta,显著减少 append-heavy 通道(如 messages)的 checkpoint 体积(来源:docs.langchain.com/oss/python/langgraph/persistence,2026-06)。
DeltaChannel使用条件(待验证):
- 当前为 beta API,适用范围、是否对所有 checkpointer 后端生效、是否需要自定义配置 以官方 persistence 文档为准(
docs.langchain.com/oss/python/langgraph/persistence,2026-06)。- 1.x → 1.3+ 升级时关注 GA 状态;启用前应在 staging 验证 checkpoint 体积与恢复行为。
- 10.5 检查清单对应项已改为「评估启用」而非默认「启用」。
10.2.2 并行与异步:Send / 并行边 / Functional API
官方 三种并发模式(来源:docs.langchain.com/oss/python/langgraph/use-graph-api,2026-06):
| 模式 | 适用场景 | 关键 API |
|---|---|---|
| 固定扇出 | 静态可知的并行子任务 | 多条 add_edge 从同一节点出发 |
| 动态扇出 | 运行时才能决定子任务数量 | Send("node", {"k": v}) 列表 |
| Functional API | LLM 工具调用、长 I/O 任务、幂等副作用 | @task + .result() |
官方 并发上限控制(来源:同上):graph.invoke(input, {"configurable": {"max_concurrency": 10}})。
官方 + 推测 parallel_tool_calls 与状态合并风险:模型 provider / LangChain 工具层可并行发起多个 tool call,但 LangGraph state 合并仍依赖 reducer 语义。生产中应避免多个并行工具写同一个覆盖式字段;需要合并的字段必须显式使用 Annotated[..., reducer](如 operator.add / add_messages),否则后写入可能覆盖先写入。Send 动态扇出也遵循同一原则:并行分支返回的同名 key 必须有可交换/可结合的 reducer,或在 join 节点中显式汇总。
官方 @task 装饰的函数返回 future-like 对象,结果自动写入 checkpoint,恢复时不重算。Tasks 适合封装外部副作用(来源:docs.langchain.com/oss/python/langgraph/functional-api,2026-06)。
10.2.3 Prompt Caching:Anthropic / OpenAI
官方 Anthropic 缓存(来源:platform.claude.com/docs/en/docs/build-with-claude/prompt-caching,2026-06):
cache_control: {type: "ephemeral"}(默认 5 分钟 TTL,1.25× 写入,0.1× 读取)。ttl: "1h"1 小时 TTL(2× 写入,0.1× 读取)。- 每请求最多 4 个 breakpoints(显式 + 自动各算一个 slot)。
- 顺序:长 TTL 必须在短 TTL 之前。
官方 OpenAI 缓存(来源:developers.openai.com/api/docs/guides/prompt-caching,2026-06):
- 自动开启,无代码改动,prompt ≥ 1024 tokens 触发。
- 路由依据:prefix hash(典型前 256 tokens)。
- 默认 in-memory TTL 5--10 分钟,最多 1 小时。
- 价格:最多降低 80% 延迟、90% 输入成本;
cached_tokens字段报告命中。
官方 跨框架配合(来源:LangChain 官方文档,2026-06):
ChatAnthropic透传cache_control({"type": "ephemeral", "ttl": "1h"})与cache_read_input_tokens/cache_creation_input_tokens。ChatOpenAI透传cached_tokens(位于usage.prompt_tokens_details.cached_tokens)------OpenAI 不使用也不接受cache_control字段;OpenAI 缓存是自动启用,无代码改动(OpenAI 官方文档:developers.openai.com/api/docs/guides/prompt-caching,2026-06)。- 在 graph 状态里读
resp.usage_metadata即可观测命中率;Anthropic / OpenAI 字段名不通用。
10.2.4 Provider Failover & Multi-Provider
官方 + 推测 LangGraph 本身不提供 provider 抽象,但可以:
- 通过 LangChain 的
init_chat_model在节点内根据错误类型切换 provider。 - 用
langchain-anthropic/langchain-openai/langchain-aws多 provider 同接口。 - 把 LLM 调用放进
@task,让 task 重试语义可被 checkpointer 恢复。
社区 常用 LiteLLM 统一 provider 入口,配合 with_fallbacks([...])(LangChain 原生)或上层重试装饰器实现 failover(来源:langchain-ai/langchain#docs,2025;标注为社区经验,官方未提供内置 failover 抽象)。
10.2.5 可靠性:Durability、Idempotency、Retry
官方 持久化模式三档(来源:docs.langchain.com/oss/python/langgraph/persistence,2026-06):
"exit":仅退出时持久化,性能最高,崩溃后无中间态。"async":下一步前异步持久化,崩溃有小窗口。"sync":下一步前同步持久化,最强保证,性能损耗最大。
官方 Functional API 的 @task + idempotency_key 是官方推荐模式(来源:functional-api 文档,2026-06):
"For side effects, API calls, writes, and retryable work: Put the operation in a
@task. Make the task idempotent because a task that began but did not finish may run again. Use an idempotency key or check whether the external action already happened."
官方 节点副作用必须幂等(来源:graph-api 文档 + interrupts 文档,2026-06;deep-research §核心事实 #6 + #11):
- Checkpoint 在 super-step 边界 保存,不在节点函数中途保存(deep-research §核心事实 #6)。
- 恢复时整个节点从头执行 ,不沿
interrupt()调用点继续(deep-research §核心事实 #11)。 - 因此任何节点内副作用都必须幂等 (idempotency key、upsert、read-before-write check),这是"pre-
interrupt副作用幂等"和"整个节点副作用幂等"两条事实共同支撑的结论,不是"只要 pre-interrupt 幂等就够"。
官方 interrupt 调用语义(来源:interrupts 文档,2026-06):
interrupt()调用按严格索引匹配,payload 应使用 JSON-serializable 值。interrupt()抛特殊异常暂停节点;恢复时通过Command(resume=...)提交。
注意 :deep-research §被 refute 的错误说法 #1 明确"严禁在 try/except 中包裹 interrupt"是绝对化、不可成立的。本稿不 采用该绝对化措辞;本节所引"幂等性"约束与 try/except 包裹方式正交 ------示例可正常用 try/except 处理业务异常,只要不捕获
interrupt自身(Python 通过让Interrupt异常基类不被吞掉即可)。
10.2.6 重试 / 熔断 / 降级 / DLQ
官方 + 社区 LangGraph 官方仅提供原子级 重试语义(@task 重跑 + durability),不直接提供"应用层"重试 / 熔断 / 降级 / DLQ 抽象;这部分需应用层自实现:
| 关注点 | LangGraph 官方能力 | 应用层补足(社区经验) |
|---|---|---|
| 任务级重试 | @task 自动从 checkpoint 重跑,相同 idempotency_key 不重复 |
tenacity 装饰器(指数退避 + jitter);自定义重试中间件 |
| 熔断(circuit breaker) | 无 | aiobreaker / pybreaker / 自建 token bucket |
| 降级(fallback) | with_fallbacks([...])(LangChain) |
LangChain 原生 + LiteLLM 统一 provider 入口 |
| DLQ(dead-letter queue) | 无"超过 N 次入 DLQ"开箱 | 自建 Redis sorted set / Postgres failed_runs 表,配合外部 worker 消费 |
应用层补足参考实现 (社区,非官方):
python# tenacity 指数退避 + 熔断 from tenacity import retry, stop_after_attempt, wait_exponential_jitter from aiobreaker import CircuitBreaker breaker = CircuitBreaker(fail_max=5, timeout_duration=60) @retry(stop=stop_after_attempt(3), wait=wait_exponential_jitter(initial=1, max=10)) @breaker async def call_llm_with_protection(prompt): return await model.ainvoke(prompt)DLQ 可用 Redis sorted set(
ZADD dlq <ts> <run_id>)+ 外部 workerZRANGEBYSCORE拉取。
10.2.7 Token 预算与 QPS 限流
官方 + 推测 LangGraph OSS 不直接提供 token 预算与 QPS 限流;这两类资源保护需在节点内或中间件实现:
| 维度 | LangGraph 官方能力 | 实现路径 |
|---|---|---|
| 并发上限(任务级) | max_concurrency(graph.invoke configurable 参数) |
官方内置 |
| Token 预算(成本) | 无 | 节点内计数器(tiktoken 预估 + 累计);LangSmith rate limit callback;外部 Redis token bucket |
| QPS 限流(速率) | 无 | 节点内 aiocache + 滑动窗口;NGINX / API Gateway 层;slowapi(FastAPI 中间件) |
不要把
max_concurrency当成完整限流 :它只控制任务并发数 ,不控制 token 消耗速率或对外 provider 的 QPS。生产环境需叠加 token 预算 + QPS 限流两层。
10.3 不推荐做法
| 反模式 | 后果 | 出处 |
|---|---|---|
节点内直接 time.sleep / requests.post |
resume 时副作用重复触发,破坏 checkpoint 重放 | 官方 functional-api 警告 |
节点内 open(path).read() 读外部文件 |
同上,恢复时路径/内容可能不一致 | deep-research §核心事实 #11(节点从头重跑) |
用 InMemorySaver / MemorySaver 兼容别名跑生产 |
进程重启 = 全部 thread 丢失,HIL 状态归零;MemorySaver 只是 InMemorySaver 的向后兼容别名 |
官方明令「不可生产」;附录 A A.2.2 / A.4.2 已统一 InMemorySaver 为主名、MemorySaver 为兼容别名 |
| 工具调用无超时、无错误分类 | 慢工具拖垮整条图,retry 风暴 | 社区经验 + 第 6 章反模式 |
路由函数依赖 time.time() / random |
checkpoint 重放时路径不一致,无法 fork | 官方 time-travel 文档隐含 |
把 messages 整列塞 LLM 不裁剪 |
命中 context 上限后 token 单价 × 输入长 | 第 4 章状态设计反模式 |
| 在 conditional edge 里写业务逻辑 | 不可单测、不可追踪 | 第 3 章反模式 |
| 缓存命中率不监控、不告警 | 看似开了缓存,实则 prefix 漂移命中率 0% | 推测 |
| 重试不带指数退避 | 雪崩放大 | 推测(与社区共识一致) |
| 无熔断 / 无 DLQ 的外部副作用调用 | 持续失败拖垮主链路,无失败重放机制 | 社区经验(tenacity / aiobreaker) |
把 max_concurrency 当成完整限流 |
缺 token 预算 + QPS 限流,成本与速率均失控 | 推测(OSS 不提供) |
并行工具调用 / Send 分支写同一覆盖式 state key |
后写入覆盖先写入,结果丢失或顺序不稳定 | 官方 reducer 语义 + 推测(并发合并风险) |
10.4 最小代码示例
示例 A:trim_messages + RemoveMessage 组合(LangGraph 1.2+)
python
# 依赖:langgraph>=1.2, langchain-core>=1.4.0,<2(与 langgraph 1.2.4 的 PyPI requires_dist 对齐)
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END, MessagesState
from langchain_core.messages.utils import (
trim_messages,
count_tokens_approximately,
)
from langchain.messages import RemoveMessage
from langgraph.checkpoint.postgres import PostgresSaver
class State(MessagesState):
summary: str
# 1) trim 决定送入 LLM 的内容(不污染 state)
def call_model(state: State):
selected = trim_messages(
state["messages"],
strategy="last",
token_counter=count_tokens_approximately,
max_tokens=2048,
start_on="human",
end_on=("human", "tool"),
)
resp = model.invoke(selected) # model = init_chat_model("claude-sonnet-4-6")
return {"messages": [resp]}
# 2) summarize + RemoveMessage 决定持久化保留什么
def maybe_summarize(state: State):
if len(state["messages"]) < 20:
return
summary_resp = model.with_structured_output(...).invoke(...)
return {
"summary": summary_resp.content,
"messages": [RemoveMessage(id=m.id) for m in state["messages"][:-4]],
}
builder = StateGraph(State)
builder.add_node("call_model", call_model)
builder.add_node("maybe_summarize", maybe_summarize)
builder.add_edge(START, "call_model")
builder.add_conditional_edges("call_model", lambda s: "maybe_summarize" if len(s["messages"]) > 20 else END)
builder.add_edge("maybe_summarize", END)
DB = "postgresql://user:pwd@host:5432/langgraph"
checkpointer = PostgresSaver.from_conn_string(DB)
checkpointer.setup()
graph = builder.compile(checkpointer=checkpointer)
示例 B:Send 动态扇出 + max_concurrency(LangGraph 1.2+)
python
import operator
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.types import Send
class Overall(TypedDict):
items: list[str]
results: Annotated[list[dict], operator.add]
class Item(TypedDict):
item: str
def split(state: Overall):
return {"items": state["items"]} # 任意长度的运行时列表
def worker(state: Item):
# 模拟 LLM 调用或外部 API
return {"results": [{"item": state["item"], "out": state["item"][::-1]}]}
def fanout(state: Overall):
return [Send("worker", {"item": x}) for x in state["items"]]
g = StateGraph(Overall)
g.add_node("split", split)
g.add_node("worker", worker)
g.add_edge(START, "split")
g.add_conditional_edges("split", fanout, ["worker"])
g.add_edge("worker", END)
graph = g.compile()
# 限流:同一 superstep 最多并发 10
graph.invoke({"items": list(range(50))}, {"configurable": {"max_concurrency": 10}})
示例 C:@task + 幂等 key + 异常处理 + 崩溃恢复(Functional API,LangGraph 1.2+)
python
import os
import stripe
from langgraph.func import entrypoint, task
from langgraph.checkpoint.postgres import PostgresSaver
from langgraph.types import Command
from langchain_core.messages import HumanMessage
from langgraph.errors import TaskException
# 1) @task 封装外部副作用
@task
def charge_customer(order_id: str, amount_cents: int) -> dict:
# 关键:把外部副作用封装在 task 中
# task 自动从 checkpoint 恢复;已成功完成的 task 不会重跑
# 使用 provider 的 idempotency_key 保证幂等
try:
return stripe.Charge.create(
amount=amount_cents,
currency="usd",
idempotency_key=order_id, # 同一 order_id 不会重复扣款
)
except stripe.error.CardError as e:
# 业务可重试错误,task 失败抛出,由 entrypoint 决定如何处理
raise
checkpointer = PostgresSaver.from_conn_string(os.environ["DB_URL"])
checkpointer.setup()
# 2) entrypoint 编排;task 失败时 .result() 抛 TaskException
@entrypoint(checkpointer=checkpointer)
def order_flow(order: dict) -> dict:
try:
charge = charge_customer(order["id"], order["amount"]).result()
except TaskException as exc:
# task 失败处理:记录 + 抛出(可在此重试或入 DLQ)
log_failure(order["id"], exc)
raise
return {"charge_id": charge["id"]}
# 3) 正常调用
config = {"configurable": {"thread_id": order["id"]}}
result = order_flow.invoke(order, config=config)
# 4) 崩溃恢复演示:
# 场景:上面 invoke 进程在 charge_customer 已成功但 entrypoint 返回前崩溃
# 操作:重新 invoke 同一 thread_id,@task 自动不重跑(idempotency_key 命中)
# 验证:第二次 invoke 不会重复扣款,stripe 控制台可看到同一 idempotency_key 只产生一次 charge
config_again = {"configurable": {"thread_id": order["id"]}}
result_again = order_flow.invoke(order, config=config_again) # 不重复扣款
关于
try/except与interrupt的关系 (澄清 deep-research §被 refute 的错误说法 #1):try/except可正常用于捕获业务异常(如stripe.error.CardError);Interrupt异常不应被吞掉,但只要不显式except Interrupt,Python 异常传播机制即能正确处理。本示例的try/except仅捕获stripe.error.CardError,不 捕获Interrupt,因此 interrupt 暂停语义不受影响。
示例 D:Anthropic prompt caching + cached_tokens 观测
python
from pathlib import Path
from langchain.chat_models import init_chat_model
from langchain.messages import SystemMessage, HumanMessage
model = init_chat_model("claude-sonnet-4-6")
# 建议:kb.md 在 compile/build 阶段读取并 cache,节点内不再做 I/O
# 原因:LangGraph 1.x 节点在 interrupt 恢复时整个节点从头执行(deep-research #11),
# 节点内 open() 会重复执行;放模块级常量更安全
KB = Path("kb.md").read_text() # 模块级常量,进程启动时读一次
def call(state):
# 1) 系统提示做长期缓存(1h TTL)
system = [
{"type": "text", "text": "你是资深客服。"},
{
"type": "text",
"text": KB, # 用模块级常量,避免节点内 open() 副作用
"cache_control": {"type": "ephemeral", "ttl": "1h"},
},
]
resp = model.invoke(
[SystemMessage(content=system), *state["messages"]],
)
# 2) 观测命中率,写到自定义字段
usage = resp.usage_metadata
print({
"input": usage.get("input_tokens"),
"cache_read": usage.get("cache_read_input_tokens"),
"cache_creation": usage.get("cache_creation_input_tokens"),
})
return {"messages": [resp]}
10.5 检查清单
- State 中消息已用
add_messagesreducer,并按需trim_messages限制送入 LLM 的长度 - 长对话(>20 轮)启用 summarize 节点 +
RemoveMessage物理裁剪 - 监控
cache_read_input_tokens/cached_tokens(Anthropic / OpenAI 字段不同),缓存命中率纳入告警 - Anthropic 缓存 breakpoints ≤ 4,长 TTL 在短 TTL 之前;OpenAI 不使用
cache_control字段 - OpenAI 提示前缀静态 ≥ 1024 tokens,动态内容放末尾
- 外部副作用(支付、邮件、写入)封装在
@task内并带 idempotency key - 节点内全部副作用 幂等(不只 pre-
interrupt部分;super-step 边界 + 节点从头重跑共同要求) - 节点内不直接
time.sleep/requests.post/open(path).read(),改用@task或模块级常量 - LLM 节点超时与重试(指数退避 + jitter)已配置;熔断(circuit breaker)已设置
- 失败超过 N 次自动入 DLQ(Redis sorted set / 外部表 + worker)
- Provider 故障有 fallback provider / 错误分类(5xx 重试,4xx 立即失败)
-
max_concurrency已根据下游 provider 限流策略配置 - token 预算(节点内计数器 / LangSmith callback / Redis token bucket)已配置
- QPS 限流(节点内 / API gateway /
slowapi)已配置;不要把max_concurrency当完整限流 -
durability三档(exit/async/sync)按业务选择并写入 runbook -
DeltaChannel当前为 beta API;适用 checkpointer 后端以官方 persistence 文档为准;升级时关注 GA 状态 - 资源密集节点不直接
time.sleep,改用astream事件或任务调度
第 11 章:生产化部署与平台选型
11.1 解决什么问题
把一个本地能跑通的 graph 推到生产,需要回答:
- 形态选择:自建 FastAPI + Postgres?Self-Hosted Lite(官方原词 Standalone server)?LangSmith 托管 Cloud?完整 Self-Hosted Enterprise?
- 平台能力边界:Streaming、HITL 暂停、长任务、Cron、Webhook、A2A / MCP、Auth、Studio UI 哪些是平台原生提供,哪些要自己接。
- 运维边界:进程模型、扩展单元、Postgres / Redis 依赖、Schema 迁移、灰度发布。
- TCO:每千次 run、每部署每分钟的计费、自托管的隐性人力成本。
官方 2025-10 起「LangGraph Platform」正式更名为 LangSmith Deployment(来源:langchain.com/langgraph-platform 页面,2026-06;docs.langchain.com/langsmith/deployment 同步)。本文档统一使用新名。
11.2 推荐做法
11.2.1 三种部署形态
官方 官方提供三种部署形态(来源:docs.langchain.com/langsmith/deployment,2026-06;v2 大纲命名映射见 ch01-03 附录 A.4):
| 形态(v2 大纲口径) | 官方原词 | 你管什么 | 平台管什么 | 适用 |
|---|---|---|---|---|
| Cloud | Cloud(SaaS) | 业务代码 | 控制面 + 数据面(AWS / GCP) | 起步期、不想运维 |
| Self-Hosted Lite | Standalone server | 容器 + Postgres + Redis + License | Runtime + API + Studio | 中小规模、想避免 cloud 锁定 |
| Self-Hosted Enterprise | Self-Hosted(Enterprise) | K8s + 全套基础设施 | License + 软件更新 | 强合规、专有云 |
命名口径声明 :本稿遵循 v2 大纲命名
Cloud / Self-Hosted Lite / Self-Hosted Enterprise;与 LangSmith Deployment 官方文档原词Cloud / Standalone server / Self-hosted的对应关系见 ch01-03 附录 A.4。本节及以下 11.2.2 / 11.2.5 / 11.2.6 / 11.3 / 11.5 全文统一使用 v2 大纲口径。
官方 共同 runtime:Assistant、Thread、Run 三种 primitive,跨形态 API 一致;变更的只是「谁运维」。
官方 关键能力(覆盖所有形态):streaming、HITL pausing、并发 input、MCP/A2A endpoint、Cron、webhooks、auth、registry、版本与回滚、Studio 调试(来源:同上)。
11.2.2 基础设施依赖
官方 + 推测 Self-Hosted Lite(官方原词 Standalone server)与 Self-Hosted Enterprise 的必备依赖:
- PostgreSQL :LangGraph checkpointer / store 后端;生产建议使用
langgraph-checkpoint-postgres==3.1.0(2026-05-12,与langgraph 1.2.x同步)并在部署管线中显式执行PostgresSaver.setup()/AsyncPostgresSaver.setup()。 - Redis :在 Self-Hosted Lite(Standalone server)中是 LangSmith 控制面组件 (任务队列、pub/sub、限流);LangGraph Python checkpointer 本身不依赖 Redis (可选
RedisSaver,与 Postgres / SQLite 并列的独立后端,不互相替代)。 - 对象存储(可选):大 blob、checkpoint 备份。
- License Server:Self-Hosted Lite / Self-Hosted Enterprise 都需要 LangSmith 商业 License。
- Container Runtime:Docker / Compose / K8s 至少其一。
Redis「必备」边界 :Self-Hosted Lite 必备(LangSmith 控制面组件);Self-Hosted Enterprise 看部署方式(可不用 Redis,改用 K8s 原生队列 / 内部 pub/sub)。不要 把 Redis 当作 LangGraph Python checkpointer 的必备后端------LangGraph checkpointer 后端是
PostgresSaver/SqliteSaver/RedisSaver三选一(每个后端是独立类)。
官方 进程模型(来源:deployment 文档与 Functional API 文档综合):
- API Server:HTTP / SSE / WebSocket 入站。
- Worker:执行 graph 节点,水平扩展单元。
- Scheduler:cron jobs、延迟任务、time-travel 恢复。
- 多数情况下三者共享同一镜像、不同 entrypoint。
11.2.3 LangGraph Server 关键 API
官方 Server 暴露的核心资源(来源:docs.langchain.com/langsmith/deployment,2026-06):
assistants--- 静态配置(graph、prompt、tools、模型)。threads--- 会话状态(持久到 Postgres)。runs--- 单次执行;runs.create支持command字段 resume interrupt。crons--- 定时任务。store--- 跨 thread 长期记忆 namespace。webhooks--- 出站回调。mcp/a2a--- 跨进程 / 跨框架协作。
SDK 版本口径 :本章只讨论 Python 版
langgraph-sdk;截至 2026-06-11,附录 A 记录的 Python SDK 最新为0.4.2,其中0.4.0是 v3 streaming 主变更批次之一。若读者同时使用 JS/TS SDK,其版本线(如 1.x)与 Python SDK 不同,不能混用版本号。
官方 + 推测 Idempotency:run 接受 idempotency_key,避免重试造成重复执行(社区经验,官方文档未给完整 spec;标注为待验证)。
11.2.4 LangGraph Studio
官方 Studio 是官方可视化 IDE,能力包括(来源:docs.langchain.com/langsmith,2026-06):
- 本地运行:连接本地 dev server,可视化 state、node 跳转。
- Cloud:连接 Cloud 部署。
- Time-travel:单步回放、fork 出新分支、修改 state 后再执行。
- Trace replay:结合 LangSmith trace 复现生产问题。
官方 + 推测 Studio 仅供开发者使用,不应直接对外;访问控制以 LangSmith Plan(Plus / Enterprise)权限与 Custom Auth(11.2.6)配置为准------Cloud Plus / Enterprise 默认按 LangSmith 组织 / 项目边界隔离;Self-Hosted Lite 需自配 Custom Auth 才能限制 Studio 访问。
11.2.5 跨版本升级、Schema 迁移、灰度
官方 升级路径(来源:PyPI + GitHub release notes,2026-06):
0.2.x→0.3.x:短过渡系列,已 EOL。0.3.x→1.x:破坏性变更 (breaking changes),需测试项:messagesreducer 与add_messages行为变化。interrupts改用Command(resume=...)形式恢复。stream v1→stream v2输出格式变化。- changelog:https://github.com/langchain-ai/langgraph/releases(请按 tag 翻阅
0.3.x → 1.0.0的 release notes)。
- 1.x 内部:minor 升级大多向后兼容,但
DeltaChannel等新功能需>=1.2(beta API,1.x → 1.3+ 升级时关注 GA 状态)。
升级风险提示 :跨大版本升级(
0.3.x→1.x、1.0→1.1stream v2)必须先读 changelog + staging 跑回放;不要把"无破坏性变更"作为既成事实写------v1.0 GA 公告曾出现"promoted to 1.0 with no breaking changes"措辞争议(ch01-03 附录 A.3 已警示)。
官方 schema 迁移:checkpointer 的 setup() 会建表;结构变更需要手工 ALTER TABLE 或迁移工具(如 alembic)。Postgres saver 文档显式提到需要预先建表(来源:persistence 文档,2026-06)。
推测 Graph 灰度发布:Cloud 与 Self-Hosted Enterprise 都支持多版本 assistant 同存;可在 router 层按比例路由 assistant_id,做 Blue/Green。详细 router 模式官方 cookbook 未提供,待补。
11.2.6 Auth:Custom Auth
官方 Custom Auth 通过 @auth.authenticate + @auth.on* 装饰器实现(来源:docs.langchain.com/langsmith/auth,2026-06;langgraph-sdk Python 0.4.x 配套;PyPI langgraph-sdk 当前稳定为 0.4.2(2026-06-01),与 langgraph==1.2.4 requires_dist <0.5.0,>=0.4.2 对齐------详见附录 A A.1.4 / A.6):
- import :
from langgraph_sdk import Auth(注:另有内部路径langgraph_api.auth.Auth,前者为公开 SDK 入口;二者等价)。 @auth.authenticate:验证身份,返回 dict,至少含identity(必需,唯一用户标识);可选is_authenticated(默认True)、permissions(权限列表,如["threads:write", "threads:read"])、org_id、role等自定义字段。@auth.on*装饰器族 (最具体的匹配优先):- global :
@auth.on(兜底,未被具体 resource/action 匹配的请求落到这里)。 - resource :
@auth.on.threads/@auth.on.assistants/@auth.on.crons(资源级统一处理)。 - action :
@auth.on.threads.create/@auth.on.threads.read/@auth.on.threads.update/@auth.on.threads.delete/@auth.on.threads.search/@auth.on.threads.create_run等(具体动作级)。
- global :
- 装饰器签名参数:
ctx: Auth.types.AuthContext(含ctx.user/ctx.permissions/ctx.resource/ctx.action)和value(请求负载,按 resource.action 类型化)。 - filter 返回 :返回
None/True= 放行;返回False= 拒绝(403);返回 filter dict({"$eq": ..., "$contains": ...}等)= 按元数据过滤。 - LangSmith Cloud:默认用 LangSmith API Key 鉴权。
- Self-Hosted Lite / Self-Hosted Enterprise :默认无认证,需自配 Custom Auth(来源:deployment 文档,2026-06)。
LangSmith 商业 License 与 Studio 访问控制 :Studio 访问控制以 LangSmith Plan(Plus / Enterprise)权限与 Custom Auth 配置为准------Cloud Plus / Enterprise 默认按 LangSmith 组织 / 项目边界隔离;Self-Hosted Lite 需自配 Custom Auth 才能限制 Studio 访问。本稿 11.2.4「权限模型见 11.2.6」的交叉引用即此意。
11.2.7 TCO 估算
价格时效性声明 :以下 Cloud Plus 价格以
langchain.com/pricing2026-06-11 抓取为准;USD 报价,不含税 ,不区分国家/区域。LangChain 保留随时调整价格的权利;部署前请以官网为准。
官方 Cloud Plus 计划(来源:langchain.com/pricing,2026-06):
- $39/seat/月,含 1 个 dev-sized deployment + 10k base traces。
- Run 计量:$0.005/deployment run(超出后)。
- Uptime:0.0036/min per Production deployment,0.0007/min per Development deployment。
- 30 天 always-on:约 155.52/prod、30.24/dev。
- 额外 traces:2.50/1k base,5.00/1k extended。
- Engine:$1.50/LCU(Plus)。
官方 Enterprise:Custom pricing,含 SSO/RBAC、SLA、培训、驻场工程。
官方 Self-Hosted Lite(官方原词 Standalone server):bring your own LangSmith License + 自行运维基础设施,无 platform subscription;仍需 LangSmith 商业 License (来源:docs.langchain.com/langsmith/deployment,2026-06;Standalone 段"bring your own license")。Studio 能力包含在 License 内;精确价格请以 LangChain 销售 / partner portal 为准(附录已列为待补)。
官方 Self-Hosted Enterprise:需 LangSmith Enterprise Plan + 自部署完整控制面 + 数据面;Custom pricing(来源:同上)。
推测 决策启发:
【编者经验阈值,官方无明确推荐】 以下数字(50k、10 人)为本稿编者按 cloud-vs-self-host 行业经验归纳,非 LangChain 官方建议。生产选型请结合真实业务算账(run 单价 × 月均 run + always-on × 月数 + License + 运维人力)。
- 月 run < 50k、团队 ≤ 10 人、合规弱:选 Cloud Plus。
- 数据合规要求中等、希望避免 cloud 锁定:Self-Hosted Lite + Postgres + Redis。
- 金融 / 医疗 / 政企 + 已有 K8s 平台:Self-Hosted Enterprise。
11.3 不推荐做法
| 反模式 | 后果 | 出处 |
|---|---|---|
| 生产用 Cloud Free / Developer tier | 无 SLA、无合规、无 SSO;正式业务不该跑这 | langchain.com/pricing 明确 |
Self-Hosted Lite(Standalone server)用 SQLite(langgraph-checkpoint-sqlite==3.1.0)做生产 checkpointer |
SQLite 适合本地/单机轻量持久化;生产多副本下单点故障、并发安全不足 | 官方生产建议用 Postgres;附录 A A.1.4 记录 sqlite 3.1.0 仅作为本地/单机场景 |
| 把 LangSmith 当作"唯一权限" | 离开 LangSmith 之后 auth 全失效 | 推测(与第 12 章 RBAC 冲突) |
| 一次升级大版本不读 changelog | 0.3 → 1.x stream/interrupt 行为变更(破坏性) | 官方 changelog 提示 |
| 不区分 dev / prod deployment | 误调用 LLM 烧钱 | 官方计费模型隐含 |
| 用 InMemory store 跑生产 | 重启数据全丢 | 官方 store 文档明令 |
| 在 Studio 里直接对接生产数据 | 开发者账户 = 数据访问者 | 推测(合规通用原则) |
自定义 Auth 只在 authenticate 里加判断,未加 on.<resource>.<action> |
list / read / create_run 路径未受保护 | 官方 auth 文档(11.2.6) |
11.4 最小代码示例
示例 A:最小 FastAPI + Postgres 自定义部署(不依赖 LangSmith)
python
# 依赖:fastapi, uvicorn, langgraph>=1.2, langgraph-checkpoint-postgres
from fastapi import FastAPI, Request
from langgraph.graph import StateGraph, START, END, MessagesState
from langgraph.checkpoint.postgres import PostgresSaver
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage
# 1) 编译一次,进程内复用
DB = "postgresql://user:pwd@host:5432/langgraph"
checkpointer = PostgresSaver.from_conn_string(DB)
checkpointer.setup()
model = init_chat_model("claude-sonnet-4-6")
def chat_node(state: MessagesState):
resp = model.invoke(state["messages"])
return {"messages": [resp]}
graph = StateGraph(MessagesState)
graph.add_node("chat", chat_node)
graph.add_edge(START, "chat")
graph.add_edge("chat", END)
app_graph = graph.compile(checkpointer=checkpointer)
# 2) HTTP API
app = FastAPI()
@app.post("/invoke/{thread_id}")
async def invoke(thread_id: str, request: Request):
body = await request.json()
config = {"configurable": {"thread_id": thread_id}}
result = await app_graph.ainvoke(
{"messages": [HumanMessage(content=body["input"])]},
config,
)
return {"output": result["messages"][-1].content}
示例 B:Custom Auth + org 隔离(LangSmith Deployment,LangGraph 1.2+ / Python SDK 0.4.x)
python
# 部署在 Cloud / Self-Hosted Lite / Self-Hosted Enterprise 都适用
# 来源:docs.langchain.com/langsmith/auth(2026-06)
from langgraph_sdk import Auth
auth = Auth()
# 1) authenticate 钩子:返回 identity / permissions / 自定义字段
@auth.authenticate
async def authenticate(headers: dict):
token = headers.get(b"authorization", b"").decode()
user = await idp.verify(token) # 你的 IdP(Auth0 / Supabase / Okta / 自建)
# identity 必填;is_authenticated 默认 True;permissions / org_id 可选
return {
"identity": user["sub"],
"is_authenticated": True,
"permissions": user["scopes"], # 例如 ["threads:write", "assistants:read"]
"org_id": user["org_id"],
}
# 2) global 钩子:所有资源默认打 org_id 标签
@auth.on
async def org_scoped(ctx, value):
metadata = value.setdefault("metadata", {})
metadata["org_id"] = ctx.user["org_id"]
return {"org_id": ctx.user["org_id"]}
# 3) action 钩子:threads.create 额外校验写权限
@auth.on.threads.create
async def on_thread_create(ctx, value):
if "threads:write" not in ctx.permissions:
raise Auth.exceptions.HTTPException(status_code=403)
metadata = value.setdefault("metadata", {})
metadata["owner"] = ctx.user.identity
return {"owner": ctx.user.identity}
关于示例 B 的可运行性 :本示例基于 docs.langchain.com/langsmith/auth 当前 API 写就(2026-06 抓取);
Auth.exceptions.HTTPException/Auth.types.AuthContext/Auth.types.on.<resource>.<action>.value均为 SDK 公开类型。具体 Python SDK 版本以langgraph-sdk>=0.4.2,<0.5.0为准(与langgraph==1.2.4依赖约束一致)。
示例 C:Durability 模式与 background run
python
# sync:每步同步持久化,强保证
async for event in graph.astream(input, durability="sync"):
handle(event)
# async:下一步前异步持久化,崩溃窗口小
async for event in graph.astream(input, durability="async"):
handle(event)
# exit:仅退出时持久化,最高吞吐
async for event in graph.astream(input, durability="exit"):
handle(event)
11.5 检查清单
- 已根据合规、规模、运维能力选定 Cloud / Self-Hosted Lite / Self-Hosted Enterprise(v2 大纲口径;与 ch01-03 附录 A.4 对齐)
- Cloud 部署区分 dev 与 prod deployment
- Self-Hosted Lite / Self-Hosted Enterprise:Postgres ≥ 14,Redis ≥ 6(LangSmith 控制面),并发 / 容量压测完成
- Custom Auth 已覆盖
authenticate+@auth.on(global)+@auth.on.<resource>.<action>至少三级 - Org / Project 隔离通过 metadata 过滤强制(不只靠 application 自觉)
- checkpointer 加密(
LANGGRAPH_AES_KEY)已开启 - 升级前阅读 changelog;特别关注 0.3 → 1.x(破坏性) 、1.0 → 1.1(stream v2)行为变更;changelog:https://github.com/langchain-ai/langgraph/releases
- Schema 迁移脚本(alembic / 手写)已写并演练
- Graph 灰度 / Blue-Green 通过 assistant_id 路由实现
- 监控:run 数量、deployment uptime、token 用量、cache 命中率
- TCO 预估:run 单价 × 月均 run + 部署 always-on 单价 × 月数 < 业务预算
- LangSmith License 续期与续约 SLA 已明确
第 12 章:安全、合规与多租户
12.1 解决什么问题
LangGraph 应用的「安全」不是单一维度,至少分四层:
- 工具安全:LLM 能调用的 API 边界(side effect、不可逆操作、跨租户数据)。
- 数据安全:PII / 秘密不能进 state、不能进 checkpoint、不能进 trace。
- 多租户安全:org / project / user 之间的数据 / 工具 / 配额严格隔离。
- 模型与提示安全:抵抗 Jailbreak、Prompt Leak、Indirect Prompt Injection。
合规维度(GDPR / HIPAA / SOC2)多属行业专属,本节明确区分「通用基线」与「行业专属」。
12.2 推荐做法
12.2.1 工具权限边界(通用基线)
官方 工具安全 = HITL 审批 + 边界检查(来源:docs.langchain.com/oss/python/langgraph/interrupts,2026-06):
- 不可逆副作用工具(发邮件、转账、删数据)必须在
interrupt()后审批。 - 工具参数可被人类编辑后再执行,不是二选一 approve / reject。
- 工具的
description决定 LLM 是否误用,质量第一杀手(详见第 6 章)。
推测 三层防御:
- 入口过滤:用 LangSmith prompt firewall 或中间件扫描 prompt。
- 审批 :高风险工具默认走
interrupt。 - 事后审计:所有工具调用记录到 append-only 日志。
12.2.2 敏感数据处理(通用基线 + 行业专属)
官方 checkpoint 加密:EncryptedSerializer.from_pycryptodome_aes() 读 LANGGRAPH_AES_KEY(来源:persistence 文档,2026-06)。
官方 LangSmith 数据保留:SaaS 默认 400 天(来源:observability 文档,2026-06)。
官方 + 推测 PII 防护(待补/待验证):
- 官方 LangSmith 文档未明确给出 PII 脱敏 API;社区常用做法:
- 在 LLM 调用前用 PII detector(Microsoft Presidio、Comet Opik)做 redact。
- 在 LangSmith SDK 中通过
metadata标记pii: true,然后配置 sampling 规则排除。 - State 中不存明文 PII,存 token(HMAC / 加密引用),输出前查表还原。
行业专属 GDPR / HIPAA / PCI-DSS:
- 涉及欧盟用户数据:data residency 必须选 Self-Hosted 部署在 EU 区域(Cloud 区域由 LangChain 提供 EU 集群)。
- HIPAA:必须 Business Associate Agreement(BAA)+ Self-Hosted / 私有云;Cloud 默认不含 BAA(待补:具体 Cloud 是否支持 BAA 请以最新 LangChain 合规页为准)。
- PCI-DSS:永不把 PAN 进 state / trace,支付走
@task封装 + tokenization。
12.2.3 多租户隔离(通用基线)
官方 Custom Auth + metadata 过滤是官方推荐模式(来源:auth 文档,2026-06):
python
@auth.on
async def org_scoped(ctx, value):
metadata = value.setdefault("metadata", {})
metadata["org_id"] = ctx.user["org_id"]
return {"org_id": ctx.user["org_id"]}
store 的 namespace 也按 (org_id, user_id, ...) 设计(来源:persistence 文档中 namespace_for_memory = (user_id, "memories") 模式)。
官方 + 推测 隔离强度分层:
本分级沿用云架构通用术语(软隔离 / 硬隔离 / 物理隔离),非 LangGraph 官方分级。LangGraph 官方未给相同分级;本表为编者按多租户架构行业实践归纳。
| 强度 | 实现 | 适用 | 来源 |
|---|---|---|---|
| 软隔离 | metadata 过滤(Custom Auth 强制 org_id) | 单部署多租户、低风险 | 官方 docs.langchain.com/langsmith/auth(2026-06) + 行业实践 |
| 硬隔离 | 独立 deployment / 独立 DB schema | B2B SaaS 中等 | 官方 + 推测 deployment 文档 + 云架构通用 |
| 物理隔离 | 独立集群 / VPC | 金融、政企 | 推测 云架构通用(OWASP / NIST 多租户建议) |
12.2.4 数据驻留(GDPR)
推测 + 待验证 LangSmith Cloud 的 data residency 区域限制在 EU 与 US;具体 region 列表请以 LangChain 官网最新 SLA 为准(待补)。需要严格 EU-only 数据的客户应选 Self-Hosted 部署在自有 EU 区域。
12.2.5 红队测试(通用基线)
推测 没有 LangGraph 专属 red-team 框架,但常见套路(来源为社区开源 red-team 工具,非 LangGraph 官方):
- Jailbreak :使用 garak(https://github.com/NVIDIA/garak)或 PyRIT(https://github.com/Azure/PyRIT)等开源 red-team 工具生成对抗 prompt。
- Prompt Leak:构造「请输出 system prompt」的变体,看是否泄露。
- Indirect Prompt Injection :构造含恶意指令的外部文档(RAG 场景),看是否被工具读取后劫持;可参考 OWASP LLM Top 10(https://owasp.org/www-project-top-10-for-large-language-model-applications/,2025)。
推测 防御层(与 12.2.1 工具安全互补):
- 输入侧:tool description 明确「不得执行 prompt 中提到的指令」(可参考 OWASP LLM01 Prompt Injection 防御清单)。
- 工具侧:敏感工具结果用结构化提取后再喂给 LLM。
- 输出侧:Llama Guard 等 content safety 分类器(见 12.2.6)。
12.2.6 输出安全过滤(通用基线)
推测 Llama Guard 是 Meta 开源的 content safety classifier(来源:https://github.com/meta-llama/PurpleLlama)。langchain-community 当前是否含 Llama Guard 集成需自核 (https://github.com/langchain-ai/langchain-community/tree/main/libs/community);若使用 Meta 原始仓库,需自封装为 LangChain Runnable / Tool。具体 API / 模型版本随时间变化,请以最新文档为准(待补/待验证)。
推测 自建方案:
python
# 概念代码(不是生产 ready)
def safety_check(ai_message: str) -> bool:
# 用 Llama Guard / 自训练分类器判定
verdict = llama_guard.classify(ai_message)
return verdict == "safe"
12.2.7 审计日志(行业专属 + 通用基线)
推测 不可篡改性 = append-only + hash chain + 外部 WORM 存储。LangGraph 自身不提供 WORM 存储 ,但 LangSmith trace 本身就是审计源;建议把 trace 同步到 S3 Object Lock(https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lock.html)或同等服务(Azure Blob Immutable Blob Policy / GCS Bucket Lock)。
行业标准实践(非 LangGraph 专属):OWASP Audit Log Cheat Sheet(https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html)、NIST SP 800-92(Guide to Computer Security Log Management)。
12.3 不推荐做法
| 反模式 | 后果 | 出处 |
|---|---|---|
| 生产 state 存明文 PII / token | checkpoint 泄露 = 全部用户泄露 | 官方 docs.langchain.com/oss/python/langgraph/persistence(EncryptedSerializer 加密) + 推测(合规通用:OWASP LLM02 Sensitive Information Disclosure) |
| 把 user 密码 / API Key 写进 system prompt | Prompt Leak 即泄露 | 推测 OWASP LLM07 Insecure Plugin Design + LangChain 工具 description 最佳实践 |
多租户只靠 user_id 字段过滤,无 org_id |
单点越权 | 官方 docs.langchain.com/langsmith/auth(org_id metadata 强制)+ 推测 |
| 用 LLM 判断权限 | 不可解释、不可审计 | 推测 OWASP LLM01 Prompt Injection + 行业实践(auth 必须 deterministic) |
| Tool 直接连生产数据库 | 一次 prompt injection = 删库 | 推测 OWASP LLM06 Excessive Agency + 行业实践(tool sandbox 原则) |
| LangSmith 留 trace 但不做 PII 脱敏 | 监管 / 法务风险 | 官方 docs.langchain.com/langsmith(trace metadata 配置)+ 推测(合规通用) |
| 不区分 dev / prod 数据 | dev 误用 prod 真实数据 | 【编者经验】dev / prod 隔离通用原则 |
| 用 Cloud 默认保留 trace 给生产 | GDPR / HIPAA 触碰 | 推测 + 官方待补 LangSmith data residency SLA(附录待补) |
12.4 最小代码示例
示例 A:敏感工具的审批(LangGraph 1.2+)
python
from langchain.tools import tool
from langgraph.types import interrupt, Command
@tool
def send_email(to: str, subject: str, body: str) -> str:
"""发送邮件;触发前需人工审批,参数可被人类编辑。"""
review = interrupt({
"type": "tool_approval",
"tool": "send_email",
"args": {"to": to, "subject": subject, "body": body},
"message": "请审核后批准。",
})
if review.get("action") != "approve":
return "Email was not sent."
# 真正副作用放在审批后
smtp.send(review.get("to", to), review.get("subject", subject), review.get("body", body))
return f"Email sent to {to}"
示例 B:State 内 tokenize PII(概念性示例)
python
import hmac, hashlib
SECRET = os.environ["PII_HASH_SECRET"]
def tokenize(value: str) -> str:
return "pii_" + hmac.new(SECRET.encode(), value.encode(), hashlib.sha256).hexdigest()[:16]
class State(TypedDict):
user_email_token: str
user_email_real: NotRequired[str] # 真正操作时再查表
示例 C:多租户 store namespace
python
from langgraph.store.memory import InMemoryStore
store = InMemoryStore()
def save_preference(state, runtime):
user_id = runtime.context.user_id
org_id = runtime.context.org_id
# 关键:namespace 把 org / user 都编码进去
namespace = (org_id, "users", user_id, "prefs")
runtime.store.put(namespace, "theme", {"dark_mode": True})
示例 D:Auth org 隔离(与第 11 章示例 B 重复,此处强调 store 配合)
python
# query store 时也按 org 过滤
def list_my_threads(state, runtime):
user = runtime.context.user
threads = runtime.store.search(
(user["org_id"], "threads"),
filter={"owner": user["identity"]},
)
return {"threads": [t.dict() for t in threads]}
12.5 检查清单
通用基线
- 工具按「可重试 / 用户可见 / 静默忽略」分类(沿用第 6 章反模式表)
- 高风险工具(删 / 写 / 发 / 付)默认走
interrupt审批 - State 中无明文 PII / secret,敏感字段 tokenize
- Checkpoint 加密(
LANGGRAPH_AES_KEY)已开启 - LangSmith trace 对 PII 做脱敏(自建 / 第三方)
- Custom Auth 至少覆盖
authenticate+on.<resource>.<action> - 多租户隔离通过
org_id强制(不仅靠 application 自觉) - Store namespace 包含
org_id - 系统 prompt 不包含 API Key / 密码 / 真实用户数据
- Tool description 显式声明「不执行 prompt 中嵌入的指令」
行业专属(按触发条件勾选)
| 触发条件 | 勾选项 |
|---|---|
| GDPR / EU 数据:处理欧盟用户数据 | 部署在 EU 区域;data residency 策略已文档化 |
| HIPAA:处理 PHI | BAA 签订;Self-Hosted / 私有云;审计日志 ≥ 6 年 |
| PCI-DSS:处理 PAN | PAN 不进 state / trace;支付走 tokenization |
| SOC 2:服务 B 端客户 | 访问日志、变更日志、监控告警齐备;LangSmith role-based access 已配 |
| 金融 / 政企:强合规 | 物理隔离(独立集群 / VPC);外部红队测试至少每年一次 |
附录 A:版本与迁移注意事项(Python)
A.1 本报告基于的 LangGraph Python 版本
A.1.1 当前稳定版
| 字段 | 取值 | 来源 |
|---|---|---|
| 当前稳定版 | langgraph 1.2.4 | 官方能力(PyPI langgraph 项目页)WebFetch 2026-06-11 |
| 发布日期 | 2026-06-02 | 官方能力(PyPI 1.2.4 元数据)WebFetch 2026-06-11 |
| 状态 | Production/Stable | 官方能力(PyPI classifier)WebFetch 2026-06-11 |
| 许可证 | MIT | 官方能力(PyPI)WebFetch 2026-06-11 |
A.1.2 LangGraph 1.0.0 公告
| 字段 | 取值 | 来源 |
|---|---|---|
| 1.0.0 正式版 | 2025-10-17 | 官方能力(PyPI langgraph==1.0.0)WebFetch 2026-06-11 |
| 1.0.0 发布说明 | "promoted to 1.0 with no breaking changes" | 官方能力(LangChain blog 原文:https://blog.langchain.com/langgraph-v1/,现 redirect 至 https://www.langchain.com/blog/langgraph-v1)WebFetch 2026-06-11 |
| 1.0.0 主要变更 | ① 移除 Python 3.9 支持(PR #6289 "chore: drop Python 3.9 (and syntax)")② "rename away from LangGraph Platform"(PR #6281;改名后新名见下方说明)③ 加入 Python 3.14 cursory 支持(PR #6298) | 官方能力(GitHub release 1.0.0 + 1.0.0rc1 changes-since 列表;#6298 来自该列表中的 Python 3.14 cursory support 条目)WebFetch 2026-06-11 |
| 1.0.0 commit | c4144bb |
官方能力(GitHub release 1.0.0 头部)WebFetch 2026-06-11 |
| 1.0.0 公告期 RC 链 | 见下方"1.0.0 公告期 RC 链"列表 | 官方能力(PyPI 版本历史)WebFetch 2026-06-11 |
1.0.0 公告期 RC 链(PyPI 版本历史,WebFetch 2026-06-11):
| 里程碑 | 发布日期 |
|---|---|
| 1.0.0a1 | 2025-08-27 |
| 1.0.0a2 | 2025-09-02 |
| 1.0.0a3 | 2025-09-07 |
| 1.0.0a4 | 2025-09-29 |
| 1.0.0rc1 | 2025-10-17 |
| 1.0.0 | 2025-10-17 |
命名口径补充 :1.0.0 起 "LangGraph Platform" 改名后新名为 LangSmith Deployment。正文部署章节见第 11 章 §11.2.1;Platform 三层 v2 大纲命名(Cloud / Self-Hosted Lite / Self-Hosted Enterprise)与官方文档原词(Cloud / Standalone server / Self-hosted)的映射见本附录 A.4。
冲突标注 :LangChain 官方 blog 自称 "1.0 with no breaking changes",但 1.1.0 release notes 中已显式标记LangGraphDeprecatedSinceV11(即对"在 1.1 中已不推荐使用"的项目会抛 DeprecationWarning),同时 1.0.0rc1 起 "drop Python 3.9",对仍依赖 3.9 的项目即为 破坏性 。建议读者将"no breaking changes"理解为"1.0 相对 0.6.x 行为语义保持" ,但运行平台与命名空间存在破坏。来源:官方能力(blog)vs 官方能力(GitHub release notes)------ 冲突已显式记录。
A.1.3 1.x 关键节点时间线(节选自 PyPI)
| 版本 | 日期 | 关键变化 | 来源 |
|---|---|---|---|
| 1.0.0 | 2025-10-17 | drop Python 3.9、rename away from LangGraph Platform、Python 3.14 cursory | 官方能力(PyPI / GitHub release)WebFetch 2026-06-11 |
| 1.0.5 | 2025-12-12 | (patch,无显著语义变更条目) | 官方能力(PyPI)WebFetch 2026-06-11 |
| 1.0.10 | 2026-02-27 | 1.0.x 末位 patch | 官方能力(PyPI)WebFetch 2026-06-11 |
| 1.1.0 | 2026-03-10 | 引入 v2 streaming 格式(opt-in) 、GraphOutput 类型、StreamPart typed dicts、LangGraphDeprecatedSinceV11 警告 |
官方能力(GitHub release tag 1.1.0)WebFetch 2026-06-11 |
| 1.1.7 | 2026-04-17 | yanked:"introduced bug with custom callback handlers" | 官方能力(PyPI yank reason)WebFetch 2026-06-11 |
| 1.1.8 | 2026-04-17 | 接替 1.1.7 | 官方能力(PyPI)WebFetch 2026-06-11 |
| 1.2.0 | 2026-05-12 | ① durable error-handler resume across host crashes(PR #7773)② set_node_defaults() 加入 StateGraph(PR #7747)③ checkpoint 层 force delta channel snapshot after max supersteps(PR #7746)④ checkpoint-sqlite 用 streaming walk 覆盖 get_delta_channel_history(PR #7702)⑤ DeltaChannel 与 delta-history APIs 标记为 beta(PR #7732)⑥ langchain-core 升到 1.4.0(PR #7767) |
官方能力(GitHub release tag 1.2.0 changes-since-1.2.0a7)WebFetch 2026-06-11 |
| 1.2.2 | 2026-05-26 | 为 id=None 的 BaseMessage 在 DeltaChannel checkpoint 写入前分配稳定 ID(#7913);检查点升到 4.1.1 |
官方能力(GitHub release 1.2.2 / 1.2.1)WebFetch 2026-06-11 |
| 1.2.3 | 2026-06-01 | yanked :"unintended merging strategy regression";曾包含 v3 streaming support to RemoteGraph、ProtocolEvent.eventId → event_id 重命名 |
官方能力(PyPI yank + GitHub release)WebFetch 2026-06-11 |
| 1.2.4 | 2026-06-02 | 当前 stable;fix: 工厂图集成测试路径(#7978);保持 _on_started 与旧 override 向后兼容(#7987) |
官方能力(PyPI / GitHub release 1.2.4)WebFetch 2026-06-11 |
待补/待验证 :1.2.x 中"v3 streaming"、"eventId→event_id"这些远程协议层改动的具体客户端影响面,建议补查
langgraph-sdk0.4.0 之后变更(WebFetch 2026-06-11 已知其 0.4.0 是 v3 streaming 主变更批次)。
A.1.4 关键依赖版本(取自 langgraph==1.2.4 PyPI 元数据)
| 依赖包 | 版本约束 | 来源 |
|---|---|---|
python |
>=3.10 |
官方能力(PyPI requires_python)WebFetch 2026-06-11 |
langchain-core |
<2,>=1.4.0 |
官方能力(PyPI requires_dist)WebFetch 2026-06-11 |
langgraph-checkpoint |
<5.0.0,>=4.1.0 |
官方能力(PyPI requires_dist)WebFetch 2026-06-11 |
langgraph-prebuilt |
<1.2.0,>=1.1.0 |
官方能力(PyPI requires_dist)WebFetch 2026-06-11 |
langgraph-sdk |
<0.5.0,>=0.4.2 |
官方能力(PyPI requires_dist)WebFetch 2026-06-11 |
pydantic |
>=2.7.4 |
官方能力(PyPI requires_dist)WebFetch 2026-06-11 |
xxhash |
>=3.5.0 |
官方能力(PyPI requires_dist)WebFetch 2026-06-11 |
生态对应版本(PyPI 最新,事实底座见上方依赖表与本附录 A.1.4 约束)
| 生态包 | 最新版 | 发布日期 | 与 langgraph 1.2 兼容性 |
|---|---|---|---|
langgraph-checkpoint |
4.1.1 | 2026-05-22 | 满足 >=4.1.0,<5.0.0 |
langgraph-checkpoint-postgres |
3.1.0 | 2026-05-12 | 与 1.2.x 同日 release,建议与 langgraph 1.2.x 同步升级 |
langgraph-checkpoint-sqlite |
3.1.0 | 2026-05-12 | 同上 |
langgraph-sdk |
0.4.2 | 2026-06-01 | 满足 >=0.4.2,<0.5.0 |
langgraph-cli |
0.4.28 | 2026-06-10 | 与 langgraph core 解耦;CLI api bound 已 bump 到 0.10.0 |
langgraph-prebuilt |
1.1.0 | 2026-05-12 | 满足 >=1.1.0,<1.2.0;与 langgraph 1.2.x 同步升级 |
langchain-core |
1.4.5 | 2026-06-11 | 满足 >=1.4.0,<2;langchain-core 1.0.0 = 2025-10-17,与 langgraph 1.0.0 同日发布 |
冲突标注 :
langgraph-prebuilt<1.2.0的上界与"prebuilt 1.1.0 已与 langgraph 1.2.x 配套发布"是否冲突------未冲突 ,因为langgraph 1.2.x的requires_dist要求 prebuilt 1.1.0~<1.2.0。待验证:当前 prebuilt 1.1.0 已发;prebuilt 1.2.x 何时出现、是否与 langgraph 1.3.x 配套仍待补。
A.1.5 最低支持 Python 版本
| 字段 | 取值 | 来源 |
|---|---|---|
| 最低 Python | 3.10(3.10 / 3.11 / 3.12 / 3.13 全部支持,含 CPython 与 PyPy) | 官方能力(PyPI classifiers / requires_python)WebFetch 2026-06-11 |
| Python 3.9 | 不再支持(1.0.0rc1 起 chore: drop) | 官方能力(GitHub release 1.0.0rc1 changes-since 0.6.10)WebFetch 2026-06-11 |
| Python 3.14 | "cursory support"(1.0.0 起) | 官方能力(PR #6298)WebFetch 2026-06-11 |
A.2 影响最佳实践判断的关键 Python API 差异
本节只标出"会影响最佳实践选型/迁移路径"的 API 入口;不写 API 字典。
A.2.1 执行入口:compile / invoke / ainvoke / stream / astream
compile()仍是图编译入口。未变更。invoke / ainvoke / stream / astream的version参数:- 默认
version="v1":与历史行为一致,stream()产出裸 tuple,invoke()返回 plain dict,interrupts 位于"__interrupt__"key。 version="v2"(1.1.0 起 opt-in) :invoke()返回GraphOutput,属性.value与.interrupts。stream()产出 typedStreamPart字典(带type字段)。- Pydantic / dataclass 状态 schema 时自动 coerce 输出(
result.value不再是 dict 而是 schema 实例)。
- 来源:官方能力(GitHub release tag 1.1.0)WebFetch 2026-06-11。
- 默认
- 迁移策略:默认行为不变 ,可在个别调用点加
version="v2"增量迁移。最佳实践:新代码直接 v2;旧代码先继续 v1,逐步替换。
A.2.2 checkpointer 选型
| Saver | 导入 | 适用场景 | 关键说明 |
|---|---|---|---|
InMemorySaver |
from langgraph.checkpoint.memory import InMemorySaver |
单元测试 / 单进程内存 | 官方文档与 ch04-06 正文主推 InMemorySaver。源码保留 MemorySaver = InMemorySaver # Kept for backwards compatibility,未标 deprecated;旧代码可继续 import MemorySaver,新代码建议直接用 InMemorySaver。 |
SqliteSaver / AsyncSqliteSaver |
同步:from langgraph.checkpoint.sqlite import SqliteSaver;异步:from langgraph.checkpoint.sqlite.aio import AsyncSqliteSaver |
本地/单机轻量持久化 | 走 langgraph-checkpoint-sqlite 3.1.0(2026-05-12);async 类名为 AsyncSqliteSaver。 |
PostgresSaver / AsyncPostgresSaver |
同步:from langgraph.checkpoint.postgres import PostgresSaver;异步:from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver |
生产/分布式 | 走 langgraph-checkpoint-postgres 3.1.0(2026-05-12)。关键 :首次使用 Postgres 时 await checkpointer.setup() 建表;未用 Alembic;async 类名为 AsyncPostgresSaver。 |
第三方 RedisSaver |
社区包 | 已成熟 | 不在 langgraph 主仓,由社区维护(如 langgraph-checkpoint-redis 系列),不在本附录权威列表中 ------待补/待验证其与 1.2.x 的兼容矩阵。 |
checkpointer schema 迁移(来源:官方能力 WebFetch 2026-06-11):
- 首次使用 Postgres 时
await checkpointer.setup()即可建表。 - 自建连接须
autocommit=True+row_factory=dict_row。 langgraph-checkpoint-postgres 3.0.0(2025-10-20)引入 "Restrict 'json' type deserialization" 安全收紧(WebFetch 2026-06-11),需要复查旧 checkpoint 中json字段反序列化行为。- 推荐设置
LANGGRAPH_STRICT_MSGPACK=true或显式传allowed_msgpack_modules,限制 msgpack 反序列化。
A.2.3 interrupt / Command(resume=...)
- 入口:
from langgraph.types import interrupt, Command。 - 形式:节点内
value = interrupt({"question": ...});恢复时graph.invoke(Command(resume=value), config)。 - 1.0 → 1.1 兼容性 :
invoke()返回值的旧式result["__interrupt__"]形式在version="v2"下已 deprecated ,会发LangGraphDeprecatedSinceV11,计划 v3.0 移除(WebFetch 2026-06-11 GitHub release 1.1.0)。 - 最佳实践 :新代码用
result.interrupts(v2);混合代码用version="v1"维持旧 dict 行为至全量迁移完成。
A.2.4 Send / dynamic parallelism
- 入口:
from langgraph.constants import Send。 - 用法:
graph.add_conditional_edges("node_a", lambda s: [Send("node_b", {...}) for x in s["items"]]). - 未在 1.0--1.2 公告中出现破坏性变更 (基于 GitHub release 1.0.0/1.1.0/1.2.0 changes-since 列表比对,WebFetch 2026-06-11)。待验证:alpha 阶段(1.0.0a4)前是否还有遗留问题。
A.2.5 Functional API @task / @entrypoint
- 入口:
from langgraph.func import entrypoint, task。 - 用途:把"函数调用"当持久化 task 调度,配合 checkpointer 实现断点续跑。
- 1.x 期间无破坏性变更(WebFetch 2026-06-11),仍是
task.get()/await task形式。 - 最佳实践:复杂图用 StateGraph,线性/简单流程用 Functional API,二者可在同一图内混用。
A.2.6 ToolNode / create_react_agent
- 入口:
from langgraph.prebuilt import ToolNode, create_react_agent。 create_react_agent(model, tools, ...):官方预置 ReAct 风格 agent 工厂。ToolNode:把 LangChain@tool列表转成图节点;与messages_statereducer 配合可自动处理 tool_call / tool_message 状态变更。- langgraph-prebuilt 1.1.0 (2026-05-12)满足
langgraph==1.2.4的>=1.1.0,<1.2.0依赖约束,WebFetch 2026-06-11 表明 prebuilt 已进入 1.x 稳定线。 - 待补/待验证:create_react_agent 1.2.x 与 langchain-core 1.4.0 之间的 model provider 兼容矩阵(需另行核对各 provider 包版本)。
A.2.7 BaseStore / 长期记忆
- 入口:
from langgraph.store.memory import InMemoryStore/from langgraph.store.postgres import PostgresStore。 - 用途:跨 thread、跨用户的键值型长期记忆。
- 1.0--1.2 期间未观察到破坏性变更(WebFetch 2026-06-11 GitHub release 1.0/1.1/1.2 公告未提及 store 破坏变更)。
- 待验证 :1.2.x
set_node_defaults(PR #7747)是否影响 store 的 namespace 解析路径------官方 release notes 未点明,待补。
A.3 迁移 checklist(5--10 项)
每条标注"为何要做"和"如何验证"。仅覆盖 Python。
1. 0.x → 1.0 重大变化
- Python 3.9 不再支持 → 升级 Python 到 ≥3.10(WebFetch 2026-06-11 官方 changelog)。
- 验证:
python --version≥ 3.10;CI 矩阵移除 3.9。
- 验证:
- 命名空间变更 "LangGraph Platform" 退出 (PR #6281)→ 文档/产品名已不再用 "Platform";不影响 Python API 名称。
- 验证:grep 内部文档/产品名是否存在 "LangGraph Platform" 字样。
- interrupts 仍以
"__interrupt__"形式暴露于invoke()返回值 (v1 行为)→ 1.0 阶段未变更。- 验证:迁移后跑现有 HITL 集成测试。
2. 1.0 → 1.1 变化
version="v2"可选开启 → 旧代码无需改动;新代码可加version="v2"拿GraphOutput/StreamPart。- 验证:在测试中分别用 v1 / v2 调用同一图,比对 interrupts 读取方式。
LangGraphDeprecatedSinceV11警告启用 →result["key"]/result["__interrupt__"]仍可用但发警告,v3.0 移除 (WebFetch 2026-06-11 官方 release notes)。- 验证:CI 设
-W error::DeprecationWarning:langgraph.*抓 1.1 期间误用。
- 验证:CI 设
3. 1.1 → 1.2 变化
- DeltaChannel 与 delta-history APIs 标记为 beta (PR #7732)→ 形态未稳定,不建议生产直接用 delta 通道。
- 验证:禁用 DeltaChannel 相关自定义逻辑后回归。
set_node_defaults()新增 (PR #7747)→ 可在 StateGraph 层定义节点默认值。- 验证:测试新 API 与既有
add_node(..., metadata=...)行为一致。
- 验证:测试新 API 与既有
- langchain-core 升到 1.4.0 (PR #7767)→ 可能引入上游破坏,检查
BaseMessage/tool_call/ structured output 行为。- 验证:跑完整 agent 集成测试。
- durable error-handler resume across host crashes (PR #7773)→ 错误处理路径在主机崩溃后可恢复。
- 验证:构造异常路径,验证恢复语义。
- 1.2.2 修复:
id=None的 BaseMessage 在 DeltaChannel 写入前分配稳定 ID (PR #7913)→ 避免"同消息多次 hash 出不同 ID"问题。- 验证:含
id=None的消息在 checkpoint 持久化时 ID 稳定。
- 验证:含
4. Pydantic v1 → v2 兼容
langgraph 1.2.4 requires pydantic>=2.7.4(WebFetch 2026-06-11 PyPI)→ 1.x 系列只支持 Pydantic v2。- 验证:检查依赖锁文件中
pydantic版本。
- 验证:检查依赖锁文件中
- 项目中若有 Pydantic v1 自定义 model,不能用
BaseModelv1 写法直接喂给 StateGraph ;需用 v2 语法(model_config、field_validator)。- 验证:
from pydantic import BaseModel必须是 v2;不混用from pydantic import v1兼容包。
- 验证:
5. 升级前必读 changelog
- 三个权威入口(官方能力 ):
- PyPI 版本历史:https://pypi.org/project/langgraph/#history(WebFetch 2026-06-11)
- GitHub releases:https://github.com/langchain-ai/langgraph/releases(WebFetch 2026-06-11)
- 官方文档站:https://langchain-ai.github.io/langgraph/(多次 WebFetch 2026-06-11 重定向到主站,未抓到具体页内容;待补/待验证)
- yanked 版本警示 (WebFetch 2026-06-11 PyPI yank reason):
1.1.7:自定义 callback handler bug → 跳到1.1.8。1.2.3:merging strategy regression → 跳到1.2.4。0.4.4:Incorrect dependency range。0.2.29/0.2.30/0.2.31:Uses internal APIs of upstream package。0.3.0:Missing dependency on langgraph-prebuilt。0.6.9:yanked。
6. checkpointer schema 迁移
- 不走 Alembic ,调用
await checkpointer.setup()(WebFetch 2026-06-11 PyPI langgraph-checkpoint-postgres 描述)。- 验证:首次启动日志含建表 SQL。
- Postgres 自建连接 必须
autocommit=True+row_factory=dict_row(WebFetch 2026-06-11)。- 验证:手动建连接后跑一次
setup(),不报SET AUTOCOMMIT类错误。
- 验证:手动建连接后跑一次
- langgraph-checkpoint-postgres 3.0.0 起收紧
json反序列化(WebFetch 2026-06-11),旧 checkpoint 读取时需allowed_msgpack_modules。- 验证:跨版本读取回放测试。
- sqlite / postgres 3.1.0 与 langgraph 1.2.x 同步(2026-05-12),升级时一起升。
- 验证:CI 中固定
(langgraph, langgraph-checkpoint-postgres) >= 3.1.0配对。
- 验证:CI 中固定
7. 验证消息 reducer 行为
add_messagesreducer 行为:在 1.x 期间官方未公告破坏(WebFetch 2026-06-11),但 1.2.2 修复了"无 ID 消息在 DeltaChannel 写入前分配稳定 ID",意味着 1.2.2 之前 写出去的 checkpoint 可能在 delta 通道中含不稳定 ID。- 验证:含多轮 tool call / tool message 的会话在不同 patch 版本间回放,比对 message ID 集合。
8. 验证 interrupt resume 语义
- v1 阶段:中断以
"__interrupt__"暴露;v2 阶段:result.interrupts暴露。- 验证:HITL 集成测试同时跑 v1 / v2,对比
(interrupt_payload, resume_payload)一致。
- 验证:HITL 集成测试同时跑 v1 / v2,对比
- 跨进程恢复:v1.2 起
durable error-handler resume across host crashes改变恢复边界(WebFetch 2026-06-11)。- 验证:kill -9 拉起后,next invoke 能正确 resume。
9. 锁文件 / 依赖管理
langgraph>=1.2.4隐式要求langchain-core>=1.4.0,<2、pydantic>=2.7.4。- 验证:
pip check无冲突;uv lock重新生成。
- 验证:
langgraph-prebuilt<1.2.0:prebuilt 1.2.x 出现时需升级 langgraph 主版本。- 验证:CI 中不出现
prebuilt==1.2.0与langgraph==1.2.x混用。
- 验证:CI 中不出现
10. 撤回到旧版本的回滚条件
- 若升 1.2.4 后命中回归,可回滚到 1.2.2 (1.2.3 yanked,建议跳过)。
- 验证:维护可用的"上一个 stable"作为应急回滚点。
A.4 Deprecated Python API 与替代方案
本节按"已公告 / 源码可见 / 待验证"三档标注。
A.4.1 官方公告的 deprecation(官方能力)
| API | deprecation 信号 | 替代方案 | 来源 |
|---|---|---|---|
result["key"]、"key" in result、result["__interrupt__"] 形式读取 v2 调用结果 |
LangGraphDeprecatedSinceV11(1.1.0 起) |
result.value、result.interrupts(v2 形式) |
官方能力(GitHub release 1.1.0)WebFetch 2026-06-11 |
get_state_version / get_state_history 的旧返回结构 |
1.x 期间未在 release notes 中显式 deprecate,但 v1.0 起 langgraph.types 公开 typed stream parts,待补/待验证(详见文末「已知限制 / 待补项」W3) |
(待验证) | 来源不足------标注待补 |
NodeStyleSend 旧名 |
(待验证;详见文末「已知限制 / 待补项」W3) | langgraph.constants.Send |
来源不足------标注待补 |
ConfigurableCallable 旧名 |
(待验证;详见文末「已知限制 / 待补项」W3) | (待补) | 来源不足------标注待补 |
A.4.2 源码 / 文档可观察的命名变化
| 旧 / 别名 | 当前 / 推荐 | 备注 | 来源 |
|---|---|---|---|
langgraph.prebuilt.ToolExecutor |
langgraph.prebuilt.ToolNode |
prebuilt 模块拆分,ToolExecutor 在 0.2.x 后被 ToolNode 取代 |
推测性建议 ------基于代码结构与 0.2.x 重构推断;官方未在本轮 WebFetch 2026-06-11 抓到的 release notes 中明确公告 ------待补/待验证 |
langgraph.checkpoint.memory.MemorySaver(源码别名) |
langgraph.checkpoint.memory.InMemorySaver(实际公开类) |
源码 __init__.py 注释 MemorySaver = InMemorySaver # Kept for backwards compatibility,未标 deprecated |
官方能力(WebFetch 2026-06-11 官方 persistence 文档明确使用 InMemorySaver;ch04-06 4.1 节定稿已与本表一致)。待验证 1.3+ 是否将 InMemorySaver 提为唯一名。 |
| 命名空间 "LangGraph Platform" | 1.0.0 起改名为部署/平台相关新名称 | PR #6281 | 官方能力(WebFetch 2026-06-11 GitHub release 1.0.0rc1) |
A.4.3 不属于 deprecation 但常见的"易错点"(社区经验 / 推测性建议)
| 现象 | 替代做法 | 来源类型 |
|---|---|---|
RedisSaver 不在 langgraph 主仓 |
用 langgraph-checkpoint-redis 等社区包;升级前务必在测试环境验证与 1.2.x 的兼容矩阵 |
推测性建议(WebFetch 2026-06-11 多次抓 langchain-ai.github.io 文档站均被 redirect 到主站,未抓到明确说明)------待补 |
langgraph.prebuilt.create_react_agent 传入 Anthropic / OpenAI / 其他 provider 时 |
同时锁 langchain-core<2,>=1.4.0 与 provider 包版本 |
推测性建议(基于 PyPI 依赖约束 WebFetch 2026-06-11 推断) |
pydantic>=2.7.4 显式要求 |
旧项目若仍用 pydantic==1.x,先升 v2 |
官方能力(WebFetch 2026-06-11 PyPI requires_dist) |
待补/待验证总览 :本节
get_state_version/NodeStyleSend/ConfigurableCallable/RedisSaver官方推荐替代等条目信息源不足,建议下一步:
- 直接查
langgraph/__init__.py、langgraph/types.py源码 grepDeprecationWarning;- 抓取
https://langchain-ai.github.io/langgraph/reference/各子页(WebFetch 2026-06-11 多次重定向未抓到内容,可能需绕过 SPA 路径)。
A.5 团队代码升级策略
来源类型 :A.5.1 / A.5.2 / A.5.3 主要为 推测性建议 (基于 langgraph API 形态 + 通用发布工程经验),A.5.4 监控项基于社区经验(主流可观测性实践)。
A.5.1 灰度发布(按 assistant_id 路由)
- 原理 :langgraph 1.x 在多租户/多 agent 场景下常用
assistant_id/graph_id标识一个图配置。把不同assistant_id指向不同版本(如 v1.0.10 stable / v1.2.4 candidate)做蓝绿/金丝雀。 - 路由实现 (推测性建议 ):
- 在网关层按
assistant_id决定调用的 langgraph runtime 进程 / 容器。 - 进程内通过
compiled_graph = builder.compile(checkpointer=...)加载固定版本。
- 在网关层按
- 验证指标 :
- 同一业务请求分别在 v1.0.10 / v1.2.4 跑一遍,对比 end-to-end 状态快照(thread state 序列化结果)。
- 监控
interrupt触发次数、stream延迟、checkpoint 写入耗时。
- 来源 :推测性建议(基于 langgraph 编译/部署模式 + WebFetch 2026-06-11 PyPI 元数据)。待验证 :官方是否提供内置
assistant_id多版本路由(langgraph-sdk 0.4.x 提供 thread/runs/assistants API,待补其多版本路由文档)。
A.5.2 蓝绿部署
- 原理:v1.0.10 stable 部署为"蓝",v1.2.4 candidate 部署为"绿",网关切换。
- 共享 checkpointer 注意事项 (推测性建议 + 社区经验 ):
- 蓝绿可共用一套 Postgres checkpointer;前提 是
langgraph-checkpoint-postgres版本一致(建议两边都升到 3.1.0)。 - 若蓝绿 checkpointer schema 不同,必须走 schema-versioned 命名(如
checkpoints_v3_1_0)并通过setup()建表。
- 蓝绿可共用一套 Postgres checkpointer;前提 是
- 回滚条件 :
- 命中率(latency P99 / 错误率)超过阈值;
- interrupt resume 跨进程失败率 > 0;
- Pydantic 校验错误率上升。
- 来源:推测性建议(基于 WebFetch 2026-06-11 PyPI 生态包版本一致性要求)。
A.5.3 回滚方案
- 代码层 :
- 锁文件
requirements.txt/pyproject.toml/uv.lock明确锁定可回滚版本组合。 - 维护"上一个 stable" tag / Docker 镜像(如
langgraph==1.2.2为当前 1.2.x 内可回滚点,跳过 1.2.3 yanked)。 - 官方能力 WebFetch 2026-06-11:1.2.3 已被 yank(merging strategy regression);不要 pin 1.2.3。
- 锁文件
- 数据层 :
- checkpoint 升级前做一次全量 snapshot(pg_dump)。
- 跨版本回放测试:把 v1.2.4 写的 checkpoint 用 v1.0.10 读一遍(反向),验证 schema 兼容矩阵。
- 来源:推测性建议;待补官方关于"checkpointer 向前 / 向后兼容矩阵"的明确文档。
- 流量层 :
- API 网关侧配置版本开关,可秒级切回。
- 监控切回时的 thread state 写入成功率。
A.5.4 监控与验证指标
以下指标项是 社区经验 (主流 agent runtime 实践)+ 推测性建议。
| 指标 | 采集方式 | 阈值参考 |
|---|---|---|
stream 各 mode 延迟(P50 / P99) |
在 stream_mode 回调中打点(us 自定义) |
v1 vs v2 需在测试中建立 baseline |
interrupt 触发率 / 恢复率 |
LangSmith trace / 自定义事件 | 恢复率应 ≈ 100%(除非业务主动取消) |
checkpoint 写入耗时 |
DB 慢查询日志 / ORM metrics | 与历史版本偏差 < 20% |
| 异常恢复成功率 | Command(resume=...) 路径 |
kill -9 后第一次 invoke 成功 |
__interrupt__ 与 result.interrupts 双形态一致性 |
灰度期 A/B 同时输出 | 一致率 ≈ 100% |
LangGraphDeprecatedSinceV11 警告计数 |
日志聚合(过滤 DeprecationWarning source) |
升级后应为 0(旧调用点已迁移) |
pydantic 校验错误率 |
结构化输出相关 trace | 不应因 langchain-core 1.4.0 升级而上升 |
- 可观测性依赖 :
- LangSmith(官方)--- 通过
LANGSMITH_TRACING=true开启; - OpenTelemetry exporter(社区)--- 通过
langchain-core内置 callbacks。
- LangSmith(官方)--- 通过
- 来源 :社区经验 + 推测性建议(WebFetch 2026-06-11 多次抓取官方 langchain-ai.github.io 文档站均重定向未抓到内容------待补对官方推荐 metrics 列表的精确引用)。
A.6 关键引用清单(按 URL 归集)
每条标注 "类型(官方/社区/推测) + 资料日期 + 引用范围"。
- https://pypi.org/project/langgraph/ --- 官方能力 / 2026-06-11 抓取 / 版本历史、依赖、Python 兼容性。
- https://pypi.org/project/langgraph/#history --- 官方能力 / 2026-06-11 抓取 / 0.x--1.2.4 完整时间线。
- https://pypi.org/pypi/langgraph/1.2.4/json --- 官方能力 / 2026-06-11 抓取 / 1.2.4
requires_dist。 - https://pypi.org/project/langgraph-checkpoint/ --- 官方能力 / 2026-06-11 / 4.1.1 + Python ≥3.10。
- https://pypi.org/project/langgraph-checkpoint-postgres/ --- 官方能力 / 2026-06-11 / 3.1.0 +
setup()迁移方法。 - https://pypi.org/project/langgraph-checkpoint-sqlite/ --- 官方能力 / 2026-06-11 / 3.1.0。
- https://pypi.org/project/langgraph-sdk/ --- 官方能力 / 2026-06-11 / 0.4.2。
- https://pypi.org/project/langchain-core/ --- 官方能力 / 2026-06-11 / 1.4.5;1.0.0 = 2025-10-17。
- https://github.com/langchain-ai/langgraph/releases/tag/1.0.0 --- 官方能力 / 2026-06-11 / 1.0.0 头部与 changes-since 1.0.0rc1。
- https://github.com/langchain-ai/langgraph/releases/tag/1.1.0 --- 官方能力 / 2026-06-11 / v2 streaming 格式、
GraphOutput、StreamPart、LangGraphDeprecatedSinceV11。 - https://github.com/langchain-ai/langgraph/releases/tag/1.2.0 --- 官方能力 / 2026-06-11 / 1.2.0 changes-since 1.2.0a7、DeltaChannel 标 beta、
set_node_defaults、langchain-core1.4.0 bump。 - https://github.com/langchain-ai/langgraph/releases --- 官方能力 / 2026-06-11 / 1.2.x 后期小版本变更。
- https://www.langchain.com/blog/langgraph-v1 --- 官方能力 / 2026-06-11(redirect 抓取)/ "1.0 with no breaking changes" 公告原文。
- https://langchain-ai.github.io/langgraph/ --- 官方能力 / 2026-06-11 多次重定向未抓到内容------待补/待验证。
附录 B:代码片段库
0. 版本基线
本附录除特别注明外,默认以下版本基线(截至 2026-06-11):
| 依赖包 | 最低版本 | 备注 |
|---|---|---|
langgraph |
>=1.0 |
LangGraph 1.0 于 2025-10-22 发布,本附录基线版本 |
langgraph-checkpoint |
>=2.0 |
与 langgraph 1.x 配套 |
langgraph-supervisor |
0.0.31 |
2025-11-19 发布 |
langgraph-swarm |
0.1.0 |
2025-12-04 发布 |
langchain |
>=1.0 |
LangChain 1.0 与 LangGraph 1.0 同期发布 |
langsmith |
>=0.4.25 |
OTEL fixes、pytest 集成需 >=0.3.4,metadata 需 >=0.7.13 |
Python |
>=3.10 |
langgraph-supervisor 明确要求 ≥3.10;部分 streaming 能力在 >=3.11 才支持 |
来源:
- LangGraph 1.0 公告:https://www.langchain.com/blog/langchain-langgraph-1dot0 (2026-06-11 抓取)
- LangGraph PyPI 历史:https://github.com/langchain-ai/langgraph/releases (2026-06-11 抓取,当前 1.2.4)
- langgraph-supervisor 仓库:https://github.com/langchain-ai/langgraph-supervisor-py (2026-06-11 抓取)
- langgraph-swarm 仓库:https://github.com/langchain-ai/langgraph-swarm-py (2026-06-11 抓取)
1. 最小 StateGraph + compile + invoke 🟢
适用场景 :单节点入门、单元测试骨架、教学演示。
依赖 :langgraph>=1.0。
Python :>=3.10。
python
# pip install "langgraph>=1.0"
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
class State(TypedDict):
value: str
def node_echo(state: State) -> dict:
return {"value": state["value"] + " -> echoed"}
workflow = StateGraph(State)
workflow.add_node("echo", node_echo)
workflow.add_edge(START, "echo")
workflow.add_edge("echo", END)
graph = workflow.compile()
result = graph.invoke({"value": "hello"})
print(result) # {'value': 'hello -> echoed'}
要点:
- 任何图在调用前都必须
.compile();compile 负责结构校验与挂载 runtime 参数(checkpointer、interrupt 列表等)。 START/END是虚拟节点,用于声明入口与出口边。
资料来源:
- 🟢 LangGraph 概念页:https://docs.langchain.com/oss/python/langgraph/graph-api (2026-06-11 抓取)
- 🟢 Deep-research 综合(事实底座 §3、§5):见附录 C 参考资料
2. TypedDict + Annotated Reducer + add_messages 🟢
适用场景 :多轮对话、消息流合并、并发节点对同一字段的累加。
依赖 :langgraph>=1.0、langchain>=1.0(提供 AnyMessage)。
Python :>=3.10。
python
# pip install "langgraph>=1.0" "langchain>=1.0"
from typing import Annotated
from typing_extensions import TypedDict
from operator import add
from langchain.messages import AnyMessage, HumanMessage
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
class ChatState(TypedDict):
# add_messages:处理 message id 去重与按序合并,支持工具消息追加
messages: Annotated[list[AnyMessage], add_messages]
# operator.add:列表 / 数值的简单累加 reducer
trace: Annotated[list[str], add]
def step_a(state: ChatState) -> dict:
return {
"messages": [HumanMessage(content="hi")],
"trace": ["a"],
}
def step_b(state: ChatState) -> dict:
return {"trace": ["b"]}
graph = (
StateGraph(ChatState)
.add_node("a", step_a)
.add_node("b", step_b)
.add_edge(START, "a")
.add_edge("a", "b")
.add_edge("b", END)
.compile()
)
print(graph.invoke({"messages": [], "trace": []}))
要点:
Annotated[X, reducer]告诉 LangGraph 这个字段不是「整体替换」,而是用 reducer 合并。add_messages是 message-aware reducer:支持 id 去重、按 id 替换、tool message 追加。- 默认(不写 Annotated)行为是「整体覆盖」------并发节点会互相覆盖,调试困难。
资料来源:
- 🟢 LangGraph quickstart(含
Annotated[list[AnyMessage], operator.add]与add_messages):https://docs.langchain.com/oss/python/langgraph/quickstart (2026-06-11 抓取) - 🟢 Deep-research 综合(事实底座 §4):见附录 C 参考资料
3. 条件路由(Conditional Edge)🟢
适用场景 :决策树、按状态分发到不同子图、map-reduce fan-out。
依赖 :langgraph>=1.0。
Python :>=3.10。
python
# pip install "langgraph>=1.0"
from typing import Literal
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.types import Send
class State(TypedDict):
topic: str
subjects: list[str]
# 路由函数必须是纯函数:只读 state,输出下一节点名(或 Send 列表)
def route_by_topic(state: State) -> Literal["joke", "summary"]:
return "joke" if state["topic"] == "humor" else "summary"
def joke(state: State) -> dict:
return {"topic": state["topic"] + ":joke"}
def summary(state: State) -> dict:
return {"topic": state["topic"] + ":summary"}
# 动态 fan-out:每个 subject 一个 Send 任务
def fan_out(state: State) -> list[Send]:
return [Send("joke", {"topic": s, "subjects": []}) for s in state["subjects"]]
builder = StateGraph(State)
builder.add_node("joke", joke)
builder.add_node("summary", summary)
# 路由函数 + 显式映射表,便于阅读
builder.add_conditional_edges(
START,
route_by_topic,
{"joke": "joke", "summary": "summary"},
)
builder.add_edge("joke", END)
builder.add_edge("summary", END)
graph = builder.compile()
print(graph.invoke({"topic": "humor", "subjects": []}))
要点:
- 路由函数必须是纯函数:不要在里面调用 LLM、写数据库或修改 state。
- 同一节点不要混用 普通 edge 与 conditional/
Command路由------两条路径都会执行。 - 需要「修改 state 的同时跳转」时,优先用
Command(update=..., goto=...)而非 conditional edge。 - 真正动态的扇出(不知道目标节点数)用
Send。
资料来源:
- 🟢 LangGraph Graph API 路由章节:https://docs.langchain.com/oss/python/langgraph/graph-api (2026-06-11 抓取)
4. create_react_agent vs 自定义 StateGraph 🟢
适用场景:
create_react_agent(工厂方法):标准 ReAct(LLM + 工具循环)足够时,最小代价上手。- 自定义 StateGraph:需要并行节点、复杂 reducer、自定义 HITL 行为、非消息型 state 时。
依赖 :langgraph>=1.0、langchain>=1.0。
Python :>=3.10。
python
# pip install "langgraph>=1.0" "langchain>=1.0" "langchain-openai"
# ===== A. 工厂方法路径:create_react_agent =====
from langchain.agents import create_agent # LangChain 1.0 推荐入口
from langchain_openai import ChatOpenAI
def get_weather(city: str) -> str:
"""Return the weather of the given city."""
return f"{city}: sunny, 22°C"
agent = create_agent(
model=ChatOpenAI(model="gpt-4o"),
tools=[get_weather],
system_prompt="You are a concise weather assistant.",
)
print(agent.invoke({"messages": [("user", "weather of Beijing?")]}))
# ===== B. 自定义图路径:直接控制节点 / reducer / 路由 =====
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition
class State(TypedDict):
messages: Annotated[list, add_messages]
llm = ChatOpenAI(model="gpt-4o").bind_tools([get_weather])
def chatbot(state: State) -> dict:
return {"messages": [llm.invoke(state["messages"])]}
builder = StateGraph(State)
builder.add_node("chatbot", chatbot)
builder.add_node("tools", ToolNode([get_weather]))
builder.add_edge(START, "chatbot")
builder.add_conditional_edges("chatbot", tools_condition)
builder.add_edge("tools", "chatbot")
custom_agent = builder.compile()
何时选哪种:
| 维度 | create_agent / create_react_agent |
自定义 StateGraph |
|---|---|---|
| ReAct 循环够用 | ✅ 推荐 | 杀鸡用牛刀 |
| 需要并行节点、map-reduce | ❌ | ✅ |
| 需要非 messages 的 state schema | ❌(Pydantic state schema 不受支持) | ✅ |
| 需要自定义中断点 / 路由 | 有限支持 | ✅ |
| 需要多 agent handoff | 通过 langgraph-supervisor/swarm |
✅ 也可手写 |
注意 :LangChain 1.0 起,langgraph.prebuilt.create_react_agent 的能力被迁移 到 langchain.agents.create_agent,两者短期共存但官方建议优先使用 create_agent。🟠 推测性建议 :新项目直接用 create_agent,老项目逐步迁移。
资料来源:
- 🟢 LangGraph 1.0 公告(
langgraph.prebuilt能力迁移至langchain.agents):https://www.langchain.com/blog/langchain-langgraph-1dot0 (2026-06-11 抓取) - 🟢 LangGraph quickstart(ReAct 风格自定义图):https://docs.langchain.com/oss/python/langgraph/quickstart (2026-06-11 抓取)
- 🟢 LangChain 结构化输出 + Agent 入口:https://docs.langchain.com/oss/python/langchain/structured-output (2026-06-11 抓取)
5. Checkpointer + interrupt / resume 🟢
适用场景 :HITL 审批、跨会话恢复、长任务断点续跑。
依赖 :langgraph>=1.0、langgraph-checkpoint>=2.0(内存);生产环境另装 langgraph-checkpoint-sqlite 或 langgraph-checkpoint-postgres。
Python :>=3.10。
python
# pip install "langgraph>=1.0" "langgraph-checkpoint>=2.0"
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.types import interrupt, Command
class State(TypedDict):
user_input: str
approved: bool
def collect(state: State) -> dict:
# interrupt 是「在节点内」抛出可恢复异常的特殊调用
answer = interrupt({"question": "approve?", "details": state["user_input"]})
return {"approved": bool(answer)}
def finalize(state: State) -> dict:
return {"user_input": state["user_input"] + (" OK" if state["approved"] else " NO")}
builder = StateGraph(State)
builder.add_node("collect", collect)
builder.add_node("finalize", finalize)
builder.add_edge(START, "collect")
builder.add_edge("collect", "finalize")
builder.add_edge("finalize", END)
# 必须在 compile 时挂载 checkpointer
graph = builder.compile(checkpointer=InMemorySaver())
config = {"configurable": {"thread_id": "demo-001"}}
# 首轮:会暂停在 interrupt
first = graph.invoke({"user_input": "deploy v2", "approved": False}, config)
print("interrupt payload:", first.get("__interrupt__"))
# 恢复:Command(resume=...) 把值喂回 interrupt 的返回位置
resumed = graph.invoke(Command(resume=True), config)
print(resumed)
要点:
interrupt()必须配合 checkpointer + thread_id 才能工作。- 恢复时整个节点从头重新执行 ------
interrupt之前的副作用必须幂等(建议:上游入库用 idempotency key、upsert 或 read-before-write)。 - 同一节点内多个
interrupt()按索引匹配 ,不要根据状态条件跳过某个interrupt。 - 并行多个分支同时挂起时,用
Command(resume={interrupt_id: value, ...})一次性恢复。 - 🟡 deep-research 提示:「严禁在
try/except中包裹 interrupt」这一说法未被官方文档绝对化背书(投票 1-2 被部分驳回),但实务上仍建议避免------若必须捕获,需把暂停异常重新抛出。
资料来源:
- 🟢 Interrupts 官方文档:https://docs.langchain.com/oss/python/langgraph/interrupts (2026-06-11 抓取)
- 🟢 Persistence 官方文档(含
InMemorySaver/SqliteSaver/PostgresSaver):https://docs.langchain.com/oss/python/langgraph/persistence (2026-06-11 抓取) - 🟢 Deep-research 综合(事实底座 §6、§8、§11):见附录 C 参考资料
6. Time Travel / Checkpoint Fork 🟢
适用场景 :调试错误轨迹、审计某个决策的上下文、A/B 测试同一前序状态的不同分支。
依赖 :langgraph>=1.0、langgraph-checkpoint>=2.0。
Python :>=3.10。
python
# pip install "langgraph>=1.0"
from typing_extensions import TypedDict, NotRequired
from langgraph.graph import StateGraph, START
from langgraph.checkpoint.memory import InMemorySaver
class State(TypedDict):
topic: NotRequired[str]
joke: NotRequired[str]
def generate_topic(state: State) -> dict:
return {"topic": "socks in the dryer"}
def write_joke(state: State) -> dict:
return {"joke": f"Why do {state['topic']} disappear? They elope!"}
checkpointer = InMemorySaver()
graph = (
StateGraph(State)
.add_node("generate_topic", generate_topic)
.add_node("write_joke", write_joke)
.add_edge(START, "generate_topic")
.add_edge("generate_topic", "write_joke")
.compile(checkpointer=checkpointer)
)
config = {"configurable": {"thread_id": "fork-demo"}}
graph.invoke({}, config)
# 1. 列出全部 checkpoint
history = list(graph.get_state_history(config))
# 2. 找到 write_joke 即将执行前的那个 checkpoint
before_joke = next(s for s in history if s.next == ("write_joke",))
# 3. 从该 checkpoint 创建分支:替换 topic
fork_config = graph.update_state(
before_joke.config,
values={"topic": "chickens"},
)
# 4. 从分支继续执行(input 传 None 即恢复)
print(graph.invoke(None, fork_config))
要点:
update_state不会回滚 原 thread,而是新建一个分支 checkpoint,原 thread 状态保持不变。- 受影响节点从分支点的 successor 开始重新执行------LLM 调用、interrupt 都会重跑(成本与幂等性要心里有数)。
- 「最后一个 checkpoint 没有 next 节点」时 replay 是 no-op,需要 fork 到更早的位置才能产生新输出。
资料来源:
- 🟢 Time-travel 官方文档:https://docs.langchain.com/oss/python/langgraph/use-time-travel (2026-06-11 抓取)
- 🟢 Deep-research 综合(事实底座 §12):见附录 C 参考资料
7. ToolNode + 结构化输出 🟢
适用场景 :经典 tool-calling 工作流、需要 LLM 返回结构化字段(而非自由文本)。
依赖 :langgraph>=1.0、langchain>=1.0、langchain-openai(或其他 chat 提供方)。
Python :>=3.10。
python
# pip install "langgraph>=1.0" "langchain>=1.0" "langchain-openai" "pydantic>=2"
from typing import Annotated
from typing_extensions import TypedDict
from pydantic import BaseModel, Field
from langchain.tools import tool
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, START
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition
# ===== 7.1 ToolNode + tools_condition =====
class ChatState(TypedDict):
messages: Annotated[list, add_messages]
@tool
def add(a: int, b: int) -> int:
"""Add two integers."""
return a + b
llm = ChatOpenAI(model="gpt-4o").bind_tools([add])
def chatbot(state: ChatState) -> dict:
return {"messages": [llm.invoke(state["messages"])]}
graph = (
StateGraph(ChatState)
.add_node("chatbot", chatbot)
.add_node("tools", ToolNode([add]))
.add_edge(START, "chatbot")
.add_conditional_edges("chatbot", tools_condition)
.add_edge("tools", "chatbot")
.compile()
)
# ===== 7.2 结构化输出 =====
class WeatherResponse(BaseModel):
city: str = Field(description="city name")
temp_c: float = Field(description="temperature in Celsius")
summary: str
structured_llm = ChatOpenAI(model="gpt-4o").with_structured_output(WeatherResponse)
parsed: WeatherResponse = structured_llm.invoke("Beijing, 22 degrees, sunny")
print(parsed.city, parsed.temp_c, parsed.summary)
要点:
ToolNode是langgraph.prebuilt中的成熟工具执行节点:自动从最后一条 AI message 解析tool_calls,并把结果回灌为ToolMessage。tools_condition是配套的路由函数:「上一步 AI 是否要求调用工具?是 →tools,否 → END」。with_structured_output(Schema)是 LangChain ChatModel 通用 API,返回的是「能直接产出 Pydantic / TypedDict 实例的 Runnable」。可在节点内嵌使用。- 🟠 推测性建议:当工具数 > 5 个,建议在 system prompt 内显式列出工具职责边界,否则模型经常乱选。
资料来源:
- 🟢
ToolNode/tools_condition见 LangGraph prebuilt:https://reference.langchain.com/python/langgraph/prebuilt/ (2026-06-11 抓取,结构基于历史官方页面) - 🟢
with_structured_output概念页:https://docs.langchain.com/oss/python/langchain/structured-output (2026-06-11 抓取) - 🟢 LangGraph quickstart(绑定工具 + 路由):https://docs.langchain.com/oss/python/langgraph/quickstart (2026-06-11 抓取)
8. langgraph-supervisor / langgraph-swarm 🟢
适用场景:
- supervisor:中心化调度多个专家 agent,由一个 supervisor 决定下一步交给谁。
- swarm:去中心化,agent 之间通过 handoff 工具相互交接,state 中维护「当前活跃 agent」。
依赖 :langgraph-supervisor==0.0.31(2025-11-19);langgraph-swarm==0.1.0(2025-12-04)。
Python :>=3.10。
8.1 supervisor 最小示例
python
# pip install langgraph-supervisor langchain-openai "langgraph>=1.0"
from langchain_openai import ChatOpenAI
from langgraph_supervisor import create_supervisor
from langgraph.prebuilt import create_react_agent
model = ChatOpenAI(model="gpt-4o")
def add(a: float, b: float) -> float:
"""Add two numbers."""
return a + b
def web_search(query: str) -> str:
"""Search the web for information."""
return "FAANG headcount 2024: Meta 67,317; Apple 164,000; Amazon 1,551,000; Netflix 14,000; Google 181,269."
math_agent = create_react_agent(
model=model, tools=[add], name="math_expert",
prompt="You are a math expert. Always use one tool at a time.",
)
research_agent = create_react_agent(
model=model, tools=[web_search], name="research_expert",
prompt="You are a world class researcher. Do not do any math.",
)
workflow = create_supervisor(
[research_agent, math_agent],
model=model,
prompt=(
"You are a team supervisor managing a research expert and a math expert. "
"For current events, use research_agent. For math, use math_agent."
),
)
app = workflow.compile()
app.invoke({"messages": [{"role": "user", "content": "combined FAANG headcount 2024?"}]})
8.2 swarm 最小示例
python
# pip install langgraph-swarm langchain-openai "langgraph>=1.0"
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from langgraph.checkpoint.memory import InMemorySaver
from langgraph_swarm import create_handoff_tool, create_swarm
model = ChatOpenAI(model="gpt-4o")
def add(a: int, b: int) -> int:
"""Add two numbers"""
return a + b
alice = create_agent(
model,
tools=[add, create_handoff_tool(agent_name="Bob", description="Transfer to Bob")],
system_prompt="You are Alice, an addition expert.",
name="Alice",
)
bob = create_agent(
model,
tools=[create_handoff_tool(agent_name="Alice", description="Transfer to Alice")],
system_prompt="You are Bob, you speak like a pirate.",
name="Bob",
)
workflow = create_swarm([alice, bob], default_active_agent="Alice")
app = workflow.compile(checkpointer=InMemorySaver())
注意:
- 🟢
langgraph-supervisorREADME 在 2026-06-11 抓取时明确写:「We now recommend using the supervisor pattern directly via tools rather than this library for most use cases.」------也就是说官方逐渐倾向于让用户用 handoff 工具自己手写 supervisor,而非依赖 prebuilt 库。 langgraph-swarm0.1.0 README 中的示例使用langchain.agents.create_agent(LangChain 1.0 入口),而非旧版create_react_agent。- supervisor / swarm 在「状态隔离、可观测性、故障恢复」上的取舍 deep-research 标记为「证据不足」,需在实战中专项验证(待补)。
资料来源:
- 🟢
langgraph-supervisor-py:https://github.com/langchain-ai/langgraph-supervisor-py (2026-06-11 抓取,版本 0.0.31) - 🟢
langgraph-swarm-py:https://github.com/langchain-ai/langgraph-swarm-py (2026-06-11 抓取,版本 0.1.0) - 🟠 Deep-research 综合「证据不足章节」第 1 条:见附录 C 参考资料
9. LangSmith + OpenTelemetry 集成 🟢
适用场景 :上线后链路追踪、Token 成本核算、与已有 OTel 体系(Tempo/Jaeger/Honeycomb 等)打通。
依赖 :langsmith>=0.4.25(含 OTEL 修复),LangChain/LangGraph 任意 1.x 版本均可。
Python :>=3.10。
bash
# pip install "langsmith[otel]>=0.4.25" langchain langgraph
# === A. 最简:开启 LangSmith 自动追踪 ===
export LANGSMITH_TRACING=true
export LANGSMITH_API_KEY="<your-langsmith-api-key>"
# 可选区域:us(默认)/eu/apac/aws
export LANGSMITH_ENDPOINT="https://api.smith.langchain.com"
# OTEL bridge:让 LangSmith trace 同步发送到任意 OTLP collector
export LANGSMITH_OTEL_ENABLED=true
bash
# === B. 仅发往外部 OTLP collector(不再写入 LangSmith) ===
export LANGSMITH_OTEL_ENABLED=true
export LANGSMITH_OTEL_ONLY=true
export OTEL_EXPORTER_OTLP_ENDPOINT="https://my-collector.example.com:4318"
export OTEL_EXPORTER_OTLP_HEADERS="x-api-key=<token>"
export OTEL_SERVICE_NAME="my-langgraph-app"
python
# === C. 任意 Python 入口直接跑,LangChain/LangGraph 自动被埋点 ===
from langgraph.prebuilt import create_react_agent # 或 langchain.agents.create_agent
from langchain_openai import ChatOpenAI
def get_weather(city: str) -> str:
"""Get weather for a city."""
return f"{city}: sunny"
agent = create_react_agent(ChatOpenAI(model="gpt-4o"), tools=[get_weather])
agent.invoke({"messages": [("user", "weather of Shanghai?")]})
# 启动后到 https://smith.langchain.com 的项目页面即可看到 trace
要点:
- LangSmith 官方推荐的环境变量名是
LANGSMITH_TRACING/LANGSMITH_API_KEY;老变量LANGCHAIN_TRACING_V2/LANGCHAIN_API_KEY仍可工作但不推荐新项目使用。 - 自托管 LangSmith:把
LANGSMITH_ENDPOINT指向<your-host>/api/v1,OTel endpoint 走<your-host>/otel。 - 区域端点:EU
eu.api.smith.langchain.com、APACapac.api.smith.langchain.com、AWS USaws.api.smith.langchain.com。 - 多目的地推荐做法:起一个 OTel Collector,让 LangSmith 与其他后端(Tempo / Honeycomb / Datadog)从 Collector 分流。
资料来源:
- 🟢 LangSmith × OpenTelemetry 官方页面:https://docs.langchain.com/langsmith/trace-with-opentelemetry (2026-06-11 抓取)
- 🟢 LangSmith pytest 集成(含版本要求矩阵):https://docs.langchain.com/langsmith/pytest (2026-06-11 抓取)
10. Eval Gate + Replay Testing 🟢
适用场景 :CI 接入「上线前必须跑 N 个评估用例」、「用历史 thread replay 到新版本图」做回归保护。
依赖 :langsmith>=0.3.4(pytest 集成)、langsmith[pytest](rich output)、openevals(LLM-as-judge 预置 prompt);replay 部分依赖 langgraph>=1.0。
Python :>=3.10。
10.1 LLM-as-Judge + pytest gate
python
# pip install -U "langsmith[pytest]>=0.4.25" openevals openai pytest
import pytest
from langsmith import testing as t
from openevals.llm import create_llm_as_judge
from openevals.prompts import CORRECTNESS_PROMPT
def generate_sql(q: str) -> str:
return "SELECT * FROM customers;" # 实际换成你的 agent / graph
@pytest.mark.langsmith
def test_sql_select_all() -> None:
q = "Get all users from the customers table"
t.log_inputs({"user_query": q})
expected = "SELECT * FROM customers;"
t.log_reference_outputs({"sql": expected})
sql = generate_sql(q)
t.log_outputs({"sql": sql})
# LLM-as-Judge:用 openevals 预置 prompt 给一个分数
judge = create_llm_as_judge(
prompt=CORRECTNESS_PROMPT,
model="openai:o3-mini",
feedback_key="correctness",
)
score = judge(inputs={"q": q}, outputs={"sql": sql}, reference_outputs={"sql": expected})
t.log_feedback(key="correctness", score=score["score"])
assert sql == expected
运行:
bash
pytest --langsmith-output tests/
# 或仅本地试跑、不上报 LangSmith:
LANGSMITH_TEST_TRACKING=false pytest tests/
10.2 历史 thread Replay 到新版本图
🟠 推测性建议(基于官方 time-travel 能力的组合用法):LangGraph 官方没有专门给出一份「跨版本 replay」工程模板,下面是一种社区常见做法。
python
# pip install "langgraph>=1.0" "langgraph-checkpoint-postgres"
from langgraph.checkpoint.postgres import PostgresSaver
# 1. 旧 thread 留存于持久 checkpointer(如 Postgres)
checkpointer = PostgresSaver.from_conn_string("postgresql://user:pwd@host/db")
checkpointer.setup()
# 2. 用「新版本」graph 装配,但挂载同一 checkpointer
new_graph = build_graph_v2().compile(checkpointer=checkpointer)
# 3. 遍历线上历史 thread,从某个早期 checkpoint replay
config = {"configurable": {"thread_id": "prod-thread-123"}}
history = list(new_graph.get_state_history(config))
replay_from = next(s for s in history if s.next == ("plan",))
# 4. 用 update_state 创建评估分支,避免污染原 thread
fork_config = new_graph.update_state(replay_from.config, values={})
new_output = new_graph.invoke(None, fork_config)
# 5. 用 LangSmith evaluate 或 pytest mark 评估 new_output
要点:
@pytest.mark.langsmith让普通 pytest 用例同时变成 LangSmith experiment------CI 既能assert失败阻塞 merge,又能在 LangSmith 后台看见曲线。LANGSMITH_TEST_TRACKING=false用于本地 dry-run。- 跨版本 replay 的本质就是「同一 checkpointer + 新图 + fork checkpoint」组合,没有专用 API。
- 🟠 推测性建议:CI 中只跑「关键路径用例」+「上线前一日的 N 个生产 thread」,避免回归集失控。
资料来源:
- 🟢 LangSmith pytest 集成:https://docs.langchain.com/langsmith/pytest (2026-06-11 抓取)
- 🟢 LangSmith evaluation quickstart(
client.evaluate+ LLM-as-judge):https://docs.langchain.com/langsmith/evaluation-quickstart (2026-06-11 抓取) - 🟢 LangSmith LLM-as-Judge 概念(UI 路径):https://docs.langchain.com/langsmith/llm-as-judge (2026-06-11 抓取)
- 🟢 LangGraph Time-travel(用于 replay):https://docs.langchain.com/oss/python/langgraph/use-time-travel (2026-06-11 抓取)
附录 C:参考资料与术语表
C.1 官方文档与关键链接
C.1.1 LangGraph 官方文档入口(Python)
注:「适用版本」为 官方/推测:官方未在每个页头标注版本要求;此处基于出现该术语的 release 推导。
C.1.2 LangGraph GitHub
| 资源 | URL | 抓取日期 | 备注 |
|---|---|---|---|
| 主仓库 | https://github.com/langchain-ai/langgraph | 2026-06-11 | 34.4k stars / 5.8k forks;MIT 许可;99.6% Python;描述 "Build resilient agents." |
| Examples(仅存档) | https://github.com/langchain-ai/langgraph/tree/main/examples | 2026-06-11 | README 注明「retained purely for archival purposes and is no longer updated」;新示例请见官方 docs |
libs/ 目录 |
https://github.com/langchain-ai/langgraph/tree/main/libs | 2026-06-11 | 含 sdk-py、cli、checkpoint、checkpoint-postgres、checkpoint-sqlite、supervisor、swarm 等子包 |
| Releases | https://github.com/langchain-ai/langgraph/releases | 2026-06-11 | 共 55 页;最新 1.2.4(2026-06-02) |
| AGENTS.md(项目 AI 协作规范) | https://github.com/langchain-ai/langgraph/blob/main/AGENTS.md | 2026-06-11 | --- |
| CLAUDE.md(项目级 AI 规范) | https://github.com/langchain-ai/langgraph/blob/main/CLAUDE.md | 2026-06-11 | --- |
C.1.3 PyPI 包
| 包名 | URL | 当前版本(2026-06-11 抓取) | 关键日期 |
|---|---|---|---|
langgraph |
https://pypi.org/project/langgraph/ | 1.2.4 | 2026-06-02;1.2.0 = 2026-05-12 |
langgraph-checkpoint |
https://pypi.org/project/langgraph-checkpoint/ | 4.1.1 | 2026-05-22;4.1.0 = 2026-05-12;4.0.0 = 2026-01-12 |
langgraph-checkpoint-postgres |
https://pypi.org/project/langgraph-checkpoint-postgres/ | 3.1.0 | 2026-05-12;3.0.0 = 2025-10-20 |
langgraph-checkpoint-sqlite |
https://pypi.org/project/langgraph-checkpoint-sqlite/ | 3.1.0 | 2026-05-12;3.0.0 = 2025-10-20 |
langgraph-supervisor |
https://pypi.org/project/langgraph-supervisor/ | 0.0.31 | 2025-11-19;0.0.1 = 2025-02-07 |
langgraph-swarm |
https://pypi.org/project/langgraph-swarm/ | 0.1.0 | 2025-12-04;0.0.1 = 2025-02-25 |
langgraph-prebuilt(PyPI) |
https://pypi.org/project/langgraph-prebuilt/ | 1.1.0(推测:基于 1.1.0 = 2026-05-12 的 release notes 推导;待精确确认) | 2026-05-12 |
langgraph-cli |
https://pypi.org/project/langgraph-cli/ | 0.4.28 | 2026-06-02 |
langgraph-sdk |
https://pypi.org/project/langgraph-sdk/ | 0.4.2 | 2026-06-02 |
注:v1.0 GA 的精确发布日期本次未在 GitHub Releases 前 10 页定位到 待补 。从 checkpoint 3.0.0 / postgres 3.0.0 / sqlite 3.0.0 同步在 2025-10-20 发版来看,langgraph 主包 1.0.0 GA 大概率落在 2025-10 中下旬 (推测 );建议后续通过
git tag或 CHANGELOG 确认。
C.1.4 LangSmith / LangGraph Platform
C.1.5 官方博客(LangChain Blog)
入口:https://blog.langchain.com/ → 301 跳转至 https://www.langchain.com/blog/ | 抓取日期:2026-06-11
| 标题 | URL | 发布日期 | 标签 |
|---|---|---|---|
| Fault Tolerance in LangGraph: Retries, Timeouts, and Error Handlers | https://www.langchain.com/blog/fault-tolerance-in-langgraph | 2026-06-04 | LangGraph;作者 Quanzheng Long、Sydney Runkle |
| How Lyft Built a Self-Serve AI Agent Platform for Customer Support with LangGraph and LangSmith | https://www.langchain.com/blog/lyft-built-a-self-serve-ai-agent-platform-for-customer-support-with-langgraph-and-langsmith | 2026-06-(待精确确认)注1 | LangGraph / LangSmith |
| From Token Streams to Agent Streams | https://www.langchain.com/blog/token-streams-to-agent-streams | 2026-05-21 | LangGraph;作者 Christian Bromann、Nick Hollon |
- 注 1:博客首页摘要列示日期为「2026-05-27」附近的相对时间;具体日期以页面为准。
- v1.0 GA 公告:本次未在 blog 检索到独立公告页(待补)。
C.1.6 LangChain Academy(LangGraph 课程)
| 课程 | URL | 抓取日期 | 备注 |
|---|---|---|---|
| Foundation: Introduction to LangGraph - Python | https://academy.langchain.com/ | 2026-06-11 | 6 模块 / 55 课 / 约 6 小时;模块:①Introduction ②State and Memory ③UX and Human-in-the-Loop ④Building Your Assistant ⑤Long-Term Memory ⑥Deployment |
| Project: Deep Agents(with LangGraph) | https://academy.langchain.com/courses/deep-agents-with-langgraph | 2026-06-11 | 官方「feature」课程 |
C.2 关键术语解释(按字母顺序)
每条目的链接均为 官方 原始链接;释义基于官方原文与 API Reference 整合。
BaseStore
- 中文释义:长时记忆(long-term memory)的底层抽象,向 Graph / Functional API 暴露
put / get / search / list_namespaces等接口;通过BaseStore实现可注入到节点或 entrypoint 中。 - 官方原文(持久化页):"Memory store ... A memory store in LangGraph is a way to store and retrieve information across threads."
- 原始链接:https://docs.langchain.com/oss/python/langgraph/persistence#memory-store
- 适用版本:>= 0.2
Channel
- 中文释义:State 的"字段管道",每次 super-step 在节点间按各自 reducer 写入;预置
LastValue、BinaryOperatorAggregate、Topic等。 - 官方原文(pregel 引用):"DeltaChannel" 参见 https://reference.langchain.com/python/langgraph/pregel/#deltachannel
- 原始链接:https://docs.langchain.com/oss/python/langgraph/persistence ;https://docs.langchain.com/oss/python/langgraph/pregel
- 适用版本:>= 0.2;Delta channel 标记为 beta(4.1.0 起)
Checkpointer
- 中文释义:持久化检查点接口,标准实现有
InMemorySaver / MemorySaver、SqliteSaver、PostgresSaver;可注入StateGraph.compile(checkpointer=...)或@entrypoint(checkpointer=...)。 - 原始链接:https://docs.langchain.com/oss/python/langgraph/persistence#checkpointer-interface
- 适用版本:>= 0.2
Command
- 中文释义:节点返回值类型,用于"组合控制流 + 状态更新"(如
Command(goto="next", update={...})),并支持Command.PARENT跨子图跳转、Command(resume=...)恢复 interrupt。 - 官方原文(Graph API 概览):"Combine control flow and state updates with
Command" - 原始链接:https://docs.langchain.com/oss/python/langgraph/graph-api#command
- 适用版本:>= 0.2
compile
- 中文释义:
StateGraph的最终编译步骤;返回可调用的Pregel实例(支持invoke/stream/astream/aget_state/aupdate_state等)。 - 原始链接:https://docs.langchain.com/oss/python/langgraph/graph-api#compiling-your-graph
- 适用版本:>= 0.2
Conditional Edge
- 中文释义:以一个返回下一个节点名的函数(或
path_map)决定的"条件边";通过add_conditional_edges(src, router, path_map)添加。 - 原始链接:https://docs.langchain.com/oss/python/langgraph/graph-api#conditional-edges
- 适用版本:>= 0.2
DeltaChannel
- 中文释义:增量式状态通道,用于跟踪 channel 字段的"未完成写入 / delta history";v4.1.0 标记为 beta。
- 官方原文(release notes 4.1.0):"Delta channel and delta-history APIs marked as beta"
- 原始链接:https://reference.langchain.com/python/langgraph/pregel/#langgraph.pregel.Pregel#deltachannel
- 适用版本:>= 4.1.0(langgraph-checkpoint)
dispatch_custom_event
- 中文释义:在节点内部通过
get_stream_writer()或在外部stream(..., stream_mode="custom")配合,向订阅者发送自定义事件。 - 原始链接:https://docs.langchain.com/oss/python/langgraph/streaming#custom-data ;https://docs.langchain.com/oss/python/langgraph/event-streaming#stream-messages
- 适用版本:>= 0.2;推荐使用 v1.2 的 event-streaming API
durability(exit / async / sync)
- 中文释义:持久化"耐久度"模式,控制 checkpoint 何时落盘:
"exit"(图退出时)、"async"(异步后台)、"sync"(同步立刻)。可在compile( durability=... )或运行时 config 中设置。 - 原始链接:https://docs.langchain.com/oss/python/langgraph/persistence#durability-modes
- 适用版本:>= 0.2(确切命名版本 待精确确认)
Functional API
- 中文释义:通过
@entrypoint与@task装饰器,将"标准 Python 控制流"包装为可检查点、可恢复、可流式的"工作流",无需显式 StateGraph。 - 原始链接:https://docs.langchain.com/oss/python/langgraph/functional-api
- 适用版本:>= 0.4
get_state_history
- 中文释义:返回某
thread_id的StateSnapshot列表(按时间倒序),是 Time Travel 的基础 API。 - 原始链接:https://docs.langchain.com/oss/python/langgraph/persistence#get-state-history ;https://docs.langchain.com/oss/python/langgraph/use-time-travel
- 适用版本:>= 0.2
Human-in-the-loop(HITL)
- 中文释义:通过
interrupt(...)暂停执行,由人类决策后再以Command(resume=...)恢复;常用于审批、编辑状态、人工接管工具调用。 - 原始链接:https://docs.langchain.com/oss/python/langgraph/interrupts
- 适用版本:>= 0.2
InMemorySaver
- 中文释义:进程内 Checkpointer 实现(早期文档中也称
MemorySaver),数据随进程退出而丢失;适合测试与本地实验。 - 原始链接:https://docs.langchain.com/oss/python/langgraph/persistence#checkpointer-libraries
- 适用版本:>= 0.2
interrupt
- 中文释义:节点或 task 内调用
interrupt(payload),暂停该 super-step 并将 payload 推送给调用方;Command(resume=...)继续。 - 官方原文(Interrupts 页):"Pause using
interrupt" - 原始链接:https://docs.langchain.com/oss/python/langgraph/interrupts#pause-using-interrupt
- 适用版本:>= 0.2
MemorySaver
- 中文释义:
InMemorySaver的早期命名(prebuilt 同名导出,官方文档中现已统一为InMemorySaver/InMemoryStore)。 - 原始链接:https://docs.langchain.com/oss/python/langgraph/persistence#checkpointer-libraries
- 适用版本:>= 0.2(迁移期)
namespace(ns)
- 中文释义:长时记忆 BaseStore 的命名空间(
("user_id", "memories")等 tuple 形式),用于按维度隔离与检索。 - 官方原文(持久化页):"Listing items in a namespace" / "Checkpoint namespace"
- 原始链接:https://docs.langchain.com/oss/python/langgraph/persistence#listing-items-in-a-namespace
- 适用版本:>= 0.2
Node
- 中文释义:
StateGraph中接受 state / config 并返回 partial state 的可调用对象;支持RetryPolicy、timeout、cache。 - 原始链接:https://docs.langchain.com/oss/python/langgraph/graph-api#nodes
- 适用版本:>= 0.2
PostgresSaver
- 中文释义:基于 PostgreSQL 的 Checkpointer 实现,依赖
langgraph-checkpoint-postgres;支持 connection pool 与加密 serializer。 - 原始链接:https://docs.langchain.com/oss/python/langgraph/persistence#checkpointer-libraries ;https://pypi.org/project/langgraph-checkpoint-postgres/
- 适用版本:>= 0.2
persistence
- 中文释义:LangGraph 的"长存状态"层,包括 checkpointer(thread / checkpoint)与 store(跨 thread 长期记忆)。
- 原始链接:https://docs.langchain.com/oss/python/langgraph/persistence
- 适用版本:>= 0.2
pydantic v1/v2
- 中文释义:StateGraph State schema 兼容
pydantic与dataclass;langgraph>=1.0已切到 pydantic v2 生态(依赖langchain-core>=1.0)。 - 原始链接:https://docs.langchain.com/oss/python/langgraph/use-graph-api#use-pydantic-models-for-graph-state ;https://docs.langchain.com/oss/python/langgraph/graph-api#schema
- 适用版本:>= 1.0(官方/推测)
reducer
- 中文释义:合并 partial state 的函数(
Annotated[list, add_messages]等);LangGraph 内置add_messages、operator.add、LastValue 缺省等。 - 原始链接:https://docs.langchain.com/oss/python/langgraph/graph-api#reducers
- 适用版本:>= 0.2
recursion_limit
- 中文释义:单次图执行的最大 super-step 步数上限(默认 25),可在
config={"recursion_limit": N}中覆盖。 - 官方原文(Graph API 概览):"Impose a recursion limit"
- 原始链接:https://docs.langchain.com/oss/python/langgraph/graph-api#recursion-limit
- 适用版本:>= 0.2
Replay
- 中文释义:Time Travel 子操作------把图从某个历史 checkpoint 的 config 重新 invoke 一次,生成"重放分支"。
- 官方原文(Time Travel 页):"Replay"
- 原始链接:https://docs.langchain.com/oss/python/langgraph/persistence#replay ;https://docs.langchain.com/oss/python/langgraph/use-time-travel
- 适用版本:>= 0.2
Resume
- 中文释义:通过
Command(resume=value)恢复被interrupt暂停的图;Functional API 中同名 API。 - 原始链接:https://docs.langchain.com/oss/python/langgraph/interrupts#resuming-interrupts ;https://docs.langchain.com/oss/python/langgraph/graph-api#resume
- 适用版本:>= 0.2
Send
- 中文释义:在条件边中创建"map"操作,将多条 payload 派发到同一目标节点;常用于 map-reduce。
- 官方原文(Graph API 概览):"Map-Reduce and the send API"
- 原始链接:https://docs.langchain.com/oss/python/langgraph/graph-api#send
- 适用版本:>= 0.2
SqliteSaver
- 中文释义:基于 SQLite 的 Checkpointer 实现,依赖
langgraph-checkpoint-sqlite;适合单机/小规模。 - 原始链接:https://docs.langchain.com/oss/python/langgraph/persistence#checkpointer-libraries ;https://pypi.org/project/langgraph-checkpoint-sqlite/
- 适用版本:>= 0.2
start / astream / stream
- 中文释义:
Pregel.invoke/Pregel.stream(同步)与Pregel.ainvoke/Pregel.astream(异步);astream返回异步迭代器。 - 原始链接:https://reference.langchain.com/python/langgraph/pregel/#langgraph.pregel.Pregel.stream ;https://reference.langchain.com/python/langgraph/pregel/#langgraph.pregel.Pregel.astream
- 适用版本:>= 0.2
StreamPart
- 中文释义:流式事件的对象类型(
ValuesStreamPart / UpdatesStreamPart / MessagesStreamPart / CustomStreamPart / CheckpointStreamPart / TasksStreamPart / DebugStreamPart)。 - 原始链接:https://reference.langchain.com/python/langgraph/types/StreamPart
- 适用版本:>= 1.0
stream_mode
- 中文释义:枚举值集合,包括
values / updates / messages / custom / checkpoints / tasks / debug;可单选或多选。 - 官方原文(Streaming 页):"
values/updates/messages/custom/checkpoints/tasks/debug" - 原始链接:https://docs.langchain.com/oss/python/langgraph/streaming#stream-modes
- 适用版本:>= 0.2
State
- 中文释义:图执行中跨节点流动的数据(可 typing 注解、含 reducer),通常用
TypedDict/dataclass/pydantic描述。 - 原始链接:https://docs.langchain.com/oss/python/langgraph/graph-api#state
- 适用版本:>= 0.2
StateGraph
- 中文释义:声明式图构建器;通过
add_node / add_edge / add_conditional_edges / compile构造可执行图。 - 原始链接:https://docs.langchain.com/oss/python/langgraph/graph-api#stategraph
- 适用版本:>= 0.2
StateSnapshot
- 中文释义:
get_state/get_state_history返回的对象,含有values / next / tasks / metadata / config / created_at / parent_config等字段。 - 官方原文(持久化页):"StateSnapshot fields"
- 原始链接:https://docs.langchain.com/oss/python/langgraph/persistence#statesnapshot-fields
- 适用版本:>= 0.2
Subgraph
- 中文释义:作为父图节点嵌入的另一个
Pregel;支持 per-invocation / per-thread / stateless 三种持久化模式。 - 原始链接:https://docs.langchain.com/oss/python/langgraph/use-subgraphs
- 适用版本:>= 0.2
super-step
- 中文释义:一轮"图步进":所有当前就绪的节点并行执行、写入 channel;结束后再确定下一批节点。
- 官方原文(持久化页):"Super-steps"
- 原始链接:https://docs.langchain.com/oss/python/langgraph/persistence#super-steps
- 适用版本:>= 0.2
Super-step boundary
- 中文释义:在 super-step 之间形成的"检查点边界";checkpointer 默认在每个 super-step 边界落盘。
- 原始链接:https://docs.langchain.com/oss/python/langgraph/persistence#super-steps
- 适用版本:>= 0.2
thread_id
- 中文释义:State / Checkpoint 的逻辑会话键,相同
thread_id共享同一历史;通常作为configurable.thread_id传入。 - 官方原文(持久化页):"Threads"
- 原始链接:https://docs.langchain.com/oss/python/langgraph/persistence#threads
- 适用版本:>= 0.2
Time Travel
- 中文释义:基于
get_state_history+update_state(fork)/invoke(replay),在历史 checkpoint 之上"重放"或"分叉"以探索替代路径。 - 原始链接:https://docs.langchain.com/oss/python/langgraph/use-time-travel
- 适用版本:>= 0.2
ToolNode
- 中文释义:预置节点,将 LLM 工具调用转译为函数调用并写回
ToolMessage;通常与create_react_agent搭配。 - 原始链接:https://docs.langchain.com/oss/python/langgraph/agents ;https://github.com/langchain-ai/langgraph/tree/main/libs/prebuilt
- 适用版本:>= 0.2
update_state
- 中文释义:以
as_node+values在指定 checkpoint 之上"分叉"出新的 State(生成新的 checkpoint 记录)。 - 官方原文(持久化页 / Time Travel 页):"Update state"
- 原始链接:https://docs.langchain.com/oss/python/langgraph/persistence#update-state
- 适用版本:>= 0.2
with_structured_output
- 中文释义:
ChatModel的工具方法,将 LLM 输出约束为指定 schema(pydantic / TypedDict / JSON Schema),常与ToolNode配合实现"结构化工具调用"。 - 原始链接:https://reference.langchain.com/python/langchain-core/language_models/chat_models/#langchain_core.language_models.chat_models.BaseChatModel.with_structured_output
- 适用版本:跟随 langchain-core(>= 0.1,1.4.x 当前)
C.3 社区资源(按优先级)
C.3.1 高 Star GitHub 项目(与 LangGraph Python 直接相关)
抓取日期:2026-06-11;筛选标准:stars >= 1k 且与 LangGraph 直接相关;以下仅作存在性记录,不背书可用性。
| 项目 | URL | 抓取日期 | 备注 |
|---|---|---|---|
| langchain-ai/langgraph(官方) | https://github.com/langchain-ai/langgraph | 2026-06-11 | 34.4k stars |
| langchain-ai/langchain(官方) | https://github.com/langchain-ai/langchain | 2026-06-11 | 嵌含 langgraph 集成 |
| langchain-ai/langgraphjs(官方) | https://github.com/langchain-ai/langgraphjs | 2026-06-11 | JS/TS 对应实现(非本附录主题) |
| langflow(Low-code UI for LangGraph) | https://github.com/logspace-ai/langflow | 2026-06-11 | 社区;stars 量级大,使用时需自查版本 |
| LlamaIndex 与 LangGraph 集成 | https://github.com/run-llama/llama_index | 2026-06-11 | 社区;提供 agents/Workflows 跨集成 |
C.3.2 半年内(2025-12 ~ 2026-06)高质量技术博客
列表来自 LangChain 官方 blog;其它社区文章可参考 LangChain Forum 的 "OSS Product Help" 分类。
| 标题 | URL | 发布日期 | 来源类型 |
|---|---|---|---|
| Fault Tolerance in LangGraph: Retries, Timeouts, and Error Handlers | https://www.langchain.com/blog/fault-tolerance-in-langgraph | 2026-06-04 | 官方 |
| How Lyft Built a Self-Serve AI Agent Platform for Customer Support with LangGraph and LangSmith | https://www.langchain.com/blog/lyft-built-a-self-serve-ai-agent-platform-for-customer-support-with-langgraph-and-langsmith | 2026-05-27(待精确确认) | 官方(客户案例) |
| From Token Streams to Agent Streams | https://www.langchain.com/blog/token-streams-to-agent-streams | 2026-05-21 | 官方 |
C.3.3 LangChain Interrupt 会议分享
入口:https://www.langchain.com/interrupt | 抓取日期:2026-06-11
实际会议录像与 slide 通过该入口分发;本次未逐项抓取(待补:建议补充 1-2 个 2025/2026 年度演讲中与 LangGraph Python 强相关的链接)。
C.3.4 关键 Discord / Forum 主题
| 平台 | 入口 URL | 抓取日期 | 备注 |
|---|---|---|---|
| LangChain Forum(Discourse) | https://forum.langchain.com/ | 2026-06-11 | 6 个分类:Announcements / OSS Product Help / LangSmith Product Help / LangChain Academy / Talking Shop / Forum Feedback |
| OSS Product Help(LangGraph & LangChain OSS) | https://forum.langchain.com/c/oss-product-help-lc-and-lg/16 | 2026-06-11 | 31 主题(首页观察值) |
| LangChain Academy 答疑分类 | https://forum.langchain.com/c/langchain-academy/10 | 2026-06-11 | 52 主题 |
| LangChain Discord | https://discord.gg/langchain → 301 → https://discord.com/invite/langchain | 2026-06-11 | 官方邀请链接;进入后请在 #langgraph / #help-langgraph 子频道检索 |
C.4 参考资料引用规范
C.4.1 引用必须包含的字段
| 字段 | 说明 |
|---|---|
| 标题 | 文档 / 文章 / 包的展示名 |
| URL | 完整可访问链接 |
| 发布日期 | 官方公告、博客、Release Note 应标注;PyPI 抓取时记录版本日期 |
| 抓取日期 | 本次访问 URL 的 UTC 日期(本次统一记 2026-06-11) |
| 来源类型 | 官方 / 社区 / 推测 三选一,必填 |
C.4.2 来源类型判定规则
- 官方 :来源域名属于
langchain.com / langchain-ai.github.io / pypi.org / github.com/langchain-ai/* / reference.langchain.com / docs.langchain.com / blog.langchain.com。 - 社区:第三方开发者文章、个人博客、Discord / Forum 用户帖、第三方 GitHub 项目。
- 推测:由官方资料综合推导、未在官方页面/原文中明示的结论(如"v1.0 GA ≈ 2025-10 中下旬")。
C.4.3 同一事实冲突时的优先级
- 官方原文档(
docs.langchain.com/reference.langchain.com)> 官方博客 > PyPI / GitHub Release Notes。 - 同一事实的版本号,以 当前 PyPI
latest为准;与 release notes 不一致时在文末"待补"中注明。
质量审查说明
本报告所有正文与附录均经过多轮审查闭环。具体节奏:
| 板块 | 轮次 | 结论 |
|---|---|---|
| 第 1--3 章 | Round 1 + Round 2 | 已闭环;Platform 三层、版本锚点、Graph 设计规范与后续章节对齐 |
| 第 4--6 章 | Round 1 + Round 2 | 已闭环;checkpointer、HITL、streaming、ToolNode、Agent 构建等口径与 1.2.4 对齐 |
| 第 7--9 章 | Round 1 + Round 2 | 已闭环;多智能体、测试与质量保障、可观测性与 deep-research 事实底座对齐 |
| 第 10--12 章 | Round 1 + Round 2 | 已闭环;性能 / 成本 / 可靠性、生产部署、安全 / 合规 / 多租户口径统一 |
| 附录 A | Round 1 + Round 2 | 已闭环;版本、迁移、deprecated API、InMemorySaver / Async*Saver、prebuilt 1.1.0 等事实修正完成 |
| 附录 B | Round 1 | 已闭环;10 个最小代码片段与 LangGraph Python 版本基线对齐 |
| 附录 C | Round 1 | 已闭环;官方文档、PyPI、LangSmith 部署、术语表和引用规范完成 |
已知限制 / 待补项
本节聚合 deep-research 综合结论、附录 A 的 W0--W8 与附录 B / C 的待补条目。
来源冲突与官方公告
- W0 / v1.0 GA 公告原文链接 :PyPI
langgraph==1.0.02025-10-17 发布,但官方 blog 检索未在主页命中独立 GA 公告;建议查完整 blog 正文或langgraph/CHANGELOG.md确认。 - 附录 A.1.2 冲突 :「1.0 with no breaking changes」与 1.1.0 显式标记
LangGraphDeprecatedSinceV11、1.0.0rc1 起 drop Python 3.9 同时存在。
文档站抓取限制
- W1 :
langchain-ai.github.io/langgraph子页多次重定向至主站空白页,未抓到内容;建议用curl+ 用户代理或读langgraph仓内docs/源 md。
历史 / 弃用 API 状态
- W3 :
get_state_version/NodeStyleSend/ConfigurableCallable等历史 API 是否仍 deprecate 未在 release notes 中显式公告。
客户端协议版本对应
- W4 :
langgraph-sdk==0.4.0引入的 v3 streaming 与 langgraph 1.2.4 core 的对应关系未在本轮抓取中确认。
第三方 checkpointer
- W5 :
RedisSaver等第三方 checkpointer 与 1.2.x 兼容矩阵不在主仓;建议查langgraph-checkpoint-redis社区包 release notes。
1.2.x 行为细节
- W7 :1.2.x
set_node_defaults(PR #7747)对 store namespace 解析的影响 release notes 未点明。 - W8:PR #6298 是否对应 Python 3.14 cursory 支持,附录 A 引用但未独立抓取。
多智能体 / 测试 / 可靠性(deep-research 综合结论中证据不足)
- 多智能体架构(Supervisor / Swarm / Hierarchical / Handoff)在状态边界、隔离、可观测性上的取舍。
- 不同 checkpointer × durability 配置(sync / async / exit)的精确故障恢复保证。
- LangSmith 与 LangGraph 闭环:哪些是 OSS 能力、哪些是平台服务。
- LangGraph Platform / Self-Hosted 部署、Python 服务集成的多租户 / 安全 / 合规 / 成本推荐架构。
部署与定价
- LangSmith Cloud 自托管 / Cloud 区域列表(EU / US / APAC)、HIPAA BAA 是否在 Cloud 支持。
- LangGraph Platform 精确计费 / SLA;Self-Hosted Lite / Enterprise 仍需 LangSmith 商业 License,精确价格以 LangChain 销售 / partner portal 为准。
runs.create接受idempotency_key的完整 spec;Graph 蓝绿发布 cookbook。
测试 / 可观测性
- Plan-and-Execute / Reflection / Reflexion 的官方可运行代码细节仍待补。
langchain-community是否含 Llama Guard 集成需自核;PII 脱敏官方 SDK 未明确给出,Presidio / HMAC tokenization 为社区方案。
数据时点
- 所有 PyPI / GitHub Releases / 官方文档 / 官方博客抓取日期均为 2026-06-11。超过该日期请按附录 C.4 的引用规范复核。
报告正文到此结束。