多Agent 意图识别与路由

多 Agent 意图识别与路由

原文章:Agent 意图识别与路由

1. 原文的思想

生产环境推荐采用:

规则 + 小模型 + LLM + State + Router + Planner + Policy + Fallback

整体流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
               用户输入
↓
Context / State
↓
Intent + Slot 提取
↓
┌─────────────┴─────────────┐
│ │
规则能确定? 不能确定
│ │
↓ 小模型
Route ↓
能确定?
┌────┴────┐
Yes No
│ │
│ LLM
│ ↓
└──────────┘
↓
Intent + Slot + Task
↓
Slot Validation
↓
┌────────────┴────────────┐
│ │
不完整 完整
│ │
↓ ↓
Clarification Router
│ ↓
└──────────────→──────────┤
↓
是否单意图 / 简单任务?
┌─────────┴─────────┐
Yes No
↓ ↓
直接路由 Planner
↓ ↓
Agent Task Decomposition
↓
Task Graph / Plan
↓
┌───────────┼───────────┐
↓ ↓ ↓
Agent A Agent B Tool C
└───────────┼───────────┘
↓
Aggregator
↓
Policy
↓
Response

核心原则:

简单问题规则解决,稳定问题小模型解决,复杂问题 LLM 解决;Router 负责分流,Planner 负责复杂任务编排,State 负责多轮上下文,Policy 负责约束,Fallback 负责兜底。

2. 我们的Agent流程图

总体流程图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
                    用户输入
↓
会话上下文 / 任务状态 / 用户权限
↓
装配当前可用领域
┌─────────────┴─────────────┐
↓ ↓
代码固定注册公共域 Paladin 配置业务域
└─────────────┬─────────────┘
↓
按用户权限过滤 Tool / Skill
↓
生成领域能力描述及委派工具列表
↓
主 Agent
理解用户意图、判断任务、选择处理方式
↓
┌─────────────┴─────────────┐
↓ ↓
可以直接回复 需要领域能力
↓ ↓
欢迎语 / 能力说明等 选择委派目标
超出范围则说明边界 ↓
│ ┌────────────┼────────────┐
│ ↓ ↓ ↓
│ 普通业务请求 文档 / 任务控制 需要长任务规划
│ ↓ ↓ ↓
│ 选择业务域 选择公共域 选择公共域
│ └────────────┼────────────┘
│ ↓
│ 生成委派工具调用
│ delegate_to_xxx(...)
│ ↓
│ 执行守卫校验
│ 当前任务状态 / 规划执行阶段
│ 单次领域委派等约束
│ ↓
│ ┌────────────┴────────────┐
│ ↓ ↓
│ 校验不通过 校验通过
│ ↓ ↓
│ 返回拒绝原因供处理 SubAgentTool 调用目标 Agent
│ ↓
│ ┌──────────────┴──────────────┐
│ ↓ ↓
│ 领域业务处理 长任务规划
│ ↓ ↓
│ 选择本域 Tool / Skill 建立任务、保存完整计划
│ ↓ ↓
│ 提取参数,缺失时补充或追问 展示计划,等待整体确认
│ ↓ ↓
│ 参数 / 权限 / 执行状态校验 确认后进入执行阶段
│ 按策略要求取得操作确认 ↓
│ ↓ 主 Agent 按计划选择业务域
│ 执行业务工具 并复用上述委派链路
│ ↓ ↓
│ 返回领域结果 持久化进度、安排续跑
│ ↓
│ ┌──────────┴──────────┐
│ ↓ ↓
│ 结果可直接完整回答 需要后续调用或综合
│ direct_return=true direct_return=false
│ ↓ ↓
│ 直接返回结果 主 Agent 继续编排
│ ↓
│ 选择下一领域 / 汇总结果
└──────────────┬─────────────────────┘
↓
用户回复

简要流程图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
          用户输入 + 会话上下文
↓
加载当前用户可用的领域能力
↓
主 Agent 理解意图并路由
↓
┌─────────────┴─────────────┐
↓ ↓
普通业务请求 需要长任务规划
↓ ↓
选择对应业务域 公共域制定并保存计划
│ ↓
│ 用户整体确认
│ ↓
│ 按计划选择对应业务域
└─────────────┬─────────────┘
↓
调用子 Agent 委派工具
↓
执行守卫与权限校验
↓
子 Agent 调用本域 Tool / Skill
↓
结果直返 / 主 Agent 继续编排或汇总
↓
用户回复

