前面我们讲过了三层意图识别,把前面的链路总结一下,一次请求进来有两个情况: - L1/L2 命中 - L3 命中 三层意图识别里,L1(正则)和 L2(向量最近邻)本质都是单标签分类器——一条规则、一条最近邻,只能给出一个意图。而L3则承载着一个至关重要的功能,那就是用户的多意图识别! (IntentRecognitionResult(L1/L2 的产物)里 multiIntent 恒为 false)
// L1/L2 的便捷工厂:只会产出单意图(multiIntent=false)
public static IntentRecognitionResult single(Source source, IntentCategory category,
Confidence confidence, String reason, Double score) {
IntentItem item = new IntentItem(category, category.getDefaultTargetAgent(), confidence, reason);
return new IntentRecognitionResult(source, List.of(item),
category, false /* multiIntent 恒为 false */, reason, score);
}
用户的一句"审批过了,帮我规划下杭州行程,顺便看看那边的酒店,再帮我把回程的高铁订了",里面塞了三四个意图(行程规划、酒店查询、火车预订),而且它们之间有先后依赖——得先规划出方案,才谈得上订哪一班高铁。 这就带来三个必须解决的问题: 1. 识别:怎么从一句话里抽出多个意图,而不是只认出最显眼的那个? 2. 排序:多个意图谁先谁后?"先规划后预订"这种依赖关系必须被尊重,否则会出现"还没方案就去订票"的荒唐执行。 3. 执行与整合:怎么把多个子任务依次交给不同的专业子智能体去做,并把它们各自的结果拼成一次自然连贯的回复,而不是甩给用户三段互不相干的机器回答?
多意图的数据契约
L3 的系统提示词 intent-recognition-agent-system.md 把多意图 JSON 的格式钉死:
{
"intents": [
{ "intent": "itinerary_planning", "target_agent": "itineraryPlanAgent",
"confidence": "high", "reason": "用户审批已通过,明确要求行程规划,并指定途经点。" },
{ "intent": "hotel_search", "target_agent": "itineraryPlanAgent",
"confidence": "high", "reason": "用户同时要求查询杭州酒店,属于行程规划后续动作。" }
],
"primary_intent": "itinerary_planning",
"multi_intent": true,
"overall_reason": "用户一句话包含行程规划和酒店查询两个意图,二者有先后依赖关系,先规划再查酒店。"
}
契约里有三个专为"多意图执行"服务的字段,我们针对多意图的约束是这样的:
2. **多意图处理**:
- 若一句话包含多个意图,必须全部识别出来,放入 `intents` 数组中。
- 按**执行优先级/依赖关系**排序:有前后依赖的(如先申请后规划、先规划后预订),先执行的排前面。
- 在 `primary_intent` 中指定最核心或最紧迫的意图。
多意图必走 MasterAgent
拿到意图识别的结果JSON 后,如果是多意图,我们必须强制走MasterAgent,因为多意图天然含有跨子智能体的编排与依赖,没有一个子智能体能独立完成,因此必须交给专门的协调者——MasterAgent。 于是多意图的执行入口收敛到这里:
private Mono<Msg> dispatchByIntent(String intentJson, List<Msg> inputMessages,
String contextQuestion, String sessionId, String userId) {
Optional<DirectDispatchPlan> planOpt = tryPlanDirectDispatch(intentJson);
if (planOpt.isPresent()) {
// 单意图快车道:直连子智能体
return dispatchSubAgentDirectly(planOpt.get(), inputMessages, contextQuestion, sessionId, userId);
}
// 多意图(及低置信/兜底)走 MasterAgent 编排
ReActAgent masterAgent = agentRegistry.getAgent(MASTER_AGENT_NAME);
return masterAgent.call(buildMasterInput(inputMessages, contextQuestion, intentJson))
.doOnSubscribe(s -> executionRegistry.register(sessionId, masterAgent))
.flatMap(masterResult -> handleMasterResult(masterResult, sessionId));
}
注意最后一句:完整的意图 JSON(含有序 intents 数组)通过 buildMasterInput 被塞进 MasterAgent 的上下文——这是 MasterAgent 知道"该按什么顺序做哪几件事"的唯一依据。 但是需要注意的是,多意图的"依次执行"并不是一段 for 循环手写的调度代码,而是交给 MasterAgent(一个 ReActAgent)的推理循环去驱动——它把每个子智能体当成一个"工具",在 ReAct 的"思考→调工具→观察→再思考"循环里,按意图清单顺序一个个调用。 MasterAgent 提示词里专门有一段"多意图处理",这就是"依次执行"的行为契约: