给 Agent 装个"总机":策略调度器让多种 Agent 各司其职
AutoAgent 会自己打磨,FlowAgent 会先规划再干,FixedAgent 按固定流程走……项目里 Agent 多了,怎么让它们各干各的活,还不改主代码?
一、一个现实问题:Agent 不止一种
做了两个 Agent 之后,你会发现一个很自然的诉求:
- AutoAgent:适合"结果要反复打磨"的任务(分析→执行→质检→回炉)
- FlowAgent:适合"流程明确"的任务(盘工具→规划→拆步→执行)
- 后来还可能有 FixedAgent(固定流水线)、XXAgent……
这时候最粗暴的写法是:
if ("auto".equals(type)) {
autoAgent.execute(...);
} else if ("flow".equals(type)) {
flowAgent.execute(...);
} else if ("fixed".equals(type)) {
fixedAgent.execute(...);
} else {
throw new RuntimeException("不认识的类型");
}
能跑,但每次加一种 Agent 都要改这段代码。改多了就变成一坨没人敢动的 if-else。
那怎么设计,才能做到"加新 Agent 不改主代码"?
二、答案:一个"不聪明"的总机
思路其实特别朴素——加一个调度器(Router),它不做任何智能决策,只做一件事:查表转发。
用户请求(带了 agentId)
│
▼
总机(调度器)
├─ 查数据库:这个 agentId 的"工种标签"是什么?
├─ 用标签去 Map 里找同名对象
└─ 找到谁,就把活派给谁
为什么说它"不聪明"?因为它整个逻辑就三步,不思考、不调模型、不做业务判断——它只是个"查表转发的接线员"。
三、总机怎么知道派给谁:两层对暗号
总机"知道"的关键,是数据库写一个名字,代码挂一个同名牌子,两边对得上:
第一层:数据库里每个 Agent 档案上写着归属
ai_agent 表加一列 strategy,由配置的人决定每个 Agent 属于哪个工种:
智能体4(查日志)→ strategy = "autoAgentExecuteStrategy"
智能体1(发文章)→ strategy = "flowAgentExecuteStrategy"
智能体6(固定活)→ strategy = "fixedAgentExecuteStrategy"
这个字段是配置,不是代码——所以"哪个 Agent 走哪条路",改数据库就行,不用动代码。
第二层:代码里每个策略门口挂着同名牌子
每个策略类通过注解注册自己的名字:
@Service("autoAgentExecuteStrategy") → AutoAgentExecuteStrategy
@Service("flowAgentExecuteStrategy") → FlowAgentExecuteStrategy
@Service("fixedAgentExecuteStrategy")→ FixedAgentExecuteStrategy
Spring 启动时,把所有实现 IExecuteStrategy 接口的类收集成一个 Map,key 就是注解名:
executeStrategyMap = {
"autoAgentExecuteStrategy" → AutoAgentExecuteStrategy对象,
"flowAgentExecuteStrategy" → FlowAgentExecuteStrategy对象,
"fixedAgentExecuteStrategy" → FixedAgentExecuteStrategy对象,
}
总机干活:拿档案上的名字,对 Map 里的牌子
// ① 查档案
String strategy = repository.queryAiAgentByAgentId(agentId).getStrategy();
// "autoAgentExecuteStrategy"
// ② 对暗号:用名字去 Map 取对象
IExecuteStrategy executeStrategy = executeStrategyMap.get(strategy);
// ③ 找不到就报错,找到就派活
if (executeStrategy == null) throw new BizException("不存在的策略:" + strategy);
executeStrategy.execute(request, emitter);
总机从头到尾只做字符串查表。 它不懂业务,不懂 Auto 和 Flow 的区别,它只是对名字。
四、为什么这个设计好:加新策略不改主代码
这是这套设计最值钱的地方——开闭原则:
- 加一种新 Agent(比如 FixedAgent)→ 写一个新类,
@Service("fixedAgentExecuteStrategy")注解一下 - Spring 自动把它收进 Map
- 总机一行不用改,因为它是从数据库读名字、从 Map 取对象
对比一下:
if-else 版:加新策略 → 改主代码 → 重新发版 → 可能改崩别人
调度器版:加新策略 → 写个新类 → 数据库配一行 → 完事
调度器版的好处还不止加策略:同一个 Agent 想换策略,改数据库字段就行(比如智能体1 从 flow 改成 auto,一行 update);多个 Agent 复用同一策略,也是配置说了算。
五、一个重要澄清:总机不是"主 Agent"
很多人一听"调度器"会以为:是不是有个"主 Agent"在操控其他 Agent?
不是。 这是两种完全不同的架构哲学:
| 路由分发(本文) | Orchestrator 编排 | |
|---|---|---|
| 入口 | 无智能,纯查表转发 | 有智能,会思考派谁 |
| 决策依据 | 数据库配置(strategy 字段) | 主 Agent 现场判断 |
| 关系 | 各策略平级,总机不操控 | 主 Agent 派活给子 Agent |
| 适合 | 策略事先可分、类型明确 | 任务需要动态编排 |
本项目的调度器属于路由分发:它不聪明,聪明的是"配置"——把"智能体4走 Auto"这件事提前写死在数据库里,总机只是执行这个决定。
六、也别忽略边界
作为学习沉淀,照例泼冷水:
- 策略名要精确匹配:数据库写的名字和注解名差一个字符就找不到,会抛"不存在的策略"。生产上可以加"启动时校验所有 agent 的 strategy 都能在 Map 找到"。
- Map 是静态注册的:策略列表在启动时就固定了,运行时不能热插拔(热插拔需要动态类加载或插件机制)。
- 调度器不做兜底:策略执行失败了,总机只负责把异常抛出去,不做降级或换策略重试(这些要策略内部或外面再包一层)。
- 真正的决策在配置:调度器把"智能"外包给了数据库,那"配错了"就是事故——配置要有校验和管理界面。
七、总结
三句话收尾:
- Agent 多了之后,需要一个"总机"统一入口——它不做智能决策,只做查表转发。
- 两暗号机制:数据库的
strategy字段(配置决定归属)+ 注解名注册的 Map(代码提供对象),字符串对上就派活,加新策略不用改总机。 - 调度器不是主 Agent——它是路由分发,不是 Orchestrator 编排;聪明在配置,不在调度器。
至此,Agent 设计三部曲完成:
- 《让 AI 学会"自己干活、自己检查、自己改"》——一个 Agent 怎么自我闭环(多角色)
- 《让 AI 学会"先写计划再干活"》——Agent 的两种执行哲学(动态循环 vs 规划执行)
- 《给 Agent 装个"总机"》——多种 Agent 怎么组织调度(路由分发)
从"一个 Agent 内部怎么设计"到"多个 Agent 外部怎么组织",这条 Agent 架构的学习路径就算闭环了。