LangChain 之 Agent 短期记忆 state 和 context 区别

简介: 本文厘清LangChain中易混淆的state与context:state是动态、可持久化的运行进展(如会话历史、中间结果),随快照跨轮留存;context是静态、只读的本次依赖(如用户ID、DB连接),调用结束即销毁。从定位、可变性等5个维度对比,直击3大放错症状,并提供实用决策口诀。

写 Agent 的人几乎都会在同一个问题上栽跟头。用户身份该放哪里,数据库连接该放哪里,跨轮要记住的偏好又该放哪里。LangChain 给了两个长得很像的容器,state 和 context,文档各讲各的,真到动手装数据时,一半人放错。这一篇把两者的区别一次讲透,标准是五个维度,落点是三个典型症状和一条决策口诀。

先澄清一个名字的误会。本文说的 context 是 runtime context,运行时上下文。它不是发给模型的 prompt,也不是上下文窗口的 token 额度,更不是模型看到的东西。它是你的代码在运行时需要的依赖和数据,模型全程看不见它。

一、三个容器,先看全景

官方把运行期数据分成三类,state 和 context 只是其中两个。

容器 定位 可变性 生命周期
context 本次运行的依赖 静态,调用开始后不变 单次调用
state 本次运行的进展 动态,随执行推进更新 单次运行,可随快照跨轮
store 跨会话的长期记忆 动态 跨 thread 跨会话

一句话记住分工。context 是本次运行的工具包,state 是本次运行的工作台,store 是跨会话的仓库。store 属于长期记忆,前面系列还没展开,本篇只对照它,不细讲。

在这里插入图片描述

二、state,会呼吸的运行状态

state 是 Agent 的短期记忆本体。它动态,模型每答一轮、工具每执行一次,state 都可能被更新。它承载 messages 会话历史,也承载你自己加的业务字段,第二十篇讲过的订单号、检索文档都住在这里。

两个关键性质。

第一,state 随 checkpointer 持久化。挂上 checkpointer 之后,每一轮结束都落一份快照,下一轮同 thread_id 进来,state 原样读回。这是多轮对话能记住 Bob 名字的机制基础。

第二,state 有归并规则。字段上可以声明 reducer,工具和中间件的更新按声明归并,不做简单覆盖。这套机制决定了 state 适合承载会演进的数据,执行到哪一步了,查到了哪些中间结果。

声明上有一个硬约束,state_schema 必须是 TypedDict,Pydantic 和 dataclass 都不行。

from typing import Annotated
from langchain.agents import AgentState


class CustomState(AgentState):          # 必须 TypedDict
    refund_stage: str                   # 默认覆盖
    verified_ids: Annotated[list[str], operator.add]   # 追加

三、context,本次运行的工具包

context 走的是另一条路。它静态,调用开始时由你显式传入,整次运行期间不变,工具和中间件对它只读。它也不持久化,invoke 结束就消失,下一次调用要什么就得再传什么。

这套设计的名字叫依赖注入。数据库连接、当前用户 id、租户 id、功能开关,这些运行依赖不写死在工具代码里,而是每次调用时从外面注入。工具不关心用户是谁,只管从 runtime.context 里取。好处直接,Agent 变成无状态的纯逻辑,换一个用户传一份新 context 就行,测试时注入 mock 对象也顺手。

声明比 state 宽松,dataclass、TypedDict、Pydantic 都支持。

from dataclasses import dataclass
from langchain.agents import create_agent


@dataclass
class ContextSchema:
    user_id: str
    tenant: str
    db_conn: object


agent = create_agent(
    model="openai:gpt-5.5",
    tools=[get_order],
    context_schema=ContextSchema,
)

调用时通过 context 参数传入,输入 dict 和 schema 实例都收。

result = agent.invoke(
    {
   "messages": [{
   "role": "user", "content": "查一下我的订单"}]},
    context=ContextSchema(
        user_id="u_123",
        tenant="east",
        db_conn=pool.connect(),
    ),
)

