Agents-A1 多智能体编排、角色切分与工具权限各管一段

文章导读
多智能体跑起来后互相抢工具、来回转交,通常不是模型能力问题,而是三层规则被混在一处:编排层写成了角色指令,角色提示词里塞了工具用法,工具权限又对所有角色放开。判断顺序建议是:先看任务流有没有明确的唤醒条件与终止条件,再看角色提示词有没有越界替别人做决定,最后看工具白名单有没有按角色收窄。这三层各自只管一段,哪一层缺规则,异常都会以“抢工具”或“反复转交”的形式暴露出来。
📋 目录
  1. Ⅰ 先定任务流:谁在什么条件下被唤醒
  2. Ⅱ 再定角色边界:每个角色只写自己那一段提示词
  3. Ⅲ 最后定工具权限:白名单按角色收窄
  4. Ⅳ 用转交次数和工具调用分布看异常
  5. Ⅴ 收敛不了时退回单角色加多工具
A A

多智能体跑起来后互相抢工具、来回转交,通常不是模型能力问题,而是三层规则被混在一处:编排层写成了角色指令,角色提示词里塞了工具用法,工具权限又对所有角色放开。判断顺序建议是:先看任务流有没有明确的唤醒条件与终止条件,再看角色提示词有没有越界替别人做决定,最后看工具白名单有没有按角色收窄。这三层各自只管一段,哪一层缺规则,异常都会以“抢工具”或“反复转交”的形式暴露出来。

把编排拆成任务流、角色边界、工具权限三层分别配置和验证:任务流只写触发条件、流转规则和终止条件;角色提示词只写本角色的判断与产出契约;工具白名单按角色收窄,取角色与全局配置的交集。验证依据是日志字段(唤醒来源、转交对象、转交次数、工具调用明细与拒绝记录),不是对话观感。如果规则写不进这三层,通常说明任务本身还不适合多角色,先退回单角色加多工具。

先定任务流:谁在什么条件下被唤醒

编排层只做一件事:根据当前状态决定下一个被唤醒的角色。它不应该描述角色该说什么、该怎么思考,否则规则会变成第二份提示词,改起来互相打架。写法上建议用“条件表达式 + 目标角色 + 终止条件”三件套,条件只引用已有字段,不去猜语义。

flow:
  - id: intake
    when: 'event.type == "new_ticket"'
    to: triage
  - id: need_research
    when: 'triage.result == "needs_data"'
    to: researcher
    max_hops_from: triage = 2
  - id: answer
    when: 'triage.result == "clear"'
    to: writer
  - id: stop
    when: 'hop_total >= 6 or no_rule_matched'
    to: __end__

上面每个字段都能在日志里对应到一条记录,这是判断“谁被唤醒”的前提。流转日志建议至少记:trace_id、hop_index、from_role、to_role、match_rule(命中了哪条规则)、reason(上一步角色给出的原因字段)、timestamp。没有 match_rule 这一项,后面很难判断是规则没写清还是角色输出不合格。日志表结构可以按需增减,但 trace_id 和 hop_index 通常要保留,否则串不起一次完整流转。

再定角色边界:每个角色只写自己那一段提示词

角色提示词写多了,最常见的后果是每个角色都在替编排层做决定:triage 直接输出“让 researcher 去查”,researcher 又顺手把最终答复写了。收敛办法是给角色一个固定骨架,把“做什么”和“不做什么”都写清楚。

role: researcher
goal: 只根据给定资料补齐事实缺口
input: {question, known_facts[]}
do:
  - 列出信息缺口
  - 检索并记录来源标记
  - 输出要点列表
dont:
  - 不写最终答复
  - 不决定下一个角色
  - 不调用任何写操作工具
output: {facts:[{claim, source_ref}], gaps[]}
tools: [search_kb, fetch_doc]

不该写进角色提示词的内容主要有四类:其他角色的职责描述、全局流转规则、工具的参数细节与重试策略、以及“什么时候结束整个会话”的判定。这些分别属于编排层和工具层,写进角色里会造成同一规则多处维护,改一处漏一处。输出结构建议尽量窄,字段越少,编排层的条件表达式越好写、越好验证。

最后定工具权限:白名单按角色收窄

抢同一个工具,多数情况是白名单给成了“全员可调用”。建议做两层映射:全局定义工具可授范围,角色再声明自己需要的子集,运行时取交集;不要只在一处写,否则改角色时容易顺手放大权限。

Agents-A1 多智能体编排、角色切分与工具权限各管一段
tools:
  search_kb:    {allow: [triage, researcher]}
  fetch_doc:    {allow: [researcher]}
  write_ticket: {allow: [writer], require_confirm: true}
  send_mail:    {allow: []}
roles:
  triage:     {tools: [search_kb]}
  researcher: {tools: [search_kb, fetch_doc]}
  writer:     {tools: [write_ticket]}

越权调用时的表现需要提前观察清楚:有的实现是工具侧直接返回 permission_denied 一类错误,有的由编排层在调用前拦截并写审计日志,还有的会出现模型换名字重试、短时间内重复调用同一工具。只要出现非授权角色对某工具的调用记录,就应该回头核对白名单,而不是去调提示词。

用转交次数和工具调用分布看异常

日志齐了以后,统计口径建议固定三种:按 trace_id 统计 hop 总数与是否命中终止条件;按 (from_role, to_role) 统计流转边频次;按 (role, tool, result) 统计调用次数与拒绝次数。这三种口径分别回答“转了几圈”“谁和谁在互踢”“谁在越权”。

  • 同一个 trace 里出现 A→B→A→B 的重复边,通常是两边都能处理同一类输入,角色边界重叠。
  • hop 数分布尾部很长,但绝大多数 trace 很短,说明少数输入反复触发同一规则,规则条件写得过宽。
  • 某角色工具调用数为零,可能是被别的角色架空,也可能它的工具没进白名单。
  • 拒绝记录集中在某个角色上,说明该角色的提示词里仍写着它无权使用的工具名,需要同步删掉。

这些是判断思路,不是阈值。到什么数量算异常,需要结合自己业务的输入分布确认,可以先记录一段时间再定基线。

收敛不了时退回单角色加多工具

如果三层规则都写清楚了仍然反复转交,说明任务本身可能不需要多角色,只是需要多个工具。这时先把角色合并成一个 executor,把工具白名单做并集,但写操作仍然保留确认;流转规则退化成角色内部一份显式的步骤清单,由同一次调用顺序执行。

roles:
  executor: {tools: [search_kb, fetch_doc, write_ticket]}
flow:
  - id: single
    when: 'event.type == "new_ticket"'
    to: executor
    max_hops_from: executor = 1
    to_end: true
steps:
  - search_kb
  - fetch_doc
  - write_ticket   # require_confirm: true

验证标准比较直接:同一批输入下不再出现跨角色转交记录,hop 数稳定在预期范围内,工具调用集中在计划内的那几支,越权与拒绝记录为零。确认单角色能稳定跑通之后,再按真实需要拆角色,比一上来就切分更容易定位问题出在哪一层。