为什么搜索速度对 AI 智能体和上下文工程很重要
我们在 2000 万文档语料库上进行的基准测试显示,Elasticsearch 在过滤向量搜索方面的吞吐量比 OpenSearch 高达 8 倍,同时在我们测试的配置中也实现了更高的 Recall@100。上下文工程不仅仅依赖快速的向量检索。随着工作流的迭代,团队还需要强大的相关性控制(如混合搜索和过滤)、操作简便性和可预测的性能。但是,由于智能体通常会在每个请求中多次运行检索、推理、检索循环,因此检索延迟会成倍增加,所以这方面的改进会直接转化为更好的端到端响应能力和更低的成本。

图表 1:吞吐量。
对于上下文工程来说,检索不是一次性的步骤。智能体和应用程序会反复运行循环,例如检索→推理→检索,以完善查询、验证事实、组合基础上下文并完成任务。这种模式在智能体工作流和迭代检索增强生成 (RAG) 中很常见。由于每个用户请求可能会多次调用检索,这会增加响应延迟和/或增加基础设施成本。

图 1:上下文工程通过反复检索和整理,将大型上下文池(文档、内存、工具、聊天记录)转化为有限的大型语言模型 (LLM) 上下文窗口。
上下文工程的优化实施是一项新兴技术。不同工作流的迭代次数差别很大。这些基准测试结果中最根本的关键概念是上下文工程具有方向性:迭代检索会使延迟成倍增加。
为什么向量搜索性能至关重要?
想象一个购物助手回答以下问题:“我需要一个价格在 60 美元以下的随身背包,它可以容纳一台 15 英寸的笔记本电脑、防水,并且可以在周五之前送达。”
在生产环境中,助手很少只发出一个向量查询然后停止。它会运行一个检索循环以构建正确的上下文,并且每一步通常会受到过滤条件的限制,如可用性、地区、发货承诺、品牌规则和政策资格。
第 1 步:解读意图,并转化为约束条件。
智能体可将请求转化为结构化的过滤条件和语义查询,例如:
- 过滤条件:有现货,可配送至用户邮编,可在周五前送达,价格低于 60 美元,有效上架
- 向量查询:“随身背包15英寸笔记本电脑防水”
步骤 2:检索候选对象,然后进行细化。
它通常会重复检索,但会有所变化,以避免遗漏好的匹配结果:
- “旅行背包随身便携笔记本电脑保护套”
- “15 英寸防水通勤背包”
- “轻型机舱背包”
每个查询都使用相同的资格过滤条件,因为检索无关或不可用的项目会造成上下文的浪费。
步骤 3:展开以确认详细信息并降低风险。
代理随后再次检索以验证影响最终答案的关键属性:
- 材料与防水性表述
- 尺寸和笔记本隔层都适合
- 退货政策或保修限制
- 库存不足时的替代方案
这就是多步上下文工程:检索、推理、检索、组合。
延迟与召回为何对上下文工程至关重要
这些交互可能涉及每个用户会话中数十次过滤的检索调用。这使得每次调用的延迟成为端到端响应时间的直接倍增因素,而低召回率则迫使进行额外的重试或导致智能体错过符合条件的项目,导致答案质量下降。
要点:在上下文工程系统中,过滤近似最近邻 (ANN) 并非一次单一查找。由于这是在约束条件下的重复操作,因此即使大型语言模型 (LLM) 是最明显的组件,向量搜索性能也会立即体现在延迟、吞吐量和成本上。
基准测试
成果度
在图表 2 中,每个点代表一个测试配置。最佳结果出现在左上方,这意味着更高的召回率和更低的延迟。Elasticsearch 的结果始终比 OpenSearch 更接近左上角,表明在相同的工作负载配置下,Elasticsearch 具有更好的速度和准确率。

