搭建SpringBoot项目、添加SpringAI依赖、配置Open AI参数,请大家直接看Spring AI入门这篇。
因为众所周知的原因,我们不能直接访问国外网站。如果有需要Open AI 代理地址,请关注公众号,点击菜单apikey免费获取!
开始调用之前,我们先创建ChatController类,然后注入ChatClient,Spring AI框架已将ChatModel注入到ChatClient中,所以这里我们不需要对模型进行任何配置。
@RestController
publicclassChatController{
privatefinalChatClient chatClient;
publicChatController(ChatClient.Builder chatClientBuilder){
this.chatClient = chatClientBuilder.build();
}
}由于本节我们将响应entity对象,这里在ChatController中创建一个对象,该对象仅有两个属性:String actor演员名称List<String> movies出演的电影列表
// record是Java 14引入的一个新特性,用于创建不可变的数据载体类。
// 它简化了创建简单数据类的过程,自动提供了equals、hashCode、toString等方法的实现。
recordActorFilms(String actor, List<String> movies){}这里我们创建一个get方法,接收一个userInput参数。
@RestController
publicclassChatController{
...
@GetMapping("/entity")
ActorFilmsentity(String userInput){
returnthis.chatClient.prompt()
.user(userInput)
.call()
.entity(ActorFilms.class);
}
}这里SpringAI进行了更高等级的封装,无法从代码直观感受到结构化输出的原理。下面使用更底层的原子API来实现同样的响应。
@RestController
publicclassChatController{
...
@GetMapping("/originEntity")
ActorFilmsoriginEntity(String userInput){
// 创建bean输出转换器
BeanOutputConverter<ActorFilms> beanOutputConverter =
newBeanOutputConverter<>(ActorFilms.class);
// 获取bean对象格式指令
Stringformat=beanOutputConverter.getFormat();
// 将指令、用户输入传入chatClient,然后调用模型,获取模型输出
Stringoutput=this.chatClient.prompt(format)
.user(userInput)
.call()
.content();
// 将模型输出转换成bean对象
ActorFilmsactorsFilms=beanOutputConverter.convert(output);
returnactorsFilms;
}
}这里我们在浏览器发送get请求http://localhost:8080/entity?userInput=列出成龙出演的电影或http://localhost:8080/originEntity?userInput=列出成龙出演的电影Spring AI还提供了重载的entity方法,签名是`entity(ParameterizedTypeReference type)``,允许指定诸如泛型列表等类型。
@RestController
publicclassChatController{
...
@GetMapping("/entityList")
List<ActorFilms>entityList(String userInput){
List<ActorFilms> actorFilms = chatClient.prompt()
.user(userInput)
.call()
.entity(newParameterizedTypeReference<List<ActorFilms>>() {});
returnactorFilms;
}
}这里我们在浏览器发送一个get请求http://localhost:8080/entityList?userInput=列出周星驰、周润发、成龙出演的电影这里就不再展示原子API的写法了,就是将new BeanOutputConverter<>(ActorFilms.class);改为new BeanOutputConverter<>(new ParameterizedTypeReference<List<ActorFilms>>() {});不再赘述
既然提供了 List 的响应,那么Map也必然不可少。
@RestController
publicclassChatController{
...
// 响应Map<String, Object>对象
@GetMapping("/mapEntity")
Map<String, Object>mapEntity(){
Map<String, Object> result =this.chatClient.prompt()
.user(u -> u.text("Provide me a List of {subject}")
.param("subject","an array of numbers from 1 to 9 under they key name 'numbers'"))
.call()
.entity(newParameterizedTypeReference<Map<String, Object>>() {
});
returnresult;
}
}浏览器发送get请求:http://localhost:8080/mapEntity如果使用原子API,则需要将转换器改为new MapOutputConverter();,表明我们要求输出的是一个Map类型。
上面的接口都是同步响应,这是由于结构化输出的原理决定的,全部数据响应完,数据结构才完整,才能进行最后一步文本转结构化。
目前SpringAI流式响应,只能使用更底层的原子 API,使用方式如下:
@RestController
publicclassChatController{
...
@GetMapping("/entityListStream")
List<ActorFilms>entityListStream(String userInput){
varconverter=newBeanOutputConverter<>(newParameterizedTypeReference<List<ActorFilms>>() {});
Flux<String> flux =this.chatClient.prompt(converter.getFormat())
.user(userInput)
.stream()
.content();
Stringcontent=flux.collectList().block().stream().collect(Collectors.joining());
List<ActorFilms> actorFilms = converter.convert(content);
returnactorFilms;
}
}这里我们在浏览器发送一个get请求http://localhost:8080/entityListStream?userInput=列出周星驰、周润发、成龙出演的电影结构化输出是大模型开发不能避免的环节,Spring AI的封装帮助程序员节省大量时间,致使大模型为下游程序、函数提供可靠输入,当然理解其原理我们才能更灵活掌握结构化输出用法。
| 欢迎光临 链载Ai (https://www.lianzai.com/) | Powered by Discuz! X3.5 |