在这里插入图片描述

嗨,我是会编程的游戏君。

今天,我将带你完成一个真正的从零到一的项目。无论你是刚刚接触Python的新手,还是对AI应用开发感兴趣的初学者,这篇文章都将像一份详尽的"食谱",一步步指导你做出一个功能完整的AI应用。

我们的目标:在90分钟内,从空的文件夹开始,构建一个能读懂PDF文档并回答问题的Web应用。

第一章:环境准备 - 搭建你的"开发厨房"

1.1 检查基础环境

首先,我们需要确保你的电脑已经安装了必要的基础软件。

✅ 检查Python是否安装

  1. 打开命令行窗口:
    • Windows:按 Win + R,输入 cmd,按回车
    • Mac:按 Cmd + Space,输入 terminal,按回车
  2. 在命令行中输入:
    python --version
    
  3. 如果你看到类似 Python 3.8.0 的版本信息,说明Python已安装。如果显示"找不到命令",请前往 Python官网 下载并安装。

✅ 检查pip是否可用

pip --version

应该显示pip的版本信息。

1.2 安装必需的Python包

逐行执行以下命令,每行结束后等待安装完成:

pip install streamlit
pip install langchain
pip install langchain-community
pip install chromadb
pip install pypdf2
pip install python-docx
pip install sentence-transformers
pip install huggingface-hub

💡 重要提示:如果遇到网络问题,可以使用国内镜像源:

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple streamlit langchain langchain-community chromadb pypdf2 python-docx sentence-transformers huggingface-hub

1.3 安装和配置Ollama

Windows/Mac用户

  1. 访问 Ollama官网
  2. 点击下载,运行安装程序
  3. 安装完成后,打开新的命令行窗口

Linux用户

curl -fsSL https://ollama.ai/install.sh | sh

验证安装

ollama --version

应该显示Ollama的版本信息。

下载AI模型

ollama pull llama3.1:8b

这个命令会下载一个约4.6GB的模型文件,根据你的网络情况可能需要10-30分钟。

测试模型

ollama run llama3.1:8b

输入一些测试文字,比如"你好",如果看到AI的回复,说明模型正常工作。按 Ctrl+C 退出。

第二章:项目创建 - 建立你的"工作台"

2.1 创建项目文件夹

  1. 在桌面上右键,选择"新建文件夹"
  2. 将文件夹命名为 smart_doc_assistant
  3. 进入该文件夹,在地址栏中输入 cmd 并回车(Windows)或右键选择"新建位于文件夹位置的终端窗口"(Mac)

2.2 创建项目文件

smart_doc_assistant 文件夹中,创建以下文件:

文件1:requirements.txt - 项目依赖清单

streamlit>=1.28.0
langchain>=0.0.346
langchain-community>=0.0.10
chromadb>=0.4.15
pypdf2>=3.0.1
python-docx>=1.1.0
sentence-transformers>=2.2.2
huggingface-hub>=0.16.4

文件2:doc_analyzer.py - 主程序文件

我们将分模块创建这个文件,请跟着我的步骤一步步来:

第三章:代码实现 - 一步步搭建"智能大脑"

3.1 导入必要的库

打开 doc_analyzer.py,从这些导入语句开始:

# doc_analyzer.py
# -*- coding: utf-8 -*-

# 基础Python库
import os
import tempfile
import time
from pathlib import Path

# 网页应用框架
import streamlit as st

# 文档处理相关
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import PyPDFLoader, Docx2txtLoader

# 向量数据库和嵌入模型
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import HuggingFaceEmbeddings

# 大语言模型
from langchain_community.llms import Ollama

# 提示词模板
from langchain.prompts import PromptTemplate

print("✅ 所有库导入成功!")

3.2 创建文档分析器类

继续在同一个文件中添加:

