在这里插入图片描述

精读 LangChain 官方文档(八)Runtime 篇:把运行期上下文注入 Agent

本文基于 LangChain Python 官方文档整理:
Runtime:https://docs.langchain.com/oss/python/langchain/runtime
对应开源文档源码:
https://github.com/langchain-ai/docs/blob/main/src/oss/langchain/runtime.mdx

上一篇 Messages 篇讲清楚了一个问题:模型看到的不是一段普通字符串,而是一组带 rolecontentmetadata 的消息协议。

但真实 Agent 系统还会遇到另一类问题:有些信息并不应该写进消息里。

例如:

  • 当前用户是谁?
  • 当前租户是哪一个?
  • 工具应该查哪一个数据库?
  • 这次调用的运行 ID 是什么?
  • 长期记忆应该从哪里读?
  • 工具执行到一半时,怎样把进度流给前端?
  • 在 LangGraph Server 上运行时,怎样知道当前 assistant、graph 和认证用户?

这些信息不一定是“给模型看的上下文”,但它们是“程序运行必须知道的上下文”。

LangChain 的 Runtime 文档解决的就是这个工程边界:不要把运行期依赖硬编码进工具,也不要把用户 ID、数据库连接、配置项和权限信息都塞进 prompt,而是通过 Runtime 在一次 Agent 调用里注入给工具和中间件。

这篇文档的核心主线可以概括成一句话:

Runtime = Context + Store + Writer + Execution Info + Server Info。它不是模型上下文,而是 Agent 运行时给工具和中间件使用的依赖注入容器。

如果说 Messages 解释“模型看到了什么”,那 Runtime 解释的是“Agent 执行时,程序侧还能拿到什么”。

Runtime运行主线图

理解这条主线后,context_schemacontextToolRuntimeruntime.storeruntime.stream_writerruntime.execution_inforuntime.server_info 就不再是分散 API,而是同一个 Agent runtime 的不同控制面。



1. Runtime(运行时)到底解决什么问题

它解决的问题:
Runtime 解决的是“Agent 执行时,工具和中间件如何拿到本次运行所需的依赖和元信息”。

官方文档开头说得很关键:LangChain 的 create_agent 底层运行在 LangGraph Runtime 之上。LangGraph 暴露的 Runtime 对象包含五类信息:

  • context:本次 Agent 调用的静态信息,例如用户 ID、数据库连接、API 客户端、配置项。
  • store:长期记忆存储,通常是一个 BaseStore 实例。
  • stream_writer:用于通过 "custom" stream mode 输出工具进度或自定义更新。
  • execution_info:当前执行身份与重试信息,例如 thread_idrun_idattempt
  • server_info:运行在 LangGraph Server 时的服务端元信息,例如 assistant ID、graph ID、认证用户。

示例:

import os
from dataclasses import dataclass

from langchain.agents import create_agent
from langchain_openai import ChatOpenAI


@dataclass
class RuntimeContext:
    user_id: str
    tenant_id: str


model = ChatOpenAI(
    model="qwen3.7-max",
    api_key=os.environ["QWEN_API_KEY"],
    base_url=os.environ["QWEN_BASE_URL"],
)

agent = create_agent(
    model=model,
    tools=[],
    context_schema=RuntimeContext,
    system_prompt="你是一名中文业务助手,需要根据当前用户上下文回答问题。",
)

result = agent.invoke(
    {"messages": [{"role": "user", "content": "请告诉我当前账号可以使用哪些功能?"}]},
    context=RuntimeContext(user_id="user-001", tenant_id="mall-a"),
)

这里:

  • RuntimeContext:本次运行上下文的数据结构,用来描述工具和中间件能读取哪些字段。
  • user_id:用户标识。真实业务里通常来自登录态或网关鉴权结果。
  • tenant_id:租户标识。多租户系统要靠它隔离数据范围。
  • context_schema:告诉 Agent runtime,context 的结构是什么。
  • context=RuntimeContext(...):本次调用真实注入的运行期数据。
  • messages:给模型看的对话输入。

