【Agentic RL 专题】二、Agentic RL——Memory

2025-11-19 15:50:23
文章摘要
文章围绕智能体中记忆模块(Memory)及其在强化学习(RL)中的应用展开。先对比不同大模型记忆能力,如Gemini 2.5pro只能单窗口记忆,GPT-5可跨窗口记忆,但均非智能体上的Memory模块。接着分析记忆模块发展,从早期上下文记忆到Agentic RL的Memory模块。还对Agentic RL的Memory模块细致划分,最后给出RAG、Token风格记忆模块代码实现.

一、前言

我们现在在大模型交互的过程中,往往会给我们一种感觉,LLM本身就已经有了记忆模块,例如我们常用的Deepseek, GPT, Gemini



  1. Gemini 2.5pro为例,开启一个对话框,让他记住我们的名字


  1. 重启一个对话框,询问模型我是谁



  1. 同样的以GPT5为例,开启一个对话框,让他记住我们的名字


  1. 重启一个对话框,询问模型我是谁时

可以发现Gemini 2.5pro无法记忆多个会话的信息。相反的,尽管是不同的对话窗口,GPT5仍然可以记住我们的名字。为什么会有这样子的区别呢?这是不是就是智能体中的Memory模块呢,如果⚠️upload failed, check dev console不是,那这个与agent中的Memory模块又有什么区别?带着这些问题,我们将开启智能体——Memory篇章的学习!

二、Memory的发展

2.1 有记忆性不一定就是Agent

上述过程我们可以发现,不同的模型有着不同的记忆能力,有的模型只能在单个窗口中记忆信息,有的模型可以实现跨窗口的记忆信息。这两种记忆信息有什么不同呢?

  1. 单个窗口记忆信息 Gemini 2.5pro本质上只是上下文的信息,当达到上下文窗口上限时,会自动去除早期的记忆;
  2. 多个窗口记忆信息 GPT5✍ 它的“记忆”并不是我们现在所说的智能体上的 Memory 模块,更像是一个外部记事本,本质上是一个 外部数据库或用户画像缓存。外部缓存可以让模型“记得”一些事实,例如用户的名字或偏好,但它并不参与模型的推理或决策过程。

🤖 而早期的Agent中Memory模块(MemoryBank, MemGPT , and Hip-poRAG)不仅仅是记忆外部的缓存,他是参与了整个决策过程的模块。记忆的内容会被动态更新,决定记忆信息的记忆和遗忘,并且影响着Agent决策的过程。

例如智能体在做出动作选择时,会根据当前的记忆进行决策,而GPT5只是根据记忆进行回复,不做任何的行动决策

2.2 LLM-Agent→Agentic RL

在早期Agent的Memory模块中,记忆依然可以被认为是静态的外部存储,主要是帮助语言模型在多轮交互中检索过去的信息,让模型看起来“记得住”一些

上下文。例如:MemoryBank, MemGPT , and Hip-poRAG。这些方法仍然采用了预定义的内存管理策略调用记忆,属于RAG-style Memory。


模块

类型

特征

是否使用 RL

MemoryBank (2023)

外部存储(RAG-style)

通过向量数据库保存会话摘要、任务结果

❌ 无 RL

MemGPT (2023)

外部 + 系统式内存

模拟操作系统的“主存储+外存”,LLM 主动在两者间读写

❌ 无 RL

HippoRAG (2024)

外部神经启发记忆

用神经启发(Hippocampus-like)机制来组织知识图谱

❌ 无 RL



Method

Type

Key Characteristics

RAG-style Memory



MemoryBank [113]

External Store

Static memory with predefined storage/retrieval rules

MemGPT [114]

External Store

OS-like agent with static memory components

HippoRAG [115]

External Store

Neuro-inspired memory with heuristic access

Prospect† [116]

RL-Guided Retrieval

Uses RL for reflection-driven retrieval adjustment

Memory-R1† [117]

RL-Guided Retrieval

RL-driven memory management: ADD/UPDATE/DELETE/NOOP

Token-level Memory



MemAgent† [118]

Explicit Token

RL controls which NL tokens to retain or overwrite

MEM1 [119]

Explicit Token

Memory pool managed by RL to enhance context handling

Memory Token [120]

Explicit Token

Structured memory for reasoning disentanglement

MemoryLLM [121]

Latent Token

Latent tokens repeatedly integrated and updated

M+ [122]

Latent Token

Scalable memory tokens for long-context tracking

IMM [123]

Latent Token

Decouples word representations and latent memory

Memory [124]

Latent Token

Forget-resistant memory tokens for evolving context

Structured Memory



Zep [125]

Temporal Graph

Temporal knowledge graph enabling structured retrieval

A-MEM [126]

Atomic Memory Notes

Symbolic atomic memory units; structured storage

G-Memory [127]

Hierarchical Graph

Multi-level memory graph with topological structure

Mem0 [128]

Structured Graph

Agent memory with full-stack graph-based design

三、代码实战

  1. RAG-STYLE MEMORY
import faiss  
import numpy as np  
from sentence_transformers import SentenceTransformer  
  
class RAGMemoryAgent:  
    def __init__(self, embedding_dim=384, memory_size=100):  
        self.memory_size = memory_size  
        self.embedding_dim = embedding_dim  
        self.memory = []  # 外部存储的记忆  
        self.model = SentenceTransformer('paraphrase-MiniLM-L6-v2')  # 使用预训练模型  
        self.index = faiss.IndexFlatL2(self.embedding_dim)  # 向量检索索引  
  
    def encode(self, text):  
        """将文本编码为固定维度的向量(384维)"""  
        vector = self.model.encode([text])  # 生成 384 维向量  
        return vector.astype(np.float32)  
  
    def add_to_memory(self, text):  
        """将新的记忆添加到外部存储中"""  
        vector = self.encode(text)  
        self.memory.append(text)  
        self.index.add(vector)  # 将向量添加到 FAISS 索引中  
  
    def retrieve_memory(self, query, top_k=1):  
        """根据查询检索最相关的记忆"""  
        query_vector = self.encode(query)  
        D, I = self.index.search(query_vector, top_k)  # 检索最相关的记忆  
        return [self.memory[i] for i in I[0]]  # 返回检索到的记忆  
  
# 测试 RAG-style 记忆  
agent = RAGMemoryAgent(embedding_dim=384)  
agent.add_to_memory("My name is Alice and I love playing chess.")  
agent.add_to_memory("I enjoy hiking in the mountains during the weekend.")  
agent.add_to_memory("I am learning reinforcement learning to improve my skills.")  
  
query = "What is your name?"  
retrieved_memory = agent.retrieve_memory(query)  
print("Retrieved Memory:", retrieved_memory)
返回:Retrieved Memory: My name is Alice and I love playing chess."
  1. TOKEN-STYLE MEMORY
import numpy as np  
from sentence_transformers import SentenceTransformer  
  
class TokenMemoryAgent:  
    def __init__(self, embedding_dim=128):  
        self.memory = []  # 存储每个 token 的记忆  
        self.embedding_dim = embedding_dim  # 嵌入维度  
        self.model = SentenceTransformer('paraphrase-MiniLM-L6-v2')  # 使用预训练模型  
  
    def encode(self, text):  
        """将文本编码为嵌入向量"""  
        vector = self.model.encode([text])  # 生成向量  
        return vector[0]  # 取第一个(单一文本的嵌入)  
  
    def add_token_memory(self, token):  
        """将一个 token 存储到记忆中"""  
        token_embedding = self.encode(token)  
        self.memory.append(token_embedding)  
  
    def update_token_memory(self, token_index, new_token):  
        """更新已有的 token 记忆"""  
        token_embedding = self.encode(new_token)  
        self.memory[token_index] = token_embedding  
  
    def retrieve_memory(self, query_token):  
        """检索与查询 token 最相关的记忆"""  
        query_embedding = self.encode(query_token)  
        similarities = [np.dot(query_embedding, token) for token in self.memory]  
        best_match_index = np.argmax(similarities)  
        return self.memory[best_match_index]  
  
# 测试 Token-style 记忆  
agent = TokenMemoryAgent(embedding_dim=128)  
agent.add_token_memory("Alice loves chess.")  
agent.add_token_memory("Weekend hiking is fun.")  
  
query = "What does Alice like?"  
retrieved_memory = agent.retrieve_memory(query)  
print("Retrieved Token Memory:", retrieved_memory)
返回的是token embedding向量
  1. 加入RL训练环境
import numpy as np
from sentence_transformers import SentenceTransformer
from stable_baselines3 import PPO
from stable_baselines3.common.envs import DummyVecEnv
from stable_baselines3.common.callbacks import BaseCallback
class TokenMemoryManagementCallback(BaseCallback):
    def __init__(self, memory_agent, verbose=0):
        super().__init__(verbose)
        self.memory_agent = memory_agent
    def _on_step(self):
        # 在每一步中,使用 RL 策略来决定是否更新/删除token
        if self.training_env.get_attr("step") % 100 == 0:
            action = np.random.choice(["retain", "discard", "add"])
            if action == "add":
                self.memory_agent.add_token("New token information")
            elif action == "discard":
                self.memory_agent.remove_token()
            elif action == "retain":
                pass
        return True
class TokenMemoryAgent:
    def __init__(self, embedding_dim=128):
        self.memory = []  # 存储每个 token 的记忆
        self.embedding_dim = embedding_dim
        self.model = SentenceTransformer('paraphrase-MiniLM-L6-v2')
    def encode(self, text):
        """将文本编码为嵌入向量"""
        vector = self.model.encode([text])
        return vector[0]  # 取第一个(单一文本的嵌入)
    def add_token(self, token):
        """将一个 token 存储到记忆中"""
        token_embedding = self.encode(token)
        self.memory.append(token_embedding)
    def remove_token(self):
        """从记忆中移除一个 token"""
        if self.memory:
            self.memory.pop(np.random.choice(len(self.memory)))
            print("Token removed.")
    def retrieve_memory(self, query_token):
        """检索与查询 token 最相关的记忆"""
        query_embedding = self.encode(query_token)
        similarities = [np.dot(query_embedding, token) for token in self.memory]
        best_match_index = np.argmax(similarities)
        return self.memory[best_match_index]
# 训练环境设置
env = DummyVecEnv([lambda: gym.make('CartPole-v1')])  # 选择合适的 RL 环境
token_agent = TokenMemoryAgent()
# 使用 PPO 算法进行训练
model = PPO("MlpPolicy", env, verbose=1)
token_callback = TokenMemoryManagementCallback(token_agent)
model.learn(total_timesteps=20000, callback=token_callback)
# 测试 Token-style 记忆
query = "What should I remember?"
retrieved_memory = token_agent.retrieve_memory(query)
print("Retrieved Token Memory:", retrieved_memory)

四、LangChain实现

4.1 ConversationBufferMemory 完整对话历史

from langchain_openai import ChatOpenAI
from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferMemory
llm = ChatOpenAI(
    model="gpt-4o-mini",
    temperature=0
)
# 记录“从对话开始到现在”的完整历史
memory = ConversationBufferMemory(
    memory_key="chat_history",   # prompt 里历史的字段名
    return_messages=True         # 以 Message 对象形式返回,更灵活
)
conversation = ConversationChain(
    llm=llm,
    memory=memory,
    verbose=True  # 打印出 prompt 和中间过程,方便你写博客讲解
)
# 连续对话测试
print(conversation.invoke("你好,我叫小明,以后请记住我的名字。")["response"])
print(conversation.invoke("我刚才说我叫什么名字?")["response"])
print(conversation.invoke("再帮我用第一人称简单介绍一下自己。")["response"])

除此之外,还可以仅保留近期 K 轮对话:

from langchain.memory import ConversationBufferWindowMemory
# 只保留最近 3 轮的对话
window_memory = ConversationBufferWindowMemory(
    k=3,
    memory_key="chat_history",
    return_messages=True
)
short_conversation = ConversationChain(
    llm=llm,
    memory=window_memory,
    verbose=True
)
# 连续问多轮,观察超过 k 轮之后早期信息会被“遗忘”
questions = [
    "1. 我叫 Alice,请记住。",
    "2. 我喜欢玩星露谷物语。",
    "3. 我讨厌夏天的蚊子。",
    "4. 你还记得我讨厌什么吗?",
    "5. 你还记得我叫什么名字吗?"
]
for q in questions:
    resp = short_conversation.invoke(q)["response"]
    print(f"Q: {q}\nA: {resp}\n{'-'*40}")

五、总结

本文详细探讨了智能体中的记忆模块(Memory Module)及其在强化学习(RL)中的应用。通过对比不同的大型语言模型(如Gemini 2.5proGPT-5)的记忆能力,我们明确了“记忆”这一概念的多样性,揭示了它在智能体决策过程中的核心作用。区分了不同记忆模块,对早期的上下文记忆到早期的Agent Memory 模块再到Agentic RL的Memory 模块作了详细的分析,并且对Agentic RL的Memory 模块做了三种不同风格的更细致的划分,最后通过代码实现了RAG和Token风格的记忆模块。


声明:该内容由作者自行发布,观点内容仅供参考,不代表平台立场;如有侵权,请联系平台删除。
标签:
技术方向
技术栈