第3天 | Spring AI Alibaba 第一个 AI 应用:ChatModel 基础使用

系列导读:本文是《Spring AI Alibaba 30 天技术博客》系列的第 3 篇。在了解了 Spring AI Alibaba 的整体架构和 Model 体系之后,今天我们将动手写第一个真正的 AI 应用——使用 ChatModel 完成与通义千问的对话交互。


📋 目录


1. 本章目标

读完本章,你将能够:

  • ✅ 从零搭建一个可运行的 Spring AI Alibaba 项目
  • ✅ 使用 ChatModel 完成基本的对话调用
  • ✅ 理解 Prompt、Message、ChatResponse 的核心概念
  • ✅ 掌握同步、异步、流式三种调用方式
  • ✅ 灵活配置模型参数(temperature、maxTokens 等)
  • ✅ 构建一个完整的”智能翻译助手”Web 应用
  • ✅ 排查常见的 API 调用错误

2. 创建 Spring Boot 项目

我们使用 Spring Boot 3.3+ 配合 Spring AI Alibaba 1.0.0+。推荐使用 start.aliyun.com 初始化项目。

<!-- pom.xml 核心依赖 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.3.5</version>
</parent>

<dependencies>
    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Spring AI Alibaba Starter -->
    <dependency>
        <groupId>com.alibaba.cloud.ai</groupId>
        <artifactId>spring-ai-alibaba-starter</artifactId>
        <version>1.0.0-M5.1</version>
    </dependency>

    <!-- Lombok(可选,简化代码) -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

版本提示:Spring AI Alibaba 的版本迭代较快,请前往 GitHub Releases 确认最新版本。本文基于 1.0.0-M5.1 编写。


3. 最小可用的 ChatModel 调用

3.1 配置文件

application.yml 中配置通义千问的 API Key:

spring:
  ai:
    dashscope:
      api-key: ${DASHSCOPE_API_KEY:sk-your-api-key-here}
      chat:
        options:
          model: qwen-plus
          temperature: 0.7
          max-tokens: 2048

安全提醒:切勿将 API Key 硬编码在代码中!使用环境变量或 Spring Cloud Config 等配置中心管理。

3.2 最简代码

只需三行核心代码,就能完成一次 AI 对话:

import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class MinimalChatRunner implements CommandLineRunner {

    private final ChatModel chatModel;

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

    @Override
    public void run(String... args) {
        // 一行代码完成 AI 对话
        String response = chatModel.call("请用一句话解释什么是 Spring AI?");
        System.out.println("AI 回复:" + response);
    }
}

3.3 运行结果

AI 回复:Spring AI 是一个基于 Spring 框架的 AI 集成库,它提供了统一的编程模型,
让开发者可以轻松地将各种 AI 模型(如 OpenAI、通义千问等)集成到 Spring Boot 应用中,
实现自然语言处理、图像生成等 AI 能力。

这三行代码背后,Spring AI Alibaba 自动完成了以下工作:

  1. 将字符串包装为 Prompt 对象
  2. Prompt 序列化为 DashScope API 所需的 JSON 格式
  3. 发起 HTTP 请求到通义千问服务
  4. 解析响应,提取回复文本
  5. 返回结果

4. ChatModel 接口详解

ChatModel 是 Spring AI 中最核心的接口,定义了三种调用方式:

public interface ChatModel {

    // 同步调用:阻塞等待完整响应
    String call(String prompt);

    // 同步调用(完整版):返回 ChatResponse,包含更多元数据
    ChatResponse call(Prompt prompt);

    // 流式调用:返回 Flux<ChatResponse>,逐字输出
    Flux<ChatResponse> stream(Prompt prompt);

    // 异步调用:返回 CompletableFuture<ChatResponse>
    default CompletableFuture<ChatResponse> async(Prompt prompt) {
        // 默认实现...
    }
}

4.1 call():同步调用

// 方式一:简单字符串(适合快速测试)
String result = chatModel.call("你好,介绍一下你自己");

// 方式二:Prompt 对象(生产推荐)
Prompt prompt = new Prompt("你好,介绍一下你自己");
ChatResponse response = chatModel.call(prompt);
String result = response.getResult().getOutput().getText();