业务场景:
在积分商城、SaaS 客服或企业知识库里,同一句“帮我查一下权益”,不同用户、租户和权限下的答案可能完全不同。Runtime 让这些业务依赖以结构化方式进入运行过程,而不是散落在全局变量或 prompt 拼接里。

最简记法:
Messages 是模型看到的上下文,Runtime 是程序执行需要的上下文。



2. context_schema 与 context:把本次运行的静态依赖注入 Agent

它解决的问题:
context_schemacontext 解决的是“怎样把本次调用才知道的信息传给 Agent runtime”。

官方文档给出的模式是:

  1. context_schema 定义上下文结构。
  2. 调用 agent.invoke(...) 时传入 context
  3. 工具或中间件通过 Runtime 读取这个 context。

这是一种典型的依赖注入思路。工具不需要自己去读全局变量,也不需要从自然语言里猜用户是谁。

context注入链路图

示例:

from dataclasses import dataclass


@dataclass
class CustomerContext:
    user_id: str
    membership_level: str
    locale: str


agent = create_agent(
    model=model,
    tools=[],
    context_schema=CustomerContext,
    system_prompt="你是一名会员服务助手,需要用中文回答。",
)

result = agent.invoke(
    {"messages": [{"role": "user", "content": "我现在能享受什么会员权益?"}]},
    context=CustomerContext(
        user_id="user-001",
        membership_level="gold",
        locale="zh-CN",
    ),
)

这里:

  • CustomerContext:上下文 schema,定义本次运行可用的业务字段。
  • membership_level:会员等级,例如 goldsilverbasic
  • locale:语言和地区偏好,例如 zh-CN
  • context_schema=CustomerContext:让 Agent runtime 知道 context 的类型。
  • context=CustomerContext(...):运行时传入的具体值。

业务场景:
会员权益、商品价格、库存范围、数据权限、服务语言都经常依赖用户身份。把这些信息作为 context 传入,比把它们写进 prompt 更稳定,也更适合测试。

最简记法:
context_schema 定义“能传什么”,context 传入“这次是什么”。



3. Runtime context 不是 LLM context,也不是 context window

它解决的问题:
这一节解决的是一个非常容易混淆的概念:Runtime context 不是提示词上下文,也不是模型上下文窗口。

可以把三者区分开:

名称 中文理解 主要给谁用 典型内容
messages / LLM context 模型上下文 模型 系统消息、用户消息、工具结果、历史对话
runtime.context 运行期上下文 工具和中间件 用户 ID、租户、权限、数据库连接、配置
context window 模型上下文窗口 模型容量限制 模型一次调用能容纳的 token 数

示例:

result = agent.invoke(
    {
        "messages": [
            {"role": "user", "content": "请帮我查询订单状态。"}
        ]
    },
    context=CustomerContext(
        user_id="user-001",
        membership_level="gold",
        locale="zh-CN",
    ),
)

这里:

  • messages:这部分会进入 Agent state,并在需要时进入模型上下文。
  • context:这部分不会自动变成模型 prompt,而是给工具和中间件读取。
  • context window:模型容量限制,不是一个需要传参的业务对象。

业务场景:
如果用户问“我的订单到了吗”,模型需要看到这句话,但真实订单查询工具还需要 user_idtenant_id、数据库连接或 API 客户端。这些运行期依赖不应该都暴露给模型。

最简记法:
模型上下文负责“让模型理解任务”,Runtime context 负责“让程序正确执行任务”。

context三分法图



4. ToolRuntime:工具读取 Runtime 的入口

它解决的问题:
ToolRuntime 解决的是“工具函数如何访问 Runtime 里的 context、store 和 writer”。

官方文档说明,在工具函数参数里使用 ToolRuntime[Context],就可以访问 Runtime 对象。这个参数是给 LangChain runtime 注入的,不需要模型自己生成。