3. 具体步骤的介绍

系统采用 主 Agent 统一理解请求、通过委派工具调用领域子 Agent、由子 Agent 执行业务 的方式。意图理解与领域选择在主 Agent 的推理过程中一起完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
用户请求与上下文
↓
装配用户可用的领域能力
↓
主 Agent 理解意图、判断处理方式
↓
简单请求直接委派 / 长任务先规划并确认
↓
执行守卫校验,调用目标子 Agent
↓
子 Agent 使用本域 Tool / Skill 执行业务
↓
结果直返 / 主 Agent 继续编排或汇总
↓
输出结果

1. 接收用户请求,准备上下文

请求进入 ChatService 后,系统获取用户、会话、权限以及本轮消息,结合会话历史和当前任务状态,准备 Agent 运行上下文。

上下文用于理解连续对话。例如,用户只说“继续”,需要结合已有任务判断继续什么;用户追问“这个文档中的图片”,需要找到此前上传文档的轻量引用。

对于已有待确认操作、部分任务恢复等场景,服务端会先尝试使用专用逻辑处理;需要常规语义理解和领域选择的请求,再进入主 Agent。

2. 装配当前用户可用的领域能力

AgentFactory 根据领域配置和用户权限,生成主 Agent 可以调用的子 Agent 集合:

  • 公共域:由代码固定注册,承担文档处理、长任务规划和任务状态协调等能力。
  • 业务域:通过 Paladin 配置系统提示词、Tool、Skill 和权限码。

系统先按用户权限过滤各领域的 Tool、Skill,再汇总实际可用的能力描述。某个领域过滤后没有任何可用能力,就不会注册对应的委派入口。

因此,主 Agent 的路由候选范围由实际配置和用户权限共同决定。

3. 主 Agent 理解意图,选择处理方式

主 Agent 结合以下信息判断用户需求:

  • 当前用户请求及会话上下文。
  • 当前任务状态。
  • 各领域的可用能力描述。
  • 系统提示词中的路由和执行规则。

随后决定直接回复、追问,还是委派给某个领域。

例如,普通活动查询应选择具备相关能力的活动域;文档理解请求应委派公共域;超出可处理范围的请求,则说明当前能力边界。

这里没有统一要求模型先输出 intent、slots、route 对象,而是通过选择委派工具和填写调用参数,表达意图理解与路由结果。

4. 判断是否需要先进行长任务规划

主 Agent 同时判断请求能否在当前轮次可靠完成。

对于需要长任务执行的请求,先委派公共域建立任务、保存完整计划并展示给用户,取得一次整体确认后,再按计划选择业务域执行。

判断依据包括:

  • 是否需要批量、分批处理。
  • 步骤之间是否存在依赖。
  • 是否需要等待异步结果。
  • 是否能够在单轮执行预算内可靠完成。

多意图不一定等于长任务,单领域请求也可能需要长任务规划。 例如,单领域的大批量操作仍可能需要计划与跨轮执行。

5. 通过委派工具调用目标子 Agent

每个可用子 Agent 都通过 AgentScope 的 SubAgentTool 包装成工具,注册到主 Agent 的工具集中,例如:

1
2
3
delegate_to_common_domain
delegate_to_activity_domain
delegate_to_delivery_domain

主 Agent 生成对应的工具调用后,框架根据注册关系调用目标子 Agent。

模型负责选择委派工具,工具名称与子 Agent 的绑定关系由代码和配置确定。

在实际调用前,执行守卫会检查当前任务状态及执行阶段,约束需要先规划的请求,并限制主 Agent 每次只委派一个领域。

6. 子 Agent 使用本域工具执行业务

目标子 Agent 是领域内的 ReActAgent,持有本域系统提示词和经过权限过滤的 Tool、Skill。

子 Agent 根据委派目标选择能力、组织参数、调用工具,再根据返回结果决定下一步:

1
2
3
4
5
6
7
8
9
理解领域任务
↓
选择 Tool / Skill,组织参数
↓
调用前校验
↓
执行并观察结果
↓
继续调用、请求补充信息或形成答案

参数处理分布在主 Agent、子 Agent 和具体工具中,没有统一的入口 Slot 管理层。缺少必要信息时,需要补充或追问;实际执行还受到参数校验、权限约束、任务状态和操作确认机制的限制。

识别出用户想执行某项操作,并不代表已经获得执行许可。

7. 根据结果继续编排,必要时保存进度

子 Agent 返回结果后,主 Agent 判断任务是否已经完成:

  • 结果完整:进入最终回复。
  • 需要其他领域能力:根据中间结果选择下一领域,继续委派。
  • 需要长任务跨轮执行:记录真实结果、执行游标和剩余工作,通过任务机制安排后续推进。
  • 缺少信息或执行结果不确定:按实际情况请求用户介入。

跨领域处理通过主 Agent 分步编排完成;当前约束下,每次只委派一个领域,领域内部再按运行时开放的安全能力执行。

8. 直接返回领域结果,或由主 Agent 汇总输出

每次领域委派都会携带 direct_return 参数:

  • true:本轮领域结果预计可以完整回答用户;返回后满足直返条件时,直接使用该结果。
  • false:还需要后续调用、规划、参数确认或多领域综合,由主 Agent 继续处理。

最终回复基于工具和子 Agent 的真实返回结果生成。普通任务输出业务答案;等待确认的任务展示待确认内容;跨轮任务则根据实际执行状态反馈进度。

4. 关键问题

4.1 我们是直接调用大模型进行意图识别的吗

是的,普通业务请求的意图识别,主要由主 Agent 调用大模型完成。

不过,我们没有单独调用一次模型来“识别意图”,再调用另一个模块路由。实际流程是:

1
2
3
4
5
6
7
用户请求 + 会话上下文 + 领域能力描述 + 委派工具定义
↓
主 Agent 的大模型
↓
直接回复 / 追问 / 生成子 Agent 委派工具调用
↓
程序校验并调用目标子 Agent

例如,用户要求查询活动配置,模型根据活动域的能力描述,选择 delegate_to_activity_domain,框架再执行该委派工具。

所以更准确的表述是:

我们通过主 Agent 的大模型完成语义理解和领域选择,以工具调用的形式输出路由决策,再由程序负责校验和执行。

另外,已有操作的明确“确认”“取消”,以及部分任务恢复请求,会先经过服务端专用逻辑处理,并非所有输入都要重新交给大模型做业务路由。

4.2 复杂任务规划我们是怎么处理的?当主agent拿到一个很复杂的请求之后,他后续是怎么做的?

更具体地说,主 Agent 负责判断和委派,公共域负责生成并保存计划,任务服务负责确认状态、执行进度和续跑控制。 普通长任务与结构化批次的执行方式还有区别。 Annotation 1

公共域是我们系统中承载通用能力和任务协调能力的一个子 Agent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
主 Agent 判断需要长任务处理
↓
委派公共域加载规划工作流
↓
必要查询 → 创建任务 → 提交完整计划
↓
保存计划,展示给用户并等待整体确认
↓
确认当前计划版本,安排执行轮次
↓
普通步骤由 Agent 编排 / 已注册批次由服务端执行
↓
保存真实结果和进度
↓
继续执行 / 等待异步结果 / 请求介入 / 完成

1. 主 Agent 先判断:这个请求是否需要进入长任务流程

判断依据包括任务规模、步骤依赖、异步等待、结果体量和当前轮次的执行预算。

例如,一个请求包含“生成图片,再使用生成结果执行后续操作”,就需要考虑异步依赖,不能只看有几个对象。

这里也有程序约束:对于“多个对象分别处理、批量处理”等明显场景,服务端会通过规则标记需要先规划,执行守卫会拦截直接执行业务的调用。

因此,一般复杂度由模型结合上下文判断,部分明确场景由规则识别并强制进入规划流程。

2. 主 Agent 把规划任务委派给公共域

主 Agent 调用 delegate_to_common_domain,将用户目标和必要上下文交给公共域,当前阶段使用 direct_return=false。

公共域通过大模型和长任务工作流完成规划。工作流规定的首次规划顺序是:

1
2
3
4
5
6
7
8
9
加载长任务工作流
↓
加载当前可用且匹配的业务工作流
↓
完成规划所必需的只读查询
↓
调用 agent_task_create 创建任务
↓
调用 agent_task_submit_plan 提交完整计划

各次 Skill 加载独占一次工具调用批次,但会在同一次公共域委派内继续推理和执行,不需要用户每加载一次就回复“继续”。

这里的 Skill 是指导模型如何规划和调用工具的规范,实际业务目标仍由大模型结合请求、查询结果和可用能力进行拆解。

3. 公共域提交的是结构化计划,并真正保存下来

计划中的每个高层步骤可以包含:

字段 含义
title、description 这一步做什么
domain 负责处理的领域
sideEffect 是否涉及写操作等副作用
waitForAsyncResult 是否需要等待异步结果
batch 可选的批次动作与各项业务参数

服务端校验计划格式;如果包含批次,还会检查动作是否有已注册执行器、参数是否合法。

保存成功后,任务会记录步骤、计划版本和摘要,进入:

1
2
AWAITING_PLAN_CONFIRMATION
等待用户确认完整计划

只有模型在回复中写出“一、二、三”,并不算规划成功;完整计划必须持久化。 对受控首次规划,委派返回时还会检查计划是否已经保存。

4. 展示完整计划,用户确认后再开始执行

系统向用户展示全部高层步骤,取得一次整体确认。

确认时,服务端会把当前 planVersion 记录为 confirmedPlanVersion,然后安排后续执行。开始运行轮次时,还会核对:

  • 当前计划版本是否已经确认。
  • 任务状态是否允许执行。
  • 续跑事件是否有效。
  • 任务是否超过执行期限。

因此,“用户确认”对应的是服务端保存的具体计划版本。用户补充要求导致计划实质变化时,需要更新计划并重新整体确认,不能沿用旧确认。

5. 确认后,按步骤类型选择执行方式

这里需要区分两条路径:

步骤类型 如何执行
普通长任务步骤 主 Agent 根据当前步骤和已有结果,委派对应领域;子 Agent 使用本域工具执行,再按任务协议记录结果
带有已注册 batch.action 的批次步骤 服务端 DeterministicBatchCoordinator 按固定参数逐项调用执行器,管理批量大小、操作标识、结果和游标

所以,前面“主 Agent 按计划委派业务域”的说法描述了普通步骤;已经声明合法结构化批次的步骤,可以由服务端直接推进,无需模型逐项编排。

一个计划也可以混合这两种步骤。例如,前面是静态参数批次,后面是需要消费前序结果的普通依赖步骤。

6. 每轮保存真实进度,剩余工作通过续跑机制推进

执行时不会要求一次模型调用完成全部工作。系统根据真实结果更新:

1
2
3
4
5
6
当前步骤
已完成结果及必要引用
批次执行游标
下一步动作
异步任务引用
失败或待核对状态

接近本轮预算时,停止启动新业务调用,完成当前轮次收口,并根据状态安排后续推进。

续跑事件携带任务和事件标识。下一轮重新读取持久化状态,再恢复到当前步骤或剩余批次。因此,正常分批执行依靠保存的进度和续跑事件推进,不靠用户反复回复“继续”。

如果正在等待异步结果,则进入等待状态,由结果监控或核对流程衔接后续处理。

7. 异常时先判断能否安全继续,完成后输出结果

计划内写操作会先建立稳定操作标识,关联任务、计划版本、步骤及游标。执行结果不确定时,优先利用已有操作引用核对,降低重复执行风险。

后续处理取决于实际状态:

  • 取得有效进展且仍有剩余:保存进度,继续推进。
  • 异步操作尚未完成:等待结果。
  • 副作用结果不确定:先核对已有操作。
  • 计划实质变化:重新规划并确认。
  • 连续没有有效进展、状态保存失败等情况:停止自动推进,等待用户处理。
  • 任务全部完成:记录完成状态,返回业务结果。

我们的实现可以概括为:模型提出计划和业务决策,程序保存计划、绑定确认版本,并控制每轮执行与恢复。 当前计划主要按编号步骤和游标推进,不应直接描述成通用 DAG 调度器。