跳转至

Spring AI

为什么要学

Spring AI 将 AI 能力带入 Java Spring 生态系统,让数百万 Java 开发者能用熟悉的方式构建 AI 应用:

  • Spring 原生:遵循 Spring 设计哲学,依赖注入、自动配置
  • 模型抽象:统一 API 屏蔽不同 AI 提供商差异
  • RAG 支持:内置向量存储、文档处理、检索能力
  • 工具调用:声明式方法即可成为 AI 工具
  • 可观测性:与 Spring Boot Actuator/Micrometer 集成
  • 企业就绪:继承 Spring Boot 的生产级特性

如果你是 Java/Spring 开发者,Spring AI 是最自然的 AI 集成方式。

核心概念

白话解释

Spring AI 就像给 Spring Boot 装了一个"AI 模块": - ChatClient:和 AI 对话的客户端(类比 RestTemplate/WebClient) - Embedding:把文本转成向量(用于搜索) - VectorStore:存储和查询向量的数据库 - Advisor:AOP风格的拦截器,在AI调用前后处理逻辑

核心概念对照表

概念说明Spring类比
ChatModelAI对话模型接口JdbcTemplate
ChatClient高级AI客户端(链式调用)WebClient
Prompt发给AI的请求HttpRequest
Message对话中的一条消息请求/响应体
Advisor拦截AI调用的中间件Spring AOP
Tool/FunctionAI可调用的Java方法@RestController
VectorStore向量数据库抽象JpaRepository
DocumentReader文档读取器文件解析器
EmbeddingModel文本向量化模型序列化器

安装配置

Maven 依赖

<!-- Spring AI BOM -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>1.0.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

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

<!-- 向量存储(PgVector) -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId>
</dependency>

Gradle

implementation 'org.springframework.ai:spring-ai-openai-spring-boot-starter'
implementation 'org.springframework.ai:spring-ai-pgvector-store-spring-boot-starter'

application.yml

spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY}
      chat:
        options:
          model: gpt-4o-mini
          temperature: 0.7
    vectorstore:
      pgvector:
        dimensions: 1536

快速上手

基础对话

@RestController
@RequestMapping("/api/ai")
public class AiController {

    private final ChatClient chatClient;

    public AiController(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder.build();
    }

    @GetMapping("/chat")
    public String chat(@RequestParam String message) {
        return chatClient.prompt()
                .user(message)
                .call()
                .content();
    }

    @GetMapping("/chat/stream")
    public Flux<String> chatStream(@RequestParam String message) {
        return chatClient.prompt()
                .user(message)
                .stream()
                .content();
    }
}

带系统提示

@GetMapping("/assistant")
public String assistant(@RequestParam String question) {
    return chatClient.prompt()
            .system("你是一位Java专家,用中文回答,提供代码示例")
            .user(question)
            .call()
            .content();
}

结构化输出

// 定义返回结构
public record MovieRecommendation(
    String title,
    int year,
    String genre,
    String reason,
    double rating
) {}

@GetMapping("/recommend")
public MovieRecommendation recommend(@RequestParam String genre) {
    return chatClient.prompt()
            .user("推荐一部" + genre + "电影")
            .call()
            .entity(MovieRecommendation.class);
}

// 列表输出
@GetMapping("/recommend-list")
public List<MovieRecommendation> recommendList(@RequestParam String genre) {
    return chatClient.prompt()
            .user("推荐3部" + genre + "电影")
            .call()
            .entity(new ParameterizedTypeReference<List<MovieRecommendation>>() {});
}

进阶用法

1. 工具调用(Function Calling)

@Service
public class WeatherService {

    @Tool(description = "获取指定城市的当前天气")
    public String getWeather(@ToolParam(description = "城市名") String city) {
        // 实际调用天气API
        return city + ": 25°C, 晴天";
    }

    @Tool(description = "获取天气预报")
    public String getForecast(
            @ToolParam(description = "城市名") String city,
            @ToolParam(description = "预报天数") int days) {
        return city + "未来" + days + "天: 晴转多云";
    }
}

@RestController
public class WeatherController {

    private final ChatClient chatClient;
    private final WeatherService weatherService;

    @GetMapping("/weather-chat")
    public String weatherChat(@RequestParam String question) {
        return chatClient.prompt()
                .user(question)
                .tools(weatherService)  // 注册工具
                .call()
                .content();
    }
}
// 用户: "北京今天天气怎么样?" → AI自动调用getWeather("北京")

2. RAG 实现

@Configuration
public class RagConfig {

    @Bean
    public VectorStore vectorStore(EmbeddingModel embeddingModel, JdbcTemplate jdbcTemplate) {
        return new PgVectorStore(jdbcTemplate, embeddingModel);
    }
}

@Service
public class DocumentService {

