【langchain4j】Springboot如何接入大模型以及实战开发-AI问答助手(一)

langchain4j介绍

官网地址:https://docs.langchain4j.dev/get-started

langchain4j可以说是java和spring的关系,spring让我们开发java应用非常简单,那么langchain4j对应的就是java开发ai的 “Spring”
他集成了AI应用的多种场景,并且抽象多种接口,让我们开发AI应用非常简单,下面介绍其常用功能,以及开发一个小的ai问答应用

AI应用的实现需求:支持对话、上下文对话、流式对话、对话数据隔离、对话数据持久化、Function Call实现特殊场景结合业务进行ai问答

为了降低模型的使用门槛,这里开发使用国内模型-阿里千问系列进行开发,登录去控制台获取key就行
https://bailian.console.aliyun.com/

项目依赖引入

依赖需要引入两个东西:langchain4j的依赖、对应的模型的依赖,但是如果通过starter的形式,只需要引入starter即可,具体可以看官方的文档
https://docs.langchain4j.dev/integrations/language-models/dashscope
注意:需要使用jdk17环境,SpringBoot3.x系列

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.xujie</groupId><artifactId>langchain4j-demo</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><spring.boot.version>3.2.4</spring.boot.version><langchain4j.version>1.0.0-beta3</langchain4j.version></properties><dependencyManagement><dependencies><!-- Spring Boot Starter父依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>${spring.boot.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-community-bom</artifactId><version>${langchain4j.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><dependencies><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-community-dashscope-spring-boot-starter</artifactId></dependency><!-- Langchain4j自己的核心库 --><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j</artifactId><version>${langchain4j.version}</version></dependency><!-- Web依赖,以及Webflux依赖,实现流式响应 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency><!-- Redis的依赖,用于消息持久化 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency></dependencies>
</project>

功能体验

首先我们先逐个体验其功能,最后再统一集成

AI对话

Langchain4j将AI的功能封装成一个Model,通过Model即可调用对应的AI功能,我们要使用千问的模型,new 一个千问的Model即可
这里有两种方式,一种是通过SpringBoot的配置文件,将模型注入到IOC容器中,另一种就是通过代码进行配置

ChatLanguageModel qwenModel = QwenChatModel.builder().apiKey("You API key here").modelName("qwen-max").build();

官方示例:

langchain4j.community.dashscope.api-key=<You API Key here>
langchain4j.community.dashscope.model-name=qwen-max
# The properties are the same as `QwenChatModel`
# e.g.
# langchain4j.community.dashscope.temperature=0.7
# langchain4j.community.dashscope.max-tokens=4096
langchain4j:community:dashscope:chat-model:api-key: ${Ali_AI_KEY} //通过环境参数model-name: qwen-max

这里我们就使用注入的方式

/*** @author Xujie* @since 2025/4/19 21:44**/
@Slf4j
@SpringBootTest
public class TestAi {@Resourceprivate ChatLanguageModel qwenChatModel;@Testpublic void test() {String response = qwenChatModel.chat("你好呀");log.info(response);}
}

控制台输出:

2025-04-19T21:47:29.593+08:00  INFO 12660 --- [           main] com.xujie.TestAi                         : 你好!有什么可以帮助你的吗?

这里说明成功了

文生图

我们再来体验一下文生图的功能,这里用的阿里 wanx2.1-t2i-turbo 模型,大家也可以去阿里的模型广场看看有哪些支持图片生成的模型,调用即可,这里我们就通过手动构造的模式去构成图片模型

@Value("${langchain4j.community.dashscope.chat-model.api-key}")private String apiKey;@Testpublic void testGeneratePicture() {WanxImageModel.WanxImageModelBuilder builder = WanxImageModel.builder();WanxImageModel wanxImageModel = builder.apiKey(apiKey).modelName("wanx2.1-t2i-turbo").build();Response<Image> imageResponse = wanxImageModel.generate("生成一只剃了毛的黑色拉布拉多");log.info(imageResponse.toString());}
Java HotSpot(TM) 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
2025-04-19T21:54:42.892+08:00  INFO 31912 --- [           main] com.xujie.TestAi                         : Response { content = Image { url = "https://dashscope-result-wlcb-acdr-1.oss-cn-wulanchabu-acdr-1.aliyuncs.com/1d/e3/20250419/b0fe3396/15a89e1c-f792-4156-bff2-5d2ccb80561e3378775189.png?Expires=1745157281&OSSAccessKeyId=LTAI5tKPD3TMqf2Lna1fASuh&Signature=m3QvlCGR4aGwVyI7Vj1IFnVY95Y%3D", base64Data = null, mimeType = null, revisedPrompt = "写实宠物摄影,一只黑色拉布拉多犬在户外草地上。它全身毛发被修剪得非常短,露出光滑的黑皮肤。狗狗四肢强健,肌肉线条明显,正抬头望向镜头,眼神机警灵动。背景是大片绿色草地和蓝天白云,阳光洒在狗身上形成自然光影效果。高清彩色摄影,近景侧面捕捉狗狗优雅姿态。" }, tokenUsage = null, finishReason = null, metadata = {} }

可以看见,这里也是成功的生成图片了,效果一般,也有可能是我Promot的问题

在这里插入图片描述

文生语音

同样,我们去模型广场看看哪些支持生成语音的模型,这里就使用cosyvoice-v1模型

@Testpublic void testGenerateVoice() {SpeechSynthesisParam param = SpeechSynthesisParam.builder().apiKey(apiKey).voice("longxiaochun").model("cosyvoice-v1").build();SpeechSynthesizer speechSynthesizer = new SpeechSynthesizer(param,null);ByteBuffer call = speechSynthesizer.call("你好,给我唱一首生日快乐歌");// Maven项目标准资源目录File file = new File("src/main/resources/response.mp3");// 确保目录存在file.getParentFile().mkdirs();try(FileOutputStream fileOutputStream = new FileOutputStream(file)) {fileOutputStream.write(call.array());} catch (Exception e) {throw new RuntimeException(e);}}

然语音文件的路径实在resources下面,也是成功了
在这里插入图片描述

对话上下文

首先,我们理解一下什么是对话上下文,我们要清楚,AI的服务是不会记录我们对话的记录,我们每一次请求都是独立的返回,不会关联我们之前的问题,那么我就要实现上下文,就只能将之前的对话记录一起加上这一次的提问,一起请求给AI,这才实现了上下文的功能
我们进行验证一下,AI是否会自动记录上下文:
我们简单改造一下第一个用例

  @Testpublic void test() {String response1 = qwenChatModel.chat("你好呀,我是XJ");String response2 = qwenChatModel.chat("我是什么名字");log.info(response1);log.info(response2);}
Java HotSpot(TM) 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
2025-04-19T22:14:23.835+08:00  INFO 38908 --- [           main] com.xujie.TestAi                         : 你好,XJ!很高兴认识你。我是Qwen,由阿里云开发的超大规模语言模型。我在这里可以帮助你解答问题、提供信息或者进行各种话题的交流。有什么我可以帮到你的吗?
2025-04-19T22:14:23.835+08:00  INFO 38908 --- [           main] com.xujie.TestAi                         : 您好!您并没有告诉我您的名字,所以我无法直接知道您的名字是什么。如果您愿意分享的话,可以告诉我您的名字或者您想让我怎么称呼您。

可以看到,两次回复都是毫无关联的;
既然这样我们如何实现上下文呢,那最简单暴力的方式就是将前面的请求和响应一起发给AI,如下

    @Testpublic void test() {UserMessage userMessage1 = new UserMessage("你好呀,我是XJ");ChatResponse chatResponse1 = qwenChatModel.chat(userMessage1);AiMessage aiMessage = chatResponse1.aiMessage();// 拿到ai的响应UserMessage userMessage2 = new UserMessage("我是什么名字");ChatResponse chatResponse2 = qwenChatModel.chat(userMessage1,aiMessage,userMessage2);log.info(chatResponse1.aiMessage().text());log.info(chatResponse2.aiMessage().text());}

两次相应如下

Java HotSpot(TM) 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
2025-04-19T22:18:23.274+08:00  INFO 40264 --- [           main] com.xujie.TestAi                         : 你好,XJ!很高兴认识你。有什么我可以帮助你的吗?或者你想聊些什么?
2025-04-19T22:18:23.274+08:00  INFO 40264 --- [           main] com.xujie.TestAi                         : 你刚才告诉我你的名字是XJ。如果你有其他的名字或者昵称,也可以告诉我哦!

可以发现,AI对话已经具有的上下文的功能,但是这样是不是太复杂?
没当发现一个东西复杂的时候,总会有对应的不复杂的情况,如果没有,那就自己造,如果要我们自己实现,那其实也是很简单,将请求AI的接口封装一下,并且内部维护对话记录,每次请求,将历史的记录携带即可。

但是框架Langchain4j的已经帮我们实现了,具体是采用动态代理的模式实现的
在这里插入图片描述

public interface AiChat{String chat(String text);TokenStream tokenStream(String text);}@Testpublic void test() {AiChat aiChat = AiServices.builder(AiChat.class).chatLanguageModel(qwenChatModel).chatMemory(MessageWindowChatMemory.withMaxMessages(10))//限制10上下文.build();String response1 = aiChat.chat("我的名字是小徐");String response2 = aiChat.chat("我的名字是什么");log.info(response1);log.info(response2);}
Java HotSpot(TM) 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
2025-04-19T22:30:34.652+08:00  INFO 32428 --- [           main] com.xujie.TestAi                         : 你好,小徐!很高兴认识你。有什么我可以帮助你的吗?
2025-04-19T22:30:34.652+08:00  INFO 32428 --- [           main] com.xujie.TestAi                         : 你的名字是小徐。如果你还有其他问题或需要帮助,随时告诉我哦!

通过返回可以看的,确实是具有的上下文的能力

回话隔离

我们与ai进行回话,通常是具有对此回话的,每一次的回话主题都不一样,需要进行隔离,那我们来看看Langchain4j如何实现的回话隔离,上面我们实现了回话的上下文,跟最初一样,我们理解一下如何实现回话隔离呢?
那肯定是通过什么ID或者其他的唯一标识来区分
而且,也确实是这样实现的
我们只需要每一次对话传入ID即可
相比上面加了一个注解以及id字段,这个注解是Langchain4j提供的,在代理中通过注解去拿到对应的值

   public interface AiChat{String chat(@MemoryId Long id, @UserMessage String text);}@Testpublic void test() {AiChat aiChat = AiServices.builder(AiChat.class).chatLanguageModel(qwenChatModel).chatMemoryProvider(memoryId -> MessageWindowChatMemory.builder().maxMessages(10).id(memoryId).build()).build();String response1 = aiChat.chat(1L,"我的名字是小徐");String response2 = aiChat.chat(1L,"我的名字是什么");log.info(response1);log.info(response2);log.info("======");response2 = aiChat.chat(2L,"我的名字是什么");log.info(response2);}
2025-04-19T22:39:18.264+08:00  INFO 40528 --- [           main] com.xujie.TestAi                         : 你好,小徐!很高兴认识你。有什么我可以帮助你的吗?
2025-04-19T22:39:18.264+08:00  INFO 40528 --- [           main] com.xujie.TestAi                         : 你的名字是小徐。如果有其他问题或需要帮助的地方,随时告诉我哦!
2025-04-19T22:39:18.264+08:00  INFO 40528 --- [           main] com.xujie.TestAi                         : ======
2025-04-19T22:39:19.739+08:00  INFO 40528 --- [           main] com.xujie.TestAi                         : 您好!您并没有告诉我您的名字,所以我无法知道您的名字是什么。如果您愿意,可以告诉我您的名字,我很乐意用您的名字来称呼您。

可以看的两个回话,确实是隔离了,第二个回话并不清楚我第一个回话的内容

Function Call

Function Call 就是我们提前预设一些场景,然后用户在进行AI问答时,如果ai觉得当前对话符合某一个场景,便会去调用预设的函数,获取函数的返回值,然后结合用户的提问,去回答用户
比如:我数据库中存储今天的香蕉价格是10元一斤
并且预设场景:水果的价格
那么用户提问:今天的香蕉多少钱一斤
那么就会提取到水果的名称:香蕉,去数据库查询香蕉的价格,为10元,然后结合用户的提问,进行回答

 @Testpublic void testFuncCall() {class FuncCallService{final Map<String,Integer> map = Map.of("香蕉",10,"苹果",12);@Tool("水果的价格")public Integer fruitsPrice(@P("水果名字") String fruitName) {return map.getOrDefault(fruitName,-1);}}AiChat aiChat = AiServices.builder(AiChat.class).chatLanguageModel(qwenChatModel).tools(new FuncCallService()).chatMemoryProvider(memoryId -> MessageWindowChatMemory.builder().maxMessages(10).id(memoryId).build()).build();String response1 = aiChat.chat(1L, "香蕉多少钱一斤");String response2 = aiChat.chat(2L, "苹果多少钱一斤");String response3 = aiChat.chat(3L, "栗子多少钱一斤");log.info(response1);log.info(response2);log.info(response3);}
2025-04-19T22:49:48.799+08:00  INFO 40704 --- [           main] com.xujie.TestAi                         : 香蕉的价格是10元一斤。
2025-04-19T22:49:48.799+08:00  INFO 40704 --- [           main] com.xujie.TestAi                         : 苹果的价格是12元一斤。
2025-04-19T22:49:48.799+08:00  INFO 40704 --- [           main] com.xujie.TestAi                         : 对不起,当前的查询工具中没有栗子的价格信息。我建议您可以去附近的市场或者在线购物网站上查看最新的价格。如果您需要查询其他水果的价格,也可以告诉我,我会尽力帮您查询。

可以看到,确实是结合我们预设的内容去执行了Func Call

综上,结合Langchain4j确实可以很方便引入AI的功能,让我们的应用具有AI的功能

项目实战放在下一篇文章中

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

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

相关文章

平均池化(Average Pooling)

1. 定义与作用​​ ​​平均池化​​是一种下采样操作&#xff0c;通过对输入区域的数值取​​平均值​​来压缩数据空间维度。其核心作用包括&#xff1a; ​​降低计算量​​&#xff1a;减少特征图尺寸&#xff0c;提升模型效率。​​保留整体特征​​&#xff1a;平滑局部…

【dify实战】chatflow结合deepseek实现基于自然语言的数据库问答、Echarts可视化展示、Excel报表下载

dify结合deepseek实现基于自然语言的数据库问答、Echarts可视化展示、Excel报表下载 观看视频&#xff0c;您将学会 在dify下如何快速的构建一个chatflow&#xff0c;来完成数据分析工作&#xff1b;如何在AI的回复中展示可视化的图表&#xff1b;如何在AI 的回复中加入Excel报…

加一:从简单问题到复杂边界的深度思考

加一&#xff1a;从简单问题到复杂边界的深度思考 引言 在算法世界里&#xff0c;有些问题看似简单&#xff0c;实则暗藏玄机&#xff0c;其中“加一”问题就是一个典型例子。所谓“加一”&#xff0c;通常指的是给一个由数字组成的数组表示的整数加一&#xff0c;这听起来简…

PointCore——利用局部全局特征的高效无监督点云异常检测器论文与算法解读

概述 三维点云异常检测旨在从训练集中检测出异常数据点&#xff0c;是工业检测、自动驾驶等众多应用的基础。然而&#xff0c;现有的点云异常检测方法通常采用多个特征存储库来充分保留局部和全局特征表示&#xff0c;这带来了高昂的计算成本以及特征之间的不匹配问题。为解决…

桌面应用UI开发方案

一、基于 Web 技术的跨平台方案 Electron Python/Go 特点&#xff1a; 技术栈&#xff1a;前端使用 HTML/CSS/JS&#xff0c;后端通过 Node.js 集成 Python/Go 模块或服务。 跨平台&#xff1a;支持 Windows、macOS、Linux 桌面端&#xff0c;适合开发桌面应用。 生态成熟&…

redis 配置日志和数据存储位置

Redis配置日志和数据存储位置 介绍 Redis是一个开源的高性能键值存储数据库&#xff0c;常用于缓存、消息队列和实时分析等场景。在使用Redis时&#xff0c;我们需要配置日志和数据存储位置&#xff0c;以便更好地管理和监控Redis的运行状态。本文将介绍如何配置Redis的日志和数…

OSI七层网络模型详解

OSI七层网络模型详解 OSI&#xff08;开放系统互连&#xff09;模型是国际标准化组织&#xff08;ISO&#xff09;提出的网络通信框架&#xff0c;旨在规范不同系统间的通信。它分为七层&#xff0c;每层承担特定功能&#xff0c;协同实现端到端的数据传输。 1. 物理层&#x…

Springboot 学习 之 logback-spring.xml 日志打印

文章目录 1. property2. springProperty3. appender4. logger4.1. 通过包路径控制日志4.2. 通过类名控制日志4.3. 按自定义 Logger 名称控制日志 5. root6. springProfile SpringBoot 项目中可以通过自定义 logback-spring.xml 中各项配置&#xff0c;实现日志的打印控制 1. p…

Gradle与Idea整合

文章目录 1. Groovy 简介2. Groovy 安装[非必须]3. 在idea中创建java工程 1. Groovy 简介 在某种程度上&#xff0c;Groovy可以被视为Java的一种脚本化改良版,Groovy也是运行在JVM上&#xff0c;它可以很好地与Java代码及其相关库进行交互操作。它是一种成熟的面向对象编程语言…

OpenFeign终极指南:超时控制、重试策略、拦截器与自定义Starter

目录 前言 使用 引入依赖 开启feign 编写feign客户端 效果 日志 超时配置 重试机制 拦截器 Fallback兜底返回 引入依赖 编写兜底实现 连接池 引入依赖 开启连接池 制作OpenFeign Starter 编写配置类 自动装配 前言 在RPC框架中&#xff0c;有openFeign和Du…

Windows桌面图标变白的解决方案

一、问题原因 桌面图标变白通常是由于系统图标缓存文件&#xff08;IconCache.db&#xff09;损坏或系统图表示现异常导致。图标缓存是Windows用于存储应用程序和文件夹图标图像的临时文件&#xff0c;当该文件损坏或系统未正确更新缓存时&#xff0c;图标会因无法加载原始图像…

【mysql】Mac 通过 brew 安装 mysql 、启动以及密码设置

Mac 通过 brew 安装 mysql 、启动以及密码设置 使用 brew 安装 mysqlmysql 启动mysql密码设置参考文章&#xff1a; 使用 brew 安装 mysql brew install mysqlmysql 启动 下载完毕&#xff0c;终端告诉我们mysql数据库没有设置密码的&#xff0c;我们可以直接执行 mysql -u r…

Manus AI:突破多语言手写识别技术壁垒之路

Manus AI与多语言手写识别 讨论Manus AI如何突破多语言手写识别的技术壁垒。 写一篇详细的博客有重点有链接超详细 Manus AI&#xff1a;突破多语言手写识别技术壁垒之路 在人工智能领域&#xff0c;多语言手写识别一直是极具挑战性的难题。不同语言的字符形态、书写规则大相…

Redis字符串类型实战:解锁五大高频应用场景

精心整理了最新的面试资料和简历模板&#xff0c;有需要的可以自行获取 点击前往百度网盘获取 点击前往夸克网盘获取 Redis的字符串&#xff08;String&#xff09;类型是最基础的数据结构&#xff0c;但其灵活性和原子性操作使其成为解决高并发场景问题的利器。本文通过真实项…

边沿耦合与宽边耦合的串扰

边沿耦合与宽边耦合的串扰 我们知道&#xff0c;如果两条走线位于同一层&#xff0c;由于耦合两条线之间会存在串扰。如果PCB层叠中有相邻的信号层&#xff0c;那么同样存在耦合&#xff0c;这两个相邻信号层的走线之间也会存在串扰。同层走线之间的耦合称为边沿耦合&#xff0…

B端可视化像企业数据的透视镜,看清关键信息

在数字化时代&#xff0c;数据已成为企业最宝贵的资产之一。然而&#xff0c;数据的价值不仅取决于其数量&#xff0c;更在于企业能否快速、准确地提取关键信息并据此做出决策。B端可视化技术的出现&#xff0c;为企业提供了一种强大的工具&#xff0c;它如同企业的“透视镜”&…

苍穹外卖项目中所涉及到的测试内容

1.使用JWT令牌封装用户令牌&#xff0c;并且设置相应的拦截器校验JWT的有效性&#xff0c;从而确保了项目的安全可靠 1.基本功能测试&#xff1a; 验证合法JWT是否能够正常通过拦截器的校验 验证非法的JWT能否正常通过拦截器的校验 2.可靠性测试&#xff1a; 3.易用性测试 …

模拟投资大师思维:AI对冲基金开源项目详解

这里写目录标题 引言项目概述核心功能详解多样化的AI投资智能体灵活的运行模式透明的决策过程 安装和使用教程环境要求安装步骤基本使用方法运行对冲基金模式运行回测模式 应用场景和实际价值教育和研究价值潜在的商业应用与现有解决方案的对比局限性与发展方向 结论 引言 随着…

YOLO拓展-锚框(anchor box)详解

一.锚框&#xff08;anchor box&#xff09;概述 1.1什么是锚框 锚框就是一种进行预测的像素框&#xff0c;通过遍历输入图像上所有可能的像素框&#xff0c;然后选出正确的目标框&#xff0c;并对位置和大小进行调整就可以完成目标检测任务。 对于yolo锚框的建设须基于实际…

Excel自定义函数取拼音首字母

1.启动Excel 2003&#xff08;其它版本请仿照操作&#xff09;&#xff0c;打开相应的工作表&#xff1b; 2.执行“工具 > 宏 > Visual Basic编辑器”命令&#xff08;或者直接按“AltF11”组合键&#xff09;&#xff0c;进入Visual Basic编辑状态&#xff1b; 3.执行“…