二、Spring AI Alibaba · ChatModel

整理时间:2026-10-09

适用版本:Spring AI 1.1.0 / Spring AI Alibaba 1.1.2.x(spring-ai-alibaba-starter-dashscope)


一、整体定位

ChatModel API 的作用:把"发一段自然语言给模型、拿回一段补全文本"这件事标准化。

  • 它建立在 Generic Model API 之上,提供 Chat 特有的抽象;
  • 通过 Prompt(输入封装)与 ChatResponse(输出封装)统一与各类 AI 模型的通信;
  • 屏蔽请求拼装与响应解析的复杂度,换模型只需换 Starter + 改配置,业务代码基本不动。

一句话记忆:「ChatModel 吃 Prompt,吐 ChatResponse」。


二、Generic Model API(地基)

java 复制代码
public interface Model<TReq extends ModelRequest<?>, TRes extends ModelResponse<?>> {
    TRes call(TReq request);
}

public interface StreamingModel<TReq extends ModelRequest<?>, TResChunk extends ModelResponse<?>> {
    Flux<TResChunk> stream(TReq request);
}

四个配套抽象:

接口 职责 关键方法
ModelRequest<T> 封装请求 T getInstructions()(输入)、ModelOptions getOptions()(参数)
ModelOptions 可定制选项 标记接口,无方法
ModelResponse<T extends ModelResult<?>> 封装响应 getResult()、getResults()、getMetadata()
ModelResult<T> 单个结果 T getOutput()、getMetadata()

泛型是这套 API 的灵魂:Model<Prompt, ChatResponse> 就是 ChatModel 的精确描述。


三、Chat Model API

3.1 两个核心接口

java 复制代码
public interface ChatModel extends Model<Prompt, ChatResponse>, StreamingChatModel {
    default String call(String message) { ... }   // 便捷版
    @Override ChatResponse call(Prompt prompt);   // 正式版
}

public interface StreamingChatModel extends StreamingModel<Prompt, ChatResponse> {
    default Flux<String> stream(String message) { ... }
    @Override Flux<ChatResponse> stream(Prompt prompt);
}
  • call(String) / stream(String):快速尝鲜用,返回纯文本,拿不到元数据和 token 用量。
  • call(Prompt) / stream(Prompt):**生产使用」,可携带 System/多轮消息 + 运行时选项。

3.2 Prompt

java 复制代码
public class Prompt implements ModelRequest<List<Message>> {
    private final List<Message> messages;
    private ChatOptions modelOptions;
    // getInstructions() → messages;getOptions() → modelOptions
}

Prompt = 消息列表 + 可选 ChatOptions 。多轮对话就是把历史一条条塞进 messages。

3.3 Message 体系

java 复制代码
public interface Content { String getText(); Map<String, Object> getMetadata(); }
public interface Message extends Content { MessageType getMessageType(); }
public interface MediaContent extends Content { Collection<Media> getMedia(); }  // 多模态
实现类 角色 场景
SystemMessage system 人设、规则、输出格式约束
UserMessage user 用户输入;对无角色概念的模型,作为标准类别兜底
AssistantMessage assistant 模型回复,也是流式输出的载体
FunctionMessage function 函数调用结果(旧式)
ToolResponseMessage tool 工具执行结果(Agent 场景)

MessageType 不是数据格式,而是这条消息在对话中扮演的角色。

3.4 ChatOptions(可移植选项)

java 复制代码
public interface ChatOptions extends ModelOptions {
    String  getModel();
    Float   getFrequencyPenalty();  // -2.0~2.0,降低重复 token
    Integer getMaxTokens();         // 最大生成 token
    Float   getPresencePenalty();   // -2.0~2.0,鼓励谈新主题
    List<String> getStopSequences();// 停止序列
    Float   getTemperature();       // 0.0~2.0,采样温度
    Integer getTopK();              // Top-K 采样
    Float   getTopP();              // Top-P 核采样
    ChatOptions copy();
}

各厂商实现可追加私有选项(如 OpenAI 的 logitBias、seed、user),这就是"统一接口 + 厂商扩展"的设计。

3.5 选项合并流程(重要)

复制代码
① 启动配置    ChatModel 初始化时设置 defaultOptions(全局默认)
      ↓
② 运行时配置  Prompt 里携带的 ChatOptions(本次请求)
      ↓
③ 合并        merge:运行时选项 优先 于 启动选项
      ↓
④ 转换输入    转成厂商原生的请求格式(如 DashScope 的 payload)
      ↓
⑤ 转换输出    厂商响应 → 标准化 ChatResponse

