记忆(Memory) #

记忆系统让 AI 应用能够记住之前的对话内容,实现连贯的多轮对话。LangChain 提供了多种记忆类型,满足不同场景的需求。

记忆系统概览 #

text
┌─────────────────────────────────────────────────────────────┐
│                    LangChain 记忆系统                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  为什么需要记忆?                                            │
│                                                             │
│  无记忆:                                                    │
│  用户: 我叫张三                                              │
│  AI: 你好,张三!                                            │
│  用户: 我叫什么?                                            │
│  AI: 抱歉,我不知道你的名字...                               │
│                                                             │
│  有记忆:                                                    │
│  用户: 我叫张三                                              │
│  AI: 你好,张三!                                            │
│  用户: 我叫什么?                                            │
│  AI: 你叫张三。                                              │
│                                                             │
└─────────────────────────────────────────────────────────────┘

记忆类型 #

1. ConversationBufferMemory #

保存完整的对话历史:

python
from langchain.memory import ConversationBufferMemory

memory = ConversationBufferMemory()

# 添加对话
memory.save_context(
    {"input": "你好,我叫张三"},
    {"output": "你好,张三!很高兴认识你"}
)

memory.save_context(
    {"input": "我喜欢编程"},
    {"output": "编程是一项很有趣的技能!"}
)

# 获取历史
print(memory.load_memory_variables({}))
# {'history': 'Human: 你好,我叫张三\nAI: 你好,张三!很高兴认识你\nHuman: 我喜欢编程\nAI: 编程是一项很有趣的技能!'}

2. ConversationBufferWindowMemory #

只保留最近 N 轮对话:

python
from langchain.memory import ConversationBufferWindowMemory

# 只保留最近 2 轮对话
memory = ConversationBufferWindowMemory(k=2)

memory.save_context({"input": "消息1"}, {"output": "回复1"})
memory.save_context({"input": "消息2"}, {"output": "回复2"})
memory.save_context({"input": "消息3"}, {"output": "回复3"})

print(memory.load_memory_variables({}))
# 只显示最近 2 轮:消息2 和 消息3
text
┌─────────────────────────────────────────────────────────────┐
│                    滑动窗口记忆                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  k=2 时的记忆窗口:                                          │
│                                                             │
│  时间线:                                                    │
│  ─────────────────────────────────────────────>             │
│                                                             │
│  [消息1] ─> [消息2] ─> [消息3] ─> [消息4]                   │
│    ❌         ✅        ✅        ✅                        │
│   丢弃      保留       保留      新增                       │
│                                                             │
│  窗口始终只保留最近的 k 轮对话                               │
│                                                             │
└─────────────────────────────────────────────────────────────┘

3. ConversationSummaryMemory #

使用 LLM 自动总结历史对话:

python
from langchain.memory import ConversationSummaryMemory
from langchain_openai import ChatOpenAI

model = ChatOpenAI(model="gpt-4o-mini")

memory = ConversationSummaryMemory(llm=model)

memory.save_context(
    {"input": "你好,我是张三,一名 Python 开发者"},
    {"output": "你好张三!很高兴认识一位 Python 开发者"}
)

memory.save_context(
    {"input": "我最近在学习机器学习"},
    {"output": "机器学习是个很好的方向!"}
)

print(memory.load_memory_variables({}))
# {'history': '用户张三是一名 Python 开发者,最近在学习机器学习。'}

4. ConversationSummaryBufferMemory #

结合总结和缓冲区,在 token 限制内保留最多信息:

python
from langchain.memory import ConversationSummaryBufferMemory

memory = ConversationSummaryBufferMemory(
    llm=model,
    max_token_limit=100  # 最大 token 数
)

# 当对话超过 token 限制时,旧对话会被总结
memory.save_context({"input": "长对话1..."}, {"output": "长回复1..."})
memory.save_context({"input": "长对话2..."}, {"output": "长回复2..."})

