ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

用 Spring AI 2.0 造一个“工单齐套欠料催交 Agent”:ChatClient + ToolCallingAdvisor + MCP 落地实录

2026/8/27 11:41:09 拓冰建站 浏览量
用 Spring AI 2.0 造一个“工单齐套欠料催交 Agent”:ChatClient + ToolCallingAdvisor + MCP 落地实录 制造业里最典型的“看了报表还得人工跑断腿”场景就是工单齐套试算 → 欠料识别 → 替代料判断 → 催交函拟稿 → PMC 人审。过去这套活儿在 ERP/MES 里只能出看板决策靠人。今天我们用 Spring AI 2.0 把它做成有边界、可审计、不越权的 Java Agent。基线Spring Boot 4.0.x Spring AI 2.0.0 GA Java 17/21。模型后端可以是 OpenAI / DeepSeek / Ollama本例不绑死厂商。一、Spring AI 2.0 里 Agent 到底长在哪儿1.x 时代工具循环埋在每家 ChatModel 里不可观察、不可拦截。2.0 干了三件事ChatClient 成为一等入口ChatModel 退居底层ToolCallingAdvisor 自动注册进 Advisor 链统一管“模型要调工具 → 执行 → 回灌 → 再问模型”的循环Tool / McpTool / ToolCallback​ 成为工具唯一声明方式老toolNames()写法已删记忆、结构化自纠、MCP 传输Streamable HTTP 默认全部重构对齐生产。所以一个“制造业 Agent”在代码层面就是ChatClient └─ Advisor Chain ├─ MessageChatMemoryAdvisor 记忆环内持久化 tool 消息 ├─ ToolCallingAdvisor 自动注册驱动工具循环 └─ HumanApprovalAdvisor 自定义写操作/改排产前拦截 └─ Tools (Tool 或 MCP ToolCallback) ├─ ErpWorkOrderTools ├─ BomInventoryTools ├─ SubstituteMaterialTools └─ SupplierCommTools拟稿不发送二、把 ERP/MES 能力包装成 ToolSpring AI 2.0 的ToolToolParam会自动推导 JSON Schema不需要手写 function spec。Component public class WorkOrderShortageTools { private final ErpClient erp; private final MesClient mes; public WorkOrderShortageTools(ErpClient erp, MesClient mes) { this.erp erp; this.mes mes; } Tool(扫描未来N天计划开工工单返回工单号/开工日/产品/BOM版本) public ListWorkOrderBrief listUpcomingWorkOrders( ToolParam(向前看的天数默认7) int horizonDays) { return erp.listPlannedWorkOrders(horizonDays); } Tool(按工单号核算齐套比对BOM用量、可用库存、在途PO答交日输出欠料行) public ShortageResult evaluateShortage( ToolParam(工单号如 WO-26-1352) String workOrderNo) { return mes.evaluateKitting(workOrderNo); } Tool(查询某物料是否有已认证替代料及工艺状态) public SubstituteProposal findSubstitute( ToolParam(缺料编码) String materialCode, ToolParam(工单号用于工艺约束校验) String workOrderNo) { return erp.findApprovedSubstitute(materialCode, workOrderNo); } Tool(根据欠料行生成催交函草稿不发送仅返回文本) public String draftExpeditingLetter( ToolParam(供应商代码) String supplierCode, ToolParam(欠料行JSON) String shortageLinesJson) { return LetterTemplate.render(supplierCode, shortageLinesJson); } }关键点draftExpeditingLetter只渲染字符串不碰邮件/钉钉网关。发送动作留在 HumanApproval 之后由独立 Service 做。如果 ERP 那边已经是 MCP Server2.0 可以直接用McpTool暴露或 MCP Client 拉取无需自己写ErpClientMcpTool(MES齐套核算) public ShortageResult mesKitting(String workOrderNo) { ... }三、ChatClient 装配工具循环 环内记忆 人审闸门Configuration public class AgentConfig { Bean public ChatClient shortageAgentClient( ChatModel chatModel, WorkOrderShortageTools tools, ChatMemory memory, // Redis/JDBC(session)/InMemory HumanApprovalAdvisor approvalAdvisor) { return ChatClient.builder(chatModel) .defaultSystem( 你是制造企业的齐套催交助理。 规则 1. 只调用已注册工具取数禁止凭空编造库存 2. 发现欠料先判替代料再拟催交函 3. 涉及改开工日、发催交函、用替代料必须产出待办草稿并交人审 4. 不执行任何写操作。 ) .defaultTools(tools) // 注册 Tool Bean .defaultAdvisors( // 记忆放在 ToolCallingAdvisor 内侧保留 tool call/result 转录 new MessageChatMemoryAdvisor(memory, wo-shortage, 200), // 自定义人审 Advisor见下文 approvalAdvisor ) .build(); } }Advisor 顺序很重要MessageChatMemoryAdvisororder ToolCallingAdvisor.DEFAULT_ORDER→ 环内持久化下次巡检能看到“上轮已查过 PO-26-0918”。HumanApprovalAdvisor挂在工具执行后、最终回流前做写操作拦截。四、人审闸门用 Advisor 扩展点卡住“越权”Spring AI 2.0 的ToolCallingAdvisor暴露doBeforeCall / doAfterCall等扩展点我们不在模型层 hack而是外挂一个 Advisor 专门识别“危险工具”public class HumanApprovalAdvisor implements Advisor { private static final SetString WRITE_TOOLS Set.of(draftExpeditingLetter); // 真实项目里发信/改工单都算写 Override public String getName() { return human-approval; } Override public int getOrder() { return ToolCallingAdvisor.DEFAULT_ORDER 100; } Override public AdvisedResponse adviseCall(AdvisedRequest request, CallDependencies deps) { // 模型发起工具调用前拦截 var toolCalls request.toolCalls(); for (var tc : toolCalls) { if (WRITE_TOOLS.contains(tc.name())) { // 不执行改回灌一条“待PMC确认”的系统消息 return AdvisedResponse.from(request) .withAugmentedResponse( 检测到写类动作[%s]已挂起。 生成草稿并存档至工单待办等待 PMC 主管确认后由外部Job发送。 .formatted(tc.name())); } } return deps.nextCall(request); } }效果Agent 跑完一轮输出的是“欠料3条 催交函草稿2份状态待审 替代料建议1条待工艺”绝不会自己点发送。五、定时触发每天 07:30 跑未来 7 天工单Service public class ShortageScanJob { private final ChatClient agent; private final PendingActionRepository pendingRepo; public ShortageScanJob(ChatClient agent, PendingActionRepository pendingRepo) { this.agent agent; this.pendingRepo pendingRepo; } Scheduled(cron 0 30 7 * * ?) public void runDaily() { String replay agent.prompt() .user(扫描未来7天开工工单逐单核算齐套列出欠料、替代料与催交草稿。) .call() .content(); // 解析 Agent 回执中的结构化待办配合 StructuredOutputValidationAdvisor 自纠 pendingRepo.saveFromAgentReport(replay); } }配合StructuredOutputValidationAdvisor模型哪怕吐了带 json 块的脏输出也会自动退回重生成不用手写 JSON 清洗。六、这一版相比 1.x 生产侧真正多出来的东西工具循环可观测Micrometer OTel 能数清每轮 tool round-trip不用再埋日志。环内记忆可持久化spring-ai-sessionevent-sourced或 Redis/Neo4j repository 能把 tool request/response 全落库复盘工单为什么改排产有依据。MCP 是原住民ERP/MES 侧只要起 MCP ServerMcpTool或McpSyncClient直接连Streamable HTTP 默认传输反向代理后面能水平扩。HITL 是 Advisor 不是 prompt 技巧人审闸门在链路上不在 system prompt 里求模型“自觉”。百工具场景不炸ToolSearchToolCallingAdvisor渐进式披露注册 200 个工具也不把 schema 全喷进 context。七、边界声明Agent 的“不做清单”不下采购订单ERP 内采购专员操作不自动改工单开工日PMC 主管在待办页点确认不直发供应商邮件人审通过后由CommunicationJob发库存不可信时只标“数据可疑”而不假装算平八、小结用 Spring AI 2.0 写制造业 Agent代码量不大但架构位置全变了ChatClient 编排、ToolCallingAdvisor 跑循环、Tool/McpTool 接业务系统、MessageChatMemoryAdvisor 留痕、自定义 Advisor 卡人审。Java 团队不需要去 Python 侧借 Agent 框架也不用把 ChatModel.call() 包一层“伪 Agent”——2.0 的 Advisor 链本身就是生产级 ReAct 运行时。工单齐套这种“规则硬、留痕严、人必须拍板”的场景正好吃满 Spring AI 2.0 的强项确定性的工具、可审计的记忆、显式的人机分界。注文中工单/物料/PO 均为演示合成数据Spring AI 2.0 需 Spring Boot 4升级老项目先查ChatMemory与tools()破坏性变更。如果你想往下接一步可以把“PMC 人审确认页”写成一个小 AdminSpring AI Alibaba Graph 或 spring-ai-agent-utils 的 task skill把PendingAction状态机化——Agent 负责拟稿Graph 负责人审流转这是 2.0 上层编排框架的标准切法。