【进阶】Python编写自己的RAG AI系统应用(Gradio交互方案)

2025-02-15T21:33:26+08:00 | 5分钟阅读 | 更新于 2025-02-15T21:33:26+08:00

Macro Zhao

【进阶】Python编写自己的RAG AI系统应用(Gradio交互方案)

本章我们将使用Gradio构建一个Python应用程序,与本地的DeepSeek-R1模型进行交互,来查询和分析PDF类型的文档。

推荐超级课程:

@TOC

先决条件

在实施之前,请确保我们已安装以下工具和库:

  • Python 3.8+
  • Langchain:由大型语言模型(LLMs)提供支持的应用程序构建框架,实现轻松检索、推理和工具集成。
  • Chromadb:一种高性能的向量数据库,适用于高效相似性搜索和嵌入存储。
  • Gradio:用于创建用户友好的网络界面。

步骤 1、安装python库

运行以下命令以安装必要的依赖项:

pip install langchain chromadb gradio ollama pymupdf

上述依赖库安装完成后,创建主文件并在其中引入下列库:

import gradio as gr
from langchain_community.document_loaders import PyMuPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.vectorstores import Chroma
from langchain_community.embeddings import OllamaEmbeddings
import ollama

步骤 2、处理PDF文档文件

def process_pdf(pdf_bytes):
    if pdf_bytes is None:
        return None, None, None

    loader = PyMuPDFLoader(pdf_bytes)
    data = loader.load()

    text_splitter = RecursiveCharacterTextSplitter(
        chunk_size=500, chunk_overlap=100
    )
    chunks = text_splitter.split_documents(data)

    embeddings = OllamaEmbeddings(model="deepseek-r1")
    vectorstore = Chroma.from_documents(
        documents=chunks, embedding=embeddings, persist_directory="./chroma_db"
    )
    retriever = vectorstore.as_retriever()

    return text_splitter, vectorstore, retriever
    

process_pdf 函数:

  • 加载并准备PDF内容以进行基于检索的回答。
  • 检查是否上传了PDF。
  • 使用 PyMuPDFLoader 提取文本。
  • 使用 RecursiveCharacterTextSplitter 将文本分割成块。
  • 使用 OllamaEmbeddings 生成向量嵌入。
  • 将嵌入存储在Chroma向量存储中,以实现高效的检索。

步骤 3:合并检索到的文档块

一旦检索到嵌入,接下来我们需要将这些嵌入拼接在一起。combine_docs() 函数将多个检索到的文档块合并成一个字符串。

def combine_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)
    

由于基于检索的模型提取的是相关摘录而不是整个文档,这个函数确保在将提取的内容传递给DeepSeek-R1之前,内容保持可读和格式正确。

步骤 4:使用Ollama查询DeepSeek-R1

现在,我们的模型输入已经准备好了。让我们使用Ollama设置DeepSeek R1。

import re

def ollama_llm(question, context):
    formatted_prompt = f"Question: {question}\n\nContext: {context}"

    response = ollama.chat(
        model="deepseek-r1",
        messages=[{"role": "user", "content": formatted_prompt}],
    )

    response_content = response["message"]["content"]

    # Remove content between <think> and </think> tags to remove thinking output
    final_answer = re.sub(r"<think>.*?</think>", "", response_content, flags=re.DOTALL).strip()

    return final_answer
    

ollama_llm() 函数将用户的问题和检索到的文档上下文格式化为结构化的提示。然后,这种格式化的输入通过 ollama.chat() 发送到 DeepSeek-R1,它会在给定的上下文中处理问题并返回相关的答案。如果你需要不带模型思考脚本的答案,使用 strip() 函数来返回最终答案。

步骤 5:构建RAG流程

现在我们有了所有必要的组件,让我们为我们的演示构建RAG流程。

def rag_chain(question, text_splitter, vectorstore, retriever):
    retrieved_docs = retriever.invoke(question)
    formatted_content = combine_docs(retrieved_docs)
    return ollama_llm(question, formatted_content)

上述函数首先使用 retriever.invoke(question) 在向量存储中进行搜索,返回最相关的文档摘录。这些摘录通过 combine_docs 函数格式化为结构化输入,并发送到 ollama_llm,确保 DeepSeek-R1 根据检索到的内容生成有根据的答案。

步骤 6:创建Gradio界面

现在我们已经建立了RAG流程。接下来,我们可以在本地构建Gradio界面,并结合 DeepSeek-R1 模型来处理PDF输入并询问与之相关的问题。

def ask_question(pdf_bytes, question):
    text_splitter, vectorstore, retriever = process_pdf(pdf_bytes)

    if text_splitter is None:
        return None  # No PDF uploaded

    result = rag_chain(question, text_splitter, vectorstore, retriever)
    return {result}

interface = gr.Interface(
    fn=ask_question,
    inputs=[
        gr.File(label="上传PDF文档"),
        gr.Textbox(label="提问"),
    ],
    outputs="text",
    title="对你的PDF文档内容进行提问",
    description="使用 DeepSeek-R1 来回答你关于PDF文档内容相关的为你。",
)

