RAG 实战教程:从零构建企业智能问答 Agent
完整代码地址:https://github.com/ziyifast/Demo/tree/main/1-AI/RAG-Knowledge-Base
1.为什么需要RAG:
想象一下,你刚入职一家公司,需要快速了解所有产品信息。你有一个"超级助理"(大语言模型,LLM),它上知天文下知地理,但存在三个致命缺陷:
| 缺陷 | 说明 | 举个例子 |
|---|---|---|
| 知识滞后 | 只学会了训练截止日期前的知识 | 问它"2026年最新的行业政策",它可能一无所知 |
| 幻觉生成 | 面对未知问题会"一本正经地胡说八道" | 调查显示,约 30% 的复杂问题回答存在事实性错误 |
| 无法访问私有数据 | 完全不了解公司内部信息 | 问"我们的产品保修期多久",它只能凭空猜测 |
2.什么是RAG:
RAG(检索增强生成) 正是为解决上述问题而生。它的核心思想很简单:不让模型凭记忆硬答,而是允许它"开卷考试"。
一句话理解:RAG = 给大模型配一个实时更新的"外部知识库"。在回答问题前,模型会先去这个知识库里"翻书"查找相关资料,然后基于查到的资料进行回答。

项目产出:
一个能基于你的私有文档(产品手册、公司制度、学习笔记)进行问答,并能溯源引用来源的智能 Agent。
一、核心概念
1. Embedding(向量嵌入)
计算机不认识"语义",只认识"数字"。Embedding 就是那个"翻译官",负责把人类的文字、图片翻译成计算机能计算的数字列表(向量)。语义相近的内容,翻译出的数字向量在空间中的距离也相近。
💡 核心规律:两个向量在空间中的距离越近 = 语义越相近。 这就是计算机"理解"语义的秘密。
2. 向量数据库(Vector Database)
普通数据库擅长找"完全一样"的内容,而向量数据库擅长找"意思最像"的内容。
核心价值:向量数据库是实现语义搜索和 RAG 的基石。
3. RAG(检索增强生成)
全称:Retrieval Augmented Generation 检索增强生成,RAG 不是一个单一技术,而是一个工作流,核心包含三大步骤:
bash
📥 离线建库阶段:将你的私有文档分块、向量化,存入向量数据库。
🔍 在线检索阶段:将用户问题向量化,在向量库中检索最相关的 Top-K 文档块。
✍️ 增强生成阶段:将"用户问题 + 检索到的文档"一起打包发给大模型,让它"有据可依"地生成回答。
4. Agent(智能体)
能调用工具、自主决策的 AI 系统。它不只是"回答",更能"行动"。
bash
RAG: 问题 → 检索 → 回答(线性流程)
Agent: 问题 → 思考 → 选工具 → 执行 → 观察 → 再思考 → ... → 回答
↑__________________________________________↓
这个"思考→行动→观察"循环就是 Agent 的智能所在
例如,当你问"查一下订单 123 的退款政策"时,Agent 会:
-
思考:需要先查订单信息,再查对应的退款政策。
-
行动:调用"订单查询工具"获取产品名。
-
观察:得到"产品是 NeoCompute Standard"。
-
思考:现在需要查这款产品的退款政策。
-
行动:调用"知识库检索工具"。
-
最终回答:给出准确的退款政策。
二、快速开始
环境要求
-
Python 3.10+
-
pip 包管理器
1️⃣ 克隆并进入项目
bash
git clone <your-repo-url> RAG-Tutorial
cd RAG-Tutorial
2️⃣ 安装依赖
bash
# 安装所有必需的 Python 包
pip install -r requirements.txt
3️⃣ 配置 API Key
bash
# 复制环境变量模板
cp .env.example .env
# 编辑 .env 文件,填入你的 API Key
# OPENAI_API_KEY=sk-your-real-key-here
💡 没有 OpenAI API Key? 可以使用免费的本地 BGE 模型:
bash
# 在所有命令后面加 --embedding bge 即可
python scripts/03_build_knowledge_base.py --embedding bge
4️⃣ 按顺序运行演示
bash
# 第一步:感受 Embedding 的魔力(无需 API Key)
python scripts/01_embedding_demo.py
# 第二步:玩转向量数据库 ChromaDB(无需 API Key)
python scripts/02_vector_db_demo.py
# 第三步:构建你的第一个知识库(需要 API Key 或用 bge)
python scripts/03_build_knowledge_base.py
# 第四步:RAG 问答(需要 API Key 或用 bge)
python scripts/04_rag_qa.py
# 第五步:Agent 智能体(需要 API Key)
python scripts/05_agent_demo.py
三、 分步教程
第一步:理解 Embedding
🎯 知识点:语义相近的文字 → 向量距离近。这就是 RAG 检索的基石。
1.运行命令:
bash
python scripts/01_embedding_demo.py
2.你会看到什么:
-
向量的样子 ------ 一句话被表示成 512 个数字
-
语义相似度对比 ------ 观察"苹果水果"和"苹果公司"的向量距离差异
-
最简单的语义搜索 ------ 用"保修多久"搜到"保修期3年"
3.效果:

4.关键代码片段:
bash
from sentence_transformers import SentenceTransformer
# 加载模型(首次自动下载)
model = SentenceTransformer("BAAI/bge-small-zh-v1.5")
# 文字 → 向量
vector = model.encode("苹果是一种很好吃的水果")
# 结果: [0.23, -0.15, 0.78, ...] (512个数字)
# 计算相似度
sim = cosine_similarity(
model.encode("产品保修多久"),
model.encode("云服务器提供 3 年标准保修服务")
)
# 结果: 0.89 (高度相似!虽然字面不同)
第二步:向量数据库实战
🎯 知识点:向量数据库不匹配字面,而是按语义相似度检索。
1.运行命令:
python
python scripts/02_vector_db_demo.py
2.你会学到:
| 操作 | 说明 | 类比 MySQL |
|---|---|---|
| 创建 Collection | 创建集合(表) | CREATE TABLE |
| 添加文档 | 存入文档+向量 | INSERT INTO |
| 语义查询 | 按相似度搜索 | 无对应!传统DB做不到 |
| 元数据过滤 | 精确+语义混合查询 | WHERE + 向量搜索 |
| 更新/删除 | 文档管理 | UPDATE / DELETE |
3.效果:

4.关键代码片段:
python
import chromadb
# 连接数据库
client = chromadb.PersistentClient(path="./chroma_demo_db")
# 创建集合
collection = client.create_collection(name="my_knowledge_base")
# 添加文档
collection.add(
documents=["产品保修期为 3 年", "支持 7 天无理由退款"],
ids=["doc_1", "doc_2"],
)
# 语义搜索
results = collection.query(
query_texts=["保修多久"],
n_results=3,
)
# 结果精确命中"产品保修期为 3 年"!
# 虽然"保修多久"和"保修期为3年"字面不同,但语义相同
第三步:构建知识库
🎯 知识点:分块策略直接影响 RAG 的检索质量。太小丢上下文,太大降精度。
通过免费开源的BGE 中文 Embedding 模型,构建知识库。官方网站:https://bge-model.com/
1.运行命令:
python
# 使用免费本地模型(BGE)
python scripts/03_build_knowledge_base.py --embedding bge
# 使用LLM 服务商API(需要修改.env配置)
python scripts/03_build_knowledge_base.py

2.这个脚本做了什么:
bash
docs/product_manual.txt(原始文档)
│
▼ 加载文档
1个 Document 对象
│
▼ 分块(chunk_size=500, overlap=50)
约 15-20 个文档块
│
▼ 向量化(Embedding)
每个块 → 1536维向量(OpenAI)/ 512维向量(BGE)
│
▼ 存入 ChromaDB
./chroma_db/ 目录(持久化存储)
分块策略说明:
分块(Chunking)是影响 RAG 检索质量的最关键环节之一
3.效果:


第四步:RAG 问答
🎯 知识点:RAG = 检索 + 生成。Embedding 模型决定了检索的上限(检索是否精准),LLM 决定了生成的下限(回答是否流畅、逻辑清晰)
1.因为涉及问答,所以必须配置外部LLM服务商提供的模型,本地的BGE是无法满足问答需求的。
BGE 等 Embedding 模型只能做"检索"(找相关文档),不具备"生成回答"的能力

2.运行命令:
bash
# 演示模式(预设问题)
python scripts/04_rag_qa.py
# 交互模式(自己提问),使用LLM服务商 提供模型,如:Deepseek、Claude、GPT等
python scripts/04_rag_qa.py --interactive
# 使用本地BGE模型进行embedding(问答还是需要LLM服务商提供的模型)
python scripts/04_rag_qa.py --embedding bge
3.效果:

4.RAG 核心流程(完整代码):
bash
from app.rag_engine import RAGEngine
# 1. 创建 RAG 引擎
engine = RAGEngine(
persist_dir="./chroma_db",
search_k=3, # 检索 Top-3 最相关文档
)
# 2. 提问
answer, sources = engine.query("产品保修期是多久?")
print(answer)
# 输出: "根据产品手册第2.1节,NeoCloud所有自研硬件产品
# 享有3年标准保修服务,核心部件(CPU、内存、主板)
# 享有5年有限保修..."
# 3. 查看引用的源文档
for doc in sources:
print(doc.page_content[:100])
chain_type 选择:
stuff:一次性将所有文档块放入 Prompt,速度最快,但受上下文窗口限制。适用于文档块较少(少于5个)的场景,新人首选。
map_reduce:对每个文档块单独生成答案,再将所有答案汇总后生成最终回答。速度较慢,能处理大量文档块。适合文档数量多、需综合信息的场景。
refine:逐篇迭代优化答案------先用首个文档块生成初始回答,后续文档块依次对上一轮答案进行补充修正。速度最慢,质量最高。适用于对准确性要求极高的场景。
map_rerank:对每个文档块分别生成答案并打分,最终只返回最高分答案。速度较快,适合仅需一个最佳答案的场景(如FAQ)。
第五步:Agent 智能体
🎯 知识点:Agent = RAG + 工具调用 + 自主决策。它能判断该用什么工具、按什么顺序,而不仅仅是回答问题。
1.运行命令:
bash
# 基础 Agent(只有知识库工具)
python scripts/05_agent_demo.py
# 带额外工具的 Agent
python scripts/05_agent_demo.py --with-extras
# 交互式 Agent
python scripts/05_agent_demo.py --interactive
效果:


Agent 与普通 RAG 的区别:
bash
# 普通 RAG:线性流程
answer = engine.query("产品保修多久")
# 内部:问题 → 向量化 → 检索 → 生成 → 返回
# Agent:思考-行动循环
response = agent.run("查订单 ORD-001,告诉我对应的退款政策")
# 内部:
# Thought: 需要先查订单信息,再查退款政策
# Action: OrderQuery("ORD-001")
# Observation: 订单产品是 NeoCompute Standard
# Thought: 现在需要查这个产品的退款政策
# Action: KnowledgeBase("NeoCompute Standard 退款政策")
# Observation: 按量付费支持随时退款,包月7天内无理由退款
# Thought: 我有足够信息了
# Final Answer: 订单 ORD-001(NeoCompute Standard)的退款政策是...
添加自定义工具:
python
from app.agent import RAGAgent
agent = RAGAgent()
# 添加你自己的工具
def my_custom_tool(input_text: str) -> str:
"""你的工具逻辑"""
return f"处理结果: {input_text}"
agent.add_tool(
name="MyTool",
func=my_custom_tool,
description="这个工具用于XXX。当你需要XXX时使用。输入:XXX格式。"
)
四、 整体架构
完整代码地址:https://github.com/ziyifast/Demo/tree/main/1-AI/RAG-Knowledge-Base
python
RAG/
├── README.md # 📖 本文档
├── requirements.txt # 📦 Python 依赖
├── .env.example # 🔑 环境变量模板
│
├── docs/ # 📂 文档目录(放你的知识库文档)
│ └── product_manual.txt # 示例:产品手册
│
├── scripts/ # 🔧 可执行脚本(按顺序学习)
│ ├── 01_embedding_demo.py # 第一步:理解 Embedding
│ ├── 02_vector_db_demo.py # 第二步:向量数据库操作
│ ├── 03_build_knowledge_base.py # 第三步:构建知识库
│ ├── 04_rag_qa.py # 第四步:RAG 问答
│ └── 05_agent_demo.py # 第五步:Agent 智能体
│
├── app/ # 📚 核心代码模块
│ ├── __init__.py # 包初始化
│ ├── knowledge_base.py # 知识库构建(文档加载、分块、向量化)
│ ├── rag_engine.py # RAG 引擎(检索 + 生成)
│ └── agent.py # Agent 智能体(工具调用 + 自主决策)
│
└── chroma_db/ # 🗄️ 向量数据库存储目录(自动生成)
└── ... # ChromaDB 持久化文件
| 模块 | 核心文件 | 职责 |
|---|---|---|
| 知识库 | app/knowledge_base.py | 文档加载、文本分块、向量化存储 |
| RAG 引擎 | app/rag_engine.py | 执行检索、构建 Prompt、调用 LLM 生成 |
| Agent | app/agent.py | 管理工具、执行"思考-行动"循环 |
五、 进阶优化
当你跑通基础流程后,可以通过以下方式提升效果:
1. 检索优化
| 优化方向 | 方法 | 效果 |
|---|---|---|
| 混合检索 | 关键词检索 (BM25) + 语义检索 (Embedding) | 兼顾精确匹配和语义理解,显著提升召回率 |
| 重排序 (Rerank) | 先用 Embedding 粗排 Top-50,再用 Cross-Encoder 精排 | 效果提升最明显,能极大提升 Top-3 准确率 |
| 多路召回 | 从不同角度(不同分块策略、不同模型)检索合并 | 提升召回覆盖率,减少漏检 |
2. 分块优化
| 优化方向 | 方法 |
|---|---|
| 语义分块 | 按段落、章节等语义边界切分,而非机械地按字数切割 |
| 父子文档 (Parent Document Retriever) | 检索细粒度的小块(精准),返回粗粒度的大块(上下文完整) |
| 滑动窗口 | 用滑动窗口生成更多有重叠的块,丰富上下文信息 |
3. Prompt 优化
bash
❌ 差 Prompt:
"回答问题: {question}\n参考资料: {context}"
✅ 好 Prompt:
"你是一个专业的企业知识库助手。请严格基于以下参考资料回答问题。
如果资料中没有相关信息,请明确说'根据现有资料无法回答',不要编造。
参考资料:
{context}
问题: {question}
请用中文回答,并注明信息来源。"
4. Embedding 模型选择
| 模型 | 维度 | 中文效果 | 费用 | 适用场景 |
|---|---|---|---|---|
| OpenAI text-embedding-3-small | 1536 | ⭐⭐⭐⭐ | $0.02/1M tokens | 通用英文优先 |
| OpenAI text-embedding-3-large | 3072 | ⭐⭐⭐⭐⭐ | $0.13/1M tokens | 高精度需求 |
| BAAI/bge-small-zh-v1.5 | 512 | ⭐⭐⭐⭐ | 免费 | 中文场景首选 |
| BAAI/bge-large-zh-v1.5 | 1024 | ⭐⭐⭐⭐⭐ | 免费 | 高精度中文场景 |
| moka-ai/m3e-base | 768 | ⭐⭐⭐⭐ | 免费 | 中文轻量选择 |
⚠️ Embedding 模型决定了召回的上限------文档向量化时丢失的语义信息,后续任何优化都无法补救。
常见问题
Q1: 我没有 OpenAI API Key 怎么办?
使用本地免费的 BGE 模型:
首次运行会自动下载模型文件(约 100MB)。
Q2: 如何替换成国产大模型(DeepSeek/Qwen/GLM)?
修改 .env 文件:
Q3: 分块大小(chunk_size)如何选择?
FAQ/短问答:300-500 字符
通用文档:500-800 字符(推荐新手从 500 开始)
技术文档/长文章:800-1500 字符
重叠:始终设为 chunk_size 的 10%
Q4: 检索多少文档(search_k)合适?
注意:k 值太大,容易引入噪声,反而干扰 LLM 生成。
k=3:大部分场景(默认推荐)
k=5:需要综合多方面信息时
k=1:精准问答(如FAQ)
Q5: 为什么检索到的内容不相关?
1.检查 Embedding 模型是否适合你的语言(中文文档用中文模型)
2.调整 chunk_size(当前可能太大或太小)
3.检查文档质量(是否包含太多噪声)
4.尝试增大 k 值看是否有相关内容被排在后面