图表 2:召回率与平均延迟(重评分 1)。
一些关键见解
s_n_r_value:size_numCandidates_rescoreOversample的简写(在这些测试中,k 和 numCandidates 设置为等于 numCandidates),例如,100_500_1表示 size=100、numCandidates=500 和 k=500,重新评分过采样=1。- 召回率:该配置的测量召回率@100
- 平均延迟(毫秒):每次查询的平均端到端延迟
- 吞吐量:每秒查询次数
- 召回率 (%):Elasticsearch 相较于 OpenSearch 的相对召回率提升 (Elasticsearch - OpenSearch) / OpenSearch
- 延迟 Xs:OpenSearch 平均延迟除以 Elasticsearch 平均延迟
- 吞吐量 Xs:Elasticsearch 吞吐量除以 OpenSearch 吞吐量
| 引擎 | `s_n_r_value` | 召回 | 平均延迟(ms) | 吞吐量 | 召回率% | 延迟 Xs | 吞吐量 Xs |
|---|---|---|---|---|---|---|---|
| Elasticsearch | 100_250_1 | 0.7704 | 25 | 534.75 | 9.70% | 2.28 | 1.91 |
| OpenSearch | 100_250_1 | 0.7023 | 57.08 | 279.58 | |||
| Elasticsearch | 100_500_1 | 0.8577 | 25.42 | 524.14 | 7.20% | 2.4 | 2 |
| OpenSearch | 100_500_1 | 0.8001 | 60.9 | 262.12 | |||
| Elasticsearch | 100_750_1 | 0.8947 | 29.67 | 528.09 | 5.72% | 2.25 | 2.21 |
| OpenSearch | 100_750_1 | 0.8463 | 66.76 | 239.11 | |||
| Elasticsearch | 100_1000_1 | 0.9156 | 29.65 | 534.5 | 4.66% | 2.46 | 2.44 |
| OpenSearch | 100_1000_1 | 0.8748 | 72.88 | 219.01 | |||
| Elasticsearch | 100_1500_1 | 0.9386 | 31.84 | 497.3 | 3.38% | 2.71 | 2.68 |
| OpenSearch | 100_1500_1 | 0.9079 | 86.16 | 185.4 | |||
| Elasticsearch | 100_2000_1 | 0.9507 | 34.69 | 457.2 | 2.57% | 2.98 | 2.96 |
| OpenSearch | 100_2000_1 | 0.9269 | 103.36 | 154.55 | |||
| Elasticsearch | 100_2500_1 | 0.9582 | 37.9 | 418.43 | 1.99% | 3.28 | 3.26 |
| OpenSearch | 100_2500_1 | 0.9395 | 124.29 | 128.53 | |||
| Elasticsearch | 100_3000_1 | 0.9636 | 41.86 | 379.4 | 1.62% | 3.46 | 3.44 |
| OpenSearch | 100_3000_1 | 0.9482 | 144.67 | 110.34 | |||
| Elasticsearch | 100_4000_1 | 0.9705 | 50.28 | 316.21 | 1.06% | 3.87 | 3.85 |
| OpenSearch | 100_4000_1 | 0.9603 | 194.36 | 82.22 | |||
| Elasticsearch | 100_5000_1 | 0.9749 | 58.77 | 270.91 | 0.73% | 4.43 | 4.41 |
| OpenSearch | 100_5000_1 | 0.9678 | 260.33 | 61.38 | |||
| Elasticsearch | 100_6000_1 | 0.9781 | 66.75 | 238.59 | 0.52% | 4.91 | 4.89 |
| OpenSearch | 100_6000_1 | 0.973 | 327.44 | 48.81 | |||
| Elasticsearch | 100_7000_1 | 0.9804 | 74.64 | 213.49 | 0.38% | 5.28 | 5.27 |
| OpenSearch | 100_7000_1 | 0.9767 | 394.24 | 40.53 | |||
| Elasticsearch | 100_8000_1 | 0.9823 | 82.28 | 193.59 | 0.27% | 6.86 | 6.83 |
| OpenSearch | 100_8000_1 | 0.9797 | 564.14 | 28.33 | |||
| Elasticsearch | 100_9000_1 | 0.9837 | 90.08 | 176.96 | 0.16% | 7.63 | 7.61 |
| OpenSearch | 100_9000_1 | 0.9821 | 687.25 | 23.25 | |||
| Elasticsearch | 100_10000_1 | 0.9848 | 97.64 | 163.31 | 0.08% | 8.38 | 8.36 |
| OpenSearch | 100_10000_1 | 0.984 | 818.64 | 19.53 |
例如,在 100_9000_1 处,OpenSearch 每次检索平均为 687 毫秒, Elasticsearch 为 90 毫秒,而在 10 步检索循环中,等待时间约为 10 x (687 - 90) = 6 秒。
查看完整结果。
方法
我们使用 Python 发送查询并跟踪响应时间及其他统计数据,向引擎发送了以下查询。请记住,任何向量搜索引擎的性能取决于您如何调整其核心参数:考虑多少个候选项,重新评分的程度,以及返回多少上下文。这些设置直接影响召回率(找到正确答案的可能性)和延迟(获得结果的速度)。
在我们的基准测试中,我们使用了通常在智能体检索循环中调整的候选对象、重新评分和结果大小设置,并测量了 Elasticsearch 在该工作负载下的表现。然后我们以相同的设置运行了 OpenSearch 作为参考。
OpenSearch
"size": <RESULT_SIZE>:返回给客户端的命中次数。在这个基准测试中,计算 Recall@100 的结果大小为 100。"k": <NUMBER_OF_CANDIDATES>:最近邻候选对象的数量。"ef_search": <NUMBER_OF_CANDIDATES>:要检查的向量数量。"oversample_factor": <OVERSAMPLE>:在重新评分之前检索了多少个候选向量。
Elasticsearch
"size": <RESULT_SIZE>:返回给客户端的命中次数。在这个基准测试中,计算 Recall@100 的结果大小为 100。"k": <NUMBER_OF_CANDIDATES>:从每个分片返回的最近邻数量。"num_candidates": <NUMBER_OF_CANDIDATES>:进行knn搜索时每个分片要考虑的最近邻候选数目。"oversample": <OVERSAMPLE>:在重新评分之前检索了多少个候选向量。
示例
Knn 查询, (100_500_1),将如下所示:
OpenSearch
Elasticsearch
完整配置、Terraform 脚本、Kubernetes 清单和基准测试代码均可在此存储库的 es-9.3-vs-os-3.5-vector-search 文件夹中找到。
集群设置
我们在六台 e2-standard-16 云服务器上运行了测试,每台服务器配备 16 个 vCPU 和 64 GB 内存。在每台服务器上,我们为每个运行搜索引擎节点的 Kubernetes pod 分配了 15 个 vCPU 和 56 GB RAM,其中 28 GB 保留给 JVM 堆。
这些集群运行 Elasticsearch 9.3.0 和 OpenSearch 3.5.0 (Lucene 10.3.2)。由于在此基准测试中两个系统使用相同的 Lucene 版本,我们观察到的吞吐量和延迟差异不能单独归因于 Lucene,而是反映了每个引擎如何集成和执行过滤后的 k 最近邻 (kNN) 检索和重新评分的差异。我们使用了一个单一索引,包含三个主分片和一个副本(因此总共 6 个分片,每个节点 1 个分片)。
我们还在同一区域使用了一台独立服务器运行基准客户端,并收集时序统计数据。

