Spring AI深度解析:将大语言模型封装为Spring Bean的设计与实现

发布时间:2026/8/11 9:28:11
Spring AI深度解析:将大语言模型封装为Spring Bean的设计与实现 1. 项目概述当Spring遇上LLM如果你是一个常年混迹在Java和Spring生态里的开发者最近肯定没少被“AI”、“大模型”、“Agent”这些词刷屏。看着Python那边LangChain、LlamaIndex玩得风生水起心里难免痒痒我们Spring开发者难道就只能调调HTTP接口写写胶水代码吗Spring AI的出现就是为了回答这个问题。它不是一个简单的SDK封装而是一次深刻的范式转变——将大语言模型LLM这种外部复杂服务抽象成Spring IoC容器中一个普普通通的、可被依赖注入的Bean。这听起来似乎只是换了个说法但其背后的意义远超你的想象。想象一下你不再需要手动管理API密钥、构建复杂的请求体、处理各种网络异常和速率限制。在你的Service里注入一个ChatClientBean然后像调用本地方法一样chatClient.call(“Hello”)就能得到AI的回复。这种体验和注入一个JdbcTemplate去操作数据库或者注入一个RestTemplate调用外部REST服务在哲学上是一脉相承的。Spring AI所做的就是为AI能力提供了Spring范式的“一等公民”待遇。它解决的远不止是方便性问题更是工程化问题。当你把LLM当作Bean来管理你天然就获得了Spring赖以成名的所有能力依赖注入DI带来的解耦与可测试性、面向切面编程AOP带来的统一监控与治理、以及强大的配置外部化与管理能力。你可以为不同的业务场景定义不同的LLM Bean比如一个用GPT-4处理创意文案另一个用Claude处理逻辑分析通过Qualifier轻松切换你可以用Retryable注解为LLM调用自动添加重试逻辑你可以通过ConfigurationProperties将模型参数、API端点等配置统一管理区分开发、测试、生产环境。所以这篇文章不是一份简单的Spring AI使用手册。我们将深入它的腹腔拆解它如何将LLM这一“庞然大物”驯服并封装成一个个温顺的Spring Bean。我们会从设计理念聊到源码实现从基础配置讲到高级定制并分享在实际企业级应用中趟过的坑和积累的经验。无论你是想评估Spring AI的可行性还是已经上手但想更深入理解其机理这篇文章都将为你提供一幅清晰的“解剖图”。2. 核心设计理念抽象、统一与扩展Spring AI的核心设计哲学可以用三个词概括抽象Abstraction、统一Unification、扩展Extension。这套理念贯穿了整个框架的顶层设计理解它是理解所有具体技术实现的前提。2.1 抽象层定义AI交互的通用语言面对市面上数十种LLM提供商OpenAI、Anthropic、Azure OpenAI、Ollama等每家API的细节都不尽相同。Spring AI没有选择为每家都写一套独特的客户端而是首先定义了一套与提供商无关的通用抽象接口。最核心的接口是ChatClient和EmbeddingClient。ChatClient定义了对话的基本契约给我一个消息列表ListMessage我还你一个回复ChatResponse。Message本身也是一个抽象它包含了内容、角色MessageType如USER, ASSISTANT, SYSTEM等通用属性。无论底层是调用GPT还是Claude对于上层的业务代码来说它们都是在操作同样的ChatClient接口。这种抽象带来的最大好处是业务逻辑与基础设施的解耦。你的核心业务代码只依赖于ChatClient这个接口。今天你用OpenAI的GPT-4明天因为成本或性能考虑想切换到Azure OpenAI的模型或者在内网环境切换到本地部署的Ollama Llama 3模型。你只需要更换底层的Bean实现和配置业务代码一行都不用改。这完美契合了Spring“针对接口编程”和“依赖注入”的核心思想。// 你的业务Service只依赖抽象的ChatClient Service public class ContentGenerationService { private final ChatClient chatClient; public ContentGenerationService(ChatClient chatClient) { this.chatClient chatClient; } public String generateBlogPost(String topic) { // 业务逻辑只使用通用接口 Prompt prompt new Prompt(new UserMessage(请写一篇关于 topic 的博客开头。)); ChatResponse response chatClient.call(prompt); return response.getResult().getOutput().getContent(); } }2.2 统一模型消息、提示与响应的标准化在抽象接口之下Spring AI建立了一套统一的领域模型Domain Model。这是实现“统一”理念的关键。消息模型 (MessageMessageType): 将AI对话中所有可能的角色系统指令、用户输入、助手回复、工具调用结果等标准化为一套枚举和数据结构。这确保了不同提供商的角色映射如OpenAI的system/user/assistant Claude的human/assistant在Spring AI内部得到统一处理。提示词模型 (Prompt): 一个Prompt包含一个ListMessage和一些可选的元数据如ChatOptions。它是对单次交互请求的完整封装。响应模型 (ChatResponse): 封装了LLM的返回结果包括主要的回复内容Generation、使用情况Usage如token数以及可能的其他生成选项如多个候选回复n1的情况。选项模型 (ChatOptions): 这是一个精妙的设计。它定义了调用LLM时可调节的所有参数如temperature创造性、maxTokens最大生成长度、topP核采样等。不同提供商支持的参数集合不同Spring AI通过ChatOptions接口定义了一个最大公约数各提供商的具体实现如OpenAiChatOptions可以扩展它添加自己特有的参数。这套统一模型是框架的“脊柱”它让上层应用可以用一致的方式处理所有LLM的输入输出而下层实现则负责与具体提供商的API进行适配和转换。2.3 扩展机制SPI与自动配置的魔法“扩展”理念体现在Spring AI极佳的开放性上。它通过Java的SPIService Provider Interface机制和Spring Boot的自动配置Auto-Configuration来无缝集成各种LLM提供商。每个LLM提供商如spring-ai-openai,spring-ai-azure-openai,spring-ai-ollama都会提供一个独立的starter模块。在这个模块的META-INF/spring.factories或更新的META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中会声明自己的自动配置类。当你在项目的pom.xml或build.gradle中引入了spring-ai-openai-spring-boot-starter依赖并配置了spring.ai.openai.api-key属性后Spring Boot的自动配置机制就会启动条件化装配:OpenAiAutoConfiguration类上通常有ConditionalOnClass检查OpenAI的客户端类是否存在和ConditionalOnProperty检查相关配置是否存在等注解。条件满足时该配置类生效。构建具体Client: 配置类内部会读取application.yml中的配置通过EnableConfigurationProperties绑定到OpenAiConnectionProperties等类使用这些配置来构建一个OpenAI官方Java客户端或类似库的实例。包装成Spring AI抽象: 然后将这个官方客户端实例“包装”成一个实现了Spring AIChatClient接口的适配器类例如OpenAiChatClient。注册为Bean: 最后将这个适配器类实例通过Bean方法注册到Spring IoC容器中。此时你的业务代码中Autowired的ChatClient实际上就是这个OpenAiChatClient的实例。这个过程对开发者完全透明。你只需要“声明”需要什么通过引入依赖和配置Spring AI和Spring Boot就会“自动”为你准备好可用的Bean。这种设计使得社区可以非常容易地为新的LLM提供商开发支持模块只需遵循相同的模式实现适配器并注册自动配置即可。注意理解这个扩展机制至关重要。当你在排查“为什么我配置了属性但Bean没生效”时首先要检查的就是依赖是否引入正确、自动配置类的条件是否满足比如类路径下是否有对应的客户端库配置属性前缀是否正确。3. 核心实现拆解Bean的诞生与生命周期理解了顶层设计我们深入到更具体的实现层。一个LLM Bean是如何被创建、初始化并最终融入Spring容器的生命周期的我们以最常用的OpenAiChatClient为例进行源码级的走查。3.1 配置属性的加载与绑定一切始于你的application.yml文件spring: ai: openai: api-key: ${OPENAI_API_KEY} chat: options: model: gpt-4-turbo-preview temperature: 0.7 max-tokens: 1000Spring Boot的ConfigurationProperties机制会将这些属性绑定到OpenAiConnectionProperties和OpenAiChatProperties这样的属性类上。这些属性类定义了所有可配置的字段及其默认值并做了良好的分组。ConfigurationProperties(prefix spring.ai.openai) public class OpenAiConnectionProperties { private String apiKey; private String baseUrl; // 可自定义用于兼容Azure OpenAI或代理 // ... getters and setters } ConfigurationProperties(prefix spring.ai.openai.chat) public class OpenAiChatProperties { private OpenAiChatOptions options new OpenAiChatOptions(); // ... getters and setters }3.2 自动配置类Bean的组装车间接下来OpenAiAutoConfiguration这个“组装车间”开始工作。我们来看一个简化版的逻辑Configuration(proxyBeanMethods false) ConditionalOnClass(OpenAiApi.class) // 条件1OpenAI API客户端类必须在类路径下 EnableConfigurationProperties({OpenAiConnectionProperties.class, OpenAiChatProperties.class}) public class OpenAiAutoConfiguration { Bean ConditionalOnMissingBean // 条件2只有当容器中没有OpenAiApi Bean时才创建 public OpenAiApi openAiApi(OpenAiConnectionProperties properties) { // 使用属性构建OpenAI官方客户端的实例 OpenAiApi api new OpenAiApi(properties.getBaseUrl(), properties.getApiKey()); // 可能还会配置一些全局的HTTP客户端参数如超时、代理等 return api; } Bean ConditionalOnMissingBean // 条件3只有当容器中没有ChatClient Bean时才创建 public ChatClient openAiChatClient(OpenAiApi openAiApi, OpenAiChatProperties chatProperties) { // 1. 创建适配器将OpenAiApi包装成Spring AI的ChatClient接口实现 OpenAiChatClient client new OpenAiChatClient(openAiApi); // 2. 将配置文件中chat.options下的默认参数设置给Client client.setDefaultOptions(chatProperties.getOptions()); return client; } }这里有几个关键点条件化装配ConditionalOnClass和ConditionalOnMissingBean确保了装配的灵活性和可覆盖性。如果你不想用自动配置的OpenAiApi比如你需要一个自定义了复杂拦截器的客户端你可以自己定义一个OpenAiApi的Bean自动配置就会跳过。依赖链openAiChatClientBean的创建依赖于openAiApiBean和chatProperties。Spring容器会按正确的顺序解析这些依赖。默认选项client.setDefaultOptions(...)这行代码非常重要。它把你在配置文件中为这个Chat Client定义的默认模型、温度等参数预先设置好。这样在业务代码中调用时如果没有特别指定选项就会使用这些默认值。3.3 适配器模式统一接口下的具体实现现在焦点到了OpenAiChatClient这个类。它是如何实现ChatClient接口的呢核心在于适配器模式。ChatClient接口的核心方法是ChatResponse call(Prompt prompt)。OpenAiChatClient的实现大致如下public class OpenAiChatClient implements ChatClient, ModelOptionsCapableOpenAiChatOptions { private final OpenAiApi openAiApi; private OpenAiChatOptions defaultOptions; Override public ChatResponse call(Prompt prompt) { // 1. 将Spring AI统一的Prompt模型转换为OpenAI API特定的请求对象 ChatCompletionRequest openAiRequest convertPromptToOpenAiRequest(prompt); // 2. 调用底层的OpenAI官方客户端 ChatCompletionResult openAiResponse openAiApi.chatCompletion(openAiRequest).block(); // 假设使用响应式这里阻塞获取 // 3. 将OpenAI API的响应对象转换回Spring AI统一的ChatResponse模型 ChatResponse springAiResponse convertOpenAiResponseToChatResponse(openAiResponse); return springAiResponse; } private ChatCompletionRequest convertPromptToOpenAiRequest(Prompt prompt) { ChatCompletionRequest request new ChatCompletionRequest(); // 合并逻辑将Prompt中的ChatOptions与Client的defaultOptions合并 OpenAiChatOptions mergedOptions mergeOptions(defaultOptions, prompt.getOptions()); request.setModel(mergedOptions.getModel()); request.setTemperature(mergedOptions.getTemperature()); // ... 设置其他参数 // 转换消息格式 request.setMessages(convertMessages(prompt.getMessages())); return request; } // ... 其他辅助方法 }转换Conversion和合并Merging是适配器层的两个核心职责转换在统一的Spring AI模型和厂商特定API模型之间进行双向转换。这包括字段名的映射、数据结构的调整、枚举值的转换等。合并处理请求级别的选项prompt.getOptions()与客户端默认选项defaultOptions的优先级。通常的规则是请求级选项会覆盖默认选项但未在请求中指定的选项仍使用默认值。这为灵活控制单次请求的行为提供了可能。3.4 Bean的生命周期与作用域至此一个完整的ChatClientBean已经存在于Spring容器中。它默认是单例Singleton作用域的这意味着整个应用共享同一个实例。这对于LLM客户端来说是合理的因为它通常是无状态的除了配置并且可以复用底层的HTTP连接池提高效率。这个Bean的生命周期由Spring容器管理实例化、属性填充、初始化如果实现了InitializingBean或定义了PostConstruct方法、然后投入使用最终在容器关闭时销毁。作为开发者你几乎不需要关心这些只需在需要的地方Autowired注入它即可。实操心得自定义你的LLM Bean自动配置很好但有时你需要更精细的控制。例如你想为同一个OpenAI服务创建两个不同的Bean一个用于处理中文默认模型gpt-4一个用于处理代码默认模型gpt-4-1106-preview。你可以轻松地通过Configuration类手动定义Configuration public class MultipleLlmConfig { Bean Qualifier(chineseChatClient) public ChatClient chineseChatClient(OpenAiApi openAiApi) { OpenAiChatClient client new OpenAiChatClient(openAiApi); client.setDefaultOptions(OpenAiChatOptions.builder() .model(gpt-4) .temperature(0.3) // 更确定性 .build()); return client; } Bean Qualifier(codeChatClient) public ChatClient codeChatClient(OpenAiApi openAiApi) { OpenAiChatClient client new OpenAiChatClient(openAiApi); client.setDefaultOptions(OpenAiChatOptions.builder() .model(gpt-4-1106-preview) .temperature(0.1) // 代码生成需要高确定性 .build()); return client; } }在业务代码中使用Qualifier注解来指定注入哪一个Bean。这种灵活性是Spring IoC容器强大能力的直接体现。4. 高级特性与定制化实践将LLM封装成Bean只是第一步。Spring生态的真正威力在于其围绕Bean构建的一整套企业级功能。Spring AI将这些能力无缝地引入了AI应用开发中。4.1 函数调用Function Calling的Spring式集成函数调用是让LLM与外部工具和系统交互的关键能力。Spring AI将其抽象为FunctionCallback接口和FunctionCallbackRegistry。它的集成方式非常“Spring”定义工具函数你可以将一个Spring Bean中的方法暴露给LLM。只需在该方法上添加Description注解来描述这个函数的功能Spring AI在启动时就会通过扫描将其注册。注册与调用你可以将多个FunctionCallback注册到一个FunctionCallbackRegistry中然后将这个registry设置给ChatClient。自动处理当LLM在对话中决定要调用某个函数时Spring AI的客户端会自动拦截这个请求从registry中找到对应的回调函数执行并将执行结果以特定的消息格式ToolMessage重新发送给LLM让LLM基于结果继续生成回复。Component public class WeatherService { Description(根据城市名称获取当前天气情况) // 关键注解 public String getWeather(Description(城市名称例如北京) String city) { // 模拟或真实调用天气API return city 的天气是晴25摄氏度。; } } Configuration public class FunctionCallConfig { Bean public FunctionCallback weatherFunctionCallback(WeatherService weatherService) { // 将Bean方法包装成FunctionCallback return new FunctionCallbackWrapper(getWeather, // 函数名 获取天气, // 函数描述供LLM理解 weatherService::getWeather, // 方法引用 new JsonSchemaConverter()); // 类型转换器 } Bean public ChatClient functionChatClient(OpenAiApi openAiApi, ListFunctionCallback toolCallbacks) { OpenAiChatClient client new OpenAiChatClient(openAiApi); // 创建注册表并注册所有回调 FunctionCallbackRegistry registry new FunctionCallbackRegistry(); toolCallbacks.forEach(registry::register); // 将注册表设置给客户端并启用函数调用 client.setFunctionCallbackRegistry(registry); client.setDefaultOptions(OpenAiChatOptions.builder() .functionCall(auto) // 启用自动函数调用 .build()); return client; } }这种设计的美妙之处在于你的工具函数本身就是一个普通的Spring Bean它可以方便地注入其他服务如数据库访问层、HTTP客户端等完全融入现有的Spring应用架构。4.2 提示词模板与上下文管理直接拼接字符串来构造提示词既容易出错又难以维护。Spring AI引入了PromptTemplate它类似于Spring MVC中的视图模板如Thymeleaf支持占位符和简单逻辑。// 在配置文件中定义模板也可在代码中 // spring.ai.prompt.templates.myTemplate请用{style}风格写一篇关于{topic}的短文。 Value(classpath:/prompts/blog-template.st) private Resource blogTemplateResource; public Prompt createBlogPrompt(String topic, String style) { // 方式1使用字符串模板 PromptTemplate template new PromptTemplate(请用{style}风格写一篇关于{topic}的短文。); MapString, Object model Map.of(style, style, topic, topic); Prompt prompt template.create(model); // 方式2使用外部模板文件更推荐便于管理 // PromptTemplate fileTemplate new PromptTemplate(blogTemplateResource); // Prompt prompt fileTemplate.create(model); return prompt; }对于多轮对话的上下文管理Spring AI提供了ChatMemory抽象。最简单的实现是InMemoryChatMemory它会自动将用户和AI的对话消息存储起来并在下一次请求时作为历史上下文发送给LLM。你可以将其配置为Bean并注入到你的对话服务中。Bean public ChatMemory chatMemory() { // 创建一个能保留最近10轮对话的内存 return new InMemoryChatMemory(new TokenCountChatMemoryStore(), 1000); // 第二个参数可以是token数上限 } Service public class ConversationService { private final ChatClient chatClient; private final ChatMemory chatMemory; public String chat(String sessionId, String userMessage) { // 1. 从内存中获取或创建该会话的历史 ListMessage history chatMemory.get(sessionId); // 2. 将历史消息和新的用户消息组合成新的Prompt ListMessage messages new ArrayList(history); messages.add(new UserMessage(userMessage)); Prompt prompt new Prompt(messages); // 3. 调用LLM ChatResponse response chatClient.call(prompt); Message assistantMessage response.getResult().getOutput(); // 4. 将本轮的用户消息和AI回复存入内存 chatMemory.add(sessionId, new UserMessage(userMessage)); chatMemory.add(sessionId, assistantMessage); return assistantMessage.getContent(); } }4.3 可观测性与AOP增强在生产环境中监控LLM调用的耗时、成功率、Token消耗是必不可少的。由于ChatClient是一个Spring Bean我们可以轻松地利用Spring AOP为其添加切面。Aspect Component Slf4j public class LlmMonitoringAspect { Around(execution(* org.springframework.ai.chat.client.ChatClient.call(..)) args(prompt)) public Object monitorLlmCall(ProceedingJoinPoint pjp, Prompt prompt) throws Throwable { long startTime System.currentTimeMillis(); String model extractModelFromPromptOrOptions(prompt); // 提取模型信息 try { Object result pjp.proceed(); // 执行实际的LLM调用 ChatResponse response (ChatResponse) result; long duration System.currentTimeMillis() - startTime; // 记录成功日志和指标 log.info(LLM调用成功 - 模型: {}, 耗时: {}ms, 消耗Token: {}, model, duration, response.getUsage()); // 可以推送指标到Micrometer/Prometheus Metrics.counter(llm.calls, model, model, status, success).increment(); Metrics.timer(llm.call.duration, model, model).record(duration, TimeUnit.MILLISECONDS); return result; } catch (Exception e) { long duration System.currentTimeMillis() - startTime; log.error(LLM调用失败 - 模型: {}, 耗时: {}ms, model, duration, e); Metrics.counter(llm.calls, model, model, status, failure).increment(); throw e; } } }通过AOP我们以非侵入的方式为所有LLM调用统一加上了监控、日志和指标收集。你还可以在这个切面中加入重试逻辑虽然Spring Retry是更好的选择、熔断机制与Resilience4j集成或审计日志。这正是Spring Bean抽象带来的巨大优势——基础设施与业务逻辑的完美分离。5. 企业级应用配置、测试与最佳实践将Spring AI用于个人项目和生产级系统考量点完全不同。下面分享一些在企业级应用中积累的实战经验。5.1 多环境与安全配置绝不将API密钥硬编码在代码中。使用环境变量或配置中心如Spring Cloud Config, Apollo是基本要求。# application-dev.yml (开发环境可使用测试Key) spring: ai: openai: api-key: ${OPENAI_API_KEY_DEV:sk-test123} base-url: https://api.openai.com/v1 # application-prod.yml (生产环境) spring: ai: openai: api-key: ${OPENAI_API_KEY_PROD} # 从安全的环境变量或密钥管理服务获取 base-url: https://api.openai.com/v1 chat: options: model: gpt-4-turbo temperature: 0.2 # 生产环境通常需要更低的随机性对于多租户或多模型提供商的场景你可能需要动态选择不同的ChatClientBean。这可以通过实现一个RoutingChatClient来完成它根据当前请求的上下文如租户ID、渠道标识来路由到背后不同的真实Client。Component public class TenantAwareChatClient implements ChatClient { private final MapString, ChatClient clientMap; // key: tenantId private final TenantContext tenantContext; // 假设有租户上下文 Override public ChatResponse call(Prompt prompt) { String tenantId tenantContext.getCurrentTenantId(); ChatClient client clientMap.get(tenantId); if (client null) { throw new IllegalArgumentException(No LLM client configured for tenant: tenantId); } return client.call(prompt); } }5.2 单元测试与集成测试测试是保证AI应用可靠性的关键。Spring AI的良好抽象让测试变得可行。1. 单元测试Mock LLM响应 由于业务代码只依赖ChatClient接口你可以轻松地使用Mockito等框架模拟它的行为。ExtendWith(MockitoExtension.class) class ContentGenerationServiceTest { Mock private ChatClient mockChatClient; InjectMocks private ContentGenerationService service; Test void generateBlogPost_shouldReturnContent() { // 1. 准备模拟的AI回复 ChatResponse mockResponse new ChatResponse(List.of( new Generation(new AssistantMessage(这是一篇模拟的博客开头。)) )); when(mockChatClient.call(any(Prompt.class))).thenReturn(mockResponse); // 2. 执行测试 String result service.generateBlogPost(Spring AI); // 3. 验证 assertThat(result).isEqualTo(这是一篇模拟的博客开头。); verify(mockChatClient).call(argThat(prompt - prompt.getMessages().get(0).getContent().contains(Spring AI) )); } }2. 集成测试使用测试容器或模拟服务器 对于需要真实调用LLM API的集成测试可以使用Testcontainers启动一个本地的Ollama容器或者使用WireMock模拟OpenAI的API端点。这样可以避免调用昂贵的云端API同时测试完整的调用链。Testcontainers SpringBootTest class OpenAiChatClientIntegrationTest { Container static OllamaContainer ollama new OllamaContainer(ollama/ollama:latest) .withModel(llama3); // 拉取并运行Llama 3模型 Autowired private ChatClient chatClient; // 这里会注入一个连接到本地Ollama的Client Test void testChatWithLocalModel() { Prompt prompt new Prompt(Hello); ChatResponse response chatClient.call(prompt); assertThat(response.getResult().getOutput().getContent()).isNotEmpty(); } }5.3 性能调优与常见陷阱连接池与超时设置LLM API调用是网络IO密集型操作。务必配置合理的HTTP客户端连接池和超时参数防止线程被长时间阻塞。如果你用的是Spring Boot默认的Reactor Netty或Apache HttpClient可以在application.yml中配置spring: ai: openai: client: connect-timeout: 5s read-timeout: 30s # LLM生成可能较慢需要较长的读取超时 max-in-memory-size: 10MB异步与非阻塞考虑使用ChatClient的响应式版本ReactiveChatClient如果底层客户端支持或者将同步调用包装在Async方法中避免阻塞Web容器的线程提高应用的整体吞吐量。Token成本控制这是企业应用的核心关切点。除了在ChatOptions中设置maxTokens外还应该在AOP切面或专门的ClientInterceptor中对所有请求和响应进行Token计数可以使用tiktoken等库进行近似估算并记录到监控系统。可以设置阈值当单次调用或每日累计消耗超过一定Token数时触发告警。Prompt注入防护当用户输入被直接拼接到Prompt模板中时存在Prompt注入风险可能导致AI偏离预设指令。务必对用户输入进行严格的清洗和转义或者使用更安全的模板引擎如Spring AI内置的或Mustache来避免直接拼接。错误处理与降级LLM服务可能不稳定。除了使用Spring Retry进行重试还应该设计降级策略。例如当主要的高质量模型如GPT-4调用失败时可以自动降级到备用模型如GPT-3.5-Turbo或者返回一个预定义的、友好的默认回复。Service public class RobustChatService { private final ChatClient primaryClient; // GPT-4 private final ChatClient fallbackClient; // GPT-3.5 private final RetryTemplate retryTemplate; public String robustChat(String userInput) { return retryTemplate.execute(context - { try { return primaryClient.call(createPrompt(userInput)).getResult().getOutput().getContent(); } catch (ResourceAccessException e) { // 网络或超时异常 if (context.getRetryCount() context.getRetryPolicy().getMaxAttempts() - 1) { // 重试耗尽执行降级 log.warn(Primary LLM failed after retries, falling back.); return fallbackClient.call(createPrompt(userInput)).getResult().getOutput().getContent(); } throw e; // 继续重试 } catch (Exception e) { // 其他非重试性错误直接降级或返回错误 log.error(Unexpected LLM error, using fallback., e); return 抱歉服务暂时不可用请稍后再试。; } }); } }将LLM抽象为Spring Bean不仅仅是技术上的便利更是一种架构思维的提升。它意味着AI能力不再是外挂的、特殊的“黑魔法”而是成为了你Spring应用体系中一个可管理、可观测、可测试、可集成的标准组件。从配置管理、依赖注入到AOP增强、测试支持Spring二十年来积累的最佳实践你现在都可以无缝地应用于AI功能的开发。这极大地降低了AI应用的生产化门槛让开发者能够更专注于业务逻辑的创新而非基础设施的挣扎。