第 19 章 · Handoff 机制

一次交接把买家惹火了

那个卖家的售后 Swarm 上线没几天,就收到一条差评。复盘对话记录,问题出在一次交接上。

买家先跟前台 Agent 掰扯了半天:报了订单号、说明了包裹在海关卡了两周、还说自己已经跟物流公司确认过是清关问题。前台一看是物流的事,交给了物流 Agent。结果物流 Agent 上来第一句:“您好,请问您的订单号是多少?遇到了什么问题?”——买家刚说完的一切,它一概不知,等于让人从头再讲一遍。买家当场炸了。

反过来还有另一种翻车:有次前台图省事,把跟买家的全部闲聊记录(包括“今天天气真好”“你们客服态度不错”这类废话)一股脑全塞给了退款 Agent,退款 Agent 被淹在无关信息里,愣是没抓住“买家要退的是哪一件”这个重点。

一个丢信息、一个塞太多——交接(Handoff)是多 Agent 协作里最容易出问题的环节。上一章我们说 Swarm 的灵魂是交接,这一章就把它单独讲透。

Handoff 是什么:转移控制权 + 传递恰当的上下文

一次好的交接,包含两个动作,缺一不可:

  1. 转移控制权(transfer of control):明确宣告“从现在起,由 B 负责了”,A 随之退场。责任清晰——不会两个 Agent 都以为对方在处理(结果都没做),也不会两个都抢着处理(结果重复做)。
  2. 传递恰当的上下文(context passing):把 B 需要的信息交给它——不多不少

第 2 点是精髓,也是那两次翻车的根源:前台交给物流时传少了(丢了订单号和清关信息),交给退款时传多了(塞进一堆废话)。恰当,是一门分寸的艺术。

上下文传递的三种粒度

到底传多少?有三种粒度,各有取舍,选对了交接就成功了一大半:

  • 全量传递(full context):把 A 的完整对话历史原样交给 B。信息最全,一点不丢——但容易污染(B 被无关内容淹没,就像退款 Agent 那次),而且历史越长越贵。适合交接双方职责高度连续、上下文都相关的场景。
  • 摘要传递(summary):让 A 把整个过程总结成一段“交接说明”交给 B——现状如何、已做了什么、接下来要注意什么。这通常是最佳平衡:干净、聚焦、又不丢关键信息。前台那次要是给物流 Agent 一句“买家订单 #12345,包裹海关卡两周,买家已确认是清关问题,需协助催办”,就不会翻车。
  • 结构化传递(structured):只传关键字段——用户 ID、问题类型、已确认的事实、待办项。最干净、最省,下游解析也最省事——但要求交接内容能被结构化成固定字段,不是所有场景都做得到。

一个好记的类比:好的交接像医院的“交班”。护士换班时,不会把病人厚厚一摞病历原样甩给接班的人,而是给一份“重点交班记录”——病人现状、已经做了什么处置、接下来要盯什么。摘要传递,就是给 Agent 之间做一份这样的交班记录。

用 pi 实现:可寻址会话让交接自然

回顾第 12 章,pi 的会话是结构化、可持久化、可寻址的——每个会话有自己的 sessionId,能被精确定位、随时取用。这个基础让交接变得顺理成章:交接,本质上就是“把一个会话的控制权,连同一段交接上下文,转移给另一个 Agent”。

概念上:

// 概念示意:交接 = 转移控制权 + 传递摘要式上下文(而非全量历史)
async function handoff(fromAgent, toAgent, session) {
  // 1. 让当前 Agent 生成一段"交班记录"——摘要,而非全量对话
  const briefing = await fromAgent.summarizeForHandoff(session, {
    include: ["用户意图", "订单/账号等关键字段", "已确认的事实", "待处理事项"],
  });

  // 2. 用交班记录初始化目标 Agent 的上下文,正式转移控制权
  return toAgent.takeOver({ briefing, sessionId: session.id });
}

这里 summarizeForHandoff 走的是摘要传递include 里那几项则带上了结构化的关键字段——两种粒度混用,既干净又不丢重点。

因为会话是可寻址的(有 sessionId),交接甚至能跨进程、跨机器发生:物流 Agent 可以跑在另一台服务器上,前台把 sessionId 和交班记录一发,它就能接手同一个会话。这正好衔接第 24 章的 RPC 远程驱动——一个 Agent 把任务交接给另一台机器上的 Agent,底层就是这套可寻址会话 + 远程调用。

交接的三种触发方式

