Claude Code 多智能体协作实战:用子任务编排构建复杂工程项目的完整指南

从单体 Prompt 到多 Agent 协作,手把手教你用 Claude Code 的 Agent 工具链完成真实项目

一、为什么需要多智能体协作?

随着项目复杂度的提升,单一 AI 对话窗口的局限性越来越明显。当你需要同时处理代码编写、测试生成、文档撰写、代码审查等多个环节时,一个 Agent 往往会出现上下文丢失、任务遗忘等问题。

Claude Code 提供了一套完整的 Agent 工具链(Task 工具),支持在主对话中派生子 Agent 独立执行子任务,主 Agent 负责编排和汇总。这种多智能体协作模式,本质上是一种主从式任务分发架构

核心优势

特性单 Agent 模式多 Agent 协作
上下文管理单一长上下文,容易溢出每个子任务独立上下文
并行能力串行执行可并行派发多个子任务
错误隔离一个环节出错影响全局子任务失败不影响其他任务
代码量限制长任务输出截断风险高子任务输出更精简可控

二、Claude Code 子 Agent 架构详解

Claude Code 中的 Task 工具支持三种子 Agent 类型:

  • general_purpose_task — 通用编程子 Agent,可执行完整的编码任务
  • Explore — 只读探索型 Agent,用于搜索、分析、调研
  • Plan — 规划型 Agent,用于设计实现方案

2.1 基本调用原理

当你在 Claude Code 中描述一个复杂任务时,Claude 会自动(或被指示)将任务拆解为多个子任务,每个子任务通过 Task 工具派发给子 Agent:

主 Agent (编排层)
├── 子 Agent A (general_purpose_task) → 编写核心业务逻辑
├── 子 Agent B (general_purpose_task) → 编写测试用例
├── 子 Agent C (Explore) → 分析现有代码结构
└── 主 Agent → 汇总结果,处理集成

2.2 Claude Code Hooks 实现自动化工作流

Claude Code 支持 Hooks 机制,可以在特定事件触发时自动执行脚本。这是构建自动化流水线的关键。

项目配置文件 .claude/settings.json

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "python3 scripts/lint_check.py \"$FILE\""
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "cd /path/to/project && pytest tests/ -x -q 2>/dev/null || echo 'Tests not found'"
          }
        ]
      }
    ],
    "Notification": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Task completed\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

三、实战案例:构建一个 REST API 项目

我们将用多 Agent 协作的方式,从零构建一个带认证的 REST API 服务。

3.1 项目结构

fastapi-auth-service/
├── app/
│   ├── __init__.py
│   ├── main.py          # 主入口
│   ├── auth/
│   │   ├── __init__.py
│   │   ├── router.py    # 认证路由
│   │   ├── service.py   # 认证服务逻辑
│   │   └── models.py    # 数据模型
│   ├── users/
│   │   ├── __init__.py
│   │   ├── router.py
│   │   ├── service.py
│   │   └── models.py
│   └── database.py
├── tests/
│   ├── test_auth.py
│   ├── test_users.py
│   └── conftest.py
├── .claude/
│   └── settings.json
├── requirements.txt
├── Dockerfile
└── README.md

3.2 步骤一:用 Explore Agent 分析需求

首先让 Explore Agent 理清项目依赖和最佳实践:

【子任务指令示例 - 发给 Explore Agent】
调研 FastAPI + JWT 认证的最佳实践方案,包括:
1. 推荐的依赖库版本(fastapi, python-jose, passlib, sqlalchemy)
2. 项目目录结构最佳实践
3. JWT token 签发与验证的安全配置
4. 密码哈希方案选择(bcrypt vs argon2)
返回完整的调研报告。

3.3 步骤二:用 general_purpose_task Agent 编写核心代码

数据模型(app/auth/models.py)

from datetime import datetime
from sqlalchemy import Column, String, DateTime, Boolean
from sqlalchemy.orm import declarative_base
import uuid

Base = declarative_base()

class User(Base):
    __tablename__ = "users"

    id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
    username = Column(String(50), unique=True, nullable=False, index=True)
    email = Column(String(255), unique=True, nullable=False, index=True)
    hashed_password = Column(String(255), nullable=False)
    is_active = Column(Boolean, default=True)
    is_superuser = Column(Boolean, default=False)
    created_at = Column(DateTime, default=datetime.utcnow)
    updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

class RefreshToken(Base):
    __tablename__ = "refresh_tokens"

    id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
    user_id = Column(String(36), nullable=False, index=True)
    token = Column(String(512), nullable=False, unique=True)
    expires_at = Column(DateTime, nullable=False)
    created_at = Column(DateTime, default=datetime.utcnow)
    is_revoked = Column(Boolean, default=False)

