跳到正文

TECH NOTE

5817 字 29 分钟 系列 9 / 22

09 实战:用 Subagents 搭一个个人助理(主管 + 日历/邮件子智能体)

ON THIS PAGE / 目录19 节

一句话讲透:主管 Agent 就像公司前台——他自己不会排日程、也不会写邮件,手里只有两张”服务台工单”(排日程、发邮件);用户说一句人话,他判断这单该转给哪个专家,专家办完事只回一句”办好了”,前台再把两句话拼成一句答复你。


这个教程要解决什么#

场景:做一个个人助理,既要管日历(查空闲、建会议),又要管邮件(写正文、发通知)。

为什么不能只用一个 Agent:如果给它直接挂上全部底层工具,会同时踩三个坑——工具太像导致选错;每个 API 的格式要求(ISO 时间、邮箱地址)全塞进一段 prompt 会变成一锅粥;想单独优化”邮件措辞”却要回归整个 Agent。

一句话目标:按领域把工具切开做成两个专职子智能体(subagent),让一个主管(supervisor)Agent 只面对两个”高层工具”做调度——主管做的是领域级路由,不是 API 级选参。

读完你能得到:

  • 一套可复用的三层架构:底层刚性 API 工具 → 中层子智能体 → 顶层主管;
  • “子智能体即工具”的具体写法(含工具描述怎么写才好路由);
  • 在子智能体这一层挂人工介入(human-in-the-loop)、由主管层负责暂停/恢复的完整 interrupt / resume 流程,以及信息流裁剪的两种做法和一个高频翻车点。

💡 提示:原文提到,如果你是从旧的 langgraph-supervisor 包迁移过来的,官方另有一篇 before/after 迁移文档(含 interrupt 与 resume):Migrate from langgraph-supervisor。


整体架构#

flowchart TD
U["用户 User"] -->|"自然语言请求"| S["主管 Supervisor<br/>只看到 2 个高层工具"]
S -->|"schedule_event"| W1["工具 Tool:schedule_event"]
S -->|"manage_email"| W2["工具 Tool:manage_email"]
W1 --> C["日历子智能体 Calendar Agent<br/>独立 prompt + 日历工具"]
W2 --> E["邮件子智能体 Email Agent<br/>独立 prompt + 邮件工具"]
C --> C1["create_calendar_event<br/>get_available_time_slots"]
E --> E1["send_email"]
C -.->|"只回传最终一条消息"| S
E -.->|"只回传最终一条消息"| S
C1 -.->|"interrupt 暂停待审"| H{"人工介入<br/>Human-in-the-loop"}
E1 -.->|"interrupt 暂停待审"| H
H -.->|"approve / edit / reject"| C1
H -.->|"approve / edit / reject"| E1
S -->|"汇总后的回答"| U

这张图在说什么:

① 两次”工具”调用,含义不同。主管手里的 schedule_event / manage_email 是”部门转接单”,不是真实 API;点开它,里面才会跑一整个子智能体的循环(自己选工具、自己调、自己总结)。

② 层的边界就是信息的边界。主管看不到子智能体内部的中间推理和中间工具调用,只拿到最后一条消息;反过来,子智能体默认也只看得到主管转给它的那一个 request 字符串——两边都是隔离的。

③ 越往上越”软”,越往下越”硬”。底层 API 工具要严格格式;中层子智能体负责把人话翻译成格式;顶层主管只负责”这事归谁 + 结果怎么合并”。人工介入挂在最下面的敏感动作上,但状态与暂停能力由最顶层持有。

一次跨领域请求的时序:

用户 ──"下周二下午 2 点开会,并邮件提醒设计团队"──▶ 主管 Agent(Supervisor)
│ 选择工具(本质是"选专家")
┌──────────────────────────────┴──────────────────────────────┐
▼ ▼
schedule_event manage_email
▼ ▼
日历子智能体(一次独立调用,无状态) 邮件子智能体(一次独立调用,无状态)
│ 只回传最终确认文本 │ 只回传最终确认文本
└──────────────────────────────┬──────────────────────────────┘
▼
主管 Agent 汇总两段结果
用户 ◀───────────────"会议已定 + 提醒邮件已发"──────────┘

说明:本文只保留骨架代码,不保证逐行可跑;完整可运行脚本见原文。另外,代码里的提示词字符串为方便中文读者已译为中文——如果你要直接复制去跑,建议回原文取英文原版提示词。


