第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.pswp.cn/news/913723.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/913723.shtml
英文地址,請注明出處:http://en.pswp.cn/news/913723.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

身份證識別api-便捷生活與安全社會的雙重保障

身份證識別技術是人工智能和圖像處理領域的杰出產物之一,正逐步滲透到我們生活的方方面面。而最直觀的作用就是簡化身份證驗證流程。現如今,無論是銀行開戶、酒店入住還是政務辦理、線上支付,都需要輸入 身份證信息進行身份驗證,傳…

跨國企業進入中國市場:如何利用亞馬遜云科技文檔 MCP 服務器解決區域差異問題

業務場景 想象一下,您是一家美國科技公司的 IT 架構師,公司剛剛決定將業務擴展到中國市場。作為技術負責人,您需要規劃如何將現有的基于亞馬遜云科技的應用遷移到中國區域。然而,您很快發現中國區的云服務環境與您熟悉的全球區域…

WPF使用WebBrowser 解決href標簽target=_blank在瀏覽器窗口打開新鏈接而非窗體內部打開的問題

前言 最近在WPF中使用WebBrowser控件顯示網頁的時候遇到一個問題,由于網頁里面有大規模的連接標簽使用了target=_blank的屬性,導致打開的網頁不是在我們的程序內部,而是調用系統瀏覽器打開了我們的網頁內容,這種情況非常的影響用戶體驗。于是就有了這篇文章內容。本文將詳細…

制作MikTex本地包可用于離線安裝包

MikTex安裝包版本是basic-miktex-24.1-x64.exe。注:basic版本表示只安裝MikTex基本包,不安裝全部包。在能夠聯網的電腦上安裝MikTex軟件后,可以按以下步驟制作本地包庫。一、制作本地包庫1、新建一個文件夾,比如在D盤新建miktex-l…

Redis基礎的介紹與使用(一)(Redis簡介以及Redis下載和安裝)

0 引言 本系列用于和大伙兒一起入門Redis,主要包括Redis的下載,分別在終端,圖形顯示界面以及JAVA代碼中進行使用,適合給需要快速了解Redis是什么以及上手使用的朋友們,希望我用最簡單的語言來講清楚相關內容&#xff…

七牛云C++開發面試題及參考答案

智能指針的原理及應用場景是什么? 智能指針是 C 中用于管理動態分配內存的工具,其核心原理是通過 RAII(資源獲取即初始化)技術,將堆內存的生命周期與對象的生命周期綁定,從而避免手動管理內存帶來的內存泄…

【Python辦公】Excel橫板表頭轉豎版通用工具(GUI版本)橫向到縱向的數據重構

目錄 專欄導讀前言項目概述功能特性技術棧核心代碼解析1. 類結構設計2. 界面布局設計3. 滾動列表實現4. 數據轉換核心邏輯5. 預覽功能實現設計亮點1. 用戶體驗優化2. 技術實現優勢3. 代碼結構優勢使用場景擴展建議總結完整代碼結尾專欄導讀 ?? 歡迎來到Python辦公自動化專欄—…

C#項目 在Vue/React前端項目中 使用使用wkeWebBrowser引用并且內部使用iframe網頁外鏈 頁面部分白屏

