Ragent项目day-06 向量Embedding与向量数据库
向量
Embedding 模型:文本到向量的转换器
知道了向量是什么,接下来的问题是:谁来做这个转换?答案是 Embedding 模型。
2.1 模型选型的关键指标
在看具体模型之前,先搞清楚选型时要看哪些指标:
|
指标 |
含义 |
为什么重要 |
|---|---|---|
|
向量维度 |
输出向量的浮点数个数 |
维度越高,表达能力越强,但存储和计算成本也越高 |
|
最大输入 token 数 |
单次能处理的最大文本长度 |
决定了你的 chunk 最大能有多长 |
|
中文效果 |
对中文文本的语义理解能力 |
中文场景必须关注,有些模型主要针对英文训练 |
|
API 成本 |
每次调用的费用 |
大规模向量化时,成本差异会很明显 |
|
是否支持本地部署 |
能不能在自己的服务器上跑 |
涉及数据安全和隐私的场景,可能不允许数据出外网 |
2.2 主流模型横向对比
|
模型 |
提供方 |
向量维度 |
最大输入 token |
中文效果 |
部署方式 |
备注 |
|---|---|---|---|---|---|---|
|
text-embedding-3-small |
OpenAI |
1536 |
8191 |
中等 |
仅云端 API |
性价比高,适合英文为主的场景 |
|
text-embedding-3-large |
OpenAI |
3072 |
8191 |
中等 |
仅云端 API |
维度更高,效果更好,成本也更高 |
|
text-embedding-v3 |
阿里通义 |
1024/768 |
8192 |
优秀 |
云端 API |
中文效果好,支持多种维度输出 |
|
BGE-large-zh |
BAAI(智源) |
1024 |
512 |
优秀 |
本地部署/API |
开源模型,中文效果突出 |
|
BGE-M3 |
BAAI(智源) |
1024 |
8192 |
优秀 |
本地部署/API |
支持多语言、多粒度,综合能力强 |
|
Qwen3-Embedding-8B |
阿里通义 |
4096 |
32768 |
优秀 |
本地部署/API |
最新一代,维度高,上下文窗口大 |
|
GTE-large-zh |
阿里通义 |
1024 |
8192 |
优秀 |
本地部署/API |
中文基准测试表现好 |
2.3 中文场景推荐
如果你的项目主要处理中文文本(比如中文知识库、中文客服系统),模型选型的优先级大致是:
- 1.
需要云端 API 且预算充足:阿里通义 text-embedding-v3,中文效果好,API 稳定
- 2.
需要云端 API 且追求性价比:通过 SiliconFlow 等平台调用 Qwen3-Embedding 或 BGE 系列,价格更低
- 3.
需要本地部署:BGE-M3 或 Qwen3-Embedding,开源可商用,中文效果优秀
- 4.
中英文混合场景:BGE-M3,专门为多语言设计
3. 向量维度怎么选
|
维度范围 |
适用场景 |
存储成本(100 万条) |
|---|---|---|
|
256~512 |
简单场景,文本短、类目少 |
约 1~2 GB |
|
768~1024 |
大多数生产场景的甜蜜点 |
约 3~4 GB |
|
1536~4096 |
对精度要求极高的场景 |
约 6~16 GB |
相似度计算:
1. 余弦相似度——最常用的度量方式
- 1.算两个向量的点积(对应位置的数字相乘,然后全部加起来)
- 2.算每个向量的模(每个数字的平方加起来,再开根号)
- 3.点积除以两个模的乘积
3. 相似度分数怎么解读
|
相似度范围 |
含义 |
实际场景举例 |
|---|---|---|
|
0.9 ~ 1.0 |
高度相关,几乎是同一个意思 |
“退货流程”和“怎么退货” |
|
0.7 ~ 0.9 |
明显相关,主题一致 |
“退货政策”和“商品能退吗” |
|
0.5 ~ 0.7 |
有一定关联,但不够紧密 |
“退货政策”和“售后服务” |
|
0.3 ~ 0.5 |
关联很弱 |
“退货政策”和“商品详情” |
|
0.0 ~ 0.3 |
基本无关 |
“退货政策”和“天气预报” |
3.1 检索阈值怎么设
-
阈值设 0.7:比较严格,只返回高度相关的结果,准确率高但可能漏掉一些相关内容
-
阈值设 0.5:比较宽松,召回率高但可能混入一些不太相关的内容
-
不设阈值,只取 Top-K:返回相似度最高的 K 个结果(比如 Top-5),不管分数多少
用 SiliconFlow API 跑通向量化全流程
Embedding API 的请求和响应格式
2.1 请求格式
curl -X POST "https://api.siliconflow.cn/v1/embeddings" \
-H "Authorization: Bearer 你的API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "Qwen/Qwen3-Embedding-8B",
"input": ["七天无理由退货"],
"encoding_format": "float"
}'
2.2 响应格式
{
"object": "list",
"data": [
{
"object": "embedding",
"index": 0,
"embedding": [0.0123, -0.0456, 0.0789, ...]
}
],
"model": "Qwen/Qwen3-Embedding-8B",
"usage": {
"prompt_tokens": 5,
"total_tokens": 5
}
}
HttpRequest请求格式,
HttpResponse响应格式
具体步骤:
1.拿到分块好的chunk
2.将所有分块好的chunk调用Embedding进行向量化
3.将问题向量化
4.根据策略匹配对应chunk
实际项目中的关键决策
跑通了 demo,离生产环境还有一段距离。这一节聊几个实际项目中绕不开的问题。
1. 模型选型:云端 API vs 本地部署
|
对比维度 |
云端 API(以 SiliconFlow 为例) |
本地部署(以 Ollama 为例) |
|---|---|---|
|
部署成本 |
零部署成本,按调用量付费 |
需要 GPU 服务器,一次性投入高 |
|
使用成本 |
按 token 计费,量大时费用可观 |
硬件折旧 + 电费,量大时单价低 |
|
延迟 |
受网络影响,通常 100~500ms |
本地调用,通常 10~50ms |
|
数据安全 |
文本需要发送到第三方服务器 |
数据不出内网,安全性高 |
|
维护成本 |
平台负责运维,省心 |
需要自己维护模型和服务 |
|
模型选择 |
平台提供多种模型,切换方便 |
需要自己下载和管理模型 |
2. 批量向量化的性能优化
2.1 分批处理
最基本的优化:把 chunks 分成固定大小的批次,逐批调用 API。
/**
* 分批向量化
*
* @param texts 所有待向量化的文本
* @param batchSize 每批的大小(建议 20~50)
* @return 所有文本的向量
*/
public List<double[]> embedInBatches(List<String> texts, int batchSize) throws Exception {
List<double[]> allEmbeddings = new ArrayList<>();
for (int i = 0; i < texts.size(); i += batchSize) {
int end = Math.min(i + batchSize, texts.size());
List<String> batch = texts.subList(i, end);
System.out.printf("向量化进度:%d/%d%n", end, texts.size());
List<double[]> batchEmbeddings = embed(batch);
allEmbeddings.addAll(batchEmbeddings);
// 简单的限流:每批之间等一下,避免触发 API 的速率限制
if (end < texts.size()) {
Thread.sleep(200);
}
}
return allEmbeddings;
}
2.2 并发控制
分批处理是串行的,如果 API 支持并发,可以用多线程加速:
import java.util.concurrent.*;
/**
* 并发批量向量化
*/
public List<double[]> embedConcurrently(List<String> texts, int batchSize,
int maxConcurrency) throws Exception {
ExecutorService executor = Executors.newFixedThreadPool(maxConcurrency);
List<Future<List<double[]>>> futures = new ArrayList<>();
for (int i = 0; i < texts.size(); i += batchSize) {
int start = i;
int end = Math.min(i + batchSize, texts.size());
List<String> batch = texts.subList(start, end);
futures.add(executor.submit(() -> embed(batch)));
}
List<double[]> allEmbeddings = new ArrayList<>();
for (Future<List<double[]>> future : futures) {
allEmbeddings.addAll(future.get());
}
executor.shutdown();
return allEmbeddings;
}
用户 query → 向量化 → 在向量数据库中做相似度匹配 → 拿到候选 chunks
↓
用元数据做二次过滤
(比如只要最近一年的、
只要用户有权限看的)
↓
最终返回结果
向量数据库
近似最近邻搜索
既然逐个比较太慢,能不能不比较所有向量,只比较其中一部分,就找到大概率最相似的那几个?
答案是可以。这就是 ANN(Approximate Nearest Neighbor,近似最近邻搜索)的核心思想。
一句话概括:向量数据库 = 向量存储 + ANN 索引 + 高效检索。它是专门为“在海量向量中快速找到最相似的那几个”这件事而设计的。
IVF(倒排文件索引):先分区再搜索
建索引阶段(离线):
- 1.用聚类算法(通常是 K-Means)把所有向量分成
nlist个簇(cluster)
- 2.每个簇有一个中心点(centroid),代表这个簇里所有向量的"平均位置"
- 3.每个向量被分配到离它最近的那个簇
检索阶段(在线):
- 1.拿到查询向量后,先计算它和所有簇中心点的距离
- 2.找到最近的
nprobe个簇(nprobe 是一个可调参数)
- 3.只在这
nprobe个簇里的向量中做精确搜索
2.2 IVF 的优缺点
|
优点 |
缺点 |
|---|---|
|
原理简单,容易理解和调优 |
需要训练聚类模型(数据量大时训练较慢) |
|
内存占用相对较低 |
聚类边界处的向量可能被漏掉(影响召回率) |
|
适合数据量非常大的场景 |
nlist 和 nprobe 的调参需要经验 |
|
支持增量插入(但可能需要定期重新聚类) |
数据分布不均匀时效果下降 |
3. HNSW(分层可导航小世界图):最主流的索引算法
3.1 HNSW 的核心思想:多层图结构