分步拆解#

§0 前置准备:装包、追踪、选模型(原文 Setup)#

安装 pip install langchain(或 conda install langchain -c conda-forge)。想做调用追踪就配上 LangSmith:设环境变量 LANGSMITH_TRACING="true" 与 LANGSMITH_API_KEY(原文给了 shell 的 export 和 Python 的 os.environ[...] 两种写法)。下面需要一个聊天模型对象(下文统一叫 model);原文用 7 个 Tab 列举各家接入方式,套路都是”装 extra + 用 init_chat_model 或具体 Model 类”,这里压成一张表(换提供方只改这一行,其余代码不动):

提供方安装初始化需要的环境变量
OpenAIpip install -U "langchain[openai]"init_chat_model("gpt-5.5")(等价 ChatOpenAI(model="gpt-5.5"))OPENAI_API_KEY
Anthropicpip install -U "langchain[anthropic]"init_chat_model("claude-sonnet-4-6")(等价 ChatAnthropic(...))ANTHROPIC_API_KEY
Azurepip install -U "langchain[openai]"init_chat_model("azure_openai:gpt-5.5", azure_deployment=...)(等价 AzureChatOpenAI(...))AZURE_OPENAI_API_KEY / AZURE_OPENAI_ENDPOINT / OPENAI_API_VERSION
Google Geminipip install -U "langchain[google-genai]"init_chat_model("google_genai:gemini-3.7-flash")(等价 ChatGoogleGenerativeAI(...))GOOGLE_API_KEY
AWS Bedrockpip install -U "langchain[aws]"init_chat_model("us.anthropic.claude-sonnet-4-6", model_provider="bedrock_converse")按 AWS 文档配置凭证
HuggingFacepip install -U "langchain[huggingface]"init_chat_model("microsoft/Phi-3-mini-4k-instruct", model_provider="huggingface", temperature=0.7, max_tokens=1024)HUGGINGFACEHUB_API_TOKEN
OpenRouterpip install -U "langchain-openrouter"init_chat_model("auto", model_provider="openrouter")(等价 ChatOpenRouter(model="auto"))OPENROUTER_API_KEY

§1 定义底层工具(Define tools)#

这一步干什么:先写最底下那层”刚性”工具——参数要求严格、没有商量余地的 API 封装。原文用桩函数(stub)代替真实的 Google Calendar / SendGrid 调用,只为把模式讲清楚。

from langchain.tools import tool
@tool
def create_calendar_event(title: str, start_time: str, # ISO:"2024-01-15T14:00:00"
end_time: str, # ISO:"2024-01-15T15:00:00"
attendees: list[str], # 邮箱地址列表
location: str = "") -> str:
"""创建日历事件。要求严格的 ISO 时间格式。"""
# 桩函数:真实项目里这里调 Google Calendar API / Outlook API
return f"Event created: {title} from {start_time} to {end_time} with {len(attendees)} attendees"
@tool
def send_email(to: list[str], subject: str, body: str, cc: list[str] = []) -> str:
"""通过邮件 API 发送邮件。要求邮箱地址格式正确。"""
return f"Email sent to {', '.join(to)} - Subject: {subject}" # 桩函数:真实项目里调 SendGrid
@tool
def get_available_time_slots(attendees: list[str], date: str, # ISO 格式:"2024-01-15"
duration_minutes: int) -> list[str]:
"""查询指定日期内这些参与者的日历空闲情况。"""
return ["09:00", "14:00", "16:00"] # 桩函数:真实项目里查日历 API

注意:@tool 的 docstring 就是给模型看的工具描述(description),不是普通注释——它是路由信号的一部分,要写清楚”什么时候用、输入长什么样”。

§2 创建两个子智能体(Create specialized sub-agents)#

这一步干什么:每一步都造一个”只懂一个领域”的小 Agent。两个子智能体是同一套模板:一个 model + 一组本领域专属的工具 + 一段本领域专属的 system prompt。

日历子智能体#

它负责把”下周二下午 2 点”这种人话翻译成精确的 ISO 时间,需要时先查空闲再建事件。

