理解 LangGraph Subagent 的 ID 关联机制

在 LangGraph/DeepAgents 中,主 agent 委派 subagent 执行任务时,通过一个精巧的 ID 关联机制来追踪整个生命周期。本文用最简洁的方式说明这个机制。


核心概念

一句话概括:task 工具调用的 id 就是 subagent 的身份标识,用来关联整个生命周期。


完整流程

步骤 1:主 agent 决定委派任务

主 agent 调用 task 工具,生成一个唯一的 tool_call_id

python
{
    "type": "updates",
    "ns": (),                                    # 主 agent,ns 为空
    "data": {
        "model_request": {
            "messages": [
                AIMessage(
                    tool_calls=[{
                        "name": "task",          # task 工具
                        "args": {
                            "subagent_type": "researcher",
                            "description": "Research AI safety"
                        },
                        "id": "call_abc123"      # ← 这个 ID 是关键
                    }]
                )
            ]
        }
    }
}

关键信息id: "call_abc123" 是这个 subagent 的唯一标识。


步骤 2:Subagent 执行中

Subagent 启动后,其所有事件都通过 ns (namespace) 携带这个 ID:

python
# Subagent 的所有事件
{
    "type": "updates",                           # 或 "messages"
    "ns": ("tools:call_abc123",),               # tools: + tool_call_id
    "data": {
        "model_request": {...},
        # 或其他节点数据
    }
}

# Subagent 的 token 流
{
    "type": "messages",
    "ns": ("tools:call_abc123",),
    "data": (
        AIMessageChunk(content="Researching..."),
        {...}
    )
}

# Subagent 内部的工具调用
{
    "type": "updates",
    "ns": ("tools:call_abc123",),
    "data": {
        "model_request": {
            "messages": [
                AIMessage(tool_calls=[{
                    "name": "web_search",
                    "id": "call_xyz789"          # 这是 subagent 内部的工具 ID
                }])
            ]
        }
    }
}

关键信息ns = ("tools:{tool_call_id}",) 标识事件来自哪个 subagent。


步骤 3:Subagent 结束,结果返回

Subagent 执行完毕后,结果通过 ToolMessage 返回给主 agent,通过 tool_call_id 关联:

python
{
    "type": "updates",
    "ns": (),                                    # 回到主 agent
    "data": {
        "tools": {
            "messages": [
                ToolMessage(
                    content="## AI Safety Report\n\n...",  # subagent 的输出
                    tool_call_id="call_abc123",  # ← 对应步骤 1 的 ID
                    name="task"
                )
            ]
        }
    }
}

关键信息tool_call_id 对应回步骤 1 中的 task 工具调用。


时序图

text
┌─────────────────────────────────────────────────────────────────────┐
│                          主 Agent                                    │
│                                                                      │
│  ① model_request: AIMessage(tool_calls=[task(id="call_abc123")])   │
│                              │                                       │
│                              │ tool_call_id = "call_abc123"         │
│                              ▼                                       │
├─────────────────────────────────────────────────────────────────────┤
│                       Subagent (researcher)                          │
│                                                                      │
│  ns = ("tools:call_abc123",)                                        │
│                                                                      │
│  ② model_request: "I'll research AI safety..."                      │
│     tools: ToolMessage(web_search result)                           │
│     model_request: "Based on my research..."                        │
│                              │                                       │
│                              │ tool_call_id = "call_abc123"         │
│                              ▼                                       │
├─────────────────────────────────────────────────────────────────────┤
│                          主 Agent                                    │
│                                                                      │
│  ③ tools: ToolMessage(tool_call_id="call_abc123", content="...")   │
│     model_request: "Here's the report..."                           │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

ID 关联表

步骤 事件类型 ns 关键 ID
1. 委派 updates () tool_calls[0].id = "call_abc123"
2. 执行 所有 subagent 事件 ("tools:call_abc123",) 通过 ns 识别
3. 结束 updates () ToolMessage.tool_call_id = "call_abc123"

前端渲染应用

理解这个 ID 关联机制后,前端可以这样处理:

typescript
// 状态管理
interface SubagentInfo {
  id: string;           // tool_call_id
  type: string;         // subagent_type
  description: string;
  status: "pending" | "running" | "complete";
  result?: string;
}

// 处理事件
function handleEvent(event: StreamPart) {
  // 1. 检测 subagent 启动
  if (event.type === "updates" && !event.ns.length) {
    const toolCalls = event.data.model_request?.messages?.[0]?.tool_calls;
    toolCalls?.forEach(tc => {
      if (tc.name === "task") {
        subagents[tc.id] = {
          id: tc.id,
          type: tc.args.subagent_type,
          description: tc.args.description,
          status: "running"
        };
      }
    });
  }
  
  // 2. 识别 subagent 事件
  if (event.ns.length && event.ns[0].startsWith("tools:")) {
    const subagentId = event.ns[0].split(":")[1];
    // 将事件路由到对应的 subagent UI 组件
    renderSubagentEvent(subagentId, event);
  }
  
  // 3. 检测 subagent 结束
  if (event.type === "updates" && event.data.tools) {
    event.data.tools.messages.forEach(msg => {
      if (msg.name === "task" && subagents[msg.tool_call_id]) {
        subagents[msg.tool_call_id].status = "complete";
        subagents[msg.tool_call_id].result = msg.content;
      }
    });
  }
}

总结

要点 说明
启动 主 agent 调用 task 工具,生成 tool_call_id
标识 Subagent 事件通过 ns = ("tools:{id}",) 标识来源
关联 结果通过 ToolMessage.tool_call_id 关联回去
用途 前端通过 ID 将事件路由到正确的 UI 组件

这个机制确保了在多个 subagent 并行执行时,每个事件都能准确归属到对应的 subagent,实现精细化的 UI 渲染。