Malize's blog Malize's blog
首页
  • 设计模式

    • 设计模式总览
    • 工作中用到的设计模式
  • 并发编程

    • 死锁
  • Git 工具

    • Git 笔记总览
    • Git 使用手册
    • Git 修改分支名
  • 技术文档

    • Docker 核心命令大全
    • Markdown 使用教程
    • npm 常用命令
    • yaml 语言教程
    • Nodejs 递归读文件
  • 构造问答系统

    • 项目背景
    • 构建答疑机器人
    • 扩展知识范围
    • 优化提示词
    • 自动化评测
    • 优化 RAG 应用
  • 构建 Agent 系统

    • Agent 基础与工具调用
    • 规划与执行
    • 多 Agent 团队协作
    • Memory 积累经验
    • Skill 可复用流程
    • Qwen Code 实践
  • 交付上线

    • 走向生产环境
    • 模型蒸馏
    • 部署模型
    • 生产实践
    • 安全合规
  • 规范 & 实践

    • 代码规范
    • sharding-jdbc
    • CIM 半导体行业
    • HTML 常用 meta
    • CSS 技巧收藏
  • GitHub & 博客

    • GitHub 高级搜索技巧
    • GitHub Actions 自动部署
    • 博客搭建 - 百度收录
  • 优质网站
  • 前端库推荐
  • 成长学习

    • 学习方法
    • 敏捷开发实战
    • 提示词工程
  • 生活

    • 实用技巧
    • 心情杂货
    • 梦境与灵感
  • 索引

    • 分类
    • 标签
    • 按年归档
GitHub (opens new window)

Malize

持续学习,持续成长
首页
  • 设计模式

    • 设计模式总览
    • 工作中用到的设计模式
  • 并发编程

    • 死锁
  • Git 工具

    • Git 笔记总览
    • Git 使用手册
    • Git 修改分支名
  • 技术文档

    • Docker 核心命令大全
    • Markdown 使用教程
    • npm 常用命令
    • yaml 语言教程
    • Nodejs 递归读文件
  • 构造问答系统

    • 项目背景
    • 构建答疑机器人
    • 扩展知识范围
    • 优化提示词
    • 自动化评测
    • 优化 RAG 应用
  • 构建 Agent 系统

    • Agent 基础与工具调用
    • 规划与执行
    • 多 Agent 团队协作
    • Memory 积累经验
    • Skill 可复用流程
    • Qwen Code 实践
  • 交付上线

    • 走向生产环境
    • 模型蒸馏
    • 部署模型
    • 生产实践
    • 安全合规
  • 规范 & 实践

    • 代码规范
    • sharding-jdbc
    • CIM 半导体行业
    • HTML 常用 meta
    • CSS 技巧收藏
  • GitHub & 博客

    • GitHub 高级搜索技巧
    • GitHub Actions 自动部署
    • 博客搭建 - 百度收录
  • 优质网站
  • 前端库推荐
  • 成长学习

    • 学习方法
    • 敏捷开发实战
    • 提示词工程
  • 生活

    • 实用技巧
    • 心情杂货
    • 梦境与灵感
  • 索引

    • 分类
    • 标签
    • 按年归档
GitHub (opens new window)
  • 课程准备

  • 构造问答系统

  • 构建Agent系统

    • 从回答问题到解决问题
    • Agent 基础与工具调用
    • 让 Agent 学会规划与执行
    • 用多 Agent 实现团队协作
    • 用 Memory 让 Agent 积累经验
      • 🚄 前言
      • 🍁 课程目标
      • 1 建立短期记忆
      • 2 信息精炼
      • 3 记忆管理策略
        • 3.1 策略一:简单"遗忘"——固定窗口截断 (Context Truncation)
        • 3.2 策略二:提炼重点——滚动摘要 (Rolling Summary)
        • 3.3 策略三:构建知识库——向量化召回 (Vector-based Retrieval)
        • 3.4 进阶:从被动上下文到主动记忆管理
        • 3.5 构建短期与长期记忆系统
      • 4 总结
      • 🔥 课后小测验
        • 🔍 单选题 3.4.1
      • ✉️ 评价反馈
    • 用 Skill 将能力固化为可复用流程
    • 用评测驱动 Agent 开发
    • Qwen Code 实践
  • 交付上线

  • 总结与展望

  • 大模型
  • 构建Agent系统
