关于 vex
vex 是面向开发者和编码 Agent 的本地代码索引与搜索工具。它结合符号查询、文本词项排序和语义搜索,并支持调用关系查询与 AST 模式匹配,基于预建索引返回紧凑结果。
主要功能
- 01结构、BM25 与语义混合搜索
- 02查找符号并提取其代码体
- 03查询调用方、被调用方和实现
- 04匹配 AST 模式
- 05在支持的语言中严格搜索引用
- 06为 Agent 工作流提供精简结果
适用场景
- 01在已索引代码库中快速定位符号
- 02探索代码时追踪调用关系
- 03名称未知时按概念查找代码
- 04在支持的语言中匹配语法模式
MCP 相关
项目包含 vex-mcp 这一 MCP 服务,向 MCP 客户端提供 vex 代码搜索能力。它运行 vex 命令行工具,需将其安装并加入 PATH,或通过 VEX_BIN 指定路径。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
Vex
快速的结构化与语义混合代码搜索。Vector(向量)+ index(索引)。
为什么选择 Vex? · 对比 · 安装 · 快速开始 · 命令 · 配置 · 搜索原理 · 基准测试 · 支持的语言 · 集成 · 测试 · 架构
$ vex check "TelemetryProcessor" # 4ms — does it exist? where? (exact name)
$ vex show "TelemetryProcessor" # extract the class body (not the whole file)
$ vex usages "Config" --strict # who references this symbol? (binder-resolved, no noise)
$ vex callers "process_event" # who calls this function? (~4ms; covers module-scope + Python/Java decorators)
$ vex implementations "BaseService" # who extends/implements this?
$ vex search "timeout retry" # fuzzy / multi-word — BM25 finds rare body terms
$ vex search "handle alert" --semantic # find by meaning, not just name
$ vex pattern 'fn $NAME($$$) -> Result' --lang rust # AST pattern matching (like ast-grep)
$ vex similar "PaymentService" # semantically close symbols
$ vex duplicates --threshold 0.95 # near-duplicate pairs
$ vex bundle --mode symbol --symbol Foo # body + callers + callees + similar in 1 call
选对工具:对于“Foo 是否存在?”,使用 vex check;对于“帮我找一些关于重试的内容”,使用 vex search。search 会进行排序融合——当没有符号完全匹配时,它会显示相邻项(调用者/导入项),这很适合探索,却不适合按确切名称查找。v1.15.0 会在标识符形式的 search 返回 0 个 FST 命中时输出 stderr 提示。
为什么选择 Vex?
- 索引后搜索约需 4–5 ms——基于 FST 的 O(query_len) 查找,而非 O(symbols);无论项目大小如何,耗时都保持不变。需要预先构建索引。索引只需构建一次(典型项目耗时数百毫秒),且构建的内容多于普通文本索引——FST + BM25 + 调用图 + 类型层次结构 + 三元组跳过索引——因此以较慢的构建速度换取成本低得多、内容更丰富的查询(参见基准测试)
- 三通道混合搜索——结构化 FST(名称)+ BM25(正文中的罕见词)+ 语义 HNSW(含义),通过 Reciprocal Rank Fusion 融合。既能在不知道确切名称时找到符号,也能避免仅使用通用语义搜索时产生的大量噪声
- 持久化调用图——
vex callers/vex callees从索引时构建的持久化索引读取(约 4 ms),而非实时扫描 tree-sitter 语法树(耗时数秒):callers是按名称键控的 FST,callees是密集 CSR 索引(v9+)。模块作用域表达式会通过合成的<module:path>调用者报告(阶段 14.1);Python + Java 函数/方法装饰器(阶段 14.2)、Kotlin 注解 + C# 方法/构造函数特性(阶段 14.2.2),以及 TypeScript 方法装饰器 + Rust 外部属性(阶段 14.2.1)都会向目标生成正向边。类级装饰器仍不可见——参见docs/LIMITATIONS.md - 可插拔的嵌入器——
Embeddertrait + 注册表;可将 MiniLM-L6-v2 替换为未来面向代码的模型(BGE、CodeBERT),无需修改调用点 - 节省 Token——紧凑输出在平均查找中通常比 grep 少用 6 倍 Token(压缩后的 JS/CSS 最多可少用 217 倍);
vex show只提取符号正文,而非整个文件 - 支持索引 20 种语言——通过 tree-sitter 实现,分为三个覆盖层级:8 种绑定语言支持类型感知的
--strict usages(Rust / TypeScript / Python / C# / C++ / Go / Java / Kotlin);16 种 T1+T2a 语言支持索引模式预筛选;全部 20 种语言均支持基础结构化 + 语义搜索(支持语言矩阵参见支持的语言) - 单个二进制文件,无需配置——无需 LSP 服务、数据库或 Docker。只需运行
vex index && vex check Foo
Vex 不是什么
vex 是一个静态分析索引工具,不是语言服务器。请如实设定预期:
- 不能替代 LSP。 不支持跳转到第三方包中的定义、重命名重构、类型检查或悬停文档。需要这些功能时,请继续使用 LSP。
vex search返回的是排序融合结果,而非精确名称查找。 结构化 FST、BM25 和语义搜索通过 RRF 融合后,按相关性排序返回结果——如果索引中没有名为Foo的符号(它从依赖项导入、已删除或拼写有误),BM25 可能会将调用方/导入项作为定义返回。对于精确符号问题(“它是否存在?”“显示其函数体”“谁调用了它?”),请使用vex check Foo/vex show Foo/vex usages Foo --strict——这些命令会绕过排序器。v1.15.0 会在类似标识符的查询没有 FST 命中时,向 stderr 打印一行提示。- 无法识别动态分派。 装饰器路由(
@router.get("/path"))、通过字符串解析的工厂函数(uvicorn.run("main:app"))、反射(getattr(obj, name)())和宏展开引用对所有 vex 命令都不可见。vex grep '\bname\b'是文本搜索的替代方案。 vex callers对函数作用域外的支持不一致。 模块级表达式(如app = create_app())会通过合成的<module:path>调用方报告(Phase 14.1)。Python + Java 函数/方法装饰器(Phase 14.2)、Kotlin 注解 + C# 方法/构造函数特性(Phase 14.2.2),以及 TypeScript 方法装饰器 + Rust 函数/方法上的外部属性(Phase 14.2.1)会生成正向边——vex callers GetMapping会列出所有 Spring 处理程序,vex callers HttpGet会列出所有 ASP.NET 操作,vex callers test会列出所有#[tokio::test]。类级装饰器(14.6)仍在路线图中。vex usages的质量因语言而异。 8 种受绑定器支持的语言可通过--strict获得适用于重构的引用;其余 11 种语言使用标识符扫描器,误报率更高。
完整的支持范围矩阵、具体复现示例以及各类查询的建议解决方法,请参阅 docs/LIMITATIONS.md。在 Python/FastAPI/Django 代码库上评估 vex 前,请先阅读该文档——框架模式是目前最常见的缺口。
对比
| vex | ripgrep | ast-index | ast-grep | Serena | |
|---|---|---|---|---|---|
| 搜索内容 | 符号定义 | 所有文本 | 符号定义 | AST 模式 | 符号(通过 LSP) |
| 需要索引吗? | 是(约 0.3-1s) | 否 | 是(构建更快) | 否 | 否 |
| 搜索速度 | 约 4-5ms(预构建 FST,恒定) | 随代码库规模变化(小型约 8ms → 大型 100ms+) | 约 8-12ms(SQLite) | 约 30ms(扫描) | 取决于 LSP |
| 语义搜索 | HNSW + 嵌入 | -- | -- | -- | -- |
| 模式匹配 | fn $NAME($$$) | 仅正则表达式 | -- | fn $NAME($$$) | 仅正则表达式 |
| 索引大小 | 比 ast-index 小约 1.5-2 倍 | 无索引 | SQLite + FTS5 | 无索引 | 无索引 |
| Token 效率 | 比 rg 少 6-217 倍 | 基准 | 比 rg 少约 3 倍 | 不适用 | 不适用 |
| 符号函数体提取 | vex show | -- | -- | -- | -- |
| 语言 | 19 种 | 任意 | 10+ 种 | 10+ 种 | 40+ 种(LSP) |
| 重构 | -- | -- | -- | -- | 重命名、移动、内联 |
| 运行时依赖 | 无 | 无 | 无 | 无 | Python + LSP |
注意:vex 的搜索速度以索引已构建为前提。Ripgrep 和 ast-grep 无需预先建立索引,可立即用于任意目录。两者的取舍会随着使用次数而摊销:如果你会多次搜索同一代码库(这在 Agent 工作流中很常见),一次性索引成本很快就能收回。
最适合:在重视 Token 效率的 AI Agent 工作流中快速搜索符号。它不能替代基于 LSP 的工具(不支持重构,也不能跳转到依赖项中的定义)。
安装
# Install script (macOS arm64, Linux x86_64): vex + vex-mcp into ~/.local/bin
curl -fsSL https://raw.githubusercontent.com/tenatarika/vex/main/install.sh | sh
# Windows (PowerShell): vex + vex-mcp into %LOCALAPPDATA%\Programs\vex, added to PATH
irm https://raw.githubusercontent.com/tenatarika/vex/main/install.ps1 | iex
# Homebrew (macOS/Linux)
brew tap tenatarika/tap
brew install vex
# cargo-binstall: prebuilt release binaries, no compiler needed
cargo binstall vex-search # → ~/.cargo/bin/vex
cargo binstall vex-search-mcp # → ~/.cargo/bin/vex-mcp (MCP server)
# crates.io (compiles from source; the crate is `vex-search`, the binary is `vex`)
cargo install vex-search --locked # → ~/.cargo/bin/vex
cargo install vex-search-mcp --locked # → ~/.cargo/bin/vex-mcp (MCP server)
# From source (any platform with a Rust toolchain)
git clone https://github.com/tenatarika/vex.git
cd vex
cargo build --release
cp target/release/vex ~/.local/bin/
安装脚本接受 VEX_VERSION=1.28.0 来固定版本,接受 VEX_INSTALL_DIR 来指定其他安装位置,并接受 VEX_NO_MCP=1 来跳过 vex-mcp。在其他平台、musl 或 glibc 版本低于 2.34 的系统上,脚本会停止运行并提示从源码构建。cargo binstall 会在有对应版本时使用相同的发行版压缩包,其他情况下则从源码构建;在 Windows 上,它会安装不含压缩包中 DirectML.dll 的 vex.exe,因此 GPU 索引会退回到 CPU。
运行 cargo install vex-search(以及任何源码构建)所需条件:
- 构建时需要网络连接:构建过程会下载预构建的 ONNX Runtime。预构建版本仅适用于
aarch64-apple-darwin、x86_64/aarch64-unknown-linux-gnu和x86_64/aarch64-pc-windows-msvc;在其他目标平台(Intel macOS、musl 等)上,请将ORT_LIB_LOCATION指向本地构建的 ONNX Runtime。 - C/C++ 工具链(Xcode Command Line Tools、
build-essential或 MSVC Build Tools),用于构建 tree-sitter 语法。 GCC 必须为 12 或更高版本:GCC 11(Ubuntu 22.04 的默认版本)无法编译捆绑的 numkong 向量内核。请安装gcc-12 g++-12,并使用CC=gcc-12 CXX=g++-12 cargo install vex-search --locked进行构建。 - Linux:
libssl-dev和pkg-config(Fedora:openssl-devel)。HTTP 技术栈通过native-tls链接 OpenSSL。 - 首次运行
vex index --semantic时会下载约 86 MB 的嵌入模型;结构搜索无需下载。 vex-search-mcp只会安装 MCP 服务。它会运行vexCLI,因此也要安装vex-search并确保vex位于PATH中,或者将VEX_BIN设为其完整路径。
Linux
每个 GitHub Release 都提供适用于 x86_64-unknown-linux-gnu 的预构建 vex。它需要 glibc 2.34 或更高版本(Ubuntu 22.04+、Debian 12+、RHEL / Rocky / AlmaLinux 9+);在较旧的系统上,请从源码构建:
curl -fsSL https://raw.githubusercontent.com/tenatarika/vex/main/install.sh | sh
# or by hand:
curl -L https://github.com/tenatarika/vex/releases/latest/download/vex-x86_64-unknown-linux-gnu.tar.gz | tar -xz
mv vex ~/.local/bin/ # or: sudo mv vex /usr/local/bin/
vex --version
此版本在 ubuntu-22.04 GitHub runner 上构建(链接 glibc,且已链接 libstdc++)。对于使用较旧 glibc 的发行版、基于 musl 的发行版(Alpine、未配置 nix-ld 的 NixOS),或 aarch64 Linux(Graviton、Pi 5、Ampere),请通过 cargo build --release 从源码构建。
Windows
每个 GitHub Release 都提供预构建的 vex.exe。通过 PowerShell 安装的最快方法:
irm https://raw.githubusercontent.com/tenatarika/vex/main/install.ps1 | iex
它会将 vex.exe、vex-mcp.exe 和 DirectML.dll 放入 %LOCALAPPDATA%\Programs\vex(每用户文件夹),并将其添加到用户的 PATH。VEX_VERSION、VEX_INSTALL_DIR 和 VEX_NO_MCP=1 的用法与 install.sh 相同。也可以手动安装:
- 从最新发行版下载
vex-x86_64-pc-windows-msvc.tar.gz - 将
vex.exe解压到稳定的位置(例如C:\Users\<you>\bin\)——可在较新的 PowerShell 中运行tar -xzf vex-x86_64-pc-windows-msvc.tar.gz,也可右键使用 7-Zip / WinRAR。安全提示:vex.exe会从自身所在文件夹加载捆绑的DirectML.dll,因此在多用户计算机或共享驱动器上,建议使用其他用户无法写入的目录(例如C:\Program Files\vex\——但这样一来,运行vex self-update时需要提升权限的 shell)。请参阅 GPU_SUPPORT.md §6。 - 将该文件夹添加到
PATH(系统属性 → 环境变量 → 编辑Path→ 添加该文件夹) - 打开新的终端并运行
vex --version
要更新,请运行 vex self-update——它会获取最新发行版,为你的平台选择合适的压缩包、验证其签名,并就地替换二进制文件。在 Windows 上,它还会安装/刷新捆绑的 DirectML.dll 旁置文件(文件内容相同时会跳过;如果旧版自更新程序删除了该文件,则会重新安装——直到 v1.16.0 的更新程序只会解压二进制文件)。macOS 和 Linux 也可以使用同一命令。
预构建二进制文件已内置 GPU 加速——Windows 使用 DirectML(适用于任何 DX12 GPU,仅需驱动;压缩包中捆绑了 redistributable
DirectML.dll),macOS arm64 使用 CoreML。NVIDIA CUDA 需要通过源码构建选择启用。运行vex gpu进行检查,并参阅 GPU 加速。
快速开始
# Index a project (structural only — fast)
vex index --path /path/to/project
# Index with semantic embeddings (slower first time, downloads 86 MB model)
vex index --path /path/to/project --semantic
# Exact-name lookup (does this symbol exist?)
vex check "PaymentService"
# Extract a symbol's body (no whole-file read)
vex show "PaymentService"
# Fuzzy / multi-word search (returns ranked neighbors when no symbol matches)
vex search "payment processing" --semantic
# Find all usages of a symbol (--strict drops string-literal / comment / wrong-scope noise)
vex usages "IndexReader" --strict
# File structure outline
vex outline src/main.rs
# Find implementations of a trait/interface
vex implementations "Iterator"
# Callgraph: who calls / is called by a function (fast path via persistent index)
vex callers "process_event"
vex callees "process_event"
# Multi-hop call graph (v1.7)
vex paths "main" "process_event" # all caller chains from main → process_event
vex reachable "process_event" # everything that transitively reaches it
vex tests-for "process_event" # tests covering process_event (path globs + name heuristic; framework label per row)
# Symbol-level diff against a branch (v1.7)
vex diff --base main # what symbols did this branch change?
# Historical view of a symbol — every commit that touched it (v1.15.0; v1.16.0 expanded)
vex index --history # build the persistent history sidecar once
vex history "PaymentService" # ~10ms — every version reachable from HEAD
vex history "PaymentService" --diff # unified diffs between consecutive versions
vex history "Foo" --since 2026-01-01 --author alice --kind function
vex history "deleted_symbol" --exact-presence # exact commit set where each blob lived (revert-aware)
# Semantic similarity by existing symbol — explain what's actually similar (v1.7)
vex similar "PaymentService" --limit 5 --min-score 0.7 --explain
# Near-duplicate pairs with reasoning (v1.7)
vex duplicates --min-score 0.95 --min-body-lines 5 --explain
# Search with per-call scope + metadata filters (v1.7)
vex search "Repository" --include 'src/**' --exclude '**/*.gen.*' --visibility public --async-only
# Why did the search return these results? (v1.7)
vex search "Foo" --why 2>trace.json
# Bundle: 4 round-trips → 1 envelope (v1.9, Phase 13.2)
vex bundle --mode symbol --symbol PaymentService # body + callers + callees + similar
vex bundle --mode pr-impact --base origin/main # changed symbols + transitive callers + tests
vex bundle --mode project --top-n 30 # top-N by reverse call-graph indegree
# Diff-context filters on every search-shaped command (v1.9, Phase 13.7-D3)
vex search "Repository" --since-branched # only files changed since branching from main
vex usages "Config" --since HEAD~3 # refs within the last 3 commits
vex callers "Foo" --changed-only # working-tree changes only
# Extract just a symbol's body — replaces Read for a specific function/class
vex show "PaymentService" # full body of the class / fn
vex show "Foo" "Bar" "Baz" # multiple symbols in one call
# Smart show truncation for token efficiency (v1.9, Phase 13.3)
vex show "BigClass" --signature-only # just the signature line
vex show "PaymentService" --head 20 # first 20 lines of the body
vex show "Foo" --no-body # signature + docstring, no body
# Ranking-eval harness — CI regression guard (v1.9, Phase 13.12)
vex eval --bench benches/ranking_golden/queries.toml # nDCG@10 / recall@10 / MRR per query
vex eval --min-ndcg 0.85 # fail if mean nDCG drops below threshold
# Capability discovery for MCP clients (v1.9, Phase 13.0)
vex capabilities # JSON: protocol_version, signals, bundle_modes, …
# Fast existence check
vex check "Foo" "Bar" "Baz"
# Incremental update (re-parses only changed files, reuses unchanged from index)
vex update
# Watch mode (re-indexes on file changes)
vex watch
# Multi-repo: treat a set of sibling repos as one workspace (v1.22.0)
vex index --workspace # build every member of .vex-workspace.toml
vex search "RetryPolicy" --workspace # fan out, results grouped by repo
vex usages Config --strict --workspace # cross-repo strict refs (v7+ index)
vex watch --workspace # keep every member incrementally fresh
# Show index stats
vex status
# GPU doctor — is the compiled EP actually engaging on this machine? (v1.16.0)
vex gpu # probes the compiled-in EP with strict registration
vex gpu cuda # narrow to one EP
vex gpu --enable # persist working device to VEX_DEVICE
# Shell completions
vex completions zsh > ~/.zfunc/_vex
命令
| 命令 | 描述 |
|---|---|
vex index [--path .] [--semantic] [--embedder ID] [--history [--history-depth N]] [--no-clusters] [--no-pattern-index] [--drop-semantic] [--gpu/--device] | 构建完整索引。--semantic 生成嵌入向量 + HNSW + BM25。--embedder 选择嵌入模型(默认值为 minilm-l6-v2)。--no-clusters 跳过符号聚类(v9)。--no-pattern-index 跳过模式骨架部分(v6)。--drop-semantic(与 --no-semantic 一起使用)删除磁盘上的语义构件(HNSW + 哈希索引 + 嵌入模型缓存)。--gpu/--device 控制 GPU 加速(支持 GPU 的构建版本)。--history(v1.15.0) 构建 Phase 14.8 持久化历史符号部分(<index_dir>/index.git_history),使 vex history <Symbol> 能以 FST 查找的速度运行。--history-depth N 将遍历限制为最新的 N 次提交(全局限制,而非逐文件限制)。 |
vex search <query> [--semantic] [--no-bm25] [--limit N] [--kind def,fn,…] [--visibility V] [--async-only] [--code-only] [--exclude-generated] [--why] | 混合搜索:结构化搜索 + BM25 + 语义搜索(启用 --semantic 时)。采用三路 RRF 融合。--kind 支持多个值(规范名称 + 元选择器 def/comment/test/ref)。元数据后置过滤器可按签名关键词缩小范围。v1.20.0(D4):每个结果的 signals 块现在会在排名序号旁提供原始 bm25_score + semantic_cosine,让 Agent 能读取绝对相关性质量;语义通道未运行时,_meta.vex.dev/semantic_channel 会报告 "not_requested" / "index_lacks_vectors";面向代码意图的查询中,--code-only 会排除 *.md/*.markdown/*.txt/*.rst/*.adoc 中的命中;--exclude-generated 会排除机器生成的文件(protobuf 存根、sqlc 输出、bindgen 绑定、ORM 架构),根据生成器的头部横幅识别——适用于将生成代码纳入版本控制的仓库;该启发式会倾向于漏报,而不是隐藏手写代码(参见 docs/LIMITATIONS.md §10.2)。--why 会将 JSON 跟踪信息附加到 stderr。v1.15.0 搜索偏移提示:当查询形似标识符(compile_query、Foo、_internal)且结构化 FST 找不到任何匹配项时,vex 会在 stderr 打印一行提示,指向 vex check / vex show / vex usages --strict——这通常是“从依赖项导入”的情况,否则 BM25 可能会将调用方显示成定义。参见 docs/COOKBOOK.md 常见问题。 |
vex show <symbol> [--limit N] [--context N] [--kind fn] [--visibility V] [--async-only] [--signature-only | --head N | --no-body] | 从源代码中提取符号主体(相比读取完整文件可节省 token)。支持与 search 相同的元数据和类型过滤器。智能截断选项(自 v1.9 起)——--signature-only 仅保留声明行,--head N 保留主体的前 N 行,--no-body 仅返回签名 + 文档字符串。选项互斥。 |
vex similar <name> [--limit N] [--min-score T] [--explain] | 查找与现有符号语义上接近的符号(HNSW 最近邻)。--explain 会为每个匹配项添加标识符 Jaccard 相似度 + 截断后的统一差异。--min-score 是 --threshold 的别名。 |
vex duplicates [--min-score T] [--min-body-lines N] [--explain] | 根据嵌入相似度列出近似重复的符号对。--explain 会显示主体的实际差异。 |
vex usages <name> [--limit N] [--strict] [--include-self] [--include-docs] | 查找符号的所有引用/用法。非严格模式使用 FST 查找;v1.20.0 默认会剔除符号自身定义行中的匹配,以及 *.md/*.markdown/*.txt/*.rst/*.adoc 中的匹配——使用 --include-self / --include-docs 可恢复 v1.20 之前的宽泛搜索行为。--strict 会从 v5 的 reference_edges 部分读取由绑定器解析的引用(Rust / TypeScript / Python / C# / C++ / Go / Java / Kotlin)。 |
vex impact <name> [--depth N] [--exclude-docs] | 一次调用即可生成删除安全性影响范围报告(自 v1.20.0 起)。整合四个引用通道——严格引用(绑定器解析)、FST 引用、grep \b<Name>\b 和直接调用图调用方——给出单一判定(safe / unsafe / uncertain)及每个通道的证据样本。在提议删除或重命名符号之前使用;一次调用即可替代手动执行 usages→grep→callers 的步骤。判定规则:严格引用或调用图调用方报告数量 >0 时为 unsafe(绑定器/图确认存在真实用法);只有文本通道命中时为 uncertain(可能是字符串分派 / 注释 / 装饰器);所有通道均返回零时才为 safe。--depth N(1..16)向后遍历调用图,以显示深度 ≥ 2 的间接调用方;--exclude-docs 会排除文档格式中的提及(*.md/*.txt/…),因此仅出现在 CHANGELOG 中的符号会变为 safe。 |
vex pattern '<pat>' --lang <lang> [--why] | 使用元变量($NAME、$_、$$$,以及 v6 命名的多行形式 $$$BODY / $$ARGS)进行 AST 模式匹配。重复的元变量会强制执行反向引用。以空格分隔的 && / ` |
vex outline <file> [--kind fn] | 显示文件结构,可按符号类型筛选。 |
vex implementations <name> | 查找扩展或实现基类、trait 或接口的类型(包括带泛型参数的情况:class Foo : Repository<T>)。基于索引(v8 层级部分)——通过 find_hierarchy_edges_by_symbol FST 和二分查找实现,仅当索引缺少该部分时才回退到原有的实时 tree-sitter 遍历。基准测试(benches/hierarchy.rs,150 个实现者):基于索引约 265 ns,实时遍历约 22.7 ms——快约 85,000 倍。 |
vex subtypes <name> [--depth N] | 沿 extends/implements 边进行传递式向下闭包(直接子级、孙级等),并为每行标注 BFS 跳数。遍历时排除 Uses(trait/mixin 组合)——混入某个 trait 并不意味着你是该 trait 所组合的一切的子类型。仅使用索引,不回退到实时遍历(需要包含 v8 层级部分的索引)。基准测试:遍历 20 跳传递链约需 1.1 µs。 |
vex modules [SYMBOL] [--min-size N] [--members N] [--sort size|cohesion] | 实际模块:由相互调用/引用的符号构成的集群,在运行 vex index 时计算(基于调用 + 引用 + 层级边的确定性 Leiden-CPM 算法;需要 v9 索引)。不带 SYMBOL 时,列出集群及其标签(占主导地位的路径前缀)、大小、内聚度和枢纽符号;带 SYMBOL 时,显示该符号所属的集群及其成员。--include/--exclude/--exclude-tests 可限定成员范围。如果索引中没有集群(v9 之前的索引、--no-clusters),则会以 empty_reason 退出,状态码为 1。隐藏别名:vex clusters。参见符号集群。 |
vex callers <name> | 查找函数的直接调用方(通过持久化调用图快速查找;缺少索引时回退到实时 tree-sitter 扫描)。 |
vex callees <name> | 查找函数的直接被调用方(同样使用快速查找)。 |
vex paths <from> <to> [--max-hops N] | 在持久化调用图中枚举从 from 到 to 的所有调用链。使用带环路防护的有界 DFS;--max-hops 默认值为 6。 |
vex reachable <target> [--max-hops N] [--limit N] | 返回其被调用方可到达 target 的符号传递集合,并为每行标注 BFS 深度。用于影响范围分析。 |
vex tests-for <target> [--max-hops N] [--limit N] [--test-pattern <glob>] [--include-fixtures] | 查找通过传递调用覆盖 <target> 的测试函数。在 vex reachable 结果上执行后置过滤:向后遍历调用图,保留位于已识别测试路径 glob 下的行(Rust / Python / TS-JS / Go / Java / Kotlin / C# / C++),并为每行标注 framework(pytest、jest、go-test 等),以便 Agent 选择正确的运行器。可重复使用的 --test-pattern <glob> 会替换默认集合;--include-fixtures 会放宽名称前缀过滤条件,并额外允许测试路径辅助函数向前跳一跳。 |
vex diff --base <rev> [--limit N] | 比较任意 git 修订版本与工作树,提供符号级差异:新增 / 删除 / 文件内移动 / 主体变更。采用 git diff --no-renames 语义,因此 git mv 会显示移动的两端。 |
vex bundle --mode <symbol|pr-impact|project> [...] | 统一的多来源汇总(自 v1.9 起)——用一次调用替代 4 次往返(show → callers → callees → similar)。--mode symbol --symbol Foo 返回主体 + 调用方 + 被调用方 + 语义相似项。--mode pr-impact --base origin/main 返回变更符号 + 传递调用方(默认深度为 2)+ 测试。--mode project [--top-n 30] 按反向调用图入度返回前 N 项(实验性——参见 docs/MCP-SCHEMA.md#bundle-modes-v19,了解响应形状和各模式对应的 mode_hints 键)。始终输出 v1 信封 { protocol_version, capabilities, _meta, results }。 |
vex check <name> [name...] | 快速检查索引中是否存在指定符号。 |
vex grep <pattern> [--filter-path path/] | 正则内容搜索(无需索引)。 |
vex update [--path .] [--semantic] [--embedder ID] [--history | --no-history] | 增量更新——仅重新解析有变更的文件,并从现有索引中复用未变更的符号。--history(v1.15.0) 通过清单保持启用:如果先前构建包含历史部分,vex update 会通过三分支遍历器保持其最新状态(无新提交时快速跳过;线性历史时增量更新;强制推送时完整重建)。--no-history 会移除该部分并将清单字段设为 null。 |
vex watch [--path .] [--semantic] [--embedder ID] | 监控文件系统,并在变更时自动重新索引。 |
vex status [--path .] [--coverage] | 显示索引统计信息:符号数量、大小、嵌入向量、调用图、BM25、GPU 支持情况。--coverage 会增加文件覆盖率诊断:按语言统计已索引文件、已发现但未索引的文件(含原因),以及磁盘上缺失的清单条目。 |
vex gpu [device] [--enable] | 诊断 GPU 加速:打印编译进此二进制文件的执行提供程序,并主动探测其是否在此计算机上启用(静默回退到 CPU 时会显示 FAILED 和设置修复说明)。vex gpu cuda 会探测一个 EP;GPU 成功启用时,--enable 会将可用设备持久化到 VEX_DEVICE(Windows 上通过 setx 设置用户环境变量;macOS/Linux 上会打印需要添加的 export 行)。参见 GPU 加速。 |
vex completions <shell> | 生成 shell 补全(bash、zsh、fish)。 |
vex init [--agents-md] [--agents-md-only] | 在当前目录中创建默认的 .vex.toml。--agents-md 还会为 Agent 工具写入 AGENTS.md。--agents-md-only 仅写入 AGENTS.md(跳过 .vex.toml)。 |
vex mcp <install|uninstall|list> --agent <id|all> [--dry-run] [--force] | 管理编码 Agent 配置中的 vex-mcp 服务条目。install 会以幂等方式注册;uninstall 会移除;list 会显示当前条目。--agent all 会分发到所有受支持的 Agent。--dry-run 仅预览,不写入。--force 会覆盖现有条目。 |
vex capabilities | 打印机器可读的能力矩阵(自 v1.9 起):protocol_version、signals、why、scope_filters、metadata_filters、empty_reason、bundle_modes、auto_update、async_update、history_diff、structured_result_kind、result_completeness、symbol_clusters。MCP / Agent 客户端会在启动时探测一次,而无需重新读取帮助文本。 |
vex eval [--bench PATH] [--min-ndcg F] [--json] | 使用人工整理的黄金查询集运行排序评估测试框架(自 v1.9 起);报告每个查询及汇总的 nDCG@10 / recall@10 / MRR。用作 CI 回归保护——当平均 nDCG 低于 --min-ndcg 时失败。默认黄金查询集:benches/ranking_golden/queries.toml。 |
vex history <Symbol> [--depth N] [--limit N] [--branch REV] [--no-index] [--since YYYY-MM-DD] [--until YYYY-MM-DD] [--author SUBSTR] [--kind KIND] [--diff] [--exact-presence] | 新增(v1.15.0);v1.16.0 扩展(Phase 14.9)。 返回从所选分支顶端可追溯的符号所有历史版本。此前运行过 vex index --history 时,查询会使用持久化 FST 旁路文件(约 10 ms——在 tokio 规模的仓库中比遍历器快 1640 倍)。如果没有该部分,则会调用 git log(约需数秒)。索引模式还能找到名称已从 HEAD 中删除的符号——遍历器无法做到。v1.16.0 新增: 日期/作者/类型过滤器(按字典序比较 YYYY-MM-DD);--diff 会渲染连续版本之间的统一差异(仅签名行会改变形状,组首保留完整签名);--exact-presence 会枚举每个条目 blob 所在的确切提交集合(可识别回滚,并受 --exact-presence-max-commits 限制);索引路径上的标识符形查询支持长度 ≥ 3 的前缀 FST 回退;JSON 信封已迁移到标准 ResponseEnvelope 形状(破坏性变更:影响读取 results.items[] 的 MCP 使用方)。完整流水线和使用指南请参见 docs/HISTORY-INDEX.md。 |
vex self-update [--check] [--yes] | 将 vex 更新到最新的 GitHub 发布版本。就地替换正在运行的二进制文件。支持 Linux、macOS 和 Windows。 |
每查询过滤器(适用于所有搜索类命令)
所有搜索类命令都接受范围过滤器。具体过滤器因命令而异:
--include <glob>/--exclude <glob>(可重复,采用 gitignore 语法)——按调用指定路径范围,无需重新索引。--exclude优先于--include。示例:vex search Foo --include 'src/**' --exclude '**/*.gen.*'。--exclude-tests(MCP:exclude_tests: true)会排除测试文件(tests/、*_test.*、test_*.py、*.spec.ts、__tests__/、tests.rs等——与vex tests-for使用的集合相同);它可与--include/--exclude组合使用,会记录在--why跟踪信息中,并由所有接受范围标志的命令应用(search、usages、callers/callees、impact、grep、show、pattern、implementations/subtypes、similar/duplicates、modules、paths、reachable、diff、bundle的pr-impact模式——在该模式下,它会过滤已更改文件以及传递调用者 / 测试行;bundle的symbol/project模式会忽略所有范围过滤器)。vex tests-for不接受--exclude-tests(退出码为 2):它会列出测试函数,因此该标志总会导致没有结果。仅基于路径:非测试文件中#[cfg(test)] mod tests块内的 Rust 单元测试不会被排除。--filter-path <substring>(别名:--filter)——对search、show、usages、grep、similar、duplicates按路径子字符串过滤。可与 glob 过滤器组合,采用 AND 逻辑。
vex search / vex show 还接受:
--visibility <public|private|protected|internal>——仅保留签名中带有显式关键字的符号。不会推断默认可见性(裸 Rustfn foo()不匹配--visibility private)。--async-only/--no-async——仅保留或排除异步 / Kotlinsuspend符号。--static-only、--sealed-only——仅保留静态类成员,或 sealed(或 Javafinal)类型。
推理标志
vex search --why会将 JSON 跟踪信息输出到 stderr(结果列表仍输出到 stdout):normalized_query、各通道命中数(FST / BM25 / semantic)、启用的回退机制(fuzzy)以及当前过滤器快照。vex pattern --why会在结果列表之后将 JSONScanTrace输出到 stderr:mode(indexed/live_scan)、root_kind_inferred、candidate_files/total_files,以及跳过索引预过滤时的fallback_reason(no-index、no-skeleton-section、empty-section、grammar-drift、partial-section、index-open-error)。MCP 调用方可在_meta.why中看到相同的 JSON。vex similar --explain/vex duplicates --explain会添加jaccard重叠分数,以及两个代码主体之间经过截断的统一差异,以便你在采取操作前判断语义聚类出的两个符号是否确为重复项。
多仓库工作区(--workspace)——v1.22.0
在 .vex-workspace.toml 中声明一组同级仓库,然后对任意命令使用 --workspace,即可将操作分发到所有仓库,并按仓库分组:
# .vex-workspace.toml (at the directory that contains the repos)
members = ["./api", "./worker", "./shared-lib"]
vex index --workspace # build every member into its own per-repo index
vex update --workspace # incremental refresh, per-repo changed/deleted counts
vex usages Config --strict --workspace # cross-repo strict refs, grouped by repo
index、update、search、grep、check、usages、impact、callers、callees、reachable、modules和watch均接受--workspace。每个成员保留各自的.vex.toml(排除项 / embedder / sections / 缓存)。- 默认情况下,引用解析和调用图解析均按仓库进行。唯一例外是
vex usages <name> --strict --workspace:它会通过 gtags 风格的名称回退机制,将仓库 B 中的引用解析到仓库 A 中定义的符号(显示为name-resolved子级)。需要 v7+ 索引——升级后请重新运行vex index。 - 如果某个成员缺少所需功能(旧索引不支持
--strict,或reachable所需的调用图),系统会将该仓库报告为不可用,而不会中止整个分发操作。--workspace与--why不能同时使用。请参阅docs/MULTIREPO.md和 LIMITATIONS §7。
配置
在项目根目录中创建 .vex.toml,以自定义 vex 的行为:
vex init # generates .vex.toml with commented defaults
# .vex.toml
# Glob patterns to exclude from indexing (gitignore syntax, on top of .gitignore)
exclude = [
"vendor/**",
"node_modules/**",
"*.generated.go",
]
# Output format — "compact" (default since v1.10.1; single-line records),
# "text" (verbose multi-line), or "json" (envelope for MCP / tools).
# format = "text"
# Enable semantic embeddings by default
semantic = true
# Automatically update index before search if stale
# auto_update = false
# With auto_update, refresh in the background rather than blocking the query:
# answers come from the index on disk, the response is flagged stale, and the
# rebuild lands for the next query. Trades freshness for latency.
# async_update = false
# GPU device for semantic indexing (GPU-enabled builds only). "auto" uses the
# compiled-in GPU EP when it initializes, else CPU; or "cpu"/"cuda"/"directml"/
# "coreml". `gpu = true/false` is shorthand for auto/cpu. See GPU Acceleration.
# device = "auto"
# gpu = true
# Embedder model: minilm-l6-v2 (default), jina-code, bge-base-en-v1.5,
# bge-large-en-v1.5, mxbai-large. Changing it requires a reindex.
# Set globally across projects with the VEX_EMBEDDER env var (this file wins).
# embedder = "minilm-l6-v2"
# VCS backend for diff-scoping (--since/--since-branched/--changed-only).
# "auto" (default) detects .git/.svn/.arc; "git" | "none" | "arc" | "svn".
# git, arc (Yandex Arc), and svn (Subversion) are all functional backends;
# svn declines --since-branched (no merge-base). "none" disables diff-scoping.
# Overridden by the --vcs flag and the $VEX_VCS env var. See docs/VCS-BACKENDS.md.
# vcs = "auto"
CLI 标志始终优先于配置值。若配置启用了语义模式,可使用 --no-semantic 明确禁用。环境变量 VEX_DEVICE 和 VEX_EMBEDDER 会作为所有项目通用的全局默认值(优先级最低,低于 .vex.toml)——请参阅 GPU 加速。
将配置排除在仓库之外
不想在仓库中放置 .vex.toml(无法将其加入 .gitignore、使用共享检出等)?vex 不会自行创建该文件——只有 vex init 会写入它——你有两种方法可以将配置保存在外部:
--config <path>/$VEX_CONFIG— 指定磁盘上任意位置的配置文件。它会完全取代仓库内的查找方式,因此仓库可保持整洁:vex --config ~/vex/this-repo.toml search Foo export VEX_CONFIG=~/vex/this-repo.toml # or set it once per shell--config的优先级高于$VEX_CONFIG;路径不存在或无效时会直接报错(vex 不会静默回退)。该文件中的相对路径会相对于文件所在目录解析。- 父目录 — 配置查找会从项目目录逐级向上,直到文件系统根目录,因此放在任一祖先目录中的
.vex.toml(例如~/work/.vex.toml,或用于全机器默认配置的~/.vex.toml)都会应用于其下的所有仓库,而无需在仓库中放置配置文件。
索引本身绝不会写入仓库——它位于缓存目录中(--cache-dir / $VEX_CACHE_DIR / 平台缓存),因此只需妥善放置配置,即可保持仓库整洁。
过期检测
Vex 会检测索引是否过期,并在搜索前发出警告:
$ vex search "Config"
Warning: index may be stale (HEAD changed). Run `vex update`.
工作原理:每次搜索时,vex 都会比较索引创建时保存的 git HEAD 与当前 HEAD(约 0.1ms,仅执行一次 git rev-parse)。如果 HEAD 已更改,索引即为过期。对于非 git 仓库,则会回退到 mtime 比较;自 v1.11 (H11) 起,当 mtime 检测触发时,vex 会流式计算文件的 xxh3_64 内容哈希,并与清单进行比较。如果哈希匹配,说明这次触碰只是表面变化(git checkout、无操作的 rustfmt、rsync --times),文件仍保持 Fresh;只有内容确实发生变化时,才会再次触发索引。
自动更新:跳过警告并就地更新:
# Per-command
vex search "Config" --auto-update
# Always (in .vex.toml)
auto_update = true
# Disable staleness check entirely
vex search "Config" --no-stale-check
GPU 加速
语义索引(--semantic)可在 GPU 上运行嵌入模型,在完整索引或冷启动索引时能大幅提速。在一台 RTX 3080 上,对包含 2.8 万个符号的 C++ 模块进行测试时,与 CPU 相比,默认 MiniLM 模型的嵌入速度在 CUDA 下快 51 倍,在 DirectML 下快 29 倍(完整基准测试和设计说明见 docs/GPU_SUPPORT.md)。
分为两个层面:二进制文件和设备:
- 预编译二进制文件内置仅需驱动的 GPU EP: Windows → DirectML(适用于任何 DX12 GPU — NVIDIA/AMD/Intel;归档中附带运行库
DirectML.dll),macOS arm64 → CoreML。无需 SDK,也无需额外安装。Linux 预编译版本仅支持 CPU。 - CUDA 需通过源码构建选择启用(在 NVIDIA 上速度最快,比 DirectML 快约 1.75 倍):
cargo install --git https://github.com/tenatarika/vex vex-search --features gpu-cuda。需要将 CUDA 12 运行时和 cuDNN 9 放入PATH(仅有 NVIDIA 驱动还不够 — 它只包含nvcuda.dll,不包含运行时/cuDNN)。其他平台的源码构建选项:--features gpu-coreml/gpu-directml。
选择设备(vex index / vex update):
--gpu/--no-gpu— 强制使用 GPU(自动选择)或 CPU。--device cpu|auto|cuda|directml|coreml— 选择特定执行提供程序。- 默认值为自动:如果已编译的 EP 初始化成功,就使用它;否则静默回退到 CPU。在启用了 CUDA 的二进制文件中,自动模式优先选择 CUDA → DirectML → CoreML;标准 Windows 预编译版本仅编译了 DirectML,因此无论电脑是否支持 CUDA,自动模式都会使用 DirectML。
- 小规模增量
vex update会继续使用 CPU(GPU 预热不值得只为处理几个符号);冷启动或大型--semantic构建会使用 GPU。
GPU 是否真的在使用? 运行 vex gpu — 它会报告已编译的 EP 并主动进行探测,因此静默回退到 CPU 时会显示 FAILED,并提供针对性的设置补救建议。vex gpu cuda 会探测单个 EP;vex gpu --enable 会将可用设备持久化到 VEX_DEVICE(在 Windows 上通过 setx 设置用户环境变量;在 macOS/Linux 上会打印要添加的 export 行)。如果 VEX_DEVICE 设定的 GPU EP 已过时,而后续构建(例如仅支持 CPU 的构建)不包含该 EP,程序会回退到 CPU,而不会报错。
环境变量
| 变量 | 作用 |
|---|---|
VEX_DEVICE | 所有项目的全局默认设备(cpu/auto/cuda/directml/coreml)。优先级低于 --device/--gpu 和 .vex.toml。 |
VEX_EMBEDDER | 全局默认嵌入器 ID(例如 jina-code)。优先级低于 --embedder 和 .vex.toml。未知 ID 会回退到默认嵌入器,并发出警告。 |
VEX_GPU_STRICT=1 | 将 ORT 静默回退到 CPU 的行为改为硬错误 — 用于确认 GPU 是否已启用。(vex gpu 会在内部请求相同的严格模式,不会触及环境变量。) |
VEX_GPU_MEM_LIMIT=<bytes> | 高级选项:限制 GPU arena 的 VRAM 上限。请设置足够大的值(≥ 工作集),否则长上下文批次会因显存不足而失败。 |
VEX_GPU_ATTN_BUDGET=<n> | 高级选项:调整基于长度的批次大小(count × max_len² 预算)。 |
输出格式
# Compact single-line records — default since v1.10.1 (token-efficient, agent-friendly)
vex search "Foo"
# Verbose multi-line / human-readable
vex search "Foo" --format text
# JSON envelope (for MCP / tool integration; what `vex-mcp` parses)
vex search "Foo" --format json
如果希望在终端中显示详细的多行视图,可在 .vex.toml 中通过 format = "text" 固定其他默认格式。
JSON 封装(v1.11.0 — 对裸数组解析器有破坏性变更)
每个 --format json 子命令都会将其负载封装在 Phase 13
封装中。形状统一,可通过 protocol_version 轻松识别:
{
"protocol_version": "v1",
"capabilities": { /* see `vex capabilities` */ },
"_meta": { "vex.dev/index_age_ms": 1200, "ttlMs": 30000, "cacheScope": "project" },
"results": [ /* the actual data, shape depends on the subcommand */ ]
}
在 v1.11 之前,只有 search 和 bundle 会返回此封装;其他
子命令(show、usages、pattern、grep、implementations、
callers、callees、paths、reachable、tests-for、check、
similar、duplicates、diff、outline、index、update、
status、eval)返回的是裸数组/对象。迁移:原先的 jq '.[0].name'
或 data[0]['name'] 现在需要改为 jq '.results[0].name' /
data['results'][0]['name']。在迁移期间,可通过
response.get('protocol_version') == 'v1' 检测封装,
以同时支持两种格式。
搜索原理
结构搜索(默认)
使用倒排索引按符号名称搜索,并拆分 CamelCase:
"PaymentService"— 完全匹配"Payment"— 前缀匹配,可找到 PaymentService、PaymentGateway"payment"— 不区分大小写,也会通过 CamelCase 词元查找
语义搜索(--semantic)
使用 MiniLM-L6-v2 将查询转换为嵌入(384 维向量),并查找含义相近的符号:
"parse source code files"可找到parse_file、extract_refs、parse_file_symbols"database storage"可找到populate_db、create_10k_db、add_root_persists_to_db"find implementations of an interface"可找到find_implementations、test_interface_extends
BM25 通道(索引包含 BM25 数据时自动启用)
基于符号主体词元(标识符、签名、文档字符串)执行经典的 Okapi BM25(K1=1.2、B=0.75)搜索。它弥补了“精确名称”(结构搜索)与“广义含义”(语义搜索)之间的差距:可查找不属于任何符号名称的稀有主体词,例如 timeout、retry、singlestore、idempotency_key。从 v1.11(阶段 8.4)起,还会从 TOML / YAML / HTML / CSS 值中提取主体词元,因此 vex search "production endpoint" --semantic 可以匹配包含 endpoint = "https://..." 的 [server] 表。传入 --no-bm25 可在单次调用中禁用此功能。
混合搜索(3 路 RRF)
当索引包含全部三个通道(通过 --semantic 构建)时,vex search 会使用倒数排名融合合并结构、BM25 和语义搜索结果。至少被 2 个通道命中的符号会标为 Hybrid;仅被一个通道命中的符号则保留原有匹配类型。一次查询即可同时减少结构搜索噪声和语义搜索模糊结果。
用法(FST)
引用存储在 FST(有限状态转换器)中,可通过 mmap 零复制查找,并支持前缀搜索。
符号簇(vex modules)
vex index 会将符号分组成相互调用或引用的代码簇(基于调用、引用和层级边执行确定性的 Leiden-CPM 算法;不含随机性,因此对同一代码树建立的两个索引结果一致)。vex modules 可读取这些分组:
vex modules # clusters of >= 3 symbols, largest first
vex modules --members 5 --sort cohesion --include 'src/**'
vex modules IndexReader # the cluster of one symbol, with its members
vex modules --format text # text output (shown below)
Modules — leiden-cpm/1 γ=1/8 · 475 clusters (≥3, showing 3) · 1,469 unclustered · 1,464 not eligible
#60 src/cli/ 39 symbols cohesion 0.49 hubs: OutputFormat, print_envelope, default_meta_for
#23 crates/vex-mcp/src/tools/ 38 symbols cohesion 0.68 hubs: opt_bool, build_command, opt_u64
#286 src/pattern/matcher/tests.rs 32 symbols cohesion 0.78 hubs: parse_pattern, find_matches, Segment
(以上输出来自本文编写时的此代码库;簇 ID 是本节中的序号,而非排名。)
- 簇的
label是最深的路径前缀,该前缀下的文件至少包含其 60 % 成员。cohesion是边权重internal / (internal + cut);枢纽是簇内连接最紧密的三个成员。 --include/--exclude/--exclude-tests会筛选成员和枢纽(超出范围的枢纽会被移除);只要至少有一个成员在范围内,就会显示该簇,其size为范围内成员数(JSON 中的size_at_build保留构建时的数量)。- 符号模式会为每个匹配项报告
status:clustered(已分簇)、unclustered(孤立)、not_eligible(标题、模块、标记语言/配置语言)或new_since_build(构建后新增)。 - 簇由
vex index计算,并在vex update期间保留且冻结。更新后,JSON 中会包含stale: true和new_since_build,文本输出末尾会出现一行!;运行vex index可重新计算。这与_meta.vex.dev/stale不同,后者仍表示“索引早于工作树”。 --limit必须至少为 1;与 SYMBOL 一起使用时,它会限制匹配的符号数量(JSONsymbols_total会报告未限制的总数),并忽略--min-size。退出代码:有结果时为0,结果为空时为1(原因会显示在results.empty_reason和标准错误中),簇区段损坏时为2。使用--workspace时,簇按仓库划分,且--limit对每个仓库分别生效。
类型感知的引用(--strict)
vex usages --strict <name> 会读取由 LSP 风格的作用域绑定器写入的 v5 reference_edges 区段。对于有绑定器的语言(Rust、TypeScript、Python、C#、C++、Go、Java、Kotlin),每个引用都会在索引时根据文件内作用域链和导入/使用关系图进行解析,然后关联到用户实际指向的全局符号,而不只是任何提及该名称的行。
这对用户的影响:
- 注释、文档字符串、字符串字面量和正则表达式正文中的标识符会被排除(此过滤对所有用户启用,并非只有
--strict才启用)。 - 被
let/const/ 函数参数遮蔽的名称会解析到内部作用域,而不是外部作用域。 use ext::Foo;/import { Foo } from './ext'/from ext import Foo会使对Foo的引用跨文件解析到索引中定义它的位置。对于 C++,带引号的#include "..."(v1.14+)会通过 BFS 遍历传递包含关系图,将Foo解析到任何可达头文件中定义的符号。系统头文件<vector>/<string>和宏包含(#include MY_HEADER)按设计不会解析。- 在索引中导入但从未定义的名称会保持为
Unresolved,且不会生成边——这比巧合匹配更好。
不带 --strict 时,vex usages 仍通过旧版引用 FST 支持所有受支持的语言;--strict 只是在八种带绑定器的语言中以召回覆盖面换取精确度。早于绑定器的 v3 / v4 索引会中止,并提示「请重新运行 vex index」。
结构模式(vex pattern)
按代码形状而非文本匹配。适用于 vex 能解析的所有语言;基于索引的预筛选(通过 v6 pattern_skeletons 区段)可加快 15 种会生成骨架的语言中的候选项筛选(除 Bash、Lua、YAML 和 TOML 外的所有语言会实时扫描)。
语法:
$NAME— 捕获单个标识符或括号平衡的表达式。同名标记变量出现两次时会强制反向引用:record($X, $X)匹配record(state, state),不匹配record(state, other)。$_— 通配符(匹配但不捕获)。$$$— 匿名省略号(匹配到下一个字面内容前的任意内容;可跨越换行符)。$$$BODY/$$ARGS— 命名多行省略号。功能与$$$完全相同,但会以给定名称捕获匹配的文本;$$$BODY适用于代码块正文,$$ARGS适用于参数列表。反向引用相等规则同样适用。&&(两侧有空格)— AND 组合。两个子模式必须在同一文件中匹配,且共享的元变量名称必须在两者中捕获相同文本:struct $S && impl $S匹配同时包含这两种形状且$S相同的文件。||(两侧有空格)— OR 组合(取并集,按(path, line)去重)。&&的优先级高于||。- 组合运算符仅在括号 / 引号深度为 0 时生效,因此
record($X, $X)和f($X && $Y)会保持为单个模式。
基于索引的预筛选:存在 v6 索引时,模式开头的字面关键字(fn、struct、class、def、impl 等)会映射到 tree-sitter 节点类型,vex pattern 只遍历持久化骨架中包含该类型的文件。匹配前会去掉关键字前的可见性 / async / export 修饰符(pub async fn $F 会正确推断为 function_item)。遇到语法漂移、缺少区段或 vex update 后区段不完整时,会回退到实时扫描——--why 会报告具体原因。
示例:
# Multi-line function body with named captures
vex pattern 'fn $NAME($$ARGS) -> Result<$T, $E> { $$$BODY }' --lang rust
# Both struct and impl for the same type in one file
vex pattern 'struct $S && impl $S' --lang rust
# Interface OR class with the same name
vex pattern 'interface $N || class $N' --lang typescript
# See which mode and what narrowing happened
vex pattern 'fn $N($$$)' --lang rust --why 2>trace.json
基准测试
与 ast-index v3.31.0(SQLite + FTS5)和 ripgrep 15.1.0 对比。
方法说明: 索引数据于 2026-08-12 在 Apple Silicon(macOS)上重新测量,使用 vex v1.25.5、release 构建、冷缓存和非语义索引。搜索数据与 2026-07-11 / v1.25.1 的运行结果相同(v1.25.2-v1.25.5 中没有改动搜索路径的内容),取 10 次运行的平均值。可使用 ./benches/bench.sh 复现(如需更大的语料库,可将 VEX_BENCH_LARGE_PROJECTS 指向自己的代码库)。数据因机器而异——应关注比例,而非绝对数值。
索引
| 项目 | vex | ast-index | vex 大小 | ast-index 大小 |
|---|---|---|---|---|
| 小型(vex 本身,6.8K 个符号) | 442 ms | 254 ms | 4.6 MB | 6.7 MB |
| 中型(ast-index 代码库,2.3K 个符号) | 204 ms | 109 ms | 1.6 MB | 3.4 MB |
客观解读: vex 的索引速度比 ast-index 慢约 1.7-1.9 倍——它在索引时构建的内容多得多(FST + BM25 + 持久化调用图 + 已解析的引用边 + v8 类型层次结构分区 + 三元组跳过索引 + 模式骨架 + 符号簇(v9)),而 ast-index 构建的是 SQLite + FTS5 存储。这笔一次性开销换来了下文所示的常数时间查询;生成的索引在磁盘上仍小约 1.4-2 倍(mmap + FST 对比 SQLite)。这些数据测于 v1.25.5(v9 之前);v9 增加了聚类处理流程,仍待重新测量。上次在 v1.25.1 测量时,差距约为 2.5-3 倍;v1.25.5 是差距缩小的主要原因——与 v1.25.4 的单变量 A/B 测试显示,在两个语料库上的冷索引耗时分别降低了 34% 和 31%,因为现在每个文件只解析一次,并由所有提取器共享语法树,而不是让每个提取器都重新解析。使用 --semantic 索引的项目会更慢(需生成 ONNX 嵌入),生成的索引也更大。
搜索:vex 与 ast-index、ripgrep 对比
中型项目(ast-index 代码库,31K 行 Rust,10 次运行的平均值):
| 查询 | vex | ast-index | rg -w | vex 对比 rg |
|---|---|---|---|---|
search | 4.7 ms | 8.3 ms | 9.2 ms | 2.0x |
SymbolKind | 4.6 ms | 8.2 ms | 8.6 ms | 1.9x |
parse_file | 4.6 ms | 7.9 ms | 8.7 ms | 1.9x |
IndexReader | 4.7 ms | 11.7 ms | 8.6 ms | 1.8x |
核心结论:无论项目规模如何,vex 搜索耗时都稳定在约 4-5 ms(FST 的复杂度为 O(query_len))——这正是较慢的索引构建所换来的优势。与 ripgrep 的对比并非完全同类:rg 会扫描原始文本而不使用索引,因此耗时会随语料库大小增长(在这个 31K 行的代码库上只需个位数毫秒,在大型代码库上则需 100 ms 以上);而 vex 执行的是预构建 FST 的查找。持久的优势在于摊销后的效率和查询结果的性质:vex 只返回符号定义(精准且节省 token),而 rg 会返回每处文本匹配(噪声多,在大模型上下文中成本高)。
模式匹配(仅 vex 支持)
中型项目(ast-index 代码库,Rust):
| 模式 | 耗时 | 匹配数 |
|---|---|---|
fn $NAME($$$) -> Result | 31 ms | 50 |
pub struct $NAME | 27 ms | 44 |
fn $NAME($$$) | 29 ms | 50 |
ast-index 和 ripgrep 不支持 AST 模式匹配。
语义搜索
示例(展示语义能力,并非延迟基准测试)——结构化搜索返回 0 条结果,而语义搜索能找到相关符号的查询:
| 查询 | 结构化搜索 | 语义搜索 |
|---|---|---|
| “解析源代码文件” | 0 | 19 |
| “数据库存储” | 0 | 20 |
| “查找接口的实现” | 0 | 20 |
| “文件系统目录遍历器” | 0 | 20 |
| “处理错误和异常” | 0 | 20 |
HNSW 与暴力搜索(语义向量搜索)
以下延迟数据是在早期版本中测得的,未在 v1.25.1 阶段重新测试——请将其视为扩展趋势(HNSW 保持平稳,暴力搜索随规模线性增长),而非当前的绝对数值。
语义搜索会先通过 ONNX 将查询嵌入为向量(约 55 ms),然后搜索存储的向量。HNSW(usearch)以 O(log N) 的近似最近邻搜索取代 O(N) 的暴力扫描:
| 符号数 | 暴力搜索 | HNSW | 加速比 |
|---|---|---|---|
| 333 | 约 3 ms | 约 3 ms | 1 倍 |
| 11K | 约 8 ms | 约 3 ms | 2.3 倍 |
| 20K | 约 11 ms | 约 3 ms | 4 倍 |
| 100K(预计) | 约 55 ms | 约 3 ms | 约 18 倍 |
无论索引大小如何,HNSW 的耗时都保持在约 3 ms。暴力搜索则随规模线性增长。语义搜索的总延迟主要由 ONNX 嵌入(约 55 ms)决定,因此对于小型代码库,端到端加速效果有限;但在大规模场景下至关重要。
| 模式 | 延迟 |
|---|---|
| 仅结构搜索 | 约 4 ms |
| 混合搜索(结构搜索 + 语义搜索) | 约 58 ms(HNSW)/ 约 66 ms(暴力搜索) |
LLM 令牌效率
AI Agent 搜索代码时,输出会直接进入上下文窗口。基于 grep 的工具会返回每一处文本匹配——包括注释、字符串、变量用法以及压缩文件中的匹配项——消耗令牌却没有增加有效信息。
vex 仅以紧凑的单行格式返回符号定义,大幅减少令牌消耗:
| vex 紧凑格式 | rg(grep) | 减少比例 | |
|---|---|---|---|
| 7 次符号查询(典型情况) | 约 220 个令牌 | 约 1,300 个令牌 | 6 倍 |
| 命中压缩 JS/CSS 文件的查询 | 约 270 个令牌 | 约 58,700 个令牌 | 217 倍 |
示例——在大型项目中搜索类名:
# rg: 20 matches across imports, usage sites, comments, tests (2,045 chars)
$ rg -w "PreAggregatedConfig" .
./models.py:3602:class PreAggregatedConfig(models.Model):
./models.py:3610: pre_aggregated_config = PreAggregatedConfig.objects.get(...)
./serializers.py:48:from .models import PreAggregatedConfig
./tests.py:12: config = PreAggregatedConfig(...)
... (16 more lines)
# vex: 1 definition (93 chars)
$ vex search "PreAggregatedConfig" --format compact
C PreAggregatedConfig models.py:3602 class PreAggregatedConfig(models.Model):
对于每项任务需要进行 10–20 次代码查询的 Agent,与 grep 相比,vex 每次会话可节省 5,000–20,000 个令牌,从而降低成本,并为推理留出更多上下文窗口空间。
支持的语言
通过 tree-sitter 索引了 20 种语言。能力列说明:
- Binder —
vex usages --strict是否会通过 LSP 风格的作用域链解析引用(阶段 11.1)?cross-file包含use/import解析;in-file会在文件内解析,但将导入视为未解析。其余语言会回退到普通vex usages使用的基于行的扫描器。 - Patterns —
vex pattern是否使用 v6 索引预筛选器(阶段 11.4)?indexed表示持久化的骨架区段会在查询时缩小候选文件范围;live-scan表示每次查询时,tree-sitter 都会遍历所有匹配语言的文件。20 种语言都支持vex pattern语法($NAME、$$$BODY、&&/||);预筛选器只会加快 16 种会生成骨架区段的语言的发现速度(除了 Bash、Lua、YAML 和 TOML 之外的所有语言)。
| 语言 | 扩展名 | 符号 | 导入 | Binder | Patterns |
|---|---|---|---|---|---|
| Rust | .rs | 函数、结构体、枚举、特征、实现、类型、常量 | use 声明 | 跨文件 | 索引 |
| TypeScript/JS | .ts、.tsx、.js、.jsx、.mjs、.cjs | 类、接口、枚举、函数、箭头函数、类型别名 | import | 跨文件 | 索引 |
| Python | .py | 类、函数(包括异步函数和带装饰器的函数) | import、from..import | 跨文件 | 索引 |
| C# | .cs | 类、接口、结构体、枚举、方法、属性 | using | 跨文件 | 索引 |
| C/C++ | .cpp、.cc、.cxx、.hpp、.hxx、.h(不索引 .c 文件,仅通过 C++ 索引 .h) | 类、结构体、函数、方法、模板、枚举 | #include | 跨文件(v1.14 会遍历带引号的 #include "...";类方法仍在文件内解析) | 索引 |
| Go | .go | 函数、方法、结构体、接口 | import | 跨文件 | 索引 |
| Java | .java | 类、接口、枚举、方法、构造函数 | import | 跨文件 | 索引 |
| Kotlin | .kt、.kts | 类、接口、对象、函数、属性 | import | 跨文件 | 索引 |
| Ruby | .rb | 类、模块、方法 | — | — | 索引 |
| Swift | .swift | 类、结构体、枚举、Actor、协议、函数 | import | — | 索引 |
| PHP | .php、.phtml | 类、接口、特征、方法、函数 | use、require | — | 索引 |
| SQL | .sql | 表、视图、函数、触发器、索引、架构、类型、序列 | ALTER TABLE 引用 | — | 索引 |
| Markdown | .md、.markdown | 标题(章节结构) | — | — | 索引 |
| Bash | .sh、.bash | 函数 | — | — | 实时扫描 |
| Lua | .lua | 函数、本地函数、表 | require | — | 实时扫描 |
| CSS | .css | 规则、选择器、@keyframes | — | — | 索引 |
| HTML | .html、.htm | 自定义元素(带连字符的标签名) | — | — | 索引 |
| YAML | .yaml、.yml | 顶层键 | — | — | 实时扫描 |
| TOML | .toml | 裸键、点分键、表 | — | — | 实时扫描 |
| Dart(索引支持) | .dart | 类、枚举、混入、扩展、函数、方法、构造函数、别名 | 导入 URI | —(基于名称的引用) | 索引 |
请参阅 docs/SUPPORTED_LANGUAGES.md,了解语法版本、ABI 级别,以及添加语言或升级语法的操作手册。将语言加入索引 Patterns 层级只需在 src/pattern/skeleton/kinds.rs 中编辑一次允许列表——阶段 11.4 后续推广(Go → Java → Kotlin → C# → C++ → Swift → PHP → Ruby,以及 SQL / Markdown / CSS / HTML)已完成;目前仅 Bash、Lua、YAML 和 TOML 仍使用实时扫描。
索引位置
macOS: ~/Library/Caches/vex/<hash>/index.vex
Linux: $XDG_CACHE_HOME/vex/<hash>/index.vex (fallback: ~/.cache/vex/<hash>/index.vex)
Windows: %LOCALAPPDATA%\vex\<hash>\index.vex (fallback: %USERPROFILE%\AppData\Local\vex\<hash>\index.vex)
每个项目都会根据其规范项目根路径的哈希值(xxh3)获得独立索引。可用以下方式覆盖:
--cache-dir <path>— 将 vex 指向自定义缓存目录$VEX_CACHE_DIR— 环境变量(优先级低于--cache-dir).vex.toml中的cache_dir— 配置文件(优先级最低)
已知限制
vex 是一款静态分析工具——有些实际调用点和引用从原理上就无法被发现。主要缺口如下:
- 函数作用域之外的
vex callers——模块级表达式会通过合成的<module:path>调用方报告(Phase 14.1)。Python 和 Java 的函数/方法装饰器(Phase 14.2)、Kotlin 注解和 C# 方法/构造函数特性(Phase 14.2.2),以及 TypeScript 方法装饰器和 Rust 函数/方法上的外部属性(Phase 14.2.1)会生成正向边。剩余缺口:类级装饰器(Phase 14.6);Rust#[derive(...)]会被有意过滤。 vex usages的质量因语言而异。 Rust、TypeScript、Python、C# 和 C++ 支持--strict(来自 v5reference_edges部分、由绑定器解析的引用,Phase 11.1)。其他语言使用基于行的标识符扫描,误报率较高。- 动态分派不可见。 通过字符串解析的工厂(
uvicorn.run("main:app"))、任务队列(celery_task.delay())、反射(getattr(obj, name)())——这些都不会生成边。 - 变通方法:
vex grep '\bname\b'是完备的文本回退方案。速度较慢(约 50 ms),但不会漏掉匹配项。
完整的覆盖矩阵、复现步骤以及各查询类型的建议,请参阅 docs/LIMITATIONS.md。
故障排除
查看内部警告
Vex 通过 tracing crate 在 parse/store 边界发出结构化日志——例如语法加载失败、mmap 重新打开、清单不匹配等。默认情况下,RUST_LOG 未设置,因此只有最关键的诊断信息会输出到 stderr。
如果搜索结果出乎意料,或索引命令行为异常,请提高日志级别:
RUST_LOG=vex=warn vex search Foo
RUST_LOG=vex=info vex index # noisier — file-level progress
若要了解搜索引擎实际执行了什么(各通道的命中数、模糊回退是否启用、应用了哪些筛选条件),请改用结构化跟踪:
vex search Foo --why 2>trace.json # trace lands on stderr as JSON
--why/why: true 的 JSON 格式请参阅 docs/MCP-SCHEMA.md。
集成
Claude Code(CLI 集成)
推荐通过 CLAUDE.md 规则将 vex 集成到 Claude Code 中(见下文)。Vex 以 CLI 工具的形式运行——Claude Code 通过 Bash 直接调用它,无需 MCP 服务。
设置:
# Install vex
brew tap tenatarika/tap && brew install vex
# In your project
cd /path/to/project
vex init # create .vex.toml
vex index # build index (add --semantic for meaning-based search;
# add --history for `vex history <Symbol>` archaeology queries — v1.15.0/v1.16.0)
然后添加 .vex.toml 配置以自动更新索引,确保 Claude 始终搜索最新的索引:
# .vex.toml
auto_update = true
# format = "compact" # already the default since v1.10.1 — set "text" if you'd rather see verbose output
**多仓库(v1.22.0):**如果 Claude Code 同时处理多个仓库,可在它们的共同父目录中放置 .vex-workspace.toml,并告诉 Claude 在调用 vex 时添加 --workspace——例如,使用 vex usages Config --strict --workspace 跨所有仓库追踪某个符号的引用,或使用 vex check Foo --workspace 查看哪些仓库定义了它。结果会按仓库分组。请参阅多仓库工作区。
Claude Code(MCP 服务)
另外,vex 还包含一个 MCP 服务(vex-mcp),可将所有命令作为 MCP 工具提供。**注意:**Homebrew 只安装 vex(不安装 vex-mcp)。从 v1.11.2 开始,每个版本都会随 vex 一起提供预编译的 vex-mcp 二进制文件,适用于构建矩阵涵盖的三个目标三元组:aarch64-apple-darwin(macOS Apple Silicon)、x86_64-unknown-linux-gnu(Linux)和 x86_64-pc-windows-msvc(Windows)。Intel Mac 和其他目标三元组仍需按下文从源码构建。
最简单的设置方式(v1.15.0+):
vex mcp install --agent claude-code
此操作会为你运行 claude mcp add --scope user --transport stdio vex --env VEX_ROOT=<root> -- <vex-mcp>(v1.27.1+)。如果 claude CLI 不在 PATH 中,则会打印该命令,但不会写入任何内容。v1.27.1 之前的版本会写入 ~/.claude/claude_desktop_config.json,而 Claude Code 不会读取此文件;升级后请重新运行该命令。
手动设置:
# 1. Download the prebuilt for your platform from
# https://github.com/tenatarika/vex/releases/latest
# e.g. vex-mcp-aarch64-apple-darwin.tar.gz / vex-mcp-x86_64-pc-windows-msvc.tar.gz
# 2. Extract and put the binary on PATH (or remember the full path).
# Source build (if you prefer or are on an unsupported triple)
cargo build --release -p vex-search-mcp
# Register with Claude Code (user scope; Claude Code keeps it in ~/.claude.json)
claude mcp add --scope user --transport stdio vex \
--env VEX_ROOT=/path/to/your/project --env VEX_DEVICE=auto \
-- /path/to/vex-mcp
# Or project scope: commit a .mcp.json at the project root
# (see integrations/claude-code/mcp.json)
VEX_DEVICE(v1.16.0)会在二进制文件使用 gpu-cuda / gpu-directml / gpu-coreml 构建时选择 GPU 执行提供程序;当 MCP 驱动的 index / update 调用在大型仓库上重建语义嵌入时,这一点很重要(在 MiniLM-L6 上,CUDA / DirectML 相比 CPU 分别快 51× / 29×)。在仅支持 CPU 的构建中,auto 是安全的(会静默降级)。运行一次 vex gpu,确认 EP 确实已启用。
MCP 工具(28 个):
search— 三路混合搜索(结构 + BM25 + 语义);接受filter/include/exclude/kind/context_path/no_bm25/--why/ 元数据过滤器 / 差异范围(since/since_branched/changed_only)find_symbol— 精确名称查找find_similar— 根据自由格式描述进行语义搜索similar— 查找现有符号的最近邻(explain会添加 Jaccard + 差异信息);差异范围duplicates— 查找近似重复的符号对(explain会显示差异);差异范围show— 从源代码中提取符号正文;Phase 13.3 截断标志(signature_only/head/no_body/collapsed,互斥)outline— 文件结构usages— 查找符号的所有引用;filter_path/strict/whyimpact— 删除安全性影响范围(判定 + 各通道证据);depth/exclude_docsgrep— 正则表达式内容搜索pattern— 使用元变量反向引用进行 AST 模式匹配;差异范围;--whyimplementations— 查找扩展基类 / trait / 接口的类型(包括泛型);差异范围subtypes— 沿 extends/implements 边进行传递闭包向下遍历(直接子类、孙类……),并标注深度;仅使用索引(不回退到实时遍历);depth/ 差异范围modules— 实际模块:相互调用 / 引用的符号集群(v9 索引,在完整vex index时计算);列出集群(标签、大小、内聚度、中心节点),或传入symbol查询其所属集群;limit/min_size/members/sort/ 作用范围 /workspace;对于较旧索引或使用--no-clusters时,返回空结果并附带empty_reasoncallers/callees— 直接调用图导航(通过持久化索引的快速路径);差异范围paths— 列举两个函数之间的调用链reachable— 目标的传递调用者tests_for— 传递覆盖目标的测试函数(标注框架)diff— 比较 git 版本与工作树之间的符号级差异check— 快速检查符号是否存在bundle— 统一的多源数据包(mode: symbol | pr-impact | project),Phase 13 信封eval— 排名评估工具(bench/min_ndcg);MCP 默认启用json: true,因此 Agent 可获取结构化的EvalReportcapabilities— 机器可读的能力矩阵(protocol_version、signals、bundle_modes、history_diff(v1.16.0)、symbol_clusters等)index/update— 构建 / 重建索引;v1.16.0 新增gpu: bool/device: cpu|auto|cuda|directml|coreml参数(仅限启用 GPU 的构建),使 Agent 无需修改环境变量或配置,即可按调用选择使用 GPU 进行语义嵌入status— 索引统计信息(现包含gpu_support/default_device(v1.16.0))history— 符号在各提交中的历史版本 (自 v1.20.0 起为 MCP 工具,D5);depth/limit/since/until/author/kind/diff/exact_presence
注意:
vex history和vex tests-for已在 v1.20.0(D5)升级为一等 MCP 工具;早期文档将history称为“仅限 CLI”的说法已过时。两者与其他所有 vex 命令一样,都会输出相同的--format json信封。 多仓库(v1.22.0): 十一个工具——search、grep、check、usages、impact、callers、callees、reachable、modules、index、update——均接受workspace: boolean参数,可将调用分发到每个.vex-workspace.toml成员,并在structuredContent.results下返回分组后的{workspace, repos:[...]}负载。将project_root指向.vex-workspace.toml所在目录或其上级目录。find_symbol不在其中(请使用check/search);工作区模式下会忽略why。详见docs/MULTIREPO-PHASE8-mcp.md。
MCP ↔ CLI 对等(v1.10): 现在,每个支持路径的工具,其架构都与 CLI 的功能范围一致。CLI 支持的所有位置均开放了 Glob 筛选器(include / exclude)、子串 filter、kind 提升、context_path 邻近提示、no_bm25、Phase 13.3 截断、diff 范围和 no_stale_check——Agent 无需再切换到 bash 来处理“main 之后 crates/api/ 下的 Rust 文件”这类范围筛选。
架构遵循规范词汇(query / symbol / symbols / path / pattern / filter / include / exclude);v1.7 之前的别名(name、file、names 等)仍然可用,并会在 JSON-RPC 响应中通过 _meta.deprecated_args: [...] 发出提示。格式错误的 JSON-RPC 输入现在会返回符合规范的 -32700 Parse error 响应(v1.9.2 修复),并在 data 字段中回显出错行的 512 个码点;stdin 上的管道断开 / EOF 会正常关闭服务,而不是丢弃正在处理的工具调用。详见 docs/MCP-SCHEMA.md。
其他兼容 MCP 的客户端(Cursor、Codex CLI、Windsurf、Cline、Continue.dev、Zed)请参阅下方的其他 MCP 客户端——使用相同的 vex-mcp 二进制文件,但配置文件不同。
其他 MCP 客户端
同一个 vex-mcp 二进制文件适用于任何兼容 MCP 的客户端。二进制文件的安装方式与上面的 Claude Code 部分相同;只有各客户端的配置文件位置和格式不同。
一行式设置(v1.15.0+):
vex mcp install --agent cursor # or any of: claude-code, codex-cli, windsurf, cline, continue, zed
vex mcp install --agent all # fan out across every supported agent
vex mcp install --agent cursor --dry-run # preview the post-merge config without writing
对于基于文件的 Agent,vex mcp install 会读取现有的 Agent 配置,在不影响其他条目的情况下合并单个 vex 服务条目,并以原子方式写回。对于 Claude Code,它会运行 claude mcp add,而不是编辑文件。此操作具有幂等性——如果条目已匹配,重新运行时会跳过且不做任何更改(--force 可覆盖此行为)。vex mcp uninstall --agent <X> 会移除该条目;vex mcp list 会列出各 Agent 当前的条目。下文记录的其他 Agent 配置文件,正是 vex mcp install 写入的内容——手动编辑、配置自动安装程序尚不支持的 Agent,或处理默认结构以外的情况时,请参阅 integrations/。
最常用配置的可直接复制粘贴片段位于 integrations/:
| Agent | 配置片段 | 磁盘上的目标文件 |
|---|---|---|
| Claude Code | integrations/claude-code/(项目级作用域使用 mcp.json) | 通过 claude mcp add 注册(用户级作用域)或 <project>/.mcp.json |
| Cursor | integrations/cursor/mcp.json | ~/.cursor/mcp.json 或 <project>/.cursor/mcp.json |
| Codex CLI (OpenAI) | integrations/codex-cli/config.toml | ~/.codex/config.toml 或 <project>/.codex/config.toml |
| Windsurf (Codeium) | integrations/windsurf/mcp_config.json | ~/.codeium/windsurf/mcp_config.json |
| Cline (CLI) | integrations/cline/mcp.json | ~/.cline/mcp.json(VS Code 扩展:通过面板 UI 配置) |
| Continue.dev | integrations/continue/vex.yaml | ./.continue/mcpServers/vex.yaml(项目级) |
| Zed | integrations/zed/settings.json | ~/.config/zed/settings.json |
各 Agent 的注意事项(自动批准标志、超时覆盖、Agent 模式要求)记录在 integrations/README.md 中。
MCP Registry(从 v1.27.2 起):vex 已作为 io.github.tenatarika/vex 收录在官方 MCP Registry 中。每个版本都会为每个平台附带一个 MCP Bundle(vex-mcp-<target>.mcpb,macOS arm64 / Linux x86_64 / Windows x86_64),其中包含 vex-mcp 和 vex;支持 MCPB 的客户端只需询问一次项目根目录,无需在 PATH 中添加其他内容。Bundle 的安装更新由客户端负责,而非 vex self-update(该命令在 Bundle 内会拒绝运行)。
Glama:也已收录在 Glama MCP 目录中,该目录会构建此服务并对其工具进行评级。
Agent 配方与工作流
将 vex-mcp 接入 Agent 后,接下来的问题是该如何向 Agent 提问,才能让它按正确顺序选择合适的工具。docs/COOKBOOK.md 收录了常见调用链的配方——代码考古、通过 usages --strict 验证的跨文件重构、通过 bundle(mode="pr-impact") 进行 PR 影响分析、清理死代码与重复内容,以及多仓库编排。每个配方都会说明工具调用顺序、这样安排的原因,以及能可靠触发该调用链的 Agent 提示词。
文档与集成:
- 完整的 vex 文档和 API 参考:https://context7.com/tenatarika/vex(位于 Context7)
- Agent 技能参考:
.claude/skills/vex/SKILL.md(包含在项目中)
Shell 集成
# Shell completions (tab-completion for commands and flags)
vex completions bash > ~/.bash_completion.d/vex # Bash
vex completions zsh > ~/.zfunc/_vex # Zsh (add ~/.zfunc to fpath)
vex completions fish > ~/.config/fish/completions/vex.fish # Fish
# Aliases — add to .zshrc / .bashrc
alias vx="vex search"
alias vxu="vex usages"
alias vxi="vex index --path ."
alias vxs="vex index --path . --semantic"
alias vxw="vex watch"
CLAUDE.md 集成
将以下内容添加到项目的 CLAUDE.md,让 Claude Code 使用 vex 而不是 grep:
## Code Search
Before first use in a project, run `vex init` to generate `.vex.toml`, then `vex index` to build the index.
Set `auto_update = true` in `.vex.toml` so the index stays fresh automatically.
Use vex for code search instead of grep or manual file reading:
- `vex check "SymbolName"` — exact-name lookup: does it exist? (~4ms)
- `vex search "SymbolName"` — fuzzy symbol search: find definitions by name or meaning
- `vex search "description" --semantic` — search by meaning (requires --semantic index)
- `vex search "rare_term"` — BM25 channel finds rare terms in symbol bodies (auto-on when index has BM25 data)
- `vex show "SymbolName"` — extract symbol body (use INSTEAD of Read for specific symbols)
- `vex show "A" "B" "C"` — extract multiple symbols at once
- `vex usages "SymbolName"` — find all references
- `vex usages "SymbolName" --strict` — refactor-grade refs (binder-resolved, high precision)
- `vex impact "SymbolName"` — delete-safety blast-radius report (safe/unsafe/uncertain)
- `vex modules [SYMBOL]` — de-facto code clusters (symbol communities)
- `vex pattern 'class $NAME(BaseModel):' --lang python` — AST pattern matching with metavariables
- `vex pattern 'fn $N($$ARGS) -> Result<$T, $E> { $$$BODY }' --lang rust` — multi-line `$$$BODY` / `$$ARGS` capture
- `vex pattern 'struct $S && impl $S' --lang rust` — AND composition (back-ref `$S` must agree across both shapes)
- `vex pattern 'interface $N || class $N' --lang typescript` — OR composition (union, deduped by `(path, line)`)
- `vex pattern '<pat>' --lang <lang> --why` — emit ScanTrace on stderr (mode / candidate vs total / fallback reason)
- `vex outline path/to/file.py` — file structure overview
- `vex implementations "BaseService"` — find types extending a class/interface
- `vex subtypes "BaseService"` — transitive-down closure over extends/implements edges (direct children, grandchildren, …)
- `vex callers "function_name"` — find all callers (~4ms via persistent call graph)
- `vex callees "function_name"` — find all callees (~4ms via persistent call graph)
- `vex paths "from" "to"` — enumerate caller chains between two functions (multi-hop)
- `vex reachable "Target"` — transitive callers of a target (blast-radius analysis)
- `vex tests-for "SymbolName"` — test functions that cover a symbol (framework-labeled)
- `vex history "SymbolName"` — historical versions of a symbol across commits
- `vex similar "SymbolName"` — semantically close symbols (requires --semantic index)
- `vex duplicates --threshold 0.95` — near-duplicate symbol pairs
- `vex diff --base main` — symbol-level diff against a branch (added / removed / moved / body-changed)
- `vex bundle --mode symbol --symbol Foo` — single-call body + callers + callees + similar (replaces 4 round-trips)
- `vex bundle --mode pr-impact --base origin/main` — changed symbols + transitive callers + tests on the current branch
Many search-shaped commands support `--filter-path "path/"` (alias `--filter`) to narrow results to a directory (e.g. `search`, `show`, `usages`, `grep`, `similar`, `duplicates`). Most search-shaped commands also accept `--since <rev>` / `--since-branched` / `--changed-only` for diff-scoping.
### Rules
- **Always prefer `vex show` over `Read`** when you need a specific function or class
- **Always prefer `vex search` over `Grep`** when looking for symbol definitions
- **Use `vex grep` instead of `Grep`** for searching inside string literals, comments, or config values
- **Use `--format compact`** for token-efficient output in automated workflows
- **Use `--kind fn`** to boost results matching a specific symbol kind (fn, struct, trait, class, etc.)
- **Use `--context-path`** with the path of the file you are currently editing to boost nearby results
- **Run `vex update` after modifying source files** if `auto_update` is not enabled in `.vex.toml`
- **Use `vex pattern ... --why`** to debug match counts — the trace tells you whether the indexed prefilter ran or fell back to live-scan, and why
- **Indexed pattern prefilter requires a full `vex index`** — after `vex update` the section is partial and `vex pattern` automatically degrades to live-scan (reason `partial-section` in `--why`)
### Indexing
- `vex index` — full structural index + pattern skeleton section (v6)
- `vex index --semantic` — with embeddings (slower, enables semantic search)
- `vex update` — incremental update (only changed files)
- `vex index --no-pattern-index` — skip the v6 pattern skeleton section if you don't use `vex pattern` (sticky across `vex update`)
- `vex index --no-clusters` — skip computing symbol clusters (v9). `vex update` keeps the opt-out, but the next plain `vex index` computes clusters again
测试
单元与集成测试
cargo nextest run --workspace # ~4,000 tests — unit, integration, property-based, adversarial
cargo test --doc # doctests
cargo clippy -- -D warnings # zero warnings policy
(建议使用 nextest ≥ 0.9.145——旧版本在 macOS 上会报告虚假的 LEAK;使用 cargo nextest self update 更新。)
测试覆盖范围包括:
- 各语言语法回归(新增):所有 20 种受支持语言均有对应的
tests/<lang>_query_test.rs——在升级 tree-sitter 语法 crate 时检测 ABI 不匹配和 AST 节点重命名 - 二进制格式:往返测试、拒绝损坏/截断/版本错误的数据、越界访问、字符串池去重、空索引
- 对抗性格式:20 项精心构造的索引测试——偏移溢出、错误的 magic/version、对齐攻击、记录截断
- 向量:384 维 f32 嵌入的写入/读取往返测试
- FST:refs FST 往返测试、前缀搜索、符号 FST 精确/前缀/模糊搜索
- 搜索:结构化搜索、模糊搜索(Levenshtein)、RRF 融合、带有类型/路径/邻近度加权的重排序
- 重排序压力测试:NaN/Infinity/零分数、10K 条结果、边缘上下文路径
- 基于属性的测试(proptest):重排序保持长度不变、输出有序、无 NaN/负分数、融合满足交换律
- 增量更新:未更改内容复用、删除内容移除、文件重命名、符号在文件间移动、空文件
- 并发:并行索引/更新(锁序列化)、并发读取器、重建索引期间读取
- 多语言:Rust、Python、Go、Kotlin、TypeScript、C++、跨语言同名、扩展名错误、1K 符号文件、深层嵌套、错误恢复
- Unicode:BOM、混合 CRLF、Unicode 标识符、空字节、空文件/仅含空白的文件
- 路径边界情况:路径含空格、深层嵌套(20 层)、符号链接、绝对路径与相对路径、Windows 反斜杠
- 调用图:Rust、Python、Go、TypeScript、Java 的调用方/被调用方
- 持久化调用图(v1.5):格式 v4 往返测试、调用方/被调用方 FST 查找、去重、隔离不同文件中的同名符号、区分同一文件中的同名符号、增量更新保留边、v3 时回退到实时扫描
- 相似项/重复项(v1.5):排除自身、阈值筛选、规范化配对去重、函数体长度筛选、空索引处理
- 可插拔嵌入器(v1.5):注册表查找、不匹配检测(包括对旧于 9.1 清单的向后兼容)、配置与 CLI 优先级、写入器变量
vector_dim - BM25 通道(v1.5):写入器/读取器往返测试、流水线输出、IDF 区分度、短文档优先、带 Hybrid 标签的三路 RRF、MatchType 标记、Unicode 词元
- 陈旧检测:git HEAD 比较、脏文件检测、mtime 回退
模糊测试
模糊测试使用 cargo-fuzz(libFuzzer + AddressSanitizer),对所有接收不可信输入的解析器进行测试——包括二进制索引格式、sidecar 文件、面向用户的模式语法,以及 JSON 清单:
# Install (once)
cargo install cargo-fuzz
# Generate seed corpus for every target
bash fuzz/generate_seeds.sh
# Run (requires nightly)
RUSTUP_TOOLCHAIN=nightly cargo fuzz run fuzz_index_reader -- -max_total_time=120
RUSTUP_TOOLCHAIN=nightly cargo fuzz run fuzz_refs_fst -- -max_total_time=60
RUSTUP_TOOLCHAIN=nightly cargo fuzz run fuzz_symbol_fst -- -max_total_time=60
RUSTUP_TOOLCHAIN=nightly cargo fuzz run fuzz_bloom_load -- -max_total_time=60
RUSTUP_TOOLCHAIN=nightly cargo fuzz run fuzz_pattern_parser -- -max_total_time=60
RUSTUP_TOOLCHAIN=nightly cargo fuzz run fuzz_manifest_load -- -max_total_time=60
RUSTUP_TOOLCHAIN=nightly cargo fuzz run fuzz_marker_load -- -max_total_time=60
RUSTUP_TOOLCHAIN=nightly cargo fuzz run fuzz_tokenize_document -- -max_total_time=60
RUSTUP_TOOLCHAIN=nightly cargo fuzz run fuzz_hash_index_load -- -max_total_time=60
RUSTUP_TOOLCHAIN=nightly cargo fuzz run fuzz_incremental_hnsw -- -max_total_time=60
RUSTUP_TOOLCHAIN=nightly cargo fuzz run fuzz_rename_chains_load -- -max_total_time=60
RUSTUP_TOOLCHAIN=nightly cargo fuzz run fuzz_state_load -- -max_total_time=60
18 个模糊测试目标覆盖了读取器的所有 unsafe 路径,以及所有接收对抗性输入的文本/sidecar 解析器:
| 目标 | 模糊测试内容 | 覆盖接口 |
|---|---|---|
fuzz_index_reader | 将任意字节作为 .vex 文件 | header()、symbol()、vector()、read_string()、file_paths()、ref_edge API |
fuzz_refs_fst | 任意 FST 和 posting 字节 | RefReader::find()、find_by_prefix()、find_ref_edges_by_symbol |
fuzz_symbol_fst | 任意 FST 和 posting 字节 | SymbolFstReader::find()、find_fuzzy()、search_with_fallback() |
fuzz_bloom_load (v1.12.0) | 任意 index.bloom sidecar | SymbolBloom::load,随后进行 may_contain 探测 |
fuzz_pattern_parser (v1.12.0) | 将任意 UTF-8 文本作为模式字符串 | parse_composite_pattern(元变量、&& / ` |
fuzz_manifest_load (v1.12.0) | 将任意 JSON 作为 manifest.json | Manifest::load(128 MiB 大小上限 + JSON 解析) |
fuzz_marker_load (v1.13.0) | 将任意文本作为 <onnx>.sha256.marker | verify_with_marker 解析器 + 决策树 |
fuzz_tokenize_document (v1.13.0) | 将任意 UTF-8 文本作为 BM25 输入 | tokenize_document(共享所有权的 String 重构之后) |
fuzz_hash_index_load (v1.14.1) | 将任意字节作为 index.hashes sidecar | hash_index::load(VEXH magic、MAX_COUNT 防护、截断) |
fuzz_incremental_hnsw (v1.15.0) | 对抗性的 new_hashes 切片 | build_hnsw_incremental_at(重复项、墓碑、去重并跳过) |
fuzz_rename_chains_load (v1.17.0) | 将任意字节作为 index.rename_chains sidecar | rename_chains::load(VEXR v1、MinHash + LSH 重放) |
fuzz_state_load (v1.18) | 将任意字节作为 index.state sidecar | incremental_state::load(VEXS v1、256 MiB 上限、bincode 负载) |
fuzz_unresolved_refs (v1.22.0) | 任意 FST、posting 和 edge 字节 | UnresolvedRefReader(v7 unresolved_refs 区段、多仓库严格回退) |
fuzz_kotlin_binder (v1.23.0) | 将任意字节作为 Kotlin 源码 | tree-sitter 解析 → 符号提取 → Kotlin bind_refs |
fuzz_unresolved_hierarchy (v1.25.0) | 任意 FST、posting 和 edge 字节 | UnresolvedHierarchyReader(v8 unresolved_hierarchy 区段) |
fuzz_csr (v1.27.0) | 任意 offsets / edge_idx 字节以及 n / m | CsrView::new + neighbors(v9 CSR、callees 和 ref-edge 形态) |
fuzz_leiden (v1.27.0) | 将任意字节解码为 ≤256 个节点的图 | 确定性 Leiden-CPM(运行两次:输出相同、每个簇均连通) |
fuzz_cluster_section (v1.27.0) | 将任意字节作为索引文件打开 | ClusterSectionReader 入口点(index.vex 中的 v9 clusters 区段) |
最近一次系统级审计(Q4-A/B 收尾,2026-06-17):当时存在的全部 11 个目标共计 约 7,600 万次
执行,0 次崩溃 / panic /
AddressSanitizer 命中 / 泄漏(每个目标 61 秒,libFuzzer +
ASan + nightly)。在此过程中,在对抗性的
ref_edge 字节触发的死代码路径中发现一个潜在的 FST panic,并在
干净运行之前修复——find_ref_edges_by_symbol 和 RefReader::find_by_prefix
现在会用 catch_unwind 包装内部 FST 遍历,因此损坏的 sidecar
会返回 Err,而不是导致进程退出。此前的基线:
v1.14.1(2026-06-05)在 9 个目标上运行了 580 万次迭代,结果干净;
v1.15.2 发布门禁(2026-06-08)在
信号最强的 4 个目标上运行了约 85.3 万次专项执行,结果干净。
模糊测试在整个项目生命周期中发现并修复了 8 个真实缺陷:
- v1.x:针对精心构造的
symbol_count进行越界读取;在奇数symbols_offset上解引用未对齐的指针;未检查超出文件大小的节偏移(强化二进制读取器)。 - v1.12.0:
SymbolBloom::load接受了n_bits = 0、k_num = 0的 sidecar。其一致性检查通过,但之后在bloomfilter::Bloom::check中因hash % 0触发 panic。修复:加载时拒绝退化尺寸。 - v1.12.0:
SymbolBloom::load接受了最高约 21 亿的k_num,导致每次may_contain调用都循环 110 秒以上(DoS,而非 panic)。修复:加载时将k_num限制为k_num <= MAX_K_NUM = 64。 - Phase 11.1.10 / Q4-A(2026-06-17):
find_ref_edges_by_symbol中的 FST 遍历会因精心构造的 refs FST 触发 panic,绕过生产环境中的catch_unwind(libfuzzer-sys的 panic hook 会在用户代码拦截之前触发)。修复:在内部遍历外层增加独立的catch_unwind,并返回Err。同时对Manifest::load进行了纵深防御强化:128 MiB 的预读大小上限可在 serde 分配数 GB 堆内存之前拦截恶意 JSON(Q4-B 审计后续措施;纵深防御,威胁模型针对用户拥有的文件)。 - v1.23.0:一个 451 字节的格式错误 Kotlin 输入使 tree-sitter 的 GLR 错误恢复进入超线性时间和内存消耗(334 秒、超过 2 GB;DoS,而非崩溃;由
fuzz_kotlin_binder发现)。修复:所有生产解析都通过parser_pool::parse_text执行,该函数会根据输入大小限制所有语言的进度回调调用次数。 - v1.23.0:
tree_sitter::Node::utf8_text()在格式错误的输入导致 tree-sitter 生成越过 EOF 的节点时触发 panic(由fuzz_kotlin_binder发现)。修复:在提取器、绑定器和模式预过滤器中,使用带边界检查的NodeTextExt(node_text/node_text_opt)替代原始utf8_text。
v1.13.0 / v1.14.1 新增的内容在新代码中未发现缺陷——由审查推动的 hash_index::save / load 中 MAX_COUNT 检查是在模糊测试运行前添加的纵深防御措施(rust-reviewer + code-reviewer 指出了 save 中会截断的 as u32 类型转换),而持续运行 300 万 / 580 万次迭代也证实了这些检查有效。
架构
CLI (clap) → Pipeline (rayon, 500-file chunks) → Tree-sitter
↓
Binary format v9 (mmap, zero-copy)
↓
┌──────────────────┬──────────────┬──────────────┬──────────────┬─────────────┬──────────────┐
↓ ↓ ↓ ↓ ↓ ↓ ↓
Symbol FST Refs FST BM25 doc HNSW vectors Call graph Hierarchy Clusters
(structural) (cross-file refs) (body tokens) (semantic) (callers FST / (v8 edges) (v9 Leiden-
callees CSR) CPM)
↓
Embedder trait → fastembed / MiniLM-L6 (default)
Per-project sidecars (in <index_dir>/):
· index.vex — primary index (format v9)
· manifest.json — metadata: embedder, sections, version, staleness tracking
· index.bloom — symbol-name bloom filter (`vex check` skips FST lookups for definitely-missing names)
· index.trigram — per-file trigram bloom so `vex grep` skips non-matching files (v1.24.1)
· index.hnsw — semantic vectors (HNSW graph)
· index.bodytokens — per-symbol terms for BM25 + semantic context (B1.2)
· index.git_history — historical symbol presence (Phase 14.8, FST + git-walk fallback)
· index.rename_chains — MinHash+LSH rename tracking across commits (Phase 14.10)
· index.state — incremental state: imported_by reverse map + writer-provenance sentinels (audit C1)
Shared cross-project (in user cache root, e.g. ~/Library/Caches/vex/blobs/):
· {sha}.bin shards — content-addressed parse cache, keyed by git blob SHA (Phase 14.7)
Search pipeline:
search → Symbol FST + BM25 + HNSW → N-way RRF fusion → Hybrid tag on cross-channel hits
usages → Refs FST + posting lists → zero-copy, --strict adds type-aware filter (v1.14.1)
history → walks git tree, follows rename chains via Jaccard + greedy 1:1 (Phase 14.10)
similar → HNSW nearest neighbors (hash-keyed for content-stable IDs)
show → tree-sitter node boundaries → symbol body extraction
pattern → AST-aware structural matcher with skeleton index
- 无 SQLite — 自定义二进制格式 v6,零拷贝 mmap 读取;读取器接受 v3+,以保持向后兼容
- 符号 FST — 持久化倒排索引,查找复杂度为 O(query_len)
- Refs FST + ref_edges — 将符号引用存储为 FST,并在写入时解析跨文件边(
store::writer中的 Pass-2);支持达到重构级别的usages --strict - 持久化调用图 —
CallEdge记录 + 按名称键控的 callers FST + 密集 callees CSR 索引(v9+;较旧索引使用 FST);在索引时构建,查询约需 4 毫秒,而实时扫描 tree-sitter 需要数秒 - BM25 通道 — 对
body_tokens使用 Okapi BM25,存在对应节时自动启用 - HNSW — 通过 usearch 实现近似最近邻搜索,语义搜索复杂度为 O(log N);条目按哈希键控,使内容稳定 ID 在重新索引后保持不变
- 可插拔嵌入器 —
Embeddertrait + 注册表;在清单中记录身份,并在搜索时检测不匹配 - 历史索引 — 每次提交中的符号存在情况 + 基于 MinHash 的重命名链跟踪(解决 LIMITATIONS §4c #2 中的 1:1 重命名问题)
- 并行解析 — 使用 rayon,每批处理 500 个文件;blob-SHA 解析缓存(在用户缓存根目录下的项目间共享)会跳过重新索引时未变文件的重新解析
- 增量更新 — 使用 xxh3 进行内容哈希;
vex update仅重新解析变更文件(从现有索引重建未变符号 + 调用边) - 监视模式 — 使用
notifycrate,防抖间隔为 500 毫秒 - N 路 RRF 融合 —
fuse_many合并结构化、BM25 和语义排序列表,并将跨通道命中项标记为Hybrid - 排序评估框架 — 在捆绑的黄金集上运行
vex eval;CI 回归门槛依据平均 nDCG@10、各查询类型下限和各通道归因(Phase 13.12 / 13.12.1)
许可证
MIT
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。