从零搭建 AI 智能 PDF 问答工具:Streamlit+LangChain + 千问大模型实战

在日常工作与学习中,我们经常需要处理大量 PDF 文档 ------ 学术论文、行业报告、书籍资料、企业手册等。传统阅读方式效率低下,尤其是面对数百页的长文档时,快速定位关键信息、精准解答疑问变得十分困难。

随着大语言模型(LLM)与检索增强生成(RAG)技术的快速发展,智能 PDF 问答工具应运而生。它能让 AI "读懂" PDF 内容,支持自然语言提问并给出精准答案,大幅提升信息获取效率。本文将基于 Streamlit、LangChain 与阿里千问大模型,手把手带你从零搭建一款功能完整的 AI 智能 PDF 问答工具,全程实战,代码可直接复用。

一、技术选型:核心框架与工具解析

在动手开发前,先明确技术栈选型,确保工具适配性与开发效率。本项目核心技术组合为 Streamlit + LangChain + 阿里千问大模型 + FAISS 向量库,各组件作用如下:

1.1 Streamlit:快速搭建 Web 交互界面

Streamlit 是一款轻量级 Python Web 框架,专为数据科学与 AI 应用设计,无需前端基础,几行代码即可搭建美观的 Web 界面。相比 Flask、Django,它开发效率极高,支持实时交互、文件上传、会话状态管理等功能,完美适配本项目的 "文件上传 - 提问 - 回答" 交互流程。

1.2 LangChain:大模型应用开发核心框架

LangChain 是连接大语言模型与外部数据的核心框架,提供文档加载、文本分割、向量存储、检索问答、对话记忆等模块化组件,能快速构建 RAG 系统。它的核心价值是让大模型 "学会使用外部知识",避免回答幻觉,同时支持对话历史记忆,实现连续问答。

1.3 阿里千问大模型:高性能中文 LLM

本项目选用阿里千问(qwen3.5-plus)作为核心大模型,通过 DashScope(阿里灵积)API 调用。千问模型在中文理解、文本生成、逻辑推理方面表现优异,支持长文本上下文,且 API 调用稳定、性价比高,适合中文场景下的 PDF 问答需求。

1.4 FAISS:轻量级高效向量数据库

FAISS 是 Meta 开源的向量数据库,专为高维向量检索优化,支持快速存储与查询文本向量。本项目用它存储 PDF 文本块的向量数据,用户提问时,先检索与问题最相关的文本片段,再交给大模型生成答案,确保回答基于 PDF 原文,准确可靠。

二、项目整体架构:从 PDF 到答案的全流程

在编写代码前,先梳理项目整体架构,明确数据流转与核心模块分工,避免开发过程中逻辑混乱。本项目架构可分为5 大核心模块,流程清晰、层层递进:

  1. Web 交互层(Streamlit):负责用户交互,包括 API 密钥输入、PDF 文件上传、问题输入、答案展示、历史对话记录管理,是用户与系统的直接接口。
  2. 会话状态管理层:基于 Streamlit 的 session_state,存储对话记忆(ConversationBufferMemory)与聊天历史,确保多轮问答时上下文连贯,不会丢失对话信息。
  3. 文档处理模块(LangChain) :接收上传的 PDF 文件,完成加载→文本分割→向量生成→向量存储全流程,将非结构化 PDF 转化为可检索的向量数据。
  4. 检索问答模块(LangChain):用户提问后,先通过 FAISS 检索相关文本片段,再将 "问题 + 相关上下文 + 对话历史" 传入千问大模型,生成精准答案。
  5. 大模型调用层(DashScope):封装千问大模型与嵌入模型的 API 调用,负责与阿里灵积平台通信,处理请求与响应,确保模型调用稳定。

简单来说,整个流程就是:用户上传 PDF→系统解析并向量化→用户提问→系统检索相关内容→大模型生成答案→展示结果并记忆对话。下面进入实战开发环节,分步实现各模块功能。

三、环境准备与依赖安装

