别让你的大模型一本正经胡说八道了!手把手教你搭一个 RAG 问答系统
摘要:大模型很强,但它有个致命的毛病——会「幻觉」。你问它一个它没学过的问题,它不会说「我不知道」,反而会一本正经地编一个答案给你。本文用最接地气的方式,带你搞懂 RAG(检索增强生成)的原理,并手把手用代码搭一个能「查资料再回答」的私有知识库问答系统。
一、先说个扎心的场景
假设你花大价钱接入了大模型 API,做了一个公司内部的「制度问答机器人」,结果同事问它:
「咱们公司的年假是怎么算的?」
它回答得头头是道、条理清晰,唯一的问题——全是编的。公司根本没有这个制度,它把网上看过的某家公司的规定安到了你头上。
这就是大模型的「幻觉」(Hallucination):模型不是故意骗你,而是它的知识截止到训练那一刻,训练完之后发生的事、你们公司内部的私密信息,它根本不知道,但它的机制又决定了它「必须给出一个像样的答案」。
于是,一本正经地胡说八道,就这么发生了。
二、RAG 到底是什么?
RAG,全称 Retrieval-Augmented Generation(检索增强生成)。别被英文名唬住,拆开看就三件事:
- Retrieval(检索):先从你的知识库里,把跟问题相关的内容「搜」出来。
- Augmented(增强):把这些搜出来的资料,作为「参考答案」塞进提示词里。
- Generation(生成):让大模型基于这些真实资料来回答,而不是凭空编。
一句话总结:让大模型「开卷考试」,而不是「闭卷瞎编」。
一张图看懂区别:
❌ 传统问答(闭卷): 用户问题 ──► 大模型 ──► 答案(可能瞎编)
✅ RAG 问答(开卷):
用户问题 ──► 检索 ──► 找到相关文档片段
│
▼
大模型 + 文档片段 ──► 答案(有据可查)
三、一个最小可跑的 RAG 系统
原理讲完了,下面直接上代码。我们用 Python + 几个常用库,搭一个能跑的最小系统。
3.1 准备数据:把文档切成小段
RAG 的第一步是把你的资料(Word、PDF、Markdown 都行)切成一小段一小段(chunk),方便检索时精准匹配。
# 把长文档切分成带重叠的小块
def split_text(text, chunk_size=500, overlap=50):
chunks = []
start = 0
while start < len(text):
end = start + chunk_size
chunks.append(text[start:end])
start = end - overlap # 保留一点重叠,避免句子被切断
return chunks
with open("公司制度.txt", "r", encoding="utf-8") as f:
doc = f.read()
chunks = split_text(doc)
print(f"文档被切成了 {len(chunks)} 个小段")
3.2 向量化:让机器「看懂」文字
计算机不能直接比两个句子的意思,所以我们要把文字转成「向量」(一串数字),意思相近的句子,向量在空间里也离得近。这一步叫 Embedding。
from sentence_transformers import SentenceTransformer
# 加载一个中文友好的向量模型
model = SentenceTransformer("BAAI/bge-small-zh-v1.5")
# 把所有小段转成向量
embeddings = model.encode(chunks, normalize_embeddings=True)
print("向量化完成,维度:", embeddings.shape)
3.3 建索引:存起来方便查
向量存哪?最简单的是用 FAISS,一个向量检索库,几行代码搞定:
import faiss
import numpy as np
dim = embeddings.shape[1]
index = faiss.IndexFlatIP(dim) # 内积相似度
index.add(embeddings.astype("float32"))
print(f"索引已建好,共 {index.ntotal} 条")
3.4 检索 + 生成:把答案「拼」出来
最后一步,用户提问 → 检索最相关的几段 → 塞给大模型回答:
def rag_answer(question, top_k=3):
# 1. 把问题也转成向量
q_vec = model.encode([question], normalize_embeddings=True)
# 2. 检索最相关的 top_k 个文档片段
scores, idxs = index.search(q_vec.astype("float32"), top_k)
retrieved = [chunks[i] for i in idxs[0]]
# 3. 拼成提示词,交给大模型
context = "\n\n".join(retrieved)
prompt = f"""请根据以下资料回答问题,如果资料中没有相关信息,请直接回答"资料中未找到相关内容"。
【资料】
{context}
【问题】
{question}
【回答】"""
# 这里调用你的大模型(以华为云盘古 / 其他大模型 API 为例)
answer = call_llm(prompt) # 替换成你的实际调用逻辑
return answer
print(rag_answer("公司年假是怎么算的?"))
到这里,一个最小可用的 RAG 问答系统就搭起来了。核心逻辑不过几十行代码。
四、几个避坑建议
RAG 虽然思路简单,但真正落地时,有几个坑要提前想清楚:
| 坑 | 现象 | 建议 |
|---|---|---|
| 切分太随意 | 检索出来的片段断章取义 | 按语义切分,保留重叠,控制 chunk 大小 |
| 向量模型选错 | 检索结果驴唇不对马嘴 | 中文场景优先选 bge、m3e 等中文模型 |
| 只检索一段 | 答案不完整 | 多召回几段(top_k=3~5)再让模型综合 |
| 模型不认资料 | 还是继续瞎编 | 提示词里明确写「资料中没有就直说」 |
| 知识库更新 | 新资料查不到 | 定期重建索引,或做增量更新 |
五、进阶方向(附华为云方案)
如果你的场景变复杂了,手动拼向量索引会越来越吃力。这时可以考虑用现成的平台能力,比如华为云的 ModelArts / MaaS 就提供了一站式的 RAG 与知识库服务,把「切分、向量化、检索、大模型调用」整条链路都封装好了,你只需上传文档、配置大模型即可,省去大量工程细节。
当然,不管用自建还是平台,RAG 的核心思想是不变的:让模型在有依据的前提下回答,这是把大模型从「玩具」变成「可靠生产工具」的关键一步。
六、写在最后
大模型的幻觉问题,短期内很难根治。但 RAG 给了我们一条非常务实的路:既然它记不住,就每次现查现答。
希望这篇能帮你把「一本正经胡说八道」的模型,变成「有据可查」的靠谱助手。如果你在搭建过程中遇到问题,欢迎在评论区交流,我们一起把它调好。
标签:#AI #大模型 #RAG #检索增强生成 #知识库 #ModelArts
- 点赞
- 收藏
- 关注作者
评论(0)