レッスン5: レビュー回路と、パターンをグラフに組み上げる
学習目標:
- レビューと修正のループ(下書きを生成し、評価し、フィードバックに基づいて書き直す)を実装し、2つの判断基準でこのループを作る価値があるかを見極める
- 「最大 N ラウンド」より賢い停止条件を書き、決定的なチェックをジャッジの前に置く
- 5つのパターンを本レッスンが言うところの「グラフ」に組み上げ、この作図体系が特定の一次資料の一文に錨を下ろした独自のメタファーであることを自分の資料に明記する
前提: レッスン1〜4を読了していること(誰が計画を持つか、チェーンとルーティング、並列化、オーケストレーター・ワーカー)、stop_reason で駆動するハーネスループを手で書けること | 前: << レッスン4 | 次: レッスン6 >>
いつも一歩足りない下書き
エージェントにデータベースのマイグレーション計画を書かせるとします。最初の下書きは一見まともです。背景、手順、実施時間帯が揃っています。しかし一目で穴が2つ見つかります。ロールバックの節は「必要ならロールバックする」としか書いておらず、リスク評価がどこにもありません。あなたはそれを指摘する2行のフィードバックを打ち込み、2稿目が返ってきて、穴は両方とも埋まり、全体の品質が一段上がります。
あなたはこの過程を何十回も繰り返してきました。毎回同じです。出力が足りず、人間が2文のフィードバックを与え、出力が目に見えて良くなる。
問題はモデルの筆が悪いことではありません。問題は、あなたのその2文のフィードバックが、作るのに苦労するものではないということです。「ロールバック手順には具体的なコマンドを含めること」「各手順にリスク評価を付けること」——これらはチェックリストで賄える類のものです。あなたがはっきり言語化できるなら、モデルにもおそらく言語化できます。ではなぜ、毎回それを言うのがあなたでなければならないのでしょうか。
この形はループとして書き出す価値があります。
レビューと修正: 「もう一度直す」を制御フローに書き込む
一次資料は一文で定義しています。1つの LLM 呼び出しがレスポンスを生成し、別の呼び出しがループの中で評価とフィードバックを提供する1。
最初の4レッスンで扱った4つのパターンには、それぞれ固有のトポロジーがあります。チェーンはタスクを一連のステップに分解し、各ステップが前のステップの出力を処理します1。ルーティングは分類してから専門化された後続タスクへ振り分けます1。並列化は同時に走らせ、結果をプログラムで集約します1。オーケストレーター・ワーカーは中央の LLM がタスクを動的に分解し、ワーカーに委任し、結果を合成します1。これらには共通の性質があります。データが前へ流れるということです。レビュー回路は戻り辺(バックエッジ)を持つ最初のパターンです。出力が生成ノードへ戻ってきます。
プロダクトではどう見えるのでしょうか。Claude Code の動的ワークフローのドキュメントに、平易な記述が1つあります。チェッカーを走らせ、失敗したものを直し、通るか、あるいは進展しなくなるまで繰り返す2。同じ分業の別の使い方を扱った記述もあります。報告される前に、独立したエージェントに互いの発見を敵対的にレビューさせる2。こちらは報告前の一度きりの相互レビューであり、戻り辺も反復もありません。これをレビュー回路にまとめて扱うのは本レッスンの分類であって、原文が同じトポロジーとして記述しているわけではありません。
1つの概念に3つの名前
ここで明示的に橋を架けておかないと、3つの別々のことを学んでいると勘違いしてしまいます。
本シリーズのコース6、マルチエージェントコラボレーションを教える回では、この「一方が書き、一方が批評する」分業をプロデューサー・レビュアーと呼んでいます。これは私たち独自の教育用語です。一次資料では、Claude Code のワークフローのドキュメントがそのプロダクト上の用法を一文で記述しています。独立したエージェントに互いの発見を敵対的にレビューさせる(本レッスンではこの一文を「敵対的相互レビュー」と略記します)。同じ形が一次資料では2つの名前を持ちます。Anthropic のパターン一覧はエバリュエーター・オプティマイザーと呼び1、Claude Code のワークフローのドキュメントは名前を与えず、用法だけを記述しています——互いの発見を敵対的にレビューさせる、というあの一文です2。
3つの名前、1つの形。違いはどの角度から見ているかです。コラボレーションの役割を論じるときは2人の演者が見え、オーケストレーションのパターンを論じるときは戻り辺が見え、プロダクトの機能を論じるときは再利用可能な品質手法が見えます。
もう1つ、分業をはっきりさせておくことがあります。本シリーズのコース10は、良いジャッジになる方法を1コース丸ごと使って教えています。ルーブリックの書き方、ジャッジの出力フォーマットの縛り方、なぜジャッジには独立したコンテキストが要るのか、なぜ書き手がジャッジを兼ねてはいけないのか。あのコースが教えるのはジャッジそのものの品質です。本レッスンはその話題を繰り返しません。本レッスンが教えるのは、ジャッジを制御フローにどう配線するかです。ループのどこに座るのか、いつ走るのか、何ラウンド走るのか、いつ止まるのか。
このループを作る価値があるとき
一次資料の適用条件はこうです。このワークフローは、明確な評価基準があり、反復的な改良が測定可能な価値をもたらす場合にとりわけ効果的です。適合の2つの兆候は、第一に、人間がフィードバックを言語化すると LLM のレスポンスが明らかに改善されること、第二に、LLM 自身がそのようなフィードバックを提供できること、です1。
この引用は以前にも見ています。本シリーズのコース10は、「レビュー・修正ループを作る価値があるか」という問いに答えるとき、まさにこの一文を引きました。同じ基準を、オーケストレーションの文脈で組み直したものです——ただし今回は、その答えを制御フロー上のループとして実装します。
分解すると、この2つの兆候はそれぞれ別の失敗モードを防いでいます。
第一の兆候は「修正しても良くならない」を防ぎます。 タスクによっては、どれだけ明確にフィードバックを述べても2稿目が良くなりません。問題は出力の言い回しではなく、入力データの欠如やタスク定義の曖昧さにあるからです。この場合ループを作るということは、同じくらい使えない版を2つ得るために2倍払うということです。検証方法は粗いですが有効です。自分で手作業を3回やってみること。その3回のうち、「人間のフィードバックで明らかに良くなった」のは何回でしたか。3回のうち2回が「フィードバックが効かなかった」なら、ループは作らないでください。
第二の兆候は「ジャッジがその種のフィードバックを出せない」を防ぎます。 人間のフィードバックが効くとしても、まだ問うべきことがあります。モデル自身が同じ種類のフィードバックを提供できるでしょうか。あなたのフィードバックが、あなたしか知らないこと(この顧客が前四半期に何を苦情として挙げたか、法務が先週口頭で何を伝えたか)に依存しているなら、モデルはその情報を持っていないので、返してくるフィードバックはまったく別のものになります。その場合は、その情報をジャッジのプロンプトに流し込んでモデルが評価できる基準に変えるか、このステップには人間が必要だと受け入れるかのどちらかです。
この2つの兆候よりさらに手前で効いてくる前提条件があります。評価基準が明確であることです。 基準が明確でないとき、ループは決まった種類の失敗を確実に生みます。ジャッジがラウンドごとに異なる、ときには矛盾する方向を指すフィードバックを返し、出力が2つの版の間を行き来し、ラウンドを使い切り、最終稿が初稿より悪くなるのです。これはループのせいではありません。基準がまだ定義されていないだけです。
決定的なチェックはジャッジの前に
一次資料が与える、決定的なシステムと非決定的なシステムの定義上の区別はこうです。決定的なシステムは同一の入力に対して毎回同じ出力を生み、非決定的なシステム——エージェントのような——は同じ初期条件でも異なるレスポンスを生成し得ます3。
ジャッジは非決定的です。「コードで確定的に判定できる」ルールをジャッジに渡すたびに、毎回同じ結果になるべきものを、毎回違う結果を返しうるもので評価していることになります——しかも追加のモデル呼び出しの料金を払いながらです。
本シリーズのコース10はこの規律を階層的な採点と呼んでいます。コードで判定できることはコードで判定し、コードで判定できないものだけをモデルに渡す、というものです。本レッスンはそれをそのままループのノード順序に写し取ります。レッスン2の引用がここでもそのまま効きます——プロセスが軌道に乗っているかを確かめるために、任意の中間ステップにプログラム的なチェックを追加できます1。ループの中の下書きはすべて中間ステップです。
マイグレーション計画の例に当てはめてみましょう。「各手順に対応するロールバックコマンドがあるか」は正規表現や構造化パースで確定的に判定できます——これがゲートです。「そのロールバックコマンドは信用できる書き方か」にはジャッジが要ります。前者が失敗した場合はジャッジを起こす必要すらありません。どの手順が欠けているかを書き手に伝えて先へ進むだけです。
ループのコード骨格
いくつかの細部は個別に取り上げる価値があります。
runAgent は完結したハーネスループです。 これはレッスン2から変わっていません。このスクリプトの await runAgent(...) はどれも、その裏で本シリーズのコース7の stop_reason 駆動ループを走らせています。これは、そのループの外側にコードで書いた制御フローをもう一層かぶせただけのものです。
この reason の値は異なる結末であり、真偽値に潰さないでください(実運用ではさらに細分化が必要になることが多く、たとえばゲートの連続失敗は独自の分類を持つべきです)。 passed はそのまま納品できます。max-rounds は通過しないままラウンドを使い切ったということで、おそらく人間への引き継ぎが必要です。no-progress はモデルが行き詰まったということで、これ以上金を燃やしても良くなりません。この3つの結末は、可観測性のデータでは3本の別々の行であるべきです。本シリーズのコース11が教えるログの取り方は、ここではこの reason フィールドに着地します。
ゲートの失敗も1ラウンドに数えます。 continue の前で rounds はすでにインクリメントされています。これは意図的です。ゲートを繰り返し落ちるのは書き手のプロンプトに問題があるということで、無制限にリトライさせても同じ穴に金を燃やすだけだからです。
停止条件は2種類とも必要です。 エージェントのループを論じた一次資料はこう述べます。タスクは完了時に終了することが多いが、制御を保つために停止条件(最大反復回数など)を含めることもよくある1。「最大 N ラウンド」はここから来ています。これはヒューズであり、どんな状況でもこのコードが停止することを保証します。「これ以上進展しない」という停止メカニズムは別の場所から来ています。チェッカーを走らせ、失敗したものを直し、通るか、あるいは進展しなくなるまで繰り返す2。こちらがヒューズより賢いのは、何ラウンド走ったかではなく、今ラウンドが前ラウンドを上回ったかを見ているからです。
スコアが上がらなければ抜ける、というのは最も実装が簡単ですが、唯一の方法ではありません。ジャッジがスコアを出力しないなら、失敗項目の件数が減ったかを見る方式に切り替えられます。タスク自体のばらつきが大きいなら、stalled カウンターを使って「2ラウンド連続で改善がなければ抜ける」に切り替えられます。どれを選ぶかは、ジャッジがどれだけ安定しているかで決まるのであって、どれが高級に聞こえるかではありません。(骨格の bestDraft に注目してください。スコアが上がらないときに抜ける方式は、常に最高スコアの下書きを保持していることを前提にしています。bestDraft を持たず bestScore だけを追跡していると、no-progress と max-rounds の出口で、より悪い現行版を渡してしまいます。)
パターンを組み合わせる: 本レッスンはそれを「グラフ」と呼ぶ
5つのパターンが出揃いました。次の問いは、それらをどう並べるかです。
まず公式の立場から。これらのビルディングブロックは規範ではなく、開発者がユースケースに合わせて形を変え組み合わせられる一般的なパターンであり、成功の鍵は、他の LLM 機能と同じく、性能を測定し実装を反復することです1。
言い換えると、「どう組み合わせるか」について一次資料が与えるのは許可であって、レシピではありません。レシピは自分で書きます。
グラフ用語についての正直な宣言
本レッスンが通して使う「グラフ/ノード/エッジ」は、本レッスン独自のエンジニアリング上のメタファーであって、公式の用語ではありません(前のセクションですでにこの意味で「ノード」「戻り辺」を使っています)。
この一文を自分のアーキテクチャ文書に写してください。"graph"、"node"、"edge"、"DAG"、"state machine" という語は、本レッスンが引用するすべての一次資料を通して一度も現れません。一次資料の語彙は workflows、patterns、orchestrator-workers、fan out です——それはパターンのカタログを論じているのであって、トポロジー構造を論じているのではありません。
それでも「グラフ」という語は必要です。5つのパターンが並んだとき、それらを明確に語る言語が要るからで、「グラフ」は最も労力の少ない選択肢だからです。ただしこのメタファーには錨が必要です。さもなければ勝手に作った専門用語にすぎません。錨になるのは、一次資料に実際に存在するこの一文です。ワークフローのスクリプト自体がループ、分岐、中間結果を保持し、モデルのコンテキストは最終的な答えだけを保持する2。
その一文はすでにグラフの3要素を名指ししています。ループ(戻り辺)、分岐(分岐点)、中間結果(状態)。私たちがしているのは、それぞれに名前を付けることだけです。
作図の約束
本レッスンの作図体系では次のとおりです。
- ノード(node) = 1つの
runAgent ループ、または純粋なコードの一片(ゲート、分類、集約、バッチ分け)。各ノードの種別にラベルを付けることが、作図でいちばん価値のある行為です。「このステップは本当にモデルが要るのか」に答えることを強制するからです。
- エッジ(edge) = 「誰の出力が誰に流れるか」。エッジはデータ構造ではなく、前の行の変数を次の行のコードが読むというだけのことです。
- 状態(state) = スクリプトの変数。ここにも一次資料の一文があります。中間結果はモデルのコンテキストに着地せず、スクリプトの変数に留まる2。「ノード間を受け渡される状態オブジェクト」という公式の概念は存在しません——それは他の領域から借りてきた言い回しです。本レッスンはその抽象を作らず、必要な変数をそのまま渡すだけにします。
5つのパターンをこの作図体系の形で
4番目の形の注釈は読み直す価値があります。並列化とオーケストレーター・ワーカーは形の上では似ており(原文の語は topographically similar)、決定的な違いはサブタスクが事前定義されず、具体的な入力に基づいてオーケストレーターが決定する点にあります1。紙に描くと、この違いは「3本の辺を描いたのは私だ」と「3本の辺を描いたのは A だ」の違いです——紙の上では見分けがつきませんが、コードでは大きく違います。
組み合わせの例
ルーティング、ファンアウト、合流、レビュー回路をつないでみます。
レッスン6が実装するのは、この図の一変種です。あの一群のチケットは受け入れ基準がたまたますべてルールとして書けるので、[レビュー] の層は {ゲート} に退化し、ファンアウトも「1件の複雑な項目を3人のワーカーに分ける」から「一群のチケットをそれぞれ1人の処理者に配る」に変わります。どこが変わり、なぜ変わったのかは、レッスン6の冒頭が1項目ずつ列挙します。ここではまず形を見てください。コードは次のレッスンまで待ちます。
組み合わせがもたらすエンジニアリング上の利得
制御フローをコードに移すことは、「理解しやすい」以上のものをもたらします。いくつかの利得には一次資料の裏付けがあります。
逐次の追跡が復旧可能性をもたらします。 ランタイムは実行の進行に合わせて各エージェントの結果を追跡しており、それが同じセッション内での再開を可能にしています2。本レッスンの作図体系に訳すと、グラフのすべてのノードは自然にチェックポイントの場所になります。ノードが終わり、結果がスクリプトの変数に着地し、その変数が「どこまで進んだか」の記録になるからです。本シリーズのコース9が教えるチェックポイント設計は、ここでは別途の土台を必要としません。ノードの境界が自然な着地点です。
細かい粒度のファンアウトはより多くの進捗を保全します。 一次資料の言葉はこうです。多数の小さなエージェントに作業を扇状に広げるワークフローは、1体の長時間エージェントよりも多くの進捗を保全します2。40 分走る長時間エージェントが1体クラッシュすれば 40 分が消えます。1分のノードが 40 個あって1つがクラッシュしたなら、失うのは1分で、しかもどの1分かが分かります。
再現可能な品質手法が再利用できるようになります。 計画をコードに移すことは、単にエージェントを増やすだけでなく、ワークフローに再現可能な品質パターンを適用させます。報告される前に独立したエージェントに互いの発見を敵対的にレビューさせたり、複数の角度から計画を起案して互いに比較検討させたりでき、単一パスより信頼できる結果が得られます2。ここでの鍵語は「再現可能」です。相互レビューを手作業で一度やるのはオペレーションですが、スクリプトに書き込めば能力になります。
決定的なガードレールが非決定的なエージェントを包みます。 Anthropic のマルチエージェント・リサーチシステムの振り返りはこう述べます。彼らは Claude の上に構築された AI エージェントの適応性を、リトライロジックや定期的なチェックポイントといった決定的なセーフガードと組み合わせています4。本レッスンの作図体系に訳すと、グラフの骨格は決定的(誰が誰を呼ぶか、いつ止まるか、失敗時にどの辺を通るか)で、ノードの内側が非決定的です。この層分けは美意識の問題ではなく、システムを運用可能にするための前提条件です。
組み合わせの規律: 層を1つ足すたびに1つのゲートを通す
利得を述べたので、次は制約です。
層を1つ足すたびに「測定可能な改善」というゲートを通してください。 一次資料は同じことを2箇所で述べており、2箇所目は明示的に繰り返しだと断っています。複雑さを足すのは、それが結果を改善すると実証できる場合に限って検討すべきです1。これは本レッスンでは特に重要です。5つのパターンが目の前に並んでいるとき、いちばんやりがちな間違いは全部使うことだからです。ノードを1つ足すことは、モデル呼び出しが1回増え、壊れうる場所が1つ増え、切り分けるべきものが1つ増えるということです。足す前に問うてください。それを外したら、指標は下がりますか。答えられないなら、まだ測っていないということです。
ノード単位のリトライとタイムアウトはエンジニアリングの実践であって、公式の設計ではありません。 一次資料がこれに触れているのは従属節1つだけです(リトライロジックや定期的なチェックポイントといった決定的なセーフガード4)。したがって以下はエンジニアリングの実践として書きます。どの一次資料にもその推奨は見つかりません。すべての runAgent ノードをタイムアウトで包み、タイムアウト後はリトライするか、このノードを失敗とマークして続行する。リトライ回数はノードの性質で決める(読み取り専用の検索ノードは数回リトライしてよく、副作用を持つノードは自動リトライを一度もしないのが望ましい)。ノードが失敗したら「この辺は飛ばせる」のか「グラフ全体を止めなければならない」のかを区別し、任意の1ノードの失敗が実行全体を道連れにしないようにする。これらは分散システムのごく普通の常識をエージェントに当てはめただけのもので、新しい何かとして扱わないでください。
深さには上限があります。 プロダクトレベルの参照値がそこにあります。デフォルトでは、サブエージェントは自分のサブエージェントを起動でき、メインの会話から3層下までです5。3層は本レッスンが発明した閾値ではありませんが、メッセージは明確です。実在のプロダクトにおける入れ子の深さは無制限ではなく、どこで止めるかを誰かが真剣に考えたということです。あなたのグラフにも同種の答えがあるべきです。描いたグラフの入れ子が5層になっているなら、まずタスクを細かく分解しすぎていることを疑ってください。もっと深く対応する方法を考えに行かないことです。
グラフは目的ではなく、タスクの形の記述である
このコースの終わりで特に防いでおきたい失敗モードが1つあります。かっこいいトポロジーを先に選び、それに詰め込めるタスクを後から探すことです。
順序は逆であるべきです。まずタスク自身の依存の形を描いてください。どのステップが並ばなければならないか(前のステップの出力が次のステップの入力)、どのステップが互いに影響しないか(どちらが先に走ってもよい)、どのステップは入力を見てからでないと何個に分けるか分からないか、どのステップの出力は誰かに批評されないと信用できないか。それを描き終えれば、どのパターンを使うかはほぼ決まります。並ぶところはチェーン、互いに独立なところは扇、見てから決めるところはオーケストレーター、批評が要るところはループです。
パターンはタスクの形に付けられた名前であって、好きに選べるメニューではありません。
そしてその規律のさらに手前で効くものがあります。可能な限り最も単純な解を見つけ、必要なときにだけ複雑さを増やすこと1。この一文はレッスン1で登場し、本レッスンの終わりでも同じ一文のままです。5つのパターンを学んだあとでも、「LLM 呼び出し1回で足りる」は完全に妥当な答えであり続けます——一次資料自身が、多くのアプリケーションでは、検索とコンテキスト内の例で単一の LLM 呼び出しを最適化するだけで通常は十分だと述べています1。
完全に動く組み合わせのコードはレッスン6にあります。本レッスンはここまでです。いまあなたの手元にあるのは、5つのパターン、1つの作図体系、そしてそれらを使わないほうがよい場合のリストです。
💻 演習
まとめ
- レビューと修正は、1つの LLM 呼び出しがレスポンスを生成し、別の呼び出しがループの中で評価とフィードバックを提供するものです1。そのプロダクト上の形は「チェッカーを走らせ、失敗したものを直し、通るか進展しなくなるまで繰り返す」であり2、敵対的相互レビューは同じ分業の別の使い方です(相互レビューは一度きり、戻り辺なし)。これをレビュー回路にまとめるのは本レッスンの分類です2。本シリーズのコース6はこれをプロデューサー・レビュアーと呼びますが、それは私たちの教育用語であり、一次資料の語彙はエバリュエーター・オプティマイザーです1。
- 作る価値があるかは2つの兆候で決まります。人間がフィードバックを言語化すると LLM のレスポンスが明らかに改善されること、そして LLM 自身もそのようなフィードバックを提供できること。評価基準が明確で、反復的な改良が測定可能な価値をもたらすときにとりわけ効果的です1。基準が明確でないなら、ループを先に作らず、まず基準を定義してください。
- 停止条件は1種類ではありません。最大反復回数のような停止条件は制御を保つために使われ1、「これ以上進展しない」はより費用対効果の高いもう1つの停止メカニズムです2。3つの結末(通過/ラウンド消尽/進展なし)は3つの異なる下流の行動に対応するので、真偽値に潰さないでください。決定的なゲートはジャッジの前に置きます。
- 5つのパターンは組み合わせられます。これらのビルディングブロックは規範ではなく、開発者がユースケースに合わせて形を変え組み合わせられる一般的なパターンであり、成功の鍵は性能を測定し実装を反復することです1。
- 「グラフ/ノード/エッジ」は本レッスン独自の作図体系であって、公式の用語ではありません。その一次資料上の錨はたった一文です——ワークフローのスクリプト自体がループ、分岐、中間結果を保持し、モデルのコンテキストは最終的な答えだけを保持する2。加えて、中間結果はスクリプトの変数に留まる2。自分の文書でこの語彙を使うときは、この宣言も一緒に入れてください。
- 組み合わせの利得は文書化されています。ランタイムは実行の進行に合わせて各エージェントの結果を追跡しており、それが同じセッション内での再開を可能にしています2。多数の小さなエージェントに作業を扇状に広げるワークフローは、1体の長時間エージェントよりも多くの進捗を保全します2。計画をコードに移すことで、ワークフローは再現可能な品質手法(敵対的相互レビュー、複数の角度からの起案と比較検討)を適用できます2。決定的なセーフガード(リトライロジックと定期的なチェックポイント)が非決定的なエージェントを包みます4。
- 制約も同じくらい明確です。複雑さを足すのは、それが結果を改善すると実証できる場合に限って検討すべきです1。ノード単位のリトライとタイムアウトはごく普通のエンジニアリングの実践であり、一次資料は従属節1つしか提供していません4。深さにも上限があり、プロダクトレベルの参照値はサブエージェントがメインの会話から3層下まで入れ子になるというものです5。まずは最も単純な解から1。
>> レッスン6: 実践: ハーネスを小さなグラフに引き上げる