示例:

from dataclasses import dataclass

from langchain.tools import ToolRuntime, tool


@dataclass
class OrderContext:
    user_id: str
    tenant_id: str


# 根据当前运行上下文查询用户订单状态
@tool
def query_latest_order(runtime: ToolRuntime[OrderContext]) -> str:
    """查询当前用户最近一笔订单状态。"""
    user_id = runtime.context.user_id
    tenant_id = runtime.context.tenant_id

    return f"租户 {tenant_id} 下,用户 {user_id} 最近一笔订单已发货,预计明天送达。"


agent = create_agent(
    model=model,
    tools=[query_latest_order],
    context_schema=OrderContext,
    system_prompt="你是一名售后客服助手。需要查订单时,先调用工具,再回答用户。",
)

result = agent.invoke(
    {"messages": [{"role": "user", "content": "我最近一笔订单到哪里了?"}]},
    context=OrderContext(user_id="user-001", tenant_id="mall-a"),
)

这里:

  • ToolRuntime[OrderContext]:工具运行时参数,类型参数说明 runtime.context 的结构。
  • runtime.context.user_id:读取当前用户 ID。
  • runtime.context.tenant_id:读取当前租户 ID。
  • query_latest_order:工具函数名,表示查询最近订单。
  • @tool:把普通 Python 函数注册成 LangChain 工具。

业务场景:
订单工具不应该让模型从用户自然语言里猜 user_id。用户只说“我最近一笔订单”,工具通过 Runtime 拿到真实登录用户,再去后端查询。

最简记法:
ToolRuntime 是工具函数里的“运行时入口”。

ToolRuntime工具入口图



5. runtime.store:在工具里读取长期记忆

它解决的问题:
runtime.store 解决的是“工具如何读取或写入跨会话的长期记忆”。

官方文档里,Runtime 对象包含一个 store,它是用于长期记忆的 BaseStore 实例。工具可以根据 runtime.context 中的用户 ID 去读取用户偏好、历史设置或长期记忆。

示例:

from dataclasses import dataclass

from langchain.tools import ToolRuntime, tool
from langgraph.store.memory import InMemoryStore


@dataclass
class PreferenceContext:
    user_id: str


# 读取当前用户在长期记忆中的邮件写作偏好
@tool
def fetch_email_preferences(runtime: ToolRuntime[PreferenceContext]) -> str:
    """读取当前用户的邮件写作偏好。"""
    default_preferences = "用户偏好:邮件要简短、礼貌,并先给结论。"

    if runtime.store is None:
        return default_preferences

    memory = runtime.store.get(("users",), runtime.context.user_id)
    if memory is None:
        return default_preferences

    return memory.value.get("preferences", default_preferences)


store = InMemoryStore()
store.put(
    ("users",),
    "user-001",
    {"preferences": "用户偏好:邮件要正式、分点说明,并附下一步建议。"},
)

agent = create_agent(
    model=model,
    tools=[fetch_email_preferences],
    context_schema=PreferenceContext,
    store=store,
    system_prompt="你是一名中文邮件助手,需要先读取用户偏好,再生成草稿。",
)

这里:

  • runtime.store:长期记忆存储入口。
  • InMemoryStore:内存版 store,适合演示和本地测试。
  • store.put(...):写入一条长期记忆。
  • store.get(...):读取一条长期记忆。
  • ("users",):命名空间,用来组织不同类型的记忆。
  • runtime.context.user_id:用当前运行上下文定位用户。

业务场景:
邮件助手、客服助手、销售助手都需要记住用户偏好。比如“这个用户喜欢正式语气”“这个客户要求报价里先列总价”“这个租户禁用某些工具”。这些不属于单轮消息,却应该影响后续运行。

最简记法:
context 定位“当前是谁”,store 读取“过去记住了什么”。



6. stream_writer:工具进度也能流式输出

它解决的问题:
stream_writer 解决的是“工具执行过程中的业务进度如何实时告诉前端”。