malize
2026-02-05
目录

用 Memory 让 Agent 积累经验

# 3.4 用Memory让Agent积累经验

# 🚄 前言

Agent会做事了,但没有跨会话的记忆——关掉窗口、清空上下文就归零,积累的偏好和踩过的坑下次还要重新交代。这节课你将掌握Agent的记忆管理策略,从简单遗忘到主动记忆,让Agent越用越懂你。

# 🍁 课程目标

你将学到:

  • 建立短期记忆:对话上下文管理与Token限制应对
  • 三种记忆管理策略:上下文截断、滚动摘要、向量化召回
  • 从被动上下文到主动记忆管理:Agent自主决定何时记、何时读
  • 构建短期与长期记忆的混合系统
# 加载百炼的 API Key 用于调用千问大模型
import os, sys
os.chdir(os.path.join(os.path.dirname(os.path.abspath('')), 'course_core'))
sys.path.insert(0, os.getcwd())

from openai import OpenAI
from config.load_key import load_key
load_key()

client = OpenAI(
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)
print(f"API Key 已加载:{os.environ['DASHSCOPE_API_KEY'][:5]}*****")
1
2
3
4
5
6
7
8
9
10
11
12
13
14

在本节课程中,你将学习如何为你的 Agent 赋予记忆能力,解决大语言模型固有的“健忘”问题。你将从一个最朴素的方法开始,逐步发现其局限性,并最终掌握业界主流的短期和长期记忆构建策略。

首先,让我们配置一下课程所需的环境。

import os

# 导入后续会用到的模块
from agentscope.agent import ReActAgent
from agentscope.memory import InMemoryMemory
from agentscope.message import Msg
from agentscope.formatter import DashScopeChatFormatter
from agentscope.embedding import DashScopeTextEmbedding
from agentscope.memory import Mem0LongTermMemory
from agentscope.model import DashScopeChatModel
from agentscope.token import HuggingFaceTokenCounter

# 定义一个辅助函数,用于创建 Agent,方便后续课程使用
def create_agent(name: str, sys_prompt: str, **kwargs) -> ReActAgent:
    """一个创建 Agent 的辅助函数"""
    # 允许调用方覆盖默认 model/formatter/memory,避免重复绑定
    formatter = kwargs.pop("formatter", DashScopeChatFormatter())
    model = kwargs.pop(
        "model",
        DashScopeChatModel(
            model_name="qwen-plus",
            api_key=os.environ.get("DASHSCOPE_API_KEY"),
            stream=True,
        ),
    )
    memory = kwargs.pop("memory", InMemoryMemory())

    return ReActAgent(
        name=name,
        sys_prompt=sys_prompt,
        model=model,
        formatter=formatter,
        memory=memory, # 默认使用最简单的内存缓冲区
        **kwargs,
    )

print("环境配置完成!")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

# 1 建立短期记忆

在之前的课程中,你正在构建一个能帮你写作课程的 Agent 团队。内容编写 Agent 刚刚完成了一份出色的初稿。你很满意,并对它说:“很好,现在请根据我们上次讨论的教学风格,把第二部分写得更生动一些。”

然而,Agent 的回应却让你失望:“好的,请问我们上次讨论了什么样的教学风格?”,它忘记了过去的任务细节。这是因为你的Agent 是无状态的。每次开启新的对话,它就会忘记过去对话的所有内容。

扩展阅读:大语言模型的核心特性——无状态 (Stateless)

