第5期|用 Spring AI 手搓一个「诊断 Agent」:让 AI 像老 SA 一样「听→想→查→下结论」
上一期我用 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> com.aftersales.diag.tools;</span>
工具设计的三条原则(这是我在 4S 店和代码堆里各踩了几年总结出来的):
-
描述要写给 LLM 看,不是写给 Java 开发看。
@Tool的description要告诉 LLM 什么时候该用这个工具、用了能拿到什么。比如「没有故障码不代表没有问题」这句话,是引导 LLM 在 OBD 无码时不要直接下「无故障」结论。 -
粒度要原子化,不要做大而全的「万能查询」。 拆成
queryPartSpec和checkPartsInventory两个工具,而不是合成一个queryPartInfo。原子工具让 LLM 的组合空间更大。 -
返回值要结构化,不要返回一坨散文。 返回 JSON 或结构化文本,LLM 解析更准确。
六、诊断 Agent 核心实现
6.1 System Prompt:诊断 Agent 的「大脑配置」
诊断 Agent 的 System Prompt 和客服完全不同。客服强调「亲切、转人工」,诊断强调**「推理链路、证据导向、不臆测」**。
<span leaf=""><span>package</span> 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> 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> 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> 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>期) 诊断 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 帮你编排一套诊断+维修的完整工单流程。
- 原文作者:知识铺
- 原文链接:https://index.zshipu.com/ai002/post/20260822/%E7%AC%AC5%E6%9C%9F%E7%94%A8-Spring-AI-%E6%89%8B%E6%90%93%E4%B8%80%E4%B8%AA%E8%AF%8A%E6%96%AD-Agent%E8%AE%A9-AI-%E5%83%8F%E8%80%81-SA-%E4%B8%80%E6%A0%B7%E5%90%AC%E6%83%B3%E6%9F%A5%E4%B8%8B%E7%BB%93%E8%AE%BA/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。
- 免责声明:本页面内容均来源于站内编辑发布,部分信息来源互联网,并不意味着本站赞同其观点或者证实其内容的真实性,如涉及版权等问题,请立即联系客服进行更改或删除,保证您的合法权益。转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。也可以邮件至 sblig@126.com