RAG 入门第一课:从零构建你的第一个“知识库”
什么是 RAG?
想象一下,你正在参加一场开卷考试。
- 大模型(LLM)是你,博学多才,但可能记不清具体的细节(比如“公司规定年假到底有几天?”)。
- RAG就是允许你在考试前,先把课本(公司手册、文档)复印一份,整理成一本**“速查笔记”**。
- 当你遇到不会的问题时,你先去翻这本“速查笔记”,找到答案,然后结合自己的知识回答。
RAG 的核心流程就是:数据准备 -> 存入知识库 -> 遇到问题时检索 -> 生成答案。
核心组件:我们的“三件套”
为了构建这个系统,我们使用了业界最标准的 Python 组合:
- 大脑(Embedding 模型):
sentence-transformers- 作用:翻译官。把人类的文字(如“怎么请假”)翻译成计算机能理解的数字列表(向量)。
- 仓库(向量数据库):
chromadb- 作用:图书管理员。负责存储这些数字,并能以毫秒级的速度找到“意思最相近”的内容。
- 胶水(Python 代码):
- 作用:包工头。把数据切分、清洗,然后指挥模型和数据库工作。
实战代码:构建完整的 RAG 闭环
以下是我们今天编写的核心代码逻辑,涵盖了从“存”到“取”的全过程。
# 安装向量数据库 ChromaDB pip install chromadb # 安装嵌入模型库 sentence-transformers pip install sentence-transformers # 安装数据处理常用的 numpy pip install numpy第一步:准备数据(切分)
计算机不喜欢处理几万字的大文章,我们需要把它切成小块。
# 模拟一本 500 页的“员工手册”raw_text=""" 公司规定,员工每年有 10 天带薪年假。 申请年假需要提前 3 天在 OA 系统提交。 另外,病假每年有 5 天,不需要扣工资,但需要提供医院证明。 """# 简单的切分逻辑:按句号切分chunks=[chunk.strip()forchunkinraw_text.split("。")ifchunk.strip()]第二步:初始化模型与数据库
这里我们加载了“翻译官”和“仓库”。
importos# 关键配置:使用国内镜像源,解决下载慢的问题os.environ['HF_ENDPOINT']='https://hf-mirror.com'importchromadbfromsentence_transformersimportSentenceTransformer# 加载更强、更准的中文模型(这是关键!)model=SentenceTransformer('all-MiniLM-L6-v2')# 2. 初始化数据库(仓库)client=chromadb.PersistentClient(path="./my_rag_db")collection=client.get_or_create_collection(name="employee_handbook")第三步:数据入库(索引)
这是 RAG 的“记忆”阶段。
# 准备元数据和 IDmetadatas=[{"category":"假期"}for_inchunks]ids=[f"id_{i}"foriinrange(len(chunks))]embeddings=model.encode(chunks).tolist()# 直接把整个列表丢进去print("正在存入数据...")collection.add(embeddings=embeddings,documents=chunks,# 原文内容metadatas=metadatas,# 标签ids=ids# 唯一编号)print("入库完成!")使用Chroma 默认的 embedding 模型(all-MiniLM-L6-v2(极小、英文为主、轻量化模型))
# 用户提问 query ="年假怎么申请?"# 检索:使用chromadb的内置文本查询功能 results = collection.query(query_texts=[query],n_results=1 # 只要最相关的一条)# 提取找到的上下文 context = results['documents'][0][0]print(context),入库 和 查询 必须使用完全相同的 embedding 模型
如果不指定embedding 模型,会使用Chroma 默认的 embedding 模型(all-MiniLM-L6-v2(极小、英文为主、轻量化模型)),
**对比:**但是chroma 默认的 embedding 模型,只能做玩具 Demo,不能做真实项目,
外接的 sentence-transformers 模型,效果碾压默认模型 10 倍以上。
第四步:检索与生成(问答)
这是 RAG 的“思考”阶段,
# 1. 用户提问query="年假怎么申请?"# 2. 检索:把问题变成向量,去数据库找最相似的片段query_embedding=model.encode([query]).tolist()results=collection.query(query_embeddings=query_embedding,n_results=1# 只要最相关的一条)# 提取找到的上下文context=results['documents'][0][0]# 3. 生成:构造提示词,把“参考资料”和“问题”打包prompt=f""" 你是一个智能助手。请根据以下参考资料回答问题。 参考资料:{context}用户问题:{query}你的回答: """# 4. 发送给大模型(伪代码)# final_answer = llm.generate(prompt)# print(final_answer)避坑指南:环境配置的“九九八十一难”
- 网络墙问题:
- 报错:
ConnectionError或下载卡住。 - 原因:Hugging Face 官方源在国内无法访问。
- 解决:设置环境变量
os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'。
- 报错:
- 文件格式误区:
- 报错:
can't open file ... .ipynb。 - 原因:不能用
python xxx.ipynb命令运行 Notebook 文件。 - 解决:在 Jupyter 界面直接点击运行,或者将其转换为
.py文件。
- 报错:
- 离线模式陷阱:
- 报错:
Outgoing traffic has been disabled。 - 原因:设置了
TRANSFORMERS_OFFLINE=1但本地其实没有模型文件。 - 解决:第一次运行时不要开启离线模式,或者手动下载模型文件到本地。
- 报错:
总结
相当于将知识库向量化,然后放在存储仓库,然后当用户提问的时候,同样将问题向量化,根据相似向量度,去查询存储仓库,召回片段,把检索到的片段+用户问题一起喂给大模型生成最终答案
向量化知识库→入库→问题向量化→按相似度召回片段→把检索到的片段+用户问题一起喂给大模型生成最终答案