跳到正文

TECH NOTE

1868 字 9 分钟 系列 14 / 22

14 附录:手写 `@tool` 包装子智能体 vs Deep Agents 的 `subagents=`——同一个原理,两种实现

ON THIS PAGE / 目录6 节

一句话结论:底层原理是同一个——都是 04 篇里的 Subagents 模式(主管 + 子智能体即工具 + 上下文隔离)。区别只在”包装”这件事谁来做:手写版你自己写 create_agent + @tool(04 篇的 tool per agent),Deep Agents 内置的是一套 单一派发工具(single dispatch tool)+ Agent 注册表 + 中间件栈——也就是 04 篇「决策二:工具模式」里那条约定优于配置的路。

实测日期:2026-08-30 | 环境:deepagents 0.7.5 + langchain 1.3.15 | 模型:DeepSeek(deepseek-v4-flash-vision-exp → 实际 deepseek-flash)| 协议:Responses API

实验脚本与抓包文件:demo/deepagents_demo.py、demo/captured_deepagents.jsonl;运行方式见复现实验说明;手写版对照见 13 附录(demo/demo.py)


一、先看结论表#

维度手写版(create_agent + @tool,04 篇 tool per agent)Deep Agents(create_deep_agent(subagents=[...]))
暴露给模型的工具每个子智能体一个工具(本例 schedule_event)只有一个 task 工具,参数 description + subagent_type
模型怎么知道有哪些子智能体工具名 + 工具描述(tool description)task 的工具描述里自动拼上一份 Agent 目录(- name: description)
子智能体怎么定义自己 create_agent(model, tools, system_prompt)传 dict(name/description/system_prompt/tools/model/…)或 CompiledSubAgent
子智能体的工具你显式指定默认继承主 Agent 的全部工具(除非显式给 tools 覆盖)
上下文隔离你自己决定传什么(我们传了 request 一句话)内置 mode:"isolated"(只传 description),可切 mode:"fork"(带父对话历史 + 复用 prompt cache)
结果怎么回到主管return result["messages"][-1].content内置:反向找最后一条非空 AIMessage → 包成 ToolMessage,并合并 state
结构化输出自己解析response_format= → 父级直接收到 JSON
人工介入 / 权限自己接 interrupt每子智能体可配 interrupt_on、permissions
可观测性自己打点自动打 ls_agent_type="subagent";工具里能读 lc_agent_name 判断是谁在调
白送的东西无默认注入 general-purpose 子智能体 + 文件系统工具(ls/read/write/edit/delete/glob/grep)+ todos/规划 + 摘要压缩 + skills + memory + 子智能体工具隔离
首轮上下文成本小(本例 in=347)大(本例 in=2622,工具 schema + 系统提示词都更重)
适合想精细控制、轻量、只用一种模式想要一整套现成的 agent harness(规划/文件/压缩/技能/子智能体)

二、原理相同的证据:源码里就是一个”单一派发工具”#

deepagents/middleware/subagents.py(0.7.5)把 subagents= 变成了这些东西:

① 工具 schema:就是 task(description, subagent_type)

class TaskToolSchema(BaseModel):
"""Input schema for the `task` tool."""
description: str = Field(description=(
"A detailed description of the task for the subagent to perform autonomously. "
"Include all necessary context and specify the expected output format."))
subagent_type: str = Field(description=(
"The type of subagent to use. Must be one of the available agent types listed in the tool description."))

② Agent 目录直接拼进工具描述

subagent_description_str = "\n".join(f"- {s['name']}: {s['description']}" for s in compiled_subagents)
description = TASK_TOOL_DESCRIPTION.format(available_agents=subagent_description_str)

③ 隔离模式:只把任务描述塞进子智能体(和我们手写版传 request 一句话是同一个动作)

subagent_state["messages"] = [HumanMessage(content=description)]

④ 结果回传:反向找最后一条非空 AIMessage,包成 ToolMessage

for msg in reversed(result["messages"]):
if isinstance(msg, AIMessage):
text = msg.text.rstrip() if msg.text else ""
if text:
content = text
break
return Command(update={**state_update, "messages": [ToolMessage(content, tool_call_id=tool_call_id)]})

⑤ 附带的可观测性:_subagent_tracing_context() 会给子智能体运行打上 ls_agent_type="subagent" 元数据。

换句话说:Deep Agents 并没有发明新机制,它是把 04 篇「单一派发工具 + 注册表」那一节产品化了,再套上 Deep Agents 自己的中间件栈。


三、抓包实证:线报文里长什么样#

同一次运行,暴露给模型的工具清单(Responses API 的 tools 数组,节选):

• ls params=['path']
• read_file params=['file_path','offset','limit']
• write_file params=['file_path','content']
• edit_file params=['file_path','old_string','new_string','replace_all']
• delete params=['file_path']
• glob params=['pattern','path']
• grep params=['pattern','path','glob','output_mode','max_count']
• task params=['description','subagent_type'] ← 唯一的派发入口
• save_note params=['title','body'] ← 我们自己的业务工具

