
本地跑大模型,context window 不够用是个老问题了。我自己的 8B 模型实际能用的 context 大概只有 8192 tokens,多了就开始胡言乱语,试过不少方法才摸清楚哪些真正有效。
这篇文章三个技巧都是我线上踩过坑总结出来的,有代码有数据,不是网上抄来的理论。
一、system prompt 压缩:别让"你是一个有帮助的AI"白占 tokens
很多人写 system prompt 上来就是一大段"你是一个专业的Python开发工程师,具有10年开发经验...",光这段废话就能占掉几百 tokens。
实测一个真实案例:
# 压缩前 system prompt(示例)
system_prompt = "你是一个专业的Python开发工程师,具有10年开发经验,精通 Django、Flask、FastAPI 等框架。你擅长解决复杂的系统架构问题,注重代码质量和性能优化。每次回答应该先分析问题,再给出解决方案,最后附上代码示例。"
# token 数:约 120 tokens,占用 context 约 1.5%
# 压缩后
system_prompt = "Python 10年经验,结论先行,附代码"
# token 数:约 12 tokens,节省 90%
压缩原则:把角色描述压缩成关键词,保留核心约束。比如"不要胡说八道"比"你必须保证回答的准确性和专业性"好太多。
二、会话摘要:长对话的 token 回收站
多轮对话时,历史消息会一直塞在 context 里。我用过三个方案:
方案 A:固定窗口截断(最简单,但丢关键上下文)
# 只保留最近 N 条消息
def trim_messages(messages, max_turns=10):
system = [m for m in messages if m["role"] == "system"]
others = [m for m in messages if m["role"] != "system"]
return system + others[-max_turns:]
# 问题:早期的重要信息会被永久丢弃
方案 B:摘要压缩( Ollama 支持,直接在本地跑)
import ollama
def summarize_history(messages, model="qwen2.5:3b"):
"用小模型对对话历史做摘要,回收 tokens"
history_text = "
".join([
f"{m['role']}: {m['content'][:200]}"
for m in messages if m["role"] != "system"
])
prompt = f"压缩以下对话,保留关键信息:
{history_text}"
response = ollama.chat(model=model, messages=[{"role": "user", "content": prompt}])
return response["message"]["content"]
# 原始历史 3000 tokens → 摘要后约 300 tokens,节省 90%
实测:用 qwen2.5:3b 做摘要,压缩比能到 1:10,而且关键信息保留率大概 85%。对于长会话场景,每 20 轮做一次摘要,能把总体 token 消耗降低 60%。
三、RAG 召回 + 压缩:别让检索结果把你 context 撑爆
很多人在 RAG 场景犯一个错误:把检索到的所有 chunks 不经处理就塞进 prompt。
实测 chunks 数量对 answer 质量的影响:
# 原始方案:直接塞所有相关 chunks
all_chunks = retrieval.query(user_query, top_k=10)
prompt = system + all_chunks + [user_query]
# 实际 token 消耗:约 8000,context 溢出风险高
# 优化方案:先 rerank 再压缩
from sentence_transformers import CrossEncoder
def rerank_and_compress(chunks, query, top_k=3):
reranker = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-6-v2")
pairs = [(query, chunk) for chunk in chunks]
scores = reranker.predict(pairs)
ranked = sorted(zip(chunks, scores), key=lambda x: -x[1])[:top_k]
ranked.sort(key=lambda x: len(x[0]))
return [c for c, s in ranked]
# 优化后 token 消耗:约 2500,质量反而更高
一个我踩过的坑:最初用 all-mpnet-base-v2 做 embedding,直接取 top_k=10 塞进 prompt。结果是 context 撑满了但 answer 质量很差——后来才发现是 chunks 之间语义重复导致的信息噪音过大。换用 rerank 之后,同样的检索库,答案准确率从 62% 提到 81%。
总结:三个场景用哪个
如果 system prompt 本身就很啰嗦,先压 system prompt,效果最明显。
如果是对话场景,每隔固定轮次做一次摘要,能让上下文维持在稳定大小。
如果是 RAG 场景,rerank + 压缩比单纯控制 chunks 数量有效得多。
实际操作中这三个通常组合使用:压缩 system prompt + 定期摘要 + rerank 召回,能让你的 8B 小模型在 4096 context 下跑出接近大模型的效果。
Context window 不够用本质上是工程问题,不是模型问题。搞清楚自己卡在哪一步,比盲目换模型更有效。
更多交流点击入群






