💡 摘要: 本文复盘笔者作为电商中台架构师,主导的工单智能分发 Agent 项目落地全流程。基于腾讯混元 Hy3(2026 年 7 月 6 日正式发布)+ WorkBuddy 办公智能体工作台 + LangGraph 0.2.x 状态机,讲清多 Agent 工单分类、SLA 路由、自动升级、闭环验证的完整链路,以及混元 Hy3 Function Calling 接入、WorkBuddy MCP 工具链编排、TokenHub API 调用三个关键技术细节。项目上线后日处理工单 8 万单,分类准确率从 78% 提到 96.8%,路由耗时从 8 分钟压到 12 秒,月节省工单处理人力成本 ¥48 万。本文为腾讯混元 Hy3 × WorkBuddy 首发体验活动 B 赛道(Agent 办公)投稿作品。
文章难度: ⭐⭐⭐⭐ (适合有 Python 基础的后端工程师 / AI 应用工程师阅读)
前置知识: 熟悉 Python 异步编程、了解大模型基本概念、对 Function Calling 和 LangGraph 有初步认知
适用版本: 腾讯混元 Hy3 (2026-07-06 正式版) / LangGraph 0.2.x / WorkBuddy 个人版 / Milvus 2.4 / FastAPI 0.110 (2026 Q3 版本)
🎯 场景开篇
2026 年 6 月 18 日 02:17,618 大促峰值第 17 个小时。工单中台值班群炸开:待分发工单队列从日常 200 飙到 8600,人工分类路由平均耗时从 90 秒劣化到 8 分 12 秒,P0 投诉工单半小时涌入 134 单,3 个二线技术组相继被拖垮,工单错派率从 8% 飙到 22%。我作为中台架构师连夜拉群复盘,根因有三:
- 工单来源散落在 5 个渠道(IM 客服转工单、订单系统告警、商家后台、用户 APP 反馈、外部 12315 投诉),人工分类规则覆盖不全;
- 大促工单同质化严重(物流异常、退款纠纷、商品质量占比 71%),人工重复判断;
- SLA 超时无自动升级,二线组人工盯盘,凌晨 0-6 点无人值守,工单直接积压到天亮。
当晚我们做出决定:必须用多 Agent 重构工单分发体系,把分类、路由、SLA、升级、闭环五个环节全部交给 Agent 处理,3 个月内把工单错派率压到 5% 以下。
恰逢 7 月 6 日腾讯混元 Hy3 正式发布并首发接入 WorkBuddy,Agent 能力对标参数规模 2-5 倍的旗舰模型,且限免至 8 月 5 日,我们果断将模型底座从原方案切到 Hy3,并把 Agent 调度入口从自研 Web 控制台迁到 WorkBuddy 工作台,这篇文章就是这 6 周落地的全流程复盘。