交接这个动作,可以由不同的角色来发起,对应不同的控制哲学:

  • 模型主动交接:Agent 通过“交接工具”(第 18 章的 handoff_to自己判断该交给谁——最灵活,是 Swarm 的做法。适合走向难料的开放场景。
  • 规则交接:按预设规则路由,比如“消息里含‘退款’关键词就转账务 Agent”“金额超过 1 万转人工”——可预测、好审计,适合结构化场景。
  • 主管调度:由主管 Agent 统一分派(第 16 章 Supervisor),下属之间不直接交接,都听主管的——中心化控制,最好调试。

三种方式对应“谁做主”这个老问题的不同答案,而且可以混用:比如平时走规则交接(可控),遇到规则覆盖不了的复杂情况,再让模型主动交接(灵活)。

动手看看

拿前台惹火买家那次交接,动手写三个版本的交接上下文:一个全量(把整段对话贴上)、一个摘要(一句话交班)、一个结构化(订单号 / 问题类型 / 已确认事实 / 待办 四个字段)。然后分别设想接手的物流 Agent 拿到后会怎么反应——哪个版本让它既不用重新问、又不会被废话带偏?

你多半会得出和本章一样的结论:摘要或结构化最好用。再想一步:什么情况下你会宁可用全量?(提示:当交接双方职责高度连续、几乎所有历史都相关时,摘要反而可能漏掉后面才用得上的细节。)

实战中的几个坑

坑一:交接丢上下文,接手方从头再问。

  • 现象:B 接手后让用户重报一遍已经说过的信息,体验割裂。
  • 原因:交接时传得太少,关键字段没带过去。
  • 对策:用摘要 + 结构化字段,把接手方必需的信息(意图、关键字段、已确认事实、待办)明确带上。

坑二:交接传全量,污染下游。

  • 现象:B 被无关的历史信息淹没,抓不住重点、还更贵。
  • 原因:图省事直接把完整对话历史甩过去。
  • 对策:默认用摘要传递;只在双方职责高度连续时才考虑全量。

坑三:责任不清,活儿悬空或重复。

  • 现象:交接后两个 Agent 都以为对方在处理(都没做),或都动手(重复做)。
  • 原因:控制权没有明确转移,缺一个“现在归谁”的清晰标记。
  • 对策:交接时显式标记控制权归属,A 交出后即退场,同一时刻只有一个 Agent 负责。

坑四:摘要把关键细节也压没了。

  • 现象:为了干净,摘要过度精简,把后面才用得上的关键细节丢了。
  • 原因:摘要时不知道下游到底需要什么,一刀切地压缩。
  • 对策:按接手方的职责定制摘要的 include 项;拿不准的关键字段用结构化字段原样保留,别揉进摘要。

对比:其他框架

就“交接时怎么转移控制权、传递多少上下文”这件事:

框架Handoff 怎么做特点
LangGraph交接就是图里一次状态转移(边),随之流动的 state 就是上下文,传多少由你写进 state控制权“轮到哪个节点”由图明确管理,责任不含糊
CrewAI前一任务输出作为后一任务的 context 传入,隐含交接粒度偏“整段输出”,摘要式精简得自己做
PydanticAI无专门 handoff 原语,靠“一个 Agent 把另一个当工具/子调用”完成传递的是强类型对象,天然契合“结构化传递”
AgnoTeam 的 route / coordinate 转交成员时携带上下文,框架帮管一部分传递粒度由协作模式与成员配置决定
pi基于可寻址会话,交接只传一段摘要式“交班记录”,还能跨进程/跨机器衔接第 24 章 RPC 远程驱动

底层都是“转移控制权 + 传递上下文”,差别在于框架传的是整段输出、图状态、还是强类型对象;而“传摘要而非全量”这门分寸,是各家都要自己把握的。PydanticAI 虽无专门原语,但它的强类型对象恰好天然适合结构化传递,算是“弱在编排、巧在类型”。

小结

  1. Handoff = 转移控制权(责任清晰、不重不漏)+ 传递恰当上下文(不多不少),是多 Agent 协作最易翻车的环节。
  2. 上下文传递有三种粒度:全量(全但易污染)、摘要(通常最佳平衡)、结构化(最干净但要求可结构化)——像医院交班一样传“重点记录”。
  3. pi 的可寻址会话让交接自然,凭 sessionId 甚至能跨进程、跨机器交接(衔接第 24 章 RPC)。
  4. 交接可由模型主动、规则、或主管触发——对应不同控制哲学,可混用。

Part 5 到此完整:从编排基础、DAG、Swarm 到 Handoff,多 Agent 协作的核心模式你已经握在手里了。Part 6 我们更进一步,看几种高级推理范式——让 Agent(乃至 Agent 群体)去啃更难、更开放的问题。