from datetime import date
from langchain.agents import create_agent
CALENDAR_AGENT_PROMPT = (
f"今天是 {date.today().isoformat()}。 " # 关键:把今天日期注入 prompt,"下周二"才有参照系
"你是一个日历排期助理。把自然语言的排期请求(例如'下周二下午 2 点')解析成 ISO 时间格式。"
"需要时用 get_available_time_slots 查询空闲;用 create_calendar_event 创建事件。"
"如果没有合适的时间段,停下来,并在答复中说明该时间不可用。"
"在最终答复里始终确认你安排了什么。"
)
calendar_agent = create_agent(model, tools=[create_calendar_event, get_available_time_slots],
system_prompt=CALENDAR_AGENT_PROMPT)

单独测一下它。这套 stream_events + interleave 循环后面每一节都用,version="v3" 是 LangChain v1 的流式接口:

query = "下周二下午 2 点安排一个 1 小时的团队会议"
stream = calendar_agent.stream_events(
{"messages": [{"role": "user", "content": query}]}, version="v3",
)
for kind, item in stream.interleave("messages", "tool_calls"):
if kind == "messages":
print(item.text, end="", flush=True) # 逐 token 输出
else:
print(f"\n工具调用: {item.tool_name}({item.input})")
print(f"工具结果: {item.output}")

一次典型运行:先调 get_available_time_slots(attendees=[], date=..., duration_minutes=60),拿到 ["09:00", "14:00", "16:00"];再调 create_calendar_event,参数被填成 2024-06-18T14:00:00 → 2024-06-18T15:00:00;最后回一句”已安排在 6 月 18 日下午 2 点,时长 1 小时”。

⚠️ 原文如此:原文正文写”把 next Tuesday at 2pm 解析成 ISO 格式(2024-01-16T14:00:00)“,但同一节的 trace 输出里实际是 2024-06-18T14:00:00。两处对不上,这里按 trace 记录。

邮件子智能体#

它只干一件事:从模糊的自然语言里推断收件人,写一封像样的邮件,然后发出去。

EMAIL_AGENT_PROMPT = (
"你是一个邮件助理。根据自然语言请求撰写专业的邮件。"
"提取收件人信息,并写出合适的标题和正文。"
"用 send_email 发送邮件,并在最终答复里始终确认你发出了什么。"
)
email_agent = create_agent(model, tools=[send_email], system_prompt=EMAIL_AGENT_PROMPT)

发给它的请求可以很随意,比如”提醒设计团队评审一下新的设计稿”。跑起来你能看到:它自己从”设计团队”推断出 design-team@example.com,生成了标题 Reminder: Please Review the New Mockups 和一段正式正文,调用 send_email,最后回一句”提醒已发出”。

这一步的要点:每个子智能体的关注面很窄——自己的工具 + 自己的 prompt。窄才能专,专才好调优。

§3 把子智能体包装成工具(Wrap sub-agents as tools)#

这一步干什么:这是整个模式最关键的动作——把”跑一整个子智能体”包装成一个普通 @tool 函数。主管从此看到的是 schedule_event 这种高层能力,而不是 create_calendar_event 这种底层 API。

@tool
def schedule_event(request: str) -> str:
"""用自然语言安排日历事件。用户想创建、修改或查询日历安排时用它;内部负责时间
解析、空闲检查和事件创建。输入:自然语言的日程请求(例如"下周二下午 2 点开会")。
"""
result = calendar_agent.invoke({"messages": [{"role": "user", "content": request}]})
return result["messages"][-1].text # 只把最终答复交回主管
@tool
def manage_email(request: str) -> str:
"""用自然语言发送邮件。用户想发通知、提醒或任何邮件沟通时用它;内部负责提取收件
人、生成标题和撰写正文。输入:自然语言的邮件请求(例如"给他们发个会议提醒")。
"""
result = email_agent.invoke({"messages": [{"role": "user", "content": request}]})
return result["messages"][-1].text

两个细节,原文特意强调过:

  • 工具描述决定路由质量。主管唯一能看到的”菜单”就是这两个函数的 name + docstring,所以”什么时候用我”必须写明白。
  • 只回传最后一条消息。子智能体的中间推理、中间工具调用,主管一概不需要看——这也是后面”信息流裁剪”的基础。

§4 创建主管智能体(Create the supervisor agent)#

这一步干什么:主管是个”很薄”的 Agent。它的工具列表里只有上一步那两个包装函数,system prompt 也只描述”你能做什么、怎么拆解、怎么协调”,完全不需要懂任何 API 格式。

SUPERVISOR_PROMPT = (
"你是一个乐于助人的个人助理。你可以安排日历事件,也可以发送邮件。"
"把用户的请求拆解成合适的工具调用,并协调这些结果。"
"当一个请求涉及多个动作时,按需顺序或并行地使用多个工具。"
)
supervisor_agent = create_agent(model, tools=[schedule_event, manage_email], # 只有两个"高层工具"
system_prompt=SUPERVISOR_PROMPT)

