RAG 陷阱:向量搜索不是语义理解

如果你今天正在构建 AI 应用程序,你可能正在使用 RAG(检索增强生成)。

这个架构诱人地简单:

  • 对文档进行分块。
  • 将它们嵌入为向量。
  • 根据用户查询搜索最近的向量。
  • 将这些块输入 LLM。
    感觉像魔法一样。你输入一个查询,系统"语义地"找到答案。

但行业中有一个肮脏的秘密:朴素 RAG 仅比关键词搜索稍微好一点。

我们像向量数据库"理解"含义一样对待它们。它们不。它们只理解几何接近性。当你依赖原始余弦相似度来驱动应用程序时,你不是在构建智能系统;你是在构建一个会说英语的随机数生成器。

1、核心知识:压缩损失

要理解为什么 RAG 失败,我们必须看看嵌入模型实际上做了什么。

它获取一系列标记(词)并将它们压缩为固定维度的向量(例如,d=768)。

这是一种有损压缩。当我们将一个长而细致的段落压缩为 768 个浮点数时,我们丢失信息。我们丢失"语法"、"否定",并且经常丢失特定的"实体关系"。

然后我们使用余弦相似度比较这些压缩向量:

问题:高相似度并不意味着"正确答案"。它意味着"相似向量空间"。

  • 查询:"如何最小化损失函数?"
  • 检索块:"损失函数代表错误......最大化它导致过拟合。"
    这两个句子的向量极其接近,因为它们包含 90% 的相同关键词(损失、函数、最大化/最小化)。向量数据库看到几何,但错过了逻辑否定。然后 LLM 读取错误的块并自信地产生错误的建议幻觉。

2、流程:"分块"灾难

第二个失败点是最常见的实现错误:盲目分块。

开发人员经常按字符数分割 PDF(例如,每 500 个字符)。

场景:想象一个跨越块 1 和块 2 边界的句子:

"产品的保修在用户......(块 1 结束)......试图打开外壳时无效。"(块 2 开始)

如果用户问 "打开外壳是否使保修无效?","保修"的向量可能存在于块 1 中,而"打开外壳"的上下文存在于块 2 中。

  • 向量数据库检索块 1。
  • LLM 读取块 1,没有提到打开外壳,并回答:"不,保修仍然有效。"
    你不是检索失败;你是边界失败。

3、朴素 RAG 与重新排序

"朴素"方法仅依赖于向量索引。"生产"方法使用向量数据库作为过滤器(候选生成)和用于排序的交叉编码器(精度)。

3.1 朴素方法(快但愚蠢)

复制代码
from langchain.vectorstores import FAISS
from langchain.embeddings import OpenAIEmbeddings

# 加载索引
vectorstore = FAISS.load_local("my_index", OpenAIEmbeddings())

# 纯粹基于余弦相似度检索前 4 个匹配
results = vectorstore.similarity_search("How to reset device?", k=4)

context = "\n".join([doc.page_content for doc in results])

# 此上下文可能包含片段或不相关的相似文本
prompt = f"Context: {context}\nQuestion: How to reset device?"

3.2 生产方法(重新排序)

我们检索大量候选者(例如,50 个)以确保我们不会错过边界,然后使用更昂贵但准确的模型(交叉编码器)对它们进行排序。

复制代码
from sentence_transformers import CrossEncoder
from langchain.vectorstores import FAISS

# 1. 高召回率获取(获得大量潜在匹配)
vectorstore = FAISS.load_local("my_index", OpenAIEmbeddings())
candidates = vectorstore.similarity_search("How to reset device?", k=50)

# 2. 精度重新排序(显式评分关系)
# CrossEncoders 接受(查询,文档)对并输出相关性分数(0-1)
reranker = CrossEncoder('ms-marco-MiniLM-L-6-v2')

# 准备对
pairs = [["How to reset device?", doc.page_content] for doc in candidates]

