Skip to content

Chapter 8: Subagent — 专业化 Agent 处理复杂工作 ​

你将构建什么 ​

三个从真实仓库中提取的生产级 Agent:

  1. 架构师 Agent — 分析系统设计,输出权衡分析表(来自 ECC,使用 Opus 模型)
  2. 安全扫描 Agent — 在真实代码中发现 SQL 注入、XSS、缺失鉴权,带严重程度评级
  3. 测试编写 Agent — 生成带覆盖率目标的测试并运行验证

这些是 Everything Claude Code(architect、code-reviewer、tdd-guide)和 gstack(CEO/Designer/Engineer 角色 agent)的实际模式。不是玩具示例 -- 这些是那些 148k 和 68k 星的仓库中使用的真实模式。

附录链接:A04 Agent 架构模式 解释了 Subagent 设计背后的 ReAct 和 plan-and-execute 范式。A09 扩展思考 介绍了何时以及如何在 subagent 中使用思考预算进行更深入的分析。

研究说明:本章的多 agent 模式借鉴了 Anthropic 的 Building effective agents 指南,该指南形式化了 Claude Code 用于 subagent 调度的编排者-执行者(orchestrator-worker)架构。

Subagent 工作原理 ​

Subagent 是运行在独立上下文窗口中的 Claude 实例。主会话启动一个 subagent 后,subagent 获得自己的工具、指令和上下文空间。只有最终结果返回。

主会话(你的对话)
  |
  +---> Subagent: architect
  |       独立上下文 (200K)
  |       工具: 仅 Read, Glob, Grep
  |       读取 40 个文件,分析架构
  |       返回: 500 token 的摘要
  |
  主会话上下文仅增长 ~500 token,而不是 40 个文件的量

为什么用 Subagent 而不是在主会话中做所有事 ​

主会话Subagent
上下文共享 -- 每次文件读取都消耗限额隔离 -- 读取不影响主会话
工具全部可用限制在你允许的范围内
模型当前模型可以指定不同模型
并行只能串行多个可同时运行
输出对话中全部可见只返回最终结果

核心优势是 上下文隔离。没有 subagent,让 Claude 审查 30 个文件会吞噬你的上下文窗口。用了 subagent,这 30 个文件存在于 subagent 的上下文中,你只收到审查报告。

GSD 称之为 "Context Rot"(上下文腐烂)问题 -- 上下文使用超过 50% 后质量下降。Subagent 是避免这个问题的方法。

Agent 定义格式 ​

Agent 是 .claude/agents/ 中的 Markdown 文件:

markdown
---
name: my-agent
description: What this agent does
tools:
  - Read
  - Glob
  - Grep
model: claude-sonnet-4-6
---

Agent instructions here.

Frontmatter 参考 ​

字段类型作用
namestringAgent 标识符
descriptionstring在 agent 列表中显示
toolslist可用工具白名单
modelstring使用哪个模型(claude-sonnet-4-6、claude-opus-4-6 等)
maxTurnsnumber最大对话轮数
isolationstringworktree = 在独立 git worktree 中运行
memoryboolean跨调用保持知识
skillslist该 agent 可以使用的 skill
mcpServerslist该 agent 可以访问的 MCP 服务器

工具限制的重要性 ​

ECC 的 code-reviewer agent 限制为 Read、Glob、Grep -- 只读工具。这不是随意的。一个能 Write 或 Bash 的 reviewer 可能会"修复"代码而不是报告问题,违背了审查的目的。

gstack 用角色限制更进一步:

  • CEO agent:只读。评估业务价值,不碰代码。
  • Designer agent:只读。审查 UX,不改实现。
  • Engineer agent:全部工具。基于 CEO 和 Designer 的反馈实现。

Demo 21: 架构师 Agent ​

21
Architect Agent with Trade-off Analysis
Intermediate~15 min

改编自 ECC 的 architect agent -- 使用 Opus 模型进行系统设计分析,输出权衡表。

问题 ​

你接手了一个代码库,或者项目复杂到需要理解全局。哪些模块依赖哪些?耦合热点在哪?如果重构认证系统会影响什么?你可以花一天读代码,或者让架构师 agent 在 2 分钟内完成。

构建 ​

1. 准备真实代码库 ​

bash
mkdir -p ~/claude-demos/demo-21/src/{api,services,models,middleware,utils} && cd ~/claude-demos/demo-21
git init && git branch -M main

# API layer
cat > src/api/routes.js << 'EOF'
import { UserService } from '../services/user.js';
import { OrderService } from '../services/order.js';
import { authMiddleware } from '../middleware/auth.js';
import { rateLimiter } from '../middleware/rateLimit.js';

export function registerRoutes(app) {
  app.post('/api/users', UserService.create);
  app.get('/api/users/:id', authMiddleware, UserService.getById);
  app.post('/api/orders', authMiddleware, rateLimiter, OrderService.create);
  app.get('/api/orders', authMiddleware, OrderService.list);
  app.delete('/api/orders/:id', authMiddleware, OrderService.delete);
}
EOF

# Services
cat > src/services/user.js << 'EOF'
import { UserModel } from '../models/user.js';
import { hashPassword, comparePassword } from '../utils/crypto.js';
import { sendEmail } from '../utils/email.js';
import { cache } from '../utils/cache.js';

export class UserService {
  static async create(req, res) {
    const { email, password, name } = req.body;
    const existing = await UserModel.findByEmail(email);
    if (existing) return res.status(409).json({ error: 'Email exists' });
    const hashed = await hashPassword(password);
    const user = await UserModel.create({ email, password: hashed, name });
    await sendEmail(email, 'Welcome!', `Hi ${name}, welcome aboard.`);
    cache.del('users:list');
    return res.status(201).json(user);
  }
  static async getById(req, res) {
    const cached = cache.get(`user:${req.params.id}`);
    if (cached) return res.json(cached);
    const user = await UserModel.findById(req.params.id);
    if (!user) return res.status(404).json({ error: 'Not found' });
    cache.set(`user:${req.params.id}`, user, 300);
    return res.json(user);
  }
}
EOF

cat > src/services/order.js << 'EOF'
import { OrderModel } from '../models/order.js';
import { UserModel } from '../models/user.js';
import { calculateTotal } from '../utils/pricing.js';
import { sendEmail } from '../utils/email.js';
import { cache } from '../utils/cache.js';
import { publishEvent } from '../utils/events.js';

export class OrderService {
  static async create(req, res) {
    const user = await UserModel.findById(req.userId);
    const total = calculateTotal(req.body.items);
    const order = await OrderModel.create({
      userId: req.userId, items: req.body.items, total, status: 'pending'
    });
    await publishEvent('order.created', { orderId: order.id, userId: req.userId });
    await sendEmail(user.email, 'Order Confirmed', `Order #${order.id}: $${total}`);
    cache.del(`orders:${req.userId}`);
    return res.status(201).json(order);
  }
  static async list(req, res) {
    const cached = cache.get(`orders:${req.userId}`);
    if (cached) return res.json(cached);
    const orders = await OrderModel.findByUserId(req.userId);
    cache.set(`orders:${req.userId}`, orders, 60);
    return res.json(orders);
  }
  static async delete(req, res) {
    const order = await OrderModel.findById(req.params.id);
    if (!order) return res.status(404).json({ error: 'Not found' });
    if (order.userId !== req.userId) return res.status(403).json({ error: 'Forbidden' });
    await OrderModel.delete(req.params.id);
    await publishEvent('order.deleted', { orderId: req.params.id });
    cache.del(`orders:${req.userId}`);
    return res.json({ success: true });
  }
}
EOF

# Models, middleware, utils (abbreviated but realistic)
cat > src/models/user.js << 'EOF'
import { db } from '../utils/db.js';
export class UserModel {
  static findByEmail(email) { return db.query('SELECT * FROM users WHERE email = $1', [email]).then(r => r.rows[0]); }
  static findById(id) { return db.query('SELECT * FROM users WHERE id = $1', [id]).then(r => r.rows[0]); }
  static create(data) { return db.query('INSERT INTO users (email, password, name) VALUES ($1,$2,$3) RETURNING *', [data.email, data.password, data.name]).then(r => r.rows[0]); }
}
EOF

