精读 LangChain 官方文档(八)Runtime 篇:把运行期上下文注入 Agent
精读 LangChain 官方文档八Runtime 篇把运行期上下文注入 Agent本文基于 LangChain Python 官方文档整理Runtimehttps://docs.langchain.com/oss/python/langchain/runtime对应开源文档源码https://github.com/langchain-ai/docs/blob/main/src/oss/langchain/runtime.mdx上一篇 Messages 篇讲清楚了一个问题模型看到的不是一段普通字符串而是一组带role、content和metadata的消息协议。但真实 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_schema、context、ToolRuntime、runtime.store、runtime.stream_writer、runtime.execution_info和runtime.server_info就不再是分散 API而是同一个 Agent runtime 的不同控制面。1. Runtime运行时到底解决什么问题它解决的问题Runtime解决的是“Agent 执行时工具和中间件如何拿到本次运行所需的依赖和元信息”。官方文档开头说得很关键LangChain 的create_agent底层运行在 LangGraph Runtime 之上。LangGraph 暴露的Runtime对象包含五类信息context本次 Agent 调用的静态信息例如用户 ID、数据库连接、API 客户端、配置项。store长期记忆存储通常是一个BaseStore实例。stream_writer用于通过customstream mode 输出工具进度或自定义更新。execution_info当前执行身份与重试信息例如thread_id、run_id、attempt。server_info运行在 LangGraph Server 时的服务端元信息例如 assistant ID、graph ID、认证用户。示例importosfromdataclassesimportdataclassfromlangchain.agentsimportcreate_agentfromlangchain_openaiimportChatOpenAIdataclassclassRuntimeContext:user_id:strtenant_id:strmodelChatOpenAI(modelqwen3.7-max,api_keyos.environ[QWEN_API_KEY],base_urlos.environ[QWEN_BASE_URL],)agentcreate_agent(modelmodel,tools[],context_schemaRuntimeContext,system_prompt你是一名中文业务助手需要根据当前用户上下文回答问题。,)resultagent.invoke({messages:[{role:user,content:请告诉我当前账号可以使用哪些功能}]},contextRuntimeContext(user_iduser-001,tenant_idmall-a),)这里RuntimeContext本次运行上下文的数据结构用来描述工具和中间件能读取哪些字段。user_id用户标识。真实业务里通常来自登录态或网关鉴权结果。tenant_id租户标识。多租户系统要靠它隔离数据范围。context_schema告诉 Agent runtimecontext的结构是什么。contextRuntimeContext(...)本次调用真实注入的运行期数据。messages给模型看的对话输入。业务场景在积分商城、SaaS 客服或企业知识库里同一句“帮我查一下权益”不同用户、租户和权限下的答案可能完全不同。Runtime让这些业务依赖以结构化方式进入运行过程而不是散落在全局变量或 prompt 拼接里。最简记法Messages是模型看到的上下文Runtime是程序执行需要的上下文。2. context_schema 与 context把本次运行的静态依赖注入 Agent它解决的问题context_schema和context解决的是“怎样把本次调用才知道的信息传给 Agent runtime”。官方文档给出的模式是用context_schema定义上下文结构。调用agent.invoke(...)时传入context。工具或中间件通过 Runtime 读取这个 context。这是一种典型的依赖注入思路。工具不需要自己去读全局变量也不需要从自然语言里猜用户是谁。示例fromdataclassesimportdataclassdataclassclassCustomerContext:user_id:strmembership_level:strlocale:stragentcreate_agent(modelmodel,tools[],context_schemaCustomerContext,system_prompt你是一名会员服务助手需要用中文回答。,)resultagent.invoke({messages:[{role:user,content:我现在能享受什么会员权益}]},contextCustomerContext(user_iduser-001,membership_levelgold,localezh-CN,),)这里CustomerContext上下文 schema定义本次运行可用的业务字段。membership_level会员等级例如gold、silver、basic。locale语言和地区偏好例如zh-CN。context_schemaCustomerContext让 Agent runtime 知道 context 的类型。contextCustomerContext(...)运行时传入的具体值。业务场景会员权益、商品价格、库存范围、数据权限、服务语言都经常依赖用户身份。把这些信息作为context传入比把它们写进 prompt 更稳定也更适合测试。最简记法context_schema定义“能传什么”context传入“这次是什么”。3. Runtime context 不是 LLM context也不是 context window它解决的问题这一节解决的是一个非常容易混淆的概念Runtime context不是提示词上下文也不是模型上下文窗口。可以把三者区分开名称中文理解主要给谁用典型内容messages/ LLM context模型上下文模型系统消息、用户消息、工具结果、历史对话runtime.context运行期上下文工具和中间件用户 ID、租户、权限、数据库连接、配置context window模型上下文窗口模型容量限制模型一次调用能容纳的 token 数示例resultagent.invoke({messages:[{role:user,content:请帮我查询订单状态。}]},contextCustomerContext(user_iduser-001,membership_levelgold,localezh-CN,),)这里messages这部分会进入 Agent state并在需要时进入模型上下文。context这部分不会自动变成模型 prompt而是给工具和中间件读取。context window模型容量限制不是一个需要传参的业务对象。业务场景如果用户问“我的订单到了吗”模型需要看到这句话但真实订单查询工具还需要user_id、tenant_id、数据库连接或 API 客户端。这些运行期依赖不应该都暴露给模型。最简记法模型上下文负责“让模型理解任务”Runtime context 负责“让程序正确执行任务”。4. ToolRuntime工具读取 Runtime 的入口它解决的问题ToolRuntime解决的是“工具函数如何访问 Runtime 里的 context、store 和 writer”。官方文档说明在工具函数参数里使用ToolRuntime[Context]就可以访问 Runtime 对象。这个参数是给 LangChain runtime 注入的不需要模型自己生成。示例fromdataclassesimportdataclassfromlangchain.toolsimportToolRuntime,tooldataclassclassOrderContext:user_id:strtenant_id:str# 根据当前运行上下文查询用户订单状态tooldefquery_latest_order(runtime:ToolRuntime[OrderContext])-str:查询当前用户最近一笔订单状态。user_idruntime.context.user_id tenant_idruntime.context.tenant_idreturnf租户{tenant_id}下用户{user_id}最近一笔订单已发货预计明天送达。agentcreate_agent(modelmodel,tools[query_latest_order],context_schemaOrderContext,system_prompt你是一名售后客服助手。需要查订单时先调用工具再回答用户。,)resultagent.invoke({messages:[{role:user,content:我最近一笔订单到哪里了}]},contextOrderContext(user_iduser-001,tenant_idmall-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.memoryimportInMemoryStoredataclassclassPreferenceContext:user_id:str# 读取当前用户在长期记忆中的邮件写作偏好tooldeffetch_email_preferences(runtime:ToolRuntime[PreferenceContext])-str:读取当前用户的邮件写作偏好。default_preferences用户偏好邮件要简短、礼貌并先给结论。ifruntime.storeisNone:returndefault_preferences memoryruntime.store.get((users,),runtime.context.user_id)ifmemoryisNone:returndefault_preferencesreturnmemory.value.get(preferences,default_preferences)storeInMemoryStore()store.put((users,),user-001,{preferences:用户偏好邮件要正式、分点说明并附下一步建议。},)agentcreate_agent(modelmodel,tools[fetch_email_preferences],context_schemaPreferenceContext,storestore,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通过customstream mode 输出自定义进度。示例fromdataclassesimportdataclassfromlangchain.toolsimportToolRuntime,tooldataclassclassReportContext: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给前端展示的中文进度文案。customstream mode用于接收自定义业务事件的流模式。业务场景数据分析 Agent、文档生成 Agent、投标方案 Agent 往往要执行几十秒甚至几分钟。与其让用户只看到转圈不如让工具把“正在读取文件、正在清洗数据、正在生成报告”这些进度流出去。最简记法模型流式输出回答stream_writer流式输出工具进度。7. execution_info知道当前运行是谁、哪一轮、重试到第几次它解决的问题runtime.execution_info解决的是“工具和中间件如何识别当前执行实例”。官方文档提到Runtime 可以访问 execution identity 和 retry information包括thread_id、run_id和 attempt number。这对于日志、审计、幂等控制和故障排查非常重要。示例fromlangchain.toolsimportToolRuntime,tool# 读取当前运行的执行标识便于日志和审计追踪tooldefinspect_execution(runtime:ToolRuntime)-str:返回当前运行的执行标识。inforuntime.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:检查当前服务端运行身份。serverruntime.server_infoifserverisNone:return当前是本地运行环境没有 LangGraph Server 元信息。ifserver.userisNone:return当前请求没有认证用户需要拒绝高风险操作。returnf当前认证用户{server.user.identity}这里runtime.server_infoLangGraph 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.runtimeimportRuntimedataclassclassSupportContext:user_name:strservice_level:str# 根据运行上下文生成个性化系统提示词dynamic_promptdefpersonalized_prompt(request:ModelRequest)-str:user_namerequest.runtime.context.user_name service_levelrequest.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})returnNoneagentcreate_agent(modelmodel,tools[],middleware[personalized_prompt,log_before_model,log_after_model],context_schemaSupportContext,)这里dynamic_prompt动态提示词中间件。ModelRequest模型调用请求对象request.runtime可以访问 Runtime。before_model模型调用前的 hook。after_model模型调用后的 hook。Runtime[SupportContext]中间件中注入的运行时对象。AgentStateAgent 当前状态类型。业务场景不同用户、渠道、会员等级可能需要不同回答风格。与其在每次用户消息里拼一大段身份信息不如用中间件从 Runtime 读取上下文动态生成稳定提示词。最简记法工具用 Runtime 做业务动作中间件用 Runtime 控制 Agent 行为。10. Runtime、State、Store 三者怎么分工它解决的问题这一节解决的是 Runtime 篇和后续 Context Engineering、Memory 篇之间最重要的边界。可以先记住这张表数据来源中文解释是否会变化生命周期常见字段runtime.context静态运行上下文单次运行内通常不变单次调用user_id、tenant_id、权限、配置、数据库连接state动态运行状态运行过程中会变化当前线程或当前运行messages、工具结果、中间步骤runtime.store长期记忆存储可读写跨会话用户偏好、历史事实、长期画像示例fromdataclassesimportdataclassdataclassclassRuntimeContext:user_id:strtenant_id:strresultagent.invoke({messages:[{role:user,content:请根据我的偏好生成一封售后邮件。}]},contextRuntimeContext(user_iduser-001,tenant_idmall-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_prompt或dynamic_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,tooldataclassclassAppContext: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 RuntimeMessages负责描述模型对话。Agent State负责承载运行中的消息和状态变化。Runtime负责把本次运行的静态依赖、长期记忆入口、自定义流输出、执行身份和服务端身份交给工具与中间件。Tools通过ToolRuntime读取这些信息执行真实业务动作。Middleware通过 Runtime 调整提示词、记录日志、控制模型调用前后的行为。到了生产环境execution_info和server_info又把日志、审计、幂等和认证边界接入进来。所以Runtime 的最简记法是Prompt 负责告诉模型“怎么回答”Runtime 负责告诉程序“这次该按谁、按什么配置、用什么依赖来执行”。理解这一层后再读 Tools、MCP、Human-in-the-loop、Guardrails、Context Engineering 和 Memory 时它们就不再是一堆孤立功能而是围绕同一个 Agent runtime 逐步展开的工程能力。