子智能体进阶异步
1. 先判断,该用智能体还是工具
主智能体先通过 task 派活,子智能体拿到独立任务后自己完成推理和工具调用,最后只把结果交回主智能体。这样主智能体不用背着所有细节继续往下推理。
写子智能体之前,先问一个很实际的问题:这件事是否值得单独拆出一个助手?
如果答案是“是”,才继续配置它的 name、description、system_prompt 和 tools
2. 适合拆成子智能体的任务
| 场景 | 说明 | 示例 |
|---|---|---|
| 多步骤任务上下文很乱 | 中间过程多,主智能体容易被大量信息干扰 | 深度研究、多轮搜索、多源资料整合 |
| 存在不同专业领域 | 不同任务需要不同知识和工具 | 金融分析、法律检索、数据库查询 |
| 需要不同模型能力 | 某些任务可能需要多模态或更强模型 | 图片理解、长文档分析、代码生成 |
| 主智能体只做统筹 | 主智能体像项目经理,只负责分派和汇总 | 「深度研搜」的 Main Agent |
3. 普通工具和子智能体的选择边界
| 任务类型 | 更适合的形式 | 原因 |
|---|---|---|
| 一次函数调用就能完成 | 普通工具 | 成本低、链路短、结果更稳定 |
| 查询天气、简单计算、格式转换 | 普通工具 | 逻辑边界清楚,不需要独立上下文 |
| 多步搜索、阅读、分析、归纳 | 子智能体 | 需要独立提示词、独立上下文和多步执行 |
| 带专属工具和复杂策略的任务 | 子智能体 | 方便限制工具权限,也方便单独调试 |
所以写代码前,更准确的判断标准不是“步骤多不多”,而是下面三个问题:
- 这些步骤是否需要不同专业能力?
- 这些步骤之间是否可以隔离上下文?
- 拆开以后收益是否大于额外成本?
一句话总结:工具适合做确定的小动作,子智能体适合做需要思考的一段小任务
配置子智能体方式
- 字典子智能体:配置一个简单助手。
CompiledSubAgent:把一个完整 Agent / Graph 封装成子智能体。
字典子智能体基本字段:
| 字段 | 作用 | 是否必填 |
|---|---|---|
name |
子智能体名称,主智能体调用时会使用 | 必填 |
description |
子智能体职责描述,主智能体靠它判断是否调用 | 必填 |
system_prompt |
子智能体自己的系统提示词 | 常用 |
tools |
子智能体可使用的工具列表 | 常用 |
model |
子智能体使用的模型,不填则通常继承主智能体模型 | 可选 |
name 是子智能体的唯一标识。后面流式输出里看到 task 调用时,subagent_type 就会对应这个名称。
description 是主智能体判断任务归属的依据。如果描述写得模糊,主智能体就容易分派错。
本案例中定义了三个简单助手:天气助手、数学助手、翻译助手:
# 字典式子智能体的核心字段:
# name 是子智能体唯一标识,流式输出里的 subagent_type 会对应它;
# description 主要给主智能体看,用来判断什么时候应该调用该助手;
# system_prompt 是子智能体自己的角色和行为约束;
# tools 是该子智能体可用的工具列表,不填 model 时通常继承主智能体模型。
weather_agent = {
"name": "weather_helper",
"description": "用于查询天气信息。当用户询问天气时,请调用此助手。",
"system_prompt": """
你是一个天气查询助手。
无论用户查询哪个城市,请统一回复:今天天气晴朗,温度25度。
""",
"tools": [],
}
math_agent = {
"name": "math_helper",
"description": "用于处理数学计算问题。当用户询问加减乘除、数字计算、算数题时,请调用此助手。",
"system_prompt": """
你是一个严谨的数学助手。
请帮助用户完成数学计算,并给出清晰、准确的答案。
""",
"tools": [],
}
translate_agent = {
"name": "translate_helper",
"description": "用于处理中英互译任务。当用户需要中文和英文之间的翻译时,请调用此助手。",
"system_prompt": """
你是一个中英翻译助手。
如果输入是中文,请翻译成英文;如果输入是英文,请翻译成中文。
""",
"tools": [],
}
注册到主智能体:
创建主智能体时,把这些字典放进 subagents 即可。
# 主智能体自己不挂普通工具,重点负责根据用户问题分派给合适的子智能体
main_agent = create_deep_agent(
model=llm,
tools=[],
subagents=[weather_agent, math_agent, translate_agent],
system_prompt="""
你是一个负责统筹任务的主智能体。
请根据用户需求选择合适的子智能体完成任务。
你不直接执行天气查询、数学计算或翻译任务,而是通过子智能体完成。
""",
)
系统提示词要说清楚主智能体的边界:它不是天气助手、数学助手或翻译助手本身,而是负责把任务交给合适的子智能体
以天气查询这组输出为例,三行日志分别对应三件事:
| 输出片段 | 代表的含义 |
|---|---|
【model】决定调用子智能体weather_helper |
主智能体判断这个问题应该交给天气助手处理 |
【agent】调用了具体的工具task |
DeepAgents 执行 task,也就是实际去调用子智能体 |
【model】返回最终结果 |
主智能体拿到子智能体结果后,再整理成最终回复给用户 |
后面分析 task 工具调用时,心里先记住这条线就可以: 用户问题 -> 主智能体选择子智能体 -> task 执行 -> 子智能体返回结果 -> 主智能体整理最终回复
子智能体本质上是task工具调用
主智能体并不是直接跳到某个子智能体里执行,而是先由模型做一次决策,在 DeepAgents 中,主智能体调用子智能体时,通常会表现为一次特殊工具调用: tool_call["name"] == "task"
它的参数里会说明两件事:
subagent_type:要调用哪个子智能体,对应前面配置里的name。description:交给这个子智能体的具体任务。
也就是说,从主智能体视角看,子智能体是一种可调度能力。它不是普通 Python 函数工具,而是一个拥有独立提示词、独立工具集和独立上下文的 Agent。