图 1:618 大促期间工单中台 Grafana 监控大盘,展示待分发队列、错派率、SLA 超时三个核心指标
🔍 痛点分析:传统工单分发系统的天花板
传统规则分发的四类核心痛点
复盘会上,我们用 30 天的工单日志做了一次彻底体检,把痛点收敛为四类。
| 痛点维度 | 传统规则分发表现 | 量化数据 | 业务影响 |
|---|---|---|---|
| 分类覆盖率 | 关键词规则 6800 条,5 人维护 | 年维护成本 ¥220 万 | 规则膨胀失控 |
| 分类准确率 | 命中率仅 78% | 22% 人工二次分类 | 人力浪费 |
| 路由时效 | 人工分类路由 8 分钟/单 | P99 22 分钟 | 体验劣化 |
| SLA 保障 | 无自动升级,人工盯盘 | 超时率 18% | 投诉升级 |
| 多渠道一致性 | 各渠道规则不统一 | 跨渠道错派率 15% | 体验割裂 |
| 夜间能力 | 0:00-8:00 无分发 | 积压工单占日均 38% | 次日擦屁股 |
业务规模与峰值压力
我们是一家年 GMV 约 35 亿的中型电商,日均订单 8 万单,日常工单量约 5000 单/日,大促场景下数据会有数量级跃升。
text
日常峰值: 5000 单/日 → 峰值分发 QPS 约 12
618 峰值: 82000 单/日 → 峰值分发 QPS 约 280
618 涌入速度: 凌晨 0 点开始 5 分钟内上涨 16 倍
夜间工单占比: 23:00-8:00 占日均 38% (无人工覆盖)
业务方对智能分发提出 5 个硬性指标:
- 分类准确率 ≥ 95%(对标人工 78%)
- 路由耗时 P99 ≤ 30 秒(对标人工 8 分钟)
- SLA 超时率 ≤ 5%(对标原 18%)
- 工单错派率 ≤ 5%(对标原 22%)
- 7×24 小时可用性 ≥ 99.5%
选型推导:为什么选混元 Hy3 + WorkBuddy
围绕"多 Agent 工单分发"这个核心诉求,我们对比了三种模型底座方案。
| 维度 | 通义千问 qwen-max | 腾讯混元 Hy3 | GPT-4o |
|---|---|---|---|
| Agent 能力 | 强 | 强(Hy3 preview 起重点强化) | 强 |
| Function Calling | 支持 | 支持(495 步长工作流验证) | 支持 |
| 上下文长度 | 128K | 256K | 128K |
| 单价(输入/输出 元/百万 Token) | 2.4 / 9.6 | 1.2 / 4.0 | 18 / 72 |
| 国内访问延迟 | 低 | 低 | 高(需海外代理) |
| WorkBuddy 接入 | 不支持 | 原生支持 | 不支持 |
| MCP 工具链 | 第三方 | 原生支持 | 第三方 |
选型推导:
- 混元 Hy3 的 Agent 能力是硬需求:Hy3 preview 已在 WorkBuddy 验证可稳定支撑长达 495 步的复杂智能体工作流,覆盖文档处理、数据分析、知识检索、MCP 工具链编排,工单分发链路正好契合;
- 256K 上下文压成本:工单历史 + 知识库 + Few-shot 单次 Prompt 约 3-5 万 Token,Hy3 的 256K 上下文足够支撑多轮长会话,无需频繁压缩;
- 价格优势显著:Hy3 输入价格 ¥1.2/百万 Token,约为 qwen-max 的 1/2、GPT-4o 的 1/15,单次工单分发成本可压到 ¥0.018;
- WorkBuddy 原生接入:Hy3 已首发接入 WorkBuddy,自选模型用户中 Hy3 占比 60%,我们直接复用 WorkBuddy 的工作流编排能力,无需自研前端控制台;
- 限免窗口期:7 月 6 日 - 8 月 5 日限免,正好覆盖项目压测 + 灰度 + 上线全流程,API 成本归零。
最终选混元 Hy3 + WorkBuddy。如果团队对腾讯生态不熟、且已有自研 Agent 平台,通义千问 + LangGraph 也够用;如果预算充足且需要多语言,GPT-4o 仍是旗舰选择。
⚠️ 风险提示:腾讯混元 Hy3 与 preview 版本 API 接口略有差异(函数调用 schema 字段名变化),本文代码基于 2026-07-06 正式版,若使用 preview 版本需自行适配。建议新项目直接上正式版。
🤖 多 Agent 工单分发架构设计
Agent 角色划分
整个工单智能分发系统由 5 个 Agent 组成,按职责清晰划分。
| Agent 角色 | 职责 | 调用工具 | 模型 |
|---|---|---|---|
| 分类 Agent (Classifier) | 识别工单类型与紧急度 | 工单历史向量检索 | hunyuan-hy3 |
| 路由 Agent (Router) | 基于 SLA 与技能组派单 | 技能组 API、用户画像 API | hunyuan-hy3 |
| SLA 监控 Agent (SLA Monitor) | 监控处理时效,超时触发升级 | 计时器、告警 API | hunyuan-hy3-turbo |
| 升级 Agent (Escalator) | 触发二线/三线升级 | 升级工单 API、通知 API | hunyuan-hy3 |
| 闭环 Agent (Closure) | 验证工单闭环,生成处理报告 | 工单状态 API、报告生成 | hunyuan-hy3 |
模型选择策略:分类、路由、升级等高复杂度任务用 hunyuan-hy3(能力强),SLA 监控等轻量任务用 hunyuan-hy3-turbo(性价比高)。这样单工单分发成本能压到 ¥0.018。
Agent 整体架构
#mermaid-svg-IqyUrrB2u8y45sdy{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-IqyUrrB2u8y45sdy .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-IqyUrrB2u8y45sdy .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-IqyUrrB2u8y45sdy .error-icon{fill:#552222;}#mermaid-svg-IqyUrrB2u8y45sdy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-IqyUrrB2u8y45sdy .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-IqyUrrB2u8y45sdy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-IqyUrrB2u8y45sdy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-IqyUrrB2u8y45sdy .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-IqyUrrB2u8y45sdy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-IqyUrrB2u8y45sdy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-IqyUrrB2u8y45sdy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-IqyUrrB2u8y45sdy .marker.cross{stroke:#333333;}#mermaid-svg-IqyUrrB2u8y45sdy svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-IqyUrrB2u8y45sdy p{margin:0;}#mermaid-svg-IqyUrrB2u8y45sdy .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-IqyUrrB2u8y45sdy .cluster-label text{fill:#333;}#mermaid-svg-IqyUrrB2u8y45sdy .cluster-label span{color:#333;}#mermaid-svg-IqyUrrB2u8y45sdy .cluster-label span p{background-color:transparent;}#mermaid-svg-IqyUrrB2u8y45sdy .label text,#mermaid-svg-IqyUrrB2u8y45sdy span{fill:#333;color:#333;}#mermaid-svg-IqyUrrB2u8y45sdy .node rect,#mermaid-svg-IqyUrrB2u8y45sdy .node circle,#mermaid-svg-IqyUrrB2u8y45sdy .node ellipse,#mermaid-svg-IqyUrrB2u8y45sdy .node polygon,#mermaid-svg-IqyUrrB2u8y45sdy .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-IqyUrrB2u8y45sdy .rough-node .label text,#mermaid-svg-IqyUrrB2u8y45sdy .node .label text,#mermaid-svg-IqyUrrB2u8y45sdy .image-shape .label,#mermaid-svg-IqyUrrB2u8y45sdy .icon-shape .label{text-anchor:middle;}#mermaid-svg-IqyUrrB2u8y45sdy .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-IqyUrrB2u8y45sdy .rough-node .label,#mermaid-svg-IqyUrrB2u8y45sdy .node .label,#mermaid-svg-IqyUrrB2u8y45sdy .image-shape .label,#mermaid-svg-IqyUrrB2u8y45sdy .icon-shape .label{text-align:center;}#mermaid-svg-IqyUrrB2u8y45sdy .node.clickable{cursor:pointer;}#mermaid-svg-IqyUrrB2u8y45sdy .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-IqyUrrB2u8y45sdy .arrowheadPath{fill:#333333;}#mermaid-svg-IqyUrrB2u8y45sdy .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-IqyUrrB2u8y45sdy .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-IqyUrrB2u8y45sdy .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IqyUrrB2u8y45sdy .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-IqyUrrB2u8y45sdy .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IqyUrrB2u8y45sdy .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-IqyUrrB2u8y45sdy .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-IqyUrrB2u8y45sdy .cluster text{fill:#333;}#mermaid-svg-IqyUrrB2u8y45sdy .cluster span{color:#333;}#mermaid-svg-IqyUrrB2u8y45sdy div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-IqyUrrB2u8y45sdy .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-IqyUrrB2u8y45sdy rect.text{fill:none;stroke-width:0;}#mermaid-svg-IqyUrrB2u8y45sdy .icon-shape,#mermaid-svg-IqyUrrB2u8y45sdy .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IqyUrrB2u8y45sdy .icon-shape p,#mermaid-svg-IqyUrrB2u8y45sdy .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-IqyUrrB2u8y45sdy .icon-shape .label rect,#mermaid-svg-IqyUrrB2u8y45sdy .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IqyUrrB2u8y45sdy .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-IqyUrrB2u8y45sdy .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-IqyUrrB2u8y45sdy :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 向量检索
技能组 API
画像 API
超时告警
升级工单
通知
正常闭环
生成报告
更新状态
新工单接入
分类 Agent Classifier
工单历史 Milvus
路由 Agent Router
技能组服务
用户画像服务
SLA 监控 Agent
升级 Agent Escalator
升级工单系统
企微/钉钉通知
闭环 Agent Closure
处理报告
工单状态服务
分类 Agent 是整个系统的入口:工单进来先经过分类,再交给路由 Agent 派单,SLA 监控 Agent 全程跟踪处理时效,超时触发升级,正常闭环后由闭环 Agent 生成处理报告。这种"Pipeline + 监控旁路"结构的好处是主链路清晰,SLA 监控独立运行不阻塞主流程。
LangGraph 状态机设计
LangGraph 的核心是 StateGraph,每个节点是一个 Agent,边是路由逻辑。我们用 TypedDict 定义状态,用 Annotated 声明归约策略。
#mermaid-svg-veykCSv4oG35gS7g{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-veykCSv4oG35gS7g .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-veykCSv4oG35gS7g .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-veykCSv4oG35gS7g .error-icon{fill:#552222;}#mermaid-svg-veykCSv4oG35gS7g .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-veykCSv4oG35gS7g .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-veykCSv4oG35gS7g .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-veykCSv4oG35gS7g .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-veykCSv4oG35gS7g .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-veykCSv4oG35gS7g .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-veykCSv4oG35gS7g .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-veykCSv4oG35gS7g .marker{fill:#333333;stroke:#333333;}#mermaid-svg-veykCSv4oG35gS7g .marker.cross{stroke:#333333;}#mermaid-svg-veykCSv4oG35gS7g svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-veykCSv4oG35gS7g p{margin:0;}#mermaid-svg-veykCSv4oG35gS7g defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-veykCSv4oG35gS7g g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-veykCSv4oG35gS7g g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-veykCSv4oG35gS7g g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-veykCSv4oG35gS7g g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-veykCSv4oG35gS7g g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-veykCSv4oG35gS7g .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-veykCSv4oG35gS7g .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-veykCSv4oG35gS7g .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-veykCSv4oG35gS7g .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-veykCSv4oG35gS7g .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-veykCSv4oG35gS7g .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-veykCSv4oG35gS7g .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-veykCSv4oG35gS7g .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-veykCSv4oG35gS7g .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-veykCSv4oG35gS7g .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-veykCSv4oG35gS7g .edgeLabel .label text{fill:#333;}#mermaid-svg-veykCSv4oG35gS7g .label div .edgeLabel{color:#333;}#mermaid-svg-veykCSv4oG35gS7g .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-veykCSv4oG35gS7g .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-veykCSv4oG35gS7g .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-veykCSv4oG35gS7g .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-veykCSv4oG35gS7g .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-veykCSv4oG35gS7g .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-veykCSv4oG35gS7g .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-veykCSv4oG35gS7g #statediagram-barbEnd{fill:#333333;}#mermaid-svg-veykCSv4oG35gS7g .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-veykCSv4oG35gS7g .cluster-label,#mermaid-svg-veykCSv4oG35gS7g .nodeLabel{color:#131300;}#mermaid-svg-veykCSv4oG35gS7g .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-veykCSv4oG35gS7g .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-veykCSv4oG35gS7g .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-veykCSv4oG35gS7g .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-veykCSv4oG35gS7g .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-veykCSv4oG35gS7g .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-veykCSv4oG35gS7g .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-veykCSv4oG35gS7g .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-veykCSv4oG35gS7g .note-edge{stroke-dasharray:5;}#mermaid-svg-veykCSv4oG35gS7g .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-veykCSv4oG35gS7g .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-veykCSv4oG35gS7g .statediagram-note text{fill:black;}#mermaid-svg-veykCSv4oG35gS7g .statediagram-note .nodeLabel{color:black;}#mermaid-svg-veykCSv4oG35gS7g .statediagram .edgeLabel{color:red;}#mermaid-svg-veykCSv4oG35gS7g #dependencyStart,#mermaid-svg-veykCSv4oG35gS7g #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-veykCSv4oG35gS7g .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-veykCSv4oG35gS7g :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 新工单
分类完成
派单完成
正常闭环
SLA 超时
升级后继续监控
生成报告
分类失败兜底
classifier
router
sla_monitor
closure
escalator
Why 说明 :状态机用 StateGraph 而非简单的函数调用链,是因为 LangGraph 会持久化每个节点的状态快照(通过 Checkpointer),工单处理中断后可从任意节点恢复,这对长流程(如 SLA 监控可能持续数小时)至关重要。
python
# agent_state.py - LangGraph 状态定义
from typing import TypedDict, Annotated, Literal
from langgraph.graph import MessagesState
class TicketAgentState(TypedDict):
"""工单分发 Agent 全局状态定义"""
# 工单原始内容(MessagesState 自带 messages,这里显式声明归约策略)
messages: Annotated[list, "add_messages"]
# 工单分类结果
ticket_type: Literal[
"consult", # 咨询
"complaint", # 投诉
"refund", # 退款
"after_sale", # 售后
"technical", # 技术
]
# 紧急度(P0/P1/P2/P3)
urgency: Literal["P0", "P1", "P2", "P3"]
# 目标技能组 ID
target_skill_group: str
# SLA 截止时间(Unix 时间戳)
sla_deadline: int
# 当前处理阶段
stage: Literal[
"classified", "routed", "sla_monitoring",
"escalated", "closed",
]
# 升级次数(用于递归限制)
escalate_count: int
# 处理报告(闭环时填充)
closure_report: str
# 会话 ID(用于 LangSmith 追踪)
session_id: str
Why 说明 :messages 用 Annotated[list, "add_messages"] 归约策略,LangGraph 会在每次节点执行后自动追加新消息而非覆盖,这样每个 Agent 都能看到完整工单处理历史,无需手动拼接。
构建 StateGraph
Why 说明 :图构建是 LangGraph 的核心。add_node 注册 Agent 函数,add_edge 定义固定跳转,add_conditional_edges 定义条件路由。recursion_limit=8 防止升级流程死循环(踩坑后从 15 降到 8,详见后文)。
python
# build_graph.py - 构建 LangGraph 状态机
from langgraph.graph import StateGraph, END
from langgraph.checkpoint.redis import RedisSaver
from agent_state import TicketAgentState
from nodes import (
classifier_node,
router_node,
sla_monitor_node,
escalator_node,
closure_node,
)
def build_ticket_routing_graph(redis_url: str):
"""构建工单分发多 Agent 状态图
Args:
redis_url: Redis 连接地址,用于状态持久化
Returns:
编译后的 LangGraph 可执行图
"""
# 1. 创建状态图
workflow = StateGraph(TicketAgentState)
# 2. 注册所有 Agent 节点
workflow.add_node("classifier", classifier_node)
workflow.add_node("router", router_node)
workflow.add_node("sla_monitor", sla_monitor_node)
workflow.add_node("escalator", escalator_node)
workflow.add_node("closure", closure_node)
# 3. 定义入口:工单先到分类 Agent
workflow.set_entry_point("classifier")
# 4. 分类 → 路由(固定边)
workflow.add_edge("classifier", "router")
# 5. 路由 → SLA 监控(固定边)
workflow.add_edge("router", "sla_monitor")
# 6. SLA 监控 → 闭环(正常)或 → 升级(超时,条件边)
workflow.add_conditional_edges(
"sla_monitor",
lambda state: "escalator" if state.get("sla_breached") else "closure",
{"escalator": "escalator", "closure": "closure"},
)
# 7. 升级 → SLA 监控(继续监控升级后的工单)
workflow.add_edge("escalator", "sla_monitor")
# 8. 闭环 → END
workflow.add_edge("closure", END)
# 9. 编译图,启用 Redis 状态持久化
checkpointer = RedisSaver.from_conn_string(redis_url)
graph = workflow.compile(
checkpointer=checkpointer,
recursion_limit=8, # 防止升级死循环,正常 2-3 次升级够用
)
return graph
🔌 腾讯混元 Hy3 接入实战
TokenHub API 客户端封装
混元 Hy3 的 API 已在腾讯云 TokenHub 上线,接口协议兼容 OpenAI 格式,但鉴权方式使用腾讯云 Signature v3。我们封装了统一的客户端,支持 Function Calling、流式输出、自动重试。
Why 说明 :腾讯云 Signature v3 签名算法较为复杂,涉及 HMAC-SHA256 多层加密,直接用 SDK 比手写签名更稳定。我们用 tencentcloud-sdk-python 基础库 + 自定义 LLM Wrapper 的方式,既保证签名可靠性,又复用 LangChain 的 LLM 接口。
python
# hunyuan_client.py - 腾讯混元 Hy3 客户端封装
import os
import json
import logging
from typing import Any, AsyncIterator
import asyncio
from tencentcloud.common import credential
from tencentcloud.hunyuan.v20230901 import (
hunyuan_client, models as hy_models,
)
from langchain_core.language_models import BaseChatModel
from langchain_core.messages import AIMessage, BaseMessage
from langchain_core.outputs import ChatGeneration, ChatResult
from tenacity import retry, stop_after_attempt, wait_exponential
logger = logging.getLogger(__name__)
class HunyuanHy3ChatModel(BaseChatModel):
"""腾讯混元 Hy3 Chat 模型 LangChain 封装
支持 Function Calling、流式输出、自动重试。
兼容 LangChain 0.3.x 接口,可直接接入 LangGraph。
"""
model_name: str = "hunyuan-hy3"
temperature: float = 0.3
max_tokens: int = 4096
timeout: int = 60
# 腾讯云鉴权(从环境变量读取,严禁硬编码)
secret_id: str = os.getenv("TENCENT_SECRET_ID", "")
secret_key: str = os.getenv("TENCENT_SECRET_KEY", "")
region: str = os.getenv("TENCENT_REGION", "ap-beijing")
def _build_client(self) -> hunyuan_client.HunyuanClient:
"""构建腾讯云混元客户端
Returns:
配置好鉴权的 HunyuanClient 实例
"""
if not self.secret_id or not self.secret_key:
raise ValueError(
"TENCENT_SECRET_ID 和 TENCENT_SECRET_KEY 必须设置"
)
cred = credential.Credential(self.secret_id, self.secret_key)
return hunyuan_client.HunyuanClient(cred, self.region)
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=2, max=10),
reraise=True,
)
def _generate(self, messages: list[BaseMessage], stop=None, **kwargs) -> ChatResult:
"""同步生成回复(带指数退避重试)
Args:
messages: LangChain 消息列表
stop: 停止词列表
**kwargs: 其他参数(如 tools、tool_choice)
Returns:
ChatResult 对象
"""
client = self._build_client()
req = hy_models.ChatCompletionsRequest()
req.from_dict({
"Model": self.model_name,
"Temperature": self.temperature,
"MaxTokens": self.max_tokens,
"Messages": [
{"Role": m.type, "Content": m.content}
for m in messages
],
# Function Calling 工具定义
"Tools": kwargs.get("tools", []),
"ToolChoice": kwargs.get("tool_choice", "auto"),
})
try:
resp = client.ChatCompletions(req)
choices = resp.Choices
if not choices:
raise ValueError("混元 Hy3 返回空 Choices")
content = choices[0].Message.Content
tool_calls = getattr(choices[0].Message, "ToolCalls", [])
return ChatResult(generations=[
ChatGeneration(message=AIMessage(
content=content,
tool_calls=tool_calls,
))
])
except Exception as e:
logger.error(
"混元 Hy3 调用失败 model=%s error=%s",
self.model_name, e, exc_info=True,
)
raise
@property
def _llm_type(self) -> str:
return "hunyuan-hy3"
⚠️ 安全提示 :
TENCENT_SECRET_ID和TENCENT_SECRET_KEY必须通过环境变量或密钥管理服务(KMS)注入,严禁在代码中硬编码。生产环境建议配合腾讯云 CAM 角色临时密钥,避免长期密钥泄露风险。
Function Calling 工具定义
工单分发需要调用技能组、用户画像、工单状态等多个内部 API,我们用 Function Calling 让 Agent 自主选择工具。
python
# tools_def.py - Function Calling 工具定义
TOOLS_SCHEMA = [
{
"type": "function",
"function": {
"name": "query_skill_group",
"description": "查询技能组信息,包括组成员、当前负载、平均处理时效",
"parameters": {
"type": "object",
"properties": {
"ticket_type": {
"type": "string",
"enum": ["consult", "complaint", "refund",
"after_sale", "technical"],
"description": "工单类型",
},
"urgency": {
"type": "string",
"enum": ["P0", "P1", "P2", "P3"],
"description": "工单紧急度",
},
},
"required": ["ticket_type", "urgency"],
},
},
},
{
"type": "function",
"function": {
"name": "query_user_profile",
"description": "查询用户画像,包括会员等级、历史投诉次数、消费金额",
"parameters": {
"type": "object",
"properties": {
"user_id": {
"type": "string",
"description": "用户唯一标识",
},
},
"required": ["user_id"],
},
},
},
{
"type": "function",
"function": {
"name": "update_ticket_status",
"description": "更新工单状态,用于派单、升级、闭环",
"parameters": {
"type": "object",
"properties": {
"ticket_id": {
"type": "string",
"description": "工单唯一标识",
},
"status": {
"type": "string",
"enum": ["routed", "escalated", "closed"],
"description": "工单新状态",
},
"assignee": {
"type": "string",
"description": "处理人 ID(派单/升级时必填)",
},
},
"required": ["ticket_id", "status"],
},
},
},
]
分类 Agent 节点实现
分类 Agent 是整个系统的"路由器",准确率直接决定后续路由是否正确。我们用 Prompt + Few-shot + 向量检索增强方案,准确率达到 96.8%。
python
# nodes.py - 分类 Agent 节点实现
from langchain_core.messages import HumanMessage, SystemMessage
from hunyuan_client import HunyuanHy3ChatModel
from agent_state import TicketAgentState
from milvus_retriever import TicketHistoryRetriever
import logging
logger = logging.getLogger(__name__)
# 混元 Hy3 模型实例(分类用标准版,SLA 监控用 turbo 版)
classifier_llm = HunyuanHy3ChatModel(model_name="hunyuan-hy3", temperature=0.1)
sla_llm = HunyuanHy3ChatModel(model_name="hunyuan-hy3-turbo", temperature=0.0)
# 工单历史向量检索器(召回相似历史工单作为分类参考)
history_retriever = TicketHistoryRetriever(
milvus_host="${MILVUS_HOST}",
collection="ticket_history",
top_k=3,
score_threshold=0.75,
)
CLASSIFIER_SYSTEM_PROMPT = """你是电商工单分类专家,负责将工单分为 5 类:
- consult(咨询):商品信息、活动规则、订单状态查询
- complaint(投诉):服务态度、物流延误、商品质量问题
- refund(退款):退款申请、退款进度、退款纠纷
- after_sale(售后):换货、维修、补发
- technical(技术):APP 故障、支付异常、账号问题
同时判定紧急度:
- P0:涉及人身安全、资金损失、舆情风险
- P1:涉及订单履约、用户体验严重受损
- P2:常规售后咨询
- P3:一般性咨询
## 输出格式(必须为合法 JSON)
{"ticket_type": "...", "urgency": "...", "reason": "..."}
## 参考历史工单
{history_tickets}
## 注意事项
1. 严禁编造工单类型,只能从上述 5 类中选择
2. 紧急度判定必须基于工单内容,不要过度保守
3. 涉及资金损失一律 P0,涉及投诉升级一律 P1
"""
async def classifier_node(state: TicketAgentState) -> dict:
"""分类 Agent 节点:识别工单类型与紧急度
Args:
state: LangGraph 状态
Returns:
状态更新字典
"""
ticket_content = state["messages"][-1].content
session_id = state["session_id"]
try:
# 1. 向量检索相似历史工单(增强分类准确率)
history = await history_retriever.aretrieve(ticket_content, top_k=3)
history_text = "\n".join(
f"- 类型:{h.type},紧急度:{h.urgency},内容摘要:{h.summary}"
for h in history
) or "无相似历史工单"
# 2. 构造 Prompt
messages = [
SystemMessage(content=CLASSIFIER_SYSTEM_PROMPT.format(
history_tickets=history_text,
)),
HumanMessage(content=f"工单内容:{ticket_content}"),
]
# 3. 调用混元 Hy3(temperature=0.1 保证分类稳定)
response = await classifier_llm.ainvoke(messages)
result = _parse_json_response(response.content)
logger.info(
"工单分类完成 session=%s type=%s urgency=%s",
session_id, result["ticket_type"], result["urgency"],
)
return {
"ticket_type": result["ticket_type"],
"urgency": result["urgency"],
"stage": "classified",
}
except Exception as e:
logger.error(
"工单分类失败 session=%s error=%s,降级到 P2 consult",
session_id, e, exc_info=True,
)
# 兜底降级:分类失败默认 P2 咨询,转人工复核
return {
"ticket_type": "consult",
"urgency": "P2",
"stage": "classified",
"need_human_review": True,
}
分类失败时降级到
P2 consult并标记need_human_review=True,而不是直接报错,这是生产级 Agent 的关键设计------任何模型异常都不能阻塞工单流转,降级到人工兜底是最后一道防线。
🎛️ WorkBuddy 工作流编排接入
MCP 工具链集成
WorkBuddy 作为腾讯原生办公智能体工作台,支持 MCP(Model Context Protocol)工具链编排。我们将工单分发的 5 个 Agent 注册为 MCP 工具,在 WorkBuddy 中可视化编排,运营同学无需写代码即可调整分发策略。
python
# workbuddy_mcp_server.py - WorkBuddy MCP 工具服务器
from mcp.server import Server
from mcp.types import Tool, TextContent
import json
import logging
from ticket_service import TicketService
logger = logging.getLogger(__name__)
server = Server("ticket-routing-agent")
ticket_service = TicketService()
@server.list_tools()
async def list_tools() -> list[Tool]:
"""向 WorkBuddy 注册工单分发 MCP 工具
Returns:
MCP 工具列表
"""
return [
Tool(
name="classify_ticket",
description="对工单进行分类与紧急度判定",
inputSchema={
"type": "object",
"properties": {
"ticket_id": {"type": "string"},
"content": {"type": "string"},
},
"required": ["ticket_id", "content"],
},
),
Tool(
name="route_ticket",
description="基于分类结果与 SLA 派单到技能组",
inputSchema={
"type": "object",
"properties": {
"ticket_id": {"type": "string"},
"ticket_type": {"type": "string"},
"urgency": {"type": "string"},
},
"required": ["ticket_id", "ticket_type", "urgency"],
},
),
Tool(
name="escalate_ticket",
description="触发工单升级到二线/三线",
inputSchema={
"type": "object",
"properties": {
"ticket_id": {"type": "string"},
"reason": {"type": "string"},
"target_level": {"type": "string", "enum": ["L2", "L3"]},
},
"required": ["ticket_id", "reason", "target_level"],
},
),
Tool(
name="close_ticket",
description="闭环工单并生成处理报告",
inputSchema={
"type": "object",
"properties": {
"ticket_id": {"type": "string"},
"resolution": {"type": "string"},
},
"required": ["ticket_id", "resolution"],
},
),
]
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
"""执行 WorkBuddy 调用的工具
Args:
name: 工具名称
arguments: 工具参数
Returns:
工具执行结果
"""
try:
if name == "classify_ticket":
result = await ticket_service.classify(
arguments["ticket_id"], arguments["content"],
)
elif name == "route_ticket":
result = await ticket_service.route(
arguments["ticket_id"],
arguments["ticket_type"],
arguments["urgency"],
)
elif name == "escalate_ticket":
result = await ticket_service.escalate(
arguments["ticket_id"],
arguments["reason"],
arguments["target_level"],
)
elif name == "close_ticket":
result = await ticket_service.close(
arguments["ticket_id"], arguments["resolution"],
)
else:
raise ValueError(f"未知工具:{name}")
return [TextContent(
type="text",
text=json.dumps(result, ensure_ascii=False),
)]
except Exception as e:
logger.error("WorkBuddy 工具调用失败 tool=%s error=%s",
name, e, exc_info=True)
return [TextContent(
type="text",
text=json.dumps({
"success": False,
"error": str(e),
}, ensure_ascii=False),
)]
WorkBuddy 工作流编排
在 WorkBuddy 工作台中,我们通过可视化编排器搭建了"工单分发工作流",运营同学可以基于 Hy3 的自然语言理解能力,用一句话描述调整策略。
text
运营同学输入示例:
"把所有 P0 投诉工单优先派给金牌客服组,5 分钟未响应升级到二线"
WorkBuddy 自动生成的工作流:
1. 触发器:新工单接入
2. 条件分支:urgency == "P0" AND ticket_type == "complaint"
3. 动作:派单到 skill_group_id = "gold_service"
4. 计时器:5 分钟
5. 条件分支:未响应
6. 动作:升级到 L2 二线组
这种"自然语言 → 工作流"的能力,让运营同学无需工程师介入就能快速调整分发策略,大促期间策略迭代频率从原来的"一周一次"提升到"一天三次"。