3.1 环境要求

  • Python 3.9+(建议 3.10,兼容性最佳)
  • 操作系统:Windows/macOS/Linux 均可
  • 网络:可访问阿里灵积 DashScope API(需注册账号获取 API 密钥)

3.2 安装核心依赖包

创建项目文件夹,在终端执行以下命令,安装项目所需依赖:

bash 复制代码
pip install streamlit langchain langchain-openai langchain-community langchain-text-splitters pypdf faiss-cpu python-dotenv

依赖包说明:

  • streamlit:Web 界面框架
  • langchain:核心 RAG 框架
  • langchain-openai:适配 OpenAI 接口(千问兼容此接口)
  • langchain-community:社区组件(PDF 加载、向量库等)
  • langchain-text-splitters:文本分割工具
  • pypdf:PDF 文件解析
  • faiss-cpu:CPU 版 FAISS 向量库
  • python-dotenv:环境变量管理

3.3 获取阿里灵积 API 密钥

  1. 访问阿里灵积控制台:https://bailian.console.aliyun.com/
  2. 注册 / 登录账号,进入 "API-KEY 管理"
  3. 创建 API 密钥,保存备用(后续代码需使用)

四、分步实现核心功能(代码实战)

项目共包含 2 个核心代码文件:main.py(Web 界面与交互逻辑)、scripts.py(PDF 处理与问答核心逻辑),以及 1 个测试用 PDF 文件(卢浮宫.pdf)。下面分步编写代码,详解关键逻辑。

4.1 编写 scripts.py:核心问答逻辑封装

scripts.py是项目的 "大脑",负责 PDF 加载、文本分割、向量生成、检索问答与大模型调用。代码如下:

python 复制代码
import os
from langchain.chains import ConversationalRetrievalChain
from langchain_community.document_loaders import PyPDFLoader
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.embeddings import DashScopeEmbeddings

def qa_agent(openai_api_key, memory, uploaded_file, question):
    # 1. 初始化千问大模型(兼容OpenAI接口)
    model = ChatOpenAI(
        model="qwen3.5-plus",  # 千问3.5增强版
        openai_api_key=os.getenv("DASHSCOPE_API_KEY"),  # DashScope API密钥
        openai_api_base="https://dashscope.aliyuncs.com/compatible-mode/v1"  # 千问API地址
    )

    # 2. 保存上传的PDF为临时文件
    file_content = uploaded_file.read()
    temp_file_path = "temp.pdf"  # 临时PDF路径
    with open(temp_file_path, "wb") as temp_file:
        temp_file.write(file_content)

    # 3. 加载PDF并分割文本
    loader = PyPDFLoader(temp_file_path)  # PDF加载器
    docs = loader.load()  # 加载PDF所有页面
    # 文本分割器:按中文语义分割,块大小500,重叠50(保证上下文连贯)
    text_splitter = RecursiveCharacterTextSplitter(
        chunk_size=500,
        chunk_overlap=50,
        separators=["\n", "。", "!", "?", ",", "、", ""]  # 中文分隔符
    )
    texts = text_splitter.split_documents(docs)  # 分割后的文本块

    # 4. 初始化嵌入模型(千问文本嵌入模型)
    embeddings_model = DashScopeEmbeddings(
        model="text-embedding-v2",  # 文本嵌入模型
        dashscope_api_key="替换为你的API密钥"
    )

    # 5. 生成向量并存储到FAISS
    db = FAISS.from_documents(texts, embeddings_model)  # 向量库
    retriever = db.as_retriever()  # 检索器

    # 6. 创建检索问答链(支持对话记忆)
    qa = ConversationalRetrievalChain.from_llm(
        llm=model,
        retriever=retriever,
        memory=memory  # 绑定对话记忆
    )

    # 7. 执行问答并返回结果
    response = qa.invoke({"chat_history": memory, "question": question})
    return response
