第一版方案几乎都是滑动窗口:只保留最近 N 条消息,更老的直接丢掉。它在聊天场景里勉强可用,在 agent 场景里会立刻出事。因为 agent 的历史不是等价的——最开头那条”帮我把登录模块重构成 XX 结构,注意不要改动数据库”往往是整个任务里信息密度最高的一句,而它恰好是最先被滑出窗口的。丢掉它之后模型会开始偏离目标、重复已经否决过的方案、违反早就说过的约束。更糟的是,如果窗口边界正好落在一次工具调用和它的结果之间,你会发给模型一段结构上非法的历史。
flowchart TD A["请求发出前\n(主动预判)"] --> M{"估算历史用量\n≥ 触发阈值?"} B["模型返回报错\n(被动兜底)"] --> C{"是上下文超长\n这一类错误?"} C -->|否| Z1["不介入\n交给其他错误处理"] C -->|是| D{"本轮兜底次数\n未超上限?"} D -->|否| Z1 M -->|否| Z2["无需压缩\n放行本次请求"] M -->|是| P D -->|是| P["先做零成本截断\n(可选,不调模型)"] P --> R["选择可压缩范围\n保留近期 + 不切开配对"] R -->|无安全范围| Z2 R --> T["进入压缩事务"] subgraph TX ["压缩事务(开始标记先写、结束标记最后写)"] T --> S1["写入开始标记\n取得压缩锁"] S1 --> S2["重放原对话 + 摘要指令\n一次模型调用"] S2 --> S3["写入摘要记录\n含被遮蔽的范围"] S3 --> S4["写入替换操作\n用摘要遮蔽旧段"] S4 --> S5["写入结束标记\n释放压缩锁"] end S5 --> G{"兜底路径:\n持久进展计数器\n推进了?"} G -->|是| RETRY["允许重试\n用新历史再发一次"] G -->|否| Z3["保留原始报错\n禁止重试"]