长对话为什么不爆上下文:Proxima上下文压缩管道Pruner与Condenser拆解

原创2026-10-14 05:01:171,680 阅读

长对话为什么不爆上下文:Proxima上下文压缩管道Pruner与Condenser拆解

如果你经常用 AI 编程助手,一定遇到过这种情况:对话进行到几十轮之后,模型突然"失忆",或者直接报出上下文窗口溢出的错误。Proxima 的解决方案是一套本地上下文压缩管道(Context Pipeline),由 Pruner 修剪器和 Condenser 压缩器两级协作,在对话 token 超预算时自动把历史记录"瘦身",让长对话稳定运行而不爆上下文。

Proxima Agent 长对话界面:左侧是对话历史,主区域展示多轮对话结果

为什么长对话容易"爆"上下文窗口

AI 模型的上下文窗口是有上限的(比如 32K token 左右)。每轮对话里,除了你和模型的文本,还会塞进大量工具调用记录:读文件的内容、执行命令的输出、搜索结果……这些东西往往一条就有几 KB。

几个轮次下来,历史消息的 token 总量就会悄悄逼近窗口上限,出现两种典型症状:

  • 模型开始"忘记"前面的约定——重要指令被挤出有效窗口;
  • 直接报错——超出窗口限制,请求被拒绝。

暴力解法是"截断历史",但那样会丢掉任务上下文。Proxima 的做法是分级压缩:先做零成本的机械修剪,不够再让 LLM 做摘要,最后才考虑硬截断。

Proxima 支持接入 Perplexity、ChatGPT、Claude、Gemini 四大 AI 提供商,无需 API Key

整条管道位于 electron/api/byok/context/ 目录,官方架构文档中也专门有一节介绍它:docs/architecture.md。

上下文压缩管道的三级工作流

编排器 index.cjs 是整条管道的总开关,默认参数如下:

参数 默认值 含义
threshold 32000 触发压缩的 token 阈值
tailBudget 8000 保护"近期对话"的 token 预算
headCount 2 头部保留条数(任务开头)

工作流程可以概括为四步:

  1. 估算:用 tokens.cjs 的字符数 ÷ 4 快速估算全部消息的 token 量(图片按固定 1600 token 计),不超阈值直接原样返回;
  2. Pruner 修剪:机械截断历史中的大块工具输出与参数,不花一分钱 LLM 费用;
  3. Condenser 压缩:修剪后仍超预算时,把中间轮次交给 LLM 总结成一条摘要消息;
  4. 硬截断兜底:如果连 LLM 摘要都不可用,就保留头部 + 尾部,丢弃中间消息并插入一条说明。

Proxima 设置页展示本地绑定 127.0.0.1 的架构总览,凭据与上下文 100% 留在本地

因为整条管道运行在 Proxima 的本地 MCP 服务进程中,压缩过程不依赖任何外部 API,对话数据也不会离开你的机器。

Pruner:零成本的"修剪"逻辑

pruner.cjs 的核心思想是:旧消息里的"大块头"没有保留价值,但"骨架"要留下。它有三条规则:

  • 保护尾部:先按 tailBudget(默认 8000 token)从最新消息往前圈定保护区,保护区内的消息一律不动——因为最近的对话恰恰是模型最需要完整信息的部分;
  • 压缩旧工具输出:保护区内 tool 角色消息只要超过 500 字符,就替换成一行"简报",例如 [read_file] src/app.py (45,210 chars)、[execute] "npm test" → 32 lines output,保留"做了什么、结果多大",扔掉具体正文;
  • 瘦身大块参数与长文本:工具调用参数超过 200 字符会被截断(JSON 值内部逐层裁剪),助手超过 2000 字符的长回复则只保留开头 800 字符 + 结尾 400 字符。

这套规则非常"懂行":对模型来说,<a href="https://link.gitcode.com/i/892bbfda4ce474500a16f2a629c4e98d" target="_blank">search] "token limit" (12,400 chars result) 这样的摘要足以支撑后续推理,而完整的 12K 字符搜索命中医是纯粹的 token 浪费。相关行为可以在测试文件 [tests/byok/context.test.js 中看到完整验证。

Condenser:让 LLM 把中间轮次"读薄"

修剪后仍超预算时,接力棒交给 condenser.cjs。它的分工非常明确:

  • 头部(默认前 2 条,通常是任务定义)原样保留;
  • 尾部(tailBudget 内的近期对话)原样保留;
  • 中间轮次整体替换成一条 user 消息,内容是 LLM 生成的摘要。

摘要质量由三个关键设计保证:

  1. 预算比例:摘要目标 token 量 = 中间轮次总 token × 20%,并限制在 500~4000 token 之间,防止摘要本身又变成"大块头";
  2. 结构化提示词:提示词明确要求摘要覆盖"执行过哪些操作、关键结果、重要决策、当前任务状态与剩余工作"四个维度,而不是泛泛的"用户问了 X";
  3. 可识别标记:摘要开头带 [CONTEXT CONDENSED — REFERENCE ONLY] 标签,下一轮再压缩时管道能认出"这已经是一条旧摘要",把其中信息并入新摘要,避免"摘要的摘要"层层衰减。

万一 LLM 摘要调用失败(比如提供商限流),管道会插入一条确定性的降级文案("N 条早期轮次已移除以节省上下文"),对话依然可以继续,不会中断。

最后一道防线:硬截断

当既没有可用的 LLM 调用、或摘要也没能压下来时,编排器会执行 _hardTruncate:保留前 2 条消息 + 尽量多的尾部消息,把中间腾不下的消息直接丢弃,并插入一条 [N earlier messages were removed to fit context window…] 的说明。这保证了无论发生什么,发给模型的请求永远不会超过窗口限制——对话宁缺毋滥,但不会崩。

关键数字速览

组件 关键参数 说明
触发阈值 threshold 32000 token 超过才启动压缩,短对话零开销
尾部保护 tailBudget 8000 token 近期对话完整保留
工具输出修剪线 500 字符 超过即替换为一行简报
工具参数修剪线 200 字符 超长 JSON 参数逐层截断
助手长文本修剪线 2000 字符 保留首 800 + 尾 400
摘要预算比例 20%(500~4000) 中间轮次压缩后的目标体积

延伸阅读

对新手来说,这套管道最值得借鉴的取舍是:能不用 LLM 就不调 LLM。机械修剪覆盖了 80% 的"水分",摘要只在必要时登场,硬截断只作为最后兜底——成本低、延迟小,还永远保证对话不中断。这正是 Proxima 长对话稳定不爆上下文的秘密。

登录后查看全文
Proxima