cat > src/models/order.js << 'EOF'
import { db } from '../utils/db.js';
export class OrderModel {
  static findById(id) { return db.query('SELECT * FROM orders WHERE id = $1', [id]).then(r => r.rows[0]); }
  static findByUserId(userId) { return db.query('SELECT * FROM orders WHERE user_id = $1', [userId]).then(r => r.rows); }
  static create(data) { return db.query('INSERT INTO orders (user_id, items, total, status) VALUES ($1,$2,$3,$4) RETURNING *', [data.userId, JSON.stringify(data.items), data.total, data.status]).then(r => r.rows[0]); }
  static delete(id) { return db.query('DELETE FROM orders WHERE id = $1', [id]); }
}
EOF

cat > src/middleware/auth.js << 'EOF'
import { verifyToken } from '../utils/crypto.js';
export function authMiddleware(req, res, next) {
  const token = req.headers.authorization?.replace('Bearer ', '');
  if (!token) return res.status(401).json({ error: 'No token' });
  try { req.userId = verifyToken(token).userId; next(); }
  catch { return res.status(401).json({ error: 'Invalid token' }); }
}
EOF

cat > src/middleware/rateLimit.js << 'EOF'
const requests = new Map();
export function rateLimiter(req, res, next) {
  const key = req.userId || req.ip;
  const now = Date.now();
  const windowMs = 60000;
  const max = 100;
  const entries = (requests.get(key) || []).filter(t => t > now - windowMs);
  if (entries.length >= max) return res.status(429).json({ error: 'Rate limited' });
  entries.push(now);
  requests.set(key, entries);
  next();
}
EOF

cat > CLAUDE.md << 'EOF'
# E-commerce API
- Node.js + Express
- PostgreSQL database
- In-memory cache (should be Redis in production)
EOF

git add -A && git commit -m "feat: initial e-commerce API"

2. 创建架构师 Agent ​

bash
mkdir -p .claude/agents

cat > .claude/agents/architect.md << 'EOF'
---
name: architect
description: Analyzes system architecture with dependency mapping and trade-off analysis
tools:
  - Read
  - Glob
  - Grep
model: claude-opus-4-6
---

You are a senior systems architect. Your job is to analyze the codebase and produce an architectural assessment that a team could use to make refactoring decisions.

## Analysis Process

1. **Map the module structure**: Read every file, identify layers (API, services, models, utils, middleware)
2. **Trace dependencies**: For each module, list what it imports and what imports it
3. **Identify patterns**: What architectural patterns are in use? (MVC, layered, event-driven, etc.)
4. **Find coupling hotspots**: Which modules are imported by the most other modules?
5. **Assess scalability**: What breaks first under 10x load?

## Output Format

Architecture Assessment ​

System Map ​

[ASCII diagram showing module relationships]

Layer Analysis ​

LayerFilesResponsibilitiesDependencies
API.........

Dependency Graph ​

[Which modules depend on which, with counts]

Coupling Hotspots ​

[Modules that are imported most frequently — these are risky to change]

Trade-off Analysis ​

DecisionCurrent ApproachAlternativeTrade-off
CachingIn-memory MapRedisSimple but not distributed

Scalability Concerns ​

[What breaks at 10x scale and why]

Refactoring Recommendations ​

Priority-ordered list with estimated effort (hours, not story points).


## Rules
- Read ALL files before drawing conclusions
- Every claim must reference a specific file
- "I'd recommend" is vague — say exactly what to change and where
- Trade-off tables must include the downside of your recommendation, not just the upside
EOF

3. 使用 ​

bash
claude
Use the architect agent to analyze this codebase and produce a full architecture assessment

4. 预期终端输出 ​

terminal
$ claude
> Use the architect agent to analyze this codebase and produce a full architecture assessment

Claude: I'll launch the architect agent to analyze your codebase.

  Spawning agent: architect (claude-opus-4-6)
  Tools allowed: Read, Glob, Grep
  
  [architect] Glob: src/**/*.js -> 8 files found
  [architect] Read: src/api/routes.js (16 lines)
  [architect] Read: src/services/user.js (18 lines)
  [architect] Read: src/services/order.js (28 lines)
  [architect] Read: src/models/user.js (6 lines)
  [architect] Read: src/models/order.js (6 lines)
  [architect] Read: src/middleware/auth.js (7 lines)
  [architect] Read: src/middleware/rateLimit.js (12 lines)
  [architect] Grep: 'import' in src/ -> 24 matches
  [architect] Analysis complete. Returning report.

