第4章:RAG技术原理与应用
RAG,全称是检索增强生成。说白了,就是给大模型配一个「外挂知识库」。
我刚开始做AI产品时,遇到一个很头疼的问题:大模型虽然能说会道,但一碰到企业内部数据、实时信息,它就「失忆」了。你问它「我们公司去年的财报怎么样」,它只能瞎编。后来我接触到RAG,才意识到——这才是大模型落地的正确姿势。
4.1 RAG架构:检索+生成
RAG的核心流程,我习惯拆成三步走:
- 检索阶段:用户提问后,先从知识库里找出最相关的文档片段
- 增强阶段:把检索到的文档片段和用户问题拼在一起,形成完整的提示词
- 生成阶段:大模型基于这个增强后的提示词,生成最终答案
你想想看,这就像考试时允许你翻书。大模型不再是凭记忆瞎蒙,而是先查资料再作答。准确率自然高得多。
关键点:RAG不是简单的「搜索+问答」,而是让检索结果成为生成过程的「上下文约束」。我见过不少团队把RAG做成了「先搜索再拼接」,结果模型根本不看检索内容——那还不如直接用搜索引擎。
我在项目中遇到过最典型的场景:客户想做一个内部知识库问答系统。员工问「报销流程是什么」,如果只用大模型,它可能回答「请填写报销单并提交审批」这种通用答案。但用了RAG后,它能检索到公司最新的《报销管理制度V3.2》,然后回答「根据公司规定,差旅报销需在钉钉提交申请,附上发票照片,3个工作日内审批」——这才是员工真正需要的答案。
4.2 向量数据库:Pinecone vs Milvus
RAG的检索环节,核心依赖向量数据库。为什么?因为我们要找的不是关键词匹配,而是语义相似度。
举个例子:用户问「怎么请假」,知识库里可能存的是「休假申请流程」。传统搜索引擎可能匹配不上,但向量数据库能算出这两句话的语义相似度高达0.92——这就是向量检索的魅力。
目前主流的向量数据库,我重点用过两个:
| 特性 | Pinecone | Milvus |
|---|---|---|
| 部署方式 | 全托管SaaS | 开源可自建 |
| 上手难度 | 低(5分钟接入) | 中(需懂Docker/K8s) |
| 成本 | 按量付费,小规模便宜 | 自建服务器成本 |
| 扩展性 | 自动扩缩容 | 需手动配置 |
| 适用场景 | 快速原型、中小规模 | 大规模、定制化需求 |
我个人习惯:做MVP或Demo时用Pinecone,省心省力。但如果是企业级项目,数据量上千万甚至上亿条,我会选Milvus——毕竟自建能控制成本,而且可以深度调优索引参数。
避坑指南:我曾经在Pinecone上跑了一个百万级知识库,结果每月账单飙到几千美金。后来迁移到Milvus,用同样的数据量,服务器成本不到十分之一。所以,别被「全托管」的便利迷惑了——规模大了,成本会吓你一跳。
4.3 Embedding模型选择
向量数据库存的是向量,那向量从哪来?答案是Embedding模型。
Embedding模型的作用,就是把文本变成一串数字(向量)。好的Embedding模型,能让语义相近的文本在向量空间里「靠得近」。
我常用的Embedding模型有这几类:
- OpenAI text-embedding-ada-002:1536维,通用性强,但需API调用,有延迟和成本
- BGE系列(BAAI/bge-large-zh):中文场景首选,1024维,开源可本地部署
- M3E(moka-ai/m3e-base):国产开源,轻量级,适合移动端或边缘设备
- Sentence-BERT:适合句子级别的语义匹配,但中文支持一般
选Embedding模型时,我建议你关注三个指标:
- 维度数:越高越精确,但存储和检索成本也越高
- 语言支持:中文场景一定要测过,别信宣传
- 推理速度:线上服务要求毫秒级响应,模型不能太大
注意:Embedding模型和生成模型是两回事。别想着用GPT-4直接生成向量——它不干这个活。Embedding模型是专门训练的,比如OpenAI的ada-002,或者BGE系列。混用会导致检索效果极差。
嗯,这里要注意:同一个知识库,必须用同一个Embedding模型。我见过有人先用了ada-002生成了一批向量,后来又换成BGE重新生成了一批,结果检索时语义完全不匹配——因为两个模型的向量空间不一样。
4.4 知识库搭建实战
理论说完了,咱们直接上手。我以一个企业内部知识库为例,带你走一遍完整流程。
第一步:数据准备
知识库的数据来源五花八门:Word文档、PDF、网页、数据库记录……我习惯先把它们统一转成Markdown格式。为什么?因为Markdown结构清晰,便于后续分块。
# 数据清洗示例(Python伪代码)
import markdown
from bs4 import BeautifulSoup
def clean_document(file_path):
# 读取原始文档
with open(file_path, 'r') as f:
content = f.read()
# 转成Markdown
md_content = markdown.markdown(content)
# 提取纯文本
soup = BeautifulSoup(md_content, 'html.parser')
text = soup.get_text()
return text
第二步:文本分块
大模型有上下文长度限制(比如4K、8K tokens),所以不能把整本书塞进去。需要把文档切成小块。
我常用的分块策略:
- 按段落分:简单粗暴,但可能切碎语义
- 按章节分:保留结构,但块大小不一
- 滑动窗口分:固定长度(比如512 tokens),重叠100 tokens,保证上下文连贯
我的经验:滑动窗口分块效果最好。块大小设为512 tokens,重叠128 tokens。这样既不会丢失上下文,又能控制检索粒度。我曾经试过按段落分,结果一个段落太长,超过了模型上下文限制——直接报错。
# 滑动窗口分块示例
def split_text(text, chunk_size=512, overlap=128):
chunks = []
start = 0
while start < len(text):
end = start + chunk_size
chunk = text[start:end]
chunks.append(chunk)
start = end - overlap
return chunks
第三步:生成向量并存储
用Embedding模型把每个块转成向量,然后存入向量数据库。
# 使用BGE模型生成向量
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('BAAI/bge-large-zh')
chunks = split_text(document_text)
vectors = model.encode(chunks)
# 存入Milvus
from pymilvus import Collection
collection = Collection("knowledge_base")
for i, (chunk, vector) in enumerate(zip(chunks, vectors)):
collection.insert([
[i], # id
[chunk], # 原始文本
[vector] # 向量
])
第四步:构建检索+生成流程
用户提问时,先检索最相关的3-5个块,然后拼成提示词,交给大模型生成答案。
def rag_query(user_question):
# 1. 向量化用户问题
question_vector = model.encode([user_question])
# 2. 检索相似块
results = collection.search(
data=question_vector,
anns_field="vector",
param={"metric_type": "IP", "params": {"nprobe": 10}},
limit=3
)
# 3. 拼接上下文
context = "\n".join([result.entity.get('text') for result in results[0]])
prompt = f"基于以下信息回答问题:\n{context}\n\n问题:{user_question}"
# 4. 调用大模型生成
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": prompt}]
)
return response.choices[0].message.content
避坑指南:我曾经在检索时只返回了1个块,结果模型信息不足,回答得支离破碎。后来我改成返回3-5个块,并让模型「如果信息不足,请明确说不知道」——效果好了很多。记住:检索的召回率比精确率更重要。
第五步:持续优化
知识库不是一次建完就完事了。我建议你定期做三件事:
- 更新数据:新文档进来,重新生成向量并插入
- 评估效果:用测试集跑一遍,看检索准确率和生成质量
- 调整参数:分块大小、重叠长度、检索数量,这些都需要根据实际场景调优
说实话,RAG的落地难点不在技术,而在数据质量。我见过太多团队把一堆乱七八糟的文档扔进去,然后抱怨RAG效果不好。你想想看,如果知识库本身就有错误、有矛盾,大模型再聪明也救不了。
所以,我的建议是:先花80%的精力把知识库整理好,再花20%的精力搭建RAG系统。顺序别搞反了。
这一章的内容就到这里。下一章我们会深入讲「提示词工程」——说白了,就是怎么跟大模型「好好说话」,让它乖乖听话。