你所不知道的RAG那些事

大半夜的,我对着屏幕抓狂------之前兴冲冲花了好几个周末整理的Python学习笔记,明明写得挺详细的,结果在"list"和"tuple"这两段里死活搜不到"列表和元组到底什么区别"这个问题。于是我就想:要是我能用 AI 帮我把这些资料"灌"给大模型,让它替我来回答问题就好了。

这个想法真的可行,因为它有个很酷的名字:RAG,全称是"检索增强生成"。你不用自己辛辛苦苦去训练或者微调大模型,也不用花大价钱买算力,只需要把现成的资料"递"给它,就能让模型变得"更懂你"。这篇文章就是想记录下我从零开始搭建这个 RAG 知识库助手的过程,希望对你也有点帮助。

一、我为什么被 RAG "种草"了?

你有没有遇到过那种情况:去问大模型一个很专业或者特别新的问题,它回答得头头是道,感觉跟真的一样,但你仔细一看,里面好多地方其实是在"胡编乱造"?这就是大模型的"幻觉"问题。

RAG 的逻辑很简单:给大模型递上一张"小抄"。

它的本质就是"开卷考试",不想让模型闭着眼睛瞎蒙,而是让它翻开书,找到靠谱的段落,照着给答案。

它其实包含三个小步骤:

  1. 检索:当你提问时,它不急着回答,先去你的资料库里搜。
  2. 增强:把你刚提的问题,和从资料库里搜出来的东西打包,一块儿送给大模型。
  3. 生成:大模型拿着这个"小抄",给你生成一个准确又像人话的回答。

这样一来,你不仅不用去训练模型,还能随时更新自己的"知识库",而且还能看到答案的来源,心里更有底。

二、这个"小抄本"是怎么运作的?

一个 RAG 系统就像个图书馆,它主要分两步走:离线建库,和在线查资料。

第一步,离线建库:整理"小抄"

  1. 加载文档:把各种格式的东西(PDF、Word、Markdown、TXT 都行)加载到系统里。我平时用的 Python 笔记就是这么扔进去的。
  2. 切分文本:你不能把一整本书甩给大模型,它一下也吃不下那么多。得把长文档切成一段段的小块儿。这步挺关键的,如果切得不好,回答的质量就会差很多。我的经验是:别用死板的固定长度去切,可以试着按段落、句子来分,尽量保证每一小块都讲的是一个完整的小知识点,长度大概在 200-500 字左右就行。
  3. 向量化:计算机看不懂咱们的文字,它只认数字。这一步就是把每一小块文字都变成一个固定长度的数字向量。神奇的地方在于,意思越接近的文本,它的向量在空间里的"距离"也就越近。就像"苹果"和"水果"的距离,会比"苹果"和"汽车"的距离近得多。
  4. 存储:把这些向量和你原来的文本块一起,存进一个专门的"向量数据库"里,比如 FAISS、ChromaDB 或者 Milvus 都行。

第二步,在线查询:随时"抄作业"

  1. 提问:当你输入一个问题时。
  2. 查询向量化:系统把你的问题也变成一个向量。
  3. 检索:拿着这个问题的向量,去刚刚建好的向量数据库里找"距离"最近的几段文本。
  4. 生成:把你提的问题和刚找到的那几段文本拼在一起,一起发给大模型,让它根据这些资料来回答。

三、动动手:用 Python 搭一个玩玩

下面是具体的操作步骤,如果你也想试试,可以跟着我一步步来。

首先,要准备的东西:

  • Python 3.7 以上的环境
  • 你的知识库文档(可以是 PDF、TXT 或者 Markdown)
  • 大模型的 API Key(比如 OpenAI、通义千问的都可以)

第一步:装好我们需要的"轮子"

打开终端,运行下面这行命令,把依赖都装上:

bash 复制代码
pip install langchain faiss-cpu sentence-transformers openai

第二步:把资料切碎一点

这一步就是把你的资料切成一段段的,方便后续处理。

python 复制代码
from langchain.text_splitter import RecursiveCharacterTextSplitter

# 假设你的笔记都放在这个文件里
with open("python_notes.txt", "r", encoding="utf-8") as f:
    text = f.read()

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,  # 每块大约500个字符
    chunk_overlap=50 # 重叠50个字符,防止信息被切断
)
chunks = text_splitter.split_text(text)
print(f"总共切成了 {len(chunks)} 个片段")

