当你把一个 AI Agent 从 Demo 推向生产环境时,第一个撞上的墙,往往不是模型能力不够,而是“记忆”无处安放。
这个现象在最近几个月变得尤其明显。你在各种技术社区里会频繁看到类似的报错:
api error: 400 this model's maximum context length is 1048576 tokens.或者:
codex ran out of room in the model's context window. start a new thread or c...再或者:
context is too large and auto-compaction could not recover this turn.这些报错背后指向的是同一个问题:Agent 的上下文窗口是有限的,但真实业务场景中的信息量是无限的。当你试图让 Agent 完成一个跨多轮、跨会话、甚至跨团队协作的长期任务时,把一切都塞进 Context 的做法必然失效。
本文想讨论的不是“怎么把 Context 调大”,而是更本质的问题:Agent 的记忆系统应该如何分层架构,如何工程化治理,以及 LangChain、LangGraph、DeepAgent 这类框架在这个问题上的解题思路分别是什么。
如果你正在做 AI Agent 的企业级落地,或者刚接触 Agent 开发不久但想避开记忆设计上的大坑,这篇文章值得读完。
1. 这篇文章真正要解决的问题
先说一个明确的判断:Agent 的生产力瓶颈,已经从“模型推理能力”转移到了“记忆管理能力”。
为什么这么说?因为单轮对话的模型能力已经足够强,但在真实业务里,Agent 很少只回答一个问题。它可能需要:
- 连续处理一个工单的多个环节,从信息收集、方案制定到执行确认。
- 在多次会话之间记住用户的偏好和历史决策。
- 在企业知识库之上完成跨文档的推理,而不是每次都重新检索一遍。
- 在长流程执行中,不被中间步骤产生的海量中间结果撑爆上下文。
这些场景有一个共同点:Agent 需要区分“这次任务中要用的信息”和“以后还可能再用到的信息”。前者是工作记忆,后者是长期记忆。如果不对这两种信息做架构上的区分,直接把所有内容塞进 Context,那么上面那些报错就是必然结果。
这篇文章会围绕以下三条主线展开:
第一,讲清楚 Context、短期记忆和长期记忆的本质区别,帮你建立 Agent 记忆分层的整体认知。
第二,结合 LangChain、LangGraph 和 DeepAgent 这三个框架,分别说明它们在记忆管理上的实现思路和工程边界。LangChain 覆盖面广、上手快;LangGraph 以图状态机为核心,适合企业级复杂流程;DeepAgent 则代表着更面向长期记忆的产品化探索。
第三,给出一个可以落地的工程治理框架:记忆内容如何存储、如何检索、如何压缩、如何做权限控制、如何在出问题时追溯。这些才是企业级项目里真正决定成败的细节。
读完这篇文章,你会知道一个可供生产参考的 Agent 记忆系统应该长什么样,也会理解为什么“给模型加记忆”这件事,本质上是一个系统工程问题,而不是简单的 API 调用或数据库存储。
2. Agent 记忆的核心概念:Context、工作记忆与长期记忆
很多开发者对 Agent 记忆的理解是从“让多轮对话不丢失上下文”开始的。这个理解没错,但远远不够。
2.1 Context:模型的“工作台”,不是“仓库”
Context(上下文窗口)是模型一次推理时能看到的全部信息。你可以把它想象成一张工作台:桌面上能摆多少文件,决定了这次任务能不能顺利处理。
在这张工作台上,至少有以下几类内容:
- 系统指令(System Prompt),定义 Agent 的角色和行为边界。
- 用户输入,包括当前问题和历史对话。
- 工具调用结果,比如检索到的文档片段、API 返回的数据、代码执行结果。
- 中间推理过程,包括 Agent 的思考步骤和规划结果。
问题在于,这张工作台的总面积是有限的。无论是 128K、200K 还是最近常见的 1M tokens,一旦超出限制,模型就无法继续工作。
更重要的是,Context 越大,模型的注意力越容易被稀释。你可能有过这种体验:让模型处理一个 100K tokens 的长文档时,它经常“忘记”文档开头的关键信息。这不是模型变笨了,而是注意力机制在超长上下文下的固有局限。
所以,Context 的正确使用方式是“精炼、按需、可回收”,而不是“全部塞进去”。它适合存放当前任务必需的信息,不适合作为长期存储。
2.2 短期记忆:一次任务过程中的“便签纸”
短期记忆,也叫工作记忆或会话记忆,指的是 Agent 在一次任务执行过程中需要保留的状态信息。
举个具体例子。一个客服 Agent 在处理用户退款请求时,需要记住这些信息:
- 用户当前会话中已经提供的信息,比如订单号、退款原因。
- 当前处理到哪一步了,是否已经向用户确认过。
- 从订单系统查到的订单状态和金额。
- 内部流程的规则约束,比如哪些商品不支持无理由退款。
短期记忆的特点是:生命周期短、更新频繁、与当前任务强相关。任务结束后,这些信息大部分不再有保存价值。
在实现层面,短期记忆最简单的形式就是“把历史消息拼进 Prompt”,但更成熟的方案是用状态对象(State)来管理。LangGraph 的核心设计思路,就是用一组结构化的 State 字段来承载短期记忆,而不是单纯依赖对话历史的拼接。
2.3 长期记忆:Agent 的“知识库”和“人格底座”
长期记忆解决的是“Agent 如何记住跨会话的信息”这个问题。
想象一个场景:用户每周都和同一个理财 Agent 沟通,第五次交流时,Agent 应该记得用户之前提到过的风险偏好、家庭情况、之前推荐的产品的接受程度。这些信息不能放在 Context 里——它们会占用大量空间,而且大部分时间用不上。它们应该被结构化地存储起来,在需要时被检索调用。
长期记忆又可以细分为两类:
- 事实性记忆:用户是谁、偏好是什么、历史决策有哪些。这类信息适合用结构化数据库或键值存储保存。
- 经验性记忆:Agent 从过往任务中学到的处理模式,比如“这类工单通常需要先走财务审核”。这类信息带有一定的主观性和归纳性,适合用语义向量库保存,通过相似度检索召回。
长期记忆的工程难度比短期记忆高一个数量级。它涉及存储选型、数据更新策略、检索质量、权限隔离、隐私合规等问题。这也是为什么很多 Agent 框架在早期版本里并不支持长期记忆——它不是一个“加个数据库”就能解决的问题。
2.4 三类记忆的边界与关系
用一个表格来总结三者差异:
| 维度 | Context 上下文 | 短期记忆 | 长期记忆 |
|---|---|---|---|
| 生命周期 | 单次推理 | 单次任务/会话 | 跨会话、长期 |
| 存储位置 | 模型输入 | 状态对象/会话存储 | 数据库/向量库/文件 |
| 数据量级 | 受模型上下文限制 | 受任务复杂度限制 | 理论上可无限增长 |
| 更新频率 | 每次推理都变化 | 任务过程中频繁变化 | 低频写入、高频读取 |
| 核心目标 | 提供当前推理所需信息 | 维持任务执行的一致性 | 沉淀知识、个性化、持续学习 |
这里想强调一个容易被忽略的点:三者之间不是相互替代关系,而是分层协作关系。一个健康的 Agent 记忆系统,应该是:长期记忆按需检索,检索结果和实时状态写入短期记忆,短期记忆中的关键信息在每次推理时被压缩进 Context。
如果你在设计 Agent 时发现 Context 总是不够用,大概率不是模型窗口太小,而是记忆分层没做好。
3. 为什么 Agent 记忆是“工程问题”而非“模型问题”
很多团队在遇到 Context 超限时,第一反应是换一个上下文更长的模型。这是可以理解的,但往往治标不治本。
3.1 模型窗口扩展的局限性
从材料中也能看到,现在主流模型已经支撑到百万级 token 的上下文窗口。这确实能缓解一部分压力,但它没有解决三个根本问题:
第一,成本问题。长上下文的每次推理都意味着更高的 token 消耗和更长的响应延迟。如果把所有信息都塞进 Context,等于用昂贵的 API 调用代替了本该由存储和检索完成的低成本工作。
第二,注意力衰减问题。模型在处理超长上下文时,对中间位置信息的关注度会降低。信息越多,模型越容易被最新内容带偏,忽略前面的关键约束。这对企业级应用来说是致命的。
第三,状态一致性问题。如果 Agent 的所有状态都“隐式地”存在于上下文文本中,那么一旦上下文被截断或压缩,状态就会丢失。而且你无法用程序化的方式判断当前 Agent“到底知道什么、不知道什么”。这在需要审计和追溯的企业场景里是不可接受的。
3.2 企业级记忆系统的真实需求
企业级 Agent 与个人玩具级 Agent 最大的区别,在于对“可控性”和“可观测性”的要求。
在企业环境里,Agent 的记忆不是一个黑盒。你需要回答这些问题:
- 这个 Agent 记住用户的哪些信息了?存在哪里?
- 这些信息的更新和删除策略是什么?
- 敏感数据是否做了权限隔离?
- 当 Agent 做出错误决策时,能否追溯到是哪个历史信息导致的?
- 当记忆数据出问题时,能否一键回滚或清理?
这些问题都没有办法通过“调大模型窗口”来解决。它们需要的是架构设计:记忆存储用什么中间件、记忆写入和读取的接口如何抽象、记忆更新的触发时机如何控制、记忆内容的权限模型如何设计。
3.3 从提示词工程到记忆工程的转变
过去两年,提示词工程(Prompt Engineering)是非常热门的话题。但你会发现,当 Agent 从“回答问题”走向“执行任务”时,提示词能覆盖的范围越来越有限。原因很简单:提示词是静态的,任务是动态的。
记忆工程解决的问题,就是“如何根据当前任务动态地组装出最合适的提示词”。它需要判断:
- 当前任务需要哪些历史信息?哪些可以丢弃?
- 信息应该以原文形式给模型,还是先做摘要压缩?
- 不同来源的信息发生冲突时,以哪个为准?
- 信息检索不到时,是如实告知,还是用默认值兜底?
这些判断逻辑本身,就是一套复杂的工程系统。框架能帮你把基础设施搭好,但具体的记忆策略仍然需要业务团队根据场景来设计。
4. 记忆系统的分层架构设计与工程治理框架
在进入具体框架之前,先把“目标架构”画出来。这样你在看后面的代码时,才知道每一个组件在整体中承担什么职责。
4.1 分层架构的五层设计
一个适合企业生产的 Agent 记忆系统,建议拆成五层:
第一层:交互接入层。负责接收用户输入、输出 Agent 回复。这一层不做记忆处理,只做格式转换和链路透传。
第二层:短期状态层。维护当前任务的状态机。记录任务进行到哪一步、已经收集了哪些关键字段、有哪些待办事项。在 LangGraph 里,这一层就是 State 对象;在传统开发里,这一层可能是一张任务表。
第三层:上下文编排层。负责在每次推理前,从短期状态和历史记忆中筛选出当前最优的上下文内容,组装成 Prompt。这是记忆系统中最核心、最需要“工程手感”的一层。做得好的上下文编排,能用极少的 token 达到极好的效果。
第四层:长期存储层。负责持久化跨会话的信息。根据数据形态选择不同存储:结构化数据用 Postgres/MySQL,非结构化文本用 Redis 或文件存储,语义搜索用向量数据库。
第五层:治理与审计层。负责记忆内容的权限控制、生命周期管理、质量监控和审计日志。这一层在 Demo 里可以省略,但在企业环境中是合规底线。
4.2 记忆的四个关键操作
无论用哪个框架,记忆系统本质上都在处理四个操作:
写入(Write):什么信息值得记住?不是所有对话内容都需要持久化。需要设计一套“记忆提取”机制,从对话中提炼出有长期价值的信息。
检索(Retrieve):面对一个新的用户请求,如何从大量历史记忆中找到相关的部分?这需要索引和检索策略,可能是关键词匹配、向量相似度,也可能是规则触发的定向查询。
更新(Update):记忆不是一成不变的。用户的偏好会变,业务规则会变,Agent 对某一事实的理解也在变。更新策略要解决“新旧信息冲突时怎么办”的问题。
遗忘(Forget):这一点最容易被忽略,但在企业合规中极其重要。用户有权要求删除自己的数据,过期的业务信息也不应该永远占用存储。遗忘机制包括定期清理、按规则自动过期、以及用户主动触发的数据删除。
这四个操作中,“遗忘”是当前大多数 Agent 框架做得最薄弱的部分。不少团队在搭建记忆系统时,只考虑了怎么存、怎么取,没有考虑怎么删。这在个人工具里问题不大,但在企业系统里,很可能会在合规审查时被一票否决。
4.3 上下文压缩:工程治理的关键手段
即使你已经做好了分层,Context 依然有可能在一次复杂任务中被大量中间结果占满。这时候就需要“上下文压缩”来做兜底。
常见的压缩策略包括:
- 摘要式压缩:把冗长的历史对话用大模型生成一段摘要,保留关键信息,丢弃细节。
- 裁剪式压缩:只保留最近 N 轮对话,更早的内容直接丢弃或转存到长期记忆。
- 关键信息提取:从历史对话中提取结构化字段,比如订单号、时间、金额,保存为 JSON 状态。
选择哪种策略,取决于你对“信息保真度”的要求。有些任务(比如法律咨询),细节就是生命,摘要压缩不可接受;有些任务(比如闲聊式的信息收集),细节无关紧要,摘要就够用。
这里要特别注意:上下文压缩不是越多越好。过度压缩会丢失关键信息,导致 Agent 行为和早期不一致。一个稳妥的做法是:先保存完整的历史到存储层,然后在每次推理时,根据任务需要决定放多少进 Context。换句话说,压缩的目的是“减少 Context 占用”,而不是“删除历史记录”。
5. LangChain 的 Agent 记忆能力与工程边界
LangChain 是最早一批把 Agent 记忆做成标准化模块的框架之一。对于刚接触 Agent 开发的读者,LangChain 是一个不错的起点。
5.1 LangChain 记忆模块的核心思路
LangChain 把记忆抽象为两个接口:ChatMessageHistory负责消息的存储和读取,BaseMemory负责在每次调用前把记忆内容注入到 Prompt 中。
它提供多种内置记忆类型:
| 记忆类型 | 工作原理 | 适用场景 |
|---|---|---|
| ConversationBufferMemory | 保存全部历史消息,直接拼入 Prompt | 短对话、对信息保真度要求高 |
| ConversationBufferWindowMemory | 只保留最近 K 轮消息 | 普通多轮对话 |
| ConversationSummaryMemory | 用摘要替代完整历史 | 长对话、信息密度低 |
| ConversationSummaryBufferMemory | 结合摘要和窗口,兼顾细节与长度 | 大多数业务场景 |
| VectorStoreRetrieverMemory | 从向量库中检索相关历史 | 需要跨会话召回用户偏好 |
5.2 LangChain 记忆实现示例
下面是一个使用ConversationSummaryBufferMemory的示例,组合了摘要和窗口两种策略。它会在对话较短时保留完整消息,在对话超过max_token_limit后,自动把更早的消息改为摘要。
# 文件路径:examples/langchain_memory_demo.py from langchain.memory import ConversationSummaryBufferMemory from langchain_openai import ChatOpenAI llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) memory = ConversationSummaryBufferMemory( llm=llm, max_token_limit=500, return_messages=True, memory_key="chat_history", ) # 模拟多轮对话 memory.save_context({"input": "你好,我叫小林,是一名后端工程师。"}, {"output": "你好小林,很高兴认识你!"}) memory.save_context({"input": "我在负责一个工单系统的重构项目。"}, {"output": "了解,工单系统重构通常要关注状态流转和数据一致性。"}) memory.save_context({"input": "目前最大的痛点是历史工单的检索效率太低。"}, {"output": "这个问题可以考虑引入全文索引或向量检索。"}) # 打印记忆内容,观察摘要和原消息的混用 messages = memory.load_memory_variables({})["chat_history"] for msg in messages: print(f"{msg.type}: {msg.content}")运行这段代码,你会发现最终记忆里既有完整的最近消息,也有被压缩后的历史摘要。LangChain 会调用你配置的大模型来生成摘要,所以max_token_limit的值决定了触发摘要的门槛。
5.3 LangChain 在企业级场景中的边界
LangChain 记忆模块的定位是“开箱即用的组件”,但它有几个问题值得注意:
第一,记忆内容与业务逻辑耦合较浅。LangChain 的记忆本质上是“把历史文本塞进 Prompt”,它不理解你的业务状态。如果你的 Agent 需要精确管理任务状态(比如“当前流程卡在财务审批环节”),LangChain 的通用记忆就不能直接满足。
第二,存储层抽象较薄。LangChain 的ChatMessageHistory可以对接 Redis、SQLite 等,但它没有给出一套完整的“记忆分层、权限隔离、生命周期管理”方案。这些需要你自己做。
第三,复杂的流程控制能力不足。当 Agent 的逻辑从简单的“聊天 + 工具调用”变成复杂的多分支、多角色协作时,LangChain 的链式抽象会变得难以维护。
这并不意味着 LangChain 不好用。恰恰相反,对于 MVP 验证或者中小型项目,LangChain 用极低的成本帮你解决了“让 Agent 有记忆”这个基础问题。只是到了企业级复杂度,你需要更结构化的方案——这就是 LangGraph 出现的背景。
6. LangGraph:用状态机架构治理 Agent 记忆与流程
LangGraph 是 LangChain 团队推出的新一代 Agent 编排框架。它和 LangChain 的核心区别在于:LangChain 是“链”,LangGraph 是“图”。这个差异对记忆管理有深远影响。
6.1 为什么状态图比链更适合 Agent 记忆
在 LangChain 模式里,一个 Agent 的执行流程是预先写好的线性链:接收输入 -> 调用工具 -> 返回输出。如果要处理分支,你需要写大量 if-else 逻辑。
在 LangGraph 模式里,Agent 的执行流程被建模为一张图:节点是计算单元(可以是 Prompt 调用、工具执行、条件判断),边是节点之间的连接关系。每次执行,Agent 根据当前状态决定走哪条边、进入哪个节点。
这个设计对记忆系统最大的好处是:状态成为显式的一等公民。
在 LangGraph 中,你可以定义一个 State 对象,它的字段就是 Agent 需要维护的全部短期记忆。每当 Agent 执行完一个节点,State 中的某些字段会被更新。这种显式状态管理,让“Agent 现在知道什么、不知道什么”变得高度可观测、可测试、可回滚。
6.2 LangGraph 状态记忆与持久化示例
下面是一个基于 LangGraph 的多步任务示例。它模拟了一个“信息收集 -> 审批 -> 执行”的流程,用 State 承载任务状态。
# 文件路径:examples/langgraph_state_demo.py from typing import TypedDict, Annotated, Literal from langgraph.graph import StateGraph, END from langgraph.checkpoint.memory import MemorySaver class AgentState(TypedDict): user_name: str request_type: str need_approval: bool approved: bool messages: list def collect_info(state: AgentState): """模拟信息收集节点。实际项目中这里会对接表单或意图识别模型。""" print(f"[collect_info] 收到请求: {state['request_type']}") return { "need_approval": state["request_type"] in {"退款", "权限申请"}, "messages": state["messages"] + ["信息收集完成"], } def decide_approval(state: AgentState) -> Literal["approve", "direct_execute"]: """条件路由:根据是否需要审批决定下一步走哪个节点。""" print(f"[decide_approval] need_approval = {state['need_approval']}") return "approve" if state["need_approval"] else "direct_execute" def approve(state: AgentState): """模拟审批节点。""" print(f"[approve] 用户 {state['user_name']} 的审批通过") return {"approved": True, "messages": state["messages"] + ["审批通过"]} def execute(state: AgentState): """模拟最终执行节点。""" print(f"[execute] 执行 {state['request_type']},审批状态: {state.get('approved', False)}") return {"messages": state["messages"] + ["执行完成"]} # 构建状态图 builder = StateGraph(AgentState) builder.add_node("collect_info", collect_info) builder.add_node("approve", approve) builder.add_node("execute", execute) builder.set_entry_point("collect_info") builder.add_conditional_edges("collect_info", decide_approval, { "approve": "approve", "direct_execute": "execute", }) builder.add_edge("approve", "execute") builder.add_edge("execute", END) # 使用 MemorySaver 开启状态持久化 checkpointer = MemorySaver() graph = builder.compile(checkpointer=checkpointer) # 模拟一次完整任务 result = graph.invoke( { "user_name": "小林", "request_type": "权限申请", "need_approval": False, "approved": False, "messages": [], }, config={"configurable": {"thread_id": "task-001"}}, ) print("最终状态:", result)这段代码里有几个关键设计:
条件路由对应decide_approval函数,它根据 State 中的need_approval字段决定流程走向。这就是 LangGraph 入门教程中常说的conditional_edge的核心用法。
状态持久化通过MemorySaver实现,thread_id用于区分不同任务。当你想恢复某个未完成的任务时,用同一个thread_id再次调用graph.invoke即可。
显式状态字段让记忆变得可编程。你不需要从大段对话文本里“猜”当前状态,直接读取state["approved"]就知道审批结果。
6.3 LangGraph 子图与模块化记忆
企业级 Agent 系统通常由多个子任务组成。比如一个“订单售后 Agent”可能有退款子图、物流查询子图、人工升级子图。如果全部画在一张图里,节点过多,状态字段也会互相污染。
LangGraph 支持把子图嵌入到父图中。子图可以有自己的内部状态,同时在父图层面共享必要的上下文。这种模块化设计,对记忆的隔离性很有帮助:退款子图的内部状态不会干扰物流查询子图。
从工程治理角度看,这比单个大图更容易测试和维护。每个子图都可以单独验证:输入特定的 State,检查输出是否符合预期。
6.4 LangGraph 与 LangChain 的选择建议
| 对比维度 | LangChain | LangGraph |
|---|---|---|
| 抽象模型 | 链式调用 | 有向状态图 |
| 状态管理 | 隐式,靠 Prompt 和历史消息 | 显式,State 对象 |
| 流程控制 | 线性为主,分支需要额外逻辑 | 原生支持条件路由、循环、并行 |
| 可观测性 | 一般 | 较强,每个节点可以记录状态变化 |
| 学习曲线 | 较平缓 | 稍高,需要理解图模型 |
| 企业级适合度 | 适合 MVP 和简单工具 | 适合复杂业务流和长流程任务 |
现在网上关于“LangGraph 和 LangChain 的区别”讨论很多,有些人认为 LangGraph 会取代 LangChain。更稳妥的判断是:LangChain 提供组件库,LangGraph 提供编排引擎,两者在 LangChain 生态内是互补关系。LangGraph 适合处理有明确流程和状态流转的业务,LangChain 适合快速组合模型和工具。
回到记忆这个话题,如果你只是做“带点历史记忆的聊天机器人”,LangChain 就够了;如果你要做“执行真实业务任务的 Agent”,必须用显式状态管理的方案,否则后期一定会被状态混乱拖垮。从工程治理角度看,LangGraph 提供的图结构和持久化机制正是企业级 Agent 记忆系统需要的底座。
7. DeepAgent:长期记忆与产品化探索
在 LangChain 和 LangGraph 之外,DeepAgent 是最近被频繁讨论的 Agent 框架方向。虽然不同团队对“DeepAgent”的定义不完全一致,但这个名字代表了一个共识:Agent 不能只依赖外部框架的记忆组件,它需要一种更内化、更持久的记忆机制。
7.1 DeepAgent 的产品定位
从行业讨论来看,DeepAgent 方向的框架通常瞄准两个目标:
第一,深度智能体。强调 Agent 不只是一个“调用大模型的壳”,而是具备感知、规划、记忆、工具调用、自我反思的完整智能体。这意味着记忆不是附属功能,而是核心架构的一部分。
第二,持续学习。DeepAgent 框架普遍重视“从过往交互中学习”。比如,用户纠正了 Agent 的错误后,Agent 能否把这次纠正转化为长期记忆,下次不再犯同样的错?这已经不是简单的“历史消息回放”,而是涉及到经验性记忆的沉淀。
7.2 DeepAgent 在记忆系统上的启发
虽然没有统一的官方文档可以直接引用,但从相关讨论中可以提炼出几个有价值的产品设计方向:
- 记忆按主题分组:同一个用户的金融偏好、技术背景、沟通风格分别存储,检索时按主题精准召回。
- 记忆有置信度:Agent 对于“用户是后端工程师”这个信息的置信度会比“用户可能喜欢 Python”更高。置信度低的记忆不应该在决策中占据太高的权重。
- 记忆可被用户编辑:用户可以查看 Agent 记住了自己什么,可以修改或删除。这一点在企业级应用里几乎是刚需。
- 记忆与工具解耦:记忆引擎是整个 Agent 运行时的共享底座,而不是绑定在某个具体的模型或工具链上。
这些理念虽然看起来“很简单”,却在工程实现上有不小的难度。比如“置信度”怎么量化?编辑记忆后,Agent 的行为一致性如何保证?这些问题都需要长期的架构打磨。
7.3 如何从架构视角看待 DeepAgent
对于实际做项目的读者,我的建议是:不要太早绑定某一个具体框架,而是先用框架验证场景,再把验证通过的场景沉淀为通用的记忆抽象层。
你可以把 LangGraph 当作流程编排底座,把 LangChain 当作组件库,把 DeepAgent 理念中关于长期记忆的部分作为你设计存储模型时的参考。更关键的是,自己设计一套面向业务的记忆接口:
# 文件路径:examples/memory_abstraction.py from abc import ABC, abstractmethod class MemoryProvider(ABC): """长期记忆的统一访问接口""" @abstractmethod def save_fact(self, user_id: str, key: str, value: str, confidence: float = 1.0): """保存一条结构化事实""" pass @abstractmethod def get_fact(self, user_id: str, key: str) -> dict | None: """读取一条结构化事实""" pass @abstractmethod def search_semantic(self, user_id: str, query: str, top_k: int = 5) -> list[dict]: """基于语义相似度召回相关历史记忆""" pass @abstractmethod def delete_fact(self, user_id: str, key: str): """删除一条事实,满足用户删除权""" pass这只是一个最小的接口抽象。好处是,你的业务逻辑只依赖这个接口,不依赖底层是 Redis、向量库还是内存。将来要换框架或升级存储,只需要实现新的MemoryProvider。
这个思路比“用哪个框架”本身更重要:框架会迭代,接口是资产。
8. 常见问题与排查思路
在实际的 Agent 记忆系统开发中,下面几个问题是出现频率最高的。把它们整理成一张排查表,建议收藏备用。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
请求报错maximum context length exceeded | 会话历史太长,没有进行压缩或裁剪 | 打印当前 Prompt 的 token 数量,检查是否超过模型限制 | 启用摘要压缩;把历史消息转存到长期记忆;只保留最近 N 轮必要消息 |
消息处理报错context is too large and auto-compaction could not recover | 上下文超出过多,自动压缩后的摘要仍然超过窗口限制 | 检查压缩逻辑的 token 估算是否准确;检查摘要是否保留了过多细节 | 降低摘要生成的max_token_limit;在压缩前先把原文完整持久化;考虑把超长文档改为向量检索 |
| Agent 处理长任务时“忘记”前文 | 状态管理不完善,上下文被截断或覆盖 | 查看状态对象的完整快照,检查状态更新函数是否有误 | 使用 LangGraph 的 State 对象显式维护关键字段;增加关键信息写入日志 |
| 多用户使用同一实例时记忆串线 | 没有按用户/会话维度隔离记忆存储 | 检查记忆存取时是否传入了user_id或thread_id | 所有记忆操作必须带上业务标识;在持久化层增加租户过滤条件 |
| 记忆检索结果与当前问题无关 | 相似度检索策略太粗糙,没有结合业务规则过滤 | 查看召回记录的评分和原始内容 | 增加关键词前置过滤;使用混合检索(关键词 + 向量);为不同记忆类型建立独立索引 |
| 历史记忆被错误更新 | 新旧信息冲突处理逻辑缺失 | 确认更新逻辑是“直接覆盖”还是“读取后合并” | 设计版本化记忆结构;写入前检查是否已有旧值;高置信度旧值不应被低置信度新值覆盖 |
| 用户要求删除数据但无法定位 | 记忆对象与用户身份未做映射 | 检查存储中是否存在按用户维度的索引 | 建立用户 ID 到所有记忆对象的映射表;实现递归删除能力 |
| 多个 Agent 流程共享状态导致干扰 | 子图之间状态未做隔离 | 检查 State 中是否有子图专属字段混入父级 | 为每个子图定义独立的内部 State;父图只保留跨子图共享的必要字段 |
8.1 一个典型排错案例
假设你的 Agent 在处理一个包含 200K tokens 文档的任务时报错:
api error: 400 this model's maximum context length is 1048576 tokens.注意,这里模型上限是 1M tokens,但仍然报错,说明请求内容本身超过了模型的上下文窗口总和(系统提示 + 历史消息 + 工具结果 + 用户输入)。
排查顺序:
- 打印发送给模型的完整请求,统计各部分 token 占比。
- 如果工具结果占大头,把工具结果改为“摘要 + 关键字段提取”,不要直接透传原始文档。
- 如果历史消息占大头,启用摘要压缩,把早期对话转为一段精炼摘要。
- 如果系统提示占大头,拆分出“固定指令”和“动态上下文”,固定指令可以预编译,不参与每次的动态计算。
9. 企业级 Agent 记忆系统的最佳实践与工程建议
最后,把前面所有内容沉淀为 8 条工程建议。这些建议来自对大量 Agent 项目的观察和总结,不一定覆盖所有场景,但值得每一个做企业级 Agent 的团队认真参考。
9.1 记忆分层必须从第一天就做
很多团队在原型阶段用简单的“全量对话记录 + 直接拼 Prompt”跑通了,就以为不需要做记忆分层。项目上线两三个月后,问题集中爆发:Context 经常超限、响应速度越来越慢、数据难以治理。
技术债务一旦产生,重构成本往往远超一开始多花两天设计分层的成本。
9.2 状态管理要显式化、可观测化
不要让你的 Agent 的“当前状态”只存在于模型的隐式理解中。把状态字段拆出来,明确定义每个字段的含义、更新时机、取值范围。这样你才能写单元测试,才能输出审计日志。
在 LangGraph 中,这意味着认真设计 State 的TypedDict。字段不是越多越好,而是“任务真正需要长期跟踪的才放入 State,一次性使用的临时变量留在节点内部”。
9.3 长期记忆要设计“遗忘机制”
这一点再怎么强调都不为过。Agent 的记忆系统如果只能写入、不能删除,在企业级环境中会变成合规风险。建议至少实现以下能力:
- 按用户维度删除全部记忆。
- 按记忆类型设置过期时间。
- 支持人工后台清理敏感内容。
9.4 优先级:检索质量 > 存储容量 > 模型窗口
在有限的研发资源下,把精力放在提升检索召回质量上,比盲目堆存储容量更有价值。一个召回精准的 10 条记忆,胜过检索模糊的 100 条历史记录。
提升检索质量的具体方向包括:为不同数据源建立独立索引、引入时间衰减权重、用业务规则过滤候选集、在应用层对召回结果排序。
9.5 注意 Agent 安全的边界
记忆系统中存储的往往是最敏感的用户信息。在设计时建议遵循最小权限原则:
- 不同角色只能访问自己权限范围内的记忆数据。
- 记忆内容的读取和更新都要有审计日志。
- 在生产环境测试退出或数据清理功能时,先在测试环境验证,并做好备份和回滚方案。
9.6 把上下文压缩做成可回退方案
当 Agent 执行失败需要重试时,如果原始上下文已经被压缩处理过,重试时可能面临信息缺失。稳妥的做法是:压缩前先持久化完整上下文到存储层。这样即使压缩后的重试失败,仍然可以从原始记录中恢复。
9.7 为每个 Agent 任务设定“记忆边界”
不是所有信息都需要被 Agent 记住。在设计阶段,明确回答:这个 Agent 需要记住哪些业务实体?哪些信息随任务结束即可丢弃?哪些信息必须跨会话保留?没有边界地记忆,最终会让系统臃肿、检索变慢、错误率上升。
9.8 框架只是起点,沉淀自己的记忆抽象层
LangChain、LangGraph、DeepAgent 都在快速演进。今天的最佳实践,半年后可能就过时了。唯一能对冲框架变化风险的做法,是在业务代码和框架之间加一层薄薄的内存接口(memory abstraction layer)。业务逻辑只调用你自定义的接口,不直接感知底层用的是哪个框架的哪个存储模块。
这样,当新的框架或更好的存储方案出现时,你的业务代码迁移成本会小很多。
10. 总结与后续学习方向
这篇文章把 Agent 记忆系统的核心命题拆成了三部分:
第一,建立了分层认知。Context 是工作台,短期记忆是便签纸,长期记忆是知识库。三者按需协作,而不是互相替代。理解了这一点,你就不会再犯“把所有信息都塞给模型”的初级错误。
第二,梳理了三个框架的定位差异。LangChain 提供组件化记忆能力,适合快速上手;LangGraph 通过显式状态机和持久化机制,把记忆管理纳入流程治理,是企业级复杂任务的更稳妥选择;DeepAgent 则在长期记忆和智能体产品化方向上做了更前沿的探索,提供了不少值得借鉴的设计理念。
第三,给出了工程治理框架。从分层架构、记忆四操作、上下文压缩,到常见问题排查和 8 条最佳实践,覆盖了一个企业级 Agent 记忆系统从设计到落地的关键环节。
如果你接下来想继续深入,建议按这个顺序实践:
- 先用 LangChain 跑通一个带基础记忆的多轮对话 Agent,感受记忆模块的边界。
- 再用 LangGraph 搭建一个至少包含三个节点、一个条件路由的流程,把状态管理练熟。
- 然后自己实现一个
MemoryProvider接口,把记忆从框架中解耦出来,对接真实的数据库或向量库。 - 最后,在一个真实的业务场景里做一次 Context 超限的“压力测试”,你会发现很多问题只有跑到生产环境附近才会暴露。
Agent 记忆系统没有银弹。它更像是一个持续演进的基础设施工程:模型在变、框架在变、业务需求在变,但“分层、可观测、可治理”的原则不会变。希望这篇文章能帮你少走一些弯路。