Skip to content

16 Runtime运行时信息

写工具的时候,你可能会遇到这样的问题:

  • 工具需要知道当前用户是谁,但不想把user_id作为参数暴露给LLM
  • 工具需要连接数据库,但不想在全局变量里存连接对象
  • 工具需要读写长期记忆,但不知道怎么获取store

Runtime就是来解决这类问题的——它是一个运行时上下文对象,让你在工具和中间件中安全地访问各种依赖信息,而不用硬编码或用全局状态。

打个比方:如果工具是工人,Runtime就是工人的"工具箱"——里面有用户信息、数据库连接、记忆存储等等,需要什么就从里面拿。

一、Runtime包含什么

Runtime对象里有五类信息:

信息说明用途
context用户自定义的静态上下文用户ID、角色、配置等
store长期记忆存储跨对话的持久化数据
execution_info执行信息thread_id、run_id
server_info服务器信息仅LangGraph Server环境可用
stream_writer流式写入器自定义流式输出

二、基本用法

2.1 定义上下文

创建Agent时用context_schema定义上下文的结构,调用时传入具体的值:

python
from dataclasses import dataclass
from langchain.agents import create_agent


@dataclass
class Context:
    user_id: str
    user_role: str


agent = create_agent(
    model="deepseek-v4-flash",
    tools=[...],
    context_schema=Context,  # 定义上下文结构
)

# 调用时传入上下文
result = agent.invoke(
    {"messages": [{"role": "user", "content": "查看我的订单"}]},
    context=Context(user_id="user_123", user_role="admin"),  # 传入具体值
)

2.2 在工具中访问

通过ToolRuntime参数访问Runtime对象:

python
from dataclasses import dataclass
from langchain.tools import tool, ToolRuntime


@dataclass
class Context:
    user_id: str


@tool
def get_my_orders(runtime: ToolRuntime[Context]) -> str:
    """获取当前用户的订单列表"""
    user_id = runtime.context.user_id  # 获取用户ID
    # 用user_id查询数据库...
    return f"用户 {user_id} 的订单: [...]"

注意看,user_id不是工具的参数,LLM看不到也改不了它。它从Runtime中自动获取,这就是依赖注入的好处:

  • LLM不会胡乱修改用户ID
  • 工具的参数列表更简洁
  • 测试时可以轻松注入不同的上下文

2.3 在中间件中访问

Node-style钩子通过Runtime参数访问,Wrap-style钩子通过ModelRequest.runtime访问:

python
from dataclasses import dataclass
from langchain.agents import create_agent, AgentState
from langchain.agents.middleware import before_model, wrap_model_call, ModelRequest, ModelResponse
from langgraph.runtime import Runtime
from typing import Callable


@dataclass
class Context:
    user_name: str
    user_role: str


# Node-style:通过runtime参数
@before_model
def log_request(state: AgentState, runtime: Runtime[Context]) -> dict | None:
    print(f"[日志] 用户 {runtime.context.user_name} 发起请求")
    return None


# Wrap-style:通过request.runtime
@wrap_model_call
def dynamic_prompt(
    request: ModelRequest,
    handler: Callable[[ModelRequest], ModelResponse],
) -> ModelResponse:
    user_name = request.runtime.context.user_name
    user_role = request.runtime.context.user_role

    system_prompt = f"你是助手,正在和 {user_name}{user_role})对话。"
    modified_request = request.override(system_prompt=system_prompt)
    return handler(modified_request)


agent = create_agent(
    model="deepseek-v4-flash",
    tools=[...],
    middleware=[log_request, dynamic_prompt],
    context_schema=Context,
)

三、实用场景

3.1 动态提示词

根据用户身份注入不同的系统提示词:

python
from dataclasses import dataclass
from langchain.agents import create_agent
from langchain.agents.middleware import wrap_model_call, ModelRequest, ModelResponse
from typing import Callable


@dataclass
class Context:
    user_role: str


