无代码,纯调试总结。
实现知识助手的 app.py 关键信息摘要
本文档总结 app.py 关键信息摘要
本文档总结了 app.py 脚本的核心组件、工作流程和使用的模型。
1. 核心技术栈
- Web 框架:
Streamlit- 用于构建交互式 Web UI。 - 向量数据库:
ChromaDB- 以持久化模式 (PersistentClient) 运行,数据存储在本地的./my_db文件夹中。 - PDF 解析库:
PyPDF(PdfReader) - 用于从 PDF 文件中提取纯文本内容。注意:此版本无法处理图片或扫描件中的文字。 - AI 客户端:
OpenAI- 作为 API 端点交互的客户端。
2. 使用的 AI 模型及服务
- API 端点:
https://api.**********.com/llm/v1 - Embedding 模型:
qwen3-embedding-8b- 用于将文本(文档片段和用户提问)转换为向量。 - 聊天生成模型:
glm-4.7- 用于根据检索到的上下文和用户问题生成最终答案。
3. 工作流程 (RAG)
应用包含两个主要的工作流程:
A. 数据入库流程 (当点击“开始处理入库”时)
- 读取 PDF: 使用
extract_text_from_pdf函数,通过PdfReader逐页提取文本。 - 文本切分: 使用
split_text_into_chunks函数将完整的文本内容切分成大小约 500 字符、重叠 50 字符的片段。 - 向量化: 遍历每一个文本片段,调用
get_embedding函数(使用qwen3-embedding-8b模型)生成向量。 - 存入数据库: 使用
collection.add()方法,将文本片段、对应的向量和 ID 批量存入 ChromaDB 的manual_docs集合中。
B. 问答检索流程 (当用户在聊天框提问时)
- 捕获问题: 通过
st.chat_input获取用户输入的prompt。 - 问题向量化: 调用
get_embedding函数将用户的prompt转换为一个查询向量q_vec。 - 数据库检索: 使用
collection.query()方法,传入查询向量q_vec,在数据库中查找最相似的5个文本片段。 - 构建上下文: 将检索到的 5 个文本片段组合成一个完整的上下文
best_context。 - 生成答案: 调用
client_ai.chat.completions.create方法(使用glm-4.7模型),将历史聊天记录、best_context和用户问题一起发送给大模型,以流式(打字机效果)生成并显示答案。 - 保存记录: 将用户的提问和 AI 的回答存入
st.session_state.messages中,以实现多轮对话记忆。
4. 关键函数
init_db(): 初始化并缓存 ChromaDB 的连接。extract_text_from_pdf(): 负责读取 PDF 文件。split_text_into_chunks(): 负责将长文本切片。get_embedding(): 负责调用 Embedding 模型生成向量。
app_with_ocr.py 升级摘要
app_with_ocr.py 是 app.py 的一个重大升级版本,核心目标是解决原版本无法处理 PDF 中图片和扫描页的问题。
1. 核心技术栈变更
-
PDF 解析库升级:
- 从:
PyPDF(PdfReader) - 升级为:
PyMuPDF(fitz) - 原因:
PyMuPDF提供了强大的功能,不仅能提取文本,还能从 PDF 页面中提取图片数据,这是实现 OCR 的基础。
- 从:
-
新增图像处理能力:
- 引入
Pillow(PIL): 用于将PyMuPDF提取出的原始图片字节流转换为可处理的图片对象。 - 引入
pytesseract: 作为 Python 连接 Tesseract OCR 引擎(需要安装并启动.exe)的桥梁,负责将图片对象中的文字识别出来。 - 引入
time: 用于监控和计算 Tesseract OCR 过程的耗时,便于调试。
- 引入
2. 数据入库流程的重大改进
数据入库的核心函数 extract_text_from_pdf 被完全重构为 extract_text_from_pdf_with_ocr,采用了更先进的“文本与图片均处理”策略。
新的提取流程 (逐页进行):
- 总是先提取直接文本: 使用
page.get_text()提取页面上所有可选中的文本。 - 总是再提取图片: 无论上一步是否提取到文本,程序都会继续使用
page.get_images()查找当前页的所有图片。 - 调用 OCR: 对找到的每一张图片,执行以下操作:
a. 启动监控: 打印日志并开始计时。
b. 调用 Tesseract: 使用pytesseract.image_to_string()对图片进行文字识别。
c. 结束监控: 打印 OCR 过程的耗时,并根据识别结果(成功、无文字、失败)输出详细的诊断日志。 - 合并内容: 将该页的“直接文本”和所有图片的“OCR 结果”合并为该页的最终文本内容。
这个新策略解决了原版本最大的痛点:
- 支持扫描件: 能够处理完全由图片组成的 PDF 页面。
- 支持图文混合页: 能够同时提取一页中的普通文本和图片中的文字,避免了信息丢失。
3. 关键函数变更
extract_text_from_pdf()被替换为extract_text_from_pdf_with_ocr(),实现了上述的全新提取逻辑。- 其他核心函数(
init_db,split_text_into_chunks,get_embedding)和问答检索流程保持不变,展现了良好代码架构的模块化优势。
app_with_vlm.py 升级摘要
app_with_vlm.py 是对 app_with_ocr.py 的终极进化,它用一个强大的多模态视觉语言模型 (VLM) 替换了传统的 OCR 引擎,实现了从“文字识别”到“图像理解”的跨越。
1. 核心技术栈与模型变更
-
引入视觉语言模型 (VLM):
- 这是本次升级的核心。应用现在引入了一个新的 AI 模型角色:“图像分析师”。
- 新增模型: 一个支持视觉功能的聊天模型(代码中占位符为
gpt-4-vision-preview)。注意:实际模型名称需根据 API 服务提供方确认。 - 目的: 不再是简单地识别图片中的字符,而是能够理解、分析和描述复杂的图片内容,如时序图、架构图、流程图等。
-
技术替换:
- 从:
pytesseract - 升级为: 直接调用支持视觉的
client_ai.chat.completions.createAPI。 - 原因: VLM 的能力远超 OCR,能提供对图片内容的深度理解和文字描述,而不仅仅是提取字符。
- 从:
-
新增辅助库:
- 引入
base64: 用于将图片数据编码成 Base64 字符串,这是通过 JSON 将图片传递给 VLM API 的标准格式。
- 引入
2. 数据入库流程的终极形态
此版本采用了“文本化存储”策略,将所有非文本信息(图片、图表)都预处理成高质量的文本描述,再存入知识库。
新的提取流程 (逐页进行):
- 提取直接文本: 流程不变,首先提取页面上的可选中文本。
- 提取图片: 流程不变,接着提取页面上的所有图片。
- 调用 VLM 进行图像理解 (关键变更):
a. 对找到的每一张图片,调用新增的describe_image_with_vlm函数。
b. 在该函数内部,图片被编码为 Base64,并连同一个描述任务的 Prompt (例如:“详细描述这张图片的内容...”) 发送给 VLM 模型。
c. VLM 返回对图片的详细文字描述。 - 合并内容: 将该页的“直接文本”和 VLM 生成的“图片描述”文本(用
[图片描述开始/结束]标签包裹以区分)合并为该页的最终内容。
这个新策略的优势是:
- 理解复杂信息: 能够处理 OCR 完全无法应对的图表和流程图,将它们的内在含义转换为可检索的文本。
- 知识的统一性: 无论原始信息是文本还是图片,最终都以高质量的文本形式存入向量数据库,简化了后续的检索流程。
- 架构优雅: 无需改动向量数据库的结构或检索逻辑,仅在数据预处理阶段引入 VLM 作为“分析师”即可。
3. 关键函数变更
- 新增
describe_image_with_vlm(): 这是一个全新的函数,封装了调用视觉大模型进行图片分析的所有逻辑。 extract_text_from_pdf_with_ocr()再次进化: 其内部的图片处理逻辑由调用pytesseract变更为调用describe_image_with_vlm()。
至此,该应用已从一个简单的文本问答助手,演进为一个能够深度理解图文混合文档的、真正的多模态知识库问答系统。
app_with_vlm.py 最终优化版解析
app_with_vlm.py 是项目的最终形态,它不仅是一个多模态知识库问答系统,更集成了多项性能和成本优化措施,使其变得高效、稳定且易于维护。
1. 核心架构:三模型分离 + 多重优化
此版本采用了先进的 “三模型分离” 架构,并在此基础上实现了 “四重优化”,是生产级 RAG 应用的典范。
-
三模型分离架构 (Three-Model Architecture):
- VLM 模型 (图像理解):
Gemini_25_Flash,通过定制的OpenAIAdapter访问,负责将图片内容(如图表、流程图、UI截图)转化为详细的文本描述。 - Embedding 模型 (文本向量化):
qwen3-embedding-8b,负责将所有文本内容(原文 + 图片描述)转换为向量,用于存储和检索。 - Chat Completion 模型 (生成答案):
glm-4.7,负责基于检索到的上下文,生成最终的对话回答。
- VLM 模型 (图像理解):
-
四重核心优化 (Four-Layer Optimization):
- 文件级缓存 (File-level Caching): 首次处理PDF后,将提取的全部内容(含元数据)保存为 JSON 缓存文件。后续若文件未变,则直接从缓存加载,彻底跳过耗时的PDF解析和VLM分析过程。
- 图像去重 (Image Deduplication): 在单次文件处理中,对每张图片计算哈希值。对于内容相同的图片,只调用一次VLM进行分析,后续直接复用结果,显著降低API调用成本和延迟。
- API分批处理 (API Batching): 在生成 Embedding 时,将成千上万的文本块分批(例如每批100个)发送给API。这从根本上解决了
413 Request Entity Too Large的错误。 - 数据库分批写入 (DB Batch Ingestion): 将数据分批次写入 ChromaDB,而不是在内存中准备好所有数据后一次性写入。这极大地降低了处理大文件时的内存消耗,使应用更加稳定。
2. 知识库构建流程 (数据入库)
数据入库流程经过精心设计,整合了上述所有优化策略。
- 文件上传与哈希计算: 用户上传 PDF,系统计算文件内容的
SHA256哈希值。 - 缓存检查: 系统根据哈希值查找对应的
.json缓存文件。- 命中缓存: 直接从 JSON 文件加载已处理好的内容列表,流程跳转到第 5 步。
- 未命中缓存: 执行完整的提取流程(第 3、4 步)。
- 内容提取与元数据附加 (Extract & Tag):
- 使用
PyMuPDF (fitz)逐页提取文本和图片。 - 对每一份内容(文本或图片描述),都附加上元数据,例如:
{"source_file": "文件名.pdf", "page_number": 5}。
- 使用
- VLM 图像理解 (核心步骤):
- 对提取的每张图片,计算其内容的哈希值。
- 检查内存缓存
image_cache: 如果该哈希值已存在,则直接复用描述;否则,调用 VLM 模型生成描述,并存入image_cache。 - VLM 生成的描述同样被附加上元数据。
- 缓存保存: 如果本次执行了提取流程,则将包含完整元数据的内容列表写入
.json缓存文件,供未来使用。 - 文本分块 (Chunking): 将所有内容(文本和图片描述)切分成更小的片段,同时确保每个片段都继承其原始的元数据。
- 分批向量化与入库 (Batch & Ingest):
- 启动一个循环,按
BATCH_SIZE(例如100) 对所有文本块进行迭代。 - 在循环的每一步内,完成一小批数据的“向量生成”和“数据库写入”操作。
- 这个“生成一点、写入一点”的模式确保了高效和低内存占用。
- 启动一个循环,按
步骤 1/3: 提取PDF内容...任务: 这个阶段是纯粹的“阅读和分析”。它会完整地读取你上传的 PDF 文件,提取所有文本,并调用 VLM 模型去理解和描述所有的图片。
产出: 所有这些处理完的内容(文本和图片描述)会被收集到一个临时的列表变量 all_content 中。
数据库操作: 无。
步骤 2/3: 正在将内容切分成小块...任务: 接收上一步生成的 all_content 列表,然后把里面每一项的长文本(无论是原文还是图片描述)切分成适合存入数据库的小块(Chunks)。
产出: 一个包含所有小文本块的列表 chunks 和一个包含对应元数据的列表 metadatas。
数据库操作: 无。
步骤 3/3: 正在处理批次...任务: 这是唯一一个与数据库写入相关的步骤。它会循环遍历上一步生成的所有小文本块。
具体操作: 在循环的每一步里,它会:
取一小批(100个)文本块。
为这一批文本块生成向量(Embeddings)。
将这一批的文本、向量和元数据一起写入 ChromaDB 数据库。
数据库操作: 有,并且是分批次写入。
3. 问答检索流程 (RAG)
问答流程利用了强大的元数据过滤功能,实现了精确检索。
- 范围选择 (Scope Selection): UI上提供一个单选框,允许用户选择是在“所有文档中检索”还是在某一个特定文件内进行检索。
- 构建
where过滤器: 根据用户的选择,动态构建 ChromaDB 的where查询条件。- 如果选择特定文件(如
report.pdf),则过滤器为{"source_file": "report.pdf"}。 - 如果选择所有文档,则不使用过滤器 (
None)。
- 如果选择特定文件(如
- 问题向量化: 将用户提问通过 Embedding 模型转换为查询向量。
- 数据库精确查询: 调用
collection.query()方法,同时传入查询向量和where过滤器。数据库只会从符合元数据条件的文档中查找相似片段。 - 生成答案与展示依据:
- 将检索到的上下文和问题提交给 Chat 模型生成答案。
- 在前端的“参考依据”部分,清晰地展示每个上下文片段来自哪个文件的哪一页,极大地提升了答案的可信度和可追溯性。
4. 关键函数与设计变更
-
extract_text_from_pdf_with_ocr():- VLM 提示词 (Prompt): 内含一个为电气、通信、测试和用户操作手册领域深度定制的、高度优化的提示词。它指导 VLM 扮演领域专家,并根据图片类型(UI截图、电路图、时序图等)执行不同的分析任务,以生成最精准、简洁的描述。
- 图像去重: 函数内部实现了基于哈希的图像去重逻辑 (
image_cache)。 - 元数据附加: 为所有提取出的文本和图片描述附加来源文件和页码等元数据。
-
侧边栏处理逻辑: 这是整个入库流程的核心,包含了文件缓存检查、调用提取函数、分块以及分批处理的循环。
-
split_content_into_chunks(): 被重构以支持带元数据的字典列表,确保分块后元数据不丢失。 -
主聊天界面: 新增了
st.radio用于范围选择,并且collection.query调用中增加了where参数以实现精确过滤。
Streamlit 用法总结
check_db.py 是一个独立的数据面板应用,用于可视化和管理 ChromaDB 数据库。它集中展示了多种 Streamlit 的核心功能和高级应用技巧。
1. 页面配置与布局 (st.set_page_config, st.title, st.divider)
- 知识点: 使用
st.set_page_config(layout="wide")实现宽屏布局,并通过st.title,st.header,st.divider等组件构建清晰的页面信息结构。 - 应用: 脚本通过这些组件划分出“摘要”、“内容详情”、“危险操作”等区域,提供了优秀的用户视觉引导。
2. 缓存与资源管理 (@st.cache_resource)
- 知识点:
@st.cache_resource装饰器用于缓存“重资源”(如数据库连接),避免在每次页面重载时重复创建。 - 应用:
connect_to_db函数使用此装饰器,确保与 ChromaDB 的连接只建立一次,极大地提升了应用性能,是 Streamlit 开发的最佳实践。
3. 流程控制 (st.stop, st.rerun)
- 知识点:
st.stop()用于在特定条件下(如数据库未就绪)中止脚本执行,防止后续代码出错。st.rerun()用于强制刷新整个页面,以立即显示状态变更。 - 应用:
- 在数据库连接失败时使用
st.stop()进行优雅的错误处理。 - 在“清空数据库”操作后使用
st.rerun()强制刷新 UI,提供即时反馈。
- 在数据库连接失败时使用
4. 交互式小组件 (st.slider, st.button, st.expander)
- 知识点: 利用滑块 (
st.slider)、按钮 (st.button) 和可折叠容器 (st.expander) 来创建丰富的用户交互体验。 - 应用:
st.slider用于让用户动态选择查看的数据条数。st.expander用于将危险的“清空”功能默认隐藏,防止误操作。st.button用于触发操作。
5. 数据展示 (st.metric, st.dataframe)
- 知识点:
st.metric用于突出显示关键指标(KPI),st.dataframe用于将pandasDataFrame 渲染为可交互的表格。 - 应用:
- 使用
st.metric醒目地展示“知识库片段总数”。 - 使用
st.dataframe提供了一个可排序、可搜索的数据浏览界面,体验远超静态表格。
- 使用
6. 高级交互:使用 st.session_state 实现二次确认
- 知识点:
st.session_state是一个可以在多次页面重载之间持久化数据的类字典对象,是实现复杂、有状态交互逻辑的核心。 - 应用: “清空数据库”的二次确认功能是
st.session_state的经典应用。- 点击“清空”按钮,将
st.session_state.confirm_delete设为True。 - 页面重载后,脚本检测到此状态,从而显示最终的确认按钮。
- 操作完成后,将状态重置为
False并刷新页面。
- 这个机制完美解决了在 Streamlit 的无状态重载模型中实现多步确认流程的难题。
- 点击“清空”按钮,将