5. ConversationTokenBufferMemory #

基于 token 数量限制记忆:

python
from langchain.memory import ConversationTokenBufferMemory

memory = ConversationTokenBufferMemory(
    llm=model,
    max_token_limit=1000
)

6. VectorStoreRetrieverMemory #

将记忆存储在向量数据库中,支持语义检索:

python
from langchain.memory import VectorStoreRetrieverMemory
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings

# 创建向量存储
vectorstore = Chroma(
    embedding_function=OpenAIEmbeddings(),
    persist_directory="./chroma_memory"
)

# 创建检索器
retriever = vectorstore.as_retriever(
    search_kwargs={"k": 3}  # 检索最相关的 3 条记忆
)

memory = VectorStoreRetrieverMemory(retriever=retriever)

# 保存记忆
memory.save_context(
    {"input": "我最喜欢的颜色是蓝色"},
    {"output": "好的,我记住了你喜欢蓝色"}
)

memory.save_context(
    {"input": "我喜欢吃披萨"},
    {"output": "披萨确实很美味!"}
)

# 基于语义检索相关记忆
print(memory.load_memory_variables({"prompt": "颜色"}))
# 会返回关于颜色的记忆
text
┌─────────────────────────────────────────────────────────────┐
│                    记忆类型对比                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  类型                    │ 特点           │ 适用场景        │
│  ─────────────────────────────────────────────────────────  │
│  Buffer                  │ 完整保存       │ 短对话         │
│  BufferWindow            │ 固定轮数       │ 有限上下文     │
│  Summary                 │ 自动总结       │ 长对话         │
│  SummaryBuffer           │ 总结+缓冲      │ 平衡方案       │
│  TokenBuffer             │ Token 限制     │ 精确控制       │
│  VectorStoreRetriever    │ 语义检索       │ 大规模记忆     │
│                                                             │
└─────────────────────────────────────────────────────────────┘

在链中使用记忆 #

基础用法 #

python
from langchain_openai import ChatOpenAI
from langchain.memory import ConversationBufferMemory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.output_parsers import StrOutputParser

model = ChatOpenAI(model="gpt-4o-mini")
memory = ConversationBufferMemory(return_messages=True)

prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个友好的助手"),
    MessagesPlaceholder(variable_name="history"),
    ("human", "{input}")
])

def chat(input_text: str) -> str:
    # 加载历史
    history = memory.load_memory_variables({})["history"]
    
    # 调用链
    chain = prompt | model | StrOutputParser()
    result = chain.invoke({
        "history": history,
        "input": input_text
    })
    
    # 保存对话
    memory.save_context(
        {"input": input_text},
        {"output": result}
    )
    
    return result

# 测试
print(chat("你好,我叫张三"))
print(chat("我叫什么?"))  # 会记住名字

使用 RunnableWithMessageHistory #

python
from langchain_core.runnables import RunnableWithMessageHistory
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.chat_history import BaseChatMessageHistory

model = ChatOpenAI(model="gpt-4o-mini")

prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个有帮助的助手"),
    MessagesPlaceholder(variable_name="history"),
    ("human", "{input}")
])

chain = prompt | model

# 会话存储
store = {}

def get_session_history(session_id: str) -> BaseChatMessageHistory:
    if session_id not in store:
        store[session_id] = ChatMessageHistory()
    return store[session_id]

# 创建带历史的链
chain_with_history = RunnableWithMessageHistory(
    chain,
    get_session_history,
    input_messages_key="input",
    history_messages_key="history"
)

# 使用
result = chain_with_history.invoke(
    {"input": "你好,我叫张三"},
    config={"configurable": {"session_id": "user-123"}}
)