图 2:WorkBuddy 工作流编排界面,展示工单分发 Agent 的可视化流程与 Hy3 模型选择
混元 Hy3 模型切换效果
我们在 WorkBuddy 中对比了 Hy3 与 Hy2、通义千问 qwen-max 在工单分类任务上的表现:
| 模型 | 分类准确率 | 单次耗时 | 单次成本 | Function Calling 成功率 |
|---|---|---|---|---|
| 混元 Hy2 | 89.2% | 1.8s | ¥0.032 | 96.1% |
| 混元 Hy3 | 96.8% | 1.2s | ¥0.018 | 99.4% |
| 通义千问 qwen-max | 95.1% | 1.5s | ¥0.041 | 98.7% |
Hy3 相比 Hy2 分类准确率提升 7.6 个百分点,单次成本下降 44%,Function Calling 成功率提升 3.3 个百分点,这也是我们果断切换到 Hy3 的核心原因。
📊 Agent 评估与 A/B 测试
评估数据集构建
我们用 3 个月历史工单(共 45 万单)构建评估集,人工标注 2000 单作为黄金标准,覆盖 5 个类型 × 4 个紧急度 = 20 个组合,每个组合 100 单。
python
# eval_dataset.py - 评估数据集加载
import json
from pathlib import Path
from dataclasses import dataclass
@dataclass
class EvalSample:
"""评估样本"""
ticket_id: str
content: str
gold_type: str # 人工标注的真实类型
gold_urgency: str # 人工标注的真实紧急度
gold_skill_group: str # 人工标注的目标技能组
def load_eval_dataset(path: str = "eval/golden_2000.jsonl") -> list[EvalSample]:
"""加载黄金评估数据集
Args:
path: 数据集文件路径(JSONL 格式)
Returns:
评估样本列表
"""
samples = []
with open(path, "r", encoding="utf-8") as f:
for line in f:
data = json.loads(line)
samples.append(EvalSample(**data))
return samples
def evaluate_classifier(samples: list[EvalSample], llm) -> dict:
"""评估分类 Agent 准确率
Args:
samples: 评估样本列表
llm: 待评估的 LLM 实例
Returns:
评估指标字典
"""
correct_type = 0
correct_urgency = 0
correct_both = 0
total = len(samples)
for s in samples:
# 调用分类 Agent
result = llm.classify(s.content)
if result["ticket_type"] == s.gold_type:
correct_type += 1
if result["urgency"] == s.gold_urgency:
correct_urgency += 1
if (result["ticket_type"] == s.gold_type
and result["urgency"] == s.gold_urgency):
correct_both += 1
return {
"type_accuracy": correct_type / total,
"urgency_accuracy": correct_urgency / total,
"overall_accuracy": correct_both / total,
"total_samples": total,
}
A/B 测试方案
上线前我们做了 2 周 A/B 测试:对照组走原规则分发系统,实验组走 Hy3 + WorkBuddy Agent 方案,各 50% 流量。
| 指标 | 对照组(规则) | 实验组(Hy3 Agent) | 提升幅度 |
|---|---|---|---|
| 分类准确率 | 78.3% | 96.8% | ⬆️ 18.5pp |
| 路由耗时 P99 | 22 分钟 | 28 秒 | ⬇️ 97.9% |
| SLA 超时率 | 18.2% | 4.1% | ⬇️ 77.5% |
| 工单错派率 | 22.1% | 3.2% | ⬇️ 85.5% |
| 单工单成本 | ¥0.085 | ¥0.018 | ⬇️ 78.8% |
A/B 测试结果远超预期,业务方拍板全量切换。

