基于向量数据库的 RAG 召回链路压测与性能可观测性指标体系构建方法
各位朋友,大家好!今天我们来聊一聊一个非常实用的主题:基于向量数据库的 RAG(Retrieval-Augmented Generation)召回链路的压测以及性能可观测性指标体系的构建。RAG 技术在 LLM 应用中扮演着至关重要的角色,而召回链路的性能直接决定了最终生成结果的质量和效率。因此,对召回链路进行充分的压测和构建完善的可观测性体系,对于保证 RAG 应用的稳定性和可靠性至关重要。
一、RAG 召回链路的核心要素
在深入压测和可观测性之前,我们需要明确 RAG 召回链路的关键组成部分:
- 文档预处理: 将原始文档转换为向量数据库可以理解和存储的格式,包括文本分割、清洗、Embedding 生成等。
- 向量数据库: 存储文档向量,并提供高效的相似性搜索能力,常见的向量数据库包括 FAISS、Milvus、Pinecone 等。
- 检索策略: 定义如何利用用户查询向量在向量数据库中进行搜索,例如 Top-K 检索、相似度阈值过滤等。
- 后处理: 对检索结果进行排序、去重、过滤等操作,以提高检索结果的质量。
二、压测的目标与策略
压测的目标是模拟真实用户场景,评估召回链路在不同负载下的性能表现,并找出潜在的瓶颈。我们需要关注以下几个关键指标:
- 吞吐量(Throughput): 单位时间内处理的查询数量,通常用 QPS(Queries Per Second)或 RPS(Requests Per Second)来衡量。
- 延迟(Latency): 处理单个查询所花费的时间,包括平均延迟、最大延迟、P95 延迟等。
- 召回率(Recall): 检索结果中包含相关文档的比例,衡量检索的准确性。
- 精度(Precision): 检索结果中相关文档的比例,衡量检索的精确性。
- 资源利用率: CPU、内存、磁盘 I/O 等资源的利用率,帮助识别资源瓶颈。
压测策略应该根据实际应用场景进行设计,例如:
- 负载类型: 模拟不同的查询负载,包括均匀负载、突发负载、混合负载等。
- 数据规模: 使用不同规模的文档数据集进行测试,评估数据规模对性能的影响。
- 并发用户数: 模拟不同数量的并发用户,评估系统的并发处理能力。
- 查询类型: 模拟不同的查询类型,包括长查询、短查询、复杂查询等。
三、压测工具与实践
我们可以使用多种工具来进行 RAG 召回链路的压测,例如:
- Locust: 一个开源的 Python 压测工具,易于使用,支持多种协议,可以模拟大量并发用户。
- JMeter: 一个功能强大的 Java 压测工具,支持多种协议,可以进行复杂的压测场景模拟。
- wrk: 一个轻量级的 HTTP 压测工具,性能高,适合测试 HTTP 接口的性能。
下面我们以 Locust 为例,演示如何对一个基于 Milvus 的 RAG 召回链路进行压测。
1. 准备工作
- 安装 Locust:
pip install locust - 安装 Milvus Python SDK:
pip install pymilvus - 准备测试数据:创建一个包含文档向量的 Milvus Collection。
2. 编写 Locust 压测脚本
from locust import HttpUser, task, between
from pymilvus import connections, utility, Collection, SearchParams
# Milvus 连接信息
MILVUS_HOST = "localhost"
MILVUS_PORT = "19530"
COLLECTION_NAME = "my_collection"
DIMENSION = 128 # 向量维度
TOP_K = 10 # 检索结果数量
# 连接 Milvus
connections.connect(host=MILVUS_HOST, port=MILVUS_PORT)
class MilvusSearchUser(HttpUser):
wait_time = between(1, 3) # 用户请求间隔时间
def on_start(self):
# 确保 Collection 存在
if not utility.has_collection(COLLECTION_NAME):
print(f"Collection {COLLECTION_NAME} does not exist. Please create it first.")
exit()
self.collection = Collection(COLLECTION_NAME)
self.collection.load() # 加载 Collection 到内存,提升检索速度
self.search_params = SearchParams(
{"metric_type": "L2", "params": {"nprobe": 16}}, # L2 距离,nprobe 参数
round_decimal=6
)
@task
def search_milvus(self):
# 生成随机向量作为查询向量
import numpy as np
query_vectors = np.random.rand(1, DIMENSION).astype("float32")
# 执行检索
try:
results = self.collection.search(
data=query_vectors,
anns_field="embedding", # 向量字段名
param=self.search_params,
limit=TOP_K,
expr=None, # 过滤条件,可以设置为None
output_fields=["id"] # 返回字段,可以设置为None
)
# 记录检索结果数量
result_count = len(results[0])
# 记录检索时间
latency = results.latency
self.environment.events.request_success.fire(
request_type="milvus_search",
name="search_milvus",
response_time=latency,
response_length=result_count,
)
except Exception as e:
self.environment.events.request_failure.fire(
request_type="milvus_search",
name="search_milvus",
response_time=0,
response_length=0,
exception=e
)
def on_stop(self):
self.collection.release() # 释放 Collection 资源
connections.disconnect("default") # 断开连接
3. 运行压测
在命令行中运行以下命令:
locust -f milvus_test.py --host=http://localhost
然后,在浏览器中访问 http://localhost:8089,即可进入 Locust Web UI,设置并发用户数、每秒启动用户数等参数,开始压测。
4. 分析压测结果
Locust Web UI 会显示实时的压测数据,包括吞吐量、延迟、错误率等。我们可以根据这些数据来评估召回链路的性能,并找出潜在的瓶颈。例如,如果发现延迟很高,可以检查 Milvus 的资源利用率,或者调整检索参数,例如 nprobe 的值。
四、性能可观测性指标体系构建
压测只能模拟特定场景下的性能表现,而可观测性则可以帮助我们实时监控系统的运行状态,及时发现和解决问题。一个完善的可观测性体系应该包括以下几个方面:
- Metrics(指标): 用于衡量系统性能的关键指标,例如吞吐量、延迟、资源利用率等。
- Logs(日志): 记录系统的运行状态、错误信息等,帮助我们诊断问题。
- Traces(链路追踪): 记录请求在系统中的调用链路,帮助我们定位性能瓶颈。
1. Metrics 指标采集
我们可以使用 Prometheus 来采集 RAG 召回链路的 Metrics 指标。Prometheus 是一个开源的监控系统,可以从各种来源采集指标数据,并提供强大的查询和告警功能。
- 自定义 Metrics: 在代码中添加自定义 Metrics,例如检索次数、检索时间等。
- 系统 Metrics: 采集系统的 CPU、内存、磁盘 I/O 等资源利用率。
- Milvus Metrics: Milvus 本身也提供了一些 Metrics 指标,例如查询延迟、查询 QPS 等。
以下代码展示了如何在 Python 中使用 Prometheus Client 库来添加自定义 Metrics:
from prometheus_client import Summary, Counter, Histogram, start_http_server
import time
import random
# 定义 Metrics
SEARCH_LATENCY = Summary('search_latency_seconds', 'Time spent processing search requests')
SEARCH_COUNT = Counter('search_total', 'Total number of search requests')
SEARCH_HISTOGRAM = Histogram('search_histogram_seconds', 'Histogram of search request durations')
# 启动 Prometheus HTTP Server
start_http_server(8000)
# 模拟搜索过程
def process_search():
start_time = time.time()
# 模拟搜索操作,这里用随机休眠模拟
time.sleep(random.random())
end_time = time.time()
latency = end_time - start_time
# 更新 Metrics
SEARCH_LATENCY.observe(latency)
SEARCH_COUNT.inc()
SEARCH_HISTOGRAM.observe(latency)
if __name__ == '__main__':
while True:
process_search()
time.sleep(0.1)
2. Logs 日志收集
可以使用 Elasticsearch、Fluentd、Kibana (EFK) 或 Loki、Promtail、Grafana (LPG) 等工具来收集和分析 RAG 召回链路的日志。
- 结构化日志: 使用 JSON 格式记录日志,方便后续分析。
- 关键事件: 记录重要的事件,例如查询开始、查询结束、错误信息等。
- 上下文信息: 在日志中包含上下文信息,例如用户 ID、查询 ID 等,方便问题追踪。
3. Traces 链路追踪
可以使用 Jaeger、Zipkin 等工具来追踪 RAG 召回链路的请求调用链路。
- Span: 表示一个请求的执行单元,例如一个函数调用、一个数据库查询等。
- Trace: 表示一个完整的请求调用链路,由多个 Span 组成。
- 上下文传递: 在请求调用链路中传递上下文信息,例如 Trace ID、Span ID 等。
4. 指标体系构建
| 指标类别 | 指标名称 | 指标描述 | 采集方式 |
|---|---|---|---|
| 性能指标 | QPS (Queries Per Second) | 每秒查询数量 | 自定义 Metrics / Milvus Metrics |
| Avg. Latency | 平均查询延迟 | 自定义 Metrics / Milvus Metrics | |
| P95 Latency | 95% 的查询延迟 | 自定义 Metrics / Milvus Metrics | |
| CPU Utilization | CPU 利用率 | 系统 Metrics (Prometheus Node Exporter) | |
| Memory Utilization | 内存利用率 | 系统 Metrics (Prometheus Node Exporter) | |
| Disk I/O | 磁盘 I/O | 系统 Metrics (Prometheus Node Exporter) | |
| 质量指标 | Recall | 检索结果召回率 | 自定义 Metrics (需要评估数据集和相关性标注) |
| Precision | 检索结果精度 | 自定义 Metrics (需要评估数据集和相关性标注) | |
| 错误指标 | Error Rate | 查询错误率 | 自定义 Metrics / Milvus Metrics / Logs 分析 |
| Timeout Rate | 查询超时率 | 自定义 Metrics / Milvus Metrics / Logs 分析 | |
| 资源指标 | Milvus Node Count | Milvus 集群节点数量 | Milvus Metrics |
| Collection Size | Collection 数据量 | Milvus Metrics | |
| 链路追踪 | End-to-End Latency | 端到端延迟(用户请求到最终结果返回的时间) | 通过 Jaeger/Zipkin 等工具进行链路追踪,分析各个环节耗时 |
5. 可视化与告警
可以使用 Grafana 来可视化 Metrics 指标,并设置告警规则。例如,当 QPS 超过阈值时,或者延迟超过阈值时,触发告警。
五、总结
今天,我们深入探讨了基于向量数据库的 RAG 召回链路的压测和性能可观测性指标体系构建。通过压测,我们可以评估召回链路的性能,找出潜在的瓶颈。而可观测性则可以帮助我们实时监控系统的运行状态,及时发现和解决问题。希望今天的分享能够帮助大家构建更稳定、更高效的 RAG 应用。
核心要点回顾
- 压测的目标是模拟真实用户场景,评估召回链路在不同负载下的性能表现。
- 可观测性体系包括 Metrics、Logs、Traces 三个方面,帮助我们实时监控系统的运行状态。
- 选择合适的工具和技术,例如 Locust、Prometheus、EFK、Jaeger 等,可以简化压测和可观测性构建的过程。