# 11. 子 Agent 使用边界和多 Agent 协作

本文用于推进 ZK Data Agent 后续的子 Agent 能力增强。目标不是“让模型更爱开子 Agent”，而是把子 Agent 收敛为一种可靠的编排手段：只在长耗时、可并行、边界清晰、上下文可隔离的任务里使用。

调研日期：2026-06-12。

## 1. 外部实践共识

### 1.1 Anthropic Research：适合宽度优先和并行探索

Anthropic 的 Research 多 Agent 系统采用 lead agent + parallel subagents。Lead agent 负责分析用户问题、制定研究策略，再派出多个 subagent 同时探索不同方向。

它强调几个判断点：

- 多 Agent 特别适合开放式研究，因为研究路径不可提前硬编码，需要根据中间发现动态调整。
- Subagent 可以拥有独立上下文窗口，并行探索不同方面，再把压缩后的结果交回主 Agent。
- 多 Agent 在 breadth-first query 上更有效，尤其是多个方向可以同时查的时候。
- 成本明显更高。Anthropic 提到 agent 通常比普通聊天消耗更多 token，多 Agent 系统消耗更高，因此只适合任务价值足够高的场景。
- 如果所有 Agent 都必须共享同一上下文，或者子任务之间依赖很多，就不适合多 Agent。

参考：https://www.anthropic.com/engineering/multi-agent-research-system

### 1.2 OpenAI Agents SDK：handoff 是专业化分工

OpenAI Agents SDK 的 handoff 用于把任务委托给另一个 Agent，特别适合不同 Agent 专注不同领域的场景。Handoff 本身以 tool 的形式暴露给 LLM，让模型决定是否转交。

这给我们的启发是：

- 子 Agent 不应该只是“再开一个一样的模型”，而应该有明确 profile。
- Profile 描述要告诉模型什么时候使用该 Agent。
- Handoff 可以带输入过滤和动态启用条件，避免把全量上下文无脑传给子 Agent。

参考：https://openai.github.io/openai-agents-python/handoffs/

### 1.3 LangChain / LangGraph：上下文工程比拆 Agent 更重要

LangChain 对多 Agent 的总结非常贴近我们的情况：多 Agent 成败的关键不是数量，而是 context engineering。每个 subagent 需要明确目标、输出格式、工具范围、数据来源和边界，否则容易重复工作、遗漏信息或互相冲突。

另一个重要观点是：以“读”为主的多 Agent 更容易成功，以“写”为主的多 Agent 更难。多个 Agent 同时写代码、写文档或写同一份数据，容易产生冲突；更好的方式是让多个子 Agent 读、查、分析，最后由主 Agent 统一合成和写入。

LangGraph Supervisor 也采用 supervisor-worker 模式：单一 supervisor 负责用户交互，worker agent 只和 supervisor 通信。

参考：

- https://www.langchain.com/blog/how-and-when-to-build-multi-agent-systems
- https://changelog.langchain.com/announcements/langgraph-supervisor-a-library-for-hierarchical-multi-agent-systems

### 1.4 AutoGen：多 Agent 是编排框架，不是默认答案

AutoGen 强调可对话、可定制、可集成工具和人工反馈的 Agent 协作。它适合构建复杂 LLM workflow，但也意味着需要显式设计对话模式、工具权限、人类检查点和终止条件。

对 ZK Data Agent 来说，这说明多 Agent 能力应该服务于“工作流编排”，不能变成模型遇到复杂任务就随意分身。

参考：https://microsoft.github.io/autogen/0.2/docs/Use-Cases/agent_chat/

## 2. ZK Data Agent 当前机制

当前子 Agent 的创建不是后端自动发生的，而是主模型调用 `Agent` 或旧名 `delegate_agent` 工具后触发。

关键链路：

```text
主 Agent 进入 turn loop
  -> 模型返回 tool_calls
  -> tool_call.name == Agent / delegate_agent
  -> _execute_delegate_agent(arguments)
  -> 解析 subagent_type / prompt / subtasks / max_turns / allow_write
  -> 创建 LocalCodingAgent 子实例
  -> 子 Agent 完成后把 child_results / delegate_batches 写回工具结果
  -> 主 Agent 继续总结或执行下一步
```

当前实现里的重要约束：

- `subtasks` 最多取前 8 个。
- 子 Agent 默认不能递归调用 `Agent` / `delegate_agent`。
- Explore、Plan、verification 等只读 Agent 禁止写文件。
- 写权限需要父 Agent 允许，并且工具参数显式 `allow_write=true`。
- 子会话会标记 `session_metadata.visibility = child`，默认不进入左侧主会话列表。
- 右侧活动区通过 `child_results`、`delegate_batches`、`dependency_skips` 展示汇总。