你可以将大模型想象成一个记忆力只有几秒钟的专家。在每一次独立的 API 调用中,它能理解你给它的所有信息并给出精彩的回答。但一旦这次调用结束,它会彻底忘记一切。它不会记得你是谁,你们之前聊过什么,你的任何偏好和要求。Agent 的每一次 reply 本质上都是一次独立的 API 调用,因此它天然地继承了这种无状态性。

那我们该如何解决呢?一个最直接的想法,就是每次和它说话时,都把之前的聊天记录“复习”一遍。

在编程实现中,这意味着你需要创建一个列表,用来存放所有的对话历史。每次向 Agent 提问时,你都把这个包含完整历史的列表一起发给它。AgentScope 中的 InMemoryMemory 就是这种朴素方案的实现。

让我们通过代码来验证一下。

# 创建一个课程编写 Agent
writing_agent = create_agent(
    name="Writer",
    sys_prompt="你是一个课程内容编写员。你的任务是编写一篇 Pandas 数据分析课程。"
)

async def run_stateless_test():
    # 第一次对话:设定教学风格
    msg1 = Msg("user", "我们的教学风格要严谨克制,请记住这一点。", "user")
    print(f"[{msg1.name}]: {msg1.content}")
    
    # Agent 会将这次对话存入它的 InMemoryMemory
    reply1 = await writing_agent(msg1)
    print(f"[{reply1.name}]: {reply1.content}")

    print("\n" + "="*20 + "\n")

    # 第二次对话:基于之前的设定提出新要求
    # 在调用时,writing_agent 会自动将 InMemoryMemory 中的历史记录和新消息一起发给模型
    msg2 = Msg("user", "很好,现在请把第二部分写得更专业一些。", "user")
    print(f"[{msg2.name}]: {msg2.content}")
    
    reply2 = await writing_agent(msg2)
    print(f"[{reply2.name}]: {reply2.content}")
    
    print("\n" + "="*20 + "\n")
    print("Agent 的短期记忆内容:")
    # 打印 Agent 的记忆,可以看到包含了全部两轮对话
    for m in await writing_agent.memory.get_memory():
        print(f"- [{m.role}] {m.name}: {m.content}")


await run_stateless_test()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

这个方案立竿见影,Agent 立刻拥有了短期内的对话记忆。

# 2 信息精炼

但当你把这个 Agent 投入真实场景,连续使用十几轮、几十轮对话后,两个严重的问题会浮现出来:

  1. 上下文窗口限制。每个大模型都有一个能处理的最大文本长度,我们称之为“上下文窗口”。随着对话轮次增加,对话历史会像滚雪球一样越来越大,最终超出模型的窗口限制,程序会直接报错。
  2. 急剧上升的成本。大模型的 API 调用是按量计费的,你发送的文本(输入)和它生成的文本(输出)中的每一个 Token 都要花钱。经过多轮对话后,每次 API 调用都需要重新发送全部历史记录,产生重复的 token 成本。
  3. 性能下降:注意力稀释 (Attention Dilution) 与“中间迷失” (Lost in the Middle)。即使你的钱包和模型的上下文窗口都能承受超长的对话历史,模型的性能也会下降。
    • 注意力稀释:随着上下文长度增长,模型遗忘上下文细节的概率会逐渐增大。
    • 中间迷失:在同等长度的上下文中,大模型往往能清晰地记得开头(首因效应)和结尾(近因效应)的信息,却容易忽略位于中间段落的关键细节。

这种朴素的“记忆”方案,只是一种寅吃卯粮的短期策略。它很快就会导致程序异常或成本超支。

这引出了一个核心问题:如何在不牺牲关键信息的前提下,有效管理上下文的长度与成本?

这个问题的本质,是如何对信息进行高效的压缩和筛选。就像你在准备开卷考试时,不会把整本教科书都抄到小抄上,而是会提炼出最重要的公式、定义和关键论点。

# 3 记忆管理策略

让我们借鉴人类准备考试的思路,探索管理 Agent 记忆的策略。