关键逻辑详解:
  • 大模型初始化 :千问模型兼容 OpenAI 接口,通过openai_api_base指定千问 API 地址,无需额外适配,降低开发成本。
  • PDF 临时存储:Streamlit 上传的文件是字节流,需保存为临时 PDF 文件,供 PyPDFLoader 加载。
  • 中文文本分割 :使用RecursiveCharacterTextSplitter,设置中文分隔符(句号、逗号等),确保分割后的文本块语义完整,避免断句混乱。
  • 嵌入模型选择 :选用千问text-embedding-v2嵌入模型,与大模型同源,中文向量匹配度更高,检索结果更精准。
  • 对话记忆绑定 :通过ConversationalRetrievalChain绑定memory,自动记录对话历史,支持多轮连续问答。

4.2 编写 main.py:Web 界面与交互逻辑

main.py是项目的 "脸面",基于 Streamlit 搭建 Web 界面,处理用户输入、文件上传、结果展示与历史对话管理。代码如下:

python 复制代码
import streamlit as st
from langchain.memory import ConversationBufferMemory
from scripts import qa_agent

# 1. 初始化会话状态(对话记忆+聊天历史)
if "memory" not in st.session_state:
    st.session_state["memory"] = ConversationBufferMemory(
        return_messages=True,
        memory_key="chat_history",
        output_key="answer"
    )
if "chat_history" not in st.session_state:
    st.session_state["chat_history"] = []

# 2. 页面配置(标题、图标、布局)
st.set_page_config(
    page_title="AI智能PDF问答工具",
    page_icon="📑",
    layout="centered"
)

# 3. 主标题
st.title("📑 AI智能PDF问答工具")

# 4. API密钥输入区域
st.subheader("🔑 API设置")
api_col1, api_col2 = st.columns([3, 1])
with api_col1:
    openai_api_key = st.text_input(
        "请输入OpenAI API密钥",
        type="password",
        placeholder="sk-..."
    )
with api_col2:
    st.markdown("[获取API密钥](阿里云百炼的api_key的网址)")
st.divider()

# 5. 文件上传区域
st.subheader("📁 文件上传")
uploaded_file = st.file_uploader(
    "点击或拖拽PDF文件到此处",
    type="pdf",
    help="支持PDF格式文件"
)

# 6. 问题输入区域
st.subheader("❓ 提问")
question = st.text_input(
    "请输入您的问题...",
    disabled=not uploaded_file,  # 未上传PDF时禁用输入
    placeholder="例如:请总结PDF的主要内容..."
)

# 7. 核心处理逻辑(上传文件+输入问题后触发)
if uploaded_file and question:
    if not openai_api_key:
        st.warning("⚠️ 请先输入OpenAI API密钥")
    else:
        with st.spinner("🤖 AI正在思考中,请稍等..."):
            try:
                # 调用scripts.py中的问答函数
                response = qa_agent(
                    openai_api_key,
                    st.session_state["memory"],
                    uploaded_file,
                    question
                )
                # 展示答案
                st.subheader("📝 答案")
                st.write(response["answer"])
                # 更新会话历史
                st.session_state["chat_history"] = response["chat_history"]
            except Exception as e:
                st.error(f"❌ 处理过程中发生错误: {str(e)}")

# 8. 历史对话记录展示
if st.session_state["chat_history"]:
    st.divider()
    with st.expander("🗣️ 历史对话记录", expanded=False):
        st.write("---")
        # 遍历对话历史(用户问题+AI答案成对展示)
        for i in range(0, len(st.session_state["chat_history"]), 2):
            if i + 1 < len(st.session_state["chat_history"]):
                human_msg = st.session_state["chat_history"][i]
                ai_msg = st.session_state["chat_history"][i + 1]
                st.markdown(f"**👤 你**: {human_msg.content}")
                st.markdown(f"**🤖 AI**: {ai_msg.content}")
                if i < len(st.session_state["chat_history"]) - 2:
                    st.write("---")
        # 清空对话历史按钮
        if st.button("🔄 清空对话历史"):
            st.session_state["memory"] = ConversationBufferMemory(
                return_messages=True,
                memory_key="chat_history",
                output_key="answer"
            )
            st.session_state["chat_history"] = []
            st.success("✅ 对话历史已清空")
            st.experimental_rerun()

