🌟 LangChain 30 天保姆级教程 · Day 21|Memory 机制实战!让 AI 记住你说过的话,实现多轮连贯对话!

系列目标 :30 天从 LangChain 入门到企业级部署
今日任务:理解 Memory 类型 → 掌握 ConversationBufferMemory → 实现"带历史的 RAG 聊天机器人"!


💭 一、为什么需要 Memory?

没有记忆的 AI 像"金鱼":

  • 用户:"帮我查订单 1001"
  • AI:"订单 1001 已发货。"
  • 用户:"那物流单号是多少?"
  • AI:"请提供订单号。"

问题:AI 忘了上一句提到的"订单 1001"!

解决方案

Memory 机制 ------ 将对话历史注入每次 Prompt,让 LLM 理解上下文!
💡 今天,我们就用 LangChain 的 Memory 模块,构建一个能记住 5 轮对话的智能客服!


🧱 二、LangChain Memory 核心类型

表格

Memory 类型 特点 适用场景
ConversationBufferMemory 保存全部历史 短对话、调试
ConversationBufferWindowMemory 只保留最近 N 轮 长对话、控制 token
ConversationSummaryMemory 用 LLM 生成摘要 超长对话(节省 token)
RedisChatMessageHistory 外部存储(分布式) Web 应用、多实例

Day 21 重点:ConversationBufferWindowMemory + RAG


🛠️ 三、动手实践 1:基础对话记忆(无 RAG)

ini 复制代码
# day21_memory_chat.py
from langchain_ollama import ChatOllama
from langchain.memory import ConversationBufferWindowMemory
from langchain.chains import ConversationChain

# 初始化 LLM 和 Memory(只记最近 3 轮)
llm = ChatOllama(model="qwen:7b", temperature=0)
memory = ConversationBufferWindowMemory(k=3)

# 创建对话链
conversation = ConversationChain(
    llm=llm,
    memory=memory,
    verbose=True
)

# 多轮对话
print(conversation.predict(input="你好!"))
print(conversation.predict(input="我叫小明"))
print(conversation.predict(input="我叫什么名字?"))  # AI 能答对!

▶️ 输出示例:

vbnet 复制代码
> Entering new ConversationChain chain...
Prompt after formatting:
The following is a friendly conversation between a human and an AI...
Current conversation:
Human: 你好!
AI: 你好!有什么我可以帮你的吗?
Human: 我叫小明
AI: 很高兴认识你,小明!
Human: 我叫什么名字?
AI: 你叫小明。

✅ 成功记住"小明"!


🤖 四、动手实践 2:RAG + Memory(终极组合)

现在,我们要构建一个既能查知识库,又能记对话的客服机器人!

步骤 1:准备带 Memory 的 Prompt

ini 复制代码
from langchain_core.prompts import PromptTemplate

# 自定义 Prompt,包含 {history} 和 {context}
template = """
你是一个公司客服助手,请根据以下信息回答问题:

已知资料:
{context}

对话历史:
{history}

用户新问题:{input}

回答:
"""

PROMPT = PromptTemplate(
    input_variables=["history", "context", "input"],
    template=template
)

步骤 2:加载 RAG 组件(复用 Day 18)

ini 复制代码
from langchain_chroma import Chroma
from langchain_ollama import OllamaEmbeddings
from langchain.chains import RetrievalQA

# 向量库
embeddings = OllamaEmbeddings(model="nomic-embed-text")
vectorstore = Chroma(persist_directory="./chroma_db", embedding_function=embeddings)
retriever = vectorstore.as_retriever(search_kwargs={"k": 2})

步骤 3:创建带 Memory 的 RAG Chain

ini 复制代码
from langchain.memory import ConversationBufferWindowMemory
from langchain.chains.conversation.base import ConversationChain

# 初始化 Memory
memory = ConversationBufferWindowMemory(
    memory_key="history",      # 对应 Prompt 中的 {history}
    input_key="input",         # 用户输入字段名
    k=3                        # 记住最近 3 轮
)

# 创建链
rag_with_memory = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff",
    retriever=retriever,
    memory=memory,             # 注入 Memory!
    chain_type_kwargs={"prompt": PROMPT},
    return_source_documents=True
)

⚠️ 注意:RetrievalQA 支持 memory 参数(LangChain >=0.1.0)


