动手实战——50 行代码构建你的第一个 AI 知识库助手

6 阅读8分钟

动手实战——50 行代码构建你的第一个 AI 知识库助手

1. 引言:从“纸上谈兵”到“真枪实弹”

在上一篇《RAG 技术原理与核心价值深度解析》中,我们通过理论推演了 RAG(检索增强生成)如何为大模型装上“外挂大脑”,解决了幻觉和知识过时的问题。

但正如 Linus Torvalds 所言:“Talk is cheap. Show me the code.”对于开发者而言,理解 RAG 最好的方式不是背诵架构图,而是亲手写一个能运行的 Demo。

今天,我们将抛开复杂的企业级架构(如分布式向量库、微服务等),回归本质,用 Python 和最基础的组件,在 10 分钟内搭建一个最小可行性 RAG 系统(Minimum Viable RAG)

2. 技术选型:麻雀虽小,五脏俱全

为了构建这个“个人知识库助手”,我们需要凑齐 RAG 的四大神兽:

  1. LLM(大语言模型):负责最终的推理和生成。我们将使用 OpenAI 协议兼容的 API(如 DeepSeek、Qwen 或 GPT-4)。
  2. Embedding Model(嵌入模型):将文本转化为向量。
  3. Vector Database(向量数据库):存储和检索向量。本次实战使用轻量级的 FAISSChroma
  4. 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 模型?

  1. Token 限制:Embedding 模型通常有输入长度限制(如 8192 tokens)。
  2. 语义稀释:一段文本越长,其向量所能表达的语义中心就越模糊。将文本切分为精简的 Chunk,有助于提高检索的精确度。
  • chunk_overlap=20 是为了防止上下文在切分处断裂,保留一定的语义连贯性。

5.2 向量化与余弦相似度(Embedding & Similarity)

vectorstore = FAISS.from_documents(documents=splits, embedding=embeddings)

这一步是 RAG 的灵魂。OpenAIEmbeddings 将文本变成了高维空间中的坐标点(Vector)。 当我们在 retriever 中执行检索时,FAISS 正在后台计算用户问题向量 VqV_q 与文档向量 VdV_d 之间的余弦相似度

similarity=cos(θ)=ABAB\text{similarity} = \cos(\theta) = \frac{A \cdot B}{\|A\| \|B\|}

得分越高,代表语义越接近,越容易被召回。

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 跑通了,但在企业级应用中,它还极其脆弱:

  1. 数据源单一:只能处理 txt,无法处理复杂的 PDF 表格、扫描件。
  2. 检索粗糙:简单的 Top-K 检索在面对复杂问题时容易失效(例如多跳推理)。
  3. 无持久化:每次运行都要重新构建索引,数据没有存入磁盘。
  4. 无历史记忆:它只能回答当前问题,无法进行多轮对话。

7. 总结与下篇预告

在这篇文章中,我们亲手搭建了一个最小化的 RAG 系统,验证了“检索+生成”的可行性。你现在拥有的代码框架,就是所有复杂 RAG 应用(如客服机器人、法律顾问助手)的雏形。

但是,真实世界的数据往往是脏乱差的。PDF 里有乱码,文档中有表格,图片里有文字。如何高质量地解析文档,是决定 RAG 系统上限的第一道门槛。

下一篇预告RAG 技术专栏(三):数据为王——PDF、Word 复杂文档解析与清洗实战。我们将深入探讨 ETL 流程,教你如何从复杂的非结构化文档中提取干净的知识。

敬请期待!


实战作业: 尝试修改 knowledge.txt 的内容,放入你们公司的产品介绍,看看 AI 能否准确回答关于产品的提问。

参考文献:

  1. datawhalechina/all-in-rag: 大模型应用开发实战一{target="_blank" class="gpt-web-url"}
  2. 设计和开发RAG 解决方案 - Azure Architecture Center{target="_blank" class="gpt-web-url"}
  3. 使用檢索增強生成技術建構代理 - Google Codelabs{target="_blank" class="gpt-web-url"}