关于spring AI调用大模型的几种方案实践
本文介绍了使用Spring AI框架调用多个大语言模型的实践方案。通过自动注入方式,文中演示了如何配置阿里灵积、DeepSeek和OpenAI兼容模型,包括依赖引入、YAML配置和Controller调用。关键点包括:1)不同平台的SDK依赖配置;2)多模型参数设置;3)构造注入时需注意Bean命名问题(阿里模型需使用"dashscopeChatModel")。还分享了排查Be
关于spring AI调用大模型的几种方案实践
背景介绍:
spring AI 是java的ai框架,旨在简化应用程序的开发过程,在不增加不必要的复杂性的前提下整合 AI(人工智能)功能。我这里提供我平时调用的大模型的方法,供初学者参考。
具体方案
1、使用自动注入的方式调用
我们可以通过引入对应依赖,配置application.yml文件实现多个大模型配置
(1)导入对应依赖
<!-- 阿里的灵积 -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
<version>1.0.0.2</version>
</dependency>
<!-- openai -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
<version>1.0.1</version>
</dependency>
<!-- deepseek -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-deepseek</artifactId>
<version>1.0.1</version>
</dependency>
<!-- lombok方便日志输出 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.38</version
</dependency>
具体你要调哪个平台的你也可以直接去看spring AI、springAI alibaba 的官方文档,都有详细说明导入对应的SDK,有很多平台的模型都是兼容openai的,你只需要填写对应的base-url即可。这个版本我是自己看官方文档填的。
(2)配置application.yml文件
spring:
application:
name: xxx
ai:
dashscope:
base-url: https://dashscope.aliyuncs.com/compatible-mode/v1
api-key: your-key
chat:
options:
model: qwen-plus
deepseek:
base-url: https://api.deepseek.com/v1
api-key: your-key
chat:
options:
model: deepseek-chat
openai:
base-url: https://api.siliconflow.cn
api-key: your-key
chat:
options:
model: deepseek-ai/DeepSeek-V3.1
server:
port: 8080
上面分别配置了阿里灵积、deepseek、硅基流动的模型,除了deepseek,其他平台都是有免费额度的。
(3)controller调用
/**
* @author szq
**/
@RestController
@Slf4j
@RequestMapping("/modelTest")
public class modelTestController {
private final ChatClient dashCopeChatClient;
private final ChatClient deepSeekChatClient;
private final ChatClient openAiChatClient;
//建议采用构造的方法注入
public modelTestController(@Qualifier("dashscopeChatModel") ChatModel dashCopeChatModel,
@Qualifier("deepSeekChatModel") ChatModel deepSeekChatModel,
@Qualifier("openAiChatModel") ChatModel openAiChatModel
) {
this.dashCopeChatClient = ChatClient.builder(dashCopeChatModel).build();
this.deepSeekChatClient = ChatClient.builder(deepSeekChatModel).build();
this.openAiChatClient = ChatClient.builder(openAiChatModel).build();
}
@GetMapping("/test-dashScopeClient")
public String testDashScopeClient(@RequestParam(value = "userQuestion",defaultValue = "先告诉我你是谁,简短评价两眼一睁就是“瓦”") String userQuestion){
log.info("用户问题:{}",userQuestion);
String result = dashCopeChatClient.prompt().user(userQuestion).call().content();
log.info("模型结果:{}",result);
return result;
}
@GetMapping("/test-deepSeekClient")
public String testDeepSeekChatClient(@RequestParam(value = "userQuestion",defaultValue = "先告诉我你是谁,简短评价两眼一睁就是“瓦”") String userQuestion){
log.info("用户问题:{}",userQuestion);
String result = deepSeekChatClient.prompt().user(userQuestion).call().content();
log.info("模型结果:{}",result);
return result;
}
@GetMapping("/test-openAiClient")
public String testOpenAiChatClient(@RequestParam(value = "userQuestion",defaultValue = "先告诉我你是谁,简短评价两眼一睁就是“瓦”") String userQuestion){
log.info("用户问题:{}",userQuestion);
String result = openAiChatClient.prompt().user(userQuestion).call().content();
log.info("模型结果:{}",result);
return result;
}
}
结果演示:
都能正常调用,但话说,两眼一睁就是“瓦”,不应该是打瓦吗! 哈哈哈哈
tips:
这里说一下自己踩过的坑
通过导入对应的依赖,我们可以看到ChatModel有了对应的实现
由于是一个接口多个实现,我采用@Qualifier(“名称”)的方式注入,但"dashScopeChatModel"注入不进来,其他两个都可以,整得我一脸懵。
后面我决定看一下对应的bean到底叫什么名字,写了一个配置
/**
* 打印所有 ChatModel Bean 的名称
*/
@Configuration
public class BeanNamePrinter {
@Bean
public CommandLineRunner printBeans(ApplicationContext applicationContext) {
return args -> {
System.out.println("\n========== 所有的 ChatModel Bean ==========");
String[] beanNames = applicationContext.getBeanNamesForType(ChatModel.class);
for (String beanName : beanNames) {
System.out.println("找到 Bean: " + beanName + " - 类型: " + applicationContext.getBean(beanName).getClass().getSimpleName());
}
System.out.println("总共找到 " + beanNames.length + " 个 ChatModel Bean");
System.out.println("==========================================\n");
};
}
}
运行结果:
我也是服了,灵积这个没有采用驼峰命名全是小写(裂开)。
2、自己注册bean的方式
这个方式更加灵活,尤其是你在一个平台需要调用多个模型时,最好关闭自动配置(spring.ai.chat.client.enabled=false
来禁用 ChatClient.Builder
bean 的自动配置)。
具体实现:
(1)写好对应的配置,我这里把ApiConfig和ClientConfig分开写的
@Configuration
public class MyApiConfiguration {
@Value("${spring.ai.dashscope.api-key}")
private String myDashScopeApiKey;
@Value("${spring.ai.dashscope.base-url}")
private String myDashScopeBaseUrl;
@Value("${spring.ai.deepseek.api-key}")
private String myDeepSeekApiKey;
@Value("${spring.ai.deepseek.base-url}")
private String myDeepSeekBaseUrl;
@Value("${spring.ai.openai.api-key}")
private String myOpenAiApiKey;
@Value("${spring.ai.openai.base-url}")
private String myOpenAiBaseUrl;
/**
*灵积
*/
@Bean("myDashScopeApi")
public DashScopeApi dashScopeApi() {
return DashScopeApi.builder()
.apiKey(myDashScopeApiKey)
.baseUrl(myDashScopeBaseUrl)
.build();
}
/**
*deepseek
*/
@Bean("myDeepSeekApi")
public DeepSeekApi deepSeekApi() {
return DeepSeekApi.builder()
.apiKey(myDeepSeekApiKey)
.baseUrl(myDeepSeekBaseUrl)
.build();
}
/**
*硅基流动
*/
@Bean("myOpenAiApi")
public OpenAiApi openAiApi() {
return OpenAiApi.builder()
.apiKey(myOpenAiApiKey)
.baseUrl(myOpenAiBaseUrl)
.build();
}
}
@Configuration
public class MyClientConfig {
@Value("${spring.ai.dashscope.chat.options.model}")
private String dashScopeModel;
@Value("${spring.ai.deepseek.chat.options.model}")
private String deepSeekModel;
@Value("${spring.ai.openai.chat.options.model}")
private String openAiModel;
/**
*灵积
*/
@Bean("myDashScopeChatModel")
public DashScopeChatModel dashScopeChatModel(@Qualifier("myDashScopeApi") DashScopeApi dashScopeApi) {
return DashScopeChatModel.builder()
.dashScopeApi(dashScopeApi)
.defaultOptions(DashScopeChatOptions.builder().withModel(dashScopeModel).build())
.build();
}
/**
*deepseek
*/
@Bean("myDeepSeekChatModel")
public DeepSeekChatModel deepseekChatModel(@Qualifier("myDeepSeekApi") DeepSeekApi deepSeekApi) {
return DeepSeekChatModel.builder()
.deepSeekApi(deepSeekApi)
.defaultOptions(DeepSeekChatOptions.builder().model(deepSeekModel).build())
.build();
}
/**
*硅基流动
*/
@Bean("myOpenAiChatModel")
public OpenAiChatModel openAiChatModel(@Qualifier("myOpenAiApi") OpenAiApi openAiApi) {
return OpenAiChatModel.builder()
.openAiApi(openAiApi)
.defaultOptions(OpenAiChatOptions.builder().model(openAiModel).build())
.build();
}
}
(2)controller里面使用
//建议采用构造的方法注入
public modelTestController(@Qualifier("myDashScopeChatModel") ChatModel dashCopeChatModel,
@Qualifier("myDeepSeekChatModel") ChatModel deepSeekChatModel,
@Qualifier("myOpenAiChatModel") ChatModel openAiChatModel
) {
this.dashCopeChatClient = ChatClient.builder(dashCopeChatModel).build();
this.deepSeekChatClient = ChatClient.builder(deepSeekChatModel).build();
this.openAiChatClient = ChatClient.builder(openAiChatModel).build();
}
tips:
1、我在使用中尝试过不禁用 ChatClient.Builder
bean 的自动配置,发现只要你定义了同类名的bean,就会略过自动配置。下面明显可以看到只有自己注册的三个bean。
2、自己注册bean时像deepseek、dashscope我们引入的是官方依赖,它可以自动识别配置里面的base-url、model,所以可以通过下面的方式注册。
@Bean("myDashScopeApi")
public DashScopeApi dashScopeApi() {
return DashScopeApi.builder()
.apiKey(myDashScopeApiKey)
.build();
}
@Bean("myDeepSeekApi")
public DeepSeekApi deepSeekApi() {
return DeepSeekApi.builder()
.apiKey(myDeepSeekApiKey)
.build();
}
@Bean("myDashScopeChatModel")
public DashScopeChatModel dashScopeChatModel(@Qualifier("myDashScopeApi") DashScopeApi dashScopeApi) {
return DashScopeChatModel.builder()
.dashScopeApi(dashScopeApi)
.build();
}
@Bean("myDeepSeekChatModel")
public DeepSeekChatModel deepseekChatModel(@Qualifier("myDeepSeekApi") DeepSeekApi deepSeekApi) {
return DeepSeekChatModel.builder()
.deepSeekApi(deepSeekApi)
.build();
}
注意: 硅基流动等使用非官方提供SDK,他无法自动读取配置里面的base-url、model 那么他会调用它默认地址。例如openai会调用:https://api.openai.com,模型名称也会不对,调用模型就会失败。所以你必须指定这些信息。
我之前就遇到了一直显示模型名称不存在。
400 - {"code":20012,"message":"Model does not exist. Please check it carefully.","data":null}
总结
1、我这里列举了两种方式调用大模型,基本上是满足日常使用了,当初我刚接触AI时特别想找一个这样的文章+代码,可惜没看到。现在自己会了就分享出来,后续也会多写一些demo,感觉上传到网上,比自己写笔记好很多,毕竟知识共享嘛,希望可以帮助到需要的人。
2、这是这个demo的github地址,感兴趣的也可以直接下载(后续也会继续完善)。
更多推荐
所有评论(0)