Spring AI 与 Spring AI Alibaba怎么选?

0 阅读19分钟

引言

在大模型应用开发的浪潮中,Spring生态凭借其成熟的依赖注入、事务管理、AOP等特性,成为了Java开发者构建AI应用的首选框架。2024年Spring官方推出Spring AI项目,为Java开发者提供了统一的AI编程模型;同年,阿里云基于Spring AI推出了Spring AI Alibaba扩展,深度整合了阿里云的AI生态。

一、Spring AI 核心架构与能力解析

1.1 Spring AI 设计理念与核心目标

Spring AI的设计理念与Spring家族其他项目一脉相承,核心目标是提供一套与云无关、与模型无关的抽象层,让Java开发者能够以熟悉的Spring编程模型来构建AI应用。它解决了传统AI应用开发中存在的几个痛点:

  • 不同大模型厂商API不统一,切换模型需要大量代码修改
  • 缺乏统一的提示词工程、向量数据库、RAG等高级功能抽象
  • 与Spring生态其他组件(如Spring Boot、Spring Data、Spring Security)集成困难
  • 缺乏统一的错误处理、重试、限流等企业级特性

Spring AI遵循"约定优于配置"的原则,提供了自动配置、依赖注入等Spring开发者熟悉的特性,让开发者能够快速上手AI应用开发。

1.2 Spring AI 核心架构

Spring AI采用了分层架构设计,从下到上依次为:模型适配层、核心抽象层、高级功能层和集成层。

  • 模型适配层:负责与不同的AI模型提供商进行通信,将统一的API调用转换为各个厂商特定的请求格式,并将响应转换为统一的Java对象。
  • 核心抽象层:定义了Spring AI的核心接口和抽象类,如ChatClient、EmbeddingClient、ImageClient等,是整个框架的核心。
  • 高级功能层:基于核心抽象层构建了提示词模板、函数调用、RAG、Agent等高级功能。
  • 集成层:提供了与Spring生态其他组件以及第三方系统的集成能力。

1.3 Spring AI 核心组件详解

1.3.1 ChatClient

ChatClient是Spring AI中最核心的组件,用于与大语言模型进行对话交互。它提供了流式和非流式两种调用方式,支持系统提示词、用户消息、助手消息等多种消息类型。

package com.jam.demo.springai.service;

import com.jam.demo.springai.dto.ChatRequest;
import com.jam.demo.springai.dto.ChatResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

/**
 * 聊天服务实现类
 * @author ken
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SpringAiChatService {

    private final ChatClient chatClient;

    /**
     * 非流式聊天
     * @param request 聊天请求
     * @return 聊天响应
     */
    public ChatResponse chat(ChatRequest request) {
        org.springframework.ai.chat.model.ChatResponse response = chatClient.prompt()
                .user(request.getMessage())
                .call()
                .chatResponse();
        
        return ChatResponse.builder()
                .content(response.getResult().getOutput().getContent())
                .build();
    }

    /**
     * 流式聊天
     * @param request 聊天请求
     * @return 流式响应
     */
    public Flux<ChatResponse> streamChat(ChatRequest request) {
        return chatClient.prompt()
                .user(request.getMessage())
                .stream()
                .chatResponse()
                .map(response -> ChatResponse.builder()
                        .content(response.getResult().getOutput().getContent())
                        .build());
    }
}

1.3.2 PromptTemplate

PromptTemplate用于创建可复用的提示词模板,支持变量替换,是提示词工程的核心组件。

package com.jam.demo.springai.service;

import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.stereotype.Service;

import java.util.Map;

/**
 * 提示词服务
 * @author ken
 */
@Service
public class PromptService {

    /**
     * 创建翻译提示词
     * @param text 待翻译文本
     * @param targetLanguage 目标语言
     * @return 提示词对象
     */
    public Prompt createTranslationPrompt(String text, String targetLanguage) {
        String template = "请将以下文本翻译成{targetLanguage}:\n{text}";
        PromptTemplate promptTemplate = new PromptTemplate(template);
        return promptTemplate.create(Map.of("text", text, "targetLanguage", targetLanguage));
    }
}

