Spring AI Alibaba(1)初识 Spring AI Alibaba
在之前的专栏中,我们已经学习了 Spring AI,这一章来认识它的"进阶版":Spring AI Alibaba。
1.1 Spring AI Alibaba 简介
2022 年 ChatGPT 火了之后,Python 生态很快冒出了 LangChain、LlamaIndex 这些 AI 开发框架。但 Java 这边一直缺个顺手的工具。Spring 社区倒也反应不慢,搞出了 Spring AI,用一套跟厂商无关的底层抽象(ChatModel、Prompt、RAG、Tool、MCP 之类的)把各种 AI 模型串起来。
Spring AI 解决的是"怎么跟模型对话"的问题。但实际做项目你会发现,光能对话还不够。你得编排流程、协调多个 Agent、管理上下文状态,这些 Spring AI 管不了。
Spring AI Alibaba 就是在 Spring AI 上面加了一层。它基于阿里云通义系列模型做了适配,主要多了这么几样东西:
- Graph 编排引擎。这是它跟 Spring AI 最大的不同。借鉴了 LangGraph 的思路,用节点(Node)、边(Edge)、状态(State)三个概念来编排工作流。你可以把 Node 理解成一个执行单元(调 LLM 或者跑你自己的 Java 代码),Edge 决定下一步走哪个 Node,State 在它们之间传递数据。这套东西还支持循环,可以搭出挺复杂的流程。
- 三层架构。最底下是 Augmented LLM 层,就是 Spring AI 那些原子能力,模型、消息、工具、向量存储这些。中间是 Graph Runtime,负责工作流编排,流式响应、人在回路、记忆管理都在这层。最上面是 Agent Framework,给了几种开箱即用的 Agent 模式:ReAct Agent(推理+行动闭环)、SequentialAgent(顺序执行)、ParallelAgent(并行)、RoutingAgent(路由分发)、LoopAgent(循环)。