§5 跑起来:主管怎么调度(Use the supervisor)#

query = (
"下周二下午 2 点和设计团队开 1 小时的会,"
"并发封邮件提醒他们评审新的设计稿。"
)
stream = supervisor_agent.stream_events(
{"messages": [{"role": "user", "content": query}]}, version="v3",
)
# 遍历方式同 §2;这一层的 tool_calls 里出现的,就是那两个"子智能体工具"

例 1:单领域请求#

“明天上午 9 点安排一个团队站会” → 主管判定这是日历任务,只调 schedule_event;日历子智能体自己解析时间、建事件、回一句确认;主管把这句确认换个说法回给用户。邮件子智能体全程没被唤醒。

例 2:跨领域请求#

上面那段 query 会得到这样的执行轨迹(节选):

工具调用: schedule_event(request="下周二下午 2 点和设计团队开 1 小时的会")
工具调用: manage_email(request="发邮件提醒设计团队评审新的设计稿") ← 同一条回复里发出
工具结果: 会议已安排在 6 月 18 日 14:00–15:00;提醒邮件已发给设计团队 ...
最终答复: 会议已定在下周二 6 月 18 日 14:00–15:00;同时也已发出评审提醒邮件。

主管在一条模型回复里同时发出了两个工具调用,运行时并行执行;两个子智能体各自干活,最后主管把两段结果合并成一段连贯答复。

💡 提示:想做全链路排查(每一次模型调用的完整 prompt 和 response),原文挂了对应的 LangSmith trace 链接,照着跑一遍最直观。另外原文有一条 Note:主管默认是顺序派发的——一个工具调用跑完才轮到下一个;但很多模型会在单次回复里一次性发多个工具调用,运行时就会并行执行。也可以显式配置并行派发,见 create_supervisor 参考文档。

三层架构在说什么#

  • 底层:刚性 API 工具,格式要求死板,没有智能;
  • 中层:子智能体,吃自然语言、吐自然语言确认,中间翻译成结构化 API 调用;
  • 顶层:主管,面向”能力”而不是”接口”,负责路由与结果合成。

好处是三件事:每层职责单一;加新领域(比如 CRM、数据库)不影响已有领域;每层可以独立测试和迭代。

§6 加人工介入(Human-in-the-loop review)#

这一步干什么:发邮件、建日程这类动作发出去就收不回,值得让人点个头。LangChain 内置了中间件(middleware)来拦截工具调用,这里要拦的正是子智能体手里的工具。两件事要同时做:

  1. 把 create_calendar_event 和 send_email 配成”命中即中断(interrupt)“,允许 approve / edit / reject 三种决定;
  2. 检查点(checkpoint)只加在顶层主管——因为暂停/恢复的是主管这份状态。
from langchain.agents.middleware import HumanInTheLoopMiddleware
from langgraph.checkpoint.memory import InMemorySaver
calendar_agent = create_agent(
model,
tools=[create_calendar_event, get_available_time_slots],
system_prompt=CALENDAR_AGENT_PROMPT,
middleware=[HumanInTheLoopMiddleware(
interrupt_on={"create_calendar_event": True}, # 命中即暂停,等人工决定
description_prefix="日历事件待审批",
)],
)
# 邮件子智能体同理,只把拦截对象换成 send_email
email_agent = create_agent(
model, tools=[send_email], system_prompt=EMAIL_AGENT_PROMPT,
middleware=[HumanInTheLoopMiddleware(interrupt_on={"send_email": True},
description_prefix="外发邮件待审批")],
)
# 检查点只加在主管这一层:暂停与恢复用的都是主管的状态
supervisor_agent = create_agent(
model,
tools=[schedule_event, manage_email],
system_prompt=SUPERVISOR_PROMPT,
checkpointer=InMemorySaver(),
)

跑同一条 query,这次会被拦下来。thread_id 是检查点的键,interrupt 事件要收集起来备用:

config = {"configurable": {"thread_id": "6"}} # 检查点按 thread_id 归档
interrupts = []
stream = supervisor_agent.stream_events(
{"messages": [{"role": "user", "content": query}]},
config, version="v3", # 带上 thread_id,检查点才有地方落
)
# 遍历同 §2;这次多了"有没有被拦住"的判断
if stream.interrupted:
for interrupt_ in stream.interrupts:
interrupts.append(interrupt_)
print(f"\n已中断: {interrupt_.id}")

