Files
analysis_claude_code/s16_workflow_runtime/README.ja.md
Cursor Agent bab23aabf7 Weave Anthropic harness essay into s16 teaching spine
Ground the progressive chapter in why custom harnesses exist, the three
single-window failure modes, dynamic vs static, tasteful patterns, when
not to use workflows, and sharp neighbors (s06/s13/s15/s17).

Co-authored-by: Xinlu Lai <CrazyBoyM@users.noreply.github.com>
2026-08-12 13:47:06 +00:00

17 KiB
Raw Blame History

s16: Workflow Runtime — レシピをコードに書く

English · 中文 · 日本語

s01 → ... → s14 → s15s16s17

「ターンごとのチャットは、10 秒ごとにシェフへメールするようなものです。Workflow は厨房が従えるレシピです。」

Harness 層: Orchestration — single-agent loop の上で multi-agent script を実行します。

モデルを信頼し、harness をエンジニアリングする。Workflow は orchestration 層での harness 設計です。


友だちとチャットだけで料理している場面を想像してください。「玉ねぎを切って」と送り、返事を待ち、「できた?」、次は「フライパンを……」。一品ならまだよいです。二十卓の宴会では、チャット自体がボトルネックになります。手順を忘れ、同じ指示を繰り返し、スマホが落ちたら最初からやり直しです。

ふつうの「モデルが指揮者」な会話も同じです。Workflow は書かれたレシピです。厨房runtimeがそれに従い、助手subagentが判断し、途中の器はカウンターに置かれます —— グループチャットの中ではありません。

そもそも harness は何のため?

デフォルトの Claude Code harness は、コーディング型の仕事に強いです。直す、走らせる、エラーを読む、また試す —— すべて同じループの中です。

ある種の仕事には、その上に定制の harness が要ります。深い調査、セキュリティ分析、agent teams、大規模な code review。SDK でその harness を先に手書きしてもよいです。あるいは —— ここが dynamic の発想ですが —— Claude にこのタスク用の harness をその場で書かせ、走らせ、良いものを保存できます。

コースのモットーを一段上げるとこうなります。各ステップの中ではモデルを信頼する。ステップ同士の構造はエンジニアリングで決める。

問題: ひとつの窓、三つの失敗

s01 から s15 まで、モデルは同じ context の中で計画と実行をします。直前の発見で次が決まるタスクには向いています。長く、大規模に並行し、硬い構造が要り、あるいは敵対的な検証が要る仕事では脆くなります。

Claude Code の設計者は、その単一ウィンドウで起きやすい三つの失敗に名前を付けています。平たい言葉では:

失敗モード どんな感じか
Agentic laziness途中で切り上げ 50 項目の review のうち 35 で「完了」と言う
Self-preferential bias自己びいき 自分の発見を自分で採点すると甘くなる —— 狐が鶏小屋を採点する
Goal drift目標の漂流 もともとの「X には触るな」が多ターンと圧縮のあいだに薄れる

会話履歴は、並行性・安定した結果の形・再開の三つを同時に預ける場所としても弱いです。多くのファイルを review する、調査してから検証する、N 個のモジュールを同じやり方で移す —— こうした仕事は形が先に分かっているので、なおさらその三つが要ります。

一息でいうアイデア

オーケストレーションを「賢さ」から「構造」へ移します。