# Architecture Assessment

## System Map
  routes.js
    |--- UserService --- UserModel --- db.js
    |       |--- crypto.js, email.js, cache.js
    |--- OrderService --- OrderModel --- db.js
    |       |--- UserModel, pricing.js, email.js, cache.js, events.js
    |--- authMiddleware --- crypto.js
    |--- rateLimiter (standalone)

## Coupling Hotspots
1. utils/cache.js — imported by 2 services (will break both if changed)
2. utils/email.js — imported by 2 services (no fallback on failure)
3. models/user.js — imported by UserService AND OrderService (cross-domain coupling)

## Trade-off Analysis
| Decision        | Current            | Alternative    | Trade-off                           |
|-----------------|--------------------|----------------|-------------------------------------|
| Caching         | In-memory Map      | Redis          | Simple but not distributed          |
| Rate limiting   | In-memory Map      | Redis + sliding | Resets on deploy, no cluster support|
| Event publishing| Direct function call| Message queue  | Tightly coupled, no retry/replay    |

## Refactoring Recommendations
1. Extract cache to Redis adapter (~2h) — highest impact
2. Add error handling to email sends (~1h) — silent failures are dangerous
3. Decouple OrderService from UserModel (~3h) — pass user data in, dont re-fetch

Token usage: ~4,200 tokens (agent context), ~620 tokens returned to main session.

刚才发生了什么? ​

1
TaskAgent
architect
↓
2
Glob
src/**/*.js
↓
3
Read (x7)
All source files
↓
4
Grep
import statements
↓
5
Return
Main session

架构师 agent 在自己的上下文中消耗了约 4,200 tokens 读取 8 个文件并分析依赖。你的主会话只收到了约 620 token 的报告。没有 subagent 的话,这 8 次文件读取会直接消耗约 3,500 tokens 的主上下文 -- 而且你还需要自己做分析。


Demo 22: 安全扫描 Agent ​

22
Security Scanner with Confidence Filtering
Intermediate~15 min

问题 ​

你需要在合并 PR 前做安全审查,但团队没有专职安全工程师。标准的"让 Claude 检查安全"方法产出模糊的结果。一个有严格输出格式和置信度过滤的专用安全 agent 能捕获真实问题、忽略噪音。

构建 ​

bash
cd ~/claude-demos/demo-21

cat > .claude/agents/security-scanner.md << 'EOF'
---
name: security-scanner
description: Finds security vulnerabilities with severity ratings and confidence scores
tools:
  - Read
  - Glob
  - Grep
model: claude-sonnet-4-6
---

You are a security engineer performing a vulnerability assessment. You are thorough, precise, and you don't cry wolf.

## Vulnerability Categories

Scan for these specific vulnerability classes:

### Injection (CRITICAL)
- SQL injection: string interpolation in queries
- Command injection: user input in shell commands
- Template injection: user input in template engines
- NoSQL injection: user input in MongoDB queries

### Authentication & Authorization (CRITICAL)
- Missing auth checks on sensitive endpoints
- Weak token generation (predictable, short, no expiry)
- Password stored in plaintext
- Missing rate limiting on auth endpoints

### Data Exposure (HIGH)
- Sensitive fields in API responses (password hashes, tokens, internal IDs)
- Verbose error messages leaking stack traces
- Missing input validation allowing oversized payloads

### Configuration (MEDIUM)
- Hardcoded credentials or API keys
- Debug mode enabled
- CORS misconfiguration
- Missing security headers

## Output Rules

1. **Confidence threshold**: Only report findings with >= 80% confidence
2. **Specific locations**: Always include file:line, never "in the auth module"
3. **Severity levels**: P0 (exploit now), P1 (exploit with effort), P2 (defense-in-depth), P3 (hardening)
4. **Proof of concept**: For P0/P1, include a curl command or input that demonstrates the vulnerability

## Output Format

Security Assessment ​

Executive Summary ​

  • Critical (P0): N findings
  • High (P1): N findings
  • Medium (P2): N findings
  • Scanned: N files

Critical Findings (P0) ​

