news 2026/10/23 6:03:50

deja-vu架构深度解析:零依赖Go二进制如何为41种Agent构建倒排索引

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
deja-vu架构深度解析:零依赖Go二进制如何为41种Agent构建倒排索引

【免费下载链接】deja-vu

Memory for Claude Code, Codex, Cursor and 38 more coding agents, built from the session history already on your disk. Local search, MCP and hooks, no LLM, one Go binary.

项目地址:https://gitcode.com/gh_mirrors/dej/deja-vu
点击查看免费下载

deja-vu 是一个零第三方依赖的 Go 单二进制,它把 Claude Code、Codex、Cursor 等 41 种编码 Agent 已经落在磁盘上的会话历史,自动解析并构建成本地倒排索引——不需要 LLM、不需要 embedding 服务、不上传任何数据,任何一个 Agent 下次启动时就能"想起"几个月前做过的决定、踩过的坑和验证过的修复。这篇文章从源码视角,拆解它从解析、索引到检索的完整链路。

对比演示:没有记忆时 Agent 对八个月前的问题毫无记录;接入 deja-vu 后,它直接引用了当时的决策。

一、为什么是"零依赖"的单二进制

先看最硬核的证据——整个项目的 go.mod 只有两行:

module github.com/vshulcz/deja-vu go 1.25

没有一行require。这意味着 deja-vu 的全部能力——41 种 Agent 的解析器、倒排索引引擎、BM25 检索、终端 UI、MCP 协议——都只用 Go 标准库实现。这个约束带来三个直接后果:

常见难点deja-vu 的解法收益
SQLite 读取需要 CGO调用系统自带的sqlite3命令行进程跨平台交叉编译零障碍
TUI 框架依赖(bubbletea 等)自研 internal/tui/ 包,termios/console mode 直接操作终端无 GUI 依赖,TERM=dumb自动降级为纯文本
索引写一半崩溃全程"写临时文件 → fsync → rename"原子交换读者永远只看到完整的新文件或旧文件

对普通用户来说,"单二进制"意味着安装只需一条命令(Homebrew、Scoop、winget、npm 均可),运行时无任何运行时、无服务进程、无云端。

二、一套注册表,统一41种Agent的历史文件

deja-vu 支持 41 种 Agent,全部清单记录在 docs/registry/registry.json。每种 Agent 的历史文件格式五花八门:有的是 JSONL 逐行追加(Claude Code 的~/.claude/projects)、有的是 SQLite 数据库(Cursor、Zed、Devin)、有的甚至是 zstd 压缩流(DeepSeek Harness)。

架构上的解法是一个注册表:internal/sources/registry.go 中的allHarnesses()为每种 Agent 登记三样东西:

  • Load:全量加载该 Agent 的所有会话;
  • Files:它把历史写在哪里(文件发现);
  • FileKind:每种文件格式的Match(路径匹配)、Parse(全量解析)、ParseFrom(从偏移量增量解析)。

所有解析器统一返回[]model.Session——无论源是 JSONL 还是 SQLite,进入索引之前都被归一化成同一种会话模型。新增一种 Agent 只需实现这套固定形状,发现、路径匹配和增量解析全部复用注册表逻辑,docs/ARCHITECTURE.md 给出了完整的六步接入指南。

文件类源用 CPU 核数大小的 worker 池并行解析,但结果按文件路径排序后再追加写入——解析可以并行,索引写入保持确定性。

三、倒排索引本体:records.bin 与 token 桶

索引落在~/.cache/deja/index.db,核心由两类文件构成:

1.records.bin—— 记录日志

长度前缀的连续记录,每条存会话键、源路径、角色(role)、文本和时间戳。两个值得注意的压缩技巧:

  • 会话键、路径、角色都在记录头部做intern(去重引用),扫描某类记录时读前缀即可跳过正文;
  • 8 KiB 以上的正文做 deflate 压缩,更小的不压——因为小正文解压开销大于压缩省下的空间。