Subagent は相変わらず判断します —— それぞれきれいな context と、焦点の定まった仕事で。script がループ、扇状の分配、マージを持ちます。中間結果は変数(と journalにあり、会話には入りません。分かれた助手 + script が握る制御フローが、laziness・自己チェックの偏り・drift への対抗策です。

Workflow Runtime Overview

1 回の Workflow tool call が、その script 実行を始めます。実行中に lifecycle / progress event が出て、最後に launch 情報・result・task state を含む tool result が返ります。

ふたつの入口 — dynamic と static

Claude Code は同じ厨房への入口を二つ開いています。

入口 渡すもの いつ使うか
Dynamic オーケストレーション用の JavaScriptscript、あとから scriptPath モデルがこのタスク用にレシピを書く
Saved name + args 良いレシピを例えば .claude/workflows/ に保存し、名前で再実行する

厨房は同じです。Dynamic は「今レシピを書く」、Saved は「カード箱から引く」—— 良い dynamic run の残した、再利用できる残りです。

このレッスンの外にはいとこもあります。static harnessあらかじめ書く Agent SDK / claude -p の编排です。static はあらゆるエッジケース向けなので、どうしても汎用になります。dynamic はこのタスク向けの特注です。形が合ったら saved にします。

このレッスンは Python の teaching runtime です。 同じアイデアを、1 行ずつ読める形で示します。デモは名前で saved workflow を登録します。概念は Claude Code の script 世界と 1:1 です。「モデルは実行可能コードを渡せない」と Claude Code について主張するのは誤りでした。ここでは単に、完全な JS インタプリタを埋め込まないだけです。

# Teaching adapter: saved の入口name + args
# Claude Code は script / scriptPath / resumeFromRunId も受け付ける。
WORKFLOW_TOOL = {
    "name": "Workflow",
    "input_schema": {
        "type": "object",
        "properties": {
            "name": {"type": "string"},
            "args": {"type": "object"},
            "resume_from_run_id": {"type": "string"},
            "resumeFromRunId": {"type": "string"},
        },
        "required": ["name"],
    },
}

プリミティブを学校のバザーで

学校のバザーでたくさんのケーキを焼くとします。各テーブルは 混ぜる → 焼く → 箱詰め。助手が味見と判断をし、レシピが順番を決めます。

Primitive 厨房での意味
agent(prompt, {schema, label, phase}) 助手ひとりに一つの仕事を頼む
pipeline(items, *stages) 既定。 各ケーキが自分で混ぜ→焼き→箱詰めを通る。A が箱詰め中でも、B はまだ混ぜているかもしれない
parallel(thunks) すべてのトレイが戻るまで待つ — 次の段が本当に全部の結果を必要とするときだけ
phase(title) 進捗ボードに「今は焼き工程」と出す
log(message) 短いステータスを一声
workflow(name, args) 小さなレシピを呼ぶ(ネストは 1 段)
args この run に渡す材料リスト
budget 使える「オーブン分」token

既定は pipeline です。次の段が直前の結果をすべてまとめて必要とするときだけ parallel を使います —— 全トレイを味見してから採点表を書く、といった場合です。

# 各 dimension が独立して audit → verify を通るstage 間に barrier なし)。
results = await ctx.pipeline(DIMENSIONS, audit, verify)
confirmed = [f for r in results if r for f in r["confirmed"]]

味のあるパターン(一覧の投げ売りではない)

パターンはレシピのスタイルだと思ってください。サンプル review-changes が頼るのは主に三つです。

パターン 平たい意味 サンプルでは
Fan-out-and-synthesize 仕事を分け、きれいな机で進め、あとでまとめる 4 つの dimension が pipeline で audit し、確認リストへ
Adversarial verification 別の助手が前の成果をあえて疑う 各 finding が verify agent を通ってから残る
Generate-and-filter 候補を出し、検査を通ったものだけ残す findings 入り → isReal だけ出し

同じ道具箱には、あとで出会うスタイルもあります。classify-and-act(種類で振り分け)、tournament(競わせて勝者を選ぶ)、loop-until-done(新しいものがなくなるまで回す)。コストに見合う、より明確で安全な結果が取れるときだけ使います。

答えを機械が読める形に

助手が散文で返してくると、次の stage は finding と verdict を reliably に対応づけられません。schema を渡します。runtime は JSON を求め、検証し、だめなら1 回だけ再試行します。それでもだめならその call はエラーになります(下の null 分離を参照)。

out = await ctx.agent(
    f"この変更に {dimension} 関連の問題がないか確認してください:\n{changes}",
    schema=FINDINGS_SCHEMA,
    label=f"audit:{dimension}",
)
# out は "findings" を持つ dict であり、段落ではない

あなたとの会話は自然言語でよいです。パイプラインには合うソケットが必要です。

助手がひとり失敗したとき

トレイがひとつ焦げても、艦隊全体を止めてはいけません。

  • parallel: 失敗した thunk はそのスロットで null / None になります。gather 自体は reject しません。
  • pipeline: 失敗した stage はその itemnull / None にし、残りの stage をスキップします。他の item は進み続けます。

マージ前に注意して絞り込みます。よくあるのは if r / .filter(Boolean) です。

verdicts = await ctx.parallel([...])  # いくつかは None かもしれない
confirmed = [
    f for f, v in zip(findings, verdicts)
    if v and v.get("isReal")
]

Journal と resume

各 run には runId があります。agent() が終わるたびに、runtime は disk 上の journal へ 1 行追記します。ノートだと思ってください。助手がオーブンから戻った順ではなく、あなたが呼んだ順です。

resumeresume_from_run_id / resumeFromRunId)では script をまた先頭から走らせますが:

  1. 呼び出し順で、各 agent() を次の journal 行と照合します。
  2. 最長の未変更プレフィックス → cache hit即座に再生
  3. 最初の変更または未完了 call でプレフィックスが切れます。
  4. それ以降はすべて live — journal の後ろに古い key が残っていても、黙って hit しません。

