Skip to content

第 23 章:多 Agent 协作 — 从单兵到团队

一个 Agent 处理复杂任务时容易迷失方向、上下文溢出、顾此失彼。多 Agent 把大问题拆给专门的角色——每个 Agent 专注一件事,协调者把结果汇总。

这一章要解决什么问题?

单 Agent 的局限性:

  • 上下文争抢:审查代码安全性和审查代码风格需要完全不同的"思维模式",放在一个上下文里会互相干扰
  • 串行瓶颈:分析 10 个文件的 bug,串行要 10 倍时间
  • 专业化缺失:一个 Agent 既要写代码又要写测试又要做 review,不如让专门的 Agent 各司其职

多 Agent 的核心思想:分解 → 并行 → 汇总


架构模式

Coordinator + Worker 模式

                    ┌─────────────┐
                    │ Coordinator │
                    │ (编排者)     │
                    └──────┬──────┘
              ┌────────────┼────────────┐
              ▼            ▼            ▼
      ┌──────────┐ ┌──────────┐ ┌──────────┐
      │ Worker A │ │ Worker B │ │ Worker C │
      │ (安全审查) │ │ (性能审查) │ │ (风格审查) │
      └──────────┘ └──────────┘ └──────────┘
  • Coordinator:接收用户任务,分解为子任务,分发给 Worker,汇总结果
  • Worker:接收单一明确的子任务,独立执行,返回结果

SubAgent 基础实现

首先定义子代理的执行接口:

typescript
interface SubAgentConfig {
  name: string;
  systemPrompt: string;
  tools: Tool[];
  model?: string;
  timeout?: number;
}

interface SubAgentResult {
  agentName: string;
  output: string;
  success: boolean;
  tokensUsed: number;
}

async function runSubAgent(
  task: string,
  config: SubAgentConfig
): Promise<SubAgentResult> {
  const messages: Message[] = [];
  const startTokens = 0;

  try {
    // 子代理有自己独立的消息历史和 system prompt
    const result = await runAgentLoop(task, messages, {
      systemPrompt: config.systemPrompt,
      tools: config.tools,
      model: config.model ?? "fast-model",
      maxTurns: 20,
    });

    return {
      agentName: config.name,
      output: result.finalResponse,
      success: true,
      tokensUsed: result.totalTokens,
    };
  } catch (err: any) {
    return {
      agentName: config.name,
      output: `Error: ${err.message}`,
      success: false,
      tokensUsed: 0,
    };
  }
}

关键设计:每个子代理有独立的消息历史和 system prompt,不会互相污染上下文。


Coordinator 实现

typescript
interface CoordinatorConfig {
  workers: SubAgentConfig[];
  model: string;
  tools: Tool[];
}