跟 Spring Boot 的集成也做得比较到位。一个 API Key 就能自动配好 ChatModel、EmbeddingModel 这些组件,Nacos 管提示词热更新,ARMS 做可观测,RocketMQ 处理异步消息,基本把阿里云生态的东西都接进来了。
简单说,Spring AI 是积木块,Spring AI Alibaba 是用积木块搭好的半成品。需要精细控制就往下走用 Graph API,图省事就直接用 Agent Framework。
下面来看看怎么用Spring AI Alibaba来搭建一个简单的智能体。
1.2 环境准备和项目准备
写代码之前先把环境搞定。
Spring AI Alibaba 跑在 Spring Boot 3.x 上,JDK 得 17 起步。终端里跑一下 java -version 看看版本够不够,不够就去装一个。
然后需要一个 API Key。Spring AI Alibaba 默认对接阿里云 DashScope 平台的通义大模型,去 DashScope 控制台(https://dashscope.console.aliyun.com/)注册登录后,开通"百炼大模型推理"服务,再到 API Keys 页面(https://dashscope.console.aliyun.com/apiKey)生成一个 Key。
Key 拿到手之后别直接写在代码里,后面会讲怎么用环境变量来配。
建一个标准的 Spring Boot 3.x 项目就行。
关于版本,写这篇文章的时候推荐用 Spring AI Alibaba 1.1.2.0,对应 Spring Boot 3.5.x 和 Spring AI 1.1.2。实际用的时候去 GitHub Releases 看一眼,有更新的稳定版本就用新的。
Spring AI Alibaba 的 Starter 组件挺多,按需引入:
| artifactId | 干什么用的 | 什么时候加 |
|---|---|---|
| spring-ai-alibaba-starter-dashscope | 对接百炼模型 | 必选 |
| spring-ai-alibaba-graph-core | Graph 工作流编排 | 要搞工作流或多 Agent 的时候 |
| spring-ai-alibaba-starter-memory | 会话记忆 | 需要多轮对话记忆的时候 |
| spring-ai-alibaba-starter-nl2sql | 自然语言转 SQL | 需要 NL2SQL 的时候 |
| spring-ai-alibaba-starter-arms-observation | 可观测 | 上生产要监控的时候 |
| spring-ai-alibaba-starter-tool-calling-* | 工具调用 | 需要 Function Calling 的时候 |
| spring-ai-alibaba-starter-document-reader-* | 文档读取 | 需要 RAG 文档解析的时候 |
这一章只做基础的模型调用,加个 dashscope starter 就够了。
1.3 配置文件
打开 application.yml,配 API Key 和模型参数。
API Key 对应的配置项是 spring.ai.dashscope.api-key。我建议用环境变量来引用,别把 Key 明文写在配置文件里:
spring:
ai:
dashscope:
api-key: ${AI_DASHSCOPE_API_KEY}
然后在系统环境变量或 IDE 的运行配置里设一下 AI_DASHSCOPE_API_KEY。
本地开发图省事的话也可以直接写死,但记得别提交到 Git。
模型参数也可以调:
spring:
ai:
dashscope:
chat:
options:
model: qwen-plus
temperature: 0.7
top-p: 0.8
max-tokens: 2048
几个参数的意思:
- model:用哪个模型,qwen-max 强但贵,qwen-plus 性价比高,看场景选
- temperature:控制随机性。0.1 这种低值输出比较稳定,0.9 就天马行空了
- top-p:和 temperature 配合,控制词汇多样性
- max-tokens:输出长度上限
框架还带了重试机制,不过默认只对服务端错误重试,4xx 这种客户端错误不会重试,免得无效请求反复发浪费额度。
把上面的东西合起来,一个完整的 application.yml 大概长这样:
spring:
ai:
dashscope:
api-key: ${AI_DASHSCOPE_API_KEY}
chat:
options:
model: qwen-plus
temperature: 0.7
top-p: 0.8
max-tokens: 2048
1.4 第一个 AI 对话
配好了就能调模型了。Spring AI Alibaba 的自动配置会帮你创建一个 ChatModel Bean,直接在 Controller 里注入就行。
写一个 ChatController:
@RestController
@RequestMapping("/api/chat")
public class ChatController {
private final ChatModel chatModel;
public ChatController(ChatModel chatModel) {
this.chatModel = chatModel;
}
@GetMapping("/simple")
public String simpleChat(@RequestParam String message) {
return chatModel.call(message);
}
}
ChatModel 有三个常用方法:
String call(String message) 是最简单的,传一句话进去,拿一句话出来。写 demo 或者场景简单的时候用这个就行。
ChatResponse call(Prompt prompt) 更灵活一些。Prompt 可以塞多条消息进去,比如一条 SystemMessage 告诉模型"你是一个翻译助手",再来一条 UserMessage 放用户输入。
ChatResponse call(Prompt prompt, ChatOptions options) 可以在运行时覆盖模型参数,比如临时把 temperature 调低。
启动应用,浏览器访问 http://localhost:8080/api/chat/simple?message=你好,配置没问题的话就能看到通义千问的回复了。第一次调用可能慢几秒,要建立网络连接。
另外框架还有个 ChatClient,是链式调用的风格,写起来比较顺:
@RestController
@RequestMapping("/api/chat-client")
public class ChatClientController {
private final ChatClient chatClient;
public ChatClientController(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
@GetMapping("/chat")
public String chat(@RequestParam String message) {
return chatClient.prompt()
.system("你是一个友好的AI助手")
.user(message)
.call()
.content();
}
}
.prompt() 开始构建请求,.system() 设系统指令,.user() 放用户消息,.call() 发请求,.content() 拿结果。ChatClient 后面章节会详细讲,这里先混个脸熟。
1.5 流式输出
同步调用有个问题:模型要生成完所有内容才一次性返回。用户等着等着以为卡了。流式输出就是边生成边返回,类似 ChatGPT 那种一个字一个字蹦出来的效果。
用 StreamingChatModel 就行,它继承了 ChatModel,多了一个 stream 方法:
Flux<ChatResponse> stream(Prompt prompt);
stream 返回的是 Flux<ChatResponse>,这是 Project Reactor 里的响应式流类型。模型每吐出一段内容,Flux 就发射一个 ChatResponse。模型每生成一个片段(Token 块),就会向 Flux 发射一个数据项。前端可以逐个接收并渲染,从而实现逐字显示的效果。
Controller 里加个流式接口:
@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());
}
这里有几个要注意的地方。
produces = MediaType.TEXT_EVENT_STREAM_VALUE 这个不能少。它告诉 Spring Web 返回 SSE(Server-Sent Events)格式,浏览器收到这种响应会保持连接,持续接收后续数据。
Flux<String> 的返回类型 Spring Web 会自动处理成流式响应。
有个容易踩的坑:getText() 返回的是累积文本,不是增量。举个例子:
第 1 次:"你好"
第 2 次:"你好,我是"
第 3 次:"你好,我是 AI 助手"
所以前端直接用最新收到的那段文本就行,不用自己拼接。
前端接收的话用 EventSource:
const eventSource = new EventSource('/api/chat/stream?message=你好');
eventSource.onmessage = (event) => {
document.getElementById('output').textContent = event.data;
};
1.6 完整代码
下面看下完整的 Spring AI Alibaba 示例代码:
package com.example.springaidemo.controller;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.StreamingChatModel;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
@RestController
@RequestMapping("/api/chat")
public class ChatController {
private final ChatModel chatModel;
private final StreamingChatModel streamingChatModel;
public ChatController(ChatModel chatModel, StreamingChatModel streamingChatModel) {
this.chatModel = chatModel;
this.streamingChatModel = streamingChatModel;
}
/**
* 简单对话 - 同步
*/
@GetMapping("/simple")
public String simple(@RequestParam(defaultValue = "你好") String message) {
return chatModel.call(message);
}
/**
* 使用 Prompt 的对话 - 同步
*/
@GetMapping("/prompt")
public String prompt(@RequestParam(defaultValue = "请解释一下什么是人工智能") String message) {
Prompt prompt = new Prompt(message);
return chatModel.call(prompt).getResult().getOutput().getText();
}
/**
* 流式对话 - 异步
*/
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> stream(@RequestParam(defaultValue = "请讲一个简短的故事") String message) {
Prompt prompt = new Prompt(message);
return streamingChatModel.stream(prompt)
.map(response -> {
if (response.getResult() != null &&
response.getResult().getOutput() != null) {
return response.getResult().getOutput().getText();
}
return "";
});
}
}
运行 SpringAiDemoApplication 类的 main 方法:
@SpringBootApplication
public class SpringAiDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAiDemoApplication.class, args);
}
}
启动后有三个接口可以试:
| 接口 | 说明 |
|---|---|
/api/chat/simple?message=你好 |
同步调用,等生成完再返回 |
/api/chat/prompt?message=... |
Prompt 方式,可以加系统指令 |
/api/chat/stream?message=... |
流式调用,逐字返回 |
1.7 踩坑记录
搭环境的过程中有几个问题挺常见的,我这里列一下。
API Key 报 401
多半是 Key 配错了。先检查配置项名字写对没有,是 spring.ai.dashscope.api-key,别写成 apikey 或 api_key。然后确认 Key 本身没问题,DashScope 控制台里重新生成一个试试。如果用的环境变量,代码里打个 System.getenv("AI_DASHSCOPE_API_KEY") 看看有没有值。
版本不兼容,报 NoSuchMethodError 或 ClassNotFoundException
这种一般是 Spring AI Alibaba、Spring AI、Spring Boot 三个的版本没对得上。当前的对应关系:
| Spring AI Alibaba | Spring AI | Spring Boot |
|---|---|---|
| 1.1.2.0 | 1.1.2 | 3.5.x |
| 1.1.0.0 | 1.1.0 | 3.4.x |
用 BOM 统一管版本,别手动指定各个依赖的版本号。升级之前先去 GitHub Releases 看看 Release Notes。
流式接口中文乱码
编码问题。Controller 方法里确保设了 UTF-8,produces 用了 TEXT_EVENT_STREAM_VALUE 的话一般没问题。如果还是乱码,检查一下前端那边是不是也按 UTF-8 解析的。
总结:这一章从 Spring AI Alibaba 是什么讲起,然后动手搭了一个能跑的应用。
下一章我们深入 Graph 编排引擎,看看怎么用 Node 和 Edge 搭出复杂的工作流。
更多推荐


所有评论(0)