    private final VectorStore vectorStore;

    // 文档导入
    public void ingestDocuments(List<Resource> resources) {
        // 读取文档
        var reader = new TikaDocumentReader(resources);
        var documents = reader.get();

        // 拆分
        var splitter = new TokenTextSplitter();
        var chunks = splitter.apply(documents);

        // 存入向量库
        vectorStore.add(chunks);
    }
}

@RestController
public class RagController {

    private final ChatClient chatClient;
    private final VectorStore vectorStore;

    @GetMapping("/rag")
    public String ragQuery(@RequestParam String question) {
        return chatClient.prompt()
                .user(question)
                .advisors(new QuestionAnswerAdvisor(vectorStore))  // RAG Advisor
                .call()
                .content();
    }
}

3. Advisor(中间件模式)

// 日志Advisor
public class LoggingAdvisor implements CallAroundAdvisor {

    @Override
    public AdvisedResponse aroundCall(AdvisedRequest request, CallAroundAdvisorChain chain) {
        log.info("AI请求: {}", request.userText());

        AdvisedResponse response = chain.nextAroundCall(request);

        log.info("AI回复: {}", response.response().getResult().getOutput().getContent());
        return response;
    }
}

// 记忆Advisor(对话历史)
@Bean
public ChatClient chatClient(ChatClient.Builder builder) {
    return builder
            .defaultAdvisors(
                new MessageChatMemoryAdvisor(new InMemoryChatMemory()),
                new LoggingAdvisor()
            )
            .build();
}

4. 多模型支持

// 配置多个AI服务
@Bean
@Qualifier("openai")
public ChatModel openaiModel() {
    return new OpenAiChatModel(new OpenAiApi(openaiKey));
}

@Bean
@Qualifier("ollama")
public ChatModel ollamaModel() {
    return new OllamaChatModel(new OllamaApi("http://localhost:11434"));
}

// 按需选择
@GetMapping("/chat")
public String chat(@RequestParam String model, @RequestParam String msg) {
    ChatModel chatModel = "local".equals(model) ? ollamaModel : openaiModel;
    return ChatClient.create(chatModel).prompt().user(msg).call().content();
}

5. 图片理解(多模态)

@GetMapping("/analyze-image")
public String analyzeImage(@RequestParam String imageUrl) {
    return chatClient.prompt()
            .user(u -> u.text("描述这张图片的内容")
                       .media(MimeTypeUtils.IMAGE_PNG, new URL(imageUrl)))
            .call()
            .content();
}

6. Embedding 和语义搜索

@Service
public class SemanticSearchService {

    private final EmbeddingModel embeddingModel;
    private final VectorStore vectorStore;

    public List<Document> search(String query, int topK) {
        return vectorStore.similaritySearch(
            SearchRequest.query(query)
                .withTopK(topK)
                .withSimilarityThreshold(0.7)
        );
    }
}

7. 对话记忆

@Bean
public ChatClient memoryChatClient(ChatClient.Builder builder) {
    return builder
            .defaultAdvisors(
                new MessageChatMemoryAdvisor(
                    new InMemoryChatMemory(), 
                    "default",
                    10  // 保留最近10条消息
                )
            )
            .build();
}

@GetMapping("/conversation")
public String conversation(
        @RequestParam String message,
        @RequestParam String sessionId) {
    return chatClient.prompt()
            .user(message)
            .advisors(a -> a.param("chat_memory_conversation_id", sessionId))
            .call()
            .content();
}

常见问题

Q1: 支持哪些 AI 模型?

提供商模型Starter
OpenAIGPT-4o/4o-minispring-ai-openai
AzureAzure OpenAIspring-ai-azure-openai
AnthropicClaudespring-ai-anthropic
Ollama本地模型spring-ai-ollama
GoogleGeminispring-ai-vertex-ai-gemini
AWSBedrockspring-ai-bedrock

Q2: VectorStore 选择?

存储Starter适用
PgVectorspring-ai-pgvector-store已有PostgreSQL
ChromaDBspring-ai-chroma-store快速原型
Milvusspring-ai-milvus-store大规模
Redisspring-ai-redis-store已有Redis
Pineconespring-ai-pinecone-store云托管

Q3: 如何处理 API 限流?

Spring AI 内置了重试机制,可通过配置调整:

spring:
  ai:
    retry:
      max-attempts: 3
      backoff:
        initial-interval: 1000
        multiplier: 2

Q4: 如何测试?

@SpringBootTest
class AiServiceTest {

    @MockBean
    private ChatModel chatModel;

    @Test
    void testChat() {
        when(chatModel.call(any(Prompt.class)))
            .thenReturn(new ChatResponse(List.of(
                new Generation("mocked response")
            )));

        // 测试你的服务
    }
}

参考资源