History Truncation

History Truncation 是在对话历史超过上下文窗口限制时,截断或移除旧消息的策略。它在保留关键上下文和控制 token 消耗之间找到平衡。

#type / concept #status / evergreen #tech / ai

[!info] related notes

History Truncation

一句话定义

History Truncation 是在对话历史超过上下文窗口限制时,截断或移除旧消息的策略。不是简单地”删掉最早的”,而是要保留关键信息、生成摘要、维护上下文连贯性。

核心原理

截断策略

策略原理优缺点
时间截断删掉最早的 N 条简单但可能丢关键信息
Token 截断从最新消息开始,累计 token 直到超限精确控制 token
重要性截断保留重要消息,删掉不重要的复杂但效果好
摘要截断旧消息生成摘要替代保留信息但增加一次 LLM 调用

Token 截断实现

def truncate_by_token(messages: list, max_tokens: int, tokenizer) -> list:
    """从最新消息开始,累计 token 直到超限"""
    # 始终保留 system message
    system_msgs = [m for m in messages if m.role == "system"]
    other_msgs = [m for m in messages if m.role != "system"]

    system_tokens = sum(tokenizer.count(m.content) for m in system_msgs)
    remaining_tokens = max_tokens - system_tokens

    # 从最新消息开始保留
    kept = []
    for msg in reversed(other_msgs):
        msg_tokens = tokenizer.count(msg.content)
        if remaining_tokens - msg_tokens < 0:
            break
        kept.insert(0, msg)
        remaining_tokens -= msg_tokens

    return system_msgs + kept

摘要截断实现

async def truncate_with_summary(messages: list, max_tokens: int, llm) -> list:
    """旧消息生成摘要,新消息保留原文"""
    system_msgs = [m for m in messages if m.role == "system"]
    other_msgs = [m for m in messages if m.role != "system"]

    # 分离旧消息和新消息
    split_point = len(other_msgs) - 10  # 保留最近 10 条
    old_msgs = other_msgs[:split_point]
    new_msgs = other_msgs[split_point:]

    # 旧消息生成摘要
    if old_msgs:
        summary = await llm.chat(f"请用 100 字总结这段对话的要点:\n{format_messages(old_msgs)}")
        summary_msg = {"role": "system", "content": f"之前的对话摘要: {summary}"}
        return system_msgs + [summary_msg] + new_msgs

    return system_msgs + new_msgs

常见坑

  1. 只删最早的: 可能删掉了关键的用户偏好信息
  2. 不保留 system message: 截断时把 system prompt 也删了
  3. 不做摘要: 直接丢弃旧消息,信息丢失
  4. 截断太频繁: 每轮都截断,增加延迟

参考资料

创建于 2026/6/30 更新于 2026/7/15