LangChain 1.3 完全教程:从入门到精通-Part 10: Memory(记忆系统)

文章目录

Part 10: Memory(记忆系统)

10.1 记忆概述

为什么需要记忆

默认情况下,LLM 是无状态的 ------ 每次调用都是独立的,模型不会记住之前的对话内容。但在实际应用中,我们需要:

  1. 多轮对话:用户期望 AI 记住之前说过的话
  2. 上下文连贯:后续问题可能引用之前的内容
  3. 个性化:记住用户的偏好和历史行为
  4. 任务连续性:复杂任务需要跨多轮交互完成
记忆类型

#mermaid-svg-3elUVsdEJWfufDC8{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3elUVsdEJWfufDC8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3elUVsdEJWfufDC8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3elUVsdEJWfufDC8 .error-icon{fill:#552222;}#mermaid-svg-3elUVsdEJWfufDC8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3elUVsdEJWfufDC8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3elUVsdEJWfufDC8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3elUVsdEJWfufDC8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3elUVsdEJWfufDC8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3elUVsdEJWfufDC8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3elUVsdEJWfufDC8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3elUVsdEJWfufDC8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3elUVsdEJWfufDC8 .marker.cross{stroke:#333333;}#mermaid-svg-3elUVsdEJWfufDC8 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3elUVsdEJWfufDC8 p{margin:0;}#mermaid-svg-3elUVsdEJWfufDC8 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3elUVsdEJWfufDC8 .cluster-label text{fill:#333;}#mermaid-svg-3elUVsdEJWfufDC8 .cluster-label span{color:#333;}#mermaid-svg-3elUVsdEJWfufDC8 .cluster-label span p{background-color:transparent;}#mermaid-svg-3elUVsdEJWfufDC8 .label text,#mermaid-svg-3elUVsdEJWfufDC8 span{fill:#333;color:#333;}#mermaid-svg-3elUVsdEJWfufDC8 .node rect,#mermaid-svg-3elUVsdEJWfufDC8 .node circle,#mermaid-svg-3elUVsdEJWfufDC8 .node ellipse,#mermaid-svg-3elUVsdEJWfufDC8 .node polygon,#mermaid-svg-3elUVsdEJWfufDC8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3elUVsdEJWfufDC8 .rough-node .label text,#mermaid-svg-3elUVsdEJWfufDC8 .node .label text,#mermaid-svg-3elUVsdEJWfufDC8 .image-shape .label,#mermaid-svg-3elUVsdEJWfufDC8 .icon-shape .label{text-anchor:middle;}#mermaid-svg-3elUVsdEJWfufDC8 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3elUVsdEJWfufDC8 .rough-node .label,#mermaid-svg-3elUVsdEJWfufDC8 .node .label,#mermaid-svg-3elUVsdEJWfufDC8 .image-shape .label,#mermaid-svg-3elUVsdEJWfufDC8 .icon-shape .label{text-align:center;}#mermaid-svg-3elUVsdEJWfufDC8 .node.clickable{cursor:pointer;}#mermaid-svg-3elUVsdEJWfufDC8 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3elUVsdEJWfufDC8 .arrowheadPath{fill:#333333;}#mermaid-svg-3elUVsdEJWfufDC8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3elUVsdEJWfufDC8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3elUVsdEJWfufDC8 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3elUVsdEJWfufDC8 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3elUVsdEJWfufDC8 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3elUVsdEJWfufDC8 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3elUVsdEJWfufDC8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3elUVsdEJWfufDC8 .cluster text{fill:#333;}#mermaid-svg-3elUVsdEJWfufDC8 .cluster span{color:#333;}#mermaid-svg-3elUVsdEJWfufDC8 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3elUVsdEJWfufDC8 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3elUVsdEJWfufDC8 rect.text{fill:none;stroke-width:0;}#mermaid-svg-3elUVsdEJWfufDC8 .icon-shape,#mermaid-svg-3elUVsdEJWfufDC8 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3elUVsdEJWfufDC8 .icon-shape p,#mermaid-svg-3elUVsdEJWfufDC8 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3elUVsdEJWfufDC8 .icon-shape .label rect,#mermaid-svg-3elUVsdEJWfufDC8 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3elUVsdEJWfufDC8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3elUVsdEJWfufDC8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3elUVsdEJWfufDC8 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Memory 记忆系统
短期记忆
长期记忆
混合记忆
全量缓冲

ConversationBufferMemory
窗口缓冲

ConversationBufferWindowMemory
摘要记忆

ConversationSummaryMemory
向量记忆

VectorStoreRetrieverMemory
摘要+缓冲

ConversationSummaryBufferMemory
LangGraph Checkpointer

记忆类型对比表格
记忆类型 存储方式 Token 消耗 信息保留 适用场景
ConversationBufferMemory 全部消息 高(线性增长) 完整 短对话
ConversationBufferWindowMemory 最近 k 轮 固定 部分丢失 中等长度对话
ConversationSummaryMemory 对话摘要 摘要级别 长对话
ConversationSummaryBufferMemory 摘要+最近消息 中等 较好 长对话(推荐)
VectorStoreRetrieverMemory 向量检索 按需 语义相关 超长对话
LangGraph Checkpointer 完整状态 按需加载 完整 生产环境(推荐)

10.2 LangGraph Checkpointer(推荐)

LangGraph 的 Checkpointer 是 LangChain 1.3 中推荐的记忆管理方案,它通过保存和恢复图的完整状态来实现记忆功能。

InMemorySaver 完整 Demo
python 复制代码
"""
LangGraph InMemorySaver 完整 Demo
安装: pip install langgraph
"""
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.graph import StateGraph, START, END
from langchain_openai import ChatOpenAI
from typing import Annotated
from typing_extensions import TypedDict


# ==========================================
# 1. 定义状态
# ==========================================

class State(TypedDict):
    """定义图的状态结构"""
    messages: list  # 消息列表(会自动累积)


# ==========================================
# 2. 定义节点
# ==========================================

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)


def chatbot(state: State):
    """聊天机器人节点:接收消息列表,返回新的消息"""
    response = llm.invoke(state["messages"])
    return {"messages": [response]}


# ==========================================
# 3. 构建图
# ==========================================

# 创建内存检查点
checkpointer = InMemorySaver()

# 构建状态图
graph_builder = StateGraph(State)
graph_builder.add_node("chatbot", chatbot)
graph_builder.add_edge(START, "chatbot")
graph_builder.add_edge("chatbot", END)

# 编译图时传入检查点
graph = graph_builder.compile(checkpointer=checkpointer)

# ==========================================
# 4. 使用 thread_id 管理对话
# ==========================================

# 每个用户/对话使用唯一的 thread_id
config = {"configurable": {"thread_id": "user_123"}}

# 第一轮对话
result = graph.invoke(
    {"messages": [("human", "我叫张三,我是一名 Python 开发者。")]},
    config=config,
)
print(f"AI: {result['messages'][-1].content}")

# 第二轮对话(AI 会记住之前的信息)
result = graph.invoke(
    {"messages": [("human", "我叫什么名字?我是做什么的?")]},
    config=config,
)
print(f"AI: {result['messages'][-1].content}")
# AI 应该回答 "你叫张三,是一名 Python 开发者"

# ==========================================
# 5. 查看对话历史
# ==========================================

# 获取当前状态
state = graph.get_state(config)
print(f"\n当前对话消息数: {len(state.values['messages'])}")
for msg in state.values['messages']:
    role = msg.type
    content = msg.content[:50]
    print(f"  [{role}] {content}...")
SqliteSaver 持久化 Demo
python 复制代码
"""
LangGraph SqliteSaver 持久化 Demo
安装: pip install langgraph
"""
from langgraph.checkpoint.sqlite import SqliteSaver
from langgraph.graph import StateGraph, START, END
from langchain_openai import ChatOpenAI
from typing_extensions import TypedDict


class State(TypedDict):
    messages: list


llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)


def chatbot(state: State):
    response = llm.invoke(state["messages"])
    return {"messages": [response]}


# ==========================================
# 使用 SQLite 持久化检查点
# ==========================================

DB_PATH = "/data/user/work/langgraph_checkpoint.db"