这段代码会按字符数自动找标点符号来切,尽量保证意思的完整。

第三步:给每段文字做个"语义指纹"

把切好的每段文字都变成一个向量,方便后面的搜索。

python 复制代码
from sentence_transformers import SentenceTransformer
import numpy as np

# 选一个轻量级的模型,本地就能跑
model = SentenceTransformer("all-MiniLM-L6-v2")
embeddings = model.encode(chunks)  # 每一行就是一个向量
print(f"向量的形状: {embeddings.shape}")

如果你的电脑配置比较好,也可以用效果更棒的"bge-base-en-v1.5"等模型。

第四步:建一个"向量图书馆"

建一个向量数据库,把刚刚生成的"语义指纹"和原文都存进去。

python 复制代码
import faiss

dimension = embeddings.shape[1]
index = faiss.IndexFlatL2(dimension)  # 用欧氏距离来衡量向量间的相似度
index.add(np.array(embeddings))
print(f"向量库里现在有 {index.ntotal} 条记录")

第五步:提问,然后把资料喂给大模型

python 复制代码
from openai import OpenAI
client = OpenAI(api_key="你的API_KEY")

def ask(question):
    # 1. 先把问题也变成向量
    q_vec = model.encode([question])
    
    # 2. 从库里找到最像的3段
    D, I = index.search(np.array(q_vec), k=3)
    
    # 3. 把这些文字拼成"小抄"
    context = "\n\n".join([chunks[i] for i in I[0]])
    
    # 4. 做一个完整的提示词
    prompt = f"""请根据以下参考资料,用中文回答问题。如果资料里没有答案,就说不知道,不要自己编造。

参考资料:
{context}

问题:{question}
回答:"""
    
    # 5. 让大模型来回答
    response = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[{"role": "user", "content": prompt}]
    )
    return response.choices[0].message.content

print(ask("Python里的list和tuple有什么区别?"))

到这里,一个最简单的 RAG 知识库问答机器人就跑起来了!是不是没有想象中那么难?

四、我踩过的坑和一些心得

在捣鼓 RAG 的过程中,我也遇到过不少坑,分享几个重要的:

1. 切文本别太死板

刚开始我为了省事,直接把笔记按固定长度切了,结果有时候一段话被拦腰切断,意思都不完整了。后来我换成按段落切,或者让模型自己识别句子边界,效果好多了。

2. 回答的质量八成靠搜索

RAG 到底好不好用,八成看你能不能搜对东西。如果搜出来的都是驴唇不对马嘴的东西,大模型再聪明也是白搭。想要搜得更准,可以试试把关键词搜索和语义搜索结合起来,或者用重排序模型把搜出来的结果再筛一遍。

3. 别让它胡编,加上"拒绝机制"

在写提示词的时候,一定要明确告诉大模型:"如果资料里找不到答案,就说不知道,别瞎编。"这一步特别重要,能很大程度上减少"一本正经地胡说八道"的情况。

4. 从最简单的模型开始

不要一上来就想着用最强的模型,完全没必要。先用轻量的本地模型把流程跑通,遇到瓶颈了再去考虑升级。像"all-MiniLM-L6-v2"这个模型,在你的笔记本上就能跑,效果也还不错。

RAG 不是什么高深莫测的魔法,它更像是一个把大模型和你的知识库连接起来的"桥梁"。现在,你也可以试试,去搭一个真正属于你自己的 RAG 应用吧!

相关推荐
Lyyaoo.2 小时前
【JAVA基础面经】List(Vector+ArrayList+LinkedList)
java·开发语言·list
ch.ju2 小时前
Java程序设计(第3版)第二章——if if else else if
java
立莹Sir2 小时前
JVM深度解析与实战指南:从源码到生产环境优化
开发语言·jvm·python
SimonKing2 小时前
144K Star的开源神器,OpenCode进阶使用全攻略
java·后端·程序员
程序边界2 小时前
NFS环境下数据库安装报错解析(上篇):一个诡异的“权限门“事件
开发语言·数据库·php
froginwe112 小时前
Ruby 正则表达式
开发语言
砍材农夫2 小时前
spring-ai 第十二mcp server调用入门(http协议)
人工智能·spring·http
大数据魔法师2 小时前
AI Agent(三)- Ollama安装与使用
人工智能
程途知微2 小时前
Java线程池运行机制与拒绝策略底层全解析
java·后端