意义 :全局一套默认参数,个别请求可临时微调(defaultOptions + Prompt(messages, runtimeOptions))。本项目正是这样:全局 application.yml 配 enable-thinking,每次调用在 Prompt 里单独传 temperature(0.7)。

3.6 ChatResponse / Generation

java 复制代码
public class ChatResponse implements ModelResponse<Generation> {
    private final ChatResponseMetadata chatResponseMetadata;  // token 用量、速率限制等
    private final List<Generation> generations;               // n 个候选结果
}
public class Generation implements ModelResult<AssistantMessage> {
    private final AssistantMessage assistantMessage;
    private ChatGenerationMetadata chatGenerationMetadata;    // 结束原因等
}

取值链路(必须记牢):

java 复制代码
String text = chatResponse.getResult()      // Generation(第一个候选)
                          .getOutput()      // AssistantMessage
                          .getText();       // 文本

getResults() 拿全部候选(对应 n>1),getResult() 是 getResults().get(0) 的快捷方式。


四、支持的模型提供商

全部走统一的 ChatModel / StreamingChatModel 接口:

提供商 流式 多模态 函数调用
OpenAI Chat Completion ✓ ✓ ✓
Azure OpenAI ✓ --- ✓
Alibaba DashScope ✓ --- ✓
Ollama ✓ ✓ ✓
Hugging Face ✗ --- ---
Google Vertex AI Gemini ✓ ✓ ✓
Amazon Bedrock --- --- ---
Mistral AI ✓ --- ✓
Anthropic ✓ --- ✓

五、DashScopeChatModel(通义千问)

5.1 前置条件

bash 复制代码
export AI_DASHSCOPE_API_KEY=your_api_key   # 阿里云百炼申请
xml 复制代码
<dependency>
    <groupId>com.alibaba.cloud.ai</groupId>
    <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
    <version>1.1.2.1</version>
</dependency>

5.2 创建与调用

java 复制代码
DashScopeApi dashScopeApi = DashScopeApi.builder()
    .apiKey(System.getenv("AI_DASHSCOPE_API_KEY"))
    .build();

ChatModel chatModel = DashScopeChatModel.builder()
    .dashScopeApi(dashScopeApi)
    .build();

// 简化调用
String r1 = chatModel.call("介绍一下Spring框架");

// 正式调用
Prompt prompt = new Prompt(new UserMessage("解释什么是微服务架构"));
ChatResponse response = chatModel.call(prompt);
String answer = response.getResult().getOutput().getText();

在 Spring Boot 中不需要手写 builder :引入 starter 后自动装配 DashScopeChatModel,直接 @Autowired/构造器注入即可(本项目就是构造器注入方式)。

5.3 启动选项 vs 运行时选项

java 复制代码
// 启动选项:全局默认
DashScopeChatOptions options = DashScopeChatOptions.builder()
    .withModel("qwen-plus")
    .withTemperature(0.7)
    .withMaxToken(2000)
    .withTopP(0.9)
    .build();
ChatModel chatModel = DashScopeChatModel.builder()
    .dashScopeApi(dashScopeApi).defaultOptions(options).build();

// 运行时选项:覆盖默认值
DashScopeChatOptions runtimeOptions = DashScopeChatOptions.builder()
    .withTemperature(0.3)   // 更确定、更适合评分/抽取
    .withMaxToken(500)
    .build();
Prompt prompt = new Prompt(new UserMessage("用一句话总结Java的特点"), runtimeOptions);
ChatResponse response = chatModel.call(prompt);

5.4 流式响应

java 复制代码
Flux<ChatResponse> responseStream = chatModel.stream(
        new Prompt("详细解释Spring Boot的自动配置原理"));

responseStream.subscribe(
    chatResponse -> System.out.print(chatResponse.getResult().getOutput().getText()),
    error        -> System.err.println("错误: " + error.getMessage()),
    ()           -> System.out.println("\n流式响应完成")
);

5.5 多轮对话

java 复制代码
List<Message> messages = List.of(
    new SystemMessage("你是一个Java专家"),
    new UserMessage("什么是Spring Boot?"),
    new AssistantMessage("Spring Boot是..."),   // 历史回复要显式回填
    new UserMessage("它有什么优势?")
);
ChatResponse response = chatModel.call(new Prompt(messages));

ChatModel 是无状态 的,上下文靠你自己维护消息列表;需要自动记忆请用 ChatMemory 或 ReactAgent 的 Saver。

5.6 函数调用(Function Calling)

java 复制代码
ToolCallback weatherFunction = FunctionToolCallback.builder("getWeather", (String city) -> "晴朗,25°C")
    .description("获取指定城市的天气")      // 描述决定模型何时调用,必须写准
    .inputType(String.class)
    .build();

