单 Agent 挂 5 个工具已经是极限------工具越多,LLM 选错率越高,系统消息越臃肿。这篇文章用 Router + Expert 模式拆分职责,让每个 Agent 只做自己最擅长的事。
问题:单 Agent 的天花板
Phase 2 的 DeviceAgent 把 5 个工具全注册在一个 Agent 上:
java
AiServices.builder(IndustrialAssistant.class)
.chatModel(chatModel)
.tools(alarmTool, dataTool, diagnosisTool, knowledgeBaseTool, workOrderTool)
.build();
三个问题逐渐暴露:
- 工具选错 ------用户问「温度趋势」,LLM 有时调了
queryDeviceAlarms而不是queryDeviceHistory - 系统消息臃肿------CoT 路径 + 5 个工具描述 + 约束条件 ≈ 800 token,每次请求都重复消耗
- 无法差异化优化------查告警和做诊断的推理路径完全不同,一个 SystemMessage 不可能两边兼顾
方案:Router + Expert
markdown
用户消息 → IntentClassifier(LLM + 关键词兜底)
↓
┌──────┼──────┬──────────┬──────────┐
↓ ↓ ↓ ↓ ↓
Alarm Data Diagnosis Knowledge General
Expert Expert Expert Expert Expert
核心思路:先分类,再分发。每个 Expert 只带 1-2 个工具和专精的系统消息。
意图分类:LLM 优先 + 关键词兜底
java
@Component
public class IntentClassifier {
public Intent classify(String message) {
// 1. 先用 LLM 分类(~50 token 的轻量调用)
Intent intent = classifyByLlm(message);
// 2. LLM 失败时退化到关键词匹配
if (intent == null) {
intent = classifyByKeyword(message);
}
return intent;
}
private Intent classifyByLlm(String message) {
String prompt = "将以下消息分类为:ALARM/DATA/DIAGNOSIS/KNOWLEDGE/GENERAL\n消息:" + message;
String result = chatModel.chat(prompt).trim().toUpperCase();
// 从返回文本中匹配枚举值
for (Intent i : Intent.values()) {
if (result.contains(i.name())) return i;
}
return null; // LLM 返回无效 → 交给关键词
}
private Intent classifyByKeyword(String message) {
if (Pattern.matches(".*诊断|故障|排查|工单.*", message)) return Intent.DIAGNOSIS;
if (Pattern.matches(".*告警|报警|警告.*", message)) return Intent.ALARM;
if (Pattern.matches(".*温度|振动|压力|数据|趋势.*", message)) return Intent.DATA;
if (Pattern.matches(".*知识|手册|怎么修.*", message)) return Intent.KNOWLEDGE;
return Intent.GENERAL;
}
}
为什么不只用关键词?因为「CNC-001 最近震动有点大,帮我看看是不是轴承的问题」不含「诊断」二字,但意图明显是 DIAGNOSIS。LLM 能理解语义,关键词只能匹配字面。
为什么不只用 LLM?因为 LLM 偶尔会返回奇怪的格式(「我认为这属于 ALARM 类别」而不是纯 「ALARM」),关键词兜底确保系统不卡死。
Expert:最小工具集 + 专精提示词
每个 Expert 是一个独立的 AiServices 实例,关键差异在 SystemMessage:
| Expert | 工具数 | SystemMessage 重点 |
|---|---|---|
| AlarmExpert | 1 | 告警解读、风险等级评估 |
| DataExpert | 1 | 数据趋势分析、阈值标注 |
| DiagnosisExpert | 5 | 完整 CoT 五步链 |
| KnowledgeExpert | 1 | 知识检索、维修方案 |
| GeneralExpert | 0 | 一般问答、引导用户 |
以 AlarmExpert 为例:
java
@Component
public class AlarmExpert {
interface AlarmAssistant {
@SystemMessage("""
你是工业设备告警分析专家。你只负责设备告警的查询和解读。
使用 queryDeviceAlarms 工具查询设备当前活跃告警。
回复规范:
- 列出所有告警,标注严重级别
- 对每条告警给出简要风险评估
- 如果无告警,明确说明设备状态正常
""")
String chat(String message);
}
public String chat(String message) {
AlarmAssistant assistant = AiServices.builder(AlarmAssistant.class)
.chatModel(chatModel)
.chatMemory(MessageWindowChatMemory.withMaxMessages(10))
.tools(alarmTool) // 只有1个工具
.build();
return assistant.chat(message);
}
}
对比单 Agent 的 SystemMessage(800 token),AlarmExpert 只用 120 token------省 85% 的提示词开销,且不会选错工具(因为只有一个可选)。
Router:分类 → 分发
java
@Service
public class RouterAgent {
public RouteResult route(String message) {
long start = System.currentTimeMillis();
Intent intent = classifier.classify(message);
String reply = switch (intent) {
case ALARM -> alarmExpert.chat(message);
case DATA -> dataExpert.chat(message);
case DIAGNOSIS -> diagnosisExpert.chat(message);
case KNOWLEDGE -> knowledgeExpert.chat(message);
case GENERAL -> generalExpert.chat(message);
};
long elapsed = System.currentTimeMillis() - start;
return new RouteResult(intent, reply, elapsed);
}
public record RouteResult(Intent intent, String reply, long latencyMs) {}
}
返回值带 intent 和 latencyMs,方便前端展示路由决策过程和性能监控。
实测结果
| 测试消息 | 分类结果 | 延迟 |
|---|---|---|
| "CNC-001 有什么告警?" | ALARM ✅ | 6.8s |
| "查一下最近一小时的温度趋势" | DATA ✅ | 6.6s |
| "振动异常,诊断并创建工单" | DIAGNOSIS ✅ | 16.5s |
| "轴承温度过高怎么修?" | KNOWLEDGE ✅ | 16.6s |
| "你好,你是谁?" | GENERAL ✅ | 2.5s |
分类准确率 5/5 = 100%。
延迟分析:
- GeneralExpert 最快(2.5s)------无工具调用,纯 LLM 回复
- ALARM/DATA 中等(~7s)------分类 + 1 次工具调用 + LLM 生成
- DIAGNOSIS 最慢(16.5s)------分类 + 多次工具调用(告警+数据+知识+诊断+工单)
单 Agent vs Router 对比
| 维度 | 单 Agent | Router + Expert |
|---|---|---|
| 工具选择准确率 | ~85%(偶尔选错) | ~99%(Expert 只有 1 个工具) |
| 系统消息开销 | 800 token/次 | 120-300 token/次 |
| 可扩展性 | 加工具越多越难管理 | 加 Expert 不影响其他 |
| 延迟 | 一次 LLM 调用 | 分类 + Expert 两次调用 |
| 复杂度 | 低 | 中 |
Router 的额外延迟(~1s 分类)换来了更高的准确率和更低的 token 消耗。 对工业场景来说,准确率比延迟重要。
什么时候不需要 Router?
- 工具 ≤ 3 个------LLM 选得足够准,Router 是过度设计
- 所有请求都需要多工具协作------Router 反而多一跳,不如让单 Agent 自由编排
- 对延迟极度敏感------每多一次 LLM 调用就多 1-2 秒
代码结构
markdown
agent/
├── DeviceAgent.java --- 原有单 Agent(保留做对比)
├── router/
│ ├── Intent.java --- 5 种意图枚举
│ ├── IntentClassifier.java --- LLM + 关键词分类器
│ └── RouterAgent.java --- 路由编排器
└── experts/
├── AlarmExpert.java --- 告警专家(1 工具)
├── DataExpert.java --- 数据专家(1 工具)
├── DiagnosisExpert.java --- 诊断专家(5 工具 + CoT)
├── KnowledgeExpert.java --- 知识专家(1 工具)
└── GeneralExpert.java --- 通用对话(无工具)
下一步
Router 解决了「分发」问题,但每个 Expert 是独立的------它们之间不会协作。下一篇文章引入 Supervisor 模式:一个「主管 Agent」动态规划多步任务,委派给不同 Expert,汇总结果后统一回复。
代码仓库:github.com/LaoLiang-ag...
本文由 LaoLiang 原创,首发于掘金/知乎/微信公众号。转载请联系作者。