当前位置: 首页 > news >正文

第4章:实战项目一 打造你的第一个AI知识库问答机器人 (RAG)

各位老铁,欢迎来到我们专栏的第一个实战项目。

在过去的三个章节里,我们已经完成了所有的理论储备和环境搭建。我们理解了LLM的本质,掌握了Prompt Engineering的要领,洞悉了Embedding和向量数据库的魔力,并且熟悉了LangChain这个强大的“瑞士军刀”。

现在,是时候将这些散落的“龙珠”汇集起来,召唤出我们的第一条“神龙”了。

这个项目,是当前AI应用领域最经典、最实用、也是商业价值最高的场景之一:本地知识库问答。我们将从零开始,构建一个完整的系统,它可以:

  1. 读取并“学习”你提供的一份或多份本地文档(例如,一份PDF格式的产品说明书或公司规章制度)。
  2. 当用户用自然语言提问时,系统能够仅仅依据这些文档的内容,给出精准的回答。
  3. 如果文档中没有相关信息,它会明确地告知用户“根据已有信息无法回答”,而不是凭空捏造(产生幻觉)。

这个项目,是RAG(检索增强生成)模式的终极体现。完成它,你将不再是一个AI领域的旁观者,而是一个能够交付真正价值的AI应用工程师

本章内容详尽,代码完整,请务必保持专注,跟上我的节奏。让我们开始构建。

4.1 项目蓝图与技术栈规划

在动工之前,我们首先要像一个架构师一样,画出我们系统的蓝图。一个清晰的架构图,是我们后续所有工作的指导方针。

我们的系统,本质上包含两个核心的、相互独立的流程:

  1. 知识注入流程 (Ingestion Pipeline):这个流程是一次性的(或者定期执行的)。它的任务是将原始文档处理成向量化的知识,并存入向量数据库。
  2. 问答检索流程 (Query Pipeline):这个流程是实时响应用户请求的。它接收用户问题,从向量数据库中检索相关知识,并交由LLM生成最终答案。

下面这张图,就是我们本次项目的核心架构图:

流程二: 问答检索 (在线/实时)
流程一: 知识注入 (离线/一次性)
5. 查询向量化
用户提问
6. 检索相关文档
7. 构建增强Prompt
8. 调用LLM生成回答
返回精准答案
2. 文本分割
1. 加载文档 (PDF)
3. 文本向量化
4. 存入向量数据库

基于这个蓝图,我们确定本次项目的技术栈:

  • 核心框架: Python 3.10+
  • AI编排: LangChain - 我们整个工作流的粘合剂。
  • 大语言模型 (LLM): 通义千问 (qwen-turbo) - 由阿里云达摩院提供,负责最终的理解和生成。
  • Embedding模型: 通义千问文本向量模型 (text-embedding-v1) - 负责将文本翻译成向量。
  • 向量数据库: ChromaDB - 一个轻量级、开源、可本地运行的向量数据库。
  • 文档加载: PyPDFLoader - LangChain集成的一个用于加载PDF文档的工具。
  • 交互界面: Streamlit - 一个能用纯Python快速构建Web UI的库。

准备工作:

  1. 获取API Key:

    • 访问阿里云百炼平台。
    • 开通服务并进入控制台,在“API-KEY管理”中创建你的专属API Key。
    • 严格遵循我们的安全准则:在你的项目根目录创建.env文件,并写入:
      DASHSCOPE_API_KEY="sk-YourAlibabaCloudApiKey"
      
      同时,将.env加入.gitignore文件。
  2. 安装所有依赖:

    • 确保你已激活之前创建的venv虚拟环境。
    • 执行以下命令,一次性安装所有我们需要的库:
      pip install langchain langchain-community dashscope chromadb pypdf streamlit langchain-chroma
      
      • langchain-chroma: ChromaDB与LangChain集成的最新独立包,我们遵循最佳实践,使用这个版本。
  3. 准备一份测试文档:

    • 在你的项目根目录下,创建一个名为docs的文件夹。
    • 找一份你感兴趣的PDF文档放进去,比如一份产品说明书、一篇论文、或者一份公开的报告。

    在这里插入图片描述

