向量数据库与 RAG 检索原理

举报
yd_212140359 发表于 2026/11/05 20:08:54 2026/11/05
【摘要】 向量数据库与 RAG 检索原理本文讲清 RAG 为什么离不开向量检索:Embedding 如何把语义变成向量、ANN 索引如何在上亿向量里快速找近邻、RAG 的完整链路与常见坑。 为什么需要向量检索传统检索靠关键词匹配,用户搜"怎么退订会员",文档里写的是"取消订阅流程",字面不重合就搜不到。向量检索先把文本编码成语义向量,再用向量距离衡量相似度,能跨越字面差异。RAG(Retrieval...

向量数据库与 RAG 检索原理

本文讲清 RAG 为什么离不开向量检索:Embedding 如何把语义变成向量、ANN 索引如何在上亿向量里快速找近邻、RAG 的完整链路与常见坑。

为什么需要向量检索

传统检索靠关键词匹配,用户搜"怎么退订会员",文档里写的是"取消订阅流程",字面不重合就搜不到。向量检索先把文本编码成语义向量,再用向量距离衡量相似度,能跨越字面差异。

RAG(Retrieval-Augmented Generation)就是在这个基础上,把检索到的片段塞进大模型上下文,让回答有据可依,减少幻觉。

Embedding:文本怎么变成向量

Embedding 模型把一段文本映射成固定维度的浮点数组,语义相近的文本在向量空间里距离更近。常见维度是 768、1024、1536。

相似度一般用余弦相似度:

cos(a, b) = (a · b) / (|a| × |b|)

值越接近 1 越相似。工程上通常先把向量归一化,这样余弦相似度等价于内积,计算更快。

选型要点:中文场景优先选中文语料训练过的模型;同一批数据必须用同一个模型编码,换模型要全量重建索引。

向量数据库:存什么、怎么查

向量数据库要解决三件事:存向量和元数据、建 ANN 索引、支持过滤与更新。

精确最近邻(暴力算所有距离)在百万级还能扛,上亿就不现实。所以用近似最近邻(ANN),用少量召回损失换数量级的性能提升。

主流 ANN 索引

索引 原理 优点 缺点
HNSW 多层可导航小世界图 查询快、召回高 内存占用大、构建慢
IVF 先聚类分桶,再查少数桶 内存友好、可扩展 需要训练、边界样本易漏
IVF-PQ IVF + 乘积量化压缩 亿级可用、省内存 有量化误差
Flat 暴力精确检索 最准 慢,只适合小数据

HNSW 是当前最常用的默认选择,参数 M(每层连接数)和 ef_construction 影响召回与构建成本,查询时 ef_search 影响召回与延迟。

常见产品

  • Milvus:分布式、功能全,适合大规模。
  • Qdrant:Rust 实现,过滤能力强,部署轻。
  • Weaviate:自带混合检索与模块化。
  • pgvector:PostgreSQL 扩展,已有 PG 时最省事。
  • Redis / Elasticsearch:也提供向量字段,适合已有基础设施复用。

RAG 的完整链路

  1. 文档解析:PDF、HTML、Markdown 转成纯文本,保留标题层级。
  2. 切分(chunk):按语义或结构切成 300~800 字的片段,带少量重叠。
  3. 嵌入:用 Embedding 模型把每个 chunk 编码成向量。
  4. 入库:向量 + 原文 + 元数据(来源、章节、时间)写入向量库。
  5. 检索:用户问题编码后查 Top-K 近邻,可叠加元数据过滤。
  6. 重排(rerank):用交叉编码器对候选片段精排,提升相关性。
  7. 生成:把片段拼进 prompt,要求模型只依据上下文回答并给出引用。

切分策略

  • 固定长度切分简单,但容易把一句话切断。
  • 按标题/段落切分更符合语义,适合结构化文档。
  • 父子块:用小块检索、用大块送给模型,兼顾召回与上下文完整。
  • 重叠窗口:相邻块保留 10%~20% 重叠,避免边界信息丢失。

混合检索与重排

纯向量检索对专有名词、编号、代码不敏感,纯关键词检索又不懂语义。实践中常用 BM25 + 向量混合召回,再用 RRF(倒数排名融合)合并。

重排模型(如 bge-reranker)对「问题-片段」逐对打分,精度高于向量相似度,但成本高,一般只对 Top 20~50 做精排。

一个最小可用的 RAG 示例

from openai import OpenAI

client = OpenAI()

def embed(texts):
    resp = client.embeddings.create(model="text-embedding-3-small", input=texts)
    return [d.embedding for d in resp.data]

# 1. 切分并入库(伪代码,向量库可为 pgvector/Qdrant/Milvus)
chunks = split_documents(docs, chunk_size=500, overlap=80)
vectors = embed([c.text for c in chunks])
vector_store.upsert(chunks, vectors)

# 2. 检索
q_vec = embed([question])[0]
hits = vector_store.search(q_vec, top_k=20)

# 3. 重排
reranked = rerank(question, hits, top_n=5)

# 4. 生成
context = "\n\n".join(h.text for h in reranked)
prompt = f"仅根据以下资料回答,找不到就说不知道。\n\n{context}\n\n问题:{question}"
answer = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": prompt}],
).choices[0].message.content

常见坑

  1. 切分太碎:语义不完整,召回片段答非所问。先保证段落完整,再控制长度。
  2. 只做向量检索:编号、人名、代码搜不准,必须配关键词召回。
  3. 不做重排:Top-K 里混入弱相关片段,模型被误导。
  4. 元数据缺失:无法按时间、部门、权限过滤,召回一堆过期或越权内容。
  5. 换模型不重建索引:查询向量和库向量不在同一空间,结果完全错乱。
  6. 忽略评估:只看 demo 效果,不做召回率与忠实度评测。

评估怎么做

  • 检索侧:Recall@K、MRR、命中率。
  • 生成侧:忠实度(是否只依据上下文)、答案相关性、引用准确率。
  • 端到端:人工抽样 + 固定问题集回归,防止调参把已有效果调坏。

最佳实践

  1. 先做小规模端到端跑通,再优化召回,不要一上来就调索引参数。
  2. 中文优先选中文 Embedding,且全链路统一模型版本。
  3. 结构化文档按标题切分,并保留来源元数据。
  4. 混合检索 + 重排是性价比最高的提升手段。
  5. 给模型明确的"不知道就说不知道"约束,并在回答里带引用。
  6. 建回归测试集,每次改切分、模型、参数都跑一遍。

总结

环节 关键点
Embedding 语义向量,模型全链路统一
索引 HNSW 常用,IVF-PQ 省内存
切分 保语义、留重叠、带元数据
检索 向量 + 关键词混合,再重排
生成 限定上下文、要求引用
评估 召回率 + 忠实度 + 回归集

RAG 的效果上限由检索决定:检索不到,模型再强也只能编。把切分、混合召回和重排做扎实,比换更大的模型更划算。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。