async function runCoordinator(
  userTask: string,
  config: CoordinatorConfig
): Promise<string> {
  // 第 1 步:让 Coordinator 分解任务
  const planPrompt = `You are a task coordinator. Break down this task into subtasks
for specialized workers.

Available workers:
${config.workers.map(w => `- ${w.name}: ${w.systemPrompt.slice(0, 100)}`).join("\n")}

User task: ${userTask}

Return a JSON array of assignments:
[{"worker": "name", "task": "specific subtask description"}]`;

  const planResponse = await callLLM(config.model, planPrompt);
  const assignments: { worker: string; task: string }[] = JSON.parse(planResponse);

  // 第 2 步:并行派发给 Workers
  const workerPromises = assignments.map(async (assignment) => {
    const workerConfig = config.workers.find(w => w.name === assignment.worker);
    if (!workerConfig) {
      return { agentName: assignment.worker, output: "Worker not found", success: false, tokensUsed: 0 };
    }
    return runSubAgent(assignment.task, workerConfig);
  });

  const results = await Promise.allSettled(workerPromises);
  const workerOutputs = results.map(r =>
    r.status === "fulfilled" ? r.value : { agentName: "unknown", output: "Failed", success: false, tokensUsed: 0 }
  );

  // 第 3 步:汇总结果
  const synthesisPrompt = `You are synthesizing results from multiple specialized workers.

Original task: ${userTask}

Worker results:
${workerOutputs.map(r => `### ${r.agentName} (${r.success ? "✓" : "✗"})\n${r.output}`).join("\n\n")}

Synthesize these into a coherent final response for the user.
Resolve any conflicts between worker outputs.
Highlight the most critical findings.`;

  return callLLM(config.model, synthesisPrompt);
}

实例:多 Agent 代码审查

typescript
// 定义三个专门的 Worker
const securityReviewer: SubAgentConfig = {
  name: "security-reviewer",
  systemPrompt: `You are a security expert. Review code for:
- SQL injection, XSS, command injection
- Authentication/authorization flaws
- Secrets in code
- Unsafe deserialization
Report ONLY security issues. Ignore style or performance.`,
  tools: [readFileTool, searchFilesTool, bashTool],
};

const performanceReviewer: SubAgentConfig = {
  name: "performance-reviewer",
  systemPrompt: `You are a performance expert. Review code for:
- O(n²) or worse algorithms
- Unnecessary allocations in hot paths
- Missing indexes in database queries
- Unbounded data structures
Report ONLY performance issues. Ignore style or security.`,
  tools: [readFileTool, searchFilesTool, bashTool],
};

const styleReviewer: SubAgentConfig = {
  name: "style-reviewer",
  systemPrompt: `You are a code style expert. Review code for:
- Naming inconsistencies
- Dead code
- Missing error handling
- Overly complex functions (>50 lines)
Report ONLY style/maintainability issues. Ignore security or performance.`,
  tools: [readFileTool, searchFilesTool],
};

// 使用
const review = await runCoordinator(
  "Review the changes in the last commit for any issues",
  {
    workers: [securityReviewer, performanceReviewer, styleReviewer],
    model: "claude-sonnet-4",
    tools: [],
  }
);

每个 Worker 只关注一个维度,不会互相干扰。Coordinator 汇总时解决冲突、排优先级。


Agent 间通信

更复杂的场景中,Agent 之间需要通信(不只是 Coordinator → Worker 的单向派发):

typescript
interface AgentMessage {
  from: string;
  to: string;
  content: string;
  timestamp: number;
}

class MessageBus {
  private queues: Map<string, AgentMessage[]> = new Map();

  send(msg: AgentMessage): void {
    const queue = this.queues.get(msg.to) ?? [];
    queue.push(msg);
    this.queues.set(msg.to, queue);
  }

  receive(agentName: string): AgentMessage[] {
    const messages = this.queues.get(agentName) ?? [];
    this.queues.set(agentName, []); // 读后清空
    return messages;
  }
}

使用场景:Worker A 发现了一个安全问题,通知 Worker B 检查相关的性能影响。

typescript
// Security worker 发现问题后通知 performance worker
messageBus.send({
  from: "security-reviewer",
  to: "performance-reviewer",
  content: "Found SQL query in auth.ts:42 that needs parameterization. Check if this path is in a hot loop.",
  timestamp: Date.now(),
});

并发控制与资源管理

多 Agent 并行时需要注意资源竞争:

typescript
interface MultiAgentOptions {
  maxConcurrency: number;     // 最多同时跑几个 Agent
  totalBudget: number;        // 所有 Agent 共享的 token 预算
  timeout: number;            // 单个 Agent 超时
}

async function runWorkersParallel(
  assignments: { worker: SubAgentConfig; task: string }[],
  options: MultiAgentOptions
): Promise<SubAgentResult[]> {
  const semaphore = new Semaphore(options.maxConcurrency);
  let totalTokensUsed = 0;

  const results = await Promise.allSettled(
    assignments.map(async ({ worker, task }) => {
      await semaphore.acquire();
      try {
        // 检查共享预算
        if (totalTokensUsed >= options.totalBudget) {
          return { agentName: worker.name, output: "Budget exhausted", success: false, tokensUsed: 0 };
        }

        const result = await runSubAgent(task, {
          ...worker,
          timeout: options.timeout,
        });

        totalTokensUsed += result.tokensUsed;
        return result;
      } finally {
        semaphore.release();
      }
    })
  );

  return results.map(r =>
    r.status === "fulfilled" ? r.value : { agentName: "unknown", output: "Failed", success: false, tokensUsed: 0 }
  );
}

适用场景

场景模式Agent 配置
代码审查并行 WorkersSecurity + Performance + Style
大型重构PipelinePlanner → Implementer → Tester
复杂调试协作Reproducer → Analyzer → Fixer
文档生成并行 + 汇总每个模块一个 Writer → Editor 汇总

什么时候不需要多 Agent

  • 任务简单,单 Agent 几轮就能完成
  • 上下文不会溢出
  • 不需要多维度并行
  • 通信开销大于收益(子任务间强依赖)

经验法则:先用单 Agent 试。当你发现 Agent 在多个维度间来回切换、丢失信息、或者串行太慢时,再拆成多 Agent。


小结

多 Agent 的核心是分解 → 并行 → 汇总。Coordinator 负责理解任务、分解为子任务、分发给专门的 Worker、最后汇总结果。每个 Worker 有独立的上下文和 system prompt,专注一个维度,不互相干扰。并行执行通过信号量控制并发、共享 token 预算防止超支。Agent 间通信通过消息总线实现,支持更复杂的协作模式。关键原则:单 Agent 能搞定的不要用多 Agent——多 Agent 的价值在于专业化分工和并行加速,代价是协调开销和结果合并的复杂性。