认证服务(app/auth/service.py)

from datetime import datetime, timedelta
from typing import Optional
from jose import JWTError, jwt
from passlib.context import CryptContext
import secrets
import uuid

SECRET_KEY = "your-secret-key-change-in-production"  # 生产环境请从环境变量读取
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
REFRESH_TOKEN_EXPIRE_DAYS = 7

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

class AuthService:
    def __init__(self, secret_key: str = SECRET_KEY):
        self.secret_key = secret_key

    def hash_password(self, plain_password: str) -> str:
        """密码哈希"""
        return pwd_context.hash(plain_password)

    def verify_password(self, plain_password: str, hashed_password: str) -> bool:
        """密码验证"""
        return pwd_context.verify(plain_password, hashed_password)

    def create_access_token(self, user_id: str, is_superuser: bool = False) -> str:
        """创建 JWT Access Token"""
        expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
        payload = {
            "sub": user_id,
            "is_superuser": is_superuser,
            "type": "access",
            "exp": expire,
            "jti": str(uuid.uuid4())
        }
        return jwt.encode(payload, self.secret_key, algorithm=ALGORITHM)

    def create_refresh_token(self) -> dict:
        """创建 Refresh Token(存数据库的 opaque token + JWT 编码)"""
        token_value = secrets.token_urlsafe(48)
        return {
            "token": token_value,
            "expires_at": datetime.utcnow() + timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS),
            "jti": str(uuid.uuid4())
        }

    def decode_token(self, token: str) -> Optional[dict]:
        """解码并验证 JWT Token"""
        try:
            payload = jwt.decode(token, self.secret_key, algorithms=[ALGORITHM])
            return payload
        except JWTError:
            return None

# 全局实例
auth_service = AuthService()

认证路由(app/auth/router.py)

from fastapi import APIRouter, HTTPException, Depends, status
from pydantic import BaseModel, EmailStr
from typing import Optional

router = APIRouter(prefix="/api/auth", tags=["认证"])

class RegisterRequest(BaseModel):
    username: str
    email: EmailStr
    password: str

class LoginRequest(BaseModel):
    username: str
    password: str

class TokenResponse(BaseModel):
    access_token: str
    refresh_token: str
    token_type: str = "bearer"

class RefreshRequest(BaseModel):
    refresh_token: str

@router.post("/register", response_model=TokenResponse, status_code=status.HTTP_201_CREATED)
async def register(request: RegisterRequest):
    """用户注册"""
    from .service import auth_service
    from ..database import get_db

    db = next(get_db())

    # 检查用户名是否已存在
    existing_user = db.query(User).filter(
        (User.username == request.username) | (User.email == request.email)
    ).first()
    if existing_user:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="用户名或邮箱已存在"
        )

    # 创建用户
    user = User(
        username=request.username,
        email=request.email,
        hashed_password=auth_service.hash_password(request.password)
    )
    db.add(user)
    db.commit()
    db.refresh(user)

    # 生成 token
    access_token = auth_service.create_access_token(user.id)
    refresh_data = auth_service.create_refresh_token()

    return TokenResponse(
        access_token=access_token,
        refresh_token=refresh_data["token"]
    )

@router.post("/login", response_model=TokenResponse)
async def login(request: LoginRequest):
    """用户登录"""
    from .service import auth_service
    from ..database import get_db

    db = next(get_db())
    user = db.query(User).filter(User.username == request.username).first()

    if not user or not auth_service.verify_password(request.password, user.hashed_password):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="用户名或密码错误",
            headers={"WWW-Authenticate": "Bearer"}
        )

    if not user.is_active:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="账户已被禁用"
        )

    access_token = auth_service.create_access_token(user.id, user.is_superuser)
    refresh_data = auth_service.create_refresh_token()

    return TokenResponse(
        access_token=access_token,
        refresh_token=refresh_data["token"]
    )

@router.post("/refresh", response_model=TokenResponse)
async def refresh_token(request: RefreshRequest):
    """刷新 Token"""
    from .service import auth_service
    from ..database import get_db

    db = next(get_db())
    stored_token = db.query(RefreshToken).filter(
        RefreshToken.token == request.refresh_token,
        RefreshToken.is_revoked == False,
        RefreshToken.expires_at > datetime.utcnow()
    ).first()

    if not stored_token:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Refresh Token 无效或已过期"
        )

    # 撤销旧 token
    stored_token.is_revoked = True
    db.commit()

    # 生成新 token
    access_token = auth_service.create_access_token(stored_token.user_id)
    refresh_data = auth_service.create_refresh_token()

    return TokenResponse(
        access_token=access_token,
        refresh_token=refresh_data["token"]
    )

3.4 步骤三:用另一个子 Agent 编写测试

【子任务指令示例 - 发给 general_purpose_task Agent】
基于 app/auth/router.py 和 app/auth/service.py 编写完整的 pytest 测试:
1. test_auth.py - 覆盖注册、登录、Token刷新、错误处理
2. conftest.py - 提供测试数据库 fixture 和测试客户端
3. 使用 SQLite 内存数据库进行测试
4. 确保测试覆盖率 > 80%

测试文件(tests/test_auth.py)

import pytest
from fastapi.testclient import TestClient
from app.main import app
from app.database import engine, Base, get_db
from sqlalchemy.orm import sessionmaker

TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

def override_get_db():
    db = TestingSessionLocal()
    try:
        yield db
    finally:
        db.close()

app.dependency_overrides[get_db] = override_get_db
client = TestClient(app)

@pytest.fixture(autouse=True)
def setup_db():
    """每个测试前重建数据库"""
    Base.metadata.create_all(bind=engine)
    yield
    Base.metadata.drop_all(bind=engine)

def test_register_success():
    """测试正常注册"""
    response = client.post("/api/auth/register", json={
        "username": "testuser",
        "email": "test@example.com",
        "password": "SecurePass123!"
    })
    assert response.status_code == 201
    data = response.json()
    assert "access_token" in data
    assert "refresh_token" in data
    assert data["token_type"] == "bearer"

def test_register_duplicate_username():
    """测试重复用户名注册"""
    client.post("/api/auth/register", json={
        "username": "dupuser",
        "email": "dup1@example.com",
        "password": "SecurePass123!"
    })
    response = client.post("/api/auth/register", json={
        "username": "dupuser",
        "email": "dup2@example.com",
        "password": "SecurePass123!"
    })
    assert response.status_code == 400
    assert "已存在" in response.json()["detail"]

def test_login_success():
    """测试正常登录"""
    # 先注册
    client.post("/api/auth/register", json={
        "username": "loginuser",
        "email": "login@example.com",
        "password": "LoginPass123!"
    })
    # 再登录
    response = client.post("/api/auth/login", json={
        "username": "loginuser",
        "password": "LoginPass123!"
    })
    assert response.status_code == 200
    assert "access_token" in response.json()

def test_login_wrong_password():
    """测试错误密码"""
    client.post("/api/auth/register", json={
        "username": "wronguser",
        "email": "wrong@example.com",
        "password": "CorrectPass123!"
    })
    response = client.post("/api/auth/login", json={
        "username": "wronguser",
        "password": "WrongPass123!"
    })
    assert response.status_code == 401

def test_login_nonexistent_user():
    """测试不存在的用户"""
    response = client.post("/api/auth/login", json={
        "username": "ghost",
        "password": "NoPass123!"
    })
    assert response.status_code == 401

def test_refresh_token():
    """测试 Token 刷新"""
    reg_resp = client.post("/api/auth/register", json={
        "username": "refreshuser",
        "email": "refresh@example.com",
        "password": "RefreshPass123!"
    })
    refresh_token = reg_resp.json()["refresh_token"]

    response = client.post("/api/auth/refresh", json={
        "refresh_token": refresh_token
    })
    assert response.status_code == 200
    new_tokens = response.json()
    assert "access_token" in new_tokens
    # 旧 token 应该失效
    old_response = client.post("/api/auth/refresh", json={
        "refresh_token": refresh_token
    })
    assert old_response.status_code == 401

def test_register_short_password():
    """测试弱密码拒绝"""
    response = client.post("/api/auth/register", json={
        "username": "weakuser",
        "email": "weak@example.com",
        "password": "123"
    })
    # 密码过短应该被 Pydantic 验证拦截
    assert response.status_code == 422

四、Claude Code 高级技巧

4.1 Claude Code 命令行快捷操作

# 在项目根目录初始化 Claude Code
claude

# 直接执行单条指令(非交互模式)
claude -p "分析 src/ 目录下的代码质量,输出改进建议"

# 从文件读取 prompt 执行
claude -p "$(cat prompts/refactor-task.md)"

# 使用特定模型
claude --model claude-sonnet-4-20250514

# 查看 Claude Code 设置
claude config list

4.2 MCP 工具集成实战

Claude Code 原生支持 MCP(Model Context Protocol),可以连接外部数据源。以下是一个连接 GitHub MCP Server 的配置:

.claude/settings.json

{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_TOKEN": "${GITHUB_TOKEN}"
      }
    },
    "database": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-postgres", "postgresql://localhost/mydb"]
    }
  }
}

配置后,Claude Code 的 Agent 可以直接查询 GitHub Issues、PR、数据库数据等。

4.3 上下文管理技巧

Claude Code 支持通过 / 命令快速管理上下文:

# 在 Claude Code 交互中:
/add file.py           # 将文件添加到上下文
/add src/**/*.py      # 批量添加目录
/remove file.py       # 移除文件
/clear                 # 清除对话历史
/compact               # 压缩对话历史(保留关键信息)
/model                 # 切换模型

4.4 多文件编辑的自动化工作流

当你需要批量修改多个文件时,Claude Code 的 Agent 可以自动编排:

【给 Claude Code 的指令】
我需要将项目中所有的 print() 调试语句替换为 logging 调用。
请执行以下步骤:
1. 先用 Explore Agent 搜索所有包含 print() 的 Python 文件
2. 用 general_purpose_task Agent 逐文件替换,在每个文件头部添加 logging 配置
3. 运行测试确认没有破坏功能
4. 输出修改清单

五、生产部署方案

5.1 Docker 部署配置

FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8000

CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]
# docker-compose.yml
version: '3.8'
services:
  api:
    build: .
    ports:
      - "8000:8000"
    environment:
      - SECRET_KEY=${SECRET_KEY}
      - DATABASE_URL=postgresql://user:pass@db:5432/authdb
    depends_on:
      - db
  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: authdb
    volumes:
      - pgdata:/var/lib/postgresql/data
volumes:
  pgdata:

5.2 Nginx 反向代理配置

server {
    listen 80;
    server_name api.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name api.example.com;

    ssl_certificate /etc/nginx/ssl/api.example.com.pem;
    ssl_certificate_key /etc/nginx/ssl/api.example.com.key;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # 速率限制
        limit_req zone=api burst=20 nodelay;
    }

    location /docs {
        proxy_pass http://127.0.0.1:8000/docs;
        # 仅内网可访问文档
        allow 10.0.0.0/8;
        deny all;
    }
}

六、常见问题 FAQ

Q1: Claude Code 子 Agent 数量有限制吗?

Claude Code 建议同时运行不超过 3 个子 Agent。每个子 Agent 独立消耗 token,过多并行会导致 API 配额快速消耗。建议将任务按优先级排序,关键路径上的任务优先执行。

Q2: 如何避免子 Agent 之间的文件冲突?

在 Claude Code 的 Task 工具中,可以通过参数指定子 Agent 的工作目录和允许操作的范围。建议不同子 Agent 操作不同目录或文件,最后由主 Agent 进行合并。

Q3: Hooks 执行失败会影响主任务吗?

默认情况下,Hooks 执行失败不会阻断主任务。Claude Code 会记录 Hook 的输出和错误,但会继续执行后续操作。如果你需要严格的行为(如 lint 检查不通过则阻止写入),可以在 Hook 脚本中返回非零退出码。

Q4: 如何在 Claude Code 中使用自定义的 MCP Server?

.claude/settings.json 中配置 mcpServers 字段,指定命令、参数和环境变量。Claude Code 会在启动时自动连接这些 Server,Agent 执行时即可调用 Server 提供的工具。

Q5: Claude Code 支持哪些编程语言?

Claude Code 支持所有主流编程语言,包括 Python、JavaScript/TypeScript、Go、Rust、Java、C/C++ 等。其子 Agent 具备完整的代码生成、编辑和执行能力。

七、总结

Claude Code 的多智能体协作机制为复杂项目开发提供了全新的工作范式。通过合理的任务拆解和 Agent 编排,你可以:

  • 并行处理多个独立子任务,大幅提升开发效率
  • 隔离风险,单个子任务失败不影响全局
  • 保持上下文清晰,每个 Agent 专注自己的职责
  • 利用 Hooks 实现自动化,从代码质量到测试全覆盖

核心要点:

  • 用 Explore Agent 做调研,Plan Agent 做规划,general_purpose_task 做执行
  • 通过 .claude/settings.json 配置 Hooks 和 MCP Servers
  • 子 Agent 间避免操作同一文件,由主 Agent 做最终集成
  • 使用 /compact 管理长对话的上下文消耗
  • Docker + Nginx 部署确保生产环境稳定性