RAG学习
RAG
一、传统大模型的核心痛点
原生大语言模型依靠预训练固化参数生成内容,存在天然的落地瓶颈,也是企业无法直接商用的核心原因,主要包含三大致命问题:
-
知识存在时间截止,无法获取实时信息
大模型的所有知识都冻结在训练数据集的截止时间,对于后续新增的行业政策、产品迭代信息、最新技术文档、时事数据完全未知,无法适配动态更新的业务场景。 -
存在严重的“模型幻觉”,容易捏造事实
模型生成内容基于概率预测,并非基于真实事实推导。在知识模糊、细节缺失的场景中,会凭空编造数据、案例、条款和专业内容,输出看似通顺但完全错误的答案,无法用于严谨的企业工作场景。 -
无法触达企业私有知识,不支持私有化业务问答
通用大模型仅具备公开互联网知识,无法主动学习和调用企业内部私有数据。包括公司制度、项目资料、技术手册、业务流程、产品台账、内部培训文档等私有核心知识,通用模型完全无法识别和解答,无法适配企业专属业务需求。
以上三大问题,导致纯大模型无法直接落地企业场景,而 RAG(检索增强生成) 就是解决这些问题的最优工程方案。
二、什么是 RAG
RAG 全称 Retrieval-Augmented Generation,检索增强生成,是当前大模型企业落地的主流、低成本核心技术。
其核心思想非常简单:不改模型、不做训练、外挂知识库。
不让大模型凭空瞎想,而是在回答用户问题之前,先从企业私有知识库、业务文档库中检索真实、准确的相关资料,把检索原文和用户问题一起送入大模型,强制模型依据真实资料生成答案。
核心公式:
用户提问 + 私有知识库检索结果 → 大模型 → 精准、无幻觉、可溯源答案
三、RAG 的核心价值
1. 彻底解决模型幻觉
所有输出内容均来自企业自有文档,有据可依、可溯源,杜绝编造内容,满足办公、技术、法务、售后等严谨场景。
2. 支持实时知识更新
新增业务文档、更新产品手册、修改制度流程,只需更新知识库,无需重新训练模型,迭代效率极高。
3. 解锁企业私有知识能力
让通用大模型适配企业专属业务,读懂内部文档、解答内部问题,实现企业知识数字化问答。
4. 数据安全可控
私有文档仅用于检索,不上传公共大模型训练,企业核心数据不会外泄。
5. 落地成本极低
对比模型微调、模型自研,RAG 属于轻量化工程方案,周期短、费用低、适合绝大多数中小企业落地。
四、标准 RAG 完整流水线
RAG 整体分为 离线建库 和 在线问答 两大阶段。
阶段一:离线文档预处理(一次性/周期性执行)
1. 多源数据接入
支持 PDF、Word、Excel、数据库、网页、Markdown、企业业务台账等所有常见企业文档格式。
2. 文本清洗
过滤页眉页脚、水印、乱码、空行、无效符号、冗余格式,保证素材纯净。
3. 文本分块 Chunking
将超长文档切分为固定长度文本块,规避超长文本向量精度低、短文本信息不全的问题,是影响检索精度的核心步骤。
4. Embedding 向量化
通过嵌入模型,将自然语言文本转化为高维数字向量,让机器可以理解语义相似度。
5. 向量入库存储
将向量 + 原始文本存入向量数据库,常用库:Milvus、FAISS、Chroma、PGVector。
阶段二:在线实时问答(用户触发)
1. 问题向量化
将用户的自然语言提问转为向量。
2. 相似度检索召回
在向量库中匹配语义最接近的 Top-N 文本片段。
3. Prompt 上下文组装
将检索内容和用户问题整合,加入约束指令,限制模型只能依据参考资料回答。
4. LLM 生成答案
大模型基于真实文档素材,输出精准答案。
五、基础 RAG 的固有缺陷
基础简易版 RAG 只能实现简单问答,在复杂企业场景存在明显短板:
1. 仅靠向量相似度匹配,容易召回语义相近但无关的无效内容;
2. 文档分块导致上下文断裂,完整逻辑被拆分,答案不完整;
3. 无筛选机制,冗余文本过多,占用上下文窗口;
4. 无法处理模糊问句、多意图问句,检索成功率低;
5. 不支持表格、结构化数据、业务数据库查询。
六、高阶优化 RAG(企业生产级方案)
生产环境中,必须叠加优化策略,构成高级 RAG:
1. 混合检索:向量语义检索 + 关键词检索,兼顾语义理解与精准匹配。
2. Rerank 重排序:初筛后二次精准打分,剔除低质量检索片段。
3. Query 问句改写:自动修复模糊提问、拆分多意图问题,提升召回率。
4. 父文档检索:检索小块、回答用全文,解决上下文断裂问题。
5. 滑动窗口分块:块与块之间保留重叠文本,保证语义连贯。
6. 自适应迭代检索:信息不足时自动二次检索,补足信息再作答。
7. 结构化 RAG:支持表格、数据库、SQL 查询,适配业务数据问答。
七、RAG 与微调(Fine-Tune)核心区别
1. RAG
外挂知识库、不改动模型权重、知识可实时更新、主打答案准确、无幻觉、适配私有数据。适合企业知识库问答、业务咨询、文档答疑。
2. 微调
修改模型参数、固化回答风格、统一话术、学习特定指令。无法实时更新知识,成本高、周期长。适合标准化输出、语气风格统一、指令对齐。
3. 最佳工程组合:RAG 保证事实准确,微调保证输出格式统一。
八、企业主流落地场景
1. 企业内部知识库问答:制度、流程、技术文档、项目资料智能问答;
2. 智能售后/客服:自动调取产品手册、故障方案、售后话术;
3. 行业专业问答:法律条文、医疗资料、汽车手册、工业规范解析;
4. 政企数字化办公:合同审查、资料检索、公文解读;
5. 智能座舱/智能硬件:本地私有手册、设备参数、功能教程问答。
九、总结
RAG 是大模型从通用娱乐走向企业商用的核心技术。它完美解决了传统大模型知识滞后、幻觉严重、无法读取企业私有知识的三大核心痛点。
基础 RAG 可以快速搭建文档问答原型,高阶 RAG 通过检索优化、重排序、迭代查询,实现生产级精准问答。在当前 AI 工程体系中,RAG 是成本最低、落地最快、实用性最强的企业 AI 应用方案。RAG 技术完整综述(通俗易懂·工程落地版)
RAG 全流程 Python 实战(分步拆解:背景+问题+方案+难点+代码)
整体链路总览:
离线构建知识库:文档加载 → 文本清洗 → 文本分块 → 文本向量化 → 向量库入库
在线问答链路:问题向量化 → 相似度检索 → 构造约束Prompt → LLM生成回答
环境依赖
python
安装依赖
pip install langchain langchain-openai chromadb python-dotenv pypdf
步骤1:文档加载(数据接入层)
1.1 业务背景
企业RAG的数据源包含PDF、Word、TXT、网页等非结构化文档,必须把文件读取为纯文本字符串,才能进入后续处理。
1.2 存在问题
1. PDF自带乱码、图片内容无法提取;
2. 不同文件格式需要编写不同读取逻辑,代码冗余;
3. 直接读取会混入页眉、页脚、水印、空白行等无效内容。
1.3 解决方案
使用LangChain封装好的文档加载器,统一多格式文件读取;后续统一增加文本清洗逻辑。
1.4 实现难点
- 扫描版PDF(图片PDF)无法直接提取文字,需要额外接入OCR;
- 长文件一次性读入会占用大量内存。
1.5 代码实现
python
from langchain.document_loaders import PyPDFLoader
加载PDF文档
loader = PyPDFLoader("企业内部技术手册.pdf")
documents = loader.load()
documents:列表,每一页为一个Document对象
print(f"一共读取页数:{len(documents)}")
步骤2:文本清洗(预处理第一层)
2.1 业务背景
原始文档中包含大量无用字符,会干扰后续分块与向量相似度计算,降低检索精度。
2.2 存在问题
1. 多余换行、空格、特殊符号;
2. 页眉页脚重复文字反复出现;
3. 分段杂乱,语句被无故截断。
2.3 解决方案
使用正则表达式过滤无效字符,合并连续换行,剔除空白文档。
2.4 实现难点
无法全自动区分页眉页脚,纯文本模式只能做通用清洗;复杂文档需要配合布局解析。
2.5 代码实现
python
import re
def clean_text(text: str) -> str:
# 去除多余换行、空格
text = re.sub(r"\n+", "\n", text)
text = re.sub(r"\s+", " ", text)
# 剔除特殊不可见字符
text = re.sub(r"[^\u4e00-\u9fa5a-zA-Z0-9.,;:!?,。;:?!]", "", text)
return text.strip()
批量清洗文档内容
for doc in documents:
doc.page_content = clean_text(doc.page_content)
步骤3:文本分块 Chunking(最核心预处理环节)
3.1 业务背景
Embedding模型存在单次输入长度限制;整份长文档直接向量化,语义混杂,相似度匹配会严重失准,必须切分成小段文本块。
3.2 存在问题
1. 块太长:上下文冗余,向量语义混杂,容易检索出无关内容;
2. 块太短:一句话被强行切断,语义不完整;
3. 关键上下文被切分到两个不同块,检索时只能命中一半信息;
4. 句子中间被生硬截断,破坏语义完整性。
3.3 解决方案
1. 使用递归字符分块器,优先按段落、换行、句号拆分,保证句子完整;
2. 设置块重叠(Overlap),相邻文本块保留一部分重复内容,避免上下文断裂;
3. 经验参数:块大小500800字符,重叠10%20%。
3.4 实现难点
- 没有通用万能参数,不同行业文档(技术文档/合同/聊天记录)需要反复调优块大小;
- 严格按照句子边界切割会大幅增加代码复杂度。
3.5 代码实现
python
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=600, # 单块文字长度
chunk_overlap=120, # 块之间重叠字符,防止上下文断裂
separators=["\n\n", "\n", "。", ",", " "]
)
执行切分
split_docs = text_splitter.split_documents(documents)
print(f"切分后文本块总数:{len(split_docs)}")
步骤4:文本向量化 Embedding
4.1 业务背景
向量数据库无法直接理解自然文字,必须把文字转换成高维浮点向量,依靠向量距离计算语义相似度。
4.2 存在问题
1. 中英文嵌入模型精度差异大,英文模型处理中文效果极差;
2. 长文本向量语义稀释,相似度计算不准;
3. 调用API产生大量费用,批量向量化速度慢;
4. 向量维度不统一,无法存入同一个向量库。
4.3 解决方案
1. 选用专门面向中文的Embedding模型(如text-embedding-ada-002 / 开源bge-embedding);
2. 严格控制chunk长度,避免超长文本嵌入;
3. 批量调用接口减少网络请求次数。
4.4 实现难点
- 开源本地Embedding需要显卡算力;调用OpenAI接口会有额度与限流;
- 不同嵌入模型的向量空间不互通,更换模型必须全量重新向量化。
4.5 代码实现
python
from langchain_openai import OpenAIEmbeddings
from dotenv import load_dotenv
import os
load_dotenv()
初始化嵌入模型
embedding = OpenAIEmbeddings(
model="text-embedding-ada-002",
openai_api_key=os.getenv("OPENAI_API_KEY")
)
步骤5:向量持久化,存入向量数据库(离线建库收尾)
5.1 业务背景
文本向量需要持久存储,在线提问时才能快速完成近邻搜索,不能每次临时计算相似度。
5.2 存在问题
1. 普通关系型数据库无法高效完成百万级向量相似度检索;
2. 向量数据量大,内存数据库重启后数据丢失;
3. 向量+原文需要绑定存储,检索到向量同时拿到对应文本片段。
5.3 解决方案
使用轻量级本地向量库Chroma,无需额外部署服务,直接文件持久化;生产环境替换为Milvus、PGVector。
5.4 实现难点
- 向量库没有统一的索引策略,数据量达到百万级后检索速度急剧下降;
- 增量更新知识库时,容易产生重复向量垃圾数据。
5.5 代码实现
python
from langchain.vectorstores import Chroma
持久化路径
persist_dir = "./chroma_db"
将文本块+向量存入向量库
db = Chroma.from_documents(
documents=split_docs,
embedding=embedding,
persist_directory=persist_dir
)
db.persist()
print("向量库构建完成并持久化到本地")
至此:离线知识库构建全部完成。
步骤6:在线阶段——用户问题向量检索
6.1 业务背景
用户输入问题后,先把问句转为向量,在库中召回语义最相近的文档片段,作为大模型的参考资料。
6.2 存在问题
1. 单纯语义检索容易召回字面上不相关、但向量近似的无关文本;
2. 召回条数过多,塞满模型上下文窗口;条数太少,信息不足;
3. 检索结果没有相关性排序,劣质文本排在前面。
6.3 解决方案
1. 限制召回数量(Top-K,一般取3~5条);
2. 进阶方案:增加关键词混合检索 + Rerank重排序过滤无效片段;
3. 加载已持久化的向量库,不用重复建库。
6.4 实现难点
相似度阈值很难固定,业务场景不同,得分阈值需要反复调试;纯向量检索无法精准匹配专有名词。
6.5 代码实现
python
加载已经建好的向量库
db = Chroma(persist_directory=persist_dir, embedding_function=embedding)
相似度检索
query = "自动驾驶L2功能使用限制"
retrieved_docs = db.similarity_search(query, k=4)
拼接检索到的参考文档上下文
context_text = "\n\n".join([doc.page_content for doc in retrieved_docs])
步骤7:构造约束Prompt(防止大模型幻觉)
7.1 业务背景
如果直接把上下文丢给大模型,模型依然会自由发挥、编造内容,必须用指令严格限制生成边界。
7.2 存在问题
Prompt约束太弱,模型脱离材料自由拓展;约束太强,语句过于死板,回答不通顺。
7.3 解决方案
编写严格事实约束模板:仅依托参考资料作答,无信息则如实告知,禁止编造。
7.4 实现难点
不同大模型对指令的遵守程度不一致,开源模型更容易突破约束,需要配合格式限制。
7.5 代码实现
python
prompt_template = """
你只能严格依据下面给出的参考资料回答用户问题,绝对不允许编造内容。
如果参考资料里没有对应信息,直接回复:暂无相关资料,不要自行拓展。
【参考资料】
【用户问题】
{question}
"""
prompt = prompt_template.format(context=context_text, question=query)
print(prompt)
步骤8:调用大模型生成最终答案
8.1 业务背景
把带参考资料的完整Prompt送入LLM,生成可溯源、无幻觉的回答,完成整条RAG链路。
8.2 存在问题
1. 上下文过长触发窗口超限,内容被截断;
2. 参考资料互相冲突时,模型会混淆信息;
3. 回答只摘抄原文,无法提炼总结。
8.3 解决方案
1. 控制检索文本总长度,适配模型上下文窗口;
2. 优化分块逻辑,减少互相矛盾的片段;
3. 在Prompt中增加提炼总结要求。
8.4 实现难点
长上下文场景下,小参数量开源模型容易丢失前面的约束指令。
8.5 代码实现
python
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model_name="gpt-3.5-turbo",
temperature=0, # temperature=0,最大限度杜绝随机编造
openai_api_key=os.getenv("OPENAI_API_KEY")
)
response = llm.invoke(prompt)
print("最终回答:")
print(response.content)
完整串联总代码(一键运行)
python
===== 依赖导入 =====
import re
import os
from dotenv import load_dotenv
from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain.vectorstores import Chroma
load_dotenv()
========== 第一阶段:离线构建知识库 ==========
1.文档加载
loader = PyPDFLoader("企业内部技术手册.pdf")
documents = loader.load()
2.文本清洗
def clean_text(text: str) -> str:
text = re.sub(r"\n+", "\n", text)
text = re.sub(r"\s+", " ", text)
text = re.sub(r"[^\u4e00-\u9fa5a-zA-Z0-9.,;:!?,。;:?!]", "", text)
return text.strip()
for doc in documents:
doc.page_content = clean_text(doc.page_content)
3.文本分块
splitter = RecursiveCharacterTextSplitter(
chunk_size=600,
chunk_overlap=120,
separators=["\n\n", "\n", "。", ","]
)
split_docs = splitter.split_documents(documents)
4.向量化+入库
embedding = OpenAIEmbeddings(model="text-embedding-ada-002")
db = Chroma.from_documents(
documents=split_docs,
embedding=embedding,
persist_directory="./chroma_db"
)
db.persist()
========== 第二阶段:在线问答RAG推理 ==========
1.加载向量库,执行检索
db = Chroma(persist_directory="./chroma_db", embedding_function=embedding)
user_query = "自动驾驶L2功能使用限制"
docs = db.similarity_search(user_query, k=4)
context = "\n\n".join([d.page_content for d in docs])
2.构造严格防幻觉Prompt
prompt = f"""
你只能严格依据下面给出的参考资料回答用户问题,绝对不允许编造内容。
如果参考资料里没有对应信息,直接回复:暂无相关资料。
【参考资料】
【问题】
{user_query}
"""
3.调用大模型输出答案
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
ans = llm.invoke(prompt)
print(ans.content)
补充:全链路难点汇总总结
环节 核心痛点 优化方案
文档加载 扫描PDF无文字、格式杂乱 接入OCR;区分结构化/非结构化文档
文本清洗 页眉页脚重复冗余 布局分析+正则过滤,人工制定过滤规则
文本分块 语义被切断、块大小难以调参 递归分块+重叠窗口;针对文档类型定制chunk参数
Embedding 中文精度差、计费成本高 换中文专用Embedding;批量请求;本地部署开源模型
向量检索 召回内容相关性低、专有名词匹配弱 混合检索(向量+全文关键词)+Rerank重排序
Prompt约束 模型突破限制产生幻觉 temperature置0,增加事实校验,强指令约束
上下文窗口 参考文本超长被截断 控制Top-K数量,精简检索片段
如果你需要,我可以再给你升级一版:高级RAG(Rerank+问句改写+父文档检索)Python代码,同样分步拆解。RAG 全流程 Python 实战(分步拆解:背景+问题+方案+难点+代码)
整体链路总览:
离线构建知识库:文档加载 → 文本清洗 → 文本分块 → 文本向量化 → 向量库入库
在线问答链路:问题向量化 → 相似度检索 → 构造约束Prompt → LLM生成回答
环境依赖
python
安装依赖
pip install langchain langchain-openai chromadb python-dotenv pypdf
步骤1:文档加载(数据接入层)
1.1 业务背景
企业RAG的数据源包含PDF、Word、TXT、网页等非结构化文档,必须把文件读取为纯文本字符串,才能进入后续处理。
1.2 存在问题
1. PDF自带乱码、图片内容无法提取;
2. 不同文件格式需要编写不同读取逻辑,代码冗余;
3. 直接读取会混入页眉、页脚、水印、空白行等无效内容。
1.3 解决方案
使用LangChain封装好的文档加载器,统一多格式文件读取;后续统一增加文本清洗逻辑。
1.4 实现难点
- 扫描版PDF(图片PDF)无法直接提取文字,需要额外接入OCR;
- 长文件一次性读入会占用大量内存。
1.5 代码实现
python
from langchain.document_loaders import PyPDFLoader
加载PDF文档
loader = PyPDFLoader("企业内部技术手册.pdf")
documents = loader.load()
documents:列表,每一页为一个Document对象
print(f"一共读取页数:{len(documents)}")
步骤2:文本清洗(预处理第一层)
2.1 业务背景
原始文档中包含大量无用字符,会干扰后续分块与向量相似度计算,降低检索精度。
2.2 存在问题
1. 多余换行、空格、特殊符号;
2. 页眉页脚重复文字反复出现;
3. 分段杂乱,语句被无故截断。
2.3 解决方案
使用正则表达式过滤无效字符,合并连续换行,剔除空白文档。
2.4 实现难点
无法全自动区分页眉页脚,纯文本模式只能做通用清洗;复杂文档需要配合布局解析。
2.5 代码实现
python
import re
def clean_text(text: str) -> str:
# 去除多余换行、空格
text = re.sub(r"\n+", "\n", text)
text = re.sub(r"\s+", " ", text)
# 剔除特殊不可见字符
text = re.sub(r"[^\u4e00-\u9fa5a-zA-Z0-9.,;:!?,。;:?!]", "", text)
return text.strip()
批量清洗文档内容
for doc in documents:
doc.page_content = clean_text(doc.page_content)
步骤3:文本分块 Chunking(最核心预处理环节)
3.1 业务背景
Embedding模型存在单次输入长度限制;整份长文档直接向量化,语义混杂,相似度匹配会严重失准,必须切分成小段文本块。
3.2 存在问题
1. 块太长:上下文冗余,向量语义混杂,容易检索出无关内容;
2. 块太短:一句话被强行切断,语义不完整;
3. 关键上下文被切分到两个不同块,检索时只能命中一半信息;
4. 句子中间被生硬截断,破坏语义完整性。
3.3 解决方案
1. 使用递归字符分块器,优先按段落、换行、句号拆分,保证句子完整;
2. 设置块重叠(Overlap),相邻文本块保留一部分重复内容,避免上下文断裂;
3. 经验参数:块大小500800字符,重叠10%20%。
3.4 实现难点
- 没有通用万能参数,不同行业文档(技术文档/合同/聊天记录)需要反复调优块大小;
- 严格按照句子边界切割会大幅增加代码复杂度。
3.5 代码实现
python
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=600, # 单块文字长度
chunk_overlap=120, # 块之间重叠字符,防止上下文断裂
separators=["\n\n", "\n", "。", ",", " "]
)
执行切分
split_docs = text_splitter.split_documents(documents)
print(f"切分后文本块总数:{len(split_docs)}")
步骤4:文本向量化 Embedding
4.1 业务背景
向量数据库无法直接理解自然文字,必须把文字转换成高维浮点向量,依靠向量距离计算语义相似度。
4.2 存在问题
1. 中英文嵌入模型精度差异大,英文模型处理中文效果极差;
2. 长文本向量语义稀释,相似度计算不准;
3. 调用API产生大量费用,批量向量化速度慢;
4. 向量维度不统一,无法存入同一个向量库。
4.3 解决方案
1. 选用专门面向中文的Embedding模型(如text-embedding-ada-002 / 开源bge-embedding);
2. 严格控制chunk长度,避免超长文本嵌入;
3. 批量调用接口减少网络请求次数。
4.4 实现难点
- 开源本地Embedding需要显卡算力;调用OpenAI接口会有额度与限流;
- 不同嵌入模型的向量空间不互通,更换模型必须全量重新向量化。
4.5 代码实现
python
from langchain_openai import OpenAIEmbeddings
from dotenv import load_dotenv
import os
load_dotenv()
初始化嵌入模型
embedding = OpenAIEmbeddings(
model="text-embedding-ada-002",
openai_api_key=os.getenv("OPENAI_API_KEY")
)
步骤5:向量持久化,存入向量数据库(离线建库收尾)
5.1 业务背景
文本向量需要持久存储,在线提问时才能快速完成近邻搜索,不能每次临时计算相似度。
5.2 存在问题
1. 普通关系型数据库无法高效完成百万级向量相似度检索;
2. 向量数据量大,内存数据库重启后数据丢失;
3. 向量+原文需要绑定存储,检索到向量同时拿到对应文本片段。
5.3 解决方案
使用轻量级本地向量库Chroma,无需额外部署服务,直接文件持久化;生产环境替换为Milvus、PGVector。
5.4 实现难点
- 向量库没有统一的索引策略,数据量达到百万级后检索速度急剧下降;
- 增量更新知识库时,容易产生重复向量垃圾数据。
5.5 代码实现
python
from langchain.vectorstores import Chroma
持久化路径
persist_dir = "./chroma_db"
将文本块+向量存入向量库
db = Chroma.from_documents(
documents=split_docs,
embedding=embedding,
persist_directory=persist_dir
)
db.persist()
print("向量库构建完成并持久化到本地")
至此:离线知识库构建全部完成。
步骤6:在线阶段——用户问题向量检索
6.1 业务背景
用户输入问题后,先把问句转为向量,在库中召回语义最相近的文档片段,作为大模型的参考资料。
6.2 存在问题
1. 单纯语义检索容易召回字面上不相关、但向量近似的无关文本;
2. 召回条数过多,塞满模型上下文窗口;条数太少,信息不足;
3. 检索结果没有相关性排序,劣质文本排在前面。
6.3 解决方案
1. 限制召回数量(Top-K,一般取3~5条);
2. 进阶方案:增加关键词混合检索 + Rerank重排序过滤无效片段;
3. 加载已持久化的向量库,不用重复建库。
6.4 实现难点
相似度阈值很难固定,业务场景不同,得分阈值需要反复调试;纯向量检索无法精准匹配专有名词。
6.5 代码实现
python
加载已经建好的向量库
db = Chroma(persist_directory=persist_dir, embedding_function=embedding)
相似度检索
query = "自动驾驶L2功能使用限制"
retrieved_docs = db.similarity_search(query, k=4)
拼接检索到的参考文档上下文
context_text = "\n\n".join([doc.page_content for doc in retrieved_docs])
步骤7:构造约束Prompt(防止大模型幻觉)
7.1 业务背景
如果直接把上下文丢给大模型,模型依然会自由发挥、编造内容,必须用指令严格限制生成边界。
7.2 存在问题
Prompt约束太弱,模型脱离材料自由拓展;约束太强,语句过于死板,回答不通顺。
7.3 解决方案
编写严格事实约束模板:仅依托参考资料作答,无信息则如实告知,禁止编造。
7.4 实现难点
不同大模型对指令的遵守程度不一致,开源模型更容易突破约束,需要配合格式限制。
7.5 代码实现
python
prompt_template = """
你只能严格依据下面给出的参考资料回答用户问题,绝对不允许编造内容。
如果参考资料里没有对应信息,直接回复:暂无相关资料,不要自行拓展。
【参考资料】
【用户问题】
{question}
"""
prompt = prompt_template.format(context=context_text, question=query)
print(prompt)
步骤8:调用大模型生成最终答案
8.1 业务背景
把带参考资料的完整Prompt送入LLM,生成可溯源、无幻觉的回答,完成整条RAG链路。
8.2 存在问题
1. 上下文过长触发窗口超限,内容被截断;
2. 参考资料互相冲突时,模型会混淆信息;
3. 回答只摘抄原文,无法提炼总结。
8.3 解决方案
1. 控制检索文本总长度,适配模型上下文窗口;
2. 优化分块逻辑,减少互相矛盾的片段;
3. 在Prompt中增加提炼总结要求。
8.4 实现难点
长上下文场景下,小参数量开源模型容易丢失前面的约束指令。
8.5 代码实现
python
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model_name="gpt-3.5-turbo",
temperature=0, # temperature=0,最大限度杜绝随机编造
openai_api_key=os.getenv("OPENAI_API_KEY")
)
response = llm.invoke(prompt)
print("最终回答:")
print(response.content)
完整串联总代码(一键运行)
python
===== 依赖导入 =====
import re
import os
from dotenv import load_dotenv
from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain.vectorstores import Chroma
load_dotenv()
========== 第一阶段:离线构建知识库 ==========
1.文档加载
loader = PyPDFLoader("企业内部技术手册.pdf")
documents = loader.load()
2.文本清洗
def clean_text(text: str) -> str:
text = re.sub(r"\n+", "\n", text)
text = re.sub(r"\s+", " ", text)
text = re.sub(r"[^\u4e00-\u9fa5a-zA-Z0-9.,;:!?,。;:?!]", "", text)
return text.strip()
for doc in documents:
doc.page_content = clean_text(doc.page_content)
3.文本分块
splitter = RecursiveCharacterTextSplitter(
chunk_size=600,
chunk_overlap=120,
separators=["\n\n", "\n", "。", ","]
)
split_docs = splitter.split_documents(documents)
4.向量化+入库
embedding = OpenAIEmbeddings(model="text-embedding-ada-002")
db = Chroma.from_documents(
documents=split_docs,
embedding=embedding,
persist_directory="./chroma_db"
)
db.persist()
========== 第二阶段:在线问答RAG推理 ==========
1.加载向量库,执行检索
db = Chroma(persist_directory="./chroma_db", embedding_function=embedding)
user_query = "自动驾驶L2功能使用限制"
docs = db.similarity_search(user_query, k=4)
context = "\n\n".join([d.page_content for d in docs])
2.构造严格防幻觉Prompt
prompt = f"""
你只能严格依据下面给出的参考资料回答用户问题,绝对不允许编造内容。
如果参考资料里没有对应信息,直接回复:暂无相关资料。
【参考资料】
【问题】
{user_query}
"""
3.调用大模型输出答案
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
ans = llm.invoke(prompt)
print(ans.content)
补充:全链路难点汇总总结
环节 核心痛点 优化方案
文档加载 扫描PDF无文字、格式杂乱 接入OCR;区分结构化/非结构化文档
文本清洗 页眉页脚重复冗余 布局分析+正则过滤,人工制定过滤规则
文本分块 语义被切断、块大小难以调参 递归分块+重叠窗口;针对文档类型定制chunk参数
Embedding 中文精度差、计费成本高 换中文专用Embedding;批量请求;本地部署开源模型
向量检索 召回内容相关性低、专有名词匹配弱 混合检索(向量+全文关键词)+Rerank重排序
Prompt约束 模型突破限制产生幻觉 temperature置0,增加事实校验,强指令约束
上下文窗口 参考文本超长被截断 控制Top-K数量,精简检索片段
如果你需要,我可以再给你升级一版:高级RAG(Rerank+问句改写+父文档检索)Python代码,同样分步拆解。
浙公网安备 33010602011771号