while(true)ループがもたらす「不整合」と「脱線」の罠
Claude CodeやCodexなど、現在主流のAIエージェントは、状態を蓄積するwhile(true)ループを共通の骨格として持つケースが多い(dev.toの解説記事)。この構造は直感的で実装が容易な反面、根本的な脆弱性を抱えている。
具体的には、実行中の中断やツール呼び出しの失敗が発生した際、ループ内部に蓄積された状態が不整合になりやすい。一度不整合が生じると、どの時点でどの状態だったのかを特定するためのデバッグや、同じ入力を再実行して挙動を再現するリプレイが極めて困難になる。
さらに、ループが継続するたびにコンテキストが肥大化し、制御フローがブラックボックス化していく。エージェントが「なぜこの判断を下したのか」を事後的に追跡できなくなることは、本番環境での信頼性を左右する技術負債として蓄積される可能性がある。
Pizzaの設計思想:イベントソーシングによる「不変ログ」への転換
Pizzaは、この問題に対してイベントソーシングの思想を適用している。メッセージ、ツール呼び出し、ファイル編集をすべてEventStore内のイベントとして記録し、一度書き込まれたイベントは変更されない不変ログとして扱われる(dev.toの解説記事)。
重要な設計判断は、UI表示、LLMへのコンテキスト構築、セッションツリーの表示がすべてこの同じログからの投影(projection)である点にある。状態はメモリ内に保持されるのではなく、ログからいつでも再構築可能であり、特定の時点の状態を再現したい場合はその時点までのイベントをリプレイすればよい。
EventStoreのストレージにはSQLiteが使用されており、すべての変更が不変ログに追記されることで、データの整合性と追跡可能性が担保される(PizzaのGitHubリポジトリ)。
flowchart LR
A["エージェント実行"] --> B["イベント生成"]
B --> C["EventStore (SQLite)"]
C --> D["UI投影"]
C --> E["LLMコンテキスト"]
C --> F["セッションツリー"]
ガードレール設計:ツール呼び出しとセッション分離のパターン
Pizzaは、モデルにread・write・grepなどの個別ツールを直接提供するのではなく、CLIツールのみを暴露し、シェルのパイプラインをモデル自身に構成させる(dev.toの解説記事)。この設計により、モデルが実行できる操作の範囲がOSのシェルセマンティクスに制約され、予期せぬ副作用の発生領域が物理的に狭まる。
また、異なるワークスペースに配置されたエージェント間の通信は、tellイベントを通じてのみ可能であり、コンテキストの横漏れを構造的に防ぐ。エージェントが「隣の作業内容を参照したがる」ような脱線を、アーキテクチャレベルで遮断する。
| 設計軸 | 個別ツール直接呼び出し型 | CLIパイプライン構成型(Pizza) |
|---|---|---|
| モデルの操作範囲 | APIが許可した範囲 | シェルセマンティクスで制約 |
| 副作用の追跡 | ツール呼び出しログに依存 | イベントログで全操作が記録 |
| セッション間漏洩 | コンテキスト共有で生じうる | tellイベント経由のみ許可 |
実行モデルの転換:Reactor駆動によるターンサイクルの実装
Pizzaはwhile(true)ループに代えて、Reactor駆動のターンサイクルを採用している。各ターンはイベントハンドラテーブルによって処理され、どのイベントがどのハンドラにルーティングされるかが明示的に定義される(PizzaのGitHubリポジトリ)。
この設計の利点は、非同期に到着するイベントに対して決定論的に応答できる点にある。ループ型では「今どの状態か」を推測する必要があったが、Reactor型ではイベントの種類とハンドラの対応が静的に把握できるため、制御フローの可視化とテストが容易になる。
さらに、TUI、デスクトップアプリ、JSON-RPCサーバーといった異なるインターフェースが、同じSessionFacadeイベントストリームを消費できる(PizzaのGitHubリポジトリ)。フロントエンドの実装を差し替えても、バックエンドの状態管理ロジックは変更不要であり、インターフェースと状態管理の分離が明確に保たれる。
実装におけるトレードオフと課題:スナップショット管理の重要性
イベントソーシングは強力なパターンだが、無償ではない。ホットパス(リクエスト処理の経路)にデータベースの読み書きが必要となり、ログサイズが肥大化するにつれてリプレイコストが線形に増加する可能性がある(dev.toの解説記事)。
長期運用では、一定間隔でスナップショットを取得し、リプレイの起点を最新のスナップショットに切り替える仕組みが必須となる。スナップショットの取得タイミング、破棄ポリシー、整合性検証といった実装コストは、設計段階で十分に見積もっておくべきである。
普及面では、npm install -g @tomsun28/pizzaでCLIとしてインストールでき、macOS・Linux・Windowsのデスクトップ版も提供されている(PizzaのGitHubリポジトリ)。導入の摩擦を下げつつ、内部設計の複雑さを隠蔽するバランスが意識されている。
まとめ:安定したAIエージェント開発への道筋
従来型のwhile(true)ループ設計は、デバッグ困難性と状態不整合という構造的な弱点を本質的に抱えている。これは実装品質の問題ではなく、アーキテクチャの選択から来る制約である。
Pizzaの事例に見られるように、EventStoreによるイベントソーシングとReactor駆動の実行モデルは、状態の追跡可能性と制御フローの可視化を同時に得られる有力なパターンである。ただし、ログサイズ管理やスナップショット設計といったトレードオフを無視すれば、今度は別の形で運用が破綻する。
実務では、エージェントの行動範囲をガードレールで物理的に区切り、状態を不変ログに委ねる設計を基本としつつ、スケールに伴うコストを設計段階で織り込むことが、安定稼働への近道となる。
関連記事
- AIエージェントの「プロンプトは境界ではない」:実務レベルでの安全対策と設計指針
- Rust製MCPサーバー構築:型安全なAIエージェント連携の実装と設計指針
- AIエージェントのチャネル横断実装:Channels SDKを活用した統一アーキテクチャ設計
参考
本記事は海外の技術トレンド「Why Does Every AI Agent Still Look Like `while (true) { ... }`?」(DEV)で話題のテーマをもとに、両儀システムソリューションズが独自に解説したものです。