# 3.1 策略一:简单"遗忘"——固定窗口截断 (Context Truncation)

最简单粗暴的方法,就是只记最近发生的事,这叫作固定窗口截断。

  • 思路:你设定一个固定的窗口大小,比如只保留最近的 N 轮对话,或者更精确地,只保留最近的 N 个 Token。当对话历史超过这个大小时,就把最老的那一轮对话丢掉,确保上下文总长度基本保持不变。
  • 相对优势:实现极其简单,计算开销小,能有效保证上下文长度永远在可控范围内,避免了报错和成本无限增长的问题。
  • 适用场景:适用于那些信息价值随时间快速衰减的场景,例如闲聊机器人或者简单的客服问答。
  • 边界条件:这是一种“一刀切”的失忆方案。如果对话早期的关键信息(例如用户在第一轮对话中设定的核心目标)被截断,Agent 就会再次“失忆”,导致对话逻辑断裂。
  • 性能隐患:即使没有超出最大窗口限制,等到窗口快满时才截断,意味着 Agent 长期带着接近极限的长上下文工作。由于注意力稀释的存在,这会导致模型处理复杂任务的能力变差。

在 AgentScope 中,这个功能由 Formatter 组件实现。你可以在初始化 Formatter 时传入 max_tokens 参数来限制上下文长度。

# 创建一个带有截断功能的 Formatter(需要提供 token 计数器,否则不会触发截断)
token_counter = HuggingFaceTokenCounter(
    "Qwen/Qwen3-8B",
    use_mirror=True,
    use_fast=True,
    trust_remote_code=True,
)
truncated_formatter = DashScopeChatFormatter(
    token_counter=token_counter,
    max_tokens=40,
)

# 创建一个使用该 Formatter 的 Agent
truncation_agent = create_agent(
    name="Trunk",
    sys_prompt="你是一个健忘的机器人,你只能记住最近发生的事情。",
    # 将默认的 formatter 替换为我们刚刚创建的带截断功能的 formatter
    formatter=truncated_formatter
)

async def run_truncation_test():
    # 进行多轮对话,故意让上下文变长
    await truncation_agent(Msg("user", "规则A:所有回答必须是陈述句。", "user"))
    await truncation_agent(Msg("user", "规则B:不能使用“你”或“我”。", "user"))
    await truncation_agent(Msg("user", "规则C:回答要尽量简短。", "user"))
    await truncation_agent(Msg("user", "规则D:数字必须用大写汉字表示。", "user"))
    
    print("经过多轮对话后,Agent 的记忆(理论上很长):")
    for m in await truncation_agent.memory.get_memory():
        print(f"- [{m.role}] {m.name}: {m.content}")
        
    print("\n" + "="*20 + "\n")
    
    # 提出一个问题,测试 Agent 是否还记得最早的规则 A
    reply = await truncation_agent(Msg("user", "请总结一下所有规则。", "user"))
    
    print(f"[{reply.name}]: {reply.content}")
    print("\nAgent 很可能已经忘记了最早的“规则A”,因为它为了满足 max_tokens=40 的限制,从记忆的开头丢弃了最早的对话。")


await run_truncation_test()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

# 3.2 策略二:提炼重点——滚动摘要 (Rolling Summary)

简单截断会直接丢弃信息,显然不够理想。一个更聪明的做法是,在遗忘细节之前,先把重点提炼出来。这就是滚动摘要策略。

  • 思路:随着对话进行,当历史记录快要“塞满”窗口时,你调用一次大模型,将最早的一部分对话内容(比如前 50%)提炼成一段简短的摘要。然后,在后续的请求中,用这段凝练的“记忆摘要”替换掉冗长的原始对话记录。
  • 相对优势:在压缩长度的同时,最大程度地保留了历史对话的核心信息,能更好地维持对话的长期连贯性。
  • 提升性能表现:尽早压缩甚至去掉那些与当前任务不相关的上下文,有助于缓解注意力稀释,从而提高 Agent 处理复杂任务的性能表现。
  • 适用场景:适用于需要长期保持目标一致性的任务,比如项目规划、长篇内容创作等。
  • 边界条件:它引入了额外的 API 调用成本(用于生成摘要),并且摘要的质量直接影响后续对话。