# 对它们评分
scores = reranker.predict(pairs)

# 3. 根据重新排序器分数选择前 N 个
top_n_indices = sorted(range(len(scores)), key=lambda i: scores[i], reverse=True)[:5]
final_context = [candidates[i].page_content for i in top_n_indices]

# 现在我们向 LLM 提供*真正*相关的块
prompt = f"Context: {final_context}\nQuestion: How to reset device?"

为什么这有效:双向编码器(向量数据库)很快但愚蠢。交叉编码器一起读取查询和块,使其能够看到查询中的*"reset"与文档中的"reboot"*匹配,即使它们的向量稍微相距较远。

4、"迷失在中间"现象

即使你检索了正确的块,你仍然可能因在提示中排列它们的方式而失败。

LLM 擅长使用上下文窗口开头和结尾的信息,但它们难以检索埋在中间的信息。

如果你只是将检索的块连接为块 1 + 块 2 + 块 3,而答案实际上在块 2 中,LLM 可能会错过它。

4.1 解决方案:倒排融合(RRF)或重新排序

不要只是首先转储最高分块,有时你需要根据逻辑流重新排序或简单地突出最重要的块。

复制代码
# 一个简单的技巧:将最高分块放在最后
# 或在最开始,永远不要埋在中间。

best_chunk = final_context[0]
other_chunks = final_context[1:]

# 策略:"固定到最后"
structured_prompt = f"""
以下是一些背景信息:
{chr(10).join(other_chunks)}

关键指令:答案最可能在以下文本中:
{best_chunk}

Question: How to reset device?
"""

5、结束语:RAG 比你想象的更难

向量数据库不是语义搜索引擎。它们是模糊匹配工具。

如果你想构建一个在生产中实际有效的 RAG 系统:

  1. 不要仅依赖余弦相似度。始终添加重新排序层(交叉编码器)。
  2. 尊重块边界。使用"父文档检索"(检索小块,但向 LLM 提供父文档)以避免将句子切成两半。
  3. 管理"中间"。高度意识到你在上下文窗口中放置信息的位置。
    停止将文本投入 Pinecone 索引并希望获得最好的结果。RAG 不是魔法棒;它是一个需要仔细优化的数据管道问题。

关键要点:

  • 余弦相似度捕获关键词密度,而不是逻辑或否定。
  • 分块策略经常切断实体之间的语义链接(例如,因果关系)。
  • 重新排序至关重要:使用向量数据库进行过滤(速度)和交叉编码器进行排序(精度)。
  • 上下文定位:LLM 忽略上下文窗口的中间;相应地构建你的提示。

原文链接:RAG 陷阱:向量搜索不是语义理解 - 汇智网

相关推荐
mCell25 分钟前
关于 Openclaw,最近的一点思考。
人工智能·安全·aigc
qq_171538851 小时前
纳采问名定佳期:中国传统订婚文化的千年传承与地域风华
人工智能
zzb15801 小时前
RAG from Scratch-优化-query
java·数据库·人工智能·后端·spring·mybatis
uzong1 小时前
315晚会曝光“AI大模型被投毒”,让AI听话,GEO是什么,带给我们什么思考
人工智能
V搜xhliang02461 小时前
机器人建模(URDF)与仿真配置
大数据·人工智能·深度学习·机器学习·自然语言处理·机器人
房产中介行业研习社1 小时前
2026年3月哪些房源管理系统功能全
大数据·运维·人工智能
Shining05961 小时前
CUDA 编程系列(三)《内存模型与规约优化》
人工智能·学习·其他·学习方法·infinitensor
lisw052 小时前
基于图像的恶意软件分类方法!
人工智能·机器学习
L-影2 小时前
AI中的Transformer:从RNN的困境到横扫一切的革命(下篇)
人工智能·rnn·ai·transformer
卷福同学2 小时前
QClaw内测体验,能用微信指挥AI干活了
人工智能·算法·ai编程