class DocumentAnalyzer:
    """
    智能文档分析器 - 这是我们应用的核心引擎
    功能:处理文档、理解内容、回答问题
    """
    
    def __init__(self):
        """初始化分析器,设置各种组件"""
        print("🚀 正在启动智能文档分析器...")
        
        # 1. 初始化大语言模型 - 这是我们AI的"大脑"
        try:
            self.llm = Ollama(model="llama3.1:8b")
            print("✅ 大语言模型加载成功")
        except Exception as e:
            print(f"❌ 模型加载失败: {e}")
            print("请确保已运行: ollama pull llama3.1:8b")
            raise
        
        # 2. 初始化文本嵌入模型 - 用于理解文本含义
        try:
            self.embeddings = HuggingFaceEmbeddings(
                model_name="BAAI/bge-small-zh-v1.5",
                model_kwargs={'device': 'cpu'},  # 使用CPU,兼容性更好
                encode_kwargs={'normalize_embeddings': True}
            )
            print("✅ 文本嵌入模型加载成功")
        except Exception as e:
            print(f"❌ 嵌入模型加载失败: {e}")
            raise
        
        # 3. 初始化文本分割器 - 将长文档切分成小块
        self.text_splitter = RecursiveCharacterTextSplitter(
            chunk_size=1000,      # 每个文本块约1000个字符
            chunk_overlap=200,    # 块之间重叠200字符,避免切断句子
            length_function=len,  # 使用Python内置的len函数计算长度
            separators=["\n\n", "\n", "。", "!", "?", ";", ",", "、", ""]
        )
        print("✅ 文本分割器配置完成")
        
        # 4. 初始化向量数据库相关变量
        self.vectorstore = None   # 向量数据库实例
        self.retriever = None     # 检索器实例
        
        print("🎉 文档分析器初始化完成!")

3.3 实现文档处理功能

继续添加方法到 DocumentAnalyzer 类中:

    def process_document(self, file_path):
        """
        处理文档的完整流程
        步骤:加载 → 分割 → 向量化 → 存储
        """
        print(f"📄 开始处理文档: {file_path}")
        
        # 步骤1: 根据文件类型选择合适的加载器
        file_extension = os.path.splitext(file_path)[1].lower()
        
        if file_extension == '.pdf':
            loader = PyPDFLoader(file_path)
            print("🔍 使用PDF加载器")
        elif file_extension == '.docx':
            loader = Docx2txtLoader(file_path)
            print("🔍 使用Word文档加载器")
        else:
            error_msg = f"不支持的文件格式: {file_extension},请使用PDF或Word文档"
            print(f"❌ {error_msg}")
            raise ValueError(error_msg)
        
        try:
            # 步骤2: 加载文档
            print("⏳ 正在加载文档内容...")
            raw_documents = loader.load()
            print(f"📝 原始文档加载完成,共 {len(raw_documents)} 页/节")
            
            # 步骤3: 分割文档
            print("✂️ 正在分割文档...")
            document_chunks = self.text_splitter.split_documents(raw_documents)
            print(f"📋 文档分割完成,共 {len(document_chunks)} 个文本块")
            
            # 显示一些样本,帮助理解分割效果
            if len(document_chunks) > 0:
                sample_chunk = document_chunks[0].page_content[:100] + "..."
                print(f"📖 文本块样例: {sample_chunk}")
            
            # 步骤4: 创建向量数据库
            print("🧠 正在构建知识库(向量化)...")
            self.vectorstore = Chroma.from_documents(
                documents=document_chunks,
                embedding=self.embeddings,
                persist_directory="./vector_store"  # 知识库保存位置
            )
            
            # 步骤5: 创建检索器
            self.retriever = self.vectorstore.as_retriever(
                search_type="similarity",
                search_kwargs={"k": 3}  # 每次检索3个最相关的文本块
            )
            
            print("✅ 文档处理完成!知识库已就绪")
            return True
            
        except Exception as e:
            print(f"❌ 文档处理过程中出错: {e}")
            raise

3.4 实现问答功能