本物の JS workflow runtime が Date.now() / Math.random() / 引数なしの new Date() を禁じるのはこのためです。非決定的な時計や乱数は prompt や呼び出し順を変え、ノートが合わなくなります。この Python デモは完全なサンドボックスではありません —— それでも script は決定的に書いてください。

journal:  [A ✓] [B ✓] [C ✓] [D ✓]
resume:   A hit → B hit → C 変更 → D は live古い D への silent hit なし)

サンプルを歩く: review-changes

4 つの review dimension が同じ 2 段階の道を通ります —— fan-out、敵対的 verify、filter。

correctness ── audit ── verify ──┐
security    ── audit ── verify ──┤── 確認済み finding を統合
performance ── audit ── verify ──┤
style       ── audit ── verify ──┘
  1. Review — 各 dimension の auditor が構造化 findings を返します(きれいな机 → 混線が減る)。
  2. Verify — 各 finding を敵対的チェッカーへverify stage 内で parallel)。書いた本人が審判を兼ねない。
  3. 本物とされたものだけ残し、severity で並べます。
async def sample_workflow(ctx, args):
    ctx.phase("Review")
    results = await ctx.pipeline(DIMENSIONS, audit, verify)
    confirmed = [f for r in results if r for f in r["confirmed"]]
    ctx.log(f"{len(confirmed)} 件の実在する問題を確認")
    return {"confirmed": confirmed}

s15 へのつなぎ方

s15 は依然として host loop です。s16 が足すのは一つの tool、Workflow だけです。モデル(またはあなた)が saved name を渡し、adapter が registry を解決して script を走らせます。

Claude Code / Pi製品 この teaching CLI
Script 言語 サンドボックス内の JavaScript 読める Python 関数
Dynamic 入口 モデルが script を書く / scriptPath を編集 文書で説明。デモは saved の name
実行中の host バックグラウンド + 通知でセッションが応答し続ける 観察しやすいよう demo / resume は前景
アイデア 同じ primitives、journal、prefix resume teaching model — 簡略化は明示する

main loop が workflow エンジンになるわけではありません。bashtask を借りるのと同じく、tool をひとつ借ります。

近所: 計画を握っているのは誰か

Workflow は「agent を増やす」ことではありません。トポロジーを誰が持つかを変えます。

近所 計画を握るもの 中間結果の置き場 向いている用途
s06 Subagent モデル、一度きり 最終 summary 以外は捨てる 汚い子タスクを隔離
s13 Agent Teams Lead モデルがターンごと + mailbox 共有タスク / メッセージ 長時間の同僚、人間らしい協働
s15 Integrated Harness 一つのループ内のモデル 会話 messages[] 積み上げ型の coding agent
s16 Workflow Script Script 変数 + journal 既知 / 大規模な構造化 fan-out + verify
s17 Goal Loop 停止境界の evaluator 会話を証拠にする 「ゴール全体は終わったか?」

より安い代替もしばしば勝ちます。skill / prompt を軟らかい計画にする、短い multi-agent チャット、手書きの static SDK orchestrator、あるいは単に大きな一回のモデルターン。単一 context より長く構造を保ちたいときに workflow へ手を伸ばします —— 審査員パネルが聞こえがいいからではありません。

Workflow を使わないとき

Workflow は token と調整コストがかかります。ふつうのコーディングの大半は、5 人の reviewer パネルを必要としません

聞いてください。この仕事は本当にもっと計算と定制 harness が要るか? ふつうの s15 の一ターン(や一つの s06 subagentで足りるなら、そこで止めます。抑制も設計思想の一部です —— 並行と専門化は、そのコストを回収しなければなりません。

試してみる

python s16_workflow_runtime/code.py          # s15 host + Workflow toolreal API
python s16_workflow_runtime/code.py demo     # 固定 fixture: phase と agent を観察
python s16_workflow_runtime/code.py resume   # 同じ runId。prefix はすべて cache hit になるはず

見るポイント:

  • workflow_phase が Review、続いて Verify
  • workflow_agent が初回は done、完全 resume では cached
  • 末尾の短い confirmed リスト。全 hit の resume は agents=0 tokens=0

s15 との対比 → 次は s17

s15 Integrated Harness s16 Workflow Runtime
loop 1 つ、モデル駆動 同じ loop。1 つの tool が script を実行
次の step を決めるもの モデルが毎ラウンド script がバッチの形を持つ
multi-agent 一度きりの subagent script 化・再開可能な agent()
失敗 / resume 会話メモリ頼り null 分離 + journal prefix

s16 = バッチの回し方。s17 = ゴール全体が終わったかどうか。

s17 Goal Loop は独立した評価器に聞きます。止めるべきか、もう一ターンか。繰り返せる workflow に硬い完了条件も要るときは、そちらと組み合わせます。