一切就绪,让我们开始编写知识注入流程的代码。

4.2 流程一:构建知识注入管道 (ingest.py)

这个脚本的任务是读取docs文件夹下的所有PDF,将它们处理后存入本地的ChromaDB数据库。这个脚本只需要运行一次。

在你的项目根目录,创建一个名为ingest.py的文件,并写入以下代码:

import os
from dotenv import load_dotenvfrom langchain_community.document_loaders import PyPDFLoader, DirectoryLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.embeddings.dashscope import DashScopeEmbeddings
from langchain_chroma import Chroma# 加载.env文件中的环境变量
load_dotenv()# 定义文档目录和Chroma持久化目录
DOCS_PATH = "docs"
CHROMA_PATH = "chroma_db"def main():"""主执行函数:1. 加载文档2. 分割文档3. 向量化并存入ChromaDB"""print("--- 开始知识注入流程 ---")# 1. 加载文档print(f"[步骤1] 从 '{DOCS_PATH}' 目录加载PDF文档...")if not os.path.exists(DOCS_PATH):print(f"错误:文档目录 '{DOCS_PATH}' 不存在。请创建该目录并放入PDF文件。")returnloader = DirectoryLoader(DOCS_PATH, glob="*.pdf", loader_cls=PyPDFLoader)documents = loader.load()if not documents:print("错误:在'docs'目录中未找到任何PDF文件。")returnprint(f"成功加载 {len(documents)} 个文档。")# 2. 分割文档print("\n[步骤2] 将加载的文档分割成小块(chunks)...")text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=100)chunks = text_splitter.split_documents(documents)print(f"文档被成功分割成 {len(chunks)} 个小块。")# 3. 向量化并存入ChromaDBprint("\n[步骤3] 初始化Embedding模型并创建Chroma数据库...")embeddings = DashScopeEmbeddings(model="text-embedding-v1")db = Chroma.from_documents(chunks, embeddings, persist_directory=CHROMA_PATH)print(f"\n--- 知识注入成功!---")print(f"向量化数据已成功存入 '{CHROMA_PATH}' 目录。")print("现在您可以运行查询脚本或Web UI来与您的知识库进行交互了。")if __name__ == "__main__":main()

代码解析:

  • DirectoryLoader: LangChain提供的便捷工具,自动遍历docs目录,使用PyPDFLoader加载所有PDF文件。
  • RecursiveCharacterTextSplitter: 智能文本分割器。chunk_size=500定义了每个文本块的目标大小,chunk_overlap=100确保块之间的上下文连续性。
  • DashScopeEmbeddings: LangChain对通义千问文本向量模型的封装。我们只需实例化,LangChain便会在需要时自动调用它。
  • langchain_chroma.Chroma: 我们使用最新的独立包来与ChromaDB交互。Chroma.from_documents方法是一个强大的“三合一”操作,它会自动完成文本块的向量化、存储,并通过persist_directory参数将数据库完整地写入本地磁盘。

如何运行?

在终端(已激活虚拟环境)中,直接运行此脚本:

python ingest.py

脚本执行完毕后,你的项目目录下会多出一个名为chroma_db的文件夹,这就是我们AI的“长期记忆体”!

4.3 流程二:构建问答检索管道 (app.py)

知识库已经建好,现在我们要构建一个能使用这个知识库的问答系统。我们直接使用Streamlit来构建一个简单的Web界面。

在你的项目根目录,创建一个名为app.py的文件,并写入以下代码:

import os
import streamlit as st
from dotenv import load_dotenvfrom langchain_community.embeddings.dashscope import DashScopeEmbeddings
from langchain_chroma import Chroma
from langchain_community.chat_models.tongyi import ChatTongyi
from langchain.prompts import PromptTemplate
from langchain.chains import RetrievalQA# 加载.env文件中的环境变量
load_dotenv()# --- 全局配置 ---
CHROMA_PATH = "chroma_db"
PROMPT_TEMPLATE = """
请注意:你是一个专业的AI助手,你的任务是根据下面提供的“背景信息”来回答问题。
请严格依据“背景信息”的内容进行回答,不要自行发挥或使用你的内部知识。
如果“背景信息”中没有足够的内容来回答问题,请直接说“根据已有信息,我无法回答该问题。”。
不允许在答案中添加任何非“背景信息”中的内容。背景信息:
{context}问题:
{question}请给出你的回答:
"""def main():"""主函数,构建Streamlit Web应用"""st.set_page_config(page_title="阿威的AI知识库", page_icon="🤖")st.title("🤖 阿威的AI知识库")st.markdown("你好!我是你的AI助手。请在下方输入框中提出你关于已上传文档的问题。")@st.cache_resourcedef initialize_components():if not os.path.exists(CHROMA_PATH):st.error(f"错误:向量数据库目录 '{CHROMA_PATH}' 不存在。请先运行 'ingest.py' 来创建数据库。")return None, Noneembeddings = DashScopeEmbeddings(model="text-embedding-v1")db = Chroma(persist_directory=CHROMA_PATH, embedding_function=embeddings)llm = ChatTongyi(model_name="qwen-turbo")return db, llmdb, llm = initialize_components()if db is None or llm is None:returnretriever = db.as_retriever()prompt = PromptTemplate(template=PROMPT_TEMPLATE, input_variables=["context", "question"])qa_chain = RetrievalQA.from_chain_type(llm=llm,chain_type="stuff",retriever=retriever,return_source_documents=True,chain_type_kwargs={"prompt": prompt})query = st.text_input("请输入你的问题:", placeholder="例如:阿里巴巴开发手册里藏着什么宝贝?")if st.button("提问"):if not query:st.warning("请输入问题后再提问。")else:with st.spinner("AI正在思考中,请稍候..."):try:result = qa_chain.invoke({"query": query})st.subheader("💡 AI的回答:")st.write(result["result"])with st.expander("查看答案来源"):for doc in result["source_documents"]:st.info(f"来源文件: {os.path.basename(doc.metadata.get('source', '未知'))}")st.write(doc.page_content)st.divider()except Exception as e:st.error(f"在回答问题时发生错误: {e}")if __name__ == "__main__":main()

代码解析:

  • st.cache_resource: Streamlit的缓存装饰器,用于缓存模型加载、数据库连接等耗时操作,避免重复执行,提升应用响应速度。
  • Chroma(...): 这里我们直接实例化Chroma类,通过persist_directory从本地加载数据库。embedding_function是必须提供的,因为Chroma需要用同一个Embedding模型来向量化用户的查询。
  • ChatTongyi: LangChain对通义千问聊天模型的封装,我们选择qwen-turbo模型以平衡性能和成本。
  • PROMPT_TEMPLATE: 这是我们RAG系统的灵魂!我们通过严格的指令为LLM设定了行为边界,包括角色扮演、核心指令、兜底策略和幻觉抑制,这直接决定了系统的可靠性。
  • db.as_retriever(): 将ChromaDB实例转换为一个标准的LangChain Retriever对象,它封装了所有检索逻辑。
  • RetrievalQA.from_chain_type: LangChain提供的构建RAG应用的便捷Chain。chain_type="stuff"表示将检索到的所有文档片段全部“塞”进Prompt的{context}部分,这是最直接的策略。

如何运行?

注意! Streamlit应用有其专属的启动方式。你不能使用python app.py来运行它。

请在终端(已激活虚拟环境)中,使用以下正确的命令来启动Web应用:

streamlit run app.py

执行该命令后,你的浏览器会自动打开一个新标签页,显示出我们应用的交互界面。现在,你可以像使用聊天机器人一样,输入你关于文档内容的问题,然后点击“提问”按钮,见证AI的强大能力。
示例:
在这里插入图片描述