# 创建 SQLite 检查点(数据持久化到磁盘)
with SqliteSaver.from_conn_string(DB_PATH) as checkpointer:
    graph_builder = StateGraph(State)
    graph_builder.add_node("chatbot", chatbot)
    graph_builder.add_edge(START, "chatbot")
    graph_builder.add_edge("chatbot", END)

    graph = graph_builder.compile(checkpointer=checkpointer)

    config = {"configurable": {"thread_id": "persistent_user_1"}}

    # 对话数据会持久化到 SQLite 数据库
    # 即使程序重启,也能恢复之前的对话
    result = graph.invoke(
        {"messages": [("human", "记住:我最喜欢的颜色是蓝色。")]},
        config=config,
    )
    print(f"AI: {result['messages'][-1].content}")

    # 模拟程序重启后恢复对话
    result = graph.invoke(
        {"messages": [("human", "我最喜欢什么颜色?")]},
        config=config,
    )
    print(f"AI: {result['messages'][-1].content}")
多用户隔离 Demo
python 复制代码
"""
多用户隔离 Demo
使用不同的 thread_id 实现用户间的对话隔离
"""
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.graph import StateGraph, START, END
from langchain_openai import ChatOpenAI
from typing_extensions import TypedDict


class State(TypedDict):
    messages: list


llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)


def chatbot(state: State):
    response = llm.invoke(state["messages"])
    return {"messages": [response]}


checkpointer = InMemorySaver()
graph_builder = StateGraph(State)
graph_builder.add_node("chatbot", chatbot)
graph_builder.add_edge(START, "chatbot")
graph_builder.add_edge("chatbot", END)
graph = graph_builder.compile(checkpointer=checkpointer)

# ==========================================
# 用户 A 的对话
# ==========================================

config_a = {"configurable": {"thread_id": "user_alice"}}

graph.invoke(
    {"messages": [("human", "我叫 Alice,我在学习机器学习。")]},
    config=config_a,
)

result = graph.invoke(
    {"messages": [("human", "我叫什么名字?")]},
    config=config_a,
)
print(f"Alice 的 AI: {result['messages'][-1].content}")
# 输出: 你叫 Alice

# ==========================================
# 用户 B 的对话(完全独立)
# ==========================================

config_b = {"configurable": {"thread_id": "user_bob"}}

result = graph.invoke(
    {"messages": [("human", "我叫什么名字?")]},
    config=config_b,
)
print(f"Bob 的 AI: {result['messages'][-1].content}")
# 输出: 我不知道你的名字(Bob 的对话是全新的)

# ==========================================
# 用户 A 继续对话(记忆仍在)
# ==========================================

result = graph.invoke(
    {"messages": [("human", "我在学什么?")]},
    config=config_a,
)
print(f"Alice 的 AI: {result['messages'][-1].content}")
# 输出: 你在学习机器学习

10.3 传统记忆组件

注意:传统记忆组件在 LangChain 1.3 中仍然可用,但推荐新项目使用 LangGraph Checkpointer。

ConversationBufferMemory Demo
python 复制代码
"""
ConversationBufferMemory Demo
保存所有对话历史
"""
from langchain.memory import ConversationBufferMemory
from langchain_openai import ChatOpenAI
from langchain.chains import ConversationChain

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

# ==========================================
# 方式一:直接使用 Memory
# ==========================================

memory = ConversationBufferMemory(
    memory_key="history",        # 存储在提示中的键名(默认 "history")
    return_messages=True,        # 返回 Message 对象(而非字符串)
    input_key="input",           # 用户输入的键名
    output_key="output",         # AI 输出的键名
    human_prefix="Human",        # 人类消息前缀
    ai_prefix="AI",              # AI 消息前缀
)

# 手动添加对话
memory.save_context(
    {"input": "你好,我叫张三。"},
    {"output": "你好张三!很高兴认识你。"}
)
memory.save_context(
    {"input": "我今天心情不错。"},
    {"output": "很高兴听到这个消息!有什么开心的事情吗?"}
)

# 查看记忆内容
print(memory.load_memory_variables({}))
# {'history': [HumanMessage('你好,我叫张三。'), AIMessage('你好张三!...'), ...]}

# ==========================================
# 方式二:与 Chain 一起使用
# ==========================================

memory2 = ConversationBufferMemory()
conversation = ConversationChain(
    llm=llm,
    memory=memory2,
    verbose=False,  # 设为 True 可查看提示详情
)

# 多轮对话
response1 = conversation.predict(input="我叫李四,是一名数据科学家。")
print(f"AI: {response1}")

response2 = conversation.predict(input="我叫什么名字?")
print(f"AI: {response2}")
# AI 应该记得 "你叫李四"

# ==========================================
# 方式三:清空记忆
# ==========================================

memory.clear()
print(f"清空后: {memory.load_memory_variables({})}")
ConversationBufferWindowMemory Demo
python 复制代码
"""
ConversationBufferWindowMemory Demo
只保留最近 k 轮对话,控制 Token 消耗
"""
from langchain.memory import ConversationBufferWindowMemory
from langchain.chains import ConversationChain
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

memory = ConversationBufferWindowMemory(
    k=2,                       # 保留最近 2 轮对话(1 轮 = 1 次人类 + 1 次 AI)
    memory_key="history",
    return_messages=True,
)

conversation = ConversationChain(llm=llm, memory=memory)

# 进行 4 轮对话
conversation.predict(input="第一轮:我喜欢吃苹果。")
conversation.predict(input="第二轮:我也喜欢吃香蕉。")
conversation.predict(input="第三轮:我最喜欢的颜色是蓝色。")
conversation.predict(input="第四轮:我住在上海。")

# 查看记忆 ------ 只有最近 2 轮
history = memory.load_memory_variables({})["history"]
print(f"记忆中的消息数: {len(history)}")
# 应该只有第 3 轮和第 4 轮的消息

# 第 1 轮和第 2 轮的信息已被丢弃
response = conversation.predict(input="我最喜欢什么颜色?")
print(f"AI: {response}")  # 应该记得蓝色(在第 3 轮中提到)
ConversationSummaryMemory Demo
python 复制代码
"""
ConversationSummaryMemory Demo
使用 LLM 对对话历史进行摘要,节省 Token
"""
from langchain.memory import ConversationSummaryMemory
from langchain.chains import ConversationChain
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

memory = ConversationSummaryMemory(
    llm=llm,                    # 用于生成摘要的 LLM
    memory_key="history",
    return_messages=False,      # 返回摘要字符串(而非消息列表)
    # max_token_limit=500,      # 摘要的最大 Token 数(可选)
)

conversation = ConversationChain(llm=llm, memory=memory)

# 多轮对话
conversation.predict(input="我叫王五,是一名全栈工程师。")
conversation.predict(input="我擅长 Python 和 JavaScript。")
conversation.predict(input="我最近在学 Rust 语言。")
conversation.predict(input="我的目标是成为一名架构师。")

# 查看摘要
summary = memory.load_memory_variables({})["history"]
print(f"对话摘要:\n{summary}")
# 输出类似: "王五是一名全栈工程师,擅长 Python 和 JavaScript,
#          最近在学习 Rust,目标是成为架构师。"
ConversationSummaryBufferMemory Demo
python 复制代码
"""
ConversationSummaryBufferMemory Demo
结合摘要和窗口缓冲,兼顾信息保留和 Token 控制
"""
from langchain.memory import ConversationSummaryBufferMemory
from langchain.chains import ConversationChain
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

memory = ConversationSummaryBufferMemory(
    llm=llm,                    # 用于生成摘要的 LLM
    max_token_limit=200,        # Token 限制(超过此值时,旧消息会被摘要)
    memory_key="history",
    return_messages=True,
)

conversation = ConversationChain(llm=llm, memory=memory)

# 多轮对话
conversation.predict(input="我叫赵六,在一家 AI 公司工作。")
conversation.predict(input="我的职位是高级算法工程师。")
conversation.predict(input="我负责 NLP 相关的项目。")
conversation.predict(input="最近在做一个基于 RAG 的智能客服系统。")

# 查看记忆
variables = memory.load_memory_variables({})
print(f"当前记忆:\n{variables}")
# 旧消息会被摘要,最近的消息保持原文
VectorStoreRetrieverMemory Demo
python 复制代码
"""
VectorStoreRetrieverMemory Demo
将对话历史存入向量数据库,通过语义检索获取相关记忆
"""
from langchain.memory import VectorStoreRetrieverMemory
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_chroma import Chroma

embeddings = OpenAIEmbeddings(model="text-embedding-3-small")

# 创建向量存储
vectorstore = Chroma(
    embedding_function=embeddings,
    collection_name="conversation_memory",
)

