Spring AI Agent 编排:ReAct 模式 + 多 Agent 协作实战
本文是《Spring AI 实战》系列第 12 章。第 9 章的 Function Calling 是单次调用------用户问一个问题,AI 调一个工具,返回结果。但真实世界中,复杂任务需要多步骤规划和执行:先搜索信息,再分析数据,最后生成报告。这就是 Agent(智能体)的世界。本章详解 Agent 概念、ReAct 模式、Spring AI 的编排支持,以及多 Agent 协作实战。
一、开篇:从单次调用到自主规划
第 9 章的 Function Calling 解决了"AI 能做事"的问题。但有个局限:AI 只能做单次调用。
用户问"北京天气怎么样?"------AI 调一次天气工具,返回结果,结束。
但真实场景中,用户的问题往往需要多步骤才能完成:
"帮我调研一下 Spring AI 和 LangChain4j 的对比,生成一份技术选型报告。"
这个任务需要:
- 搜索 Spring AI 的资料(多次搜索)
- 搜索 LangChain4j 的资料(多次搜索)
- 对比分析两个框架的优劣
- 整理成结构化的报告
Function Calling 做不到------它只支持"用户提问 -> AI 调工具 -> 返回结果"的单次循环。对于需要规划、决策、多步执行的复杂任务,你需要 Agent。
Agent = AI + 工具 + 自主决策能力。 Agent 能自己决定"先做什么、后做什么、什么时候做完"------这就是 Agent 和 Function Calling 的本质区别。
二、什么是 Agent
2.1 定义
Agent(智能体)是一个能自主感知环境、做出决策、执行行动、观察结果,并根据结果调整下一步行动的 AI 系统。
简单说:
- Function Calling:你告诉 AI "调用这个工具",AI 照做
- Agent:你告诉 AI "帮我完成这个任务",AI 自己规划怎么做、做什么、按什么顺序做
类比:Function Calling 像是一个严格执行指令的员工------你说查订单,他就查订单,查完就等着。Agent 像是一个能独当一面的项目经理------你说"帮我出一份调研报告",他自己分解任务、安排资源、控制进度、交付成果。
2.2 Agent vs Function Calling 对比
| 维度 | Function Calling | Agent |
|---|---|---|
| 决策权 | AI 被动响应:用户问 -> AI 调工具 -> 结束 | AI 主动规划:用户给目标 -> AI 拆解步骤 -> 执行 -> 判断是否完成 |
| 调用次数 | 单次(或简单的工具链) | 多次、多轮、可嵌套 |
| 步骤规划 | 无------AI 不需要规划,直接调用 | 有------AI 需要先思考"怎么完成这个任务" |
| 循环控制 | 框架控制:调完工具就结束 | Agent 控制:自己判断"任务完成了吗?没完成就继续" |
| 适用场景 | 简单查询:查天气、查订单 | 复杂任务:调研报告、数据分析、多源信息整合 |
| 成本 | 低(1-2 次 LLM 调用) | 高(可能 5-10+ 次 LLM 调用) |
| 延迟 | 低(< 5s) | 高(可能 30s+) |
| 可控性 | 高(行为可预测) | 低(AI 的决策路径不确定) |
2.3 Agent 的核心循环
Agent 的运行基于一个核心循环------感知、推理、行动、观察:
+----------------------------------------------------------+
| Agent 核心循环 |
| |
| +-----------+ +-----------+ +-----------+ |
| | 感知 | -> | 推理 | -> | 行动 | |
| | (Perceive) | | (Reason) | | (Act) | |
| | | | | | | |
| | 接收任务 | | 分析当前 | | 调用工具 | |
| | 读取上下文 | | 规划步骤 | | 搜索信息 | |
| | 理解目标 | | 选择工具 | | 生成内容 | |
| +-----------+ +-----------+ +-----------+ |
| | |
| v |
| +-----------+ |
| | 观察 | |
| | (Observe) | |
| | | |
| | 查看结果 | |
| | 评估进展 | |
| +-----------+ |
| | |
| 任务完成了? ----+-----> 输出结果 |
| | No |
| v |
| 回到"推理" |
| 重新规划下一步 |
+----------------------------------------------------------+
关键点:Agent 循环是 AI 自主控制的。 它不是你预设的流程图,而是 AI 在每一步根据当前状态自己决定下一步做什么。这是 Agent 和传统工作流的根本区别。
三、Spring AI 的 Agent 支持
3.1 Graph / Workflow 概念
Spring AI 2.0.0 引入了 Agent 编排的抽象,核心概念是 Graph(图) 和 Workflow(工作流)。
Graph 是一个有向图结构,每个节点代表一个处理步骤,边代表步骤之间的流转关系。Agent 的执行就是在图上"走"的过程。
[开始节点] --> [规划节点] --> [搜索节点] --> [分析节点] --> [输出节点]
^ | |
| v |
+--- [判断:需要更多信息?] ---+
|
v (不需要)
[生成报告] --> [结束节点]
Workflow 是 Graph 的一种具体化------预定义好节点和边的顺序,Agent 按固定流程执行。
3.2 编排节点类型
Spring AI 支持以下几种编排节点类型:
| 节点类型 | 说明 | 适用场景 |
|---|---|---|
| ChatNode | 调用大模型进行推理或生成 | Agent 的"思考"步骤 |
| ToolNode | 调用工具函数 | Agent 的"行动"步骤 |
| ConditionNode | 条件判断,决定下一步走哪个分支 | Agent 的"决策"步骤 |
| RouterNode | 根据输入路由到不同子图 | 多 Agent 协作时的分发 |
| SubGraph | 嵌套一个子图 | 复杂任务的拆分 |
3.3 Spring AI Agent 的基本结构
java
/**
* Spring AI Agent 的基本结构(概念性代码)
*
* 核心思路:定义一组节点和边,让 AI 在节点间自主流转。
* 每个节点执行一个动作(思考/调工具/判断),
* AI 根据执行结果决定下一步去哪个节点。
*/
@Configuration
public class AgentConfig {
@Bean
public ChatClient agentClient(ChatClient.Builder builder,
SearchTools searchTools,
AnalysisTools analysisTools) {
return builder
.defaultSystem("""
你是一个调研助手。你的任务是:
1. 分析用户的调研需求
2. 搜索相关信息
3. 整理分析
4. 生成调研报告
你可以多次调用搜索工具来收集信息。
收集到足够信息后,直接生成最终报告。
""")
.defaultTools(searchTools, analysisTools)
.build();
}
}
注意:Spring AI 2.0.0 的 Agent API 仍在快速演进中。本章讲解的是核心概念和设计思路,具体 API 可能随版本更新而变化。推荐关注 Spring AI 官方文档获取最新 API。
四、ReAct 模式详解
4.1 什么是 ReAct?
ReAct(Reasoning + Action)是目前最常用的 Agent 模式,由 Yao et al. 在 2023 年提出。
核心思想:Agent 的每一步都同时包含"推理"(Thought)和"行动"(Action),交替进行。
Thought 1:用户要调研 Spring AI,我需要先搜索基本信息
Action 1:调用 search("Spring AI 框架介绍")
Observation 1:Spring AI 是 Spring 官方的 AI 集成框架...
Thought 2:信息不够,我还需要了解它的核心特性
Action 2:调用 search("Spring AI 核心特性 功能")
Observation 2:Spring AI 支持 ChatClient、RAG、Function Calling...
Thought 3:还需要和 LangChain4j 对比
Action 3:调用 search("LangChain4j 功能特性")
Observation 3:LangChain4j 是一个 Java AI 框架...
Thought 4:信息足够了,可以开始写对比报告
Action 4:(不再调用工具,直接生成报告)
Output:根据调研结果,Spring AI 和 LangChain4j 的对比如下...
4.2 ReAct vs Chain-of-Thought
| 维度 | Chain-of-Thought (CoT) | ReAct |
|---|---|---|
| 能做什么 | 只能"想"(推理) | 能"想"也能"做"(推理+行动) |
| 获取信息 | 只依赖 Prompt 中的已有信息 | 可以主动调用工具获取新信息 |
| 适用场景 | 数学推理、逻辑推理 | 开放式调研、信息收集、数据分析 |
| 输出 | 一串推理步骤 + 最终答案 | 推理步骤 + 行动 + 观察结果 + 最终答案 |
| 比喻 | 闭卷考试(只能用脑子想) | 开卷考试(可以查资料) |
CoT 是 ReAct 的基础------ReAct 本质上是 CoT + 工具调用的结合。
4.3 ReAct 模式代码框架
以下是基于 Spring AI 实现 ReAct 模式的代码框架:
java
package com.example.springaidemo.agent;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* ReAct Agent 服务
*
* 核心思路:在 System Prompt 中要求 AI 按 ReAct 格式输出
* (Thought -> Action -> Observation 循环),
* 框架解析 AI 输出,自动执行工具,将结果拼入上下文,
* 然后让 AI 继续推理。
*
* Spring AI 的 Function Calling 机制已经内置了 ReAct 循环:
* - AI 返回工具调用 = Action
* - Spring AI 执行工具 = Observation
* - AI 收到结果后继续推理 = Thought
*
* 你只需要配置好 System Prompt 和工具,框架自动完成循环。
*/
@Service
public class ReactAgentService {
private final ChatClient agentClient;
public ReactAgentService(ChatClient agentClient) {
this.agentClient = agentClient;
}
/**
* 执行 ReAct 任务
*
* 关键:设置 maxIterations 限制循环次数,防止无限循环。
*
* Spring AI 内部的执行流程:
* 1. 发送任务 + 工具列表给 AI
* 2. AI 输出 Thought(可能包含 Action = 工具调用)
* 3. 如果有 Action -> 执行工具 -> Observation -> 回到步骤 1
* 4. 如果没有 Action -> AI 认为任务完成 -> 输出最终结果
* 5. 重复直到 AI 输出最终结果 或 达到 maxIterations
*/
public String execute(String task) {
return agentClient.prompt()
.user(task)
.call()
.content();
}
}
4.4 ReAct Agent 配置
java
@Configuration
public class ReactAgentConfig {
@Bean
public ChatClient reactAgentClient(
ChatClient.Builder builder,
SearchTools searchTools,
CalculatorTools calcTools) {
return builder
.defaultSystem("""
你是一个 ReAct 智能体。你需要通过思考和行动来完成任务。
工作方式:
1. 仔细分析任务,确定需要哪些信息
2. 逐步调用工具收集信息(可以多次调用)
3. 信息充分后,综合分析并给出最终答案
注意事项:
- 不要一次调用太多工具,每步只调用最需要的 1-2 个
- 如果工具返回的信息不够,继续调用其他工具补充
- 收集到足够信息后,直接给出最终答案,不要继续调用工具
- 最终回答要结构化,有层次
""")
.defaultTools(searchTools, calcTools)
.build();
}
}
java
/**
* 搜索工具
*/
@Component
public class SearchTools {
@Tool(description = "搜索互联网信息。当需要查询某个话题的最新信息、资料或数据时调用。")
public String searchWeb(
@ToolParam(description = "搜索关键词") String query) {
// 调用搜索 API(实际项目中替换为真实的搜索服务)
return searchService.search(query);
}
}
/**
* 计算工具
*/
@Component
public class CalculatorTools {
@Tool(description = "执行数学计算。当需要计算数值、百分比、平均值等时调用。")
public String calculate(
@ToolParam(description = "数学表达式,如 '100 * 0.15' 或 '(200 + 300) / 2'")
String expression) {
try {
// 简易表达式求值(生产环境用安全的数学表达式解析库)
return String.valueOf(scriptEngine.eval(expression));
} catch (Exception e) {
return "计算错误:" + e.getMessage();
}
}
}
4.5 关键设计要点
1. 限制最大循环次数
ReAct 模式的最大风险是无限循环------AI 不断调用工具,永远不输出最终结果。Spring AI 通过配置可以限制最大工具调用次数:
java
// 通过 ChatOptions 设置最大工具调用轮次
return builder
.defaultChatOptions(ChatOptions.builder()
.maxIterations(10) // 最多执行 10 轮工具调用
.build())
.build();
2. 在 System Prompt 中明确终止条件
告诉 AI "什么时候该停",减少不必要的工具调用:
停止调用的条件:
- 已经收集到足够的信息来回答用户的问题
- 工具返回的信息和之前重复
- 已经调用了 3 次工具还没有新信息
3. 工具返回结果要简洁
每次工具调用的结果都会拼入 Prompt,消耗 Token。工具返回结果要尽量简洁、结构化,避免返回大量无关信息。
五、多 Agent 协作
5.1 什么时候需要多 Agent?
单个 Agent 能处理大多数任务,但当任务足够复杂或需要不同专业能力时,多 Agent 协作更合适:
- 能力差异大:一个 Agent 擅长搜索,另一个擅长代码分析,第三个擅长写作
- 任务可并行:多个独立子任务可以同时执行
- 需要专业分工:不同 Agent 负责不同领域,各自有专属的工具集
- 需要层级管理:一个"管理者 Agent"分配任务给多个"执行者 Agent"
5.2 三种协作模式
模式一:串行协作
Agent A 完成任务 -> 结果传给 Agent B -> Agent B 继续 -> 结果传给 Agent C
搜索 Agent 分析 Agent 写作 Agent
+-----------+ 搜索结果 +-----------+ 分析结果 +-----------+
| 搜索资料 | ----------> | 数据分析 | ----------> | 生成报告 |
| 收集信息 | | 提炼要点 | | 润色排版 |
+-----------+ +-----------+ +-----------+
适用场景:前后有依赖关系的任务(必须先搜完资料,才能分析;分析完才能写报告)。
模式二:并行协作
多个 Agent 同时执行不同子任务,最后汇总结果。
+-- 搜索 Agent A:搜索 Spring AI 资料
用户任务 --> 分发 --+-- 搜索 Agent B:搜索 LangChain4j 资料
+-- 搜索 Agent C:搜索其他框架资料
|
v
汇总 Agent:整合三份搜索结果,生成对比报告
适用场景:子任务之间互相独立,可以同时进行(如分别搜索不同来源的信息)。
模式三:层级协作
一个"管理者 Agent"负责规划和分配任务,多个"执行者 Agent"负责具体执行。
+------------------+
| 管理者 Agent |
| (规划 + 分配) |
+------------------+
| | |
+-----------+ | +-----------+
v v v
+-----------+ +-----------+ +-----------+
| 搜索 Agent | | 分析 Agent | | 写作 Agent |
+-----------+ +-----------+ +-----------+
适用场景:复杂项目,需要协调多个专业 Agent 完成不同子任务。
5.3 多 Agent 的实现思路
java
/**
* 多 Agent 协作框架(概念性代码)
*
* 核心思路:每个 Agent 有自己的 ChatClient、System Prompt 和工具集。
* 管理者 Agent 负责拆解任务、分配子任务、汇总结果。
*/
@Service
public class MultiAgentOrchestrator {
private final ChatClient managerAgent;
private final ChatClient searchAgent;
private final ChatClient analysisAgent;
private final ChatClient writerAgent;
/**
* 串行协作:搜索 -> 分析 -> 写作
*/
public String sequentialExecute(String task) {
// Step 1:搜索 Agent 收集信息
String searchResult = searchAgent.prompt()
.user("请搜索以下主题的相关信息:" + task)
.call()
.content();
// Step 2:分析 Agent 分析信息
String analysisResult = analysisAgent.prompt()
.user("请分析以下信息,提炼关键要点:\n" + searchResult)
.call()
.content();
// Step 3:写作 Agent 生成最终报告
String report = writerAgent.prompt()
.user("请基于以下分析结果,生成结构化的报告:\n" + analysisResult)
.call()
.content();
return report;
}
/**
* 并行协作:多个搜索 Agent 同时搜索不同方向
*/
public String parallelExecute(String task) {
// 并行执行多个搜索任务(Java 21 虚拟线程)
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
List<Future<String>> futures = List.of(
executor.submit(() -> searchAgent.prompt()
.user("搜索:" + task + " 的最新动态").call().content()),
executor.submit(() -> searchAgent.prompt()
.user("搜索:" + task + " 的技术架构").call().content()),
executor.submit(() -> searchAgent.prompt()
.user("搜索:" + task + " 的优缺点").call().content())
);
// 汇总所有搜索结果
StringBuilder allResults = new StringBuilder();
for (Future<String> f : futures) {
allResults.append("---\n").append(f.get()).append("\n");
}
// 管理者 Agent 汇总
return managerAgent.prompt()
.user("请综合以下信息,生成一份完整的调研报告:\n" + allResults)
.call()
.content();
}
}
}
六、实战:调研 Agent
6.1 需求
用户输入一个调研主题(如"Spring AI vs LangChain4j 技术选型"),Agent 自动:
- 搜索相关信息
- 分析对比
- 生成结构化的调研报告
6.2 设计思路
采用 ReAct 模式 + 串行协作:
用户输入调研主题
|
v
ReAct Agent(带搜索工具)
- 自主决定搜索什么关键词
- 搜索多次,直到信息足够
- 输出调研报告
|
v
返回结构化报告
6.3 代码实现
java
package com.example.springaidemo.agent;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;
/**
* 调研 Agent
*
* 一个完整的 ReAct Agent 实现。
* Agent 通过多轮搜索收集信息,最后生成调研报告。
*/
@Service
public class ResearchAgentService {
private final ChatClient researchClient;
public ResearchAgentService(ChatClient researchClient) {
this.researchClient = researchClient;
}
/**
* 执行调研任务
*
* @param topic 调研主题,如"Spring AI vs LangChain4j"
* @return 结构化的调研报告
*/
public String research(String topic) {
return researchClient.prompt()
.user("请调研以下主题:%s".formatted(topic))
.call()
.content();
}
}
java
/**
* 调研 Agent 的搜索工具
*/
@Component
public class ResearchTools {
private final RestClient restClient;
public ResearchTools(RestClient.Builder builder) {
this.restClient = builder.baseUrl("https://api.search.example.com").build();
}
@Tool(description = "搜索技术文章和文档。当需要了解某个技术框架、工具或概念的信息时调用。" +
"返回搜索结果的标题、摘要和链接。")
public String searchArticles(
@ToolParam(description = "搜索关键词,如'Spring AI 功能特性'")
String query) {
try {
return restClient.get()
.uri("/search?q={query}", query)
.retrieve()
.body(String.class);
} catch (Exception e) {
return "搜索失败:" + e.getMessage();
}
}
@Tool(description = "获取指定 URL 的网页内容。" +
"当搜索结果中有一篇看起来很相关的文章,需要查看详细内容时调用。")
public String fetchWebpage(
@ToolParam(description = "网页 URL")
String url) {
try {
return restClient.get()
.uri("/fetch?url={url}", url)
.retrieve()
.body(String.class);
} catch (Exception e) {
return "获取网页失败:" + e.getMessage();
}
}
}
java
/**
* 调研 Agent 配置
*/
@Configuration
public class ResearchAgentConfig {
@Bean
public ChatClient researchClient(
ChatClient.Builder builder,
ResearchTools researchTools) {
return builder
.defaultSystem("""
你是一个专业的技术调研助手。你的任务是通过搜索和分析,生成高质量的调研报告。
工作流程:
1. 分析调研主题,确定需要搜索的关键词
2. 逐步搜索相关资料(可以搜索 3-5 次)
3. 如果某篇文章特别相关,获取详细内容
4. 综合所有信息,生成结构化报告
报告格式:
## 1. 概述
(100字以内的主题概述)
## 2. 详细分析
(分小节分析各个维度)
## 3. 对比总结
(如有对比,用表格展示)
## 4. 结论与建议
(明确的选型建议或结论)
注意:引用信息来源时标注出处。
""")
.defaultTools(researchTools)
.build();
}
}
java
/**
* 调研 Agent Controller
*/
@RestController
@RequestMapping("/api/research")
public class ResearchController {
private final ResearchAgentService researchService;
public ResearchController(ResearchAgentService researchService) {
this.researchService = researchService;
}
/**
* 发起调研任务
*
* 调研 Agent 会自主执行多轮搜索,可能耗时 30-60 秒。
* 建议前端显示"正在调研中..."的加载状态。
*/
@PostMapping
public Map<String, String> research(
@RequestBody Map<String, String> request) {
String topic = request.get("topic");
if (topic == null || topic.isBlank()) {
return Map.of("error", "调研主题不能为空");
}
String report = researchService.research(topic);
return Map.of("topic", topic, "report", report);
}
}
6.4 测试
bash
# 测试调研 Agent
curl -X POST "http://localhost:8080/api/research" \
-H "Content-Type: application/json" \
-d '{"topic": "Spring AI 和 LangChain4j 技术选型对比"}'
# Agent 内部会:
# 1. 搜索 "Spring AI 功能特性"
# 2. 搜索 "LangChain4j 功能特性"
# 3. 搜索 "Spring AI vs LangChain4j 对比"
# 4. 可能获取某篇重要文章的详细内容
# 5. 综合分析,生成结构化报告
#
# 整个过程可能需要 30-60 秒
小结
本章从 Function Calling 到 Agent 的跃迁:
- Agent 定义:AI + 工具 + 自主决策能力。Agent 能自己规划步骤、选择工具、判断完成条件
- 核心循环:感知 -> 推理 -> 行动 -> 观察 -> 循环或终止。AI 在循环中自主控制
- Spring AI 支持:通过 Graph/Workflow 概念编排 Agent,支持 ChatNode、ToolNode、ConditionNode 等节点类型
- ReAct 模式:Reasoning + Action 交替进行。是当前最主流的 Agent 模式。Spring AI 的 Function Calling 机制已内置 ReAct 循环
- 多 Agent 协作:串行(依赖关系)、并行(独立子任务)、层级(管理者分配任务)
- 实战:调研 Agent ------ 自主搜索、分析、生成报告
Agent 的核心权衡是"智能"和"可控":Agent 越自主,行为越不可预测,调试和测试难度也越大。建议从简单的 ReAct 模式开始,逐步增加复杂度。同时务必设置最大迭代次数,防止无限循环。
