RAG 性能总卡在检索?先搞懂 Milvus 向量数据库再说

RAG 性能总卡在检索?先搞懂 Milvus 向量数据库再说

在构建 Agent 记忆模块时,我完整走了一遍 Milvus 的接入流程。本文以 AI 日记项目为例,梳理其中的关键技术点。


一、向量数据库解决什么问题

传统数据库靠关键词精确匹配,搜"户外活动"找不到"爬山""公园散步"这类语义相关的内容。向量数据库的做法是将文本通过 Embedding 模型转为高维向量,以向量距离衡量语义相似度,从而实现"以义搜义"。

在 RAG 架构中,向量数据库承担知识检索环节,是 AI Agent 实现长期记忆的基础。

核心概念映射

概念 类比 说明
Collection Table 数据表
Entity Row 一条记录
Field Column 字段,标量或向量类型
Partition 分区表 逻辑隔离,用于多租户/按时间归档
Index Index 加速 ANN(近似最近邻)搜索

二、环境准备

bash 复制代码
pnpm add @zilliz/milvus2-sdk-node @langchain/openai dotenv
js 复制代码
import { MilvusClient } from '@zilliz/milvus2-sdk-node';
import { OpenAIEmbeddings } from '@langchain/openai';

// 以阿里通义 text-embedding-v2 为例,维度 1536
const embeddings = new OpenAIEmbeddings({
  apiKey: process.env.OPENAI_API_KEY,
  model: 'text-embedding-v2',
  configuration: { baseURL: process.env.OPENAI_BASE_URL },
  dimensions: 1536,
});

const client = new MilvusClient({
  address: process.env.MILVUS_ADDRESS,
  token: process.env.MILVUS_TOKEN,
  maxRetries: 3,
  timeout: 30000,
});

async function getEmbeddings(text) {
  return await embeddings.embedQuery(text);
}

Zilliz Cloud 免运维开箱即用;自托管则通过 Docker Compose 一键启动,默认地址 localhost:19530,无需 token。


三、Schema 设计与 Collection 管理

3.1 字段类型

类型 使用场景
VarChar id, content, date, mood
Int64 / Float / Bool 数值及布尔标量
JSON 灵活的元数据
Array 标签 tags
FloatVector content 的 Embedding
BinaryVector / SparseFloatVector 图像指纹 / 关键词稀疏向量

3.2 创建 AI 日记 Collection

js 复制代码
await client.createCollection({
  collection_name: 'ai_diary',
  fields: [
    { name: 'id', data_type: DataType.VarChar, max_length: 50, is_primary_key: true },
    { name: 'vector', data_type: DataType.FloatVector, dim: 1536 },
    { name: 'content', data_type: DataType.VarChar, max_length: 5000 },
    { name: 'date', data_type: DataType.VarChar, max_length: 50 },
    { name: 'mood', data_type: DataType.VarChar, max_length: 50 },
    { name: 'tags', data_type: DataType.Array, element_type: DataType.VarChar, max_capacity: 10, max_length: 50 },
  ],
});

Milvus 也支持动态 SchemaenableDynamicField: true),未声明字段自动归入 JSON 列,适合原型快速迭代,生产建议切回强 Schema。

3.3 生命周期操作

js 复制代码
await client.hasCollection({ collection_name: 'ai_diary' });
await client.describeCollection({ collection_name: 'ai_diary' });
await client.showCollections();
await client.alterCollection({ collection_name: 'ai_diary', properties: { 'collection.ttl.seconds': '604800' } });
await client.dropCollection({ collection_name: 'ai_diary' });

3.4 Partition 分区

js 复制代码
await client.createPartition({ collection_name: 'ai_diary', partition_name: '2026_01' });
await client.insert({ collection_name: 'ai_diary', partition_name: '2026_01', data: [...] });
await client.search({ collection_name: 'ai_diary', partition_names: ['2026_01'], vector, limit: 10 });

限定分区搜索可大幅缩小候选集,显著提升 QPS。


四、向量索引------性能的核心

4.1 索引选型对比

索引 原理 精度 内存 适用规模
FLAT 暴力搜索 100% <10 万
IVF_FLAT K-Means + 候选集暴搜 >95% 百万级
IVF_SQ8 IVF + 8-bit 量化 >92% 低(4×压缩) 百万级,内存受限
IVF_PQ IVF + 乘积量化 >85% 极低(16×) 千万级
HNSW 分层图搜索 >98% 百万级,高 QPS
DISKANN 基于 SSD >95% 磁盘 亿级

