[AI工程] Spring AI第二篇: 2.0 快速接入 DeepSeek、阿里百炼与 Ollama


💡 做 AI 应用,第一步不是 RAG,也不是 Agent,而是把模型调用、流式输出、参数控制和多模态能力先跑通。

Java 开发者常见的三条路径:通过 DeepSeek 使用云端模型;通过 阿里云百炼 接入通义与多模态能力;或使用 Ollama 在本地运行模型。

这篇文章从零创建 Spring AI 2.0 项目,完整演示三种接入方式,以及 temperature、推理模型、流式响应与多模态的使用。
请添加图片描述


1. 创建 Spring AI 2.0 项目

Spring AI 是把 Spring Boot 应用与模型、企业数据、工具调用、RAG、MCP 连接起来的框架。

Spring Boot
  --> Spring AI
    --> 云端模型:DeepSeek / 百炼
    --> 本地模型:Ollama
    --> Chat / Stream / Image / Audio / RAG / MCP

下面使用 Spring Boot 4.0.0、JDK 17+ 与 Spring AI 2.0.1。生产环境更建议 JDK 21+。

<?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>4.0.0</version>
        <relativePath/>
    </parent>

    <groupId>com.gc</groupId>
    <artifactId>spring-ai-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-ai-demo</name>
    <description>程序员GC:Spring AI 2.0 示例</description>

    <properties>
        <java.version>21</java.version>
        <spring-ai.version>2.0.1</spring-ai.version>
    </properties>

    <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>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

spring-ai-bom 的作用是统一管理 Spring AI 组件版本。后续接入模型、向量库或 MCP 时,不需要给每个 Starter 单独指定版本。


2. 接入 DeepSeek:普通对话、流式输出与推理模型

在这里插入图片描述

先在 DeepSeek 开放平台 创建 API Key。添加依赖:

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-deepseek</artifactId>
</dependency>

配置 application.yml

spring:
  ai:
    deepseek:
      api-key: ${DEEPSEEK_API_KEY}
      chat:
        model: deepseek-chat
        temperature: 0.5
        max-tokens: 1024

本地设置环境变量:

export DEEPSEEK_API_KEY=你的_API_KEY

不要把 API Key 写进 application.yml 后提交 Git。

2.1 普通对话

Starter 会自动配置 DeepSeekChatModel。Spring AI 2.0 中,ChatModel 遵循统一的模型调用抽象,模型提供方不同,调用方式基本一致。

@SpringBootTest
class DeepSeekTest {

    @Test
    void testChat(@Autowired DeepSeekChatModel chatModel) {
        String result = chatModel.call("用一句话解释什么是 RAG");
        System.out.println(result);
    }
}

更推荐业务代码中使用 ChatClient,因为它更适合后续叠加 Prompt、Memory、Advisor、Tool Calling 与 RAG。

@RestController
@RequestMapping("/ai")
class ChatController {

