如何(he)在(zai) Spring Boot 應用中(zhong)配置多個 Spring AI 的(de) LLM 客戶端
1. 概述
越來越多的(de)(de)現(xian)代應用開始集成大型(xing)語言模型(xing)(LLM),以(yi)構建更智能的(de)(de)功能。如何使(shi)用Spring AI快速整合LLM能力(li)到自己的(de)(de)Spring Boot應用,在之(zhi)前的(de)(de)博文中有過很(hen)多篇關于。雖(sui)然一(yi)個(ge) LLM 能勝任(ren)多種任(ren)務(wu),但只依賴單一(yi)模型(xing)并(bing)不總是最優。
不(bu)同模(mo)型各(ge)有側重:有的(de)擅長技(ji)術分(fen)析(xi),有的(de)更(geng)適合創意寫作。簡單任(ren)務更(geng)適合輕(qing)量、性價比(bi)高的(de)模(mo)型;復雜任(ren)務則交給更(geng)強(qiang)大(da)的(de)模(mo)型。
本(ben)文將演示如何借助 Spring AI,在 Spring Boot 應用(yong)中(zhong)集成多(duo)個 LLM。
我們既(ji)會配置來自不同供應(ying)商(shang)的(de)模(mo)型,也會配置同一供應(ying)商(shang)下(xia)的(de)多個模(mo)型。隨(sui)后基于這些配置,構建一個具(ju)備彈性的(de)聊天機器人(ren),在故障時可自動在模(mo)型間切(qie)換。
2. 配置不同供應商的 LLM
我們先在應用(yong)中配置來自不同供應商(shang)的兩(liang)個(ge) LLM。
在本文示例中,我們將使用 OpenAI 和 Anthropic 作為 AI 模型提供商。
2.1. 配置主 LLM
我們先將一個 OpenAI 模型配置為(wei)主(zhu) LLM。
首先,在項目的 pom.xml 文件中(zhong)添加所需依(yi)賴(lai):
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
<version>1.0.2</version>
</dependency>
該 是對 的(de)封裝,使我們能夠在應(ying)用(yong)中與 OpenAI 模(mo)型交互(hu)。
接著,在 application.yaml 中配(pei)置我們的 OpenAI API Key 和聊天(tian)模型:
spring:
ai:
open-ai:
api-key: ${OPENAI_API_KEY}
chat:
options:
model: ${PRIMARY_LLM}
temperature: 1
我們使用 ${} 屬性占位符從環境變量中加載屬性值。另外,我們將溫度設置為 1,因為較新的(de) OpenAI 模型。
在完成上述屬性配置后,Spring AI 會自動創建一個 OpenAiChatModel 類型的 bean。我們使用它來定義一個 ChatClient bean,作為與 LLM 交互的主要入口:
@Configuration
class ChatbotConfiguration {
@Bean
@Primary
ChatClient primaryChatClient(OpenAiChatModel chatModel) {
return ChatClient.create(chatModel);
}
}
在 ChatbotConfiguration 類中,我們使用 OpenAiChatModel bean 創建了主 LLM 的 ChatClient。
我們使用 @Primary 注解標記該 bean。當在組件中注入 ChatClient 且未使用 Qualifier 時,Spring Boot 會自動注入它。
2.2. 配置次級 LLM
現(xian)在(zai),我們將配置一個來(lai)自(zi) Anthropic 的模型作為(wei)次級(ji) LLM。
首先,在 pom.xml 中添加 :
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-anthropic</artifactId>
<version>1.0.2</version>
</dependency>
該依賴是(shi)對 的(de)封裝,提(ti)供了(le)與 Anthropic 模型建立(li)連接并交互所需(xu)的(de)類。
接著,為次(ci)級模(mo)型(xing)定義配置屬(shu)性:
spring:
ai:
anthropic:
api-key: ${ANTHROPIC_API_KEY}
chat:
options:
model: ${SECONDARY_LLM}
與主(zhu) LLM 的配置類似,我們從環(huan)境(jing)變量中加(jia)載 和模型 ID。
最后,為次級模型創建一個專用的 ChatClient bean:
@Bean
ChatClient secondaryChatClient(AnthropicChatModel chatModel) {
return ChatClient.create(chatModel);
}
這里,我們使用 Spring AI 自動配置的 AnthropicChatModel bean 創建了 secondaryChatClient。
3. 配置同一供應商的多個 LLM
很多時候,我們需要配置的多個 LLM 可能來自同一 AI 供應商。
Spring AI 并不原生支持這種場景,其自動配置每個供應商只會創建一個 ChatModel bean。因此,對于額外的模型,我們需要手動定義 ChatModel bean。
讓我們來看看具體過程,并在應用中配置第二個 Anthropic 模型:
spring:
ai:
anthropic:
chat:
options:
tertiary-model: ${TERTIARY_LLM}
在 application.yaml 的 Anthropic 配置下,我們添加了一個自定義屬性來保存第三個(tertiary)LLM 的模型名稱。
接(jie)著,為第三個 LLM 定義必要的 bean:
@Bean
ChatModel tertiaryChatModel(
AnthropicApi anthropicApi,
AnthropicChatModel anthropicChatModel,
@Value("${spring.ai.anthropic.chat.options.tertiary-model}") String tertiaryModelName
) {
AnthropicChatOptions chatOptions = anthropicChatModel.getDefaultOptions().copy();
chatOptions.setModel(tertiaryModelName);
return AnthropicChatModel.builder()
.anthropicApi(anthropicApi)
.defaultOptions(chatOptions)
.build();
}
@Bean
ChatClient tertiaryChatClient(@Qualifier("tertiaryChatModel") ChatModel tertiaryChatModel) {
return ChatClient.create(tertiaryChatModel);
}
首先,為創建自定義的 ChatModel bean,我們注入自動配置的 AnthropicApi bean、用于創建次級 LLM 的默認 AnthropicChatModel bean,并通過 @Value 注入第三個(ge)模型的名稱屬性(xing)。
我們復制現有 AnthropicChatModel 的默認選項,并僅覆蓋其中的模型名稱。
該設置假定兩個 Anthropic 模型共享同一個 API Key 及其他配置。如果需要不同的屬性,可以進一步自定義 AnthropicChatOptions。
最后,我們使用自定義的 tertiaryChatModel 在配置類中創建第三個 ChatClient bean。
4. 探索一個實用用例
在完成多模型配置后,讓我們實現一個實用用例。我們將構建一個具備彈性的聊天機器人,當主模型出現故障時可按順序自動回退到替代模型。
4.1. 構建具備彈性的聊天機器人
為實現回退邏輯,我們(men)將(jiang)使用(yong) Spring Retry。
創建一個新的 ChatbotService 類,并注入我們定義的三個 ChatClient。接著,定(ding)義一個入口方法使(shi)用主 LLM:
@Retryable(retryFor = Exception.class, maxAttempts = 3)
String chat(String prompt) {
logger.debug("Attempting to process prompt '{}' with primary LLM. Attempt #{}",
prompt, RetrySynchronizationManager.getContext().getRetryCount() + 1);
return primaryChatClient
.prompt(prompt)
.call()
.content();
}
這里,我們創建了一個使用 primaryChatClient 的 chat() 方法。該方法使用 @Retryable 注解,在遇(yu)到(dao)任(ren)意 Exception 時最(zui)多(duo)重(zhong)試(shi)三次。
接(jie)著,定義一(yi)個恢復方法:
@Recover
String chat(Exception exception, String prompt) {
logger.warn("Primary LLM failure. Error received: {}", exception.getMessage());
logger.debug("Attempting to process prompt '{}' with secondary LLM", prompt);
try {
return secondaryChatClient
.prompt(prompt)
.call()
.content();
} catch (Exception e) {
logger.warn("Secondary LLM failure: {}", e.getMessage());
logger.debug("Attempting to process prompt '{}' with tertiary LLM", prompt);
return tertiaryChatClient
.prompt(prompt)
.call()
.content();
}
}
使用 @Recover 注解標記的重載 chat() 方法將作為原始 chat() 方法失敗并耗盡重試后的回退處理。
我們首先嘗試通過 secondaryChatClient 獲取響應;如果仍失敗,則最后再嘗試使用 tertiaryChatClient。
這里使(shi)用了(le)簡(jian)單(dan)的 try-catch 實現,因為 Spring Retry 每個方(fang)法簽(qian)名只允(yun)許(xu)一個恢復(fu)方(fang)法。但在生產(chan)應用中,我(wo)們應考慮使(shi)用更完善的方(fang)案,例如 Resilience4j。
在完成服務層實現后,我們再對外暴露一個 REST API:
@PostMapping("/api/chatbot/chat")
ChatResponse chat(@RequestBody ChatRequest request) {
String response = chatbotService.chat(request.prompt);
return new ChatResponse(response);
}
record ChatRequest(String prompt) {}
record ChatResponse(String response) {}
這里定義了一個 POST 接口 /api/chatbot/chat,接收 prompt,將其傳遞到服務層,最后把 response 包裝在 ChatResponse record 中返回。
4.2. 測試我們的聊天機器人
最后,我們來測試聊天機器人,驗證回退機制是否正常工作。
通過環境變量啟動應用:為(wei)主、次級 LLM 設(she)置無效(xiao)模(mo)型名稱(cheng),同(tong)時為(wei)第三個 LLM 設(she)置一(yi)個有效(xiao)的模(mo)型名稱(cheng):
OPENAI_API_KEY=.... \
ANTHROPIC_API_KEY=.... \
PRIMARY_LLM=gpt-100 \
SECONDARY_LLM=claude-opus-200 \
TERTIARY_LLM=claude-3-haiku-20240307 \
mvn spring-boot:run
在上述命令中,gpt-100 和 claude-opus-200 是無效的模型名稱,會導致 API 錯誤;而 是 Anthropic 提供的有效模型。
接著,使用(yong) HTTPie CLI 調用(yong)接口,與(yu)聊天機器(qi)人(ren)交(jiao)互:
http POST :8080/api/chatbot/chat prompt="What is the capital of France?"
這里我們向聊天(tian)機器人發送(song)一個簡單(dan)的提(ti)示詞,看(kan)看(kan)返(fan)回(hui)結果:
{
"response": "The capital of France is Paris."
}
可以看到,盡管主、次級 LLM 的配置為無效模型,聊天機器人仍返回了正確響應,這驗證了系統成功回退到了第三個 LLM。
為了更直觀(guan)地看到回退(tui)邏輯的執行(xing)過程,我們再來看一下應用日志:
[2025-09-30 12:56:03] [DEBUG] [com.baeldung.multillm.ChatbotService] - Attempting to process prompt 'What is the capital of France?' with primary LLM. Attempt #1
[2025-09-30 12:56:05] [DEBUG] [com.baeldung.multillm.ChatbotService] - Attempting to process prompt 'What is the capital of France?' with primary LLM. Attempt #2
[2025-09-30 12:56:06] [DEBUG] [com.baeldung.multillm.ChatbotService] - Attempting to process prompt 'What is the capital of France?' with primary LLM. Attempt #3
[2025-09-30 12:56:07] [WARN] [com.baeldung.multillm.ChatbotService] - Primary LLM failure. Error received: HTTP 404 - {
"error": {
"message": "The model `gpt-100` does not exist or you do not have access to it.",
"type": "invalid_request_error",
"param": null,
"code": "model_not_found"
}
}
[2025-09-30 12:56:07] [DEBUG] [com.baeldung.multillm.ChatbotService] - Attempting to process prompt 'What is the capital of France?' with secondary LLM
[2025-09-30 12:56:07] [WARN] [com.baeldung.multillm.ChatbotService] - Secondary LLM failure: HTTP 404 - {"type":"error","error":{"type":"not_found_error","message":"model: claude-opus-200"},"request_id":"req_011CTeBrAY8rstsSPiJyv3sj"}
[2025-09-30 12:56:07] [DEBUG] [com.baeldung.multillm.ChatbotService] - Attempting to process prompt 'What is the capital of France?' with tertiary LLM
日(ri)志清晰地展示了請求的(de)執行流程。
可以看到,主 LLM 連續三次嘗試失敗;隨后服務嘗試使用次級 LLM,仍然失敗;最終調用第三個 LLM 處理提示詞并返回了我們看到的響應。
這表(biao)明回退機制(zhi)按設計正常工作,即使(shi)多個(ge) LLM 同(tong)時失敗,聊天機器(qi)人(ren)仍(reng)保(bao)持可用。
5. 小結
本(ben)文(wen)探討了如何(he)在(zai)單個 Spring AI 應(ying)用中集成多(duo)(duo)個 LLM。首先,我們(men)演示了 Spring AI 的(de)抽象層如何(he)簡化(hua)來自不同(tong)(tong)供(gong)應(ying)商(shang)(如 OpenAI 與(yu) Anthropic)的(de)模(mo)(mo)型(xing)(xing)配(pei)置(zhi)。隨后,我們(men)解(jie)決(jue)了更復(fu)雜的(de)場景:在(zai)同(tong)(tong)一(yi)(yi)供(gong)應(ying)商(shang)下配(pei)置(zhi)多(duo)(duo)個模(mo)(mo)型(xing)(xing),并(bing)在(zai) Spring AI 的(de)自動配(pei)置(zhi)不夠用時(shi)創(chuang)建自定(ding)義 bean。最后,我們(men)利用多(duo)(duo)模(mo)(mo)型(xing)(xing)配(pei)置(zhi)構建了一(yi)(yi)個具有高可(ke)用性(xing)(xing)的(de)彈性(xing)(xing)聊(liao)天機器(qi)人。借助 Spring Retry,我們(men)實(shi)現了級(ji)聯(lian)回退模(mo)(mo)式,在(zai)發生故障時(shi)可(ke)在(zai)不同(tong)(tong) LLM 間(jian)自動切換。
