LLM 多轮对话上下文管理:Token 控制与记忆机制设计
“为什么聊了几十句之后,AI 就忘了我叫什么名字?”
这是开发 LLM 应用时最常遇到的问题。大语言模型(LLM)由于 Context Window(上下文窗口) 的限制(如 GPT-4 的 128k),无法无限记住所有历史对话。而且,Token 越多,推理越慢,费用越贵。
本文将介绍如何在多轮对话中优雅地管理上下文(Context),让 AI 既“记性好”又“省钱”。
什么是上下文窗口?
你可以把 Context Window 想象成 AI 的“短期记忆”容量。每次你发给 API 的不仅仅是当前这句话,而是必须包含之前的历史记录。
// 每次请求都要带上 history
messages: [
{ role: "system", content: "..." },
{ role: "user", content: "Hi, I'm Bob." },
{ role: "assistant", content: "Hi Bob!" },
{ role: "user", content: "What's my name?" } // 如果没有上面几句,AI 不知道你是谁
]
当对话越来越长,超过 Token 限制时,最早的信息就会被截断丢失。
策略一:滑动窗口(Sliding Window)
这是最简单粗暴的方法:只保留最近的 N 轮对话。
- 原理: 每次请求只带最近的 10 条消息。
- 优点: 实现简单,Token 消耗稳定。
- 缺点: AI 会患上“健忘症”,丢失早期的关键信息(如用户一开始设定的名字或任务目标)。
改进版: 始终保留 System Prompt 和第一轮对话,只滑动中间的部分。
策略二:摘要记忆(Summary Memory)
当对话过长时,让 AI 自己给之前的对话写个“总结”。
- 原理: 每隔 5 轮对话,调用一次 LLM,生成一个 Summary。
- Prompt: "请简要总结上述对话的关键信息,包含用户的偏好和已达成的共识。"
- Context 结构:
- System Prompt
- Summary of past conversation
- Recent M messages (Raw text)
优点: 能保留长期记忆的一般性内容。 缺点: 每隔几轮就要多消耗一次 API 调用生成摘要;细节会丢失。
策略三:向量检索记忆(Vector Store / RAG Memory)
对于超长对话或需要记住特定细节(如两天前提到的书名),向量数据库是最佳方案。
- 原理:
- 将每一轮对话切片,存入向量数据库(如 Pinecone, Milvus)。
- 当用户提问时,先在数据库中搜索与当前问题语义相关的历史记录。
- 将检索到的相关历史片段插入到 Prompt 中。
Prompt 结构:
System: 你是一个助手。 Context (从数据库检索到的相关记忆): 用户提到他喜欢科幻小说。 User: 给我推荐几本书。
优点: 近乎无限的记忆容量,能够召回很久以前的细节。 缺点: 技术复杂度高,需要维护向量库。
Token 计算与成本优化
在发送请求前,务必预估 Token 数量。
- 分词器(Tokenizer): 不要用
string.length估算。使用tiktoken(OpenAI) 等库精确计算。 - 动态压缩: 如果 Token 超出预算,优先压缩哪部分?
- System Prompt (通常不动)
- Tools Definition (不能动)
- History (优先删除中间的,或 Summarize)
LangChain 中的记忆模块
LangChain 提供了开箱即用的 Memory 组件:
ConversationBufferMemory: 全量保存(适合短对话)。ConversationBufferWindowMemory: 滑动窗口。ConversationSummaryMemory: 自动摘要。VectorStoreRetrieverMemory: 向量检索。
最佳实践:混合策略
构建一个健壮的 Chatbot,通常采用混合策略:
- System Prompt: 始终置顶。
- 短期记忆: 保留最近 5-10 轮完整对话(保证即时连贯性)。
- 长期记忆: 对更早的对话进行摘要,或者存入 User Profile(用户画像)数据库。
- 关键信息提取: 另外运行一个后台任务,从对话中提取关键实体(姓名、职业、偏好)存入结构化数据库。
结语
上下文管理是在“智能程度”和“推理成本”之间做平衡的艺术。对于大多数应用,"滑动窗口 + System Prompt 保护" 已经能解决 80% 的问题。


