上周我问 Claude:“我们项目的部署文档放哪了?” 它跟我扯了一通 Kubernetes 最佳实践,写得头头是道,就是跟我们项目没半毛钱关系。

还有一次让它查某个 API 的最新用法,它给了一段去年就废弃的代码,还说"亲测可用"。

AI 不是笨,是它在闭卷考试。RAG(检索增强生成)就是解决这个问题的:给大模型配一本参考书,让它查完再答。

大模型为什么会"编"答案

大模型训练的时候确实读了很多书,但它有两个硬伤。

记忆有截止日期。Claude 的训练数据到 2025 年初,你问它 2026 年的事,它只能靠猜。就像你让一个 2020 年毕业的学生回答 2026 年的时事题,他总不能说"我翻翻课本"吧。

另一个问题是它不认识你家的数据。你公司的内部文档、产品手册、客户记录,大模型压根没见过。据 Gartner 2025 年的报告,企业 80% 以上的数据属于非公开的内部数据。你让大模型回答相关问题,相当于让刚入职的实习生回答"公司上个季度的退款率是多少",他不编才怪。

那怎么办?给它一本参考书,让它开卷考试。

这就是 RAG – 检索增强生成(Retrieval-Augmented Generation)。

RAG 检索增强生成是什么

RAG 的思路朴素到有点不好意思讲。你上学考试那会儿:

闭卷考试:老师问什么,全靠脑子里记住的东西。记不住就瞎编,编得像模像样但经不起推敲。

开卷考试:先翻书找到相关的章节,读一遍,再根据找到的内容回答。靠谱得多。

RAG 就是把 AI 从闭卷模式切换到开卷模式。流程也不复杂:

  1. 你提问:“PostgreSQL 18 的异步 IO 性能提升了多少?”
  2. 系统帮你翻书:从你的知识库里找到最相关的几段内容
  3. AI 带着资料答题:Claude 读完这些内容,再给你一个有理有据的回答

整个过程就像你去图书馆查资料。你不会把整个图书馆的书都搬回家,而是先查目录,找到相关的那几本,翻到对应的章节,然后写你的报告。

Embedding 向量搜索:怎么"翻书"的

这里要讲一个东西:Embedding,中文叫嵌入向量。也是 RAG 系统的核心组件。

名字唬人,原理直觉。

Embedding 向量空间:意思相近的文本距离近,意思不同的距离远

想象每段文字都是一个人,站在一个巨大的操场上。意思相近的人站得近,意思不同的人站得远。“如何配置 nginx"和"nginx 反向代理设置"这两个人几乎肩并肩,而"今天天气真好"则站在操场对角线的另一头。

Embedding 做的事情就是给每段文字分配一个"坐标”。这个坐标不是二维的,而是上千维的(别试图想象,想象不了),但道理是一样的:语义越接近,坐标距离越近。

有了坐标,搜索就变成了"找最近的邻居"。你的提问被转换成一个坐标,然后在所有文档的坐标中找最近的几个。

这比关键词搜索强在哪?关键词搜索只认字面匹配,你搜"怎么让网站变快",它找不到"Web 性能优化指南"这篇文章,因为一个词都没对上。Embedding 搜索能理解两句话说的是同一件事。

四步搭建 RAG 系统(附完整代码)

原理讲完了,上代码。

第一步,把你的文档切成小块。

一整本书塞给 AI 不现实,上下文窗口塞不下。你得把文档切成合适大小的块,每块大概 200-500 个 token。

def chunk_text(text, chunk_size=400, overlap=50):
    """把长文本切成有重叠的小块"""
    words = text.split()
    chunks = []
    for i in range(0, len(words), chunk_size - overlap):
        chunk = ' '.join(words[i:i + chunk_size])
        chunks.append(chunk)
    return chunks

# 把你的文档切块
documents = [
    "PostgreSQL 18 引入了基于 io_uring 的异步 IO...",
    "Redis 缓存层的真实延迟不是命中时的延迟...",
    "B 树跳跃扫描可以跳过复合索引的前导列...",
]