子智能体并不是“凭空被调用”的。主智能体先请求模型判断下一步,模型返回 task 工具调用,DeepAgents 再根据 subagent_type 找到对应子智能体执行
还要补一个容易混淆的细节:子智能体不是普通工具,但它在主智能体眼里会像工具一样返回结果
原因是主智能体调用子智能体时,走的是 task 这个特殊工具。子智能体执行完以后,它的结果会回到主智能体这边,并且通常表现为一条 ToolMessage
一个“主智能体调用子智能体再回答”的简单流程,常见链路是:
主智能体模型:判断调用哪个子智能体 -> 子智能体模型:完成被分派的局部任务 -> 主智能体模型:整合子智能体结果并输出最终答案
这也是为什么子智能体比普通工具成本更高:普通工具通常只是一次函数或 API 调用,而子智能体本身还会走一段 Agent 执行过程
如何在流式输出里识别Task:
重点看 model 节点里最后一条消息的 tool_calls。普通工具调用和子智能体调用都会出现在这里,区别主要看工具名.
| 观察位置 | 普通工具调用 | 子智能体调用 |
|---|---|---|
tool_call["name"] |
工具自己的名字,例如 internet_search |
固定是 task |
tool_call["args"] |
普通工具的入参 | 包含 subagent_type 和 description |
| 返回结果所在节点 | tools |
也是 tools,因为 task 本身表现为一个工具结果 |
所以在流式解析代码里,只要判断工具名是不是 task,就能区分“调用普通工具”和“分派子智能体”:
if tool_call["name"] == "task":
print(f"调用子智能体:{tool_call['args']['subagent_type']}")
else:
print(f"调用普通工具:{tool_call['name']}")
把stream改为astream异步
| 同步写法 | 异步写法 |
|---|---|
def test_stream(...) |
async def test_stream(...) |
for chunk in stream |
async for chunk in stream |
main_agent.stream(...) |
main_agent.astream(...) |
并发执行
如果想同时发起多个任务,可以使用 asyncio.gather()
async def batch_run():
# 这里得到的是协程对象;传给 gather 后,会由事件循环并发调度
task1 = test_stream("北京今天的天气怎么样?")
task2 = test_stream("请将'你是最棒的'翻译成英文。")
# 打印类型只是为了让初学者看到:调用 async 函数不会立刻执行,而是先返回 coroutine
print(type(task1))
print(type(task2))
# gather 会等待两个协程都完成;哪个请求先拿到结果,就会先输出自己的流式片段
await asyncio.gather(task1, task2)
asyncio.run(batch_run())
这里的执行方式不再是“任务 1 完成后再开始任务 2”,而是两个任务一起推进。哪个任务先拿到模型或工具结果,哪个任务就先输出。
子智能体嵌套的边界
我们可能想配置一个层级结构,比如 CEO 调 CTO,CTO 再调 Coder。
# 1. 底层 Coder:理想情况下,它应该只负责具体代码实现
coder_config = {
"name": "Coder",
"description": "高级 Python 工程师,负责接收具体的编码任务并实现代码。",
"system_prompt": """
你是一名高级 Python 工程师。
你的职责是接收具体的编码任务,并给出对应的代码实现。
""",
"tools": [],
}
# 2. 中间层 CTO:理想情况下,它负责拆解任务,再委派给 Coder
cto_config = {
"name": "CTO",
"description": "技术总监,负责将战略需求转化为技术任务,并分配给工程师。",
# system_prompt 可以约束 CTO 的角色,但不能让 dict 自动支持嵌套子智能体
"system_prompt": """
你是技术总监。
你不直接编写代码。
你的职责包括:
1. 分析 CEO 的需求。
2. 设计技术方案。
3. 调用 Coder 子智能体完成具体的代码编写工作。
""",
"tools": [],
# 关键边界:普通 dict 子智能体的标准字段里没有 subagents。
# 这一行是“反例式演示”,用于观察底层是否忽略该字段,而不是推荐用法。
"subagents": [coder_config],
}
第一,顶层 CEO 确实调用了 task,并且 subagent_type 是 CTO,这说明 CEO -> CTO 这一层委派是生效的
第二,后面的输出里没有继续出现 subagent_type == "Coder"。也就是说,虽然 cto_config 里写了 "subagents": [coder_config],但普通字典式子智能体并没有稳定形成 CTO -> Coder 的二级委派
字典式子智能体适合轻量分工,不适合承载复杂层级架构。
如果确实要做更复杂的层级结构,建议使用后面第 4 章要讲的 CompiledSubAgent,或者把复杂逻辑封装成一个独立的 LangGraph 图,再作为子智能体挂到 DeepAgents 中

浙公网安备 33010602011771号