一、分类模块只管进哪个 Topic先把分类这件事的边界划死分类不调业务 Agent分类只决定这条消息进哪个 Topic。它不查订单、不查物流更不直接回答用户——它的全部产出就是一个分类结果然后按这个结果把消息扔到对应队列。这种只路由不执行的克制是后面所有解耦的前提。这条链路上有两个外部依赖Redis 提供会话上下文让分类知道这是新会话还是接在某段对话后面Nacos 提供分类服务自己的系统提示词。问题分类服务拿到这两样调一次模型得到分类 JSON再投递。时序就是图 1 那样。核心入口是classifyQuestion(String question, String sessionId)。下面这段是我从原 PPT 代码里重写干净的骨架关键方法名保留先看整体再逐段拆java public ClassificationResult classifyQuestion ( String question , String sessionId) { try { // ① 取会话历史作为分类记忆空则标记新会话 String context sessionMemory . getConversationContext (sessionId); if (context null || context . trim (). isEmpty () ) { context 新会话无历史上下文 ; } else { log . info ( 取到会话上下文 sessionId{}, len{} , sessionId, context . length ()); } // ② 用问题上下文拼分类提示词 String prompt promptBuilder . buildClassificationPrompt (question, context); if (prompt null || prompt . trim (). isEmpty () ) { return ClassificationResult . failure ( ClassificationErrorCode . CONFIG_ERROR ); } // ③ 调模型返回强制 JSON 的分类结果 String raw llmClient . chat (prompt); return ClassificationResult . parse (raw); } catch ( Exception e ) { log . error ( 分类失败 sessionId{} , sessionId, e); return ClassificationResult . failure ( ClassificationErrorCode . MODEL_ERROR ); } }原 PPT 里这段长这样截图分辨率低看个结构对照即可逐段看三个关键动作。第①步拉上下文空上下文不是报错是业务信号——直接给个新会话无历史上下文的占位字符串塞进去。为什么要这么做因为分类模型拿到这是新会话后就不会强行结合上一轮的订单号去猜新会话和老会话的分类策略本来就该不一样。第②步构造提示词失败要直接failure返回这是配置问题Nacos 没配好不能吞掉继续往下走。第③步调模型注意整个方法被 try-catch 兜住——分类是同步链路的第一道闸它挂了消息不能丢要走后面的 fallback。═════════二、分类 Prompt 的输出契约与 unknown 兜底分类模型吐回来的不是一句自然语言而是一段强制 JSON。为什么强制 JSON因为后面要靠程序解析这个 category 字段去查 Topic 映射表模型要是自由发挥说我觉得这像是个退款问题程序没法路由。契约长这样json { total_questions : 1 , context_summary : 用户在追问上周订单的退款进度 , questions : [ { id : 1 , original_text : 我的退款怎么还没到账 , category : agent.payment , context_relevance : 高 , extracted_info : { order_no : SO20260908001 , user_id : U10086 , problem_desc : 退款超期未到账 , expected_solution : 确认退款进度并催促 }, confidence : 0.92 , transfer_to : null } ] }几个字段值得展开。category用agent.业务域两级格式——第一级agent表示走 Agent 链路另有faq/kb_qa走轻量通道见第 4 讲三路消费第二级对应第 2 讲的六个垂直 Agent。context_relevance高/中/低是告诉下游这条问题和历史对话的关联程度关联低的问题下游 Agent 就少带历史省 token。extracted_info把订单号、用户 ID 这类结构化信息从自然语言里抠出来——这是白送的分类这一步顺手做了实体抽取后面 Agent 调订单服务直接拿order_no不用再解析一遍。transfer_to是 Agent 侧误判时的转派信号Agent 发现答不了时填目标 Agent 名调度层重新路由。分类原则有五条PPT 里写得很死原则含义优先选最具体分类退款进度查询归 agent.payment不要偷懒归 complaint多意图选 unknown一句话里又问物流又问退款别硬猜一个模糊问题选 unknown读不懂的、缺关键信息的直接 unknown每个问题必须有分类questions[] 里不允许出现无 category 的条目结合对话历史提高准确率靠 Redis 上下文判断指代“它”那个指的是哪个订单unknown 有两层触发一是模型主动判不出 → 输出unknown二是模型判出了但confidence 0.6→ 程序降级为 unknown。两层兜底确保低置信度问题不会硬塞进错误的 Agent。第四条原则和 unknown 看似矛盾其实不矛盾允许 unknown是刻意留的安全出口。很多人做分类第一反应是模型必须给个确定答案我恰恰相反——分类错比不分类更贵。把一个退款问题错路由到售前询价 AgentAgent 会用促单话术去答退款政策这种错误用户感知极强还没法自动发现而路由到 unknown 走 fallback最多是慢一点、转个人工。答错一个退款政策比慢三秒贵得多。═════════三、分类→Topic一张 YAML 映射表拿到 category 之后怎么变成具体的 Topic不是写一堆 if-else而是 Nacos 上一张映射表。绑定类长这样java Slf4j Data Component RefreshScope // Nacos 配置热更新改完不重启 ConfigurationProperties ( prefix question-categories ) public class QuestionCategoryConfig { // key分类类别agent.payment / faq / unknownvalue对应投递的 Topic private Map String , String categoryMappings new HashMap () ; // 兜底 Topic分类未命中或 confidence 过低时走这里 private String defaultFallback agent.default-fallback ; }原 PPT 里这个类的注解就是上面这几行对照看RefreshScope是关键——它让这张映射表跟着 Nacos 实时刷新。新增一个分类运营在控制台加一行 YAML不用发版、不用重启。实际 YAML 长这样yaml question-categories : category-mappings : # 第一级答题通道分流第4讲三路消费 faq : chat.fastqa # 简单FAQ模板回复不调Agent kb_qa : chat.rag # 知识库检索问答RAG链路 # 第二级agent 通道按业务域细分对应第2讲六个垂直Agent agent.product-inquiry : agent.product-inquiry # 询价 agent.payment : agent.payment # 支付含退款进度查询 agent.stock : agent.stock # 库存 agent.logistics : agent.logistics # 物流 agent.warranty : agent.warranty # 故障报修 agent.invoice : agent.invoice # 发票 # 扩展分类投诉/技术支持暂走兜底后续可独立成Agent agent.complaint : agent.default-fallback agent.technical-support : agent.default-fallback unknown : agent.default-fallback # 分类置信度低兜底 default-fallback : agent.default-fallback投递那一行就极薄topic categoryMappings.getOrDefault(category, defaultFallback)。分类出agent.logistics就进agent.logistics分类出faq直接进chat.fastqa不调 Agent分类出unknown或者映射表压根没配统一落default-fallback。第 4 讲讲的消息队列削峰生产者在这一步就结束了——投完即返回下游按自己的速度消费。═════════四、Agent 动态装载运行时现场装配消费者那边才是这套设计的精华。先纠正一个直觉Agent 不是启动时 new 好、挂在那等消息的。服务启动时你根本不知道这一秒会来一条物流问题还是支付问题提前把六个垂直 Agent 全实例化好既浪费内存又让每个 Agent 的提示词难以隔离。真实做法是——消费端拿到分类结果后按businessType去 Nacos 现场把系统提示词和 MCP 配置捞出来现拼一个 Agent 跑这一轮对话跑完就丢。消费端入口是MessageConsumerService.consumeMessage骨架重写后是这样java public boolean consumeMessage ( String messageJson) { Message msg parse (messageJson) ; // 解出 sessionId/content/businessType/extractedInfo // businessType 由生产端投递时从 category 填入消息头如 agent.payment String businessType msg . getBusinessType (); // ① 按业务类型从 Nacos 取系统提示词和 MCP 配置 BusinessTypeConfig cfg dynamicConfigService . getBusinessTypeConfig (businessType); String systemPrompt cfg null ? : cfg . getSystemPrompt (); McpConfig mcpConfig dynamicConfigService . getMcpConfig (businessType); // ② 读会话历史 String history sessionMemory . getConversationContext ( msg . getSessionId ()); // ③ 把提示词、工具集、分类时抽取的实体信息一起交给 Agent 跑这一轮 String reply assistantService . chat ( msg . getSessionId (), msg . getContent (), systemPrompt, mcpConfig, history, msg . getExtractedInfo ()); // 分类时抽的 order_no/user_id 直接用 // ④ 回复经 IM 推回用户 imService . pushMessage ( msg . getSessionId (), reply); return true ; }原 PPT 里这段看红色下划线标的那几个调用点注意chat()的入参里systemPrompt和mcpConfig是每次调用现传的不是 Agent 实例上固化的字段。这就是动态装载四个字在代码上的全部含义Agent 运行时本身是个空壳它是谁、会调哪些工具全看这一轮 Nacos 给它喂什么配置。那 Nacos 里这些配置长什么样按业务类型一个 Data ID 一份比如物流客服就是logistics.inquiry-agent-config.yaml里面按业务隔离了两样东西——这一块是示意骨架落地时按真实话术填yaml # logistics.inquiry-agent-config.yamlnamespacepublic, groupDEFAULT_GROUP system-prompt : | 你是某电商物流客服。只回答物流轨迹、派送、签收问题。 语气克制查不到单号就引导用户提供订单号不要猜。 涉及退款/赔偿一律转工单不要自行承诺。 mcp : servers : - name : logistics-service transport : sse endpoint : http://logistics-service:8080/sse # 第6讲讲怎么套这个壳看出来了吗售前话术和售后话术是两个 YAML物理隔离。运营想改物流客服的语气只动logistics.*.yaml碰不到退款那一份。这就是第 1 讲说的每个 Agent 的提示词能单独改、单独回滚到这一讲终于落到了 Nacos 的 Data ID 上。═════════五、为什么这样设计整条链路过完回头看它的两个设计动机。第一分类与执行分离等于路由策略和业务话术解耦。分类服务只认这是 agent.payment它不知道退款话术是什么、支付 Agent 有哪些工具消费端拿到agent.payment就去捞支付的提示词。路由规则改比如新增一个分类不动任何业务话术话术改退款政策更新不动路由。两个团队各改各的不用互相提 PR。第二提示词和 MCP 配置全部 Nacos 化等于改话术不发版。传统做法里提示词写死在代码或常量类里改一句客服要走提代码→评审→构建→灰度→发布全流程一次话术调整搞一下午。现在 Nacos 控制台改 YAMLRefreshScope秒级生效客服主管自己就能改。这在客服这种话术高频迭代的场景是生产力级别的差别。我觉得这套设计最值钱的不是代码本身是那个unknown兜底分类和default-fallbackTopic——宁可转人工、走通用链路也别让分类错误的问题进了错误的 Agent。default-fallback的消费端跑什么极简骨架yaml # default-fallback 的 system_prompt不调业务工具只收集信息转人工 system-prompt : | 你是通用客服助手。这个问题暂时无法自动处理。 请礼貌致歉收集用户的订单号和问题描述然后创建人工工单。 禁止猜测业务答案禁止承诺赔偿或退款。 mcp : servers : [] # 不挂任何业务工具只有工单创建客服场景里答错一个退款政策比慢三秒贵得多。代码照抄能跑但这层宁慢勿错的克制很多人第一版是想不起来加的。═════════写在最后这一讲把第 2、4 讲的设计落到了代码上分类模块只路由、分类 JSON 有强制契约、Topic 靠 YAML 映射、Agent 在消费现场按业务类型装配提示词和工具集。可交付物清单——分类伪代码、JSON 输出契约、question-categories.yaml映射、消费端动态装载伪代码——都在上面可直接搬进自己的项目对一遍。代码跑通了但 Agent 要调订单、物流、工单这些存量服务怎么办下一讲用 Higress 控制台实证存量服务一行代码不改套个 MCP 壳就能被 Agent 调用。你在做 Agent 路由时踩过分类错进错 Agent的坑吗评论区聊聊你是怎么兜底的我挑典型案例在第 6 讲补一段。学AI大模型的正确顺序千万不要搞错了2026年AI风口已来各行各业的AI渗透肉眼可见超多公司要么转型做AI相关产品要么高薪挖AI技术人才机遇直接摆在眼前有往AI方向发展或者本身有后端编程基础的朋友直接冲AI大模型应用开发转岗超合适就算暂时不打算转岗了解大模型、RAG、Prompt、Agent这些热门概念能上手做简单项目也绝对是求职加分王给大家整理了超全最新的AI大模型应用开发学习清单和资料手把手帮你快速入门学习路线:✅大模型基础认知—大模型核心原理、发展历程、主流模型GPT、文心一言等特点解析✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑✅开发基础能力—Python进阶、API接口调用、大模型开发框架LangChain等实操✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经以上6大模块看似清晰好上手实则每个部分都有扎实的核心内容需要吃透我把大模型的学习全流程已经整理好了抓住AI时代风口轻松解锁职业新可能希望大家都能把握机遇实现薪资/职业跃迁这份完整版的大模型 AI 学习资料已经上传CSDN朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】
企业数字化 ERP 产品动态
相关推荐
DeepSeek LeetCode 107. 二叉树的层序遍历 II Kotlin实现 LeetCode 107. 二叉树的层序遍历 II — Kotlin 实现
思路
标准 BFS 层序遍历,每遍历完一层得到一个 List。题目要求自底向上,所以:方案一:每层结果插入到 result 的头部(add(0, level))方案二ÿ… · 2026/9/26 4:14:51
YOLOv5疲劳驾驶检测全流程实战:从数据集标注到模型部署 简介:这是一套面向计算机相关专业学生与项目实战学习者的疲劳驾驶检测识别毕业设计资源,基于YOLOv5目标检测框架实现,可直接用于毕设、课程设计或期末大作业。项目经导师指导并通过评审,评分达97分,代码经过严格调试&a… · 2026/9/26 5:02:16
Substrate框架解析:从核心设计到节点搭建实操指南 1. 从“substrate”这个词说起:它到底是什么,能解决什么问题第一次听到“substrate”这个词,很多人会以为是生物学里的“底物”或者材料学里的“基材”。但在技术圈,尤其是做系统开发、区块链底层、甚至云原生基础设施的人眼里&am… · 2026/9/26 5:02:16
LL(1)语法分析器C++实战实现与工程化改造 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 5:02:16
md文件怎么编辑?改个错字不用开一百兆的软件,三种方式按顺手排 现在,md文件,双击就能看了。如果发现第三段有个错字、表格里填错一个数,多数人的做法是:关掉阅读窗口,开Typora或VS Code,等加载,改两个字,保存,切回来再看排版对不对。 … · 2026/9/26 5:02:16
视频使用全链路实战:从播放、截帧到录制上传与转码 视频处理这块,我前前后后折腾了不少项目,从最早的网页视频播放,到后来视频截帧、录制、上传、转码,几乎把“video-use”这个词覆盖的链路都踩了一遍。这个标题看起来简单,但真正把视频从“能放”做到“好用”ÿ… · 2026/9/26 5:02:16
扩散模型连续时间框架:SDE与ODE视角及一步生成解析 扩散模型这两年在生成式AI领域的热度不用我多说,从图像生成到机器人动作规划,背后几乎都能看到它的影子。但很多人在上手跑通Stable Diffusion或者Diffusion Policy之后,对底层的数学框架其实还是一知半解——尤其是当论文里出现SDE、ODE、概… · 2026/9/26 5:02:10
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍 简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践 一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46