图 3:A/B 测试 14 天数据对比,展示分类准确率、路由耗时、SLA 超时率三个核心指标
🚀 生产部署与压测
部署架构
#mermaid-svg-vcOPkhSag1jTYj3Y{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vcOPkhSag1jTYj3Y .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vcOPkhSag1jTYj3Y .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vcOPkhSag1jTYj3Y .error-icon{fill:#552222;}#mermaid-svg-vcOPkhSag1jTYj3Y .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vcOPkhSag1jTYj3Y .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vcOPkhSag1jTYj3Y .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vcOPkhSag1jTYj3Y .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vcOPkhSag1jTYj3Y .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vcOPkhSag1jTYj3Y .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vcOPkhSag1jTYj3Y .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vcOPkhSag1jTYj3Y .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vcOPkhSag1jTYj3Y .marker.cross{stroke:#333333;}#mermaid-svg-vcOPkhSag1jTYj3Y svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vcOPkhSag1jTYj3Y p{margin:0;}#mermaid-svg-vcOPkhSag1jTYj3Y .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-vcOPkhSag1jTYj3Y .cluster-label text{fill:#333;}#mermaid-svg-vcOPkhSag1jTYj3Y .cluster-label span{color:#333;}#mermaid-svg-vcOPkhSag1jTYj3Y .cluster-label span p{background-color:transparent;}#mermaid-svg-vcOPkhSag1jTYj3Y .label text,#mermaid-svg-vcOPkhSag1jTYj3Y span{fill:#333;color:#333;}#mermaid-svg-vcOPkhSag1jTYj3Y .node rect,#mermaid-svg-vcOPkhSag1jTYj3Y .node circle,#mermaid-svg-vcOPkhSag1jTYj3Y .node ellipse,#mermaid-svg-vcOPkhSag1jTYj3Y .node polygon,#mermaid-svg-vcOPkhSag1jTYj3Y .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vcOPkhSag1jTYj3Y .rough-node .label text,#mermaid-svg-vcOPkhSag1jTYj3Y .node .label text,#mermaid-svg-vcOPkhSag1jTYj3Y .image-shape .label,#mermaid-svg-vcOPkhSag1jTYj3Y .icon-shape .label{text-anchor:middle;}#mermaid-svg-vcOPkhSag1jTYj3Y .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vcOPkhSag1jTYj3Y .rough-node .label,#mermaid-svg-vcOPkhSag1jTYj3Y .node .label,#mermaid-svg-vcOPkhSag1jTYj3Y .image-shape .label,#mermaid-svg-vcOPkhSag1jTYj3Y .icon-shape .label{text-align:center;}#mermaid-svg-vcOPkhSag1jTYj3Y .node.clickable{cursor:pointer;}#mermaid-svg-vcOPkhSag1jTYj3Y .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vcOPkhSag1jTYj3Y .arrowheadPath{fill:#333333;}#mermaid-svg-vcOPkhSag1jTYj3Y .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vcOPkhSag1jTYj3Y .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vcOPkhSag1jTYj3Y .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vcOPkhSag1jTYj3Y .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vcOPkhSag1jTYj3Y .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vcOPkhSag1jTYj3Y .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vcOPkhSag1jTYj3Y .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vcOPkhSag1jTYj3Y .cluster text{fill:#333;}#mermaid-svg-vcOPkhSag1jTYj3Y .cluster span{color:#333;}#mermaid-svg-vcOPkhSag1jTYj3Y div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vcOPkhSag1jTYj3Y .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vcOPkhSag1jTYj3Y rect.text{fill:none;stroke-width:0;}#mermaid-svg-vcOPkhSag1jTYj3Y .icon-shape,#mermaid-svg-vcOPkhSag1jTYj3Y .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vcOPkhSag1jTYj3Y .icon-shape p,#mermaid-svg-vcOPkhSag1jTYj3Y .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vcOPkhSag1jTYj3Y .icon-shape .label rect,#mermaid-svg-vcOPkhSag1jTYj3Y .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vcOPkhSag1jTYj3Y .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vcOPkhSag1jTYj3Y .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vcOPkhSag1jTYj3Y :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} HTTP/Webhook
TokenHub API
TokenHub API
TokenHub API
WorkBuddy 工作台
负载均衡 SLB
FastAPI 实例 1
FastAPI 实例 2
FastAPI 实例 3
Redis 集群
Milvus 集群
混元 Hy3 服务
Prometheus
Grafana
部署清单:
- FastAPI 实例 × 3(4 核 8G,腾讯云 CVM)
- Redis 集群 × 3 主 3 从(状态持久化)
- Milvus 集群 × 3 节点(工单历史向量检索)
- 混元 Hy3 API(腾讯云 TokenHub,按量付费)
- WorkBuddy 企业版(工作流编排入口)
- Prometheus + Grafana(监控告警)
压测结果
用 Locust 模拟 618 峰值流量,压测 2 小时:
text
峰值 QPS: 320 (目标 280)
P50 耗时: 1.2s
P99 耗时: 4.8s (目标 ≤ 30s)
错误率: 0.12% (目标 ≤ 1%)
Token 消耗: 420 万/小时
API 成本: ¥6.8/小时 (限免期 ¥0)
压测通过,满足业务方 5 个硬性指标。
🐛 踩坑实录:三次生产事故
事故 1:Hy3 Function Calling 偶发幻觉
现象 :上线第 5 天,Grafana 告警显示某工单被派到不存在的技能组 ID gold_service_v2,实际系统中只有 gold_service。当天共发现 23 起类似幻觉。
排查过程:
bash
# 1. 在 LangSmith 按 session_id 过滤,找到问题工单
# 发现 router_agent 调用了 query_skill_group 工具
# 但工具返回的 group_id 是 "gold_service"
# Agent 最终输出却是 "gold_service_v2"
# 2. 查 Prompt
# ROUTER_PROMPT 里 Few-shot 示例用了 "gold_service_v2" 作为示例值
# Hy3 把示例值当成了真实数据
# 3. 查混元 Hy3 文档
# Hy3 在 Function Calling 场景下对 Few-shot 示例值敏感度高
# 比 Hy2 更容易"模仿"示例
根因:
- Few-shot 示例值污染:Prompt 里的示例技能组 ID 被 Hy3 当成真实数据;
- 缺少工具返回校验:Agent 输出未校验是否与工具返回一致;
- Hy3 模仿能力强:Hy3 比 Hy2 更易受 Few-shot 影响,这是 Agent 能力提升的副作用。
解决方案:
- Few-shot 脱敏 :把示例技能组 ID 改成
SKILL_GROUP_PLACEHOLDER,避免模仿; - 工具返回守卫:在 router 节点校验输出 group_id 是否在工具返回的候选列表中;
- Hy3 专属 Prompt 规范:Hy3 场景下 Few-shot 示例值一律用占位符,真实数据通过工具注入。
预防措施:所有涉及 ID、金额、日期的输出,必须校验"是否来自工具返回",不来自工具的一律拦截。这条规则我们在 004 智能客服项目也踩过,Hy3 场景下要更严格。
事故 2:升级流程死循环
现象 :上线第 12 天,Grafana 告警显示某工单 Token 消耗突破 8 万,单次调用耗时 180 秒。查 LangSmith 发现:SLA 监控 Agent 和升级 Agent 互相调用 8 次,触发了 recursion_limit 才停止。
排查过程:
bash
# 1. 查 LangSmith Trace
# 发现链路: sla_monitor → escalator → sla_monitor → escalator → ...
# 循环了 8 次才被 recursion_limit 拦截
# 2. 查状态
# escalator 升级到 L2 后,sla_monitor 继续监控
# 但 L2 处理时效还是超时,再次触发 escalator
# escalator 又升级到 L2(没有判断是否已升级)
根因:
- 升级幂等性缺失 :
escalator节点没有判断escalate_count,重复升级到同一级别; - SLA 阈值未调整:升级到 L2 后,SLA 监控仍用 L1 的时效阈值,必然再次超时;
recursion_limit设得过高:设了 15,导致循环 8 次才终止,Token 浪费严重。
解决方案:
- 升级幂等校验 :
escalator节点检查escalate_count,超过 2 次直接转人工; - SLA 阈值分级:L1 阈值 30 分钟,L2 阈值 2 小时,L3 阈值 8 小时;
- 降低 recursion_limit:从 15 降到 8,足够覆盖正常 3 次升级,又能快速拦截循环。
预防措施 :所有 Agent 节点必须做幂等性校验,recursion_limit 不超过 8,升级类操作必须有"最大升级次数"硬限。
事故 3:WorkBuddy 工作流与 LangGraph 状态不一致
现象:上线第 18 天,运营同学反馈 WorkBuddy 工作台显示某工单"已闭环",但工单系统实际状态仍是"处理中"。当天共发现 47 起类似不一致。
排查过程:
bash
# 1. 查工单系统日志
# 发现 closure_agent 调用了 update_ticket_status 工具
# 工具返回成功,但工单系统状态未更新
# 2. 查 WorkBuddy 工作流
# WorkBuddy 显示 "已闭环" 是因为 MCP 工具返回了 {"success": true}
# 但实际工单系统事务回滚了
# 3. 查数据库
# 发现工单状态更新触发了审计日志写入
# 审计日志写入失败导致整个事务回滚
# 但 MCP 工具没有感知到事务回滚,仍返回成功
根因:
- MCP 工具返回与实际事务脱节 :工具返回
success=true,但数据库事务回滚; - WorkBuddy 信任工具返回:WorkBuddy 没有二次校验工单系统真实状态;
- 缺少状态同步机制:LangGraph 状态与 WorkBuddy 工作流状态各自维护,无对账。
解决方案:
- MCP 工具事务一致性 :
close_ticket工具在事务提交后再返回success=true,事务回滚返回success=false; - WorkBuddy 二次校验:WorkBuddy 工作流在关键节点(闭环、升级)增加"工单系统状态查询"步骤;
- 状态对账任务:每小时跑一次对账任务,发现不一致自动修复。
预防措施:MCP 工具必须保证"返回成功 = 数据库已提交",WorkBuddy 工作流与 LangGraph 状态必须有对账机制。
📊 运维监控与自动化保障
Agent 系统上线后,运维监控比开发阶段更重要。我们部署了完整的监控告警 + 自动化运维体系。
监控告警架构
#mermaid-svg-krH9jzGWYyDjPA3L{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-krH9jzGWYyDjPA3L .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-krH9jzGWYyDjPA3L .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-krH9jzGWYyDjPA3L .error-icon{fill:#552222;}#mermaid-svg-krH9jzGWYyDjPA3L .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-krH9jzGWYyDjPA3L .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-krH9jzGWYyDjPA3L .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-krH9jzGWYyDjPA3L .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-krH9jzGWYyDjPA3L .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-krH9jzGWYyDjPA3L .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-krH9jzGWYyDjPA3L .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-krH9jzGWYyDjPA3L .marker{fill:#333333;stroke:#333333;}#mermaid-svg-krH9jzGWYyDjPA3L .marker.cross{stroke:#333333;}#mermaid-svg-krH9jzGWYyDjPA3L svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-krH9jzGWYyDjPA3L p{margin:0;}#mermaid-svg-krH9jzGWYyDjPA3L .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-krH9jzGWYyDjPA3L .cluster-label text{fill:#333;}#mermaid-svg-krH9jzGWYyDjPA3L .cluster-label span{color:#333;}#mermaid-svg-krH9jzGWYyDjPA3L .cluster-label span p{background-color:transparent;}#mermaid-svg-krH9jzGWYyDjPA3L .label text,#mermaid-svg-krH9jzGWYyDjPA3L span{fill:#333;color:#333;}#mermaid-svg-krH9jzGWYyDjPA3L .node rect,#mermaid-svg-krH9jzGWYyDjPA3L .node circle,#mermaid-svg-krH9jzGWYyDjPA3L .node ellipse,#mermaid-svg-krH9jzGWYyDjPA3L .node polygon,#mermaid-svg-krH9jzGWYyDjPA3L .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-krH9jzGWYyDjPA3L .rough-node .label text,#mermaid-svg-krH9jzGWYyDjPA3L .node .label text,#mermaid-svg-krH9jzGWYyDjPA3L .image-shape .label,#mermaid-svg-krH9jzGWYyDjPA3L .icon-shape .label{text-anchor:middle;}#mermaid-svg-krH9jzGWYyDjPA3L .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-krH9jzGWYyDjPA3L .rough-node .label,#mermaid-svg-krH9jzGWYyDjPA3L .node .label,#mermaid-svg-krH9jzGWYyDjPA3L .image-shape .label,#mermaid-svg-krH9jzGWYyDjPA3L .icon-shape .label{text-align:center;}#mermaid-svg-krH9jzGWYyDjPA3L .node.clickable{cursor:pointer;}#mermaid-svg-krH9jzGWYyDjPA3L .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-krH9jzGWYyDjPA3L .arrowheadPath{fill:#333333;}#mermaid-svg-krH9jzGWYyDjPA3L .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-krH9jzGWYyDjPA3L .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-krH9jzGWYyDjPA3L .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-krH9jzGWYyDjPA3L .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-krH9jzGWYyDjPA3L .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-krH9jzGWYyDjPA3L .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-krH9jzGWYyDjPA3L .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-krH9jzGWYyDjPA3L .cluster text{fill:#333;}#mermaid-svg-krH9jzGWYyDjPA3L .cluster span{color:#333;}#mermaid-svg-krH9jzGWYyDjPA3L div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-krH9jzGWYyDjPA3L .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-krH9jzGWYyDjPA3L rect.text{fill:none;stroke-width:0;}#mermaid-svg-krH9jzGWYyDjPA3L .icon-shape,#mermaid-svg-krH9jzGWYyDjPA3L .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-krH9jzGWYyDjPA3L .icon-shape p,#mermaid-svg-krH9jzGWYyDjPA3L .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-krH9jzGWYyDjPA3L .icon-shape .label rect,#mermaid-svg-krH9jzGWYyDjPA3L .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-krH9jzGWYyDjPA3L .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-krH9jzGWYyDjPA3L .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-krH9jzGWYyDjPA3L :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} WorkBuddy 工作台
FastAPI 服务
Prometheus 指标
LangSmith Trace
Alertmanager
企业微信
钉钉机器人
邮件
LangSmith 仪表盘
ELK 日志
Kibana
Grafana 大盘
运维团队
监控项配置
| 监控项 | 指标 | 告警阈值 | 检查频率 | 告警级别 |
|---|---|---|---|---|
| 服务可用性 | HTTP 5xx 率 | > 1% (5min) | 30s | P0 |
| 路由耗时 | P99 延迟 | > 30s (5min) | 30s | P1 |
| 分类准确率 | 离线评估 | < 95% | 1天 | P1 |
| Agent 死循环率 | recursion_limit 触发 | > 0.1% | 5min | P1 |
| Token 消耗 | 单工单 Token | > 50000 | 实时 | P2 |
| Hy3 API 成本 | 日累计成本 | > ¥3000 | 1小时 | P1 |
| Function Calling 失败率 | 工具异常 / 总调用 | > 5% | 5min | P1 |
| 工单错派率 | 错派 / 总工单 | > 5% | 1小时 | P2 |
| WorkBuddy 状态不一致 | 对账差异 | > 0.1% | 1小时 | P1 |
Prometheus 告警规则
告警规则集中在 Prometheus + Alertmanager,FastAPI 用
prometheus-fastapi-instrumentator暴露指标。规则按"可用性、性能、成本、业务"分类,避免告警风暴。
yaml
# rules.yml - 工单分发 Agent 告警规则
groups:
- name: ticket_agent_availability
interval: 30s
rules:
- alert: TicketAgentHighErrorRate
expr: |
sum(rate(http_requests_total{job="ticket-agent", status=~"5.."}[5m]))
/ sum(rate(http_requests_total{job="ticket-agent"}[5m]))
> 0.01
for: 5m
labels:
severity: P0
team: ticket-mid
annotations:
summary: "工单 Agent 5xx 错误率超 1%"
description: "当前 5xx 率:{{ $value }}%,请立即排查"
- alert: TicketAgentRoutingSlow
expr: |
histogram_quantile(0.99,
rate(http_request_duration_seconds_bucket{job="ticket-agent", path="/route"}[5m]))
> 30
for: 5m
labels:
severity: P1
team: ticket-mid
annotations:
summary: "工单路由 P99 耗时超 30 秒"
description: "当前 P99:{{ $value }}秒"
- name: ticket_agent_cost
interval: 60s
rules:
- alert: Hy3ApiCostHigh
expr: |
sum(hunyuan_token_cost_usd_total{model="hunyuan-hy3"}[1h]) * 7.2 > 3000
for: 10m
labels:
severity: P1
team: ticket-mid
annotations:
summary: "混元 Hy3 API 日成本超 ¥3000"
description: "当前小时成本:¥{{ $value }}"
- alert: TicketTokenBurst
expr: |
max_over_time(hunyuan_tokens_per_ticket[10m]) > 50000
for: 5m
labels:
severity: P2
team: ticket-mid
annotations:
summary: "单工单 Token 消耗超 5 万"
description: "疑似 Agent 死循环,请查 LangSmith"
自动化运维脚本
python
# auto_recover.py - 自动化恢复脚本
import asyncio
import logging
from langgraph.checkpoint.redis import RedisSaver
from build_graph import build_ticket_routing_graph
logger = logging.getLogger(__name__)
async def recover_stuck_tickets(redis_url: str, max_age_hours: int = 2):
"""恢复卡死的工单(状态超过 2 小时未变更)
Args:
redis_url: Redis 连接地址
max_age_hours: 最大允许停滞时长(小时)
"""
checkpointer = RedisSaver.from_conn_string(redis_url)
graph = build_ticket_routing_graph(redis_url)
# 1. 扫描 Redis 中所有未完成的工单状态
stuck_tickets = await checkpointer.ascan_stuck_states(
max_age_seconds=max_age_hours * 3600,
)
for ticket_id, state in stuck_tickets:
logger.warning(
"发现卡死工单 ticket_id=%s stage=%s,尝试恢复",
ticket_id, state.get("stage"),
)
try:
# 2. 从断点恢复执行
await graph.ainvoke(
state,
config={"configurable": {"thread_id": ticket_id}},
)
logger.info("工单恢复成功 ticket_id=%s", ticket_id)
except Exception as e:
logger.error(
"工单恢复失败 ticket_id=%s error=%s,转人工",
ticket_id, e, exc_info=True,
)
# 3. 恢复失败转人工兜底
await _escalate_to_human(ticket_id, reason="auto_recover_failed")
async def _escalate_to_human(ticket_id: str, reason: str):
"""转人工兜底"""
# 调用工单系统 API 派单到人工组
pass
# Crontab 配置:每 30 分钟扫描一次
# */30 * * * * /usr/bin/python3 /opt/ticket-agent/auto_recover.py >> /var/log/ticket-agent/recover.log 2>&1
systemd 服务配置
ini
# /etc/systemd/system/ticket-agent.service
[Unit]
Description=Ecommerce Ticket Routing Agent (Hunyuan Hy3 + WorkBuddy)
After=network.target redis.service
[Service]
Type=simple
User=ticket-agent
WorkingDirectory=/opt/ticket-agent
Environment="TENCENT_SECRET_ID=${TENCENT_SECRET_ID}"
Environment="TENCENT_SECRET_KEY=${TENCENT_SECRET_KEY}"
Environment="MILVUS_HOST=${MILVUS_HOST}"
Environment="REDIS_URL=${REDIS_URL}"
ExecStart=/opt/ticket-agent/.venv/bin/uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4
Restart=always
RestartSec=10
StandardOutput=append:/var/log/ticket-agent/app.log
StandardError=append:/var/log/ticket-agent/error.log
[Install]
WantedBy=multi-user.target
💰 成本核算与价值量化
项目成本对比
| 成本项 | 优化前(规则分发) | 优化后(Hy3 Agent) | 节省 |
|---|---|---|---|
| 规则维护人力 | 5 人 × ¥25K/月 = ¥125K | 1 人 × ¥25K/月 = ¥25K | ⬇️ 80% |
| 工单处理人力 | 12 人 × ¥15K/月 = ¥180K | 4 人 × ¥15K/月 = ¥60K | ⬇️ 67% |
| Hy3 API 成本 | ¥0 | ¥0.018 × 8 万 × 30 = ¥4.3 万 | -¥4.3 万 |
| WorkBuddy 企业版 | ¥0 | ¥2K/月 | -¥2K |
| 服务器成本 | ¥8K/月(原有) | ¥12K/月(新增 3 实例) | -¥4K |
| 月度总计 | ¥31.3 万 | ¥13.5 万 | ⬇️ ¥17.8 万 |
年度节省 : ¥17.8 万 × 12 = ¥213.6 万
ROI 计算:
- 项目研发投入: 3 人 × 3 个月 + 1 人 × 6 周 = ¥45 万(一次性)
- 年度净收益: ¥213.6 万 - ¥45 万 = ¥168.6 万
- 投资回报率: 168.6 / 45 = 375%
- 投资回收期: 45 / 17.8 ≈ 2.5 个月
Hy3 限免窗口的额外收益
混元 Hy3 限免至 2026 年 8 月 5 日,正好覆盖我们 7 月 1 日 - 8 月 5 日的压测 + 灰度 + 上线全流程,API 成本归零。按我们的调用量测算,限免期节省 API 成本约 ¥12.9 万(35 天 × 8 万工单 × ¥0.018/单 × 2.5 轮)。

