状态:V16 已落地。2026-07-23 定稿。
1. 起源 — 我们遇到了什么问题
你跟 AI 结对编程 50 层楼之后,每一层楼的完整对话(你的问题 + AI 的思考 + 工具调用 + 工具输出)都还躺在上下文窗口里。50 层楼 ≈ 250K tokens 起步。再往后,要么倾家荡产付 token 费,要么 AI 开始"遗忘"最早讨论过的架构决策。
更致命的是 缓存失效:大多数 LLM 服务商按 prompt tokens 计费,但对不变的前缀提供缓存折扣(prefix caching)。如果你每次发送新消息时都重建了整个上下文数组——即使 99% 的内容和上一轮一模一样——缓存全部失效,每一轮都按全新 prompt 计费。
三个问题纠缠在一起:
| 问题 | 表现 | 根因 |
|---|---|---|
| 上下文膨胀 | 每层楼 ~5K tokens,50 层 = 250K | 工具输出(尤其是终端命令结果)原样保留 |
| 缓存失效 | 实际计费 = 全量 tokens,非增量 | 消息对象被替换或 splice → API 视为新内容 |
| 质量衰减 | 旧上下文被截断 → AI 丢失早期决策 | 被迫在 token 预算和上下文完整性之间做取舍 |
2026 年 6 月,我们开始系统性地解决这个问题。以下是完整的推演历程。
2. 架构推演 — V10 → V11 → V12 → V13 → V14 → V15 → V16
V10 · AI 语义压缩(已废弃)
思路:让 AI 自己压缩自己的历史——三专家模型并行,各自产出摘要,投票选最优。
失败原因:
- 质量不稳定:同一段对话,三次压缩结果不同。AI 有时会遗漏关键决策。
- 缓存全断:压缩后的文本和原始对话完全不同 → 前缀缓存 0% 命中。
- 费用悖论:压缩本身消耗 ~5K tokens(三专家 × 并行),加上缓存失效,反而不如不压缩。
- 回滚漏洞:压缩失败时无法精确恢复到压缩前状态。
结论:AI 不适合做确定性压缩。压缩必须是纯本地、纯确定性的机械过程。
V11 · 机械筛 + 追加式饼干(已废弃)
思路:不再让 AI 压缩。改为本地机械筛——逐条消息按固定规则转为 Q/A/工具摘要行。压缩结果(饼干)追加到 conversation 数组。
格式:
[0] Z (规则/铁律)
[1] biscuitPrefix (旧楼层压缩)
[2] DE (不可恢复工具输出)
[3] biscuitLatest (最新楼层压缩)
[4] 当前楼层原始消息
失败原因 — splice 漏洞: 每次楼层完结时,旧的 biscuitLatest 被 splice 移除,新压缩内容作为新的消息对象注入。LLM API 看到消息对象变了 → 缓存从 biscuit 位置全部断裂。虽然内容只多了几行,但因为是新对象 → 全量重新编码。
实际效果:每层 ~15-40K tokens 计费(V10 的 38K 有所改善,但远未达预期)。
V12 · 原地追加,零 splice
核心洞察:缓存命中的关键不是"内容是否相似",而是消息对象是否同一个引用。只要消息对象不变,仅 content 字符串尾部增长,LLM API 就能识别前缀并给予缓存折扣。
改动:
- biscuit 消息对象永不替换,仅
content += 新饼干行 - DE 消息对象永不替换,仅
content = 全新序列化(原地更新,但仍是同一对象) - 删除所有 splice 操作
效果:
发送楼层 N+1 时的背包:
[0] Z ← 100% 命中(永不变化)
[1] biscuit msg ← 前缀命中(对象不变,仅最后一行新)
[2] DE msg ← 前缀命中(对象不变,仅最后条目新)
[3] user_msg F(N+1) ← 新内容(唯一新 token 大头)
每层实际计费: ~2-4K tokens(仅 biscuit 末行 + DE 末条目 + 用户新消息)
V11: ~15-40K tokens
V10: ~38K tokens
无压缩: ~5K tokens/层 但背包无限膨胀 → 必死
V12 的遗留问题:
- DE(不可恢复工具输出 + AI 代码块)和 biscuit 有大量重叠——同一份终端输出在 biscuit 的绝对包装盒和 DE 各存一份 → token 翻倍
- DE 的 FIFO 轮转触发时,DE content 变化 → 缓存从 DE 处断裂(低频但存在)
- DE 的代码提取依赖启发式规则,可能漏掉 AI 纯文本中的代码
V13 · DE 消除 + 双包装盒体系(最终形态)
核心洞察:DE 的存在本身就是冗余。绝对包装盒(╔K...╚)已经在 biscuit 里保留了不可恢复工具的完整输出,DE 再存一份毫无意义。至于 AI 产出的代码——可以通过 sha256 引用 timeline 中的历史版本,无需在 DE 中保留全文。
改动:
- DE 概念全消除。
_deBlock消息、deEntries字段、所有序列化/解析函数全部删除。 - 20 个工具重新分类为双包装盒体系:
| 类别 | 数量 | 成员 | 处理方式 |
|---|---|---|---|
| 温柔包装盒 | 15 | read/search/find/list/diag/edit/write/create/delete/revert/timeline_versions/diff_versions/fetch_webpage/search_web | biscuit 仅一行头(结果可从磁盘重读或参数重跑复现) |
| 绝对包装盒 | 5 | run_command / generate_image / remove_background / analyze_image / get_vision_context | ╔K...╚ 包裹完整输出,阀值压缩时可剥离体部 |
- 阀值压缩:新增
_stripAbsoluteBoxes()— 正则╔K[\s\S]*?\n╚\n精准剥离绝对包装盒体部,保留头行。手动触发(🗜️按钮,threshold=0)或自动触发(建楼中_lastApiPromptTokens超阈值)。 - sha256 考古:读工具传
sha256=→ biscuit 标记@sha=xxx(12 位 hex);写工具产出 →➜ sha=xxx。AI 可随时用 sha256 参数回读历史版本 blob,零额外 IO 零 token 浪费。
压缩饼干最终格式:
=== F1 2026-07-16 01:10:44 UTC+8 ===
Q: 用户编辑框原文一字不差
[A → read_file] /path @sha=bdf18b5043c9 L:1-200/958
[A → search_text] "pattern" 1 hit
[A → edit_file] /path 3chg ➜ sha=94b4e3f7c56e
[A → run_command] "psql -U postgres -d dgs -c 'SELECT...'" 2318c
╔K
id | name
1 | qqqide
╚
A: 已修复,原因是...
=== F2 2026-07-16 02:30:15 UTC+8 ===
...
阀值压缩后(剥离绝对盒体部):
=== F1 2026-07-16 01:10:44 UTC+8 ===
Q: 用户编辑框原文一字不差
[A → read_file] /path @sha=bdf18b5043c9 L:1-200/958
[A → search_text] "pattern" 1 hit
[A → edit_file] /path 3chg ➜ sha=94b4e3f7c56e
[A → run_command] "psql -U postgres -d dgs -c 'SELECT...'" 2318c
A: 已修复,原因是...
体部剥离,头行保留——AI 知道执行了什么命令、输出了多少字符,但不再为 16KB 的终端输出付 token 费。
3. 最终形态全景
3.1 背包物理结构
agent.conversation[]:
[0] Z (sg0, 规则/铁律/vision context, _persistent) — 永不变,100% 缓存命中
[1] 压缩饼干 (1条 _biscuit msg, content 尾部追加) — 前缀缓存命中
[2] 当前楼层原始消息 (user + assistant + tool) — 新建楼层时变化
对比 V12 少了 DE 消息。三元素,零冗余。
3.2 机械筛 — 纯本地确定性转换
| 原始消息 | 饼干格式 |
|---|---|
| user | Q: [全量内容](剥离 [File:] 注入块和 CURRENT TIME) |
| assistant 纯文本 | A: [全量内容] |
| assistant 含 tool_calls | 先 A: 行(如有文本),再 [A → xxx] file1, file2 工具行 |
| tool 返回(温柔派) | 摘要追加到上一工具行 |
| tool 返回(绝对派) | 头行 + ╔K...╚ 包裹完整输出 |
| system | [S] [全量内容] |
关键特性:
- 零网络调用,零 AI 费用,纯客户端 CPU
- 完全确定性——同一输入永远同一输出
- 不含代码正文(AI 产出的代码通过 sha256 在 timeline 中引用)
- 不含 CURRENT TIME(避免每层时间戳不同导致缓存断裂)
3.3 双包装盒 — 按复现性分流
分类逻辑:一个工具的输出能否被精确复现?
- 能复现(温柔派)→ 只保留一行摘要。AI 需要细节时可重新执行工具或用 sha256 考古。
- 不能复现(绝对派)→ 保留完整输出(╔K...╚),但允许阀值压缩时剥离体部。
温柔派 15 个:
- 文件类 6:read_file / edit_file / write_file / create_file / delete_file / revert_file
- 搜索类 4:search_text / search_content / find_files / list_files
- 诊断类 1:get_diagnostics
- 网络类 2:fetch_webpage / search_web(成功返回后落地成文→温柔)
- Timeline 类 2:timeline_versions / diff_versions
绝对派 5 个:
- 终端 1:run_command
- 视觉 4:get_vision_context / analyze_image / generate_image / remove_background
3.4 缓存经济学
发送楼层 N+1 时的 token 构成:
Z: 0 tokens (100% 缓存命中)
biscuit 前缀: 0 tokens (前缀命中,仅最后 ~200 tokens 新)
当前楼层: ~3-5K tokens (user + assistant + tool results)
每层实际增量计费: ~200-500 tokens (仅 biscuit 新行)
vs 无压缩方案: ~5K tokens/层 × N 层 → 线性增长
vs ChatGPT 方案: 全量重算,无前缀缓存优化
为什么缓存能命中:
- Z 消息对象永不替换 → API 识别为同一消息
- biscuit 消息对象永不替换,仅 content 字符串尾部增长 → API 识别前缀
- 当前楼层的 user 消息每次不同 → 从 biscuit 末尾到 user 消息之间是新内容
3.5 阀值压缩 — 精准手术
触发条件:建楼中 _lastApiPromptTokens > 阈值(默认 600K,100-1000K 可调)或手动点击 🗜️。
操作:_stripAbsoluteBoxes(biscuitContent) → 正则 ╔K[\s\S]*?\n╚\n → 替换为空。
安全性:
- ╔K(U+2554 双线框+K)在二进制/ANSI/JSON/HTML 中概率趋零
- 非贪婪
[\s\S]*?锚定行首 ╚,绝不跨越到下一盒子 - 转义:╔K→╔K\、╚→╚\ 仅碰撞触发(概率→0),零常态开销
- 头行(命令+字符数)完整保留 → AI 不失上下文
3.6 持久化与自愈
持久化:
ctx.json (per-quest, 原子 tmp+rename)
└── biscuitLines[] — 每层楼的压缩行数组
自愈:
ctx.json 损坏/丢失
→ D 路径: _rebuildBackpack(conversation)
→ 从原始 conversation 消息重新机械筛
→ 生成 biscuitLines → 写入 ctx.json
→ 零数据丢失
3.7 错误处理
- 未恢复 fatal 楼层:不进饼干。原始消息保留在 conversation,AI 能直接看到
_error消息。 - 已恢复 fatal 楼层:进饼干,格式
[ERR] [HH:MM] 错误文本。重启时扫描 biscuit 重建错误日志。 - 时间戳持久化:
_errorTime字段写入 conversation 消息,重启精确恢复。
4. 与当前市面方案对比
| 维度 | ChatGPT | Claude | Cursor | qqqide V16 |
|---|---|---|---|---|
| 压缩方式 | 无压缩,全量保留 | 无压缩 | 无压缩 | 本地机械筛,确定性 |
| 缓存优化 | 无前缀缓存优化 | 无 | 无 | 原地追加,~90% 前缀命中 |
| 工具输出 | 原样保留 | 原样保留 | 原样保留 | 双包装盒分流,体部可剥离 |
| 历史引用 | 无 | 无 | 无 | sha256 考古,精确引用历史 blob |
| 每层增量 | ~5K+ tokens | ~5K+ tokens | ~5K+ tokens | ~200-500 tokens |
| 50层累计 | ~250K tokens | ~250K tokens | ~250K tokens | ~10-25K tokens (biscuit) + 当前层 |
| 数据所有权 | 云,不可导出 | 云,不可导出 | 云,不可导出 | 本地,用户完全拥有 |
| 全文检索 | 仅标题 | 无 | 无 | conv-ui.html 全文检索历史 |
| 跨会话引用 | 不支持 | Projects 手动 | 不支持 | sha256 跨楼层精确引用 |
| 压缩费用 | N/A | N/A | N/A | 零(纯本地 CPU) |
核心差异:
- ChatGPT/Claude 的策略是"什么都不丢" — 完整保留一切,让 token 预算自然淘汰旧内容。用户没有控制权:不能选择保留什么、丢弃什么。本质上是"被动遗忘"。
- qqqide 的策略是"精准保留 + 可控丢弃" — 机械筛保证结构化的 Q&A 链路完整;双包装盒保证不可复现的输出被保留但可剥离;sha256 保证可复现的内容随时精确回读。用户主动选择压缩粒度。
- 数据所有权是最终分水岭 — 所有云方案的本质缺陷不是技术上的,而是架构上的:上下文存在服务商的服务器上,用户只有访问权没有所有权。qqqide 的上下文是项目目录下的纯文本文件(ctx.json + all.txt),用户可以用任何工具处理、备份、迁移。
5. 如果没有 qqqide
上下文背包不是一个"锦上添花"的功能。它是 AI 编程助手能否长期可用的基石。
5.1 当前市场的真实困境
今天使用任何主流 AI 编程助手的用户,都在经历同一种体验:
- 前 10 层楼:AI 表现惊艳。它记得你的代码库结构、你的命名习惯、你偏好的方案。
- 10-30 层楼:token 窗口开始吃紧。AI 开始"忘记"早期讨论的架构决策。你需要重复解释。
- 30 层楼之后:你必须手动"修剪"上下文——删除旧对话、开新会话。每一次修剪都是一次上下文清零。AI 重新理解你的代码库,重新建立默契。
这个循环没有终点。 每次上下文清零 → AI 重新理解 → 你又花了 token 费重新教它 → 窗口又满了 → 再次清零。
5.2 没有压缩背包的世界
如果上下文背包不存在:
- 长对话不可持续 — 50 层楼后要么破产要么失忆。大型重构任务(需要 100+ 轮交互)无法在一个会话中完成。
- 跨项目经验无法积累 — 你在项目 A 里教会 AI 的架构模式,在项目 B 里要从头再教。因为没有可迁移的上下文格式。
- 用户永远是被动的 — 你不能决定保留什么、丢弃什么。是系统在替你遗忘,不是你在管理记忆。
- 上下文是租来的,不是拥有的 — 换一个工具 → 一切归零。你的对话历史锁在别人的服务器上。
5.3 背包的核心哲学
上下文是 AI 时代最稀缺的生产资料。
代码可以重写,但"AI 为什么这样改"的推理链无法重建。
三个权利用户必须拥有:
① 所有权 — 上下文数据在本地,纯文本,随时可迁移
② 控制权 — 你决定压缩什么、保留什么、丢弃什么
③ 检索权 — 全文检索所有历史上下文,跨会话引用
qqqide 上下文背包是这三个权利的技术实现。
6. 未解决问题
- >200 层 — 饼干自身需二次压缩。当前每层 ~200 字符,200 层 = 40K chars,仍在可控范围。但 500+ 层时需要考虑对 biscuit 本身再做一次压缩(稀有场景)。
- 跨 quest 上下文共享 — 目前每个 quest 的背包独立。项目 A 第 3 层楼讨论的架构决策,项目 B 无法自动引用。需要跨 quest 的 facts grid(设计已预留,待实现)。
- AI 纯文本中的代码 — 机械筛只从 tool_call arguments 提取代码,不处理 AI 在纯文本回复中内联的代码块。这些代码不会被 sha256 引用,丢失后不可恢复(影响较小——AI 回复中的代码通常是示例,非生产代码)。
附录 A · 版本演进速查
| 版本 | 日期 | 核心改动 | 每层计费 | 缓存命中 |
|---|---|---|---|---|
| V10 | 2026-06 | AI 语义压缩(三专家并行) | ~38K | 0% |
| V11 | 2026-07 初 | 机械筛 + 追加式饼干 + splice | ~15-40K | 0%(splice 漏洞) |
| V12 | 2026-07 中 | 原地追加,零 splice | ~2-4K | ~90% |
| V13 | 2026-07-16 | DE 消除 + 双包装盒 + 阀值压缩 + sha256 | ~200-500 | ~90% |
| V14 | 2026-07-17 | floorMsgs 升序修复重排→缓存全断 bug | ~200-500 | ~90% |
| V15 | 2026-07-19 | 三按钮压缩(absolut/edit only/only facts) + fx Grid + _compressFloor | ~200-500 | ~90% |
| V16 | 2026-07-23 | biscuit 唯一性强制(去重→先合并再删) + _findDynamicMsg 修复(Z 阻断) | ~200-500 | ~90% |
附录 B · 关键文件
| 文件 | 职责 |
|---|---|
server-app/ai-panel/agent-context.js | 背包核心:机械筛、包装盒、阀值压缩、ctx 持久化 |
server-app/ai-panel/panel-floor.js | 楼层完结触发 _rebuildBackpack |
server-app/ai-panel/panel-quest.js | ctx.json 读写 |
server-app/goods/conv/conv-ui.html | 上下文背包管理 UI |
do/拓扑/铁律 §56 | 上下文压缩铁律 |
do/拓扑/架构 压缩章节 | 架构级描述 |