NeoAnderson
V2EX  ›  Rust

zg 使用 Rust 重写,索引构建耗时减少 65%,峰值内存降低 53%

  •  
  •   NeoAnderson · 3h 39m ago · 226 views

    zg(zvec-grep)把 向量语义检索、BM25 关键词检索和 ripgrep 精确匹配装进了同一个本地命令行工具,同时通过 MCP 提供给 Claude Code、Codex、Cursor 等 AI Agent 使用。0.3.0 版本用 Rust 完整替换了原来的 Node.js 实现:安装包体积减少 79.5%,CLI 启动耗时减少 82.5%,索引构建耗时减少 64.5%,构建期峰值内存降低 53.2%。

    项目地址:https://github.com/zvec-ai/zvec-grep


    一、zg 是什么

    zg(zvec-grep)是阿里巴巴开源的本地检索工具,面向人和 AI Agent,把向量语义检索、BM25 关键词检索和 ripgrep 精确匹配整合到同一个入口。

    zg 演示:安装 Agent 集成、为工作区建立索引,再让 Agent 用 zg 检索

    rg 是很多 Rust 开发者每天都在用的工具。函数名、配置项、报错信息这类明确目标,用 rg 查又快又准,而且能搜全。

    但现在越来越多的检索请求来自 AI Agent,输入也从明确的符号变成了自然语言:“启动时怎么恢复主题偏好?”“用户权限在哪里校验?”。代码里对应的实现可能叫 hydratePreferences,文档里可能写成“账号授权与审批”,和问题本身没有一个相同的词。这种时候只用文本匹配,要么漏掉,要么把范围放得很大,得到一堆没有排序的匹配行。Agent 只能反复换关键词、搜索、读文件,工具调用次数、耗时和 Token 都会上去。

    zg(zvec-grep) 就是为这个问题做的本地检索工具。它保留 rg 精确、快速、可以穷尽匹配的优点,再补上语义发现、相关性排序和上下文组织:

    • 一个入口,多种检索:向量语义检索、BM25、二者混合检索(RRF 融合排序),以及 rg 风格的精确/正则匹配。可以先按语义找方向,再按关键词收窄,最后用正则精确核对。
    • 理解代码结构:借助 tree-sitter 解析 C/C++、Go、Java、JavaScript/TypeScript、Python、Rust 等语言,按符号、签名和层级切分;Markdown 按标题和章节切分;JSON、TOML、YAML、CSV 等文本数据也能参与检索。
    • 结果紧凑、来源可查:默认返回排序后的紧凑预览,每条结果带文件路径和行号,需要时再读全文,避免无关内容塞满 Agent 的上下文。
    • 默认本地运行:文件扫描、Embedding、索引和检索默认都在本机完成。内置多种端侧模型,远程 Embedding 必须经过用户明确授权才会启用。
    • 人和 Agent 共用一份索引:zg 底层用 Zvec 嵌入式向量数据库同时存放向量索引和 BM25 索引,不用另外部署数据库服务。CLI 和 MCP 读写的是同一份索引,并支持增量更新和自动刷新。

    在代码仓库问答 SWE-QA-Bench 和深度研究问答 BrowseComp-Plus 的配对 A/B 评测中(同一 Agent、同一模型、同一 Prompt,只比较是否接入 zg):

    • SWE-QA-Bench:工具调用减少一半以上,输入 Token 减少近一半,评审得分提高 1.50 分;
    • BrowseComp-Plus:准确率从 98.67% 提高到 99.00%,输入 Token 减少 37.56%,工具调用减少 43.52%,Agent 耗时减少 38.58%。

    二、从 Node.js 到 Rust:重写带来了什么

    为什么重写

    zg 的前几个版本(0.2.x)用 TypeScript/Node.js 实现。对一个高频调用的命令行检索工具来说,这个运行时有几个绕不开的成本:

    • 每次单独调用 CLI 都要启动 Node.js 运行时并加载 JS 模块,Agent 连续调用时,这部分时间会累积起来;
    • 部分索引路径要在 JavaScript 普通数组和 TypedArray 之间转换,还要在 worker 线程之间传数据,带来额外的复制和临时内存分配;
    • 安装包要带上整套 JS 依赖和原生插件,体积很大。

    大型工作区和 Agent 工作流对索引耗时、查询延迟和资源占用都很敏感,所以我们用 Rust 重写了整条核心执行链路。

    性能对比

    测试环境:Apple M3 Pro、18 GB 内存、macOS 27.2,Node.js v24.16.0。索引数据集使用 BEIR 中的 SciFact、NFCorpus、ArguAna 和 FiQA 四个公开语料,共 75,128 篇文档。两个版本使用同一个本地 Embedding 模型,都走 CPU 推理,Embedding 并发设为 4。

    指标 Node.js 0.2.2 Rust 0.3.0 改善
    安装包体积(含依赖) 167.5 MiB 34.3 MiB 减少 79.5%
    CLI 启动耗时 153.1 ms 26.8 ms 减少 82.5%
    索引构建总耗时 219.13 s 77.84 s 减少 64.5%
    平均构建峰值内存(RSS) 4,210.26 MiB 1,968.61 MiB 降低 53.2%
    平均检索耗时(MCP) 20.93 ms 14.88 ms 减少 28.9%

    换算成实际体验:四个语料的首次建索引时间从约 3 分 39 秒缩短到约 1 分 18 秒;构建期峰值内存从约 4.11 GiB 降到约 1.92 GiB,而且四组测试每一组都下降了。本机同时开着编辑器、Agent 和其他开发工具时,这部分内存节省很明显。

    性能从哪里来

    • 原生启动:直接运行编译好的原生程序,不再需要初始化 Node.js 运行时和加载 JS 模块。CLI 冷启动从 153 ms 降到 27 ms。
    • 计算路径更直接:文本编码和向量计算在原生代码中完成,使用明确的数值类型和连续内存数组,编译器更容易优化计算循环。
    • 减少中间转换:计算结果一直以原生数值数组的形式存在,不再在 JS 普通数组和 TypedArray 之间来回转换,数据复制和临时分配都少了。
    • 共享资源、统一调度:在支持的本地推理路径上,各计算线程共享已加载的分词器和模型数据,避免重复初始化,任务由原生线程池统一调度。

    三、上手:安装与使用

    1. 安装

    zg 目前通过 npm 分发(后续会支持其他安装方式),运行时直接执行原生程序,不依赖 Node.js。支持 macOS、Linux 和 Windows。

    npm install -g @zvec/zvec-grep --allow-scripts=@zvec/zvec-grep
    zg --version
    

    注:安装依赖 npm 的 postinstall 脚本把原生程序放入合适位置和按需下载 CUDA,所以要加上 --allow-scripts 允许它运行。

    2. 接入 AI Agent(可选)

    # 自动发现本机已安装的 Agent 并配置 MCP
    zg --install
    
    # 也可以指定目标,例如 Claude Code / Codex / OpenCode
    zg --install --target claude --yes
    zg --install --target codex --yes
    

    目前支持的目标包括 claude、codex、opencode、cursor、qwen、qoder、copilot、vscode、grok、pi。配置完成后重新连接 Agent,Agent 会根据问题自己判断什么时候调用 zg。

    3. 建立索引

    cd your-repository
    
    # 默认使用面向代码的轻量本地模型 local/potion-code-16m-v2
    zg --index
    
    # 查看索引覆盖范围、Embedding 模型和存储状态
    zg --status
    

    索引保存在工作区根目录下的 .zvec-grep/。之后文件有变化时只做增量处理,内容没变的文件不会重新计算 Embedding。如果要换模型,用 zg --index --rebuild --embedding <model> 重建索引。

    注:在交互式终端里,即使跳过这一步直接搜索,zg 也会先引导你选模型并建好索引。脚本或 CI 这类非交互环境不会自动建索引,需要先显式执行 zg --index。

    4. 检索

    # 自然语言 / 语义检索(默认混合检索:向量 + BM25,RRF 融合排序)
    zg "where is the connection pool timeout configured"
    zg "用户权限在哪里校验"
    
    # 只走 BM25 关键词检索,或只走向量检索
    zg --fts "AuthService"
    zg --vector "retry with exponential backoff"
    
    # 同时执行多个查询,--fuse 把多组结果合并后统一排序
    zg --hybrid "connection pool" --hybrid "socket timeout" --fuse
    
    # 用 rg 风格做精确/正则匹配(不需要索引)
    zg --rg -n "AuthService" src
    zg --rg -F "fn main(" crates
    
    # 限定范围:文件类型、glob、条数
    zg "error handling for invalid config" -t rust -g 'crates/**' --limit 5
    
    # 调整预览详细程度:none / short(默认)/ full / detail
    zg --preview detail "how indexing jobs are scheduled" --limit 3
    

    5. 示例:用小说测试一下

    mkdir zg-mystery && cd zg-mystery
    curl --retry 3 --retry-all-errors --progress-bar -fL \
      -o alice-in-wonderland.txt https://raw.githubusercontent.com/GITenberg/Alice-s-Adventures-in-Wonderland_11/master/11.txt \
      -o sherlock-holmes.txt https://raw.githubusercontent.com/GITenberg/The-Memoirs-of-Sherlock-Holmes_834/master/834.txt
    
    # 语料是英文小说,换用面向文本检索的本地模型
    zg --index --embedding local/potion-retrieval-32m
    
    zg --preview detail "An unseen creature left a few marks. What did the detective infer?" --limit 3
    

    查询里没有出现 Holmes、weasel、foot-marks 这些原文用词,zg 依然会把《福尔摩斯回忆录》中《驼背人》里根据脚印推断动物的段落排在最前面,并给出具体行号。


    四、写在最后

    zg 0.3.0 是 Rust 版的第一个正式版本。接下来我们会继续推进图检索与重排、PDF/Office 等更多内容格式、更高的上下文效率,以及本地模型和索引的性能优化。

    欢迎 Rust 社区的朋友试用,也欢迎提 Issue、PR,或者分享你在大型代码库、知识库和 Agent 工作流里的使用情况:

    如果觉得有用,欢迎给项目点个 Star。

    No Comments Yet
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   2436 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 37ms · UTC 11:09 · PVG 19:09 · LAX 03:09 · JFK 06:09
    ♥ Do have faith in what you're doing.