如果是使用wkeWebBrowser的引用方式 非常有可能是版本問題導致的 問題分析 1. wkeWebBrowser 的局限性 不支持或不完全支持 ES6 語法(如 let, const, Promise, async/await) 缺少對現代 Web API 的支持(如 Intl, fetch, WebSocket&#xff0…

系統架構設計師論文分享-論微服務架構

我的軟考歷程 摘要 2023年2月,我所在的公司通過了研發紗線MES系統的立項,該系統為國內紗線工廠提供SAAS服務,旨在提高紗線工廠的數字化和智能化水平。我在該項目中擔任系統架構設計師一職,負責該項目的架構設計工作。本文結合我…

The History of Big Data

數據洪流悄然重塑世界的進程中,大數據的歷史是技術迭代與需求驅動的交響。從 2003 年分布式系統雛形初現,到 Hadoop 掀起開源浪潮,再到 Spark、容器化技術與深度學習的接力革新,以及 Hadoop 生態的興衰起落,大數據發展…

【JS逆向基礎】數據分析之正則表達式

前言:前面介紹了關于JS逆向所需的基本知識,比如前端三件套等,從這里開始就要進入到數據分析的范圍內了,當然對于一些小白而言一些基本的知識還是需要知道的,比如正則,XPATNY與BS4;三個內容用三篇…

Mac mini 高性價比擴容 + Crossover 游戲實測 全流程手冊

Mac mini 高性價比擴容 Crossover 游戲實測 全流程手冊 本文將圖文并茂地指導你如何: 為 M4 Mac mini 外置擴容(綠聯 USB4 硬盤盒 致態 TiPlus7100)安裝并配置 Crossover/Whisky 運行 Windows 應用實測游戲運行性能、診斷常見異常一、準備工…

【PyTorch】PyTorch中torch.nn模塊的卷積層

PyTorch深度學習總結 第七章 PyTorch中torch.nn模塊的卷積層 文章目錄PyTorch深度學習總結前言一、torch.nn模塊1. 模塊的基本組成部分1.1 層(Layers)1.2 損失函數(Loss Functions)1.3 激活函數(Activation Functions…

Rust簡潔控制流:if let與let else高效編程指南

文章目錄Rust簡潔控制流:if let與let else高效編程指南🎯 if let:專注單一匹配場景💡 if let核心優勢:🔄 if let與else搭配使用🚀 let else:錯誤處理與提前返回💎 let el…

upload-labs靶場通關詳解:第19關 條件競爭(二)

一、分析源代碼//index.php // 初始化變量:標記上傳狀態和錯誤消息 $is_upload false; $msg null;// 檢查是否通過POST方式提交了表單 if (isset($_POST[submit])) {// 引入自定義上傳類require_once("./myupload.php");// 生成基于時間戳的文件名&…

一天兩道力扣(3)

解法一:class Solution(object):def invertTree(self, root):if not root:return Noneroot.left, root.right root.right, root.leftself.invertTree(root.right)self.invertTree(root.left)return root解析:遞歸解法二:class Solution(obje…

jenkins2025安裝、插件、郵箱發送使用

Tips:卸載從新安裝(需要在C盤線先刪除.jenkins文件),然后換個默認瀏覽器從新安裝推薦的插件(不然安裝插件這一步會報錯,連接不到jenkins) 一、jenkins安裝 訪問jenkins官網:https://www.jenkins.io/download/ 雙擊war包開始下載…

vue中通過tabs 切換 時 顯示不同的echarts 特殊處理

需要進行特殊處理 比如強制 進行resize 的方法 不然 大小顯示會出現問題我先把全部的代碼弄上<script setup lang"ts"> import { ref, onMounted, onBeforeUnmount, nextTick } from vue import { useRoute } from vue-router import { message } from ant-des…

淺度解讀-(未完成版)淺層神經網絡-深層神經網絡

文章目錄淺層神經網絡的前向傳播計算流程矩陣在運算時形狀的變化激活函數的作用為什么要有激活函數反向傳播深層神經網絡參數超參數參數初始化初始化權重的值選擇淺層神經網絡的前向傳播 計算流程 #mermaid-svg-tMPs4IUCtqxvhJ24 {font-family:"trebuchet ms",verda…

【vben3源碼解讀】【useEcharts】【VueUse】詳解useEcharts這個hooks的作用與相關庫的使用(VueUse)

源代碼 import type { EChartsOption } from echarts;import type { Ref } from vue;import type { Nullable } from vben/types;import type EchartsUI from ./echarts-ui.vue;import { computed, nextTick, watch } from vue;import { usePreferences } from vben/preference…