1.版本引入
在第一章的第五小节已经介绍了Spring AI所需要的java版本及spring boot的版本,接下来我们spring AI所有的依赖皆使用1.0.0-M7版本(1.0.0-SNAPSHOT还有些小问题)。
- 引入spring官方repository
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
<repository>
<name>Central Portal Snapshots</name>
<id>central-portal-snapshots</id>
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
- 引入spring AI的dependencyManagement
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0-M7</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
- 引入springboot
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-parent</artifactId>
<version>3.4.0</version>
</parent>
-
引入大模型客户端
本次测试我们都使用minimax大模型作测试
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-minimax</artifactId>
</dependency>
2.创建Chat client及使用
使用大模型API
需要创建此大模型的API KEY
,每个模型都要去其官网申请,minmax申请 (登陆后在“我的”那块申请)
2.1.手动配置
手动配置只使用spring-ai-minimax
包,而不需要使用springboot相关的容器等,其他所有的的大模型客户端都是一样的,其springboot集成的spring-ai-starter-model-minimax
客户端都包含spring-ai-minimax
包。
-
spring-ai-starter-model-minimax
依赖关系
-
千帆
spring-ai-starter-model-qianfan
依赖关系
- 调用minmax大模型 API ->>
package com.service;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.minimax.MiniMaxChatModel;
import org.springframework.ai.minimax.MiniMaxChatOptions;
import org.springframework.ai.minimax.api.MiniMaxApi;
public class TestChatClient {
public void createChatClient() {
// 创建客户端
var miniMaxApi = new MiniMaxApi("你的API KEY");
// 配置客户端
var chatModel = new MiniMaxChatModel(miniMaxApi, MiniMaxChatOptions.builder()
.model(MiniMaxApi.ChatModel.ABAB_6_5_S_Chat.getValue())
.temperature(0.4)
.maxTokens(200)
.build());
// 配置提示词
var prompt = new Prompt("你是谁");
var response = chatModel.call(prompt);
System.out.println(response.getResult().getOutput().getText());
}
public static void main(String[] args) {
var testChatClient = new TestChatClient();
testChatClient.createChatClient();
}
输出:
2.2. Spring Boot 自动配置
ChatClient
提供了流畅的 API 用于与 AI 模型进行通信。它支持同步和流式编程模型。ChatClient
使用对象创建。ChatClient.Builder
中可以获取任何Spring Boot ChatModel`自动配置的实例,也可以通过编程方式创建一个。
- application.properties配置
spring.ai.minimax.api-key=你的API KEY
spring.ai.minimax.chat.options.model=abab6.5s-chat
spring.ai.minimax.chat.options.temperature=0.4
- service及其实现类
package com.service;
public interface TestSpringAIService {
String generate(String message);
}
serviceImpl实现类:
package com.service.impl;
import com.service.TestSpringAIService;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.stereotype.Service;
@Service
public class TestSpringAIServiceImpl implements TestSpringAIService {
private final ChatClient chatClient;
// 通过ChatClient.Builder自动注入
public TestSpringAIServiceImpl(ChatClient.Builder chatClientBuilder) {
this.chatClient = chatClientBuilder.build();
}
@Override
public String generate(String message) {
return chatClient.prompt().user(message).call().content();
}
}
[!NOTE]
AI 模型主要有两种类型的提示词:系统提示词(
SystemPrompt
)和用户提示词(UserPrompt
),前者优先级高于后者。这些提示词通常包含占位符,这些占位符会在运行时根据用户输入进行替换,以定制 AI 模型对用户输入的响应。
可以指定提示词选项,例如要使用的 AI 模型的名称以及控制生成输出的随机性或创造性的温度设置。
- controller层
package com.controller;
import com.service.TestSpringAIService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestSpringAIController {
@Autowired
private TestSpringAIService service;
@GetMapping("/ai/generate")
public String generate(@RequestParam(value = "message", defaultValue = "你是谁") String message) {
return service.generate(message);
}
}
- 运行结果
3.ChatClient进阶详解
3.1.ChatClient提示词构建
ChatClient
的Fluent API
提供了三种不同的方式来创建提示词,通过重载prompt
方法启动Fluent API
流程:
三种函数都返回ChatClientRequestSpec
类型,后续都可以使用Fluent API
的方式继续构建系统提示词
和用户提示词
。
3.2.ChatClient请求响应
ChatClient API
提供了几种使用fluent API
格式化AI模型响应的方法。
3.2.1.同步响应
AI模型的响应是
ChatResponse
类型。它是spring-ai-model
包中的类,也是上面我们2.1章节中调用大模型响应的类型。所有spring基础的大模型包返回的都是这个类型,spring boot集成的大模型包再此基础上做了一层包装组合。其ChatClientRequestSpec.call()
方法返回的是ChatClient.CallResponseSpec
接口类型,该接口中存在获取ChatResponse
的接口、content
和映射为Entity<T.class>
实体类的方法。
public interface CallResponseSpec {
@Nullable
<T> T entity(ParameterizedTypeReference<T> type);
@Nullable
<T> T entity(StructuredOutputConverter<T> structuredOutputConverter);
@Nullable
<T> T entity(Class<T> type);
@Nullable
ChatResponse chatResponse(); // 获取ChatResponse定义的接口
@Nullable
String content();
<T> ResponseEntity<ChatResponse, T> responseEntity(Class<T> type);
<T> ResponseEntity<ChatResponse, T> responseEntity(ParameterizedTypeReference<T> type);
<T> ResponseEntity<ChatResponse, T> responseEntity(StructuredOutputConverter<T> structuredOutputConverter);
}
3.2.1.1.返回ChatResponse
ChatResponse chatResponse = chatClient.prompt()
.user("你是谁")
.call()
.chatResponse();
public class ChatResponse implements ModelResponse<Generation> {
// 包含有关响应生成方式的元数据
private final ChatResponseMetadata chatResponseMetadata;
// 多个响应
private final List<Generation> generations;
......
}
[!NOTE]
元数据包含用于创建响应的令牌数量(每个令牌大约占一个单词的 3/4)。此信息非常重要,因为托管的 AI 模型会根据每个请求使用的令牌数量收费。
3.2.1.2.返回Entity<T.class>
此返回方式可以将返回内容String映射成一个实体类Entity,他会自动识别返回内容与实体类的关系并做映射。
如下面的示例:
message为“生成一个中国女演员及其电影作品”
,controller层我们省略。
- 直接返回String
public String generate(String message) {
return chatClient.prompt().user(message).call().content();
}
返回值:
中国女演员:赵丽颖 电影作品: 1. 《乘风破浪》(2017年):在这部电影中,赵丽颖饰演小花,一个温柔贤惠的妻子和母亲,与邓超饰演的角色有一段感人的爱情故事。 2. 《西游记之女儿国》(2018年):赵丽颖在这部魔幻喜剧电影中扮演女儿国国王,与冯绍峰饰演的唐僧有一段跨越种族和信仰的爱情。 3. 《密战》(2017年):在这部谍战片中,赵丽颖饰演地下工作者兰芳,与郭富城饰演的角色共同为抗战胜利而奋斗。 4. 《新永不消逝的电波》(2019年):赵丽颖在这部战争片中扮演共产党员何兰芬,与佟大为饰演的角色一起在敌后进行秘密电报工作。 5. 《刺杀小说家》(2021年):在这部奇幻动作电影中,赵丽颖饰演反派角色屠灵,与雷佳音、杨幂等演员共同演绎一场关于现实与小说世界的冒险故事
- 使用Entity映射
实体类:
package com.model;
import java.util.List;
public class ActorFilmsModel {
private String actor;
private List<String> movies;
public ActorFilmsModel() {
}
public ActorFilmsModel(String actor, List<String> movies) {
this.actor = actor;
this.movies = movies;
}
// 此处省略set and get
}
service调用并映射:
public ActorFilmsModel generateActorFilms(String message) {
var res= chatClient.prompt()
.user(message)
.call();
return res.entity(ActorFilmsModel.class);
}
返回值:
{
"actor": "赵薇",
"movies": [
"致我们终将逝去的青春",
"画皮",
"亲爱的",
"天下无贼"
]
}
根据两次返回值的判断,我们充分感觉到返回Entity类映射会对返回值做处理,提取相关的内容映射到Entity<T.class>
实体类中。
还有一种entity
带有签名的重载方法entity(ParameterizedTypeReference<T> type)
,可让指定诸如通用列表之类的类型:
List<ActorFilms> actorFilms = chatClient.prompt()
.user("生成两位中国女演员及其电影作品")
.call()
.entity(new ParameterizedTypeReference<List<ActorFilmsModel>>() {});
返回结果:
[
{
"actor":"赵薇",
"movies":["致我们终将逝去的青春","画皮"]},
{
"actor":"周迅",
"movies":["如果・爱","李米的猜想"]
}
]
3.2.2.流式响应
stream()
方法可让获得异步响应。
Flux<String> output = chatClient.prompt()
.user("你是谁")
.stream()
.content();
stream()
方法返回StreamResponseSpec
接口类型,其中有两个接口:
interface StreamResponseSpec {
Flux<ChatResponse> chatResponse(); //返回由AI模型生成的字符串的Flux
Flux<String> content(); // 返回ChatResponse对象的Flux,该对象包含有关响应的附加元数据
}
ChatResponse
还可以使用该方法进行流式传输Flux<ChatResponse> chatResponse()
。
spring ai也提供了一种更便捷的方法,使用响应式stream()
方法返回 Java 实体的同时使用结构化输出转换器(Structured Output Converter
)显式转换聚合响应。
例如:
public String generateStream() {
var converter = new BeanOutputConverter<>(new ParameterizedTypeReference<List<ActorFilmsModel>>() {
});
Flux<String> flux = this.chatClient.prompt()
.user(u -> u.text("""
生成一位中国女演员及其电影作品
{format}
""")
.param("format", converter.getFormat()))
.stream()
.content();
return flux.collectList().block().stream().collect(Collectors.joining());
}
返回值:
{
"actor": "赵薇",
"movies": [
"致我们终将逝去的青春",
"画皮",
"亲爱的",
"天下无贼"
]
}