步骤 4:测试多轮 RAG 对话

bash 复制代码
# 第一轮
res1 = rag_with_memory({"query": "员工年假有多少天?"})
print("Q1:", res1["result"])

# 第二轮(指代上文)
res2 = rag_with_memory({"query": "那病假呢?"})  # AI 知道在问"假期政策"
print("Q2:", res2["result"])

# 第三轮(混合上下文)
res3 = rag_with_memory({"query": "刚才说的年假能跨年吗?"})
print("Q3:", res3["result"])

▶️ 预期效果:

  • Q2:正确返回病假政策(而非重复年假)
  • Q3:正确引用"年假不可跨年"条款

真正实现

  • 语义检索(RAG)
  • 上下文理解(Memory)
  • 引用溯源(Source Documents)

☁️ 五、进阶:用 Redis 存储对话历史(Web 应用必备)

在 Web 服务中,需按 session_id 存储历史:

ini 复制代码
from langchain_community.chat_message_histories import RedisChatMessageHistory
from langchain.memory import ConversationSummaryBufferMemory

def get_memory(session_id: str):
    message_history = RedisChatMessageHistory(
        url="redis://localhost:6379",
        ttl=3600,  # 1小时过期
        session_id=session_id
    )
    return ConversationSummaryBufferMemory(
        chat_memory=message_history,
        max_token_limit=500,
        llm=llm  # 用于生成摘要
    )

# 在 FastAPI 中使用
@app.post("/chat")
async def chat(req: ChatRequest):
    memory = get_memory(req.session_id)
    chain = RetrievalQA(..., memory=memory, ...)
    return chain({"query": req.message})

✅ 支持分布式部署、会话隔离、自动过期!


⚠️ 六、注意事项 & 最佳实践

表格

问题 建议
Token 超限 WindowMemorySummaryMemory 控制长度
历史干扰检索 在 Prompt 中明确区分"历史"和"资料"
敏感信息留存 定期清理 Redis;Memory 中脱敏(结合 Day 14)
多用户混淆 务必用 session_id 隔离(Web 场景)
成本增加 每次调用都传历史 → 增加 token 消耗

💡 生产建议

  • 默认只记 2~3 轮(足够覆盖 90% 场景)
  • 对"重置对话"指令清空 Memory
  • 监控平均对话轮数,优化 k

📦 七、配套代码结构

bash 复制代码
langchain-30-days/
└── day21/
    ├── memory_rag_chat.py        # 带记忆的 RAG 聊天
    └── web_memory_demo.py        # Redis + FastAPI 示例(进阶)

📝 八、今日小结

  • ✅ 理解了 Memory 对多轮对话的必要性
  • ✅ 学会了 ConversationBufferWindowMemory 控制历史长度
  • ✅ 实现了 RAG + Memory 的融合系统
  • ✅ 掌握了用 Redis 支持 Web 应用的分布式记忆
  • ✅ 知道了 Token 控制与安全清理策略

🎯 明日预告:Day 22 ------ 长文档处理!MapReduce 与 Refine Chain 解决超长上下文难题!

相关推荐
kyle-fang6 小时前
langchain各类文档加载
windows·python·langchain
donglianyou6 小时前
Agent技术详解与实战
python·langchain·agent·langgraph
凌奕7 小时前
LangChain 接外部工具:MCP 和 CLI 到底该选哪个?
langchain
liu****8 小时前
LangGraph-AI应用开发框架(五)
python·langchain·大模型·langgraph
花千树-0108 小时前
三个 Agent 并行调研:用 concurrent 节点构建并发-汇聚式旅游规划助手
java·langchain·agent·function call·multi agent·mcp·harness
是小蟹呀^8 小时前
【整理】Agent中的ReAct架构
langchain·agent·react
舒一笑18 小时前
大模型根本不是“学会了”,它只是会“看例子”:一文讲透 In-context Learning(ICL)
langchain·llm·openai
Java后端的Ai之路21 小时前
LangChain ReAct Agent 核心技术问答
前端·react.js·langchain
Java后端的Ai之路1 天前
LangChain 面试问答指南2
面试·职场和发展·langchain
Java后端的Ai之路1 天前
当大模型开始“水土不服“:从通才到专才的进化论——Fine-tuning 企业级实战全攻略
人工智能·python·langchain·rag·lcel