背景与目标
- 背景: 企业在落地生成式 AI 时,往往需要在不同平台(如 DeepSeek、阿里云百炼 DashScope、本地 Ollama)与多款模型之间灵活切换,以平衡效果、成本与可用性。
- 目标 : 基于 Spring AI 提供统一的服务,支持:
- 通过端点
/chat
按名称直切多个模型(固定预置的ChatClient
)。 - 通过端点
/chat2
指定平台与模型并自定义temperature
,支持流式返回。
- 通过端点
本文基于模块 03-multi-model-switch
的完整实现展开说明,覆盖依赖、配置、核心代码、接口调用与常见问题。
模块与依赖
模块:03-multi-model-switch
关键依赖包括 Spring Boot Web、Spring AI DeepSeek、Alibaba DashScope、Ollama、测试、Lombok:
xml
<dependencies>
<!--deepseek-->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-deepseek</artifactId>
</dependency>
<!--百炼-->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
<!--ollama-->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-ollama</artifactId>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--单元测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
配置文件
src/main/resources/application.properties
中配置了 API Key、默认模型与本地 Ollama 地址等:
properties
spring.application.name=chat-client
# deepseek
spring.ai.deepseek.api-key=${DEEPSEEK_API_KEY}
spring.ai.deepseek.chat.options.model=deepseek-reasoner
# dashscope (阿里云百炼)
spring.ai.dashscope.api-key=${DASHSCOPE_API_KEY}
spring.ai.dashscope.chat.options.model=qwen-plus
# ollama (本地)
spring.ai.ollama.base-url=http://127.0.0.1:11434
spring.ai.ollama.chat.model=qwen3:0.6b
环境变量说明:
DEEPSEEK_API_KEY
: DeepSeek 平台密钥DASHSCOPE_API_KEY
: 阿里云百炼密钥- 如果使用本地 Ollama,请确保
http://127.0.0.1:11434
可访问,且事先拉取好模型(如qwen3:0.6b
)。
核心设计与代码解读
1) 统一模型装配与客户端暴露 AiConfig
提供多个 ChatClient
Bean:deepseekR1
(推理/Reasoner)、deepseekV3
(聊天/Chat)、ollama
(本地模型)。通过不同 ChatModel
与默认 options
,形成"即插即用"的客户端:
java
@Configuration
public class AiConfig {
@Bean
public ChatClient deepseekR1(DeepSeekChatProperties chatProperties) {
DeepSeekApi deepSeekApi = DeepSeekApi.builder()
.apiKey(System.getenv("DEEPSEEK_API_KEY"))
.build();
DeepSeekChatModel deepSeekChatModel = DeepSeekChatModel.builder()
.deepSeekApi(deepSeekApi)
.defaultOptions(DeepSeekChatOptions.builder()
.model(DeepSeekApi.ChatModel.DEEPSEEK_REASONER)
.build())
.build();
return ChatClient.builder(deepSeekChatModel).build();
}
@Bean
public ChatClient deepseekV3() {
DeepSeekApi deepSeekApi = DeepSeekApi.builder()
.apiKey(System.getenv("DEEPSEEK_API_KEY"))
.build();
DeepSeekChatModel deepSeekChatModel = DeepSeekChatModel.builder()
.deepSeekApi(deepSeekApi)
.defaultOptions(DeepSeekChatOptions.builder()
.model(DeepSeekApi.ChatModel.DEEPSEEK_CHAT)
.build())
.build();
return ChatClient.builder(deepSeekChatModel).build();
}
@Bean
public ChatClient ollama(@Autowired OllamaApi ollamaApi,
@Autowired OllamaChatProperties options) {
OllamaChatModel ollamaChatModel = OllamaChatModel.builder()
.ollamaApi(ollamaApi)
.defaultOptions(OllamaOptions.builder()
.model(options.getModel())
.build())
.build();
return ChatClient.builder(ollamaChatModel).build();
}
}
要点:
- DeepSeek 通过
DeepSeekApi
+DeepSeekChatModel
以不同默认model
暴露为两个ChatClient
。 - Ollama 使用
OllamaApi
和配置中的model
作为默认本地模型。
2) 简单模型切换端点 /chat
(按 Bean 名直查)
通过自动注入 Map<String, ChatClient>
,用 model
参数直接索引对应 ChatClient
:
java
@Slf4j
@RestController
public class MultiModelsController {
@Autowired
private Map<String, ChatClient> chatClientMap;
@PostConstruct
public void init(){
chatClientMap.forEach((key, value) ->
log.info("model: {} : {}", key, value.getClass().getName()));
}
@GetMapping("/chat")
String generation(@RequestParam String message,
@RequestParam String model) {
ChatClient chatClient = chatClientMap.get(model);
String content = chatClient.prompt().user(message).call().content();
log.info("model: {} : {}", model, content);
return content;
}
}
可用 model
值示例(取决于 Spring 注入命名):deepseekR1
、deepseekV3
、ollama
。
调用示例:
bash
curl 'http://localhost:8080/chat?model=deepseekV3&message=用50字解释大语言模型'
3) 平台+模型+温度+流式端点 /chat2
支持 platform
、model
、temperature
的动态指定,并以流式文本输出:
java
@RestController
public class MultiPlatformAndModelController {
HashMap<String, ChatModel> platforms = new HashMap<>();
public MultiPlatformAndModelController(DashScopeChatModel dashScopeChatModel,
DeepSeekChatModel deepSeekChatModel,
OllamaChatModel ollamaChatModel) {
platforms.put("dashscope", dashScopeChatModel);
platforms.put("ollama", ollamaChatModel);
platforms.put("deepseek", deepSeekChatModel);
}
@RequestMapping(value = "/chat2", produces = "text/stream;charset=UTF-8")
public Flux<String> chat(String message, MultiPlatformAndModelOptions options) {
String platform = options.getPlatform();
ChatModel chatModel = platforms.get(platform);
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultOptions(ChatOptions.builder()
.temperature(options.getTemperature())
.model(options.getModel())
.build())
.build();
return chatClient.prompt().user(message).stream().content();
}
}
options
参数对象:
java
@Data
public class MultiPlatformAndModelOptions {
private String platform;
private String model;
private Double temperature;
}
调用示例(浏览器/curl
均可):
- DeepSeek(Chat):
/chat2?platform=deepseek&model=deepseek-chat&temperature=0.7&message=你是谁
- Ollama(本地 qwen3):
/chat2?platform=ollama&model=qwen3:0.6b&temperature=0.7&message=你是谁
- DashScope(通义千问):
/chat2?platform=dashscope&model=qwen-plus&temperature=0.7&message=你是谁
bash
curl -N 'http://localhost:8080/chat2?platform=ollama&model=qwen3:0.6b&temperature=0.3&message=用三点概括面向对象编程核心'
4) 启动类
java
@SpringBootApplication
public class MultiModelApplication {
public static void main(String[] args) {
SpringApplication.run(MultiModelApplication.class, args);
}
}
运行与验证
- 设置环境变量(macOS/zsh 举例):
bash
export DEEPSEEK_API_KEY='你的deepseek_key'
export DASHSCOPE_API_KEY='你的dashscope_key'
- 确保本地 Ollama(可选)正常运行,并已拉取模型:
bash
ollama run qwen3:0.6b
- 启动应用:
bash
cd /Volumes/artisan/code/2025/spring-ai-artisan/03-multi-model-switch
./mvnw spring-boot:run
- 验证直切端点:
bash
curl 'http://localhost:8080/chat?model=deepseekR1&message=解释下RAG是什么'
- 验证平台+模型+流式端点:
bash
curl -N 'http://localhost:8080/chat2?platform=dashscope&model=qwen-plus&temperature=0.2&message=用两句话说明微服务的优缺点'
设计要点与最佳实践
- 解耦平台与模型 :通过
platforms
映射与ChatOptions.model
,将平台选择与具体模型名解耦,便于扩展。 - 流式输出:对长文本响应更友好,前端可边收边显,降低等待感。
- 多 Bean 注入 Map :按名称索引
ChatClient
实例,快速实现"按名切换"。 - 安全与配置:生产环境使用更安全的密钥管理(KMS、Vault)。模型名应与平台兼容,不匹配会报错或无效。
- 容错 :对
platform
、model
判空与合法性校验;上游平台不通时返回可读错误并降级(如回退到本地 Ollama)。
常见问题(FAQ)
- 为什么
/chat
的model
用 Bean 名而非实际模型名?/chat
是"按 Bean 名直切"的简单方式,适合预置几个典型配置;实际模型名通过 Bean 内默认options
指定。 /chat2
的model
必须与平台支持的模型名一致吗? 是。比如platform=ollama
时model
应是本地已就绪的 Ollama 模型名。- 如何新增平台? 新增对应
ChatModel
的依赖与 Bean,将其放入platforms
映射,并在配置中补充默认参数即可。 - 如何控制输出长度、系统提示词等? 在
ChatOptions
中继续填充如maxTokens
、system
等参数;或在prompt()
中添加 system/assistant 指令。
至此,基于 Spring AI 的多平台多模型动态切换方案已经打通:可在 DeepSeek、DashScope 与本地 Ollama 之间灵活切换,支持按名直切与平台+模型+温度的流式对话,便于在实际项目中快速对比与落地。