result = chain_with_history.invoke(
    {"input": "我叫什么?"},
    config={"configurable": {"session_id": "user-123"}}
)
text
┌─────────────────────────────────────────────────────────────┐
│                    会话管理流程                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  用户请求 ───> 获取会话历史 ───> 构建提示 ───> 调用模型      │
│                                    │                        │
│                                    ▼                        │
│  返回响应 <─── 保存对话历史 <─── 获取响应                    │
│                                                             │
│  session_id 用于区分不同用户/会话                            │
│                                                             │
└─────────────────────────────────────────────────────────────┘

持久化存储 #

文件存储 #

python
from langchain_community.chat_message_histories import FileChatMessageHistory

# 创建文件存储的历史记录
history = FileChatMessageHistory("chat_history.json")

# 添加消息
from langchain_core.messages import HumanMessage, AIMessage
history.add_message(HumanMessage(content="你好"))
history.add_message(AIMessage(content="你好!"))

# 读取消息
print(history.messages)

Redis 存储 #

python
from langchain_community.chat_message_histories import RedisChatMessageHistory

history = RedisChatMessageHistory(
    session_id="user-123",
    url="redis://localhost:6379"
)

history.add_message(HumanMessage(content="你好"))

SQLite 存储 #

python
from langchain_community.chat_message_histories import SQLChatMessageHistory

history = SQLChatMessageHistory(
    session_id="user-123",
    connection="sqlite:///chat_history.db"
)

MongoDB 存储 #

python
from langchain_community.chat_message_histories import MongoDBChatMessageHistory

history = MongoDBChatMessageHistory(
    session_id="user-123",
    connection_string="mongodb://localhost:27017",
    database_name="chat_db",
    collection_name="chat_history"
)

自定义记忆 #

创建自定义记忆类 #

python
from langchain.memory import BaseChatMemory
from langchain_core.messages import BaseMessage, HumanMessage, AIMessage
from typing import List, Dict, Any

class CustomMemory(BaseChatMemory):
    """自定义记忆:只记住包含关键词的对话"""
    
    def __init__(self, keywords: List[str]):
        self.keywords = keywords
        self.messages: List[BaseMessage] = []
    
    @property
    def memory_variables(self) -> List[str]:
        return ["history"]
    
    def load_memory_variables(self, inputs: Dict[str, Any]) -> Dict[str, str]:
        # 只返回包含关键词的消息
        relevant_messages = []
        for msg in self.messages:
            if any(kw in msg.content for kw in self.keywords):
                relevant_messages.append(msg)
        
        return {"history": "\n".join([m.content for m in relevant_messages])}
    
    def save_context(self, inputs: Dict[str, Any], outputs: Dict[str, str]) -> None:
        input_text = inputs.get("input", "")
        output_text = outputs.get("output", "")
        
        # 只保存包含关键词的对话
        if any(kw in input_text or kw in output_text for kw in self.keywords):
            self.messages.append(HumanMessage(content=input_text))
            self.messages.append(AIMessage(content=output_text))
    
    def clear(self) -> None:
        self.messages = []

# 使用
memory = CustomMemory(keywords=["名字", "喜欢", "重要"])

带权重的记忆 #

python
from collections import defaultdict
from datetime import datetime

class WeightedMemory:
    """带权重的记忆:重要信息权重更高"""
    
    def __init__(self):
        self.memories = []
        self.importance_keywords = {
            "名字": 10,
            "喜欢": 5,
            "讨厌": 5,
            "重要": 8,
            "记住": 7
        }
    
    def add_memory(self, text: str, response: str):
        # 计算重要性
        importance = 0
        for keyword, weight in self.importance_keywords.items():
            if keyword in text:
                importance += weight
        
        self.memories.append({
            "text": text,
            "response": response,
            "importance": importance,
            "timestamp": datetime.now()
        })
    
    def get_relevant_memories(self, query: str, top_k: int = 5):
        # 按重要性排序
        sorted_memories = sorted(
            self.memories,
            key=lambda x: x["importance"],
            reverse=True
        )
        return sorted_memories[:top_k]

记忆管理策略 #

1. 清理过期记忆 #

python
from datetime import datetime, timedelta