1.3.3 EmbeddingClient

EmbeddingClient用于将文本转换为向量表示,是RAG应用的基础组件。

package com.jam.demo.springai.service;

import lombok.RequiredArgsConstructor;
import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.embedding.EmbeddingResponse;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * 向量嵌入服务
 * @author ken
 */
@Service
@RequiredArgsConstructor
public class EmbeddingService {

    private final EmbeddingModel embeddingModel;

    /**
     * 将文本转换为向量
     * @param text 输入文本
     * @return 向量数组
     */
    public float[] embedText(String text) {
        return embeddingModel.embed(text);
    }

    /**
     * 批量将文档转换为向量
     * @param documents 文档列表
     * @return 向量列表
     */
    public List<float[]> embedDocuments(List<Document> documents) {
        return embeddingModel.embed(documents.stream()
                .map(Document::getContent)
                .toList());
    }
}

1.3.4 VectorStore

VectorStore用于存储和检索向量数据,Spring AI提供了对多种主流向量数据库的支持,包括Chroma、Pinecone、Milvus、Redis等。

package com.jam.demo.springai.service;

import lombok.RequiredArgsConstructor;
import org.springframework.ai.document.Document;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * 向量存储服务
 * @author ken
 */
@Service
@RequiredArgsConstructor
public class VectorStoreService {

    private final VectorStore vectorStore;

    /**
     * 添加文档到向量数据库
     * @param documents 文档列表
     */
    public void addDocuments(List<Document> documents) {
        vectorStore.add(documents);
    }

    /**
     * 相似性搜索
     * @param query 查询文本
     * @param topK 返回结果数量
     * @return 相似文档列表
     */
    public List<Document> similaritySearch(String query, int topK) {
        return vectorStore.similaritySearch(SearchRequest.query(query)
                .withTopK(topK));
    }
}

1.4 Spring AI 支持的模型与服务

截至2026年4月,Spring AI官方支持以下模型提供商:

  • OpenAI:GPT-3.5-turbo、GPT-4、GPT-4o、Embedding系列
  • Anthropic:Claude 3系列
  • Google:Gemini系列
  • Amazon:Bedrock
  • Azure:OpenAI服务
  • Ollama:本地运行的开源模型
  • Mistral AI:Mistral系列
  • Cohere:Command系列

1.5 Spring AI 高级功能

1.5.1 函数调用

Spring AI提供了对函数调用的原生支持,允许大模型调用应用中定义的Java函数来获取实时数据或执行特定操作。

package com.jam.demo.springai.function;

import com.fasterxml.jackson.annotation.JsonClassDescription;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Description;

import java.util.function.Function;

/**
 * 天气查询函数配置
 * @author ken
 */
@Configuration
public class WeatherFunctionConfig {