4.4 总结、挑战与展望

恭喜你!你已经成功地从零开始,构建并运行了一个功能完备、逻辑严谨的AI知识库问答系统。

让我们回顾一下我们所取得的成就:

  1. 我们设计并实现了分离的知识注入和问答检索两大流程,这是工程上的最佳实践。
  2. 我们掌握了使用LangChain整合文档加载、文本分割、Embedding、向量存储的全过程。
  3. 我们学会了如何构建一个高质量的RAG Prompt,来约束LLM的行为,抑制幻觉。
  4. 我们成功地将**检索器(Retriever)大语言模型(LLM)**通过RetrievalQA链无缝地结合在一起。
  5. 我们还用Streamlit为我们的应用穿上了一件漂亮的“外衣”,让它变得可用、可交互。

然而,作为一个严谨的工程师,我们也要清醒地认识到,我们这个V1版本的系统还存在一些挑战和可优化的空间

  • 检索质量: RAG系统的天花板取决于检索器能否找对相关的文档。如何优化检索效果是一个重要的课题。
  • 分块策略 (Chunking): chunk_sizechunk_overlap的设置对结果影响很大,需要根据文档类型进行调整。
  • 成本与延迟: 在生产环境中,需要对API调用和数据库查询的延迟与成本进行监控和优化。
  • 评估体系: 如何科学地评估问答系统的好坏?这需要一套评估标准和数据集,是AI应用工程化中的重要一环。

这些挑战,也正是我们未来继续深入学习和探索的方向。

在本章之后,你已经掌握了构建“知识型”AI应用的核心技能。在下一章,我们将探索一个更令人兴奋的领域——AI智能体(Agent)。我们将赋予AI“手”和“脚”,让它不再仅仅是“回答”问题,而是能够“执行”任务,成为一个真正能为你干活的“数字员工”。

实战之旅,精彩继续。我们下期见。

http://www.lryc.cn/news/583259.html

相关文章:

  • LeetCode 138题解 | 随机链表的复制
  • 光伏无人机3D建模:毫秒级精度设计
  • 老年人与机器人玩具的情感连接
  • 什么是 AMR 格式?简鹿音频转换器轻松批量转换 AMR 为 MP3
  • 论文阅读|汽车虚拟环绕音响系统设计与实现策略的比较研究
  • OpenCV图片操作100例:从入门到精通指南(4)
  • NLP:初识RNN模型(概念、分类、作用)
  • 继承与多态:面向对象编程的两大支柱
  • stockapi股票实时tick数据,技术指标macd,kdj,cci,日k线数据
  • 如何将FPGA设计的验证效率提升1000倍以上(3)
  • oracle ocp题库有多少道题,以及题库背诵技巧
  • JavaEE初阶第八期:解锁多线程,从 “单车道” 到 “高速公路” 的编程升级(六)
  • 软件设计师中级概念题
  • Selenium+Pytest自动化测试框架实战前言#
  • 汽车工业制造领域与数字孪生技术的关联性研究​
  • Microsoft AZ-305 Exam Question
  • 迁移Oracle SH 示例 schema 到 PostgreSQL
  • 亚马逊广告进阶指南:长尾词应如何去挖掘
  • RapidRAW RAW 图像编辑器
  • 游戏开发学习记录
  • 码云创建分支
  • 分库分表之实战-sharding-JDBC绑定表配置实战
  • 掌握PDF转CAD技巧,提升工程设计效率
  • 模型内部进行特征提取时,除了“减法”之外,还有哪些技术
  • Android ttyS2无法打开该如何配置 + ttyS0和ttyS1可以
  • BEV感知算法:自动驾驶的“上帝视角“革命
  • c语言学习_函数递归2
  • 深度学习模型在C++平台的部署
  • Spring Boot微服务中集成gRPC实践经验分享
  • 1️⃣理解大语言模型