作者:来自 Elastic Dave Moore

Sourcerer 在代码检索质量上与 Claude Code 和 Codex 相当,搜索速度最高比 grep 快数千倍。每个答案都会链接回跨代码仓库和版本中的确切文件和代码行。
代码是其自身行为的事实来源;它始终具有权威性,并且永远不会过时。权威答案存在于特定代码仓库中的特定提交中,但企业部署依赖于许多相互协作的有版本项目。理解这一切是如何工作的,需要进行大量代码搜索。
App A v1.2.3 是否支持 Feature X?在 Kubernetes 上运行时,它是否与 App B v9.8.7 兼容?我是否需要更多 JVM 堆空间?
这些都是我们的现场团队经常处理的问题。回答这些问题比看起来要困难。文档可以提供上下文,但它是一种抽象,无法预见每一个可能的问题。当我们遇到文档没有涵盖的问题时,我们要么打断一个本应该在开发代码的工程师,要么自己去代码中寻找答案。很多时候,我们既没有时间,也没有足够的专业知识去浏览如此大量的代码。
对于你笔记本电脑上的单个代码仓库,编码 agent 可以很好地完成这项工作。如果我们能够将这种能力扩展到整个代码资产,会不会很棒?作为一名现场工程师,我希望拥有这样的能力来服务我的客户:跨越我们支持的每一个项目、依赖项、平台和版本的 agentic 代码智能。而且我希望它以带链接的引用为依据,并作为一项服务随时向所有人提供。
因此,我使用 Elasticsearch 和 Elastic Agent Builder 构建了它,并将其打包成一个命令行界面(CLI)。我以 Apache 2.0 许可证发布了它,并将其命名为 Sourcerer。这篇博客文章报告了 Sourcerer 作为代码研究 agent 的多项性能基准测试,并详细介绍了其实现的设计和背后的原因。
Sourcerer
Sourcerer 像一个前沿的编码 agent 一样探索代码,可以跨多个有版本的代码仓库进行搜索,速度与在单个代码仓库中搜索一样快,并生成带有链接引用的答案,从而建立可信度。
Sourcerer 由以下部分组成:
-
Agent Builder 中用于 tools、skills 和 agents 的一组配置文件。
-
一组索引模板,用于存储和搜索来自 Git 提交快照的代码。
-
一个 CLI,用于安装这些资源,以及从远程 Git 代码仓库中对提交快照进行索引和清理。
在 Elastic,我们正在使用 Sourcerer 直接从源代码为客户提供可验证的软件信息。我们的内部部署已经从自己的公共和私有代码仓库中索引了超过十亿行代码。其中还包括 Apache Lucene 和 OpenJDK 等核心依赖项,以及 Kubernetes 和 OpenTelemetry 等常用集成。我们的解决方案架构师、客户架构师、咨询架构师和支持工程师不再需要从文档中寻找答案,也不再需要联系那些本应该开发软件、而不是提供支持的工程师。
代码搜索基准测试
Agentic 代码检索
SWE-Explore 是 Zhang 等人于 2026 年 6 月 5 日发布的一项新基准测试,用于评估"编码 agents 探索、定位和排序代码仓库上下文的能力"。它似乎是目前唯一专门测试 agentic 代码检索质量的基准测试。我使用 Sourcerer 运行了这项基准测试,以了解它的表现,并将其与原论文中的其他编码 agents 进行比较;随后又使用 Claude Code 运行了一次,以测量并比较它与 Sourcerer 在 token 使用量和任务持续时间方面的表现。
检索得分
Sourcerer 在代码检索的相关性指标上表现得与前沿编码 agents 一样好(见图 1)。综合检索得分是所有检索指标的算术平均值,并根据论文中报告的 Pearson 相关系数(r)进行加权;我使用这种方式按照整体检索质量的测量结果对 agents 进行排名。Sourcerer 比 Claude Code 低 0.002,比 Codex 高 0.022,得分范围为 0.0--1.0。鉴于大型语言模型(LLMs)的非确定性,每次运行的结果都会略有不同,因此这些差异应该被理解为统计意义上的平手。

总体而言,包括 Sourcerer 在内的所有编码 agents 在精确率指标上表现良好,但在召回率指标上的表现不够理想,不过召回率指标的 Pearson 相关系数较低,因此重要性也较低。表 1 展示了 Sourcerer 的检索得分,以及原论文中测试的其他 agents 的得分(第 8 页,表 6)。"SignalReg" 是作者所称的 "NoiseReg" 的倒数(即 1 -- NoiseReg);我这样处理是为了让该指标与其他指标保持一致,因为其他指标的取值范围都意味着数值越高越好。