此刻会拿到两条中断事件,分别对应”日历事件待审批""外发邮件待审批”,每条里带着子智能体已经解析好的工具参数(日历的 title / start_time / attendees,邮件的 to / subject / body)——正好拿去给人看。

接着用 Command 按中断 ID 逐条给决定。原文的演示是:日历那条直接批准,邮件那条改掉标题再放行:

from langgraph.types import Command
resume = {}
for interrupt_ in interrupts:
if interrupt_.id == "2b56f299be313ad8bc689eff02973f16": # 邮件那条:改标题后放行
edited_action = interrupt_.value["action_requests"][0].copy()
edited_action["args"]["subject"] = "设计稿提醒"
resume[interrupt_.id] = {"decisions": [{"type": "edit", "edited_action": edited_action}]}
else: # 日历那条:直接批准
resume[interrupt_.id] = {"decisions": [{"type": "approve"}]}
stream = supervisor_agent.stream_events(Command(resume=resume), config, version="v3")

恢复后流程继续走完:日历建好、邮件按改后的标题发出(回执里 Subject: 设计稿提醒),主管照旧汇总成一段答复。

§7 进阶:控制信息流(Control information flow)#

默认行为:子智能体只拿到主管转来的那一个 request 字符串。多数场景够用,但遇到”那就按明天同一时间安排”这种带指代的请求,子智能体就会一脸懵——它看不到之前的对话。

做法一:给子智能体补上下文。用 ToolRuntime 读到主管的状态(state),把用户最初那句话捞出来,跟子请求拼在一起喂进去。

from langchain.tools import tool, ToolRuntime
@tool
def schedule_event(request: str, runtime: ToolRuntime) -> str:
"""用自然语言安排日历事件。"""
# 从主管的 state 里取出用户最初那一句原话
original_user_message = next(
m for m in runtime.state["messages"] if m.type == "human")
prompt = (f"你正在协助处理下面这位用户的请求:\n\n{original_user_message.text}\n\n"
f"你这次要完成的子任务:\n\n{request}")
result = calendar_agent.invoke({"messages": [{"role": "user", "content": prompt}]})
return result["messages"][-1].text

做法二:控制回传给主管的内容。主管不一定只需要一句人话,有时结构化结果更好用。

import json
# ...(前面几行同 §3:拿到 result = calendar_agent.invoke(...))
# 方案一:只回一句确认文本(上下文最省)
return result["messages"][-1].text
# 方案二:回结构化数据,主管更容易做后续判断
# return json.dumps({"status": "success", "event_id": "evt_123",
# "summary": result["messages"][-1].text})

原文在这里给了一句重要提醒:一定要在子智能体的 prompt 里强调”最终消息要包含所有关键信息”。一个常见翻车姿势是——子智能体老老实实调了工具,但最终答复里什么结果都没写,主管于是收到一个空壳,只能瞎编或者反问用户。

关键机制专讲#

① “子智能体 = 被包装成工具”到底意味着什么#

这不是修辞,是三个实打实的工程约束:

  • 无状态。calendar_agent.invoke(...) 每次都是一个全新的、只含一条 user 消息的会话;子智能体不记得上次干过什么,除非显式配置检查点。
  • 每次调用从零开始。没有跨调用的记忆,也没有”上次那个会议的 ID”。要延续上下文,得自己在包装函数里把信息拼进 prompt(见 §7 做法一)。
  • 上下文隔离。主管的对话历史不会自动流进子智能体,子智能体的中间过程也不会自动流回主管。信息只能靠你手写的那个字符串进出——这既是限制,也是这个模式最大的价值:主管的上下文窗口不会被两个领域的过程细节撑爆。

② 主管如何决策”派谁”#

主管的决策依据只有三样,全都在它能看到的范围内:

  1. 工具名:schedule_event / manage_email——语义直白最重要;
  2. 工具描述(docstring):写清”什么时候用我、输入长什么样”;
  3. system prompt 里的分工说明:“拆解请求、按需顺序或并行调用”。

换句话说,主管的”路由准确率”是提示词工程问题,不是代码问题。它做的是领域级判断(“这是日程还是邮件”),不是 API 级判断(“该填 ISO 还是时间戳”)。把这两层混在一起,正是单 Agent 方案会退化的原因。

