【LangGraph】LangGraph 工具中访问运行时上下文——ToolRuntime

【LangGraph】新篇章:LangGraph 工具中访问运行时上下文------ToolRuntime

  • 前言
    • 1、为什么工具需要运行时上下文?
      • [1.1 工具的传统痛点](#1.1 工具的传统痛点)
      • [1.2 ToolRuntime 提供的资源](#1.2 ToolRuntime 提供的资源)
    • 2、代码详解:天气查询助手工具
      • [2.1 定义状态和上下文](#2.1 定义状态和上下文)
      • [2.2 定义工具,使用 ToolRuntime](#2.2 定义工具,使用 ToolRuntime)
      • [2.3 绑定工具到模型](#2.3 绑定工具到模型)
      • [2.4 编写 LLM 节点](#2.4 编写 LLM 节点)
      • [2.5 构建图并添加工具节点](#2.5 构建图并添加工具节点)
      • [2.6 流式调用并传入上下文和状态](#2.6 流式调用并传入上下文和状态)
    • [三、深入 ToolRuntime:访问 Store 和流式输出](#三、深入 ToolRuntime:访问 Store 和流式输出)
      • [3.1 访问跨会话存储(Store)](#3.1 访问跨会话存储(Store))
      • [3.2 从工具发送流式自定义数据](#3.2 从工具发送流式自定义数据)
      • [3.3 使用 InjectedStore 和 InjectedState](#3.3 使用 InjectedStore 和 InjectedState)
    • 四、常见问题与注意事项
      • [4.1 为什么我的工具收不到 runtime?](#4.1 为什么我的工具收不到 runtime?)
      • [4.2 工具中修改 runtime.state 会生效吗?](#4.2 工具中修改 runtime.state 会生效吗?)
      • [4.3 如何调试工具中的 runtime?](#4.3 如何调试工具中的 runtime?)
      • [4.4 性能注意事项](#4.4 性能注意事项)
    • 五、总结
    • 六、内容扩展

上一篇------> 【LangGraph】运行时上下文(Runtime Context)

前言

在第一篇文章中,我们学习了如何在图节点中通过 Runtime[Context] 访问静态上下文

但在真实应用中,大量逻辑封装在工具(Tool)内部------例如数据库查询、API 调用、计算等

这些工具同样需要知道当前用户是谁、使用哪种语言,甚至需要读取图的状态和长期存储 LangGraph 提供了 ToolRuntime 对象,让工具可以无缝访问这些运行时资源 本文通过一个完整的天气查询助手案例,带你掌握 ToolRuntime 的所有用法

1、为什么工具需要运行时上下文?

1.1 工具的传统痛点

考虑一个天气查询工具

在没有 ToolRuntime 的情况下,我们通常通过工具的参数来传递用户信息:

python 复制代码
def search_weather(user_id: str, city: str) -> str:
    # 需要外部传入 user_id

这要求调用工具的上层逻辑(通常是 LLM 节点)必须负责从 State 或上下文中提取 user_id 并填入参数

当工具很多时,这种传参方式非常繁琐且容易出错

更麻烦的是,工具可能还需要:

  • 记录是谁调用了它(用于审计)

  • 读取用户的历史偏好(从 Store 中获取预算范围)

  • 发出流式进度事件

  • 根据当前 State 中已经收集的信息调整行为

ToolRuntime 将这些能力统一注入到工具中,让工具变得自包含且无侵入


1.2 ToolRuntime 提供的资源

ToolRuntime 是一个包装类,在工具被调用时由 LangGraph 自动注入

它包含以下属性:

属性 类型 说明
runtime.context Context 类型(自定义) 静态运行时上下文
runtime.state dict 当前图状态的只读副本
runtime.store BaseStore 跨会话持久化存储(编译传入 store 才可用)
get_stream_writer() 函数(langgraph.config 导入) 发送自定义流式事件

工具只需要声明一个 runtime: ToolRuntime 参数,LangGraph 会在执行工具时自动填充


2、代码详解:天气查询助手工具

下面我们构建一个完整的 LangGraph 应用,其中包含一个能够访问上下文和状态的天气查询工具。

2.1 定义状态和上下文

python 复制代码
from dataclasses import dataclass
from langgraph.graph import MessagesState
from langgraph.prebuilt import ToolRuntime

class State(MessagesState):
    user_name: str = ""   # 动态状态:用户名称,可从对话中提取

@dataclass
class Context:
    user_id: str          # 静态上下文:用户标识

MessagesState 是 LangGraph 预定义的状态,包含一个 messages: list[AnyMessage] 字段,并配置了 operator.add 合并策略

我们扩展它添加了 user_name


2.2 定义工具,使用 ToolRuntime

python 复制代码
def search(runtime: ToolRuntime[Context]) -> str:
    """搜索工具,返回天气信息"""
    # 访问静态上下文
    user_id = runtime.context.user_id
    
    # 访问图状态(动态运行时上下文)
    user_name = runtime.state.get("user_name", "用户")
    
    # 打印日志(实际开发中可以写入数据库)
    print(f"日志记录:user_id: {user_id}, user_name: {user_name} 调用查询工具")
    
    return f"用户 {user_id} ({user_name}) 查询天气结果:晴天,15-20°"

关键点:

  • 工具参数的名称必须为 runtime,类型标注为 ToolRuntime[YourContextType]

  • runtime.context 可以访问我们在调用图时传入的上下文对象

  • runtime.state 是当前 State 的只读副本

    注意:State 是一个 TypedDict,因此取值的语法是 runtime.state["user_name"]


2.3 绑定工具到模型

这里我使用的是阿里云的通义千问模型(qwen-turbo)作为示例,(练习的话可以跟我一样,记得提前配置好环境变量)并将工具绑定到模型

python 复制代码
from langchain.chat_models import init_chat_model
from langchain_core.messages import SystemMessage, HumanMessage

model_with_tool = init_chat_model(
    model="qwen-turbo",
    model_provider="openai",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    api_key="your-api-key", 
    temperature=0
).bind_tools([search])

2.4 编写 LLM 节点

LLM 节点负责调用模型,并决定是否调用工具,这里之前也讲过

python 复制代码
def llm_call(state: State):
    messages = [
        SystemMessage(content="你是天气助手,必须调用工具查询天气")
    ] + state["messages"]
    result = model_with_tool.invoke(messages)
    return {"messages": [result]}

这里使用了 SystemMessage 强制模型调用工具(实际开发中可根据情况调整)


2.5 构建图并添加工具节点

python 复制代码
from langgraph.constants import START, END
from langgraph.graph import StateGraph
from langgraph.prebuilt import ToolNode, tools_condition

builder = StateGraph(State, context_schema=Context)
builder.add_node(llm_call)
builder.add_node("tool_node", ToolNode([search]))
builder.add_edge(START, "llm_call")
builder.add_conditional_edges(
    "llm_call",
    tools_condition,#裁判节点,判断走tool_node 或 END
    {
        "tools": "tool_node",
        "__end__": END
    }
)
builder.add_edge("tool_node", "llm_call")

graph = builder.compile()

ToolNode 是 LangGraph 预定义的节点,它知道如何执行工具并返回 ToolMessage
tools_condition 是预定义的条件路由函数:如果最后一条消息包含 tool_calls,则返回 "tools",否则返回 "__end__"


2.6 流式调用并传入上下文和状态

python 复制代码
for chunk in graph.stream(
    {
        "messages": [HumanMessage(content="今天烟台莱山区天气怎么样")],
        "user_name": "小猫"
    },
    context={"user_id": "123"}
):
    for node, update in chunk.items():
        print(f"节点 {node} 更新的消息如下")
        update["messages"][-1].pretty_print()

执行流程

  1. 用户消息进入 llm_call 节点

  2. 模型决定调用 search 工具,参数由模型自动生成(城市等)

  3. LangGraph 执行 tool_node,调用 search 函数,此时 runtime.context.user_id 为 "123",runtime.state["user_name"] 为 "小猫"

  4. 工具返回结果字符串,作为 ToolMessage 添加到 State

  5. 再次回到 llm_call 节点,模型根据工具结果生成最终回答

  6. 流式输出会依次打印各个节点产生的消息


输出示例(简化):

text

节点 llm_call 更新的消息如下

================================ Ai Message ================================

Tool Calls:

search (call_xxx)

Args:

query: 烟台莱山区天气

节点 tool_node 更新的消息如下

================================ Tool Message ================================

用户 123 (小猫) 查询天气结果:晴天,15-20°

节点 llm_call 更新的消息如下

================================ Ai Message ================================

根据查询,烟台莱山区今天晴天,气温15-20度


三、深入 ToolRuntime:访问 Store 和流式输出

3.1 访问跨会话存储(Store)

如果我们在编译图时传入了 store(例如 InMemoryStore 或 PostgresStore),工具可以通过 runtime.store 读写长期记忆

下面是一个记录用户历史查询的例子:

python 复制代码
import uuid

def search_with_store(runtime: ToolRuntime[Context], city: str) -> str:
    user_id = runtime.context.user_id
    namespace = (user_id, "weather_history")
    # 保存本次查询
    history_entry = {"city": city, "timestamp": "now"}
    
    key = str(uuid.uuid4())
    runtime.store.put(namespace, key, history_entry)
    # 读取最近5条历史
    recent = runtime.store.search(namespace, limit=5)
    return f"查询 {city} 结果:晴天。您最近查过 {len(recent)} 次天气。"

3.2 从工具发送流式自定义数据

长时间运行的工具可以通过 get_stream_writer() 发送进度事件,前端可以实时展示进度

python 复制代码
from langgraph.config import get_stream_writer
import time

def long_task(runtime: ToolRuntime) -> str:
    writer = get_stream_writer()
    writer({"status": "start", "message": "开始处理..."})
    
    for i in range(1, 4):
        time.sleep(1)
        writer({"status": "progress", "step": i, "total": 3})
        
    writer({"status": "end", "result": "完成"})
    return "任务完成"

在调用图时,需要指定 stream_mode="custom" 或 ["updates", "custom"] 来接收这些自定义事件


3.3 使用 InjectedStore 和 InjectedState

LangGraph 也支持通过类型注解直接注入 Store 或 State,而不通过 runtime:

python 复制代码
from typing import Annotated, Any
from langgraph.prebuilt import InjectedStore, InjectedState
from langgraph.store.base import BaseStore

@tool
def get_budget(
    state: Annotated[dict, InjectedState()],
    store: Annotated[BaseStore, InjectedStore()],
    user_id: str
) -> str:
    # 直接从参数中获取 state 和 store
    budget = state.get("budget")
    prefs = store.search((user_id, "prefs"))

这种方式使工具签名更清晰,但需要记住使用 Annotated 标记

选择哪种风格取决于个人偏好,ToolRuntime 方式更简洁统一


四、常见问题与注意事项

4.1 为什么我的工具收不到 runtime?

确保:

  • 工具函数参数名必须是 runtime,类型为 ToolRuntimeToolRuntime[YourContext]

  • 你在构建图时正确指定了 context_schema

  • 你在调用图时传入了 context 参数

  • 工具是通过 ToolNode 执行的,而不是手动调用


4.2 工具中修改 runtime.state 会生效吗?

回答:不会
runtime.state 是只读的副本,任何修改都不会影响图的实际状态

如果你需要更新状态,应该由节点(比如 LLM 节点)通过返回值来完成


4.3 如何调试工具中的 runtime?

可以在工具内打印 dir(runtime) 查看可用属性,或者打印 runtime.context、runtime.state 等LangGraph Studio 的追踪也会显示工具调用时的上下文信息


4.4 性能注意事项

runtime.state 包含整个消息历史,在大规模对话中可能很大

如果工具只需要少量字段,可以考虑在节点中提前提取并存入独立的 State 字段,以减少传递开销


五、总结

本文通过一个完整的天气查询助手案例,详细介绍了如何在 LangGraph 的工具中使用 ToolRuntime 访问:

  • 静态运行时上下文(runtime.context)

  • 图当前状态(runtime.state)

  • 跨会话存储(runtime.store,需要编译时传入)

  • 流式自定义数据(配合 get_stream_writer())

我们还对比了 ToolRuntime 方式和 InjectedStore/InjectedState 方式,并给出了最佳实践和注意事项

掌握这些能力后,你的工具将不再是简单的函数,而是能够感知环境、记忆用户、实时反馈的智能组件

结合第一篇文章中图节点的上下文访问,LangGraph 为你提供了从顶层到底层统一、连贯的运行时信息传递方案

六、内容扩展

我们可以尝试:

  • 将天气工具改为从真实 API(如高德天气)获取数据,并在工具内部添加重试和超时逻辑

  • 使用 PostgresStore 替代 InMemoryStore,实现生产级别的长期记忆

  • 组合使用 interrupt() 和 ToolRuntime,在工具中实现人工审批(例如查询高额订单时要求确认)

LangGraph 的运行时上下文机制,是构建生产级、多租户、可观测的 AI Agent 系统的基础设施之一

希望这两篇文章能帮助大家熟练运用这一强大特性


ok啊运行时上下文篇章结束了,我们下期再会~拜拜

相关推荐
β添砖java1 小时前
深度学习(16)卷积层里的填充和步幅
人工智能·深度学习
云烟成雨TD1 小时前
Spring AI 1.x 系列【29】Embedding Model(嵌入模型)
java·人工智能·spring
波动几何1 小时前
代理记账行业十大功能集群技能体系技能bookkeeping-agency-skill-system
人工智能
数字化顾问1 小时前
(121页PPT)DG1886IT信息化规划报告(附下载方式)
大数据·人工智能
贫民窟的勇敢爷们2 小时前
Flask + 大模型:快速构建 AI 应用的极简开发方案
人工智能
我都学杂了。。。2 小时前
你可以外包思考,但不能外包理解
人工智能
枫叶丹42 小时前
【HarmonyOS 6.0】CANN Kit 新增支持获取 AI 模型 Dump 维测数据功能详解
开发语言·人工智能·华为·信息可视化·harmonyos
计算机魔术师2 小时前
【职场观察 | 技术人处境】五一假期结束,职场两边同时加速——“简历热“和“优化潮“背后的结构性逻辑
人工智能·面试·职场和发展·cot 推理·技术人求职·ai替代逻辑
幸福巡礼2 小时前
【 LangChain 1.2 实战(四)】构建一个模块化的天气查询 Agent
java·前端·langchain