Token 使用量和任务持续时间
Zhang 等人没有发布 token 使用量或任务持续时间方面的指标,因此我再次运行了这项基准测试,以获取 Claude Code 的这些指标。鉴于使用任何采用 LLM 的 agent 运行这项基准测试都需要消耗大量 token,我选择只测试一个编码 agent,而 Claude Code 是我认为大多数人会觉得最适合用于比较的 agent。
与 Claude Code 相比,Sourcerer 完成全部 848 个基准测试任务多使用了约 13.9% 的 token。Sourcerer 使用了 156,379,977 个输入 token 和 1,612,175 个输出 token,而 Claude Code 使用了 137,629,435 个输入 token 和 1,137,176 个输出 token。Sourcerer 完成全部 848 个基准测试任务所需的时间长了约 8.9%。Sourcerer 用时 40,717 秒,而 Claude Code 用时 37,460 秒。
我认为,在 token 使用量和任务持续时间方面,这些结果是可以接受的适度额外成本,因为它换来了跨多个代码仓库和版本进行高效搜索的能力。不过,Sourcerer 的 tools、skills 和系统提示词、Agent Builder 的 harness,或者 Elasticsearch 和 Lucene 的搜索引擎,都还有进一步探索优化的空间。
单代码仓库与多代码仓库范围
关键在于,SWE-Explore 基准测试只测量 agents 在给定任务中、单个代码仓库的单个提交快照范围内进行搜索时的检索得分、token 使用量和任务持续时间。这是开发编码 agent 的典型搜索范围。Sourcerer 的目标范围要广泛得多,覆盖多个代码仓库的多个提交快照。本报告接下来介绍的 "搜索速度和可扩展性" 基准测试展示了 Sourcerer 在跨多个代码仓库进行搜索时所具有的独特优势。
检索基准测试方法
附录 A 详细介绍了这些基准测试的配置。
Sourcerer 搜索了 SWE-Explore-Bench 数据集的所有已索引表示(参见附录 A)。两次基准测试运行都使用了原论文中使用的相同 GPT-5.4 模型。Sourcerer 通过 Elastic Inference Service(EIS)与 GPT-5.4 通信,而 Claude Code 则通过一个 shim 代理与 GPT-5.4 通信,以实现与 OpenAI API 的兼容。
我尽力确保 Sourcerer 的基准测试任务提示词与 Claude Code 的保持 一一 对应(参见附录 A)。两个 agents 收到的角色和任务说明以及输出格式完全相同,唯一的区别是各自简短的 harness 特定说明。我要求 Sourcerer 不使用其代码仓库发现 skill,而是向它提供明确的代码仓库过滤指令,从而确保它与已经获得解析后目录的 Claude Code 处于公平的竞争环境。另一种做法是保持 Sourcerer 的代码仓库发现 skill 启用,同时要求 Claude Code 在包含基准测试所需全部代码仓库的文件系统中自行查找代码仓库。
除了 tools 之外,我没有修改 Sourcerer 的系统提示词和 skills,而是保留其默认配置,因为我们比较的是两个 harness 的整体表现;而 Claude Code 中的很多部分都是闭源的,我们既无法看到,也无法控制。
代码搜索速度和可扩展性
编码 agents 往往首先使用外部 tools 来匹配子字符串或正则表达式,作为第一层检索手段。LLMs 在训练过程中会学习在文件系统中探索代码时使用 ls 和 grep 等 shell 命令。Claude Code 自带的 Grep tool 会调用 [ripgrep](https://github.com/BurntSushi/ripgrep "ripgrep")。我希望在 Elasticsearch 中复现这种行为。
Elasticsearch 提供了一种 [wildcard](https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/keyword#wildcard-field-type "wildcard") 字段类型,可以将正则表达式匹配扩展到数十亿个文档。Sourcerer 使用 [sourcerer.code.grep](https://github.com/elastic/sourcerer/blob/main/src/sourcerer/elastic/agent_builder_tools/sourcerer.code.grep.yml "sourcerer.code.grep") 在 Elasticsearch 中模拟 grep 的输入和输出。这是一个 Elasticsearch Query Language(ES|QL)tool,它会对索引中的 wildcard 字段执行 [RLIKE](https://www.elastic.co/docs/reference/query-languages/sql/sql-like-rlike-operators "RLIKE") 查询,其中每个文档包含一行代码的内容。Sourcerer 还提供了 [sourcerer.code.search](https://github.com/elastic/sourcerer/blob/main/src/sourcerer/elastic/agent_builder_tools/sourcerer.code.search.yml "sourcerer.code.search") ,它会针对经过分析的文本字段执行基于 BM25 排名的 [MATCH](https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/search-functions/match "MATCH") 查询,用于按照相关性排序进行发现,而不是进行精确的子字符串检索。
我使用两种不同的语料库规模和模式稀有程度,将这两种方式的速度与文件系统上的 ripgrep 和 grep 进行了基准测试。语料库规模包括来自 elastic/elasticsearch 代码仓库的一个提交(7,215,509 行代码)和 52 个提交(200,325,684 行代码)。单个提交对应 v9.4.3 的发布标签。52 个提交涵盖从 v6.0.1 到 v9.4.3 的每个主要版本和次要版本的最新补丁版本标签。Sourcerer 搜索的是 Elasticsearch 中已建立索引的语料库,而 ripgrep 和 grep 搜索的是存储在文件系统中的语料库,这反映了它们各自的使用场景。正则表达式模式包括一个在这些提交中出现频率较低的模式(DiskBBQ),以及一个出现频率较高的模式(XContentType)。
sourcerer.code.grep
我进行基准测试的第一个搜索,是针对 DiskBBQ 的一个稀有模式,该模式只出现在部分提交中:
.*[dD][iI][sS][kK][-_]?[bB][bB][qQ].*
跨单个提交的搜索延迟(单位:秒)(从 7,215,509 行代码中找到 605 个匹配项):
|-----------------------|-----------|--------|---------|----------|-----------|-----------------------------|
| Retrieval method | Cache | p0 | p50 | p100 | stdev | vs. sourcerer.code.grep |
| sourcerer.code.grep | Cold | 0.069s | 0.124s | 0.167s | 0.020s | - |
| sourcerer.code.grep | Warm | 0.027s | 0.029s | 0.046s | 0.005s | - |
| ripgrep | Cold | 0.788s | 0.800s | 0.809s | 0.005s | ~6.5x slower |
| ripgrep | Warm | 0.081s | 0.088s | 0.109s | 0.009s | ~3.0x slower |
| grep | Cold | 3.565s | 3.580s | 3.821s | 0.055s | ~28.9x slower |
| grep | Warm | 0.825s | 0.827s | 0.833s | 0.002s | ~28.5x slower |
跨 52 个提交的搜索延迟(单位:秒)(从 200,325,684 行代码中找到 1,041 个匹配项):
|-----------------------|-----------|----------|----------|----------|-----------|-----------------------------|
| Retrieval method | Cache | p0 | p50 | p100 | stdev | vs. sourcerer.code.grep |
| sourcerer.code.grep | Cold | 0.159s | 0.164s | 0.270s | 0.028s | - |
| sourcerer.code.grep | Warm | 0.027s | 0.031s | 0.053s | 0.006s | - |
| ripgrep | Cold | 22.356s | 22.364s | 22.459s | 0.026s | ~136.4x slower |
| ripgrep | Warm | 16.017s | 16.123s | 16.297s | 0.058s | ~520.1x slower |
| grep | Cold | 101.962s | 102.386s | 104.281s | 0.752s | ~624.3x slower |
| grep | Warm | 73.082s | 73.507s | 74.915s | 0.536s | ~2,371.2x slower |
表 2。在冷缓存和热缓存条件下、两种代码语料库范围中,sourcerer.code.grep、ripgrep 和 grep 的 p0/p50/p100/标准差检索速度(每种方法在每种缓存状态下运行 20 次;每次热缓存测量前丢弃 3 次预热运行)。所有百分位数均通过线性插值计算。比率均以对应缓存状态下 sourcerer.code.grep 的 p50 为基准计算。有关缓存定义以及查询/命令语法,请参见"方法"部分。
wildcard 字段类型会为每个值建立 trigram 索引,并将其用作过滤器,在验证完整匹配之前缩小正则表达式的候选集合。对于像这样的高选择性模式(720 万行中有 605 个匹配项,2 亿行中有 1,041 个匹配项),相对于语料库大小,其时间复杂度接近亚线性。ripgrep 和 grep 都执行线性时间复杂度的扫描,其中 ripgrep 使用多线程以及单指令、多数据(SIMD)加速的字面量预过滤来加速搜索,但两者都没有类似基于索引的 trigram 搜索那样的机制,可以跳过语料库中的绝大多数内容。
这一点在不同方案的扩展方式上表现得非常明显。从单提交语料库扩大到全部提交语料库,代码行数增加了 27.8 倍。sourcerer.code.grep 的热缓存时间几乎没有变化,从 0.029 秒增加到 0.031 秒。ripgrep 的热缓存时间从 0.089 秒增加到 16.1 秒,变化了 183 倍;而 grep 从 0.83 秒增加到 1.2 分钟,变化了 89 倍。在这套硬件上,这两个文件系统 tools 随语料库大小的扩展都比线性更差,而基于索引的方法几乎保持不变。
我进行基准测试的第二个搜索,是针对 XContentType 的一个常见模式,该模式出现在所有提交中:
.*[xX][cC][oO][nN][tT][eE][nN][tT][tT][yY][pP][eE].*
跨单个提交的搜索延迟(单位:秒)(从 7,215,509 行代码中找到 7,999 个匹配项):
| 检索方法 | 缓存 | p0 | p50 | p100 | 标准差 | 相对于 sourcerer.code.grep |
|---|---|---|---|---|---|---|
sourcerer.code.grep |
冷 | 0.158 秒 | 0.172 秒 | 0.224 秒 | 0.015 秒 | --- |
sourcerer.code.grep |
热 | 0.055 秒 | 0.057 秒 | 0.140 秒 | 0.023 秒 | --- |
ripgrep |
冷 | 0.794 秒 | 0.804 秒 | 0.824 秒 | 0.007 秒 | 约慢 4.7 倍 |
ripgrep |
热 | 0.093 秒 | 0.095 秒 | 0.096 秒 | 0.001 秒 | 约慢 1.7 倍 |
grep |
冷 | 3.385 秒 | 3.399 秒 | 3.421 秒 | 0.010 秒 | 约慢 19.8 倍 |
grep |
热 | 0.681 秒 | 0.683 秒 | 0.686 秒 | 0.001 秒 | 约慢 12.1 倍 |
跨 52 个提交的搜索延迟(单位:秒)(从 200,325,684 行代码中找到 290,662 个匹配项):
| 检索方法 | 缓存 | p0 | p50 | p100 | 标准差 | 相对于 sourcerer.code.grep |
|---|---|---|---|---|---|---|
sourcerer.code.grep |
冷 | 1.587 秒 | 1.644 秒 | 1.756 秒 | 0.047 秒 | --- |
sourcerer.code.grep |
热 | 1.480 秒 | 1.541 秒 | 1.627 秒 | 0.040 秒 | --- |
ripgrep |
冷 | 22.426 秒 | 22.440 秒 | 22.548 秒 | 0.028 秒 | 约慢 13.6 倍 |
ripgrep |
热 | 16.021 秒 | 16.276 秒 | 16.498 秒 | 0.119 秒 | 约慢 10.6 倍 |
grep |
冷 | 97.699 秒 | 97.922 秒 | 99.150 秒 | 0.404 秒 | 约慢 59.6 倍 |
grep |
热 | 68.883 秒 | 69.184 秒 | 70.374 秒 | 0.360 秒 | 约慢 44.9 倍 |
表 3。与表 2 相同条件下,模式 .*[xX][cC][oO][nN][tT][eE][nN][tT][tT][yY][pP][eE].* 的 p0/p50/p100/标准差检索速度。
sourcerer.code.grep 相对于 grep 的搜索延迟差异表明,模式的匹配数量与语料库大小同样重要:
| 语料库 | 语料库大小 | 缓存 | 使用稀有模式(DiskBBQ)时 sourcerer.code.grep 的速度 | 使用常见模式(XContentType)时 sourcerer.code.grep 的速度 |
|---|---|---|---|---|
| 1 个提交 | 7,215,509 行 | 冷 | 约快 28.9 倍 | 约快 19.8 倍 |
| 1 个提交 | 7,215,509 行 | 热 | 约快 28.5 倍 | 约快 12.1 倍 |
| 52 个提交 | 200,325,684 行 | 冷 | 约快 624.3 倍 | 约快 59.6 倍 |
| 52 个提交 | 200,325,684 行 | 热 | 约快 2,371.2 倍 | 约快 44.9 倍 |
表 4。该表展示了 sourcerer.code.grep 在两种不同语料库规模和两种不同模式稀有程度下,相对于 grep 的速度提升。
匹配数量多得多的模式显示出明显更小的 Elasticsearch 优势,尤其是在全部提交范围内,优势从 2,371 倍下降到 45 倍。原因可以从绝对数值中看出来:在全部提交范围内,sourcerer.code.grep 的热缓存时间从 0.031 秒(DiskBBQ)增加到 1.541 秒(XContentType),匹配数量增加 279 倍,而时间增加了 49.7 倍;与此同时,grep 的热缓存时间几乎没有变化(从 73.5 秒到 69.2 秒,基本保持不变),因为无论有多少内容匹配,它都会扫描相同数量的字节。wildcard 字段的 trigram 索引之所以表现出相对于语料库大小的亚线性特征,正是因为它会在验证之前缩小候选集合。一旦某个模式匹配数十万行,瓶颈就会从缩小候选集合转移到收集并序列化所有匹配结果,这部分成本取决于匹配数量,而不是语料库大小。即使在这种对 Elasticsearch 不太有利的情况下,sourcerer.code.grep 仍然具有明显的优势,但优势幅度在很大程度上取决于搜索的选择性,而不仅仅取决于语料库的大小。
sourcerer.code.search
Sourcerer 的另一个检索 tool sourcerer.code.search 执行的是基于 BM25 排名的 MATCH 查询,而不是精确子字符串正则表达式匹配。下面列出了它在两种模式下的速度,供参考。
| 模式 | 语料库范围 | p0 | p50 | p100 | 标准差 | 找到的匹配数 |
|---|---|---|---|---|---|---|
| DiskBBQ | 1 个提交 | 0.013 秒 | 0.017 秒 | 0.018 秒 | 0.002 秒 | 288 |
| DiskBBQ | 52 个提交 | 0.014 秒 | 0.020 秒 | 0.046 秒 | 0.007 秒 | 493 |
| XContentType | 1 个提交 | 0.062 秒 | 0.063 秒 | 0.154 秒 | 0.020 秒 | 7,177 |
| XContentType | 52 个提交 | 2.203 秒 | 2.359 秒 | 2.611 秒 | 0.125 秒 | 249,737 |
表 5。在仅使用热缓存的情况下,两个模式在两种语料库范围内的 sourcerer.code.search p0/p50/p100/标准差检索速度(每行运行 20 次;省略冷缓存数据;参见"方法"部分)。匹配数量是 sourcerer.code.search 自身的匹配数量,而不是基于正则表达式的方法的匹配数量;BM25 匹配的是 token,而不是子字符串,因此这些数据无法直接与表 2--4 进行比较。
速度优势的决定因素
在测试的每一种语料库规模、每一种模式稀有程度以及每一种缓存状态下,sourcerer.code.grep 都优于 ripgrep 和 grep。但它并不是以固定幅度领先的。对于常见模式,优势约为 3--30 倍;对于稀有模式,优势超过 2,300 倍。这是因为基于索引的搜索和暴力搜索对不同因素的响应方式不同。随着模式的选择性提高,[wildcard](https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/keyword#wildcard-field-type "wildcard") 字段的 trigram 缩小候选集合所需执行的工作会减少,而 grep 和 ripgrep 无论语料库中实际有多少内容匹配,都需要执行相同数量的工作。对于完全不知道确切字符串是什么的情况,sourcerer.code.search 则提供了第三种选择。

图 2:Sourcerer 的 sourcerer.code.grep tool 在测试的每一种语料库大小和模式稀有程度组合下,都优于 ripgrep 和 grep,有时甚至达到多个数量级的优势。在大型语料库中搜索稀有模式时,其优势最为明显;在小型语料库中搜索常见模式时,其优势最弱。
这些搜索速度基准测试展示了对于企业级代码搜索 agent 而言,可扩展 在实践中意味着什么:能够以交互式速度搜索任意数量代码仓库的历史,就像开发编码 agent 在文件系统中搜索单个代码仓库的工作状态一样。
速度基准测试方法
附录 B 详细介绍了该基准测试的配置。需要注意的是,虽然 Elasticsearch 部署包含两个数据节点,并且每个数据节点的规格都与运行 ripgrep 和 grep 的虚拟机相同,但基准测试使用的是一个包含一个主分片和一个副本分片的索引。每次搜索都只在单个分片上运行,这意味着尽管整个 Elasticsearch 部署的总容量是 ripgrep 和 grep 所使用容量的两倍,但 sourcerer.code.grep 每次搜索可用的 vCPU 和内存与 ripgrep 和 grep 相同。实际上,与仅使用一个数据节点相比,使用两个数据节点反而带来了轻微的延迟_开销_。为简洁起见,我省略了使用一个数据节点进行基准测试的结果。我们不建议在生产环境中使用单节点部署,因此,在这项基准测试中纳入多节点部署所带来的真实延迟开销是有意义的。
我在冷缓存和热缓存两种情况下比较了所有检索方法,每种方法运行 20 次冷缓存测试和 20 次热缓存测试。Elasticsearch 基准测试和文件系统基准测试中冷缓存和热缓存的定义并不完全对应。对于 ripgrep 和 grep,_冷缓存_意味着在每次冷缓存运行之前执行一次完整的页面缓存清理(sync; echo 3 > /proc/sys/vm/drop_caches);而_热缓存_意味着先连续执行 3 次不计入统计的预热运行,然后执行 20 次测量运行,中间不清理缓存。对于 ES|QL,_冷缓存_意味着在每次运行之前调用 POST /_cache/clear。这只会清理 Elasticsearch 的内部缓存,而不会清理数据节点的操作系统页面缓存,因为在 Elastic Cloud Hosted(ECH)上无法手动清理这些缓存。对于 ES|QL,_热缓存_意味着在 20 次测量运行之前立即执行 3 次不计入统计的预热查询,以帮助确保两个数据节点上的缓存都处于热状态。表 5 中省略了 sourcerer.code.search 的冷缓存数据,原因与本文其他地方讨论的相同:它的冷缓存测量结果与自身的热缓存测量结果在统计上无法区分。这表明操作系统级别的缓存清理限制对它的影响比对 sourcerer.code.grep 的影响更大,后者始终表现出一致且符合实际物理规律的冷缓存/热缓存差异。
代码搜索索引吞吐量
我没有对索引吞吐量进行正式的基准测试。下面分享一些我的总体观察。
通常,我在 Google Cloud Platform(GCP)的 c4a-highcpu 实例上看到,每个数据节点配备约 16GiB RAM 和约 8 个 vCPU 时,持续索引吞吐量为每秒 20K--25K 行代码。这包括写入一个主分片及其副本分片。在 Elastic Cloud Serverless 上,当 Search Power 设置为 "高性能" 时,我看到的吞吐量最高可达每秒约 60K 行代码。
Elastic 的一个工程团队将 Sourcerer 的索引吞吐量与使用语义代码搜索的实现进行了比较,这些实现分别使用稀疏向量生成(Elastic Learned Sparse EncodeR ELSER)或稠密向量嵌入生成(Jina)。Sourcerer 的索引速度比 [.elser-2-elastic](https://www.elastic.co/docs/explore-analyze/machine-learning/nlp/ml-nlp-elser ".elser-2-elastic") 快约 25 倍,比 [.jina-embeddings-v5-text-small](https://jina.ai/models/jina-embeddings-v5-text-small/ ".jina-embeddings-v5-text-small") 快约 15 倍,而它们的检索质量相近。更具体地说,使用 ELSER 索引需要约 6 小时,而使用 Sourcerer 索引只需要约 14 分钟。
代码搜索解决方案设计和设计依据
这篇博客文章的其余部分将解释我在设计 Sourcerer 时所做决策背后的原因,为 Elasticsearch 和生成式 AI(GenAI)实践者提供专家视角。
目标
最终,我们希望拥有一个 agent,通过搜索主要事实来源(代码本身)来回答有关已部署软件及其支持基础设施的问题,并生成引用这些来源的、可验证的回答,从而让用户能够信任这些回答。受到编码 agents 使用 grep 所取得成功的启发,我为 Sourcerer 设定的主要功能目标是使用 Agent Builder 复现编码 agent 的搜索行为,并生成带有引用的回答。我的非功能目标是在跨多个有版本的代码仓库进行搜索时,同时保持速度快、可扩展和准确,并维持可接受的成本和易用性。在这些目标中,复现编码 agents 的搜索行为最为关键,因为它将决定访问模式、索引设计和查询设计,以及它们对非功能目标产生的影响。
访问模式
从人的角度来看,预期的访问模式很简单:我们希望用自然语言询问有关软件的问题,并获得以事实来源为依据的自然语言回答。从处理这些问题的 agent 的角度来看,预期的访问模式是复现编码 agents 的搜索行为,以找到它所需要的信息。编码 agents 所使用的 LLMs 经过大量训练,会使用 ls 或 find、grep 或 ripgrep、cat、head、tail 等 shell 命令来探索代码。Claude Code 的内置 tools,例如 Glob 和 Grep,也提供了类似的功能。
我选择顺应模型接受训练的方式。因此,我为 Sourcerer 设计的访问模式,是在 Agent Builder 中使用 ES|QL tools 来复现 shell 命令的名称、输入和输出。这样,Agent Builder harness 就能让 LLM 利用其经过训练的直觉,实现与 Claude Code 或 Codex 等前沿 harness 类似的结果,而无需用关于如何使用不同搜索接口的指令填满 LLM 有限的上下文窗口。这是 Sourcerer 需要解决的主要上下文工程问题。解决这个问题后,就可以实现更快的搜索,并同时扩展到多个代码仓库,使 agent 能够回答涉及许多不同版本软件项目协同工作的部署问题。
索引设计
我决定使用三个索引模板来满足这种访问模式:
-
[sourcerer-refs](https://github.com/elastic/sourcerer/blob/main/src/sourcerer/elastic/index_templates/sourcerer-v2-refs.json "sourcerer-refs"):每个文档为单个 Git 引用或 ref 索引高层元数据,该 ref 通过唯一的提交哈希标识,并且可以关联标签名称或分支名称。一个 ref 代表代码仓库在某个时间点的完整快照。这是一个较小的索引。agent 主要使用它来发现可供搜索的代码仓库和快照。 -
[sourcerer-files](https://github.com/elastic/sourcerer/blob/main/src/sourcerer/elastic/index_templates/sourcerer-v2-files.json "sourcerer-files"):每个文档为给定 ref 中的单个文件索引元数据。这是一个更大的索引。agent 主要使用它按照ls的语义浏览文件和目录。 -
[sourcerer-lines](https://github.com/elastic/sourcerer/blob/main/src/sourcerer/elastic/index_templates/sourcerer-v2-lines.json "sourcerer-lines"):每个文档为给定 ref 中给定文件的一行带编号的代码索引其内容。是的,每一行代码都会成为一个文档。这是最大的索引(但可能没有你想象的那么大)。agent 主要使用它按照grep和cat的语义搜索和查看代码。
这种设计几乎完全采用反规范化。每个索引都使用相同的命名空间字段,以实现快速、无需 join 的过滤。每个被索引的 ref 都存储其提交快照中的所有文件和代码行,而不是存储 diff 并在搜索时重新构建,也不是存储唯一的文件和代码行,并为每个文件和代码行维护一个可变的 ref 名称数组。这些选择以重复存储(最廉价的计算资源)换取更快的搜索速度和更小的 segment 合并压力。
命名空间
三个索引都使用四个字段来为 ref、文件或代码行建立命名空间:
-
git.host:Git 托管提供商(例如 github、gitlab)。 -
git.org:账户名称(例如 elastic)。 -
git.repo:Git 代码仓库(例如 elasticsearch、kibana)。 -
git.commit:提交哈希,以完整的 40 字符 SHA-1 摘要存储,以确保完整性。
文件和代码行的文档 _id 哈希同样使用 {git.host}、{git.org}、{git.repo} 和 {git.commit} 建立命名空间,以支持幂等索引。这意味着你可以安全地重新运行索引任务,而不会产生重复文档。
同样,索引名称也采用相同的语义建立命名空间,并使用波浪号(~)作为可靠的分隔符,因为 ~ 是 Git 代码仓库名称和组织名称中不允许使用的字符:
-
sourcerer-v*-files~{git.host}~{git.org}~{git.repo} -
sourcerer-v*-lines~{git.host}~{git.org}~{git.repo}
这种命名空间约定有许多优势:
-
agents 可以通过这些通用的范围字段快速缩小 ref、文件以及代码行的搜索空间,从而保持搜索快速且聚焦。
-
这些语义反映了常见的权限边界。你可以通过基于 host、组织或代码仓库实现你所选择的索引级安全和/或文档级安全,来复现 Git 托管提供商的访问策略。
-
你可以立即删除整个代码仓库、组织或 host 的索引,而无需执行开销昂贵的
[_delete_by_query](https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-delete-by-query "_delete_by_query")。 -
索引名称已经为未来不同粒度的设计做好准备。Sourcerer 最终可能允许按照
{git.host}、{git.host}~{git.org}或{git.host}~{git.org}~{git.repo}~{git.commit}对代码进行索引,以实现选择性的分片大小优化。查询语法不会受到影响,因为查询针对的是索引别名(sourcerer-files和sourcerer-lines),而不是单个索引。
设置
三个索引设置有助于优化存储成本和搜索速度:
-
索引排序可以带来更快的搜索和更好的压缩,但代价是降低索引吞吐量。每个索引都会根据
git.host、git.org、git.repo、git.commit对磁盘上的文档进行排序。文件和代码行文档会进一步按照file.path排序,而代码行文档还会进一步按照line.number排序。 -
Synthetic _source会丢弃
_source,并在重新索引时根据需要重新构建它。没有任何查询会访问_source,因此它属于无用负担。启用此设置可以回收 files 和 lines 索引约 50% 的存储空间。虽然它需要 Enterprise 许可证,但在没有许可证的部署上启用它并不会阻止索引创建;该设置会被忽略。 -
[best_compression](https://www.elastic.co/docs/reference/elasticsearch/index-settings/index-modules#index-codec "best_compression")是没有 Enterprise 许可证、无法使用 Synthetic_source的 Elastic 部署的备用方案。它可以为_source提供不错的压缩效果(根据我的观察,可节省约 11% 的存储空间),代价是索引吞吐量适度下降(约慢 15%);由于查询不会获取_source,因此搜索速度基本不受影响。
我使用索引别名来支持重新索引到新 schema 时的零停机升级。
映射
这些索引主要使用 keyword 字段。它们可以通过基本的 wildcard 支持和聚合实现高效过滤,同时优化存储使用和索引吞吐量。
line.content 字段同时被索引为 [wildcard](https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/keyword#wildcard-field-type "wildcard") 字段和 [text](https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/text "text") 字段,并针对代码搜索对分词和 similarity 设置进行了调优。这让 agents 可以选择使用熟悉且高效的类似 grep 的正则表达式,在 [wildcard](https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/keyword#wildcard-field-type "wildcard") 字段上搜索代码;或者使用 [text](https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/text "text") 字段的倒排索引,仅返回排名最高的匹配代码行,以减少 token 使用量并提高搜索速度。这两种方式都非常快速且具有良好的可扩展性,大多数情况下都可以在几毫秒内完成。
分片
虽然随着 Elastic Cloud Serverless 等无状态平台的发展,分片的重要性正在降低,但我希望该解决方案能够适配 Elasticsearch 的所有部署模式。因此,我关注了索引设计对主分片数量和大小的影响。根据我的观察,我预计大多数用户不需要过多关注分片。
默认情况下,Elasticsearch 对每个数据节点强制实施 1,000 个分片的软限制(包括副本)。这意味着该解决方案在每个数据节点上索引不到 250 个代码仓库时,就会达到这一软限制,因为每个代码仓库都会写入一个 files 索引和一个 lines 索引,而两者各包含一个主分片和一个副本分片。此外,还有一个将分片大小限制在约 50GB 的传统最佳实践,这会影响每个代码仓库可以索引多少个 ref。这两个限制都可以稍微突破一些。但它们表明,这种解决方案设计确实针对其预期使用场景进行了优化,即搜索受支持的已部署软件的提交快照。你不会使用 Sourcerer 为 Internet 上的每个代码仓库建立索引,也不应该使用它为每个临时开发分支建立索引。此外,你还应该决定每个代码仓库值得保留多少个 ref。
代码仓库级别的索引粒度似乎在分片数量和分片大小之间取得了恰当的平衡。作为参考,Kibana 是 GitHub 上最大的代码仓库之一。我观察到,当仅保留从 v6.0.0 到 v9.5.0 的每个主要版本和次要版本发布中最新的补丁版本时,其分片大小为 60GB--75GB,是可管理的范围。这对于我们在实际环境中看到的 Elastic 部署来说已经具有很好的覆盖范围。如果这是目前最大的代码仓库之一,那么只要有合理的保留策略,你基本可以预计其他任何代码仓库都能放入单个分片中,我将在下一节"清理"中讨论这一点。
清理
默认情况下,Sourcerer 会保留你索引的所有内容。清理功能可以让你删除旧的 ref,以防止数据无限增长。你可以根据 ref 的年龄以及代码仓库中已索引的 ref 数量来定义 ref 保留策略。你也可以根据代码仓库中已索引的语义版本数量,在 major、minor、patch、build 和 prerelease 等任意粒度上定义这些策略。一些常见的配置包括:仅保留默认分支的最新提交,或者仅保留每个主要版本和次要版本发布标签中最新的补丁版本标签。
Elastic Agent Builder tools
有了索引设计之后,我们就可以查看用于查询这些索引的 tools。
Agent Builder 中的 ES|QL tools 是带有描述的参数化 ES|QL 查询,用于指导 agent 使用这些 tools。Sourcerer 针对多个用途提供了 tools:代码仓库发现、文件发现、代码搜索和代码展示。这些 tools 复现了编码 agents 在探索代码时倾向使用的 shell 命令的名称、输入和输出,使它们足够直观,让 LLM 只需要在上下文窗口中接收最少的指令就能使用。
代码仓库发现
这些通常是 agent 首先调用的 tools。与大多数在文件系统中的单个代码仓库内进行搜索的编码 agents 不同,Sourcerer 知道其搜索空间很可能包含多个代码仓库和版本,因此它的第一步是决定搜索范围应该限定在哪些代码仓库和 ref 上。
-
sourcerer.repos.list:列出可供搜索的代码仓库。 -
sourcerer.repos.search:列出文件内容与给定查询最匹配的代码仓库。 -
sourcerer.refs.list:列出可供搜索的代码仓库和 ref。
文件发现
所有文件发现 tools 都支持对文件路径进行 glob 匹配(* 和 **)。
-
sourcerer.files.ls:列出与给定模式匹配的文件和目录。 -
sourcerer.files.tree:以树状格式列出与给定模式匹配的文件和目录。 -
sourcerer.files.wc:统计每个匹配文件的行数、单词数、字符数、字节数和最长行长度。
代码搜索
所有文件代码搜索 tools 都支持对文件路径进行 glob 匹配(* 和 **)。
-
[sourcerer.code.grep](https://github.com/elastic/sourcerer/blob/main/src/sourcerer/elastic/agent_builder_tools/sourcerer.code.grep.yml "sourcerer.code.grep"):使用[RLIKE](https://www.elastic.co/docs/reference/query-languages/sql/sql-like-rlike-operators "RLIKE")在[wildcard](https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/keyword#wildcard-field-type "wildcard")字段上搜索代码行,以快速执行正则表达式。 -
[sourcerer.code.search](https://github.com/elastic/sourcerer/blob/main/src/sourcerer/elastic/agent_builder_tools/sourcerer.code.search.yml "sourcerer.code.search"):使用[MATCH](https://www.elastic.co/docs/reference/query-languages/sql/sql-functions-search#sql-functions-search-match "MATCH")在针对代码搜索进行调优的[text](https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/text "text")字段上搜索代码行。
代码检索
所有文件代码检索 tools 都会拼接每个匹配文件中所需的代码行,并以 grep -n 格式将它们作为一个连续的代码块返回。这是编码 agents 所偏好的格式,包括 Claude Code 内置的 code.claude.com/docs/en/too... tool。这使 agent 可以看到文件内容的忠实表示,并获得代码行级别的归属信息,从而实现精确引用,而不需要 agent 自己重建文件内容或通过推理确定代码行号。
例如,[sourcerer.files.head](https://github.com/elastic/sourcerer/blob/main/src/sourcerer/elastic/agent_builder_tools/sourcerer.files.head.yml "sourcerer.files.head") tool 会从 lines 索引中的 5 个文档重建 Kibana 的 README.md 文件,并按照以下格式展示前 5 行:
kotlin
`
1. 1:# Kibana
2. 2:
3. 3:Kibana is the open source interface to query, analyze, visualize, and manage your data stored in Elasticsearch.
4. 4:
5. 5:- [Getting Started](#getting-started)
`AI写代码
所有文件代码检索 tools 都支持对文件路径进行 glob 匹配(* 和 **)。agents 通常使用这些 tools 来展示单个文件的内容。
-
[sourcerer.files.cat](https://github.com/elastic/sourcerer/blob/main/src/sourcerer/elastic/agent_builder_tools/sourcerer.files.cat.yml "sourcerer.files.cat"):拼接并展示每个匹配文件的所有代码行。 -
[sourcerer.files.head](https://github.com/elastic/sourcerer/blob/main/src/sourcerer/elastic/agent_builder_tools/sourcerer.files.head.yml "sourcerer.files.head"):拼接并展示每个匹配文件的前n行。 -
[sourcerer.files.tail](https://github.com/elastic/sourcerer/blob/main/src/sourcerer/elastic/agent_builder_tools/sourcerer.files.tail.yml "sourcerer.files.tail"):拼接并展示每个匹配文件的最后n行。 -
[sourcerer.files.read_lines](https://github.com/elastic/sourcerer/blob/main/src/sourcerer/elastic/agent_builder_tools/sourcerer.files.read_lines.yml "sourcerer.files.read_lines"):拼接并展示每个匹配文件中两个给定行号之间的代码行范围。
Agent Builder skills
有了这些 tools 之后,我们就可以查看用于指导 agent 正确使用这些 tools 的 skills。agents 通常会按照以下顺序调用这些 skills:
-
[sourcerer-repo-discovery](https://github.com/elastic/sourcerer/blob/main/src/sourcerer/skills/repo-discovery/SKILL.md "sourcerer-repo-discovery"):指导 agent 根据给定提示词发现并选择可供搜索的代码仓库。虽然这通常是 agent 调用的第一个 skill,但当 agent 从其他代码仓库追踪依赖项,或者回答涉及多个代码仓库或版本的问题时,也可能再次调用它。 -
[sourcerer-ref-resolution](https://github.com/elastic/sourcerer/blob/main/src/sourcerer/skills/ref-resolution/SKILL.md "sourcerer-ref-resolution"):指导 agent 将标签或分支的名称解析为唯一且不可变的提交哈希。这使 agent 能够可靠地将搜索范围限定到单个提交快照。 -
[sourcerer-code-search](https://github.com/elastic/sourcerer/blob/main/src/sourcerer/skills/code-search/SKILL.md "sourcerer-code-search"):指导 agent 探索代码,并提供关于何时以及如何使用可用 tools 以实现最大效率的基本最佳实践。 -
[sourcerer-code-citations](https://github.com/elastic/sourcerer/blob/main/src/sourcerer/skills/code-citations/SKILL.md "sourcerer-code-citations"):指导 agent 引用文件、目录、代码行以及代码行范围。Sourcerer 会为每个主要 Git 托管提供商自动生成更具体的引用 skills,使其引用链接符合各个托管平台对应的 URL 格式。
Agent 系统提示词
Agent 的最终打包版本包含一个简洁描述 agent 角色及其高级指令的系统提示词。定义系统提示词的配置文件同时定义了 agent 可以使用的 tools 和 skills,因此它只能执行我们允许它执行的操作。
Sourcerer CLI
Sourcerer CLI 协助完成设置和索引,以及清理操作,从而让日常运行保持简单。配置通过 [sourcerer.yml](https://github.com/elastic/sourcerer/blob/main/specs/sourcerer-yml.md "sourcerer.yml") 配置文件进行管理。
-
sourcerer setup:幂等地加载索引模板和 Agent Builder 配置,以及 Kibana 仪表板。这通常只需要执行一次,并且只需几秒钟。 -
sourcerer index:检查是否存在与[sourcerer.yml](https://github.com/elastic/sourcerer/blob/main/specs/sourcerer-yml.md "sourcerer.yml")中定义的模式匹配的新 ref,然后以幂等方式对其建立索引,跳过已经建立索引的 ref,以及根据保留策略符合清理条件的 ref。它会调用git来克隆代码仓库、列出远程 ref,以及检出 ref。 -
sourcerer prune:检查保留策略中是否有符合清理条件的 ref,然后使用_delete_by_query从三个索引中删除这些 ref。
你可以使用 cron 等外部调度器轻松安排索引和清理任务。在 Elastic 的内部使用场景中,我们将 [sourcerer.yml](https://github.com/elastic/sourcerer/blob/main/specs/sourcerer-yml.md "sourcerer.yml") 文件维护在私有 Git 代码仓库中,并使用 GitHub Actions调度索引和清理。我更倾向于频繁地对代码建立索引,同时在正常工作时间之外进行清理,以防止 agent 在对话过程中突然失去上下文。
功能许可证总结
为了保持透明,下面总结了该解决方案设计中提及的所有非开源软件(非 OSS)功能的许可证级别。
订阅功能:
-
Agent Builder(可选;你也可以使用 Elasticsearch API,通过其他 harness 查询这些索引)。
-
Synthetic _source(可选)。
-
文档级安全(可选)。
免费功能,由 Elastic 专有(根据 Open Source Initiative(OSI)的定义不属于 OSS):
订阅服务让你可以享受 Agent Builder 带来的"一切开箱即用"的体验,同时提供资源优化、更细粒度的安全权限以及平台支持。没有订阅,你仍然可以使用 Sourcerer CLI 对代码进行索引和清理,并使用 Elasticsearch API 搜索代码。
总结
Sourcerer 证明了 Elasticsearch + Agent Builder 是用于 agentic 企业代码智能的出色解决方案,这一点体现在多个方面:
-
**准确:**Sourcerer 的 agentic 代码检索质量与前沿编码 agents 相当,这一点通过其在学术基准测试 SWE-Explore 上的表现得到了证明。
-
**快速:**Sourcerer 的查询速度从单个代码仓库中的毫秒级,到跨越 10 亿行代码时的数秒级不等。索引速度也远快于使用向量嵌入生成所能达到的速度。
-
**可扩展:**Elasticsearch 的分片支持横向扩展,使得搜索整个企业软件资产的当前状态和历史状态成为可能。或者,Serverless 的无状态架构可以自然地进行扩展,而无需规划分片。
-
**高韧性:**Elasticsearch 的副本机制支持高可用性,使 agent 能够全天候运行。同样,Serverless 的无状态架构天然提供高可用性。
-
**多语言:**Sourcerer 的索引和检索方法完全与编程语言无关,并且能够容忍格式错误的代码。
-
**安全:**Elasticsearch 的文档级安全可以在组织、代码仓库和提交级别为人和 agents 强制执行访问策略。
-
**高效:**Sourcerer 证明了对于其目标使用场景而言,它能够高效利用存储、内存、计算资源和 token 消耗。
-
**易管理:**Sourcerer CLI,以及它对原生
git命令和 Elastic REST API 的使用,使其易于开始使用、运行和进行调度。 -
**通用:**所有行业的组织都会构建软件,而 Git 是约 85% 组织的事实来源(source)。Sourcerer 对所有这些组织都有价值。
Sourcerer 目前还不到两个月,而基准测试结果表明,它在召回率、token 效率以及任务耗时方面仍有改进空间。在不久的将来,我会继续推进这个项目。
亲自试试
Sourcerer 依赖 Elasticsearch 和 Kibana。你可以通过以下几种方式开始使用:
-
Elastic Cloud(包含 Enterprise 试用版)。
-
使用 Docker 本地部署(包含 Enterprise 试用版)。
然后,你就可以开始使用 Sourcerer。
我使用 Agent Builder 构建了这个项目。你会构建什么?
参考文献
Sen, S.(2026)。Is Grep All You Need? How Agent Harnesses Reshape Agentic Search 。arXiv 2605.15184 Is Grep All You Need? How Agent Harnesses Reshape Agentic Search。
Zhang, S.(2026)。SWE-Explore: Benchmarking How Coding Agents Explore Repositories 。arXiv 2606.07297 SWE-Explore: Benchmarking How Coding Agents Explore Repositories。
附录
附录 A. SWE-Explore 基准测试配置
基准测试环境:
-
Sourcerer 版本:v1.0.0(提交哈希:26d2e84e3f9e4c1e1598a48532eba9a739465c3e)
-
Elastic Cloud Hosted(ECH):
-
区域:GCP - 洛杉矶(us-west2)
-
CPU 优化型硬件(c4a-highcpu)
-
3 个 Elasticsearch 数据节点(每个节点配备 16GiB RAM、8 个 vCPU)
-
2 个 Kibana 实例(每个实例配备 2GiB RAM)
-
Elastic stack 版本:v9.5.0(提交哈希:dbedef007f580447413782705acb9afec41f945b)
-
-
Claude Code 版本:2.1.202
-
LLM:GPT-5.4
通过 GET /_cat/indices 获取的索引信息:
arduino
`index pri rep docs.count docs.deleted store.size`AI写代码
markdown
`
1. index pri rep docs.count docs.deleted store.size
2. sourcerer-v1-files~ansible~ansible 1 1 234423 0 18.5mb
3. sourcerer-v1-files~apache~druid 1 1 46720 0 4.1mb
4. sourcerer-v1-files~apache~lucene 1 1 47932 0 4.2mb
5. sourcerer-v1-files~astral-sh~ruff 1 1 49021 0 9.1mb
6. sourcerer-v1-files~astropy~astropy 1 1 38994 0 6.1mb
7. sourcerer-v1-files~axios~axios 1 1 339 0 89kb
8. sourcerer-v1-files~babel~babel 1 1 24798 0 4.9mb
9. sourcerer-v1-files~briannesbitt~carbon 1 1 14054 0 1.2mb
10. sourcerer-v1-files~burntsushi~ripgrep 1 1 408 0 232.4kb
11. sourcerer-v1-files~caddyserver~caddy 1 1 2162 0 533.9kb
12. sourcerer-v1-files~django~django 1 1 1329708 0 91.7mb
13. sourcerer-v1-files~element-hq~element-web 1 1 29426 0 6.1mb
14. sourcerer-v1-files~facebook~docusaurus 1 1 9288 0 2mb
15. sourcerer-v1-files~faker-ruby~faker 1 1 1130 0 207.2kb
16. sourcerer-v1-files~fastlane~fastlane 1 1 13663 0 1.7mb
17. sourcerer-v1-files~flipt-io~flipt 1 1 10518 0 873.8kb
18. sourcerer-v1-files~fluent~fluentd 1 1 3340 0 744.4kb
19. sourcerer-v1-files~fmtlib~fmt 1 1 1243 0 133.7kb
20. sourcerer-v1-files~future-architect~vuls 1 1 3952 0 828kb
21. sourcerer-v1-files~gin-gonic~gin 1 1 217 0 76.6kb
22. sourcerer-v1-files~gohugoio~hugo 1 1 11545 0 1.2mb
23. sourcerer-v1-files~google~gson 1 1 1432 0 349.4kb
24. sourcerer-v1-files~gravitational~teleport 1 1 100317 0 16.3mb
25. sourcerer-v1-files~hashicorp~terraform 1 1 13511 0 1.5mb
26. sourcerer-v1-files~immutable-js~immutable-js 1 1 458 0 120kb
27. sourcerer-v1-files~internetarchive~openlibrary 1 1 35448 0 5.9mb
28. sourcerer-v1-files~javaparser~javaparser 1 1 5154 0 1.3mb
29. sourcerer-v1-files~jekyll~jekyll 1 1 720 0 272.9kb
30. sourcerer-v1-files~jordansissel~fpm 1 1 167 0 98.3kb
31. sourcerer-v1-files~jqlang~jq 1 1 1172 0 291.4kb
32. sourcerer-v1-files~laravel~framework 1 1 29021 0 4.9mb
33. sourcerer-v1-files~matplotlib~matplotlib 1 1 133013 0 19.9mb
34. sourcerer-v1-files~micropython~micropython 1 1 15631 0 3mb
35. sourcerer-v1-files~mrdoob~three.js 1 1 9858 0 1.1mb
36. sourcerer-v1-files~mwaskom~seaborn 1 1 633 0 181kb
37. sourcerer-v1-files~navidrome~navidrome 1 1 10301 0 2.1mb
38. sourcerer-v1-files~nlohmann~json 1 1 1090 0 281.9kb
39. sourcerer-v1-files~nodebb~nodebb 1 1 104805 0 14.5mb
40. sourcerer-v1-files~nushell~nushell 1 1 8990 0 1.7mb
41. sourcerer-v1-files~pallets~flask 1 1 251 0 120.1kb
42. sourcerer-v1-files~php-cs-fixer~php-cs-fixer 1 1 8300 0 1mb
43. sourcerer-v1-files~phpoffice~phpspreadsheet 1 1 17140 0 3.6mb
44. sourcerer-v1-files~preactjs~preact 1 1 3446 0 772.5kb
45. sourcerer-v1-files~projectlombok~lombok 1 1 24153 0 4mb
46. sourcerer-v1-files~prometheus~prometheus 1 1 3416 0 644.9kb
47. sourcerer-v1-files~protonmail~webclients 1 1 84927 0 14.9mb
48. sourcerer-v1-files~psf~requests 1 1 928 0 329.4kb
49. sourcerer-v1-files~pydata~xarray 1 1 5114 0 910.6kb
50. sourcerer-v1-files~pylint-dev~pylint 1 1 24945 0 3.8mb
51. sourcerer-v1-files~pytest-dev~pytest 1 1 8880 0 1.3mb
52. sourcerer-v1-files~qutebrowser~qutebrowser 1 1 29453 0 4.6mb
53. sourcerer-v1-files~reactivex~rxjava 1 1 1959 0 523.7kb
54. sourcerer-v1-files~redis~redis 1 1 12646 0 2mb
55. sourcerer-v1-files~rubocop~rubocop 1 1 15349 0 3.3mb
56. sourcerer-v1-files~scikit-learn~scikit-learn 1 1 39030 0 6.2mb
57. sourcerer-v1-files~sharkdp~bat 1 1 2526 0 789.3kb
58. sourcerer-v1-files~sphinx-doc~sphinx 1 1 55638 0 8.8mb
59. sourcerer-v1-files~sympy~sympy 1 1 112572 0 16.7mb
60. sourcerer-v1-files~tokio-rs~axum 1 1 1474 0 365.9kb
61. sourcerer-v1-files~tokio-rs~tokio 1 1 5175 0 1mb
62. sourcerer-v1-files~tutao~tutanota 1 1 9267 0 1.6mb
63. sourcerer-v1-files~uutils~coreutils 1 1 3226 0 806.6kb
64. sourcerer-v1-files~valkey-io~valkey 1 1 6681 0 1.4mb
65. sourcerer-v1-files~vuejs~core 1 1 2785 0 750kb
66. sourcerer-v1-lines~ansible~ansible 1 1 23067027 0 6.9gb
67. sourcerer-v1-lines~apache~druid 1 1 10230691 0 1.4gb
68. sourcerer-v1-lines~apache~lucene 1 1 9857695 0 1.5gb
69. sourcerer-v1-lines~astral-sh~ruff 1 1 5895776 0 797.9mb
70. sourcerer-v1-lines~astropy~astropy 1 1 16855521 0 5.1gb
71. sourcerer-v1-lines~axios~axios 1 1 104915 0 17.3mb
72. sourcerer-v1-lines~babel~babel 1 1 661054 0 96.4mb
73. sourcerer-v1-lines~briannesbitt~carbon 1 1 2256429 0 339mb
74. sourcerer-v1-lines~burntsushi~ripgrep 1 1 121406 0 20.5mb
75. sourcerer-v1-lines~caddyserver~caddy 1 1 423391 0 60.4mb
76. sourcerer-v1-lines~django~django 1 1 187595831 0 26.4gb
77. sourcerer-v1-lines~element-hq~element-web 1 1 5435563 0 1gb
78. sourcerer-v1-lines~facebook~docusaurus 1 1 1098502 0 173mb
79. sourcerer-v1-lines~faker-ruby~faker 1 1 281240 0 72.8mb
80. sourcerer-v1-lines~fastlane~fastlane 1 1 3774359 0 515.3mb
81. sourcerer-v1-lines~flipt-io~flipt 1 1 2318144 0 339.1mb
82. sourcerer-v1-lines~fluent~fluentd 1 1 616236 0 87.9mb
83. sourcerer-v1-lines~fmtlib~fmt 1 1 525144 0 78.7mb
84. sourcerer-v1-lines~future-architect~vuls 1 1 1340951 0 410.3mb
85. sourcerer-v1-lines~gin-gonic~gin 1 1 42186 0 6.2mb
86. sourcerer-v1-lines~gohugoio~hugo 1 1 1323287 0 415.2mb
87. sourcerer-v1-lines~google~gson 1 1 269864 0 82.1mb
88. sourcerer-v1-lines~gravitational~teleport 1 1 35961497 0 5.3gb
89. sourcerer-v1-lines~hashicorp~terraform 1 1 1913651 0 284.3mb
90. sourcerer-v1-lines~immutable-js~immutable-js 1 1 132082 0 41.1mb
91. sourcerer-v1-lines~internetarchive~openlibrary 1 1 6505234 0 903.1mb
92. sourcerer-v1-lines~javaparser~javaparser 1 1 756387 0 233.7mb
93. sourcerer-v1-lines~jekyll~jekyll 1 1 57072 0 9.2mb
94. sourcerer-v1-lines~jordansissel~fpm 1 1 32285 0 8.8mb
95. sourcerer-v1-lines~jqlang~jq 1 1 373367 0 56.1mb
96. sourcerer-v1-lines~laravel~framework 1 1 4680025 0 1.2gb
97. sourcerer-v1-lines~matplotlib~matplotlib 1 1 24426468 0 3.6gb
98. sourcerer-v1-lines~micropython~micropython 1 1 2104199 0 320mb
99. sourcerer-v1-lines~mrdoob~three.js 1 1 5519778 0 1014.6mb
100. sourcerer-v1-lines~mwaskom~seaborn 1 1 219103 0 35mb
101. sourcerer-v1-lines~navidrome~navidrome 1 1 1293741 0 405.1mb
102. sourcerer-v1-lines~nlohmann~json 1 1 174859 0 53.5mb
103. sourcerer-v1-lines~nodebb~nodebb 1 1 6621106 0 2.2gb
104. sourcerer-v1-lines~nushell~nushell 1 1 1424029 0 405.3mb
105. sourcerer-v1-lines~pallets~flask 1 1 34538 0 11.1mb
106. sourcerer-v1-lines~php-cs-fixer~php-cs-fixer 1 1 1272280 0 357.6mb
107. sourcerer-v1-lines~phpoffice~phpspreadsheet 1 1 1999435 0 291mb
108. sourcerer-v1-lines~preactjs~preact 1 1 975966 0 274.3mb
109. sourcerer-v1-lines~projectlombok~lombok 1 1 1480367 0 470.7mb
110. sourcerer-v1-lines~prometheus~prometheus 1 1 1132908 0 484.6mb
111. sourcerer-v1-lines~protonmail~webclients 1 1 22247802 0 3.2gb
112. sourcerer-v1-lines~psf~requests 1 1 337782 0 144.1mb
113. sourcerer-v1-lines~pydata~xarray 1 1 2413091 0 714.7mb
114. sourcerer-v1-lines~pylint-dev~pylint 1 1 1188057 0 351.7mb
115. sourcerer-v1-lines~pytest-dev~pytest 1 1 1882734 0 532.2mb
116. sourcerer-v1-lines~qutebrowser~qutebrowser 1 1 6734755 0 1gb
117. sourcerer-v1-lines~reactivex~rxjava 1 1 486774 0 135.3mb
118. sourcerer-v1-lines~redis~redis 1 1 3551629 0 1gb
119. sourcerer-v1-lines~rubocop~rubocop 1 1 2899319 0 763.3mb
120. sourcerer-v1-lines~scikit-learn~scikit-learn 1 1 10940081 0 3.2gb
121. sourcerer-v1-lines~sharkdp~bat 1 1 317845 0 118mb
122. sourcerer-v1-lines~sphinx-doc~sphinx 1 1 15102452 0 3.9gb
123. sourcerer-v1-lines~sympy~sympy 1 1 45538891 0 13.8gb
124. sourcerer-v1-lines~tokio-rs~axum 1 1 146486 0 40.6mb
125. sourcerer-v1-lines~tokio-rs~tokio 1 1 1068294 0 293.9mb
126. sourcerer-v1-lines~tutao~tutanota 1 1 2932455 0 985.3mb
127. sourcerer-v1-lines~uutils~coreutils 1 1 478330 0 127.7mb
128. sourcerer-v1-lines~valkey-io~valkey 1 1 1876977 0 575.5mb
129. sourcerer-v1-lines~vuejs~core 1 1 684268 0 190.9mb
130. sourcerer-v1-refs 1 1 847 0 457.5kb
`AI写代码收起代码块
基准测试任务提示词:

表 6。该表比较了原论文中使用的 Claude Code 任务提示词和本次 benchmark 运行中使用的 Sourcerer 任务提示词。高亮部分表示 Sourcerer 的任务提示词与 Claude Code 的任务提示词之间的差异:红色表示 Claude Code 任务提示词中存在、但 Sourcerer 没有使用的指令;绿色表示 Sourcerer 任务提示词中特有的指令。
附录 B。代码搜索速度和可扩展性 benchmark 配置
Benchmark 环境:
-
Sourcerer 版本:v2.0.0(commit hash:dcc373b3bf7b24168e00ed7b172aea7227f705f9)
-
用于 ES|QL 的 Elastic Cloud Hosted(ECH):
-
区域:GCP - Los Angeles(us-west2)
-
CPU 优化型硬件(c4a-highcpu)
-
2 个 Elasticsearch data nodes(每个节点 16GiB RAM、8 个 vCPUs)
-
1 个 Kibana 实例(2GiB RAM)
-
Elastic Stack 版本:v9.5.0(commit hash:8d4246a64bc255212407b1b313fe402391299c88)
-
-
用于
ripgrep和grep的虚拟机:-
区域:GCP - Los Angeles(us-west2-a)
-
CPU 优化型硬件(c4a-highcpu-8-lssd)
-
16GiB RAM
-
镜像:projects/ubuntu-os-cloud/global/images/ubuntu-2404-noble-arm64-v20260717
-
Provisioned IOPS:3300
-
Provisioned throughput:215
-
索引信息由 GET /_cat/indices 返回:
lua
`
1. index pri rep docs.count docs.deleted store.size
2. sourcerer-v2-lines~github~elastic~elasticsearch 1 0 200387235 0 44.8gb
`AI写代码
调整 Cluster settings,以避免在高频 pattern 上截断返回的匹配数量:
bash
`
1. PUT /_cluster/settings
2. {
3. "persistent": {
4. "esql.query.result_truncation_max_size": 1000000
5. }
6. }
`AI写代码
ripgrep 和 grep 使用的正则表达式:
-
DiskBBQ:
.*[dD][iI][sS][kK][-_]?[bB][bB][qQ].* -
XContentType:
.*[xX][cC][oO][nN][tT][eE][nN][tT][tT][yY][pP][eE].*
sourcerer.code.grep 使用的 ES|QL 查询语法:
ini
`
1. FROM sourcerer-lines
2. | WHERE git.host LIKE ?git_host
3. AND git.org LIKE ?git_org
4. AND git.repo LIKE ?git_repo
5. AND git.commit LIKE ?git_commit
6. AND file.path LIKE ?file_path
7. AND line.content RLIKE ?regex
8. | EVAL _fp_is_recursive = ?file_path != REPLACE(?file_path, "[*][*]", "")
9. | EVAL _fp_segs = LENGTH(?file_path) - LENGTH(REPLACE(?file_path, "/", "")) + 1
10. | EVAL _file_segs = MV_COUNT(SPLIT(file.path, "/"))
11. | WHERE _fp_is_recursive OR _file_segs == _fp_segs
12. | KEEP git.host, git.org, git.repo, git.commit, file.path, file.size, line.number, line.content
13. | SORT git.host, git.org, git.repo, git.commit, file.path, line.number
14. | LIMIT ?n
`AI写代码
sourcerer.code.search 使用的 ES|QL 查询语法:
sql
`
1. FROM sourcerer-lines METADATA _score
2. | WHERE git.host LIKE ?git_host
3. AND git.org LIKE ?git_org
4. AND git.repo LIKE ?git_repo
5. AND git.commit LIKE ?git_commit
6. AND file.path LIKE ?file_path
7. AND MATCH(line.content.text, ?q)
8. | EVAL _fp_is_recursive = ?file_path != REPLACE(?file_path, "[*][*]", "")
9. | EVAL _fp_segs = LENGTH(?file_path) - LENGTH(REPLACE(?file_path, "/", "")) + 1
10. | EVAL _file_segs = MV_COUNT(SPLIT(file.path, "/"))
11. | WHERE _fp_is_recursive OR _file_segs == _fp_segs
12. | SORT _score DESC
13. | KEEP git.host, git.org, git.repo, git.commit, file.path, file.size, line.number, line.content
14. | LIMIT ?n
`AI写代码
所有测试中使用的 ES|QL 查询参数:
-
git_host="github" -
git_org="elastic" -
git_repo="elasticsearch" -
n=1000000
特定测试中使用的 ES|QL 查询参数:
-
单个 commit 的 corpus:
git_commit="45f6a06b1b441b41fe711059b8720013173e7c89" -
所有 commit 的 corpus:
git_commit="*" -
sourcerer.code.grep的稀有 pattern(DiskBBQ):regex=".*[dD][iI][sS][kK][-_]?[bB][bB][qQ].*" -
sourcerer.code.grep的常见 pattern(XContentType):regex=".*[xX][cC][oO][nN][tT][eE][nN][tT][tT][yY][pP][eE].*" -
sourcerer.code.search的常见 pattern(DiskBBQ):q="DiskBBQ" -
sourcerer.code.search的常见 pattern(XContentType):q="XContentType"
n 被设置得足够高,以便获取每个 pattern 的真实、未截断的匹配数量(DiskBBQ 分别为 605 和 1,041 个匹配;XContentType 分别为 7,999 和 290,662 个匹配)。在每种情况下,都通过将 hits_returned 与 ripgrep 和 grep 在相同底层数据上的匹配数量进行比较进行了确认。
ripgrep 使用的命令语法:
rg -n --no-ignore -j 6
grep 使用的命令语法:
grep -E -r -n --binary-files=without-match --exclude-dir=.git
每个 cloned repository snapshot 的目录,以及其中由 Git 跟踪的非二进制文件的大致总大小。这就是 ripgrep 和 grep 的搜索空间:
markdown
`
1. 54M elastic-elasticsearch-v6.0.1
2. 55M elastic-elasticsearch-v6.1.4
3. 56M elastic-elasticsearch-v6.2.4
4. 79M elastic-elasticsearch-v6.3.2
5. 83M elastic-elasticsearch-v6.4.3
6. 89M elastic-elasticsearch-v6.5.4
7. 95M elastic-elasticsearch-v6.6.2
8. 99M elastic-elasticsearch-v6.7.2
9. 100M elastic-elasticsearch-v6.8.23
10. 99M elastic-elasticsearch-v7.0.1
11. 99M elastic-elasticsearch-v7.1.1
12. 135M elastic-elasticsearch-v7.10.2
13. 137M elastic-elasticsearch-v7.11.2
14. 140M elastic-elasticsearch-v7.12.1
15. 144M elastic-elasticsearch-v7.13.4
16. 147M elastic-elasticsearch-v7.14.2
17. 149M elastic-elasticsearch-v7.15.2
18. 154M elastic-elasticsearch-v7.16.3
19. 157M elastic-elasticsearch-v7.17.29
20. 103M elastic-elasticsearch-v7.2.1
21. 105M elastic-elasticsearch-v7.3.2
22. 109M elastic-elasticsearch-v7.4.2
23. 113M elastic-elasticsearch-v7.5.2
24. 118M elastic-elasticsearch-v7.6.2
25. 124M elastic-elasticsearch-v7.7.1
26. 127M elastic-elasticsearch-v7.8.1
27. 132M elastic-elasticsearch-v7.9.3
28. 149M elastic-elasticsearch-v8.0.1
29. 152M elastic-elasticsearch-v8.1.3
30. 173M elastic-elasticsearch-v8.10.4
31. 182M elastic-elasticsearch-v8.11.4
32. 187M elastic-elasticsearch-v8.12.2
33. 191M elastic-elasticsearch-v8.13.4
34. 196M elastic-elasticsearch-v8.14.3
35. 205M elastic-elasticsearch-v8.15.5
36. 230M elastic-elasticsearch-v8.16.6
37. 233M elastic-elasticsearch-v8.17.10
38. 241M elastic-elasticsearch-v8.18.8
39. 250M elastic-elasticsearch-v8.19.18
40. 149M elastic-elasticsearch-v8.2.3
41. 153M elastic-elasticsearch-v8.3.3
42. 155M elastic-elasticsearch-v8.4.3
43. 159M elastic-elasticsearch-v8.5.3
44. 161M elastic-elasticsearch-v8.6.2
45. 164M elastic-elasticsearch-v8.7.1
46. 168M elastic-elasticsearch-v8.8.2
47. 170M elastic-elasticsearch-v8.9.2
48. 231M elastic-elasticsearch-v9.0.8
49. 244M elastic-elasticsearch-v9.1.10
50. 254M elastic-elasticsearch-v9.2.8
51. 267M elastic-elasticsearch-v9.3.7
52. 305M elastic-elasticsearch-v9.4.3
`AI写代码
原文:Code search with Elastic: Thousands of times faster than grep | Elasticsearch Labs