最近のLLMエージェントは「自律的に動けること」が重視されがちです。タスクを渡すと、調査し、計画し、実装し、検証し、次の作業まで自分で選ぶ。うまく動くと、とても便利です。
一方で、自由を与えすぎると壊れ方も大きくなります。同じ作業を繰り返す。優先順位がおかしくなる。網羅性が保証できない。あとから「なぜその順番で作業したのか」を追えない。こうした問題は、エージェントを長く回すほど目立ちます。
そこで発想を逆にします。LLMに「次に何をするか」まで決めさせません。外部プログラムが状態を読み、次の作業単位を選び、プロンプトを生成し、終了条件を判定します。LLMは、渡された1回分の作業を実行するだけにします。
この設計を、ここでは Prompt Iterator Pattern、またはプロンプトイテレータ方式と呼びます。
最近のCoding Agentまわりでは、個々のプロンプトをどう書くかよりも、「エージェントにプロンプトを渡し続けるループをどう設計するか」が議論されています。Peter Steinberger氏の「coding agentsにプロンプトを書くのではなく、エージェントにプロンプトするループを設計すべきだ」 という投稿は、その流れを端的に表しています。
Prompt Iterator Patternは、このLoop設計の一つです。単に良いプロンプトを1回書くのではなく、次のプロンプトを生成する仕組みそのものを設計します。
LLMエージェントは巨大な while(true) になりがち
多くのLLMエージェント設計は、暗黙的に次のようなループを持っています。
- 現在の状態を読む
- 次にやることを決める
- 作業する
- 結果を見る
- 進捗を更新する
- 終わったか判断する
- まだなら次へ進む
これ自体は自然です。問題は、これらをひとつの巨大なプロンプトやエージェント内部の判断に押し込めてしまうことです。
LLMに以下を全部任せると、制御が難しくなります。
- 状態管理
- タスク選択
- 優先順位付け
- 網羅性の担保
- 進捗管理
- 終了判定
- 実作業
- エラー時の判断
これは、アプリケーション全体をひとつの巨大な while true と巨大な if 文で書くようなものです。最初は動きます。しかし、あとから観測しづらく、止めづらく、修正しづらくなります。
Prompt Iterator Patternとは何か
Prompt Iterator Patternは、この巨大なループを分解する設計です。
役割を3つに分けます。
| 役割 | 責務 |
|---|---|
| Iterator | 状態を読み、次の仕事を選び、プロンプトを出力する |
| LLM | 渡されたプロンプトの作業だけを実行する |
| Runner | IteratorとLLMをつなぎ、ループを回す |
ポイントは、LLMを「意思決定者」ではなく「作業者」に寄せることです。
LLMに任せる範囲を狭くします。その代わり、システム全体としての制御性を上げます。
これはUnixの「ひとつのプログラムはひとつの仕事をうまくやる」という考え方を、LLMエージェントに持ち込む設計です。Iteratorは次のプロンプトを作る。LLM Runnerはプロンプトを実行する。Result Ingestorは結果を状態に戻す。それぞれが小さな責務を持ちます。
この考え方は、以前書いたAgentic CLI Design とも相性が良いです。CLIを人間向けUIではなく、エージェントが呼び出すプロトコルとして設計する、という発想の延長にあります。
基本モデル
流れはシンプルです。
flowchart LR
State[(State)]
Iterator[Iterator Command]
Prompt[Prompt]
LLM[LLM Runner]
Result[Result]
Ingestor[Result Ingestor]
State --> Iterator
Iterator --> Prompt
Prompt --> LLM
LLM --> Result
Result --> Ingestor
Ingestor --> State
状態は外部に置きます。たとえば、JSON、SQLite、Git上のファイル、issue tracker、レビューキューなどです。
Iteratorは状態を読んで、次に実行すべき作業を1つ選びます。そして、その作業に必要なプロンプトを標準出力に出します。
LLM Runnerは、そのプロンプトを読んで実行します。結果はファイル、ログ、パッチ、コメント、JSONなどとして残します。
Result Ingestorは、LLMの結果を読み取り、状態を更新します。成功、失敗、再試行、保留、人間承認待ちなどを記録します。
最小インターフェイス
この方式の最小形は、ただのCLIループです。
次の例は、Iteratorが次のプロンプトを生成し、LLM Runnerがそれを実行し、Ingestorが結果を状態に戻す構成です。前提は、prompt-iterator が標準出力にプロンプトを書き、終了コードで継続可否を返すことです。確認観点は、プロンプト生成・LLM実行・状態更新がそれぞれ独立して観測できることです。
|
|
これだけです。
重要なのは、ループの判断がLLMの内側にないことです。prompt-iterator が「次があるか」を決めます。LLMは、渡された prompt.md の作業だけを実行します。
CLI契約を決める
Prompt Iterator Patternでは、CLIの契約を明確にします。ここが曖昧だと、またLLM側に判断が戻ってしまいます。
最低限、次の契約にすると扱いやすいです。
| 要素 | 契約 |
|---|---|
stdout |
次にLLMへ渡すプロンプト |
stderr |
人間向けログ |
exit 0 |
継続。次のプロンプトを出力した |
exit 1 |
完了。もう処理する仕事がない |
exit 2+ |
エラー。人間または上位Runnerが対応する |
この分離が大事です。
stdout は機械が読む出力です。ここにはプロンプトだけを出します。余計な進捗ログやデバッグログを混ぜません。
stderr は人間が読む出力です。どの状態を読んだか、なぜそのタスクを選んだか、何件残っているか、といった情報を出します。
終了コードは、Runnerの制御に使います。LLMに「続けるべきですか?」と聞く必要はありません。
このあたりは、POSIX系CLIで長く使われてきた標準出力・標準エラー出力・終了ステータスの考え方をそのまま使えます。細かい設計では、POSIX Utility Conventions のような既存のCLI慣習も参考になります。
Iteratorは何をするのか
Iteratorの責務は「次の仕事を選ぶ」ことです。
もう少し分解すると、次の処理をします。
- 状態を読む
- 未処理、失敗、保留などの候補を集める
- 優先順位や依存関係を見て1つ選ぶ
- LLMに渡すプロンプトを生成する
- 継続・完了・エラーを終了コードで返す
Iteratorは、LLMよりも決定的に動くべきです。
たとえば、ファイルレビューなら「未レビューのファイル一覧」を状態として持ちます。Iteratorはその中から次の1ファイルを選びます。LLMには「このファイルをこのルールでレビューしてください」とだけ渡します。
LLMは、全体の未レビュー件数を知る必要はありません。次にどのファイルへ進むかも決めません。
Result Ingestorは何をするのか
Result Ingestorは、LLMの出力を状態に戻す部品です。
ここを省略すると、LLMの作業結果がログに流れて終わります。次のループで何をすべきか判断できません。
Ingestorの仕事は、たとえば次のようなものです。
- LLMが生成した結果ファイルを読む
- 成功・失敗・要再試行を判定する
- 対象タスクを完了済みにする
- 指摘事項やパッチを保存する
- エラー内容を記録する
- 人間承認待ちにする
- 次回のIteratorが読める形で状態を更新する
LLMの出力形式は、できるだけ構造化すると扱いやすいです。Markdownでもよいですが、Ingestorが読む部分はJSONやYAMLに寄せると安定します。
抽象例: レビュー対象を順番に返すCLI
具体例として、レビュー対象を順番に返すCLIを考えます。
ここでは review-gauntlet という名前のCLIを例にします。これは特定のツール名というより、「レビュー対象を順番に返すCLI」の抽象例だと思ってください。前提は、CLI側が未レビュー項目を状態として持ち、ready が次のレビュー用プロンプトを出すことです。確認観点は、LLMがレビュー順を決めず、CLIが返した対象だけをレビューすることです。
|
|
この例では、責務が分かれています。
review-gauntlet ready- 次にレビューすべき対象を選ぶ
- レビュー観点を含むプロンプトを生成する
- 対象がなければ exit
1で終了する
opencode run- 渡されたプロンプトを実行する
- レビュー結果を出力する
review-gauntlet ingest- レビュー結果を読み取る
- 対象を完了済みにする
- 指摘や未解決事項を状態に保存する
この形にすると、LLMが途中で「次は別の観点を見よう」「このファイルは飛ばそう」と勝手に決めにくくなります。必要ならIterator側のロジックを変更します。
プロンプトは「1回分の仕事」にする
Prompt Iterator Patternで生成するプロンプトは、小さいほど安定します。
悪いプロンプトは、次のようなものです。
リポジトリ全体を確認し、重要な問題を探し、必要なら修正し、完了まで進めてください。
これはLLMに任せすぎです。何を優先するか、どこまで見るか、いつ終えるかが曖昧です。
良いプロンプトは、次のように範囲を切ります。
- 対象ファイル
- 適用するルール
- 出力形式
- 禁止事項
- 完了条件
- 状態更新に必要なメタデータ
たとえば、1ファイルずつレビューするなら、Iteratorは次のようなプロンプトを生成します。
以下は、IteratorがLLMに渡す1回分のレビュー指示の例です。前提は、対象ファイルとレビュー観点がIterator側で決定済みであることです。確認観点は、LLMの出力からIngestorが判定結果と指摘事項を取り出せることです。
|
|
このように、LLMの自由度を「対象作業の中」に閉じ込めます。
LLMは賢い実行者として使います。プロジェクト全体の進行管理者にはしません。
どこに状態を置くか
状態の置き場所は、用途によって変わります。
小さな用途ならJSONファイルで十分です。
以下は、Iteratorが読む状態ファイルの例です。前提は、各タスクが pending、done、failed などの状態を持つことです。確認観点は、Iteratorがこの状態だけを見て次のタスクを決められることです。
|
|
もう少し大きくなると、SQLiteが便利です。複数プロセスから扱うなら、ロックやトランザクションも考えやすくなります。
GitHub Issues、Linear、Notion、独自DBなどを状態ストアにしてもよいです。大事なのは、Iteratorが「次に何をするか」を再現可能な形で決められることです。
向いている用途
Prompt Iterator Patternは、網羅性や進捗管理が必要なタスクに向いています。
たとえば、次のような用途です。
files × rulesの網羅レビュー- issueの順次処理
- テスト失敗の修正ループ
- 章ごとのドキュメント生成
- データ変換バッチ
- lint / typecheck / test を見ながらの修正
- 人間承認待ちタスクの再開
- 大量のプロンプト評価
- 移行作業のチェックリスト消化
特に「全部見たと言えること」が大事なタスクに向いています。
LLMに自由探索させると、抜け漏れを証明しづらくなります。Iteratorが対象リストを持っていれば、何件中何件が完了したかを明確にできます。
向いていない用途
逆に、探索的なタスクには向かない場合があります。
たとえば、次のような作業です。
- まだ目的が曖昧な調査
- 企画やアイデア出し
- 解法自体を発見したい問題
- 人間と対話しながら方向性を決める作業
- 強い創造性が必要な初期設計
この場合は、LLMにある程度の自由度を渡したほうがよいです。
Prompt Iterator Patternは、LLMの能力を下げる設計ではありません。LLMに任せるべき範囲と、プログラムで制御すべき範囲を分ける設計です。
実装時のチェックリスト
実装するときは、次の点を確認すると安定します。
Iterator
- 状態を外部ファイルまたはDBから読んでいる
- 次のタスク選択が決定的である
-
stdoutにプロンプトだけを出している -
stderrに人間向けログを出している - タスクがあれば exit
0 - タスクがなければ exit
1 - 異常時は exit
2+
Prompt
- 1回分の作業に絞っている
- 対象が明確である
- 完了条件が明確である
- 出力形式が明確である
- 禁止事項が明確である
- 状態更新に必要なIDを含んでいる
LLM Runner
- 標準入力からプロンプトを読める
- 結果をファイルまたは標準出力に残せる
- 失敗時のログを保存できる
- タイムアウトやリトライの扱いが決まっている
Result Ingestor
- LLMの出力を検証している
- 成功・失敗・保留を状態に反映している
- 不正な出力を失敗として記録できる
- 再試行回数を管理している
- 人間承認待ちを表現できる
関連する考え方
Prompt Iterator Patternは、単独の発明というより、エージェント時代のCLI設計を組み合わせたパターンです。
関連する記事としては、次のあたりが近いです。
- Agentic CLI Design: CLIをAIエージェント向けプロトコルとして設計する7つの原則
- Agent Skillsを最小実装する: Pythonで作るスキル発見とプロンプト注入
- Conflux: OpenSpecベースのAI開発オーケストレータを公開しました
- PLP: プロンプトをデプロイせずに差し替える標準仕様
Prompt Iterator Patternは、これらのうち「反復実行」と「次の作業選択」に焦点を当てたものです。プロンプトを管理するだけでなく、プロンプトを順番に生成し、LLMを1ステップずつ動かします。
自律性を下げると、制御性が上がる
LLMエージェントの設計では、つい「もっと自律的にできないか」と考えたくなります。
しかし、実運用では逆が効く場面も多いです。
LLMに任せる範囲を狭くする。次に何をするかは外部プログラムが決める。状態と終了条件をプログラム側に置く。これだけで、エージェントはかなり扱いやすくなります。
Prompt Iterator Patternの本質は、LLMエージェントの自律性を下げることで、システム全体の制御性を上げることです。
LLMを万能の自律エージェントとして扱うのではなく、強力な1ステップ実行器として扱う。その周りを、小さくて観測可能なCLIで組み立てる。
このほうが、Unix的で、テストしやすく、止めやすく、再開しやすいです。LLMエージェントをプロダクションに近い場所で使うなら、この分離はかなり実用的な設計パターンになります。