**系列**:06-多工具集成实战 **标签**:Git Worktree、Hermes Agent、多Agent并行、前端后端测试 **日期**:2026-05-22

工作树多Agent并行:Git Worktree隔离 + Hermes Spawn 3个独立Agent

系列:06-多工具集成实战 标签:Git Worktree、Hermes Agent、多Agent并行、前端后端测试 日期:2026-05-22

简介

在真实的项目开发中,前端、后端、测试往往是三条并行推进的工作线。当这三个任务由不同的大模型 Agent 分别承担时,如何避免它们互相干扰、如何保证代码隔离、又如何最终汇聚到同一个 Git 仓库?这就是本文要解决的核心问题。

我们将使用 Git Worktree 实现物理级别的目录隔离,结合 Hermes Agent 的多实例 Spawn 能力,让 3 个独立的 Agent 同时在不同的工作树中推进各自的开发任务。

一、为什么需要 Git Worktree?

1.1 传统多分支开发的痛点

在没有 Worktree 的情况下,如果你需要同时开发三个功能分支,常见的做法是:

bash
# 切换来切换去,极其痛苦
git checkout feat/frontend-ui    # 做前端
git stash
git checkout feat/backend-api    # 做后端
git stash
git checkout feat/test-suite     # 做测试

这种方式的致命问题:

  1. 上下文频繁切换:每次 checkout 都要重新安装依赖、重启服务
  2. 无法并行:同一时刻只能在一个分支上工作
  3. IDE 混乱:编辑器索引不断刷新,自动补全失效
  4. Agent 冲突:多个 Agent 操作同一个目录会导致文件锁竞争

1.2 Git Worktree 的优雅方案

Git Worktree 允许你从同一个仓库中"克隆"出多个独立的工作目录,每个目录绑定不同的分支:

text
项目根目录/
├── .git/              # 共享的 .git 目录
├── main/              # 主工作树(main分支)
├── worktrees/
│   ├── frontend/      # feat/frontend-ui 分支
│   ├── backend/       # feat/backend-api 分支
│   └── test/          # feat/test-suite 分支

