上一期我用 Spring AI 搭了一个客服 Agent,核心是 RAG + Memory + Tool Calling + MCP Client 四大模块。但客服 Agent 本质是「问答」——客户问、Agent 答。今天我们要往前走一步,进入售后最核心、最考验功力的环节:诊断。诊断不是问答,是推理。


一、开篇:客服 Agent 跑通了,然后呢?

第4期结尾我说了一句话:「这个 Demo 虽然叫客服,但它的模块完全可以复用到诊断 Agent 中。」有读者后台问我:老张,复用是复用了,但诊断和客服到底差在哪?

差在一个字:推理。

客服场景:客户问「5 万公里保养多少钱」→ Agent 查手册 → 报价。一问一答,路径确定。

诊断场景:客户说「低速刹车有异响」→ Agent 要判断是刹车片?分泵?导向销?制动盘?→ 查维保历史 → 查 OBD 故障码 → 查配件库存 → 综合下结论。每一步的结果都会改变下一步的方向。

这就是第2期讲的ReAct 模式——Reasoning + Acting,推理与行动交替进行。客服 Agent 用的是「检索 + 生成」,诊断 Agent 用的是「思考 + 调用 + 观察 + 再思考」。

今天的目标:用 Spring AI 把 ReAct 模式写成代码,让 Agent 像一个干了 8 年的老 SA 一样诊断故障。


二、诊断 Agent 和客服 Agent 的本质区别

在写代码之前,先把这两类 Agent 的差异想清楚。这不是学术讨论,而是架构决策——选错模式,代码写得再漂亮也跑不出正确结果。

| 维度

|

客服 Agent(第4期)

|

诊断 Agent(本期)

核心模式

|

RAG 问答 + 单轮工具调用

|

ReAct 循环:Thought→Action→Observation→…→Final Answer

| |

交互特点

|

客户主动提问,Agent 被动回答

|

Agent 主动追问、主动调工具、主动下结论

| |

工具调用次数

|

通常 1 次(查一下就答)

|

可能 3-5 次,每次结果影响下一步

| |

推理深度

|

浅:意图匹配 → 知识检索 → 生成回复

|

深:症状分析 → 假设生成 → 验证 → 排除 → 结论

| |

错误代价

|

低:答错可转人工

|

高:误诊可能导致错误维修,客户投诉

| |

Spring AI 关键能力

|

ChatClient + VectorStore + ChatMemory

|

ChatClient + Tool Calling 循环 + 结构化输出

|

一句话总结:客服 Agent 是「听到问题找答案」,诊断 Agent 是「听到症状找病因」。找答案靠检索,找病因靠推理。


三、场景还原:老 SA 是怎么诊断的?

还是那辆 2022 款奥迪 Q5。张先生说:

「师傅,最近低速刹车的时候有尖锐异响,特别是倒车的时候更明显。我马上要跑一趟长途,有点担心安全问题。」

如果我还在 4S 店当 SA,脑子里是这样转的:

<span leaf="">第<span>1</span>轮:</span>

看到了吗?这就是 ReAct。四轮「想→做→看」,每一轮都基于上一轮的观察结果调整推理方向。这不是线性流程,而是一个收敛的推理链。

现在的问题来了:这个循环,用 Spring AI 怎么写?


四、Spring AI 中的 ReAct:Tool Calling 循环就是 ReAct

很多人以为 ReAct 要自己写 while 循环、自己解析 LLM 输出的 Thought/Action/Observation。在 Spring AI 里,不需要。

Spring AI 的 ChatClient 在你注册了 @Tool 之后,底层自动跑的就是 ReAct 循环:

<span leaf="">用户输入 → LLM 思考要不要调工具</span>

这就是 ReAct 的 Thought → Action → Observation → Thought → … → Final Answer。

关键在于:你要把工具设计得足够「原子化」,让 LLM 有足够的「行动选择空间」。


五、诊断 Agent 的工具设计

诊断 Agent 的核心不是代码量大,而是工具设计。工具就是 SA 的「手和眼」——你能查什么,决定了你能诊断什么。

5.1 工具清单

| 工具名

|

作用

|

对应 SA 动作

|

数据来源

queryMaintenanceHistory

查车辆维保历史

|

翻系统看上次换了什么

|