3.2 用一个具体例子走一遍 HNSW 的检索过程
为了让你更直观地理解,咱们用一个简化的例子走一遍。
假设向量数据库里有 8 个向量(A、B、C、D、E、F、G、H),HNSW 建了 3 层图。现在要查询和向量 Q 最相似的向量。
Layer 2(顶层):只有 A 和 E 两个向量
-
从 A 开始,计算 Q 和 A 的距离、Q 和 E 的距离
-
发现 E 离 Q 更近,移动到 E
Layer 1(中间层):有 A、C、E、G 四个向量
-
从 E 出发,看 E 的邻居:C 和 G
-
计算 Q 和 C、Q 和 G 的距离
-
发现 G 离 Q 更近,移动到 G
Layer 0(底层):所有 8 个向量都在
-
从 G 出发,看 G 的邻居:F 和 H
-
计算 Q 和 F、Q 和 H 的距离
-
发现 H 离 Q 最近
-
再看 H 的邻居,没有比 H 更近的了
-
结果:H 是和 Q 最相似的向量
整个过程只计算了 6 次距离(A、E、C、G、F、H),而不是 8 次。数据量小的时候差距不明显,但如果有 100 万个向量,HNSW 通常只需要计算几百到几千次距离就能找到结果。
|
参数 |
含义 |
调大的效果 |
调小的效果 |
|---|---|---|---|
|
M |
每个向量在每层的最大连接数 |
召回率更高,但内存占用更大,建索引更慢 |
内存省,但召回率可能下降 |
|
efConstruction |
建索引时的搜索宽度 |
索引质量更高(连接更合理),但建索引更慢 |
建索引快,但索引质量可能下降 |
4. 索引算法对比:怎么选
Milvus 支持多种索引类型,下面是最常用的几种对比:
|
索引类型 |
核心思想 |
检索速度 |
召回率 |
内存占用 |
适用数据量 |
适用场景 |
|---|---|---|---|---|---|---|
|
FLAT |
暴力搜索,不建索引 |
最慢 |
100%(精确) |
低(只存原始向量) |
< 10 万 |
对精度要求极高,数据量小 |
|
IVF_FLAT |
聚类分区 + 簇内精确搜索 |
快 |
95%~99% |
较低 |
百万~千万 |
数据量大,内存有限 |
|
IVF_SQ8 |
聚类分区 + 标量量化压缩 |
快 |
93%~97% |
低(向量压缩为 1/4) |
千万~亿级 |
数据量很大,愿意牺牲一点精度换内存 |
|
HNSW |
多层图结构 |
最快 |
97%~99.5% |
高(需存图结构) |
百万~千万 |
对速度和精度都有要求,内存充足 |
|
DISKANN |
基于磁盘的图索引 |
较快 |
95%~98% |
低(索引在磁盘) |
亿级 |
数据量极大,内存不够放 HNSW |
怎么选?一个简单的决策路径:
-
数据量 < 10 万,直接用 FLAT,暴力搜索就够了
-
数据量 10 万~500 万,内存充足 → HNSW;内存有限 → IVF_FLAT
-
数据量 500 万~5000 万,HNSW 如果内存放得下就用 HNSW,放不下用 IVF_SQ8
-
数据量 > 5000 万,考虑 DISKANN 或 IVF_PQ
主流向量数据库对比与选型
1. 向量数据库的分类
1.1 专用向量数据库
1.2 传统数据库的向量扩展
1.3 怎么选
一个简单的判断标准:
-
如果你的向量数据量 < 50 万,且项目已经在用 PostgreSQL → pgvector 够用,省事
-
如果向量数据量 > 50 万,或者对检索性能有较高要求 → 用专用向量数据库
-
如果是学习和原型验证阶段 → Chroma(轻量,Python 生态好)或 Milvus(功能全,Java SDK 完善)
2. 主流方案对比
|
数据库 |
类型 |
部署方式 |
适用数据量 |
语言 SDK |
索引类型 |
标量过滤 |
开源 |
适用场景 |
|---|---|---|---|---|---|---|---|---|
|
Milvus |
专用 |
自部署(Docker/K8s)或 Zilliz Cloud |
百万~十亿级 |
Java、Python、Go、Node.js |
HNSW、IVF 系列、DISKANN 等 |
支持 |
是(Apache 2.0) |
大规模生产环境,Java 技术栈 |
|
Qdrant |
专用 |
自部署(Docker)或 Qdrant Cloud |
百万~亿级 |
Python、Rust、Go、Java |
HNSW |
支持 |
是(Apache 2.0) |
Rust 生态,高性能单机场景 |
|
Weaviate |
专用 |
自部署(Docker)或 Weaviate Cloud |
百万~千万级 |
Python、Go、Java、JS |
HNSW |
支持 |
是(BSD-3) |
内置向量化能力,全托管偏好 |
|
Pinecone |
专用 |
纯云托管(无自部署) |
百万~亿级 |
Python、Node.js |
自研 |
支持 |
否 |
不想运维,纯云方案 |
|
Chroma |
专用 |
嵌入式 / Docker |
< 百万 |
Python、JS |
HNSW |
支持 |
是(Apache 2.0) |
原型验证,轻量场景 |
|
pgvector |
扩展 |
随 PostgreSQL 部署 |
< 百万 |
所有支持 PG 的语言 |
HNSW、IVF_FLAT |
支持(SQL WHERE) |
是 |
已有 PG,数据量不大 |
Milvus 核心概念:和传统数据库做类比
1. Collection = 表
2. Schema = 表结构
一个典型的 RAG 场景的 Schema 包含三类字段:
|
字段类型 |
示例 |
说明 |
|---|---|---|
|
主键字段 |
id(Int64 或 VarChar) |
每条数据的唯一标识,类似 MySQL 的主键 |
|
向量字段 |
vector(FloatVector) |
存储 Embedding 向量,需要指定维度 |
|
标量字段 |
chunk_text、doc_id、category 等 |
存储元数据,用于过滤和展示 |
向量字段存储的是高维浮点数数组(比如 4096 维的 float 数组),它不能做等值查询(两个向量完全相等的概率几乎为零),只能做相似度检索(找最近的 Top-K 个)。向量字段需要建专门的向量索引(HNSW、IVF 等),这和标量字段的 B+ 树索引是完全不同的东西。
3. Index = 索引
Milvus 中的索引分两种:
-
向量索引:为向量字段创建的 ANN 索引(HNSW、IVF_FLAT 等),用于加速向量相似度检索。这是 Milvus 的核心能力。
-
标量索引:为标量字段创建的索引,用于加速过滤条件的执行。类似 MySQL 的 B+ 树索引。
在 RAG 场景中,通常需要同时用到两种索引:向量索引用于找到语义最相似的 chunk,标量索引用于按元数据过滤(比如只搜索某个类别的 chunk)。
4. Partition = 分区

和 MySQL 做个对照:
|
Milvus 概念 |
MySQL 对应 |
说明 |
|---|---|---|
|
Collection |
Table |
数据的基本组织单位 |
|
Schema |
CREATE TABLE 的列定义 |
定义字段名、类型、约束 |
|
Field |
Column |
单个字段 |
|
Partition |
分区表的 Partition |
按业务维度划分数据 |
|
向量索引(HNSW 等) |
无直接对应 |
MySQL 没有向量索引 |
|
标量索引 |
B+ 树索引 |
加速标量字段的查询 |
|
Entity |
Row |
一条数据记录 |
安装docker

http:

milvus操作:
yml:
name: milvus-stack
services:
rustfs:
container_name: rustfs
image: rustfs/rustfs:1.0.0-alpha.72
command:
- "--address"
- ":9000"
- "--console-enable"
- "--access-key"
- "rustfsadmin"
- "--secret-key"
- "rustfsadmin"
- "/data"
environment:
- RUSTFS_ACCESS_KEY=rustfsadmin
- RUSTFS_SECRET_KEY=rustfsadmin
- RUSTFS_CONSOLE_ENABLE=true
ports:
- "9000:9000"
- "9001:9001"
volumes:
- rustfs-data:/data
healthcheck:
test: ["CMD", "sh", "-c", "wget -qO- http://localhost:9000/ || exit 1"]
interval: 30s
timeout: 10s
retries: 5
etcd:
container_name: etcd
image: quay.io/coreos/etcd:v3.5.18
environment:
- ETCD_AUTO_COMPACTION_MODE=revision
- ETCD_AUTO_COMPACTION_RETENTION=1000
- ETCD_QUOTA_BACKEND_BYTES=4294967296
- ETCD_SNAPSHOT_COUNT=50000
command: >
etcd
-advertise-client-urls=http://etcd:2379
-listen-client-urls http://0.0.0.0:2379
--data-dir /etcd
volumes:
- etcd-data:/etcd
healthcheck:
test: ["CMD", "etcdctl", "endpoint", "health"]
interval: 30s
timeout: 20s
retries: 3
standalone:
container_name: milvus-standalone
image: milvusdb/milvus:v2.6.6
command: ["milvus", "run", "standalone"]
security_opt:
- seccomp:unconfined
environment:
ETCD_ENDPOINTS: etcd:2379
MINIO_ADDRESS: rustfs:9000
MINIO_ACCESS_KEY_ID: rustfsadmin
MINIO_SECRET_ACCESS_KEY: rustfsadmin
volumes:
- milvus-data:/var/lib/milvus
ports:
- "19530:19530"
- "9091:9091"
depends_on:
- etcd
- rustfs
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9091/healthz"]
interval: 30s
start_period: 90s
timeout: 20s
retries: 3
attu:
container_name: milvus-attu
image: zilliz/attu:v2.6.3
environment:
MILVUS_URL: milvus-standalone:19530
ports:
- "8000:3000"
depends_on:
- standalone
volumes:
rustfs-data:
etcd-data:
milvus-data:
networks:
default:
name: milvus-net
各组件的作用:
|
组件 |
作用 |
端口 |
|---|---|---|
|
rustfs |
对象存储,存储 Milvus 的索引文件和日志 |
9000(API)、9001(控制台) |
|
etcd |
元数据存储,管理 Milvus 的集群元信息 |
2379 |
|
standalone |
Milvus 单机版服务 |
19530(gRPC)、9091(健康检查) |
|
attu |
Milvus 可视化管理界面 |
8000 |
1.创建表:
CreateCollectionReq.CollectionSchema schema = client.createSchema();
schema.addField(AddFieldReq.builder()
.fieldName("id")
.dataType(DataType.Int64)
.isPrimaryKey(true)
.autoID(true)
.build());
schema.addField(AddFieldReq.builder()
.fieldName("vector")
.dataType(DataType.FloatVector)
.dimension(VECTOR_DIM)
.build());
schema.addField(AddFieldReq.builder()
.fieldName("chunk_text")
.dataType(DataType.VarChar)
.maxLength(8192)
.build());
schema.addField(AddFieldReq.builder()
.fieldName("doc_id")
.dataType(DataType.VarChar)
.maxLength(64)
.build());
schema.addField(AddFieldReq.builder()
.fieldName("category")
.dataType(DataType.VarChar)
.maxLength(32)
.build());
CreateCollectionReq createCollectionReq = CreateCollectionReq.builder()
.collectionName(COLLECTION_NAME)
.collectionSchema(schema)
.build();
2.添加数据:
// 调用 Embedding API 生成向量
List<List<Float>> vectors = getEmbeddings(chunkTexts);
// 组装插入数据
List<JsonObject> rows = new ArrayList<>();
for (int i = 0; i < chunkTexts.size(); i++) {
JsonObject row = new JsonObject();
row.addProperty("chunk_text", chunkTexts.get(i));
row.addProperty("doc_id", docIds.get(i));
row.addProperty("category", categories.get(i));
row.add("vector", GSON.toJsonTree(vectors.get(i)));
rows.add(row);
}
// 插入 Milvus
InsertReq insertReq = InsertReq.builder()
.collectionName("customer_service_chunks")
.data(rows)
.build();
InsertResp insertResp = client.insert(insertReq);
System.out.println("插入成功,数量:" + insertResp.getInsertCnt());
3.添加索引
IndexParam vectorIndex = IndexParam.builder()
.fieldName("vector")
.indexType(IndexParam.IndexType.HNSW)
.metricType(IndexParam.MetricType.COSINE) // 余弦相似度
.extraParams(Map.of(
"M", 16, // 每个向量的最大连接数
"efConstruction", 256 // 建索引时的搜索宽度
))
.build();
// 为 category 标量字段创建索引(加速过滤查询)
IndexParam categoryIndex = IndexParam.builder()
.fieldName("category")
.indexType(IndexParam.IndexType.TRIE) // 字符串类型用 Trie 索引
.build();
CreateIndexReq createIndexReq = CreateIndexReq.builder()
.collectionName("customer_service_chunks")
.indexParams(List.of(vectorIndex, categoryIndex))
.build();
4.加载内存:
client.loadCollection(LoadCollectionReq.builder()
.collectionName("customer_service_chunks")
.build());
System.out.println("Collection 已加载到内存");
5.匹配向量:
List<List<Float>> queryVectors = getEmbeddings(List.of(query));
List<BaseVector> milvusQueryVectors = queryVectors.stream()
.map(FloatVec::new) // FloatVec(List<Float>)
.collect(java.util.stream.Collectors.toList());
// 执行向量检索
SearchReq searchReq = SearchReq.builder()
.collectionName("customer_service_chunks")
.data(milvusQueryVectors) // 查询向量
.topK(3) // 返回最相似的 3 个结果
.outputFields(List.of("chunk_text", "doc_id", "category")) // 需要返回的字段
.annsField("vector") // 指定在哪个向量字段上检索
.searchParams(Map.of("ef", 128)) // HNSW 检索时的搜索宽度
.build();
SearchResp searchResp = client.search(searchReq);
-
topK(3):返回相似度最高的 3 个结果。在 RAG 场景中,通常取 3~10 个,具体取多少取决于你给大模型的上下文窗口有多大
-
outputFields:指定返回哪些标量字段。不指定的话只返回主键和相似度分数
-
searchParams中的ef = 128:HNSW 检索时的搜索宽度。ef越大,召回率越高但检索越慢。一般设置为topK的 4~16 倍
-
annsField("vector"):指定在哪个向量字段上做检索。如果 Collection 只有一个向量字段,可以省略
6.筛选匹配向量:filter方法
// 执行向量检索
// 混合检索:向量相似度 + 标量过滤
// 只在退货政策类的 chunk 里检索
SearchReq filteredSearchReq = SearchReq.builder()
.collectionName("customer_service_chunks")
.data(milvusQueryVectors)
.topK(3)
.outputFields(List.of("chunk_text", "doc_id", "category"))
.annsField("vector")
.filter("category == \"return_policy\"") // 只搜索退货政策类
.searchParams(Map.of("ef", 128))
.build();
SearchResp filteredResp = client.search(filteredSearchReq);
filter 表达式支持的语法很丰富,常用的有:
|
表达式 |
含义 |
|---|---|
|
|
等值匹配 |
|
|
多值匹配 |
|
|
不等于 |
|
|
组合条件 |
7. 索引类型怎么选
前面的“索引算法对比”表格已经给了一个大致的方向,这里再给一个更实操的决策流程:
-
先问自己:数据量有多大?
-
< 10 万条:直接用
FLAT,暴力搜索就够了,省去调参的麻烦
-
10 万 ~ 500 万条:优先选
HNSW,速度快、召回率高
-
500 万 ~ 5000 万条:看内存够不够。够就
HNSW,不够就IVF_SQ8(向量压缩到原来的 1/4)
-
5000 万条:考虑
DISKANN(索引放磁盘)或IVF_PQ(更激进的压缩)
-
-
再问自己:对召回率的要求有多高?
-
RAG 场景通常取 Top-5 到 Top-10,对召回率的容忍度较高,
HNSW和IVF_FLAT都能满足
-
如果是人脸识别、指纹匹配等对精度要求极高的场景,可能需要
FLAT或者把 HNSW 的参数调得很大
-
对于大多数 RAG 项目,HNSW 是默认选择,不需要纠结。
8.删除向量:
DeleteReq deleteReq = DeleteReq.builder()
.collectionName("customer_service_chunks")
.filter("doc_id == \"doc_return_001\"")
.build();
client.delete(deleteReq);
5. 性能调优的几个关键参数
最后汇总一下影响性能的关键参数,方便你在实际项目中调优:
5.1 HNSW 索引参数
|
参数 |
作用 |
推荐值 |
调大 |
调小 |
|---|---|---|---|---|
|
M |
每个向量的最大连接数 |
8~32,通常 16 |
召回率↑,内存↑,建索引速度↓ |
内存↓,召回率可能↓ |
|
efConstruction |
建索引时的搜索宽度 |
128~512,通常 256 |
索引质量↑,建索引速度↓ |
建索引速度↑,索引质量↓ |
|
ef |
检索时的搜索宽度 |
topK 的 4~16 倍 |
召回率↑,检索速度↓ |
检索速度↑,召回率↓ |
一个实用的调参思路:先用默认值(M=16,efConstruction=256,ef=topK×8)跑起来,然后根据实际的召回率和延迟表现微调。大多数情况下默认值就够用了。
更多推荐
所有评论(0)