langchain短期 + 长期记忆架构
整体架构
用户输入
│
├── 短期记忆(WindowMemory)── 内存,最近 K 条消息
│
├── 长期记忆(ChromaDB) ──── 语义检索,历史重点
│
├── 知识库(ChromaDB) ────── 文档检索
│
└── 拼接 Prompt → LLM → 输出
│
├── 写入短期窗口(内存)
├── 写入 MySQL(完整备份)
└── 后台提取长期记忆点 → 写入 ChromaDB
三层存储
| 层级 | 存储 | 数据源 | 用途 | 容量 |
|---|---|---|---|---|
| 短期 | 内存 OrderedDict |
当前会话实时写入 | 最近 K 轮对话连贯性 | k × 2 条消息 |
| 长期 | ChromaDB longMemory 集合 |
每次回复后 AI 自动提取 | 跨 session 的语义记忆 | 无限制 |
| 备份 | MySQL chat_messages 表 |
每次对话实时写入 | 前端历史展示、审计 | 无限制 |
短期记忆(WindowMemory)
实现
python
# memory_manager.py
class MemoryManager:
def __init__(self, window_k=2):
self.window_k = window_k
self._windows = OrderedDict()
def get_context(self, session_id):
"""返回窗口内的最近消息"""
memory = self.get_window(session_id)
return memory.load_memory_variables({})["history"]
机制
- 每个 session 独立一个
ConversationBufferWindowMemory(k=2) k=2表示保留最近 2 轮对话(= 4 条消息:2 条 user + 2 条 assistant)- 新消息通过
save_context写入,超出 K 轮自动丢弃最早的消息 - 纯内存操作,不查数据库
写入时机
python
# streamInvoke 中
self.memoryger.get_window(session_id).chat_memory.add_user_message(question)
...
self.memoryger.get_window(session_id).chat_memory.add_ai_message(answer)
长期记忆(ChromaDB)
提取
每次对话完成后,后台线程调用 LLM 从对话中提取关键信息:
python
# langchain_chat.py generateSummary()
prompt = """
总结以下对话的核心语义。
保留:任务目标、问题描述、解决方案、重要约束
忽略:礼貌用语、重复内容、确认性回复
如果没有任何实质性内容,返回:NONE
用户:... AI:...
总结:"""
提取结果存入 ChromaDB 的 longMemory 集合:
python
chromadb.write_long_memory(
texts=["提取的摘要"],
metadata_list=[{"session_id": "xxx"}]
)
检索
用户提问时,从 longMemory 集合中按 session 检索相关内容:
python
longMemoryter = chromadb.get_long_memory_retrieve(
k=10,
filter={"session_id": input_data.session_id}
)
longMemorys = longMemoryter.invoke(input_data.question)
Prompt 组装
最终发给 LLM 的 prompt 结构:
历史对话摘要(ChromaDB 长期记忆):
{longMemorys}
最近通话记录(WindowMemory 短期窗口):
{recentHistory}
资料(知识库 RAG 检索):
{docs}
用户问题:
{input}
各模块职责
| 模块 | 文件 | 职责 |
|---|---|---|
MemoryManager |
memory_manager.py |
管理短期窗口,按 session 隔离 |
Chromadb |
langchain_chromadb.py |
知识库 + 长期记忆的向量存储 |
LangchainChat |
langchain_chat.py |
主流程编排,流式对话 |
FinishReasonHandler |
langchain_chat.py |
检测 LLM 输出是否被截断 |
数据流完整链路
用户发送消息
│
▼
1. 从 MemoryManager 取短期窗口消息 ── 内存,不查 DB
2. 从 ChromaDB 检索长期记忆 ──────── 按 session_id 语义检索
3. 从 ChromaDB 检索知识库 ────────── RAG 检索
4. 拼接 Prompt → LLM 流式生成
│
▼
流式输出到前端
│
▼
完成后:
├── 写入短期窗口(MemoryManager)
├── 写入 MySQL(create_chat_message)
└── 后台线程 → LLM 提取摘要 → 写入 ChromaDB longMemory
版本历史
| 版本 | 变更 |
|---|---|
| v1 | MySQL 全量加载 + ContextBudget 手动截断 |
| v2 | ConversationSummaryBufferMemory 压缩摘要 |
| v3 | 短期窗口 + ChromaDB 长期记忆(当前) |