17.03概念更新于 2026年9月17日

Agent as Tool 与 Handoff

调用后返回,还是移交控制权。

17.03 · 课时讲解

Agent as Tool 与 Handoff

调用后返回,还是移交控制权?关键在于控制权和用户对话最后归谁。

1 / 11

一句话理解:Agent as Tool 是“请同事帮忙查一下,结果还给我,我继续负责”;Handoff 是“这个客户转给你了,后面你来跟”。两者的根本区别是:控制权和用户对话最后归谁。

在多 Agent 系统里,“一个 Agent 找另一个 Agent”只有两种本质不同的方式。一种像函数调用:调用方把一个明确的子任务交出去,拿回结果,继续自己的工作。另一种像转接电话:当前 Agent 把整个对话和执行权交出去,自己退场。

选错方式的代价很具体。本该 Handoff 的场景用了 Agent as Tool,用户会感觉永远在和一个“传话筒”对话,专家 Agent 的回答被转述得走了样;本该 Agent as Tool 的场景用了 Handoff,就没人负责把几份结果综合成一个答案,出了错也不知道该找谁。

两种机制对比

Agent as Tool:调用后返回。

控制权的流向如图 1:

Agent as Tool 的控制权流向:主 Agent 调用历史工单分析 Agent,子 Agent 在独立上下文里运行后把结论返回,主 Agent 继续并回复用户 用户 主 Agent 控制权 子 Agent 历史工单分析 提出问题 判断,调用工具 独立运行,看不到主对话 继续判断、调用其他工具 收到最终回复 调用 summarize_history 返回:3 张同类工单 控制权始终在主 Agent:子 Agent 只看到调用参数,结果回到调用方
图 1 · Agent as Tool:子 Agent 的结果回到主 Agent,控制权始终不变

Handoff:移交控制权。

控制权的流向如图 2:

Handoff 的控制权流向:分诊 Agent 调用 transfer_to_refund_agent 后退出,退款 Agent 接管对话并直接与用户交流 用户 分诊 Agent 退款 Agent 接管后的控制权 要求退款 识别意图 transfer_to_refund_agent 退出,不再参与 接管对话,直接回复用户 与退款 Agent 多轮沟通 控制权转移给退款 Agent:它直接面对用户,并对后续答案负责
图 2 · Handoff:分诊 Agent 把对话交出去后退出,退款 Agent 直接面对用户
维度 Agent as Tool Handoff
控制权 始终在调用方 转移给接手方
谁面对用户 调用方 接手方
对方看到什么 调用方传入的参数(一段任务描述或结构化输入) 默认通常是之前的对话历史,可以过滤
结束后 结果返回给调用方继续处理 调用方不再参与
最终答案负责人 调用方 接手方
类比 请同事协助查资料 把客户转给另一个部门
适合 有界的子任务,需要综合多个专家结果 专家需要直接与用户多轮沟通、切换指令与权限

在主流 SDK 中长什么样

OpenAI Agents SDK 同时提供了两种机制,文档对它们的区分是:Agent as Tool 让一个 Manager Agent 保持控制,通过 Agent.as_tool() 调用专家 Agent,适合由一个 Agent 负责最终答案、综合多个专家的输出;Handoff 让分诊 Agent 把对话路由给专家,专家成为当前活跃的 Agent 并直接回复。

from agents import Agent, handoff
from agents.extensions import handoff_filters

history_agent = Agent(
    name="History analyst",
    instructions="分析用户的历史工单,只返回同类问题与判断依据。",
)

refund_agent = Agent(
    name="Refund agent",
    instructions="按退款政策处理用户的退款请求。",
)

# Agent as Tool:主 Agent 保持控制,历史工单分析结果返回给它
order_agent = Agent(
    name="Order support agent",
    instructions="诊断订单售后问题并回复用户。",
    tools=[
        history_agent.as_tool(
            tool_name="summarize_history",
            tool_description="分析指定订单相关的历史工单并返回同类问题",
        )
    ],
)

# Handoff:分诊 Agent 可以把对话整个交给订单 Agent 或退款 Agent
triage_agent = Agent(
    name="Triage agent",
    instructions="判断用户意图,交给合适的专家。",
    handoffs=[
        order_agent,
        handoff(refund_agent, input_filter=handoff_filters.remove_all_tools),
    ],
)

几个值得注意的细节(以 SDK 当前文档为准):

  • 对模型来说,Handoff 也表现为一个工具,名字默认形如 transfer_to_refund_agent。
  • 发生 Handoff 时,接手的 Agent 默认能看到之前的完整对话历史;可以用 input_filter 过滤,例如 remove_all_tools 会去掉历史中所有与工具调用相关的条目。
  • handoff() 还支持 on_handoff 回调(交接时触发,例如预先拉取数据)和 input_type(让模型在交接时填写结构化参数,例如交接原因)。
  • SDK 的追踪会为 Handoff 单独记录 Span,方便排查“为什么被转走”。

