运行时上下文Runtime context什么是运行上下文接下来我们继续讲解 LangGraph 的其他核心能力首先重点介绍运行时上下文。首先我们来理解上下文的基本定义。上下文指的是程序运行过程中能够被访问到的数据与环境相关信息。我们此前学习的state同样是贯穿程序运行全程、可随时读取的数据因此state也属于上下文的范畴。在 LangGraph 中上下文主要用来传递各类关键信息比如用户身份、系统配置参数后续学习数据库调用时数据库连接信息、各类密钥也都可以通过上下文进行传递和读取。除此之外对话状态、聊天历史记录这类数据同样能够存放在上下文当中。简单来说之前所学的state就是程序运行阶段可正常访问的一类上下文。上下文分类针对上下文我们可以依据两个维度进行分类。第一个维度是可变性也就是判断数据在程序运行过程中是否会发生改变。运行过程中内容保持不变的数据我们称之为静态上下文运行过程中内容会发生变动的数据则称之为动态上下文。第二个维度是生命周期。如果上下文仅在单次会话、单次程序运行中生效就叫做运行时上下文如果数据可以跨多次会话持久保存无论重复运行多少次程序、开启多少轮对话都能保留这类上下文就是跨会话上下文。上下文可按两个维度分类维度类型描述示例可变性静态上下文运行中不变的数据用户 ID、数据库连接动态上下文运行中会变化的数据对话记录、中间结果生命周期运行时上下文单次运行 / 线程有效当前请求的临时数据跨会话上下文多次会话持久化用户偏好、历史记录结合这两个分类维度我们结合实际场景进一步区分。像用户 ID、数据库连接信息在单次程序运行中不会发生变化属于静态上下文而聊天记录、程序运行产生的中间结果与状态存储在state当中内容会不断变化属于动态上下文。从生命周期角度来看当前请求产生的临时数据仅在单次运行内有效结束后数据便会销毁这就是典型的运行时上下文我们日常使用的state大多属于这类。如果开启多线程并重复执行工作流也可以通过线程级别的状态还原重新加载这类数据。而用户偏好设置、长期对话历史这类数据会被持久化保存不受单次会话限制就属于跨会话上下文。将可变性与生命周期两个维度相互组合LangGraph 中就形成了三类上下文其中两类是我们此前已经接触过的内容。 第一类是动态运行时上下文既具备动态可变的特性生命周期又仅限单次运行。我们之前讲到的图状态快照、state都属于这一类。状态快照中存放着state数据内容可以动态修改且只在单次运行、单一线程内生效完全符合动态运行时上下文的特征。第二类是动态跨会话上下文数据支持修改、具备动态属性同时生命周期可以跨越多次会话。我们之前学习的store存储模块就对应这类上下文我们可以对其中的数据执行查询、新增、修改等操作并且数据能够长期保存实现跨会话使用。以上两类内容大家已经有所了解而我们本篇重点学习的是第三类 ——静态运行时上下文。这类上下文有两个核心特点一是数据为静态程序运行全程不允许修改二是生命周期仅限单次运行、单一线程。适合存放在这里的数据都是单线程运行中固定不变的内容例如用户 ID、系统配置、接口密钥、数据库连接地址URI等。因此在 LangGraph 中包含三种上下文类型可变性生命周期访问方式静态运行时上下文静态单次运行 / 线程context参数传入动态运行时上下文动态单次运行图状态对象动态跨会话上下文动态跨会话存储Store这里我们结合过往知识点做回顾。此前使用跨会话持久化功能时我们需要依靠用户 ID 构建命名空间以此在store中完成数据的查询和存储。当时我们将用户 ID 放置在配置项里结合现在上下文的知识来看用户 ID 在单次调用中不会改变完全适合存入静态运行时上下文。总结来说我们之前学过的checkpoints检查点对应动态运行时上下文store存储对应动态跨会话上下文而今天全新学习、需要掌握用法的就是静态运行时上下文。场景练习理解完基础概念后我们通过一个实战场景练习进一步区分三类上下文的使用场景。假设我们要开发一款智能旅行规划助手该助手有五项核心需求根据用户母语给出对应语言的回答、依据会员等级提供差异化服务、连接旅游数据库查询数据、根据当下季节推荐游玩项目、记忆用户历史查询记录并给出精准建议。数据类型上下文类型为什么数据库连接静态运行时上下文每次查询都需要单次运行中不变用户语言偏好静态运行时上下文个性化回答单次运行中不变用户会员等级静态运行时上下文服务分级单次运行中不变当前季节静态运行时上下文推荐季节性活动单次运行中不变对话历史动态运行时上下文了解上下文单次运行中变化用户旅行偏好跨会话上下文长期记忆跨会话对应场景里的六类数据我们逐一分析归类数据库连接信息每次查询都需要使用且单次运行中不会改变归类为静态运行时上下文。用户语言偏好单次工作流启动后用户使用的语言就已确定运行中不会变更归类为静态运行时上下文。用户会员等级单次调用过程中用户身份固定等级信息不会改变归类为静态运行时上下文。当前季节单次推荐流程里季节是固定值不会中途更改归类为静态运行时上下文。历史对话信息对话过程中内容会持续新增、不断变化且仅在单次会话内生效归类为动态运行时上下文。用户旅行偏好需要长期记忆、跨多次会话使用归类为动态跨会话上下文。通过这个案例我们就能清晰区分静态与动态、运行时与跨会话这四类上下文的差异。掌握概念之后接下来我们讲解代码层面如何配置和使用静态运行时上下文。记住良好的上下文管理是构建复杂、可维护 AI 应用的关键正确的上下文设计能让我们的 AI 应用✅ 更高效避免重复传递不变数据✅ 更智能基于上下文提供个性化服务✅ 更可维护清晰的数据边界和职责分离✅ 更易扩展支持多用户、多场景、长期记忆配置运行时上下文定义上下文模式首先需要定义上下文的数据结构Python 中主要有两种常用方式一种是使用dataclass装饰器该特性在 Python3.7 及以上版本可用它能自动为类生成初始化、判等等基础方法简化类的编写另一种是定义TypedDict和我们平时定义state的方式保持一致。两种方式都可以明确定义上下文包含的字段与数据类型也是创建静态运行时上下文的标准写法。接下来结合代码示例演示在 LangGraph 图结构中使用静态运行时上下文。 我们知道常规的节点函数会接收state参数state对应动态运行时上下文内容支持修改。而想要使用静态运行时上下文需要给节点函数新增一个runtime参数该参数的类型为 LangGraph 提供的Runtime并关联我们自定义的上下文结构。实操步骤如下第一步通过TypedDict定义state里面存放允许动态修改的数据比如对话消息列表、用户名第二步通过dataclass定义静态上下文结构存放运行中固定不变的数据例如用户 ID、使用语言并可以为字段设置默认值。from typing import TypedDict, List, Optional from dataclasses import dataclass from langgraph.graph import StateGraph, add_messages from langgraph.runtime import Runtime # 注意实际导入路径可能根据版本不同 # 第一步定义动态运行时上下文State- 支持修改 class MyState(TypedDict): messages: List[str] # 对话消息列表使用 add_messages 可以实现累加 username: str # 用户名可在运行中修改 # 第二步定义静态运行时上下文 - 运行中固定不变 dataclass class MyStaticContext: user_id: str # 用户ID固定不变 language: str zh # 使用语言默认中文 # 可以添加更多固定配置 # region: str CN # subscription_level: str free在节点中访问上下文在节点函数中我们可以同时读取两类上下文通过runtime.context获取静态运行时上下文的数据比如读取用户使用的语言根据语言字段判断返回中文问候 “你好” 还是英文问候 “Hello”通过state获取动态运行时上下文的数据比如读取用户名若未设置则默认显示 “访客”。由于state支持修改我们最终返回新的数据就能更新状态内容这也印证了它动态可变的特性。这种分类存储的方式也更符合开发规范固定不变的配置、身份信息统一放入静态运行时上下文可变更的业务数据存入state代码逻辑会更加清晰可读性和可维护性也会大幅提升。节点函数可通过runtime参数访问上下文。# 节点函数同时接收 state动态和 runtime静态上下文 def greeting_node(state: MyState, runtime: Runtime[MyStaticContext]) - dict: 问候节点根据静态上下文的语言配置和动态上下文的用户名返回问候语 # 从 runtime 获取静态上下文数据 static_context runtime.context language static_context.language user_id static_context.user_id # 从 state 获取动态数据 username state.get(username, 访客) # 根据语言选择问候语 if language zh: greeting f你好{username}用户ID{user_id} elif language en: greeting fHello, {username}! User ID: {user_id} elif language ja: greeting fこんにちは、{username}さんユーザーID{user_id} else: greeting fHello, {username}! User ID: {user_id} # 返回新数据来更新 state动态运行时上下文支持修改 return { messages: [greeting], # 添加问候消息 username: username # 可以保持不变也可以修改 } def process_node(state: MyState, runtime: Runtime[MyStaticContext]) - dict: 处理节点根据静态配置和动态数据执行不同逻辑 static_context runtime.context # 可以根据静态配置决定行为 if static_context.language zh: # 中文用户走特定逻辑 return {messages: [处理中文用户请求]} else: # 其他语言用户走通用逻辑 return {messages: [Processing user request]}在图中使用上下文模式完成节点编写后下一步是构建图结构。在实例化StateGraph构建图时需要新增context_schema参数传入我们自定义的上下文类告诉当前图需要依赖该套静态运行时上下文。这里要区分两个配置阶段数据的静态 / 动态属性是在构建图StateGraph阶段定义的而生命周期单次运行 / 跨会话则是在图编译compile阶段通过checkpoint等配置来指定两种配置可以自由组合使用。创建图时传入context_schema参数。# 实例化 StateGraph传入 state 类型和静态上下文类型 graph_builder StateGraph( MyState, context_schemaMyStaticContext # 新增参数告诉图需要依赖静态运行时上下文 ) # 添加节点 graph_builder.add_node(greeting, greeting_node) graph_builder.add_node(process, process_node) # 添加边示例顺序执行 graph_builder.set_entry_point(greeting) graph_builder.add_edge(greeting, process) graph_builder.set_finish_point(process) # 编译图这里不启用 checkpoint所以是单次运行 # 注意生命周期单次运行/跨会话通过 compile 的参数配置 graph graph_builder.compile() # 默认单次运行不持久化状态运行图时传入上下文图的节点、边全部配置完成后调用compile方法完成编译之后就可以执行图逻辑。调用graph.invoke()方法时需要传入两部分内容第一部分是state的初始数据也就是动态状态的初始化内容第二部分是context字典传入静态运行时上下文的对应字段比如用户 ID、使用语言。# 调用 invoke 方法需要传入两部分内容 # 第一部分state 的初始数据动态状态初始化 initial_state { messages: [], # 初始为空列表 username: 张三 # 用户名 } # 第二部分context 字典静态运行时上下文 static_context_data { user_id: USER_12345, language: zh # 中文问候 } # 执行图 result graph.invoke( initial_state, contextstatic_context_data # 传入静态上下文 ) # 查看结果 print(执行结果) print(f消息列表{result[messages]}) print(f用户名{result[username]}) # 示例输出 # 执行结果 # 消息列表[你好张三用户IDUSER_12345, 处理中文用户请求] # 用户名张三运行代码后可以看到节点能够正常读取context中的语言配置自动匹配对应的问候语这就证明静态运行时上下文可以正常读取和使用。不同场景的调用示例# 场景1英文用户不传初始 state使用默认空值 en_result graph.invoke( {}, # state 初始数据为空 context{user_id: USER_67890, language: en} ) print(f英文用户{en_result[messages]}) # 输出英文用户[Hello, 访客! User ID: USER_67890, Processing user request] # 场景2日文用户 ja_result graph.invoke( {username: 田中, messages: []}, context{user_id: USER_111, language: ja} ) print(f日文用户{ja_result[messages]}) # 输出日文用户[こんにちは、田中さんユーザーIDUSER_111, Processing user request] # 场景3同一个静态上下文可以用于多个不同 state common_context {user_id: USER_999, language: zh} user1_result graph.invoke({username: 李四, messages: []}, contextcommon_context) user2_result graph.invoke({username: 王五, messages: []}, contextcommon_context)带 Checkpoint 的跨会话场景from langgraph.checkpoint import MemorySaver # 编译时启用 checkpoint实现跨会话持久化 checkpointer MemorySaver() graph_with_persistence graph_builder.compile(checkpointercheckpointer) # 第一次调用传入配置ID状态会被保存 config {configurable: {thread_id: user_session_001}} result1 graph_with_persistence.invoke( {username: 赵六, messages: []}, context{user_id: USER_001, language: zh}, configconfig ) # 第二次调用使用相同的 thread_id可以获取之前的状态 result2 graph_with_persistence.invoke( {}, # 可以不传初始 state会从 checkpoint 恢复 context{user_id: USER_001, language: zh}, configconfig ) # 注意静态上下文每次都需要重新传入因为它不会保存在 checkpoint 中最后补充两个使用要点第一调用时state的初始数据可以不传不传则默认全部为空后续运行中可自由修改第二静态运行时上下文必须每次调用都主动传入因为它不支持运行中修改系统无法自动维护只有每次调用时传递完整数据节点才能正常读取使用。【完整示例】import operator from typing import TypedDict, List, Optional, Annotated from dataclasses import dataclass from langgraph.graph import StateGraph, add_messages from langgraph.runtime import Runtime # 注意实际导入路径可能根据版本不同 # 第一步定义动态运行时上下文State- 支持修改 class MyState(TypedDict): messages: Annotated[List[str], operator.add] username: str # 用户名可在运行中修改 # 第二步定义静态运行时上下文 - 运行中固定不变 dataclass class MyStaticContext: user_id: str # 用户ID固定不变 language: str zh # 使用语言默认中文 # 可以添加更多固定配置 # region: str CN # subscription_level: str free # 节点函数同时接收 state动态和 runtime静态上下文 def greeting_node(state: MyState, runtime: Runtime[MyStaticContext]) - dict: 问候节点根据静态上下文的语言配置和动态上下文的用户名返回问候语 # 从 runtime 获取静态上下文数据 static_context runtime.context language static_context.language user_id static_context.user_id # 从 state 获取动态数据 username state.get(username, 访客) # 根据语言选择问候语 if language zh: greeting f你好{username}用户ID{user_id} elif language en: greeting fHello, {username}! User ID: {user_id} elif language ja: greeting fこんにちは、{username}さんユーザーID{user_id} else: greeting fHello, {username}! User ID: {user_id} # 返回新数据来更新 state动态运行时上下文支持修改 return { messages: [greeting], # 添加问候消息 username: username # 可以保持不变也可以修改 } def process_node(state: MyState, runtime: Runtime[MyStaticContext]) - dict: 处理节点根据静态配置和动态数据执行不同逻辑 static_context runtime.context # 可以根据静态配置决定行为 if static_context.language zh: # 中文用户走特定逻辑 return {messages: [处理中文用户请求]} else: # 其他语言用户走通用逻辑 return {messages: [Processing user request]} # 实例化 StateGraph传入 state 类型和静态上下文类型 graph_builder StateGraph( MyState, context_schemaMyStaticContext # 新增参数告诉图需要依赖静态运行时上下文 ) # 添加节点 graph_builder.add_node(greeting, greeting_node) graph_builder.add_node(process, process_node) # 添加边示例顺序执行 graph_builder.set_entry_point(greeting) graph_builder.add_edge(greeting, process) graph_builder.set_finish_point(process) # 编译图这里不启用 checkpoint所以是单次运行 # 注意生命周期单次运行/跨会话通过 compile 的参数配置 graph graph_builder.compile() # 默认单次运行不持久化状态 # 调用 invoke 方法需要传入两部分内容 # # 第一部分state 的初始数据动态状态初始化 # initial_state { # messages: [], # 初始为空列表 # username: 张三 # 用户名 # } # # # 第二部分context 字典静态运行时上下文 # static_context_data { # user_id: USER_12345, # language: ja # 中文问候 # } # # # 执行图 # result graph.invoke( # initial_state, # contextstatic_context_data # 传入静态上下文 # ) # # # 查看结果 # print(执行结果) # print(f消息列表{result[messages]}) # print(f用户名{result[username]}) # 场景1英文用户不传初始 state使用默认空值 en_result graph.invoke( {}, # state 初始数据为空 context{user_id: USER_67890, language: en} ) print(f英文用户{en_result[messages]}) # 输出英文用户[Hello, 访客! User ID: USER_67890, Processing user request] # 场景2日文用户 ja_result graph.invoke( {username: 田中, messages: []}, context{user_id: USER_111, language: ja} ) print(f日文用户{ja_result[messages]}) # 输出日文用户[こんにちは、田中さんユーザーIDUSER_111, Processing user request] # 场景3同一个静态上下文可以用于多个不同 state common_context {user_id: USER_999, language: zh} user1_result graph.invoke({username: 李四, messages: []}, contextcommon_context) user2_result graph.invoke({username: 王五, messages: []}, contextcommon_context)以上就是 LangGraph 中运行时上下文的完整概念、分类、场景应用以及静态运行时上下文的代码配置、节点访问、调用执行的全部用法。在工具中访问上下文除了在节点中使用上下文以外工具同样也支持使用上下文不过工具里的使用方式和节点存在区别下面我们详细介绍具体用法。先做一个名称约定方便后续讲解和理解后续我们将静态运行时上下文统一称作上下文context把动态运行时上下文依旧沿用之前的叫法称作状态state。大家需要分清两个概念的底层含义这能帮助我们更好地区分和使用。工具是调用外部系统、API、数据库交互或执行计算的功能。因此对于用户身份、配置参数、数据库连接、API 密钥等这类调用 API 的参数信息和配置信息则需要传递给工具。上下文对工具的重要性个性化响应根据用户上下文提供定制化回答权限控制基于用户身份限制工具访问状态感知工具可以根据当前状态决定行为依赖注入避免硬编码配置提高可测试性基本用法现在我们新建案例来演示工具中上下文的使用。首先完成基础结构的定义第一步先定义状态。我们创建消息状态结构内部设置消息列表字段同时新增user_name字段该字段内容在运行过程中允许发生变动默认设置为空字符串。from typing import List, Annotated from typing_extensions import TypedDict from langgraph.graph.message import add_messages # 创建消息状态结构 class MyState(TypedDict): messages: Annotated[List, add_messages] # 消息列表字段 user_name: str # 用户名运行过程中允许变动默认设置为空字符串接着定义上下文结构这里使用数据类来实现上下文当中仅保留user_id这一个字段即可用来存放单次运行中固定不变的用户标识信息。from dataclasses import dataclass # 使用数据类实现上下文结构 dataclass class MyContext: user_id: str # 单次运行中固定不变的用户标识信息基础结构定义完成后开始编写业务逻辑。首先创建大模型相关节点这个节点的作用是让大语言模型绑定工具并判断是否需要调用工具。该节点仅接收state状态参数不在节点内部读取上下文我们把读取上下文和状态的逻辑全部放在工具中实现。在编写节点之前需要先初始化大模型并绑定工具。我们选用对应的模型同时提前定义好待绑定的工具。这里我们设计一个search搜索工具模拟天气查询的业务场景。先为工具添加标准注解初步搭建工具框架。这个工具无需额外入参核心逻辑是模拟接口调用直接返回模拟结果例如查询到天气为晴天气温 15 至 20 度。定义一个带有运行时信息的工具如下所示from langchain.tools import tool, ToolRuntime # 设计一个search搜索工具模拟天气查询业务场景 tool def search_weather(runtime: ToolRuntime[MyContext, MyState]) - str: 查询天气信息。 当用户询问某个地方的天气时调用此工具获取天气数据。 返回内容包括天气状况和温度范围。 # 核心逻辑模拟接口调用直接返回模拟结果 return 查询到天气晴天气温 15 至 20 度这里重点来看工具如何读取上下文与状态。想要在工具中使用相关数据需要新增ToolRuntime类型的参数这也是工具和节点的核心区别节点使用Runtime工具则使用ToolRuntime。借助这个参数我们既可以读取静态上下文也能读取动态状态。函数类型签名为什么这样设计节点fn(state, runtime)state是节点的核心输入输出设计为显式参数更清晰工具fn(args, runtime)工具的参数由 LLM 决定如city: strstate不是工具的自然输入所以放到runtime.state里作为额外上下文工具可以通过ToolRuntime参数访问运行时信息。这个参数为工具提供包括State图状态数据Context静态上下文Store持久化存储等使用ToolRuntime时只需在工具签名中添加runtime: ToolRuntime它会自动注入。调用时无需手动传输。通过runtime.context.user_id就能获取上下文里的用户 ID通过runtime.state可以拿到全局状态进而读取到状态中的user_name。本质上来说context和state都属于广义的上下文范畴前者对应静态数据后者对应动态数据只是在 LangGraph 中我们习惯将动态数据单独称作状态。为了直观验证取值效果我们在工具内添加日志打印语句记录当前发起查询的用户 ID 和用户名模拟线上环境的日志记录功能。至此工具中读取上下文、读取状态的代码就编写完成了。from langchain.tools import tool, ToolRuntime tool def search_weather(runtime: ToolRuntime[MyContext, MyState]) - str: 查询天气信息。 当用户询问某个地方的天气时调用此工具获取天气数据。 返回内容包括天气状况和温度范围。 # 通过 runtime.context.user_id 获取上下文里的用户 ID user_id runtime.context.user_id # 通过 runtime.state 拿到全局状态读取 user_name user_name runtime.state.get(user_name, 匿名用户) # 添加日志打印验证取值效果 print(f【工具日志】用户ID: {user_id}, 用户名: {user_name}) return 查询到天气晴天气温 15 至 20 度完成工具开发后继续编写大模型节点的逻辑。该节点会调用初始化好的大模型先设置系统提示词告知模型支持调用工具查询天气再拼接历史消息一并传入模型。模型执行后会返回 AI 消息我们将这条消息追加到消息列表中完成节点逻辑编写。from langchain_openai import ChatOpenAI from langchain_core.messages import SystemMessage, HumanMessage, AIMessage def llm_node(state: MyState) - dict: 大模型节点判断是否需要调用工具 # 初始化大模型并绑定工具 model ChatOpenAI(modelgpt-3.5-turbo, temperature0) model_with_tools model.bind_tools([search_weather]) # 设置系统提示词 system_prompt SystemMessage( content你是一个助手支持调用工具查询天气。 ) # 拼接历史消息 messages [system_prompt] state.get(messages, []) # 调用大模型 response model_with_tools.invoke(messages) # 将AI消息追加到消息列表中 return {messages: [response]}结构、工具、节点全部就绪后开始构建并编译图。实例化StateGraph时除了传入定义好的状态还必须指定context_schema绑定自定义上下文结构这是使用静态上下文的必要配置。随后依次向图中添加两个节点第一个是刚刚编写的大模型节点用于判断是否调用工具第二个是工具节点需要使用框架提供的ToolNode来封装我们定义的search工具这个节点的作用是真正执行工具逻辑。两个节点分工明确大模型节点负责决策工具节点负责执行。接下来配置节点之间的连线。首先设置起始节点指向大模型节点。由于流程存在分支这里需要配置条件边模型返回的消息中如果包含工具调用指令就跳转到工具节点执行查询如果没有工具调用指令就直接结束流程。我们可以直接使用 LangGraph 内置的tools_condition方法实现该判断逻辑这个方法会自动检测消息内的工具调用标识并根据结果分发流程。我们做好路径映射判定需要调用工具时流程走向工具节点无需调用工具则直接终止。工具执行完成后还需要配置一条普通连线让工具节点重新回到大模型节点。原因在于工具执行后会产生新的工具消息此时需要把历史所有消息用户提问、模型调用指令、工具返回结果再次交给大模型由模型整合全部信息生成最终回复给用户的内容。连线配置完毕直接编译图本次编译暂不配置检查点相关功能。from langgraph.graph import StateGraph, END, START from langgraph.prebuilt import ToolNode, tools_condition # 实例化StateGraph传入状态并指定context_schema绑定上下文结构 graph_builder StateGraph( MyState, context_schemaMyContext # 必须指定这是使用静态上下文的必要配置 ) # 添加两个节点 graph_builder.add_node(llm, llm_node) # 大模型节点决策 graph_builder.add_node(tools, ToolNode([search_weather])) # 工具节点执行 # 设置起始节点指向大模型节点 graph_builder.add_edge(START, llm) # 配置条件边使用内置tools_condition判断是否需要调用工具 graph_builder.add_conditional_edges( llm, tools_condition, { tools: tools, # 需要调用工具 → 跳转到工具节点 END: END # 无需调用工具 → 直接结束 } ) # 配置普通连线工具执行完成后回到大模型节点 graph_builder.add_edge(tools, llm) # 编译图暂不配置检查点 graph graph_builder.compile()图编译完成后采用流式调用的方式执行图逻辑。from langchain_core.messages import HumanMessage # 准备输入参数 # 第一组状态的初始化数据 initial_state { messages: [HumanMessage(content今天西安天气如何)], user_name: 小明 } # 第二组上下文数据 static_context { user_id: USER_12345 } # 流式调用执行 for event in graph.stream(initial_state, contextstatic_context): for node_name, state_update in event.items(): print(f节点: {node_name}) if messages in state_update: print(f消息: {state_update[messages][-1].content})节点: llm 消息: 好的我来查询一下西安的天气信息。 【工具日志】用户ID: USER_12345, 用户名: 小明 节点: tools 消息: 查询到天气晴天气温 15 至 20 度 节点: llm 消息: 今天西安的天气情况如下 - **天气状况**☀️ 晴天 - **温度范围**15°C ~ 20°C 天气不错适合外出活动不过早晚温差较大建议带件外套哦 进程已结束退出代码为 0流式调用会逐个输出每个节点的执行结果能清晰看到整个流程的运转过程。调用时需要传入两组参数第一组是状态的初始化数据在消息列表中传入用户提问 “今天西安天气如何”同时设置user_name为 “小明”第二组是上下文数据以字典形式传入user_id。在结果接收环节遍历流式返回的数据。流式数据会以节点名称、状态更新内容的元组形式返回我们依次打印节点名称和最新的消息内容查看每一步的执行结果。运行代码后可以清晰看到完整的执行流程首先执行大模型节点模型生成带有工具调用指令的消息触发工具调用接着执行工具节点日志成功打印出我们传入的user_id和user_name证明工具正常读取到了上下文与状态同时返回模拟的天气查询结果最后流程回到大模型节点模型整合所有信息输出最终的自然语言回答。通过这个案例就能明确在工具中借助ToolRuntime参数既可以读取静态的上下文数据也能读取动态的状态数据。【完整示例】构建一个支持搜索的 AI 系统假设调用搜索 API 需要用户数据作为参数则需要向工具中传入相关信息。关键步骤如下定义状态、上下文结构定义工具节点ToolNode、定义 LLM 节点构建并编译图需加入状态和上下文参数执行并验证结果from typing import List, Annotated from langchain_core.messages import SystemMessage, HumanMessage from typing_extensions import TypedDict from langgraph.graph.message import add_messages from dataclasses import dataclass from langchain.tools import tool, ToolRuntime from langgraph.graph import StateGraph, END, START from langgraph.prebuilt import ToolNode, tools_condition import os from dotenv import load_dotenv from langchain_openai import ChatOpenAI load_dotenv() # 设置 DeepSeek API Key os.environ[DEEPSEEK_API_KEY] sk-............ # 创建消息状态结构 class MyState(TypedDict): messages: Annotated[List, add_messages] # 消息列表字段 user_name: str # 用户名运行过程中允许变动默认设置为空字符串 # 使用数据类实现上下文结构 dataclass class MyContext: user_id: str # 单次运行中固定不变的用户标识信息 tool def search_weather(runtime: ToolRuntime[MyContext, MyState]) - str: 查询天气信息。 当用户询问某个地方的天气时调用此工具获取天气数据。 返回内容包括天气状况和温度范围。 # 通过 runtime.context.user_id 获取上下文里的用户 ID user_id runtime.context.user_id # 通过 runtime.state 拿到全局状态读取 user_name user_name runtime.state.get(user_name, 匿名用户) # 添加日志打印验证取值效果 print(f【工具日志】用户ID: {user_id}, 用户名: {user_name}) return 查询到天气晴天气温 15 至 20 度 def llm_node(state: MyState) - dict: 大模型节点判断是否需要调用工具 # 初始化大模型并绑定工具 # 使用 OpenAI 兼容方式调用 DeepSeek model ChatOpenAI( modeldeepseek-chat, # 或 deepseek-reasoner temperature0, openai_api_keyos.environ[DEEPSEEK_API_KEY], base_urlhttps://api.deepseek.com/v1 ) model_with_tools model.bind_tools([search_weather]) # 设置系统提示词 system_prompt SystemMessage( content你是一个助手支持调用工具查询天气。 ) # 拼接历史消息 messages [system_prompt] state.get(messages, []) # 调用大模型 response model_with_tools.invoke(messages) # 将AI消息追加到消息列表中 return {messages: [response]} # 实例化StateGraph传入状态并指定context_schema绑定上下文结构 graph_builder StateGraph( MyState, context_schemaMyContext # 必须指定这是使用静态上下文的必要配置 ) # 添加两个节点 graph_builder.add_node(llm, llm_node) # 大模型节点决策 graph_builder.add_node(tools, ToolNode([search_weather])) # 工具节点执行 # 设置起始节点指向大模型节点 graph_builder.add_edge(START, llm) # 配置条件边使用内置tools_condition判断是否需要调用工具 graph_builder.add_conditional_edges( llm, tools_condition, { tools: tools, # 需要调用工具 → 跳转到工具节点 END: END # 无需调用工具 → 直接结束 } ) # 配置普通连线工具执行完成后回到大模型节点 graph_builder.add_edge(tools, llm) # 编译图暂不配置检查点 graph graph_builder.compile() # 准备输入参数 # 第一组状态的初始化数据 initial_state { messages: [HumanMessage(content今天西安天气如何)], user_name: 小明 } # 第二组上下文数据 static_context { user_id: USER_12345 } # 流式调用执行 for event in graph.stream(initial_state, contextstatic_context): for node_name, state_update in event.items(): print(f节点: {node_name}) if messages in state_update: print(f消息: {state_update[messages][-1].content})总结一下上下文的整体使用方式我们可以单独定义类来存放静态上下文数据这类数据会在整个工作流的运行过程中全局可访问。不管是图中的普通节点还是自定义工具都能够按照对应语法读取上下文和状态。以上就是 LangGraph 中上下文完整的使用方法。