Spring AI 全链路实战:带你从零基础到手搓完整 Agent 项目---SpringAI介绍与Model模块
前言
大家好,这里是程序员阿亮,从今天开始打算跟xdm一起学习一下SpringAI这个技术框架,并且带xdm完成一个基于SpringAI的RAG+Agent的项目,从0基础到手搓完整的Agent项目
不过,对于基础的Agent和RAG的知识点,xdm可以之前看我前俩期播客:
这一期,咱们先从介绍SpringAI与环境准备开始
一、SpringAI是什么?
1.1 介绍

根据官方文档:(https://spring.io/projects/spring-ai)的介绍,实际上SpringAI就是一个Spring 官方推出的 应用框架,专为人工智能工程化设计。它的目标是将 Spring 生态系统的设计原则(如可移植性、模块化、POJO 构建)应用于 AI 领域,简化企业级应用中集成大语言模型(LLM)的复杂度的框架
1.2 核心模块

实际上SpringAI的模块划分并不多,我打算从
├── Model(模型集成)←
├── Vector Store(向量库)
├── Document Processing(文档处理)
├── Chat Memory(对话记忆)
├── RAG / Tools / MCP / Evaluation(扩展优化)
└── Agent(智能体)
这几个方面层层递进地进行详细讲解。
二、Model模块
2.1 模块定位
Model 模块是 Spring AI 的核心引擎,负责:
- 屏蔽差异:统一 OpenAI、Azure、Ollama、Anthropic 等不同厂商的 API 差异。
- 对话管理:处理 Prompt 构建、消息流转、响应解析。
- 能力扩展:支持流式输出、结构化数据、函数调用(Function Calling)。
2.2 如何配置
引入依赖
<dependencies>
<!-- 核心 Starter -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
<!-- 若使用 Ollama 则改为 spring-ai-starter-model-ollama -->
</dependency>
<!-- Web 支持(用于实战 Controller) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Lombok(简化代码) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
spring:
ai:
openai:
# API Key 配置
api-key: ${OPENAI_API_KEY}
# 基础 URL(若使用兼容 OpenAI 协议的国内模型可修改此处)
base-url: https://api.openai.com/v1
chat:
options:
# 默认模型
model: gpt-4o
# 默认温度值 (0.0 - 2.0)
temperature: 0.7
还有就是进行基本的文件配置
三、Model模块核心API讲解
接下来我来讲解一下SpringAI的Model模块的核心API
3.1 ChatClient
ChatClient 是 Spring AI 1.0 引入的流式构建器模式高级 API,旨在简化调用链。
3.1.1 创建与注入
作用:获取客户端实例,支持多例构建。
@RestController
public class ModelController {
// 1. 注入 Builder(Spring Boot 自动配置)
private final ChatClient.Builder chatClientBuilder;
// 2. 构造函数注入
public ModelController(ChatClient.Builder chatClientBuilder) {
this.chatClientBuilder = chatClientBuilder;
}
// 3. 构建默认客户端
private ChatClient createClient() {
return chatClientBuilder.build();
}
}
简单说这个API就是把我们跟模型进行交互的底层给封装了,集成了非常丰富的功能
3.1.2 基础对话 API: prompt().call().content()
作用:发送文本提示,同步获取纯文本响应。
举个例子
@GetMapping("/basic")
public String basicChat() {
// 1. 构建客户端
ChatClient client = createClient();
// 2. 构建 Prompt 并调用
// .prompt(String): 设置用户消息
// .call(): 执行同步请求
// .content(): 提取响应中的文本内容
String response = client
.prompt("你好,请介绍下 Spring AI")
.call()
.content();
return response;
}
特别需要注意的是返回的结果的对象结构是:
ChatResponse (模型完整响应)
├─► List<Generation> getResults() // 可能返回多个候选结果
├─► ChatResponseMetadata getMetadata() // 响应级元数据
│
└─► Generation (单次生成结果)
├─► AssistantMessage getOutput() // 模型输出的消息内容
├─► ChatGenerationMetadata getMetadata() // 生成级元数据
│
└─► AssistantMessage (助手消息)
├─► String getText() // 文本内容 你常用的
├─► Map<String,Object> getMetadata() // 消息级元数据
├─► List<ToolCall> getToolCalls() // 函数调用请求
├─► List<Media> getMedia() // 多模态内容
└─► MessageType getMessageType() // 消息类型: ASSISTANT
3.1.3 系统消息 API: system()
作用:设定 AI 的角色、行为准则或背景知识(System Message)。
@GetMapping("/system")
public String systemChat() {
String response = createClient()
.prompt("翻译这句话:Hello World")
// .system(String): 设置系统指令,优先级高于用户消息
.system("你是一个专业的翻译助手,只输出翻译结果,不要解释")
.call()
.content();
return response;
}
这种系统提示词一般是我们写死在后端文件中,用于给模型进行角色调整、提供背景知识的工具,其实也是Prompt
3.1.4 多轮对话消息 API: messages()
API
作用:手动构建完整的消息历史(User/Assistant/System),用于精细控制上下文。
@GetMapping("/messages")
public String messagesChat() {
// 1. 构建消息列表
List<Message> messages = List.of(
// SystemMessage: 系统指令
new SystemMessage("你是一个数学助手"),
// UserMessage: 用户输入
new UserMessage("1+1 等于几?"),
// AssistantMessage: 模拟历史回答(用于 Few-Shot 或上下文延续)
new AssistantMessage("等于 2"),
// 新一轮用户输入
new UserMessage("那 2+2 呢?")
);
// 2. 传入消息列表
String response = createClient()
.messages(messages)
.call()
.content();
return response; // 预期输出:等于 4
}
三种Message的关系和区别:
三种消息角色对比
|
消息类型 |
对应角色 |
发送方 |
核心作用 |
典型内容 |
|---|---|---|---|---|
|
|
system |
开发者设定 |
设定人设/规则/边界,指导模型行为 |
"你是一个数学助手"、"请用中文回答"、"不要提供医疗建议" |
|
|
user |
终端用户 |
发起提问/提供输入,驱动对话流程 |
"1+1等于几?"、"解释一下红黑树" |
|
|
assistant |
模型回复 |
记录历史回复,构建多轮对话上下文 |
"等于2"、"红黑树是一种自平衡二叉搜索树..." |
3.1.5 流式输出 API: stream()
作用:服务端发送事件(SSE),前端逐字显示,降低首字延迟。
就是咱们平时用ChatGPT不是一个一个chunk显示嘛,就是基于流式API去实现的
一般是基于SSE或者WebFlux
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamChat() {
// 1. 调用 stream() 而非 call()
// 2. 返回 Flux<ChatResponse> 流
Flux<ChatResponse> responseFlux = createClient()
.prompt("写一首关于春天的诗,每句一行")
.stream();
// 3. 转换流数据
return responseFlux
// .getResult(): 获取生成结果
// .getOutput(): 获取消息内容
// .getText(): 获取文本片段
.map(response -> response.getResult().getOutput().getText());
}
大致链路就是:
用户请求 /stream
│
▼
Spring Controller 方法执行
│
▼
createClient().prompt(...).stream()
│
▼
[异步] 连接大模型流式接口(如 OpenAI /stream)
│
▼
模型每生成一个 token → 返回一个 ChatResponse 片段
│
▼
.map() 提取文本 → Flux<String> 发射一个字符串片段
│
▼
Spring WebFlux 自动将 Flux<String> 序列化为 SSE 格式:
│ data: 春
│ data: 风
│ data: 吹
│ ...
▼
浏览器/客户端逐行接收并渲染 → 实现"打字机效果"
3.1.6 结构化输出 API: entity()
作用:强制模型输出特定 JSON 格式,并自动反序列化为 Java Bean(POJO)。
// 定义目标结构
public record UserInfo(String name, int age, String city) {}
@GetMapping("/entity")
public UserInfo entityChat() {
// 1. 调用 .entity(Class)
// 底层会自动构建 Function Calling 或 JSON Mode 提示
UserInfo info = createClient()
.prompt("用户说:我叫张三,今年 25 岁,住在北京")
.call()
.entity(UserInfo.class);
return info; // 自动转换为 {name:"张三", age:25, city:"北京"}
}
这实际就是FunctionCalling,也是我们实现Tool调用的核心,也是我们Agent的核心部分之一
3.1.7 模型参数配置 API: options()
作用:动态覆盖全局配置,针对单次请求调整温度、模型类型等
@GetMapping("/options")
public String optionsChat() {
// 1. 构建特定模型的 Options
OpenAiOptions options = OpenAiOptions.builder()
.model("gpt-4o-mini") // 指定模型
.temperature(0.2) // 降低随机性,更严谨
.maxTokens(100) // 限制最大输出长度
.build();
String response = createClient()
.prompt("解释量子力学")
// .options(Object): 传入特定提供商的 Options 对象
.options(options)
.call()
.content();
return response;
}
3.1.8 工具调用 API: tools() / toolNames()
作用:注册 Java 方法供模型调用,实现联网搜索、数据库查询等能力。
// 定义工具类
@Component
public class WeatherTool {
@Tool(description = "查询城市天气") // @Tool 注解标记为可调用函数
public String getWeather(String city) {
return city + " 天气晴朗,25 度";
}
}
// Controller 中使用
@GetMapping("/tools")
public String toolsChat(@Autowired WeatherTool weatherTool) {
String response = createClient()
.prompt("北京天气怎么样?")
// 方式 1: 直接传入工具对象列表
.tools(List.of(weatherTool))
// 方式 2: 或者指定工具名称 .toolNames("getWeather")
.call()
.content();
// 模型会自动识别意图,调用 getWeather("北京") 并整合结果
return response;
}
3.1.9 Advisors (切面顾问) API: advisors()
作用:在请求前后插入逻辑,如记忆管理、日志记录、安全过滤。
@GetMapping("/advisors")
public String advisorsChat(@Autowired ChatMemory chatMemory) {
String response = createClient()
.prompt("我叫小明,记住我的名字")
// 添加记忆顾问,自动管理上下文
.advisors(new MessageChatMemoryAdvisor(chatMemory))
// 添加日志顾问
.advisors(new SimpleLoggerAdvisor())
.call()
.content();
return response;
}
3.2 ChatModel
ChatModel 是更底层的接口,ChatClient 内部其实是基于它构建的。当你需要完全控制请求/响应对象时使用。
3.2.1 ChatClient vs ChatModel 返回差异
|
调用方式 |
返回类型 |
适用场景 |
示例 |
|---|---|---|---|
|
|
|
快速获取文本 |
|
|
|
|
需要元数据/多结果 |
查看 token 用量 |
|
|
|
结构化输出 |
|
|
|
|
底层 API 直接调用 |
自定义集成 |
|
|
|
流式文本 |
打字机效果 |
|
|
|
流式 + 元数据 |
实时显示 token 消耗 |
ChatClient 是 ChatModel 的高级封装:
ChatClient提供了 fluent API、模板、Advisor 等增强功能,底层最终调用ChatModel。
特别需要注意的是返回的结果的对象结构是:
ChatResponse (模型完整响应)
├─► List<Generation> getResults() // 可能返回多个候选结果
├─► ChatResponseMetadata getMetadata() // 响应级元数据
│
└─► Generation (单次生成结果)
├─► AssistantMessage getOutput() // 模型输出的消息内容
├─► ChatGenerationMetadata getMetadata() // 生成级元数据
│
└─► AssistantMessage (助手消息)
├─► String getText() // 文本内容 你常用的
├─► Map<String,Object> getMetadata() // 消息级元数据
├─► List<ToolCall> getToolCalls() // 函数调用请求
├─► List<Media> getMedia() // 多模态内容
└─► MessageType getMessageType() // 消息类型: ASSISTANT
3.2.2 注入与调用
@Autowired
private ChatModel chatModel; // 底层模型接口
@GetMapping("/model-low-level")
public String lowLevelChat() {
// 1. 构建消息
UserMessage userMessage = new UserMessage("你好");
// 2. 构建 Prompt 对象
Prompt prompt = new Prompt(userMessage);
// 3. 调用 chat 方法
ChatResponse response = chatModel.call(prompt);
// 4. 解析结果
return response.getResult().getOutput().getText();
}
3.2.3 流式调用 (Streaming)
@GetMapping("/model-stream")
public Flux<ChatResponse> modelStream() {
Prompt prompt = new Prompt("写个故事");
// 返回 Flux 流
return chatModel.stream(prompt);
}
3.2.4 异步调用 (Async)
@GetMapping("/model-async")
public CompletableFuture<String> asyncChat() {
Prompt prompt = new Prompt("异步任务");
// 返回 CompletableFuture
CompletableFuture<ChatResponse> future = chatModel.callAsync(prompt);
return future.thenApply(resp ->
resp.getResult().getOutput().getText()
);
}
四、基于ChatClient的实践练习
学了上述内容,大家完全可以接一个Deepseek或者其他厂商的model去做一个demo了!
这里给一个小实例:
场景:构建一个支持记忆、工具调用(查订单)、结构化输出(提取用户意图)的客服系统。
4.1 项目结构
src/main/java
├── config/ # 配置类
├── model/ # POJO 定义
├── tools/ # 工具函数
└── controller/ # 接口入口
4.2 定义结构化数据 (POJO)
package com.example.demo.model;
import lombok.Data;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
/**
* 用户意图识别结果
* 用于将自然语言转换为业务可处理的对象
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserIntent {
// 意图类型:QUERY_ORDER, COMPLAINT, CONSULT
private String type;
// 提取的关键参数,如订单号
private String orderId;
// 用户情绪分数 1-10
private int sentimentScore;
}
4.3 定义业务工具 (Tools)
package com.example.demo.tools;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.stereotype.Component;
@Component
public class OrderService {
/**
* 模拟数据库查询订单
* @Tool 描述至关重要,决定了 LLM 何时调用此函数
*/
@Tool(description = "根据订单号查询订单状态和物流信息")
public String queryOrderStatus(String orderId) {
// 模拟业务逻辑
if ("ORD123".equals(orderId)) {
return "订单 ORD123 已发货,物流单号 SF123456";
}
return "未找到订单:" + orderId;
}
}
4.4 控制器实现 (Controller)
package com.example.demo.controller;
import com.example.demo.model.UserIntent;
import com.example.demo.tools.OrderService;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.MessageChatMemoryAdvisor;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
@RequestMapping("/api/cs")
public class CustomerServiceController {
private final ChatClient chatClient;
private final OrderService orderService;
private final ChatMemory chatMemory;
// 注入依赖
public CustomerServiceController(ChatClient.Builder builder,
OrderService orderService,
ChatMemory chatMemory) {
this.orderService = orderService;
this.chatMemory = chatMemory;
// 构建默认客户端,预置系统指令
this.chatClient = builder
.defaultSystem("""
你是电商智能客服「小蜜」。
1. 先识别用户意图。
2. 如果需要查订单,调用 queryOrderStatus 工具。
3. 语气亲切专业。
""")
.build();
}
/**
* 综合实战接口
* 1. 记忆上下文
* 2. 工具调用
* 3. 意图识别
*/
@PostMapping("/chat")
public Map<String, Object> chat(@RequestParam String message,
@RequestParam String sessionId) {
// 1. 意图识别 (结构化输出)
// 单独调用一次用于分类,实际生产中可优化为一步
UserIntent intent = chatClient.prompt()
.system("请分析用户输入的意图,返回 JSON 格式")
.user(message)
.call()
.entity(UserIntent.class);
// 2. 正式对话 (带记忆 + 工具)
String reply = chatClient.prompt()
.user(message)
// 启用记忆顾问,传入会话 ID 实现多用户隔离
.advisors(a -> a.param(MessageChatMemoryAdvisor.CONVERSATION_ID, sessionId))
// 注册工具,模型可根据意图自动调用
.tools(orderService)
.call()
.content();
// 3. 返回组合结果
return Map.of(
"reply", reply,
"intent", intent.getType(),
"orderId", intent.getOrderId(),
"sentiment", intent.getSentimentScore()
);
}
}
那么急于以上的实验我们就学习到了ChatClient和ChatModel的实际使用了!
五、总结
实际上本文讲解了SpringAI与其第一个核心模块:Model模块,接下来会讲解的是:
├── Model(模型集成)
├── Vector Store(向量库)
├── Document Processing(文档处理)
├── Chat Memory(对话记忆)
├── RAG / Tools / MCP / Evaluation(扩展优化)
└── Agent(智能体)等模块
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐




所有评论(0)