栈说明:示例运行在 AgentScope Java(
io.agentscope:agentscope)上,模型为 阿里云通义 DashScope。短期记忆用InMemoryMemory,长期记忆用 Mem0 扩展(Mem0LongTermMemory),上下文压缩可用内置AutoContextMemory。
本文代码:为可独立阅读的 Java 片段;落地单测可参考
module-ai/src/test/java/com/baoma/ai/debug/下既有 AgentScope 测试的写法(AgentScopeDashScopeModels、Msg.builder().textContent(...).block())。
前言
Agent 会做事了,但没有跨会话的记忆——关掉窗口、清空上下文就归零,积累的偏好和踩过的坑下次还要重新交代。这节课你将掌握 Agent 的记忆管理策略,从简单遗忘到主动记忆,让 Agent 越用越懂你。
本篇文章内容涉及
- 建立短期记忆:对话上下文管理与 Token 限制应对
- 三种记忆管理策略:上下文截断、滚动摘要、向量化召回
- 从被动上下文到主动记忆管理:Agent 自主决定何时记、何时读
- 构建短期与长期记忆的混合系统
在本节课程中,你将学习如何为 Agent 赋予记忆能力,解决大语言模型固有的「健忘」问题。你将从一个最朴素的方法开始,逐步发现其局限性,并最终掌握业界主流的短期和长期记忆构建策略。
首先,配置课程所需环境。Java 侧集中封装模型与默认 InMemoryMemory:
package com.baoma.ai.debug.support;
import com.baoma.ai.debug.support.AgentScopeDashScopeModels;
import io.agentscope.core.ReActAgent;
import io.agentscope.core.memory.InMemoryMemory;
import io.agentscope.core.memory.Memory;
import io.agentscope.core.model.DashScopeChatModel;
import io.agentscope.core.model.Model;
/** 记忆课程:统一创建带短期记忆的 ReActAgent。 */
public final class MemoryAgentSupport {
private MemoryAgentSupport() {
}
/**
* 创建课程内容编写 Agent;调用方可覆盖 model / memory / longTermMemory 等。
*/
public static ReActAgent createWriterAgent(
String apiKey,
String modelName,
String sysPrompt,
Memory memory) {
Model model = AgentScopeDashScopeModels.buildDashScopeChatModel(apiKey, modelName);
Memory mem = memory != null ? memory : new InMemoryMemory();
return ReActAgent.builder()
.name("Writer")
.sysPrompt(sysPrompt)
.model(model)
.memory(mem)
.maxIters(24)
.build();
}
public static ReActAgent createWriterAgent(String apiKey, String modelName, String sysPrompt) {
return createWriterAgent(apiKey, modelName, sysPrompt, null);
}
}
// 单测入口示例:校验 API Key 后打印(勿在代码中硬编码 sk-...)
import org.junit.jupiter.api.Assumptions;
import com.baoma.ai.debug.support.AgentScopeDashScopeModels;
String apiKey = AgentScopeDashScopeModels.firstNonBlank(
System.getenv("DASHSCOPE_API_KEY"),
System.getenv("AGENTSCOPE_DEBUG_API_KEY")
);
Assumptions.assumeTrue(apiKey != null && !apiKey.isBlank(), "请配置 DASHSCOPE_API_KEY");
System.out.println("API Key 已加载:" + apiKey.substring(0, Math.min(5, apiKey.length())) + "*****");
1 建立短期记忆
在之前的课程中,你正在构建一个能帮你写作课程的 Agent 团队。内容编写 Agent 刚刚完成了一份出色的初稿。你很满意,并对它说:「很好,现在请根据我们上次讨论的教学风格,把第二部分写得更生动一些。」
然而,Agent 的回应却让你失望:「好的,请问我们上次讨论了什么样的教学风格?」——它忘记了过去的任务细节。这是因为你的 Agent 是无状态的。每次开启新的对话,它就会忘记过去对话的所有内容。
扩展阅读:大语言模型的核心特性——无状态 (Stateless)
你可以将大模型想象成一个记忆力只有几秒钟的专家。在每一次独立的 API 调用中,它能理解你给它的所有信息并给出精彩的回答。但一旦这次调用结束,它会彻底忘记一切。它不会记得你是谁,你们之前聊过什么,你的任何偏好和要求。Agent 的每一次 call 本质上都是一次独立的模型请求,因此它天然地继承了这种无状态性。
那我们该如何解决呢?一个最直接的想法,就是每次和它说话时,都把之前的聊天记录「复习」一遍。
在编程实现中,这意味着你需要维护对话历史列表,每次向 Agent 提问时都把完整历史一并格式化进 Prompt。AgentScope Java 中的 InMemoryMemory 就是这种朴素方案的实现:每次 agent.call(msg) 时,Formatter 会把 memory.getMessages() 中的历史与当前消息一起发给模型。
Java:验证短期记忆
import com.baoma.ai.debug.support.MemoryAgentSupport;
import io.agentscope.core.ReActAgent;
import io.agentscope.core.message.Msg;
import java.util.Objects;
String sysPrompt = "你是一个课程内容编写员。你的任务是编写一篇 Pandas 数据分析课程。";
ReActAgent writingAgent = MemoryAgentSupport.createWriterAgent(apiKey, modelName, sysPrompt);
// 第一次对话:设定教学风格
Msg msg1 = Msg.builder().textContent("我们的教学风格要严谨克制,请记住这一点。").build();
System.out.println("[user]: " + msg1.getTextContent());
Msg reply1 = Objects.requireNonNull(writingAgent.call(msg1).block(), "首轮回复为空");
System.out.println("[" + reply1.getName() + "]: " + reply1.getTextContent());
System.out.println("\n" + "=".repeat(20) + "\n");
// 第二次对话:基于之前的设定提出新要求
// writingAgent 会自动将 InMemoryMemory 中的历史与新消息一起发给模型
Msg msg2 = Msg.builder().textContent("很好,现在请把第二部分写得更专业一些。").build();
System.out.println("[user]: " + msg2.getTextContent());
Msg reply2 = Objects.requireNonNull(writingAgent.call(msg2).block(), "次轮回复为空");
System.out.println("[" + reply2.getName() + "]: " + reply2.getTextContent());
System.out.println("\n" + "=".repeat(20) + "\n");
System.out.println("Agent 的短期记忆内容:");
for (Msg m : writingAgent.getMemory().getMessages()) {
System.out.printf("- [%s] %s: %s%n", m.getRole(), m.getName(), m.getTextContent());
}
这个方案立竿见影,Agent 立刻拥有了当前会话内的短期对话记忆。
2 信息精炼
但当你把这个 Agent 投入真实场景,连续使用十几轮、几十轮对话后,两个严重的问题会浮现出来:
| 问题 | 说明 |
|---|---|
| 上下文窗口限制 | 每个大模型都有最大上下文长度;历史像滚雪球一样变大,最终超出窗口,请求失败。 |
| 急剧上升的成本 | API 按 Token 计费;多轮后每次都要重发全部历史,输入 Token 重复计费。 |
| 性能下降 | 注意力稀释:上下文越长,模型遗忘细节的概率越大。中间迷失:模型更易记住开头与结尾,忽略中间关键信息。 |
这种朴素的「记忆」方案,只是一种寅吃卯粮的短期策略。它很快就会导致程序异常或成本超支。
这引出了一个核心问题:如何在不牺牲关键信息的前提下,有效管理上下文的长度与成本?
这个问题的本质,是如何对信息进行高效的压缩和筛选。就像你在准备开卷考试时,不会把整本教科书都抄到小抄上,而是会提炼出最重要的公式、定义和关键论点。
3 记忆管理策略
让我们借鉴人类准备考试的思路,探索管理 Agent 记忆的策略。
3.1 策略一:简单「遗忘」—固定窗口截断 (Context Truncation)
最简单粗暴的方法,就是只记最近发生的事,这叫作固定窗口截断。