    private final ChatClient chatClient;

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

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

2.2 流式对话

聊天窗口或实时生成场景使用流式响应:

@Test
void testStream(@Autowired DeepSeekChatModel chatModel) {
    Flux<String> stream = chatModel.stream("写一首关于清晨的短诗");
    stream.toIterable().forEach(System.out::print);
}

Controller 中可直接输出 SSE:

@GetMapping(value = "/stream", produces = "text/event-stream")
Flux<String> stream(@RequestParam String message) {
    return chatClient.prompt()
            .user(message)
            .stream()
            .content();
}

2.3 temperaturemaxTokensstop

temperature 决定输出随机性。它不是“幻觉开关”,但会明显影响回答风格。

Temperature适合场景输出特点
0.0 ~ 0.2代码补全、结构化抽取、严谨问答稳定、收敛
0.3 ~ 0.6摘要、客服、日常助手平衡、稳妥
0.7 ~ 1.0标题、文案、内容创作更灵活、有创意
1.1+脑暴、故事、发散创作多样,但更容易跑偏

代码中按请求覆盖参数:

@Test
void testOptions(@Autowired DeepSeekChatModel chatModel) {
    DeepSeekChatOptions options = DeepSeekChatOptions.builder()
            .temperature(0.8)
            .maxTokens(200)
            .build();

    ChatResponse response = chatModel.call(
            new Prompt("写一句描述清晨的诗。", options)
    );

    System.out.println(response.getResult().getOutput().getText());
}

对于事实型问答,除了调低温度,更重要的是在提示词中约束回答边界:

请只基于已知事实回答。
信息不足时请明确说明,不要自行补充或推测。
请简洁、客观地给出答案。

maxTokens 用于限制单次回复长度;stop 可以在模型生成指定文本时提前截断输出。它适合控制格式,不适合作为内容安全的唯一手段。

spring:
  ai:
    deepseek:
      chat:
        model: deepseek-chat
        max-tokens: 50
        stop:
          - "\n"
          - "。"

2.4 使用推理模型

推理模型会同时返回最终结果与 reasoning 信息。以 deepseek-reasoner 为例:

@Test
void testReasoner(@Autowired DeepSeekChatModel chatModel) {
    DeepSeekChatOptions options = DeepSeekChatOptions.builder()
            .model("deepseek-reasoner")
            .build();

    ChatResponse response = chatModel.call(
            new Prompt("比较 Redis 与 Caffeine 的典型使用场景。", options)
    );

    AssistantMessage message = response.getResult().getOutput();

    String reasoning = message.getMetadata().get("reasoningContent");
    String answer = message.getText();

    System.out.println("推理信息:" + reasoning);
    System.out.println("最终回答:" + answer);
}

实际产品中,通常只展示最终回答。推理信息更适合用于调试、评测和内部观测,不建议默认直接暴露给终端用户。

Spring AI 对 DeepSeek 的自动配置、重试与参数说明可见:官方文档

综合测试代码:

import org.junit.jupiter.api.Test;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.deepseek.DeepSeekAssistantMessage;
import org.springframework.ai.deepseek.DeepSeekChatModel;
import org.springframework.ai.deepseek.DeepSeekChatOptions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import reactor.core.publisher.Flux;

@SpringBootTest
public class DeepSeekTest {
    /**
     * 启动加载测试:
     * 2.0 中 ChatModel 实现了统一的 Model<Prompt, ChatResponse> 接口,所有模型遵循同一调用规范。
     * @param chatModel
     */
    @Test
    public void testChat(@Autowired DeepSeekChatModel chatModel) {
        String call = chatModel.call("你是谁");
        System.out.println(call);
    }

    /**
     *  2.0 中 StreamingChatModel 接口
     * @param chatModel
     */
    @Test
    public void testChat2(@Autowired DeepSeekChatModel chatModel) {
        Flux<String> stream = chatModel.stream("你是谁");
        stream.toIterable().forEach(System.out::print);
    }


    /**
     * options 配置选项
     * 2.0 中 ChatOptions 是统一的请求选项接口,支持 temperature、topK、topP、maxTokens 等参数。
     * temperature(温度)
     * 0-2 浮点数值
     * 数值越高 更有创造性 热情
     * 数值越低 保守
     * @param chatModel
     */
    @Test
    public void testChatOptions(@Autowired DeepSeekChatModel chatModel) {
        DeepSeekChatOptions options = DeepSeekChatOptions.builder().temperature(0.1d).build();
        ChatResponse res = chatModel.call(new Prompt("请写一句诗描述清晨。", options));
        System.out.println(res.getResult().getOutput().getText());
    }


    /**
     * 模型推理: 设置深度思考,思考的内容有个专业名词叫:Chain of Thought (CoT)
     * @param deepSeekChatModel
     * 在 DeepSeek 中,deepseek-reasoner 模型是深度思考模型:
     */


    @Test
    public void deepSeekReasonerExample(@Autowired DeepSeekChatModel deepSeekChatModel) {


        DeepSeekChatOptions options = DeepSeekChatOptions.builder().model("deepseek-reasoner").build();

        Prompt prompt = new Prompt("请写一句诗描述清晨。", options);
        ChatResponse response = deepSeekChatModel.call(prompt);

        // 2.0 中 AssistantMessage 获取推理内容
        DeepSeekAssistantMessage message =
                (DeepSeekAssistantMessage) response.getResult().getOutput();

        String content = message.getText();
        String reasoningContent = message.getReasoningContent();

        System.out.println(reasoningContent.toString());
        System.out.println("--------------------------------------------");
        System.out.println(content);
    }