@wrap_model_call
def role_based_prompt(
    request: ModelRequest,
    handler: Callable[[ModelRequest], ModelResponse],
) -> ModelResponse:
    role = request.runtime.context.user_role

    prompts = {
        "admin": "你是管理员助手,可以执行所有操作,包括删除和修改数据。",
        "user": "你是普通用户助手,只能查询信息,不能执行修改操作。",
        "guest": "你是访客助手,只能查看公开信息。",
    }

    system_prompt = prompts.get(role, prompts["guest"])
    return handler(request.override(system_prompt=system_prompt))


agent = create_agent(
    model="deepseek-v4-flash",
    tools=[...],
    middleware=[role_based_prompt],
    context_schema=Context,
)

3.2 权限控制

在中间件中根据用户角色限制工具使用:

python
from dataclasses import dataclass
from langchain.agents import create_agent, AgentState
from langchain.agents.middleware import wrap_model_call, ModelRequest, ModelResponse
from typing import Callable


@dataclass
class Context:
    user_role: str


@wrap_model_call
def filter_tools_by_role(
    request: ModelRequest,
    handler: Callable[[ModelRequest], ModelResponse],
) -> ModelResponse:
    role = request.runtime.context.user_role

    # 根据角色过滤工具
    if role == "guest":
        # 访客只能用查询工具
        allowed_tools = [t for t in request.tools if t.name.startswith("query_")]
        return handler(request.override(tools=allowed_tools))

    return handler(request)


agent = create_agent(
    model="deepseek-v4-flash",
    tools=[query_users, query_orders, delete_user, modify_order],
    middleware=[filter_tools_by_role],
    context_schema=Context,
)

3.3 获取执行信息

通过runtime.execution_info获取当前执行的标识信息:

python
from langchain.tools import tool, ToolRuntime


@tool
def log_action(action: str, runtime: ToolRuntime) -> str:
    """记录操作日志"""
    info = runtime.execution_info
    print(f"[日志] Thread: {info.thread_id}, Run: {info.run_id}")
    print(f"[日志] 操作: {action}")
    return f"操作 '{action}' 已记录"

3.4 访问长期记忆

通过runtime.store读写跨对话的持久化数据:

python
from langchain.tools import tool, ToolRuntime


@tool
def save_preference(key: str, value: str, runtime: ToolRuntime) -> str:
    """保存用户偏好设置"""
    user_id = runtime.context.user_id
    if runtime.store:
        runtime.store.put(("preferences", user_id), key, {"value": value})
        return f"偏好 '{key}' 已保存"
    return "存储不可用"


@tool
def get_preference(key: str, runtime: ToolRuntime) -> str:
    """获取用户偏好设置"""
    user_id = runtime.context.user_id
    if runtime.store:
        memory = runtime.store.get(("preferences", user_id), key)
        if memory:
            return f"{key}: {memory.value['value']}"
    return f"未找到偏好 '{key}'"

四、依赖注入的好处

用Runtime做依赖注入,比传统的全局变量或参数传递好在哪?

方式问题
全局变量线程不安全、难测试、耦合严重
工具参数LLM能看到和修改、参数列表膨胀
Runtime注入安全、可测试、解耦

用Runtime注入的代码更容易测试——测试时只需要传入不同的context就行:

python
# 生产环境
agent.invoke(
    {"messages": [...]},
    context=Context(user_id="real_user", user_role="admin"),
)

# 测试环境
agent.invoke(
    {"messages": [...]},
    context=Context(user_id="test_user", user_role="guest"),
)

五、总结

Runtime是LangChain中实现依赖注入的核心机制:

  • context:注入用户身份、配置等静态信息
  • store:访问长期记忆存储
  • execution_info:获取thread_id、run_id等执行标识
  • 在工具中:通过ToolRuntime参数访问
  • 在中间件中:通过Runtime参数或ModelRequest.runtime访问

用Runtime代替全局变量,让你的工具更安全、更灵活、更容易测试。

在下一篇文章中,我们将学习context上下文工程,了解如何精心设计Agent看到的信息。