图 2:集群设置示意图。
该数据集
对于这个基准测试,我们使用了一个大规模电子商务风格的目录嵌入数据集,包含 2000 万份文档,旨在反映实际的大规模筛选后向量检索的扩展能力。
每份文件代表一个目录项目,包括:
- 一种用于近似 kNN 检索的 128 维稠密向量嵌入。
- 结构化元数据字段用于筛选(例如,项目有效性和可用性,以及其他目录限制条件),从而支持常见的生产环境模式,即仅在符合条件的子集内检索最近邻。
我们之所以选择这个数据集,是因为它捕捉到了我们在生产中看到的智能体和 RAG 型系统所面临的核心性能挑战:仅有矢量相似性是不够的,检索经常受到筛选条件的限制,系统必须在这些限制条件下保持较高的召回率和较低的延迟。与较小的 QA 风格数据集相比,2000 万文档的语料库更能反映筛选后 ANN 系统在实践中面临的扩展和候选压力。
结论
在现代 AI 架构中,尤其是在那些围绕上下文工程构建的架构中,向量搜索速度并非一个微不足道的实现细节。它是一个倍增因素。当智能体和工作流迭代检索→推理→检索时,检索性能直接影响端到端延迟、吞吐量以及输入到模型中的上下文质量。
在我们的基准测试中,与 OpenSearch 相比,当 Elasticsearch 在正确性取决于检索正确文档而不仅仅是相似向量的情况下,始终能以更低的延迟提供更高的召回率。在受控数据集上,差异是显而易见的,而在生产中,这些收益会在大量检索调用中累积,从而提高响应速度、增加容量裕度并降低基础设施成本。