适用场景:简单的问答、翻译、摘要等,响应时间在 1~5 秒内可接受的情况。

注意事项

  • 同步调用会阻塞当前线程,直到收到完整响应
  • 如果模型响应较慢(如生成长文),可能导致 HTTP 超时
  • 生产环境中建议配合 @Async 或消息队列使用

4.2 stream():流式调用

流式调用是提升用户体验的关键技术。用户在模型生成过程中就能看到文字逐字出现,而不是等待数秒后一次性显示全部内容。

import org.springframework.ai.chat.model.ChatResponse;
import reactor.core.publisher.Flux;

// 流式调用示例
public void streamChat(String question) {
    Prompt prompt = new Prompt(question);
    Flux<ChatResponse> responseFlux = chatModel.stream(prompt);

    responseFlux
        .map(response -> response.getResult().getOutput().getText())
        .filter(text -> text != null && !text.isEmpty())
        .doOnNext(chunk -> System.out.print(chunk))  // 逐字打印
        .doOnError(error -> System.err.println("流式调用出错:" + error.getMessage()))
        .doOnComplete(() -> System.out.println("\n[生成完成]"))
        .blockLast();  // 等待流完成
}

在 WebFlux 中直接使用

@RestController
@RequestMapping("/api/chat")
public class ChatController {

    private final ChatModel chatModel;

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

    // 返回 Server-Sent Events (SSE),前端可直接用 EventSource 接收
    @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> streamChat(@RequestParam String message) {
        return chatModel.stream(new Prompt(message))
            .map(response -> response.getResult().getOutput().getText())
            .filter(text -> text != null && !text.isEmpty());
    }
}

适用场景

  • 聊天界面需要实时显示生成过程
  • 生成长文本(文章、代码等),用户希望尽早看到开头
  • 需要实时进度反馈的场景

4.3 async():异步调用

import java.util.concurrent.CompletableFuture;

public CompletableFuture<String> asyncChat(String question) {
    Prompt prompt = new Prompt(question);
    return chatModel.async(prompt)
        .thenApply(response -> response.getResult().getOutput().getText());
}

// 使用示例
asyncChat("请写一首关于春天的诗")
    .thenAccept(poem -> System.out.println("异步回复:" + poem))
    .exceptionally(ex -> {
        System.err.println("异步调用失败:" + ex.getMessage());
        return null;
    });

适用场景

  • 需要并发调用多个 AI 请求
  • 不阻塞主线程的后台任务
  • 批量处理场景

5. Prompt 构建完整指南

Prompt 是 AI 应用的灵魂。在 Spring AI 中,Prompt 对象是构建请求的核心载体。

5.1 简单文本 Prompt

// 最简单的方式
Prompt prompt = new Prompt("请列出 Java 17 的 5 个新特性");

5.2 多角色消息 Prompt

真实的对话场景中,我们需要区分不同角色的消息:

import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;

List<Message> messages = List.of(
    // 系统消息:设定 AI 的角色和行为准则
    new SystemMessage("你是一位资深的 Java 技术专家,擅长用通俗易懂的方式解释技术概念。"),

    // 历史对话:模拟多轮对话上下文
    new UserMessage("什么是函数式编程?"),
    new AssistantMessage("函数式编程是一种编程范式,它将计算视为数学函数的求值..."),

    // 当前用户输入
    new UserMessage("那它和面向对象编程有什么区别?")
);

Prompt prompt = new Prompt(messages);
ChatResponse response = chatModel.call(prompt);

5.3 带系统提示的对话

这是最常见的生产模式——通过 SystemMessage 设定 AI 的角色、语气、输出格式等:

public String translateWithRole(String text, String targetLang) {
    List<Message> messages = List.of(
        new SystemMessage(String.format("""
            你是一位专业的翻译专家。请遵守以下规则:
            1. 仅输出翻译结果,不要解释
            2. 保持原文的语气和风格
            3. 专业术语使用业界通用译法
            4. 目标语言:%s
            """, targetLang)),
        new UserMessage(text)
    );

    return chatModel.call(new Prompt(messages));
}

5.4 带参数的 Prompt

