我构建了一个 RAG 管道。随后我意识到检索才是真正的模型

发布: (2026年4月8日 GMT+8 11:03)
6 分钟阅读
原文: Dev.to

Source: Dev.to

大家都在谈论 LLM。GPT‑4、Claude、Gemini——它们是明星。但在构建我的第一个真实 RAG 流水线后,我学到了一件令人谦卑的事:LLM 是可互换的部件,检索系统才是真正的工作者。

让我来给你展示一下我的意思。

我们都在复制的四步流水线

  1. Ingest – 将文档切分为块
  2. Embed – 将块转化为向量
  3. Retrieve – 找到前 k 个相似块
  4. Generate – LLM 使用该上下文生成答案

它能工作。我的机器人可以用引用来回答公司政策问题。我感到很聪明。

然后我问道:“我可以为数字产品退款吗?”

LLM 给出了一个漂亮且自信的答案,但完全错误。因为我的检索返回了一段关于实体退货(30 天,原包装)的内容,完全错过了位于两段之后的数字产品例外。

LLM 完美地完成了它的工作。检索失败了。

为什么检索才是真正的模型

你认为重要的实际重要的
使用的 LLM文档切分方式
Prompt 工程嵌入质量
系统提示检索后的重新排序

LLM 只负责格式化答案。检索决定答案是否真实。

修复我的流水线的代码

语义搜索单独使用时会遗漏像 “non‑refundable after download” 这样的精确短语。关键词搜索单独使用时会忽略语义。混合搜索将两者结合。下面是核心实现(使用 FAISS + BM25):

from sentence_transformers import SentenceTransformer
import faiss, numpy as np
from rank_bm25 import BM25Okapi

# 1. Load documents and embed
docs = [
    "Refund within 30 days, physical items only.",
    "Digital products: non-refundable after download.",
    "Contact support for defective digital items."
]
model = SentenceTransformer('all-MiniLM-L6-v2')
embeddings = model.encode(docs)
index = faiss.IndexFlatL2(embeddings.shape[1])
index.add(np.array(embeddings, dtype='float32'))

# 2. BM25 keyword index (tokenized)
tokenized_docs = [doc.lower().split() for doc in docs]
bm25 = BM25Okapi(tokenized_docs)

# 3. Hybrid search function
def hybrid_search(query, top_k=2, alpha=0.5):
    # Semantic score (distance -> similarity)
    query_vec = model.encode([query])
    distances, indices = index.search(query_vec, top_k)
    semantic_scores = 1 / (1 + distances[0])

    # Keyword score
    query_tokens = query.lower().split()
    bm25_scores = bm25.get_scores(query_tokens)
    top_bm25_idx = np.argsort(bm25_scores)[-top_k:][::-1]
    keyword_scores = [bm25_scores[i] for i in top_bm25_idx]

    # Combine (normalized)
    combined = {}
    for i, idx in enumerate(indices[0]):
        combined[idx] = alpha * semantic_scores[i]
    for i, idx in enumerate(top_bm25_idx):
        combined[idx] = combined.get(idx, 0) + (1 - alpha) * (keyword_scores[i] / max(keyword_scores))

    return sorted(combined.items(), key=lambda x: x[1], reverse=True)[:top_k]

# 4. Test
query = "Can I get my money back for a digital product?"
results = hybrid_search(query)
for idx, score in results:
    print(f"Score: {score:.2f} | {docs[idx]}")
# Output: Score: 0.92 | Digital products: non-refundable after download.

alpha=0.5 使意义和精确措辞之间取得平衡。若不使用混合搜索,数字产品的那段内容会被忽略;使用后,它会作为最高结果出现。

让我的流水线提升 10 倍的三项改动

  • 块大小不是默认值 – 改为使用重叠块(200 个 token,重叠 50 个 token)。
  • 仅使用语义搜索会误导 – 添加了 BM25 混合搜索(见上方代码)。
  • 重新排序改变了一切 – 使用小型交叉编码器对前 10 个块重新打分,使准确率从 72 % 提升到 91 %。

大多数人犯的错误

我们把 RAG 当作 LLM 问题来处理。因此我们会调整提示、切换模型、添加系统指令。

但 LLM 被迫 使用你提供的任何上下文。如果你喂给它错误的片段,它会自信地产生幻觉。如果你喂给它正确的片段,即使是小模型也能正确回答。

瓶颈几乎从来不是 LLM,而是检索器。

我现在的不同做法

在编写任何一行代理代码之前,我会先问三个问题:

  1. “如果我手动搜索我的向量数据库,我会找到准确回答此问题的句子吗?”
  2. “我的检索是否同时适用于同义词 and 精确关键词?” → 如果没有,使用混合搜索。
  3. “检索到的排名第一的片段真的最优吗?” → 如果没有,添加重新排序器。

结论

AI 产业向你推销模型。但在生产环境的 RAG 系统中,模型是最便宜、最容易替换的组件。难点——也是区分真正可用机器人和演示软件的关键——在于把正确的信息放入上下文窗口。

LLM 是笔。检索是记忆。而记忆让系统变得有用。

所以,下次你的 RAG 机器人出错时,别怪 GPT。检查一下你检索到了什么。真正的问题就出在这里。

0 浏览
Back to Blog

相关文章

阅读更多 »

讨论:AI 与机器学习类别

超越 RAG:为什么 AI 代理需要自托管的“记忆中心” 大多数使用 LLM 的开发者都遇到了同样的瓶颈:上下文窗口的限制以及“遗忘”……