interface.launch()

Interface 类有三个核心参数:

  • fn:要围绕用户界面(UI)包装的函数
  • inputs:用于输入的 Gradio 组件。组件的数量应与函数中的参数数量相匹配。
  • outputs:用于输出的 Gradio 组件。组件的数量应与函数返回值的数量相匹配。

至此,我们执行了以下步骤:

  • 检查是否上传了PDF。
  • 使用 process_pdf 函数处理PDF,以提取文本并生成文档嵌入。
  • 将用户的查询和文档嵌入传递给 rag_chain() 函数,以检索相关信息并生成上下文准确的响应。
  • 设置基于Gradio的网页界面,允许用户上传PDF并询问其内容的问题。
  • 使用 gr.Interface() 函数定义布局,接受PDF文件和文本查询作为输入。
  • 使用 interface.launch() 启动应用程序,通过网页浏览器实现无缝、交互式的基于文档的问答。

PS:

结论

使用Ollama在本地运行DeepSeek-R1可以实现更快、私密且成本效益高的模型推理。

通过简单的安装过程、CLI交互、API支持和Python集成,你可以将DeepSeek-R1用于各种AI应用,从一般查询到复杂的基于检索的任务。

完整程序示例


import gradio as gr
from langchain_community.document_loaders import PyMuPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.vectorstores import Chroma
from langchain_community.embeddings import OllamaEmbeddings
import ollama


def process_pdf(pdf_bytes):
    if pdf_bytes is None:
        return None, None, None

    loader = PyMuPDFLoader(pdf_bytes)
    data = loader.load()

    text_splitter = RecursiveCharacterTextSplitter(
        chunk_size=500, chunk_overlap=100
    )
    chunks = text_splitter.split_documents(data)

    embeddings = OllamaEmbeddings(model="bge-m3:latest")
    vectorstore = Chroma.from_documents(
        documents=chunks, embedding=embeddings, persist_directory="./chroma_db"
    )
    retriever = vectorstore.as_retriever()

    return text_splitter, vectorstore, retriever


def combine_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)


import re


def ollama_llm(question, context):
    formatted_prompt = f"Question: {question}\n\nContext: {context}"

    response = ollama.chat(
        model="deepseek-r1:7b",
        messages=[{"role": "user", "content": formatted_prompt}],
    )

    response_content = response["message"]["content"]

    # Remove content between <think> and </think> tags to remove thinking output
    final_answer = re.sub(r"<think>.*?</think>", "", response_content, flags=re.DOTALL).strip()

    return final_answer

def rag_chain(question, text_splitter, vectorstore, retriever):
    retrieved_docs = retriever.invoke(question)
    formatted_content = combine_docs(retrieved_docs)
    return ollama_llm(question, formatted_content)


def ask_question(pdf_bytes, question):
    text_splitter, vectorstore, retriever = process_pdf(pdf_bytes)

    if text_splitter is None:
        return None  # No PDF uploaded

    result = rag_chain(question, text_splitter, vectorstore, retriever)
    return {result}

interface = gr.Interface(
    fn=ask_question,
    inputs=[
        gr.File(label="上传PDF文档"),
        gr.Textbox(label="提问"),
    ],
    outputs="text",
    title="对你的PDF文档内容进行提问",
    description="使用 DeepSeek-R1 来回答你关于PDF文档内容相关的问题。",
)

interface.launch()

为了跟上AI的最新发展,我推荐以下博客:

© 2011 - 2025 Macro Zhao的分享站

关于我

如遇到加载502错误,请尝试刷新😄

Hi,欢迎访问 Macro Zhao 的博客。Macro Zhao(或 Macro)是我在互联网上经常使用的名字。

我是一个热衷于技术探索和分享的IT工程师,在这里我会记录分享一些关于技术、工作和生活上的事情。

我的CSDN博客:
https://macro-zhao.blog.csdn.net/

欢迎你通过评论或者邮件与我交流。
Mail Me

推荐好玩(You'll Like)
  • AI 动·画
    • 这是一款有趣·免费的能让您画的画中的角色动起来的AI工具。
    • 支持几十种动作生成。
我的项目(My Projects)
  • 爱学习网

  • 小乙日语App

    • 这是一个帮助日语学习者学习日语的App。
      (当然初衷也是为了自用😄)
    • 界面干净,简洁,漂亮!
    • 其中包含 N1 + N2 的全部单词和语法。
    • 不需注册,更不需要订阅!完全免费!
  • 小乙日文阅读器

    • 词汇不够?照样能读日语名著!
    • 越读积累越多,积跬步致千里!
    • 哪里不会点哪里!妈妈再也不担心我读不了原版读物了!
赞助我(Sponsor Me)

如果你喜欢我的作品或者发现它们对你有所帮助,可以考虑给我买一杯咖啡 ☕️。这将激励我在未来创作和分享更多的项目和技术。🦾

👉 请我喝一杯咖啡

If you like my works or find them helpful, please consider buying me a cup of coffee ☕️. It inspires me to create and share more projects in the future. 🦾

👉 Buy me a coffee