task 工具的描述(真实报文,节选):

Launch an ephemeral subagent to handle a complex, multi-step task in an isolated context window.
Available agent types and the tools they have access to:
- general-purpose: General-purpose agent for researching complex questions, ...
- research-agent: 用于需要多步调研、比较、查证的问题
Specify subagent_type to select the agent. Usage notes:
- Launch multiple agents concurrently when their tasks are independent, using a single message with multiple tool calls.
- Each invocation is stateless: the agent sees only the prompt you give it and returns a single final report. ...

三个关键信息:① 一个工具管所有子智能体(靠 subagent_type 分发);② general-purpose 是自动注入的(我们只传了 research-agent);③ 描述里直接告诉模型”可以一次消息里并发派多个”、“每次调用是无状态的、只看到你给的那段 prompt”——这跟 04 篇讲的 specs / inputs 两个阀门一一对应。

这次运行的 4 次模型调用(形状和手写版完全一致)#

#谁在调模型上下文产出in / out
1主管system(技术选型助手) + 用户原话(+ 8 个工具的 schema)task(description=…, subagent_type="research-agent") + 一句”我来交给子代理”2622 / 466
2子智能体只有它自己的 system + 主管给的任务描述(输入项数 = 2)ls(path="/")2536 / 841
3子智能体上者 + ls 结果结构化调研结论(回给主管)3392 / 1473
4主管system + 用户原话 + task 调用 + 工具结果面向用户的最终回答4561 / 933

对照 13 篇手写版的 ①主管派活 → ②子智能体调工具 → ③子智能体作答 → ④主管收尾——结构完全同构。

两个额外观察:

  • 子智能体默认继承主 Agent 的工具:research-agent 没写 tools,于是它手里直接有 ls(文件系统工具),第 2 次调用就去 ls("/") 找资料了。想限制它,就得显式传 tools=(文档:显式指定会整体覆盖继承来的工具集)。
  • 派活次数由模型决定:同一份代码的前一次运行里,主管连续调了 5 次 task(12 条消息,边调研边补细节);抓包这次只调了 1 次。所以”Deep Agents 一次请求 = 几次模型调用”没有固定答案——上限取决于模型何时认为够了。

四、什么时候用哪个#

你的情况建议
只需要”一个主管 + 几个专家”、想完全掌控提示词与工具边界、不想引入额外中间件手写 @tool 包装(04 篇 tool per agent),代码量也就十几行
子智能体数量多、团队各自维护、希望加一个新专家”不改协调者代码”task 单一派发工具(手写或者直接用 Deep Agents)
需要规划(todos)、虚拟文件系统、超长上下文压缩、技能按需加载、权限与人工介入Deep Agents(这些是白送的;手写得自己拼)
想让子智能体”接着父级已开工的活干”Deep Agents mode:"fork"(手写版要自己把父级 messages 传进去)
只是想让子智能体独立跑一段、要结构化结果两者都行;Deep Agents 用 response_format= 更省事

代价提醒:Deep Agents 的首轮上下文明显更贵(本例 in=2622 vs 手写版 in=347,因为多了 7 个文件系统工具的 schema 与更重的系统提示词),而且它默认注入 general-purpose、默认让子智能体继承全部工具——这两条默认值在”工具很危险”的场景里要主动改掉(general_purpose_subagent=GeneralPurposeSubagentProfile(enabled=False) 关掉默认子智能体;给子智能体显式 tools= 收窄权限)。


五、自己复现#

Terminal window
# 抓包跑 Deep Agents 版(脚本见 demo/deepagents_demo.py)
CAPTURE_OUT=$PWD/captured_deepagents.jsonl \
/tmp/mitmenv/bin/mitmdump -q -s capture.py -p 8888 --set confdir=/tmp/subagents-demo/.mitmproxy
export CLOUD_MODEL_BASE_URL=https://api.deepseek.com
export CLOUD_MODEL_API_KEY=<你的 key>
export CLOUD_MODEL_NAME=deepseek-v4-flash-vision-exp
export CLOUD_MODEL_PROTOCOL=RESPONSES
HTTPS_PROXY=http://127.0.0.1:8888 SSL_CERT_FILE=/tmp/subagents-demo/.mitmproxy/mitmproxy-ca-cert.pem \
python deepagents_demo.py
python summarize.py captured_deepagents.jsonl

说明:两次抓包(13 篇的手写版、本篇的 Deep Agents 版)用的是同一个 mitmdump addon,抓包文件只存请求/响应正文,不含请求头与密钥。

14 附录:手写 `@tool` 包装子智能体 vs Deep Agents 的 `subagents=`——同一个原理,两种实现
https://aicooleo.cn/blog/posts/multiagent-14-appendix-tool-wrap/
作者
aicooleo
发布于
2026-08-30
许可协议
CC BY-NC-SA 4.0
多 Agent 精讲 · 第 14 / 22 篇 返回合集