「Issue 番号だけ渡したら PR まで勝手に進んでほしい」
開発者なら一度は夢見るやつです。私たちはこれを Claude Code(Pro $20/月、Max $100-200/月)の Workflow 機能 で実際に動かしています。Issue 番号をひとつ渡すと、要件分析、実装計画、コーディング、テスト、品質評価、PR 作成までが無人で進みます。多い日には1日で50 commit 分の issue 処理がこのフローで流れます。
Workflow 機能は .claude/workflows/ に置いた JavaScript ファイルで、複数の subagent をオーケストレーションする仕組みです。実際に運用しているスクリプトを例に、この機能で何ができるか、どこに発想の転換があるかを紹介します。
先に結論を言うと、Workflow 機能の本質は 複数エージェントの段取りを、AI の自律判断ではなくただの JavaScript として書ける ことです。地味に聞こえますが、マルチエージェント自動化の信頼性はここで決まります。
この記事で学べること
- Workflow 機能の基本構造と、skill / subagent との役割の違い
agent()/parallel()/workflow()という 3 つの API で何ができるか- 「判断は LLM、進行はスクリプト」という設計が効く理由
- モデル一括切替・テレメトリ記録・人間への差し戻しなど実運用パターン
- workflow runtime の制約(ESM import 不可など)とその回避策
Workflow 機能とは — skill・subagent と何が違うか
Claude Code には、エージェントに仕事をさせる仕組みが複数あります。位置づけを整理するとこうなります。
| 仕組み | 実体 | フローを制御するのは誰か |
|---|---|---|
| skill | Markdown の手順書(SKILL.md) | LLM(読んで解釈・実行) |
| subagent | 単機能の作業者定義(.claude/agents/*.md) | 呼び出し側の LLM |
| workflow | JS スクリプト(.claude/workflows/*.js) | スクリプト(決定論) |
skill はよくできた仕組みですが、手順書の実行順や回数は LLM の解釈に委ねられます。10 回中 9 回は正しく動いても、1 回は「テストが通らなかったので評価は省略しました」のような手抜きが起きます。フェーズが多く、エージェントを何体も束ねる長いフローほどこの揺らぎが致命傷になります。
Workflowは「段取り」をコード化します。ファイルは meta を export してフェーズを宣言します。実物の冒頭はこんな形です。
export const meta = {
name: 'dev-flow',
description: 'Issue から LGTM まで: 分析 → 計画 → 実装 → test green → 評価 → PR',
phases: [
{ title: 'Setup' }, { title: 'Analyze' }, { title: 'Plan' },
{ title: 'Implement' }, { title: 'Validate' }, { title: 'Evaluate' },
{ title: 'PR' },
],
}
これで /dev-flow 123 のようにスラッシュコマンドとして起動でき、args でパラメータを受け取れます。進捗はフェーズ単位で可視化されます。
中核 API は agent() — LLM の判断を JSON で受け取る
workflow script の中心は agent() です。プロンプト・agent 種別・返り値の JSON Schema を渡して subagent を起動すると、schema に沿った JSON が返ってきます。
phase('Analyze')
const req = await agent(
`Issue の要件・受入条件を抽出し、実装規模を micro / standard / complex で評価して返せ`,
{ agentType: 'dev-runner', schema: REQ, label: 'analyze' },
)
// req.acceptance_criteria や req.shape を、ただの JS 変数として扱える
ここが発想の転換点です。エージェントの出力が 型のあるデータ として着地するので、分岐・ループ・集計はぜんぶ普通のプログラミングになります。中間結果を外部の状態ファイルに書き出す必要もありません。
たとえば実運用のスクリプトでは、抽出した受入条件が空だったり曖昧な点が閾値を超えたりしたら、その場で構造化した値を返して止まります。
if ((req.acceptance_criteria ?? []).length === 0 || ambiguities.length > AMBIGUITY_MAX) {
return {
status: 'needs_clarification',
missing_context: ambiguities,
// 呼び出し元が人間に確認し、Issue を更新して再起動する
}
}
「曖昧なまま実装し出来上がってから人間が泣く」コースを、if 文 1 つで止められるわけです。
設計思想: 判断は LLM、進行は JavaScript
実運用している Issue→PR の workflow は、おおまかに次のフェーズで進みます(実物にはこの間にセキュリティ検査や merge 判定も挟まっています)。
| フェーズ | やること | 呼ぶエージェント |
|---|---|---|
| Setup | worktree とブランチ作成 | 軽量 runner(haiku 固定) |
| Analyze | Issue 分析・実装規模の判定 | runner |
| Plan | 実装計画の作成 ⇄ 計画レビュー | planner / plan-reviewer |
| Implement | 計画タスクを直列・並列で実装 | implementer |
| Validate | テスト実行 → green になるまで修正 | runner / implementer |
| Evaluate | 独立した評価者による品質判定 | evaluator |
| PR | commit と PR 作成 | runner |
図にするとこうです。表では見えない 差し戻しループ と 人間への出口 が、実はこの workflow の本体です。
計画の質を判断する、コードを書く、品質を評価する——ここは LLM の仕事です。一方で「計画レビューを最大何回まで回すか」「評価が fail なら計画と実装のどちらに差し戻すか」はスクリプトの仕事。ループ上限はプロンプトの口約束ではなく定数です。
const PLAN_MAX = 8 // 計画レビュー上限
const EVAL_MAX = 10 // 評価差し戻し上限
const GREEN_MAX = 3 // test green までの実装差し戻し上限
「最大 3 回まで再試行してください」と祈る方式との違いは運用すると体感できます。for 文は7回目でも飽きません。
実装規模も micro / standard / complex の 3 段階に分類します。micro なら計画レビューと評価をスキップし、complex ならフルのレビュー反復を回す深さの切替を if 文で行っています。全部の Issue に最重装備のゲートを通すと時間もコストも無駄です。この出し分けが決定論でできるのは大きいです。
parallel() — 独立タスクだけを並列にする
計画フェーズの返り値は、タスクを serial / parallel の 2 レーンに分解した JSON です。依存のないタスクは parallel() で同時実装させます。
for (const t of plan.serial) {
results.push(await agent(implPrompt(t), { agentType: 'implementer', schema: IMPL }))
}
const par = plan.parallel.map((t) => () =>
agent(implPrompt(t), { agentType: 'implementer', schema: IMPL }))
const parResults = await parallel(par)
ポイントは、並列の安全条件をスクリプト側で強制していることです。並列レーンに置けるのは変更ファイルが互いに重ならないタスクだけです。ファイルが衝突するタスクを計画が並列に置いてきたら、orchestrator 側が検出して直列レーンへ降格させます。LLM に「衝突しないように分解してね」とお願いするだけでなく、信用せずに検査するのが安全です。
また、worktree は 1 つを全エージェントで共有します。エージェントごとに worktree を分けると成果物が分散して統合作業が発生するためで、「並列にするのはタスク、分離するのはファイル」という整理です。詳細はworktree 並列開発の記事に書きました。
workflow() — 反復ループをサブワークフローに部品化する
PR 作成後の「レビュー ⇄ 修正を LGTM まで反復」処理は、別ファイルの workflow に切り出して呼んでいます。
const iterate = await workflow('pr-iterate', { pr: pr.pr_number })
呼ばれる側は単体でも /pr-iterate <PR番号> として起動でき、既存 PR にだけ自動レビューを回す使い方も成立します。なおネストは 1 段までで、サブワークフローの中でさらに workflow() を呼ぶとエラーになります。実物には「ここに足すと 2 段になるので入れないこと」というコメントが置いてあります。
実運用で効いた Workflow ならではのパターン
モデルの一括切替が定数 1 行
agent() は呼び出しごとに opts.model でモデルを上書きできます。品質ゲート系の 4 エージェント(計画・計画レビュー・評価・PR レビュー)のモデルをスクリプト冒頭の定数で一括管理しています。
const QUALITY_MODEL = 'fable' // 戻すときはこの 1 行を 'opus' にする
const plan = await agent(prompt,
{ agentType: 'dev-planner', model: QUALITY_MODEL, schema: PLAN })
直近では品質ゲート系エージェントを新モデルへ切り替える試験運用をこの形で始めました。レビュー反復の削減効果をテレメトリで実測し、ダメなら定数を書き戻すだけで完全に元へ戻ります。A/B 実験のスイッチが定数 2 行で済むのは、段取りがコードである恩恵そのものです。
テレメトリの記録もコードで確定実行
「完走したらログを記録して」とエージェントに頼む方式は、たまに忘れられます。workflow なら完走時の処理もただのコードパスです。実運用では、実装規模の判定結果・計画と評価のイテレーション数・評価 verdict などを完走時に journal スクリプトへ記録しています。ループ上限のような決め打ちパラメータを、勘ではなく実測で調整するための原資料になります。
行き止まりは throw ではなく構造化 return
workflow の返り値も JSON です。要件が曖昧で進めないときは例外で死ぬのではなく、status: 'needs_clarification' と「何が分からなかったか」のリストを返します。呼び出し元のセッションがそれを受けて人間に質問し、Issue を更新してから再起動する。worktree は保持されるので途中までの作業は無駄になりません。この「人間へ戻す出口」の設計は「PR自動化の最後に人間が必ず見る3点」で掘り下げています。
ハマりどころ: workflow runtime の制約
ここまで「ただの JavaScript」と書いてきましたが、実行環境は Node.js そのものではありません。import すら書けません。運用で踏んだ制約を 3 つ挙げます。
1. ESM import が使えない。 workflow loader はモジュール import を解決しないため、複数の workflow で共有したい純関数は各ファイルへインラインコピーする必要があります。手作業のコピーは必ず腐ります。私たちはマーカー区間を canonical ファイルから自動再生成する generator を作りました。生成区間が canonical と全文一致することを CI のテストで保証しています。直近の改修でこの同期を generator 化したのは手動同期の弱点をまとめて潰すためでした。関数単位の正規表現抽出が template literal で誤抽出する、手書きの関数名リストに追加し忘れると新規関数が無監視になる、といった弱点です。
2. fs も Date.now() も無い。 ファイル書き出し API が無く、生成した長文の PR コメントを投稿したいときは間接手順になります。本文を delimiter 付きでプロンプトへ埋め込み、agent 側に Write tool で一時ファイルへ保存させてから --body-file で投稿させます。shell の echo や heredoc を経由させないのは、コードフェンスやバッククォートを含む本文がそこで壊れるからです。
3. effort は指定できない。 agent() の opts には model はあっても推論の深さ(effort)はありません。深さを変えたいエージェントは frontmatter 側で固定します。モデルを恒久的に固定したい用途も、定数 override ではなく専用のエージェント定義を作り agentType を切り替える方が素直です。
まとめ
Workflow 機能は、マルチエージェント自動化の主導権を LLM からスクリプトへ取り戻す仕組みです。
- 段取り(フェーズ遷移・ループ・分岐・並列)は JavaScript が決定論で実行する
- 判断(計画・実装・評価)は
agent()で LLM に委譲し JSON Schema で受け取る - 反復処理は
workflow()のサブワークフロー(ネスト 1 段まで)に部品化する
LLM に任せる範囲が狭いほど自動化は退屈になり、退屈になるほど信頼できます。このパイプラインの品質ゲートをエージェントがすり抜けようとした攻防は品質ゲートの抜け道の記事に書きました。その攻防が成立する土台も「ゲートが必ず実行される」という workflow の決定論性です。複数エージェントの自動化が「うまく動くときはすごいが、たまに脱線する」段階で止まっている方は、段取りをスクリプトへ移すこの機能から試してみてください。



