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服务器,…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...
令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
华为OD机试-最短木板长度-二分法(A卷,100分)
此题是一个最大化最小值的典型例题, 因为搜索范围是有界的,上界最大木板长度补充的全部木料长度,下界最小木板长度; 即left0,right10^6; 我们可以设置一个候选值x(mid),将木板的长度全部都补充到x,如果成功…...
Ubuntu系统复制(U盘-电脑硬盘)
所需环境 电脑自带硬盘:1块 (1T) U盘1:Ubuntu系统引导盘(用于“U盘2”复制到“电脑自带硬盘”) U盘2:Ubuntu系统盘(1T,用于被复制) !!!建议“电脑…...
git: early EOF
macOS报错: Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/.git/ remote: Enumerating objects: 2691797, done. remote: Counting objects: 100% (1760/1760), done. remote: Compressing objects: 100% (636/636…...
数据结构第5章:树和二叉树完全指南(自整理详细图文笔记)
名人说:莫道桑榆晚,为霞尚满天。——刘禹锡(刘梦得,诗豪) 原创笔记:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 上一篇:《数据结构第4章 数组和广义表》…...
机器学习的数学基础:线性模型
线性模型 线性模型的基本形式为: f ( x ) ω T x b f\left(\boldsymbol{x}\right)\boldsymbol{\omega}^\text{T}\boldsymbol{x}b f(x)ωTxb 回归问题 利用最小二乘法,得到 ω \boldsymbol{\omega} ω和 b b b的参数估计$ \boldsymbol{\hat{\omega}}…...
电脑桌面太单调,用Python写一个桌面小宠物应用。
下面是一个使用Python创建的简单桌面小宠物应用。这个小宠物会在桌面上游荡,可以响应鼠标点击,并且有简单的动画效果。 import tkinter as tk import random import time from PIL import Image, ImageTk import os import sysclass DesktopPet:def __i…...
【PX4飞控】mavros gps相关话题分析,经纬度海拔获取方法,卫星数锁定状态获取方法
使用 ROS1-Noetic 和 mavros v1.20.1, 携带经纬度海拔的话题主要有三个: /mavros/global_position/raw/fix/mavros/gpsstatus/gps1/raw/mavros/global_position/global 查看 mavros 源码,来分析他们的发布过程。发现前两个话题都对应了同一…...
C++11 constexpr和字面类型:从入门到精通
文章目录 引言一、constexpr的基本概念与使用1.1 constexpr的定义与作用1.2 constexpr变量1.3 constexpr函数1.4 constexpr在类构造函数中的应用1.5 constexpr的优势 二、字面类型的基本概念与使用2.1 字面类型的定义与作用2.2 字面类型的应用场景2.2.1 常量定义2.2.2 模板参数…...
