Skip to main content

AI记忆策略

你在设计聊天机器人时,希望本轮对话(当前会话)能让模型记住前面几轮的上下文,但又不想每次都把全部历史消息原封不动地塞进 prompt(会导致上下文长度爆炸、成本飙升、噪声干扰模型判断力下降)。

以下是目前工业界和开源社区中使用最广泛、性价比相对较高的几种本轮对话记忆方案,按推荐优先级排序(2025-2026主流做法):

排名方案名称上下文长度控制信息保留质量实现难度Token 节省推荐场景代表库/框架
1滚动窗口 + 自动摘要(最推荐)优秀良好~优秀极高绝大部分中长对话产品LangChain, LlamaIndex, Mem0
2固定窗口 + 最近N轮很好中等客服、简单问答、短期任务几乎所有框架
3实体/关键事实抽取 + 内存KV优秀很好~优秀极高需要跨轮记住人名、数字、偏好、待办Mem0, LangGraph
4向量检索式记忆(RAG式)极好很好(需调优)极高非常长对话、跨会话记忆Pinecone + LangChain
5全历史(baseline)最好(短期)最低上下文窗口极大或对话极短时

目前工业界最主流的实用组合方案(2025-2026)

“滚动窗口 + 动态摘要 + 少量关键事实保护”(性价比最高)

核心思想:

  1. 永远只给模型看最近的 K轮完整对话(比如 6–12 轮,约占窗口的 40–60%)
  2. 更早的对话被压缩成 1–3段摘要(放在最前面)
  3. 可选:把最重要的几条事实/实体/状态以结构化方式强制放在 system prompt 或开头(类似“用户档案”)

实现伪代码(最常见写法):

# 假设你使用类似 LangChain / LlamaIndex / 纯 openai api 的结构

class SmartRollingMemory:

def __init__(self, max_recent_turns=8, max_summary_tokens=400):
self.history = [] # 完整 [(role, content), ...]
self.summary = "" # 压缩后的早期对话摘要
self.key_facts = [] # 可选:抽取的关键事实列表
self.max_recent = max_recent_turns
self.max_summary_tok = max_summary_tokens

def add_message(self, role, content):
self.history.append((role, content))

def get_prompt_messages(self, system_prompt):
# 超过一定轮次才触发压缩
if len(self.history) // 2 > self.max_recent + 4:
# 找出要压缩的部分
to_summarize = self.history[:-self.max_recent * 2] # 最早的对话
new_summary = self._call_llm_to_summarize(to_summarize)

# 更新摘要(可以累加式,也可以覆盖式)
self.summary = self._merge_summaries(self.summary, new_summary)

# 可选:从要压缩的部分再抽取关键事实
self.key_facts.extend(self._extract_key_facts(to_summarize))
self.key_facts = self.key_facts[-30:] # 限制数量

# 裁剪历史,只保留最近的
self.history = self.history[-self.max_recent * 2:]

# 最终要发给模型的内容
messages = [{"role": "system", "content": system_prompt}]

# 插入用户档案 / 关键事实(非常有效)
if self.key_facts:
messages.append({"role": "system", "content": "重要记忆(不要遗忘):\n" + "\n".join(self.key_facts)})

# 插入早期对话摘要
if self.summary:
messages.append({"role": "system", "content": f"之前对话摘要:{self.summary}"})

# 插入最近的完整对话
for role, content in self.history:
messages.append({"role": role, "content": content})

return messages

def _call_llm_to_summarize(self, old_msgs) -> str:
# 用一个小模型或同一个模型做摘要,提示词很重要
prompt = f"""请用简洁的第三人称总结下面对话的主要内容、关键决定、待办事项、用户偏好等,不要遗漏重要事实,控制在300字以内:\n{msgs_to_text(old_msgs)}"""
return call_llm(prompt, max_tokens=450)

def _extract_key_facts(self, msgs):
# 可以用更强的结构化提取提示,或者用 function calling / regex
return ["用户最喜欢的颜色是深空灰", "用户有三个小孩", "项目截止日期是2026-03-15"]

更激进但效果很好的几种变体(按需选择)

  1. 双摘要:一个长期摘要(整段对话),一个中期摘要(最近30轮左右)
  2. 重要性打分:每轮结束后给消息打重要性分,低于阈值的更容易被替换/删除
  3. 状态快照:像游戏存档一样,每10轮强制让模型输出一次「当前已知用户画像 + 任务状态」,存下来反复使用
  4. Mem0 / Letta 风格:彻底把记忆变成 key-value 事实存储 + 向量召回,需要长期记忆时最强,但开发成本高

快速建议(不同场景选哪个)

场景推荐方案保留轮数建议是否抽关键事实
普通闲聊、客服固定窗口(最近8–15轮)8–15
连续多天的写代码/写方案滚动窗口 + 自动摘要6–10是(强烈推荐)
需要记住用户各种偏好滚动窗口 + 关键事实保护6–12
超长任务(几十上百轮)向量RAG记忆 + 摘要4–8是 + 向量召回