1. 要解决什么问题
你跟 AI 聊了三个月,帮它理解了你的代码库架构、你的命名习惯、你偏好的技术栈。然后你换了一个 AI 工具——一切归零。所有的"默契"、所有积累的上下文、所有你花时间解释过的事情——都留在了上一个工具的服务器上。你无法导出、无法搜索、无法迁移。
你创造了上下文,但你并不拥有它。
当今所有主流 AI 编程助手(ChatGPT、Claude、GitHub Copilot、Cursor、JetBrains AI Assistant)的共同架构是:对话历史存储在服务商的云服务器上。你是租客,不是房东。你能"用"你的上下文,但你不能"带走"它——不能全文检索它、不能跨会话引用它、不能在新工具里复用它。
更根本的问题是:上下文是 AI 时代最稀缺的生产资料。 代码可以重写,但"AI 为什么这样改"的推理链、"上次踩过什么坑"的经验、"这个架构决策是怎么来的"的来龙去脉——这些一旦丢失,无法重建。而当今的所有方案,都在系统性地制造这种丢失。
本质问题:用户的上下文数据,所有权和使用权应该无条件归属用户,而不是服务商。
2. 历史方案的失败点
ChatGPT:「会话列表 + 搜索框」
- 对话存在 OpenAI 服务器上,用户在网页端可以搜索标题
- 不能全文检索对话内容(只能搜标题)
- 导出是手动的一次性 JSON dump,没有工具链消费
- 跨会话引用?不存在。一个会话里让 AI 参考另一个会话的内容?复制粘贴。
- 根本缺陷:上下文是活的(持续生长),但 ChatGPT 把它当死的(静态列表)。
Claude:「Projects + 知识库」
- Projects 允许上传文件作为背景知识
- 但对话历史仍然不可全文检索
- 知识库需要手动维护——每次学到新东西,用户要自己摘出来放进知识库
- 根本缺陷:把"上下文管理"的负担转嫁给了用户。AI 应该自动积累上下文,而不是让用户当档案管理员。
GitHub Copilot:「内联补全 + 聊天面板」
- 聊天面板有历史记录,但仅限当前 IDE 会话
- 没有跨会话引用
- 上下文窗口内的代码理解是实时的,但不持久化——下次打开 IDE,AI 重新读你的代码
- 根本缺陷:上下文是瞬时的(ephemeral)。每次对话从零开始,浪费 token 重复理解已理解过的东西。
Cursor:「代码库索引 + 聊天历史」
- 代码库索引(codebase indexing)是所有工具里最先进的
- 聊天历史存储在云端,支持简单的历史浏览
- 但不支持全文检索历史对话
- 上下文不可导出、不可迁移到其他工具
- 根本缺陷:代码库上下文优秀,但对话上下文管理停留在"聊天记录列表"层面。
JetBrains AI Assistant:「IDE 内嵌 + 云存储」
- 对话历史存在 JetBrains 云
- 没有跨会话搜索
- 上下文完全绑定 JetBrains 生态
- 根本缺陷:和所有云方案一样——你拥有的是访问权,不是所有权。
共同缺陷:三条铁律全部违反
| 铁律 | 含义 | 现存方案 |
|---|---|---|
| 可检索 | 用户能全文检索所有历史上下文 | 无一做到 |
| 可迁移 | 上下文是开放格式,可导入其他工具 | 仅 ChatGPT 有 JSON 导出,但无工具链消费 |
| 可复用 | 新会话能自动引用旧会话的上下文 | 无一做到 |
所有方案都假设:上下文是服务商的资产,用户只是被授权访问。 这是一个根本性的错误。上下文是用户花时间、花精力、花钱创造的智力成果。它应该和用户的代码一样,物理存储在用户的硬盘上,以开放格式保存,随时可检索、可迁移、可复用。
3. 我们如何一步步设计和实现
我们的方案不是一次性"设计"出来的。它是七个步骤的递进推演——每一步解决上一步暴露的问题,最终收敛到一套完整的上下文所有权架构。
3.1 第一步:回答"上下文存哪里"——本地文件系统的选择
最初始的问题是:AI 对话记录应该存在哪里?
云方案的诱惑是巨大的:零配置、多设备同步、不占本地空间。但我们对云方案做了三个追问,答案全部否定:
- 用户能随时拿到原始数据吗? 不能。云端 API 只能按服务商规定的粒度访问(标题列表、单个会话),没有批量导出。
- 数据格式是谁定义的? 服务商。用户无法控制格式演进,今天能读的数据明天可能因为 API 变更而失效。
- 服务停止后数据还在吗? 不在。你的上下文和你的订阅一起消失。
结论:上下文必须和用户的代码放在一起——物理上、格式上、控制权上。
我们选择的方案是:所有上下文存储在项目根目录下的 _qqq/quests/ 中,和项目代码处于同一棵文件树。拷贝项目文件夹 = 完整迁移所有 AI 对话历史。Git 可以版本控制它(如果用户选择),任何备份工具都能保护它。
这是最"无聊"的选择——文件系统。但正是这个"无聊"的选择,解开了后续所有创新的枷锁。因为数据在本地,我们才能在它上面建搜索引擎、建跨任务引用、建 trace 因果链。
3.2 第二步:回答"存什么格式"——双轨制(all.json + all.txt)
文件系统存放什么格式?我们拒绝单一格式,因为上下文有两种根本不同的消费者:
| 消费者 | 需求 | 格式要求 |
|---|---|---|
| 机器(AI 模型) | 加载到上下文窗口、解析 tool_calls、读取计费信息 | 结构化 JSON |
| 人类(开发者) | 快速浏览对话内容、grep 关键词、在记事本里打开 | 纯文本 TXT |
于是有了双轨制——每层楼(floor)同时产生两个文件:
all.json:机器消费。包含完整 conversation 数组(每条消息的 role、content、tool_calls、tool_call_id)、计费元数据(ge 消耗、模型 tier)、文件快照引用(blob_hash 指向 timeline)。AI 可以在一个read_file调用里加载整层楼的完整上下文。all.txt:人类消费。同样的对话内容,但是纯文本 Markdown 格式——用户消息、AI 回复、工具调用和结果,按时间顺序排列。任何文本编辑器、grep、ripgrep 都能直接消费。
第三个文件 reasoning.txt 是 all.txt 的截断版——工具返回结果截断到 300 字。用途是快速审计:"这一层楼 AI 调了什么工具、拿到了什么结果",不需要翻阅可能几十 KB 的完整工具输出。
关键原则:一个事实,两份表达。 不冗余存储——两份文件描述同一段对话,但针对不同的消费者优化。JSON 给 AI 做结构化推理,TXT 给人做全文检索和快速阅读。
3.3 第三步:回答"怎么组织"——quest/floor/house/room 层级目录
下一个问题:当有 50 个 quest、每个 quest 有 20 层楼时,怎样组织目录结构才不会变成一锅粥?
我们定义了一个严格的层级命名体系:
quest(一次对话会话)
└── floor(一次用户回车,可能含多次 API 调用)
└── house(一次 API 调用,可能含多次 tool call)
└── room(一次工具调用)
对应的目录结构:
_qqq/quests/
├── _index.json ← 所有 quest 的元信息索引
├── q1.搭建数据库层/
│ ├── f1.设计 schema/
│ │ ├── all.json
│ │ ├── all.txt
│ │ └── reasoning.txt
│ ├── f2.调试索引/
│ └── f3.性能优化/
├── q2.重构 API 层/
│ └── f1.开始/
└── q3.前端改造/
└── ...
这个层级体系的关键属性是:每层楼是独立可寻址的最小单元。 你可以精确引用 "q1/f2"——那一层楼的完整对话、完整推理、完整工具调用链——而不需要加载整个 q1。这为跨任务引用奠定了基础:AI 不需要把整个 quest 塞进上下文窗口,只需要精确加载相关的几层楼。
3.4 第四步:回答"怎么快速找到"——_index.json + ripgrep 全文检索
目录结构解决了"怎么存",但没解决"怎么找"。当积累了上百层楼、几十万个字时,用户需要两种查找能力:
A. 结构化查找 — _index.json
_index.json 是一个轻量索引文件,记录了所有 quest 的元信息:quest ID、标题、创建时间、楼层数、每层楼的标题和状态。加载这个文件(通常 < 10KB)就能画出完整的 quest 列表,不需要扫描目录。
B. 全文检索 — ripgrep
用户真正需要的是:"我三个月前和 AI 讨论过 PostgreSQL 的 WAL 配置——是哪次对话来着?"
qqqide 集成了 ripgrep(Rust 实现、mmap 加速、SIMD 向量化),直接对 _qqq/quests/ 下所有 all.txt 执行正则搜索。结果毫秒级返回——因为数据在本地,因为格式是纯文本。
用户输入: "PostgreSQL WAL"
ripgrep 扫描: _qqq/quests/**/all.txt
返回: q1/f3/all.txt:42 → "我们决定把 WAL 段大小设为 64MB..."
q5/f1/all.txt:18 → "PostgreSQL WAL 归档用 pg_receivewal..."
对比的关键差异:
| ChatGPT | Claude | Copilot | Cursor | qqqide | |
|---|---|---|---|---|---|
| 搜索范围 | 标题 | 标题 | 无 | 标题 | 全文 |
| 搜索速度 | 云端 API,不定 | 云端 API,不定 | N/A | 云端 | 本地,毫秒 |
| 离线可用 | ❌ | ❌ | ❌ | ❌ | ✅ |
| 正则支持 | ❌ | ❌ | ❌ | ❌ | ✅ |
这不是一个"更好的搜索框"。这是把 AI 对话从"聊天软件"变成了"知识库"。而这一切的前提是第 3.1 步的选择——数据在本地,才能跑本地搜索引擎。
3.5 第五步:回答"代码和对话什么关系"——Timeline trace 因果链
有了对话的存储和检索,下一个自然的问题是:这些对话产生了哪些代码?反过来,这段代码是哪次对话产生的?
我们自建了 timeline 版本系统(_qqq/timeline/),核心架构:
blobs/{sha256[:2]}/{sha256}.gz ← 文件内容不可变快照(SHA256 行尾归一化去重)
timeline.db ← SQLite 全量快照索引
timeline.wal ← NDJSON 增量日志(≤99 行)
每次 AI 工具调用(write_file、edit_file、create_file)在完成时自动捕获文件快照,并打上 trace 标记:
trace = "q{questId}/f{floorNum}/h{houseNum}/r{roomNum}"
例如 q3/f2/h1/r2 意味着:这个文件变更是 quest 3、第 2 层楼、第 1 次 API 调用、第 2 个工具调用产生的。
效果:
用户在 diff 窗口看到某行代码变更
→ trace 显示: q3/f2/h1/r2
→ 点击跳转到 q3 的第 2 层楼
→ 看到 AI 当时的完整推理:
"这个字段必须改成 TEXT 而不是 VARCHAR(255),
因为 PostgreSQL 里两者存储效率相同,
但 TEXT 避免了未来数据超长的风险..."
传统的 Git blame 只能回答谁、什么时候改的,commit message 最多回答改了什么。但永远回答不了为什么这样改。Trace 把代码和产生它的推理链永久绑定——这是只有 AI 辅助编程时代才会出现的新型工程知识。
3.6 第六步:回答"怎么跨任务复用"——跨 quest 上下文协作
前五步都是基础设施。第六步是它们共同支撑的核心能力:跨任务上下文协作。
完整工作流
┌─────────────────────────────────────────────────────────┐
│ 步骤 1: 全文检索定位相关上下文 │
│ │
│ 用户在 q8 ("重构 API 层") 中,需要参考之前的数据库设计 │
│ → ripgrep 搜索 "schema" "PostgreSQL" "表结构" │
│ → 命中 q1/f1, q1/f3, q5/f2 │
│ → 每个命中显示匹配行 + 文件路径 + 楼层标题 │
│ │
├─────────────────────────────────────────────────────────┤
│ 步骤 2: 提取成果 │
│ │
│ 用户打开 q1/f3(PostgreSQL schema 设计的那层楼) │
│ → all.txt 显示完整的人类可读对话 │
│ → 看到 AI 在 q1/f3 中的结论: │
│ "最终 schema 使用 UUID 主键、BRIN 索引、 │
│ 分区表按月份 range 切分..." │
│ → 用户知道:这就是我要复用的成果 │
│ │
├─────────────────────────────────────────────────────────┤
│ 步骤 3: 注入新任务 │
│ │
│ 方式 A — 手动引用: │
│ 用户在 q8 中说 "参考 q1/f3 的 schema 设计继续" │
│ → AI 读取 q1/f3/all.json │
│ → AI 看到 q1/f3 的完整对话上下文 │
│ → AI 知道当时的约束、决策、踩过的坑 │
│ → 在 q1/f3 的基础上继续,不重复解释 │
│ │
│ 方式 B — 自动发现: │
│ AI 在 q8 中自动搜索相关历史楼层 │
│ → 将相关上下文注入当前对话 │
│ → 用户不需要知道哪些历史有关 │
│ → AI 像一个记得所有历史的老搭档 │
│ │
├─────────────────────────────────────────────────────────┤
│ 步骤 4: 开分支 │
│ │
│ 用户想基于 q1 的全部成果尝试不同的方向 │
│ → 创建 q9 "换 MySQL 引擎重做数据库层" │
│ → 将 q1 的完整上下文作为 q9 的起点 │
│ → AI 看到: q1 的设计决策 + q9 的新目标 │
│ → "把 PostgreSQL schema 翻译成 MySQL, │
│ 注意 q1/f3 提到的 BRIN 索引在 MySQL 中不存在, │
│ 需要用其他方案替代..." │
│ │
└─────────────────────────────────────────────────────────┘
为什么只有 qqqide 能做到
这个工作流对云方案来说在物理上不可行,原因有三:
- 数据不在本地。云方案的对话历史在服务商服务器上。你无法用 ripgrep 扫描它——API 只支持按标题搜索,不支持全文。每次查找都要网络往返,延迟不可预测。
- 格式不可消费。即使云方案给了你导出,通常是一次性 JSON dump,没有目录结构、没有索引、没有楼层粒度的独立寻址。你不能精确引用 "会话 3 的第 5 条消息"——你只能把整个 JSON 塞给 AI。
- 没有 trace 关联。代码和对话是分离的——你在 Git 里看到一行改动,不知道它对应哪次 AI 对话。反之,你在对话里提到 "改了 schema.sql",不知道当时的代码是什么状态。
qqqide 做到这一点,是因为我们在前五步已经建造了完整的原料保存体系:
- 第 3.1 步(本地存储)→ 数据就在 ripgrep 能扫到的地方
- 第 3.2 步(双轨格式)→ JSON 给 AI 加载,TXT 给 ripgrep 搜索
- 第 3.3 步(层级目录)→ 每层楼独立可寻址,精确引用不浪费 token
- 第 3.4 步(全文检索)→ 毫秒级定位相关上下文
- 第 3.5 步(trace 因果链)→ 代码能找到产生它的对话,对话能找到它产生的代码
五步递进,缺一不可。缺任何一步,跨任务协作就是空中楼阁。
3.7 第七步:预渲染——重启零损耗
最后一步是消除摩擦:如果每次重启 IDE 都要重新渲染所有对话 HTML,用户会感觉"卡"。
我们在 all.json 中引入了 ai_html 字段:每层楼在流式渲染完成后,将最终的 DOM HTML 预计算并持久化。下次重启时,直接从 ai_html 读出 innerHTML——零重新渲染,零等待。
流式渲染完成
→ _contentWrap.innerHTML 序列化为 HTML 字符串
→ 写入 all.json 的 ai_html 字段
→ 重启时: ai_html → innerHTML
→ 用户看到的是完全相同的渲染结果,瞬间加载
这不是性能优化。这是所有权的一部分——你拥有的不仅是对话的文本内容,还有对话的视觉呈现。 即使三十年后,你用不同的工具打开 all.json,那段对话的完整面貌依然可以还原。
4. 核心突破:一切原料的完整保存
回看这七步,可以归纳为一个核心命题:跨任务上下文协作的前提,是拥有一切原料。
什么是一切原料?一场 AI 对话产生的全部可留存信息:
| 原料 | 存储位置 | 用途 |
|---|---|---|
| 完整对话(结构化) | all.json | AI 加载到上下文窗口 |
| 完整对话(人类可读) | all.txt | 全文检索、快速浏览、跨工具消费 |
| 审计摘要 | reasoning.txt | 快速了解"AI 做了什么" |
| 代码快照(SHA256 不可变) | timeline/blobs/ | 任何时候恢复任何版本 |
| 代码变更索引 | timeline.db + timeline.wal | 查询文件变更历史 |
| 因果链 | trace 标记 (q{n}/f{n}/h{n}/r{n}) | 代码 ↔ 对话双向追溯 |
| 预渲染 HTML | all.json 的 ai_html | 重启即时恢复 |
| Quest 元信息 | _index.json | O(1) 列出所有对话 |
云方案只给你其中最多 2 项(对话文本 + 会话列表),而且是以服务商控制的格式、在服务商控制的服务器上。
我们的方案给你全部 8 项,在你自己的硬盘上,以开放格式保存。
这就是"将上下文还给用户"的技术含义。不是一句口号——是七步递进建造的完整原料保存体系。因为所有原料都在本地,你才能:
- 全文检索:ripgrep 扫所有
all.txt,毫秒级 - 精确引用:读某一层楼的
all.json,AI 看到完整上下文 - 开分支:复制一个 quest 的全部上下文作为新起点
- 查因果:看到代码 → trace → 找到产生它的对话 → 看到当时的推理
- 迁移:拷贝项目文件夹 = 带走一切
这不是功能对比,是物理可行性对比
| 能力 | 云方案的物理瓶颈 | qqqide 的物理基础 |
|---|---|---|
| 全文检索 | 数据在远端,API 不支持 | 数据在本地,ripgrep 直扫 |
| 精确引用 | 无楼层粒度导出 | 每层楼独立目录 + all.json |
| 开分支 | 无法复制完整上下文 | 本地目录拷贝 |
| 查因果 | 代码和对话无关联 | trace 标记双向链接 |
| 迁移 | 服务商控制导出格式和粒度 | 开放格式 + 本地文件 |
所有能力差异的根源,不是"我们做得更好",而是"我们拥有原料"。 一旦数据在本地、格式开放、层级可寻址、因果有 trace,跨任务协作就是自然的产物。反过来说,数据在云端、格式封闭、层级扁平、因果断裂——再聪明的算法也做不了跨任务协作。
5. 与一切现存方案的对比矩阵
| 维度 | ChatGPT | Claude | Copilot | Cursor | JetBrains AI | qqqide |
|---|---|---|---|---|---|---|
| 上下文物理位置 | 云端 | 云端 | 云端 | 云端 | 云端 | 本地硬盘 |
| 数据所有权 | 访问权 | 访问权 | 访问权 | 访问权 | 访问权 | 所有权 |
| 全文检索 | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ ripgrep |
| 开放格式 | JSON导出 | ❌ | ❌ | ❌ | ❌ | JSON+TXT |
| 人类可读存档 | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ all.txt |
| 离线可用 | ❌ | ❌ | 部分 | ❌ | ❌ | ✅ |
| 跨会话引用 | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
| 代码-对话关联 | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ trace |
| 可迁移性 | 手动导出 | ❌ | ❌ | ❌ | ❌ | 拷贝文件夹 |
| 隐私 | 云端明文 | 云端明文 | 云端明文 | 云端明文 | 云端明文 | 本地 |
| 免订阅费 | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
| 不绑定厂商 | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ 开放格式 |
这不是"我们比他们多几个功能"。这是两种完全不同的上下文哲学:
- 云哲学:上下文是服务的附属品。你付费获得访问权,服务终止 = 上下文蒸发。
- 本地哲学:上下文是你的财产。你创造它,你拥有它,你控制它。工具只是帮助你在本地管理它。
6. 深层含义:上下文是 AI 时代的源代码
代码开源运动用了三十年让"代码应该属于写它的人"成为共识。AI 时代面临同样的问题,只是资产从"代码"变成了"上下文"。
代码是死的,上下文是活的。 一段代码可以重写,但"为什么这样写"的推理链、"踩过哪些坑"的经验库、"架构决策怎样演化"的时间线——这些一旦丢失,无法用金钱买回来。
今天的所有 AI 工具都在做同一件事:用免费/低价的服务换取你的上下文数据。 你越用,积累越多;积累越多,迁移成本越高;迁移成本越高,你越离不开。这不是技术选择,这是商业锁定。
我们的方案在根本架构上拒绝这个模式:
- 上下文物理存储在用户硬盘上,开放格式(JSON + TXT)
- 全文检索、跨任务引用、代码关联全部本地完成
- 零厂商锁定:拷贝文件夹 = 完整迁移到任何兼容工具
- 隐私:对话内容从不出你的电脑(AI 调用时发送的仅是当前楼层上下文)
用户创造了上下文。用户应该拥有上下文。用户应该能随意使用自己的上下文——搜索、引用、迁移、删除。这不是一个"feature request",这是数字时代的基本权利。
7. 结论
我们把 AI 上下文从云端的"服务附属品"变成了本地的"用户财产"。七步递进实现:
| 步骤 | 问题 | 方案 |
|---|---|---|
| 一 | 上下文存哪里 | 本地文件系统 _qqq/quests/,和代码同树 |
| 二 | 存什么格式 | 双轨制:all.json(机器)+ all.txt(人类) |
| 三 | 怎么组织 | quest/floor/house/room 层级,每层楼独立可寻址 |
| 四 | 怎么快速找到 | _index.json 索引 + ripgrep 全文检索 |
| 五 | 代码和对话什么关系 | Timeline trace 因果链,双向追溯 |
| 六 | 怎么跨任务复用 | 搜索→定位→提取→注入,或直接开分支 |
| 七 | 重启损耗 | 预渲染 ai_html,重启零重建 |
七步共同指向一个目标:用户对自己创造的上下文拥有完整的所有权和使用权。 AI 工具的角色是帮助用户管理和复用这些上下文,而不是把上下文锁在自己的服务器上。
核心洞察只有一个:你只有拥有一切原料,才能真正使用它们。 全文检索、跨任务引用、因果追溯、任务分支——这些不是"高级功能",而是用户对自己数据的基本权利。我们只是把这些权利从服务商手里拿回来,还给了用户。
这就是我们将用户上下文及其使用权还给用户的方式。