RAG 向量检索:从“查字“到“查意“

LLM 很聪明,但它有一个硬伤:知识截止到训练那天 。你今天问它"公司上周更新的报销流程",它只能瞎编。RAG(Retrieval-Augmented Generation,检索增强生成)就是为了解决这个问题:先检索外部知识,再让 LLM 基于检索结果生成答案。而 RAG 的检索环节,核心靠的就是向量检索

这篇文章从零讲清楚:什么是向量检索、Embedding 干了什么、相似度怎么算、大规模检索怎么加速,以及它在 Agent 里到底长什么样。

一、传统搜索 vs 向量检索:为什么"同义词"是个大麻烦

传统搜索引擎(比如数据库里 LIKE '%关键词%' 或倒排索引的 BM25)的核心逻辑是字面匹配。你用"调薪"去搜,文档里写的是"薪酬调整流程"------一个词对不上,直接搜不到。

这叫词汇鸿沟(lexical gap):你说的话和文档里写的话用的是不同词,但意思一样。传统搜索对此毫无办法------它没有"语义理解"能力,只有"字符串匹配"能力。

向量检索换了一个思路:不找"字",找"意思"。它把查询和文档都转成一串数字(向量),然后在数学空间里算距离。语义相近 → 距离近 → 命中。你说"怎么调薪",文档里有"薪酬调整流程",它们在向量空间里紧挨着------哪怕字面上完全不同。

对比维度 传统关键词搜索 向量检索
匹配依据 字面命中(TF-IDF/BM25) 语义距离(余弦相似度等)
"调薪"搜"薪酬调整" 搜不到 能搜到,向量距离近
搜人名、型号、编号 精准 可能漂移
搜"大概意思" 无能为力 强项

一句话:传统搜索像在找 ,向量检索像在找 。成熟的系统都会做混合检索(Hybrid Search)------两路结果融合,互补短板。

二、Embedding:把文字变成"语义坐标"

为什么需要 Embedding

计算机只认识数字。想让计算机判断"猫在沙发上睡觉"和"猫咪在沙发上打盹"意思相近,得先把这两句话都变成数字。Embedding 就是这个转换过程------把文本映射到一个高维向量空间里的一个点

输入一段文字,Embedding 模型输出一个固定长度的浮点数数组,比如 OpenAI 的 text-embedding-3-small 吐出 1536 个浮点数:

复制代码
[0.012, -0.453, 0.891, -0.023, ..., 0.147]   # 共 1536 维

你没法想象 1536 维空间长什么样(人类大脑超过三维就抓瞎了),但数学上完全合理。Embedding 模型在训练时就是靠海量语料学到了一个规律:把语义相近的文字放在高维空间中相邻的位置

经典类比:向量算术

Embedding 的妙处还不止于此。经典实验:"国王" - "男人" + "女人" ≈ "女王"。向量可以做加减法,而且结果还符合人类直觉------这说明向量确实编码了语义信息,不只是随机数字。

主流 Embedding 模型一览

模型 维度 特点
OpenAI text-embedding-3-small 1536 通用性强,性价比高,中文支持不错
OpenAI text-embedding-3-large 3072 精度更高,维度更大,适合高要求场景
BGE (BAAI) 768/1024 开源,中文优化,自部署免 API 费用
Jina Embeddings v3 1024 多语言,支持 8192 token 长文本输入
Cohere Embed v3 1024 支持多种输入类型(搜索文档/搜索查询等)

选模型的核心原则:入库和检索必须用同一个 Embedding 模型。不同模型产出的向量空间不一致,换模型 = 推倒重来。

三、相似度:怎么判断两个向量"靠得近"

向量检索最后一步,就是拿用户 query 的向量,去向量库里找距离最近的 K 个文档向量。这里"距离"有三种常用度量:

余弦相似度(Cosine Similarity)

最通用。计算两个向量夹角的余弦值,范围 [-1, 1]只看方向不看长度,所以长的文档和短的 query 也能公平比较。

复制代码
余弦相似度 = (A · B) / (||A|| × ||B||)

欧氏距离(Euclidean Distance)

两点之间直线距离。当向量已经 L2 归一化后,欧氏距离和余弦相似度等价。

点积(Dot Product)

两个向量逐元素相乘再求和。向量归一化后 = 余弦相似度。适合需要加权排序的场景。

实际工程里 90% 的场景用余弦相似度就对了。推理开销小,语义表达力足够。

