Summarization / Compression
摘要压缩对话太长就压缩成摘要,腾出上下文空间。
详解
摘要压缩是一种上下文管理策略:当对话历史积累到接近上下文窗口上限时,用 LLM 把早期的若干轮对话「压缩」成一段简短摘要,用摘要替换原始消息列表,从而释放 token 空间。与滑动窗口(直接丢弃旧消息)相比,摘要压缩保留了关键信息——不是硬截断,而是「精华留下,细节丢掉」。典型做法是设置一个触发阈值(如当前 token 数超过上下文窗口的 70%),一旦触发就把最早的若干条消息发给 LLM,要求生成一段摘要,然后用一条系统消息「之前的对话摘要:…」代替那些旧消息,把最近几轮完整保留。代价是多一次 LLM 调用(额外的延迟和费用),但带来更好的上下文连贯性。生产系统里常见的混合策略是:最近 N 轮保留原文,更早的历史压缩为摘要,两者拼在一起送入模型。
一个类比
想象你在做会议记录:会议进行到第三小时,白板快写满了,你不是直接擦掉最开始的内容,而是先把前两小时的要点浓缩成几行「前情提要」写在最顶部,再擦掉原始的密密麻麻的记录,继续往下写新内容。这样既腾出了地方,又没丢掉关键决定。
举个例子
import anthropic
client = anthropic.Anthropic()
MAX_TURNS = 6 # 超过 6 轮(12 条消息)时触发压缩
summary = "" # 当前积累的摘要(全局状态)
history = [] # 仅保留最近几轮的完整消息
def split_at_user_boundary(msgs: list, keep_turns: int = 2):
"""从末尾找到第 keep_turns 个 user 消息,在那里分割"""
user_count = 0
split_idx = 0
for i in range(len(msgs) - 1, -1, -1):
if msgs[i]["role"] == "user":
user_count += 1
if user_count >= keep_turns:
split_idx = i
break
return msgs[:split_idx], msgs[split_idx:] # recent 保证从 user 开始
def compress_if_needed():
global summary, history
if len(history) <= MAX_TURNS * 2:
return
old_msgs, history = split_at_user_boundary(history, keep_turns=2)
# 调用 LLM 把早期消息压缩成摘要
to_compress_text = "\n".join(f"{m['role']}: {m['content']}" for m in old_msgs)
prompt = f"已有摘要:{summary}\n\n新增对话:\n{to_compress_text}\n\n请更新并输出新摘要(3-5 句)。"
resp = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=256,
messages=[{"role": "user", "content": prompt}],
)
summary = resp.content[0].text # 滚动更新摘要
def chat(user_input: str) -> str:
history.append({"role": "user", "content": user_input})
compress_if_needed() # 超限时先压缩
system_ctx = f"早期对话摘要:{summary}" if summary else ""
resp = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=256,
system=system_ctx, # 摘要放进 system,不污染 messages 结构
messages=history,
)
reply = resp.content[0].text
history.append({"role": "assistant", "content": reply})
return replyPYTHON 示例
相关概念
- → Context Window· 上下文窗口
- → Sliding Window· 滑动窗口