关键优势:

  • 共享 .git 对象:不需要克隆整个仓库,节省磁盘空间
  • 物理隔离:每个 worktree 是完全独立的目录
  • 并行操作:可以同时在三个目录中运行 git addgit commit、构建命令
  • 自动同步:在一个 worktree 中的 push 会在其他 worktree 中可见(通过 git fetch

二、实战:Hermes Spawn 3个独立 Agent

2.1 初始化项目与 Worktree

首先,创建一个完整的前后端分离项目,然后初始化 Worktree:

bash
# 创建主项目
mkdir -p /opt/data/multi-agent-project && cd /opt/data/multi-agent-project
git init
git checkout -b main

# 创建项目骨架
mkdir -p frontend/src backend/src test/suites

# 初始化前端
cd frontend
npm init -y
npm install react react-dom typescript @types/react
cd ..

# 初始化后端
cd backend
npm init -y
npm install express @types/express
cd ..

# 提交初始结构
git add -A
git commit -m "feat: initialize project structure"

接下来,创建三个 Worktree:

bash
# 创建三个分支并绑定 worktree
git branch feat/frontend-ui
git branch feat/backend-api
git branch feat/test-suite

# 创建 worktree 目录
git worktree add ../worktrees/frontend feat/frontend-ui
git worktree add ../worktrees/backend feat/backend-api
git worktree add ../worktrees/test feat/test-suite

# 验证
git worktree list
# /opt/data/multi-agent-project        main
# /opt/data/worktrees/frontend         feat/frontend-ui
# /opt/data/worktrees/backend          feat/backend-api
# /opt/data/worktrees/test             feat/test-suite

2.2 Hermes Agent 配置文件

我们使用 Hermes 的 spawn 能力,配置三个独立 Agent 实例。每个 Agent 拥有不同的 system prompt、工作目录和任务目标。

创建配置文件 hermes-agents-config.json

json
{
  "agents": [
    {
      "id": "frontend-agent",
      "name": "Frontend Developer Agent",
      "model": "claude-sonnet-4",
      "workdir": "/opt/data/worktrees/frontend",
      "system_prompt": "你是资深前端开发工程师,负责 React 组件开发。你的工作目录是 /opt/data/worktrees/frontend。请按照以下要求完成任务:\n1. 使用 React + TypeScript 编写组件\n2. 遵循 Clean Code 原则\n3. 每次完成后自动 git commit\n4. 遇到构建错误时自行调试修复",
      "tasks": [
        "创建 UserDashboard 组件",
        "实现数据表格与分页功能",
        "编写单元测试"
      ],
      "allowed_commands": ["npm run build", "npm test", "git add", "git commit", "git push"]
    },
    {
      "id": "backend-agent",
      "name": "Backend Developer Agent",
      "model": "claude-sonnet-4",
      "workdir": "/opt/data/worktrees/backend",
      "system_prompt": "你是资深后端开发工程师,负责 Node.js API 开发。你的工作目录是 /opt/data/worktrees/backend。请按照以下要求完成任务:\n1. 使用 Express + TypeScript 构建 RESTful API\n2. 遵循 REST 规范\n3. 每次完成后自动 git commit\n4. 编写完整的接口文档",
      "tasks": [
        "创建用户 CRUD API",
        "实现 JWT 认证中间件",
        "添加请求速率限制"
      ],
      "allowed_commands": ["npm run build", "npm start", "npm test", "git add", "git commit", "git push"]
    },
    {
      "id": "test-agent",
      "name": "Test Engineer Agent",
      "model": "claude-sonnet-4",
      "workdir": "/opt/data/worktrees/test",
      "system_prompt": "你是资深测试工程师,负责集成测试和 E2E 测试。你的工作目录是 /opt/data/worktrees/test。请按照以下要求完成任务:\n1. 使用 Jest + Supertest 编写 API 测试\n2. 使用 Playwright 编写 E2E 测试\n3. 每次完成后自动 git commit\n4. 发现 Bug 时创建 issue 描述",
      "tasks": [
        "编写 API 集成测试套件",
        "创建 E2E 用户流程测试",
        "生成测试覆盖率报告"
      ],
      "allowed_commands": ["npm test", "npm run e2e", "git add", "git commit", "git push"]
    }
  ],
  "orchestration": {
    "parallel": true,
    "sync_interval": "30s",
    "merge_strategy": "rebase",
    "conflict_resolution": "manual"
  }
}

2.3 Hermes 编排脚本

编写一个编排脚本,让 Hermes 同时启动三个 Agent:

bash
#!/bin/bash
# multi-agent-spawn.sh - Hermes 多Agent并行编排脚本

set -e

PROJECT_ROOT="/opt/data"
WORKTREE_ROOT="/opt/data/worktrees"
CONFIG="/opt/data/multi-agent-project/hermes-agents-config.json"

echo "=== 多Agent并行编排系统启动 ==="
echo "开始时间: $(date)"

# 阶段1: 同步所有 worktree 到最新状态
echo ""
echo "📡 阶段1: 同步所有工作树..."
cd "$PROJECT_ROOT/multi-agent-project"
git fetch origin

for wt in frontend backend test; do
  cd "$WORKTREE_ROOT/$wt"
  git pull origin feat/${wt}-ui 2>/dev/null || git pull origin feat/${wt}-api 2>/dev/null || git pull origin feat/test-suite 2>/dev/null || true
  echo "  ✅ $wt worktree 已同步"
done

# 阶段2: Spawn 三个独立 Agent
echo ""
echo "🚀 阶段2: 启动三个独立Agent..."

# Agent 1: 前端开发
hermes spawn \
  --config "$CONFIG" \
  --agent-id frontend-agent \
  --session-id frontend-session-$(date +%s) \
  --async \
  --log "$PROJECT_ROOT/logs/frontend-agent.log" &
FRONTEND_PID=$!
echo "  ✅ Frontend Agent 已启动 (PID: $FRONTEND_PID)"

# Agent 2: 后端开发
hermes spawn \
  --config "$CONFIG" \
  --agent-id backend-agent \
  --session-id backend-session-$(date +%s) \
  --async \
  --log "$PROJECT_ROOT/logs/backend-agent.log" &
BACKEND_PID=$!
echo "  ✅ Backend Agent 已启动 (PID: $BACKEND_PID)"

# Agent 3: 测试开发
hermes spawn \
  --config "$CONFIG" \
  --agent-id test-agent \
  --session-id test-session-$(date +%s) \
  --async \
  --log "$PROJECT_ROOT/logs/test-agent.log" &
TEST_PID=$!
echo "  ✅ Test Agent 已启动 (PID: $TEST_PID)"

echo ""
echo "⏳ 等待所有Agent完成..."

# 阶段3: 等待并收集结果
wait $FRONTEND_PID && echo "  ✅ Frontend Agent 完成" || echo "  ❌ Frontend Agent 失败"
wait $BACKEND_PID && echo "  ✅ Backend Agent 完成" || echo "  ❌ Backend Agent 失败"
wait $TEST_PID && echo "  ✅ Test Agent 完成" || echo "  ❌ Test Agent 失败"

# 阶段4: 汇总各 Agent 产出
echo ""
echo "📊 阶段4: 汇总产出..."

for wt in frontend backend test; do
  cd "$WORKTREE_ROOT/$wt"
  echo ""
  echo "--- ${wt} 分支变更 ---"
  git log --oneline -5
  echo "文件变更:"
  git diff --stat main..HEAD 2>/dev/null || echo "  (无变更)"
done

echo ""
echo "=== 编排完成 ==="
echo "结束时间: $(date)"

2.4 Agent 实际工作示例

让我们看看每个 Agent 在各自 worktree 中的实际工作内容。

Frontend Agent 的工作产出:

typescript
// worktrees/frontend/src/components/UserDashboard.tsx
import React, { useState, useEffect, useCallback } from 'react';
import { DataTable } from './DataTable';
import { Pagination } from './Pagination';
import { fetchUsers, User } from '../api/users';

interface UserDashboardProps {
  pageSize?: number;
  onUserSelect?: (user: User) => void;
}

export const UserDashboard: React.FC<UserDashboardProps> = ({
  pageSize = 10,
  onUserSelect,
}) => {
  const [users, setUsers] = useState<User[]>([]);
  const [currentPage, setCurrentPage] = useState(1);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [totalPages, setTotalPages] = useState(1);

  const loadData = useCallback(async (page: number) => {
    try {
      setLoading(true);
      setError(null);
      const response = await fetchUsers({ page, pageSize });
      setUsers(response.data);
      setTotalPages(response.pagination.totalPages);
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Unknown error');
    } finally {
      setLoading(false);
    }
  }, [pageSize]);

  useEffect(() => {
    loadData(currentPage);
  }, [currentPage, loadData]);

  if (loading && users.length === 0) {
    return <div className="dashboard-loading">Loading users...</div>;
  }

  if (error) {
    return <div className="dashboard-error">Error: {error}</div>;
  }

  return (
    <div className="user-dashboard">
      <h2>User Management</h2>
      <DataTable
        data={users}
        columns={['id', 'name', 'email', 'role', 'status']}
        onRowClick={onUserSelect}
      />
      <Pagination
        currentPage={currentPage}
        totalPages={totalPages}
        onPageChange={setCurrentPage}
      />
    </div>
  );
};

export default UserDashboard;

Backend Agent 的工作产出:

typescript
// worktrees/backend/src/routes/users.ts
import { Router, Request, Response } from 'express';
import { body, param, query } from 'express-validator';
import { validate } from '../middleware/validate';
import { authenticate } from '../middleware/authenticate';
import { rateLimit } from '../middleware/rateLimit';
import { UserService } from '../services/userService';

const router = Router();
const userService = new UserService();

// GET /api/users?page=1&pageSize=10
router.get(
  '/',
  authenticate,
  rateLimit({ windowMs: 60000, max: 100 }),
  query('page').optional().isInt({ min: 1 }),
  query('pageSize').optional().isInt({ min: 1, max: 100 }),
  validate,
  async (req: Request, res: Response) => {
    const page = parseInt(req.query.page as string) || 1;
    const pageSize = parseInt(req.query.pageSize as string) || 10;

    const { users, total } = await userService.findAll(page, pageSize);

    res.json({
      data: users,
      pagination: {
        page,
        pageSize,
        total,
        totalPages: Math.ceil(total / pageSize),
      },
    });
  }
);

// POST /api/users
router.post(
  '/',
  authenticate,
  rateLimit({ windowMs: 60000, max: 20 }),
  body('name').isString().isLength({ min: 2, max: 50 }),
  body('email').isEmail(),
  body('role').isIn(['admin', 'user', 'moderator']),
  validate,
  async (req: Request, res: Response) => {
    const user = await userService.create(req.body);
    res.status(201).json({ data: user });
  }
);

// PUT /api/users/:id
router.put(
  '/:id',
  authenticate,
  param('id').isInt({ min: 1 }),
  body('name').optional().isString().isLength({ min: 2, max: 50 }),
  body('email').optional().isEmail(),
  validate,
  async (req: Request, res: Response) => {
    const user = await userService.update(
      parseInt(req.params.id),
      req.body
    );
    res.json({ data: user });
  }
);

// DELETE /api/users/:id
router.delete(
  '/:id',
  authenticate,
  param('id').isInt({ min: 1 }),
  validate,
  async (req: Request, res: Response) => {
    await userService.delete(parseInt(req.params.id));
    res.status(204).send();
  }
);

export default router;

Test Agent 的工作产出:

typescript
// worktrees/test/suites/api/userApi.test.ts
import request from 'supertest';
import { app } from '../../../backend/src/app';
import { createTestUser, generateToken, cleanupDb } from '../helpers/testDb';

describe('User API Integration Tests', () => {
  let authToken: string;

  beforeEach(async () => {
    await cleanupDb();
    const adminUser = await createTestUser({ role: 'admin' });
    authToken = generateToken(adminUser);
  });

  describe('GET /api/users', () => {
    it('should return paginated users for authenticated admin', async () => {
      // 创建测试数据
      for (let i = 0; i < 25; i++) {
        await createTestUser({ name: `User ${i}` });
      }

      const response = await request(app)
        .get('/api/users')
        .query({ page: 1, pageSize: 10 })
        .set('Authorization', `Bearer ${authToken}`);

      expect(response.status).toBe(200);
      expect(response.body.data).toHaveLength(10);
      expect(response.body.pagination).toEqual({
        page: 1,
        pageSize: 10,
        total: 25,
        totalPages: 3,
      });
    });

    it('should reject unauthenticated requests', async () => {
      const response = await request(app).get('/api/users');
      expect(response.status).toBe(401);
      expect(response.body.error).toBe('Authentication required');
    });

    it('should respect rate limiting', async () => {
      const requests = Array(101).fill(null).map(() =>
        request(app)
          .get('/api/users')
          .set('Authorization', `Bearer ${authToken}`)
      );

      const responses = await Promise.all(requests);
      const rejected = responses.filter(r => r.status === 429);
      expect(rejected.length).toBeGreaterThan(0);
    });
  });

  describe('POST /api/users', () => {
    it('should create a new user with valid data', async () => {
      const response = await request(app)
        .post('/api/users')
        .set('Authorization', `Bearer ${authToken}`)
        .send({
          name: 'New User',
          email: 'newuser@example.com',
          role: 'user',
        });

      expect(response.status).toBe(201);
      expect(response.body.data).toMatchObject({
        name: 'New User',
        email: 'newuser@example.com',
        role: 'user',
      });
    });

    it('should reject invalid email', async () => {
      const response = await request(app)
        .post('/api/users')
        .set('Authorization', `Bearer ${authToken}`)
        .send({
          name: 'Bad User',
          email: 'not-an-email',
          role: 'user',
        });

      expect(response.status).toBe(400);
      expect(response.body.errors).toContainEqual(
        expect.objectContaining({ field: 'email' })
      );
    });
  });
});

三、合并与集成

3.1 各 Agent 自动提交

每个 Agent 在完成自己的任务后,会自动在各自的 worktree 中提交:

bash
# Frontend Agent 提交
cd /opt/data/worktrees/frontend
git add -A
git commit -m "feat(frontend): implement UserDashboard with pagination

- Created UserDashboard component with React + TypeScript
- Added DataTable and Pagination sub-components
- Implemented error handling and loading states
- Integrated with users API"

# Backend Agent 提交
cd /opt/data/worktrees/backend
git add -A
git commit -m "feat(backend): implement user CRUD API with auth & rate limiting

- Created RESTful endpoints for user management
- Added JWT authentication middleware
- Implemented rate limiting per endpoint
- Added request validation with express-validator"

# Test Agent 提交
cd /opt/data/worktrees/test
git add -A
git commit -m "feat(test): add comprehensive API integration tests

- Created user API test suite with supertest
- Added pagination, auth, and rate limit tests
- Implemented test database helpers
- Added E2E test skeletons for user workflows"

3.2 集成到主分支

当三个 Agent 都完成各自任务后,我们在主仓库中将三个分支合并:

bash
cd /opt/data/multi-agent-project

# 拉取所有 worktree 的变更
git fetch --all

# 合并前端分支
git merge feat/frontend-ui --no-ff -m "Merge frontend: UserDashboard component"

# 合并后端分支
git merge feat/backend-api --no-ff -m "Merge backend: user CRUD API"

# 合并测试分支
git merge feat/test-suite --no-ff -m "Merge test: API integration tests"

# 解决可能的冲突
# 如果 frontend 和 backend 都修改了 src/types/index.ts:
# 手动编辑后:
git add src/types/index.ts
git commit -m "chore: resolve merge conflict in types"

# 推送到远程
git push origin main

3.3 清理 Worktree

项目合并后,可以清理不再需要的 worktree:

bash
# 删除 worktree(同时删除对应分支)
git worktree remove ../worktrees/frontend
git worktree remove ../worktrees/backend
git worktree remove ../worktrees/test

# 或者保留 worktree 目录但删除 git 关联
git worktree prune

# 查看当前 worktree 状态
git worktree list

四、架构优势分析

4.1 并行度对比

方案 并行度 隔离性 资源消耗
串行单 Agent 1x N/A 最低
多分支 checkout 切换 1x(伪并行)
完整仓库克隆 3x 最高
Git Worktree + Hermes 3x 中等

4.2 资源消耗分析

text
完整克隆方案:
  仓库大小: 500MB × 3 = 1500MB
  安装依赖: node_modules × 3 = 600MB
  总计: ~2100MB

Worktree 方案:
  共享 .git: 500MB
  工作目录: 50MB × 3 = 150MB
  共享依赖: node_modules (symlink) = 200MB
  总计: ~850MB

节省: ~60% 磁盘空间

4.3 错误隔离

每个 Agent 在自己的 worktree 中操作,即使某个 Agent 出现以下问题也不会影响其他 Agent:

  • 构建失败:不会阻塞其他分支的构建
  • 依赖冲突:worktree 间的 node_modules 可以独立配置
  • Git 冲突:冲突只在 merge 阶段出现,不影响并行开发
  • Agent 崩溃:单个 Agent 失败不影响其他 Agent 运行

五、最佳实践

5.1 Worktree 命名规范

bash
# 推荐格式:<功能域>/<任务描述>
git worktree add ../worktrees/feat-auth       feat/auth-system
git worktree add ../worktrees/fix-payment     fix/payment-bug
git worktree add ../worktrees/perf-dashboard  perf/optimize-dashboard

# 避免过长的名称
git worktree add ../worktrees/feat            feat/very-long-feature-name

5.2 Agent 通信机制

虽然三个 Agent 在不同的 worktree 中工作,但它们可能需要共享信息。推荐做法:

bash
# 方式1:通过 Git 提交消息传递信息
git commit -m "feat(backend): add user API

NOTE: @frontend-agent User endpoint is ready at /api/users
      Response format: { data: User[], pagination: {...} }
      Auth header required: Authorization: Bearer <token>"

# 方式2:通过共享文件传递契约
# 创建 contracts/api-contract.json
# 所有 Agent 读取这个文件了解 API 规范

5.3 同步策略

bash
#!/bin/bash
# sync-worktrees.sh - 定期同步脚本

WORKTREE_ROOT="/opt/data/worktrees"
INTERVAL=300  # 5分钟

while true; do
  echo "[$(date)] 同步所有 worktree..."

  for wt in "$WORKTREE_ROOT"/*/; do
    if [ -d "$wt/.git" ]; then
      cd "$wt"
      git fetch origin
      BRANCH=$(git branch --show-current)
      git rebase origin/$BRANCH 2>/dev/null || true
      echo "  ✅ $(basename $wt) 已同步"
    fi
  done

  sleep $INTERVAL
done

配图说明

图片 说明
architecture.png 架构图:Hermes Spawn 三个 Agent,分别对应 frontend/backend/test 三个 worktree,最终 merge 到 main 分支
worktree-diagram.png Git Worktree 结构图:共享 .git 对象,独立工作目录的可视化
timeline.png 时间线图:三个 Agent 并行工作的时间轴,展示并行度提升

总结

本文详细演示了如何使用 Git Worktree + Hermes Agent 实现真正的前后端测试三端并行开发。核心要点:

  1. Git Worktree 提供了物理隔离且共享 .git 对象的轻量级多分支方案,比完整克隆节省约 60% 磁盘空间
  2. Hermes Spawn 可以为每个 worktree 配置独立的 Agent 实例,拥有不同的 system prompt、任务目标和权限控制
  3. 并行开发 的效率提升是线性的——3 个 Agent 同时工作,理论上可以达到 3 倍的开发速度
  4. 自动提交与合并 确保了代码变更可以有序地集成到主分支

这套方案特别适合:

  • 前后端分离项目的并行开发
  • 大型功能模块的拆分开发
  • 测试团队与开发团队的并行工作
  • Code Review 前的预发分支验证

下篇预告

CICD多Agent流水线

在下一篇中,我们将把这套并行开发方案与 CI/CD 流水线深度集成。你将看到:

  • 🔄 完整的 构建→审查→测试→部署 四阶段流水线
  • 🔍 Codex 负责 PR 代码审查,自动检测代码质量和规范
  • 🔧 Claude Code 根据审查结果自动修复问题
  • 📢 Hermes 负责最终部署和多渠道通知
  • 如何将三个 Agent 无缝串联到 GitHub Actions / GitLab CI 中

敬请期待!


本文属于「AI Agent 实战」系列。如果你对本系列感兴趣,欢迎订阅或分享给你的团队。