# 9. 使用说明
st.divider()
st.subheader("ℹ️ 使用说明")
st.write("1. 输入OpenAI API密钥")
st.write("2. 上传PDF文件")
st.write("3. 输入您的问题")
st.write("4. 查看AI的回答")
关键逻辑详解:
  • 会话状态初始化 :Streamlit 的session_state用于跨交互保存数据,初始化memory(对话记忆对象)与chat_history(对话历史列表),确保页面刷新后对话不丢失。
  • 交互体验优化 :未上传 PDF 时禁用问题输入框,避免无效操作;添加加载动画(st.spinner),提升等待体验;错误时展示具体异常信息,便于排查问题。
  • 历史对话管理 :用expander折叠历史对话,界面更简洁;支持一键清空对话历史,重置记忆对象,满足多文档问答需求。

4.3 测试用 PDF 文件:卢浮宫.pdf

为方便测试,准备一份简单的 PDF 文档(卢浮宫介绍),内容包含卢浮宫的历史、位置、开放时间等信息。上传后可提问:"卢浮宫的开放时间是什么?""卢浮宫的建筑始建于哪一年?" 等,验证问答准确性。

五、项目运行与功能测试

5.1 启动项目

在终端进入项目文件夹,执行以下命令启动 Streamlit 应用:

bash 复制代码
streamlit run main.py