2.buckets/*.bin—— 真正的倒排索引

每个桶文件是一组 token 到postings(倒排列表)的映射:token → 一组(记录偏移, 会话序号, 1 bit 标记)。写入实现在 internal/index/store_io.go 的writeBucket:token 排序后,偏移量不存储而是累加推导,省掉每 token 8 字节;更妙的是会话 ID 采用delta 编码(相对上一条 postings 的增量)——因为 records.bin 本就按会话顺序写,实测 99.66% 的 postings 会话号不减,桶文件因此缩小约 5.9%。每个桶文件开头的DJB2magic 字节会在格式变更时改变,防止旧字节被新规则误读成"错误的会话 ID"而不是"损坏"。

辅助文件:recoffsets.bin记录每个会话在 records.bin 中的位置(可失效重建的纯缓存);manifest.gob/sessions.gob存索引版本号、源文件状态、脱敏计数与会话元数据。

四、中文倒排索引:没有空格怎么办 ⚙️

英文分词按空格切,但中文、日语、泰语没有词间空格。deja-vu 在 internal/index/cjkindex.go 用了一组专门机制,这也是索引格式版本号频繁升级的原因(internal/index/index.go 里从 v12 到 v44 的注释,基本是一部"多语言分词踩坑史"):

  • CJK 二元字(bigram):连续汉字切成重叠双字词,查询侧同样切分,索引体积与召回率取平衡;
  • 繁体→简体折叠:写入前统一转成简体键,避免"記憶體"与"内存"各占一个键;
  • 日语长音符号、泰语、阿拉伯文组合音符:都经历过"被切成碎片"的 bug 修复——比如 Hindi 的हिन्दी曾因元音符不算 CJK 而被索引成两个碎片,v33 专门为此重建索引;
  • 功能词 bigram 剔除:的了、什么这类高频虚词对不丢弃,实测 42 MB 中文语料桶文件从 75.5 MB 降到 70.7 MB。

五、检索链路:BM25 只是第一步 🔍

一次查询的完整流程(详见 docs/ARCHITECTURE.md):

  1. 查询分词(CJK 走 bigram 路径);
  2. 从 token 桶读取各词倒排列表;
  3. 多词查询求交得到候选;
  4. 按--harness、--project、--since用会话元数据先过滤,再读出全部候选记录;
  5. 按会话分组,用BM25(k1=1.2, b=0.75)打分:用户提问词的贡献乘 1.3,时间衰减公式为0.5 + 0.5/(1+age_days)——时间最多让分数减半,永远不会把老记忆清零;
  6. 分数相同时比更新时间,再比会话 ID,最后才截断结果数。

BM25 之上还叠了几个有界的"语义信号",每个只能打破平局、不能凌驾相关性:达到结论的会话(decision)上浮、被rejected标记的会话在每个命中处如实显示、高频被复用的会话按log2曲线最多上浮 50%。可选地,deja embed会另写一个向量边车文件,对最多 64 个候选做混合重排(0.5 词法分 + 0.5 余弦相似度),向量文件损坏则静默回退纯词法结果——默认零依赖,想要语义能力再自己开启。

六、增量索引:只读新增的那几行 ⚡

一个月亮大的历史库不可能每次全量重建。EnsureForSearch对比当前文件集与 manifest,走出四个分支:

文件变化处理
manifest 新鲜什么都不做,直接答
版本/作用域不匹配重建(内容版本与磁盘格式分开追踪)
纯追加的 JSONL从上次安全偏移续读,只更新受影响的桶
文件被删或中段被改重写索引,但保留未变记录

追加分支有个容易忽略的陷阱:Agent 可能"回卷"会话——截断文件后重写并超过原长度,看起来和正常增长一模一样。deja-vu 用SafeSize(上次读到的最后一个完整行偏移)加PrefixHash(已读前缀指纹)来区分真增长和重写;而全量校验一个 250 MB 会话文件要 1.7 秒,于是又引入了PrefixSample采样指纹。更关键的是:搜索永远不等重写——EnsureForSearchStale先用现有索引应答,剩余重建交给后台 warmup。

冷重建时的并行度也做过精算:先全部解析,再由单个 goroutine串行写 records/桶/manifest 保证一致性;五个边车文件(共现表、修复对、重复命令表、失败清单、会话事实)互不读写对方,因此并行落盘——这是实测 51 秒降到 30 秒的主要来源。

七、边车文件与隐私防线 🔐

索引主体之外,构建期还会挖出几类"高价值事实",各自独立成边车文件:

  • fixraw.gob(internal/index/fixpair.go):错误 → 当时如何修好的配对。deja fix靠它回答"上次同样的报错后来怎么解决的";
  • cooccur(internal/index/cooccur.go):词共现表,支撑"你大概想找"式的查询修正;
  • commands.go:带退出码的命令记录,deja how用它给出"这台机器上真实跑通过的调用方式";
  • friction:每个会话首次出现的重复错误首行,会话启动块直接引用,无需回读会话。

隐私上,internal/redact/ 脱敏器跑在每一条写记录路径之前(全量重建、增量追加、导出皆然):AWS 密钥、Bearer/JWT、PEM 私钥块、--password命令行凭据、netrc 行、甚至散文里写的"管理员密码是……"都会被抹去,而键名和上下文保留可搜索。每条可能误伤正常文本的规则都带门控条件,边界在 docs/SECURITY-MODEL.md 中有正式声明。

八、让记忆自己"找上门":MCP 与 hooks

索引建好只是起点。deja-vu 用两种机制把召回推给 Agent,全部复用同一套索引与检索代码:

MCP 服务器(cmd/deja/mcp.go):JSON-RPC over stdio,只暴露一个工具deja,用mode参数区分八种能力——recall(紧凑摘录)、context(摘要)、blame(谁改过这个文件)、fix(同错的既往修复)、how(真实可用命令)、orient(项目惯用命令与常开文件)、remember(存一条持久决策)、handoff(跨 Agent 交接)。

hooks(deja install --auto自动接线):

  • hook-context:会话启动时注入 ≤2KB 的项目记忆摘要,永远不触发冷构建,出错静默退出、绝不阻塞 Agent 启动;
  • hook-prompt/hook-tool:提问或即将编辑文件前,注入"你在这里打过卡"或该文件的历史决策;
  • hook-tool-after:命令失败时,递上之前验证过能通过的修复(且明确拒绝不可逆操作如 force push);
  • hook-precompact:上下文压缩前抢救结构化状态——实测 43 次压缩里摘要只保住了 77% 的决策和 0.2% 的命令,剩下的靠它接回;
  • hook-session-end:会话结束落戳,防止 Agent 的召回结果里出现"自己引用自己"。

总结:一套架构的三个核心决策

决策做法换来什么
零依赖单二进制只用 Go 标准库,sqlite3 走子进程,TUI 自研十秒装完、离线运行、无服务
注册表 + 归一化会话模型41 种 Agent 各写解析器,输出统一model.Session一份索引,所有 Agent 共用记忆
纯倒排 + 有界信号排序词法 BM25 为主,语义向量仅作可选重排可解释、可离线、损坏可静默回退

如果你想动手验证,可以克隆仓库(https://gitcode.com/gh_mirrors/dej/deja-vu)后从 docs/ARCHITECTURE.md 和 internal/index/ 读起——格式版本注释、桶文件编码、增量门控的每一个决定,源码里都写清了它要修的那个具体 bug 和它测出来的数字。

【免费下载链接】deja-vu

Memory for Claude Code, Codex, Cursor and 38 more coding agents, built from the session history already on your disk. Local search, MCP and hooks, no LLM, one Go binary.

项目地址:https://gitcode.com/gh_mirrors/dej/deja-vu
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/18 12:59:54

数据全生命周期管控,企业数据质量治理该怎么做?

数据全生命周期,覆盖采集、录入、传输、存储、加工、应用、归档等一系列环节,很多企业做数据治理的误区,就是割裂看待各个环节:采集归业务系统,清洗归数据中台,质量检查是数据团队单独工作,各环…

作者头像 李华
网站建设 2026/10/23 14:57:17

滚丝机与攻牙机:金属紧固件加工核心技术解析

1. 认识紧固件加工的两大核心设备在金属紧固件生产线上,滚丝机和攻牙机就像厨师的菜刀和案板,是每天都要打交道的"老伙计"。作为从业十五年的工艺工程师,我见过太多因为设备选型不当或操作失误导致的生产事故。今天咱们就掰开揉碎聊…

作者头像 李华
网站建设 2026/10/19 15:44:26

方差分析从原理到实战:为什么不能用t检验做多组比较

前两天我帮一个做实验的朋友看数据。他设计了三组处理,每组五个重复,测完指标之后直接把三组数据两两之间跑了三轮t检验,结果拿回来问我“为什么有的显著有的不显著,该信哪个”。我反问他:你知道两两比较做了三次之后&…

作者头像 李华
网站建设 2026/10/23 8:35:22

S3d生成Review教程

(一),切换到Drawing and Reports,选择相应要生成review的文件夹,右键选择New>Spreadsheet Reports。选择生成的New Spreadsheet Reports,右键选择Create Reports,选择Smartplant Review层级下…

作者头像 李华
网站建设 2026/10/22 13:34:46

Win10下MASM与LINK汇编工具链配置及编译链接实战指南

简介:这份资源面向在 Windows 10 环境下学习汇编语言、需要搭建底层开发工具链的初学者与系统编程爱好者,核心提供 MASM.exe 汇编器与 Link.exe 链接器,解决从汇编源码到可执行文件的编译与链接问题。压缩包共 11 个文件,以 9 个 …

作者头像 李华
网站建设 2026/10/22 5:05:06

关保落地指南:从识别认定到应急处置的五个关键环节

简介:信息安全技术 关键信息基础设施网络安全保护基本要求.doc 是一份整理自国家标准征求意见稿的文档,由全国信息安全标准化技术委员会归口,面向网络安全工程师、关键信息基础设施运营单位安全人员、等级保护测评机构及相关管理者。文档以识…

作者头像 李华