继续添加方法:

    def ask_question(self, question):
        """
        向文档提问的核心方法
        步骤:检索相关文本 → 构建提示 → 生成答案
        """
        print(f"🤔 接收到问题: {question}")
        
        # 检查是否已经处理过文档
        if self.retriever is None:
            error_msg = "请先上传并处理文档"
            print(f"❌ {error_msg}")
            return error_msg
        
        try:
            # 步骤1: 检索相关文档片段
            print("🔎 正在检索相关文档内容...")
            start_time = time.time()
            relevant_docs = self.retriever.get_relevant_documents(question)
            retrieval_time = time.time() - start_time
            print(f"📚 检索到 {len(relevant_docs)} 个相关文本块,耗时 {retrieval_time:.2f} 秒")
            
            # 显示检索到的内容片段(调试用)
            for i, doc in enumerate(relevant_docs):
                preview = doc.page_content[:80] + "..." if len(doc.page_content) > 80 else doc.page_content
                print(f"   {i+1}. {preview}")
            
            # 步骤2: 构建上下文
            context = "\n\n".join([doc.page_content for doc in relevant_docs])
            
            # 步骤3: 构建提示词
            prompt_template = """
请你作为一个专业的文档分析助手,严格根据以下上下文信息回答问题。

重要要求:
1. 答案必须完全基于提供的上下文信息
2. 如果上下文信息不足以回答问题,请明确说明"根据文档内容,我无法找到相关信息"
3. 不要编造任何不在上下文中的信息
4. 回答应该准确、简洁、有用

上下文信息:
{context}

用户问题:{question}

请根据以上上下文信息回答问题:
"""
            prompt = PromptTemplate.from_template(prompt_template)
            formatted_prompt = prompt.format(context=context, question=question)
            
            # 步骤4: 生成答案
            print("💭 AI正在思考并生成答案...")
            start_time = time.time()
            answer = self.llm.invoke(formatted_prompt)
            generation_time = time.time() - start_time
            print(f"✅ 答案生成完成,耗时 {generation_time:.2f} 秒")
            
            return answer
            
        except Exception as e:
            error_msg = f"回答问题过程中出错: {e}"
            print(f"❌ {error_msg}")
            return error_msg

第四章:用户界面 - 打造"友好面孔"

4.1 创建Streamlit界面

在同一个文件的末尾添加:

def setup_streamlit_ui():
    """
    配置和创建Streamlit用户界面
    """
    # 页面基础配置
    st.set_page_config(
        page_title="智能文档分析助手 - 游戏君制作",
        page_icon="📚",
        layout="wide",
        initial_sidebar_state="expanded"
    )
    
    # 页面标题和介绍
    st.title("📚 智能文档分析助手")
    st.markdown("""
    **欢迎使用!** 这是一个能够理解文档内容并回答问题的AI助手。
    
    ### 🎯 功能特点:
    - 📖 支持PDF和Word文档
    - 🧠 深度理解文档语义
    - ❓ 智能回答文档相关问题
    - 🔒 100%本地运行,保护隐私
    - ⚡ 快速响应,准确可靠
    """)
    
    # 初始化会话状态
    if 'analyzer' not in st.session_state:
        st.session_state.analyzer = DocumentAnalyzer()
        st.success("✅ AI助手初始化完成!")
    
    if 'document_processed' not in st.session_state:
        st.session_state.document_processed = False
    
    if 'current_filename' not in st.session_state:
        st.session_state.current_filename = None

def handle_file_upload():
    """
    处理文件上传逻辑
    """
    st.markdown("---")
    st.subheader("📤 第一步:上传文档")
    
    uploaded_file = st.file_uploader(
        "选择PDF或Word文档",
        type=['pdf', 'docx'],
        help="支持的文件格式:PDF (.pdf), Word (.docx)",
        key="file_uploader"
    )
    
    if uploaded_file is not None:
        # 显示文件信息
        file_size = len(uploaded_file.getvalue()) / 1024  # KB
        st.info(f"📄 已选择文件: **{uploaded_file.name}** ({file_size:.1f} KB)")
        
        # 保存上传的文件到临时位置
        with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(uploaded_file.name)[1]) as tmp_file:
            tmp_file.write(uploaded_file.getvalue())
            tmp_file_path = tmp_file.name
        
        # 处理文档按钮
        if st.button("🚀 开始分析文档", type="primary", use_container_width=True):
            with st.spinner("⏳ 正在深度分析文档内容,这可能需要一些时间,请耐心等待..."):
                try:
                    success = st.session_state.analyzer.process_document(tmp_file_path)
                    if success:
                        st.session_state.document_processed = True
                        st.session_state.current_filename = uploaded_file.name
                        st.success(f"✅ 文档 `{uploaded_file.name}` 分析完成!现在可以提问了")
                        
                        # 显示预设问题建议
                        show_question_suggestions()
                        
                except Exception as e:
                    st.error(f"❌ 文档处理失败: {str(e)}")
                    st.info("💡 建议:检查文件是否损坏,或尝试使用其他文档")
        else:
            st.info("💡 点击上面的按钮开始分析文档")
        
        # 清理临时文件
        try:
            os.unlink(tmp_file_path)
        except:
            pass