启动成功后,浏览器会自动打开 Web 界面(默认地址:http://localhost:8501)。

5.2 功能测试步骤

  1. 输入 API 密钥 :在 "API 设置" 区域输入你的 DashScope API 密钥(注意:密钥以sk-开头)。
  2. 上传 PDF 文件:点击 "文件上传" 区域,选择 "卢浮宫.pdf" 上传,上传成功后可看到文件名。
  3. 输入问题并提问 :在 "提问" 区域输入问题,例如:
    • 问题 1:卢浮宫的建筑始建于哪一年?
    • 问题 2:卢浮宫现在的开放时间是怎样的?
    • 问题 3:请总结卢浮宫的主要信息。
  4. 查看答案:等待几秒后,AI 会基于 PDF 内容生成精准答案,展示在 "答案" 区域。
  5. 多轮对话测试:继续输入关联问题,例如:"卢浮宫的参观费用是什么时候开始收取的?",验证对话记忆是否生效,上下文是否连贯。
  6. 历史对话查看与清空:展开 "历史对话记录",可查看所有问答记录;点击 "清空对话历史",可重置对话,重新开始。

5.3 测试结果示例

以 "卢浮宫的开放时间是什么?" 为例,AI 生成的答案如下:

卢浮宫除周二公休和特殊假日外,通常向游客全面开放参观。在 1855 年至 1922 年期间,除周一外全天向公众免费开放;1922 年开始收费。1824 年起,公众可在星期日和节假日参观,其他日子仅对艺术家和外国游客开放。

答案完全基于 PDF 原文,准确无误,无幻觉信息,验证了系统的可靠性。

六、核心技术深度解析:RAG 如何让 AI 读懂 PDF?

本项目的核心是检索增强生成(RAG)技术,它解决了大模型 "知识过时""幻觉回答""无法处理长文档" 三大痛点。下面深度解析 RAG 的核心原理,帮助你理解系统背后的逻辑。

6.1 RAG 的核心流程:分 "离线处理" 与 "在线问答" 两阶段

阶段 1:离线文档处理(上传 PDF 时执行)
  1. 文档加载:用 PyPDFLoader 读取 PDF 内容,提取所有文本。
  2. 文本分割:将长文本分割为 500 字左右的短文本块(Chunk),避免超出大模型上下文限制,同时保证语义完整。
  3. 向量生成 :用嵌入模型(text-embedding-v2)将每个文本块转化为高维向量(1024 维),向量能精准表征文本语义,语义相似的文本向量距离更近。
  4. 向量存储:将所有文本块的向量与原文存储到 FAISS 向量库,建立索引,便于快速检索。
阶段 2:在线问答(用户提问时执行)
  1. 问题向量化:用户提问后,用同样的嵌入模型将问题转化为向量。
  2. 相似向量检索 :在 FAISS 向量库中,检索与问题向量距离最近的 Top-k 文本块(默认 k=3),这些文本块就是与问题最相关的上下文。
  3. Prompt 构建:将 "用户问题 + 检索到的相关上下文 + 对话历史" 拼接成 Prompt,传入大模型。
  4. 答案生成:大模型基于 Prompt 中的上下文,生成精准、可靠的答案,确保答案完全基于 PDF 原文,避免幻觉。

6.2 对话记忆原理:如何实现多轮连贯问答?

本项目使用 LangChain 的ConversationBufferMemory实现对话记忆,其核心原理是:自动记录每一轮的 "用户问题 + AI 答案",并在后续问答时,将历史对话自动拼接到 Prompt 中,让大模型能理解上下文,实现连贯问答。

例如:

  • 第一轮问题:卢浮宫始建于哪一年?
  • 第一轮答案:1190 年左右。
  • 第二轮问题:现在的开放时间呢?
  • Prompt 会自动拼接:"历史对话:用户:卢浮宫始建于哪一年?AI:1190 年左右。用户:现在的开放时间呢?",大模型据此生成答案。

七、总结与感悟

本文基于 Streamlit、LangChain 与阿里千问大模型,从零搭建了一款功能完整的 AI 智能 PDF 问答工具,实现了 PDF 上传、文本解析、向量检索、智能问答、对话记忆等核心功能。整个开发过程无需复杂的前端与算法基础,基于模块化框架快速落地,充分体现了大模型时代 "低代码开发 AI 应用" 的优势。

通过本项目,我们不仅掌握了 Streamlit Web 开发、LangChain RAG 系统搭建、大模型 API 调用等核心技能,更深入理解了检索增强生成(RAG)技术的核心原理 ------让 AI 学会使用外部知识,是解决大模型痛点的关键

在实际应用中,这款工具可广泛用于学术研究(论文精读)、职场办公(报告分析)、学习备考(资料梳理)等场景,大幅提升信息获取效率。同时,项目的优化与扩展方向也为后续迭代提供了清晰思路,可根据实际需求持续升级,打造更强大、更易用的智能文档问答工具。

最后,随着大模型技术的不断发展,AI 应用的开发门槛会持续降低,未来会有更多基于 RAG 技术的智能工具落地,赋能各行各业,让信息获取更高效、更智能。

相关推荐
奔跑中的小象3 小时前
统信UOS + 天数AI卡部署SGLang服务手册
人工智能·uos·sglang·天数智芯
DevSecOps选型指南3 小时前
中国版Mythos,为何是悬镜安全灵脉CodeAI?
人工智能·安全
Shockang3 小时前
LangGraph 状态机实战
人工智能
刹那芳华19923 小时前
循环神经网络的从零开始实现(RNN)
人工智能·rnn·深度学习
阿童木写作4 小时前
跨境图片翻译工具多合一,批量图片视频字幕翻译加智能抠图
人工智能·python·音视频·语音识别
科技之门4 小时前
AI3D从建模到贴图、绑骨和动画的完整流程怎么做?V2Fun完整工作流指南
人工智能·3d·贴图
宇宙第一小趴菜4 小时前
二、机器学习的应用领域和发展史
人工智能·机器学习
前端开发江鸟4 小时前
我写过 MCP Server,却一直以为 MCP 只有 Tool
人工智能
天国梦4 小时前
自习室智能化升级避坑指南:天学网AI智习室方案实测与选型建议
大数据·人工智能
阿里云云原生5 小时前
可用性从 99.9% 跃升至 99.995%:畅捷通如何用 AI 重塑运维底座?
运维·网络·人工智能