四、ANN 加速:百万向量怎么毫秒级搜完

为什么要近似

如果暴力搜索(kNN)------拿 query 向量和库里每个向量都算一遍距离再排序------复杂度是 O(N)。N = 十万的时候还行,N = 百万、千万就崩了:每次查询要跑几百万次浮点运算,延迟秒级起步。

所以实际用的不是精确搜索,而是 ANN(Approximate Nearest Neighbor,近似最近邻)------牺牲一丁点精度(比如从"100% 召回"降到 "99.5% 召回"),换回几十上百倍的加速。

两大主流算法

IVF(Inverted File,倒排文件):先把所有向量用 K-Means 聚成 N 个桶(簇)。查询时只搜离 query 最近的几个桶,桶内做暴力搜索。核心思想是"先粗筛再细查"。

关键参数:

  • nlist:分多少个桶。越大越细,但建索引越慢。
  • nprobe:查几个桶。越大召回越高,但越慢。

HNSW(Hierarchical Navigable Small World,分层可导航小世界图):目前精度和速度的标杆。构建一个分层图结构------上层稀疏用于"导航到大致区域",下层稠密用于"精确定位"。搜索时从顶层逐层下降,类似在多层停车场里从顶层俯瞰逐步找到车位。

关键参数:

  • M:每个节点的最大连接数(16~64)。越大越精确,但内存也越大。
  • efSearch:搜索时考察的候选数(50~200)。越大召回越高,但越慢。
对比维度 IVF HNSW
召回精度 高,但依赖 nprobe 调参 极高,通常优于 IVF
检索速度 极快,接近 O(log N)
构建速度 快(只聚类) 慢(逐点建图)
内存占用 高(存全部原始向量) 高(存图结构 + 向量)
适合场景 平衡速度与精度的通用场景 高要求场景(RAG、推荐系统)

代表向量数据库

数据库 核心算法 特点
FAISS (Meta) IVF, HNSW, PQ 库而非数据库,极高性能,GPU 加速
Milvus IVF, HNSW 分布式架构,支持十亿级
Qdrant HNSW 过滤功能丰富,Rust 实现性能好
Chroma HNSW 轻量易用,适合原型和中小规模
pgvector IVF, HNSW PostgreSQL 扩展,和业务库同存

五、分块策略:切多大才合适

Embedding 模型再好,分块没搞对,检索照样烂。这是工程中最容易被低估的环节。

核心矛盾

  • 切太大(比如 2048 token):一段里塞了三个不同话题,Embedding 向量变成一个"杂糅语义点",搜什么都能命中一点,但精准度极差。
  • 切太小(比如 128 token):一句完整的话被腰斩,上下文丢失,LLM 拿到碎片答不出完整答案。

工程上的经验值

  • 通用场景:512 token 为一块,相邻块重叠 10%~15%(overlap)。这样关键句落在边界处也不被切开。
  • 技术文档/代码:按自然边界切------函数级、段落级、章节级。不要机械按字符数切。
  • QA 场景:每个 Q&A 对独立成块。
python 复制代码
# chunking_config.py --- 典型分块配置
from langchain.text_splitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,        # 每块 500 字符
    chunk_overlap=50,      # 相邻块重叠 50 字符(10%)
    separators=["\n\n", "\n", "。", ".", " "]  # 优先按自然边界切
)
chunks = splitter.create_documents([document])

分块同时要带上元数据:来源文档名、章节标题、时间戳、版本号。检索时可以用元数据做过滤("只搜 2024 年的文档"),精度大幅提升。

六、Rerank:少花钱、多办事

向量检索拉回 top-20 个候选块,直接全塞给 LLM?token 浪费不说,后面十几条可能是噪音。

两阶段检索是性价比最优解:

  1. 粗筛(向量检索):从全库拉 top-20 ~ top-50
  2. 精排(Rerank 模型):用一个专门的重排序模型对候选集打分,取 top-3 ~ top-5 喂 LLM

Rerank 模型比 Embedding 模型更"贵"但更准------它会把 query 和候选文档逐对精细比对,而不是只算一次向量距离。Cohere Rerank、BGE-Reranker 是常用选择。

