105 行代码激活多智能体:一次需求审计引发的架构觉醒
我们以为自己有多智能体协作,逐行核对需求文档后才发现——我们只是在给 SDK 的实验功能当展示层。修复只需要 105 行代码,但找到这 105 行代码的路,走了两周。
故事的起点:一次例行的需求文档核对
我在整理 AgentZero(一款 Agent-First 的桌面 AI 工作台)的产品需求文档。流程很简单——逐条读文档声明,然后 grep 代码验证。
核对到 3.2 Agent Teams(多 Agent 协作) 时,文档写着:
用户指令 → 主 Agent 分析 → 创建 Team
├── Agent A: 代码分析
├── Agent B: 测试覆盖率检查
└── Agent C: 安全审计
↓
邮箱通信 + 任务依赖图
↓
汇总结果 → 用户
看起来很完善。但当我打开 agent-team-service.ts 时:
/**
* Agent Team Service — 文件系统轮询
*
* 从 SDK 工作区目录读取团队任务状态和 Agent 间邮箱消息。
* SDK 运行时会将任务/通信数据写入工作区的约定目录:
* - .claude/tasks/ — 任务 JSON 文件
* - .claude/mailbox/ — 邮箱 JSON 文件
*
* 渲染进程通过 IPC 每 2s 调用 getAgentTeamData() 轮询最新状态。
*/
只有读取,没有写入。
继续搜:
grep -r "createTeam\|spawn.*agent\|subAgent\|dispatch.*task" apps/electron/src/main/
# 结果:无匹配
再看 orchestrator 怎么激活 Teams:
// agent-orchestrator.ts
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: '1',
CLAUDE_CODE_ENABLE_TASKS: 'true',
真相浮出水面:AgentZero 没有实现任何多智能体编排逻辑。 它只是:
- 设了两个环境变量,开启 SDK 的实验功能
- 每 2 秒轮询 SDK 写入的文件
- 渲染到 UI 上
我们以为自己有的"Agent Teams",只是一个展示层。
竞品也一样:读源码比读官网有用
带着这个发现,我立刻去看了竞品 Proma 的源码。
// Proma 的 agent-orchestrator.ts:1163
agents: buildBuiltinAgents(),
Proma 比我们多做了一步。 虽然它也没有自己实现编排,但它注册了 3 个内置子代理:
export function buildBuiltinAgents(): Record<string, AgentDefinition> {
return {
'code-reviewer': {
description: '代码审查子代理...',
prompt: '你是一个专注于代码质量的审查员...',
tools: ['Read', 'Glob', 'Grep', 'Bash'],
model: 'haiku',
},
'explorer': { /* ... */ },
'researcher': { /* ... */ },
}
}
这意味着 Proma 的主 Agent 知道自己有"帮手"可以调用。而 AgentZero 的主 Agent——什么都不知道。
然后我又研究了 Claude Code 的源码。那才是真正的多智能体:
- Fork Subagent:上下文继承,prompt cache 共享,同步执行
- Swarm Teammates:独立进程/pane,文件锁任务系统,mailbox 通信
- AsyncLocalStorage 隔离:in-process 多 agent 并发安全
三个项目的差距一目了然:
| 项目 | 多智能体实现 |
|---|---|
| Claude Code | 完整自主编排(Fork + Swarm) |
| Proma | SDK 代理 + 注册 3 个 subagent |
| AgentZero | SDK 代理 + 什么都没注册 |
根因:SDK 给了枪,我们忘了装弹
回到 Claude Agent SDK 的 API:
import { query, type AgentDefinition } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt: "Review this PR for security issues",
options: {
allowedTools: ["Read", "Grep", "Glob", "Agent"], // ← 允许调用 Agent 工具
agents: { // ← 注册可用的子代理
"security-reviewer": createSecurityAgent("strict")
}
}
})) {
if ("result" in message) console.log(message.result);
}
关键在两个参数:
allowedTools包含"Agent"→ 主 Agent 知道自己可以 spawn subagentagents注册子代理定义 → 主 Agent 知道有哪些帮手、各自的能力边界
AgentZero 的 queryOptions 里两个都没有:
// AgentZero 之前的代码
const queryOptions = {
pathToClaudeCodeExecutable: cliPath,
model: modelId,
cwd: workspacePath,
// ... 权限、系统提示、MCP 配置
// ❌ 没有 agents
// ❌ 没有 allowedTools
}
我们设了 CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 告诉 SDK "开启 Teams 功能",但没有注册任何子代理定义。 相当于买了一把枪但忘了装弹。
修复:105 行代码
知道问题在哪之后,修复极其简单。
文件 1:agent-prompt-builder.ts — 定义子代理(+100 行)
export function buildBuiltinAgents(): Record<string, AgentDefinition> {
return {
'code-reviewer': {
description: '代码审查子代理。审查代码质量、发现潜在问题。',
prompt: `你是一个专注于代码质量的审查员...`,
tools: ['Read', 'Glob', 'Grep', 'Bash'], // 只读工具,不能改代码
model: 'haiku', // 成本约为 sonnet 的 1/10
},
'explorer': { /* 代码库探索 */ },
'researcher': { /* 技术调研 */ },
'security-auditor': { /* 安全审计 */ },
}
}
文件 2:agent-orchestrator.ts — 注册到 SDK(+5 行)
import { buildBuiltinAgents } from './agent-prompt-builder.ts'
const queryOptions = {
// ... 现有配置不变
// 内置 SubAgent 定义
agents: buildBuiltinAgents(),
}
就这样。4 个子代理,全部用 haiku 模型,全部限制为只读工具。
为什么之前没发现?
回头看,有三个认知盲区:
1. 文档自欺
需求文档写着"Agent Teams — 多 Agent 协作",加上 UI 确实能显示 Team Panel、Task Board、Agent Cards,看起来功能是完整的。但实际上主 Agent 从来不会主动创建子代理,因为它根本不知道有这个选项。
教训:文档描述的是"系统可以做什么",不是"系统真的在做什么"。
2. 环境变量 ≠ 功能
设了 CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 就以为 Teams 功能激活了。实际上这只是告诉 SDK "你可以支持 Teams",但没告诉它"你有哪些 agent 可以用"。
教训:开关 ≠ 弹药。
3. 没有读竞品源码
如果早点看 Proma 的 buildBuiltinAgents(),或者看 SDK 的 AgentDefinition 类型定义,几分钟就能发现问题。但我们把时间花在了"看竞品的功能演示"而不是"看竞品的代码实现"。
教训:读源码比读文档、看 demo 都管用。
设计决策:为什么子代理全用 haiku?
这不是偷懒,是刻意的设计:
| 决策 | 理由 |
|---|---|
| haiku 模型 | 成本约为 sonnet 的 1/10。子代理做的是搜索和分析,不需要最强推理 |
| 只读工具 | 子代理只能读代码,不能写。防止"帮手"意外改坏东西 |
| 明确输出格式 | 每个 agent 的 prompt 规定了输出结构(如 🔴/🟡/🟢 分级),主 Agent 容易消化 |
| 4 个而非 10 个 | YAGNI。code-reviewer + explorer + researcher + security-auditor 覆盖了 90% 的辅助场景 |
这个策略和 Proma 几乎一样——因为在 SDK 驱动的架构下,这就是最优解。真正的差异化要等自主编排(Phase 2)才能实现。
更大的图景:三层多智能体
这次修复只是第一层。完整的多智能体路线图是:
Layer 1: SDK 注册(已完成 ✅)
→ 注册 AgentDefinition,让 SDK 主 Agent 可以调用子代理
→ 成本:105 行代码
→ 效果:主 Agent 变得"更积极"地使用 subagent
Layer 2: AZ 原生编排(规划中)
→ 不依赖 SDK 实验特性,AZ 自主管理 subagent 生命周期
→ 参考 Claude Code 的 in-process + AsyncLocalStorage 模型
→ 效果:不受 SDK 变更影响,支持非 Anthropic 模型
Layer 3: 分布式协作(远期)
→ 多设备 Agent 协作
→ 效果:真正的 Agent 工作团队
当前所有竞品(Proma、Conductor、Cowork)都停在 Layer 1。谁先做到 Layer 2,谁就有真正的护城河。
总结
| 数字 | |
|---|---|
| 发现问题的时间 | 需求核对第 3.2 节时 |
| 根因定位的时间 | 读了 3 个项目的源码后 |
| 修复代码量 | 105 行(2 个文件) |
| 核心改动 | 1 个 import + 1 行 agents: buildBuiltinAgents() |
| 之前遗漏的原因 | 文档自欺 + 环境变量幻觉 + 没读竞品源码 |
最大的感悟: 有时候功能不是"没实现",而是"没激活"。SDK 已经给了完整的多智能体基础设施,我们只是忘了告诉它——你有帮手。