Spring AI -快速开发ChatGPT应用
-
Spring AI介绍
Spring AI是AI工程师的一个应用框架,它提供了一个友好的API和开发AI应用的抽象,旨在简化AI应用的开发工序,例如开发一款基于ChatGPT的对话、图片、音频等应用程序。
Spring AI已经集成了OpenAI的API,因此我们不需要实现向OpenAI发送请求和接收响应的交互程序了,Spring AI已经实现了这一内容,我们只需要通过调用Spring AI为我们提供的接口即可
项目地址:https://github.com/spring-projects-experimental/spring-ai
文档地址:https://docs.spring.io/spring-ai/reference/
Spring AI能做什么?
- 支持目前主流大语言模型平台,例如 OpenAI、Microsoft、Amazon、Google 和 Huggingface;
- 支持阻塞与流式的文本对话;
- 支持图像生成(当前仅限OpenAI的dall-e-*模型和SD);
- 支持嵌入模型;
- 支持LLM生成的内容转为POJO;
- 支持主流的向量数据库或平台:Azure Vector Search, Chroma, Milvus, Neo4j, PostgreSQL/PGVector, PineCone, Qdrant, Redis 和 Weaviate
- 支持函数调用
- 支持自动装配和启动器(与Spring Boot完美集成);
- 提供用于数据处理工程的ETL框架;
-
项目实践
-
准备工作
-
版本说明
- OpenAI的Key
- OpenAI的Api
- JDK >= 17
- Spring 6.x;Spring Boot 3.x
- Spring AI 0.8.1-SNAPSHOT
-
pom引入
<!-- 仓库定义 -->
<repositories><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>
<!-- 依赖管理配置 -->
<dependencyManagement><dependencies><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-bom</artifactId><version>0.8.1-SNAPSHOT</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement>
<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
用于请求OpenAI平台相关模型,例如:对话用的ChatGPT、画图用的Dall-e-2/3、文本嵌入text-embedding-ada-002以及音频合成与识别的whisper和tts等相关模型。
-
配置文件
application.yml
将相关key和api信息进行填写
spring:ai:openai:api-key: 123base-url: https://api.openai.com
- 快速对话ChatClient
@Slf4j
@RestController
@RequestMapping("/chat")
public class ChatController {@Autowiredprivate ChatClient chatClient;@GetMapping("/demo")public String chat(String prompt){return chatClient.call(prompt);}}
运行结果:
-
流式对话StreamingChatClient
流失对话的核心就是流式传输,AI的响应数据是一点一点传过来的,不用等AI将文本全部生成出来了才传过来。一定程度上能够提高使用上的响应速度,给用户一个非常好的体验。
@Slf4j
@RestController
@RequestMapping("/chat")
public class ChatController {@Autowiredprivate StreamingChatClient streamingChatClient;
// 流式调用 将produces声明为文本事件流@GetMapping(value = "/stream",produces = MediaType.TEXT_EVENT_STREAM_VALUE)public Flux<String> stream(String prompt){long startTime = System.currentTimeMillis();Flux<String> res=streamingChatClient.stream(prompt).flatMapSequential(Flux::just);long endTime = System.currentTimeMillis();long duration = endTime - startTime;log.info("流式调用执行时间:{}",duration);// 将流中的内容按顺序返回return res;}
}
运行结果:
-
上下文对话
ChatGPT上下文对话的实现原理较为简单,本质上其实就是将不同角色的聊天信息依次存储在一个队列中发送给ChatGPT即可,然后ChatGPT会根据整个聊天信息对回复内容进行判断。在OpenAI提供的接口中,每条信息的角色总共分为三类:
- SystemMessage:系统限制信息,这种信息在对话中的权重很大,AI会优先依据SystemMessage里的内容进行回复;
- UserMessage:用户信息
- AssistantMessage:AI回复信息
不过,根据OpenAI的计费规则,你的消息队列越长,单次问询需要的费用就会越高,因此我们需要对这个消息列表的长度进行限制。
@Slf4j
@RestController
@RequestMapping("/chat")
public class ChatController {@Autowiredprivate ChatClient chatClient;// 历史消息列表static List<Message> historyMessage = new ArrayList<>();// 历史消息列表的最大长度static int maxLen = 10;@GetMapping("/context")public String context(String prompt) {// 用户输入的文本是UserMessagehistoryMessage.add(new UserMessage(prompt));// 发给AI前对历史消息对列的长度进行检查if(historyMessage.size() > maxLen){historyMessage = historyMessage.subList(historyMessage.size()-maxLen-1,historyMessage.size());}// 获取AssistantMessageChatResponse chatResponse = chatClient.call(new Prompt(historyMessage));AssistantMessage assistantMessage = chatResponse.getResult().getOutput();// 将AI回复的消息放到历史消息列表中historyMessage.add(assistantMessage);return assistantMessage.getContent();}}
-
人设设定
人设设定功能来自于“提示词(prompt)工程”的理论基础,可用来提高大语言模型处理复杂任务场景的能力
上面介绍Message的时候提到SystemMessage对AI生成的内容影响权重较大,人设设定就是需要靠SystemMessage
实现。我们提供一个SystemMessage放入历史消息列表中,并让SystemMessage在每次发给AI时始终在历史消息列表中。
@Slf4j
@RestController
@RequestMapping("/chat")
public class ChatController {@Autowiredprivate ChatClient chatClient;// 历史消息列表final String systemPrompt="你现在是一个喜欢扮可爱的人,说话嗲嗲的";List<Message> historyMessage = new ArrayList<>(List.of(new SystemMessage(systemPrompt)));// 历史消息列表的最大长度static int maxLen = 10;@GetMapping("/context")public String context(String prompt) {// 用户输入的文本是UserMessagehistoryMessage.add(new UserMessage(prompt));// 发给AI前对历史消息对列的长度进行检查if(historyMessage.size() > maxLen){historyMessage = historyMessage.subList(historyMessage.size()-maxLen-1,historyMessage.size());}// 获取AssistantMessageChatResponse chatResponse = chatClient.call(new Prompt(historyMessage));AssistantMessage assistantMessage = chatResponse.getResult().getOutput();// 将AI回复的消息放到历史消息列表中historyMessage.add(assistantMessage);return assistantMessage.getContent();}}
运行结果:
-
模板语法PromptTemplate
Spring AI为我们提供了提示词模板,允许我们通过一些模板,快速地动态生成提示词并发起提问
@Slf4j
@RestController
@RequestMapping("/prompts")
public class PromptController {@Autowiredprivate ChatClient chatClient;@Value("classpath:prompt.st")private Resource templateResource;@GetMapping("/template")public String promptTemplate(String author){// 提示词final String template = "请问{author}最受欢迎的书是哪本书?什么时候发布的?书的内容是什么?";PromptTemplate promptTemplate = new PromptTemplate(template);// 动态地将author填充进去Prompt prompt = promptTemplate.create(Map.of("author", author));ChatResponse chatResponse = chatClient.call(prompt);AssistantMessage assistantMessage = chatResponse.getResult().getOutput();return assistantMessage.getContent();}@GetMapping("/config/template")public String promptConfigTemplate(String author) {PromptTemplate promptTemplate = new PromptTemplate(templateResource);// 动态地将author填充进去Prompt prompt = promptTemplate.create(Map.of("author", author));ChatResponse chatResponse = chatClient.call(prompt);AssistantMessage assistantMessage = chatResponse.getResult().getOutput();return assistantMessage.getContent();}}
请问{author}最受欢迎的书是哪本书?什么时候发布的?书的内容是什么?
-
OutputParser 生成解析器
SpringAi还为我们提供了OutputParser解析器,该解析器可以将AI生成的内容解析为Java Bean对象。该解析器类似于ORM框架中的Mapper,将AI的生成内容映射为Java对象。
@Slf4j
@RestController
@RequestMapping("/parser")
public class ParserController {@Autowiredprivate ChatClient chatClient;@GetMapping("/bean")public Movie getBookByAuthor(String actor) {final String template = """请告诉我{actor}最受欢迎的电影是哪个?什么时间上映?大概讲了什么?{format}""";// 定义一个输出解析器OutputParser<Movie> movieParser = new BeanOutputParser<>(Movie.class);PromptTemplate promptTemplate = new PromptTemplate(template);Prompt prompt = promptTemplate.create(Map.of("actor", actor, "format", movieParser.getFormat()));ChatResponse chatResponse = chatClient.call(prompt);AssistantMessage assistantMessage = chatResponse.getResult().getOutput();// 解析为一个Bean对象Movie movie = movieParser.parse(assistantMessage.getContent());return movie;}
}@Data
@AllArgsConstructor
@NoArgsConstructor
public class Movie {private String actor;private String movieName;private String publishedDate;private String description;
}
-
绘图ImageClient
Spring AI提供了图片生成接口,该接口可以用于与各种专门用于图像生成的人工智能模型进行交互。
在调用绘图时,我们只需要像调用对话一样传入一个Prompt:ImagePrompt。ImagePrompt中包含了我们需要绘制的图片信息,包括:ImageMessage(绘图指令)、ImageOptions(图片数、图片配置、返回的图片格式、绘图模型等)。AI拿到我们的Prompt后会根据里面的内容对图像进行生产
ImageOptions重要属性
- model:绘图模型,默认dall-e-3
- dall-e-3:1024 x 1024 、 1024 x 1792、1792 x 1024;
- dall-e-2: 256 x 256、512 x 512 、 1024 x 1024;
- responseFormat:返回的图片格式,url 和 b64_json
@Slf4j
@RestController
@RequestMapping("/image")
public class ImageController {@Autowiredprivate ImageClient imageClient;@GetMapping("/image")public String image(String prompt) {ImagePrompt imagePrompt =new ImagePrompt(prompt, OpenAiImageOptions.builder().withModel(OpenAiImageApi.ImageModel.DALL_E_3.getValue()).withHeight(1024).withWidth(1024).withResponseFormat("url") // URL or b64_json.build());ImageResponse imageResponse = imageClient.call(imagePrompt);List<ImageGeneration> results = imageResponse.getResults();// 图片urlString url = results.get(0).getOutput().getUrl();return String.format("<img src='%s' alt='%s'>",url,prompt);}
}
-
AI自查实现对话和绘图
通过AI自查手段将文本模型和图片生成模型进行组合实现一个既可以生成文本也可以生成AI的接口。这个关键点就是利用提示词限制AI的回复内容以达到一个自查手段
AI自查就是让AI判断你的问题是画一个图还是简简单单的对话。
- 用户输入文本prompt;
- 先让AI判断文本prompt是否需要图片;
- 如果需要图片,调用绘图模型获取绘图结果;
- 如果不需要图片,直接调用对话模型;’
@Slf4j
@RestController
@RequestMapping("/judge")
public class JudgeByAiController {@Autowiredprivate ChatClient chatClient;@Autowiredprivate ImageClient imageClient;@Value("classpath:judge.st")private Resource templateResource;@RequestMapping("/ai")public String ai(String prompt){try {return judge(prompt)?image(prompt):chat(prompt);} catch (Exception e) {return "error";}}private boolean judge(String promptString){PromptTemplate promptTemplate = new PromptTemplate(templateResource);// 动态地将prompt填充进去Prompt prompt = promptTemplate.create(Map.of("prompt", promptString));ChatResponse chatResponse = chatClient.call(prompt);AssistantMessage assistantMessage = chatResponse.getResult().getOutput();String judgeResult=assistantMessage.getContent();return judgeResult.toLowerCase().contains("yes")?true:false;}private String chat(String prompt){String res=chatClient.call(prompt);return res;}private String image(String prompt) {ImagePrompt imagePrompt =new ImagePrompt(prompt, OpenAiImageOptions.builder().withModel(OpenAiImageApi.ImageModel.DALL_E_3.getValue()).withHeight(1024).withWidth(1024).withResponseFormat("url") // URL or b64_json.build());ImageResponse imageResponse = imageClient.call(imagePrompt);List<ImageGeneration> results = imageResponse.getResults();// 图片urlString url = results.get(0).getOutput().getUrl();return String.format("<img src='%s' alt='%s'>",url,prompt);}
}
Does this message want to generate an AI picture, image, art or anything similar? {prompt} . Simply answer with a yes or no.
-
LLM领域新浪潮即将来临
openAI真正走向CloseAI,LLM国产替代浪潮即将到来
危!OpenAI 将限制中国开发者访问 API 服务-CSDN博客
相关文章:

Spring AI -快速开发ChatGPT应用
Spring AI介绍 Spring AI是AI工程师的一个应用框架,它提供了一个友好的API和开发AI应用的抽象,旨在简化AI应用的开发工序,例如开发一款基于ChatGPT的对话、图片、音频等应用程序。 Spring AI已经集成了OpenAI的API,因此我们不需…...

Modern C++ 智能指针
Why? 原始指针存在缺陷,不符合现代编程语言的需要。 原始指针的缺陷: 指针指向一片内存,使用者无法得知到底是指向了什么,是数组还是对象?使用完指针是否需要销毁?什么时候销毁?如…...

Python的100道经典练习题,每日一练,必成大神!!!
Python的100道经典练习题是一个广泛而深入的学习资源,可以帮助Python初学者和进阶者巩固和提升编程技能 完整的100多道练习题可在下面图片免沸获取哦~ 整理了100道Python的题目,如果你是一位初学者,这一百多道题可以 帮助你轻松的使用Python…...

代码回滚命令
定位到当前分支 git branch回滚到指定的commit git reset --hard 85da0cb8322accad143cpush到远程分支 git push --force...

[ASIS 2019]Unicorn shop1
打开题目 随便输入信息看一下 操作失败,只让输入一个字符 不妨抓包看一下,信息,发现 从中可以发现源代码是如何处理price的 使用的是unicodedata.numeric() 但我们查看页面源码时,看到源码处理方式是utf-8 所以,前…...
LangChain与泛型编程:探索代码生成的新维度
LangChain与泛型编程:探索代码生成的新维度 在软件开发领域,泛型编程是一种允许创建可重用组件的技术,这些组件可以在多种数据类型上工作的编程范式。LangChain作为一个假设的编程辅助工具,如果存在,它可能会支持泛型…...

day25
一、进程间通信(IPC) 1.1 进程间通信的引入 1> 对于多个线程之间通信,我们可以使用临界资源来完成,通过一个线程任务对临界资源进行修改,另一个线程也可以使用已经修改过的临界资源,但是要注意使用…...

红黑树的概念和模拟实现[C++]
文章目录 红黑树的概念一、红黑树的性质红黑树原理二、红黑树的优势和比较 红黑树的模拟实现构建红黑树的数据结构定义节点的基本结构和初始化方式插入新节点插入新节点的颜色调整颜色和结构以满足红黑树性质 红黑树的应用场景 红黑树的概念 一、红黑树的性质 红黑树是一种自平…...
网络安全应急响应概述
前言 在网络安全领域,有一句广为人知的话:“没有绝对的安全”。这意味着任何系统都有可能被攻破。安全攻击的发生并不可怕,可怕的是从头到尾都毫无察觉。当系统遭遇攻击时,企业的安全人员需要立即进行应急响应,以将影响…...

【C++】链表操作技巧综合:重排链表(带你理顺链表的做题思路)
1.题目 2.算法思路 这是一道关于链表的综合题,一共涉及到三个步骤,其中每个步骤单拎出来就可以当一道单独的题目。所以需要大家对链表的操作十分熟悉,否则可能需要大量的时间做这道题目,而且还要很多的bug。 第一个步骤…...

行为型设计模式2:观察者/职责链/中介者/访问者
设计模式:观察者/职责链/中介者/访问者 (qq.com)...

叛逆,批判
1、对以往说法的批判之一(第一次这么公开批判是2004-2005年): 这部英文版的《数学百科全书》似乎是从俄语版翻译过来的?我查了三本引用的图书文献,都没有关于“nonsingular”和“singular”的类似下面的说法ÿ…...

Linux 命令,mkdir说明与使用
1:mkdir命令功用: 用于创建一个或多个目录,创建目录,必须在父目录中写上权限。 新目录的默认模式为0777,可以由系统或用的umask来修改。 2:命令构件: mkdir [options] directories 3:参数选项: -m&#x…...

24. 两两交换链表中的节点(Java)
目录 题目描述:示例 :代码实现: 题目描述: 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换&am…...

linux虚拟机设置固定ip
修改/etc/sysconfig/network-scripts目录下ifcfg-eth0文件,各虚拟机这个文件名不一致,ifcfg-XX格式 vim /etc/sysconfig/network-scripts/ifcfg-eth0BOOTPROTO设置为static,然后在最后添加固定IP地址和默认网关、DNS等配置,IP地址…...

mysql问题解决
1.etl数据同步时,发现连接不上要同步的数据库 解决方法:关闭mysql的ssl,步骤如下: 在MySQL中禁用SSL连接涉及修改服务器的配置文件(通常是my.cnf或my.ini,取决于你的操作系统和MySQL版本)。以…...

类和对象(下)C++
1.初始化列表 1.为什么有初始化列表,它的作用? ->初始化列表,是构造函数初始化的另一种形式。 ->在语法上面理解,初始化列表可以认定为是每个成员变量定义初始化的地方. ->引用成员变量,const成员变量&am…...

常用在线 Webshell 查杀工具推荐
一、简介 这篇文章将介绍几款常用的在线 Webshell 查杀工具,包括长亭牧云、微步在线云沙箱、河马和VirusTotal。每个工具都有其独特的特点和优势,用于帮助用户有效检测和清除各类恶意 Webshell,保障网站和服务器的安全。文章将深入探讨它们的…...

RPC远程调用框架Dubbo
一、分布式服务调用_什么是RPC RPC(Remote Procedure Call)远程过程调用,它是一种通过网络从远程计算机程序上请求服务。 大白话理解就是:RPC让你用别人家的东西就像自己家的一样。 RPC两个作用: 屏蔽远程调用跟本地调用的区别,…...

基于STM32的智能灌溉系统
目录 引言环境准备工作 硬件准备软件安装与配置系统设计 系统架构硬件连接代码实现 初始化代码传感器读取和控制代码应用场景 农业灌溉花园自动灌溉常见问题及解决方案 常见问题解决方案结论 1. 引言 智能灌溉系统通过实时监测土壤湿度和环境温度,自动控制灌溉设…...

【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...

python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...

Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...
Java编程之桥接模式
定义 桥接模式(Bridge Pattern)属于结构型设计模式,它的核心意图是将抽象部分与实现部分分离,使它们可以独立地变化。这种模式通过组合关系来替代继承关系,从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...

【从零学习JVM|第三篇】类的生命周期(高频面试题)
前言: 在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。 目录 …...

【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
多模态图像修复系统:基于深度学习的图片修复实现
多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...