目前 AgentScope 尚未内置此功能,但你可以很容易地通过自定义 Memory 类来实现这个逻辑。下面是一个概念性的实现思路:

# 这是一个伪代码示例,用于演示滚动摘要的核心逻辑
# 它不能直接运行,需要你继承 agentscope.memory.MemoryBase 并实现完整逻辑

class SummaryMemory: # (MemoryBase)
    def __init__(self, buffer_size=10, summary_ratio=0.5):
        self.history = []
        self.buffer_size = buffer_size
        self.summary_ratio = summary_ratio

    def add(self, message):
        self.history.append(message)
        self.try_summarize()

    def try_summarize(self):
        if len(self.history) > self.buffer_size:
            # 1. 确定要摘要的部分
            num_to_summarize = int(len(self.history) * self.summary_ratio)
            messages_to_summarize = self.history[:num_to_summarize]
            
            # 2. 调用大模型生成摘要 (伪代码)
            # summary_text = llm.call("请将以下对话总结为一段话:", messages_to_summarize)
            summary_text = "用户设定了教学风格为风趣幽默,并要求内容生动。"
            
            summary_message = Msg("system", f"【历史摘要】{summary_text}", "system")
            
            # 3. 用摘要替换原始对话
            self.history = [summary_message] + self.history[num_to_summarize:]
            print(f"--- 记忆已压缩,当前长度 {len(self.history)} ---")
            
    def get_memory(self):
        return self.history

# 使用示例
# summary_mem = SummaryMemory()
# summary_mem.add(Msg("user", "我们的教学风格要风趣幽默。", "user"))
# ... 经过多轮对话后 ...
# summary_mem.add(Msg("user", "再增加一个关于成本的案例。", "user")) # 此时可能会触发摘要
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

# 3.3 策略三:构建知识库——向量化召回 (Vector-based Retrieval)

前面的策略依然在用一种线性的、无差别的方式处理所有对话历史。但这并不符合我们人类的记忆模式。你的记忆不是一条按时间顺序播放的磁带,而是一个巨大的、相互关联的知识网络。

这种“按需检索”的模式,正是构建高级记忆系统的核心。该策略彻底改变了游戏规则:你不再试图把所有历史都塞进上下文,而是将每一轮对话都变成可独立检索的“记忆碎片”,存入一个专门的“长期记忆库”。

  • 思路:

    1. 存储 (Ingestion):每轮对话结束后,你将对话内容转换成一个数学向量(Embedding),然后连同原文一起存入一个向量数据库 (Vector Database)。
    2. 检索 (Retrieval):当用户提出新问题时,你先将这个问题也转换成向量,然后去数据库中进行相似度搜索,找出与当前问题最相关的几条历史对话记录。
    3. 组合 (Composition):最后,你将检索到的“相关记忆”和用户的“最新问题”组合成一个精简而高效的上下文,再发送给大模型。
  • 相对优势:从根本上摆脱了上下文窗口的长度束缚,能根据当前意图,从海量信息中精准地“回忆”起最相关的内容,极大地节约了成本。

  • 适用场景:这是构建真正智能、可长期交互的 Agent 的基石。适用于个性化助手、企业知识库、智能学习伴侣等所有复杂场景。

  • 边界条件:系统复杂度最高。它引入了 Embedding 模型和向量数据库等新的技术栈。

AgentScope 通过 Mem0LongTermMemory 模块优雅地实现了这一功能。它支持两种工作模式:

  1. static_control:在每次 Agent 回复前后,自动地、被动地保存和检索记忆。
  2. agent_control:赋予 Agent 主动管理记忆的工具(record_to_memory, retrieve_from_memory),让 Agent 自行决定何时记忆、何时回忆。