python 复制代码
# retrieval.py --- 两阶段检索伪代码
def retrieve_and_rerank(query: str, top_k: int = 20, final_k: int = 3) -> list[str]:
    # 阶段1:向量粗筛
    query_vec = embed(query)
    candidates = vector_db.search(query_vec, top_k=top_k)

    # 阶段2:Rerank 精排
    scores = reranker.compute_scores(query, [c.text for c in candidates])
    ranked = sorted(zip(candidates, scores), key=lambda x: x[1], reverse=True)

    return [c.text for c, _ in ranked[:final_k]]

七、在 Agent 中长什么样

Agent 用 RAG 不需要把每个细节都自己手写,成熟的框架已经封装好了。以你正在学的 LangChain 为例:

python 复制代码
# agent_rag_pipeline.py --- Agent 中 RAG 的最小闭环
from langchain.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma

# 1. 加载文档 + 分块
loader = TextLoader("company_policy.txt")
docs = loader.load()
chunks = RecursiveCharacterTextSplitter(
    chunk_size=500, chunk_overlap=50
).split_documents(docs)

# 2. 向量化 + 存入向量库
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_documents(chunks, embeddings)

# 3. 作为检索器接入 Agent
retriever = vectorstore.as_retriever(search_kwargs={"k": 5})

# 4. Agent 调用时,retriever 自动搜相关文档注入上下文

完整链路:用户问 → Agent 决策 → 调 retriever 搜向量库 → 拿 top-k 文档 → 拼 prompt → LLM 生成 → 返回答案

关键设计点:

  • retriever 对 Agent 来说是一个工具(Tool),和其他工具(API 调用、数据库查询、代码执行)并列。Agent 自己决定什么时候该用检索、什么时候不该。
  • 检索到的内容注入到 LLM 的 system prompt 或上下文里,而不是替换用户原始问题。

八、常见坑

现象 真相
向量检索结果完全不对 入库和检索用了不同的 Embedding 模型,向量空间不统一
换了个相似问法就搜不到了 Embedding 模型对领域术语没训练过,考虑微调或换领域模型
明明文档有答案,检索就是命中不了 分块太大,语义被稀释;或分块太小,关键上下文被切断
向量检索快但内存爆炸 没做向量压缩(PQ),1536 维 × 百万条 = 几个 GB 起
Rerank 加了反而变慢 Rerank 模型和 Embedding 模型有"排序偏好冲突"------粗筛选出的候选集本身不行,精排救不回来
混合检索分数无法对齐 向量余弦值(-1~1)和 BM25 得分量纲不同,直接加权无效;用 RRF(倒数排名融合)代替

九、总结

向量检索用一句话概括:把文本映射到高维向量的语义空间,靠距离度量"意思有多近",用 ANN 算法在大规模数据中毫秒级找出最相关的文档。它在 RAG 链路里扮演"外部大脑的记忆索引"------不是取代 LLM,而是让 LLM 有据可依。

工程上的优先级:分块策略 > Embedding 模型选择 > 向量数据库选型。很多项目在向量数据库上纠结半天,结果分块一塌糊涂------方向跑偏了。

最后记住一个核心判断:向量检索擅长"语义模糊匹配",不擅长"精确条件查找"。人名、型号、编号、日期范围、法条编号------这些老老实实用过滤条件或关键词搜索,别硬往向量上套。

相关推荐
断眉的派大星1 小时前
CLIP原理详解:图像与文本的跨模态学习
人工智能·深度学习·机器学习
码云之上1 小时前
Loop Engineering:把模型调用变成可控的多步任务
人工智能·前端框架·前端工程化
未来智慧谷1 小时前
从 Atlas 关停复盘:AI 应用的三种载体形态怎么选(独立应用 / 浏览器扩展 / 桌面宿主)
前端·人工智能·ai·架构·浏览器
bug嘛我经常写1 小时前
dbf文件UTF-8转GBK编码,以及两编码文件互转
数据库·python
dogstarhuang1 小时前
Kimi K3 本地部署实战:从 1.56TB 权重到推理服务的完整成本分析
java·人工智能·后端·ai·开源·接口·程序员创富
去伪存真20251 小时前
数字工厂与产线孪生的平台能力深度拆解
大数据·运维·人工智能·机器人
人工智能培训1 小时前
中小企业低成本落地AI工具实操方案
大数据·人工智能·gpt·机器学习·agi
知行合一。。。1 小时前
LangGraph--03--本地服务与 Studio 调试
数据库
胡耀超1 小时前
AI出事后,怎么查?——读《AI Forensics》
人工智能·python·数字取证·ai取证·