Claude Agent SDK 的 Subagent 属于 Agent as Tool 这一类:主 Agent 通过 Agent 工具调用子 Agent,子 Agent 在新的上下文中运行(除非显式使用 fork),父 Agent 传给它的只有调用时的那段提示词,子 Agent 结束后只把最终消息作为工具结果返回。下一课「Subagent 与上下文隔离」会详细展开。

客服场景:两种方式一起用

用户在客服窗口输入:“订单 A1024 怎么还没处理好?再不处理我就要退款了。”

图 3 画出了这次对话中 Agent 之间的关系:

客服场景中两种方式一起用:分诊 Agent 交接给订单 Agent,订单 Agent 调用普通工具和 Agent as Tool,用户要求退款后再交接给退款 Agent 用户:“订单 A1024 怎么还没处理好?再不处理我就要退款了。” 分诊 Agent 主意图:订单查询 订单 Agent 接管对话,回复用户 退款 Agent 按政策处理,必要时审批 Handoff 用户:“算了,直接退款吧” Handoff + 交接包 普通工具 get_order、get_payment Agent as Tool summarize_history · 独立上下文
图 3 · 客服场景:两次 Handoff 转移对话,一次 Agent as Tool 保护订单 Agent 的上下文

为什么这样分:

  • 历史工单分析用 Agent as Tool:它是一个有界的子任务,结论要由订单 Agent 综合判断;历史工单原文很长,放在独立上下文里能保护订单 Agent 的上下文。
  • 退款用 Handoff:退款需要和用户多轮确认(金额、原因、退回方式),遵循完全不同的政策指令,而且需要不同的写权限。让订单 Agent 转述退款对话既慢又容易出错。

交接包:Handoff 时到底传什么

Handoff 最大的风险是信息丢失或信息过载:完整历史里可能有大量与退款无关的工具结果,只传一句“用户要退款”又会丢掉关键事实。更稳妥的做法是除了对话历史外,传递一份结构化的交接包:

{
  "handoff_reason": "user_requested_refund",
  "customer_intent": "因订单处理超时申请全额退款",
  "verified_facts": [
    "订单 A1024 已支付,金额 ¥328",
    "订单 11 月 3 日起停留在人工审核环节"
  ],
  "unverified_claims": ["用户称已多次联系客服但无人回复"],
  "commitments_made": ["已告知用户预计明天处理完成"],
  "open_questions": ["用户是否接受部分补偿代替退款"],
  "constraints": ["退款金额超过 ¥200 需要人工审批"]
}

注意 commitments_made 这一项:前一个 Agent 对用户做过的承诺,接手的 Agent 必须知道,否则就会出现“刚说明天处理完成,转眼又说无法确认”的前后矛盾。

常见误区

  • “Handoff 之后前一个 Agent 还能兜底。” 控制权已经转移,除非设计了回交机制,否则前一个 Agent 不再参与。需要统一兜底,应该用 Agent as Tool。
  • “Agent as Tool 时,子 Agent 自然知道上下文。” 子 Agent 只看到你传给它的输入。任务目标、已知事实、输出格式都必须写进调用参数里。
  • “交接时把全部历史传过去最保险。” 过长的历史会稀释接手 Agent 的注意力,工具结果里还可能包含不可信内容。按需过滤,并附上结构化摘要。
  • “用户察觉不到交接。” 语气、能力、权限变化用户都能感受到。界面上最好让用户知道“现在由退款专员为你处理”。

产品经理检查清单

  • 每一次“Agent 找 Agent”,是需要结果返回(Agent as Tool),还是需要转移对话(Handoff)?
  • 最终对用户负责的是哪个 Agent?出错时找谁?
  • Agent as Tool 的调用参数里,是否包含了子任务目标、必要事实和输出格式?
  • Handoff 时传递哪些历史、过滤哪些内容?是否有结构化交接包?
  • 接手 Agent 的权限是否与其职责匹配,而不是继承前一个 Agent 的全部权限?
  • 用户能否感知到交接?交接后能否回到之前的 Agent 或转人工?

练习

为一个电商客服系统设计 Agent 之间的调用关系(30–45 分钟):

  1. 列出分诊、订单查询、退款、发票四个 Agent,画出它们之间的箭头,并在每个箭头上标注“Tool”或“Handoff”。
  2. 为每个标注写一句理由。
  3. 为“订单查询 → 退款”这次 Handoff 写一份交接包 JSON,至少包含已验证事实、已做承诺和约束三项。

延伸阅读

下一步

Agent as Tool 最常见、也最实用的形态是 Subagent。下一课「Subagent 与上下文隔离」讲如何用子 Agent 处理探索性任务,同时保护主 Agent 的上下文。

试试:上下文、MCP、沙箱、Prompt 注入、外链