| 维度 | 内容 |
|---|---|
| 思路 | 设定固定窗口:只保留最近 N 轮对话,或最近 N 条消息;超出时丢弃最老的记录。 |
| 相对优势 | 实现简单、开销小,上下文长度可控,避免报错与成本无限增长。 |
| 适用场景 | 信息价值随时间快速衰减:闲聊、简单客服。 |
| 边界条件 | 早期关键信息(如第一轮设定的核心目标)被截断后,Agent 再次「失忆」,对话逻辑断裂。 |
| 性能隐患 | 窗口长期接近上限时,模型仍带着接近极限的长上下文工作,注意力稀释依旧存在。 |
AgentScope Java 做法
package com.baoma.ai.debug;
import com.baoma.ai.debug.support.MemoryAgentScopeTestSupport;
import com.baoma.ai.debug.support.MemoryAgentSupport;
import com.baoma.ai.debug.support.WindowedInMemoryMemory;
import io.agentscope.core.ReActAgent;
import io.agentscope.core.message.Msg;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.util.Objects;
class AgentContextTruncationAgentScopeTest {
private static final int WINDOW_MAX_MESSAGES = 4;
@Test
@DisplayName("AgentScope:窗口截断后难以复述最早规则")
void contextTruncationForgetsEarliestRules() {
String apiKey = MemoryAgentScopeTestSupport.requireDashScopeApiKey();
String modelName = MemoryAgentScopeTestSupport.memoryModelName();
MemoryAgentScopeTestSupport.printApiKeyLoaded(apiKey);
String truncSysPrompt = """
你是一个健忘的机器人,你只能记住最近发生的事情。
用户会逐条给出规则 A/B/C/D;你只能根据当前对话记忆中仍存在的规则作答。
若记忆中已无某条规则,请明确说「记忆中无该规则」,不要编造。
""";
WindowedInMemoryMemory windowed = new WindowedInMemoryMemory(WINDOW_MAX_MESSAGES);
ReActAgent truncationAgent = MemoryAgentSupport.createWriterAgent(
apiKey, modelName, truncSysPrompt, windowed);
callUser(truncationAgent, "规则A:所有回答必须是陈述句。");
callUser(truncationAgent, "规则B:不能使用「你」或「我」。");
callUser(truncationAgent, "规则C:回答要尽量简短。");
callUser(truncationAgent, "规则D:数字必须用大写汉字表示。");
System.out.println("经过多轮对话后,Memory 中仍保留的消息(窗口上限 "
+ WINDOW_MAX_MESSAGES + " 条):");
MemoryAgentScopeTestSupport.printMemoryDump(truncationAgent.getMemory().getMessages());
Assertions.assertTrue(truncationAgent.getMemory().getMessages().size() <= WINDOW_MAX_MESSAGES);
MemoryAgentScopeTestSupport.printSeparator();
Msg summaryReply = Objects.requireNonNull(
truncationAgent.call(Msg.builder().textContent("请总结一下所有规则。").build()).block(),
"总结回复为空");
MemoryAgentScopeTestSupport.printAgentMsg(summaryReply);
boolean memoryMentionsRuleA = truncationAgent.getMemory().getMessages().stream()
.map(Msg::getTextContent)
.filter(Objects::nonNull)
.anyMatch(t -> t.contains("规则A") || t.contains("陈述句"));
System.out.println("\n[观测] Memory 中是否仍含「规则A」: " + memoryMentionsRuleA);
}
private static void callUser(ReActAgent agent, String text) {
Msg user = Msg.builder().textContent(text).build();
MemoryAgentScopeTestSupport.printUserMsg(user);
Msg reply = Objects.requireNonNull(agent.call(user).block(), "Agent 返回为空");
MemoryAgentScopeTestSupport.printAgentMsg(reply);
System.out.println();
}
}
方式 B:生产环境可结合 AutoContextMemory 按 Token 阈值自动压缩,而非单纯丢弃。
3.2 策略二:提炼重点——滚动摘要 (Rolling Summary)
简单截断会直接丢弃信息,显然不够理想。更聪明的做法是:在遗忘细节之前,先把重点提炼出来——这就是滚动摘要策略。