这里有个细节:块和块之间要有重叠。不然一个关键信息正好被切成两半,两边都不完整,搜索的时候就找不到了。就像你撕报纸叠鞋垫,撕的时候尽量别从文章正中间下手。

第二步,给每块文档生成 Embedding。

Anthropic 推荐用 Voyage AI 的 Embedding 模型,当前最新的是 voyage-3.5,支持 32000 token 的上下文长度,生成 1024 维的向量。

import voyageai
import numpy as np

vo = voyageai.Client()

# 给文档生成向量(注意 input_type="document")
doc_embeddings = vo.embed(
    documents,
    model="voyage-3.5",
    input_type="document"
).embeddings

注意 input_type="document" 这个参数别丢。Voyage 会根据类型在文本前面加不同的指令前缀,查询用 "query",文档用 "document"。加了类型标注比不加检索质量更高,Voyage 自己测过。

第三步,用户提问时,搜索最相关的块。

def search(query, doc_embeddings, documents, top_k=3):
    """语义搜索:找到最相关的文档块"""
    # 给问题也生成向量
    query_embd = vo.embed(
        [query],
        model="voyage-3.5",
        input_type="query"
    ).embeddings[0]

    # 算相似度(Voyage 的向量已经归一化了,点积就是余弦相似度)
    similarities = np.dot(doc_embeddings, query_embd)

    # 取最相关的 top_k 个
    top_indices = np.argsort(similarities)[-top_k:][::-1]
    return [(documents[i], similarities[i]) for i in top_indices]

results = search("io_uring 性能提升多少", doc_embeddings, documents)

第四步,把检索结果塞给 Claude,让它开卷答题。

import anthropic

client = anthropic.Anthropic()

def rag_answer(query, retrieved_docs):
    """带着检索结果让 Claude 回答"""
    context = "\n\n---\n\n".join([doc for doc, score in retrieved_docs])

    response = client.messages.create(
        model="claude-sonnet-4-5-20250514",
        max_tokens=1024,
        messages=[{
            "role": "user",
            "content": f"""根据以下参考资料回答问题。如果资料中没有相关信息,请明确说明。

参考资料:
{context}

问题:{query}"""
        }]
    )
    return response.content[0].text

四步走完,一个基础的 RAG 系统就搭好了。你的 AI 从此不用靠猜了。

RAG 四步流程:提问、搜索、检索、生成

Contextual Retrieval:检索失败率降低 67% 的方法

上面的基础版有个问题。

假设你的知识库里有一段话:“该公司第三季度的收入增长了 15%。” 你把它切成块之后,这段话就变成了一个孤零零的句子。“该公司"是哪家公司?“第三季度"是哪一年的?上下文丢了。

传统 RAG 就像你从一本书里撕下一页纸,光看这一页你可能搞不清楚它在说什么。

Contextual Retrieval:给切块补上上下文说明

Anthropic 在 2024 年底提出了 Contextual Retrieval (上下文检索)来解决这个问题。做法不复杂:切块之后、生成 Embedding 之前,先让 Claude 给每个块加一段"上下文说明”。

比如那段"该公司第三季度的收入增长了 15%",Claude 会在前面加上:“这段内容来自苹果公司 2025 年度财报,讨论的是 2025 年第三季度的财务表现。” 这样一来,即使块被单独拿出来,信息也是完整的。

效果怎么样?Anthropic 的实测数据:

方案检索失败率
传统 Embedding5.7%
Contextual Embedding3.7%(降低 35%)
Contextual Embedding + BM252.9%(降低 49%)
上面全加上 Reranking1.9%(降低 67%)

检索失败率从 5.7% 降到 1.9%。

成本也低。利用 Claude 的 Prompt Caching(提示缓存),给一百万 token 的文档做上下文标注大约 1 美元。一本 30 万字的技术手册,几块钱处理完。

RAG 适用场景判断

别什么场景都上 RAG。

知识库小于 20 万 token(大约 15 万字)的,直接塞进提示词就行。Claude 的上下文窗口有 200K token,一本小书直接放进去,比搞一套 RAG 管线简单太多了。你家厨房就三个抽屉,没必要建一套仓库管理系统来找开瓶器。