# 创建向量记忆
memory = VectorStoreRetrieverMemory(
    retriever=vectorstore.as_retriever(
        search_kwargs={"k": 3},  # 每次检索 3 条相关记忆
    ),
    memory_key="history",
    return_messages=True,
)

# ==========================================
# 保存对话到记忆
# ==========================================

memory.save_context(
    {"input": "我最喜欢的编程语言是 Python。"},
    {"output": "Python 确实是一门很棒的语言!"}
)

memory.save_context(
    {"input": "我最近在学习 LangChain 框架。"},
    {"output": "LangChain 是一个强大的 LLM 应用开发框架。"}
)

memory.save_context(
    {"input": "我的宠物叫小白,是一只猫。"},
    {"output": "小白听起来很可爱!"}
)

memory.save_context(
    {"input": "我住在北京市海淀区。"},
    {"output": "海淀区是北京的高校和科技企业聚集地。"}
)

# ==========================================
# 通过语义检索获取相关记忆
# ==========================================

result = memory.load_memory_variables(
    {"input": "我喜欢什么编程语言?"}  # 会检索与"编程语言"相关的记忆
)

print("相关记忆:")
for msg in result["history"]:
    print(f"  [{msg.type}] {msg.content}")

# 输出应该包含关于 Python 和 LangChain 的对话
# 但不会包含关于宠物和住址的对话(与编程语言无关)
每种记忆的适用场景
记忆类型 Token 消耗 信息保留 适用场景
ConversationBufferMemory 完整 短对话(<10 轮)
ConversationBufferWindowMemory 固定 最近 k 轮 中等对话,关注近期上下文
ConversationSummaryMemory 摘要级别 长对话,只需大致上下文
ConversationSummaryBufferMemory 中等 摘要+近期 长对话(最平衡的选择
VectorStoreRetrieverMemory 按需 语义相关 超长对话,需要精准检索

10.4 在链中使用记忆

LCEL + 记忆 Demo
python 复制代码
"""
LCEL + 记忆 Demo
使用 RunnableWithMessageHistory 为 LCEL 链添加记忆
"""
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_openai import ChatOpenAI
from langchain_community.chat_message_histories import ChatMessageHistory

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

# ==========================================
# 1. 创建带历史消息占位符的提示
# ==========================================

prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个有帮助的 AI 助手。请根据对话历史回答问题。"),
    MessagesPlaceholder(variable_name="history"),  # 对话历史占位符
    ("human", "{question}"),
])

# ==========================================
# 2. 创建链
# ==========================================

chain = prompt | llm

# ==========================================
# 3. 创建消息历史存储
# ==========================================

# 使用字典存储不同会话的消息历史
store = {}  # {session_id: ChatMessageHistory}


def get_session_history(session_id: str):
    """获取或创建会话的消息历史"""
    if session_id not in store:
        store[session_id] = ChatMessageHistory()
    return store[session_id]


# ==========================================
# 4. 包装链(添加记忆功能)
# ==========================================

chain_with_history = RunnableWithMessageHistory(
    chain,
    get_session_history,           # 获取消息历史的函数
    input_messages_key="question", # 输入消息的键名
    history_messages_key="history",# 历史消息的键名
)

# ==========================================
# 5. 使用带记忆的链
# ==========================================

config = {"configurable": {"session_id": "session_1"}}

# 第一轮
response = chain_with_history.invoke(
    {"question": "我叫小明,我是一名 AI 工程师。"},
    config=config,
)
print(f"AI: {response.content}")

# 第二轮(有记忆)
response = chain_with_history.invoke(
    {"question": "我叫什么名字?做什么工作的?"},
    config=config,
)
print(f"AI: {response.content}")
# 应该回答 "你叫小明,是一名 AI 工程师"

# 不同会话(独立记忆)
config2 = {"configurable": {"session_id": "session_2"}}
response = chain_with_history.invoke(
    {"question": "你知道我是谁吗?"},
    config=config2,
)
print(f"AI: {response.content}")
# 应该回答 "我不知道"(新会话,没有历史)
带记忆的 RAG Demo
python 复制代码
"""
带记忆的 RAG Demo
结合对话记忆和检索增强生成
"""
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_chroma import Chroma
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables import RunnablePassthrough
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_core.output_parsers import StrOutputParser
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.documents import Document

embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

