第 10 / 15 章 约 7 分钟
Multi-Agent 协作
拆分子任务,处理上下文隔离、并发和失败。
内容同步:2026年10月25日 · 文档版本 6
这个模块在 Agent 里的位置
SubAgent 不是“多调几个模型”,而是上下文隔离和职责拆分。
它解决的是:
- 大任务导致 parent 上下文膨胀。
- 不同子任务需要不同工具和模型。
- 检索、分析、校验可以并行。
- 某个子任务失败不应拖垮全部结论。
如果只是让多个 Agent 各说一段话再拼接,通常只会增加成本和失败面。
先记住几个概念
| 概念 | 含义 | 为什么重要 |
|---|---|---|
| Parent Agent | 负责任务拆分和综合 | 保存任务主线 |
| Child Agent | 负责一个受限子任务 | 隔离上下文和工具 |
| parentSummary | child 给 parent 的结论摘要 | 控制 parent 上下文 |
| refs | 稳定引用 ID | 支持证据校验 |
| Terminal status | completed / failed / cancelled | 防止子任务被静默丢弃 |
| Concurrency cap | 并发上限 | 控制资源和成本 |
| Tool allowlist | child 可用工具 | 缩小攻击面和误用面 |
从一个真实问题开始:parent 被三个 child 的“完整过程”淹没
任务:
分析 30 天、50 万实体,输出 Top 10 风险实体和证据。
系统拆了三个 child:
- Child A:Top 实体检索。
- Child B:证据窗口分析。
- Child C:引用一致性校验。
每个 child 都把完整推理过程返回给 parent:
{
"reasoning": [
"我先查了 50 万实体",
"然后过滤了 3 万条",
"又看了 20 个窗口",
"最后得出结论"
],
"toolResults": [...]
}
结果:
- parent 上下文迅速膨胀。
- parent 也触发 compaction。
- 引用被摘要丢失。
- parent 重复查询。
这说明 child 的价值不在“多一个模型”,而在隔离探索成本,只回传可消费的结论。
child 应该返回什么
一个合格的 child 输出:
{
"parentSummary": "Top 10 实体中 3 个风险显著上升",
"refs": ["win-1001", "win-1002"],
"confidence": 0.86,
"unresolvedQuestions": []
}
不应返回:
- 完整推理过程。
- 全量工具结果。
- 原始 JSON。
- 和 parent 无关的中间状态。
什么时候适合拆 SubAgent
适合拆的条件:
- 子任务上下文天然隔离。
- 子任务可以独立校验。
- parent 只需要结论。
- 子任务可以并行。
- 失败状态可以单独表达。
不适合的情况:
| 场景 | 不适合的原因 |
|---|---|
| 任务很短 | 调度成本大于收益 |
| 强依赖同一上下文 | 拆开会丢共享状态 |
| 输出只是文本拼接 | 没有隔离收益 |
| 需要实时人机交互 | 子任务会引入延迟和状态同步 |
每个 child 必须有独立边界
一个 child 至少要有:
- 独立 session。
- 独立 tool allowlist。
- 独立 timeout。
- 独立 output schema。
- 独立 usage 记录。
- 独立失败状态。
例如:
| Child | 工具 | 模型 | timeout |
|---|---|---|---|
| Top 实体检索 | query_top_risk_entities | fast | 60s |
| 证据分析 | query_evidence_windows | main | 120s |
| 引用校验 | get_source_by_id | fast | 30s |
不要让 child 继承 parent 的全部工具,尤其是写工具。
数据库设计
create table agent_subtask_runs (
id bigint primary key,
parent_run_id bigint not null,
agent_name text not null,
status text not null,
input_ref text not null,
output_ref text,
started_at timestamptz not null,
ended_at timestamptz,
unique (parent_run_id, agent_name)
);
create index agent_subtask_parent_idx
on agent_subtask_runs (parent_run_id, status);
设计要点:
- parent 和 child 分表。
- agent_name 唯一,避免同一子任务重复执行。
- status 必须有终态。
- input/output 存对象存储指针,不塞 JSONB。
child 失败不能被静默丢弃
一个典型错误:
{
"parentSummary": "已生成报告",
"children": [
{ "agent": "A", "status": "completed" },
{ "agent": "B", "status": "failed" }
]
}
如果 parent 忽略 B 的失败,报告看起来完成了,但缺少证据依据。
正确做法:
- parent 收到 failed child。
- 报告中明确“证据分析缺失”。
- 输出 unresolvedQuestions。
- 不把缺失写成零。
防止 child 重复查询
三个 child 同时查同一时间窗,是常见浪费。
解决方式:
- parent 先做共享事实查询。
- 把共享 refs 传给 child。
- child 只做各自专项分析。
- 相同工具调用可以共享 cache。
示例:
{
"sharedFocusWindows": [
{
"windowId": "win-1001",
"reason": "风险峰值"
}
]
}
child 基于共享窗口继续分析,而不是各自重新查一遍。
取消必须传播到 child
parent 被取消时,不能只取消 parent 自己。
必须:
- 停止所有 child。
- 释放 child 持有的工具和资源。
- 将 child 标记为 cancelled。
- 阻断 pending action。
否则会出现 parent 已取消,child 还在后台继续查库和耗 token。
和传统后端的关系
Multi-Agent 协作可以类比成后端里的服务编排:
| Agent 里的概念 | 传统后端里的对应物 |
|---|---|
| Parent Agent | 编排服务 / workflow engine |
| Child Agent | 子任务 worker |
| parentSummary | RPC 返回值 |
| refs | 共享主键 / 外键引用 |
| Concurrency cap | 线程池 / worker pool |
| Child failure | 子任务失败 / 熔断 |
区别在于,child 的输出不仅要给人看,还要能进入 parent 的模型上下文,所以必须额外控制输出大小和引用稳定性。
常见工程错误
| 错误 | 实际后果 | 正确做法 |
|---|---|---|
| child 返回完整过程 | parent 上下文膨胀 | parentSummary + refs |
| child 无 output schema | parent 无法校验 | 强制 schema |
| child 失败被忽略 | 报告缺依据 | terminal status + unresolvedQuestions |
| child 继承全部工具 | 越权和误用 | tool allowlist |
| child 重复查询 | 成本浪费 | shared refs / cache |
| 无并发上限 | 资源失控 | max concurrent |
| parent 取消不传播 | child 继续执行 | AbortSignal |
上线前检查
- 每个 child 有明确职责。
- 输出 schema 强制。
- child 有 timeout。
- 失败不能静默丢弃。
- parent 可取消 child。
- 并发有上限。
- 共享数据通过 refs 传递。
- 每个 child 独立记录 usage 和工具调用。