4.2 索引创建

js 复制代码
// 默认选择:IVF_FLAT + COSINE
await client.createIndex({
  collection_name: 'ai_diary',
  field_name: 'vector',
  index_type: 'IVF_FLAT',
  metric_type: 'COSINE',
  params: { nlist: 1536 },
});

// HNSW------高召回高QPS,内存换性能
await client.createIndex({
  collection_name: 'ai_diary', field_name: 'vector',
  index_type: 'HNSW', metric_type: 'COSINE',
  params: { M: 16, efConstruction: 200 },
});

// IVF_SQ8------向量压缩到 1/4
await client.createIndex({
  collection_name: 'ai_diary', field_name: 'vector',
  index_type: 'IVF_SQ8', metric_type: 'COSINE',
  params: { nlist: 1024 },
});

决策逻辑:<10 万用 FLAT;百万级默认 IVF_FLAT,内存受限用 IVF_SQ8 或 IVF_PQ,高 QPS 用 HNSW;亿级走 DISKANN。

4.3 相似度度量

方式 公式 适用
COSINE 余弦相似度 文本语义(最常用)
L2 欧几里得距离 未归一化向量
IP 内积 归一化向量(计算最快)
HAMMING / JACCARD 汉明/杰卡德 BinaryVector

注意:如果向量已 L2 归一化,IP 与 COSINE 数学等价,IP 计算开销更小。本项目的 text-embedding-v2 未做归一化,故用 COSINE。


五、数据操作

插入

js 复制代码
const diaryData = [
  { id: 'diary_001', content: '今天天气很好,去公园散步了...', date: '2026-01-10', mood: 'happy', tags: ['生活', '散步'] },
  { id: 'diary_002', content: '今天工作很忙,完成了一个重要的项目里程碑...', date: '2026-01-11', mood: 'excited', tags: ['工作', '成就'] },
  { id: 'diary_003', content: '周末和朋友去爬山,天气很好...', date: '2026-01-12', mood: 'relaxed', tags: ['户外', '朋友'] },
  { id: 'diary_004', content: '今天学习了Milvus向量数据库...', date: '2026-01-12', mood: 'curious', tags: ['学习', '技术'] },
  { id: 'diary_005', content: '晚上做了一顿丰盛的晚餐...', date: '2026-01-13', mood: 'proud', tags: ['美食', '家庭'] },
];

const data = await Promise.all(diaryData.map(async d => ({ ...d, vector: await getEmbeddings(d.content) })));
const { insert_cnt } = await client.insert({ collection_name: 'ai_diary', data });

Upsert / Query / Delete

js 复制代码
// Upsert:存在即更新,不存在即插入
await client.upsert({ collection_name: 'ai_diary', data: [{ id: 'diary_001', vector: newVec, content: '...' }] });

// Query:标量条件精确匹配,不走向量
await client.query({ collection_name: 'ai_diary', expr: 'mood == "happy"', output_fields: ['id', 'content'], limit: 100 });

// Delete
await client.delete({ collection_name: 'ai_diary', expr: 'id in ["diary_003"]' });
await client.delete({ collection_name: 'ai_diary', expr: 'date < "2026-01-01"' });

Query vs Search:Query 对标量做精确过滤,速度快;Search 做向量 ANN 搜索,能匹配语义相近的结果。


六、向量搜索

基础语义搜索

js 复制代码
await client.loadCollection({ collection_name: 'ai_diary' });

const query = '我想看看关于户外活动的日记';
const queryVector = await getEmbeddings(query);

const { results } = await client.search({
  collection_name: 'ai_diary',
  vector: queryVector,
  limit: 3,
  metric_type: 'COSINE',
  output_fields: ['id', 'content', 'date', 'mood', 'tags'],
});

搜索"户外活动"会优先返回 diary_003(爬山)和 diary_001(公园散步),尽管原文中并不包含"户外活动"这个词。

标量过滤搜索

js 复制代码
// 向量 + 条件组合,生产中的标准用法
await client.search({
  collection_name: 'ai_diary',
  vector: queryVector,
  limit: 10,
  filter: 'array_contains(tags, "户外") and date >= "2026-01-10"',
  output_fields: ['id', 'content', 'tags'],
});