    @Test
    public void deepSeekReasonerStreamExample(@Autowired DeepSeekChatModel deepSeekChatModel) {
//
        DeepSeekChatOptions options = DeepSeekChatOptions.builder()
                .model("deepseek-reasoner").build();

        Prompt prompt = new Prompt("请写一句诗描述清晨。", options);
        Flux<ChatResponse> stream = deepSeekChatModel.stream(prompt);

        stream.toIterable().forEach(res -> {
            DeepSeekAssistantMessage message =
                    (DeepSeekAssistantMessage) res.getResult().getOutput();
            String reasoningContent = message.getReasoningContent();
            System.out.print(reasoningContent);
        });
        System.out.println("--------------------------------------------");
        stream.toIterable().forEach(res -> {
            AssistantMessage assistantMessage = res.getResult().getOutput();
            String content = assistantMessage.getText();
            System.out.print(content);
        });
    }


3. DeepSeek 调用过程到底发生了什么?

调用一句 chatModel.call("你是谁"),底层大致经过以下流程:

用户文本
  --> Prompt / UserMessage
  --> DeepSeekChatModel
  --> 请求对象 JSON
  --> DeepSeek API
  --> Spring Retry
  --> ChatResponse

ChatResponse 中最值得关注的是:

结构包含内容
ChatResponseMetadata模型信息、Token Usage、响应元数据
Generation单次生成结果
AssistantMessage模型输出文本、多模态内容、附加元数据
ChatGenerationMetadata与本次生成相关的附加信息

这也是 Spring AI 的价值:无论底层是 DeepSeek、OpenAI、Ollama 还是百炼,应用层都能用接近一致的 PromptChatModelChatResponse 编程模型。


4. 接入阿里百炼:聊天、图片、语音与多模态

Spring AI Alibaba 是阿里云围绕 Spring AI 提供的扩展,支持百炼模型、图片、音频、向量检索、工具调用等能力。

版本迭代较快,Spring AI Alibaba 与 Spring AI 2.0 的兼容版本需要以官方版本说明为准;不要把不同时间的 BOM 随意混用。

基础依赖示例:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-bom</artifactId>
            <version>2.0.0-M1.1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>com.alibaba.cloud.ai</groupId>
        <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
    </dependency>
</dependencies>

配置 API Key:

spring:
  ai:
    dashscope:
      api-key: ${AI_DASHSCOPE_API_KEY}

普通对话:

@Test
void testQwen(@Autowired DashScopeChatModel chatModel) {
    String result = chatModel.call("你好,请介绍一下 Spring AI");
    System.out.println(result);
}

4.1 文生图

@Test
void textToImage(@Autowired DashScopeImageModel imageModel) {
    DashScopeImageOptions options = DashScopeImageOptions.builder()
            .withModel("wanx2.1-t2i-turbo")
            .build();

    ImageResponse response = imageModel.call(
            new ImagePrompt("一位正在编写 Java AI 应用的程序员,科技插画风格", options)
    );

    String imageUrl = response.getResult().getOutput().getUrl();
    System.out.println(imageUrl);
}

4.2 文生语音

@Test
void textToAudio(
        @Autowired DashScopeSpeechSynthesisModel speechModel
) throws IOException {

    SpeechSynthesisResponse response = speechModel.call(
            new SpeechSynthesisPrompt("大家好,我是程序员GC。")
    );

    ByteBuffer audio = response.getResult().getOutput().getAudio();

    try (FileOutputStream output =
                 new FileOutputStream("output.mp3")) {
        output.write(audio.array());
    }
}

4.3 语音转文字

@Test
void audioToText(
        @Autowired DashScopeAudioTranscriptionModel model
) throws MalformedURLException {

    Resource audio = new UrlResource(
            "https://example.com/demo.wav"
    );

    AudioTranscriptionResponse response = model.call(
            new AudioTranscriptionPrompt(audio)
    );

    System.out.println(response.getResult().getOutput());
}

4.4 多模态图片理解

@Test
void testMultimodal(
        @Autowired DashScopeChatModel chatModel
) {

    Resource image = new ClassPathResource("images/demo.png");

    Media media = new Media(
            MimeTypeUtils.IMAGE_PNG,
            image
    );

    Prompt prompt = Prompt.builder()
            .messages(
                    UserMessage.builder()
                            .text("识别图片中的内容,并给出简短说明。")
                            .media(media)
                            .build()
            )
            .build();

    ChatResponse response = chatModel.call(prompt);

    System.out.println(
            response.getResult().getOutput().getText()
    );
}

百炼相关的图片、语音与多模态模型名称会持续变化,上线前应在百炼控制台确认当前可用模型与计费规则。


5. 接入 Ollama:本地模型与私有化部署

Ollama 是本地运行大模型的工具。它适合数据不出网、内网知识库、离线开发或模型实验场景。

安装地址:Ollama Download

# 查看本地模型
ollama list

# 拉取并运行模型
ollama run qwen3:4b

4b 表示约 40 亿参数,但不等于“只要 4GB 显存”。实际资源消耗还取决于量化精度、上下文长度、系统内存、GPU 类型和并发数。

添加依赖:

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-ollama</artifactId>
</dependency>

配置:

spring:
  ai:
    ollama:
      base-url: http://localhost:11434
      chat:
        model: qwen3:4b
        temperature: 0.5
        think: false

普通调用:

@SpringBootTest
class OllamaTest {