DashScopeChatOptions options = DashScopeChatOptions.builder()
    .withToolCallbacks(List.of(weatherFunction))
    .build();

ChatResponse response = chatModel.call(new Prompt("北京的天气怎么样?", options));

5.7 支持的模型

模型 特点
qwen-turbo 速度快、成本低,适合高频短任务
qwen-plus 增强版,综合性价比高
qwen-max 旗舰版,复杂推理最强
qwen-max-longcontext 长文本场景

选型建议 :出题/创作类用 plus;简历评分、答案评估这类需要稳定判定 + 长 JSON 输出的场景,建议 qwen-max + 低温度(0.2~0.3)。

5.8 与 ReactAgent 集成

java 复制代码
ReactAgent agent = ReactAgent.builder()
    .name("my_agent")
    .model(chatModel)                       // 复用同一个 ChatModel 实例
    .systemPrompt("你是一个有帮助的AI助手")
    .build();
AssistantMessage response = agent.call("帮我分析这个问题");

六、对照本项目(xs-interview-agent)

MockInterviewService 是这套 API 的标准用法范本:

java 复制代码
List<Message> messages = new ArrayList<>();
messages.add(new SystemMessage(resumeAnalysisSystemPromptResource));   // system 角色
messages.add(new UserMessage(promptTemplate.render(...)));             // user 角色

Prompt prompt = new Prompt(messages,                                   // 运行时选项
        DashScopeChatOptions.builder().temperature(0.7).build());

String response = chatModel.call(prompt).getResult().getOutput().getText();  // 标准取值链路

对照笔记可做的三处优化:

  1. 温度分场景 :出题保持 0.7,评分与评估改 0.2(new Prompt(messages, lowTempOptions)),稳定性明显提升。

  2. 别丢 ChatResponse :当前链式取值直接丢弃了中间对象,等于丢弃 ChatResponseMetadata(token 消耗、finishReason)。建议先接住再做日志/成本统计:

    java 复制代码
    ChatResponse cr = chatModel.call(prompt);
    log.info("tokens={}", cr.getMetadata().getUsage());
    String text = cr.getResult().getOutput().getText();
  3. 长任务改流式 :chatModel.stream(prompt) + SSE 推送到 interview.html,解决评估阶段长时间白屏;或用 outputType/BeanOutputConverter 让框架帮你做结构化输出,替掉手写的 JsonNode 解析与 JSON 去围栏逻辑。


七、速查卡

java 复制代码
// 1. 注入(Spring Boot 自动装配,推荐)
private final ChatModel chatModel;   // 构造器注入

// 2. 极简
String text = chatModel.call("你好");

// 3. 系统提示 + 用户消息 + 运行时参数
List<Message> msgs = List.of(new SystemMessage("..."), new UserMessage("..."));
Prompt prompt = new Prompt(msgs, DashScopeChatOptions.builder()
        .withModel("qwen-plus").withTemperature(0.2).withMaxToken(4096).build());

// 4. 取值
String  out  = chatModel.call(prompt).getResult().getOutput().getText();
Flux<ChatResponse> flux = chatModel.stream(prompt);

取值链路 :ChatResponse → getResult() → Generation → getOutput() → AssistantMessage → getText()

合并优先级 :运行时 Prompt 的 ChatOptions 覆盖 启动时 defaultOptions。

相关推荐
十年Java程序媛1 小时前
Java 接口和抽象类对比|Java8 新特性,抛弃老旧八股,正确选型
java·spring boot·后端
caoerzhong1 小时前
JeeWMS 开源 WMS 部署避坑指南:Java 仓库管理系统的环境基线、四类根因与可复现交付
java·开发语言·开源
用户3721574261351 小时前
Java 合并 PDF 文件:完整合并、指定页面合并与流合并
java
北冥you鱼2 小时前
Go 语言空接口(interface{})使用场景与最佳实践
开发语言·windows·golang
for_ever_love__2 小时前
MySQL 全文索引实战:FULLTEXT、ngram 中文分词与 MATCH AGAINST 到底该怎么用
java·python·mysql·全文检索·分词·索引·ngram
java资料站2 小时前
五、Spring AI Alibaba · Memory · Saver(短期会话)
java·spring·microsoft
夜之眷属3 小时前
Core dump 崩溃排查:JVM 宕机后,那份 core 文件怎么用 gdb 还原现场
java·运维·服务器·jvm
波加曼大王3 小时前
# vLLM不要迷信PagedAttention神话,聊聊线上藏着的内部碎片陷阱
java·架构
SL_staff3 小时前
目标健康度自检清单:开发者视角下的目标-计划-任务链路断点诊断
java·开源·github