基于你已经学习完:
- 文档加载
- 文本分割
- 向量存储(Chroma)
- 语义检索
一、什么是 RAG?(超通俗解释)
RAG = Retrieval-Augmented Generation
检索增强生成
大白话:
- 大模型本身不知道你的文档内容
- 所以先去文档里检索相关内容
- 把检索到的内容喂给大模型
- 让大模型只根据文档内容回答
这就叫 RAG。
二、RAG 标准 5 步流程(必须背下来)
- 加载文档(PDF / TXT / Word)
- 文本分割(切成小块,保留语义)
- 向量化(小块 → 向量)
- 存入向量库(Chroma / FAISS)
- 用户提问 → 检索 → 给模型回答
三、RAG 为什么比直接问 LLM 强?
- LLM 会瞎编
- RAG 只根据文档回答,不会编
- LLM 不知道你的私有数据
- RAG 可以读取私有文档
- RAG 回答永远准确、可追溯
四、现在!我带你实现 最简单、最标准、可直接运行的 RAG 系统
只用你已经学过的知识:
- Document Loader
- Text Splitter
- Chroma 向量库
- Embedding
- LLM + Prompt
- 普通 Chain
五、完整可运行代码(极简纯净版 RAG)
1. 安装依赖
pip install -U langchain langchain-openai langchain-community pypdf chromadb python-dotenv
2. 完整代码(直接跑)
import os
from dotenv import load_dotenv
from langchain_community.document_loaders import PyPDFLoader
from langchain_community.vectorstores import Chroma
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
# 加载环境变量
load_dotenv()
# ================= 配置 ====================
PDF_PATH = "Dubbo面试.pdf"
CHROMA_PATH = "./chroma_dir"
# 智普模型
# llm = ChatOpenAI(
# api_key=os.getenv("QWEN_API_KEY"),
# base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
# model='qwen3-max', # 必须用 pro模型,支持工具调用
# temperature=0 # 必须0 保证不乱造
# )
llm = ChatOpenAI(
api_key=os.getenv("ZHIPU_API_KEY"),
base_url="https://open.bigmodel.cn/api/paas/v4",
model='glm-4.6', # 必须用 pro模型,支持工具调用
temperature=0 # 必须0 保证不乱造
)
# 嵌入模型 model="embedding-3", # 智谱嵌入模型
embedding = OpenAIEmbeddings(
openai_api_key=os.getenv("ZHIPU_API_KEY"), # 智谱API Key
openai_api_base="https://open.bigmodel.cn/api/paas/v4",
model="embedding-3"
)
# 加载PDF
loader = PyPDFLoader(PDF_PATH)
documents = loader.load()
# 文本分割
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
separators=["\n\n", "\n", "。", "!", "?", " "]
)
chunks = splitter.split_documents(documents)
cleaned = []
# 清空文档中的空 保留文章的完整性 文档里面有图片的话就会出现空
for chunk in chunks:
# 1. 必须有 page_content
if not hasattr(chunk, "page_content"):
continue
# 2. 内容不能为空
content = chunk.page_content.strip()
if len(content) == 0:
continue
# 3. 内容不能是奇怪的非字符串(防止通义报错)
if not isinstance(content, str):
continue
chunk.page_content = str(content)
cleaned.append(chunk)
# 构建向量库
vector_db = Chroma.from_documents(
documents = cleaned,
embedding = embedding,
persist_directory = CHROMA_PATH,
)
# 重复持久化 过时了 会有警告提示
# vector_db.persist()
# 检索器 核心
retriever = vector_db.as_retriever(search_kwargs={"k":3})
# rag 提示词
prompt = ChatPromptTemplate.from_template("""
你是一个严格的文档问答助手。
只根据下面的【文档内容】回答问题,绝对不能编造信息。
文档内容:
{context}
用户问题:
{question}
如果文档中没有答案,请直接回复:
"文档中没有相关内容"
""")
# 构建RAG 链
rag_chain = (
{"context":retriever, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
# 运行 RAG
print("✅️ RAG 系统启动成功! 开始提问吧")
while True:
question = input("你:")
if question == "q": break
ans = rag_chain.invoke(question)
print(f"🤖: {ans} \n")
六、这个 RAG 系统能做什么?
- 上传任意 PDF
- 问文档里的内容
- 只根据文档回答,不会编
- 不知道就说:文档中没有相关内容
- 回答准确、可靠
七、我用最简单的话讲清楚 RAG 链的结构
rag_chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
逐行解释:
- context = retriever → 去向量库检索相关内容
- question = 用户问题 → 直接传递问题
- 把两者拼起来给 Prompt
- LLM 根据文档回答
- 输出解析成字符串
这就是 最简单、最标准的 RAG 结构!
八、总结一句最核心的话
RAG = 检索 + 提示 + 生成
检索 = 从文档找答案
提示 = 告诉模型只看文档
生成 = 模型回答
==================================分隔符==================================
一、给 RAG 文档问答做 可视化网页界面( 可视化界面 RAG**)**
用 Streamlit 做,零前端代码、一行命令就能跑,自带漂亮网页 UI:
- 侧边栏上传 PDF
- 自动加载、分割、建 Chroma 向量库
- 聊天式对话框
- 基于文档 RAG 回答,不瞎编
- 展示检索到的参考文档片段
1. 安装依赖
pip install -U langchain langchain-openai langchain-community pypdf chromadb python-dotenv streamlit
2. 项目结构
你的项目文件夹/
├── .env
├── app_rag_ui.py # 可视化界面主程序
└── test.pdf # 可上传任意PDF
3. .env 文件
DOUBAO_API_KEY=你的豆包API_KEY
二、完整可运行代码 app_rag_ui.py
import os
import shutil
from dotenv import load_dotenv
import streamlit as st
# LangChain 相关
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_community.vectorstores import Chroma
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
# 加载环境变量
load_dotenv()
# ===================== 全局配置 =====================
CHROMA_DB_DIR = "./chroma_rag_ui_db"
CHUNK_SIZE = 1000
CHUNK_OVERLAP = 200
# 初始化 LLM
llm = ChatOpenAI(
api_key=os.getenv("DOUBAO_API_KEY"),
base_url="https://ark.cn-beijing.volces.com/api/v3",
model="doubao-pro-32k",
temperature=0.0
)
# 初始化嵌入模型
embedding = OpenAIEmbeddings(
api_key=os.getenv("DOUBAO_API_KEY"),
base_url="https://ark.cn-beijing.volces.com/api/v3"
)
# ===================== 工具函数 =====================
def clear_chroma_db():
"""清空旧向量库"""
if os.path.exists(CHROMA_DB_DIR):
shutil.rmtree(CHROMA_DB_DIR)
def load_and_split_pdf(pdf_path):
"""加载PDF + 语义分割"""
loader = PyPDFLoader(pdf_path)
docs = loader.load()
splitter = RecursiveCharacterTextSplitter(
chunk_size=CHUNK_SIZE,
chunk_overlap=CHUNK_OVERLAP,
separators=["\n\n", "\n", "。", "!", "?", " "]
)
chunks = splitter.split_documents(docs)
return chunks
def build_vector_db(chunks):
"""构建Chroma向量库"""
vector_db = Chroma.from_documents(
documents=chunks,
embedding=embedding,
persist_directory=CHROMA_DB_DIR
)
vector_db.persist()
return vector_db
def build_rag_chain(vector_db):
"""构建RAG问答链"""
retriever = vector_db.as_retriever(k=3)
prompt = ChatPromptTemplate.from_template("""
你是专业文档问答助手,**严格仅根据提供的文档上下文回答**。
规则:
1. 只使用上下文信息,禁止编造、禁止使用外部知识
2. 没有相关内容就直接回复:文档中没有相关内容
3. 回答简洁条理清晰
文档上下文:
{context}
用户问题:
{question}
""")
rag_chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
return rag_chain, retriever
# ===================== Streamlit 页面 =====================
st.set_page_config(page_title="RAG 文档问答机器人", layout="wide")
st.title("📚 RAG 文档问答可视化系统")
# 侧边栏
with st.sidebar:
st.header("📄 文档上传")
uploaded_file = st.file_uploader("上传 PDF 文件", type="pdf")
if uploaded_file:
# 保存临时PDF
temp_pdf_path = "temp_upload.pdf"
with open(temp_pdf_path, "wb") as f:
f.write(uploaded_file.read())
# 清空旧库
clear_chroma_db()
with st.spinner("正在加载文档、分割、构建向量库..."):
chunks = load_and_split_pdf(temp_pdf_path)
vector_db = build_vector_db(chunks)
rag_chain, retriever = build_rag_chain(vector_db)
# 存入session全局复用
st.session_state["rag_chain"] = rag_chain
st.session_state["retriever"] = retriever
st.success("✅ 文档加载完成,可以开始提问!")
# 主页面聊天
if "chat_history" not in st.session_state:
st.session_state["chat_history"] = []
# 渲染历史对话
for msg in st.session_state["chat_history"]:
with st.chat_message(msg["role"]):
st.markdown(msg["content"])
# 输入框
user_query = st.chat_input("请输入你的问题...")
if user_query:
# 展示用户问题
st.chat_message("user").markdown(user_query)
st.session_state["chat_history"].append({"role":"user","content":user_query})
if "rag_chain" not in st.session_state:
ans = "⚠️ 请先在侧边栏上传 PDF 文档"
else:
# RAG 生成回答
with st.spinner("AI 正在思考并检索文档..."):
ans = st.session_state["rag_chain"].invoke(user_query)
# 检索参考片段
refs = st.session_state["retriever"].get_relevant_documents(user_query)
# 展示AI回答
with st.chat_message("assistant"):
st.markdown(ans)
# 折叠展示参考文档片段
with st.expander("📖 查看检索参考文档片段"):
for idx, doc in enumerate(refs, 1):
st.write(f"**参考片段 {idx}(第{doc.metadata.get('page','未知')}页)**")
st.write(doc.page_content)
st.divider()
st.session_state["chat_history"].append({"role":"assistant","content":ans})
三、运行方式
终端执行:
streamlit run app_rag_ui.py
自动弹出浏览器网页界面,功能:
- 左侧上传任意 PDF
- 自动:加载 → 分割 → 向量化 → 存入 Chroma
- 下方对话框直接提问
- 底部可展开查看检索到的原文参考片段
- 严格基于文档回答,不瞎编
四、界面功能亮点
- 🎨 网页可视化,不用控制台
- 📤 支持随时上传新 PDF,自动清空旧向量库
- 💬 聊天式对话历史
- 🔍 可查看每一轮用到的检索原文片段 + 页码
- 🧠 纯 RAG 标准流程,语义检索,比关键词匹配精准很多