Graph Engineering with Claude — Dynamic Workflows で「線」を「グラフ」にする¶
作成日: 2026-08-08 出典 / きっかけ: @0xCodez「Graph Engineering with Claude: 14-Step roadmap from 0 to graph architect」(2026-07-20、498万インプレッション) 一次資料: Anthropic 公式ドキュメント / 発表ブログ / Bun 移行事例 関連: loop_engineering_入門 claude_code_並行開発_branch_fork_btw_worktree_整理 cursor_agent_swarm_planner_worker経済性_整理 Fable5_3日間で300万円課金_並列エージェント運用の物理的課題 llm_agent_sandbox_隔離技術 AI支出を横ばいに保つ_デフォルト_ルーティング_キャッシング
0. 要点(3行)¶
- 一言で言うと: マルチステップのエージェントを「上から順に並んだ行列」ではなく DAG(ノード=仕事、エッジ=データの流れ) として描き直す話。「A の次に B」ではなく「B は A の出力を読むか?」だけがエッジの有無を決める。読まないなら待つ必要はない。
- 何が効くか: Claude Code の dynamic workflows(2026-05-28 GA)は、そのグラフを Claude が書いたプレーン JavaScript として実行する。制御フロー(ループ・分岐・fan-out)はコード=決定的、判断はノード=モデル。中間結果はスクリプト変数に留まるので、Claude の context には最終回答しか載らない。
- 実用上の勘所: 元ツイートは「オーケストレーションはタダ」と強調するが、run 全体は通常セッションより大幅に高い(Bun 移行は API 換算 約 $165,000)。トポロジー(
parallel()の barrier かpipeline()か)がレイテンシとコストを直接決める。既定はpipeline()。
出典の性格に注意: 元ツイートは @0xCodez(SF 拠点の AI 系コンテンツ発信者、Substack: movez.substack.com)による二次資料であり、内容の実質は Anthropic 公式ドキュメントの再構成である。「14-Step roadmap」は同アカウントの定型フォーマット(Loop engineering 版も同じ型)。語彙の整理としては質が高いが、仕様の細部は公式 docs を正とする(下記 §5 に差分を列挙)。
1. 中核の発想 — 「and then」はエッジではない¶
元ツイートの一番の芯はここに尽きる。
A node is a unit of work — one agent, one bounded job. An edge is a dependency: this node's output feeds that node's input. The mistake is treating "and then" as an edge.
【あなたが書きがちな線形エージェント(=縮退したグラフ)】
A ──→ B ──→ C ──→ D
各ノードは in 1本 / out 1本。C が詰まれば D は永遠に来ない。
そして矢印の2〜3本は「打った順」でしかなく、データが流れていない。
【問い】各矢印について: 次のノードは前のノードの出力を読むか?
読まない ⇒ エッジではない ⇒ 待ちは純粋な無駄
【描き直した結果】
┌──→ B ──┐
A ────┼──→ C ──┼──→ E (B/C/D は独立、E だけが全部を必要とする)
└──→ D ──┘
「要約して、それから天気を教えて」——この2つの間にエッジは無い。天気は要約を消費しない。線形スクリプトはこれを不必要に鎖に繋いでいる。
判定基準は1つだけ: そのエッジを実際にデータが渡るか。 渡らないなら切る。切ると鎖は自然に「幅」に崩れる。
2. Dynamic Workflows とは何か(公式仕様)¶
| 項目 | 内容 | 出典 |
|---|---|---|
| 実体 | Claude が書く プレーン JavaScript のオーケストレーションスクリプト。ランタイムが会話とは別の隔離環境でバックグラウンド実行 | 公式 docs |
| GA | 2026-05-28(research preview を経て一般提供) | 発表ブログ |
| 要件 | Claude Code v2.1.154 以降。全有料プランで利用可(Pro のみ /config の Dynamic workflows 行で明示的に有効化) |
公式 docs |
| 提供面 | CLI / Desktop / IDE 拡張 / web / claude -p / Agent SDK / Anthropic API / Bedrock / Vertex / Microsoft Foundry |
公式 docs |
| 中間結果の置き場 | スクリプト変数(Claude の context ではない)。だから context を溢れさせずに数百エージェントまでスケールする | 公式 docs |
位置づけの整理(公式 docs の比較表を要約)。「誰が計画を保持しているか」が違いの本質である。
| Subagents | Skills | Agent teams | Workflows | |
|---|---|---|---|---|
| 実体 | Claude が spawn する worker | Claude が従う指示 | lead が peer を監督 | ランタイムが実行するスクリプト |
| 次に何が走るか決めるのは | Claude(ターンごと) | Claude | lead agent | スクリプト |
| 中間結果の在り処 | context window | context window | 共有タスクリスト | スクリプト変数 |
| 再利用できるもの | worker 定義 | 指示文 | チーム定義 | オーケストレーションそのもの |
| 規模 | 1ターンに数個 | 同左 | 数体の長寿命 peer | 1 run に数十〜数百 |
| 中断時 | ターンをやり直し | 同左 | teammate は走り続ける | 同一セッション内で resume 可 |
3. API 早見表(公式 docs + Workflow ツール仕様)¶
スクリプトの先頭は必ず export const meta = {...}(純粋なリテラル。変数・関数呼び出し・スプレッド不可)。本体はトップレベル await 可のプレーン JS。
export const meta = {
name: 'audit-routes',
description: 'Audit every route handler for missing auth checks',
phases: [{ title: 'Discover' }, { title: 'Audit' }],
}
const found = await agent('List every .ts file under src/routes/.', {
schema: { type: 'object', required: ['files'],
properties: { files: { type: 'array', items: { type: 'string' } } } },
})
const audits = await pipeline(found.files, file =>
agent(`Audit ${file} for missing authentication checks.`, { label: file }),
)
return audits.filter(Boolean) // 停止/APIエラーの agent は null になる
| プリミティブ | 挙動 | 勘所 |
|---|---|---|
agent(prompt, opts) |
サブエージェントを1体 spawn。schema 無しなら最終テキスト(文字列)、schema 有りなら StructuredOutput ツール呼び出しを強制し検証済みオブジェクトを返す |
検証はツール呼び出し層で走るので、不一致ならモデルがリトライする。自前パース&祈りが要らない |
parallel(thunks) |
thunk 配列を同時実行。barrier(全部待つ) | throw した thunk は null に解決され、バッチ全体を落とさない。必ず .filter(Boolean) |
pipeline(items, ...stages) |
各 item を全ステージに独立して流す。barrier 無し | item A がステージ3にいる間に item B はステージ1でよい。壁時計=最遅の1本の鎖 |
phase(title) / log(msg) |
進捗表示のグルーピング / ナレーション行 | opts.phase で明示指定すると pipeline 内の race を避けられる |
workflow(name\|{scriptPath}, args) |
別ワークフローをインラインで子実行 | ネストは1段まで。並列上限・エージェント数・予算を親と共有 |
args |
Workflow 呼び出し時の入力がそのままグローバルに入る | JSON 値で渡す(文字列化した配列を渡すと args.map が落ちる) |
opts の主要オプション
| オプション | 用途 | 注意 |
|---|---|---|
schema |
出力を JSON Schema で強制 | 契約を固定する主要手段 |
model |
そのノードだけモデルを差し替え | 無指定はセッションモデルを継承(= 大規模 run が丸ごとセッション単価で課金される) |
effort |
low〜max の推論努力 |
機械的ステージは low、検証・判定だけ上げる |
isolation: 'worktree' |
専用 git worktree で実行 | 高コスト(1体あたり 約200〜500ms + ディスク)。並列で書き込むトポロジーだけに使う |
agentType |
カスタムサブエージェント種別 | schema と併用可 |
label |
表示ラベル上書き | 進捗ビューの可読性 |
ランタイム制約(公式 docs の "Behavior and limits" + ツール仕様)
- 同時実行は最大16体(CPU コアが少ないマシンではさらに減る。実装上は
min(16, cores-2))。超過分はキューイングされ順次実行されるので、100件渡しても全部完走する - 1 run あたり合計 1,000 エージェント(暴走ループの backstop)
- 1回の
parallel()/pipeline()に渡せるのは 最大 4,096 items(超過は明示エラー。黙って切り捨てない) - run 中のユーザー入力は不可(ステージ間で人間の sign-off が要るなら、ステージごとに別ワークフローとして走らせる)
- スクリプトから FS/シェルへの直接アクセス不可。読み書き・コマンド実行は agent の仕事
import()を含むスクリプトは起動前に失敗(モジュールロード不可)- TypeScript ではない(型注釈・interface・generics はパースエラー)
Date.now()/Math.random()/ 引数なしnew Date()は throw する(resume を壊すため)。時刻はargsで渡し、乱数性は index でプロンプトを変える
4. トポロジーの型(ここが実務の本体)¶
4-1. ダイヤモンド: split → work → merge¶
┌──→ [worker] ──┐
[split] ──────┼──→ [worker] ──┼──→ [merge] ──→ answer
└──→ [worker] ──┘
fan-out 並列に働く fan-in(barrier)
正準形は fan out → reduce → synthesize。fan-out で幅を稼ぎ、reduce は素の JS コードで圧縮し(flatten / dedupe / filter に agent は不要——ここが「エッジはタダ」の実質)、最後の1体が答えを書く。
「どうすればエージェントにもっと多くのステップを踏ませられるか」ではなく「split はどこで merge はどこか」を問うようになる、という発想の転換が肝。
4-2. parallel() か pipeline() か — 一番間違えるところ¶
【parallel(barrier あり)】 全員が最遅に足を引っ張られる
stage1: ████ ██████████ ███
↑ ここで全員待つ
stage2: ████ ███ ██████
【pipeline(barrier なし)】 速い item は先に終わる
item A: ██ ███ ██
item B: ████ ██████ ███
item C: ███ ██ ████
既定は pipeline()。 barrier が正当化されるのは、ステージ N が前ステージ全部の横断的な文脈を必要とするときだけ:
- 全結果を突き合わせた dedupe / merge
- 合計ゼロなら以降を丸ごとスキップする early-exit
- プロンプトが「他の findings と比較して」と参照する場合
逆に正当化しない理由:
- 「flatten / map / filter したいから」→ それはエッジ。pipeline のステージ内でやればいい
- 「ステージが概念的に別だから」→ 別であることと同期することは別
- 「コードが綺麗だから」→ barrier のレイテンシは実在する測定可能な無駄
臭い判定: parallel → transform → parallel と書いていて、真ん中の transform に item 間の依存が無いなら、その barrier は不要。
4-3. 検証をエッジに置く(グラフの本当のレバレッジ)¶
エージェントを増やすことではなく、その周りに巻ける構造が価値を生む。
| パターン | 中身 | いつ効くか |
|---|---|---|
| Adversarial verify | finding ごとに N 体の懐疑者を独立 spawn し、反証せよと指示。過半が生き残ったら通す(迷ったら refuted=true に倒す) | もっともらしいが誤っている findings を殺す |
| Perspective-diverse verify | 検証者ごとに異なるレンズを与える(correctness / security / 再現するか) | 失敗モードが複数ある場合。同一検証 N 回では絶対に捕れない |
| Judge panel | 異なる角度から N 案を生成 → 並列 judge で採点 → 勝者から合成しつつ次点の良い所を接ぐ | 解空間が広い設計判断。「1案を反復」より強い |
| Loop-until-dry | K ラウンド連続で新規ゼロになるまで finder を回す | サイズ未知の探索(バグ掃討など) |
| Completeness critic | 最後に「何が抜けているか(未実行のモダリティ・未検証の主張・未読の出典)」を問う1体 | 出てきたものが次ラウンドの仕事になる |
loop-until-dry の唯一にして最大の落とし穴(元ツイートが正しく強調している点):
dedupe は「確定した結果」ではなく「これまで見た全部」に対して行う。 confirmed に対して dedupe すると、judge に棄却された findings が毎ラウンド蘇り、ループは永遠に dry にならない。同じ袋小路を再発見し続けるためだけに金を払う機械が完成する。
4-4. 失敗の封じ込め¶
- 鎖では C が死ねば D は走らない。グラフでは失敗をノードに閉じ込める:
parallel()内の throw はnullに解決される →.filter(Boolean)が封じ込め装置 - fan-in は「全部揃っている前提」ではなく「欠けていても成立する」ように設計する
- より厄介なのはノード同士の踏み合い(並列でファイルを書くと衝突する)。対策が
isolation: 'worktree'。ただし並列で書き込むトポロジーだけの安全ベルトであり、全 run に課す税ではない(cf. llm_agent_sandbox_隔離技術、claude_code_並行開発_branch_fork_btw_worktree_整理)
4-5. モデルのティア分け¶
グラフにすると「どのノードが判断でどのノードが作業か」が可視化される。
- 退屈なノード(抽出・分類・転記)→ 安いモデル。判断が宿るノード(統合・裁定)→ 高いモデル
- 既定では全サブエージェントがセッションモデルを継承するので、放っておくと大規模 run が丸ごとセッション単価で請求される
- 環境変数
CLAUDE_CODE_SUBAGENT_MODELはスクリプトのmodel指定より優先される(公式 docs 明記) - 組織の
availableModelsallowlist がスクリプトの要求モデルを塞いでいる場合は代替モデルに substitute され、/workflowsの進捗ビューに要求/代替の両方が警告表示される
cf. AI支出を横ばいに保つ_デフォルト_ルーティング_キャッシング — 「デフォルトを安く、必要な所だけ上げる」は同じ構造の話。 cf. cursor_agent_swarm_planner_worker経済性_整理 — Cursor が「高性能プランナー+安価ワーカー」でコストを 1/8 にした実験。merge/plan ノードだけ上げて fan-out ノードを下げるという、まさに §4-5 の型の実測値。
5. 元ツイート vs 公式仕様 — 差分と補正¶
「概ね正確だが、7月20日時点の記述であり細部が古い/重要な制約が抜けている」というのが結論。
| # | ツイートの主張 | 検証結果 | 判定 |
|---|---|---|---|
| 1 | 「プロンプトに workflow と言えば Claude が書く」 | 古い。トリガーキーワードは現在 ultracode。workflow が literal keyword だったのは v2.1.160 より前。自然文("use a workflow")は両バージョンで有効 |
⚠️ 要補正 |
| 2 | parallel() は barrier、throw は null 化 |
公式 docs / ツール仕様と一致 | ✅ |
| 3 | 既定は pipeline()、barrier は横断依存があるときだけ |
一致(臭い判定まで含めて正確) | ✅ |
| 4 | schema でサブエージェントに構造化出力を強制、ツール層で検証・リトライ | 一致 | ✅ |
| 5 | 「コーディネーション自体はモデルトークンゼロ」 | 実質正しい(スクリプト実行にモデル推論を伴わず、トークンは agent() 単位で発生)が、run 全体は通常セッションより大幅に高いと公式が明記している。この警告がツイートには無い |
⚠️ 片面的 |
| 6 | s を押すと .claude/workflows/ に保存 |
一致。保存先はプロジェクト .claude/workflows/ と個人 ~/.claude/workflows/ を Tab で切替。同名ならプロジェクト側が勝つ |
✅ |
| 7 | /deep-research は本番出荷済みのグラフ |
一致。ただし v2.1.218 以降はユーザーが起動したときのみ実行(以前は Claude が自発起動できた)。v2.1.196 以降、検証不能だった主張は「反証」ではなく「未検証」として報告される | ✅(補足あり) |
| 8 | ultracode を on にすると全ての実質的タスクでワークフローを計画 | 一致。/effort ultracode = xhigh 推論努力 + 自動オーケストレーション。claude --effort ultracode で起動時指定可(v2.1.203+)。セッション限りでリセットされる |
✅ |
| 9 | Bun ランタイムの移植を adversarial review 込みで実現 | 一致(詳細は §6) | ✅ |
| 10 | — | 言及なし: サイズガイドライン(small<5 / medium<15 / large<50、既定 medium、v2.1.219+) |
🆕 追記 |
| 11 | — | 言及なし: Large workflow 警告(25体超 または 予測150万トークン超で進捗行に表示。advisory で run は止まらない。ultracode 中は非表示) | 🆕 追記 |
| 12 | — | 言及なし: resume のリプレイ規則(下記) | 🆕 重要 |
resume の規則 — トポロジー選択の第2の根拠¶
公式 docs にあってツイートに無い、実務上いちばん効く仕様。
再開時のリプレイはエージェントの起動順に従う。キャッシュ結果は最初に未完了だったエージェントで打ち止めになり、それ以降に起動したエージェントは完了済みでも全部やり直しになる。
起動順: A → B → C → D (B の実行中に停止)
再開時: A=キャッシュ / B=再実行(未完了だった)/ C=再実行 / D=再実行
↑ 完了していたのに、B より後に起動したので捨てられる
つまり 「多数の小さいエージェントに fan-out したワークフローの方が、1体の長いエージェントより進捗が残る」。ツイートはトポロジーをレイテンシの話としてしか語っていないが、実は中断耐性の話でもある。なお resume は同一セッション内のみ。Claude Code を終了すると次回セッションでは最初からになる。
6. 実証: Bun の Zig → Rust 移植¶
元ツイート step 09 が引く「実在の事例」。Anthropic の公式資料でも数値が揺れているので、両方を出典付きで併記する。
| 項目 | 発表ブログ | 移行事例ブログ |
|---|---|---|
| 規模 | Rust 75万行 | 100万行(less than two weeks で生成) |
| 期間 | 11日(first commit → merge) | 2週間弱 |
| テスト | 既存スイート 99.8% 通過 | マージ前に CI で既存スイート 100% 通過、マージ後に 19件のリグレッション(すべて修正済み) |
| 実行者 | — | Jarred Sumner(Bun 共同創業者・Anthropic Member of Technical Staff) |
| レビュー体制 | — | 典型的な失敗モード8種に対応した8体のレビュー用サブエージェント |
| コスト | — | uncached input 59億トークン + output 6.9億トークン ≒ API 価格で約 $165,000 |
| 成果 | — | バイナリ 19% 小型化(Linux/Windows)、性能 2〜5% 高速化 |
adversarial review の実際の形: 1体の Claude がコードを書き、別の2体には diff だけを渡し「これは壊れていると仮定せよ」と指示する。レビュー側は実装側の推論過程を見ない、出力だけを見る。これで、非同期コールバックが必要とする前に Box が drop されるプロセス生成コードの use-after-free など、マージされていたはずの実バグが捕捉された。
工程は6段階: ①ルールブック・依存マップ・ギャップ目録の作成 → ②サンプルファイルでルールをストレステスト → ③残り全部を翻訳 → ④コンパイル → ⑤スモークテスト → ⑥元実装との挙動一致検証。途中 Rust コンパイラが約16,000件のエラーを報告した局面もあったが、失敗を「実験の破綻」ではなく「キュー」として扱ったのが要点(この数字は二次資料経由なので要確認)。
7. 自分の環境への当てはめ¶
現状(2026-08-08 実測)
| 項目 | 値 | 含意 |
|---|---|---|
| Claude Code | v2.1.223 | v2.1.154+ なので dynamic workflows は利用可。keyword は ultracode、/deep-research は手動起動のみ、サイズガイドライン既定 medium がすべて適用される版 |
~/.claude/settings.json の effortLevel |
xhigh |
ultracode ではない = 自動ワークフロー計画は off。ワークフローは「明示的に頼んだときだけ」走る |
~/.claude/workflows/ |
存在しない | 保存済みワークフローはまだゼロ。s で保存した資産が1つも無い状態 |
| サイズガイドライン | medium(15体未満) |
既定のまま |
disableWorkflows |
未設定 | 有効 |
接続する既存の資産
~/harness-loop-lab— ハーネス/ループエンジニアリングの学習ラボ。「ハーネスは足場、ループは周回、そして仕事の形はグラフ」という三層の語彙が、まさにこのノートで補完される(cf. loop_engineering_入門)~/temporal-workflows/~/research-orchestrator— 既に Temporal でオーケストレーションしている。Temporal と dynamic workflows は競合ではなくレイヤーが違う: Temporal は「マシンを跨いで永続・再開する常駐ワークフロー」、dynamic workflows は「1セッション内でサブエージェント群を捌く実行時グラフ」。前者の1アクティビティの中身として後者を呼ぶ、が自然な合成~/.claude/loops/— launchd に焼き込んだ定期ウォッチャー。ツイートの「Ecosystem scan on a schedule」に対応するが、こちらはクラウド常駐ではなくローカル cron 層で既に実現済み~/.claude/CLAUDE.mdの並列 Task 実行ルール・サブエージェントのモデル指定ルール(機械的タスクは sonnet/haiku を明示)は、§4-5 のティア分けをすでに手作業でやっていることを意味する。ワークフロー化するならmodelオプションに移せばいい
すぐ試せる形(元ツイートの「Six graphs」から、自分の資産に合うもの)
- PR 差分の adversarial review — 既存の
/code-reviewセルフレビュー運用(rules/common/git-workflow.md)の強化版。diff サイズでルーティングし、大きければ correctness / security / performance の異なるレンズで並列監査 → judge panel で統合 - サイズ未知の探索 —
~/ai-engineering-digest/~/st-japan-labの巡回は現状 GitHub Actions の直列処理。loop-until-dry 型に置き換える余地がある(ただし既存の cron 層で足りているなら動かさない) - リポ横断監査 —
~/.claudeの rules/skills/settings の整合性チェックを1ファイル1エージェントで fan-out
注意: 現在のセッション設定では「ワークフローと /deep-research はユーザーが明示的に要求したときだけ使う」方針になっている。この方針はコスト面で妥当(§5 の #5、§6 の $165,000 を参照)。ワークフロー化は「毎回」ではなく「幅が本当に必要な仕事」に絞る。
8. まとめ — いつグラフにするか¶
| 状況 | 推奨アプローチ | 理由 |
|---|---|---|
| ステップが本当に前の出力を読む | そのまま線形(ワークフロー不要) | エッジが実在する。グラフにしても幅が出ない |
| 同じ処理を N ファイル / N ソースに適用 | pipeline() で fan-out |
barrier 無しで速い item から終わる |
| 全結果を突き合わせる必要がある(dedupe / ランキング / early-exit) | parallel() の barrier + reduce は素の JS |
barrier が正当化される唯一の場面 |
| findings の確度を上げたい | adversarial verify(反証させる)/perspective-diverse(レンズを変える) | 同一検証 N 回では捕れない失敗モードがある |
| サイズが分からない探索 | loop-until-dry(K ラウンド新規ゼロで停止) | 固定回数カウンタは末尾を取りこぼす。dedupe は「見た全部」に対して |
| 並列でファイルを書く | isolation: 'worktree' |
それ以外では高コストな税 |
| 大規模 run のコストが気になる | 狭いスライスで先に試す + 反復ノードを安いモデルへ + /model を run 前に確認 |
サイズガイドラインと Large workflow 警告は advisory であって上限ではない |
| ステージ間に人間の承認を挟みたい | ステージごとに別ワークフロー | run 中のユーザー入力はランタイムが受け付けない |
| セッションを跨いで永続・再開したい | Temporal 等の常駐層(dynamic workflows ではない) | resume は同一セッション内のみ |
結論の一文(元ツイートの締め、これは素直に良い):
A prompter asks a question. An architect draws a graph. 線形エージェントは天井ではなく、単に最初の形だった。人が文字を打つ順序と一致しているから、誰もが最初にそこへ手を伸ばすというだけの。
参考リンク¶
一次資料 - Orchestrate subagents at scale with dynamic workflows — Claude Code Docs(API・制約・resume・サイズガイドライン・保存先の正) - Introducing dynamic workflows in Claude Code — Anthropic(2026-05-28 GA、提供範囲、コスト警告) - How Anthropic runs large-scale code migrations with Claude Code(Bun 移植の工程・adversarial review・トークン実績) - Create custom subagents — Claude Code Docs
元ネタ(二次資料) - @0xCodez「Graph Engineering with Claude: 14-Step roadmap」(2026-07-20) - @0xCodez「Loop engineering: 14-step roadmap from prompter to loop designer」(同じ型の前作)
第三者の解説・検証 - Claude Code Adds Dynamic Workflows for Parallel Agent Coordination — InfoQ - Claude Code Workflows: Deterministic Multi-Agent Orchestration — alexop.dev - Trying Dynamic Workflow in Claude Code — azukiazusa.dev - Rewriting Bun in Rust — Simon Willison - Anthropic launches dynamic workflows for Claude Code — TestingCatalog
作成: 2026-08-08 / 最終更新: 2026-08-08