← 返回文章列表← Back to posts

105 行代码激活多智能体:一次需求审计引发的架构觉醒105 Lines to Unlock Multi-Agent: How a Requirements Audit Revealed an Architectural Blind Spot

逐行核对 AgentZero 需求文档时发现——我们以为的多智能体协作只是 SDK 的展示层。读竞品源码找到根因,105 行代码完成修复。Line-by-line requirements audit revealed AgentZero's 'multi-agent' was just a display layer for the SDK. Reading competitor source code found the root cause — fixed in 105 lines.

·12 分钟阅读min read

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 没有实现任何多智能体编排逻辑。 它只是:

  1. 设了两个环境变量,开启 SDK 的实验功能
  2. 每 2 秒轮询 SDK 写入的文件
  3. 渲染到 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 subagent
  • agents 注册子代理定义 → 主 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 已经给了完整的多智能体基础设施,我们只是忘了告诉它——你有帮手。