indexio 发布:面向编码智能体的代码搜索引擎,通过 MCP 接入并宣称减半 token 用量
I cut my agent token usage in half
indexio 是一个面向 AI 编码智能体的代码搜索引擎,一次性索引组织内所有仓库,以毫秒级响应查询,并通过 MCP 和 HTTP API 作为工具提供给智能体。
indexio 是一款面向 AI 编码代理的代码搜索引擎。它一次性索引一个组织的所有仓库。它能在毫秒内回答查询。它通过模型上下文协议(MCP)和 HTTP API 将索引作为工具提供给代理。
indexio 是一个用 Rust 编写的单一二进制文件。它不需要数据库或外部服务。索引是一个数据目录中的一组文件。你可以将该目录复制到另一台机器。
让你的代理安装它
将下面的代码块粘贴到 Claude Code、Cursor、Codex 或任何能运行 shell 命令的编码代理中。它会安装 indexio、索引你的代码、注册 MCP 服务器并验证结果。将 ~/code 替换为存放你仓库的文件夹。
Install indexio (https://github.com/IOServicesLabs/indexio) on this machine and connect it
to me. Do these steps in order and stop at the first failure with the exact error.
1. Install the binary.
- Linux or macOS: curl -fsSL https://raw.githubusercontent.com/IOServicesLabs/indexio/main/install.sh | sh
- Windows (PowerShell): irm https://raw.githubusercontent.com/IOServicesLabs/indexio/main/install.ps1 | iex
- If neither works, use `pip install indexio-cli`, `npm install -g indexio`, or `cargo install --git https://github.com/IOServicesLabs/indexio indexio`.
The binary lands in ~/.local/bin (Linux, macOS) or %LOCALAPPDATA%\Programs\indexio (Windows).
If `indexio --version` is not found afterwards, add that folder to PATH and open a new shell.
`git` must be on PATH.
2. Index my code: indexio add ~/code
This registers every git repository under the folder at any depth and builds the index.
Nothing is uploaded; the index lives in ~/.indexio.
3. Register the MCP server with the agent you are.
- Claude Code: indexio setup claude --claude-md ~/.claude/CLAUDE.md
then indexio hook install
(the hooks route file reads and greps through the index; scripts and builds run as before)
- Any other MCP client: add this server to its MCP configuration and restart the client:
command: indexio args: ["mcp"]
Use the absolute path of the binary if the client does not search PATH.
4. Verify. All four must succeed:
- indexio --version prints a version
- indexio stats lists the repositories and a file count above zero
- indexio search "TODO" --limit 3 returns hits from my code
- indexio usage prints a report (it is empty until I use the tools)
5. Tell me what was installed, where the data directory is, which repositories were
indexed, and that I must restart my agent session so it picks up the new MCP server.
Do not modify any file inside my repositories.
之后代理会得到:code_search、code_grep、find_symbol、who_calls、file_outline、read_span、impact_of_symbol、impact_of_diff 和 recall 作为工具,从索引中在毫秒内得到回答。工具表描述了每一个工具。
安装
任选其一。每一种都会给你 indexio 命令。
| 平台 | 命令 |
|---|---|
| Linux、macOS | curl -fsSL https://raw.githubusercontent.com/IOServicesLabs/indexio/main/install.sh | sh |
| Windows(PowerShell) | irm https://raw.githubusercontent.com/IOServicesLabs/indexio/main/install.ps1 | iex |
| npm(任意平台) | npm install -g indexio |
| pip(任意平台) | pip install indexio-cli |
| Docker | docker pull ghcr.io/ioserviceslabs/indexio |
| Homebrew、winget | 尚未支持;请使用脚本或上述某个包 |
| 从源码构建(Rust 1.85+) | cargo install --git https://github.com/IOServicesLabs/indexio indexio |
这些脚本和 npm 包会下载适用于你的操作系统和 CPU 的最新 release 二进制文件,并校验其 SHA-256。pip wheel 内置了二进制文件,每个平台一个 wheel。pip 包名为 indexio-cli,因为 PyPI 上的 indexio 属于一个不相关的项目;它安装的命令是 indexio。容器镜像完全无需安装即可使用:参见 使用 Docker 部署。git 必须在 PATH 中。无需服务、数据库或模型下载。
升级
再次运行相同的安装命令。~/.indexio 中的索引会保留,你无需重新索引。
| 安装方式 | 升级命令 |
|---|---|
| 脚本,Linux 或 macOS | curl -fsSL https://raw.githubusercontent.com/IOServicesLabs/indexio/main/install.sh | sh |
| 脚本,Windows | irm https://raw.githubusercontent.com/IOServicesLabs/indexio/main/install.ps1 | iex |
| npm | npm install -g indexio@latest |
| pip | pip install -U indexio-cli |
| Docker | docker pull ghcr.io/ioserviceslabs/indexio:latest,然后重新创建容器 |
| 源码 | cargo install --git https://github.com/IOServicesLabs/indexio indexio --force |
然后还有两个步骤:
- 运行
indexio hook install。它会添加新版本带来的钩子并修复其他钩子。它会保留你自己的钩子。 - 重启你的代理会话。正在运行的会话会一直保留其 indexio 服务器,直到重启。
正在运行的会话不会阻止你升级:
- 这两个脚本会在会话使用二进制文件时替换它。在 Windows 上,
install.ps1会将正在运行的indexio.exe移动到indexio.old.exe,然后复制新的进去。 - 在 Windows 上,npm 和 pip 会尝试就地覆盖二进制文件,而 Windows 不允许在它运行时这样做。请先关闭你的代理会话,或使用脚本。
- 仍在运行旧版本的会话可以读取新版本写入的索引。
- 第一个新服务器会在后台完成其一次性工作:它为没有向量的文件生成嵌入,并写入会话卡片。调用不会被阻塞。
快速开始
三条命令。第三条仅适用于 Claude Code。
indexio add ~/code # 1. index every git repository under a folder indexio search parse_config # 2. search it (add --mode hybrid for a question in words) indexio setup claude # 3. register the MCP server with Claude Code
然后重启 Claude Code。它的会话现在拥有 indexio 工具。两条可选命令可以带来更大的节省:
indexio hook install # reads and greps of indexed files go through the index indexio sync # run from cron or a timer to keep the index current
indexio stats 显示已索引的内容。indexio usage 显示索引提供的 token 数与内置工具本会返回的 token 数的对比。
所有数据都放入一个目录:--data-dir、$INDEXIO_DATA_DIR 或 ~/.indexio。
目录
- 让你的代理安装它
- indexio 的功能
- 添加源
- 保持索引最新
- 嵌入
- 永不索引的内容
- 保护 HTTP API
- 团队索引:实时工作树叠加
- 企业设置
- 在 Claude Code 中使用 indexio
- 实测 token 节省
- 影响分析
- 从其他工具使用 indexio
- 命令参考
- 环境变量
- 使用 Docker 部署
- 架构
- 实测性能
- 限制
- 开发与发布
indexio 的功能
indexio 回答关于组织代码的三类问题。
| 类型 | 示例 | 方法 |
|---|---|---|
| 词法 | WalkState::new 在哪里定义?哪些 Rust 文件匹配 /fn parse_*/? |
Trigram 索引、正则表达式、精确标识符 |
| 语义 | 我们如何缓冲打印机输出?我们在哪里刷新认证令牌? | 对语法感知的代码块进行向量搜索 |
| 混合 | 任何问题 | 词法、BM25 和语义结果,通过倒数排名融合(RRF)合并 |
indexio 还了解代码的定义和调用图。它可以告诉你某个符号在哪里定义、谁调用了它,以及修改它会影响什么。
添加源
每个 add 会立即索引源代码并构建语义平面。
indexio add ~/code # every git repository under the folder, at any depth indexio add /srv/checkouts/legacy-monolith # a folder without git: indexed as-is indexio add github:my-org # a GitHub organization or user indexio add github:my-org/payments # one GitHub repository indexio add azdo:my-org/Platform # an Azure DevOps project indexio add https://gitlab.example.com/g/r.git # any git host
搜索:
indexio search parse_config
indexio search "how is backpressure applied" --mode hybrid将索引提供给智能体:
indexio mcp # MCP over stdio indexio serve # HTTP API on http://127.0.0.1:7717
indexio 在 <data-dir>/sources.json 中记住每个源。indexio sources 列出源和已注册的仓库。indexio remove <source> 忘记一个源。
add 的有用标志:--limit 20(在大型组织上进行试运行)、--include-forks、--include-archived、--full-clone、--dest DIR(远程克隆存放的位置)、--no-embed、--exclude PATH、--pdf-text。
包含仓库和其他文件的文件夹。一个文件夹通常包含 git 仓库以及不属于任何仓库的文件:笔记、规格说明、导出文件、PDF。indexio add 将每个仓库作为仓库索引,并将其他文件作为一个以该文件夹命名的额外仓库索引:
projectA/ indexio add ~/projectA
├── backend/ (git) → repository "backend"
├── frontend/ (git) → repository "frontend"
├── docs/notes.md → repository "projectA": the contents
├── docs/spec.pdf → repository "projectA": the name only
└── old-copy/ → left out with --exclude old-copy
- 文本文件按内容索引,直至大小限制(文本为 256 KiB,代码为 4 MiB)。
- 文档(PDF、Word、Excel、PowerPoint、OpenDocument、RTF、EPUB)以及超过限制的文本文件仅按名称索引。
list_files和按名称中词语的搜索可以找到它们。对其中一个执行read_span会提示其内容未被索引;请读取文件本身。钩子允许对此类文件的 Read 操作通过。 --pdf-text会索引这些 PDF 的文本层,而不是它们的名称。从文档(Word、Google Docs、浏览器)导出的 PDF 具有文本层,因此搜索可以找到其中的词语。扫描版 PDF 没有文本层,会保留仅按名称的条目:indexio 不进行 OCR。提取的文本受文本大小限制。超过 64 MiB 的 PDF,或读取时间超过 20 秒的 PDF,会保留仅按名称的条目。该选项会随源一起记住。- 图像、媒体、归档文件和其他二进制文件不会被索引。
--exclude PATH(可重复,相对于文件夹)会将某个文件夹排除在外:它不会被视为仓库,其文件也不会被索引。排除项会随源一起记住,并在你再次运行add时累积。- 单独注册的文件夹不会被第二次索引。
凭据。indexio 仅从环境读取凭据:GITHUB_TOKEN(或 GH_TOKEN)和 AZDO_TOKEN(或 AZURE_DEVOPS_EXT_PAT、SYSTEM_ACCESSTOKEN)。它会将它们作为授权头发送给列表 API 和 git。它绝不会将它们放入克隆 URL、日志或 .git/config 中。公共仓库不需要令牌。
保持索引最新
从 cron 或定时器运行 indexio sync,例如每 15 分钟一次。每次运行都是增量的:
- 新仓库会被克隆。
- 发生更改的 git 仓库按提交进行增量索引。
- 发生更改的普通文件夹按内容哈希进行增量索引。
- 新增和更改的块会被嵌入。所有其他块都是缓存命中。
包含 156 个仓库和 17,530 个文件的文件夹树在 11 秒内以无操作方式同步。
正在运行的 indexio mcp 服务器会自行做更多事情:
- 它会监视会话所在的工作仓库。当文件发生变化时,它会在每次调用前重新索引工作树。未提交和未跟踪的编辑内容可立即被搜索;它们的嵌入会在几百毫秒内于后台线程中跟进。
- 每 10 分钟,它会探测其他仓库是否有 HEAD 移动,并重新索引那些发生移动的仓库,同时按内容重新索引发生变化的普通文件夹(第一遍之后,未变化的文件只需一次 stat)。
- 如果会话在其文件夹注册之前就已启动,它会在
indexio add后的一分钟内采用该仓库。无需重启。
嵌入
内置模型
默认嵌入器是 Random Indexing(rindex)。这是一种语义模型,indexio 在进程内基于你自己的代码、在 CPU 上、通过一次流式遍历构建。它不需要训练基础设施、不需要下载、也不需要 GPU。它学习你组织的词汇表:内部服务名称、代码名称和 API,这些是任何预训练模型都不知道的。
每个新块都是一次增量更新,因此 indexio sync 保持低成本。
在 20 个困难概念查询上测量(bench/):混合 recall@5 为 60%,MRR 为 0.397。仅使用词法搜索在同一组查询上为 5% 和 0.050。
可选的神经端点
为了获得最佳检索质量,可将 indexio 指向任何 OpenAI 兼容的嵌入端点,例如 vLLM 或 TEI 上的 Qwen3-Embedding。这两个模型命名空间共存。
export INDEXIO_EMBED_BASE=http://embed.internal.example:8080 export INDEXIO_EMBED_MODEL=Qwen/Qwen3-Embedding-8B export INDEXIO_EMBED_DIM=1024 export INDEXIO_EMBED_KEY=... # only if the gateway needs it indexio embed # builds vec/<model-id>.*.civec
嵌入按 (chunk hash, model id) 进行内容寻址。两个共享同一文件的仓库共享其嵌入。跨组织的重复块不会产生额外成本。
可选的重排序器
添加一个重排序器端点,例如 TEI 或 vLLM 上的 Qwen3-Reranker。当查询要求时,indexio 会使用它:indexio search --mode hybrid --rerank、通过 HTTP 的 rerank=1,或通过 MCP 的 rerank: true。没有端点时,--rerank 不起作用。
export INDEXIO_RERANK_BASE=http://rerank.internal.example:8081 export INDEXIO_RERANK_MODEL=Qwen/Qwen3-Reranker-8B export INDEXIO_RERANK_KEY=...
永不索引的内容
- 没有已知代码、文档、配置、脚本或数据扩展名的文件,例如数据库、图像、归档文件和编译输出。
- 二进制内容:前 8 KiB 中出现 NUL 字节则跳过该文件。超过 4 MiB 的文件,以及超过 256 KiB 的文本文件。在普通文件夹中,这些文件和文档(PDF、Office)仅按名称索引(参见 添加源)。
- 构建输出、依赖和缓存文件夹(
node_modules、target、dist、vendor,任何带有CACHEDIR.TAG的文件夹)、被 git 忽略的文件、除配置文件夹(.github、.cargo、.vscode、.devcontainer…)之外的隐藏文件夹、lock、压缩和 source-map 文件。 - 凭据:
.env文件、私钥和证书(pem、key、p12、pfx、jks、gpg…)、.netrc、.npmrc、Terraform state。由早期版本索引的凭据文件会在下一次同步时被移除。.env.example和*.pub会保留。
recall 搜索的会话记录和运行日志在存储前会被脱敏:看起来像机密的设置值、URL 中的密码、API 密钥、bearer 令牌、JWT 和私钥块都会被替换为 <redacted>。
保护 HTTP API
默认情况下,indexio serve 绑定到回环地址。
indexio serve --auth-token $(openssl rand -hex 32) # one bearer token
如需按令牌控制仓库访问,请使用 ACL 文件:
cat > acl.json <<'EOF' {"tokens":{"indexio-reader":{"allow":["team-*","core"]},"indexio-admin":{"allow":["*"]}}} EOF indexio serve --acl-file acl.json
使用 ACL 文件时:
- 未知令牌会收到 HTTP 401。
- 超出允许模式的命中会从
/search、/symbol和/calls中移除。 - 管理路由需要带有
allow: ["*"]的令牌。其他令牌会收到 HTTP 403。 GET /health始终开放。
注意。除非你还设置了 --auth-token、$INDEXIO_AUTH_TOKEN 或 --acl-file,否则会拒绝绑定到回环地址(--bind 0.0.0.0)以外的地址。通过 stdio 运行的 MCP 服务器以本地用户身份运行,并且按设计没有身份验证。请将面向网络的 MCP 服务器置于你自己的网关之后。
团队索引:实时工作树叠加
indexio serve 可以把每个开发者未提交的更改叠加到共享索引之上,因此回答你的 agent 看到的是你的工作树——编辑、未跟踪文件、删除——而其他人仍然看到已推送的状态。开发者机器上除了 agent harness 中的一个 shell hook 外无需安装任何东西:不需要 indexio 客户端。
工作原理
- 服务器索引已推送的仓库(通常的
add+sync)。 - agent harness 中的
PreToolUsehook 会在每次 indexio 工具调用前运行integrations/claude-code/indexio-team-sync.sh。该脚本将工作树(包括未跟踪文件)与远程默认分支的 merge-base 进行 diff,并将补丁 POST 到/team/worktree。 - 服务器将补丁应用到一个以调用者用户名为键的小型 overlay 分片中,叠加在基础分片之上:更改的路径遮蔽基础索引,删除的路径被隐藏,其余一切从共享索引读取。应用时在临时索引上使用 git plumbing——无需 checkout,无需 worktree。
- 最后一个被接受的补丁的哈希会保存在磁盘上,因此工作未变更时的重复调用不产生任何开销。该 hook 不打印任何内容且始终以 0 退出:它永远不会阻塞工具调用或污染 agent 的上下文。
Overlay 在 INDEXIO_TEAM_TTL_DAYS(默认 7)内无更新则过期。
服务器设置
使用 ACL 文件提供服务,其 token 携带 "user"——该名称作为 overlay 的键:
cat > acl.json <<'EOF' {"tokens":{ "tok-alice":{"allow":["*"],"user":"alice"}, "tok-bob": {"allow":["*"],"user":"bob"}, "tok-ci": {"allow":["*"]} }} EOF indexio serve --acl-file acl.json --bind 0.0.0.0
不带 "user" 的 token 可用于读取但没有 overlay(CI,见上文)。使用单个 --auth-token 而非 ACL 文件时,客户端通过 X-Indexio-User 头选择其用户。
开发者设置
indexio setup claude --team-sync # writes the hook script, prints the entry export INDEXIO_URL=https://indexio.example.com export INDEXIO_TOKEN=tok-alice
setup claude --team-sync 将 hook 脚本写入 Claude 配置目录,并打印 PreToolUse 条目用于 settings.json——matcher 为 mcp__indexio__.*,因此同步会在任何 indexio 工具调用前立即运行。使用单个共享 token 而非每用户 token 时,还需将 INDEXIO_USER 设置为要扮演的名称。
服务器端强制执行的内容
- 凭据路径(
Lang::is_secret_path)永远不会被接受进入 overlay,超过 32 MiB 的补丁也不会。 - ACL
allow模式过滤每个用户读取的内容——对 MCP 而言作用于文档本身(已渲染的文本无法取消显示),对 REST API 而言作为命中过滤。 - Overlay 用户名被限制为
[A-Za-z0-9._@-],不能以点开头,因此 overlay 状态保持路径安全(SSO 身份通常是电子邮件)。
限制
- Overlay 是词法平面。向量搜索对基础内容进行排名;对已跟踪文件的编辑会通过每个工具显现,而全新的未跟踪文件和删除对词法、符号和大纲工具可见。
- 基础必须是服务器拥有的提交(与
origin/HEAD的 merge-base,否则为origin/main、origin/master或上游分支),否则上传会被拒绝并返回 HTTP 409,并在下次调用时重试。服务器自行获取 origin,速率限制为每分钟一次。 - 该 hook 针对 Claude Code 调优发布;
PreToolUse契约是其他 harness 正在趋同的那个,但只有 Claude Code 经过测试。
企业设置
以上所有内容都在一台机器上运行。同一个二进制文件可扩展到团队服务器或组织范围的部署;变化的部分是网络边缘和身份验证。
网络内部(LAN / VPN)
最简单的拓扑:一台 indexio 服务器位于内部主机上,绑定到私有接口;在可信网络上 TLS 可选。
indexio serve --bind 10.0.0.5 --acl-file acl.json
开发者通过 INDEXIO_URL + INDEXIO_TOKEN 将团队同步 hook 指向它。从 ACL 文件开始;当分发 token 成为负担时,转向 SSO(见下文)。
面向互联网
服务器使用纯 HTTP 通信,且在没有凭据时拒绝广泛绑定,因此将其置于终止 TLS 并转发到回环端口的反向代理之后。一个最小的 Caddy 站点:
indexio.example.com {
reverse_proxy 127.0.0.1:7717
}
然后使用 SSO 而非静态令牌来提供服务:短期身份令牌胜过笔记本电脑上的长期 bearer 令牌。
使用 OIDC 提供商(Entra ID、Google、Keycloak)进行 SSO
--oidc-issuer + --oidc-audience 取代静态令牌模式。服务器在 <issuer>/.well-known/openid-configuration 处发现提供商,缓存其 JWKS,并在查找身份之前验证每个 bearer 令牌(RS256、issuer、audience、expiry)——失败即关闭:
cat > users.json <<'EOF' {"users":{ "alice@corp.example":{"allow":["*"],"user":"alice"}, "bob@corp.example":{"allow":["payments-*"]} }} EOF indexio serve \ --oidc-issuer https://login.microsoftonline.com/<tenant-id>/v2.0 \ --oidc-audience <application-id-uri> \ --acl-file users.json --bind 0.0.0.0
- 身份是令牌的
email声明(否则为sub)。users.json中不存在的身份会得到 HTTP 401:配置来源是你现有的目录,通过手动或同步任务镜像到此文件中。 allow的工作方式与令牌 ACL 完全相同;/admin仍然需要*。"user"设置团队覆盖名称——没有它则使用电子邮件。- IdP 细节。Entra ID:一个带有公开 API 的应用注册;audience 是 Application ID URI。Google Workspace:一个 Web 应用程序 OAuth 客户端;audience 是客户端 ID。Keycloak:你 realm 中的一个机密客户端;audience 是客户端 ID,issuer 是
https://<host>/realms/<realm>。 - 签名密钥无需重启即可轮换:未知的
kid会按需重新获取 JWKS。 - OIDC 模式取代
--auth-token/INDEXIO_AUTH_TOKEN;将它们留空。--oidc-audience需要--oidc-issuer。
试一试:十分钟内搞定 Google Workspace
确切的流程,已于 2026-09-27 针对实时服务器进行端到端验证。
-
Google Cloud 控制台:创建一个项目(比如
indexio-pilot)。 -
APIs & Services → OAuth consent screen:External;应用名称
indexio pilot;范围openid和email;在 Test users 下添加你自己的 Google 账户——当应用处于“Testing”状态时,只有测试用户可以同意。这是每个人都会忘记的步骤。 -
Credentials → Create OAuth client ID → Web application:添加授权重定向 URI
https://developers.google.com/oauthplayground。 -
在 https://developers.google.com/oauthplayground 生成一个真实的 ID 令牌:齿轮图标 → “Use your own OAuth credentials” → 粘贴客户端 ID 和密钥 → 范围
openid email→ Authorize APIs → 同意 → Exchange authorization code for tokens → 复制id_token。 -
以客户端 ID 作为 audience,并将你的电子邮件放入 users 文件来提供服务:
cat > users.json <<'EOF' {"users":{"you@corp.example":{"allow":["*"],"user":"pilot-you"}}} EOF indexio serve --bind 0.0.0.0 \ --oidc-issuer https://accounts.google.com \ --oidc-audience <client-id>.apps.googleusercontent.com \ --acl-file users.json
-
从任何地方进行冒烟测试:
curl -H "Authorization: Bearer $ID_TOKEN" http://indexio.example.com:7717/search?q=parse # 200 with the token; 401 without. The token lives one hour — re-run # the playground exchange for a fresh one.
除了 playground 之外,任何能运行 OAuth 同意流程的东西都可以生成服务器接受的 bearer:IDE 插件、服务器前面的小型登录页面,或 CI 任务。
GitHub
GitHub 不是你自有 API 的通用 OIDC 提供商,但有两种模式可以覆盖它:
-
GitHub Actions → indexio 已经是 OIDC。 带有
permissions: id-token: write的任务请求一个 JWT(issuer 为https://token.actions.githubusercontent.com,audience 由你选择),并将其作为 bearer 令牌交给 indexio 客户端。服务器像验证任何 OIDC 令牌一样验证它:indexio serve --oidc-issuer https://token.actions.githubusercontent.com \ --oidc-audience indexio --acl-file users.json
CI 机器不持有静态令牌,
users.json中的允许列表限定了每个工作流可以读取的内容。 -
拥有 GitHub 账户的人类。 在前面放置一个 OIDC 网关(为 GitHub 配置的 oauth2-proxy),并在网关和 indexio 之间保留 ACL 文件令牌,或者将人类访问迁移到 Entra ID / Google,那里才是目录支持的配置所属之处。
人类 vs 代理
让这两类群体使用不同的凭据:人类使用 SSO 短期令牌,代理和 CI 使用限定在其所接触仓库的 ACL 文件令牌(allow: ["payments-*"])。撤销人类是禁用其目录账户;撤销代理是删除 ACL 文件中的一行。
将 indexio 与 Claude Code 一起使用
indexio 通过 stdio 使用 MCP。这是 Claude Code、Claude Agent SDK 以及大多数编码代理的原生工具接口。
注册服务器
执行以下之一:
-
运行
indexio setup claude。它会为此二进制文件和数据目录运行claude mcp add,并打印针对你的CLAUDE.md的指导块。添加--claude-md ~/.claude/CLAUDE.md以追加该块。 -
运行
claude mcp add indexio -- /usr/local/bin/indexio mcp --data-dir /var/lib/ciindex。 -
向项目提交一个
.mcp.json:{ "mcpServers": { "indexio": { "command": "/usr/local/bin/indexio", "args": ["mcp", "--data-dir", "/var/lib/ciindex"] } } }
安装 hooks
indexio hook install
这会将四个 Claude Code hooks 写入 ~/.claude/settings.json:
| Hook | 作用 |
|---|---|
| PreToolUse, Bash | 对已索引文件的 shell 读取或搜索(cat、sed -n、head、grep、rg、find,或仅打开文件的 python -c 或 node -e 单行命令)会被拒绝。拒绝信息会指出能给出相同结果的 indexio 调用。同时做其他工作的命令,例如脚本或构建,会按原样运行。 |
| PreToolUse, Read | 对已索引文件的整文件 Read 会被拒绝。拒绝信息会指出 file_outline 和 read_span。 |
| PreCompact | 会话记录会被导入 recall 工具。 |
| SessionStart | 新会话会获得一行关于同一项目中上一个会话的信息:其标题、日期、更改的文件、提交,以及用于其卡片的 recall 调用(70–115 tokens)。压缩后,会话会取回自己更改的文件和提交(75–300 tokens)。恢复的会话不会获得任何内容。 |
Bash hook 还会将脚本和构建(python、node、npm、pytest、go、
make …)路由到 indexio run,从而让长输出不进入上下文:返回开头
几行、错误行和最后几行,并附带一个 runs:<repo>/<log> 指针,
完整输出在 30 天内仍可搜索。
要按原样运行被拒绝的命令,请向其添加 # raw 并再次发送,或设置
INDEXIO_RAW=1。
工具
| 工具 | 用于 |
|---|---|
code_search |
字面或正则搜索(mode: "lexical")、概念搜索("semantic"),或两者兼有("hybrid")。看起来像 grep 模式且无匹配的词法查询会作为正则运行。由多个词组成且无匹配的词法查询会作为混合搜索运行。 |
code_grep |
跨所有仓库的 grep -n:每个文件的每一匹配行,按排名排列。repo 和 path 参数,或末尾的 repo:、path: 和 lang: 过滤器。不匹配的括号会被视为字面量。 |
find_symbol |
函数、结构体、类、trait 或 enum 的定义位置。 |
who_calls |
某个符号的每个已记录调用者。 |
semantic_search |
仅向量搜索。 |
list_files |
对已索引路径进行 glob 或子串匹配,按目录折叠。没有模式会列出每个文件。 |
file_outline |
某个文件的每个定义及其起始行和结束行,或 markdown 文件的标题。只需几百个 tokens,而不是整个文件。mod tests 或 Test… 类的测试在一行中:名称和起始行。 |
read_span |
已索引文件的精确行范围。没有结束行时,返回起始行处的整个定义。 |
impact_of_symbol |
某个符号跨所有仓库的传递调用者。 |
impact_of_diff |
被补丁或未提交工作树触及的调用者和导入者。 |
refresh_index |
从会话内部进行增量重新索引、嵌入和重新加载。 |
recall |
搜索较早的会话和已存储的命令输出:请求、回答、工具调用、结果、构建和测试日志。单独一条命中会附带其交互。使用 brief: true 时,它会改为返回会话卡片:每个会话约 1,000 tokens,包含请求、更改的文件、提交和结果。query: "" 给出此项目的最新会话。 |
index_stats |
已索引的内容、其最新程度,以及过去一周相对内置工具的使用情况。 |
服务器会告诉 Claude 每个 indexio 工具替代哪个内置工具,以及哪些 仓库已被索引。然后 Claude 会自行将标识符路由到词法搜索,将 问题路由到混合搜索。它通过大纲和跨度读取文件,而不是读取整个文件。
结果是密集的纯文本:分组的 repo:path 和 line: snippet 行、折叠的文件
列表、带编号的跨度。没有分数、排名或 JSON 标点。对于相同的命中,这比紧凑 JSON 少用 54% 的 tokens。设置 INDEXIO_MCP_FORMAT=json 以使用 JSON 形式。
Token 节省量,实测
与内置工具相比,在相同任务上
bench/ab_readme.py 通过 Claude Code 的内置工具以及 indexio,在相同的仓库上执行编码智能体所做的任务。Grep 将每个匹配项打印为 path:line:text。Read 打印整个文件,最多 2,000 行。Glob 每行打印一个路径。Token 使用 tiktoken 计数。三个真实仓库,索引中有 44 个,每个仓库 10 个符号和 3 种文件类型,问题由模型编写。
| 任务 | 内置 | indexio | 变化 |
|---|---|---|---|
| 查找符号定义的位置(30) | 814 | 1,421 | 更差。find_symbol 列出每个仓库。有针对性的 grep 只找到一个。 |
| 列出符号的调用点(30) | 2,018 | 1,409 | −30 % |
| 读取一个函数,文件已知,行号未知(30) | 419,380 | 41,612 | −90 % |
| 列出某一类型的文件(9) | 12,216 | 3,887 | −68 % |
| 回答关于代码的问题(11) | 1,144,842 | 1,883 | −99.8 % |
| 所有任务 | 1,579,270 | 50,212 | −97 % |
在内置工具更便宜的地方,差异是几百个 token。在它昂贵的地方,昂贵程度是数量级的,因为 Read 返回文件,而 Grep 返回每个单词的每个匹配项。
在真实流量上
服务器记录每次调用及其返回的字节数。在内置工具有机械等价物的地方,服务器还记录该工具本应返回的内容:
| 调用 | 内置等价物 |
|---|---|
read_span |
整个文件的 Read,每个文件版本计费一次:文件更改后再次计费 |
find_symbol、who_calls、code_grep、词法 code_search |
会话仓库中的 Grep,前 250 行 |
list_files |
作为绝对路径的 Glob |
file_outline |
无。计为成本。 |
混合搜索、recall、影响工具 |
无。未比较。 |
index_stats 以所有会话中最近七天结束。indexio usage 按工具和按仓库打印相同内容。作者机器上一天的真实编码会话:423 次比较调用,提供 220k token,而内置工具本应返回 596k,减少了 63%。仅 read_span:−71%。
影响分析
indexio impact --symbol RateLimiter::acquire --depth 2 # transitive callers, all repositories indexio impact --diff my-service # uncommitted working-tree changes indexio impact --diff-file pr.patch --repo my-service # a patch indexio impact --file my-service:src/auth/token.rs # every definition in a file, plus importers indexio outline my-service:src/auth/token.rs # definitions with line ranges indexio span my-service:src/auth/token.rs 120:160 # exact lines
该遍历是在索引所持有的反向调用图上进行广度优先搜索。它在索引时零成本,并在毫秒内回答。补丁通过仅对更改文件的 tree-sitter 遍历映射到定义。更改的文件被排除在结果之外。它们的导入者通过语言感知的导入查询找到。
边是基于名称的。Foo::new 和 Bar::new 共享节点 new。每个位置携带其 caller、path 和 symbol,因此智能体可以过滤。扇出上限使中心名称保持有界;truncated: true 告诉你上限何时触发。
HTTP API 具有相同的接口:GET /impact/symbol、POST /impact/diff、GET /outline、GET /span。它们都应用 ACL。
从其他工具使用 indexio
任何智能体框架都可以使用 HTTP API。
curl 'http://127.0.0.1:7717/search?q=auth%20token%20refresh&mode=hybrid&limit=10' curl 'http://127.0.0.1:7717/symbol/RateLimiter' curl 'http://127.0.0.1:7717/calls/RateLimiter::acquire'
响应是命中的 JSON 列表:仓库、路径、行、列、片段、分数。
命令参考
每个命令接受 --data-dir DIR。默认是 $INDEXIO_DATA_DIR,然后是 ~/.indexio。
索引代码
indexio add ~/code # every git repository under a folder indexio add ~/notes # a folder without git: indexed as-is indexio add github:my-org # a GitHub organization indexio add github:my-org/payments # one repository indexio add azdo:my-org/Platform # an Azure DevOps project indexio add https://gitlab.example.com/g/r.git indexio add github:my-org --limit 20 --no-embed # trial run: 20 repositories, lexical only indexio add github:my-org --dest /srv/clones # where remote clones go indexio index ~/code/one-repo --name one # one repository, no source bookkeeping indexio sources # sources and registered repositories indexio remove github:my-org # forget a source
保持最新
indexio sync # pull, discover, delta re-index, embed. Run from cron. indexio sync --no-embed # lexical plane only indexio reindex --repo payments # one repository, from HEAD indexio reindex --repo payments --worktree # from the working tree, uncommitted edits included indexio reindex --all
搜索
indexio search parse_config # exact identifier indexio search 'parse_config repo:payments lang:rust' # filters: repo: lang: path: case: indexio search '"token refresh"' # phrase indexio search '/fn (parse|load)_config\(/' # regex indexio search 'how is backpressure applied' --mode hybrid # question indexio search 'retry with exponential backoff' --mode semantic --limit 10 indexio search 'rate limiter' --mode hybrid --fusion combmnz # alternative fusion indexio search 'rate limiter' --mode hybrid --rerank # needs INDEXIO_RERANK_BASE indexio search parse_config --json
符号与影响范围
indexio symbol parse_config # definitions indexio calls parse_config # call sites indexio impact --symbol parse_config # transitive callers and importers, 2 hops indexio impact --symbol parse_config --depth 3 --max-sites 1000 indexio impact --diff payments # the uncommitted working tree indexio impact --diff payments --base origin/main git diff main | indexio impact --diff-file - --repo payments indexio impact --file payments:src/config.rs indexio impact --symbol parse_config --json
从索引读取
indexio outline payments:src/config.rs # definitions with start and end lines indexio span payments:src/config.rs 120 # the definition at line 120 indexio span payments:src/config.rs 120:160 # a range, 400 lines at most
嵌入
indexio embed # embed every repository, incrementally indexio embed --repo payments indexio embed --all --rebuild-model # retrain the built-in model from scratch indexio embed --max-chars 800 # smaller chunks indexio embcas-stats # embedding cache statistics indexio embcas-stats --coverage # per repository: indexed files without vectors
服务
indexio mcp # MCP over stdio indexio mcp --repo payments # pin the session's repository indexio serve # HTTP on 127.0.0.1:7717 indexio serve --port 8080 --auth-token $(openssl rand -hex 32) indexio serve --bind 0.0.0.0 --auth-token "$TOKEN" # beyond localhost: a token is mandatory indexio serve --acl-file acl.json indexio serve --oidc-issuer https://idp.example.com --oidc-audience indexio-api --acl-file users.json # team worktree overlays: POST /team/worktree (see "Team indexing" above)
智能体设置
indexio setup claude indexio setup claude --claude-md ~/.claude/CLAUDE.md indexio setup claude --team-sync # also install the team worktree-sync hook indexio hook install indexio hook install --print-only indexio sessions # import Claude Code transcripts for recall indexio sessions --project C--Users-me-code-payments
运行命令而不填满上下文
indexio run -- python scripts/probe.py --fast # short output printed as is; long output stored + digested indexio run -- npm test # the Bash hook does this rewrite for you indexio run --cwd ~/code/payments -- pytest -q
存储的日志是 runs:<repo>/<stamp>-<command>.log:read_span 它查找摘要遗漏的行,recall 稍后找到它。日志在 INDEXIO_RETAIN_DAYS 后滚动删除。
测量与维护
indexio stats # repositories, files, shards, index size indexio usage # last 7 days of MCP calls, against the built-in tools indexio usage --days 1 --json indexio compact # merge shards indexio compact --vectors # also merge vector segments indexio cas-stats
环境变量
| 变量 | 效果 |
|---|---|
INDEXIO_DATA_DIR |
数据目录。默认 ~/.indexio。 |
INDEXIO_REPO |
MCP 会话工作的仓库。默认:包含工作目录的那个。 |
INDEXIO_BIND |
serve 的绑定地址。非回环需要 token 或 ACL。 |
INDEXIO_AUTH_TOKEN |
serve 的 Bearer token。 |
INDEXIO_TEAM_TTL_DAYS |
团队工作树覆盖层在没有更新的情况下保留的天数。默认 7。 |
INDEXIO_URL、INDEXIO_TOKEN、INDEXIO_USER |
团队同步钩子的服务器、bearer token 和要充当的用户(仅在使用共享 token 时)。 |
INDEXIO_EMBED_BASE、INDEXIO_EMBED_MODEL、INDEXIO_EMBED_DIM、INDEXIO_EMBED_KEY |
一个 OpenAI 兼容的嵌入端点。 |
INDEXIO_RERANK_BASE、INDEXIO_RERANK_MODEL、INDEXIO_RERANK_KEY |
一个重排序器端点。 |
GITHUB_TOKEN 或 GH_TOKEN、AZDO_TOKEN |
远程源的凭据。从不写入磁盘。 |
INDEXIO_MCP_FORMAT |
text(默认)或 json 工具结果。 |
INDEXIO_FUSE_WEIGHTS |
混合分支权重 lex,bm25,sem。默认 0.5,1.0,0.3。 |
INDEXIO_EXPAND |
启用同义词库查询扩展。默认关闭;测量结果更差。 |
INDEXIO_RETAIN_DAYS |
保留运行日志和渲染的会话记录的天数。默认 30,0 表示全部保留。 |
INDEXIO_NO_USAGE |
不写入使用日志。用于基准测试。 |
INDEXIO_RAW |
按输入原样运行一个挂接的 Bash 命令。 |
INDEXIO_HOOK_DEBUG |
Bash 钩子在 stderr 上解释其决策。 |
使用 Docker 部署
发布工作流为 linux/amd64 和
linux/arm64 发布 ghcr.io/ioserviceslabs/indexio。镜像以非特权用户运行。数据目录为 /data。
包含 git 和 CA 证书。
如果你是在为自己的 agent 索引自己的机器,请改为安装二进制文件。 容器适用于多人或多台机器通过 HTTP 查询的共享索引。在你自己的笔记本电脑上,它会让你失去 Bash 钩子、即时工作树刷新和一层路径转换,却没有任何收益。请使用上方的 Install。本节其余内容针对共享场景,以及在不安装任何东西的情况下试用 indexio。
索引本机上的一个文件夹并搜索它,除了 Docker 什么都不用安装:
docker run --rm -v indexio-data:/data -v ~/code:/repos:ro ghcr.io/ioserviceslabs/indexio add /repos
docker run --rm -v indexio-data:/data ghcr.io/ioserviceslabs/indexio search parse_config
docker run --rm -v indexio-data:/data ghcr.io/ioserviceslabs/indexio stats提供 HTTP API:
docker run -d --name indexio -p 127.0.0.1:7717:7717 -v indexio-data:/data -e INDEXIO_AUTH_TOKEN="$(openssl rand -hex 32)" ghcr.io/ioserviceslabs/indexio curl -H "Authorization: Bearer $TOKEN" 'http://127.0.0.1:7717/search?q=parse_config'
将容器作为 MCP 服务器提供给 agent(通过 docker run -i 使用 stdio):
claude mcp add indexio -- docker run -i --rm -v indexio-data:/data -v ~/code:/repos:ro ghcr.io/ioserviceslabs/indexio mcp容器如何索引你机器上的代码
除了你绑定挂载进去的内容外,容器无法访问你的磁盘。有两个挂载发挥作用,它们并不对称:
| 挂载 | 用途 | 所需访问权限 |
|---|---|---|
-v ~/code:/repos:ro |
要索引的代码。可以挂载任何内容:一个仓库、一个检出根目录、多个 -v 标志。 |
只读即可。索引从不会写入你的源代码。 |
-v indexio-data:/data |
索引本身。 | 可写。这是一个 Docker 卷,而不是你主机上的文件夹。 |
indexio add /repos 遍历挂载点,注册其下任意深度的每个 git 仓库,并逐一索引。没有 git 仓库的文件夹会作为普通树进行索引。每个仓库都会记录在 /data/repos/<name>.json 中,路径为 容器所看到的样子——/repos/my-service,绝不是 C:\Users\you\code\my-service。结果是相对于仓库的(my-service:src/main.rs),因此搜索不受影响,但主机路径不会出现在索引中的任何地方。
在构建大型索引之前,有三个值得了解的后果:
- 容器索引和主机索引不可互换。如果你将主机二进制指向在容器中构建的数据目录,记录的
/repos/...路径在主机上并不存在,因此重新索引以及对未索引内容执行read_span都会失败。选择一种并坚持使用,或者保留两个数据目录。 - 新鲜度来自同步循环,而不是文件监视器。对本地文件夹执行
indexio sync会重新发现根目录下的仓库,并对磁盘上当前的内容进行增量重新索引。它不会克隆、拉取或写入你的源代码——只有远程源类型(github:、azdo:、git URL)才会获取内容。docker-compose.yml中的sync服务每 15 分钟运行一次。 - 自动刷新无法跨越挂载。
indexio mcp通常会监视当前仓库,并在编辑后约 25 毫秒内重新索引工作树。文件系统事件无法可靠地从 Windows 或 macOS 主机跨越 Docker Desktop 绑定挂载,因此在容器中,应将索引视为新鲜到最近一次sync或refresh_index调用,而不是最近一次按键。这是在编辑代码的 agent 旁边运行主机二进制文件的主要原因。
Bash 钩子(indexio hook install)也需要主机二进制文件:它们会重写你 shell 中的命令,而容器无法看到这些。
在 Windows 上,请使用 PowerShell 而不是上面的 bash 命令行来生成服务令牌:
$bytes = New-Object byte[] 32 [System.Security.Cryptography.RandomNumberGenerator]::Fill($bytes) $env:INDEXIO_AUTH_TOKEN = [System.BitConverter]::ToString($bytes).Replace('-','').ToLower()
该镜像设置了 INDEXIO_BIND=0.0.0.0,因此如果没有 serve 或 INDEXIO_AUTH_TOKEN,--acl-file 会拒绝启动:把所有源码的索引未经认证就发布出去可不是小事。不要通过在容器里设置 INDEXIO_BIND=127.0.0.1 来“修复”这个问题——那样服务会绑定到容器自身的回环地址,发布的端口就无法访问它了。保持绑定在 0.0.0.0,设置一个令牌,并发布到 127.0.0.1:7717:7717,这样只有你自己的机器能连接。
持续索引一个文件夹
假设代码位于 C:\codebuilds,你希望它按定时器重新索引,并由一个 agent 通过 MCP 查询它。这种情况不需要认证令牌:令牌保护的是 serve,即 HTTP API,而这个配置并不运行它。
注册该文件夹并构建一次索引。add 将 /repos 记录为持久源,后续同步会重新读取它:
docker run --rm -v indexio-data:/data -v C:/codebuilds:/repos:ro ` ghcr.io/ioserviceslabs/indexio add /repos
然后让一个容器持续运行,循环重新同步。该镜像的入口点是那个二进制文件,所以覆盖它以获得一个 shell:
docker run -d --name indexio-sync --restart unless-stopped ` -v indexio-data:/data -v C:/codebuilds:/repos:ro ` --entrypoint /bin/sh ghcr.io/ioserviceslabs/indexio ` -c "while true; do indexio sync || true; sleep 900; done"
让 agent 指向同一个卷。MCP 服务器使用 stdio,因此不需要端口也不需要令牌:
claude mcp add indexio -- docker run -i --rm -v indexio-data:/data -v C:/codebuilds:/repos:ro ghcr.io/ioserviceslabs/indexio mcp
用 docker run --rm -v indexio-data:/data ghcr.io/ioserviceslabs/indexio stats 检查它,它应该列出在 C:\codebuilds 下找到的每个仓库。
在挂载路径中使用正斜杠(C:/codebuilds),并确保该驱动器在 Docker Desktop 的文件共享设置中已共享。如果 15 分钟太粗,可以调低 sleep——但编辑只有在下次同步后才可见,所以如果你想让索引随着你的编辑实时跟踪,请改为运行宿主机上的二进制文件。
要用 docker-compose.yml 做同样的事,设置文件夹并只启动同步服务:
$env:INDEXIO_REPOS = "C:/codebuilds" docker compose up -d sync docker compose exec sync indexio add /repos
Compose 在选择服务之前会对整个文件进行插值,因此即使你不启动 indexio 服务,也必须设置 INDEXIO_AUTH_TOKEN 才能让文件解析通过。像上面那样设置它,或者使用普通的 docker run 形式。
docker-compose.yml 在一个卷上运行两个服务:
indexio:HTTP API,仅发布到 localhost。sync:每 15 分钟执行一次indexio sync。GITHUB_TOKEN和AZDO_TOKEN会透传。
- 将要索引的文件夹放在
./repos下。 - 启动服务:
INDEXIO_AUTH_TOKEN=$(openssl rand -hex 32) docker compose up -d - 一次性索引这些文件夹:
docker compose exec sync indexio add /repos
容器绑定到 0.0.0.0。如果没有 INDEXIO_AUTH_TOKEN 或 ACL 文件,该二进制文件会拒绝启动。docker build -t indexio . 从源码构建相同的镜像。
架构
┌──────────────────────────── data dir ────────────────────────────┐
git repos │ shards/*.cidx immutable mmap'd index shards (single file) │
│ │ cas/ global content-addressed store (blake3 keyed) │
▼ │ embcas/ embedding CAS: (chunk-hash, model-id) → vector │
indexio-ingest │ vec/*.civec int8 vector segments + binary prescan │
git delta │ repos/*.json per-repo indexing state (last commit) │
│ └──────────────────────────────────────────────────────────────────┘
▼
Lexical plane Semantic plane
───────────────────── ──────────────────────────
trigram inverted index tree-sitter chunking
FST term dictionary, LEB128 postings pluggable embedder (built-in model
symbols + call edges (tree-sitter, or any OpenAI-compatible endpoint)
6 languages, AST discarded at once) embedding CAS (org-wide dedup)
query planner: required literals, mmap'd binary prescan + int8 rescore
galloping intersection, verify, BM25
└─── indexio-query: RRF fusion → optional reranker ───┘
│
CLI · HTTP (axum) · MCP (stdio JSON-RPC)
五个设计决策:
- 三元组索引,而非常驻 AST。 indexio 解析 AST,从中提取符号、调用边和块边界,然后将其丢弃。常驻 AST 的内存开销是文本大小的 20 到 50 倍。三元组分片只占语料的 1 到 3 倍,并能在毫秒级内响应。
- 全局内容寻址存储。 提取的产物和嵌入在整个组织内按内容哈希作为键。一个 fork、一份 vendored 副本或一个重复文件只会被索引和嵌入一次。
- 带墓碑的不可变 mmap 分片。 一次索引构建会写入新分片。删除是一个位图墓碑。
indexio compact在后台合并分片。读者永远不会阻塞写者。 - Git 原生增量索引。 重新索引会遍历 HEAD 树的 diff。未更改的 blob 不会被再次读取、解析或嵌入。单文件提交的重新索引耗时不到一秒。
- 独立的语义平面。 向量索引派生自存储和分片。你可以更改嵌入模型或重建该平面,而无需触碰词法索引。
数据布局:
<data-dir>/
shards/*.cidx immutable index shards (mmap'd)
cas/v2/<hh>/<rest> content-addressed extracted artifacts (zstd)
embcas/<model>/ content-addressed embeddings, one pack per model
vec/<model>.*.civec int8 vector segments with a binary prescan
bm25/*.cibm25 chunk-level BM25 sidecar (mmap'd)
sem/*.rimodel the built-in semantic model (mmap'd)
repos/<name>.json per-repository state
sessions/ rendered session transcripts for recall (30-day window)
runs/<repo>/*.log command output kept by `indexio run` (30-day window)
usage/<day>.jsonl the MCP usage log
实测性能
存储与启动,在作者的机器上对一个包含 44 个仓库、10,000 个文件的数据目录(docs/SPEC-P10.md)测得:
| 指标 | 之前 | 之后 |
|---|---|---|
| 143 MB 源码的数据目录 | 5.3 GB | 1.0 GB |
| 服务器启动:时间、读取字节数、RSS | 188 ms、302 MB、417 MB | 10 ms、0 MB、74 MB |
| 混合查询,热进程 | 130 到 190 ms | 7 到 14 ms |
| 正则 grep,热进程 | 424 ms | 28 ms |
find_symbol、who_calls |
12 ms | 0.5 ms |
| 编辑后重新索引工作树 | 不可用 | 约 25 ms |
通过 shell 和通过索引进行相同的查找(bench/ab_speed.py,这个仓库,一个 1,300 个文件的 TypeScript 应用给出了相同的图景)。实际耗时,10 次的中位数,索引侧包含 MCP 传输:
| 查找 | Shell | indexio |
|---|---|---|
标识符 grep(rg -n) |
32 ms | 0.6 ms |
正则 grep(rg -n) |
34 ms | 2.5 ms |
对应用执行 grep -rn |
26 到 860 ms | 0.7 ms |
读取 120 行(sed -n) |
23 ms | 0.3 ms |
find -name |
25 到 410 ms | 0.9 ms |
| 定义查找 | 33 ms | 0.1 ms |
每次 Bash 工具调用还要为 shell 支付约 15 ms,并在其命令运行前为两个 PreToolUse 钩子支付约 30 ms。MCP 调用两者都不支付。在真实流量上(7 天内 5,355 次调用),read_span 的中位数为 0 ms,code_search 为 12 ms,code_grep 为 29 ms;第 90 百分位数保持在 130 ms 以下。
重复,在一个 2 核 4 GB RAM 的沙箱上测量:
| 场景 | 结果 |
|---|---|
索引 serde,然后一个相同的 fork |
Fork:100 % 缓存命中,3.0 s 而非 10.0 s |
索引 ripgrep,然后一个有一个文件更改的 fork |
Fork:100 % 缓存命中,2.5 s 而非 6.6 s |
| 重新嵌入 4 个仓库,19,977 个块实例 | 4,731 个唯一嵌入,76 % 去重 |
同一沙箱上的词法延迟:稀有标识符 21 ms,常见词项 81 ms,正则 254 ms。单文件提交的增量重新索引:792 ms。
20 个概念查询上的语义质量(bench/known_answers20.json,bench/eval_modes.py):
| 模式 | MRR | recall@5 |
|---|---|---|
| 词法 | 0.050 | 5 % |
| 语义 | 0.293 | 40 % |
| 混合 | 0.397 | 60 % |
已测量并拒绝:CombMNZ 融合得分低于 RRF。离线重叠重排器得分低于单独的混合。同义词库查询扩展得分低于纯语义搜索。IVF 预扫描没有带来加速。这些默认保持关闭。
限制
- 没有类型解析的交叉引用。 调用边基于名称。像
new、get和run这样的重载名称会产生噪声。按caller和path过滤。 impact --diff中的未跟踪文件。git diff不包含它们。暂存它们,或传入一个补丁。- 内置模型不是神经的。 它有用且便宜。为了神经质量,设置
INDEXIO_EMBED_BASE。 - MCP 没有身份验证。 它以本地用户身份通过 stdio 运行。没有网关不要将其暴露到网络。
- 没有 Web UI 也没有分布式查询。 一个二进制文件,一个数据目录。
开发与发布
cargo test --workspace # every crate, 325 tests
cargo clippy --workspace --all-targets
tools/build-release.sh # release build; remaps local paths out of the binary
Crates:indexio-types(共享类型,发布编码),indexio-core(n-gram,验证,查询字面量),indexio-index(分片,合并,墓碑),indexio-symbols(6 种语言的 tree-sitter 符号、调用和块),indexio-ingest(git 增量,内容存储,源),indexio-embed(嵌入器,嵌入存储,向量索引,管道),indexio-query(规划器,排序,融合,影响),indexio(CLI,HTTP,MCP,钩子)。设计说明在 docs/ 中;docs/DEVELOPMENT.md 是操作手册。
CI(.github/workflows/ci.yml)在每次推送和拉取请求时运行。它检查清单可解析、脚本可编译且工作流有效。它在 90 秒后被终止。构建和测试套件在本地运行;它们不适合那个预算。
发布(.github/workflows/release.yml)来自 main:
- 在
Cargo.toml中设置version([workspace.package])。 - 合并到
main。在不到 90 秒内,工作流创建标签v<version>和一个带有生成说明的 GitHub 发布。没有版本更改的合并什么也不做。 - 然后同一工作流为 Linux(x86_64、aarch64)、macOS(Apple 芯片、Intel)和 Windows(x86_64)构建二进制文件,将每个归档文件连同
.sha256和一个 pip wheel 附加到发布,将容器镜像发布到ghcr.io,当NPM_TOKEN密钥存在时发布 npm 包,并当PYPI_API_TOKEN密钥存在时将indexio-cliwheels 发布到 PyPI。这些作业编译 Rust 并需要几分钟。
tools/release-assets.sh 从维护者机器构建并附加一个资产,用于工作流未覆盖的平台。
许可证
Apache License 2.0。参见 LICENSE 和 NOTICE。
来源:Hacker News · github.com