def clean_old_memories(memories: list, days: int = 7):
    """清理超过指定天数的记忆"""
    cutoff = datetime.now() - timedelta(days=days)
    return [m for m in memories if m["timestamp"] > cutoff]

2. 压缩记忆 #

python
from langchain.chains import LLMChain
from langchain_core.prompts import PromptTemplate

def compress_memories(model, memories: list) -> str:
    """使用 LLM 压缩多条记忆"""
    memory_text = "\n".join([
        f"用户: {m['text']}\nAI: {m['response']}"
        for m in memories
    ])
    
    prompt = PromptTemplate.from_template(
        "请将以下对话历史压缩成简短的摘要:\n\n{history}"
    )
    
    chain = prompt | model | StrOutputParser()
    return chain.invoke({"history": memory_text})

3. 记忆优先级 #

python
class PriorityMemory:
    """按优先级管理记忆"""
    
    def __init__(self, max_items: int = 100):
        self.max_items = max_items
        self.memories = []
    
    def add(self, text: str, priority: int = 0):
        self.memories.append({
            "text": text,
            "priority": priority,
            "access_count": 0
        })
        
        # 超过限制时,移除优先级最低的
        if len(self.memories) > self.max_items:
            self.memories.sort(key=lambda x: x["priority"])
            self.memories = self.memories[-self.max_items:]
    
    def access(self, index: int):
        """访问记忆时增加访问计数"""
        if 0 <= index < len(self.memories):
            self.memories[index]["access_count"] += 1
            # 热门记忆提升优先级
            self.memories[index]["priority"] += 1

完整示例 #

智能客服系统 #

python
from langchain_openai import ChatOpenAI
from langchain.memory import ConversationSummaryBufferMemory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnableWithMessageHistory
from langchain_community.chat_message_histories import RedisChatMessageHistory

model = ChatOpenAI(model="gpt-4o-mini")

prompt = ChatPromptTemplate.from_messages([
    ("system", """你是一个专业的客服助手。
    
请记住用户的重要信息,包括:
- 姓名
- 订单号
- 问题描述
- 联系方式

请用专业、友好的语气回复。"""),
    MessagesPlaceholder(variable_name="history"),
    ("human", "{input}")
])

def get_session_history(session_id: str):
    return RedisChatMessageHistory(
        session_id=session_id,
        url="redis://localhost:6379"
    )

chain = prompt | model | StrOutputParser()

chain_with_history = RunnableWithMessageHistory(
    chain,
    get_session_history,
    input_messages_key="input",
    history_messages_key="history"
)

def chat(session_id: str, message: str) -> str:
    return chain_with_history.invoke(
        {"input": message},
        config={"configurable": {"session_id": session_id}}
    )

# 使用
session_id = "customer-001"
print(chat(session_id, "你好,我叫张三,订单号是12345"))
print(chat(session_id, "我的订单还没收到,能帮我查一下吗?"))
print(chat(session_id, "我的订单号是多少?"))  # 会记住订单号

最佳实践 #

1. 选择合适的记忆类型 #

python
# 短对话 - Buffer
memory = ConversationBufferMemory()

# 长对话 - Summary
memory = ConversationSummaryMemory(llm=model)

# 有限上下文 - Window
memory = ConversationBufferWindowMemory(k=5)

# 大规模应用 - VectorStore
memory = VectorStoreRetrieverMemory(retriever=retriever)

2. 设置合理的限制 #

python
# Token 限制
memory = ConversationTokenBufferMemory(
    llm=model,
    max_token_limit=2000  # 根据模型上下文长度设置
)

# 轮数限制
memory = ConversationBufferWindowMemory(k=10)

3. 定期清理 #

python
# 设置过期时间
# 使用 Redis 时可以设置 TTL
history = RedisChatMessageHistory(
    session_id="user-123",
    url="redis://localhost:6379",
    ttl=86400  # 24小时后过期
)

下一步 #

最后更新:2026-03-30