Spring AI 2.0 Agent 入门与最小实践
Spring AI 是 Spring 生态面向人工智能应用开发提供的一组统一抽象。对于已经接触过 Spring Boot 的开发者,它最大的价值在于继续沿用 Bean、依赖注入、Controller、Service 等熟悉的开发方式,同时将大模型调用、工具调用、对话记忆、RAG 和调用增强等能力纳入 Spring 应用。
Spring AI 2.0.0 已于 2026 年 6 月正式发布,主要面向 Spring Boot 4.0/4.1 与 Spring Framework 7.0。对于 Agent 开发,2.0 中值得重点理解的一项变化是 Tool Calling 与 Advisor Chain 的进一步结合。工具调用循环可以由 ChatClient 中的 ToolCallingAdvisor 管理,因此工具、记忆、日志和其他增强逻辑能够在统一的调用链中组合。

目录
一、Spring AI 2.0 中的 Agent 由哪些组件组成
二、ChatClient:Spring AI 应用的调用入口
六、ToolCallingAdvisor:形成最小 Agent Loop
一、Spring AI 2.0 中的 Agent 由哪些组件组成
先观察一个经过简化的 Spring AI Agent 调用过程:
用户请求
↓
ChatClient
↓
Advisor Chain
├── ChatMemory
├── 日志 / 安全控制
├── RAG
└── ToolCallingAdvisor
↓
ChatModel
↓
LLM
↓
判断是否调用 Tool
↓
Java Tool
↓
Tool Result 返回模型
↓
最终回答
这里真正需要优先掌握的组件并不多。
| 组件 | 作用 |
|---|---|
ChatClient | Spring AI 应用中调用模型的主要入口 |
ChatModel | 对具体大模型提供统一模型抽象 |
Prompt | 表示发送给模型的消息和相关参数 |
Advisor | 在 AI 调用过程中进行观察、增强、修改或干预 |
ChatMemory | 保存需要继续提供给模型的对话上下文 |
Tool | 将 Java 方法开放给模型,使模型能够读取数据或执行操作 |
ToolCallingAdvisor | 管理模型请求工具、执行工具、继续调用模型的循环 |
| RAG Advisor | 在调用模型以前检索外部知识并补充上下文 |
| Structured Output | 将模型回答转换为 Java 对象 |
【核心迭代思路】这几个组件可以逐层加入。最初只有
ChatClient + ChatModel时,程序已经可以完成普通大模型问答。加入ChatMemory后,应用能够维持多轮上下文。加入Tool后,模型可以使用 Java 程序提供的真实能力。再加入 Advisor,可以进一步完成日志、安全控制、RAG 和调用过程干预。
Spring AI 官方也将 ChatClient 作为高层模型交互 API。Spring Boot 可以自动创建 ChatClient.Builder,开发者随后通过链式 API 构造 Prompt、Advisor 和 Tools。
二、ChatClient:Spring AI 应用的调用入口
ChatClient 是 Spring AI 中非常值得先记住的类。它提供了类似 WebClient、RestClient 的链式调用方式,可以组织用户消息、系统消息、工具和 Advisor【入口、组织功能】。
最简单的调用只有下面几行:
@RestController
@RequestMapping("/ai")
public class AiController {
// 注入 ChatClient,它是 Spring AI 中调用模型的统一入口
private final ChatClient chatClient;
// 通过构造器注入 ChatClient.Builder,并构建出 ChatClient 实例
public AiController(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
// 处理 GET 请求 /ai/chat,接收用户传入的 message 参数
@GetMapping("/chat")
public String chat(@RequestParam String message) {
// 链式调用:构造 Prompt -> 加入用户消息 -> 发起同步调用 -> 获取模型返回文本
return chatClient
.prompt()
.user(message)
.call()
.content();
}
}
其中:
.prompt()
表示开始构造这一次模型请求。
.user(message)
加入用户消息。
.call()
发起同步模型调用。
.content()
直接取得模型生成的文本结果。
因此,Spring AI 最值得先记住的一行代码就是:
chatClient
.prompt()
.user(message)
.call()
.content();
Spring AI 还可以通过:
.system("你是一个 Java 学习助手")
增加 System Prompt,用于规定模型长期角色和行为。
例如:
String result = chatClient
.prompt()
.system("你是一个 Java 学习助手,回答保持简洁。")
.user("介绍一下 Redis")
.call()
.content();
ChatModel 位于更底层,负责提供统一的大模型调用接口。具体使用 DeepSeek、OpenAI、Ollama 等模型时,对应实现最终都进入这一模型抽象。日常 Agent 业务代码通常围绕 ChatClient 组织,因为后续的 Advisor、Memory 和 Tool 都可以继续挂接在这条调用路径上。
三、Advisor:在模型调用过程中加入增强逻辑
Advisor 是理解 Spring AI Agent 很重要的一个概念。可以将 Advisor 理解为 AI 调用过程中的拦截与增强机制。它位于业务代码和真正的模型调用之间,可以查看请求、修改 Prompt、增加上下文、记录日志、执行安全检查,也可以处理模型返回结果。Spring AI 官方将 Advisor API 定义为用于 intercept、modify 和 enhance AI interaction 的可组合机制。
它的工作位置可以简化成:
Controller
↓
ChatClient
↓
Advisor 1
↓
Advisor 2
↓
Advisor 3
↓
ChatModel
↓
LLM
多个 Advisor 会组成一条 Advisor Chain。每一个 Advisor 只负责自己的任务,然后将请求继续交给下一个 Advisor。执行顺序由 Advisor 的 order 决定。
这使得很多能力可以在不修改核心业务 Controller 的情况下加入。
例如,一个原本的调用是:
chatClient
.prompt()
.user(message)
.call()
.content();
如果需要增加日志,可以加入 Spring AI 已经提供的:
SimpleLoggerAdvisor
代码只需要变成:
chatClient
.prompt()
.advisors(new SimpleLoggerAdvisor())
.user(message)
.call()
.content();
业务仍然负责“向模型提出问题”,日志逻辑由 Advisor 单独处理。SimpleLoggerAdvisor 会记录 ChatClient 请求和响应数据,主要用于调试和监控。
Spring AI 已经提供了一些很有代表性的 Advisor:
MessageChatMemoryAdvisor
→ 对话记忆
QuestionAnswerAdvisor
→ 基础 RAG
RetrievalAugmentationAdvisor
→ 更完整的 RAG
SimpleLoggerAdvisor
→ 请求和响应日志
SafeGuardAdvisor
→ 内容安全控制
ToolCallingAdvisor
→ 工具调用循环
这些能力都可以沿着 Advisor Chain 加入 ChatClient。
因此,在 Agent 应用中,Advisor 很适合承担横向能力。例如记忆管理、RAG、日志、内容审核、调用跟踪等逻辑可以独立存在,Agent 主业务代码仍然保持较为简洁。
四、ChatMemory:让 Agent 具备多轮上下文
大模型接口本身是无状态的。
ChatMemory 解决的是这一问题。它负责保存当前对话中需要继续提供给模型的消息,使后续请求能够携带之前的上下文。Spring AI 将具体消息存储职责进一步交给 ChatMemoryRepository,Memory 本身负责决定哪些消息应该继续保留。
Spring AI 2.0 默认使用的实现是:
MessageWindowChatMemory
它维护一个滑动消息窗口。当消息数量超过限制时,会逐渐移除较早的消息,同时保留 System Message。默认窗口大小为 20 条消息。
也可以自行创建:
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.maxMessages(20)
.build();
有了 Memory,还需要一个组件把历史消息真正放入每一次模型请求。这个任务由:
MessageChatMemoryAdvisor
完成。
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(
MessageChatMemoryAdvisor
.builder(chatMemory)
.build()
)
.build();
此时调用路径变成:
用户新消息
↓
MessageChatMemoryAdvisor
↓
根据 conversationId 查找历史消息
↓
历史消息 + 当前消息
↓
ChatModel
↓
LLM
↓
新的回答继续写入 Memory
Spring AI 2.0 要求使用 Memory Advisor 时提供 conversationId。这个 ID 用于区分不同用户或者不同会话。缺少 ChatMemory.CONVERSATION_ID 会直接产生运行时异常。
例如:
String conversationId = "session-001";
String result = chatClient
.prompt()
.user("我负责键盘库存管理")
.advisors(advisor -> advisor.param(
ChatMemory.CONVERSATION_ID,
conversationId
))
.call()
.content();
下一次继续使用相同的:
session-001
MessageChatMemoryAdvisor 就能够取得这段会话对应的上下文。
这里还有一个容易混淆的概念:ChatMemory 主要服务于“模型当前需要记住什么”。完整聊天记录通常属于业务数据,应当单独持久化。Spring AI 官方也明确区分 Chat Memory 与完整 Chat History。
五、Tool:让 Agent 能够真正使用程序能力
有了 Memory,Agent 可以记住上下文,但它目前仍然主要依靠模型自身知识回答问题。
假设用户询问:
keyboard 现在还有多少库存?
模型无法天然知道应用数据库中的实时库存。
这时可以给模型一个 Java 方法:
getStock("keyboard")
Spring AI 将这种可以由模型请求执行的方法称为 Tool。
一个最简单的 Tool 可以这样写:
@Component
public class InventoryTools {
// 模拟商品库存数据:keyboard 有 6 件,mouse 有 24 件
private static final Map<String, Integer> STOCK = Map.of(
"keyboard", 6,
"mouse", 24
);
// @Tool 注解将该方法暴露给模型,description 用于描述工具用途
@Tool(description = "查询指定商品当前库存数量")
public String getStock(
@ToolParam(description = "商品名称") String product) {
// 根据商品名称(转小写)查询库存
Integer stock = STOCK.get(product.toLowerCase());
// 未找到该商品时返回提示信息
if (stock == null) {
return "没有找到该商品";
}
// 返回库存查询结果
return product + " 当前库存为 " + stock + " 件";
}
}
这里真正关键的是:
@Tool
Spring AI 会根据这个 Java 方法生成对应的 Tool Definition,包括工具名称、描述以及参数结构。模型可以根据用户任务判断是否需要调用它。官方文档建议认真编写 Tool 的描述,因为模型会根据名称、描述和参数 Schema 判断工具用途。
随后将 Tool 提供给 ChatClient:
String result = chatClient
.prompt()
.user("keyboard 现在还有多少库存?")
.tools(inventoryTools) // 将库存工具注册给 ChatClient
.call()
.content();
这里已经产生了一项很重要的变化。
普通模型调用是:
用户
↓
LLM
↓
回答
加入 Tool 后可能形成:
用户
↓
LLM
↓
判断需要查询库存
↓
调用 getStock("keyboard")
↓
Java 返回库存 6 件
↓
工具结果重新提供给 LLM
↓
LLM 生成最终回答
模型负责判断“需要使用哪个工具以及传入什么参数”。Java 应用真正执行工具方法。
六、ToolCallingAdvisor:形成最小 Agent Loop
Tool 能够解决“Agent 可以做什么”的问题,还需要有组件负责管理整个工具调用过程。
Spring AI 2.0 中,这个组件就是:
ToolCallingAdvisor
当使用 ChatClient 时,Spring AI 会自动注册 ToolCallingAdvisor。它负责执行模型产生的 Tool Call,将结果重新提供给模型,并继续这一过程,直到模型给出一个不再包含工具调用请求的结果。
因此,一个典型工具调用循环可以表示为:
用户任务
↓
LLM 第一次推理
↓
需要 Tool?
↓ 是
生成 Tool Call
↓
ToolCallingAdvisor
↓
执行 Java Tool
↓
得到 Tool Result
↓
再次调用 LLM
↓
还需要 Tool?
↓ 否
最终回答
这已经具备一个最小 Agent 的核心特征:模型能够根据当前任务决定下一步操作,调用外部能力,并根据执行结果继续完成任务。
Spring AI 2.0 将这一循环纳入 Advisor Chain,因此 Tool Calling 可以与 Memory、RAG、日志和其他 Advisor 继续组合。
对于普通开发,通常不需要手动创建 ToolCallingAdvisor。
只要:
.tools(inventoryTools)
或者在 ChatClient 中注册:
.defaultTools(inventoryTools)
即可。ChatClient 会负责默认工具调用循环。
七、组合一个最小可运行 Spring AI Agent
现在把前面的组件组合起来。这个 Agent 只做一件事情:管理简单商品库存。
它需要具备三项能力:
能够与用户对话
能够记住同一会话的上下文
能够自主调用库存查询 Tool
1. 引入 Spring AI
这里以 DeepSeek 为例。
Spring AI 2.0 官方提供:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-deepseek</artifactId>
</dependency>
Spring AI 官方 DeepSeek Starter 会通过 Spring Boot 自动配置创建对应的模型组件。
API Key 放在环境变量中:
spring:
ai:
deepseek:
api-key: ${DEEPSEEK_API_KEY}
Spring AI 2.0 的官方 DeepSeek 配置属性使用【按指定路径填充DS API即可】:
spring.ai.deepseek.api-key
2. 创建库存 Tool
@Component
public class InventoryTools {
// 模拟商品库存数据:keyboard 有 6 件,mouse 有 24 件
private static final Map<String, Integer> STOCK = Map.of(
"keyboard", 6,
"mouse", 24
);
// @Tool 注解将该方法暴露给模型,description 用于描述工具用途
@Tool(description = "查询商品库存。当用户询问库存数量或者是否需要补货时使用")
public String getStock(
@ToolParam(description = "商品名称") String product) {
// 根据商品名称(转小写)查询库存
Integer stock = STOCK.get(product.toLowerCase());
// 未找到该商品时返回提示信息
if (stock == null) {
return "没有找到商品:" + product;
}
// 返回库存查询结果
return product + " 当前库存为 " + stock + " 件";
}
}
3. 创建 ChatMemory 和 ChatClient
@Configuration
public class AiConfig {
// 创建 ChatMemory Bean,用于保存多轮对话上下文
@Bean
public ChatMemory chatMemory() {
// 使用滑动窗口内存,最多保留 20 条消息
return MessageWindowChatMemory.builder()
.maxMessages(20)
.build();
}
// 创建 ChatClient Bean,组装记忆、日志和工具
@Bean
public ChatClient chatClient(
ChatClient.Builder builder,
ChatMemory chatMemory,
InventoryTools inventoryTools) {
return builder
// 设置系统提示词,定义 Agent 行为规则
.defaultSystem("""
你是一个库存管理助手。
涉及商品实时库存时必须调用库存查询工具。
当库存低于 10 件时提醒补货。
回答保持简洁。
""")
// 加入多轮对话记忆
.defaultAdvisors(
MessageChatMemoryAdvisor
.builder(chatMemory)
.build(),
// 记录 ChatClient 请求和响应
new SimpleLoggerAdvisor()
)
// 将库存工具提供给 Agent
.defaultTools(inventoryTools)
.build();
}
}
这里已经把三个非常重要的 Agent 组件组合到了一起:
System Prompt
→ 定义 Agent 行为
MessageChatMemoryAdvisor
→ 提供上下文
InventoryTools
→ 提供真实操作能力
Tool Calling Loop 则由 ChatClient 默认注册的 ToolCallingAdvisor 管理。
4. 提供接口
Controller 可以保持非常简单:
@RestController
@RequestMapping("/agent")
public class InventoryAgentController {
// 注入 ChatClient,它是 Spring AI 中调用模型的统一入口
private final ChatClient chatClient;
// 通过构造器注入 ChatClient
public InventoryAgentController(ChatClient chatClient) {
this.chatClient = chatClient;
}
// 处理 GET 请求 /agent,接收用户消息和会话 ID
@GetMapping
public String agent(
@RequestParam String message,
@RequestParam String conversationId) {
return chatClient
.prompt()
// 当前用户消息
.user(message)
// 指定当前会话
.advisors(advisor -> advisor.param(
ChatMemory.CONVERSATION_ID,
conversationId
))
// 调用 Agent
.call()
.content();
}
}
真正与 Spring AI 交互的业务代码最终仍然只有:
chatClient
.prompt()
.user(message)
.advisors(...)
.call()
.content();
大量 Agent 能力已经通过 ChatClient 配置完成(Configuration文件)。
八、一次请求内部究竟发生了什么
现在发送:
/agent?conversationId=001&message=keyboard需要补货吗
程序内部可以按照下面的过程理解:
① 用户请求
keyboard 需要补货吗?
↓
② ChatClient
构造当前 Prompt
↓
③ MessageChatMemoryAdvisor
读取 conversationId = 001
对应的历史消息
↓
④ LLM
发现回答问题需要真实库存
↓
⑤ Tool Calling
模型产生类似:
getStock("keyboard")
↓
⑥ Java Tool
返回:
keyboard 当前库存为 6 件
↓
⑦ ToolCallingAdvisor
把 Tool Result 重新交给模型
↓
⑧ LLM
结合规则:
库存低于 10 件需要提醒补货
↓
⑨ 最终回答
keyboard 当前库存为 6 件,
建议及时补货。
在这段过程中,Controller 没有编写:
if (用户问库存) {
调用库存方法;
}
模型根据 Tool Definition 和当前任务决定是否使用工具。
这正是最小 Agent 相比普通聊天程序最值得关注的变化。
九、继续丰富 Agent 时还会加入什么
在理解上面的最小 Agent 后,可以继续沿着同一结构扩展。
例如需要让 Agent 查询企业知识库,可以加入:
QuestionAnswerAdvisor
或者:
RetrievalAugmentationAdvisor
Spring AI 将 RAG 能力与 Advisor API 结合,可以在模型调用以前检索 Vector Store,并将相关文档加入上下文。( RAG 最直观的位置就是放在 MessageChatMemoryAdvisor 之后、第一次进入 LLM 之前。它先根据当前问题及必要的对话上下文检索知识库,然后把检索结果补进 Prompt。)
如果 Agent 的结果需要直接进入后端业务逻辑,可以使用 Structured Output:
record RestockDecision(
String product,
int stock,
boolean needRestock
) {}
随后:
RestockDecision result = chatClient
.prompt()
.user("分析 keyboard 当前是否需要补货")
.call()
.entity(RestockDecision.class); // 将模型结果转换为 Java 对象
Spring AI 的 .entity() 可以将模型结果直接转换成 Java 类型。2.0 还提供 Schema Validation 等能力,用于检查结构化结果并在结构不符合要求时重新尝试。
如果工具越来越多,还可以进一步接入 MCP。MCP 为模型访问外部工具和资源提供统一协议,使数据库、API、文件系统或独立工具服务能够通过标准化方式进入 Agent。
因此,一个更加完整的 Spring AI Agent 可以逐渐形成:
ChatClient
│
Advisor Chain
│
┌───────────────┼───────────────┐
│ │ │
Memory RAG Safety
│ │ │
└───────────────┼───────────────┘
│
ToolCallingAdvisor
│
ChatModel / LLM
│
判断下一步需要什么
│
┌─────────┴─────────┐
│ │
Tools MCP
│ │
└─────────┬─────────┘
│
外部真实能力
对于 Spring AI 2.0 的第一阶段学习,掌握下面这条路径已经足够:
ChatClient
↓
Advisor
↓
ChatMemory
↓
Tool
↓
ToolCallingAdvisor
↓
最小 Agent
其中
ChatClient负责组织一次 AI 调用,Advisor负责向调用过程加入独立增强逻辑,ChatMemory负责维持上下文,Tool将 Java 能力开放给模型,ToolCallingAdvisor负责完成“模型判断—工具执行—结果返回模型—继续判断”的循环。
理解这一结构后,RAG、结构化输出、MCP、多 Agent 编排等内容都可以继续建立在同一套 Spring AI 调用模型之上。
更多推荐

所有评论(0)