window 下用Ollama 开发一个简单文档问答系统

文档问答系统

本系统利用先进的语言模型和检索技术,为用户提供基于上传文件内容的问答服务。支持多种文件格式,包括 Word、PDF、CSV、SQL 和 TXT 文件。

功能介绍

文件上传

  • 用户可以同时上传多个文件。
  • 支持的文件类型包括:.doc, .docx, .pdf, .csv, .sql, .txt
  • 系统会自动处理上传的文件,并提取文本内容。

问答处理

  • 用户输入问题后,系统会根据上传的文件内容,使用检索增强生成(RAG)技术生成答案。
  • RAG 技术结合了信息检索和语言生成,提高了答案的准确性和相关性。

技术细节

  • 语言模型:使用 Ollama 模型处理语言生成任务。
  • 检索系统:通过 Chroma 向量存储和检索框架,对文本内容进行索引和检索。
  • 文本处理:使用递归字符文本分割器处理大文本文件,确保信息的完整性。

使用指南

  1. 通过界面上传文件。可以手动放到upload目录。
  2. 在提问框中输入您的问题。
  3. 调整 temperaturetop_p 参数以控制答案生成的随机性和多样性。
  4. 提交问题,系统将显示基于文件内容的答案。

界面展示

系统提供一个简洁明了的界面,用户可以轻松上传文件和输入问题。温度和概率顶部参数可通过滑块调整,使用户能够根据需要定制答案的生成。

结论

本文档问答系统是一个强大的工具,适用于需要从特定文件内容中快速获取信息的用户。无论是学术研究、业务报告还是技术文档分析,本系统都能提供有效的支持。
在这里插入图片描述