[SQLI-001] SQL Injection in user lookup ​

  • File: src/services/user.js:15
  • Confidence: 95%
  • Vector: email parameter from request body interpolated into SQL query
  • Impact: Full database read/write access
  • PoC: curl -X POST /api/users -d '{"email":"' OR 1=1 --"}'
  • Fix: Use parameterized queries
    javascript
    // Before
    db.query(`SELECT * FROM users WHERE email = '${email}'`)
    // After  
    db.query('SELECT * FROM users WHERE email = $1', [email])

...

Passed Checks ​

[List security measures that ARE in place — this matters for context]

EOF

测试 ​

bash
claude
Use the security-scanner agent to scan all files in src/ for vulnerabilities

预期终端输出 ​

terminal
$ claude
> Use the security-scanner agent to scan all files in src/ for vulnerabilities

Claude: Launching the security-scanner agent to assess your codebase.

  Spawning agent: security-scanner (claude-sonnet-4-6)
  Tools allowed: Read, Glob, Grep
  
  [security-scanner] Glob: src/**/*.js -> 8 files found
  [security-scanner] Read: src/api/routes.js
  [security-scanner] Read: src/services/user.js
  [security-scanner] Read: src/services/order.js
  [security-scanner] Read: src/middleware/auth.js
  [security-scanner] Read: src/middleware/rateLimit.js
  [security-scanner] Grep: 'password' in src/ -> 4 matches
  [security-scanner] Grep: 'req.body' in src/ -> 3 matches
  [security-scanner] Grep: 'req.params' in src/ -> 4 matches
  [security-scanner] Assessment complete. Returning report.

# Security Assessment

## Executive Summary
- Critical (P0): 0 findings
- High (P1): 2 findings
- Medium (P2): 3 findings
- Scanned: 8 files

## High Findings (P1)

### [AUTH-001] Missing input validation on user creation
- **File**: src/services/user.js:4
- **Confidence**: 92%
- **Vector**: No validation on email format, password length, or name content
- **Impact**: Can create users with empty passwords or invalid emails
- **PoC**: curl -X POST /api/users -d '{"email":"not-an-email","password":"","name":""}'
- **Fix**: Add input validation before processing

### [AUTH-002] Rate limiter not applied to user creation
- **File**: src/api/routes.js:8
- **Confidence**: 88%
- **Vector**: POST /api/users has no rate limiting, only POST /api/orders does
- **Impact**: Brute-force account creation, credential stuffing
- **Fix**: Add rateLimiter middleware to user creation route

## Medium Findings (P2)

### [CACHE-001] In-memory rate limiter resets on restart
- **File**: src/middleware/rateLimit.js:1
- **Confidence**: 95%
- **Vector**: Rate limit state stored in-process Map, lost on deploy/restart
- **Impact**: Rate limiting ineffective across restarts or multiple instances

### [DATA-001] Password hash may leak in user response
- **File**: src/services/user.js:8
- **Confidence**: 82%
- **Vector**: UserModel.create returns all columns including password hash
- **Impact**: Password hash exposed in 201 response body

### [ERR-001] Silent notification failure
- **File**: src/services/order.js:10
- **Confidence**: 90%
- **Vector**: sendEmail failure caught and discarded with .catch(() => {})
- **Impact**: Users never notified of order confirmation, no logging

## Passed Checks
- SQL queries use parameterized queries ($1, $2 placeholders) -- no injection risk
- Auth middleware validates JWT tokens before allowing access
- Order deletion checks ownership (userId match) -- proper authorization
- Sensitive routes protected with authMiddleware

刚才发生了什么? ​

1
TaskAgent
security-scanner
↓
2
Glob + Read
All source files
↓
3
Grep (x3)
Security-sensitive patterns
↓
4
Return
Main session

注意 agent 没有报告的内容:它发现了参数化 SQL 查询并正确将其分类为安全的。80% 置信度门槛过滤掉了推测性发现。这就是有用的安全审查和嘈杂审查之间的区别。

ECC 的置信度过滤 ​

这直接来自 ECC 的 code-reviewer agent 模式:低于 80% 置信度的发现被丢弃。这个规则消除了大部分让 AI 代码审查烦人的误报。没有它,你得到 20 个"发现"其中 15 个是噪音。有了它,你得到 5 个都是真实的发现。