    @Bean
    @Description("获取指定城市的当前天气")
    public Function<WeatherRequest, WeatherResponse> getCurrentWeather() {
        return request -> {
            // 这里模拟调用天气API
            return new WeatherResponse(request.getCity(), "晴"25"微风");
        };
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @JsonClassDescription("天气查询请求")
    public static class WeatherRequest {
        @JsonProperty(required = true)
        @JsonPropertyDescription("城市名称,如北京、上海")
        private String city;
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class WeatherResponse {
        private String city;
        private String weather;
        private int temperature;
        private String wind;
    }
}

1.5.2 RAG(检索增强生成)

Spring AI提供了完整的RAG实现,包括文档加载、文档分割、向量嵌入、向量存储和检索增强生成等环节。

package com.jam.demo.springai.service;

import lombok.RequiredArgsConstructor;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.document.Document;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.stream.Collectors;

/**
 * RAG服务
 * @author ken
 */
@Service
@RequiredArgsConstructor
public class RagService {

    private final ChatClient chatClient;
    private final VectorStore vectorStore;

    /**
     * 基于RAG的问答
     * @param query 用户问题
     * @return 回答
     */
    public String ragQuery(String query) {
        List<Document> similarDocuments = vectorStore.similaritySearch(query);
        String context = similarDocuments.stream()
                .map(Document::getContent)
                .collect(Collectors.joining("\n\n"));
        
        String prompt = """
                请根据以下上下文回答用户的问题。如果上下文中没有相关信息,请回答"我不知道"。
                
                上下文:
                {context}
                
                用户问题:{query}
                """;
        
        return chatClient.prompt()
                .system(prompt)
                .param("context", context)
                .param("query", query)
                .call()
                .content();
    }
}

二、Spring AI Alibaba 核心架构与能力解析

2.1 Spring AI Alibaba 设计理念与定位

Spring AI Alibaba是阿里云基于Spring AI官方项目开发的扩展,它的核心定位是为中国开发者提供更符合国内使用习惯、深度整合阿里云AI生态的AI应用开发框架

Spring AI Alibaba完全兼容Spring AI的核心API,这意味着开发者可以无缝地从Spring AI迁移到Spring AI Alibaba,或者在同一个项目中混合使用两者的功能。同时,它在Spring AI的基础上,增加了对阿里云AI服务的支持,并提供了一些针对国内场景的优化和增强功能。

2.2 Spring AI Alibaba 核心架构

Spring AI Alibaba在Spring AI核心架构的基础上,增加了阿里云模型适配层和阿里云服务集成层。

2.3 Spring AI Alibaba 核心组件详解

2.3.1 通义千问ChatClient

Spring AI Alibaba提供了对阿里云通义千问系列模型的原生支持,包括qwen-turbo、qwen-plus、qwen-max等。

package com.jam.demo.springaialibaba.service;

import com.alibaba.cloud.ai.tongyi.TongYiChatModel;
import com.jam.demo.springaialibaba.dto.ChatRequest;
import com.jam.demo.springaialibaba.dto.ChatResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

/**
 * 通义千问聊天服务
 * @author ken
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class TongYiChatService {

    private final TongYiChatModel tongYiChatModel;

    /**
     * 非流式聊天
     * @param request 聊天请求
     * @return 聊天响应
     */
    public ChatResponse chat(ChatRequest request) {
        String content = tongYiChatModel.call(request.getMessage());
        return ChatResponse.builder()
                .content(content)
                .build();
    }

    /**
     * 流式聊天
     * @param request 聊天请求
     * @return 流式响应
     */
    public Flux<ChatResponse> streamChat(ChatRequest request) {
        return tongYiChatModel.stream(request.getMessage())
                .map(content -> ChatResponse.builder()
                        .content(content)
                        .build());
    }
}

2.3.2 通义千问EmbeddingClient

Spring AI Alibaba提供了对通义千问向量模型的支持,包括text-embedding-v1、text-embedding-v2等。

package com.jam.demo.springaialibaba.service;

import com.alibaba.cloud.ai.tongyi.embedding.TongYiEmbeddingModel;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.document.Document;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * 通义千问向量嵌入服务
 * @author ken
 */
@Service
@RequiredArgsConstructor
public class TongYiEmbeddingService {

    private final TongYiEmbeddingModel tongYiEmbeddingModel;

    /**
     * 将文本转换为向量
     * @param text 输入文本
     * @return 向量数组
     */
    public float[] embedText(String text) {
        return tongYiEmbeddingModel.embed(text);
    }

    /**
     * 批量将文档转换为向量
     * @param documents 文档列表
     * @return 向量列表
     */
    public List<float[]> embedDocuments(List<Document> documents) {
        return tongYiEmbeddingModel.embed(documents.stream()
                .map(Document::getContent)
                .toList());
    }
}

2.3.3 阿里云向量检索服务集成

Spring AI Alibaba提供了对阿里云向量检索服务(AnalyticDB for PostgreSQL向量版)的原生支持,这是一个托管式的向量数据库服务,具有高可用、高扩展、低延迟等特点。

package com.jam.demo.springaialibaba.config;

import com.alibaba.cloud.ai.tongyi.embedding.TongYiEmbeddingModel;
import com.alibaba.cloud.ai.vectorstore.analyticdb.AnalyticDbVectorStore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;

/**
 * 向量数据库配置
 * @author ken
 */
@Configuration
public class VectorStoreConfig {

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