Spring AI 支持使用 PromptTemplate 进行参数化模板填充,这在需要动态替换内容的场景中非常有用:

import org.springframework.ai.chat.prompt.PromptTemplate;
import java.util.Map;

// 模板字符串,使用 {variableName} 占位
String templateText = """
    请分析以下代码的问题,并给出修复建议:

    语言:{language}
    代码:
    ```
    {code}
    ```

    请从以下维度分析:
    1. 性能问题
    2. 安全隐患
    3. 可读性改进
    """;

PromptTemplate promptTemplate = new PromptTemplate(templateText);
Prompt prompt = promptTemplate.create(Map.of(
    "language", "Java",
    "code", """
        public class UserService {
            public User findUserById(Long id) {
                String sql = "SELECT * FROM users WHERE id = " + id;
                return jdbcTemplate.queryForObject(sql, User.class);
            }
        }
        """
));

ChatResponse response = chatModel.call(prompt);

模板引擎:除了简单的 {variable} 替换,Spring AI 还支持更复杂的模板语法。我们将在第4天详细讲解 Prompt 工程与模板引擎。


6. ChatOptions 参数配置

模型参数直接影响 AI 的输出质量。理解并正确配置这些参数是写出好应用的关键。

6.1 基础参数

参数类型默认值说明
modelStringqwen-plus使用的模型名称
temperatureDouble0.7创造性程度。0=确定性输出,1=高度随机
maxTokensInteger2048最大输出 token 数
topPDouble0.8核采样阈值,控制输出多样性
topKInteger50采样时考虑的 Top-K 候选数
stopSequencesList<String>[]遇到这些序列时停止生成
presencePenaltyDouble0.0降低重复话题的倾向(-2.0 ~ 2.0)
frequencyPenaltyDouble0.0降低重复词汇的倾向(-2.0 ~ 2.0)

6.2 通过配置类设置

import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AIConfig {

    @Bean
    public DashScopeChatOptions chatOptions() {
        return DashScopeChatOptions.builder()
            .withModel("qwen-plus")
            .withTemperature(0.7)
            .withMaxTokens(4096)
            .withTopP(0.8)
            .withTopK(50)
            .build();
    }
}

6.3 运行时动态覆盖

有时不同请求需要不同的参数。例如:创意写作需要高 temperature,代码生成需要低 temperature。Spring AI 允许在每次调用时覆盖默认配置:

import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;

// 创意写作模式:高创造性
public String creativeWriting(String topic) {
    ChatOptions options = DashScopeChatOptions.builder()
        .withModel("qwen-plus")
        .withTemperature(0.9)   // 高创造性
        .withTopP(0.95)
        .withMaxTokens(4096)
        .build();

    Prompt prompt = new Prompt(
        new UserMessage("请以「" + topic + "」为主题,写一篇散文"),
        options  // 运行时覆盖配置
    );

    return chatModel.call(prompt);
}

// 代码生成模式:高精确性
public String codeGeneration(String description) {
    ChatOptions options = DashScopeChatOptions.builder()
        .withModel("qwen-plus")
        .withTemperature(0.1)   // 低创造性,高精确性
        .withTopP(0.5)
        .withMaxTokens(8192)    // 代码可能很长
        .build();

    Prompt prompt = new Prompt(
        new UserMessage("请实现以下功能:\n" + description),
        options
    );

    return chatModel.call(prompt);
}

7. 响应解析:从 ChatResponse 中提取信息

完整的 ChatResponse 包含丰富的元数据,不只是回复文本那么简单。

Prompt prompt = new Prompt("请解释什么是微服务");
ChatResponse response = chatModel.call(prompt);

// === 1. 获取回复文本 ===
String text = response.getResult().getOutput().getText();
System.out.println("回复:" + text);

// === 2. 获取 Token 用量 ===
Usage usage = response.getMetadata().getUsage();
System.out.println("Prompt Tokens: " + usage.getPromptTokens());
System.out.println("Completion Tokens: " + usage.getCompletionTokens());
System.out.println("Total Tokens: " + usage.getTotalTokens());

// === 3. 获取 Finish Reason ===
String finishReason = response.getResult().getMetadata().getFinishReason();
System.out.println("结束原因:" + finishReason);
// 可能的值:
// - "stop":正常完成
// - "length":达到了 maxTokens 限制
// - "content_filter":触发了内容安全过滤