注意对比。state 的字段是混在输入 dict 里的,messages 和业务字段同一个包。context 是独立参数,和 state 分得干干净净。

在这里插入图片描述

四、代码里怎么访问

工具里两者都从 runtime 取,泛型注解把类型写全,context 走属性访问,state 走键访问。

from langchain.tools import tool, ToolRuntime


@tool
def get_order(runtime: ToolRuntime[ContextSchema, CustomState]) -> str:
    """查询当前用户订单。"""
    user_id = runtime.context.user_id        # 依赖,只读
    stage = runtime.state["refund_stage"]    # 进展,可读可写
    return query(runtime.context.db_conn, user_id, stage)

中间件里,dynamic_prompt 的入参 ModelRequest 两条路都通,这也是两者最容易被混用的一处。

@dynamic_prompt
def personalized_prompt(request: ModelRequest) -> str:
    name = request.runtime.context.user_id       # 本次身份
    stage = request.state.get("refund_stage")    # 运行进展
    return f"你是售后助手,当前阶段 {stage},服务用户 {name}"

语义差别要分清。context 里的身份每次调用都要重新传,不传就没有。state 里的进展跨轮留存,这一轮工具写进去,下一轮同 thread 还在。同样一行代码取值,取的是两种命运的数据。

五、放错地方的三个症状

容器选错,bug 的长相是固定的。

症状一,身份放进 state。用户 id 随快照被持久化,这个 thread 从此绑定了这个用户。换人接着用同一个 thread_id,Agent 还在用上一个用户的身份查数据,权限越界就在这里发生。隐私数据落库多了,合规审计也多一摊事。身份是本次调用的依赖,进 context,用完即消。

症状二,跨轮信息放进 context。用户第二轮说接着上一单聊,context 里啥都没有,因为上一轮的 context 已经销毁了。Agent 一脸茫然,体验直接破功。要跨轮记住的东西,进 state 挂 checkpointer。

症状三,中间进展放进 context。context 只读,工具查到退款单号没地方放,只能拼进消息里让模型记,token 花了双份,还不可靠。运行中产生的中间结果,state 里的业务字段才是它的家。

在这里插入图片描述

六、五个维度对比速查表

维度 state context
定位 本次运行的进展 本次运行的依赖
可变性 动态,随执行更新 静态,运行期不变
持久化 checkpointer 逐轮快照 不持久化,用完即消
声明约束 必须 TypedDict dataclass 等三种皆可
传入方式 混在输入 dict 独立 context 参数
工具访问 runtime.state 键访问 runtime.context 属性访问
典型内容 messages、阶段标记、中间结果 用户 id、数据库连接、功能开关
写入方 工具与中间件提交更新 只有调用方注入

七、一条口诀收尾

选容器的时候问自己两个问题。这条信息在一次调用里会不会变,会变的进 state,不变的进 context。这条信息下一轮还要不要,要的进 state 让快照留住,不要的进 context 用完即消。

两个都答不上来的,大概率是跨会话的用户偏好,那是 store 的地盘。把这条口诀贴在工位上,state 和 context 从此不再放错。

