当前位置: 首页 > news >正文

当 Agent 学会“自愈”:Spring AI ReAct 多工具协同下的高并发差旅系统重构实录

当 Agent 学会“自愈”:Spring AI ReAct 多工具协同下的高并发差旅系统重构实录

从“写死流程的编排服务”到“能推理、会补偿、可观测、能扩缩”的生产级 Agent 架构,一次企业差旅系统的完整重构。


一、前言:问题从来不在“能不能调用大模型”,而在“系统能不能稳定完成任务”

很多团队第一次把大模型接入业务系统时,关注点通常是:

  • 模型回答够不够聪明
  • Prompt 写得好不好
  • 工具调用能不能跑通

但真正进入生产后,技术焦点会立刻变化:

  • 第三方供应商超时了怎么办
  • 一个任务里跨了预算、审批、机票、酒店四个服务,失败后如何补偿
  • 高峰期 5000 个并发请求打进来,Agent 会不会把线程池、连接池、模型额度一起打爆
  • 模型多调用了两次工具,如何限流、审计、止损
  • 出现错误决策后,怎么复盘“它为什么这么做”

这篇文章讨论的不是“如何做一个能演示的 Agent Demo”,而是如何把 Spring AI + ReAct + 多工具协同 真正落到一个 高并发、强约束、可观测、可恢复 的差旅系统中。

我们会围绕一个真实而典型的企业场景展开:员工提交差旅申请后,系统需要在预算约束下自动查询交通与酒店、做方案组合、提交审批、完成预占与下单,并在外部服务抖动、库存变化、预算竞争、审批超时等异常条件下尽量自动恢复。

这正是 Agent 最适合发力、同时也最容易“翻车”的场景。


二、业务背景:差旅系统为什么天然适合 Agent,但又天然难做

2.1 差旅业务的核心特征

一个完整的差旅预订链路,通常至少涉及如下步骤:

  1. 员工提交出差需求:出发地、目的地、日期、预算、偏好。
  2. 系统校验预算余额、差旅等级、合规规则。
  3. 查询航班、高铁、酒店、企业协议价。
  4. 组合候选方案并做价格、时效、合规性排序。
  5. 发起审批或走自动审批。
  6. 审批通过后完成实际下单。
  7. 成功后扣减预算、生成行程、发送通知。
  8. 失败时执行释放预算、撤销订单、关闭审批等补偿动作。

从架构视角看,这类业务有四个典型特点:

  • 长链路:一次请求跨越多个内部与外部系统。
  • 弱一致性:预算、审批、库存、订单并不在同一事务内。
  • 高波动:价格、库存、舱位、房态可能分钟级变化。
  • 强约束:不能因为模型“觉得合理”就直接扣款或下单。

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 则可以自己做:

  1. 先校验预算与差标。
  2. 再查询航班与高铁。
  3. 判断时间窗是否满足“上午到达”。
  4. 查询酒店并按地理位置排序。
  5. 如果首选酒店满房,自动尝试次优方案。
  6. 如果机票价格超预算,退而选择高铁。
  7. 成功后发起审批与预算锁定。

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”,而是以下四件更重要的事:

  1. 工具边界怎么划分
  2. 工具结果如何结构化表达,方便模型继续推理
  3. 工具调用如何做限流、超时、鉴权、幂等、审计
  4. 整个 loop 如何防失控

五、重构目标:把差旅系统从编排服务升级为 Agent Runtime

5.1 旧架构

原有架构大致如下:

Web/App -> Gateway -> travel-processor -> budget-service -> flight-service -> hotel-service -> approval-service -> notification-service

travel-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 │ │ │ └──────────────┬───┴───────────
http://www.jsqmd.com/news/695159/

相关文章:

  • 从0.(9)=1说起:深入理解小数与分数的等价转换,附Python/Go两种实现
  • 别再手写递归了!用Hutool的TreeUtil搞定Java后台树形菜单(附排序踩坑实录)
  • RK3566开发板串口波特率修改背后:聊聊U-Boot、DTS和DDR初始化的那些事儿
  • Kioxia推出面向PC OEM的全新主流KIOXIA BG8系列固态硬盘
  • Elasticsearch零基础入门:服务器完整启动与配置实战教程
  • STM32CubeMX配置PWM驱动MG90S舵机:从零到转动的保姆级避坑指南
  • AI Agent Harness Engineering 成本优化指南:从算力到开发的全链路降本技巧
  • CSS Grid完全指南
  • 暴力枚举就够了?你可能错过了这道题真正的“降维打击”
  • UI前端美化技能提升日志day7:(原生苹方字体全局适配+合规页脚完整像素级落地)
  • 别再手动量了!用C#给Catia加个自动测量小工具(附完整源码)
  • 救命!论文AI率被导师骂?这两个工具每天免费查重+AIGC检测[特殊字符]
  • 从挂号拥堵到智能秒答:用 LangChain4j 打造高并发企业级医疗助手的全攻略
  • Flutter UI组件高级技巧与最佳实践
  • 手把手教你:Aocoda F405V2飞控从STM32F405升级到AT32F435的完整引脚迁移指南
  • 哔哩下载姬downkyi:5分钟掌握B站视频下载终极指南
  • 告别Xshell和FinalShell!我用Tabby+SFTP插件搞定服务器文件管理,附详细配置流程
  • 告别第三方服务:手把手教你为Web应用自建基于S3的断点续传文件上传功能
  • 告别“滑动窗口”:超像素如何让高光谱解混更精准、更高效?
  • 知识融合实战:从数据冲突到统一图谱的工程化路径
  • KLayout版图设计终极指南:从零开始掌握开源EDA工具的完整教程
  • 一张表对比瑞芯微RK3572/RK3576/RK3568-盈鹏飞嵌入式
  • 代码考古学:用 git blame 和 git show 揪出 Bug 的‘元凶’(附实战排查流程)
  • 毕业设计别再愁了!手把手教你用PHP+MySQL+微信小程序搭建企业官网(附完整源码)
  • 基于虚拟磁链的直接功率控制在MATLAB仿真中的整流器和逆变器仿真研究及其参考文献
  • Arduino项目数据存储升级:手把手教你用AT24C02 EEPROM保存传感器数据(附防数据丢失技巧)
  • LT9611EX芯片实战:如何用龙迅MIPI转HDMI1.4方案搞定4K机顶盒设计(附电路图)
  • 高并发 架构设计二
  • AI写论文别错过!4个AI论文写作神器,助力期刊论文顺利发表!
  • Kaggle夺冠方案:基于cuML的三层堆叠集成技术解析