Files
analysis_claude_code/s16_workflow_runtime/README.ja.md
Cursor Agent e28bec6dd4 Rewrite s16 workflow chapter for clarity and teaching fidelity
Retell the Runtime lesson as a progressive story (two doors, kitchen
primitives, null-isolation, longest-prefix resume) in EN/ZH/JA, and align
the mini-runtime with Claude Code / Pi semantics so the docs and code agree.

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

11 KiB
Raw Blame History

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

English · 中文 · 日本語

s01 → ... → s14 → s15s16s17

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

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


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

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

問題

s01 から s15 まで、各ラウンドでモデルが次の tool を選びます。直前の発見で次の道が変わるタスクには向いています。

一方、形が先に分かっている仕事もあります。

  • 複数の観点で多くのファイルを review する
  • 調査 → 検証 → 統合
  • N 個のモジュールを同じやり方で移行する

計画を messages[] の中だけで「覚えている」と、三つのことが起きます。orchestration の雑音で context が埋まる、途中で計画がずれる、落ちたら完了済みの作業までやり直す。

必要なのは並行性、安定した結果の形、そして再開です。会話履歴だけにそれを預けるのは弱いです。

一息でいうアイデア

計画をコードへ移します。 Subagent は相変わらず判断します。script がループ、扇状の分配、マージを持ちます。中間結果は変数にあり、会話には入りません。

Workflow Runtime Overview

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

ふたつの入口

Claude Code は、workflow の始め方について正直です。

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

厨房は同じです。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"]]

答えを機械が読める形に

助手が散文で返してくると、次の 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 段階の道を通ります。

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 をひとつ借ります。

試してみる

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 は独立した評価器に聞きます。止めるべきか、もう一ターンか。