目录
相关文章
|
23天前
|
人工智能 JSON 自然语言处理
LangChain 六大使用场景:哪些值得做,哪些不值得,一张决策图说清
本文系统梳理LangChain六大核心场景(RAG、Agent、对话系统、结构化提取、内容生成、多模态),逐项解析痛点、机制、落地要点与适用边界,并附“不该用”的反向清单。强调“模型+外部世界”协同才是框架价值所在,助力AI工程化理性选型。
379 0
|
19天前
|
人工智能 中间件 Java
LangChain 到底是什么:从链式连接到 Agent 运行时,讲透 v1.0 的核心概念
LangChain 是大模型应用开发框架,非单纯API封装。v1.0聚焦Agent工程化:以`create_agent`为核心,基于LangGraph实现可持久化、可中断、可观测的智能体循环;统一模型接口、强化工具描述、分层记忆管理,并通过中间件支持人工介入、摘要压缩与隐私脱敏,推动AI从“能跑”走向“可靠生产”。(239字)
286 1
|
23天前
|
SQL 存储 分布式计算
一篇文章搞懂 Hive:从数仓定位到架构、表设计与数据倾斜治理
Hive是基于Hadoop的SQL数据仓库工具,不存数据、不算数据,仅作“胶水层”:元数据存MySQL,数据落HDFS,计算交YARN。支持内外表、分区/分桶、ORC/Parquet列存及丰富优化策略,专为海量离线分析而生,是企业级数仓十五年事实标准。(239字)
610 1
|
20天前
|
机器学习/深度学习 分布式计算 监控
HDFS 检查点机制:fsimage 与 edits 合并及 HA 实现
本文深度解析HDFS NameNode的Checkpoint机制,涵盖fsimage与edits原理、SecondaryNameNode与QJM下的合并流程、事务ID定位、容量规划、监控告警及实战排障命令,助你告别重启卡在“replaying edits”的半小时等待。(239字)
100 1
|
14天前
|
存储 Java 调度
Flink 2.3 任务提交与架构模型:一次提交,七道手
本文深度解析Flink 2.3任务提交全流程:厘清Client、JobManager、TaskManager与外部组件的分工;详解7步提交链路(从StreamGraph到Task部署);对比Application/Session两种部署模式差异;剖析slot资源与四层并行度(算子/环境/客户端/系统级)的配平逻辑;并总结5大高频故障及避坑指南。
215 0
|
17天前
|
存储 分布式计算 固态存储
HDFS Block 为什么默认 128M:寻址时间与传输速率的权衡
HDFS默认Block设为128MB,源于磁盘寻址时间(约10ms)与传输速率(约100MB/s)的工程权衡:为使寻址仅占传输时间1%,理想块大小≈100MB;再对齐二进制幂次,取最接近的128MB。它平衡了NameNode内存开销、寻址效率与计算并行度。
85 0
|
18天前
|
SQL 消息中间件 缓存
为什么是 Flink:八个特性,三个问题,六类场景
本文深入剖析Flink核心选型逻辑:摒弃空洞特性对比,直击实时计算三大本质问题——数据晚到能否算对(时间)、海量状态存哪(状态)、宕机重启是否重算(一致性)。以“延迟×状态×正确性”三维坐标精准定位适用场景,揭示Flink价值在于将“算得对”内化为架构能力,而非依赖运维补救。
197 0
|
18天前
|
消息中间件 分布式计算 Kafka
Flink 的对手换了:三条分野,重排实时计算选型
本文深度剖析2026年Flink与Spark等流处理方案的本质差异,摒弃过时对比表,聚焦**执行模型、状态归属、交付形态**三大分野,直击选型核心:不是“谁更强”,而是“哪种复杂度你付得起”。
232 0
|
19天前
|
SQL 存储 NoSQL
Flink SQL 之 Table 分组与聚合原理及代码实现:从状态管理到两阶段聚合与数据倾斜处理
本文深入解析Flink SQL中核心的分组聚合(groupBy + 聚合函数),涵盖本质定义、四步执行原理、六大类内置函数、状态管理与回撤机制、两阶段聚合、微批预聚合、分组集、自定义聚合函数及完整代码实现,并总结6大常见坑与8条最佳实践,助你写出高性能、高稳定的流式聚合作业。(239字)
167 0
|
20天前
|
消息中间件 监控 Kafka
Structured Streaming 数据源落地案例:四种源、四段可直接抄的代码与踩坑实录
本文详解Structured Streaming四大生产级数据源实战:Kafka限流防积压、File源原子写入与maxFileAge误读避坑、Rate源渐进式背压验证、foreachBatch一托三幂等分发。每个案例均按“约束→写法→翻车→修复”闭环展开,直击参数设计本质与线上事故根因。(239字)
88 0

热门文章

最新文章