当 Agent 学会“自愈”:Spring AI ReAct 多工具协同下的高并发差旅系统重构实录
当 Agent 学会“自愈”:Spring AI ReAct 多工具协同下的高并发差旅系统重构实录
从“写死流程的编排服务”到“能推理、会补偿、可观测、能扩缩”的生产级 Agent 架构,一次企业差旅系统的完整重构。
一、前言:问题从来不在“能不能调用大模型”,而在“系统能不能稳定完成任务”
很多团队第一次把大模型接入业务系统时,关注点通常是:
- 模型回答够不够聪明
- Prompt 写得好不好
- 工具调用能不能跑通
但真正进入生产后,技术焦点会立刻变化:
- 第三方供应商超时了怎么办
- 一个任务里跨了预算、审批、机票、酒店四个服务,失败后如何补偿
- 高峰期 5000 个并发请求打进来,Agent 会不会把线程池、连接池、模型额度一起打爆
- 模型多调用了两次工具,如何限流、审计、止损
- 出现错误决策后,怎么复盘“它为什么这么做”
这篇文章讨论的不是“如何做一个能演示的 Agent Demo”,而是如何把 Spring AI + ReAct + 多工具协同 真正落到一个 高并发、强约束、可观测、可恢复 的差旅系统中。
我们会围绕一个真实而典型的企业场景展开:员工提交差旅申请后,系统需要在预算约束下自动查询交通与酒店、做方案组合、提交审批、完成预占与下单,并在外部服务抖动、库存变化、预算竞争、审批超时等异常条件下尽量自动恢复。
这正是 Agent 最适合发力、同时也最容易“翻车”的场景。
二、业务背景:差旅系统为什么天然适合 Agent,但又天然难做
2.1 差旅业务的核心特征
一个完整的差旅预订链路,通常至少涉及如下步骤:
- 员工提交出差需求:出发地、目的地、日期、预算、偏好。
- 系统校验预算余额、差旅等级、合规规则。
- 查询航班、高铁、酒店、企业协议价。
- 组合候选方案并做价格、时效、合规性排序。
- 发起审批或走自动审批。
- 审批通过后完成实际下单。
- 成功后扣减预算、生成行程、发送通知。
- 失败时执行释放预算、撤销订单、关闭审批等补偿动作。
从架构视角看,这类业务有四个典型特点:
- 长链路:一次请求跨越多个内部与外部系统。
- 弱一致性:预算、审批、库存、订单并不在同一事务内。
- 高波动:价格、库存、舱位、房态可能分钟级变化。
- 强约束:不能因为模型“觉得合理”就直接扣款或下单。
2.2 传统工作流为什么越来越吃力
过去我们的做法,是在一个 travel-processor 编排服务里用状态机或流程引擎把流程串起来:
申请创建 -> 预算校验 -> 查询航班 -> 查询酒店 -> 生成方案 -> 发起审批 -> 下单 -> 通知这种方式在确定性强、分支少的流程里非常有效,但在差旅业务里会遇到明显瓶颈:
- “航班售罄后改选相近时段高铁”这类策略难以穷举。
- 每接一个新供应商,就要改一次流程编排。
- 异常分支爆炸,
if-else + retry + fallback很快失控。 - 工作流擅长“按图执行”,不擅长“基于上下文动态选择替代方案”。
换句话说,传统编排能解决“流程确定性”,但很难解决“执行期不确定性”。
而差旅系统真正痛的地方,恰恰在执行期。
2.3 一次典型线上事故
某次月初报销周期前,大量员工集中提交出差申请,系统出现如下连锁问题:
- 机票供应商 A 响应时间从 300ms 飙升到 6s。
- 编排服务同步阻塞等待结果,HTTP 工作线程大量堆积。
- 预算服务限流,后续请求批量失败。
- 部分审批单已经创建,但订单未完成,形成脏数据。
- 客服只能人工筛选失败单,逐条重放。
这类事故说明一个问题:
系统缺的不是“重试机制”,而是“具备上下文感知能力的恢复机制”。
这也是我们引入 Agent 的真正原因。
三、为什么是 ReAct:它解决的不是“会聊天”,而是“会决策”
3.1 ReAct 的本质
ReAct = Reasoning + Acting。
它不是让模型直接生成一个答案,而是让模型在任务执行中不断经历以下循环:
理解目标 -> 推理下一步 -> 调用工具 -> 观察结果 -> 修正策略 -> 继续执行这套机制的价值在于,模型不再只充当“文本生成器”,而是升级成一个“任务求解器”。
以差旅预订为例,用户说:
帮我订明天从上海到北京的行程,预算 2800 元,尽量上午到达,酒店离客户公司近一点。
传统接口需要前端把参数拆好、流程写死; ReAct Agent 则可以自己做:
- 先校验预算与差标。
- 再查询航班与高铁。
- 判断时间窗是否满足“上午到达”。
- 查询酒店并按地理位置排序。
- 如果首选酒店满房,自动尝试次优方案。
- 如果机票价格超预算,退而选择高铁。
- 成功后发起审批与预算锁定。
3.2 ReAct 不等于“把所有决定交给模型”
这是生产落地里最容易踩的坑。
ReAct 的正确姿势不是让模型自由发挥,而是:
- 由模型负责推理与选择
- 由系统负责约束边界与执行治理
也就是说:
- 模型决定“查什么”“先做什么”“失败后换什么”
- 平台决定“哪些工具可用”“哪些操作需要审批”“哪些参数必须校验”“哪些动作必须幂等”
生产级 Agent 的关键不是自由度,而是 受控自治。
3.3 ReAct 和工作流引擎的关系
两者不是替代关系,而是分工关系:
- 工作流引擎 适合强约束、确定性、审计要求高的固定流程。
- ReAct Agent 适合弱确定性、多候选路径、需动态试错的任务求解。
最佳实践往往不是“全 Agent 化”,而是:
- 把“动态决策”交给 Agent
- 把“确定执行”交给工作流或领域服务
例如:
- “选航班/选酒店/失败切换供应商”由 Agent 处理
- “审批流转/财务记账/对账归档”由工作流引擎处理
这才是工程上可控的组合。
四、Spring AI 在这里扮演什么角色
4.1 Spring AI 的价值不只是 @Tool
Spring AI 真正适合企业 Java 体系的原因有三点:
- 能和 Spring Boot / Spring Cloud / WebFlux / Micrometer / OpenTelemetry 自然整合
- 支持把领域能力封装为可治理的工具调用接口
- 可以把模型调用、工具执行、记忆、Advisor、可观测串进统一工程体系
从官方文档当前的 1.0 系列能力来看,Spring AI 已经把工具调用抽象为 ToolCallback,同时支持 @Tool 声明式工具、ChatClient 集成、ToolContext 透传上下文等机制,这让它很适合构建企业内部的 Agent Runtime,而不是只做一个轻量 SDK 封装。
4.2 工具调用链路的本质
在 Spring AI 中,一次工具调用的基本过程可以抽象为:
ChatClient 发起请求 -> 把工具定义传给模型 -> 模型决定调用哪个工具 -> Spring AI 根据工具名路由到本地 ToolCallback -> 工具执行并返回结构化结果 -> 结果重新喂给模型 -> 模型决定继续调用工具或输出最终响应这意味着我们真正要设计的,不是“会不会写 @Tool”,而是以下四件更重要的事:
- 工具边界怎么划分
- 工具结果如何结构化表达,方便模型继续推理
- 工具调用如何做限流、超时、鉴权、幂等、审计
- 整个 loop 如何防失控
五、重构目标:把差旅系统从编排服务升级为 Agent Runtime
5.1 旧架构
原有架构大致如下:
Web/App -> Gateway -> travel-processor -> budget-service -> flight-service -> hotel-service -> approval-service -> notification-servicetravel-processor 负责把所有流程硬编码。
这种架构的问题是:
- 编排层越来越厚,业务与异常策略强耦合。
- 每增加一个外部能力,编排服务都要改。
- 下游抖动时,编排层既是瓶颈点,也是放大器。
5.2 新架构
重构后,我们引入一个独立的 travel-agent-service,它不直接承载所有领域逻辑,而是作为 Agent Runtime + Tool Governance Layer。
┌──────────────────────────────────────────────────────────────┐ │ Client / Gateway │ └──────────────────────────────┬───────────────────────────────┘ │ ┌──────────────────────────────▼───────────────────────────────┐ │ travel-agent-service │ │ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ Agent Orchestrator │ │ │ │ - Prompt Template │ │ │ │ - ReAct Loop │ │ │ │ - Step Guard │ │ │ │ - Risk Policy │ │ │ └────────────────────────────────────────────────────────┘ │ │ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ Tool Governance Layer │ │ │ │ - Tool Registry │ │ │ │ - Timeout / Retry / RateLimit │ │ │ │ - Idempotency / Audit / Trace │ │ │ │ - Result Normalization │ │ │ └────────────────────────────────────────────────────────┘ │ └───────┬──────────────────┬──────────────────┬────────────────┘ │ │ │ ▼ ▼ ▼ budget-service inventory-services approval-service │ │ │ └──────────────┬───┴───────────