动手实战——50 行代码构建你的第一个 AI 知识库助手
1. 引言:从“纸上谈兵”到“真枪实弹”
在上一篇《RAG 技术原理与核心价值深度解析》中,我们通过理论推演了 RAG(检索增强生成)如何为大模型装上“外挂大脑”,解决了幻觉和知识过时的问题。
但正如 Linus Torvalds 所言:“Talk is cheap. Show me the code.”对于开发者而言,理解 RAG 最好的方式不是背诵架构图,而是亲手写一个能运行的 Demo。
今天,我们将抛开复杂的企业级架构(如分布式向量库、微服务等),回归本质,用 Python 和最基础的组件,在 10 分钟内搭建一个最小可行性 RAG 系统(Minimum Viable RAG)。
2. 技术选型:麻雀虽小,五脏俱全
为了构建这个“个人知识库助手”,我们需要凑齐 RAG 的四大神兽:
- LLM(大语言模型):负责最终的推理和生成。我们将使用 OpenAI 协议兼容的 API(如 DeepSeek、Qwen 或 GPT-4)。
- Embedding Model(嵌入模型):将文本转化为向量。
- Vector Database(向量数据库):存储和检索向量。本次实战使用轻量级的 FAISS 或 Chroma。
- Orchestration Framework(编排框架):串联整个流程。我们将使用 LangChain,它是目前最流行的 LLM 应用开发框架。
参考资源:datawhalechina/all-in-rag{target="_blank" class="gpt-web-url"} 提供了丰富的 RAG 实战代码结构,适合初学者深入学习。
3. 环境搭建:工欲善其事
首先,确保你的电脑上安装了 Python 3.10+。我们将创建一个虚拟环境并安装必要的依赖库。
3.1 安装依赖
打开终端(Terminal)或 CMD,执行以下命令:
# 创建虚拟环境
python -m venv rag_env
source rag_env/bin/activate # Mac/Linux
# rag_env\Scripts\activate # Windows
# 安装核心库
pip install langchain langchain-community langchain-openai faiss-cpu python-dotenv
langchain:核心编排逻辑。langchain-openai:调用 LLM 和 Embedding 模型(支持 OpenAI 协议的模型)。faiss-cpu:Facebook 开源的高效向量检索库,适合本地运行。python-dotenv:管理 API Key 等环境变量。
3.2 配置环境变量
在项目根目录下创建一个 .env 文件,填入你的模型 API Key。这里以通用的 OpenAI 格式为例(如果你使用 DeepSeek 或 阿里千问,只需修改 base_url):
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx #阿里云的token
OPENAI_API_BASE=https://api.deepseek.com/v1 或者https://dashscope.aliyuncs.com/compatible-mode/v1 # 示例:使用 DeepSeek 或者阿里云
4. 实战代码:构建你的 RAG 引擎
我们将按照 加载 -> 切分 -> 向量化 -> 存储 -> 检索 -> 生成 的标准流水线来编写代码。
创建一个名为 simple_rag.py 的文件,输入以下代码:
import os
from dotenv import load_dotenv
# 1. 导入必要的模块
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_community.vectorstores import FAISS
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
# 加载环境变量
#load_dotenv()
qwen_model = "qwen-turbo"
qwen_api_key = "sk-xxx"
# --- 第一步:准备数据 (Indexing Phase) ---
def create_vector_db():
print("🚀 开始构建知识库...")
# 模拟一个本地文档(实际场景可读取 PDF/Word)
with open("knowledge.txt", "w", encoding="utf-8") as f:
f.write("""
RAG技术专栏说明:
1. RAG全称是Retrieval-Augmented Generation,即检索增强生成。
2. RAG的核心价值在于解决大模型的幻觉问题和知识时效性问题。
3. 本专栏作者是AI架构师,计划更新10篇文章。
4. 今天是2026年1月14日,天气晴朗,适合写代码。
""")
# 1.1 加载文档
loader = TextLoader("knowledge.txt", encoding="utf-8")
docs = loader.load()
# 1.2 文档切分 (Chunking)
# 将长文档切分为小的片段,每个片段 100 字符,重叠 20 字符
text_splitter = RecursiveCharacterTextSplitter(chunk_size=100, chunk_overlap=20)
splits = text_splitter.split_documents(docs)
# 1.3 向量化并存储 (Embedding & Storage)
# 使用 OpenAI 兼容的 Embedding 模型
embeddings = OpenAIEmbeddings(
#1. 替换为你的阿里 DashScope API Key
api_key=qwen_api_key,
# 2. 关键:使用阿里的 OpenAI 兼容接口地址
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
# 3. 指定阿里的嵌入模型,例如 text-embedding-v1 或 text-embedding-v2
model="text-embedding-v2",
# 4. 禁用本地 token 长度检查(非常重要,否则可能会报错)
check_embedding_ctx_length=False
)
vectorstore = FAISS.from_documents(documents=splits, embedding=embeddings)
print(f"✅ 知识库构建完成,共切分为 {len(splits)} 个片段。")
return vectorstore
# --- 第二步:检索与生成 (Retrieval & Generation Phase) ---
def run_rag_chain(vectorstore, question):
print(f"\n🤔 用户提问: {question}")
# 2.1 定义检索器 (Retriever)
retriever = vectorstore.as_retriever(search_kwargs={"k": 2}) # 检索最相关的2个片段
# 2.2 定义 Prompt 模板
template = """你是一个智能助手。请根据下面的上下文回答问题。如果不知道,请直接说不知道。
上下文:
{context}
问题:
{question}
"""
prompt = ChatPromptTemplate.from_template(template)
# 2.3 初始化 LLM
#llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
llm = ChatOpenAI(
# 1. 替换为你的阿里 DashScope API Key
api_key=qwen_api_key,
# 2. 关键:使用阿里的 OpenAI 兼容接口地址
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
# 3. 指定阿里千问的模型名称,例如 qwen-plus, qwen-turbo, qwen-max
model=qwen_model,
# 其他可选参数
temperature=0.7,
max_tokens=1024,
)
# 2.4 构建 RAG 链 (LangChain Expression Language - LCEL)
# 流程:检索 -> 格式化上下文 -> 填充 Prompt -> 输入 LLM -> 解析输出
rag_chain = (
{"context": retriever | (lambda docs: "\n\n".join(doc.page_content for doc in docs)),
"question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
# 2.5 执行
response = rag_chain.invoke(question)
print(f"🤖 AI 回答: {response}")
if __name__ == "__main__":
# 初始化向量库
vector_db = create_vector_db()
# 测试问题 1:关于 RAG 定义(知识库里有)
run_rag_chain(vector_db, "RAG的全称是什么?")
# 测试问题 2:关于作者(知识库里有)
run_rag_chain(vector_db, "本专栏计划更新多少篇文章?")
# 测试问题 3:关于训练数据之外的实时信息(知识库里有)
run_rag_chain(vector_db, "今天是几号?")
5. 代码深度解析:透过现象看本质
这段不到 60 行的代码,完美复现了 RAG 的标准生命周期。让我们参考 Azure Architecture Center{target="_blank" class="gpt-web-url"} 和 RedHat{target="_blank" class="gpt-web-url"} 的架构定义,逐一拆解其中的玄机。
5.1 文档切分(Chunking)的艺术
text_splitter = RecursiveCharacterTextSplitter(chunk_size=100, chunk_overlap=20)
为什么不能直接把整本书塞给 Embedding 模型?
- Token 限制:Embedding 模型通常有输入长度限制(如 8192 tokens)。
- 语义稀释:一段文本越长,其向量所能表达的语义中心就越模糊。将文本切分为精简的
Chunk,有助于提高检索的精确度。
chunk_overlap=20是为了防止上下文在切分处断裂,保留一定的语义连贯性。
5.2 向量化与余弦相似度(Embedding & Similarity)
vectorstore = FAISS.from_documents(documents=splits, embedding=embeddings)
这一步是 RAG 的灵魂。OpenAIEmbeddings 将文本变成了高维空间中的坐标点(Vector)。
当我们在 retriever 中执行检索时,FAISS 正在后台计算用户问题向量 与文档向量 之间的余弦相似度:
得分越高,代表语义越接近,越容易被召回。
5.3 LCEL 链式调用
rag_chain = ( ... | prompt | llm | ... )
这里使用了 LangChain 的 LCEL (LangChain Expression Language) 语法。它像 Unix 的管道符 | 一样,将检索结果注入 Prompt,再将 Prompt 喂给 LLM,形成了一个自动化的数据流。
6. 运行结果与局限性分析
运行上述脚本,你应该能看到类似以下的输出:
🚀 开始构建知识库...
✅ 知识库构建完成,共切分为 4 个片段。
🤔 用户提问: 今天是几号?
🤖 AI 回答: 今天是2026年1月14日。
注意!如果你直接问原生的 ChatGPT-3.5 “今天是几号”,它可能会告诉你它是 2021 年训练的,无法回答。但通过 RAG,我们成功地让它“知道”了 knowledge.txt 中写入的 2026 年的信息。
当前系统的“软肋”
虽然这个 Demo 跑通了,但在企业级应用中,它还极其脆弱:
- 数据源单一:只能处理 txt,无法处理复杂的 PDF 表格、扫描件。
- 延伸阅读:基于MinerU 和AWS Serverless 构建企业级RAG 文档处理平台{target="_blank" class="gpt-web-url"} 提到了 PDF 解析的复杂性。
- 检索粗糙:简单的 Top-K 检索在面对复杂问题时容易失效(例如多跳推理)。
- 无持久化:每次运行都要重新构建索引,数据没有存入磁盘。
- 无历史记忆:它只能回答当前问题,无法进行多轮对话。
7. 总结与下篇预告
在这篇文章中,我们亲手搭建了一个最小化的 RAG 系统,验证了“检索+生成”的可行性。你现在拥有的代码框架,就是所有复杂 RAG 应用(如客服机器人、法律顾问助手)的雏形。
但是,真实世界的数据往往是脏乱差的。PDF 里有乱码,文档中有表格,图片里有文字。如何高质量地解析文档,是决定 RAG 系统上限的第一道门槛。
下一篇预告: RAG 技术专栏(三):数据为王——PDF、Word 复杂文档解析与清洗实战。我们将深入探讨 ETL 流程,教你如何从复杂的非结构化文档中提取干净的知识。
敬请期待!
实战作业:
尝试修改 knowledge.txt 的内容,放入你们公司的产品介绍,看看 AI 能否准确回答关于产品的提问。
参考文献:
- datawhalechina/all-in-rag: 大模型应用开发实战一{target="_blank" class="gpt-web-url"}
- 设计和开发RAG 解决方案 - Azure Architecture Center{target="_blank" class="gpt-web-url"}
- 使用檢索增強生成技術建構代理 - Google Codelabs{target="_blank" class="gpt-web-url"}