让我们先看看更简单的 static_control 模式。

# 1. 初始化长期记忆模块
# 它需要一个语言模型(用于内部处理)和一个嵌入模型
from mem0.vector_stores.configs import VectorStoreConfig

# 为 Qdrant 本地向量库指定与 DashScope Embedding 一致的维度(2048)
vector_store = VectorStoreConfig(
    config={
        "on_disk": False,
        "embedding_model_dims": 2048,
    }
)

long_term_memory = Mem0LongTermMemory(
    agent_name="Writer",
    user_name="user",
    model=DashScopeChatModel(
        model_name="qwen-plus",
        api_key=os.environ.get("DASHSCOPE_API_KEY"),
        stream=False,
    ),
    embedding_model=DashScopeTextEmbedding(
        model_name="text-embedding-v4",
        api_key=os.environ.get("DASHSCOPE_API_KEY"),
        dimensions=2048
    ),
    vector_store_config=vector_store,
)

# 2. 创建一个装备了长期记忆的 Agent
ltm_agent_static = create_agent(
    name="LTM_Writer_Static",
    sys_prompt="你是一个拥有长期记忆的课程编写员。",
    long_term_memory=long_term_memory,
    long_term_memory_mode="static_control", # 关键参数:设置为静态控制模式
)


async def run_ltm_static_test():
    # 对话一:存入一个关键信息
    msg1 = Msg("user", "记住,我们正在写一篇 Pandas 数据分析课程,目前已经写完初稿。", "user")
    print(f"[{msg1.name}]: {msg1.content}")
    reply1 = await ltm_agent_static(msg1)
    print(f"[{reply1.name}]: {reply1.content}")
    # 在这一步之后,对话内容会被自动存入长期记忆

    print("\n" + "="*20 + " 模拟新的一次会话 " + "="*20 + "\n")

    # 清空 Agent 的短期记忆,模拟一次全新的会话
    await ltm_agent_static.memory.clear()
    print("Agent 的短期记忆已被清空。")

    # 对话二:提出一个相关问题
    # Agent 在回复前,会用问题“这门课适合谁?”去长期记忆中检索
    msg2 = Msg("user", "我们上次的工作进度到哪儿了?", "user")
    print(f"[{msg2.name}]: {msg2.content}")
    reply2 = await ltm_agent_static(msg2)
    print(f"[{reply2.name}]: {reply2.content}")
    print("\n注意: 即使短期记忆被清空,Agent 依然能回答正确,因为它从长期记忆中检索到了相关信息。")

await run_ltm_static_test()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

# 3.4 进阶:从被动上下文到主动记忆管理

至此,你已经掌握了为 Agent 构建记忆系统的强大策略。但一个真正智能的 Agent,不应只是被动地接收你处理好的上下文,它应该能主动地管理自己的记忆。

通过将 long_term_memory_mode 设置为 agent_control,ReActAgent 会自动获得 record_to_memory 和 retrieve_from_memory 两个工具。你需要在系统提示词中引导它使用这些工具。

# 复用之前创建的 long_term_memory 实例
# 重新创建一个 Agent,这次使用 agent_control 模式
from textwrap import dedent
ltm_agent_active = create_agent(
    name="LTM_Writer_Active",
    sys_prompt=dedent(
        "你是一个拥有主动记忆管理能力的课程编写员。\n"
        "你可以使用以下工具来管理你的长期记忆:\n"
        "- `record_to_memory(data: str)`: 将一段重要的信息记录到长期记忆中。\n"
        "- `retrieve_from_memory(query: str) -> str`: 根据查询从长期记忆中检索相关信息。\n"
        "在回答问题前,先思考是否需要检索记忆。在对话结束后,思考是否有关键信息需要记录。"
    ),
    long_term_memory=long_term_memory,
    long_term_memory_mode="agent_control", # 关键参数:设置为 Agent 控制模式
)


