一个进程内三元组索引,让 Pi 里的代码搜索在热索引上通常比 ripgrep 快 40–70…
35/100
综合分
10
GitHub 星标
88
npm 周下载
—/5
用户评分
数据截至 2026/9/17 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add snapgrepnpm 源 · snapgrep 的发布者账号已校验与本仓库一致
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:待复验
装得上吗[实装验证]—
曾在 dsh 0.2.1-rc.2 下真实安装成功,但当前 dsh 已更新至 0.2.2-rc.1,本站尚未在新版下复验
L4 · 真实安装等级说明 →数据截至 2026/10/24·判据 →
图例:✓ 通过! 需注意— 未判定 / 无数据· 「未判定」= 本站尚无数据,不代表插件有问题
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-tools@deepseek-ai/schemastery给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
界面预览
1 张 · 点击查看大图功能亮点
- 进程内三元组索引
- 代码搜索提速
- 无需额外进程
进程内 trigram 索引,让 Pi&DSH 里的代码搜索比 ripgrep 快 20-70 倍,结果完全一致,还不用额外起进程。
最近更新
在 GitHub 查看全部 →v0.1.02026-08-14
一个进程内 trigram 索引,让 Pi、oh-my-pi 和 DeepSeek Harness 里的代码搜索比 ripgrep 快得多,结果逐字节完全一致。
没有旁路进程,没有守护进程,只有一个原生插件加载在智能体进程内。
· 安装
curl -fsSL | sh
自动识别你的平台,只下载这台机器能跑的那个插件(约 1.2 MB),装进 ~/.pi/agent/extensions。不编译任何东西,也不启动守护进程。
想手动装,或者想让每台机器都备一份?snapgrep-extension-all-platforms.tar.gz 打包了全部五个插件,加载时自动挑对的那个:
curl -L | tar xz
mkdir -p ~/.pi/agent/extensions && mv pi-fast-grep ~/.pi/agent/extensions/
oh-my-pi 用同一个目录即可,不用改。DeepSeek Harness 见安装指南,它是以插件形式挂载,而不是复制目录。
· 平台
| 平台 | 插件 |
| --- | --- |
| macOS Apple Silicon | pi-fast-grep-kernel.darwin-arm64.node |
| macOS Intel | pi-fast-grep-kernel.darwin-x64.node |
| Linux x64(glibc) | pi-fast-grep-kernel.linux-x64-gnu.node |
| Linux arm64(glibc) | pi-fast-grep-kernel.linux-arm64-gnu.node |
| Windows x64 | pi-fast-grep-kernel.win32-x64-msvc.node |
下载整个压缩包,或者自己搭目录时只下单个 .node。Alpine/musl 还没构建。
· 预期效果
提速来自不读文件,所以快多少取决于 ripgrep 本来要打开多少个文件。在 Linux 单核、20 MB / 5,001 个文件的仓库上实测:
| 场景 | snapgrep | ripgrep | 提速 |
| --- | ---: | ---: | ---: |
| 每个文件都命中 | 6.98 ms | 18.5 ms | 2.6× |
| 300 个文件命中 | 0.22 ms | 14.1 ms | 63.5× |
| 1 个文件命中 | 0.006 ms | 14.2 ms | 2458× |
全都命中时没什么可跳过的,2.6× 就是索引能赢到的极限。智能体真实搜索大多落在中间——在几十到几百个文件里找一个符号名。
· 正确性
每条结果都在同一快照、同一组参数下和 ripgrep 比对:missing = 0 && extra = 0,顺序、字节偏移、上下文行、截断也都一致。24 条基准查询全部精确匹配。
索引搞不定的查询会退回完整 ripgrep 搜索,而不是给个残缺答案,并在结果元数据里说明原因。不是干净 Git 树的工作区同样退回:结果正确,只是没加速。
· 已知限制
· Windows 插件已构建,但还没纳入原生测试套件。路径里含 8.3 短名(C:\Users\RUNNER~1\...)时,内核打不开索引父目录。macOS 和 Linux 在构建它们的平台上跑完整原生套件。
· 24 条基准查询里有 4 条退回 ripgrep:它们依赖 ripgrep 的二进制文件和 NUL 字节输出语义,目前还没复刻。
· 不产出 Alpine/musl 构建。
数据来自仓库的 GitHub Release,可能与插件实际版本不一致。
README
由 DeepSeek 最新模型翻译生成snapgrep
一个进程内三元组索引,让 Pi 里的代码搜索在热索引上通常比 ripgrep 快 40–70 倍——而且每条结果都逐字节对照 ripgrep 校验过。
没有 sidecar 进程。没有守护进程。只有一个 3.2 MB 的原生插件,加载在智能体进程内部。
安装
Pi
npm install -g snapgrep
Pi 内置的 grep 会被自动替换,每个项目都生效。想确认的话,让 Pi 搜索一个你知道确实存在的字符串——工具详情里会显示 actualBackend: kernel。
oh-my-pi (omp)
npm install -g snapgrep
同一个包,不用改任何东西:omp 把 @earendil-works/pi-coding-agent 当作别名作用域,它的加载器同时接受 .pi 和 .omp 目录。
DeepSeek Harness (dsh)
dsh plugin --profile headless add snapgrep
用 dsh --profile headless --dump-config | grep snapgrep 验证。
它会同时替换 grep 和 glob:注册表会直接拒绝重名的工具,所以内置的搜索那一行被禁用,由这个插件提供两者。
展开完整文档(README 剩余 1 节)
snapgrep
一个进程内三元组索引,让 Pi 里的代码搜索在热索引上通常比 ripgrep 快 40–70 倍——而且每条结果都逐字节对照 ripgrep 校验过。
没有 sidecar 进程。没有守护进程。只有一个 3.2 MB 的原生插件,加载在智能体进程内部。
安装
Pi
npm install -g snapgrep
Pi 内置的 grep 会被自动替换,每个项目都生效。想确认的话,让 Pi 搜索一个你知道确实存在的字符串——工具详情里会显示 actualBackend: kernel。
oh-my-pi (omp)
npm install -g snapgrep
同一个包,不用改任何东西:omp 把 @earendil-works/pi-coding-agent 当作别名作用域,它的加载器同时接受 .pi 和 .omp 目录。
DeepSeek Harness (dsh)
dsh plugin --profile headless add snapgrep
用 dsh --profile headless --dump-config | grep snapgrep 验证。
它会同时替换 grep 和 glob:注册表会直接拒绝重名的工具,所以内置的搜索那一行被禁用,由这个插件提供两者。glob 跑的是内置工具原本用的那条 ripgrep 命令。
只会下载你的机器能跑的那个插件——大约 1.2 MB,两个包。不编译任何东西,也不启动守护进程。
预编译版本覆盖 macOS(Apple Silicon 和 Intel)、Linux(x64 和 arm64,glibc)以及 Windows x64。Alpine/musl 还没构建;在不支持的平台上,扩展会直接说出它找不到的那个文件名,而不是悄悄失败。
其他安装方式 —— 不用 npm、只装单个项目,或者一份拷贝给所有机器用
不用 npm 的话,有个脚本会从 latest release 拉取匹配的压缩包,装到 ~/.pi/agent/extensions:
curl -fsSL https://raw.githubusercontent.com/Owen718/snapgrep/main/install.sh | sh
设置 PI_EXTENSIONS_DIR 可以装到别处。想要一个在任何机器上都能用的目录,同一个 release 里的 snapgrep-extension-all-platforms.tar.gz 包含全部五个插件,加载时自动挑对的那个。
装进单个项目而不是全局:
git clone https://github.com/Owen718/snapgrep.git
mkdir -p /path/to/your-project/.pi/extensions
cp -R snapgrep/artifacts/pi-extension/pi-fast-grep /path/to/your-project/.pi/extensions/
cd /path/to/your-project
pi --approve
--approve 只有第一次需要,用来信任项目级扩展。这个 artifact 自带一个只作用于自身目录的 .gitignore,所以装它不会弄脏你的仓库。
从源码构建:npm run build:kernel && npm run package:extension。
中文安装说明见 安装说明.md。
为什么会有这个项目
编程智能体在不停地搜索。在大仓库里,每次 grep 调用都意味着 ripgrep 要把磁盘上的每个字节再读一遍——几百毫秒,每分钟好几次,没完没了。
用索引支撑搜索能解决这个问题,但通常的做法是配一个旁路守护进程(Zoekt,以及所有基于它的东西):又多一个进程要启动、要守护、要同步,跑偏了还得调试。对一个频繁启停的命令行智能体来说,这套机器太重了。
snapgrep 把整个索引放进智能体自己的进程里。Rust 在 git 快照上构建并查询三元组索引;Node 通过 N-API 调用它。从启动到第一次查询大约半秒,索引比它索引的代码还小。
实测结果
所有数字都是 P50,取 3 次预热后的 7 次实测迭代,kernel 和 ripgrep 在同一轮里交替运行,使用同一份仓库快照、相同的查询参数和输出上限。
对比 ripgrep
| 查询 | snapgrep | ripgrep | 加速比 |
| 罕见 token,4,000 个文件里 1 处匹配 | 0.120 ms | 230.8 ms | 1921× |
| 转义正则,1,200 个文件匹配 | 3.188 ms | 220.2 ms | 69× |
| 17 MB 仓库里的 createServer | 2.065 ms | 147.7 ms | 72× |
| defineConfig,186 个候选文件 | 2.574 ms | 142.9 ms | 56× |
| import\.meta,538 个候选文件 | 4.179 ms | 156.5 ms | 37× |
| 带路径过滤的搜索 | 0.973 ms | 8.0 ms | 8× |
加速比消失的地方
加速来自不读文件,所以它取决于 ripgrep 本来要打开多少文件。把这个数字推到极限,优势就没了。以下是一次独立运行的结果,Linux,单核,20 MB / 5,001 个文件的仓库:
| 场景 | snapgrep | ripgrep | 加速比 |
| 每个文件都匹配(5,001 个文件) | 6.98 ms | 18.5 ms | 2.6× |
| 300 个文件匹配 | 0.22 ms | 14.1 ms | 63.5× |
| 1 个文件匹配(罕见 token) | 0.006 ms | 14.2 ms | 2458× |
每个文件都匹配时,索引没有东西可跳过,2.6× 就是它能赢的全部。 在 JavaScript 仓库里查 function 这类查询,就按这个数字做规划。四位数的那些是另一个极端:ripgrep 必须扫完整棵树才能证明某个 token 只出现一次,而索引直接从倒排列表给出答案。
真实智能体的搜索大多落在中间——某个符号名出现在几十到几百个文件里。
对比 Zoekt
Zoekt 是索引支撑引擎的参照实现。snapgrep 从索引里直接回答的每个查询,至少快一倍,都是同一轮里对 Zoekt 测出来的:
| 查询 | snapgrep | Zoekt | 比值 |
| Glob 过滤 .yaml,800 个文件 | 1.533 ms | 70.4 ms | 0.022 |
| 大小写不敏感,500 个文件 | 3.003 ms | 51.8 ms | 0.058 |
| Glob 过滤 .ts | 3.053 ms | 14.8 ms | 0.206 |
| 大小写不敏感 defineConfig | 4.806 ms | 13.8 ms | 0.347 |
Zoekt 每次查询的开销,大头都在 HTTP 传输、JSON 解码,以及跨进程边界做二次校验。去掉进程边界,这三项就都没了。
资源占用
| | 合成语料 17.0 MB | 真实仓库 17.4 MB |
| 索引大小 | 6.5 MB(源码的 0.38×) | 15.9 MB(源码的 0.91×) |
| 冷启动到首次查询 | 662 ms | 508 ms |
| 常驻进程数 | 0 | 0 |
为什么要自己写内核
不是因为 trigram 算法有多新。它并不新——Zoekt 在这方面已经做得很好了,这个项目每接受一处改动,都会拿 Zoekt 做对照测量。
原因在于:买下索引,就等于买下它的进程边界,而边界的开销比搜索本身还大。下面是 Zoekt 在 vite 语料上最慢的那次查询,18.32 ms 究竟花在了哪里:
| 阶段 | 耗时 | 占比 |
| Zoekt 自身的搜索 | 1.24 ms | 7% |
| HTTP 传输 + JSON 解码 | 3.58 ms | 20% |
| 校验(主要是 rg 进程启动) | 8.34 ms | 45% |
| 本地合并与分类 | 5.14 ms | 28% |
搜索本身只占账单的 7%。 剩下 93% 都是索引住在别处的代价:序列化查询、穿过 socket、解码 JSON,再拉起 ripgrep 重新读一遍索引早就装进内存的文件。在这台机器上,光是启动一次 ripgrep 就有约 6 ms 的下限——这一项就超过了整个搜索。
自己掌握内核,才能让这 93% 消失:
- 索引直接映射进智能体自己的内存。 没有 socket,没有 JSON,没有序列化。一次查询就是一次函数调用。
- 校验读的是 mmap 后的索引,不是磁盘。 候选结果在进程内、对着已经驻留的字节确认,所以既不启动进程,也不二次读盘。
- 不支持的查询可以明确失败,而不是近似糊弄。 这一条不是性能理由,也正是通用库不够用的原因。早先的尝试用现成文件查找器做候选筛选;在 Linux 内核源码树上,它返回 17,767 个文件,而 ripgrep 找到 17,772 个。少了 5 个文件,来自嵌套的 .gitignore 重新包含规则——而最后再跑一遍 ripgrep 只能删掉多余结果,永远找不回候选阶段已经丢掉的那些。当召回边界是别人的实现细节时,你既无法证明它,也无法修它。
- 索引格式针对这类数据形态调过——压缩内容块、delta-varint 的 gram 元数据——索引因此只有所索引源码的 0.38–0.91×。
这个取舍是真实存在的,也值得说清楚:每个平台都要构建和分发一个 .node,而且 ripgrep 的每一种行为都得重新验证,不能直接继承。二十四个基准查询里,目前有二十个由索引直接服务;另外四个撞上了 ripgrep 对二进制文件和 NUL 字节的输出语义,这些还没复刻,所以它们走回退,而不是靠猜。
正确性优先
每个结果集都会和 ripgrep 在相同快照、相同参数下的结果做比对。达标线是 missing = 0 && extra = 0,而且顺序、字节偏移、上下文行、截断行为都要完全一致。
全部 24 条一致性查询都和 ripgrep 完全匹配。 其中 20 条由索引直接作答,另外 4 条回退到 ripgrep,比对覆盖这两条路径。每一次被接受的改动都保持了这个结果。
凡是索引无法正确处理的查询,都会回退到完整的 ripgrep 搜索,而不是返回部分答案。回退会在结果元数据里带上原因,所以不支持的查询绝不会看起来像空结果。
目前会回退的情况:
- 二进制文件,且适用 ripgrep 的 NUL 字节输出语义
- 正则与路径或 glob 过滤条件组合
- 字面量短于 3 字节,或跨换行
- 大小写不敏感搜索,且模式含非 ASCII 字符
- 超出已验证子集的 glob 语法(!、?、[]、{})
这些是正确性边界,不是缺失的功能。每一条都是尚未证明能完全复现 ripgrep 行为的情况,所以索引拒绝猜测。
保持新鲜
索引只有反映你刚编辑的内容才有用。
任何可能修改文件的工具运行之前,正在进行的搜索会先排空,索引随即失效。工具结束后,索引从当前工作树重建。因此搜索只能观察到完整状态,绝不会看到编辑到一半的残缺状态。
在 300 个文件的仓库上实测恢复时间:从编辑到搜索重新走索引,耗时 41–62 ms。连续两次 edit 调用和一次 bash 调用之后,各跟一次搜索,都回到了索引并和 ripgrep 完全匹配。
重建进行期间,搜索回退到 ripgrep。结果正确,只是没有加速。
工作原理
Pi (Node) snapgrep (Rust, same process)
┌────────────────┐ N-API ┌──────────────────────────────┐
│ grep tool │ ──────────► │ trigram index over a git │
│ │ ◄────────── │ snapshot, mmap'd │
└───────┬────────┘ └──────────────┬───────────────┘
│ │ candidates
│ unsupported query, ▼
│ or index rebuilding ┌────────────────────────┐
▼ │ in-process exact verify│
ripgrep (full search) └────────────────────────┘
1. 把模式拆成 trigram,对它们的倒排列表求交集,得到候选文件。
2. 在候选层面应用 hidden / path / glob / case 过滤,早于任何文件读取。
3. 在 mmap 的索引上做进程内精确校验——不起子进程,也不从磁盘重读。
4. 只物化请求的 limit 和 context 真正需要的那几行。
索引每个仓库一个文件,从干净的 git 快照构建,内容块压缩存储,gram 元数据用 delta-varint 编码。
开发
./scripts/bootstrap.sh # pins dependencies, builds, runs the test suite
npm run check # type check
npm test # unit and integration tests
npm run test:kernel # native tests against the real addon
npm run benchmark # correctness and performance harness
需要 Node 22.19+、Rust,以及 PATH 上有 ripgrep。
最近几轮验收通过的基准测试结果产物在 artifacts/results/,包括正确性运行、同批次 A/B 无回归测量,以及上面引用的交付就绪数据。
方法说明
这个项目踩坑踩出来的两条测量规则,都值得抄走:
永远不要拿早先某次运行记录的 P50 做对比。 同一份没改过的代码,跨会话重新测量会漂移 −31% 到 +9% —— 足以凭空造出一个并不存在的回归,或者把一个真实回归藏起来。每次无回归检查都在同一进程批次里跑两个版本,ABAB 交替,各自带自己的预热。
对一条什么都不干的查询来说,5% 阈值毫无意义。 候选文件为零的搜索,5% 大约就是 4 微秒 —— 低于计时噪声。拿两个字节完全相同的构建互相对跑,就能看出每一类查询各自带多少噪声,阈值正是从这次测量定出来的:接近零工作量的查询用绝对微秒边界,其余一律用相对的 5% 边界。这套校准立刻抓到一个真实的 5 µs 回归,起因是新增了一个逐实例属性,改变了 V8 的对象布局。
License
MIT预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
Owen718/snapgrep
一个进程内三元组索引,让 Pi 里的代码搜索在热索引上通常比 ripgrep 快 40–70…
相关插件 · 搜索 / 抓取
LLM 知识平台Tencent/WeKnora
概览 ·快速开始 ·新特性 ·功能 ·客户端 ·文档 ·开发
★ 36,379已汉化DSH
72
智能体记忆系统MemTensor/MemOS
MemOS 2.0 Stardust星尘
★ 12,247已汉化DSH
71.3
跨代理记忆库zilliztech/memsearch
跨平台语义记忆,给 AI 编程智能体用。
★ 2,823已汉化DSH
70.9
技能语料库EverMind-AI/SkillCorpus
SkillCorpus 能给你什么
★ 696已汉化DSH
69.8
动画花园资源检索yjl9903/AnimeGarden
動漫花園 镜像站 | 动画 BT 资源聚合站 | 动画 BT 资源开放接口
★ 1,393已汉化DSH
69.1
代码图谱记忆syncable-dev/memtrace-public
你的智能体值得拥有结构化记忆。
★ 511已汉化MCP
67.9
这个插件是你维护的吗?
作者本人可认领并核对本条目;认领通过后,详情页会显示「作者已认领」标记。