Demo 23: 测试编写 Agent ​

23
Test Writer Agent with Coverage Targets
Intermediate~20 min

问题 ​

你有没测试的代码。从零开始写测试很枯燥。测试编写 agent 读取代码,识别公开 API,生成覆盖正常路径和错误路径的测试用例,然后运行验证它们通过。

构建 ​

bash
cd ~/claude-demos/demo-21

cat > .claude/agents/test-writer.md << 'EOF'
---
name: test-writer
description: Generates comprehensive tests with coverage targets
tools:
  - Read
  - Write
  - Glob
  - Grep
  - Bash
model: claude-sonnet-4-6
---

You are a test engineer. You write tests that catch real bugs, not tests that just increase coverage numbers.

## Process

1. **Read the target code** — understand the public API, inputs, outputs, side effects
2. **Identify test scenarios**:
   - Happy path (normal usage)
   - Edge cases (empty input, boundary values, null/undefined)
   - Error paths (invalid input, missing dependencies, network failures)
   - Security cases (injection attempts, auth bypass, oversized input)
3. **Write the tests** using the project's test framework (detect from package.json)
4. **Run the tests** to verify they pass
5. **Report coverage**

## Test Quality Rules

- Each `it()` block tests exactly ONE behavior
- Test names describe the behavior, not the implementation: "returns 404 when user not found" not "tests getById"
- Mock external dependencies (database, email, cache) — don't test third-party code
- Include at least one test per public function/method
- Error path tests are not optional — every `catch` and error return must be tested

## Output Format

Test Report ​

Files Created ​

  • tests/services/user.test.js (12 tests)
  • tests/services/order.test.js (15 tests)

Coverage Summary ​

ModuleFunctionsBranchesLines
UserService100%85%92%
OrderService100%80%88%

Test Results ​

  • Total: 27 tests
  • Passed: 27
  • Failed: 0

Scenarios Covered ​

  • User creation: valid input, duplicate email, missing fields
  • Order creation: valid order, invalid items, unauthorized
  • Order deletion: own order, other user's order, non-existent ...

## Framework Detection

Check `package.json` for test framework:
- `vitest` -> use Vitest with `describe/it/expect`
- `jest` -> use Jest with `describe/it/expect`
- Neither -> install Vitest and configure it

If the project has no `package.json`, create one.
EOF

测试 ​

bash
# First install a test framework
cd ~/claude-demos/demo-21
npm init -y
npm install --save-dev vitest

claude
Use the test-writer agent to generate tests for src/services/user.js and
src/services/order.js, then run them

预期终端输出 ​

terminal
$ claude
> Use the test-writer agent to generate tests for src/services/user.js and
  src/services/order.js, then run them

Claude: Launching the test-writer agent. This agent has Write and Bash access
so it can create test files and run them.

  Spawning agent: test-writer (claude-sonnet-4-6)
  Tools allowed: Read, Write, Glob, Grep, Bash
  
  [test-writer] Read: package.json -> vitest detected
  [test-writer] Read: src/services/user.js (18 lines)
  [test-writer] Read: src/services/order.js (28 lines)
  [test-writer] Read: src/models/user.js (dependencies)
  [test-writer] Read: src/models/order.js (dependencies)
  [test-writer] Write: tests/services/user.test.js (12 test cases)
  [test-writer] Write: tests/services/order.test.js (15 test cases)
  [test-writer] Bash: npx vitest run --reporter=verbose

  stdout | tests/services/user.test.js
    UserService
      create
        + returns 201 with valid user data (3ms)
        + returns 409 when email already exists (1ms)
        + hashes password before storing (2ms)
        + sends welcome email after creation (1ms)
        + invalidates user list cache (1ms)
      getById
        + returns cached user when available (1ms)
        + fetches from database on cache miss (2ms)
        + returns 404 when user not found (1ms)
        + sets cache with 300s TTL on fetch (1ms)

  stdout | tests/services/order.test.js
    OrderService
      create
        + creates order with correct total (2ms)
        + publishes order.created event (1ms)
        + sends confirmation email to user (1ms)
        + invalidates order cache for user (1ms)
      list
        + returns cached orders when available (1ms)
        + fetches from database on cache miss (2ms)
        + sets cache with 60s TTL (1ms)
      delete
        + returns 404 when order not found (1ms)
        + returns 403 when user does not own order (1ms)
        + deletes order and publishes event (2ms)
        + invalidates order cache after deletion (1ms)

  Test Files  2 passed (2)
       Tests  20 passed (20)
    Duration  0.34s

  [test-writer] Complete. Returning report.