def show_question_suggestions():
    """
    显示预设问题建议
    """
    st.subheader("💡 试试这些问题:")
    
    col1, col2, col3 = st.columns(3)
    
    suggested_questions = [
        "用3-5个要点总结这份文档的核心内容",
        "文档的主要观点和结论是什么?",
        "找出文档中提到的关键数据、日期或人物",
        "文档的结构是怎样的?有哪些主要章节?",
        "作者想要解决什么问题或传达什么信息?"
    ]
    
    with col1:
        if st.button(suggested_questions[0], use_container_width=True):
            st.session_state.auto_question = suggested_questions[0]
    with col2:
        if st.button(suggested_questions[1], use_container_width=True):
            st.session_state.auto_question = suggested_questions[1]
    with col3:
        if st.button(suggested_questions[2], use_container_width=True):
            st.session_state.auto_question = suggested_questions[2]
    
    col4, col5 = st.columns(2)
    with col4:
        if st.button(suggested_questions[3], use_container_width=True):
            st.session_state.auto_question = suggested_questions[3]
    with col5:
        if st.button(suggested_questions[4], use_container_width=True):
            st.session_state.auto_question = suggested_questions[4]

def handle_question_answering():
    """
    处理问答交互逻辑
    """
    if st.session_state.document_processed:
        st.markdown("---")
        st.subheader("🤔 第二步:向文档提问")
        
        # 问题输入框
        question = st.text_input(
            "输入你的问题:",
            placeholder="例如:总结一下第三章的主要内容是什么?",
            key="question_input",
            value=st.session_state.get('auto_question', '')
        )
        
        # 提问按钮
        col1, col2 = st.columns([3, 1])
        with col1:
            if st.button("🔍 获取答案", type="primary", use_container_width=True) and question:
                with st.spinner("🧠 AI正在仔细分析文档,为你寻找最佳答案..."):
                    answer = st.session_state.analyzer.ask_question(question)
                
                # 显示答案
                st.markdown("---")
                st.subheader("📝 答案:")
                st.success(answer)
                
                # 答案质量反馈
                st.markdown("---")
                st.caption("这个答案对你有帮助吗?")
                feedback_col1, feedback_col2, feedback_col3 = st.columns(3)
                with feedback_col1:
                    st.button("👍 有帮助", use_container_width=True)
                with feedback_col2:
                    st.button("👎 不准确", use_container_width=True)
                with feedback_col3:
                    st.button("🔄 再问一个", use_container_width=True)
        
        with col2:
            if st.button("🔄 清理对话", use_container_width=True):
                st.session_state.auto_question = ""
                st.rerun()

def setup_sidebar():
    """
    配置侧边栏信息
    """
    with st.sidebar:
        st.header("ℹ️ 使用指南")
        
        st.markdown("""
        **使用步骤:**
        1. 📤 上传PDF或Word文档
        2. 🚀 点击"分析文档"按钮
        3. 🤔 输入你的问题
        4. 🔍 获取AI生成的答案
        
        **支持的功能:**
        - 文档内容总结
        - 关键信息提取
        - 章节内容分析
        - 语义理解问答
        
        **技术架构:**
        - 前端:Streamlit
        - AI框架:LangChain
        - 本地模型:Ollama + Llama 3.1
        - 向量数据库:Chroma
        """)
        
        st.markdown("---")
        st.header("⚙️ 系统状态")
        
        if st.session_state.document_processed:
            st.success("✅ 文档已就绪")
            st.info(f"📄 当前文档: {st.session_state.current_filename}")
        else:
            st.warning("⏳ 等待文档上传")
        
        # 系统操作
        st.markdown("---")
        st.header("🔧 操作")
        
        if st.button("🗑️ 重置系统", type="secondary"):
            # 重置会话状态
            for key in list(st.session_state.keys()):
                del st.session_state[key]
            st.rerun()

