如何在有限 Token 下保留关键信息?
Token 是所有大模型应用的硬通货,所谓“记忆管理”,本质上就是在有限 Token 里做信息蒸馏。关键不是存得多,而是让存下来的每一个 token 都在“刀刃上”。
🎯 核心逻辑:把对话当成“被遗忘权”的资源¶

我把它拆成三个连续动作:定义关键 → 榨取信息密度 → 动态调度。
1️⃣ 先定义“什么是关键”——价值滤波器¶
不是每句话都值得留。我会把对话信息分成三类:
def info_value(message: str) -> float:
"""给消息打分,决定是否进入记忆流程"""
if any(kw in message for kw in ["我叫", "电话是", "地址在"]):
return 1.0
if any(kw in message for kw in ["偏好", "以后", "不喜欢", "记住"]):
return 0.9
if len(message) < 5:
return 0.0
return 0.2 # 普通陈述
2️⃣ 压缩信息密度——摘要与结构化抽取¶
当对话长度逼近 Token 预算时,不要粗暴截断,而是把旧的对话“炼”成浓缩摘要。
我习惯用一个小型、快速的模型(或同一个大模型但低温度)把过去 N 轮“榨”成三个字段:
def compress_messages(messages: list[dict], llm) -> dict:
prompt = f"""
请从以下对话中提取:
1. 关键事实(用户姓名、偏好、已确认的决定)
2. 当前任务进度
3. 最重要的用户情绪或偏好变化
对话:
{format_messages(messages)}
输出JSON。
"""
return json.loads(llm.invoke(prompt))
这样 2000 Token 的旧对话,可能就被压成一段 200 Token 的摘要,信息密度提升 10 倍。
3️⃣ 动态调度:Token 预算的“零和博弈”¶
窗口是一个动态平衡的容器,我维护三个逻辑分区:
关键实现:
import tiktoken
class TokenBudget:
def __init__(self, max_tokens=4000):
self.max_tokens = max_tokens
self.enc = tiktoken.get_encoding("cl100k_base")
self.system_msg = ""
self.messages = [] # 原始最近消息
self.archive = "" # 压缩后的历史摘要
def count(self):
return (len(self.enc.encode(self.system_msg)) +
sum(len(self.enc.encode(m["content"])) for m in self.messages) +
len(self.enc.encode(self.archive)))
def add_message(self, msg):
self.messages.append(msg)
self._rebalance()
def _rebalance(self):
"""超过预算时,把最旧的消息合并进 archive,并压缩 archive"""
while self.count() > self.max_tokens and len(self.messages) > 2:
# 取出最旧的一条,丢给压缩机
oldest = self.messages.pop(1) # 保留系统消息和最新用户
self.archive = self._merge_into_archive(oldest["content"], self.archive)
# 如果 archive 本身太大,对它再做一次摘要压缩
if len(self.enc.encode(self.archive)) > 500:
self.archive = self._summarize(self.archive)
def _merge_into_archive(self, text, current_archive):
# 实际场景会用 LLM 做增量合并
return current_archive + "\n" + text
def _summarize(self, text):
# 调用 LLM 将 text 压缩到 300 tokens 以内
return f"[摘要] {text[:300]}..." # 示意
这样,窗口始终控制在预算内,且 archive 里保留了整段对话的“知识结晶”。
4️⃣ 完整示例:一个可运行的记忆管理器¶
import tiktoken
from collections import deque
class CondensedMemory:
def __init__(self, llm, max_tokens=4000):
self.llm = llm
self.max_tokens = max_tokens
self.enc = tiktoken.get_encoding("cl100k_base")
self.system = ""
self.recent = deque() # 保留最近的原始消息
self.summary = "" # 压缩积累的摘要
def add(self, role, content):
if self._should_discard(content):
return
self.recent.append({"role": role, "content": content})
self._maybe_compress()
def get_context(self):
"""返回拼接好的上下文"""
parts = [self.system]
if self.summary:
parts.append(f"[历史摘要] {self.summary}")
parts.extend([f"{m['role']}: {m['content']}" for m in self.recent])
return "\n".join(parts)
def _should_discard(self, content):
return content.strip() in ["好的", "收到", "ok"] or len(content) < 4
def _maybe_compress(self):
current_tokens = len(self.enc.encode(self.get_context()))
if current_tokens <= self.max_tokens * 0.8:
return
# 将 recent 的前半部分压缩进 summary
split_point = len(self.recent) // 2
old_messages = [self.recent.popleft() for _ in range(split_point)]
text_block = "\n".join([f"{m['role']}: {m['content']}" for m in old_messages])
# 调用 LLM 做摘要(替换为真实调用)
new_summary = self.llm.compress(text_block)
if self.summary:
self.summary = self.llm.merge_summaries(self.summary, new_summary)
else:
self.summary = new_summary
🧠 一个值得反思的点¶
我们都认为“保留关键信息”是为了更准确,但实验发现:适当丢弃低权重的上下文噪音,反而能提升复杂推理的正确率。因为模型在某些任务上会被无关细节带偏。所以,“有限 Token”不是缺陷,而是一个隐式正则化器。面试时抛出这一点,往往能把讨论拉到更高维度。