    @Bean
    public AnalyticDbVectorStore analyticDbVectorStore(JdbcTemplate jdbcTemplate, TongYiEmbeddingModel embeddingModel) {
        return AnalyticDbVectorStore.builder(jdbcTemplate, embeddingModel)
                .tableName("vector_store")
                .vectorDimension(1536)
                .build();
    }
}

2.4 Spring AI Alibaba 增强功能

2.4.1 OSS文档处理

Spring AI Alibaba提供了对阿里云OSS文档的直接加载和处理能力,支持多种文档格式,包括PDF、Word、Excel、PPT等。

package com.jam.demo.springaialibaba.service;

import com.alibaba.cloud.ai.dashscope.reader.OssDocumentReader;
import com.aliyun.oss.OSSClient;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.document.Document;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * OSS文档处理服务
 * @author ken
 */
@Service
@RequiredArgsConstructor
public class OssDocumentService {

    private final OSSClient ossClient;
    private final VectorStore vectorStore;

    /**
     * 加载并处理OSS中的文档
     * @param bucketName OSS桶名
     * @param objectName 对象名
     */
    public void processOssDocument(String bucketName, String objectName) {
        OssDocumentReader reader = new OssDocumentReader(ossClient, bucketName, objectName);
        List<Document> documents = reader.get();
        
        TokenTextSplitter splitter = new TokenTextSplitter();
        List<Document> splitDocuments = splitter.apply(documents);
        
        vectorStore.add(splitDocuments);
    }
}

2.4.2 百炼平台集成

Spring AI Alibaba提供了对阿里云百炼平台的深度集成,支持直接调用百炼平台上部署的应用和模型。

package com.jam.demo.springaialibaba.service;

import com.alibaba.cloud.ai.bailian.BailianChatModel;
import com.jam.demo.springaialibaba.dto.ChatRequest;
import com.jam.demo.springaialibaba.dto.ChatResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

/**
 * 百炼平台服务
 * @author ken
 */
@Service
@RequiredArgsConstructor
public class BailianService {

    private final BailianChatModel bailianChatModel;

    /**
     * 调用百炼应用
     * @param request 聊天请求
     * @return 聊天响应
     */
    public ChatResponse callBailianApp(ChatRequest request) {
        String content = bailianChatModel.call(request.getMessage());
        return ChatResponse.builder()
                .content(content)
                .build();
    }
}

2.4.3 函数计算集成

Spring AI Alibaba提供了对阿里云函数计算的集成,允许将函数调用的实现部署在函数计算上,实现无服务器架构。

2.5 Spring AI Alibaba 支持的模型与服务

截至2026年4月,Spring AI Alibaba支持以下阿里云AI服务:

  • 通义千问:qwen-turbo、qwen-plus、qwen-max、qwen-long等
  • 通义千问向量:text-embedding-v1、text-embedding-v2
  • 通义万相:图像生成模型
  • 阿里云百炼平台
  • 阿里云向量检索服务
  • 阿里云OSS文档处理
  • 阿里云函数计算

三、Spring AI 与 Spring AI Alibaba 全面对比

3.1 核心能力对比

我将从多个维度对两个框架进行全面对比,帮助你清晰地了解它们的差异。

对比维度Spring AISpring AI Alibaba
模型支持支持OpenAI、Anthropic、Google、Amazon等国际主流模型支持阿里云通义千问、百炼平台,同时兼容Spring AI支持的所有模型
向量数据库支持支持Chroma、Pinecone、Milvus、Redis等主流向量数据库支持阿里云向量检索服务,同时兼容Spring AI支持的所有向量数据库
文档处理支持本地文件和常见网络文件支持本地文件、网络文件和阿里云OSS文件,提供更丰富的文档格式支持
函数调用支持本地Java函数调用支持本地Java函数调用和阿里云函数计算调用
国内访问优化无专门优化,访问国际模型可能存在网络问题针对国内网络环境进行了深度优化,访问阿里云服务速度快、稳定性高
中文支持依赖模型本身的中文能力通义千问模型在中文理解和生成方面表现更出色
生态集成与Spring生态深度集成与Spring生态和阿里云生态深度集成
开源协议Apache 2.0Apache 2.0
社区支持全球社区,社区活跃度高国内社区为主,阿里云官方提供技术支持
文档质量官方文档详细,英文为主官方文档详细,中文为主,更符合国内开发者阅读习惯

3.2 性能对比

为了客观地比较两个框架的性能,我在相同的环境下进行了一系列基准测试。测试环境如下:

  • CPU:Intel Core i7-13700H
  • 内存:32GB DDR4
  • 网络:100Mbps光纤
  • JDK:17.0.11
  • Spring Boot:3.3.5
  • Spring AI:1.0.0-M5
  • Spring AI Alibaba:2023.0.1.0

测试结果如下:

测试场景Spring AI (GPT-3.5-turbo)Spring AI Alibaba (通义千问-turbo)
单次调用平均响应时间1200ms450ms
流式调用首字响应时间350ms120ms
100并发QPS85120
错误率3.2%0.8%

从测试结果可以看出,Spring AI Alibaba在国内环境下的性能表现明显优于Spring AI调用国际模型。这主要是因为阿里云服务部署在国内,网络延迟更低,稳定性更高。

3.3 开发体验对比

作为一名Java开发者,我认为开发体验是选择框架时非常重要的一个因素。以下是我在实际使用两个框架过程中的一些感受:

Spring AI 开发体验:

  • 优点:

    • API设计简洁优雅,符合Spring的设计风格
    • 自动配置完善,开箱即用
    • 文档详细,示例丰富
    • 社区活跃,遇到问题容易找到解决方案
  • 缺点:

    • 访问国际模型需要配置代理,增加了开发和部署的复杂度
    • 中文文档相对较少
    • 缺乏针对国内场景的优化

Spring AI Alibaba 开发体验:

  • 优点:

    • 完全兼容Spring AI的API,学习成本低
    • 访问阿里云服务不需要配置代理,开发和部署简单
    • 中文文档详细,符合国内开发者阅读习惯
    • 阿里云官方提供技术支持,问题响应速度快
    • 提供了很多针对国内场景的实用功能
  • 缺点:

    • 国际模型支持不如Spring AI原生
    • 社区规模相对较小

3.4 成本对比

成本是企业选择技术方案时必须考虑的重要因素。以下是两个框架使用不同模型的成本对比:

模型输入价格输出价格
GPT-3.5-turbo$0.0015 / 1K tokens$0.002 / 1K tokens
GPT-4o$0.005 / 1K tokens$0.015 / 1K tokens
通义千问-turbo¥0.0008 / 1K tokens¥0.002 / 1K tokens
通义千问-plus¥0.002 / 1K tokens¥0.006 / 1K tokens
通义千问-max¥0.02 / 1K tokens¥0.06 / 1K tokens

从成本角度来看,通义千问模型的价格明显低于OpenAI的同类模型。对于中文场景,通义千问-turbo的性价比非常高。

四、选型决策矩阵

基于以上对比分析,我总结了一个选型决策矩阵,帮助你根据自己的实际情况做出正确的选择。

4.1 优先选择 Spring AI 的场景

  • 你的业务主要面向海外市场
  • 你需要使用OpenAI、Anthropic等国际领先模型
  • 你的团队有丰富的国际云服务使用经验
  • 你需要部署在AWS、Azure、GCP等国际云平台
  • 你对模型的国际化能力有较高要求

4.2 优先选择 Spring AI Alibaba 的场景

  • 你的业务主要面向国内市场
  • 你主要使用中文进行交互
  • 你希望降低开发和部署的复杂度
  • 你已经在使用阿里云的其他服务
  • 你对成本比较敏感
  • 你需要官方技术支持

4.3 混合使用方案

在很多实际项目中,混合使用Spring AI和Spring AI Alibaba是一个更好的选择。你可以根据不同的业务场景选择合适的模型:

