Agent 对话为什么会「失忆」?ChatMemory 三种策略对比与工业选型

你在第三轮对话里问"我之前说我是谁?",Agent 沉默了。不是 LLM 不够聪明,是你选的 Memory 策略不对。


问题:Agent 的「失忆」从哪来?

Agent 每轮对话都是独立的 HTTP 请求。如果不做任何处理,LLM 看到的是:

arduino 复制代码
请求 1: "我叫张三,是 CNC-001 的运维工程师。"
请求 2: "CNC-001 有什么告警?"              ← 已经不知道你是谁了
请求 3: "我之前说我是谁?"                    ← LLM:???

LLM 是无状态的。 每轮对话它只看当前请求的消息。要让 Agent 记住上下文,必须把历史消息带上。

这就是 ChatMemory 的作用------在每轮请求时,把历史消息拼接进去。但拼多少?怎么拼?这就是策略选择的问题。


三种策略

LangChain4j 提供了两种 ChatMemory 实现(本质上对应两种淘汰策略):

1. MessageWindowChatMemory --- 按消息条数

java 复制代码
// 保留最近 20 条消息(默认)
ChatMemory memory = MessageWindowChatMemory.withMaxMessages(20);

逻辑最简单:消息队列,超出窗口的最早消息自动丢弃。20 条消息大约覆盖 4-5 轮完整对话(用户消息 + AI 回复 + 工具调用结果算多条消息)。

2. TokenWindowChatMemory --- 按 token 数量

java 复制代码
// 保留最近 2000 个 token
ChatMemory memory = TokenWindowChatMemory.withMaxTokens(2000, new OpenAiTokenizer());

按 token 计数,超出窗口的消息丢弃。2000 个 token 约等于 1500 个中文字符或 10-15 轮短对话。

3. 短窗口(实验对照)

额外加一个极短的 MessageWindowChatMemory(4),专门用来模拟"记忆不够用"的场景:

java 复制代码
ChatMemory shortMemory = MessageWindowChatMemory.withMaxMessages(4);

4 条消息------两轮对话就满了。用来对比验证 Memory 策略对 Agent 行为的影响。


实验:同一段对话,三种记忆

我在 industrial-agent-long 中写了一个 MemoryComparisonService,用同样的三段对话分别跑三个策略:

java 复制代码
// 标准对话:自我介绍 → 查询设备 → 追问身份
List<String> conversation = List.of(
    "你好,我叫张三,是CNC-001的运维工程师。",
    "CNC-001现在有什么告警吗?",
    "我之前说我是谁?我叫什么名字?我负责哪台设备?"
);

Map<String, List<String>> results = memoryComparison.compare(conversation);

典型结果:

策略 第三轮回复 结论
messageWindow(20) "你是张三,CNC-001 的运维工程师" ✅ 正确记忆
messageWindow(4) "抱歉,我无法确定你的身份" ❌ 失忆
tokenWindow(2000t) "你是张三,负责 CNC-001" ✅ 正确记忆

短窗口(4)的「失忆」过程:第二轮对话结束时,4 条消息的窗口已满(用户自我介绍 + AI 回复 + 用户问告警 + AI 回复工具调用)。第三轮追问身份时,第一轮的自我介绍已经被淘汰了------Agent 确实「忘了」你是谁。

工业场景怎么选?

场景 推荐策略 窗口大小 原因
运维对话助手 MessageWindow 20-30 对话轮次数基本固定(查告警→查数据→诊断),消息数可控
RAG 知识库问答 TokenWindow 3000-4000 检索到的文档片段可能很长,token 数不可控,按 token 限制更安全
设备配置向导 MessageWindow 10 步骤数固定,不需要长记忆
多轮深度诊断 MessageWindow 40+ 需要回溯根因,上下文越长越好
Token 预算敏感 TokenWindow 1000-2000 精确控制每次请求的 token 消耗

一个容易忽略的细节:工具调用的返回结果也算消息。如果你查了一次数据库返回 500 行的 JSON,这一条消息就可能占满整个 token 窗口。


MessageWindow vs TokenWindow 的本质区别

维度 MessageWindow TokenWindow
淘汰依据 消息条数 token 数量
可预测性 高(消息数固定) 低(取决于每条消息长度)
成本控制 弱(单条消息可能很长) 强(硬上限)
适用场景 对话轮次可控 单条消息长度不可控

选择逻辑:如果你的对话是「短消息 + 多轮次」→ MessageWindow;如果你的对话包含「长文档」→ TokenWindow。如果不确定,用 TokenWindow 更安全------至少你不会因为一条 2000 token 的消息而超过上下文窗口。


持久化:重启不丢记忆

当前 MessageWindowChatMemory 存在 JVM 内存中,服务重启就没了。LangChain4j 预留了 ChatMemoryStore 接口:

java 复制代码
// 概念示意(LangChain4j 0.35.0 需要自行实现)
ChatMemory persistentMemory = MessageWindowChatMemory.builder()
    .maxMessages(20)
    .chatMemoryStore(new YourRedisStore())  // 接入 Redis / DB
    .build();

什么时候需要持久化?

  • 单实例部署 + 非关键对话:内存就够了
  • 多实例部署:必须共享 Memory(用户可能被路由到不同实例)
  • 关键诊断场景:持久化可以审计------"Agent 说了什么?为什么做出这个判断?"

一句话总结

Agent 的「记忆」不是免费的------每条历史消息都是 token。ChatMemory 的本质是用淘汰策略在「记住足够上下文」和「控制 token 成本」之间做权衡。

短对话用小窗口,长诊断用大窗口,RAG 场景用 TokenWindow------没有银弹,但有正确的选择。


本文由 LaoLiang 原创,首发于掘金/知乎/微信公众号。转载请联系作者。

相关推荐
vilya3 小时前
手机端 Agent 怎么动起来:Kotlin 与 Python 之间的桥
android·agent
QYR_113 小时前
机械制造用固体润滑涂层市场分析:2032年全球市场规模预计达8.78亿元,CAGR为6.2%
大数据·人工智能·物联网
奈落246 小时前
独立的AI编程助手正在消失?从文心快码并入百度搭子、Qwen Code转向CLI,看编程工具正被吸收进更大的智能体平台
百度·agent·ai编程·ai 编程
七牛开发者7 小时前
从 Bun 到 Deno,AI 时代的开发工具正在经历什么?
agent·deno·bun
李永奉7 小时前
日常使用的锂电池上,新、旧锂电池的差异分析
单片机·嵌入式硬件·物联网·智能手机·电脑
用户721746588267 小时前
默认校验只覆盖 3.18%:bl dataset validate 抽样窗与 capability 三种返回态的字段级拆解
llm·agent·ai编程
Csvn7 小时前
延迟别再看平均值了:AI 应用指标与告警实战(O03)
人工智能·aigc·agent
用户21296835261688 小时前
我给自己的 LLM 框架重写了记忆系统,然后三天吃出两个真 Bug
agent
hpoenixf8 小时前
Agent 持续进化的秘诀:让你的 Agent 从大哥大进化成智能手机
agent
fellow999 小时前
DSH Desktop:把 DeepSeek Harness 变成真正的桌面应用
electron·agent·ai编程·deepseek