import gradio as gr
import os
import uuid
from docx import Document
import PyPDF2
import pandas as pd
from langchain_community.llms import Ollama
from langchain.callbacks.manager import CallbackManager
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
from langchain_community.document_loaders import WebBaseLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import OllamaEmbeddings
from langchain.chains import RetrievalQA
from langchain_core.prompts import PromptTemplatedef init_ollama_llm(model, temperature, top_p):return Ollama(model=model,temperature=temperature,top_p=top_p,callback_manager=CallbackManager([StreamingStdOutCallbackHandler()]))def content_web(url):loader = WebBaseLoader(web_paths=(url,),)docs = loader.load()text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)splits = text_splitter.split_documents(docs)return splitsdef chroma_retriever_store_content(splits):vectorstore = Chroma.from_documents(documents=splits,embedding=OllamaEmbeddings(model='nomic-embed-text'))return vectorstore.as_retriever()def rag_prompt():return PromptTemplate(input_variables=['context', 'question'],template='''您是一个问答任务的助手,且需使用中文回答。请根据以下检索到的上下文信息用中文回答问题。如果您不知道答案,请直接回答“我不知道”,无需任何解释。问题:{question} 上下文:{context} 答案:''')def ollama_rag_chroma_web_content(web_url, question, temperature, top_p):llm = init_ollama_llm('llama3', temperature, top_p)splits = content_web(web_url)retriever = chroma_retriever_store_content(splits)qa_chain = RetrievalQA.from_chain_type(llm, retriever=retriever, chain_type_kwargs={'prompt': rag_prompt()})return qa_chain.invoke({'query': question})['result']def process_files_and_chat(files, question, temperature, top_p):# 首先处理上传的文件if files is not None:upload_result, _ = upload_file(files)if "失败" in upload_result:return upload_resultuploads_dir = 'uploads'file_paths = [os.path.join(uploads_dir, f) for f in os.listdir(uploads_dir) if os.path.isfile(os.path.join(uploads_dir, f))]if not file_paths:return "请上传文件"all_texts = []# 处理所有上传的文件for file_path in file_paths:print(f"处理文件: {file_path}")_, file_extension = os.path.splitext(file_path)try:if file_extension.lower() == '.pdf':print(f"开始处理 PDF 文件: {file_path}")with open(file_path, 'rb') as f:pdf_reader = PyPDF2.PdfReader(f)text = ''for page in pdf_reader.pages:text += page.extract_text() + '\n'print(text)all_texts.append(text)elif file_extension.lower() == '.sql':print(f"开始处理 SQL 文件: {file_path}")with open(file_path, 'r', encoding='utf-8') as f:text = f.read()text = f"SQL文件内容:\n{text}"all_texts.append(text)elif file_extension.lower() == '.csv':print(f"开始处理 CSV 文件: {file_path}")df = pd.read_csv(file_path)text = f"���名: {', '.join(df.columns)}\n"text += df.to_string(index=False)all_texts.append(text)elif file_extension.lower() == '.txt':print(f"开始处理文本文件: {file_path}")with open(file_path, 'r', encoding='utf-8') as f:text = f.read()all_texts.append(text)elif file_extension.lower() in ['.doc', '.docx']:print(f"开始处理 Word 文件: {file_path}")doc = Document(file_path)text = '\n'.join([paragraph.text for paragraph in doc.paragraphs])for table in doc.tables:for row in table.rows:for cell in row.cells:text += '\n' + cell.textall_texts.append(text)else:print(f"跳过不支持的文件类型: {file_extension}")continueexcept Exception as e:print(f"处理文件 {file_path} 时出错: {str(e)}")continueif not all_texts:return "没有可处理的文件内容"# 合并所有文本combined_text = "\n\n".join(all_texts)print(f"成功处理 {len(file_paths)} 个文件,总文本长度: {len(combined_text)} 字符")# 使用RAG处理问题try:print("初始化LLM模型...")llm = init_ollama_llm('llama3', temperature, top_p)text_splitter = RecursiveCharacterTextSplitter(chunk_size=500,chunk_overlap=50,length_function=len,is_separator_regex=False)splits = text_splitter.split_text(combined_text)if not splits:return "文档内容为空或无法正确分割"print(f"文本分割完成,共 {len(splits)} 个片段")print("初始化嵌入模型...")embeddings = OllamaEmbeddings(model='nomic-embed-text',base_url="http://localhost:11434")print("创建向量存储...")vectorstore = Chroma.from_texts(texts=splits,embedding=embeddings,collection_name="doc_qa",persist_directory="./chroma_file_db")retriever = vectorstore.as_retriever(search_kwargs={"k": 1})qa_chain = RetrievalQA.from_chain_type(llm=llm,retriever=retriever,chain_type_kwargs={'prompt': rag_prompt(),'verbose': True})print("处理问题...")result = qa_chain.invoke({'query': question})return result['result']except Exception as e:print(f"处理问题时出错: {str(e)}")return f"处理问题时出错: {str(e)}"def detect_file_type(file_content, original_name=''):"""根据文件内容和名称检测文件类型"""# 首先尝试从文件名获取扩展名if original_name:ext = os.path.splitext(original_name)[1].lower()if ext in ['.doc', '.docx', '.pdf', '.txt', '.csv', '.sql']:return ext# 如果没有文件名或扩展名不支持,尝试通过内容判断try:# 检查文件头部特征if file_content.startswith(b'%PDF'):return '.pdf'# Word文档的特征(.docx 文件是 ZIP 格式,以 PK 开头)elif file_content.startswith(b'PK'):return '.docx'# 尝试解码为文本try:content_start = file_content[:1000].decode('utf-8')# SQL文件特征检测sql_keywords = ['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'CREATE', 'DROP', 'ALTER']if any(keyword in content_start.upper() for keyword in sql_keywords):return '.sql'# CSV文件特征检测if ',' in content_start and '\n' in content_start:return '.csv'# 默认作为文本文件return '.txt'except UnicodeDecodeError:# 如果无法解码为文本,检查是否是 DOC 文件if b'\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1' in file_content[:10]:return '.doc'return Noneexcept Exception as e:print(f"文件类型检测失败: {str(e)}")return Nonedef upload_file(files):try:if not os.path.exists("uploads"):os.makedirs("uploads")file_paths = []for file in files:try:# 读取文件内容if isinstance(file, bytes):file_content = fileelif hasattr(file, 'read'):file_content = file.read()if hasattr(file, 'seek'):file.seek(0)  # 重置文件指针else:print(f"警告:无法读取文件内容")continue# 获取原始文件名(如果有)original_name = ''if isinstance(file, dict) and 'name' in file:original_name = file['name']elif hasattr(file, 'name'):original_name = file.nameelif hasattr(file, 'filename'):original_name = file.filename# 检测文件类型ext = detect_file_type(file_content, original_name)if not ext:print(f"警告:无法识别文件类型")continue# 生成唯一文件名unique_filename = str(uuid.uuid4()) + extsave_path = os.path.join("uploads", unique_filename)print(f"保存文件: {original_name or '未命名文件'} -> {save_path}")# 保存文件with open(save_path, "wb") as f:if isinstance(file_content, str):f.write(file_content.encode())else:f.write(file_content)file_paths.append(save_path)except Exception as e:print(f"处理文件出错: {str(e)}")continueif not file_paths:return "没有成功上传任何文件", Nonereturn f"成功上传 {len(file_paths)} 个文件", file_pathsexcept Exception as e:print(f"上传过程出错: {str(e)}")return f"文件上传失败: {str(e)}", None# 更新界面
ol = gr.Interface(fn=process_files_and_chat,inputs=[gr.File(label='上传文件', type='binary', file_count='multiple'),  # 移除 optional 参数gr.Textbox(label='问题', placeholder='请输入您的问题'),gr.Slider(label='temperature', minimum=0, maximum=1, step=0.1, value=0.1),gr.Slider(label='top_p', minimum=0, maximum=1, step=0.1, value=0.4)],outputs='text',title='文档问答系统',description='支持同时上传多个文件(Word、PDF、CSV、SQL、TXT等),输入问题获取基于文档内容的答案'
)ol.launch()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/483172.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

全国296个地级市平均房价数据(2000-2022年)

全国296个地级市平均房价数据(2000-2022年),包括面板数据和截面数据 点击下载 1、数据来源:安居客、房天下、房价行情网等住房交易网页整理 2、时间跨度:2000-2022年 3、区域范围:全国296个地级市 4、缺失说明:西…

贴片式内存卡 ​SD NAND​

SD NAND FLASH 贴片式SD卡 贴片式t卡 存储芯片 1. 什么是贴片式内存卡 贴片式内存卡是指一种将内存芯片直接贴装在电路板上的内存卡类型。与传统的插针式内存卡(如SD卡、MicroSD卡)不同,贴片式内存卡通常不具有外部引脚或接口,而…

C—操作符易错点

strlen与sizeof strlen求的是大小,包含“\0” strlen求的是,长度不包括“\0” 注意:空格也算一个字符 操作符“/”(除法) 对于除法操作符来说,两边都是整数,那么就是整数除法 如果想计算出小数&#x…

基于PyTorch框架的线性回归实现指南

目录 ​编辑 1. 线性回归基础 2. PyTorch环境搭建 3. 数据准备 4. 定义线性回归模型 5. 损失函数和优化器 6. 训练模型 7. 评估模型 8. 结论 线性回归是统计学和机器学习中最基本的预测模型之一,它试图找到输入特征和输出结果之间的线性关系。在深度学习框…

R语言机器学习论文(六):总结

文章目录 介绍参考文献介绍 本文采用R语言对来自进行数据描述、数据预处理、特征筛选和模型构建。 最后我们获得了一个能有效区分乳腺组织的随机森林预测模型,它的性能非常好,这意味着它可能拥有非常好的临床价值。 在本文中,我们利用R语言对来自美国加州大学欧文分校的B…

基于Java Springboot校园导航微信小程序

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术:Html、Css、Js、Vue、Element-ui 数据库:MySQL 后端技术:Java、Spring Boot、MyBatis 三、运行环境 开发工具:IDEA/eclipse微信开发…

面试题-RocketMQ的基本架构、支持的消息模式、如何保证消息的可靠传输

相关问题 1、RocketMQ的基本架构是怎样的?请简述各组件的作用。 2、RocketMQ支持哪几种消息模式(如点对点、发布/订阅)?请简要说明它们的区别。 3、如何使用Java客户端实现一个简单的消息生产者和消费者? 4、RocketMQ…

WPF+LibVLC开发播放器-LibVLC在C#中的使用

使用WPFLibVLC快速 开发一个播放器 安装包Nuget 安装下面两个包,必须安装两个 一个是相关框架对应的包,Winform就安装LibVLCSharp.Winform;WPF就安装LibVLCSharp.WPF,以此类推,他们都默认依赖LibVLCSharp,不需要例外安装 一个是…

CSS变量用法及实践

目录 一、基本用法 1.1、定义变量 1.2、使用变量 1.3 、修改变量的值 二、命名规范 2.1、使用有意义的名称 2.2、使用命名空间 三、变量值类型 3.1、如果变量值是一个字符串,可以与其他字符串拼接,例如: 3.2、 如果变量值是数值&a…

WEB开发: 丢掉包袱,拥抱ASP.NET CORE!

今天的 Web 开发可以说进入了一个全新的时代,前后端分离、云原生、微服务等等一系列现代技术架构应运而生。在这个背景下,作为开发者,你一定希望找到一个高效、灵活、易于扩展且具有良好性能的框架。那么,ASP.NET Core 显然是一个…

【汇编语言】标志寄存器(一) —— 标志寄存器中的标志位:ZF、PF、SF、CF、OF 一网打尽

前言 📌 汇编语言是很多相关课程(如数据结构、操作系统、微机原理)的重要基础。但仅仅从课程的角度出发就太片面了,其实学习汇编语言可以深入理解计算机底层工作原理,提升代码效率,尤其在嵌入式系统和性能优…

纯Go语言开发人脸检测、瞳孔/眼睛定位与面部特征检测插件-助力GoFly快速开发框架

前言​ 开发纯go插件的原因是因为目前 Go 生态系统中几乎所有现有的人脸检测解决方案都是纯粹绑定到一些 C/C 库,如 ​​OpenCV​​ 或 ​​​dlib​​​,但通过 ​​​cgo​​​ 调用 C 程序会引入巨大的延迟,并在性能方面产生显著的权衡。…

2、Three.js初步认识场景Scene、相机Camera、渲染器Renderer三要素

三要素之间关系: 有了虚拟场景Scene,相机录像Camera,在相机小屏幕上看到的Renderer Scene当前空间 Mesh人在场景 Camera相机录像 Renderer显示器上 首先先描述下Scene: 这个场景为三要素之一,一切需要展示的东西都需…

电子电气架构 --- 车载网关GW连接外部IP Tester

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 所谓鸡汤,要么蛊惑你认命,要么怂恿你拼命,但都是回避问题的根源,以现象替代逻辑,以情绪代替思考,把消极接受现实的懦弱,伪装成乐观面对不幸的…

防火墙iptables

一、概述 1.1相关概念 入侵监测系统 在互联网访问的过程中,不阻断任何网络访问,也不会定位网络的威胁,提供告警和事后的监督,类似于监控。 入侵防御系统 透明工作模式,在判定为攻击行为或者是病毒威胁时&#xff0c…

Android Camera2采集并编码为H.264

前言 本篇博文主要讲述的是基于Android原生MediaCodec通过Camera2 API进行图像数据采集并编码为H.264的实现过程,如果对此感兴趣的不妨驻足观看,也欢迎大家大家对本文中描述不当或者不正确的地方进行指正。如果对于Camera2预览还不熟悉的可以观看博主上…

【ROS2】Ubuntu22.04安装ROS humble

一. ROS简介 1.1 什么是ROS ROS 是一个适用于机器人的开源的元操作系统。它提供了操作系统应有的服务,包括硬件抽象,底层设备控制,常用函数的实现,进程间消息传递,以及包管理。ROS的核心思想就是将机器人的软件功能做…

Redis开发05:使用stackexchange.redis库对redis进行增删改查

一、安装第三方库 二、官网 StackExchange.Redis |通用型 redis 客户端 三、连接示例 private static string redisConnectionString "localhost:6379,passwordyourpassword,defaultDatabase0,allowAdmintrue,asyncTimeout10000";private static string redisConn…

【golang】单元测试,以及出现undefined时的解决方案

单元测试 要对某一方法进行测试时,例如如下这一简单减法函数,选中函数名后右键->转到->测试 1)Empty test file 就是一个空文件,我们可以自己写测试的逻辑 但是直接点绿色箭头运行会出问题: 找不到包。我们要在…

火语言RPA流程组件介绍--键盘按键

🚩【组件功能】:模拟键盘按键 配置预览 配置说明 按键 点击后,在弹出的软键盘上选择需要的按键 执行后等待时间(ms) 默认值300,执行该组件后等待300毫秒后执行下一个组件. 输入输出 输入类型 万能对象类型(System.Object)输出类型 万能对象类型…