Streaming 篇已经讲过,Agent 可以流式返回模型 token 或 agent progress。Runtime 文档补充的是:工具内部也可以使用 stream writer,通过 "custom" stream mode 输出自定义进度。

示例:

from dataclasses import dataclass

from langchain.tools import ToolRuntime, tool


@dataclass
class ReportContext:
    user_id: str


# 分步生成业务报告,并通过 stream_writer 输出工具进度
@tool
def generate_sales_report(runtime: ToolRuntime[ReportContext]) -> str:
    """生成当前用户可访问的销售报告。"""
    if runtime.stream_writer:
        runtime.stream_writer({"stage": "load_data", "message": "正在读取销售数据"})

    if runtime.stream_writer:
        runtime.stream_writer({"stage": "analyze", "message": "正在分析核心指标"})

    if runtime.stream_writer:
        runtime.stream_writer({"stage": "write_report", "message": "正在生成中文报告"})

    return "销售报告已生成:本周订单量上升,复购率保持稳定。"

这里:

  • runtime.stream_writer:自定义流式输出入口。
  • stage:阶段字段,表示工具执行到了哪一步。
  • message:给前端展示的中文进度文案。
  • "custom" stream mode:用于接收自定义业务事件的流模式。

业务场景:
数据分析 Agent、文档生成 Agent、投标方案 Agent 往往要执行几十秒甚至几分钟。与其让用户只看到转圈,不如让工具把“正在读取文件、正在清洗数据、正在生成报告”这些进度流出去。

最简记法:
模型流式输出回答,stream_writer 流式输出工具进度。

Store与Writer图



7. execution_info:知道当前运行是谁、哪一轮、重试到第几次

它解决的问题:
runtime.execution_info 解决的是“工具和中间件如何识别当前执行实例”。

官方文档提到,Runtime 可以访问 execution identity 和 retry information,包括 thread_idrun_id 和 attempt number。这对于日志、审计、幂等控制和故障排查非常重要。

示例:

from langchain.tools import ToolRuntime, tool


# 读取当前运行的执行标识,便于日志和审计追踪
@tool
def inspect_execution(runtime: ToolRuntime) -> str:
    """返回当前运行的执行标识。"""
    info = runtime.execution_info
    return (
        f"当前线程:{info.thread_id};"
        f"当前运行:{info.run_id};"
        f"当前尝试次数:{info.attempt}"
    )

这里:

  • runtime.execution_info:当前运行的执行信息。
  • thread_id:对话线程或任务线程标识。
  • run_id:本次运行标识。
  • attempt:当前尝试次数,和重试机制相关。
  • inspect_execution:示例工具名,用来演示读取执行信息。

业务场景:
如果某个退款工具被调用了两次,工程侧需要知道这是不是同一个 run_id 的重试,还是用户重新发起了一次请求。没有执行信息,日志很容易变成一堆互相对不上的文本。

最简记法:
execution_info 给每次 Agent 运行贴上可追踪的身份标签。



8. server_info:在 LangGraph Server 上读取服务端身份

它解决的问题:
runtime.server_info 解决的是“Agent 部署到 LangGraph Server 后,如何读取服务端元信息和认证用户”。

官方文档提醒:server_info 只在运行于 LangGraph Server 时可用,本地开发时通常是 None。它可以包含 assistant ID、graph ID 和 authenticated user 等信息。

示例:

from langchain.tools import ToolRuntime, tool


# 根据 LangGraph Server 认证状态决定是否允许继续执行
@tool
def check_server_identity(runtime: ToolRuntime) -> str:
    """检查当前服务端运行身份。"""
    server = runtime.server_info

    if server is None:
        return "当前是本地运行环境,没有 LangGraph Server 元信息。"

    if server.user is None:
        return "当前请求没有认证用户,需要拒绝高风险操作。"

    return f"当前认证用户:{server.user.identity}"