    @Test
    void testChat(@Autowired OllamaChatModel chatModel) {
        String result = chatModel.call("你是谁?");
        System.out.println(result);
    }

    @Test
    void testStream(@Autowired OllamaChatModel chatModel) {
        chatModel.stream("介绍一下 Java 虚拟线程。")
                .toIterable()
                .forEach(System.out::print);
    }
}

对于支持推理能力的模型,优先使用 think: false 之类的模型参数控制,而不是依赖 Prompt 中的 /no_think。提示词指令并非所有模型都保证支持。

5.1 Ollama 多模态

以支持视觉的模型为例:

@Test
void testVision(@Autowired OllamaChatModel chatModel) {
    Resource image = new ClassPathResource("images/architecture.png");

    Media media = new Media(
            MimeTypeUtils.IMAGE_PNG,
            image
    );

    OllamaChatOptions options = OllamaChatOptions.builder()
            .model("gemma3")
            .build();

    ChatResponse response = chatModel.call(
            new Prompt(
                    UserMessage.builder()
                            .text("请解释这张架构图。")
                            .media(media)
                            .build(),
                    options
            )
    );

    System.out.println(
            response.getResult().getOutput().getText()
    );
}

Ollama 支持哪些多模态模型,以其模型库和本地已安装版本为准。Spring AI 当前的 Ollama 集成说明见:官方文档

在这里插入图片描述


最后总结

  • 如果你只是想快速跑通聊天能力:
    选择 DeepSeek API,使用 DeepSeekChatModelChatClient

  • 如果你需要图片、语音、多模态与国内云模型生态:
    选择阿里百炼与 Spring AI Alibaba,但注意 BOM 与 Spring AI 的版本兼容。

  • 如果业务数据不能出网:
    使用 Ollama 本地部署,先测清楚模型、量化、硬件和并发的真实资源消耗。

Spring AI 的核心价值,不是替你写一条 HTTP 请求,而是用统一抽象把模型、流式响应、工具、RAG、MCP 与可观测性纳入 Spring 工程体系。

参考资料 & 致谢

[1] Spring AI 官方项目
[2] Spring AI DeepSeek Chat 文档
[3] Spring AI Ollama Chat 文档
[4] DeepSeek 开放平台
[5] Spring AI Alibaba
[6] 阿里云百炼
[7] Ollama 官网

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

OxYGC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值