【LangGraph】新篇章:LangGraph 运行时上下文(Runtime Context)
- 前言
-
- 1、什么是运行时上下文
-
- [1.1 为什么需要运行时上下文](#1.1 为什么需要运行时上下文)
- [1.2 运行时上下文的本质](#1.2 运行时上下文的本质)
- 2、代码详解:一个多语言问候助手
-
- [2.1 定义上下文和状态](#2.1 定义上下文和状态)
- [2.2 编写节点函数,访问上下文](#2.2 编写节点函数,访问上下文)
- [2.4 调用图并传入上下文](#2.4 调用图并传入上下文)
- [2.5 完整代码回顾](#2.5 完整代码回顾)
- [3、深入理解:运行时上下文 vs 状态 vs 存储](#3、深入理解:运行时上下文 vs 状态 vs 存储)
- 4、进阶用法与常见场景
-
- [4.1 在条件边中使用上下文](#4.1 在条件边中使用上下文)
- [4.2 在子图中传递上下文](#4.2 在子图中传递上下文)
- [4.3 与异步流式调用结合](#4.3 与异步流式调用结合)
- [4.4 使用 TypedDict 代替 dataclass](#4.4 使用 TypedDict 代替 dataclass)
- 5、最佳实践与注意事项
-
- [5.1 最佳实践](#5.1 最佳实践)
- [5.2 常见误区](#5.2 常见误区)
- [5.3 调试技巧](#5.3 调试技巧)
- 6、总结

前言
在 LangGraph 中构建多租户 AI 应用时,我们经常需要传递一些环境信息------ 比如当前用户的 ID、语言偏好、模型提供商等这些信息不属于对话消息,也不是需要持久化的状态,但直接影响工作流的行为,LangGraph 提供了 运行时上下文(Runtime Context) 机制,让这类配置信息可以优雅地注入到图的每一个节点中
本文将通过一个完整的示例,带你彻底掌握运行时上下文的定义、使用和最佳实践
1、什么是运行时上下文
1.1 为什么需要运行时上下文
举个例子啊:
假设:我们正在开发一个支持中英文双语的智能客服系统
我们会遇到------>不同用户有不同的语言偏好
在没有运行时上下文的情况下,我们可能需要在每个节点中显式传递 language 参数,或者把它塞进 State 中
但这会带来几个问题:
- State 污染:语言偏好不是对话内容,也不是节点间需要累积的数据,放在 State 里显得多余
- 重复代码:每个节点都要从 State 中取出 language 并处理
- 测试困难:每次调用都需要手动构造包含 language 的初始 State
更好的做法是将这类静态配置信息与动态 State分离
LangGraph 的运行时上下文正是为此设计的
1.2 运行时上下文的本质
运行时上下文(Runtime Context)是一次 invoke调用中保持不变的外部配置数据。
它的生命周期仅限于单次调用,不参与图的持久化(不会被 checkpointer保存)
我们可以把它理解为给图传递的 "参数",类似于 HTTP 请求中的 Header
在 LangGraph 中,使用时需要三个步骤:
- 定义上下文的数据结构(通常用 @dataclass 或 TypedDict)
- 在构建 StateGraph 时指定 context_schema
- 在节点函数中通过 Runtime[Context] 参数访问
- 调用图时通过 context 参数传入具体值
下面我们用一个完整的、可运行的例子来演示
2、代码详解:一个多语言问候助手
2.1 定义上下文和状态
首先导入必要的模块,并定义静态上下文 Context和动态状态 State
python
from dataclasses import dataclass
from typing import TypedDict
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.constants import END, START
from langgraph.graph import StateGraph
from langgraph.runtime import Runtime
# 静态上下文:单次调用中不变的配置
@dataclass
class Context:
user_id: str # 用户标识(可用于日志、审计)
language: str = "En" # 语言偏好,默认英文
# 动态状态:会在节点间传递和演化
class State(TypedDict):
user_name: str # 用户名称(可能来自用户输入)
message: list[str] # 存储生成的消息
为什么要用 @dataclass?
dataclass 提供了默认值、类型提示和简洁的语法,非常适合定义上下文
我们也可以使用 TypedDict,但 dataclass 支持默认值,更灵活(切记两个)
2.2 编写节点函数,访问上下文
节点函数需要接收 state: State 和 runtime: Runtime[Context] 两个参数
Runtime 是一个泛型类,其 .context 属性就是我们在调用时传入的上下文对象
python
def node(state: State, runtime: Runtime[Context]):
# 静态运行时上下文:语言偏好
if runtime.context.language == "En":
greeting = "hello"
else:
greeting = "你好"
# 动态运行时上下文:从 State 中获取用户名(或默认值)
user_name = state.get("user_name", "Guest")
return {
"message": [f"{greeting}, {user_name}"]
}
注意:节点只能读取 runtime.context,不能修改它。如果需要修改,应该通过 State 传递。
### 2.3 构建并编译图
在构建 StateGraph 时,通过 context_schema 参数绑定上下文类型。
python
运行
builder = StateGraph(State, context_schema=Context)
builder.add_node(node)
builder.add_edge(START, "node")
builder.add_edge("node", END)
graph = builder.compile()
尽管本例没有使用持久化,但 compile() 不加 checkpointer 也可以正常运行
我们不需要持久化上下文,因为它每次调用都是新的
2.4 调用图并传入上下文
python
result = graph.invoke(
{"user_name": "张三"},
context={"user_id": "123", "language": "中文"}
)
print(result) # 输出: {'user_name': '张三', 'message': ['你好, 张三']}
注意 context 参数接受一个字典,其键必须与 Context 的字段匹配
这里我们传入 "language": "中文",节点中的判断条件 runtime.context.language == "En" 为假,因此输出 "你好"
2.5 完整代码回顾
python
from dataclasses import dataclass
from typing import TypedDict
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.constants import END, START
from langgraph.graph import StateGraph
from langgraph.runtime import Runtime
@dataclass
class Context:
user_id: str
language: str = "En"
class State(TypedDict):
user_name: str
message: list[str]
def node(state: State, runtime: Runtime[Context]):
if runtime.context.language == "En":
greeting = "hello"
else:
greeting = "你好"
user_name = state.get("user_name", "Guest")
return {"message": [f"{greeting}, {user_name}"]}
builder = StateGraph(State, context_schema=Context)
builder.add_node(node)
builder.add_edge(START, "node")
builder.add_edge("node", END)
graph = builder.compile()
result = graph.invoke(
{"user_name": "张三"},
context={"user_id": "123", "language": "中文"}
)
print(result) # {'user_name': '张三', 'message': ['你好, 张三']}
3、深入理解:运行时上下文 vs 状态 vs 存储
在 LangGraph 中,有三个容易混淆的概念
我们可以用一张表彻底区分它们:
| 概念 | 可变性 | 生命周期 | 持久化 | 典型用途 |
|---|---|---|---|---|
| 运行时上下文 | 只读(单次调用内不变) | 单次 invoke/stream | 否(每次调用独立) | 用户 ID、语言、租户配置、模型选择 |
| 图状态(State) | 可读写(节点间可更新) | 同一 thread_id 下跨节点 | ✅ 由 checkpointer 保存 | 对话消息、累积结果、中间变量 |
| 跨会话存储(Store) | 可读写 | 跨 thread_id 永久 | ✅ 由 Store 持久化 | 用户偏好、历史预定、长期记忆 |
关键结论:
- 如果你需要数据在一次调用中保持恒定,用运行时上下文
- 如果你需要数据在多轮对话中持续演化,用 State
- 如果你需要数据在不同对话之间共享(比如用户长期偏好),用 Store
4、进阶用法与常见场景
4.1 在条件边中使用上下文
上下文不仅在节点中可用,也可以在条件边函数中访问。例如,根据上下文中的 model_provider 决定走哪个分支:
python
def select_model_branch(state: State, runtime: Runtime[Context]) -> str:
if runtime.context.model_provider == "openai":
return "openai_node"
else:
return "anthropic_node"
builder.add_conditional_edges("router", select_model_branch)
4.2 在子图中传递上下文
当你将子图作为主图的一个节点时,上下文会自动传播给子图
(前提是子图的 context_schema 兼容)
LangGraph 会在调用子图时自动合并父图的上下文
python
# 子图定义
sub_builder = StateGraph(SubState, context_schema=SubContext)
...
sub_graph = sub_builder.compile()
# 主图
main_builder = StateGraph(MainState, context_schema=MainContext)
main_builder.add_node("sub", sub_graph) # 上下文会自动传递
4.3 与异步流式调用结合
在 astream 或 astream_events 中,同样支持 context 参数:
python
async for chunk in graph.astream(inputs, context=my_context)
上下文会贯穿整个异步执行过程
4.4 使用 TypedDict 代替 dataclass
如果你更偏爱 TypedDict,也可以:
python
from typing import TypedDict
class Context(TypedDict):
user_id: str
language: str
然后在调用时传入字典即可
但 TypedDict 不支持默认值,需要手动处理缺失字段
5、最佳实践与注意事项
5.1 最佳实践
将身份、配置类信息放入上下文,而不是 State
例如 user_id、tenant_id、model_name、language
为上下文字段提供合理的默认值,避免节点中出现大量的空值检查
在大型项目中,为不同的子图定义不同的上下文类型,保持职责清晰
在日志和调试信息中打印 runtime.context,便于追踪请求
5.2 常见误区
- 在节点中修改
runtime.context并期待它影响后续节点
上下文是不可变的,应该在 State 中存储可变数据 - 忘记指定 context_schema,导致节点中 runtime.context 为 None 并报错
- 在上下文中放入不可序列化的对象(如数据库连接、文件句柄)
5.3 调试技巧
使用 print(runtime.context) 我们可以在节点中快速查看当前上下文的值
6、总结
本文通过一个简单的多语言问候示例,全面介绍了 LangGraph 运行时上下文的概念、定义方式、节点内访问方法以及在图调用时的传递方式
我们还对比了上下文、状态和存储的区别,并给出了最佳实践
核心要点回顾:
- 运行时上下文用于传递单次调用中不变的静态配置
- 使用
@dataclass定义上下文模式,并在构建StateGraph时指定context_schem - 节点函数中通过
runtime: Runtime[Context]参数访问,调用时通过context=传入 - 上下文与
State、Store互补,各自解决不同层次的数据需求
掌握了运行时上下文,你的 LangGraph 工作流将更具模块化和可配置性!!!!
本次分享就到这里了,下一篇我们将在此基础上,学习如何在 工具(Tool) 内部访问上下文、State 和 Store,让工具也能感知运行时环境,拜拜~~