这里:

  • runtime.server_info:LangGraph Server 运行时的服务端元信息。
  • server.user:认证用户信息,可能为空。
  • server.user.identity:认证用户身份标识。
  • server is None:表示当前不是 LangGraph Server 环境,常见于本地开发。

业务场景:
同一套 Agent 在本地测试、灰度环境和正式服务上运行时,安全策略可能不同。高风险工具可以通过 server_info 判断是否有认证用户,再决定是否继续。

最简记法:
server_info 让 Agent 知道自己运行在哪个服务端身份里。

执行与服务端信息图



9. Runtime in Middleware:中间件也能读取运行上下文

它解决的问题:
Runtime 不只给工具用,也给中间件用。中间件可以根据运行上下文动态修改提示词、记录日志、控制模型调用前后的行为。

官方文档展示了三类常见入口:

  • dynamic_prompt:根据 request.runtime.context 生成动态系统提示词。
  • before_model:模型调用前读取 runtime,做日志、校验或上下文调整。
  • after_model:模型调用后读取 runtime,做日志、审计或状态处理。

示例:

from dataclasses import dataclass

from langchain.agents import AgentState, create_agent
from langchain.agents.middleware import ModelRequest, after_model, before_model, dynamic_prompt
from langgraph.runtime import Runtime


@dataclass
class SupportContext:
    user_name: str
    service_level: str


# 根据运行上下文生成个性化系统提示词
@dynamic_prompt
def personalized_prompt(request: ModelRequest) -> str:
    user_name = request.runtime.context.user_name
    service_level = request.runtime.context.service_level
    return (
        f"你是一名中文客服助手。当前用户是 {user_name},"
        f"服务等级是 {service_level}。回答要准确、礼貌,并说明下一步。"
    )


# 在模型调用前记录当前用户信息
@before_model
def log_before_model(state: AgentState, runtime: Runtime[SupportContext]) -> dict | None:
    print(f"开始处理用户:{runtime.context.user_name}")
    return None


# 在模型调用后记录当前用户信息
@after_model
def log_after_model(state: AgentState, runtime: Runtime[SupportContext]) -> dict | None:
    print(f"完成处理用户:{runtime.context.user_name}")
    return None


agent = create_agent(
    model=model,
    tools=[],
    middleware=[personalized_prompt, log_before_model, log_after_model],
    context_schema=SupportContext,
)

这里:

  • dynamic_prompt:动态提示词中间件。
  • ModelRequest:模型调用请求对象,request.runtime 可以访问 Runtime。
  • before_model:模型调用前的 hook。
  • after_model:模型调用后的 hook。
  • Runtime[SupportContext]:中间件中注入的运行时对象。
  • AgentState:Agent 当前状态类型。

业务场景:
不同用户、渠道、会员等级可能需要不同回答风格。与其在每次用户消息里拼一大段身份信息,不如用中间件从 Runtime 读取上下文,动态生成稳定提示词。

最简记法:
工具用 Runtime 做业务动作,中间件用 Runtime 控制 Agent 行为。

中间件Runtime访问图



10. Runtime、State、Store 三者怎么分工

它解决的问题:
这一节解决的是 Runtime 篇和后续 Context Engineering、Memory 篇之间最重要的边界。

可以先记住这张表:

数据来源 中文解释 是否会变化 生命周期 常见字段
runtime.context 静态运行上下文 单次运行内通常不变 单次调用 user_idtenant_id、权限、配置、数据库连接
state 动态运行状态 运行过程中会变化 当前线程或当前运行 messages、工具结果、中间步骤
runtime.store 长期记忆存储 可读写 跨会话 用户偏好、历史事实、长期画像

示例:

from dataclasses import dataclass


@dataclass
class RuntimeContext:
    user_id: str
    tenant_id: str


result = agent.invoke(
    {
        "messages": [
            {"role": "user", "content": "请根据我的偏好生成一封售后邮件。"}
        ]
    },
    context=RuntimeContext(user_id="user-001", tenant_id="mall-a"),
)

