超过 20 轮对话后无限制追加消息会导致令牌浪费、延迟上升、模型遗忘早期约束;应将短暂对话与持久状态分离,用滑动窗口裁剪历史。
部署 AI Agent 时最常见的错误,就是把聊天历史当作只追加的日志。在早期原型阶段,把每次用户输入、工具响应和原始 JSON 数据块都直接追加到 messages 数组里,确实能正常工作。
到了生产环境,这种模式在二十轮之后就崩溃了。Token 用量随对话深度线性增长,导致 API 延迟和推理成本飙升。更糟糕的是,模型会出现"中间迷失"退化——忘记早期的约束条件,甚至因 Token 限制错误直接崩溃。
# 这种 naive 反模式:列表无限增长
messages.append({"role": "user", "content": user_input})
messages.append({"role": "assistant", "content": llm_response})
# 30 轮之后:15000 个 token 浪费在过时的工具载荷上
response = client.chat.completions.create(model="gpt-4o", messages=messages)
把未经修剪的历史记录直接丢给 LLM,会让你的数据库变成昂贵的延迟陷阱。
解决方案是将临时对话与持久化对话状态解耦。
我们不再强迫 LLM 在每轮对话中都重新解析整个对话历史来理解"十分钟前发生了什么",而是将上下文拆分为两个不同的层次:
滑动窗口缓冲区:只保留最近 $N$ 轮对话,维持即时的对话节奏和流畅性。
结构化状态 JSON:维护一个紧凑的、确定性的键值存储,存放关键事实(例如:当前用户目标、已收集的表单值、已解决的错误)。
用户输入
│
▼
┌─────────────────────────────────────────┐
│ Context Assembler │
│ ─────────────────────────────────────── │
│ 1. 静态 System Prompt(身份设定) │
│ 2. 当前 State JSON(事实与目标) │
│ 3. 滑动窗口缓冲区(最近 N 轮) │
└─────────────────────────────────────────┘
│
▼
LLM 推理(有限且可预测)
这样无论对话持续 3 轮还是 300 轮,你的 Token 载荷都保持平坦。
下面是一个轻量级的上下文管理器,可以直接插入到你的后端服务管道中:
from typing import Any, Dict, List
def build_bounded_context(
system_prompt: str,
raw_history: List[Dict[str, str]],
state_payload: Dict[str, Any],
max_turns: int = 6
) -> List[Dict[str, str]]:
"""Assemble a token-bounded context payload with structured state."""
# Enforce strict sliding window on ephemeral chat history
trimmed_history = raw_history[-max_turns:] if len(raw_history) > max_turns else raw_history
# Inject current state directly as a system-level context injection
state_injection = {
"role": "system",
"content": f"CURRENT_SESSION_STATE: {state_payload}"
}
return [{"role": "system", "content": system_prompt}, state_injection] + trimmed_history
这种模式提供了确定性的上下文边界。你的后端保证传递给 provider 的上下文大小永远不会超过你计算好的预算:
如果 Agent 需要更新持久化状态(比如收货地址或用户意图),通过工具调用异步提取该状态,存入你的数据库,然后在下次调用时注入干净的 JSON 字典。
状态应该存在你的数据库里,而不是上下文窗口中:把 LLM 上下文当作 RAM,把你的数据库当作硬盘。将事实提取到结构化字段中,而不是让模型重新读取 40 轮历史记录。
N 保持在 4 到 8 轮之间:实践中,即时对话上下文很少需要超过最近 3-4 轮往返。更早的内容通常是噪声。
让状态更新具备幂等性:使用显式的工具/函数调用来更新结构化状态模式,而不是用自由形式的 LLM 摘要,以避免幻觉导致的状态漂移。