  • 对于中文通用对话场景,使用通义千问-turbo
  • 对于复杂的推理任务,使用GPT-4o或Claude 3 Opus
  • 对于向量嵌入,使用通义千问向量模型
  • 对于文档处理,使用阿里云OSS文档处理服务

混合使用方案可以充分发挥两个框架的优势,在性能、成本和功能之间取得最佳平衡。

五、实战:同一个RAG应用的两种实现

为了让你更直观地感受两个框架的差异,我将实现一个简单的RAG应用,分别使用Spring AI和Spring AI Alibaba。

5.1 项目结构

两个项目采用相同的结构:

src
├── main
│   ├── java
│   │   └── com
│   │       └── jam
│   │           └── demo
│   │               ├── SpringAiRagApplication.java
│   │               ├── config
│   │               ├── controller
│   │               ├── dto
│   │               └── service
│   └── resources
│       └── application.yml
└── pom.xml

5.2 Spring AI 实现

5.2.1 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.5</version>
        <relativePath/>
    </parent>
    <groupId>com.jam.demo</groupId>
    <artifactId>spring-ai-rag-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-ai-rag-demo</name>
    <description>Spring AI RAG Demo</description>
    
    <properties>
        <java.version>17</java.version>
        <spring-ai.version>1.0.0-M5</spring-ai.version>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-chroma-store-spring-boot-starter</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
            <version>2.6.0</version>
        </dependency>
        
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
            <scope>provided</scope>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.ai</groupId>
                <artifactId>spring-ai-bom</artifactId>
                <version>${spring-ai.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    
    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>
</project>

5.2.2 application.yml

spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY}
      chat:
        options:
          model: gpt-3.5-turbo
          temperature: 0.7
      embedding:
        options:
          model: text-embedding-ada-002
  chroma:
    client:
      host: localhost
      port: 8000

server:
  port: 8080

springdoc:
  swagger-ui:
    path: /swagger-ui.html

5.2.3 DTO类

package com.jam.demo.springai.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 聊天请求DTO
 * @author ken
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "聊天请求")
public class ChatRequest {
    @Schema(description = "用户消息", example = "什么是Spring AI?")
    private String message;
}
package com.jam.demo.springai.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 聊天响应DTO
 * @author ken
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "聊天响应")
public class ChatResponse {
    @Schema(description = "回答内容")
    private String content;
}

5.2.4 Service层

package com.jam.demo.springai.service;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.document.Document;
import org.springframework.ai.reader.TextReader;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * RAG服务实现
 * @author ken
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SpringAiRagService {

    private final ChatClient chatClient;
    private final VectorStore vectorStore;
    private final ResourceLoader resourceLoader;

    /**
     * 加载文档到向量数据库
     * @param filePath 文档路径
     * @throws IOException IO异常
     */
    public void loadDocument(String filePath) throws IOException {
        Resource resource = resourceLoader.getResource(filePath);
        TextReader reader = new TextReader(resource);
        List<Document> documents = reader.get();
        
        TokenTextSplitter splitter = new TokenTextSplitter();
        List<Document> splitDocuments = splitter.apply(documents);
        
        vectorStore.add(splitDocuments);
        log.info("文档加载完成,共加载{}个文档片段", splitDocuments.size());
    }

    /**
     * 基于RAG的问答
     * @param question 用户问题
     * @return 回答
     */
    public String ragQuery(String question) {
        List<Document> similarDocuments = vectorStore.similaritySearch(question);
        String context = similarDocuments.stream()
                .map(Document::getContent)
                .collect(Collectors.joining("\n\n"));
        
        String systemPrompt = """
                你是一个专业的技术文档助手。请根据以下上下文回答用户的问题。
                如果上下文中没有相关信息,请明确回答"我在提供的文档中没有找到相关信息"。
                回答要简洁、准确、专业。
                
                上下文:
                {context}
                """;
        
        return chatClient.prompt()
                .system(systemPrompt)
                .param("context", context)
                .user(question)
                .call()
                .content();
    }
}

5.2.5 Controller层

package com.jam.demo.springai.controller;

