用户说了一句话,系统如何以最低成本、最高确定性,把消息交给正确的 Agent。
多 Agent 消息路由:用户消息如何找到正确的 Agent
多 Agent 系统真正麻烦的地方,不只是“每个 Agent 会什么”,而是:
用户发来一条消息,到底该让哪个 Agent 处理?
比如:
帮我看看有没有合适的 Java 岗位
系统可能同时存在:
岗位推荐 Agent
岗位检索 Agent
身份采集 Agent
任务 Agent
默认聊天 Agent
如果路由错了,后面的模型能力再强也没用。
因此,多 Agent 系统通常需要专门的 Router。
1. 一条消息怎么到 Agent?
可以把整个过程理解成一条判断链:
IM Message
↓
渠道适配
↓
Message Router
↓
身份检查
↓
系统命令
↓
显式 Agent 切换
↓
已有会话绑定?
↓
意图分类
↓
Agent 匹配
↓
执行 Agent
这里最重要的设计思想是:
能用确定性规则解决的问题,不要急着调用大模型。
例如:
/help
/reset
/job-recommend
这些都是明确指令,直接处理即可。
没必要花一次 LLM 调用去问:
“请判断 /help 的用户意图。”
2. 为什么要做多层意图分类?
自然语言本身存在模糊性。
例如:
实习
它可能表示:
想找实习岗位
也可能表示:
想聊自己的实习经历
所以一种比较实用的方案是:
规则
↓
关键词
↓
LLM
越往后:
成本更高
速度更慢
理解能力更强
3. 第一层:系统命令
最高优先级。
例如:
/help
/reset
/agents
/job-recommend
直接精确匹配。
命令匹配成功
→ confidence = 1.0
→ 直接执行
这是最确定、最快、最便宜的一层。
4. 第二层:关键词加权
普通自然语言可以先跑关键词。
例如:
投简历 → 1.0
岗位推荐 → 0.95
找工作 → 0.8
实习 → 0.5
分类器找到权重最高的关键词。
然后:
>= 0.9
→ 直接使用
0.7 ~ 0.9
→ 可以使用
< 0.7
→ 交给 LLM
例如:
“帮我投简历”
已经非常明确,根本不需要调用模型。
这背后的原则是:
规则负责高确定性场景,LLM 负责语义模糊场景。
5. 第三层:LLM 兜底分类
关键词判断不了,再使用大模型。
把:
用户消息
+
当前可用 Agent 列表
一起交给 LLM。
要求返回:
public record IntentResult(
IntentType intent,
double confidence,
String reasoning
) {}
这里刚好用到之前学过的:
Structured Output
完整关系:
User Message
↓
LLM Intent Classifier
↓
Structured Output
↓
Intent + Confidence
↓
Agent Router
这样不用手动解析模型返回的自然语言。
6. 为什么不是所有消息都做一次分类?
因为多轮聊天里,大量消息其实属于同一个 Agent。
例如:
用户:
帮我推荐武汉 Java 岗位
Agent:
……
用户:
25K 以上
Agent:
……
用户:
这个第二个怎么样?
后面两句话单独拿出来都非常模糊。
如果每次重新分类:
“25K以上”
可能被错误路由。
更重要的是,新 Agent 根本不知道之前聊了什么。
所以需要:
Session → Agent Binding
7. 会话绑定
第一次完成分类:
Conversation A
↓
JobRecommendAgent
就建立:
Conversation A
→ JobRecommendAgent
后续消息:
Conversation A
↓
发现已有绑定
↓
直接 JobRecommendAgent
跳过:
关键词分类
LLM 分类
Agent 匹配
于是一次完整流程:
第一次消息
→ 分类
→ Agent
→ 建立绑定
后续消息
→ 查绑定
→ Agent
这不仅省钱,还能保持上下文连续。
8. 什么时候解除绑定?
Agent 不能永远绑着。
通常考虑:
/reset
→ 强制解除
没有绑定
→ 重新分类
TTL 过期
→ 重新分类
用户显式切换 Agent
→ 更新绑定
例如设置:
TTL = 6 hours
本质上是在定义:
一次对话上下文在多长时间内仍然认为属于同一个业务意图。
9. Agent 怎么自动注册?
如果以后增加:
ResumeAgent
InterviewAgent
SubscriptionAgent
每次都修改 Router:
if (...) {
return resumeAgent;
} else if (...) {
return interviewAgent;
}
很快就会变成巨大的 if-else。
更好的方式是定义统一接口:
public interface BizAgent {
AgentIntro getAgentIntro();
String process(...);
List<IntentType> supportedIntents();
}
实现类:
@Component
public class JobRecommendAgent
implements BizAgent {
}
Spring 启动后自动收集:
JobRecommendAgent
JobSearchAgent
IdentityAgent
...
形成:
Agent Registry
路由时:
Intent
↓
Agent Registry
↓
找到支持该 Intent 的 Agent
于是新增 Agent 不需要修改 Router。
这就是:
插件式 Agent 注册。
10. 多个 Agent 支持同一个意图怎么办?
例如:
JobRecommendAgent
AdvancedJobRecommendAgent
都支持:
JOB_RECOMMEND
可以增加:
priority
路由时:
候选 Agent
↓
权限过滤
↓
priority 排序
↓
取最高优先级
同时还可以结合用户角色:
普通用户
→ Basic Agent
高级用户
→ Advanced Agent
所以 Agent Registry 不只是一个 Map,它还可以负责:
Agent Discovery
Permission
Priority
Capability Matching
11. 对话路由和工作流路由不要混
你前面学的 LangGraph4j 也有“路由”。
但它们不是一回事。
对话路由
依据:
用户自然语言
例如:
“帮我找个 Java 工作”
需要:
规则
关键词
LLM
有一定不确定性。
工作流路由
依据:
业务数据状态
例如:
if (gatherList.isEmpty()) {
return END;
}
是完全确定的。
所以可以直接记:
自然语言决定下一步
→ Message Router
数据状态决定下一步
→ Workflow Router
12. 整体架构
最终可以抽象成:
IM Message
↓
Message Router
↓
┌──────────────┼──────────────┐
↓ ↓ ↓
Command Session Binding Intent
↓
Keyword Classifier
↓
LLM Classifier
↓
Agent Registry
↓
Permission + Priority
↓
Agent
↓
LlmCaller
其中:
Router
负责找谁
Agent
负责业务
LlmCaller
负责调用模型
ChatClient
负责组织 LLM 调用
职责非常清晰。
13. 最值得沉淀的几个原则
可以记住这几个关键词:
Short Circuit
→ 能提前返回就不要继续分类
Rule First
→ 确定性逻辑优先
LLM Fallback
→ 模糊语义才交给模型
Session Binding
→ 多轮对话避免重复分类
Agent Registry
→ Agent 插件化注册
Permission + Priority
→ 控制 Agent 选择
Intent Routing
→ 自然语言路由
Workflow Routing
→ 数据状态路由
最后总结
多 Agent 路由真正要解决的不是:
“怎么让 LLM 判断一个 Intent?”
而是:
怎么设计一套从便宜、快速、确定,到昂贵、智能、模糊的分层路由体系。
一个比较合理的顺序就是:
系统命令
↓
显式 Agent
↓
Session Binding
↓
关键词
↓
LLM
↓
Agent Registry
最值得记住的一句话:
多 Agent 路由不要什么都交给 LLM:确定性的请求用规则短路,连续对话用会话绑定,只有真正模糊的自然语言才调用 LLM 做意图分类。
把前面的知识继续串起来:
Message Router
→ 用户该去哪一个 Agent
Agent Registry
→ 系统有哪些 Agent
LlmCaller
→ Agent 怎么调用 AI
ChatClient
→ 怎么组织一次模型调用
Advisor
→ 怎么增强调用链
ChatMemory
→ 怎么维持上下文
ReAct / MCP
→ Agent 怎么使用工具
LangGraph4j
→ 多步骤任务怎么编排