# Test Report
## Files Created
- tests/services/user.test.js (9 tests)
- tests/services/order.test.js (11 tests)

## Test Results
- Total: 20 tests
- Passed: 20
- Failed: 0

刚才发生了什么? ​

1
TaskAgent
test-writer
↓
2
Read (x5)
Source files + package.json
↓
3
Write (x2)
Test files
↓
4
Bash
npx vitest run

与只读 agent 的关键区别:测试编写者有 Write 和 Bash 权限,因为它的工作需要创建文件和执行命令。工具限制应该匹配 agent 的角色 -- 不是一律限制。


常见问题排查 ​

Subagent 超出工具白名单 ​

terminal
$ claude
> Use the security-scanner agent to fix the SQL injection in user.js

Claude: Launching security-scanner agent...

  [security-scanner] Attempted to use tool: Write
  Error: Tool 'Write' is not in the allowed list for agent 'security-scanner'.
  Allowed tools: Read, Glob, Grep
  
  [security-scanner] I found the SQL injection issue but I cannot fix it directly.
  My tools are restricted to read-only access. Here is the finding and 
  recommended fix for you to apply manually...

发生了什么:你让一个只读 agent 做修改。Agent 尝试使用 Write 但工具白名单拦截了。这是设计预期的行为 -- 安全扫描器应该报告问题,而不是修复它们。

修复:自己应用修复,或者使用有写权限的 agent(如测试编写者)来处理需要代码变更的任务。如果你想要一个"安全修复者"agent,单独创建一个在 tools 列表中包含 Write 的。

模型不匹配 ​

terminal
$ claude
> Use the architect agent to quickly check if routes.js looks correct

Claude: Launching architect agent...

  Spawning agent: architect (claude-opus-4-6)
  [architect] Reading src/api/routes.js...
  [architect] Reading src/services/user.js...
  [architect] Reading src/services/order.js...
  [architect] Reading all remaining 5 files...
  [architect] Analyzing full dependency graph...
  
  (30 seconds later)
  
  # Full Architecture Assessment
  ## System Map...
  ## Layer Analysis...
  ## Dependency Graph...
  [... 800 token detailed report for a simple question ...]

发生了什么:你用 Opus 驱动的架构师 agent 来回答一个快速问题。Opus 更慢,产出的分析比需要的更深入。你等了 30 秒才拿到一个快速检查就够了的报告。

修复:为任务匹配合适的模型。快速检查直接问主会话(使用你当前的模型,通常是 Sonnet)。把架构师 agent 留给完整的架构审查。你也可以创建一个轻量级的 quick-review agent 用 Sonnet 来快速回答:

yaml
---
name: quick-review
model: claude-sonnet-4-6
tools: [Read, Glob, Grep]
---
Give brief, focused answers. No full reports unless asked.

Subagent 超时 ​

terminal
$ claude
> Use the architect agent to analyze this monorepo

Claude: Launching architect agent...

  Spawning agent: architect (claude-opus-4-6)
  [architect] Glob: src/**/*.js -> 847 files found
  [architect] Read: src/api/routes/index.js
  [architect] Read: src/api/routes/auth.js
  [architect] Read: src/api/routes/users.js
  ... (reading files one by one) ...
  
  Warning: Agent 'architect' reached maximum turns (25). Returning partial results.
  
  # Architecture Assessment (PARTIAL)
  ## System Map
  Only analyzed 120 of 847 files before reaching turn limit.

发生了什么:Agent 试图逐个读取大型 monorepo 中的所有 847 个文件,在完成之前就达到了 maxTurns 限制。

修复:如果需要可以在 agent 定义中添加 maxTurns,但更重要的是限定 agent 的任务范围。不要说"分析这个 monorepo",而是说"分析 src/api/ 和 src/services/ 目录"。你也可以更新 agent 指令让它优先广度而非深度:

## For large codebases (>100 files)
1. Glob to map the full structure
2. Read only entry points and high-fan-in modules
3. Use Grep to trace dependencies without reading every file
4. Sample 2-3 files per directory instead of reading all

Agent 设计原则 ​

来自 ECC:模型选择 ​

ECC 为不同 agent 使用不同模型:

  • Architect:Opus(深度分析、权衡推理)
  • Code reviewer:Sonnet(速度快,模式匹配够用)
  • TDD guide:Sonnet(迭代式,需要速度而非深度)

匹配模型到任务。不要什么都用 Opus -- 更慢更贵。不要用 Haiku 做架构分析 -- 会遗漏细微差别。

来自 gstack:角色化 Agent ​

gstack 的角色系统分配不同的 视角,不只是不同的工具:

  • CEO 审查:"值得做吗?业务影响是什么?"
  • Design 审查:"UX 流程合理吗?可访问性如何?"
  • Engineering 审查:"架构合理吗?风险在哪?"

你可以将此应用到自己的 agent 上。上面的安全扫描器是一个"工程审查"agent。你可以添加一个从用户视角评估功能的"产品审查"agent。

Worktree 隔离 ​

对于需要做实验性修改的 agent:

yaml
---
name: refactor-experiment
isolation: worktree
tools:
  - Read
  - Write
  - Edit
  - Bash
---

Agent 在独立的 git worktree 中工作。修改不影响你的工作目录。实验成功就合并,失败就删除 worktree。


练习:构建 3-Agent 审查管线 ​

创建三个从不同视角审查代码的 agent:

  1. product-reviewer:只读。从用户角度评估。"这个功能合理吗?直觉吗?"
  2. security-reviewer:只读。使用 Demo 22 的安全扫描模式。
  3. performance-reviewer:只读。查找 N+1 查询、不必要的分配、缺失缓存。

在同一代码库上运行全部三个。比较发现 -- 应该互补而非重叠。

成功标准 ​

  • [ ] 三个 agent 文件存在于 .claude/agents/
  • [ ] 每个有合适的工具限制(全部只读)
  • [ ] 每个产出结构化报告,带具体 file:line 引用
  • [ ] 发现在 agent 之间不显著重叠
  • [ ] 可以在单个 Claude 会话中运行并获得组合视图

知识检测 ​

为什么 ECC 将其 code-reviewer agent 限制为只有 Read、Glob 和 Grep?
为了通过限制工具使用来节省 API 成本
为了防止 reviewer 修改代码而不是报告问题
因为 Sonnet 不能使用 Write 或 Bash 工具
为了让 agent 在更少可用工具时运行更快
你的架构师 agent(Opus)读取了 40 个文件并产出了一个 500 token 的报告。你的主会话上下文增长了多少?
约 40 个文件的 token 量(agent 读取的所有内容)
约 500 tokens(只有返回的报告)
零 token(subagent 完全不可见)
agent 上下文的一半(共享内存)
安全扫描器发现了一个潜在问题但只有 70% 的置信度。会发生什么?
以低置信度警告报告该问题
完全丢弃该发现因为低于 80% 门槛
将该发现上报给主会话让人工审查
将严重程度从 P0 改为 P3
你需要快速检查一个文件但唯一的 agent 使用 Opus。你应该怎么做?
照样使用 Opus agent -- 更高质量总是值得等待的
直接问主会话而不是启动 agent
每次需要不同模型时都创建一个新的 agent 文件
把现有 agent 改成 Sonnet,用完再改回来

总结 ​

Subagent 让 Claude Code 从单一对话扩展为专家团队。本章的三个模式 -- 架构师、安全扫描、测试编写 -- 覆盖了最常见的需求。

要点:

  • 上下文隔离是使用 subagent 的主要原因 -- 避免 GSD 的 "Context Rot"(上下文使用超过 50% 后质量下降)
  • 工具限制强制 agent 的角色 -- 能 Write 的 reviewer 不是 reviewer
  • 模型选择很重要 -- Opus 用于深度,Sonnet 用于速度
  • ECC 的 80% 置信度门槛消除审查中的误报
  • Worktree 隔离让 agent 无风险地实验

下一章:Chapter 9: Agent 团队 -- 编排多个 agent 并行处理复杂任务。

基于 MIT 许可发布