// === 4. 获取模型信息 ===
String model = response.getMetadata().getModel();
System.out.println("使用模型:" + model);

// === 5. 获取 ID ===
String id = response.getMetadata().getId();
System.out.println("请求 ID:" + id);

7.1 获取回复文本

最常见的操作——直接获取 AI 的回复内容:

String result = chatModel.call("你好");
// 等价于:
ChatResponse response = chatModel.call(new Prompt("你好"));
String result2 = response.getResult().getOutput().getText();

7.2 获取 Token 用量

Token 用量监控对于成本控制至关重要:

import org.springframework.ai.chat.metadata.Usage;

public void logUsage(Prompt prompt, ChatResponse response) {
    Usage usage = response.getMetadata().getUsage();

    // 记录到日志系统
    log.info("AI 调用 - PromptTokens: {}, CompletionTokens: {}, TotalTokens: {}",
        usage.getPromptTokens(),
        usage.getCompletionTokens(),
        usage.getTotalTokens()
    );

    // 可以用于计费统计
    // double cost = calculateCost(usage.getTotalTokens());
}

7.3 获取 Finish Reason

判断 AI 是否完整回答了你的问题:

String finishReason = response.getResult().getMetadata().getFinishReason();

switch (finishReason) {
    case "stop" -> System.out.println("正常完成");
    case "length" -> {
        System.out.println("回答被截断!考虑增加 maxTokens 参数");
        // 可以继续发送请求让 AI 补充
    }
    case "content_filter" -> {
        System.out.println("触发了内容安全过滤");
        // 记录并处理
    }
    default -> System.out.println("未知结束原因:" + finishReason);
}

8. 完整实战案例:智能翻译助手

让我们把前面学到的所有知识整合起来,构建一个功能完整的智能翻译 Web 应用。

8.1 项目结构

src/main/java/com/example/translator/
├── TranslatorApplication.java      # 启动类
├── config/
│   └── AIConfig.java              # AI 配置
├── service/
│   └── TranslationService.java    # 翻译业务逻辑
├── controller/
│   └── TranslationController.java # REST API
└── dto/
    ├── TranslationRequest.java    # 请求 DTO
    └── TranslationResponse.java   # 响应 DTO

8.2 配置类

package com.example.translator.config;

import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AIConfig {

    /**
     * 翻译场景的专用配置
     * 翻译任务需要精确性,设置较低的 temperature
     */
    @Bean("translationOptions")
    public DashScopeChatOptions translationOptions() {
        return DashScopeChatOptions.builder()
            .withModel("qwen-plus")
            .withTemperature(0.2)    // 翻译需要精确,低创造性
            .withTopP(0.5)
            .withMaxTokens(4096)
            .build();
    }
}

8.3 Service 层

package com.example.translator.service;

import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;

@Service
public class TranslationService {

    private final ChatModel chatModel;
    private final DashScopeChatOptions translationOptions;

    // 支持的语言对
    private static final Map<String, String> LANG_PAIRS = Map.of(
        "zh-en", "中文 -> 英文",
        "en-zh", "英文 -> 中文",
        "zh-ja", "中文 -> 日文",
        "ja-zh", "日文 -> 中文",
        "zh-ko", "中文 -> 韩文",
        "en-ja", "英文 -> 日文"
    );

    public TranslationService(
            ChatModel chatModel,
            @Qualifier("translationOptions") DashScopeChatOptions translationOptions) {
        this.chatModel = chatModel;
        this.translationOptions = translationOptions;
    }

