Agentsには、より多くのプロンプトではなく制御フローが必要
(bsuh.bearblog.dev)- 複雑な作業を安定して処理するエージェントには、より精巧なプロンプトチェーンではなく、ソフトウェアにエンコードされた決定的な制御フローが必要
- プロンプトでMANDATORYやDO NOT SKIPのような表現に依存しているなら、プロンプティングの限界に達している状態
- 文が提案のように動作し、関数がハルシネーションを起こしながら「Success」を返すプログラミング言語を想像すると、複雑さが増すほど推論は不可能になり、信頼性は崩壊する
- ソフトウェアは、ライブラリ、モジュール、関数で構成される再帰的な組み合わせ可能性を通じて拡張され、予測可能な動作を提供するため、局所的な推論が可能
- プロンプトチェーンは狭い作業には有用だが、非決定的で、仕様が弱く、検証が難しいため、同じ性質は持てない
エージェントの信頼性に必要な構造
- 信頼性を確保するには、ロジックを自然言語による説明から取り出してランタイムへ移す必要がある
- 必要な構造は、LLMをシステム全体ではなく1つの構成要素として扱う決定的なスキャフォールド
- このスキャフォールドには、明示的な状態遷移と検証チェックポイントを含めるべき
- 決定的なオーケストレーションだけでは十分ではなく、静かに失敗しうるシステムには積極的なエラー検出が必要
- プログラムによる検証がなければ、選択肢は3つに絞られる
-
監視者(Babysitter)
- 人がループ内に残り、エラーが伝播する前に捕まえなければならない
-
監査者(Auditor)
- 実行が終わった後で、結果全体を徹底的に検証しなければならない
-
祈り(Prayer)
- 結果を雰囲気で受け入れるやり方に依存することになる
-
1件のコメント
Hacker Newsのコメント
1000%同意する。Anthropicが繰り返し言っている「将来のモデル性能を基準に作れ、すぐにもっと良くなる」という太鼓の音が、だんだん信じがたくなってきた。
ブラウザセッション内で要件Markdownファイル200個をなめるQAエージェントを作ったが、チーム効率を大きく上げた良いシステムだった。だが「このディレクトリの要件ファイルを見て、各ファイルごとにアプリがその要件を満たしているか確認するToDo項目を作れ」のような 高レベルの制御フロー をモデルに任せると、30個くらいから崩れた。
ファイルを落としたり、一部のファイル群を3回ずつテストして3分で済むはずのことを10分かけたり、1つのファイルのエラーのせいで前の4ファイルを理由もなく再テストしたりした。Opus 4.6とGPT 5.4、軽く見たOpus 4.7とGPT 5.5でも、ワークフローのオーケストレーション能力は一貫していなかった。
結局、モデルの周りに非常に単純な 決定的ハーネス を作り、各テストケースごとにモデルを呼び出してテストし、結果を配列に保存してからファイルに書くようにしたところ、システム信頼性が圧倒的に良くなった。だがCursor Cloud AgentsやAnthropicのようなマネージドエージェントプラットフォームは、「エージェントがすべてを実行すべきだ」にこだわりすぎていて、適切な箇所に少し決定性を入れる価値が見えていないように思える。
それは、この技術が人全体やワークフロー全体、プロジェクト全体を置き換えるという主張に冷や水を浴びせるからだ。生産性を大きく押し上げ、開発者の採用市場や賃金には壊滅的な影響を与えうるとは思うが、この特定技術の現行版が彼らの言うレベルまで行くとは思えない。「人間の開発チームの雑務のかなりの部分を減らしてくれる非常に有用なツール」として位置付けていたなら、開発者は欲しがるが役員はそれほど欲しがらず、投資家も黙ってはいなかっただろう。
また、細かく強く統制された段階は、テスト自動化やCSIファンフィクション5巻を一瞬で書く巨大モデルよりも、もっと小さくて安くて特化したモデルを差し込むのにずっと向いている。
一部のベンチマークでは、1つの問題に何度も試行させて、失敗率は無視し、成功した結果が一度でも出ればその結果だけを採用していないか?
apply_patchのようなツールにcheck_compilationとrun_unit_testsを組み合わせたところ、性能が大きく改善した。ツール名は今でもapply_patchのままだが、今ではパッチが成功するとビルドとテストに関する追加情報も返す。エージェントの成功率は約80%から、今のところほぼ 決定的 に見えるレベルまで上がった。もうプロンプトにコンパイルと単体テストの過程をわざわざ説明する必要はなく、どの依存条件で実行されたかとその結果だけ返せばよい。
最近の流行から外れている感じもする。かなり前から前払いトークンとカスタムハーネスを使ってきたが、ただうまく動く。大半のニュースは無視していい。明示的に狙った問題ではCopilot系ツールはもはや役に立たず、あるコードベースでは同じGPT 5.4系モデルを使っていても性能の次元がまるで違う。
みんなスキルでこのパターンを見落としている。
SKILL.mdの隣にコードを置けば特定の動作を保証できるのに、みんな奇妙なくらいプロンプト作成に依存している。CLIを作る必要もなく、作業が入った単純なskill.pyで十分だ。claude -pを呼ぶヘルパーを置いてもよい。数年後にも人々がLLMを使ってはいるが、結局は学ばなければならない 統制された語彙と文法 を通してしか使えなくなっていたら面白いと思う。15年前に皆がNoSQLへ移行したのに、すぐJSONの中にスキーマを作り直したのと同じだ。
問題の一部は、そもそもLLMの適用先を間違えていることではないかと思う。別の場所でも出ていたように、エージェントのプロンプトは、できるだけ反復可能で検証可能で決定的なやり方で作業を行う コードを書け であるべきなのかもしれない。
エージェント出力の検証も含まれるべきだ。全体目標は、プログラムでより効率的に、しばしばより正確に処理できる作業からLLMを外すことだ。
AIを本番環境につなぎ、API呼び出しで直接何かをさせるのは良くない。アプリでAIが担うべき唯一の用途は、読み取りや分類のようなものだと思う。昔ながらのCRUDアプリの「R」を置き換える程度だ。
同じAIベースの「R」エンドポイントで、プロンプトに応じて「C」「U」「D」のフォームを自動入力するのは構わないが、人がレビューする前に顧客のために何かを変更してはいけない。CRUDアプリは依然としてCRUDアプリであり、今後もそうだろう。ただ、顧客や内部ツール、Jenkinsパイプラインなどにフォーム自動補完や行動提案をしてくれる非常に賢い「R」エンドポイントができただけだ。行動を提案することはできても、直接実行してはならない。
llm -> prompt -> result、llm -> prompt + prompt encoded as skill -> result、llm -> prompt + deterministic code encoded as skill -> resultに進んでいくようだ。初期にコード生成をプロンプトでやらせれば、決定的コードへ行く道を短縮はできるが、それでもなお 非決定的ラッパー の中に決定的コードを入れていることになる。長期課題を成功させるには、多くの場合欠けている決定性レイヤーが必要だ。
エージェントループやフレームワークを通じて、非決定的境界の外側に決定的コードを置く必要がある。そうすれば
決定的エージェントフロー -> 非決定的意思決定 -> 決定的ツールのように、非決定的判断が決定性レイヤーの間に挟まる。実験では非常に強力なパターンで、auto-researcherのようなツールでエージェントが自ら決定性を作れるとさらに強い。今は小さな ドメイン特化言語 と単一ツールを使い、エージェントがその言語で書いたスクリプトを入力させている。より動的なユースケースを扱えるし、誤った文法はパーサーが簡単に捉えてエージェントに返せる。
ハードウェア制御チームは文書とスプレッドシートで仕様を渡し、モバイルチームはそれを見てインターフェースライブラリをコーディングし、サーバーと合わせて検証していた。私は文書をTSVに変換し、その一部をClaudeに送って、人間が書いた仕様の微妙な部分を保持するTSVパーサーを書かせた。
すべての境界ケースを処理し、中間結果をJSONで生成するまでに150回を超える反復が必要だった。その後、ClaudeはApolloの上にカスタムの接着コードを載せ、モバイルアプリが消費するコードを生成するコードジェネレーターを書くのを手伝ってくれた。
このパイプライン全体はGithub Actionsの一部として動き、ライブラリバリデーターが失敗したときだけClaudeを呼ぶ。失敗時には、何が悪かったかを特定し、解決策を提案し、PRを作るよう依頼内容に含まれるmdファイルがある。その後は人間がレビュー、修正、マージする。ここまでに使った総クレジットは350ドル未満だ。
趣旨には同意するが、結論は変えるべきだと思う。プロンプトの限界にぶつかったら、実行時にLLMで作業をさせようとするのではなく、その作業を行うソフトウェア をLLMに書かせるべきだ。
実行時におけるLLMの役割は、通常はユーザーが厳格なビジネスルールを内蔵したソフトウェアシステムに合う入力を選ぶのを助ける程度に縮小していくはずだ。
最初の週はプロンプトがどんどん大きくなり、性能は下がる一方だった。2週目には、ノート、タスク、プロジェクト、人のようなオブジェクトを正確に定義し、それらに対してよく定義された操作を行うメソッドを定義することに集中した。指摘のとおり、エージェントの表面は自然言語を入力バリデーターを通るコマンドと引数へ変換する 翻訳レイヤー に縮小される。
こういうLLMならstrawberryテストでもっとうまくやれたかもしれない。
Claudeに、私のワークフローでテスト実行のようなよくあるケースを処理するシェルスクリプトをいくつか自分で書かせた。おかげで今では30分間ぐるぐる回るのではなく、そのツールを実行してセットアップを終える。
何かをするために一回限りの奇怪なシェルやPythonワンライナーの実行許可を求めてくるたびに、自動承認できるツールを使わせるべきか考えるようになった。
だからこそ「次世代AI」という表現をよく使う。単にLLMだけを意味しているわけではない。LLMはかなり面白く、基礎的な進歩がこれ以上なくても、より興味深い形で活用・最適化され続けることで、まだ価値を生み続けると思う。
しかし一部には、どんな形であれ 根本的な次世代の改善 が必要に見える。LLMが「絶対にXするな」をぼかしてしまい、多くの作業の末に「お願いだからXしろ」のように受け取ってしまう現象は、その動作原理の根っこに近いように見える。私たちはまだ何ができるか探っている初期の興奮の中にいるので忘れがちだが、LLMがAIに求めるすべてではない。
「絶対にXするな」を人間のように扱える構造が必要だ。「コンテキストウィンドウ」の代わりに、人間に近い記憶階層を持つ構造も必要だ。最初は同じAIだったとしても、2人が十分長く会話すれば、最終的には2つのAIが単にコンテキストウィンドウだけ違うのではなく、実際に別個の存在になるようなものだ。
もちろんそれがどんな姿になるかは誰にも分からない。ただ、LLMがAIの最終解答だと考える理由もない。
プロンプト強制 → 決定的フロー → プロンプト強制へと一周回った立場からすると、同意しない。
「飛ばすな」が失敗するのは、エージェントが多すぎる仕事を抱えていて、コンテキスト内の他の内容がこの指示から注意を奪うからだ。
しかし、強制を担当するエージェントが、作る側のエージェントと同じでなければならないと言った人はいない。決定的制御フローに賢い意思決定ロジックをある程度エンコードすることはできるが、硬直的すぎると機能せず、複雑すぎると、いっそエージェントを使うほうがセットアップと保守のコストが安い。
要するに必要なのは3種類のエージェントだ。ループを管理し、問題が起きたら適切なものを起動する 監督者、適切なエージェントへ委任し、必要な場所でガードレールを強制する オーケストレーター、そして作業単位を実行する ワーカー だ。
私から見ると、あらゆるハーネスがこの点で間違っていて、中にはひどく間違っているものもある。
たとえば スラッシュコマンド は誤った機能だ。コンテキストウィンドウの状態やこのセッションで使った金額を確認するために、チャットボットが1ターンを終えるまで待たなければならないべきではない。制御はチャットループと直交しているべきだ。
テキスト生成器の入出力を制御することとは何の関係もないものまで、「チャットなんだからIRCボットみたいに回そう」という理由だけでチャット動作に絡め取られている。
最近はLLMエージェントが山ほどあるが、制御とエージェントループと表現レイヤーをきちんと分離したものはほとんどない。いくつかは最低限ヘッドレスモードがあるので、その点は良い。
/statusがちゃんと動く。他はそうでもないが。
会話の間を行き来したり更新を見たりするのにも便利だ。たまに端末でClaude Codeやopencodeを使うが、Codexデスクトップアプリに比べると体験はずっと悪い。
「文は提案であり、関数が幻覚を起こしながら『Success』を返すプログラミング言語を想像してみろ。推論は不可能になり、複雑さが増すほど信頼性は崩れる」という話は、本質的には 宣言型プログラミング に近い。
ほとんどの伝統的プログラミングは、開発者になじみのある命令型だ。正確な指示の集合を与え、そのとおりに従うことを期待する。エージェントは命令型よりはるかに宣言型に近く、結果を与えると、その結果を得るために動く。
もちろんSQLのような宣言型では結果がかなり一貫していてよく定義されているが、それでもどう処理するかは内部エンジンを信頼している。エージェントを宣言型として考えると、ルーブ・ゴールドバーグ式の「制御」システムを設計しようとするよりずっと助けになった。合わなければ検証して間違いだと報告し、再試行するか別のアプローチを取ればいい。
本当に命令型が必要なら命令型で書けばいい。あるいはエージェントにそう書かせればいい。これは作業に合っていない道具を使おうとしているように読める。
宣言型に見えるかもしれないが、それは幻想の中での話だ。私たちは実際にはAIに目標を説明して解釈させているのではなく、人間代理のキャラクターがコンピューターキャラクターと会話する物語文書があり、現実の私たちは、その背後でLLMがより首尾一貫した物語をつなぎ合わせ、その中から有用なものを取り出せることを期待している。
これは単なる学術的な区別ではない。物語があると分かっていれば、入力と出力の関係を理解して戦略を立てるモデルの改善につながる。たとえばプロンプトインジェクションのようなリスクを理解する助けになり、どの学習データを入れるか外すかの指針にもなる。
そうするとLLMと似た問題、つまり非常に注意しないと静かな失敗、反復、矛盾にぶつかる。本質は同じ閉世界仮定の問題かもしれない。LLMではそれが、知らないと認める代わりに幻覚として現れる。
そして言うとおり、非決定的なLLMに宣言型で「この最終状態へ連れていけ」と指示すると、軌道を外れる可能性はさらに高くなる。
だが、クエリ自体がLLMのように変化するわけではない。
この問題はかなり長く考えてきた。専門化の話ともつながる。モデルが専門化するほど、基礎レベルの能力はむしろ下がるように見え、ごくわずかな抽象化を目指すと両方の利点を得られるかもしれない。
かなり具体的な例だが、考える材料にはなる。
Podcast 20分要約: https://pub-6333550e348d4a5abe6f40ae47d2925c.r2.dev/EP008.ht...
論文: https://arxiv.org/abs/2605.00225
2023年のAuto-GPTの時点ですでに見えていた。人々はGPTに「運転」させていたが、ほとんどの場合に実際必要だったのはPython10行と、おそらく数回の
llm()呼び出しだけだった。代替案は、その10行のPythonを可能な限り最も高価で、遅く、信頼性の低い方法で実行することだ。もちろん人気はあった。
たとえば大半はエージェントをインターネット調査に使っていた。何時間も動かしたあげく散漫になったり、元々何をしていたか忘れたりした。
一方で
import duckduckgoとimport llmで、20秒で同じことをする10行コードを書けるし、実際に決定的に動き、コストは50分の1で済む。現在のモデルははるかに良くなっていて、Auto-GPTが今なら現実になるほど十分改善している。だが、仕様の甘い制御フロー を可能な限り最も高価なやり方で実行するのが依然として悪い考えであることに変わりはない。