向量

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 表达式支持的语法很丰富,常用的有:

                      表达式

                      含义

                      category == "return_policy"

                      等值匹配

                      category in ["return_policy", "logistics"]

                      多值匹配

                      doc_id != "doc_promo_001"

                      不等于

                      category == "return_policy" and doc_id == "doc_return_001"

                      组合条件

                      7. 索引类型怎么选

                      前面的“索引算法对比”表格已经给了一个大致的方向,这里再给一个更实操的决策流程:

                      • 先问自己:数据量有多大?

                        • < 10 万条:直接用 FLAT,暴力搜索就够了,省去调参的麻烦

                        • 10 万 ~ 500 万条:优先选 HNSW,速度快、召回率高

                        • 500 万 ~ 5000 万条:看内存够不够。够就 HNSW,不够就 IVF_SQ8(向量压缩到原来的 1/4)

                        • 5000 万条:考虑 DISKANN(索引放磁盘)或 IVF_PQ(更激进的压缩)

                        • 再问自己:对召回率的要求有多高?

                          • RAG 场景通常取 Top-5 到 Top-10,对召回率的容忍度较高,HNSWIVF_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)跑起来,然后根据实际的召回率和延迟表现微调。大多数情况下默认值就够用了。

                              更多推荐