相关代码：

```text
src/agent_tools.py            Agent 工具 schema 和描述
src/agent_prompting.py        子 Agent 系统提示词注入
src/builtin_agents.py         内置 Agent profile 和 when_to_use
src/agent_runtime.py          _execute_delegate_agent / _run_single_subtask
frontend/.../activity-panel   子 Agent 活动汇总展示
```

## 3. 我们的设计定位

ZK Data Agent 里应该有三层能力：

```text
主 Agent
  负责用户意图、当前会话状态、Skill 选择、最终决策、最终写入和回复。

Skill
  负责领域流程、业务知识、稳定脚本、review 门禁和产物规范。

子 Agent
  负责边界清晰的独立子任务，尤其是读、查、分析、验证、候选召回。
```

子 Agent 不是 Skill 的替代品，也不是主 Agent 的替代品。

- Skill 回答“这类业务流程应该怎么做”。
- Tool 回答“这个确定性动作怎么执行”。
- 子 Agent 回答“这个可隔离子任务能不能交给另一个 Agent 独立完成”。

## 4. 什么时候应该优先使用子 Agent

### 4.1 明显适合

满足下面多个条件时，可以优先考虑子 Agent：

| 判断项 | 说明 | 示例 |
|---|---|---|
| 长耗时 | 主 Agent 直接串行做会占用很多轮 | 查多个索引、读大量文件、批量分析 badcase |
| 可并行 | 子任务之间没有强依赖 | 同时分析 5 个 rid、同时对比 3 个标签边界 |
| 可隔离 | 子任务只需要一小段输入，不依赖完整上下文 | 只给某个文件路径、某个 request_id、某个候选集合 |
| 读多写少 | 子任务主要是检索、阅读、总结、验证 | Explore / Plan / verification |
| 输出可压缩 | 子 Agent 结果能用结构化摘要交回主 Agent | 输出发现、证据、风险、建议 |
| 价值足够高 | 多消耗 token 和时间是值得的 | 线上事故排查、复杂数据策略、发布前验证 |

典型场景：

- 多个 request_id / session / badcase 可以独立排查。
- 多个文件目录需要分别探索，再合并成架构判断。
- 一个任务需要先从多个候选方向召回证据。
- 非平凡代码修改后，需要 verification agent 独立跑测试和检查。
- 上下文窗口快满，且有一个清晰阶段可以交给新上下文继续探索。

### 4.2 可以使用，但需要谨慎

这些场景可以用子 Agent，但必须限制边界：

- 需要生成多个候选方案，但最后只能采用一个方案。
- 需要多个 Agent 分别给 review 意见，但最终修改只能由主 Agent 做。
- 需要长时间跑工具或搜索，但要避免子 Agent 自己继续扩散任务。
- 需要子 Agent 写临时文件，必须限定在当前 session 的 scratchpad 或 output，并明确 `allow_write=true`。

### 4.3 不应该使用

这些场景默认不要开子 Agent：

| 场景 | 原因 |
|---|---|
| 一次普通问答或一次工具调用 | 额外开 Agent 只会增加延迟和 token |
| 强依赖连续上下文 | 子 Agent 容易拿不到主会话里的隐含状态 |
| 子任务之间依赖链很长 | 调度成本高，失败恢复复杂 |
| 多个 Agent 同时写同一份产物 | 容易冲突，合并困难 |
| 需要和用户持续互动 | 子 Agent 的交互会割裂主会话体验 |
| 主 Agent 自己还没想清任务边界 | 边界不清时拆出去只会放大混乱 |
| 业务流程已有明确 Skill | 应先用 Skill，而不是直接用通用子 Agent |

## 5. 子 Agent 调用前检查表

主 Agent 在调用 `Agent` 前，应在内部完成这个检查：

```text
1. 这个任务能否直接用一个工具完成？
   能 -> 不开子 Agent。

2. 这个任务是否命中明确 Skill？
   是 -> 先调用 Skill，除非 Skill 内明确建议拆子 Agent。

3. 是否存在独立子任务？
   没有 -> 不开子 Agent。

4. 子任务是否可以用少量输入描述清楚？
   不能 -> 不开子 Agent，先由主 Agent 整理上下文。

5. 子任务结果是否能用结构化摘要交回？
   不能 -> 不开子 Agent。

6. 子任务是否主要是读、查、分析、验证？
   是 -> 适合。
   否 -> 需要明确写权限、路径和合并策略。

7. 是否有并行收益或上下文隔离收益？
   没有 -> 不开子 Agent。
```

## 6. 子 Agent Prompt 契约

每次委托都应该给子 Agent 一个完整契约。不要只写“帮我看看这个”。

推荐格式：

```text
任务目标：
  你要完成什么，不要做什么。

输入材料：
  文件路径、request_id、query、候选数据、时间范围等。

上下文边界：
  只依赖这些输入；不要假设完整主会话上下文。

允许动作：
  只读 / 可运行命令 / 可写临时文件 / 可写 output。

禁止动作：
  不修改平台代码；不提交 git；不改非本 session 文件。

输出格式：
  用固定字段返回，例如 findings、evidence、risks、next_actions。

失败处理：
  找不到数据时说明尝试过的路径和下一步建议。
```

示例：

```text
请作为只读 Explore 子 Agent，分析 request_id=xxx 的线上日志。
只允许读取 ELK 查询结果和当前 session 文件，不写文件。
目标是判断是否能还原 query、prev_session、模型 prompt、模型输出。
输出 JSON：
{
  "request_id": "...",
  "found_fields": [],
  "missing_fields": [],
  "evidence": [],
  "recommended_next_query": ""
}
```

## 7. 和 Skill 的组合方式

### 7.1 Skill 内可以建议子 Agent，但不应该滥用

Skill 可以在流程里写：

- 当候选样本超过一定数量，可以用 Explore 子 Agent 并行抽样。
- 当多个 rid 独立排查，可以每个 rid 一个子 Agent。
- 当生成数据前需要多标签边界 review，可以让多个只读子 Agent 分别分析边界。

但 Skill 不应该写：

- 每次执行都必须开子 Agent。
- 所有生成都交给子 Agent。
- 让多个子 Agent 同时写最终数据。

### 7.2 主 Agent 负责最终合成

推荐模式：

```text
子 Agent A：查证据
子 Agent B：查另一批证据
子 Agent C：做只读验证
  -> 主 Agent 汇总
  -> 主 Agent 决定下一步
  -> 主 Agent 调脚本写最终产物
```

不推荐：

```text
子 Agent A 写一半数据
子 Agent B 写另一半数据
子 Agent C 修改格式
  -> 主 Agent 被动猜测哪个文件是最终版
```

## 8. UI 和可观测性原则

子 Agent 不应该像普通会话一样挤进左侧列表。用户关心的是：

- 主任务有没有拆子任务。
- 拆了几个。
- 每个子任务是否完成、失败、跳过。
- 每个子任务的关键发现是什么。
- 必要时能 drill down 到子会话详情。

因此 UI 默认应该：

- 左侧会话列表只显示主会话。
- 右侧活动区展示子 Agent 汇总、批次、子任务摘要。
- 子 Agent 详情默认折叠。
- 子 Agent 输出应该有结构化摘要，不展示完整长日志。

## 9. 后续增强方向

### P0：收敛提示词和工具描述

- 在系统提示词里明确“子 Agent 不是默认策略”。
- 把“适合/不适合”的判断表加入子 Agent 指导。
- `Agent` 工具描述中补充：优先用于独立、可并行、长耗时、读多写少的子任务。
- 对 product-data、online-mining-v2、label-master 的 Skill 文档增加是否建议子 Agent 的边界。

### P1：结构化委托和活动展示

- 为 `Agent` 参数增加更明确的 `output_contract` 或 `expected_fields`。
- 活动区展示子任务输入、状态、摘要、失败原因。
- 支持从活动区打开 child session，但默认隐藏。
- 对运行中的子 Agent 做更稳定的 live event 汇总。

### P2：策略控制和评估

- 增加每轮最多子 Agent 数量、并发数、总 token 预算。
- 增加“只读子 Agent”默认模式。
- 记录每次子 Agent 的收益：是否减少主任务耗时、是否提升结果质量、是否造成失败。
- 建立小型 eval：同一任务单 Agent vs 子 Agent，对比准确率、耗时、token、用户体验。

### P3：业务化多 Agent

- online-mining-v2：多个 rid / 多个索引方向并行探索。
- label-master：多个候选标签边界并行查证，然后主 Agent 汇总裁决。
- product-data：批量数据质量 review 可并行，但最终 records 写入必须由主 Agent 或确定性脚本完成。

## 10. 当前建议

短期不要把多 Agent 做成自动默认能力。更稳的路线是：

```text
默认：主 Agent + Skill + Tool
遇到明确并行探索 / 独立验证 / 长耗时检索
  -> 主 Agent 显式调用子 Agent
  -> 子 Agent 只返回结构化摘要
  -> 主 Agent 合成、写入、回复
```

一句话原则：

```text
子 Agent 负责把复杂任务拆成可独立完成的观察和验证；
主 Agent 负责保持用户上下文、做最终决策和产物写入。
```