DMS / CRM

| | readObdFaultCodes |

读 OBD 故障码

|

插诊断仪读码

|

诊断设备接口

| | queryPartSpec |

查配件技术规格(磨损极限、扭矩值等)

|

翻维修手册

|

知识库 / ETKA

| | checkPartsInventory |

查配件库存

|

打电话问配件仓

|

WMS

| | queryRepairGuide |

查维修方案和工时

|

翻维修手册查标准工时

|

知识库

| | estimateRepairCost |

估算维修费用

|

算配件费 + 工时费

|

报价引擎

|

5.2 工具实现代码

<span leaf=""><span>package</span>&nbsp;com.aftersales.diag.tools;</span>

工具设计的三条原则(这是我在 4S 店和代码堆里各踩了几年总结出来的):

  1. 描述要写给 LLM 看,不是写给 Java 开发看。@Tool 的 description 要告诉 LLM 什么时候该用这个工具、用了能拿到什么。比如「没有故障码不代表没有问题」这句话,是引导 LLM 在 OBD 无码时不要直接下「无故障」结论。

  2. 粒度要原子化,不要做大而全的「万能查询」。 拆成 queryPartSpec 和 checkPartsInventory 两个工具,而不是合成一个 queryPartInfo。原子工具让 LLM 的组合空间更大。

  3. 返回值要结构化,不要返回一坨散文。 返回 JSON 或结构化文本,LLM 解析更准确。


六、诊断 Agent 核心实现

6.1 System Prompt:诊断 Agent 的「大脑配置」

诊断 Agent 的 System Prompt 和客服完全不同。客服强调「亲切、转人工」,诊断强调**「推理链路、证据导向、不臆测」**。

<span leaf=""><span>package</span>&nbsp;com.aftersales.diag.agent;</span>

注意这一行:

<span leaf="">.tools(”queryMaintenanceHistory”, ”readObdFaultCodes”,</span>

这是 Spring AI 的魔法所在:你把工具注册给 ChatClient,LLM 自己决定什么时候调、调哪个、调几次。你不需要写任何 if-else 或 while 循环——Spring AI 底层自动处理了「LLM 返回工具调用 → 执行工具 → 把结果喂回 LLM → LLM 决定继续调还是出结论」的整个 ReAct 循环。

6.2 对比:如果不靠框架,ReAct 循环长什么样?

为了让你理解 Spring AI 帮你省了多少事,我贴一段不使用框架时手写的 ReAct 循环伪代码:

<span leaf=""><span>// ❌ 不用框架的手写 ReAct 循环——又长又脆</span></span>

这段代码的问题:LLM 输出格式不稳定、工具解析容易出错、错误处理复杂、没有 Memory 支持。Spring AI 用一行.tools(…)就把这些全包了。这就是第3期我说「用对框架少踩 80% 的坑」的具体含义。


七、模拟数据层:让 Agent 能跑起来

Demo 要能跑,得有模拟数据。我建一个 Mock 服务来模拟 DMS、OBD、WMS 的返回。

<span leaf=""><span>package</span>&nbsp;com.aftersales.diag.service;</span>
<span leaf="">package com.<span>aftersales</span>.<span>diag</span>.<span>service</span>;</span>
<span leaf="">package com.<span>aftersales</span>.<span>diag</span>.<span>service</span>;</span>
<span leaf="">package com.<span>aftersales</span>.<span>diag</span>.<span>service</span>;</span>
<span leaf="">package com.<span>aftersales</span>.<span>diag</span>.<span>service</span>;</span>

八、REST 接口与运行效果

8.1 Controller

<span leaf=""><span>package</span>&nbsp;com.aftersales.diag.controller;</span>

8.2 运行效果

<span leaf="">curl -X POST http://localhost:8080/api/diagnosis/diagnose \</span>

8.3 Agent 的 ReAct 执行过程(日志视角)

以下是 Spring AI DEBUG 日志中可以看到的 Agent 推理过程(简化呈现):

<span leaf="">[<span>DEBUG</span>] User Message: 车牌浙A12345,奥迪Q5L,低速刹车异响,倒车更明显,要跑长途</span>

8.4 最终诊断报告

<span leaf=""><span>═══════════════════════════════════════════</span></span>

注意看:Agent 主动调了 6 次工具,每次调用都基于上一轮的结果调整方向——这正是 ReAct 的精髓。客服 Agent 调 1 次工具就出答案;诊断 Agent 调 6 次,因为诊断本身就是一个排除法过程。


九、进阶:让 Agent 的推理过程可追溯

诊断和客服最大的区别在于可解释性要求。客服答错了客户最多不满意;诊断错了,客户可能拿报告去投诉。所以诊断 Agent 的推理过程必须可追溯。

9.1 开启 Spring AI 的工具调用日志

<span leaf=""><span>spring</span>:</span>

9.2 自定义工具调用审计

<span leaf=""><span>package</span>&nbsp;com.aftersales.diag.audit;</span>

然后把它挂到 ChatClient 上:

<span leaf=""><span>// 在 DiagnosticAgent 中</span></span>

这样每一次诊断的完整推理链路都可以回溯,出了问题能查到是哪一步的推理出了偏差。


十、踩坑清单:ReAct 诊断 Agent 的 5 个暗坑

我在实际开发中踩过的坑,提前给你标出来:

坑1:工具描述太模糊,LLM 不知道什么时候该用

<span leaf="">❌ description = ”查询配件信息”</span>

LLM 选工具靠的是 description。描述不清,LLM 要么不调,要么乱调。

坑2:工具返回太长,撑爆上下文窗口

OBD 返回几百个参数、维保历史返回几百条记录——这些全塞进上下文,几轮之后 Token 就爆了。

<span leaf="">解决方案:</span>

坑3:temperature 太高,Agent 开始「创造性诊断」

<span leaf=""><span># ❌ 诊断 Agent 不要用高 temperature</span></span>

temperature 高了,Agent 可能「发明」客户没描述过的症状,或者跳过查证直接下结论。

坑4:没有设置最大步数,Agent 陷入无限循环

ReAct 模式有个经典问题:LLM 可能反复调用同一个工具,或者在两个工具之间来回弹,始终不出 Final Answer。

Spring AI 底层有最大工具调用次数限制,但你也要在 System Prompt 里加约束:

<span leaf="">【诊断规则】</span>

坑5:没有「我不知道」选项,Agent 硬编结论

<span leaf="">【诊断规则】</span>

一个不会说「我不确定」的诊断 Agent 是危险的。让它学会承认不确定性,比让它假装什么都懂重要一百倍。


十一、诊断 Agent 架构全景图

<span leaf="">┌─────────────────────────────────────────────────────────────────────────┐</span>

十二、项目结构

<span leaf="">aftersales<span>-</span>diagnostic<span>-</span>agent<span>/</span></span>

十三、客服 Agent vs 诊断 Agent:一图看懂

<span leaf="">客服 Agent(第<span>4</span>期) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;诊断 Agent(第<span>5</span>期)</span>

十四、总结 & 下期预告

今天我从第2期的 ReAct 理论走到了代码实现,用 Spring AI 搭建了一个完整的汽车售后诊断 Agent:

  • 6 个原子化诊断工具:维保历史、OBD 读码、配件规格、库存查询、维修方案、费用估算

  • ReAct 循环由 Spring AI 自动驱动:你只需要注册工具和写好 System Prompt,.tools(...) 一行搞定

  • 推理过程可审计:通过自定义 Advisor 记录每一步的 Thought → Action → Observation

  • 5 个实战踩坑:工具描述、上下文管理、temperature、最大步数、不确定性表达

核心认知:诊断 Agent 的难点不在代码量,而在三个地方——工具设计的粒度、System Prompt 的诊断规则、不确定性时的兜底策略。这三件事决定了你的 Agent 是「老 SA」还是「实习生」。

第6期预告:Plan-and-Execute 模式落地——用 Spring AI 搭一个「维修工单编排 Agent」,让它像售后经理一样「开单 → 派工 → 执行 → 质检」一次性规划全流程。ReAct 是「走一步看一步」,Plan-and-Execute 是「先想好再干」,两者怎么选?下期代码见。

互动话题:如果你是售后经理,你店里最难诊断的故障是什么?是间歇性异响?还是偶发亮灯?把你觉得最「玄学」的故障发在评论区,下期我可能会用 Plan-and-Execute 帮你编排一套诊断+维修的完整工单流程。