精读 LangChain 官方文档(八)Runtime 篇:把运行期上下文注入 Agent
2026/6/26 5:16:41 网站建设 项目流程

精读 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 执行时,程序侧还能拿到什么”。

理解这条主线后,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、认证用户。

示例:

importosfromdataclassesimportdataclassfromlangchain.agentsimportcreate_agentfromlangchain_openaiimportChatOpenAI@dataclassclassRuntimeContext:user_id:strtenant_id:strmodel=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。

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

示例:

fromdataclassesimportdataclass@dataclassclassCustomerContext:user_id:strmembership_level:strlocale:stragent=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 负责“让程序正确执行任务”。



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

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

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

示例:

fromdataclassesimportdataclassfromlangchain.toolsimportToolRuntime,tool@dataclassclassOrderContext:user_id:strtenant_id:str# 根据当前运行上下文查询用户订单状态@tooldefquery_latest_order(runtime:ToolRuntime[OrderContext])->str:"""查询当前用户最近一笔订单状态。"""user_id=runtime.context.user_id tenant_id=runtime.context.tenant_idreturnf"租户{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是工具函数里的“运行时入口”。



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

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

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

示例:

fromdataclassesimportdataclassfromlangchain.toolsimportToolRuntime,toolfromlanggraph.store.memoryimportInMemoryStore@dataclassclassPreferenceContext:user_id:str# 读取当前用户在长期记忆中的邮件写作偏好@tooldeffetch_email_preferences(runtime:ToolRuntime[PreferenceContext])->str:"""读取当前用户的邮件写作偏好。"""default_preferences="用户偏好:邮件要简短、礼貌,并先给结论。"ifruntime.storeisNone:returndefault_preferences memory=runtime.store.get(("users",),runtime.context.user_id)ifmemoryisNone:returndefault_preferencesreturnmemory.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 输出自定义进度。

示例:

fromdataclassesimportdataclassfromlangchain.toolsimportToolRuntime,tool@dataclassclassReportContext:user_id:str# 分步生成业务报告,并通过 stream_writer 输出工具进度@tooldefgenerate_sales_report(runtime:ToolRuntime[ReportContext])->str:"""生成当前用户可访问的销售报告。"""ifruntime.stream_writer:runtime.stream_writer({"stage":"load_data","message":"正在读取销售数据"})ifruntime.stream_writer:runtime.stream_writer({"stage":"analyze","message":"正在分析核心指标"})ifruntime.stream_writer:runtime.stream_writer({"stage":"write_report","message":"正在生成中文报告"})return"销售报告已生成:本周订单量上升,复购率保持稳定。"

这里:

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

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

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



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

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

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

示例:

fromlangchain.toolsimportToolRuntime,tool# 读取当前运行的执行标识,便于日志和审计追踪@tooldefinspect_execution(runtime:ToolRuntime)->str:"""返回当前运行的执行标识。"""info=runtime.execution_inforeturn(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 等信息。

示例:

fromlangchain.toolsimportToolRuntime,tool# 根据 LangGraph Server 认证状态决定是否允许继续执行@tooldefcheck_server_identity(runtime:ToolRuntime)->str:"""检查当前服务端运行身份。"""server=runtime.server_infoifserverisNone:return"当前是本地运行环境,没有 LangGraph Server 元信息。"ifserver.userisNone:return"当前请求没有认证用户,需要拒绝高风险操作。"returnf"当前认证用户:{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,做日志、审计或状态处理。

示例:

fromdataclassesimportdataclassfromlangchain.agentsimportAgentState,create_agentfromlangchain.agents.middlewareimportModelRequest,after_model,before_model,dynamic_promptfromlanggraph.runtimeimportRuntime@dataclassclassSupportContext:user_name:strservice_level:str# 根据运行上下文生成个性化系统提示词@dynamic_promptdefpersonalized_prompt(request:ModelRequest)->str:user_name=request.runtime.context.user_name service_level=request.runtime.context.service_levelreturn(f"你是一名中文客服助手。当前用户是{user_name},"f"服务等级是{service_level}。回答要准确、礼貌,并说明下一步。")# 在模型调用前记录当前用户信息@before_modeldeflog_before_model(state:AgentState,runtime:Runtime[SupportContext])->dict|None:print(f"开始处理用户:{runtime.context.user_name}")returnNone# 在模型调用后记录当前用户信息@after_modeldeflog_after_model(state:AgentState,runtime:Runtime[SupportContext])->dict|None:print(f"完成处理用户:{runtime.context.user_name}")returnNoneagent=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 行为。



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

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

可以先记住这张表:

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

示例:

fromdataclassesimportdataclass@dataclassclassRuntimeContext:user_id:strtenant_id:strresult=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是跨会话保留下来的记忆。



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

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

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

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

示例:

fromdataclassesimportdataclassfromlangchain.toolsimportToolRuntime,tool@dataclassclassAppContext:user_id:strtenant_id:strcan_refund:bool# 根据运行上下文判断当前用户是否允许发起退款@tooldefcheck_refund_permission(runtime:ToolRuntime[AppContext])->str:"""检查当前用户是否具备退款权限。"""ifnotruntime.context.can_refund:return"当前账号没有退款权限,需要转人工处理。"returnf"用户{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 逐步展开的工程能力。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询