def main():
    """
    主函数 - 协调所有界面组件
    """
    setup_streamlit_ui()
    setup_sidebar()
    handle_file_upload()
    handle_question_answering()
    
    # 页脚信息
    st.markdown("---")
    st.caption("智能文档分析助手 | 由会编程的游戏君制作 | 基于LangChain和Ollama技术")

# 程序入口
if __name__ == "__main__":
    main()

第五章:运行和测试 - 启动你的应用

5.1 启动应用

  1. 确保你在 smart_doc_assistant 文件夹中

  2. 在命令行中运行:

    streamlit run doc_analyzer.py
    
  3. 等待几秒钟,浏览器会自动打开,显示你的智能文档助手界面

5.2 测试完整流程

测试步骤

  1. 准备一个PDF文档(可以从网上下载一篇技术文章或使用你的简历)
  2. 在界面中点击"上传文档",选择你的PDF文件
  3. 点击"开始分析文档"按钮
  4. 等待处理完成(会有成功提示)
  5. 在问题输入框中输入:“总结这份文档的主要内容”
  6. 点击"获取答案"按钮
  7. 查看AI生成的答案

5.3 预期效果

如果一切正常,你应该看到:

  • ✅ 文件上传成功提示
  • ✅ 文档分析完成提示
  • ✅ AI生成的文档总结内容
  • ✅ 界面响应流畅

第六章:故障排除 - 遇到问题怎么办

6.1 常见问题及解决方案

❌ 问题1:启动时报错 “ModuleNotFoundError”

解决方案:手动安装缺失的包
pip install 缺失的包名

❌ 问题2:Ollama连接失败

解决方案:确保Ollama服务正在运行
1. 打开新的命令行窗口
2. 运行:ollama serve
3. 保持这个窗口打开,在另一个窗口运行Streamlit

❌ 问题3:文档处理时间太长

解决方案:调整文本分割参数
在代码中修改:
chunk_size=500  # 减小块大小
chunk_overlap=100  # 减小重叠大小

❌ 问题4:内存不足错误

解决方案:使用更小的模型
ollama pull llama3:8b  # 如果用的是更大的模型,换小一点的

6.2 调试技巧

如果遇到问题,可以:

  1. 查看命令行输出:所有处理日志都会显示在运行Streamlit的命令行窗口中
  2. 使用小文件测试:先用一个1-2页的简单PDF测试
  3. 分步调试:注释掉部分代码,逐步测试每个功能模块

第七章:项目总结与扩展

7.1 我们构建了什么

通过这个项目,你成功创建了一个完整的AI应用,包含:

  • 文档处理管道:PDF/Word解析、文本分割、向量化存储
  • 智能问答系统:语义检索、提示词工程、答案生成
  • 用户友好界面:Streamlit Web界面、实时反馈、预设问题
  • 本地化部署:完全离线运行、数据隐私保护

7.2 扩展思路

想要进一步改进这个项目?可以考虑:

  1. 支持更多格式

    # 添加PPT支持
    from langchain_community.document_loaders import UnstructuredPowerPointLoader
    
  2. 添加对话历史

    # 在类中添加
    self.conversation_history = []
    
  3. 批量处理文档

    # 支持整个文件夹的文档
    def process_folder(self, folder_path):
    
  4. 部署到服务器

    # 使用Docker容器化部署
    docker build -t doc-assistant .
    

这就是本期的分享,我是游戏君,我们下期再见!

实践任务:成功运行这个应用后,在评论区分享你测试的文档类型和AI给出的最让你惊讶的答案。

下期预告:我们将学习如何将这个本地应用部署到云服务器,让你的团队成员也能通过链接访问使用!

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