Spring-AI讲解
Spring-AI
langchain(python) langchain4j
官网:
https://spring.io/projects/spring-ai#learn
整合chatgpt
前置准备
- open-ai-key:
https://api.xty.app/register?aff=PuZD
https://xiaoai.plus/
https://eylink.cn/
或者淘宝搜: open ai key - 魔法软件, 由于国家相关法律规定,建议大家自行准备。(中转可暂不准备)
- jdk17
4 springboot3
示例代码:https://gitee.com/xscodeit/ai-openai-examples.git
实现

创建完后会发现加入了依赖:
<dependencyManagement><dependencies><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-bom</artifactId><version>1.0.0-SNAPSHOT</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-openai-spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies>
设置代理, 如果你请求的大模型的api接口不是国内的。需要将程序设置代理:
public static void main(String[] args) {// 设置代理String proxy = "127.0.0.1"; // 如果代理在你本机就127.0.0.1 , 如果代理是其他服务器相应设置int port = 7890; //设置科学上网代理的端口,System.setProperty("proxyType", "4");System.setProperty("proxyPort", Integer.toString(port));System.setProperty("proxyHost", proxy);System.setProperty("proxySet", "true");SpringApplication.run(Application.class, args);}
设置key:
xushu:# 官方(自行配置)openai:key: ${OPENAI_KEY}url: ${OPENAI_URL}# 中转(自行配置)aicore:key: ${OPEN_AI_KEY}url: ${OPEN_AI_URL}spring:ai:openai:api-key: ${xushu.aicore.key}base-url: ${xushu.aicore.url}
示例代码:
private final ChatClient chatClient;
private final OpenAiChatModel chatClient2;
private final OpenAiImageModel imageClient;
private final OpenAiAudioTranscriptionModel audioClient;
private final OpenAiAudioApi openAiAudioApi;@Value("${OPEN_AI_KEY}")
private String openAiKey;@GetMapping("/ai/simple")
public Map<String, String> completion(@RequestParam(value = "message", defaultValue = "给我讲个笑话") String message) {System.out.println(openAiKey);var value=chatClient.prompt().user(message).call().content();return Map.of("generation",value );
}@GetMapping(value="/ai/stream",produces="text/sse;charset=UTF-8")
public Flux<String> stream(@RequestParam(value = "message", defaultValue = "给我讲个笑话") String message ) {System.out.println(openAiKey);return chatClient.prompt().user(message).stream().content();
}@GetMapping(value="/ai/img",produces="text/html")
public String image(@RequestParam(value = "message", defaultValue = "猫") String message ) {ImageResponse response = imageClient.call(new ImagePrompt(message,OpenAiImageOptions.builder().withQuality("hd").withN(1).withModel(OpenAiImageApi.ImageModel.DALL_E_2.getValue())// dall-e-2 256.withHeight(256).withWidth(256).build()));String url = response.getResult().getOutput().getUrl();System.out.println(url);return "<img src='"+url+"'/>";}@GetMapping(value="/ai/audit2text")
public String audit2text() {var transcriptionOptions = OpenAiAudioTranscriptionOptions.builder().withResponseFormat(OpenAiAudioApi.TranscriptResponseFormat.TEXT).withTemperature(0f).build();// flac、mp3、mp4、mpeg、mpga、m4a、ogg、wav 或 webm。var audioFile = new ClassPathResource("/hello.mp3");AudioTranscriptionPrompt transcriptionRequest = new AudioTranscriptionPrompt(audioFile, transcriptionOptions);AudioTranscriptionResponse response = audioClient.call(transcriptionRequest);//openAiAudioApi.createTranscription()return response.getResult().getOutput();
}@GetMapping(value="/ai/text2audit")
public String text2audit() {ResponseEntity<byte[]> speech = openAiAudioApi.createSpeech(OpenAiAudioApi.SpeechRequest.builder().withVoice(OpenAiAudioApi.SpeechRequest.Voice.ONYX).withInput("你好,我是徐庶").build());byte[] body = speech.getBody();// 将byte[]存为 mp3文件try {writeByteArrayToMp3(body, System.getProperty("user.dir"));} catch (IOException e) {throw new RuntimeException(e);}return "ok";
}public static void writeByteArrayToMp3(byte[] audioBytes, String outputFilePath) throws IOException {// 创建FileOutputStream实例FileOutputStream fos = new FileOutputStream(outputFilePath+"/xushu.mp3");// 将字节数组写入文件fos.write(audioBytes);// 关闭文件输出流fos.close();
}@GetMapping(value="/ai/mutil")
public String mutilModel(String message,String imgUrl) throws IOException {byte[] imageData = new ClassPathResource("/test.png").getContentAsByteArray();var userMessage = new UserMessage("这个图片你看出什么?", // contentList.of(new Media(MimeTypeUtils.IMAGE_PNG, imageData))); // mediaChatResponse response = chatClient.call(new Prompt(userMessage,OpenAiChatOptions.builder().withModel(OpenAiApi.ChatModel.GPT_4_TURBO_PREVIEW.getValue()).build()));return response.getResult().getOutput().getContent();
}
function-call
AI本身是不具备实时消息能力的, 比如问“现在北京的天气是什么”, AI是不知道的, 这个时候我们需要通过接口来帮助AI完成,大致流程:

function-call实现的代码:
- 在发送文本时, 同时设置Funcation ; 关键代码:.withFunction(“getWaitTime”)
ChatResponse response = chatClient.call(new Prompt(List.of(userMessage),OpenAiChatOptions.builder().withFunction("getWaitTime").build()));
- 当代码执行完call时(AI响应之后), 会再调用getWaitTime对应的bean的apply方法。
@Bean
public Function<WaitTimeService.Request, WaitTimeService.Response> getWaitTime() {return new WaitTimeService();
}
- 并且会把 getWaitTime该bean的Request作为funcation-call的返回参数, 即可在apply方法中获取Request对应的参数
@Override
public Response apply(Request request) {String name = request.name();String location = request.location();// todo...return new Response(location+"有10个!");
}
// jdk17新特性 密封类
public record Request(String name,String location) {}
public record Response(String weather) {}
随后Response会再次丢给AI组织语言, 进行响应,最终
源码:
看源码之前, 了解下该接口需要哪些参数:https://platform.openai.com/docs/api-reference/chat/create
请求: userMessage=“长沙有多少叫徐庶的”
调用call方法, 执行openai的远程api请求
@Override
public ChatResponse call(Prompt prompt) {ChatCompletionRequest request = createRequest(prompt, false);return this.retryTemplate.execute(ctx -> {ResponseEntity<ChatCompletion> completionEntity = this.callWithFunctionSupport(request);var chatCompletion = completionEntity.getBody();if (chatCompletion == null) {logger.warn("No chat completion returned for prompt: {}", prompt);return new ChatResponse(List.of());}RateLimit rateLimits = OpenAiResponseHeaderExtractor.extractAiResponseHeaders(completionEntity);List<Generation> generations = chatCompletion.choices().stream().map(choice -> {return new Generation(choice.message().content(), toMap(chatCompletion.id(), choice)).withGenerationMetadata(ChatGenerationMetadata.from(choice.finishReason().name(), null));}).toList();return new ChatResponse(generations,OpenAiChatResponseMetadata.from(completionEntity.getBody()).withRateLimit(rateLimits));});
- 通过createRequest将withFunction参数解析到request.tools
ChatCompletionRequest request = createRequest(prompt, false);
tools属性:包含了

关于open-ai对tools参数的说明:https://platform.openai.com/docs/api-reference/chat/create#chat-create-tools

-
来到callWithFunctionSupport 真正调用远程api接口
protected Resp callWithFunctionSupport(Req request) { Resp response = this.doChatCompletion(request); return this.handleFunctionCallOrReturn(request, response); }
这里2句关键代码: -
this.doChatCompletion(request); 方法会正常请求chat completion 接口并且会带上funcation-call参数并携带tools属性 , 并且返回对话中的funcation-call所需参数(即WaitTimeService.Request的参数)

-
handleFunctionCallOrReturn 执行Function-callback方法, 此时会调用WaitTimeService.apply方法
a. 拿到之前解析的functionCallback即
b. 将arguments从Json转换为对象调用WaitTimeService.apply
c. 将返回的数据再次请求大模型
发展
SpringAI社区非常活跃
在后续的版本都会更新国内常用的大模型
https://github.com/spring-projects/spring-ai/issues?q=Moonshot

阿里也率先为自己得通义大模型封装了基于SpringAI的spring-cloud-starter-alibaba-ai
https://sca.aliyun.com/?spm=0.29160081.0.0.77ff60c5NGK3QD
相关文章:
Spring-AI讲解
Spring-AI langchain(python) langchain4j 官网: https://spring.io/projects/spring-ai#learn 整合chatgpt 前置准备 open-ai-key: https://api.xty.app/register?affPuZD https://xiaoai.plus/ https://eylink.cn/ 或者淘宝搜: open ai key魔法…...
【brew安装失败】DNS 查询 raw.githubusercontent.com 返回的是 0.0.0.0
从你提供的 nslookup 输出看,DNS 查询 raw.githubusercontent.com 返回的是 0.0.0.0,这通常意味着无法解析该域名或该域名被某些 DNS 屏蔽了。这种情况通常有几个可能的原因: 可能的原因和解决方法 本地 DNS 问题: 有可能是你的本…...
HTML——29. 音频引入二
<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>音频引入</title></head><body><!--audio:在网页中引入音频IE8以及之前版本不支持属性名和属性值一样,可以只写属性名src属性:指定音频文件…...
代码随想录训练营第三十四天| 62.不同路径 63. 不同路径 II
62.不同路径 题目链接:62. 不同路径 - 力扣(LeetCode) 讲解链接:代码随想录 动态规划五步走 1 定义dp数组是到dp[i][j]时有dp[i][j]条路径 dp[i][j] :表示从(0 ,0)出发…...
V90伺服PN版组态配置<一>
1、添加PLC之后,继续博图中网络视图中添加新设备,添加伺服驱动器组态设备 2、SINAMICS V90 PN V1.0 3、修改驱动器的IP地址。 【注意】 在项目中提前做好项目规划,如PLC设备从192.168.0.1开始,顺序递增------个位数都是CPU设备…...
又一年。。。。。。
2024,浑浑噩噩的一年。 除了100以内的加减法(数据,数据,还是数据。。。。。。),似乎没做些什么。 脸盲症越来越重的,怕是哪天连自己都不认得自己的了。 看到什么,听到什…...
xterm + vue3 + websocket 终端界面
xterm.js 下载插件 // xterm npm install --save xterm// xterm-addon-fit 使终端适应包含元素 npm install --save xterm-addon-fit// xterm-addon-attach 通过websocket附加到运行中的服务器进程 npm install --save xterm-addon-attach <template><div :…...
医疗数仓业务数据采集与同步
业务数据采集与同步 业务采集组件配置业务数据同步概述数据同步策略选择数据同步工具概述1.1.4 全量表数据同步DataX配置文件生成全量表数据同步脚本增量表数据同步 MySQL - Maxwell - Kafka - Flume - HDFSMaxwell配置增量表首日全量同步 业务采集组件配置 Maxwell将业务采集到…...
数字孪生智慧水利与水务所包含的应用场景有哪些?二者有何区别
水利和水务是两个密切相关但有所区别的概念,它们在水资源管理和保护方面各自承担着不同的职责和功能。 定义 智慧水务:智慧水务是指通过物联网、大数据、云计算、人工智能等新一代信息技术,对城市供水、排水、污水处理、水质监测等水务系统…...
Qt Creator项目构建配置说明
QT安装好之后,在安装目录的Tools\QtCreator\bin下找到qtcreator.exe文件并双击打开 点击文件-新建文件或项目 选择Qt Widgets Application 设置项目名称以及路径 make工具选择qmake(cmake还未尝试过) 设置主界面对应类的名称、父类&#…...
进程间通信的“五大武器”
😄作者简介: 小曾同学.com,一个致力于测试开发的博主⛽️,主要职责:测试开发、CI/CD 如果文章知识点有错误的地方,还请大家指正,让我们一起学习,一起进步。 😊 座右铭:不…...
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之循环结构(for循环语句)(六)
实战训练1—输出九九乘法表 问题描述: 在学校里学过九九乘法表,编程实现打印九九乘法表。 输入格式: 无输入 输出格式: 1*11 2*12 2*24 3*13 3*26 3*39 4*14 4*28 4*312 4*416 5*15 5*210 5*315 5*420 5*525 6*16 6*212 6*318 6*424 6*5…...
封装echarts成vue component
封装echarts成vue component EChartsLineComponent 文章目录 封装echarts成vue component EChartsLineComponent封装说明重写重点EChartsLineComponent的源码 使用说明调用EChartsLineComponent示例源码 封装说明 为了减少一些公共代码和方便使用echarts的line图形,…...
uniapp Stripe 支付
引入 Stripe npm install stripe/stripe-js import { loadStripe } from stripe/stripe-js; Stripe 提供两种不同类型组件 Payment Element 和 Card Element:如果你使用的是 Payment Element,它是一个更高级别的组件,能够自动处理多种支…...
Windows onnxruntime编译openvino
理论上来说,可以直接访问 ONNXRuntime Releases 下载 dll 文件,然后从官方文档中下载缺少的头文件以直接调用,但我没有尝试过。 1. 下载 OpenVINO 包 从官网下载 OpenVINO 的安装包并放置在 C:\Program Files (x86) 路径下,例如…...
vue3+TS+vite中Echarts的安装与使用
概述 技术栈:Vue3TsViteEcharts 简述:图文详解,教你如何在Vue项目中引入Echarts,封装Echarts组件,并实现常用Echats图列 文章目录 一,效果图 二,引入Echarts 2.1安装Echarts 2.2main.ts中引…...
期末算法分析程序填空题
目录 5-1 最小生成树(普里姆算法) 5-2 快速排序(分治法) 输入样例: 输出样例: 5-3 归并排序(递归法) 输入样例: 输出样例: 5-4 求解编辑距离问题(动态规划法)…...
搭建android开发环境 android studio
1、环境介绍 在进行安卓开发时,需要掌握java,需要安卓SDK,需要一款编辑器,还需要软件的测试环境(真机或虚拟机)。 早起开发安卓app,使用的是eclipse加安卓SDK,需要自行搭建。 目前开…...
R语言6种将字符转成数字的方法,写在新年来临之际
咱们临床研究中,拿到数据后首先要对数据进行清洗,把数据变成咱们想要的格式,才能进行下一步分析,其中数据中的字符转成数字是个重要的内容,因为字符中常含有特殊符号,不利于分析,转成数字后才能…...
RocketMQ学习笔记(持续更新中......)
目录 1. 单机搭建 2. 测试RocketMQ 3. 集群搭建 4. 集群启动 5. RocketMQ-DashBoard搭建 6. 不同类型消息发送 1.同步消息 2. 异步消息发送 3. 单向发送消息 7. 消费消息 1. 单机搭建 1. 先从rocketmq官网下载二进制包,ftp上传至linux服务器,…...
2026墙体广告供应商亲测靠谱!
行业痛点分析墙体广告领域面临着诸多核心技术挑战。传统户外大牌、短视频投放费用高昂,单次投放曝光有限,数据表明,下沉市场触达成本居高不下,中小品牌难以承担长期投放。城市广告无法渗透乡镇、农村等下沉市场,目标客…...
毕业设计别再只做温度计了!用STM32打造多功能测量仪,让你的毕设脱颖而出
突破传统:用STM32打造智能测量仪器的毕业设计实战指南 当毕业设计季来临,许多电子工程专业的学生陷入了选题困境——温度计、蓝牙小车、智能家居控制...这些被无数前辈重复实现的项目早已失去了新意。如何在众多相似作品中脱颖而出?本文将带你…...
Prodigal原核生物基因预测工具:3天从零到精通的完整指南
Prodigal原核生物基因预测工具:3天从零到精通的完整指南 【免费下载链接】Prodigal Prodigal Gene Prediction Software 项目地址: https://gitcode.com/gh_mirrors/pr/Prodigal 你是否正在为原核生物基因组分析而烦恼?面对海量的DNA序列数据&…...
1Remote终极指南:如何快速管理所有远程连接
1Remote终极指南:如何快速管理所有远程连接 【免费下载链接】1Remote One Remote Access Manager to Rule Them All 项目地址: https://gitcode.com/gh_mirrors/1r/1Remote 1Remote是一款现代化的个人远程会话管理器,专为IT专业人士和开发者设计&…...
PyMol实战:从PDB下载1lEP到绘制靶点-药物相互作用图的保姆级教程
PyMol实战:从PDB下载1lEP到绘制靶点-药物相互作用图的保姆级教程 在药物研发和结构生物学领域,可视化分析靶点-药物相互作用是理解分子识别机制的关键环节。PyMol作为一款专业的分子可视化工具,能够帮助研究人员从原子层面解析蛋白质-配体复合…...
从‘过拟合陷阱’到可靠评估:手把手教你用Python和Scikit-learn玩转交叉验证(含RepeatedKFold/LeaveOneOut)
从‘过拟合陷阱’到可靠评估:手把手教你用Python和Scikit-learn玩转交叉验证(含RepeatedKFold/LeaveOneOut) 当你满怀期待地将训练集上准确率高达98%的模型部署到生产环境,却发现实际预测效果惨不忍睹时,那种落差感就…...
深入YOLOv8损失函数:为什么自带的Focal Loss会报错?一次完整的源码调试与修复记录
深入YOLOv8损失函数:为什么自带的Focal Loss会报错?一次完整的源码调试与修复记录 在目标检测领域,YOLOv8凭借其卓越的性能和易用性赢得了广泛关注。然而,当开发者尝试深入模型内部机制时,往往会遇到一些意料之外的挑战…...
MC/DC覆盖率:从原理到实战,破解100%覆盖率的迷思与挑战
1. 项目概述:当“完美”成为负担在软件测试领域,尤其是对安全关键系统(比如航空航天、汽车电子、医疗设备)的验证,我们常常听到一个词:100%覆盖率。这听起来像是一个终极目标,一个完美的终点。但…...
MATLAB数据处理小技巧:用reshape函数把一维数组变成你想要的任意形状(附图像处理实例)
MATLAB数据处理实战:reshape函数的高效应用与图像处理案例 当你面对一堆杂乱无章的一维数据时,是否曾为如何将其整理成适合分析的格式而头疼?在MATLAB中,reshape函数就像一位魔术师,能够在不改变数据本质的情况下&…...
STM32串口高效通信秘籍:巧用DMA+空闲中断实现不定长数据收发(基于CubeIDE)
STM32串口高效通信秘籍:巧用DMA空闲中断实现不定长数据收发(基于CubeIDE) 在物联网设备和嵌入式系统开发中,串口通信是最基础也最关键的通信方式之一。无论是传感器数据采集、设备间通信还是与上位机交互,稳定高效的串…...
