在进入正式的subagent学习之前,其实有必要对messages和response做一个说明。

messages 是一个列表结构,表示传递给模型的上下文信息,每一项都包含 role(角色,如 user / assistant / system)和 content(具体内容)。例如 messages[0] 就代表用户当前输入的一句话。在调试器中看到的 0 = {...},本质上就是这个列表中的第一个元素,即你发给模型的请求内容。

image.png

response 则是模型返回的结果,通常是一个结构化对象而不是简单字符串。它包含了模型 ID、唯一请求 ID,以及最关键的 content 字段。不同于传统接口直接返回文本,一些模型(如 qwen3.5-plus)会以“块”的形式返回内容,比如 ThinkingBlock(思考过程)和 TextBlock(最终回答)。此外,调试器中看到的 special variablesfunction variables 并不是业务数据,它们只是 Python 对象自带的底层属性和方法(如 __class__append 等),主要用于调试和内部机制,一般不需要关注。

image.png

我们仍然从核心函数agent_loop开始,其实与之前的tools_use不同的是,这里多了对于task的处理过程,也就是如果模型返回一个 tool_use block 且 block.name == "task" 时,代码不会像普通工具那样直接调用 TOOL_HANDLERS,而是进入特殊分支:提取 block.input["prompt"],然后调用 run_subagent(...)。这意味着,模型不是要执行某个具体功能(比如读文件、查数据),而是希望再启动一个新的子 Agent 来完成一个子任务。本质上,这是一种“把复杂问题拆成更小问题再解决”的机制。

可以先看一下这个task,task需要prompt和description这两个属性,这正是上面的函数所input的。

image.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# -- Subagent: fresh context, filtered tools, summary-only return --
def run_subagent(prompt: str) -> str:
sub_messages = [{"role": "user", "content": prompt}] # fresh context
for _ in range(30): # safety limit
response = client.messages.create(
model=MODEL, system=SUBAGENT_SYSTEM, messages=sub_messages,
tools=CHILD_TOOLS, max_tokens=8000,
)
sub_messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
break
results = []
for block in response.content:
if block.type == "tool_use":
handler = TOOL_HANDLERS.get(block.name)
output = handler(**block.input) if handler else f"Unknown tool: {block.name}"
results.append({"type": "tool_result", "tool_use_id": block.id, "content": str(output)[:50000]})
sub_messages.append({"role": "user", "content": results})
# Only the final text returns to the parent -- child context is discarded
return "".join(b.text for b in response.content if hasattr(b, "text")) or "(no summary)"

看一下subagent,我们可以看到agent_loop其实是相似的,不太一样的可能就是调用的tools是CHILD_TOOLS。

1
2
3
4
5
# -- Parent tools: base tools + task dispatcher --
PARENT_TOOLS = CHILD_TOOLS + [
{"name": "task", "description": "Spawn a subagent with fresh context. It shares the filesystem but not conversation history.",
"input_schema": {"type": "object", "properties": {"prompt": {"type": "string"}, "description": {"type": "string", "description": "Short description of the task"}}, "required": ["prompt"]}},
]

主agent可以看到其实是有一个task的,而subagent没有,这样的话可以避免无限subagent。

1
2
3

return "".join(b.text for b in response.content if hasattr(b, "text")) or "(no summary)"

当subagent执行完之后,会将text作为output,加入到result,最终作为messages进入agent_loop。
其实感觉就是把subagent作为一个tool去使用。

上下文隔离

其实就是说subagent的messages不会去进入到主agent的message。

image.png