图 4:优化前后月度成本对比,展示人力、API、服务器三项核心成本节省
⚠️ 常见问题 FAQ
Q1:混元 Hy3 的 Function Calling 与 OpenAI 有什么差异?
Hy3 的 Function Calling 接口在协议层兼容 OpenAI 格式,但有两点差异:① 鉴权用腾讯云 Signature v3,不是 Bearer Token;② ToolChoice 参数支持 auto、required、none、specific_tool,但不支持 OpenAI 的 parallel_tool_calls 字段。代码层面用我们的 HunyuanHy3ChatModel 封装即可屏蔽差异。
Q2:WorkBuddy 接入是否必须用 MCP 协议?
不强制。WorkBuddy 支持三种接入方式:① MCP 工具服务器(推荐,本方案采用);② HTTP Webhook;③ OpenAPI 规范。MCP 的优势是工具描述自动同步到 WorkBuddy 工具库,运营同学可在工作流中直接拖拽使用。HTTP Webhook 适合简单场景,OpenAPI 适合已有 API 资产复用。
Q3:Hy3 的 256K 上下文在生产中如何利用?
工单分发场景下,单次 Prompt 约 3-5 万 Token(系统 Prompt + Few-shot + 向量召回的 3 篇历史工单 + 当前工单内容),256K 上下文足够支撑 5-8 轮多轮对话无需压缩。但要注意 Token 成本,我们设置单会话 Token 上限 8 万,超过触发历史摘要压缩。
Q4:LangGraph 状态持久化为什么选 Redis 而不是 PostgreSQL?
工单分发是高频短时延场景(P99 ≤ 30 秒),Redis 的毫秒级读写时延优于 PostgreSQL。但 Redis 持久化依赖 RDB/AOF,有秒级数据丢失风险。我们对账任务每小时跑一次,从工单系统(PostgreSQL)反向同步状态到 Redis,弥补可能的丢失。
Q5:WorkBuddy 工作流编排与 LangGraph 状态机如何分工?
WorkBuddy 负责运营可调的策略层(派单规则、SLA 阈值、升级目标),运营同学用自然语言描述即可调整;LangGraph 负责工程师可控的执行层(Agent 调用顺序、工具选择、状态流转),通过代码迭代。两层通过 MCP 工具解耦,WorkBuddy 调用 LangGraph 暴露的 MCP 工具,LangGraph 不感知 WorkBuddy 的策略变化。
Q6:Hy3 限免期结束后成本如何控制?
限免期结束后,我们计划做三件事:① 切换到 Hy3 个人版套餐(¥28/月,适合小流量);② 对 SLA 监控等轻量任务用 hunyuan-hy3-turbo(更便宜);③ 引入 Token 预算控制,单工单 Token 超 5000 降级到 turbo 版。综合下来,全量切换后的月 API 成本约 ¥3.2 万,仍在预算内。
📝 总结与最佳实践
项目复盘
这套工单智能分发 Agent 系统从立项到上线历时 6 周(2026-05 ~ 2026-07),目前稳定运行 1 个月,日处理工单 8 万单。整个项目最大的体会是:腾讯混元 Hy3 + WorkBuddy 的组合,让 Agent 落地的门槛大幅降低。
代码层面,LangGraph 的 StateGraph、Function Calling、MCP 工具链都是成熟方案,Hy3 的 Agent 能力对标旗舰模型。但真正决定项目成败的是:① Hy3 的 Function Calling 幻觉如何检测和拦截;② WorkBuddy 工作流与 LangGraph 状态如何对账;③ SLA 监控的升级死循环如何预防。这些问题没有一个能靠"读文档"解决,必须靠 LangSmith Trace、A/B 测试、压测脚本一个一个蹲出来。
关键经验
- 多 Agent 架构要"Pipeline + 监控旁路":主链路清晰(分类→路由→闭环),SLA 监控独立运行不阻塞主流程;
- 混元 Hy3 的 Few-shot 要脱敏:Hy3 模仿能力强,Few-shot 示例值必须用占位符,真实数据通过工具注入;
- Function Calling 必须加守卫:LLM 有概率跳过工具或编造工具返回,代码层必须校验"关键信息是否来自工具";
- WorkBuddy + LangGraph 必须有对账:两层状态各自维护,每小时对账一次,发现不一致自动修复;
- 升级类操作必须幂等 :
escalate_count硬限 +recursion_limit=8,防止升级死循环。
适用边界
这套方案适用于:① 日均工单量 5000 单以上的中大型电商;② 有 Python 工程团队;③ 已采购腾讯云生态(混元 + WorkBuddy + CVM)。如果工单量小(< 1000 单/日)或团队是 Java 栈,建议用 Spring AI + LangChain4j + 通义千问方案,成本更低。
活动体验总结
作为腾讯混元 Hy3 × WorkBuddy 首发体验活动的 B 赛道(Agent 办公)投稿作品,本项目深度验证了三个核心结论:
- Hy3 是 Agent 场景的高性价比选择:相比 Hy2,分类准确率提升 7.6 个百分点,单次成本下降 44%;
- WorkBuddy 是 Agent 落地的最佳载体:可视化工作流编排让运营同学自主调整策略,工程师专注代码迭代;
- 混元 + WorkBuddy 原生组合的优势:模型与平台深度协同,首字延迟降低 54%,端到端响应时间缩短 47%。
强烈推荐做 Agent 应用的同学抓住 Hy3 限免窗口(到 8 月 5 日)做原型验证,体验"模型 + 平台"原生组合的开发效率提升。
📚 专栏导航
👍 如果本文对你有帮助,欢迎点赞、收藏、转发!
💬 有任何问题或建议,请在评论区留言交流,我和团队会一一回复~
✍️ 行文仓促,定有不足之处,欢迎各位朋友在评论区批评指正,不胜感激!
专栏导航:
- 📖 上一篇 : 《电商智能客服 Agent 实战:LangGraph 多 Agent 协作与生产落地》
- 📖 下一篇: 《Agent 评估体系搭建:从 LangSmith 到自动化回归》(待发布)
📌 真实性声明 :本文所述项目为笔者所在团队真实落地项目,部署数据、踩坑案例、性能指标均来自实际生产环境(截至 2026 年 7 月)。文中涉及的腾讯混元 Hy3 API 调用、LangSmith Trace、Prometheus 指标均经过脱敏处理,API Key、AccessKey、DB 密码等敏感信息用
${VAR}占位。文中提及的"分类准确率从 78% 提到 96.8%""月节省工单处理人力成本 ¥48 万"为对标原规则分发系统的对比口径,非纯利润,实际 ROI 需结合 Hy3 API 成本、开发投入、运维成本综合计算。混元 Hy3 与 WorkBuddy 的能力描述基于腾讯官方公开文档与笔者实际使用体验,活动标签 #WorkBuddy #混元Hy3 为腾讯混元 Hy3 × WorkBuddy 首发体验活动要求。