# 准备知识库
documents = [
    Document(page_content="LangChain 支持多种向量数据库,包括 Chroma、FAISS、Pinecone 等。"),
    Document(page_content="RAG 系统的核心步骤:文档加载、分割、嵌入、存储、检索、生成。"),
    Document(page_content="文本分割推荐使用 RecursiveCharacterTextSplitter,chunk_size 建议 500-1000。"),
]

vectorstore = Chroma.from_documents(documents, embeddings)
retriever = vectorstore.as_retriever(k=2)

# 创建提示模板(包含对话历史)
prompt = ChatPromptTemplate.from_messages([
    ("system", """你是一个专业的 AI 助手。请根据以下上下文和对话历史回答问题。

上下文:
{context}

如果上下文中没有相关信息,请基于你的知识回答,但要说明这不是来自知识库。"""),
    MessagesPlaceholder(variable_name="history"),
    ("human", "{question}"),
])


def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)


# 创建基础 RAG 链
rag_chain = (
    {
        "context": retriever | format_docs,
        "question": RunnablePassthrough(),
    }
    | prompt
    | llm
    | StrOutputParser()
)

# 添加记忆
store = {}


def get_history(session_id: str):
    if session_id not in store:
        store[session_id] = ChatMessageHistory()
    return store[session_id]


rag_with_memory = RunnableWithMessageHistory(
    rag_chain,
    get_history,
    input_messages_key="question",
    history_messages_key="history",
)

# 使用
config = {"configurable": {"session_id": "rag_session_1"}}

response = rag_with_memory.invoke("什么是 RAG?", config=config)
print(f"AI: {response}")

# 后续问题可以引用之前的回答
response = rag_with_memory.invoke("它有哪些核心步骤?", config=config)
print(f"AI: {response}")
# AI 应该理解 "它" 指的是 RAG

10.5 记忆最佳实践

"""

记忆最佳实践总结

"""

==========================================

1. 记忆类型选择指南

=========================================="""

对话长度 推荐记忆类型 理由
< 10 轮 ConversationBufferMemory 简单直接,信息完整
10-50 轮 ConversationSummaryBufferMemory 平衡 Token 和信息保留
> 50 轮 VectorStoreRetrieverMemory 语义检索,不受长度限制
生产环境 LangGraph Checkpointer 可靠、可扩展、支持持久化
多用户 LangGraph Checkpointer (thread_id) 天然支持用户隔离
"""

==========================================

2. Token 消耗优化

=========================================="""

  1. 使用 ConversationSummaryBufferMemory 而非 ConversationBufferMemory
  2. 设置合理的 max_token_limit(建议 500-1000)
  3. 定期清理过期的对话历史
  4. 使用 VectorStoreRetrieverMemory 进行语义检索,只加载相关记忆
  5. 对长对话使用摘要而非原文
  6. 考虑使用更便宜的模型生成摘要
    """

==========================================

3. 长期记忆管理

=========================================="""

  1. 重要信息提取:从对话中提取关键事实存入结构化存储
  2. 定期摘要:对长时间未访问的对话进行摘要
  3. 分层存储:近期对话保持原文,远期对话只保留摘要
  4. 向量检索:使用向量数据库存储历史对话,按需检索
  5. 用户画像:维护用户的偏好和特征信息
    """

相关推荐
糖果店的幽灵1 小时前
LangChain 1.3 完全教程:从入门到精通-Part 8: Vector Stores(向量存储)
langchain
杜子不疼.2 小时前
Agent Skills 的演进治理与 Swarm Skills 自演进
服务器·数据库·microsoft
tealcwu2 小时前
【Unity实战】Unity IAP 5.3 中实现 Windows Custom Store 实战教程
windows·unity·游戏引擎
坊钰3 小时前
【LangChain框架入门级】1
langchain
yumgpkpm3 小时前
华为HUAWEI昇腾910B下千问Qwen3.6-27B在的推理加速实践
sql·华为·langchain·json·ai编程·ai写作·gpu算力
ZenosDoron3 小时前
vsnprintf可变参数格式化输出函数
windows
牛猫Data3 小时前
POWER BI技巧:报告名称的Emoji符号妙用
microsoft·数据分析·数据可视化·powerbi
许彰午4 小时前
11_Java集合框架概述
java·windows·python
lhxcc_fly4 小时前
5.LangChain--输出解析器
langchain·llm·输出解析器