🧠 图解记忆:近保真、远保要、超远按需,方案取决于任务;点击图片可查看原图。
💡 答案要点
问题背景:
用户上传100页PDF(约50K tokens)
GPT-4 Context Window: 8K tokens
怎么处理? ❌ 无法直接输入解决方案对比
| 方案 | 原理 | 优缺点 | 适用场景 |
|---|---|---|---|
| 截断 | 丢弃超出部分 | ❌丢失信息 | 不推荐 |
| 滑动窗口 | 保留最近N个token | ❌丢失早期信息 | 实时对话 |
| 摘要压缩 | LLM递归摘要 | ⚠️可能失真 | 文档概览 |
| RAG分块 | 切分+向量检索 | ✅最优⭐ | 问答/检索 |
| 长上下文模型 | 用128K窗口模型 | 💰成本高 | 必要时 |
方案1: 滑动窗口(Sliding Window)
原理:
python
# 维护一个固定大小的窗口
max_tokens = 4000
history = []
while True:
user_input = get_user_input()
history.append(user_input)
# 计算总token数
total_tokens = count_tokens(history)
# 超出窗口,删除最早的消息
while total_tokens > max_tokens:
history.pop(0) # 删除第一条
total_tokens = count_tokens(history)
# 用窗口内容生成回复
response = llm.generate(history)
history.append(response)优化: Token 预算分配组合方案(2026 生产实践):
核心原则:让模型"忘掉细节,记住要点"——不是单一方案,是组合拳:
1. 实时计算 Token 数:每次发送前计算消息列表总 Token(所有策略的基础)
2. 分层保留优先级:System Prompt 必须保留 → 当前问题必须保留 → 历史按优先级裁剪
3. 摘要触发条件:历史 Token 超预算 60% 时触发——最早 N 轮调 LLM 压成摘要替换原文
4. 兜底:摘要后仍超限 → 摘要存向量库,按当前问题检索最相关片段
分层保真原则(关键):
近期对话 → 原文保留(保真)
远期对话 → 摘要压缩(保要)
超远期 → 向量检索(按需)
⚠️ 误区:不要把所有历史都做摘要——摘要是有损压缩,每压一次丢一些细节。
分层管理:近保真、远保要、超远按需。优化: 分层管理
展开 Python 代码示例(37 行)
python
class ConversationManager:
def __init__(self, max_recent=3, summary_every=10):
self.recent_messages = [] # 最近3轮完整保留
self.summary = "" # 历史摘要
self.message_count = 0
def add_message(self, role, content):
self.recent_messages.append({"role": role, "content": content})
self.message_count += 1
# 保留最近3轮
if len(self.recent_messages) > 6: # 3轮=6条消息
# 把旧消息做摘要
old = self.recent_messages.pop(0)
old_pair = self.recent_messages.pop(0)
self.summary += llm.summarize([old, old_pair])
return self.get_context()
def get_context(self):
# 上下文 = 历史摘要 + 最近3轮原文
context = []
if self.summary:
context.append({
"role": "system",
"content": f"历史对话摘要: {self.summary}"
})
context.extend(self.recent_messages)
return context
# 使用
manager = ConversationManager()
manager.add_message("user", "你好")
manager.add_message("assistant", "你好,有什么可以帮你?")
...
context = manager.get_context() # 自动管理上下文效果:
- Token消耗: 稳定在4K以内
- 信息保留: 最近3轮完整+历史摘要
- 成本: 每10轮多1次摘要API调用
方案2: RAG分块检索(推荐⭐)
完整流程:
展开 Python 代码示例(49 行)
python
# 步骤1: 文档预处理
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, # 每块1000字符
chunk_overlap=200, # 200字符重叠
separators=["\n\n", "\n", "。", ". "]
)
chunks = splitter.split_text(long_document)
# 100页PDF → 约150个chunks
# 步骤2: 向量化存储
from langchain.vectorstores import Qdrant
from langchain.embeddings import OpenAIEmbeddings
embeddings = OpenAIEmbeddings()
vector_store = Qdrant.from_texts(
texts=chunks,
embedding=embeddings,
collection_name="my_documents"
)
# 步骤3: 用户提问时检索
def answer_question(question):
# 检索top-5相关chunks
relevant_chunks = vector_store.similarity_search(
question, k=5
)
# 拼接成上下文
context = "\n\n".join([chunk.page_content for chunk in relevant_chunks])
# LLM生成答案
prompt = f"""
基于以下内容回答问题:
{context}
问题: {question}
答案:
"""
answer = llm.generate(prompt)
return answer
# 使用
question = "文档中提到的主要结论是什么?"
answer = answer_question(question)优势:
- ✅ 支持无限长文档
- ✅ 只检索相关部分,节省token
- ✅ 可追溯来源(返回chunk ID)
方案3: 递归摘要
Map-Reduce摘要:
python
def recursive_summarize(long_text, max_chunk_size=4000):
# 如果短于限制,直接摘要
if len(long_text) < max_chunk_size:
return llm.summarize(long_text)
# 切分
chunks = split_text(long_text, max_chunk_size)
# Map: 每个chunk单独摘要
summaries = [llm.summarize(chunk) for chunk in chunks]
# Reduce: 合并摘要
combined = "\n\n".join(summaries)
# 递归(如果合并后还是太长)
return recursive_summarize(combined, max_chunk_size)
# 示例
long_doc = load_100_page_pdf()
summary = recursive_summarize(long_doc)
print(summary) # 最终浓缩版适用场景:
- 只需要概览,不需要细节
- 没有特定问题,想了解大意
方案4: 长上下文模型
模型对比:
| 模型 | Context Window | 价格(1M tokens) | 适用 |
|---|---|---|---|
| DeepSeek V4-Flash | 16K | $1.5 | 短对话 |
| GPT-4 | 8K | $30 | 通用 |
| GPT-4-32K | 32K | $60 | 长文档 |
| GPT-4-128K | 128K | $120 | 超长 |
| Claude-2 | 100K | $8 | 性价比⭐ |
| Claude-3 | 200K | $15 | 极长文档 |
选择策略:
python
def choose_model(text_length):
tokens = count_tokens(text)
if tokens < 4000:
return "deepseek-v4-flash" # 便宜
elif tokens < 30000:
return "claude-2" # 性价比高
elif tokens < 120000:
return "gpt-4-128k" # 贵但强
else:
return "RAG" # 超长必须分块Lost in the Middle问题
现象:
给LLM一个20K的文档,关键信息在中间
→ LLM往往只关注开头和结尾
→ "大海捞针"失败解决:
python
# 方法1: 把关键内容放开头/结尾
prompt = f"""
关键信息: {key_info}
背景资料:
{long_context}
问题: {question}
"""
# 方法2: 分段+多次查询
def multi_pass_qa(long_text, question):
# Pass 1: 快速扫描找相关段落
relevant_sections = quick_scan(long_text, question)
# Pass 2: 深入分析相关段落
answers = []
for section in relevant_sections:
answer = llm.generate(f"{section}\n\n{question}")
answers.append(answer)
# Pass 3: 综合答案
final = llm.generate(f"综合以下答案: {answers}")
return final面试话术:
示例表达(仅在能用本人经历或可复现实验佐证时使用): "长文本处理首选RAG:切分+检索+生成。滑动窗口适合对话,但会丢失早期信息,我们用分层管理解决——最近3轮保留原文,历史做摘要。长上下文模型虽强但贵,128K窗口成本是8K的15倍,只在必要时用。"
