Spring AI 凭什么能"一套代码换任意大模型"?

用 Java 做 AI 应用的人越来越多,但多数人还在为不同厂商的 API 疲于适配。Spring AI 用一层抽象把 OpenAI、Ollama、Azure 全部统一,业务代码零改动切换模型。这篇拆解它的核心设计。

Spring AI 凭什么能"一套代码换任意大模型"?

Java 开发者做 AI 应用有个绕不开的尴尬:今天用 OpenAI,明天想换本地 Ollama 省成本,后天客户要求上 Azure。每换一家,API 调用、鉴权、返回结构全不一样,业务代码得重写一遍。

Spring AI 就是来填这个坑的。它的核心思路一句话:复刻 Spring 对数据库的玩法——用一层抽象,把”具体哪家模型”和”你的业务代码”彻底解耦。正如 JPA 让你不用关心背后是 MySQL 还是 PostgreSQL,Spring AI 让你不用关心背后是 GPT 还是 llama3。

抽象层长什么样

应用层   ChatClient 统一 API
   │
Spring AI 抽象层   ChatModel / EmbeddingModel / VectorStore
   │
具体实现(Starter)   OpenAI / Ollama / Azure / ...
   │
底层模型服务   OpenAI API / Ollama 本地 / Azure OpenAI

关键在中间那层 ChatModel 抽象。你写业务代码时只认 ChatClient,具体是谁来实现,由引入的 starter 和配置决定。

切模型只需改配置

这是最实用的部分。先引入两个 starter(只引你需要的):

<!-- OpenAI -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
<!-- 本地 Ollama -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
</dependency>

切到 OpenAI:

spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY}
      model: gpt-4
      temperature: 0.7

切到本地 Ollama:

spring:
  ai:
    ollama:
      base-url: http://localhost:11434
      model: llama3
      temperature: 0.7

业务代码一行都不用动:

@Service
public class UnifiedChatService {

    private final ChatClient chatClient;

    // 注入统一的 Builder,具体实现由配置决定
    public UnifiedChatService(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder.build();
    }

    // 完全不关心底层是 OpenAI 还是 Ollama
    public String chat(String message) {
        return chatClient.prompt()
                .user(message)
                .call()
                .content();
    }
}

从云端大模型迁到本地模型,改个 yml 就行。对企业来说,这意味着”厂商锁定”的顾虑基本消失。

ChatClient 的设计哲学:AI 世界的 JdbcClient

官方把 ChatClient 类比为 JDBC 层的 JdbcClient——高级抽象、方便使用;而 ChatModel 是底层核心、负责协议适配。这个类比很准。

它用建造者模式提供链式调用,读起来很顺:

String response = chatClient.prompt()
    .system("You are a helpful assistant")
    .user("What is Spring AI?")
    .options(ChatOptions.builder().temperature(0.8).build())
    .call()
    .content();

几个我觉得真正提升开发体验的点:

结构化输出:直接把 LLM 返回的 JSON 映射成 Java 对象,不用自己解析。

public record ActorFilms(String actor, List<String> movies) {}

ActorFilms result = chatClient.prompt()
    .user("Generate the filmography for Tom Hanks")
    .call()
    .entity(ActorFilms.class);  // 自动 JSON 解析 + 类型转换

顾问链(Advisor Chain):这是我最想推荐的。它用责任链模式在请求/响应前后做拦截增强,日志、记忆、RAG、工具调用全是 Advisor,可以像搭积木一样组合:

ChatClient client = ChatClient.builder(chatModel)
    .defaultAdvisors(loggerAdvisor, memoryAdvisor, ragAdvisor, toolAdvisor)
    .build();

比如加一个 QuestionAnswerAdvisor 就把 RAG 接进来了,业务代码完全无感。从 1.1.0 开始还有递归顾问,能做”输出不达标就自动重试”的迭代工作流。

为什么这件事对 Java 开发者重要

Python 生态里换模型也不算难,但 Java 后端的特点是企业级、长生命周期、怕绑定。Spring AI 把”可移植性”做成默认值,配合 Spring Boot 的 starter 自动配置,样板代码极少。

我目前用它在做两件事:一是把内部知识库问答从 OpenAI 切到本地模型,成本直接归零;二是用 Advisor 链把日志、记忆、RAG 串成一条流水线,比自己拼 HTTP 调用清爽太多。

如果你的技术栈是 Java,又想碰 AI,Spring AI 是目前最省心的入口。欢迎在关于页找我交流。

奇妙感 本文采用署名-非商业性使用-相同方式共享协议,转载请注明出处与作者。

目录