关于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地址,感兴趣的也可以直接下载(后续也会继续完善)。

Logo

更多推荐