支持的表达式:==, !=, >, >=, <, <= / and, or, not / in [...] / like "prefix%" / array_contains() / metadata["key"]

范围搜索与分页

js 复制代码
// 按相似度阈值搜索
await client.search({ ..., params: { radius: 0.8, range_filter: 0.5 } });

// 分页
await client.search({ ..., limit: 10, offset: 0 });
await client.search({ ..., limit: 10, offset: 10 });

多向量混合搜索(2.4+)

同时用稠密向量(语义)和稀疏向量(关键词)做多路召回,RRF 重排序:

js 复制代码
await client.hybridSearch({
  collection_name: 'ai_diary',
  searches: [
    { vector: denseVec, anns_field: 'dense_vector', metric_type: 'COSINE', limit: 100 },
    { vector: sparseVec, anns_field: 'sparse_vector', metric_type: 'IP', limit: 100 },
  ],
  rerank: { strategy: 'rrf', params: { k: 60 } },
  limit: 10,
  output_fields: ['id', 'content'],
});

七、内存管理与一致性

Collection 必须 load 进 QueryNode 内存才能被检索,用毕 release 释放:

js 复制代码
await client.loadCollection({ collection_name: 'ai_diary', replica_number: 2 });
await client.releaseCollection({ collection_name: 'ai_diary' });

四种一致性级别:

级别 语义 场景
Strong 线性一致,保证读到最新 金融、库存
Session 同客户端 read-your-writes 写后即查
Bounded 容忍指定窗口内的延迟 一般业务
Eventually 最终一致,可能陈旧 推荐、分析
js 复制代码
await client.insert({ ..., consistency_level: 'Session' });

八、生产要点

错误重试 :SDK 内置 maxRetries,业务层建议再兜一层 rate_limit 的退避重试。

批量写入 :每批 1000 条分片提交,最后显式 flush 确保落盘。

模型选型

模型 维度 特点
text-embedding-3-small (OpenAI) 1536 通用均衡
text-embedding-v2 (阿里) 1536 中文语义强
BGE-M3 (BAAI) 1024 开源,稠密+稀疏
M3E-large 1024 开源中文轻量

选定后不要轻易更换------换模型意味着所有向量重新入库。

成本控制:维度选 1536 即可;非核心场景用 IVF_SQ8 压缩 75% 内存;按时间分区 + TTL 自动清理;冷数据 release、热数据常驻。


九、总结

本文以 AI 日记项目为线索,覆盖了 Milvus 从接入到生产的核心链路:

  1. Schema:字段类型、强/动态 Schema、Partition 分区
  2. 索引:FLAT → IVF_FLAT → HNSW → IVF_SQ8 → IVFPQ → DISKANN 的递进路径
  3. 操作:Insert / Upsert / Query / Delete
  4. 搜索:语义搜索、标量过滤、范围搜索、分页、多向量混合+RFF
  5. 可靠性:一致性级别与内存管理
  6. 工程:重试、批量写入、模型选型、成本优化

向量数据库是 RAG 的记忆基础,搞懂 Milvus 就搞懂了 AI 检索链路的底层逻辑。

相关推荐
TrisighT1 小时前
Agent 并行工具调用快了 0.8 秒——200 组实测发现,多花的那 3100 token 全打了水漂
aigc·agent·ai编程
Token炼金师1 小时前
千卡月崩五次:loss spike、硬件故障、checkpoint 与断点续训 —— 训练可靠性的生死线
人工智能·深度学习·llm
Token炼金师1 小时前
指令的塑形:从野生语言模型到听话助手 —— 监督微调 SFT 全链路
人工智能·深度学习·llm
新知图书1 小时前
多模态智能体开发工具链推荐(IDE/调试工具/日志工具)
人工智能·agent·多模态·ai agent·智能体
武子康1 小时前
🔥 codex-plugin-cc 原理全拆:四层桥接 + JSON-RPC 2.0 + JSONL + 后台任务状态机
人工智能·llm·claude
思考着亮1 小时前
2.模型的创建与调用
agent
SelectDB技术团队1 小时前
Agent 可观测性:Apache Doris / SelectDB 的技术能力、选型对比与实践
数据库·人工智能·agent·可观测·ai-native·apache doris·selectdb
怕浪猫2 小时前
第15章 安全、伦理与合规
langchain·openai·agent
早春的树长在理想三旬3 小时前
工具抽象的艺术:可插拔Skill系统的设计哲学
人工智能·agent·springai