async def run_ltm_active_test():
    # 对话一:Agent 自主决定记录信息
    msg1 = Msg("user", "课程的写作风格必须非常严谨和学术化,这是一个核心要求。", "user")
    print(f"[{msg1.name}]: {msg1.content}")
    reply1 = await ltm_agent_active(msg1)
    # Agent 在这里的思考过程中,会判断“核心要求”是重要信息,并调用 record_to_memory 工具
    print(f"[{reply1.name}]: {reply1.content}")
    
    print("\n" + "="*20 + " 模拟一次新的会话 " + "="*20 + "\n")
    await ltm_agent_active.memory.clear()

    # 对话二:Agent 自主决定检索信息
    msg2 = Msg("user", "我忘了,我们课程的写作风格是什么来着?", "user")
    print(f"[{msg2.name}]: {msg2.content}")
    reply2 = await ltm_agent_active(msg2)
    # Agent 在这里的思考过程中,会先调用 retrieve_from_memory(query="写作风格"),再根据检索结果生成回答
    print(f"[{reply2.name}]: {reply2.content}")

await run_ltm_active_test()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

通过这种方式,记忆不再是你从外部“喂”给 Agent 的数据,而是它自己主动获取、存储和维护的内在知识。这使得 Agent 从一个简单的“工具”向真正的“伙伴”进化。

扩展阅读:成本效益分析

你可能会认为,向量化召回和主动记忆管理引入了 Embedding 模型调用、向量数据库查询以及额外的 LLM 推理(用于决定是否调用工具),这会增加成本和时延。

然而,我们不能简单地将“一次高质量输出”的成本与“一次低质量输出”的成本进行比较。因为如果单次调用无法有效解决问题(比如忘记了关键要求),那么它的成本无论多低,都是一种浪费。

一个更公平的比较是:为获得一个“可用”的答案,两种路径的总成本是多少?

  • 路径A(无长期记忆):一次调用,得到错误答案(问题未解决)。你不得不手动提醒,再调用一次,才能解决问题。
  • 路径B(有长期记忆):一次调用(包含检索),直接得到正确答案(问题解决)。

从这个角度看,为记忆系统增加的投入,是确保产出质量、避免重复无效尝试的必要投资。

# 3.5 构建短期与长期记忆系统

通过上述策略的组合,你已经为 Agent 构建了一个完整的记忆系统,它可以清晰地划分为两个部分:

  • 短期记忆 (Short-term Memory):基于对话缓冲区,通过截断或摘要策略进行管理。它的核心职责是维持当前会话的连贯性。例如,记住你刚刚提出的,关于“第二章内容需要增加一个案例”的具体要求。
  • 长期记忆 (Long-term Memory):基于向量数据库和工具化调用。它的核心职责是持久化存储关键信息,并支持跨会话的智能检索。例如,记住整个课程项目的核心目标是“为初学者设计,风格需风趣幽默”。

拥有记忆的 Agent,不再是一个冰冷的、一次性的问答机器。它能够从经验中学习,记住你的偏好,理解长期的语境,最终从一个简单的“工具”进化为真正的“伙伴”。

落地实践与使用建议

一、 快速引入记忆能力

你可以借助成熟的开源框架或平台,快速为你的 Agent 添加记忆能力。

  • AgentScope: 如本课程所示,AgentScope 提供了 InMemoryMemory 作为基础短期记忆,以及集成了 Mem0 的 Mem0LongTermMemory 作为开箱即用的长期记忆解决方案。
  • Mem0: 一个专为 AI 应用设计的开源智能记忆层,它将向量化召回、记忆冲突处理等复杂逻辑封装起来,提供了强大的底层支持。
  • LangChain Memory: 强大的 Agent 开发框架 LangChain 提供了丰富的内置 Memory 模块,包括 ConversationBufferMemory(缓冲区)、ConversationSummaryMemory(摘要)和 VectorStoreRetrieverMemory(向量召回)等,允许你根据需求灵活组合。
  • 阿里云百炼平台: 对于希望快速验证或非技术背景的团队,百炼平台提供了可视化的 Agent 构建流程,你只需通过简单的配置,即可为 Agent 开启长期记忆功能。