这里:

  • runtime.context.user_id:单次运行的用户身份。
  • state["messages"]:当前对话状态,会随着多轮对话变化。
  • runtime.store:可以读取跨会话保留的用户偏好。

业务场景:
一个邮件助手要同时使用三类数据:本轮用户问题在 messages 里;用户 ID 和租户在 runtime.context 里;用户长期写作偏好在 runtime.store 里。三者混在一起,系统就很难测试、追踪和治理。

最简记法:
context 是这次运行带来的身份,state 是运行中变化的过程,store 是跨会话保留下来的记忆。

Runtime-State-Store分工图



11. 工程落地:什么时候该用 Runtime,而不是 prompt 或全局变量

它解决的问题:
这一节把 Runtime 文档转成工程判断:哪些信息应该走 Runtime,哪些信息应该进入 messages,哪些信息应该进 store。

可以用下面这张表做设计检查:

工程问题 更适合放在哪里 原因
用户本轮提问 messages 需要模型理解
系统角色和回答风格 system_promptdynamic_prompt 控制模型行为
当前登录用户 ID runtime.context 工具和中间件需要,模型不一定要看
当前租户、权限、渠道 runtime.context 用于数据隔离和策略判断
数据库连接、API 客户端 runtime.context 程序依赖,不应暴露给模型
用户长期偏好 runtime.store 跨会话复用
当前对话历史 state["messages"] 多轮状态
运行 ID、线程 ID、重试次数 runtime.execution_info 日志、审计、幂等控制
LangGraph Server 认证用户 runtime.server_info 服务端安全判断
工具执行进度 runtime.stream_writer 前端进度展示

示例:

from dataclasses import dataclass

from langchain.tools import ToolRuntime, tool


@dataclass
class AppContext:
    user_id: str
    tenant_id: str
    can_refund: bool


# 根据运行上下文判断当前用户是否允许发起退款
@tool
def check_refund_permission(runtime: ToolRuntime[AppContext]) -> str:
    """检查当前用户是否具备退款权限。"""
    if not runtime.context.can_refund:
        return "当前账号没有退款权限,需要转人工处理。"

    return f"用户 {runtime.context.user_id} 可以在租户 {runtime.context.tenant_id} 下发起退款。"

这里:

  • can_refund:权限字段,属于程序侧判断,不应该让模型凭自然语言决定。
  • check_refund_permission:工具函数名,表示检查退款权限。
  • runtime.context.can_refund:从运行期上下文读取真实权限。

业务场景:
退款、发券、改地址、删除数据、导出报表等高影响动作,都应该依赖后端权限和运行时身份,而不是只靠模型“觉得可以”。

最简记法:
能决定程序怎么执行的业务依赖,优先放 Runtime;需要模型理解的任务内容,才放 messages。



总结:Runtime 是 Agent 的运行期依赖注入层

读完 Runtime 文档,最重要的不是记住某一个参数,而是建立一个新的分层模型:

Messages -> Agent State -> Runtime -> Tools / Middleware -> Production Runtime

Messages 负责描述模型对话。
Agent State 负责承载运行中的消息和状态变化。
Runtime 负责把本次运行的静态依赖、长期记忆入口、自定义流输出、执行身份和服务端身份交给工具与中间件。
Tools 通过 ToolRuntime 读取这些信息,执行真实业务动作。
Middleware 通过 Runtime 调整提示词、记录日志、控制模型调用前后的行为。
到了生产环境,execution_infoserver_info 又把日志、审计、幂等和认证边界接入进来。

所以,Runtime 的最简记法是:

Prompt 负责告诉模型“怎么回答”,Runtime 负责告诉程序“这次该按谁、按什么配置、用什么依赖来执行”。

理解这一层后,再读 Tools、MCP、Human-in-the-loop、Guardrails、Context Engineering 和 Memory 时,它们就不再是一堆孤立功能,而是围绕同一个 Agent runtime 逐步展开的工程能力。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