    /**
     * 执行翻译
     */
    public TranslationResult translate(String text, String langPair) {
        if (!LANG_PAIRS.containsKey(langPair)) {
            throw new IllegalArgumentException("不支持的语言对:" + langPair);
        }

        String langDirection = LANG_PAIRS.get(langPair);
        String[] langs = langPair.split("-");
        String targetLangName = getLangName(langs[1]);

        // 构建系统提示
        SystemMessage systemMessage = new SystemMessage(buildSystemPrompt(langDirection, targetLangName));

        // 构建用户消息
        UserMessage userMessage = new UserMessage(text);

        // 构建 Prompt
        Prompt prompt = new Prompt(
            List.of(systemMessage, userMessage),
            translationOptions
        );

        // 调用 AI
        ChatResponse response = chatModel.call(prompt);

        // 提取结果
        String translatedText = response.getResult().getOutput().getText();
        int promptTokens = response.getMetadata().getUsage().getPromptTokens();
        int completionTokens = response.getMetadata().getUsage().getCompletionTokens();

        return new TranslationResult(
            text,
            translatedText,
            langDirection,
            promptTokens,
            completionTokens
        );
    }

    /**
     * 构建系统提示词
     */
    private String buildSystemPrompt(String langDirection, String targetLangName) {
        return """
            你是一位专业的翻译专家,精通 %s 的互译。请遵守以下规则:

            1. 仅输出翻译结果,不要添加任何解释或额外内容
            2. 保持原文的语气、风格和情感
            3. 专有名词、品牌名、技术术语保留原文或使用业界通用译法
            4. 如果原文有多个段落,逐段翻译
            5. 如果遇到无法确定的内容,使用 [?] 标记
            6. 不要对原文内容进行评判或补充说明

            当前翻译方向:%s
            请确保翻译准确、流畅、自然。
            """.formatted(langDirection, targetLangName);
    }

    private String getLangName(String code) {
        return Map.of(
            "zh", "中文",
            "en", "英文",
            "ja", "日文",
            "ko", "韩文"
        ).getOrDefault(code, code);
    }

    /**
     * 翻译结果 DTO
     */
    public record TranslationResult(
        String originalText,
        String translatedText,
        String langDirection,
        int promptTokens,
        int completionTokens
    ) {}
}

8.4 Controller 层

package com.example.translator.controller;

import com.example.translator.service.TranslationService;
import com.example.translator.service.TranslationService.TranslationResult;
import jakarta.validation.constraints.NotBlank;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@RestController
@RequestMapping("/api/translate")
public class TranslationController {

    private final TranslationService translationService;

    public TranslationController(TranslationService translationService) {
        this.translationService = translationService;
    }

    /**
     * 翻译接口
     * POST /api/translate
     * Body: { "text": "你好世界", "langPair": "zh-en" }
     */
    @PostMapping
    public ResponseEntity<Map<String, Object>> translate(
            @RequestBody TranslationRequest request) {

        TranslationResult result = translationService.translate(
            request.text(),
            request.langPair()
        );

        return ResponseEntity.ok(Map.of(
            "success", true,
            "data", Map.of(
                "original", result.originalText(),
                "translated", result.translatedText(),
                "direction", result.langDirection(),
                "tokens", Map.of(
                    "prompt", result.promptTokens(),
                    "completion", result.completionTokens(),
                    "total", result.promptTokens() + result.completionTokens()
                )
            )
        ));
    }

    /**
     * 获取支持的语言对
     * GET /api/translate/languages
     */
    @GetMapping("/languages")
    public ResponseEntity<Map<String, String>> getLanguages() {
        return ResponseEntity.ok(Map.of(
            "zh-en", "中文 → 英文",
            "en-zh", "英文 → 中文",
            "zh-ja", "中文 → 日文",
            "ja-zh", "日文 → 中文",
            "zh-ko", "中文 → 韩文",
            "en-ja", "英文 → 日文"
        ));
    }

    /**
     * 请求 DTO
     */
    public record TranslationRequest(
        @NotBlank(message = "翻译文本不能为空") String text,
        @NotBlank(message = "语言对不能为空") String langPair
    ) {}
}

8.5 测试验证

# 1. 查看支持的语言
curl http://localhost:8080/api/translate/languages

# 2. 执行翻译
curl -X POST http://localhost:8080/api/translate \
  -H "Content-Type: application/json" \
  -d '{
    "text": "人工智能正在改变世界",
    "langPair": "zh-en"
  }'

# 响应:
# {
#   "success": true,
#   "data": {
#     "original": "人工智能正在改变世界",
#     "translated": "Artificial intelligence is changing the world.",
#     "direction": "中文 -> 英文",
#     "tokens": {
#       "prompt": 45,
#       "completion": 12,
#       "total": 57
#     }
#   }
# }

