Spring AI Alibaba ReAct Agent实战:从Tool Calling到Agent,企业AI复杂业务该如何设计?
目录
1. 为什么复杂业务任务需要Agent?
2. ReAct Agent到底是怎么工作的?
3. 实战:设计一个订单异常处理Agent
3.1 定义业务请求和返回对象
4. 使用Spring AI Alibaba实现ReAct Agent
4.1 引入Agent Framework
4.2 创建订单查询Tool
4.3 创建ReAct Agent
4.4 执行Agent
5. 一次完整任务到底是如何执行的?
6. 企业Agent真正难的不是“会调用”,而是“可控”
6.1 Agent必须有停止条件
6.2 Tool调用失败不能让Agent自己猜
6.3 查询型Tool和操作型Tool必须分级
6.4 Agent必须可观测
7. Agent和Workflow到底怎么选?
Workflow更适合什么?
Agent更适合什么?
企业项目中更常见的是混合模式
前言
如果只是让大模型回答问题,现在的实现已经非常成熟。
如果需要获取企业系统中的实时数据,也可以通过 Tool Calling,让模型调用订单、库存、物流、知识库等业务能力。
但真正开始做企业 AI 项目后,会遇到另一类需求:
用户给你的不是一个明确的操作,而是一个目标。
比如:
我的订单 A1001 三天没发货了,帮我看看是什么原因,如果是库存问题,就帮我创建一个客服工单。
这句话看起来简单,但真正执行起来并不是调用一个接口就能完成。
系统至少需要:
查询订单 ↓ 判断订单当前状态 ↓ 根据结果决定是否查询物流 ↓ 必要时继续查询库存 ↓ 判断异常原因 ↓ 决定是否创建工单 ↓ 汇总执行结果其中最关键的一点是:
后面执行哪一步,并不能在任务开始前完全确定。
如果订单已经发货,下一步可能查询物流。
如果订单还没出库,下一步可能查询库存。
如果库存正常,又可能需要检查订单审核状态。
执行路径依赖上一步的结果动态变化。
这就是 Agent 开始发挥作用的地方。
这篇文章不准备只演示如何创建一个ReactAgent,而是通过一个企业订单异常处理场景,完整看看:
- Tool Calling 和 Agent 到底差在哪里;
- ReAct Agent 如何完成多步骤任务;
- Spring AI Alibaba 如何实现 ReAct Agent;
- Tool 应该如何设计;
- 企业项目为什么不能让 Agent 无限自主执行;
- Agent 和 Workflow 到底应该怎么选。
1. 为什么复杂业务任务需要Agent?
先从最简单的 Tool Calling 开始。
用户问:
查询订单 A1001 当前是什么状态。
模型只需要:
识别用户意图 ↓ 选择 queryOrder ↓ 生成 orderNo=A1001 ↓ 执行 Tool ↓ 获得订单状态 ↓ 生成最终回答这是一个非常典型的 Tool Calling 场景。
但是,如果用户的问题变成:
我的订单 A1001 一直没有发货,帮我查一下原因,如果库存异常就创建客服工单。
问题就发生了变化。
因为模型在任务开始之前,并不知道究竟需要调用几个 Tool。
可能是:
queryOrder ↓ queryInventory ↓ createTicket也可能是:
queryOrder ↓ queryLogistics甚至查询一次之后就已经能够回答用户。
这时候真正需要解决的问题已经不是:
调用哪个 Tool?
而是:
为了完成用户目标,下一步应该做什么?
我现在更倾向于这样理解两者的区别:
Tool Calling解决的是“模型如何使用一个外部能力”。
而:
Agent解决的是“模型如何围绕一个目标,持续决定下一步行动”。
Spring AI Alibaba 当前的ReactAgent本身就是按照这种循环方式执行:模型进行决策,需要外部能力时调用 Tool,读取 Tool 返回结果后继续执行,直到模型生成最终答案或者达到停止条件。
2. ReAct Agent到底是怎么工作的?
ReAct 来自两个单词:
Reasoning + Acting它的核心思想并不复杂。
Agent 不要求模型第一次就生成完整答案,而是允许它在任务执行过程中不断经历:
判断当前状态 ↓ 选择下一步行动 ↓ 执行行动 ↓ 观察执行结果 ↓ 再次判断直到任务完成。
为了避免把 ReAct 理解得过于抽象,我们直接套到订单场景里。
用户:
订单 A1001 为什么还没发货?如果是库存问题,就创建一个客服工单。
Agent 第一步需要获得订单信息:
Action queryOrder(A1001)Tool 返回:
订单状态:待发货 商品SKU:SKU-10086现在 Agent 获得了新的业务事实。
下一步可能继续:
Action queryInventory(SKU-10086)得到:
可用库存:0 预计补货时间:2026-08-17这时候 Agent 才能确定:
订单无法发货与库存不足有关。
然后根据用户最开始给出的目标:
Action createTicket(...)创建工单成功以后,系统已经拥有足够信息,最后再生成结果:
订单 A1001 当前处于待发货状态,商品库存不足,预计 8 月 17 日补货。我已经为该订单创建客服工单 T20260815001。
这里真正重要的不是它调用了三个 Tool。
而是:
第二个、第三个 Tool 是否执行,是由前面 Tool 的结果决定的。
这就是动态任务执行。
3. 实战:设计一个订单异常处理Agent
理解执行方式以后,再开始写代码。
这次准备四个业务能力:
queryOrder queryInventory queryLogistics createTicket分别负责:
| Tool | 作用 |
|---|---|
| queryOrder | 查询订单及商品信息 |
| queryInventory | 查询商品库存 |
| queryLogistics | 查询已发货订单物流 |
| createTicket | 创建客服工单 |
这里有一个我认为非常重要的设计原则:
不要给 Agent 一个万能 Tool。
例如直接设计:
processOrder(String orderNo)然后在方法内部:
查订单 查库存 查物流 创建工单技术上当然可以。
但这样一来,真正决定执行流程的其实还是 Java 代码。
Agent 只是调用了一个传统业务接口,并没有真正参与任务决策。
更合理的方式是把稳定、明确的业务能力拆出来:
Agent ├── queryOrder ├── queryInventory ├── queryLogistics └── createTicket然后由 Agent 根据当前任务状态动态组合。
但是 Tool 也不能拆得无限细。
我一般会遵循:
一个Tool完成一个完整、明确、可审计的业务能力。
比如:
queryOrder是合理的。
但如果拆成:
queryOrderStatus queryOrderAmount queryOrderSku queryOrderCreateTime就很容易变成过度拆分。
Tool 数量增加以后,不仅增加模型选择难度,Tool 名称、描述和输入 Schema 本身也都会成为模型调用上下文的一部分。Spring AI 官方也强调,Tool 的名称、描述和参数 Schema 会帮助模型判断什么时候以及如何调用工具。
3.1 定义业务请求和返回对象
首先准备几个简单对象:
public record OrderQuery(String orderNo) { } public record OrderInfo(String orderNo, String skuId, String status) { } public record InventoryQuery(String skuId) { } public record InventoryInfo(String skuId, Integer availableStock, String expectedRestockTime) { } public record LogisticsQuery(String orderNo) { } public record LogisticsInfo(String orderNo, String status, String latestNode) { } public record TicketRequest(String orderNo, String reason) { } public record TicketResult(String ticketNo, String status) { }真实项目中这些对象背后可以继续调用:
MySQL Redis 订单中心 库存中心 物流服务 客服工单系统Agent 不需要知道底层数据从哪里来。
它只需要知道:
我拥有哪些稳定的业务能力。
4. 使用Spring AI Alibaba实现ReAct Agent
当前 Spring AI Alibaba Agent Framework 可以直接通过ReactAgent.builder()创建 Agent,并向 Agent 注册ToolCallback。官方文档也提供了FunctionToolCallback、Memory、Hooks 和 Interceptors 等扩展方式。
4.1 引入Agent Framework
以官方 Agent Framework 文档中的依赖方式为例:
<dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-agent-framework</artifactId> <version>${spring-ai-alibaba.version}</version> </dependency>模型 Starter 根据项目实际使用的模型选择。
例如使用 DashScope:
<dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-starter-dashscope</artifactId> <version>${spring-ai-alibaba.version}</version> </dependency>实际项目建议通过 BOM 或统一版本变量管理依赖版本,避免不同模块分别写死版本。
4.2 创建订单查询Tool
这里使用FunctionToolCallback。
@Component public class QueryOrderTool implements Function<OrderQuery, OrderInfo> { private final OrderService orderService; public QueryOrderTool(OrderService orderService) { this.orderService = orderService; } @Override public OrderInfo apply(OrderQuery request) { return orderService.queryOrder( request.orderNo() ); } }注册 Tool:
ToolCallback queryOrderTool = FunctionToolCallback.builder( "queryOrder", queryOrderToolFunction) .description(""" 查询订单基础信息。 当需要确认订单是否存在、 当前订单状态以及订单对应商品时使用。 """) .inputType(OrderQuery.class) .build();Spring AI 会根据输入类型生成参数 Schema,模型根据 Tool 名称、描述以及 Schema 判断如何调用工具。
其他几个 Tool 采用相同方式:
ToolCallback queryInventoryTool = FunctionToolCallback.builder( "queryInventory", queryInventoryFunction) .description(""" 查询商品当前可用库存。 当需要判断订单是否因为库存不足 导致无法发货时使用。 """) .inputType(InventoryQuery.class) .build();物流:
ToolCallback queryLogisticsTool = FunctionToolCallback.builder( "queryLogistics", queryLogisticsFunction) .description(""" 查询已发货订单的物流状态。 仅当订单已经发货, 需要确定物流进度时使用。 """) .inputType(LogisticsQuery.class) .build();创建工单:
ToolCallback createTicketTool = FunctionToolCallback.builder( "createTicket", createTicketFunction) .description(""" 为异常订单创建客服工单。 当已经确认订单存在异常, 并且需要客服继续处理时使用。 不允许在没有明确异常原因时创建工单。 """) .inputType(TicketRequest.class) .build();这里尤其不要忽略 Tool Description。
模型看不到:
createTicket()里面究竟有什么业务代码。
它判断“什么时候调用”,主要依赖的就是 Tool Definition。
所以 Tool Description 本质上也是 Agent Prompt Engineering 的一部分。
4.3 创建ReAct Agent
准备系统指令:
String instruction = """ 你是企业订单异常处理助手。 你的目标是帮助用户分析订单异常原因。 执行原则: 1. 涉及实时订单信息时必须调用工具查询, 不允许猜测订单状态。 2. 根据订单当前状态决定后续动作。 3. 未发货订单可以继续检查库存。 4. 已发货订单可以查询物流状态。 5. 只有确认存在需要人工继续处理的异常时, 才允许创建客服工单。 6. 不允许伪造订单、库存、物流或工单信息。 7. 工具调用失败时明确告诉用户, 不允许根据经验补全结果。 完成任务后,向用户说明: - 当前订单状态 - 判断出的异常原因 - 已执行的处理动作 """;创建 Agent:
ReactAgent orderAgent = ReactAgent.builder() .name("order_exception_agent") .model(chatModel) .instruction(instruction) .tools( queryOrderTool, queryInventoryTool, queryLogisticsTool, createTicketTool ) .saver(new MemorySaver()) .build();到这里,一个基础 ReAct Agent 就已经完成。
4.4 执行Agent
调用非常简单:
AssistantMessage response = orderAgent.call(""" 我的订单A1001三天没有发货了, 帮我看看是什么原因。 如果确认是库存异常, 帮我创建一个客服工单。 """); System.out.println(response.getText());可能得到:
订单A1001当前状态为待发货。 经查询,该订单商品SKU-10086当前可用库存为0, 预计8月17日补货,因此暂时无法正常发货。 已为该订单创建客服工单: T20260815001。 客服人员将继续跟进该订单。代码看起来并不复杂。
但和普通 Tool Calling 相比,真正发生变化的是:
执行流程不再完全由开发人员提前写死。
5. 一次完整任务到底是如何执行的?
假设订单数据如下:
订单:A1001 状态:待发货 SKU:SKU-10086库存:
SKU-10086 可用库存:0 预计补货:2026-08-17用户输入:
订单A1001一直没有发货, 帮我查下什么原因。 如果库存有问题, 就创建一个客服工单。Agent 第一次处理时并不知道:
订单是否存在 订单有没有发货 商品是什么 库存是否正常 是否真的需要创建工单因此第一步会调用:
queryOrder得到:
{ "orderNo": "A1001", "skuId": "SKU-10086", "status": "待发货" }因为订单没有发货,物流查询没有意义。
Agent 可以继续选择:
queryInventory得到:
{ "skuId": "SKU-10086", "availableStock": 0, "expectedRestockTime": "2026-08-17" }现在才真正确定:
订单未发货 + 库存为0 = 库存异常用户又提前授权:
如果库存异常,就创建客服工单。
因此继续:
createTicket得到:
{ "ticketNo": "T20260815001", "status": "CREATED" }信息已经足够,Agent 停止调用 Tool,生成最终回答。
这就是一次完整的 ReAct 循环。
这里也能看出:
Agent真正有价值的地方并不是“能够调用很多Tool”,而是能够根据中间结果调整执行路径。
如果订单第一次查询出来:
status = 已发货那后面合理的路径就应该变成:
queryOrder ↓ queryLogistics ↓ 返回物流状态而不是继续查询库存和创建工单。
同一个用户入口,因为业务状态不同,可以形成完全不同的执行链。
6. 企业Agent真正难的不是“会调用”,而是“可控”
如果文章写到这里就结束,其实只能算一个 Agent Demo。
真正进入企业项目以后,我认为 Agent 最难解决的问题并不是:
怎么让模型调用 Tool?
而是:
怎么保证它只在允许的范围里行动。
6.1 Agent必须有停止条件
ReAct 本质上是一个循环。
正常情况:
Model ↓ Tool ↓ Model ↓ Tool ↓ Model ↓ Final Answer但异常情况下完全可能出现:
queryOrder ↓ queryInventory ↓ queryOrder ↓ queryInventory ↓ queryOrder ↓ ……如果没有控制,就可能带来:
- 模型调用次数持续增加;
- Token成本不断增加;
- 外部接口反复调用;
- 整体请求长时间不结束。
Spring AI Alibaba 当前 Agent Framework 已经提供ModelCallLimitHook对模型调用次数进行限制,用来避免失控循环和过高调用成本。
例如:
ModelCallLimitHook modelCallLimit = ModelCallLimitHook.builder() .runLimit(6) .build();加入 Agent:
ReactAgent orderAgent = ReactAgent.builder() .name("order_exception_agent") .model(chatModel) .instruction(instruction) .tools( queryOrderTool, queryInventoryTool, queryLogisticsTool, createTicketTool ) .hooks(modelCallLimit) .saver(new MemorySaver()) .build();实际生产中,我还会在 Agent 外层增加整体 Timeout。
最终形成:
Agent最大执行步数 + 模型调用限制 + Tool自身Timeout + 整个Agent链路Timeout多层控制。
6.2 Tool调用失败不能让Agent自己猜
真实企业环境一定会出现:
数据库超时 RPC失败 库存中心不可用 物流接口异常 参数错误 订单不存在 权限不足如果 Tool 抛异常以后直接把整个 Agent 打成 500,用户体验很差。
但另一种做法更危险:
Tool失败以后,让模型自己推测结果。
比如库存接口失败,模型却回答:
大概率是库存不足。
这是企业 AI 中必须避免的。
更合理的是把错误转换成明确的 Tool Result:
INVENTORY_SERVICE_UNAVAILABLE然后告诉模型:
当前无法确认库存情况,不允许继续推断库存异常。
Spring AI Alibaba 当前也提供 Tool Interceptor,可用于工具错误处理、重试和失败结果转换;官方还提供了ToolRetryInterceptor对适合重试的瞬时错误进行有限重试。
但重试也不能无脑使用。
例如:
queryInventory查询失败,可以安全重试一次。
而:
createTicket如果请求超时,却不能直接判断“没有创建成功”。
这时候首先应该依赖:
幂等Key + 业务状态查询确认第一次是否已经成功,再决定是否重试。
这仍然是传统后端工程问题。
Agent 并没有改变这一点。
6.3 查询型Tool和操作型Tool必须分级
我不会让所有 Tool 都拥有同样的执行权限。
例如:
低风险
查询订单 查询库存 查询物流 查询知识库通常经过身份和数据权限校验后,可以允许 Agent 自动执行。
中风险
创建客服工单 生成报表 创建草稿 提交内部任务可以根据业务规则决定是否允许自动执行。
高风险
退款 删除数据 最终审批 修改权限 付款 批量修改我不会简单地通过 Prompt:
执行之前一定要询问用户。
然后就认为系统安全了。
因为 Prompt 是模型约束。
真正的企业安全边界应该是:
代码权限 + 业务规则 + 审批/确认 + 审计高风险 Tool 最好通过 Human-in-the-loop 进行人工确认。
Spring AI Alibaba 当前已经提供HumanInTheLoopHook,可以针对特定 Tool 暂停 Agent 执行,等待人工批准、修改或拒绝;这种中断恢复需要配合持久化 Checkpointer 保存 Agent 状态。
逻辑可以设计成:
Agent判断需要退款 ↓ 准备refund Tool Call ↓ 暂停 ↓ 人工/用户确认 ↓ 批准? ├── 否 → 取消 └── 是 → 真正执行refund这也是我认为企业 Agent 和 Demo Agent 最大的差别之一:
不是Agent越自主越高级。
企业真正需要的是:
在可控边界内自主。
6.4 Agent必须可观测
传统接口出问题,我们通常会看:
TraceId 接口耗时 SQL RPC 异常日志到了 Agent 场景还要再增加一层。
至少应该能够知道:
一次Agent任务执行了多少步 调用了多少次模型 调用了哪些Tool Tool参数是什么 Tool执行是否成功 每一步耗时多少 整个Agent耗时多少 消耗多少Token 最终任务是否完成否则用户说:
AI昨天把一个订单处理错了。
开发人员连它当时:
调用过什么Tool 为什么进入下一步 哪一个Tool返回异常都不知道,就几乎没有办法定位问题。
所以 Agent 真正生产化以后:
Tracing + Tool Audit + Token Usage + Latency + Success Rate都应该纳入监控。
这也是后面做 Agent Evaluation 的基础。
7. Agent和Workflow到底怎么选?
理解 Agent 以后,另一个很容易出现的问题是:
既然 Agent 可以动态决定下一步,是不是复杂业务全部交给 Agent 就行?
我认为恰恰相反。
越是核心业务,越不应该为了“智能”而强行Agent化。
Workflow更适合什么?
如果业务流程天然确定:
创建订单 ↓ 参数校验 ↓ 库存锁定 ↓ 支付 ↓ 生成订单 ↓ 通知这类流程最重要的是:
稳定 可预测 可测试 可回滚显然 Workflow 更合适。
Agent更适合什么?
如果只有目标明确,但具体执行路径不确定:
分析这个订单为什么异常,并根据情况给出处理方案。
系统开始时不知道:
需要查订单? 需要查物流? 需要查库存? 需要查规则? 是否需要创建工单?路径依赖运行时信息动态产生。
这种场景 Agent 更合适。
企业项目中更常见的是混合模式
真正让我更认可的一种设计其实是:
Workflow ↓ 固定确定性流程 ↓ Agent ↓ 处理局部不确定任务 ↓ Human-in-the-loop ↓ Workflow继续执行例如一个售后流程:
用户提交售后申请 ↓ Workflow完成身份和订单校验 ↓ Agent分析问题与相关资料 ↓ Agent给出处理建议 ↓ 人工确认高风险操作 ↓ Workflow执行退款/换货 ↓ 记录审计结果这里:
确定的事情交给代码和Workflow。
不确定的判断交给Agent。
高风险决策交给人。
这也是我现在理解企业 Agent 架构非常重要的一条原则:
Agent不是Workflow的替代品。
它更像是给原来完全确定性的企业系统增加了一块:
能够处理不确定任务的动态决策能力。
总结
如果只是看代码,创建一个 Spring AI AlibabaReactAgent并不复杂:
ReactAgent.builder() .name("order_exception_agent") .model(chatModel) .tools(...) .instruction(...) .build();真正困难的是代码之外的问题。
什么时候应该调用 Tool?
Tool 应该拆多细?
什么时候继续执行?
什么时候停止?
调用失败怎么办?
哪些 Tool 可以自动调用?
哪些操作必须人工确认?
Agent 和 Workflow 应该如何组合?
这些问题才决定一个 Agent 最后是:
一个能够演示的Demo。
还是:
一个真正能够进入企业系统的AI能力。
我现在更愿意把这几个概念理解成:
Tool Calling 让AI拥有“做事的能力” Agent 让AI能够围绕目标 动态决定“下一步做什么” Workflow 负责确定性的业务流程 Human-in-the-loop 负责控制高风险决策所以企业 AI 从 Tool Calling 走向 Agent,并不是简单地:
多调用几个 Tool。
真正发生的变化是:
我们开始把一部分原本必须提前写死在代码中的执行路径,交给模型根据上下文动态决策。
而企业级架构真正需要解决的,则是另一半问题:
如何把这种不确定性,限制在一个确定、可观测、可审计、可控制的边界之内。
这才是 Agent 真正从 Demo 走向生产的开始。
📚 推荐专栏
🏗️企业AI架构与实战
从企业真实落地视角,持续分享RAG、Agent、Workflow、AI Gateway、工程治理与系统架构设计。
👉 https://blog.csdn.net/qupengkun/category_13192323.html
🤖Java转AI技术专栏
从0到1学习AI应用开发,持续分享Spring AI、RAG、Agent与企业级AI项目实战。
👉 https://blog.csdn.net/qupengkun/category_13184360.html
🧩AI开源项目实战
持续分享Java AI业务项目、AI Coding效率工具和工程治理实践。
👉 https://blog.csdn.net/qupengkun/category_13189373.html
🚀AI转型日记
记录一名10年Java开发者从传统后端转向AI应用开发的全过程。
👉 https://blog.csdn.net/qupengkun/category_13183497.html
👨💻 关于作者
QCoding
专注AI应用开发与Java技术实践。
持续分享Spring AI、RAG、Agent、企业级AI项目实战、架构设计与职业成长。