LangGraph 的 Runtime 机制,说穿了,就是帮开发者把“脏活累活”分开管理。在构建 AI Agent 时,节点的执行环境往往比我们想象的要复杂得多。
从宏观架构来看,Runtime 的引入并非偶然。它解决的是一个非常实际的问题:State(状态)越来越臃肿,运行依赖越来越混乱。
1. Runtime 要解决什么问题
先来看一个典型的场景。一个节点在执行时,通常需要两类信息:
- 业务数据:比如用户输入、历史对话记录、检索到的文档片段。
- 运行信息:比如用户的唯一标识(
user_id)、当前会话的线程 ID(thread_id)、数据库连接对象、外部存储的访问句柄。
这两类信息的性质完全不同。如果一股脑儿全塞进 State,后果是显而易见的:State 变得臃肿不堪,而且像数据库连接这种资源对象,本身就是不可序列化的,强行存到检查点(checkpoint)里只会引发各种诡异错误。
Runtime 的核心价值就在这里——它把那些“运行依赖”从 State 中剥离出来,让节点既能拿到完整的环境信息,又不污染业务数据的纯净性。
2. Runtime 和 State 有什么区别
两者的职责分工非常清晰:
State负责“图在处理什么”——它是业务数据的载体。Runtime负责“节点依赖什么来完成处理”——它是运行环境的注入者。
换句话说,State 管的是数据的流向,Runtime 管的是执行的上下文。这样设计之后,整个图的状态模型变得清晰、可维护,节点之间的耦合自然就降低了。
3. LangGraph 中 Runtime 是如何工作的
在 LangGraph 里,当你调用 invoke() 方法时,框架会在内部根据你传入的 config 和 context,自动构建出 Runtime 对象。这个过程中,有三个角色是绕不开的:
State:存储输入、输出和中间的业务结果。Context:承载需要注入到 Runtime 中的用户级或服务级上下文,比如user_id、数据库连接器。RunnableConfig:承载本次调用的配置参数,最典型的就是thread_id。
节点在执行时,可以同时从这三类来源读取信息,真正做到“各司其职”。
4. 示例:节点如何访问 Runtime
下面的代码演示了一个最基础的用法:节点不仅读取了 state 中的业务输入,还通过 config 获取了 thread_id,并通过 runtime.context 拿到了 user_id 和数据库名称。
1 from dataclasses import dataclass
2 from langchain_core.runnables import RunnableConfig
3 from langgraph.graph import START, StateGraph
4 from langgraph.runtime import Runtime
5 from typing_extensions import TypedDict
6
7 class State(TypedDict):
8 input: str
9 results: str
10
11 class DBConnector:
12 def __init__(self, db_name: str = None):
13 self.db_name = db_name
14
15 @dataclass
16 class Context:
17 user_id: str = "default"
18 db_connector: DBConnector = None
19
20 def my_node(state: State, config: RunnableConfig, runtime: Runtime):
21 thread_id = config["configurable"]["thread_id"]
22 user_id = runtime.context.user_id
23 db_name = runtime.context.db_connector.db_name
24 return {
25 "results": (
26 f"Hello, {state['input']}, I am {user_id} in thread {thread_id}, "
27 f"db_name = {db_name} !")}
28
29 if __name__ == "__main__":
30 builder = StateGraph(State, context_schema=Context)
31 builder.add_node("my_node", my_node)
32 builder.add_edge(START, "my_node")
33 graph = builder.compile()
34 config = RunnableConfig(
35 configurable={"thread_id": "fd1bd9f0-5d3c-487a-938e-b14513efa48k"})
36 context = Context(
37 user_id="demo_user",
38 db_connector=DBConnector(db_name="my_database.db"))
39 result = graph.invoke({"input": "Everyone"}, config=config, context=context)
40 print(result)
# {'input': 'Everyone', 'results': 'Hello, Everyone, I am demo_user in thread fd1bd9f0-5d3c-487a-938e-b14513efa48k, db_name = my_database.db !'}
这段代码清晰地展示了三个层面的分工:
state只关注业务的输入和输出。config负责传递线程级别的配置参数。runtime.context承载用户信息和外部资源的访问句柄。
最终的效果是:节点能够拿到完整的运行环境,但 State 依然保持干净、可序列化。
5. 实际工程里 Runtime 常放什么
在真实项目中,Runtime 通常会承载以下几类内容:
thread_id:用于实现短期记忆与会话隔离。user_id:用于实现长期记忆的用户级数据隔离。Store对象、数据库连接:用于访问长短期记忆或外部系统。RunnableConfig中的其他扩展参数:用于传递本次调用的控制配置,比如模型选择、温度参数等。
举个例子,在构建一个聊天助手时,PostgresSa ver 通常用于短期检查点的保存,PostgresStore 则用于长期记忆的存取。这些资源对象通过 Runtime 注入节点,远比硬编码在 State 中要优雅得多。
6. 小结
Runtime 的本质,就是 LangGraph 为“运行环境管理”提供的一层正式抽象。它把业务状态和系统资源清晰地区分开来,让 State 保持纯净,让节点能够精准地访问用户上下文、线程配置和外部存储对象。
可以这样理解:Runtime 是 LangGraph 这座大厦里连接业务逻辑与底层资源的关键桥梁,理解了它,你才能真正掌握 Agent 上下文管理的最佳实践。