9. 常见错误排查

错误 1:API Key 无效

com.alibaba.dashscope.exception.ApiException: Invalid API-key provided.

解决方案

  1. 确认环境变量 DASHSCOPE_API_KEY 已正确设置
  2. 前往 DashScope 控制台 确认 Key 状态
  3. 检查 application.yml 中的配置路径是否正确

错误 2:模型不存在

Model not found: qwen-turbo-pro

解决方案

  1. 确认模型名称拼写正确(常见模型:qwen-turboqwen-plusqwen-max
  2. 确认你的账户有权限访问该模型
  3. 参考 DashScope 模型列表

错误 3:Token 超限

This model's maximum context length is 8192 tokens.

解决方案

  1. 减少 Prompt 中的输入内容
  2. 如果对话历史过长,进行摘要或截断
  3. 切换到支持更大上下文的模型(如 qwen-max 支持 32768 tokens)

错误 4:超时

java.net.SocketTimeoutException: Read timed out

解决方案

# 在 application.yml 中增加超时配置
spring:
  ai:
    dashscope:
      timeout:
        connect: 30s
        read: 120s

错误 5:Finish Reason 为 “length”

这说明回答被截断了。解决方案:

  • 增加 maxTokens 参数
  • 使用流式调用,并在截断处继续生成(发送包含已生成内容的 Prompt)

10. 最佳实践总结

🎯 调用方式选择

场景推荐方式原因
简单问答、后台批处理call()实现简单,逻辑清晰
聊天界面、长文生成stream()用户体验好,减少等待感
并发处理、异步任务async()不阻塞主线程
实时推荐、低延迟场景stream() + 缓存结合缓存减少实际调用

🎯 Prompt 设计原则

  1. 角色明确:始终使用 SystemMessage 设定 AI 角色
  2. 规则具体:规则越具体,输出越稳定(”仅输出 JSON” 比 “简洁回答” 好)
  3. 格式固定:输出格式要求用明确的格式说明(JSON Schema、Markdown 等)
  4. 上下文管理:控制对话历史长度,避免超过模型上下文窗口

🎯 参数调优建议

场景temperaturetopPmaxTokens
代码生成0.1~0.20.3~0.54096~8192
翻译0.2~0.30.5~0.72048~4096
数据分析0.1~0.30.5~0.72048~4096
创意写作0.7~0.90.8~0.954096~8192
头脑风暴0.8~1.00.9~1.02048~4096
聊天对话0.6~0.80.7~0.92048

🎯 生产环境建议

  1. 使用 @Qualifier 区分多个 ChatModel Bean(当你配置了多个模型时)
  2. 记录每次调用的 Token 用量,用于成本核算
  3. 设置合理的超时时间,避免无限等待
  4. 实现重试机制(我们将在后续章节详细介绍)
  5. 监控 API 响应时间和错误率

11. 总结

今天我们完成了 Spring AI Alibaba 的第一个完整应用。我们从最小化的三行代码出发,逐步深入到:

  • ✅ ChatModel 的三种调用方式(同步、异步、流式)
  • ✅ Prompt 的多种构建方式(简单文本、多角色消息、参数化模板)
  • ✅ ChatOptions 参数配置(全局配置 + 运行时覆盖)
  • ✅ ChatResponse 元数据提取(Token 用量、Finish Reason)
  • ✅ 完整的智能翻译助手实战项目

核心要点回顾

  1. 一行代码即可开始chatModel.call("你的问题") 是最简单的入口
  2. 生产使用 Prompt 对象:带 SystemMessage 的 Prompt 能获得更稳定的输出
  3. 流式调用是用户体验的关键stream() 返回 Flux<ChatResponse>,逐字输出
  4. 参数决定输出质量:不同场景需要不同的 temperature 和 maxTokens
  5. Token 用量需要监控response.getMetadata().getUsage() 获取用量数据

在下一篇文章中,我们将深入探讨 Prompt 工程与模板引擎,学习如何设计高质量的 Prompt、使用 PromptTemplate 进行参数化模板填充,以及构建可复用的 Prompt 组件。