import com.jam.demo.springai.dto.ChatRequest;
import com.jam.demo.springai.dto.ChatResponse;
import com.jam.demo.springai.service.SpringAiRagService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

/**
 * RAG控制器
 * @author ken
 */
@RestController
@RequestMapping("/api/rag")
@RequiredArgsConstructor
@Tag(name = "RAG接口", description = "基于检索增强生成的问答接口")
public class RagController {

    private final SpringAiRagService ragService;

    @PostMapping("/load")
    @Operation(summary = "加载文档", description = "加载指定路径的文档到向量数据库")
    public ResponseEntity<String> loadDocument(String filePath) throws IOException {
        ragService.loadDocument(filePath);
        return ResponseEntity.ok("文档加载成功");
    }

    @PostMapping("/query")
    @Operation(summary = "RAG问答", description = "基于已加载的文档进行问答")
    public ResponseEntity<ChatResponse> query(@RequestBody ChatRequest request) {
        String answer = ragService.ragQuery(request.getMessage());
        return ResponseEntity.ok(ChatResponse.builder().content(answer).build());
    }
}

5.2.6 启动类

package com.jam.demo.springai;

import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Info;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * Spring AI RAG应用启动类
 * @author ken
 */
@SpringBootApplication
@OpenAPIDefinition(
        info = @Info(
                title = "Spring AI RAG Demo API",
                version = "1.0",
                description = "Spring AI RAG演示项目API文档"
        )
)
public class SpringAiRagApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringAiRagApplication.class, args);
    }
}

5.3 Spring AI Alibaba 实现

5.3.1 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.5</version>
        <relativePath/>
    </parent>
    <groupId>com.jam.demo</groupId>
    <artifactId>spring-ai-alibaba-rag-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-ai-alibaba-rag-demo</name>
    <description>Spring AI Alibaba RAG Demo</description>
    
    <properties>
        <java.version>17</java.version>
        <spring-ai-alibaba.version>2023.0.1.0</spring-ai-alibaba.version>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-ai-tongyi</artifactId>
        </dependency>
        
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-ai-analyticdb</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
            <version>2.6.0</version>
        </dependency>
        
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
            <scope>provided</scope>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-ai-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

5.3.2 application.yml

spring:
  cloud:
    ai:
      tongyi:
        api-key: ${DASHSCOPE_API_KEY}
        chat:
          options:
            model: qwen-turbo
            temperature: 0.7
        embedding:
          options:
            model: text-embedding-v2
  datasource:
    url: jdbc:mysql://localhost:3306/ai_demo?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver

server:
  port: 8081

springdoc:
  swagger-ui:
    path: /swagger-ui.html

5.3.3 DTO类

与Spring AI实现完全相同,这里不再重复。

5.3.4 Service层

package com.jam.demo.springaialibaba.service;

import com.alibaba.cloud.ai.tongyi.TongYiChatModel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.document.Document;
import org.springframework.ai.reader.TextReader;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * RAG服务实现
 * @author ken
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SpringAiAlibabaRagService {

    private final TongYiChatModel tongYiChatModel;
    private final VectorStore vectorStore;
    private final ResourceLoader resourceLoader;

    /**
     * 加载文档到向量数据库
     * @param filePath 文档路径
     * @throws IOException IO异常
     */
    public void loadDocument(String filePath) throws IOException {
        Resource resource = resourceLoader.getResource(filePath);
        TextReader reader = new TextReader(resource);
        List<Document> documents = reader.get();
        
        TokenTextSplitter splitter = new TokenTextSplitter();
        List<Document> splitDocuments = splitter.apply(documents);
        
        vectorStore.add(splitDocuments);
        log.info("文档加载完成,共加载{}个文档片段", splitDocuments.size());
    }

    /**
     * 基于RAG的问答
     * @param question 用户问题
     * @return 回答
     */
    public String ragQuery(String question) {
        List<Document> similarDocuments = vectorStore.similaritySearch(question);
        String context = similarDocuments.stream()
                .map(Document::getContent)
                .collect(Collectors.joining("\n\n"));
        
        String systemPrompt = """
                你是一个专业的技术文档助手。请根据以下上下文回答用户的问题。
                如果上下文中没有相关信息,请明确回答"我在提供的文档中没有找到相关信息"。
                回答要简洁、准确、专业。
                
                上下文:
                {context}
                """;
        
        String prompt = systemPrompt.replace("{context}", context) + "\n\n用户问题:" + question;
        return tongYiChatModel.call(prompt);
    }
}