| 维度 | 内容 |
|---|---|
| 思路 | 历史快满时,用模型将最早一段对话压缩成摘要消息,用摘要替换冗长原文。 |
| 相对优势 | 压缩长度的同时保留核心信息,长期连贯性更好;去掉无关上下文可缓解注意力稀释。 |
| 适用场景 | 项目规划、长篇内容创作等需长期保持目标一致的任务。 |
| 边界条件 | 额外 API 调用成本;摘要质量直接影响后续对话。 |
在 AgentScope Java 中,同类能力已由扩展模块 AutoContextMemory 提供:按 maxToken、tokenRatio 等配置在超限时调用模型做上下文压缩,并可 offload 大段工具输出。
package com.baoma.ai.debug;
import com.baoma.ai.debug.support.AgentScopeDashScopeModels;
import com.baoma.ai.debug.support.MemoryAgentScopeTestSupport;
import io.agentscope.core.ReActAgent;
import io.agentscope.core.memory.autocontext.AutoContextConfig;
import io.agentscope.core.memory.autocontext.AutoContextMemory;
import io.agentscope.core.message.Msg;
import io.agentscope.core.model.Model;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.util.Objects;
class AgentAutoContextMemoryAgentScopeTest {
@Test
@DisplayName("AgentScope:AutoContextMemory 多轮对话后可压缩历史")
void rollingSummaryWithAutoContextMemory() {
String apiKey = MemoryAgentScopeTestSupport.requireDashScopeApiKey();
String modelName = MemoryAgentScopeTestSupport.memoryModelName();
MemoryAgentScopeTestSupport.printApiKeyLoaded(apiKey);
Model model = AgentScopeDashScopeModels.buildDashScopeChatModel(apiKey, modelName);
AutoContextConfig ctxConfig = AutoContextConfig.builder()
.maxToken(8_000)
.tokenRatio(0.75)
.lastKeep(6)
.msgThreshold(8)
.build();
AutoContextMemory summaryMemory = new AutoContextMemory(ctxConfig, model);
ReActAgent rollingAgent = ReActAgent.builder()
.name("WriterWithSummary")
.sysPrompt("你是课程内容编写员。用户会说明教学风格与章节要求;请简洁确认并记住要点。")
.model(model)
.memory(summaryMemory)
.maxIters(24)
.build();
callUser(rollingAgent, "我们的教学风格要风趣幽默,请记住。");
callUser(rollingAgent, "第二章需要增加一个关于成本的案例。");
callUser(rollingAgent, "第三章要强调 Pandas 的 groupby 用法。");
callUser(rollingAgent, "请用一句话复述我们约定的教学风格。");
boolean compressed = summaryMemory.compressIfNeeded();
System.out.println("compressIfNeeded() 是否触发压缩: " + compressed);
System.out.println("压缩事件数: " + summaryMemory.getCompressionEvents().size());
MemoryAgentScopeTestSupport.printSeparator();
System.out.println("压缩后 Memory 中的消息:");
MemoryAgentScopeTestSupport.printMemoryDump(summaryMemory.getMessages());
Assertions.assertFalse(summaryMemory.getMessages().isEmpty());
}
private static void callUser(ReActAgent agent, String text) {
Msg user = Msg.builder().textContent(text).build();
MemoryAgentScopeTestSupport.printUserMsg(user);
Msg reply = Objects.requireNonNull(agent.call(user).block(), "Agent 返回为空");
MemoryAgentScopeTestSupport.printAgentMsg(reply);
System.out.println();
}
}
Java 侧由
AutoContextMemory在压缩流程中写入结构化摘要/压缩事件,可通过getCompressionEvents()观测。
3.3 策略三:构建知识库——向量化召回 (Vector-based Retrieval)
前面的策略依然在用一种线性的、无差别的方式处理所有对话历史。但这并不符合人类的记忆模式——记忆更像相互关联的知识网络,而非按时间播放的磁带。
按需检索是构建高级记忆系统的核心:不再试图把所有历史都塞进上下文,而是将每轮对话变成可检索的「记忆碎片」,存入长期记忆库。