③ 人工介入到底加在哪一层#

这是最容易搞混的一点,记三句话:

  • 拦截的钩子挂在子智能体上。HumanInTheLoopMiddleware(interrupt_on={"send_email": True}) 配在 email_agent 上——要被审的是它手里的 send_email,不是主管手里的 manage_email。
  • 暂停/恢复的能力挂在主管上。checkpointer=InMemorySaver() 只加在顶层;原文明确说这是必需的,没有检查点就无法暂停再恢复。
  • 中断会冒泡到最外层。你在主管的 stream 上就能看到来自两个子智能体的 interrupt,只需按 interrupt_.id 分别回决定:approve(照原样执行)、edit(改参数后执行)、reject(不执行)。

所以调用方(前端/AI 应用)只需要面对一个入口:跑主管 → 收到中断列表 → 展示给人 → 收集决定 → Command(resume=...) 继续跑。子智能体的存在对调用方是透明的。

④ 信息流怎么裁剪#

核心原则:边界上只传结论,不传过程。

  • 往下的方向:默认只传 request 字符串(最省);需要消歧时再补一句用户原话。不要图省事把整段对话历史灌给每个子智能体——那等于把隔离白做了。
  • 往上的方向:return result["messages"][-1].text 只回最终答复。中间的工具调用链、推理过程,主管不需要也不该看到。若希望主管做更精确的后续动作(比如”把刚建的事件 ID 记下来”),再改成回 JSON(§7 方案二)。
  • 一个隐性成本:裁得越狠,子智能体越”失忆”。所以每个子智能体的 prompt 里都要有一句”最终答复必须包含全部关键信息”——这是在裁剪和信息完整之间唯一的安全带。

完整代码在哪#

📎 官方教程:Build a personal assistant with subagents

完整示例见原文 Complete working example 一节(原文放在一个默认折叠的 Expandable 代码块里,约 160 行,把 §1 定义工具 → §2 建子智能体 → §3 包装成工具 → §4 建主管 → §5 跑起来串成一个脚本)。本文只摘骨架片段,不重复粘贴。原文 Next steps 指向的下一步阅读:handoffs(智能体之间直接对话)、context engineering(精细调信息流)、multi-agent overview(横向比较各模式)。


小结#

  1. 主管 + 子智能体 = 按领域切工具,再用一层薄薄的”路由 Agent”把它们串起来。主管看不到底层 API,子智能体看不到全局对话——隔离是设计出来的,不是自然发生的。
  2. 包装函数是唯一的接口。schedule_event(request: str) -> str 这种签名就是这个模式的全部魔法:进去一句人话,出来一句结论。
  3. 提示词就是路由表。工具名、工具描述、主管的 system prompt——主管”派谁”完全由这三样决定,调优要从这里下手。
  4. 人工介入挂在敏感动作上,检查点挂顶层。HumanInTheLoopMiddleware 配在子智能体,checkpointer 配在主管;中断会冒泡到最外层统一处理。
  5. 信息流默认是断的,需要就手动接。往下补上下文用 ToolRuntime,往上裁剪只回最终消息;同时别忘了让子智能体在最终答复里”把话说全”。

一句话选型#

该用:有多个界限清晰的领域(日历、邮件、CRM、数据库、检索),每个领域内部工具多或逻辑绕;你希望有一个中心点统一控制流程和对外输出;子智能体不需要直接跟用户对话。—— 这时主管模式能明显降低单 Agent 的工具选择负担,也让每个领域可以独立迭代。

别用:

  • 只有两三个工具——直接一个 Agent 就够,多一层只是多一层延迟和调试成本;
  • 需要子智能体直接和用户聊(比如”转人工专家”式体验)——用 handoff 交接模式,而不是把子智能体藏进工具里;
  • 需要智能体之间平级协作(互相讨论、辩论、投票)——那是另一类多智能体模式,主管模式天生是星形结构,不适合平级通信。

一句话:主管模式买的是”关注点分离”和”中心化控制”,代价是多一跳模型调用和一套需要精心维护的提示词边界。

09 实战:用 Subagents 搭一个个人助理(主管 + 日历/邮件子智能体)
https://aicooleo.cn/blog/posts/multiagent-09-case-subagents/
作者
aicooleo
发布于
2026-08-28
许可协议
CC BY-NC-SA 4.0
多 Agent 精讲 · 第 9 / 22 篇 返回合集