二、 记忆使用建议

  • 有选择地记忆:记忆并非越多越好。积累大量低价值或噪声信息会干扰后续检索效果。你应该建立记忆写入的准入机制,例如仅在用户显式要求(“请记住...”)或信息重要性高于某个阈值时才写入。agent_control 模式就是实现这一点的好方法。
  • 持续治理:记忆是一类动态的数据资产,需要建立持续治理机制,包括定期清理过时信息、合并重复条目、校验事实准确性,并为用户提供主动管理自己记忆的接口(查看、修改、删除)。
  • 场景化应用:不同业务场景对记忆的需求不同。例如,在要求风格一致的课程文档工作流中,不应记录个性化的偏好;但对于产品的事实信息(如 API 参数、功能限制),则可以记录并定期审查其有效性,确保 Agent 生成内容的准确性。

# 4 总结

让我们回顾一下你在本节学到的知识:

  • 问题的根源:无状态性:大语言模型本身没有记忆,每次调用都是独立的。朴素地传递完整历史会导致上下文超长和成本失控。
  • 短期记忆 vs 长期记忆:你可以通过”截断”与”摘要”策略管理短期记忆,维持当前会话连贯性;通过”向量化召回”构建长期记忆,实现跨会话的知识存取。
  • 主动记忆管理:通过为 Agent 提供 save, recall, update 等记忆工具,你可以让它从被动接收上下文,转变为能主动管理和运用自身记忆的智能体,实现真正的学习与成长。
  • 记忆的最佳实践:一个强大的记忆系统需要被有效治理。你应该有选择地写入、持续地清理和更新记忆,并根据具体业务场景决定应该记忆什么、不记忆什么。

# 🔥 课后小测验

# 🔍 单选题 3.4.1

你的课程编写Agent在连续工作30轮后,忘记了第1轮用户设定的"风格要求严谨学术化",开始输出轻松口语化的内容。你当前使用的是简单截断策略(保留最近N轮)。最合理的改进方案是什么❓
  • A. 将截断窗口从N轮扩大到2N轮,确保早期的风格要求不被丢弃
  • B. 将风格要求硬编码到系统提示词中,使其不受记忆管理策略的影响
  • C. 引入向量化长期记忆,将关键偏好持久化存储并在每次回复前按需检索
  • D. 改用滚动摘要策略,定期将旧对话压缩为摘要以保留核心信息

【点击查看答案】

✅ 参考答案:C 📝 解析: A只是延迟问题发生的时间,窗口再大也终会被填满,且长上下文会导致注意力稀释。B在此场景可行,但仅适用于已知的固定偏好,无法处理用户在对话中动态产生的新偏好,不具备通用性。D比截断好,能保留部分关键信息,但摘要质量取决于压缩模型,长期累积仍可能丢失细节。C是最合理的方案:向量化长期记忆将关键偏好持久化到向量数据库中,每次按当前任务语义检索最相关的记忆,从根本上摆脱上下文窗口的长度限制。

# ✉️ 评价反馈

欢迎你参与阿里云大模型ACP课程问卷 (opens new window) 反馈学习体验和课程评价。 你的批评和鼓励都是我们前进的动力!

编辑 (opens new window)
#大模型#ACP认证#阿里云
用多 Agent 实现团队协作
用 Skill 将能力固化为可复用流程

← 用多 Agent 实现团队协作 用 Skill 将能力固化为可复用流程→

最近更新
01
Docker 核心命令大全
07-04
02
CIM半导体行业业务流程详解 原创
07-04
03
死锁 原创
04-15
更多文章>
Theme by Vdoing | Copyright © 2023-2026 Malize | GitHub | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式