需要 RAG 的场景:

  • 企业知识库,几十万到几百万字的文档
  • 频繁更新的内容,比如产品手册、API 文档
  • 需要精确引用来源的场景,比如法律、医疗咨询
  • 多个不同来源的知识需要整合

不太需要 RAG 的场景:

  • 通用知识问答,大模型自己就知道
  • 数据量很小,直接塞上下文更简单
  • 需要推理而不是检索的任务

RAG 常见问题和避坑指南

块切得太小或太大。

切太小,每块信息不完整,AI 答得支离破碎。切太大,检索精度下降,一大段里面有用的就一句话。一般 200-500 token 是个比较好的范围,但具体要根据你的文档类型调。技术文档可以切大一点,因为一个概念往往需要一段完整的描述;FAQ 类的可以切小一点,一问一答就是一块。

只用语义搜索。

Embedding 搜索擅长理解意思,但对精确术语匹配不太行。你搜"ERR_CONNECTION_REFUSED”,语义搜索可能给你返回一堆关于"网络连接问题"的泛泛内容,而不是精确匹配这个错误码的解决方案。

办法是语义搜索 + BM25 关键词搜索混合使用。两种方式各取 top-20,合并去重,再用 Reranking 模型排序。Anthropic 的数据显示,混合方案比单用语义搜索检索失败率降 49%。

检索到了不相关的内容还硬用。

有时候知识库里确实没有答案,但系统还是会返回"最相关"的几个块(只是相似度最高的,不代表真的相关)。AI 拿到这些似是而非的内容,可能会生成一个看起来合理但实际上驴唇不对马嘴的回答。

办法是设一个相似度阈值。低于阈值的检索结果直接丢掉,让 AI 老老实实说"我在现有资料中没有找到相关信息"。诚实比瞎编值钱。

忘了更新索引。

文档更新了但 Embedding 没重新生成,AI 还在用旧信息回答。这种错误隐蔽,AI 的回答格式完美、逻辑通顺,就是内容过时了。定时任务跑起来,文档有变动就重新索引。

Voyage AI Embedding 模型选型对比

Anthropic 官方推荐 Voyage AI 的模型,目前有几个选择:

模型适用场景上下文长度
voyage-3.5通用检索,性价比最优32,000 token
voyage-3-large最高检索质量32,000 token
voyage-3.5-lite低延迟、低成本32,000 token
voyage-code-3代码检索32,000 token
voyage-finance-2金融领域32,000 token
voyage-law-2法律领域16,000 token

如果你不知道选哪个,voyage-3.5 稳。做代码相关的用 voyage-code-3,做金融用 voyage-finance-2。专用模型在对应领域的检索质量比通用模型高不少。

Voyage 还支持向量量化。原始的 32 位浮点向量可以压缩成 8 位整数甚至 1 位二进制,存储空间分别缩小 4 倍和 32 倍。大规模知识库省存储,检索也更快。

今天就能动手

看到这里别光收藏。

选你团队最常查的一份文档,用上面的代码切块、生成 Embedding、存到一个 numpy 数组里。半小时的事。四步代码拼在一起不到 50 行,跑起来对着你的文档问几个问题,感受一下。

然后拿同一个问题,分别用纯 Claude 和 RAG + Claude 回答,对比差别。特别是涉及你们公司内部信息的问题,差距会让你想骂人 – 为什么不早点搞。

说白了 RAG 就是给 AI 配了一本参考书。这本书可以是你的产品文档、公司 Wiki、历史工单,甚至整个维基百科。AI 不用再靠那些可能过时的"记忆"硬撑了。


你的业务场景里,最想让 AI “翻"哪本书?是公司的内部文档,还是某个技术领域的知识库?评论区聊聊,说不定你的需求也是别人正在解决的问题。

下一篇聊聊 Claude 的 Tool Use,让 AI 不光能查资料,还能调接口、查数据库、发通知。从"只会说"到"能动手”。


参考资料:


觉得有用就点个赞。有朋友也在折腾 AI 应用的,转给他看看。关注梦兽编程,后面还有更多实战内容。