Spring AI 多模型接入实战:OpenAI、Ollama、通义千问切换只需改配置

本文是《Spring AI 实战》系列第 3 章。真实项目中,你可能今天用 OpenAI,明天就要换成 Ollama 降成本,后天又要接入通义千问满足合规要求。Spring AI 的模型抽象层让这一切变得简单------切换模型只需改配置文件,Java 代码一行不动。


一、真实场景:老板要换模型降成本

假设你的 AI 客服系统已经上线,使用的是 OpenAI GPT-4o,每月 API 费用 5000 美元。某天老板看了账单,要求把成本降下来。

如果你用的是直接调用 OpenAI API 的方式:

java 复制代码
// 硬编码了 OpenAI 的 API 地址、请求格式、认证方式
// 换模型 = 重写所有 HTTP 调用代码
Request request = new Request.Builder()
    .url("https://api.openai.com/v1/chat/completions")  // 硬编码
    .addHeader("Authorization", "Bearer " + openaiApiKey)  // 硬编码
    .post(RequestBody.create(openaiFormatJson, ...))       // 硬编码
    .build();

换成本地 Ollama 或通义千问?对不起,上面的代码全部作废,推倒重来。 API 地址不同、认证方式不同、请求格式不同、响应格式不同。

但如果你用的是 Spring AI,只需要两步:

  1. 换 Starter 依赖(pom.xml
  2. 换配置(application.yml

Java 代码一行不动。 这不是魔法,这是 Spring AI 模型抽象层的威力。


二、Spring AI 的模型抽象层原理

2.1 ChatModel 接口设计

Spring AI 定义了一个核心接口 ChatModel,所有模型厂商的实现都遵循这个接口:

java 复制代码
// Spring AI 的核心接口(简化版)
public interface ChatModel {
    /**
     * 调用大模型,传入 Prompt,返回 ChatResponse
     * 不管底层是 OpenAI、Ollama 还是通义千问,调用方式完全一样
     */
    ChatResponse call(Prompt prompt);
}

各厂商提供的实现类:

复制代码
ChatModel(接口)
├── OpenAiChatModel       → 调用 OpenAI GPT 系列
├── OllamaChatModel       → 调用本地 Ollama 模型
├── AnthropicChatModel    → 调用 Anthropic Claude
├── GeminiChatModel       → 调用 Google Gemini
├── MistralAiChatModel    → 调用 Mistral AI
└── ...更多厂商实现

2.2 为什么切换模型只需改配置

Spring Boot 的自动配置机制根据你引入的 Starter,自动创建对应的 ChatModel Bean:

java 复制代码
// 引入 spring-ai-starter-model-openai 后
// Spring AI 自动创建 OpenAiChatModel Bean

// 引入 spring-ai-starter-model-ollama 后
// Spring AI 自动创建 OllamaChatModel Bean

// 你的代码只依赖 ChatModel 接口,不依赖具体实现
// 所以切换 Starter = 切换底层实现,上层代码无感知

这就是经典的"面向接口编程"------和 Spring Data 切换数据库驱动(MySQL → PostgreSQL → Oracle)是同一个道理。


三、OpenAI 接入详解

3.1 Starter 依赖

xml 复制代码
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>

3.2 application.yml 配置

yaml 复制代码
spring:
  ai:
    openai:
      # API Key(必填,从环境变量读取)
      api-key: ${OPENAI_API_KEY}
      
      # 自定义 API 地址(可选)
      # 如果你使用代理或兼容 OpenAI 协议的第三方服务,需要设置这个
      # 默认值:https://api.openai.com
      # base-url: https://your-proxy.com/v1
      
      chat:
        options:
          # 模型名称
          model: gpt-4o-mini
          # 温度参数(0-2,越高越有创造性)
          temperature: 0.7
          # 单次回复最大 token 数
          max-tokens: 2048
          # 核采样参数(0-1)
          top-p: 1.0

3.3 完整的 Service 调用代码

java 复制代码
@Service
public class OpenAiChatService {

    private final ChatClient chatClient;

    public OpenAiChatService(ChatClient.Builder builder) {
        this.chatClient = builder
                .defaultSystem("你是一个专业的 AI 助手,回答简洁清晰")
                .build();
    }

    /**
     * 简单对话
     */
    public String chat(String message) {
        return chatClient.prompt()
                .user(message)
                .call()
                .content();
    }

    /**
     * 对话并获取 token 用量(用于成本监控)
     */
    public ChatResult chatWithUsage(String message) {
        ChatResponse response = chatClient.prompt()
                .user(message)
                .call()
                .chatResponse();

        var usage = response.getMetadata().getUsage();
        return new ChatResult(
                response.getResult().getOutput().getText(),
                usage.getPromptTokens(),
                usage.getGenerationTokens(),
                usage.getTotalTokens()
        );
    }

    // 使用 Java Record 作为返回值
    public record ChatResult(
            String content,
            long promptTokens,
            long generationTokens,
            long totalTokens
    ) {}
}

3.4 OpenAI 计费详解

模型 输入价格(每 1M tokens) 输出价格(每 1M tokens) 上下文窗口 推荐场景
GPT-4o $2.50 $10.00 128K 复杂推理、长文档分析、高质量生成
GPT-4o-mini $0.15 $0.60 128K 日常对话、分类、摘要(性价比之王)
GPT-3.5-turbo $0.50 $1.50 16K 简单任务(已被 4o-mini 取代)
o1-mini $3.00 $12.00 128K 复杂数学、编程推理
o1-preview $15.00 $60.00 128K 最强推理,但价格极高

实战建议:大多数场景先用 GPT-4o-mini。它的成本只有 GPT-4o 的 1/16,但质量已经非常好了。只有当 4o-mini 的输出质量不满足要求时,再升级到 GPT-4o。

3.5 踩坑提醒

  • API Key 管理:OpenAI 的 API Key 泄露 = 你的信用卡被盗刷。务必使用环境变量,不要提交到 Git。建议在 OpenAI 后台设置 Monthly Limit(月度消费上限)。
  • 速率限制:OpenAI 有 RPM(每分钟请求数)和 TPM(每分钟 Token 数)限制。免费账号限制更严格。生产环境需要实现重试和降级策略。
  • 网络问题 :中国大陆直连 OpenAI API 不稳定,需要配置代理。可以在 base-url 中设置代理地址。

四、Ollama 本地模型接入

4.1 安装方式

bash 复制代码
# Windows
winget install Ollama.Ollama

# Mac
brew install ollama

# Linux
curl -fsSL https://ollama.com/install.sh | sh

安装后 Ollama 作为后台服务运行,默认监听 http://localhost:11434

4.2 常用模型推荐

模型名 参数量 中文能力 显存需求 适用场景
qwen2.5 7B 优秀 8GB(推荐 16GB) 中文对话、问答、翻译,中文场景首选
llama3.1 8B 一般 8GB(推荐 16GB) 英文对话、代码生成,英文能力最强
deepseek-r1:8b 8B 良好 8GB(推荐 16GB) 复杂推理、数学、逻辑分析
gemma2:9b 9B 良好 8GB(推荐 16GB) 通用对话、指令跟随
phi3.5:3.8b 3.8B 一般 4GB 轻量级任务、资源受限环境
bash 复制代码
# 拉取推荐的中文模型
ollama pull qwen2.5:7b

# 验证模型可用
ollama run qwen2.5:7b "你好,请介绍一下自己"

4.3 切换依赖:pom.xml

xml 复制代码
<!-- 注释掉 OpenAI Starter -->
<!-- <dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-openai</artifactId>
</dependency> -->

<!-- 换成 Ollama Starter -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-ollama</artifactId>
</dependency>

4.4 切换配置:application.yml

yaml 复制代码
spring:
  ai:
    ollama:
      base-url: http://localhost:11434
      chat:
        options:
          model: qwen2.5:7b
          # Ollama 也支持 temperature 等参数
          temperature: 0.7

4.5 Java 代码完全不变

java 复制代码
// 这个 Controller 在 OpenAI 和 Ollama 两种配置下都能正常工作
// 因为 ChatClient 依赖的是 ChatModel 接口,不是具体实现
@RestController
public class ChatController {

    private final ChatClient chatClient;

    public ChatController(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }

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

这就是 Spring AI 的核心价值:代码与模型解耦。

4.6 踩坑提醒

  • 模型拉取慢 :7B 模型约 4.5GB,国内下载可能很慢。解决方案:设置 Ollama 镜像环境变量 OLLAMA_HOST 或使用国内镜像站。
  • 端口冲突 :Ollama 默认使用 11434 端口。如果该端口被占用,可以通过环境变量 OLLAMA_HOST=0.0.0.0:11435 修改。
  • Mac M 芯片兼容:Apple Silicon 原生支持,使用 Metal 加速,体验很好。但 14B+ 的模型可能需要 32GB 以上内存。
  • 模型质量差异:本地 7B 模型的能力远不如 GPT-4o。对于简单的分类、提取任务够用;复杂推理任务建议用更大的模型或云端 API。

五、通义千问百炼平台接入

5.1 为什么国内推荐通义千问

对比项 OpenAI(海外) 通义千问百炼(国内)
网络延迟 200-500ms(海外服务器) 50-100ms(国内服务器)
数据合规 数据出境,需评估合规风险 数据不出境,天然合规
中文能力 优秀 优秀(专门优化中文)
价格 GPT-4o-mini: 0.15/0.6 qwen-plus: 更便宜(有免费额度)
支付方式 需要国际信用卡 支付宝即可

结论:如果你是面向国内用户的项目,通义千问是生产环境的首选。

5.2 注册百炼平台获取 API Key

  1. 打开 阿里云百炼平台
  2. 登录阿里云账号(没有就注册一个)
  3. 进入"API-KEY 管理",创建一个 API Key
  4. 复制 API Key,设置环境变量
bash 复制代码
# Windows PowerShell
$env:DASHSCOPE_API_KEY="sk-xxxxxxxxxxxxxxxx"

# Linux/Mac
export DASHSCOPE_API_KEY=sk-xxxxxxxxxxxxxxxx

5.3 兼容 OpenAI 协议的配置方式

通义千问兼容 OpenAI 的 API 协议,所以可以直接使用 Spring AI 的 OpenAI Starter,只需要修改 base-url

xml 复制代码
<!-- 依赖不变,还是用 OpenAI Starter -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
yaml 复制代码
spring:
  ai:
    openai:
      # 关键:把 base-url 改成通义千问的兼容地址
      base-url: https://dashscope.aliyuncs.com/compatible-mode/v1
      # API Key 使用百炼平台的 Key
      api-key: ${DASHSCOPE_API_KEY}
      chat:
        options:
          # 使用通义千问的模型
          model: qwen-plus
          temperature: 0.7

看到没有?依赖和代码都没变,只改了 base-urlapi-key 这就是 OpenAI 兼容协议的好处------通义千问、DeepSeek、智谱、Moonshot 等国内大模型都兼容 OpenAI 协议,都可以用同样的方式接入。

5.4 通义模型选择表格

模型 定位 输入价格 输出价格 上下文窗口 适合场景
qwen-turbo 快速响应 0.8元/百万tokens 2元/百万tokens 128K 高并发、实时对话
qwen-plus 均衡性价比 4元/百万tokens 8元/百万tokens 128K 日常对话、内容生成(推荐)
qwen-max 旗舰模型 20元/百万tokens 60元/百万tokens 32K 复杂推理、长文本分析
qwen-long 超长上下文 0.5元/百万tokens 2元/百万tokens 1000K+ 长文档处理(100万字)

实战建议 :大多数场景用 qwen-plus。它的性价比最高,中文能力优秀。只有当 qwen-plus 的输出质量不满足要求时(比如复杂的数学推理),再升级到 qwen-max


六、模型参数调优指南

不管你用哪个模型,这三个参数都是核心调优点:

6.1 temperature 详解

效果 类比
0 完全确定性,每次返回相同结果 查字典------同一个词永远查到同一个释义
0.3 基本确定,微小变化 教科书------严谨准确
0.7 适中,有一定创造性 有经验的同事------专业但不死板
1.0 有创造性,每次可能不同 脑暴会议------想法多样
1.5+ 高创造性,可能胡说八道 喝醉了的创意总监------天马行空但不可靠

6.2 top_p 详解

top_p 是核采样参数(也叫 nucleus sampling),它和 temperature 一起控制输出的随机性。

  • top_p = 1.0:不限制,考虑所有可能的 token
  • top_p = 0.9:只考虑概率累计前 90% 的 token(排除 10% 的低概率 token)
  • top_p = 0.5:只考虑概率累计前 50% 的 token(非常保守)

实战建议 :一般情况下用 top_p = 1.0(默认值),通过 temperature 来控制随机性即可。两者同时调整的效果不是简单的叠加,一般不需要同时大幅调整。

6.3 max_tokens 详解

控制 AI 单次回复的最大 token 数量。

  • 1 token 大约 0.75 个英文单词或 0.5 个中文字
  • max_tokens = 100:大约 50-75 个中文字
  • max_tokens = 2048:大约 1000-1500 个中文字
  • max_tokens = 4096:大约 2000-3000 个中文字

注意max_tokens 设置过小会导致回复被截断;设置过大会浪费 token(计费)和增加延迟。

6.4 五种场景的参数推荐

场景 temperature top_p max_tokens 原因
代码生成 0.1-0.2 1.0 2048 需要确定性输出,代码不能乱编
问答/知识查询 0.3-0.5 1.0 1024 需要准确,但允许适当变化
翻译 0.1-0.3 1.0 2048 翻译需要精确,不应该"创造性发挥"
创意写作 0.7-1.0 0.9 4096 需要创造力和多样性
数据分析 0.2-0.4 1.0 2048 需要准确的数据分析,不能编造数据
yaml 复制代码
# 场景化配置示例
spring:
  ai:
    openai:
      chat:
        options:
          # 代码生成场景
          model: gpt-4o-mini
          temperature: 0.2
          max-tokens: 2048

6.5 踩坑提醒

  • temperature 过高导致胡说八道:这是最常见的坑。如果你的应用需要准确输出(代码、数据、事实),务必把 temperature 设在 0.3 以下。很多 AI "幻觉"问题就是 temperature 设置过高导致的。
  • 不同模型对参数的敏感度不同:GPT-4o 对参数变化不太敏感(在 0.1-1.0 范围内都比较稳定),但较小的本地模型可能对 temperature 非常敏感。测试时注意对比。
  • max_tokens 和成本的关系:max_tokens 设置得越大,单次调用的最大成本就越高。建议根据实际需求设置合理上限。

七、多模型路由

7.1 场景

你的系统有不同类型的请求:简单的问候、常见问题用便宜快速的模型;复杂的技术问题用好模型;代码生成用专门的代码模型。

如何根据请求类型自动路由到不同的模型?

7.2 @Qualifier 多 Bean 配置

如果你的应用同时引入了多个 Starter(比如同时引入 OpenAI 和 Ollama),Spring Boot 会自动创建多个 ChatModel Bean。你需要用 @Qualifier 区分它们:

java 复制代码
@Configuration
public class MultiModelConfig {

    /**
     * 主力模型:用于日常对话和一般任务
     * 使用 OpenAI GPT-4o-mini
     */
    @Bean
    @Primary  // 默认的 ChatModel
    public ChatClient primaryChatClient(ChatClient.Builder builder) {
        return builder.build();
    }

    /**
     * 高级模型:用于复杂推理和分析任务
     * 需要单独注入 OpenAiChatModel 并构建
     */
    @Bean("advancedChatClient")
    public ChatClient advancedChatClient(
            @Qualifier("openAiChatModel") ChatModel advancedModel,
            ChatClient.Builder builder) {
        // 用更高级的模型构建(需要在 yml 中配置第二个模型)
        return builder
                .defaultModel(advancedModel)
                .defaultSystem("你是一个资深的技术专家")
                .build();
    }
}

7.3 路由 Service 代码

java 复制代码
@Service
public class SmartChatService {

    private final ChatClient simpleClient;   // 便宜快速的模型
    private final ChatClient advancedClient;  // 贵但强大的模型

    public SmartChatService(
            @Primary ChatClient simpleClient,
            @Qualifier("advancedChatClient") ChatClient advancedClient) {
        this.simpleClient = simpleClient;
        this.advancedClient = advancedClient;
    }

    /**
     * 智能路由:根据问题复杂度选择不同的模型
     * 
     * 简单问题(问候、闲聊、常见问题)→ 便宜模型
     * 复杂问题(技术分析、代码生成、推理)→ 好模型
     */
    public String smartChat(String message) {
        if (isSimpleQuestion(message)) {
            // 简单问题用便宜模型,省钱省时间
            return simpleClient.prompt()
                    .user(message)
                    .call()
                    .content();
        } else {
            // 复杂问题用好模型,保证质量
            return advancedClient.prompt()
                    .user(message)
                    .call()
                    .content();
        }
    }

    /**
     * 判断问题是否简单(简化版)
     * 实际项目中可以用轻量级分类器或规则引擎
     */
    private boolean isSimpleQuestion(String message) {
        String msg = message.toLowerCase();
        // 简单问候
        return msg.matches(".*(你好|hi|hello|谢谢|再见|拜拜).*")
                // 短问题(通常比较简单)
                || message.length() < 10;
    }
}

7.4 另一种方案:单模型 + 不同参数

如果你的应用只用一个模型厂商(比如只用 OpenAI),但需要根据场景调整参数:

java 复制代码
@Service
public class ParameterRouterService {

    private final ChatModel chatModel;

    public ParameterRouterService(ChatModel chatModel) {
        this.chatModel = chatModel;
    }

    /**
     * 根据场景动态调整参数
     */
    public String chatWithParams(String message, String scenario) {
        // 根据场景构建不同的参数
        var options = switch (scenario) {
            case "code" -> ChatOptions.builder()
                    .temperature(0.2)     // 代码生成:低温
                    .maxTokens(4096)       // 允许较长的代码输出
                    .build();
            case "creative" -> ChatOptions.builder()
                    .temperature(1.0)      // 创意写作:高温
                    .maxTokens(2048)
                    .build();
            default -> ChatOptions.builder()
                    .temperature(0.7)     // 默认
                    .maxTokens(1024)
                    .build();
        };

        Prompt prompt = new Prompt(
                List.of(new UserMessage(message)),
                options
        );

        return chatModel.call(prompt)
                .getResult().getOutput().getText();
    }
}

八、小结

本章我们学习了 Spring AI 的多模型接入能力:

  1. 模型抽象层原理ChatModel 接口 + 各厂商实现,面向接口编程
  2. OpenAI 接入:Starter + 配置 + 计费详解,生产环境首选 GPT-4o-mini
  3. Ollama 本地模型:零成本、数据不出本地,推荐 qwen2.5:7b
  4. 通义千问百炼:国内首选,兼容 OpenAI 协议,改 base-url 即可
  5. 模型参数调优:temperature、top_p、max_tokens 的原理和场景推荐值
  6. 多模型路由:根据请求复杂度自动选择模型,省钱又保质

下一章,我们将学习 Prompt Engineering 和 Advisor 机制------同样的模型,好的 Prompt 能让输出质量翻倍。


Spring AI 实战 --- 第3章:完整内容与源码

相关推荐
tianyatest1 小时前
表格分类统计及排序
java·excel·暖通
aixingkong9211 小时前
AI超节点系统基础知识地图
人工智能
易知微EasyV数据可视化1 小时前
定制化3D组件开发:打造差异化孪生体验
前端·人工智能·经验分享·数字孪生
richdata1 小时前
千店千面的智能配货体系:门店差异化分配怎么做
大数据·人工智能·数据挖掘
Leinwin1 小时前
技术视角深度对比:Google Gemini 与 Microsoft 365 Copilot 企业级 AI 办公架构解析
人工智能·microsoft·copilot
code_pgf1 小时前
OpenAI Agents SDK 技术详解报告
大数据·人工智能
Emerson_20261 小时前
通用 AI Agent 开发步骤
人工智能·agent·agent开发步骤
老兵发新帖1 小时前
UXUI两个开源项目对比分析
人工智能·开源
万亿少女的梦1682 小时前
基于Spring Boot与MySQL的乡镇群众服务系统设计与实现
java·spring boot·mysql·权限管理·前后端分离