| 步骤 | 说明 |
|---|---|
| 存储 (Ingestion) | 每轮对话结束后,你将对话内容转换成一个数学向量(Embedding)对话内容 Embedding 后连同原文写入向量库(或 Mem0 托管层)。 |
| 检索 (Retrieval) | 用户提出新问题向量化,你先将这个问题也转换成向量,然后去数据库中进行相似度搜索,找出与当前问题最相关的几条历史对话记录。 |
| 组合 (Composition) | 将检索到的记忆 + 最新问题组成精简上下文,再调用大模型。 |
| 维度 | 内容 |
|---|---|
| 相对优势 | 摆脱上下文长度硬上限;按当前意图精准「回忆」,可以极大的节约成本。 |
| 适用场景 | 个性化助手、企业知识库、智能学习伴侣等复杂场景。 |
| 边界条件 | 复杂度最高:Embedding、向量库/记忆服务、检索质量调优。 |
AgentScope Java:Mem0LongTermMemory
AgentScope Java 通过 Mem0LongTermMemory(io.agentscope.core.memory.mem0)集成 Mem0。Java 版通过 Mem0 API(Mem0ApiType.PLATFORM 云服务,或 SELF_HOSTED 自建)访问记忆层,无需在单测里本地起 Qdrant。
支持两种工作模式:
| Java 枚举 | Python 字符串 | 行为 |
|---|---|---|
LongTermMemoryMode.STATIC_CONTROL |
static_control |
每次回复前后自动保存/检索记忆 |
LongTermMemoryMode.AGENT_CONTROL |
agent_control |
Agent 获得 record_to_memory / retrieve_from_memory 工具,自主决定何时记、何时读 |
Java 单测:Mem0 长期记忆(STATIC_CONTROL + AGENT_CONTROL)
文件:module-ai/src/test/java/com/baoma/ai/debug/AgentMem0LongTermMemoryAgentScopeTest.java
内含两个 @Test:mem0StaticControlCrossSessionRecall(§3.3)、mem0AgentControlRecordAndRetrieve(§3.4)。
package com.baoma.ai.debug;
import com.baoma.ai.debug.support.AgentScopeDashScopeModels;
import com.baoma.ai.debug.support.MemoryAgentScopeTestSupport;
import io.agentscope.core.ReActAgent;
import io.agentscope.core.memory.InMemoryMemory;
import io.agentscope.core.memory.LongTermMemoryMode;
import io.agentscope.core.memory.mem0.Mem0ApiType;
import io.agentscope.core.memory.mem0.Mem0LongTermMemory;
import io.agentscope.core.message.Msg;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.time.Duration;
import java.util.Objects;
class AgentMem0LongTermMemoryAgentScopeTest {
@Test
@DisplayName("AgentScope:Mem0 STATIC_CONTROL 清空短期记忆后仍能回忆进度")
void mem0StaticControlCrossSessionRecall() {
String apiKey = MemoryAgentScopeTestSupport.requireDashScopeApiKey();
MemoryAgentScopeTestSupport.assumeMem0ApiKeyPresent();
String modelName = MemoryAgentScopeTestSupport.memoryModelName();
MemoryAgentScopeTestSupport.printApiKeyLoaded(apiKey);
Mem0LongTermMemory longTermMemory = buildMem0LongTermMemory("LTM_Writer_Static");
ReActAgent ltmAgentStatic = ReActAgent.builder()
.name("LTM_Writer_Static")
.sysPrompt("你是一个拥有长期记忆的课程编写员。请根据已知记忆准确回答用户。")
.model(AgentScopeDashScopeModels.buildDashScopeChatModel(apiKey, modelName))
.memory(new InMemoryMemory())
.longTermMemory(longTermMemory)
.longTermMemoryMode(LongTermMemoryMode.STATIC_CONTROL)
.maxIters(24)
.build();
Msg msg1 = Msg.builder()
.textContent("记住,我们正在写一篇 Pandas 数据分析课程,目前已经写完初稿。")
.build();
MemoryAgentScopeTestSupport.printUserMsg(msg1);
Msg reply1 = Objects.requireNonNull(ltmAgentStatic.call(msg1).block(), "首轮回复为空");
MemoryAgentScopeTestSupport.printAgentMsg(reply1);
System.out.println("\n" + "=".repeat(20) + " 模拟新的一次会话 " + "=".repeat(20) + "\n");
ltmAgentStatic.getMemory().clear();
System.out.println("Agent 的短期记忆已被清空。");
Msg msg2 = Msg.builder().textContent("我们上次的工作进度到哪儿了?").build();
MemoryAgentScopeTestSupport.printUserMsg(msg2);
Msg reply2 = Objects.requireNonNull(ltmAgentStatic.call(msg2).block(), "次轮回复为空");
MemoryAgentScopeTestSupport.printAgentMsg(reply2);
Assertions.assertFalse(Objects.requireNonNullElse(reply2.getTextContent(), "").isBlank());
}
@Test
@DisplayName("AgentScope:Mem0 AGENT_CONTROL 主动记录与检索写作风格")
void mem0AgentControlRecordAndRetrieve() {
String apiKey = MemoryAgentScopeTestSupport.requireDashScopeApiKey();
MemoryAgentScopeTestSupport.assumeMem0ApiKeyPresent();
String modelName = MemoryAgentScopeTestSupport.memoryModelName();
MemoryAgentScopeTestSupport.printApiKeyLoaded(apiKey);
Mem0LongTermMemory longTermMemory = buildMem0LongTermMemory("LTM_Writer_Active");
ReActAgent ltmAgentActive = ReActAgent.builder()
.name("LTM_Writer_Active")
.sysPrompt("""
你是一个拥有主动记忆管理能力的课程编写员。
你可以使用以下工具来管理你的长期记忆:
- record_to_memory:将一段重要的信息记录到长期记忆中。
- retrieve_from_memory:根据查询从长期记忆中检索相关信息。
在回答问题前,先思考是否需要检索记忆。在对话结束后,思考是否有关键信息需要记录。
用户说明「核心要求」「请记住」类信息时,应调用 record_to_memory 落库。
""")
.model(AgentScopeDashScopeModels.buildDashScopeChatModel(apiKey, modelName))
.memory(new InMemoryMemory())
.longTermMemory(longTermMemory)
.longTermMemoryMode(LongTermMemoryMode.AGENT_CONTROL)
.maxIters(32)
.build();
Msg activeMsg1 = Msg.builder()
.textContent("课程的写作风格必须非常严谨和学术化,这是一个核心要求。")
.build();
MemoryAgentScopeTestSupport.printUserMsg(activeMsg1);
Msg activeReply1 = Objects.requireNonNull(ltmAgentActive.call(activeMsg1).block(), "首轮回复为空");
MemoryAgentScopeTestSupport.printAgentMsg(activeReply1);
System.out.println("\n" + "=".repeat(20) + " 模拟一次新的会话 " + "=".repeat(20) + "\n");
ltmAgentActive.getMemory().clear();
Msg activeMsg2 = Msg.builder()
.textContent("我忘了,我们课程的写作风格是什么来着?")
.build();
MemoryAgentScopeTestSupport.printUserMsg(activeMsg2);
Msg activeReply2 = Objects.requireNonNull(ltmAgentActive.call(activeMsg2).block(), "次轮回复为空");
MemoryAgentScopeTestSupport.printAgentMsg(activeReply2);
Assertions.assertFalse(Objects.requireNonNullElse(activeReply2.getTextContent(), "").isBlank());
}
private static Mem0LongTermMemory buildMem0LongTermMemory(String agentName) {
String mem0Key = MemoryAgentScopeTestSupport.mem0ApiKeyOrNull();
var builder = Mem0LongTermMemory.builder()
.agentName(agentName)
.userId("memory-demo-user")
.apiKey(mem0Key)
.timeout(Duration.ofSeconds(60));
String baseUrl = AgentScopeDashScopeModels.firstNonBlank(System.getenv("MEM0_API_BASE_URL"));
if (baseUrl != null) {
builder.apiBaseUrl(baseUrl).apiType(Mem0ApiType.SELF_HOSTED);
} else {
builder.apiType(Mem0ApiType.PLATFORM);
}
return builder.build();
}
}
3.4 进阶:从被动上下文到主动记忆管理
一个真正智能的 Agent,不应只是被动接收你处理好的上下文,它应该能主动地管理自己的记忆。
将 longTermMemoryMode 设为 AGENT_CONTROL 时,ReActAgent 会暴露 record_to_memory、retrieve_from_memory 工具。实现见上一节单测中的 mem0AgentControlRecordAndRetrieve()。
通过这种方式,记忆不再是你从外部「喂」给 Agent 的数据,而是它自己主动获取、存储和维护的内在知识。
扩展阅读:成本效益分析
向量化召回和主动记忆管理引入了 Embedding、向量检索以及额外的工具调用推理,表面上增加了单次调用的成本与时延。
但更公平的比较是:为获得一个「可用」的答案,两条路径的总成本是多少?
| 路径 | 描述 |
|---|---|
| 路径 A(无长期记忆) | 一次调用得到错误/遗忘关键要求的答案 → 用户手动提醒 → 再次调用,总成本翻倍且体验差。 |
| 路径 B(有长期记忆) | 一次调用(含检索)直接得到可用答案,问题解决。 |
为记忆系统增加的投入,是确保产出质量、避免重复无效尝试的必要投资。
3.5 构建短期与长期记忆系统
通过上述策略的组合,你可以为 Agent 构建完整的记忆系统:
| 类型 | 实现要点 | 职责 |
|---|---|---|
| 短期记忆 |
InMemoryMemory + 窗口截断 / AutoContextMemory
|
维持当前会话连贯性,如「第二章要加一个案例」 |
| 长期记忆 |
Mem0LongTermMemory + STATIC_CONTROL / AGENT_CONTROL
|
跨会话持久化与检索,如「课程面向初学者、风格风趣幽默」 |
拥有记忆的 Agent,不再是一次性的问答机器;它能从经验中学习,记住偏好,理解长期语境,从「工具」向「伙伴」进化。
落地实践与使用建议
一、快速引入记忆能力
| 方案 | 说明 |
|---|---|
| AgentScope Java |
InMemoryMemory 作基础短期记忆;AutoContextMemory 作上下文压缩;Mem0LongTermMemory 作长期记忆。 |
| Mem0 | 专为 AI 应用设计的记忆层,封装向量化、冲突处理等;Java 通过 API 接入。 |
| Spring AI |
ChatMemory、MessageWindowChatMemory、MessageChatMemoryAdvisor 等(见仓库 博客_Spring_AI_对话记忆_ChatMemory_MySQL与Advisor实践.md)。 |
| 阿里云百炼 | 可视化 Agent 流程中可配置长期记忆,适合快速验证。 |
二、记忆使用建议
-
有选择地记忆:低价值噪声会干扰检索;仅在用户显式「请记住…」或重要性高时写入;
AGENT_CONTROL便于让模型自行判断。 - 持续治理:定期清理过时条目、合并重复、校验事实;提供用户查看/修改/删除记忆的接口。
- 场景化应用:课程文档工作流可能不记录个性化偏好,但 API 参数、功能限制等产品事实值得记录并定期审查。
4 总结
让我们回顾一下你在本节学到的知识:
- 问题的根源:无状态性 — 大模型每次调用独立;朴素传递完整历史会导致上下文超长与成本失控。
- 短期记忆 vs 长期记忆 — 用「截断」与「摘要/压缩」管理短期;用「向量化召回 / Mem0」构建长期,实现跨会话存取。
-
主动记忆管理 —
AGENT_CONTROL下 Agent 通过record_to_memory、retrieve_from_memory主动管理记忆,从被动上下文进化为可学习与成长的智能体。 - 记忆的最佳实践 — 有选择地写入、持续清理与更新,并按业务场景决定记什么、不记什么。
Java 实现要点(便于对照代码)
| 能力 | Java API |
|---|---|
| 短期缓冲 |
InMemoryMemory,getMessages() / clear()
|
| 固定窗口截断 | 自定义 WindowedInMemoryMemory 或业务层 deleteMessage(0)
|
| 滚动摘要/压缩 |
AutoContextMemory + AutoContextConfig
|
| 长期记忆 |
Mem0LongTermMemory.builder() + LongTermMemoryMode
|
| 多轮调用 |
ReActAgent.call(Msg).block()(Project Reactor) |
| 模型与 Key |
AgentScopeDashScopeModels.buildDashScopeChatModel,Key 仅环境变量
|