前言
当一开始听说Spring-AI项目时是很懵的,什么?Spring开始训练模型了?不应该啊,Java还能卷模型赛道了吗?...
打开官网了解了下,才知道原来是缝合怪:
Spring AI is an application framework for AI engineering. Its goal is to apply to the AI domain Spring ecosystem design principles such as portability and modular design and promote using POJOs as the building blocks of an application to the AI domain.
官网地址:https://spring.io/projects/spring-ai
换言之就是虽然Java不能卷算法,但是我可以提供一套封装来让你们调用模型提供的API服务,众所周知,我的抽象封装能力还是很强的。你看你们现在这么多厂商提供服务,不得需要一个统一的门面来减少接入成本嘛...
官网列举了目前国外主流的平台:
Chat Models
OpenAI
Azure Open AI
Amazon Bedrock
Cohere's Command
AI21 Labs' Jurassic-2
Meta's LLama 2
Amazon's Titan
Google Vertex AI Palm
Google Gemini
HuggingFace - access thousands of models, including those from Meta such as Llama2
Ollama - run AI models on your local machine
MistralAI
Text-to-image Models
OpenAI with DALL-E
StabilityAI
Transcription (audio to text) Models
OpenAI
... 数不胜数,更何况还有很多国内大厂开源的模型。
所以Spring提供了一套统一的封装门面,其他的厂商也可以基于门面来实现自己的Client,比如Alibaba就接入了com.alibaba.cloud.ai.tongyi.chat.TongYiChatClient
。
这就引申出了本博客,记录了下我接入Spring-Ai(OpenAI)、Spring-Ai-Alibaba(TongYi)的过程。
接入Spring-Ai
Spring-Ai的接入相对简单,因为最新的start.spring.io
已经维护了OpenAI
的依赖包,在IDEA使用Spring-Initializr
即可完成初始化。
完成Gradle的初始化之后,得到了一个标准的Spring项目,只需要做一个简单的配置:
spring.application.name=spring-ai-demo
# 生成结果多样性参数,值在0~2之间,值越大越随机越小越固定,但就算为0也会有随机性
spring.ai.openai.chat.temperature=0.7
spring.ai.chat.client.enabled=true
# 如果你需要代理的话
spring.ai.openai.base-url=https://api.xty.app
# 填写自己的key
spring.ai.openai.api-key=${OPENAI_API_KEY}
# 填写你需要使用的模型(也可以使用时代码指定)
spring.ai.openai.chat.options.model=gpt-3.5-turbo
接下来只需要编写一个Java的控制器,来接收HTTP请求,就可以完成对OpenAI的对话。
/**
* @author imyzt
* @date 2024/06/19
* @description AI 入口
*/
@RestController
@RequestMapping
public class AiController {
@Resource
private ChatClient chatClient;
@GetMapping("/ai/chat")
Map<String, Object> chat(@RequestParam String question) {
ChatClient.ChatClientRequest.CallPromptResponseSpec call = chatClient.prompt(new Prompt(question)).call();
return Map.of("question", question, "answer", call.chatResponse());
}
}
整体接入还是比较简单的,但是这里踩了一个坑,不知道是我引入的版本比较新还是什么缘故,它的ChatClient
Bean 竟然没有自动注册!所以我还手动注册了一个Bean,代码如下:
@Bean
public ChatClient chatClient(@Autowired OpenAiChatModel openAiChatModel) {
return ChatClient.builder(openAiChatModel).build();
}
代码
Spring-Ai-Alibaba
OpenAI由于API-KEY的费用蛮高,虽然完成了代码的接入,但是最终我还是没有购买它的API...所以又看了国内的厂商,目前主要是Alibaba完成了Spring-Ai的接入,整体接入其实也很简单,建议首先看一遍官网的例子,接下来可以跟着步骤走一下试试。
创建项目,引入依赖
首先完成一个普通SpringBootWeb项目创建,然后引入Alibaba-Ai的依赖
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-ai</artifactId>
<exclusions>
<exclusion>
<artifactId>spring-ai-core</artifactId>
<groupId>org.springframework.ai</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.springboot.ai</groupId>
<artifactId>spring-ai-core</artifactId>
<version>1.0.3</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2023.0.1.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
上面的内容,我把无关紧要的依赖去除了,只保留了核心部分,其中有一个关键点是exclusions
了spring-ai-core
,因为Alibaba引入的版本太老了。改为自己重新引入最新的版本,其他的和官方文档无差别。
配置
spring:
application:
name: spring-ai-alibaba-demo
cloud:
ai:
tongyi:
api-key: ${TONGYI_KEY}
images:
enabled: true
chat:
enabled: true
编写控制器
@Slf4j
@RestController
@RequestMapping
public class AiController {
@Resource
private ChatClient chatClient;
@Resource
private ImageClient imageClient;
@GetMapping("/ai/chat")
public String chat(@RequestParam String question) {
ChatResponse call = chatClient.call(new Prompt(question));
return call.getResult().getOutput().getContent();
}
@GetMapping("/ai/aigc")
public String aigc(@RequestParam String question) {
ImageResponse call = imageClient.call(new ImagePrompt(question));
return call.getResult().getOutput().getUrl();
}
}
完成上面的步骤,基本就完成了接入,在postman
上面输入地址,就可以进行测试了。因为通义不仅有chat,还可以文生图,所以我完成下演示:
文生文
(质量不予置评)
文生图
Spring-Ai-Alibaba还提供了一些示例,在他们的官方Github上,可以参考。
官方还提供了一个简单的HTML来进行页面展示,也可以自己跑一下看看,最终效果如下: