---
title: "嵌套 Agent、TeamAgent 与子图"
---



复杂系统不应堆在一个超大 Graph 中。可以组合三类执行单元：

```text
主 Graph
  ├─ 单 Agent
  ├─ TeamAgent
  └─ Graph.asTask() 子图
```

### 1、TeamAgent 作为 Agent 节点

#### 1.1 父 TeamAgent Graph 直接嵌套子 TeamAgent

`TeamAgent` 本身实现 `Agent`，因此可成为父 TeamAgent Graph 的 Activity。父 TeamTrace 和 TeamProtocol 会为子团队准备本次 Prompt：

```java
TeamAgent devTeam = TeamAgent.of(null)
        .name("dev_team")
        .protocol(TeamProtocols.SEQUENTIAL)
        .feedbackMode(false)
        .agentAdd(coder, tester)
        .outputKey("team.dev.result")
        .build();

Agent reviewer = SimpleAgent.of(chatModel)
        .name("reviewer")
        .role("质量审核员")
        .instruction("审核开发小组交付物：\n#{team.dev.result}")
        .outputKey("review.result")
        .build();

TeamAgent project = TeamAgent.of(null)
        .name("project_graph")
        .protocol(TeamProtocols.NONE)
        .agentAdd(devTeam, reviewer)
        .graphAdjuster(spec -> {
            spec.addStart(Agent.ID_START).linkAdd(devTeam.name());
            spec.addActivity(devTeam).linkAdd(reviewer.name());
            spec.addActivity(reviewer).linkAdd(Agent.ID_END);
            spec.addEnd(Agent.ID_END);
        })
        .build();
```

父 TeamTrace 记录 `dev_team` 和 `reviewer`；子 TeamTrace 记录 `coder` 和 `tester`：

```java
TeamTrace projectTrace = project.getTrace(session);
TeamTrace devTrace = devTeam.getTrace(session);
```

团队名称必须稳定且避免冲突。默认 TraceKey 是 `__` 加团队名称，TeamAgent 内部 Graph ID 也使用该 TraceKey，例如：

```text
team.name = dev_team
trace.key = __dev_team
graph.id  = __dev_team
```

#### 1.2 独立业务 Graph 必须显式调用 TeamAgent

普通独立 Graph 没有父 TeamTrace，不能直接写 `spec.addActivity(devTeam)` 后期待业务 Context 自动变成团队 Prompt。应使用适配 Activity 明确输入、Session 和输出：

```java
spec.addActivity("dev_team_step")
        .task((ctx, node) -> {
            AgentSession session = ctx.getAs(Agent.KEY_SESSION);
            String requirement = ctx.getAs("input.requirement");

            String result = devTeam.prompt(requirement)
                    .session(session)
                    .call()
                    .getContent();

            ctx.put("team.dev.result", result);
        })
        .linkAdd("next");
```

此时 TeamAgent 仍负责自己的内部 Graph、TeamTrace 和结果收敛；外层业务 Graph 负责把业务数据映射成团队 Prompt。只有在父 TeamAgent 的内部 Graph 中，才适合把子 TeamAgent 直接作为 Agent 节点使用。

### 2、Graph.asTask() 确定性子图

```java
Graph prepareGraph = Graph.create("prepare_graph", spec -> {
    spec.addStart("prepare_start").linkAdd("normalize");
    spec.addActivity("normalize")
            .task((ctx, node) -> {
                String input = ctx.getAs("input.raw");
                ctx.put("input.normalized", input.trim());
            })
            .linkAdd("prepare_end");
    spec.addEnd("prepare_end");
});
```

放入主图：

```java
Graph mainGraph = Graph.create("main_graph", spec -> {
    spec.addStart("start").linkAdd(prepareGraph.getId());
    spec.addActivity(prepareGraph.asTask()).linkAdd("agent_step");

    spec.addActivity("agent_step")
            .task((ctx, node) -> {
                AgentSession session = ctx.getAs(Agent.KEY_SESSION);
                String prompt = ctx.getAs("input.normalized");
                String result = analyst.prompt(prompt)
                        .session(session)
                        .call()
                        .getContent();
                ctx.put("analysis.result", result);
            })
            .linkAdd("end");
    spec.addEnd("end");
});
```

完整执行准备：

```java
FlowContext context = FlowContext.of("main-001");
context.put("input.raw", "  分析迁移方案  ");
InMemoryAgentSession.of(context);

FlowEngine.newInstance().eval(mainGraph, context);
```

`asTask()` 直接持有 Graph，不要求预先注册。子图沿用父图的 Context、FlowEngine、Driver、FlowTrace 和执行步数。

### 3、通过 #graphId 引用子图

YAML/JSON 或按 ID 组合时可以使用：

```java
.task("#prepare_graph")
```

被引用 Graph 必须先加载到同一个 FlowEngine：

```java
engine.load(prepareGraph);
engine.load(mainGraph);
engine.eval("main_graph", context);
```

TeamAgent 内部使用 `FlowEngine.newInstance(true)` 创建简化引擎。该模式使用不可写的空 Graph/命名 Driver 注册表，不用于动态 `load/unload`，也不能依赖 `#graphId` 在内部解析注册式子图。在 `graphAdjuster` 中组合普通子图时，应使用直接持有 Graph 的 `prepareGraph.asTask()`。

### 4、两类嵌套的本质区别

| 维度 | TeamAgent 嵌套 | Graph 子图 |
|---|---|---|
| 目标 | 多 Agent 协作单元 | 确定性流程复用 |
| TeamTrace | 子团队有独立 TeamTrace | 不自动创建 |
| Prompt | 子团队有协议和 working memory | 不自动处理 |
| 输出 | 可用 TeamAgent `outputKey` | 子图自行写 Context |
| Context | 与父团队共享 Session Context | 与父图共享 Context |
| 停止/恢复 | 同时涉及 TeamTrace 和 FlowTrace | 主要由 FlowTrace 定位 |

### 5、ID 唯一性范围

Graph ID 应在以下范围保持唯一：

- 同一个 FlowEngine 的注册空间；
- 共享同一 FlowContext 的嵌套执行树。

它不是整个 JVM 的强制全局唯一，但复用 ID 会污染 FlowTrace 和网关临时状态。

Node ID 只需在所属 Graph 内唯一；`Graph.asTask()` 默认使用子图 ID 作为父图节点 ID，因此还要避免与父图节点重名。

### 6、停止和中断传播

`asTask()` 和 `#graphId` 都在同一执行交换器中运行：

- 子图正常到 End 后，父图继续；
- 子图 `stop()` 会停止整个共享执行；
- 子图 `interrupt()` 会截断当前分支；
- 恢复时按各自 Graph ID 的 FlowTrace 位置定位。

因此子图节点也必须满足幂等要求。

### 7、分层建议

```text
主 Graph：业务阶段、权限和治理边界
TeamAgent：某阶段内的多 Agent 协作
子 Graph：可复用的确定性处理过程
单 Agent：边界清晰的认知任务
```

这种分层比一个超大 TeamAgent 更容易测试、观察、恢复和版本演进。