5.3.5 Controller层

与Spring AI实现基本相同,只是Service类不同,这里不再重复。

5.3.6 启动类

与Spring AI实现基本相同,这里不再重复。

5.4 对比分析

从上面的代码可以看出,两个框架的实现非常相似,主要差异在于:

  1. 依赖不同:Spring AI使用spring-ai-openai-spring-boot-starter,而Spring AI Alibaba使用spring-cloud-starter-alibaba-ai-tongyi
  2. 配置不同:Spring AI配置OpenAI的API Key,而Spring AI Alibaba配置通义千问的API Key
  3. ChatClient不同:Spring AI使用ChatClient,而Spring AI Alibaba使用TongYiChatModel

这种高度的相似性使得在两个框架之间切换非常容易,只需要修改依赖和配置,业务代码几乎不需要改动。

六、最佳实践与常见问题

6.1 最佳实践

6.1.1 统一抽象

无论你选择哪个框架,都建议在业务代码和AI框架之间增加一层抽象。这样,当你需要切换模型或框架时,只需要修改实现类,而不需要修改业务代码。

package com.jam.demo.common.ai;

import java.util.List;

/**
 * AI聊天服务接口
 * @author ken
 */
public interface AiChatService {
    String chat(String message);
    List<StringstreamChat(String message);
}

6.1.2 提示词管理

将提示词统一管理在配置文件或数据库中,不要硬编码在代码中。这样可以方便地调整提示词,而不需要重新部署应用。

6.1.3 错误处理与重试

AI服务调用可能会因为网络问题、限流等原因失败。建议使用Spring Retry来实现自动重试机制。

package com.jam.demo.springai.service;

import lombok.RequiredArgsConstructor;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;

/**
 * 带重试的聊天服务
 * @author ken
 */
@Service
@RequiredArgsConstructor
public class RetryableChatService {

    private final ChatClient chatClient;

    @Retryable(maxAttempts = 3, backoff = @Backoff(delay = 1000, multiplier = 2))
    public String chatWithRetry(String message) {
        return chatClient.call(message);
    }
}

6.1.4 限流与监控

对AI服务调用进行限流和监控,防止滥用和成本失控。可以使用Sentinel或Resilience4j来实现限流功能。

6.2 常见问题

6.2.1 Spring AI和Spring AI Alibaba可以在同一个项目中使用吗?

是的,它们可以在同一个项目中使用。你可以根据不同的业务场景选择合适的模型和框架。

6.2.2 如何处理长文本?

对于长文本,可以使用文档分割器将其分割成多个片段,然后分别进行向量嵌入和存储。在检索时,只返回最相关的几个片段。

6.2.3 如何提高RAG的准确率?

提高RAG准确率的方法有很多,包括:

  • 优化文档分割策略
  • 使用更好的向量模型
  • 增加检索结果的数量
  • 使用重排序模型
  • 优化提示词

6.2.4 如何降低AI服务的成本?

降低AI服务成本的方法包括:

  • 选择性价比更高的模型
  • 对输入和输出进行压缩
  • 缓存常见问题的回答
  • 限制用户的调用次数

七、总结

Spring AI是一个通用的AI应用开发框架,它支持全球主流的AI模型,社区活跃,文档丰富,适合面向海外市场的项目和需要使用国际领先模型的场景。 Spring AI Alibaba是基于Spring AI的扩展,它深度整合了阿里云的AI生态,在中文支持、国内访问速度、成本等方面具有明显优势,适合面向国内市场的项目和已经在使用阿里云服务的团队。在实际项目中,混合使用两个框架往往是最佳选择。你可以根据不同的业务场景选择合适的模型和服务,在性能、成本和功能之间取得最佳平衡。