跳到正文
Hacker News· iohotspot·· 7 天前AI 评分62

indexio 发布:面向编码智能体的代码搜索引擎,通过 MCP 接入并宣称减半 token 用量

I cut my agent token usage in half

AI 导读

indexio 是一个面向 AI 编码智能体的代码搜索引擎,一次性索引组织内所有仓库,以毫秒级响应查询,并通过 MCP 和 HTTP API 作为工具提供给智能体。

正文 · AI 翻译

release npm PyPI license

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

然后还有两个步骤:

  1. 运行 indexio hook install。它会添加新版本带来的钩子并修复其他钩子。它会保留你自己的钩子。
  2. 重启你的代理会话。正在运行的会话会一直保留其 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。

目录

  1. 让你的代理安装它
  2. indexio 的功能
  3. 添加源
  4. 保持索引最新
  5. 嵌入
  6. 永不索引的内容
  7. 保护 HTTP API
  8. 团队索引:实时工作树叠加
  9. 企业设置
  10. 在 Claude Code 中使用 indexio
  11. 实测 token 节省
  12. 影响分析
  13. 从其他工具使用 indexio
  14. 命令参考
  15. 环境变量
  16. 使用 Docker 部署
  17. 架构
  18. 实测性能
  19. 限制
  20. 开发与发布

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 客户端。

工作原理

  1. 服务器索引已推送的仓库(通常的 add + sync)。
  2. agent harness 中的 PreToolUse hook 会在每次 indexio 工具调用前运行 integrations/claude-code/indexio-team-sync.sh。该脚本将工作树(包括未跟踪文件)与远程默认分支的 merge-base 进行 diff,并将补丁 POST 到 /team/worktree。
  3. 服务器将补丁应用到一个以调用者用户名为键的小型 overlay 分片中,叠加在基础分片之上:更改的路径遮蔽基础索引,删除的路径被隐藏,其余一切从共享索引读取。应用时在临时索引上使用 git plumbing——无需 checkout,无需 worktree。
  4. 最后一个被接受的补丁的哈希会保存在磁盘上,因此工作未变更时的重复调用不产生任何开销。该 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 针对实时服务器进行端到端验证。

  1. Google Cloud 控制台:创建一个项目(比如 indexio-pilot)。

  2. APIs & Services → OAuth consent screen:External;应用名称 indexio pilot;范围 openid 和 email;在 Test users 下添加你自己的 Google 账户——当应用处于“Testing”状态时,只有测试用户可以同意。这是每个人都会忘记的步骤。

  3. Credentials → Create OAuth client ID → Web application:添加授权重定向 URI https://developers.google.com/oauthplayground。

  4. 在 https://developers.google.com/oauthplayground 生成一个真实的 ID 令牌:齿轮图标 → “Use your own OAuth credentials” → 粘贴客户端 ID 和密钥 → 范围 openid email → Authorize APIs → 同意 → Exchange authorization code for tokens → 复制 id_token。

  5. 以客户端 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
  6. 从任何地方进行冒烟测试:

    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 会透传。
  1. 将要索引的文件夹放在 ./repos 下。
  2. 启动服务:INDEXIO_AUTH_TOKEN=$(openssl rand -hex 32) docker compose up -d
  3. 一次性索引这些文件夹: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)

五个设计决策:

  1. 三元组索引,而非常驻 AST。 indexio 解析 AST,从中提取符号、调用边和块边界,然后将其丢弃。常驻 AST 的内存开销是文本大小的 20 到 50 倍。三元组分片只占语料的 1 到 3 倍,并能在毫秒级内响应。
  2. 全局内容寻址存储。 提取的产物和嵌入在整个组织内按内容哈希作为键。一个 fork、一份 vendored 副本或一个重复文件只会被索引和嵌入一次。
  3. 带墓碑的不可变 mmap 分片。 一次索引构建会写入新分片。删除是一个位图墓碑。indexio compact 在后台合并分片。读者永远不会阻塞写者。
  4. Git 原生增量索引。 重新索引会遍历 HEAD 树的 diff。未更改的 blob 不会被再次读取、解析或嵌入。单文件提交的重新索引耗时不到一秒。
  5. 独立的语义平面。 向量索引派生自存储和分片。你可以更改嵌入模型或重建该平面,而无需触碰词法索引。

数据布局:

<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:

  1. 在 Cargo.toml 中设置 version([workspace.package])。
  2. 合并到 main。在不到 90 秒内,工作流创建标签 v<version> 和一个带有生成说明的 GitHub 发布。没有版本更改的合并什么也不做。
  3. 然后同一工作流为 Linux(x86_64、aarch64)、macOS(Apple 芯片、Intel)和 Windows(x86_64)构建二进制文件,将每个归档文件连同 .sha256 和一个 pip wheel 附加到发布,将容器镜像发布到 ghcr.io,当 NPM_TOKEN 密钥存在时发布 npm 包,并当 PYPI_API_TOKEN 密钥存在时将 indexio-cli wheels 发布到 PyPI。这些作业编译 Rust 并需要几分钟。

tools/release-assets.sh 从维护者机器构建并附加一个资产,用于工作流未覆盖的平台。

许可证

Apache License 2.0。参见 LICENSE 和 NOTICE。

来源:Hacker News · github.com