Java读取PDF后做知识库问答_SpringAI实现

核心思路:
简单来说,就是把PDF文件读取并向量化,然后放到向量存储里面,再通过大模型,来实现问答。
RAG(检索增强生成)介绍:
检索增强生成(RAG)是一种结合了信息检索和文本生成的技术,旨在提高大模型的响应准确性和相关性。通过将检索模型(用于搜索专有数据集或知识库)与生成模型(如大型语言模型LLM)相结合,RAG能够利用私有或专有的数据来辅助生成更精确的回答。这样不仅减少了由于缺乏特定背景知识导致的大模型“幻觉”现象,还使得生成的内容更加贴合用户的需求和上下文环境,特别适合于需要处理企业内部数据的应用场景。
Spring AI alibaba介绍
Spring AI Alibaba 是基于 Spring Ai 构建的,用于集成阿里云通义大模型服务的应用框架。它允许开发者通过简单的配置和少量代码,将强大的AI能力如对话、文生图等快速融入到 Java 应用程序中。其核心优势在于提供了一套标准化接口,使得应用程序能够轻松切换不同的AI提供商而无需大量修改代码;同时,该框架支持流式输出,并提供了Prompt模板等功能来简化开发流程,极大地提高了效率和灵活性。通过与Spring Boot生态系统的无缝集成,Spring AI Alibaba为开发者打造了一个既高效又便捷的AI应用开发环境。
详细例子:
1 后端代码编写
读PDF->向量化->向量存储->读取展现
1. 环境准备
确保你的开发环境满足以下条件:
- JDK版本在17或以上。
- Spring Boot版本为3.3.x或更高。
- 已经从阿里云申请到了通义千问API的
api-key
。
2. 配置项目以使用Spring AI Alibaba
2.1 设置API Key
在启动应用之前,请设置环境变量AI_DASHSCOPE_API_KEY
为你获得的API密钥值,并且在application.properties
中正确引用它:
spring.ai.dashscope.api-key: ${AI_DASHSCOPE_API_KEY}
2.2 添加依赖
需要添加对spring-ai-alibaba-starter
的依赖到你的pom.xml
文件中,并且指定正确的Spring Boot父级依赖。同时不要忘记包含必要的仓库地址以便获取最新的快照版本。
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-staper-parent</artifactId><version>3.3.4</version></parent><dependencies><dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-starter</artifactId><version>1.0.0-M2</version></dependency></dependencies><repositories><repository><id>sonatype-snapshots</id><url>https://oss.sonatype.org/content/repositories/snapshots</url><snapshots><enabled>true</enabled></snapshots></repository><repository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/milestone</url><snapshots><enabled>false</enabled></snapshots></repository><repository><id>spring-snapshots</id><name>Spring Snapshots</name><url>https://repo.spring.io/snapshot</url><releases><enabled>false</enabled></releases></repository></repositories>
3. 编写RAG服务代码
创建一个名为RagService
的服务类,用于处理与向量存储、文档检索相关的逻辑。该服务还将负责初始化索引构建及查询操作。
public class RagService {private final ChatClient chatClient;private final VectorStore vectorStore;private final DashScopeApi dashscopeApi = new DashScopeApi("你的apiKey");DocumentRetriever retriever;public RagService(ChatClient chatClient, EmbeddingModel embeddingModel) {this.chatClient = chatClient;vectorStore = new DashScopeCloudStore(dashscopeApi, new DashScopeStoreOptions("spring-ai知识库"));retriever = new DashScopeDocumentRetriever(dashscopeApi, DashScopeDocumentRetrieverOptions.builder().withIndexName("spring-ai知识库").build());}public String buildIndex() {String filePath = "/path/to/阿里巴巴财报.pdf";DocumentReader reader = new DashScopeDocumentCloudReader(filePath, dashscopeApi, null);List<Document> documentList = reader.get();vectorStore.add(documentList);return "SUCCESS";}public StreamResponseSpec queryWithDocumentRetrieval(String message) {StreamResponseSpec response = chatClient.prompt().user(message).advisors(new DocumentRetrievalAdvisor(retriever, DEFAULT_USER_TEXT_ADVISE)).stream();return response;}
}
4. 创建控制器暴露接口
接下来定义一个REST控制器,用来接收HTTP请求并将结果返回给客户端。
@RestController
@RequestMapping("/ai")
public class RagController {private final RagService ragService;public RagController(RagService ragService) {this.ragService = ragService;}@GetMapping("/steamChat")public Flux<String> generate(@RequestParam(value = "input", required = true) String input,HttpServletResponse httpResponse) {StreamResponseSpec chatResponse = ragService.queryWithDocumentRetrieval(input);httpResponse.setCharacterEncoding("UTF-8");return chatResponse.content();}@GetMapping("/buildIndex")public String buildIndex() {return ragService.buildIndex();}
}
5. 运行应用程序
在运行此应用程序之前,请确保已经完成了索引的构建(调用/buildIndex
)。之后可以通过访问http://localhost:8080/ai/steamChat?input=你的问题
来查询财务报告中的信息了。
通过上述步骤,你就可以成功地利用检索增强技术来处理阿里巴巴财务报表PDF文件,并通过一个简单的Web API提供交互式问答功能。这不仅能够帮助用户更高效地查找所需信息,同时也展示了如何结合现有技术和工具快速搭建起实用的服务。
检索增强的前端代码编写
构建项目并填写代码
首先,创建一个新的 React 应用并安装所需的依赖:
npx create-react-app ragChatFrontend
cd ragChatFrontend
npm install
public/index.html
在public/index.html
中不需要做特别的修改,保持默认即可。
src/index.js
确保你的src/index.js
如下所示,它负责渲染应用的根组件App
:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';ReactDOM.render(<React.StrictMode><App /></React.StrictMode>,document.getElementById('root')
);
src/App.js
这个文件定义了应用的主要布局。我们在这个例子中将只包含一个聊天组件:
import React from 'react';
import RAGChatComponent from './components/RAGChatComponent';function App() {return (<div className="App"><RAGChatComponent /></div>);
}export default App;
src/components/RAGChatComponent.js
这是主要的功能实现部分,我们将在这里处理用户输入、向后端发送请求以及展示返回的数据流。
import React, { useState } from 'react';function RAGChatComponent() {const [input, setInput] = useState('');const [messages, setMessages] = useState('');const handleInputChange = (event) => {setInput(event.target.value);};const handleSendMessage = async () => {if (input.trim() === '') return;try {// 发送请求到后端的RAG Chat接口const response = await fetch(`http://localhost:8080/ai/streamChat?input=${encodeURIComponent(input)}`);const reader = response.body.getReader();const decoder = new TextDecoder('utf-8');let done = false;while (!done) {const { value, done: readerDone } = await reader.read();done = readerDone;const chunk = decoder.decode(value, { stream: true });setMessages((prevMessages) => prevMessages + chunk);}// 在每次请求完成后添加换行符以区分不同轮次的消息setMessages((prevMessages) => prevMessages + '\n\n=============================\n\n');} catch (error) {console.error('Failed to fetch', error);}};const handleClearMessages = () => {setMessages('');};return (<div><inputtype="text"value={input}onChange={handleInputChange}placeholder="Enter your message"/><button onClick={handleSendMessage}>Send</button><button onClick={handleClearMessages}>Clear</button><div><h3>Messages:</h3><pre>{messages}</pre></div></div>);
}export default RAGChatComponent;
运行项目
- 启动前端服务:
cd ragChatFrontend
npm start
解释步骤
- 我们创建了一个新的React应用,并构建了一个简单的界面来与支持检索增强(RAG)的聊天服务进行交互。
- 用户可以在文本框内输入消息并通过点击“Send”按钮将其发送给后端。
- 消息通过HTTP GET请求被发送到指定URL,即
http://localhost:8080/ai/steamChat?input=...
。这里使用了fetch
API来发起异步请求,并且通过读取响应体中的数据流来逐步显示返回的内容。
- 当接收到新数据块时,这些数据会被解码为字符串并追加到当前的消息列表中。
- 最后,在每次请求完成之后都会插入一个分隔线,以便于清晰地区分不同的对话回合。
- 提供了一个清除功能,允许用户清空消息历史记录以便开始新一轮对话。
此方案利用了浏览器内置的TextDecoder
和ReadableStream
API来高效地处理从服务器接收的数据流,非常适合于实时性要求较高的应用场景如在线聊天等。
相关文章:

Java读取PDF后做知识库问答_SpringAI实现
核心思路: 简单来说,就是把PDF文件读取并向量化,然后放到向量存储里面,再通过大模型,来实现问答。 RAG(检索增强生成)介绍: 检索增强生成&#x…...

打开exe程序显示没有适当的访问权限
打开exe程序显示没有适当的访问权限 打开.exe可执行程序,显示Windows 无法访问指定设备、路径或文件。你可能没有适当的权限访问该项目。 解决方法 鼠标选中该文件或文件夹,右键单击选择属性,在弹出的属性选项卡中切换到安全选项卡…...
Python异步编程:使用`create_task`并发执行协程
Python异步编程:使用create_task并发执行协程 1. 什么是create_task?2. 为什么需要create_task?3. 如何使用create_task?3.1 基本用法3.2 任务的返回值 4. 注意事项5. 总结 在Python的异步编程中,asyncio库为我们提供了…...

从零开始搭建你的DolphinScheduler分布式任务调度平台实战指南
文章目录 前言1. 安装部署DolphinScheduler1.1 启动服务 2. 登录DolphinScheduler界面3. 安装内网穿透工具4. 配置Dolphin Scheduler公网地址5. 固定DolphinScheduler公网地址 前言 本篇教程和大家分享一下DolphinScheduler的安装部署及如何实现公网远程访问,结合内…...

第五课:Python学习之if语句
判断(if)语句 目标 开发中的应用场景if 语句体验if 语句进阶综合应用 01. 开发中的应用场景 生活中的判断几乎是无所不在的,我们每天都在做各种各样的选择,如果这样?如果那样?…… 程序中的判断 # 定义…...

群晖前面加了雷池社区版,安装失败,然后无法识别出用户真实访问IP
有nas的相信对公网都不模式,在现在基础上传带宽能有100兆的时代,有公网代表着家里有一个小服务器,像百度网盘,优酷这种在线服务都能部署为私有化服务。但现在运营商几乎不可能提供公网ip,要么自己买个云服务器做内网穿…...
【秋招笔试】10.13拼多多(已改编)秋招-三语言题解
🍭 大家好这里是 春秋招笔试突围,一起备战大厂笔试 💻 ACM金牌团队🏅️ | 多次AK大厂笔试 | 大厂实习经历 ✨ 本系列打算持续跟新 春秋招笔试题 👏 感谢大家的订阅➕ 和 喜欢💗 和 手里的小花花🌸 ✨ 笔试合集传送们 -> 🧷春秋招笔试合集 🍒 本专栏已收集…...
50个JAVA常见代码大全:学完这篇从Java小白到架构师(附带讲解)
基础语法 1. Hello World public class HelloWorld {public static void main(String[] args) {System.out.println("Hello, World!");} }讲解 这是一个典型的Java程序,它定义了一个名为HelloWorld的类,该类包含一个main方法——Java应用程序的入口点。System.o…...

Microsoft SQL Server 2008 R2 (RTM) - 10.50.1600.0 SP1升级到SP3操作方法(x64)
1、首先安装时候选择升级SQLEXPRADV_x64_CHS.exe。 2、接着安装SQLServer2008R2SP1-KB2528583-x64-sp1补丁后10.50.2500.0。 3、接着安装升级SQLEXPRWT_x64_CHS.exe。 4、继续安装SP3:SQLServer2008R2SP3-KB2979597-x64-CHS。 5、最后安装SP3补丁:SQ…...

Centos7安装Git及配置Github
Background Git 是一个开源的分布式版本控制系统,由 Linus Torvalds(Linux 内核的创始人)在 2005 年创建。它被设计用来快速有效地处理从小到大的项目版本管理。Git 目前是全世界最流行的版本控制系统,广泛应用于软件开发中。 1、…...

MobileNet v3(相比于MobileNet v2)
概述: 更新Block(bneck) 使用NAS搜索参数 (Neural Architecture Search) 重新设计耗时层结构 更准确,更高效 以及表中数据展示 更新Block 1.加入SE模块 2.更新了激活函数 首先通过一个1*1的卷积层来进行一个升维处理&#…...

短视频剪辑入门指南:这四大软件值得推荐!
要在众多的短视频作品中脱颖而出并不容易,这就要求制作者不仅要具备良好的创意,还需要掌握一定的剪辑技巧。这里给大家推荐几个好用的短视频剪辑工具! 福昕视频剪辑 直达链接:www.pdf365.cn/foxit-clip/ 操作教程:立…...

网络编程(22)——通过beast库快速实现websocket服务器
目录 二十二、day22 1. websocket简述 2. 基于TCP长连接实现sebsocket a. Connection b. ConnectionMgr c. WebServer d. 编译的小问题 3. 测试 4. 基于http实现的websocket 二十二、day22 因为http受限于请求-响应模式,客户端发起请求,服务器…...
从视频截取每一帧作为图像
查看视频有多少帧 import cv2def count_frames_per_second(video_path):cap cv2.VideoCapture(video_path)if not cap.isOpened():print("Error: Could not open video")return None# Get frames per secondfps cap.get(cv2.CAP_PROP_FPS)# Get total number of f…...

终端 数据表格
// // Created by HongDaYu on 17 十月 2024. //#ifndef HDYSDK_UTIL_H #define HDYSDK_UTIL_H#include <cstdint> #include <string> #include <list> #include <iomanip> #include <memory>class dataGrid { private:std::list<const char*…...
2.4.ReactOS系统运行级别降低IRQL级别KfLowerIrql 函数
2.4.ReactOS系统运行级别降低IRQL级别KfLowerIrql 函数 2.4.ReactOS系统运行级别降低IRQL级别KfLowerIrql 函数 文章目录 2.4.ReactOS系统运行级别降低IRQL级别KfLowerIrql 函数KfLowerIrql 函数 KfLowerIrql 函数 /*******************************************************…...

数字后端实现静态时序分析STA Timing Signoff之min period violation
今天给大家分享一个在高性能数字IC后端实现timing signoff阶段经常遇到的min period violation。大部分时候出现memory min period问题基本上都是需要返工重新生成memory的。这是非常致命的错误,希望大家在做静态时序分析时一定要查看min period violation。 什么是…...

phpstorm+phpstudy 配置xdebug(无需开启浏览器扩展)
今天又被xdebug折磨了,忘记了以前咋配置了现在百度发现好多都是各种浏览器扩展而且也没有真正的用到项目上的都是测试的地址怎么样的 我就简单写一下自己实战吧 不支持workerman swoole hyperf等这种服务框架 如果你会请教教我 工具版本phpstudy8.1.xphpstorm2021.x…...

AI赋能安全运营 | 赛宁网安深度参与四川省网络安全沙龙
为促进四川省、市网络安全公共服务领域的经验交流与深入探讨,打通网络安全供需上下游,加速汇聚省、市优质网络安全设备和服务资源,提升巴中市乃至四川省网络安全防护水平,共同推动四川省网络安全事业的蓬勃发展。 2024年10月15日…...
R语言中,.RData 和 .rds 的区别
.RData 和 .rds 是 R 语言中两种不同的数据保存格式,二者有一些关键的区别: 1. 存储内容的类型: .RData 文件:可以同时保存多个对象(如数据框、向量、列表等),当你加载 .RData 文件时…...

LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...

免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...

vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...

c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...
在 Spring Boot 项目里,MYSQL中json类型字段使用
前言: 因为程序特殊需求导致,需要mysql数据库存储json类型数据,因此记录一下使用流程 1.java实体中新增字段 private List<User> users 2.增加mybatis-plus注解 TableField(typeHandler FastjsonTypeHandler.class) private Lis…...

若依登录用户名和密码加密
/*** 获取公钥:前端用来密码加密* return*/GetMapping("/getPublicKey")public RSAUtil.RSAKeyPair getPublicKey() {return RSAUtil.rsaKeyPair();}新建RSAUti.Java package com.ruoyi.common.utils;import org.apache.commons.codec.binary.Base64; im…...