Claude Codeを並列で動かしたら、直列70秒の処理が11.8秒で終わった。 この記事では、claude CLIを子プロセスで大量並列起動するオーケストレーターの作り方と、質問の重さに応じて自動で最適な実行方式を選ぶルーターの実装を紹介する。読んだ後、同じ仕組みを自分の環境で動かせるようになる。
背景:Claude Codeは1回1回が遅い
Claude Code(claude CLI)は強力だけど、1回の実行に平均10秒かかる。「SaaS企業20社を調べて」みたいなタスクだと、1社ずつ直列で回すと200秒(3分以上)。これを並列化すれば理論上は10秒で終わるはず。
やりたかったこと:
- N個のタスクを同時実行して結果を統合
- レート制限に引っかかったら自動リトライ
- 質問の重さに応じてLIGHT/BATCH/DEEPを自動選択
構成:たった2ファイル
orchestrator/
orchestrator.ts ← 並列バッチエンジン(p-limit + spawn)
smart.ts ← 自動ルーター(LIGHT/BATCH/DEEP判定)
package.json
依存は p-limit(並列数制御)と tsx(TypeScript実行)だけ。
Step 1: claude CLIを子プロセスで並列起動
最初にClaude Code SDKの query() を試したが、Node.js v25との互換性問題で動かなかった。結局 spawn で claude コマンドを直接叩く方式 が一番安定した。
import { spawn } from "node:child_process";
function runClaude(prompt: string, options: { timeout: number }): Promise<string> {
return new Promise((resolve, reject) => {
const child = spawn("/opt/homebrew/bin/claude", [
"--output-format", "json",
"--max-turns", "3",
"-p", prompt,
], {
stdio: ["ignore", "pipe", "pipe"], // stdinをignoreにしないと3秒待ちが発生
});
let stdout = "";
child.stdout.on("data", (d: Buffer) => { stdout += d.toString(); });
const timer = setTimeout(() => {
child.kill("SIGTERM");
reject(new Error("TIMEOUT"));
}, options.timeout);
child.on("close", (code) => {
clearTimeout(timer);
if (code !== 0) { reject(new Error(`Exit ${code}`)); return; }
const json = JSON.parse(stdout);
resolve(json.result ?? stdout);
});
});
}
ポイントは stdio: ["ignore", "pipe", "pipe"]。これがないと claude がstdin待ちで3秒ハングする。
Step 2: p-limitで並列数を制御
Promise.all だけだと全タスクが同時に走ってAPIレート制限に引っかかる。p-limit で同時実行数を制御する。
import pLimit from "p-limit";
const limit = pLimit(10); // 同時10並列
const promises = tasks.map((task) =>
limit(async () => {
const result = await executeTask(task);
completed++;
showProgress(completed, total, task.id, result.status);
return result;
})
);
const results = await Promise.all(promises);
Step 3: 適応的並列度 + リトライ
concurrency 50で試したら全タスクがtimeoutした。APIレート制限を検知して自動で並列度を下げる AdaptiveLimiter を実装。
class AdaptiveLimiter {
reportSuccess(): void {
this.consecutiveSuccess++;
// 10回連続成功で並列度UP
if (this.consecutiveSuccess >= 10) {
this.currentConcurrency = Math.min(this.currentConcurrency + 2, this.maxConcurrency);
}
}
reportFailure(): void {
this.consecutiveFailure++;
// 3回連続失敗で並列度半減
if (this.consecutiveFailure >= 3) {
this.currentConcurrency = Math.max(Math.floor(this.currentConcurrency / 2), 2);
}
}
}
リトライは指数バックオフ(5秒→10秒→20秒)で実装。レート制限・タイムアウトのみリトライ対象にして、それ以外のエラーは即失敗にする。
Step 4: smart.ts — 質問の重さで自動ルーティング
毎回 npx tsx orchestrator.ts --input tasks.json とか打ちたくない。質問を投げるだけで自動判定する smart.ts を作った。
ユーザー入力
↓
[自動分類] Claude が LIGHT / BATCH / DEEP を判定
↓
┌─ LIGHT → そのまま回答(10秒)
├─ BATCH → tasks.json自動生成 → 並列実行(50秒)
└─ DEEP → 質問分解 → 並列収集 → Opus統合(3分)
判定基準:
| モード | トリガー | 例 |
|---|---|---|
| LIGHT | 単純な1問 | 「Reactとは」 |
| BATCH | 同種N件(N≧3) | 「SaaS5社を調べて」 |
| DEEP | 多角的分析が必要 | 「物流SaaSの勝ち筋を徹底調査」 |
DEEPモードの核心は 分解→並列→統合 の3段パイプライン:
- Claudeが質問を10-30個の独立した調査タスクに分解
- orchestrator.tsで全タスクを並列実行
- 全結果をClaude Opus(最上位モデル)に渡して統合レポート生成
ベンチマーク結果
直列 vs 並列(6タスク)
| 方式 | 所要時間 | 倍率 |
|---|---|---|
| 素のclaude CLI × 6回直列 | 70秒 | 基準 |
| オーケストレーター直列 (c=1) | 51.4秒 | 1.4倍 |
| オーケストレーター並列 (c=6) | 11.8秒 | 5.9倍 |
20タスクの実測
| 方式 | 所要時間 |
|---|---|
| 素CLI直列(推定) | 約180秒 |
| v1 (c=5) | 76.7秒 |
| v2 適応的並列 (c=20) | 55.0秒 |
DEEPモード実測
「物流SaaSの今後の勝ち筋を徹底調査して」→ 8ソース並列収集 → Opus統合 → 163秒で完了。手動で同じことやったら数時間はかかる。
ハマりポイント
1. Claude Code SDKがNode.js v25で動かない
SDKバンドル版の cli.js 内部で Cannot read properties of undefined (reading 'prototype') エラー。spawn で直接 claude コマンドを叩く方式に切り替えて解決。
2. stdinを閉じないと3秒ハングする
execFile だとstdinがパイプのまま残り、claude が入力待ちで3秒停止する。spawn + stdio: ["ignore", "pipe", "pipe"] で解決。
3. concurrency 50で全timeout
APIレート制限。適応的並列度制御で解決。最初は5から始めて、成功が続いたら徐々にUPする方式が安定。
Step 5: スキル化 — 「徹底調査して」で自動発動
ここが一番大事。せっかくオーケストレーターを作っても、毎回 npx tsx smart.ts -p "..." と打つのは面倒すぎる。Claude Codeの スキル として登録すれば、普段の会話の中で「徹底調査して」と言うだけで自動発動する。
スキルファイルを作る
~/.claude/skills/smart-research/SKILL.md を作成する。
---
description: "質問の重さを自動判定し、LIGHT/BATCH/DEEPを切り替えて最適な方法で調査する"
---
# smart-research: 自動ルーティング調査エンジン
## 自動発動トリガー
| パターン | モード |
|----------|--------|
| 「N社調べて」「N個比較」「リスト化」 | BATCH |
| 「徹底調査」「深掘り」「100ソース」「多角的に」 | DEEP |
| 「市場調査」「競合分析」「業界分析」 | DEEP |
| 「〜について調べて」(単一対象) | LIGHT |
## 実行方法
### LIGHT(単純な質問)
そのまま回答する。
### BATCH(同種N件の並列調査)
1. 質問からタスクリストを自動生成
2. orchestrator.ts で並列実行
3. 結果を統合して報告
### DEEP(複雑な1問の徹底調査)
1. 質問を10-30個の独立した調査タスクに分解
2. orchestrator.ts で並列収集
3. 全結果を統合してレポート生成
team-leadのルーティングにも追加
既にカスタムsubagentでteam-leadを定義しているなら、ルーティングテーブルに1行追加するだけ。
| 「徹底調査」「N社調べて」「市場調査」「100ソース」 | smart-research スキル |
実際の使用感
これで普段の会話がこう変わる:
Before:
自分: 物流SaaSの競合を調べたい
Claude: [1社ずつ順番に検索... 5分経過]
After:
自分: 物流SaaSの競合を徹底調査して
→ [DEEP] 自動判定
→ 20個の調査タスクに分解
→ 20エージェントが同時にWeb検索
→ Opusが統合レポート生成
→ 3分で完了
何もコマンドを打っていない。 「徹底調査して」と言っただけ。裏側でオーケストレーターが勝手に動いて、分解→並列収集→統合をやってくれる。
これが最終的なゴール。CLIツールを作ること自体が目的じゃなく、普段の会話体験を変えることが目的だった。
全体アーキテクチャ
最終的にできた仕組みを図にするとこうなる。
「〜を徹底調査して」
↓
[smart-research スキル] 自動発動
↓
[分類] LIGHT / BATCH / DEEP を判定
↓
[DEEP の場合]
↓
Phase A: 質問を20個の調査タスクに分解
↓
Phase B: orchestrator.ts で20並列実行
├── エージェント1: 競合調査 → Web検索
├── エージェント2: 市場規模 → Web検索
├── エージェント3: 技術動向 → Web検索
├── ...(同時20並列)
└── エージェント20: 投資動向 → Web検索
↓
Phase C: 全結果をOpusが統合レポートに
↓
results/ にMarkdown保存 + 会話内で表示
ベンチマーク:この仕組みで何が変わったか
| 操作 | Before | After | 改善 |
|---|---|---|---|
| 1社調査 | 10秒 | 10秒 | 変わらない |
| 6社調査 | 70秒 | 11.8秒 | 5.9倍 |
| 20社調査 | 約180秒 | 55秒 | 3.3倍 |
| 複雑な1問の深掘り | 手動で数時間 | 3分 | 桁違い |
まとめ:明日から使えるアクションリスト
-
npm install p-limit tsxして orchestrator.ts をコピー - tasks.jsonに並列実行したいタスクを書く
-
npx tsx orchestrator.ts --input tasks.json --concurrency 10で動作確認 - smart.tsで自動ルーティングを追加
-
~/.claude/skills/smart-research/SKILL.mdを作ってスキル登録 - 会話で「徹底調査して」と言うだけの世界へ
ツールを作って終わりじゃなく、普段の会話に溶け込むまで自動化するのがポイント。コマンドを打つ時点で負け。「調べて」と言ったら勝手に最適な方法で動く——それが自分にとっての理想形だった。