F-13: プッシュ vs プル¶
この相反は何か¶
AI エージェントの処理結果や進捗状況をクライアントに伝達する際、「サーバー側から能動的に通知するか(プッシュ:SSE/WebSocket/Webhook)、それともクライアント側から定期的に問い合わせるか(プル:ポーリング)」という二択が F-13 の相反です。
この選択が排他的になる理由は、通信の主導権がサーバー側にあるかクライアント側にあるかで、インフラ構成・接続管理・障害対応の設計が根本的に異なるためです。プッシュでは常時接続(WebSocket)またはサーバー発信のイベントストリーム(SSE)を維持する必要があり、プルではクライアントが定期的にリクエストを送るステートレスな構成になります。同一の通信チャネルで両方を同時に行うことは通常ありません。
AI エージェントの文脈でこの相反が重要になるのは、エージェント処理のレイテンシが数秒から数十分まで広範囲に分布するためです。従来の Web API では数百ミリ秒で応答が返るため通信方式の選択が大きな影響を持ちませんでしたが、エージェントでは待機時間が長く、進捗表示・中間結果の提示・タイムアウト回避がすべてこの選択に依存します。
選択肢A:プッシュ(SSE / WebSocket / Webhook)¶
プッシュ方式では、サーバー側がイベントの発生(処理進捗、中間結果、完了通知)を検知した時点で、即座にクライアントに通知を送ります。技術的には Server-Sent Events(SSE)、WebSocket、Webhook の三種類が主に使われます。
SSE は HTTP ベースの単方向ストリームで、サーバーからクライアントへのイベント配信に特化しています。HTTP/2 との親和性が高く、既存のロードバランサーやプロキシとの互換性も良好です。エージェントの「トークンストリーミング」や「ステップ進捗通知」に最も適した技術です。
WebSocket は双方向通信を提供し、クライアントからの中断指示やパラメータ変更をリアルタイムで受け付ける場合に有用です。ただし、ステートフルな接続管理が必要になり、スケールアウト時のセッションアフィニティやロードバランサーの設定が複雑になります。
Webhook はサーバー間通知に適しており、処理完了時にクライアントが指定したエンドポイントに結果を POST します。クライアントが常時接続を維持する必要がなく、バッチ処理やシステム間連携に向いています。
プッシュの強みは即時性とリソース効率です。イベント発生と同時にクライアントに伝わるため、ユーザーは処理の進行状況をリアルタイムで確認できます。また、変化がないときには通信が発生しないため、ポーリングのような無駄なリクエストがありません。
弱点はインフラの複雑さです。常時接続の管理、接続断時の再接続ロジック、メッセージの順序保証、exactly-once 配信の保証など、考慮すべき点が多くなります。
選択肢B:プル(ポーリング)¶
プル方式では、クライアントが定期的にサーバーに対して「処理は終わりましたか」と問い合わせます。サーバーは現在の状態(処理中・完了・エラー)を返すだけで、能動的な通知は行いません。
プルの最大の強みは実装と運用のシンプルさです。サーバーは通常の REST API としてステートレスに動作でき、ロードバランサーやキャッシュの構成も標準的です。常時接続の管理が不要なため、スケールアウトが容易で、CDN や API Gateway との相性も良好です。クライアント側の実装も setInterval で十分であり、接続断の処理や再接続ロジックが不要です。
また、クライアントが自分のペースで問い合わせるため、サーバー側の負荷変動に左右されません。プッシュでは大量のクライアントへの同時通知がサーバーに負荷スパイクを生じさせますが、プルではクライアント側で間隔を調整できます。
弱点は遅延とリソースの浪費です。ポーリング間隔が長ければ結果の取得が遅れ、短ければ「変化なし」の応答が大量に発生してサーバー負荷が増加します。特にエージェント処理のように完了タイミングが予測困難な場合、ポーリング間隔の最適化が難しくなります。
ユーザー体験の観点では、処理の進行状況がリアルタイムに見えないため、長時間処理では「動いているのか止まっているのか分からない」という不安を生みやすいです。
選定基準¶
この相反の選定は主に [latency_budget] で決まります。
プッシュを選ぶべき条件:
- リアルタイムの進捗表示が必要な場合。ユーザーが画面を見ながら待つ対話型アプリケーションでは、トークンのストリーミング表示やステップの進行状況の表示がユーザー体験を大きく改善します。
[latency_budget]が中程度(数秒~数分)の場合。完了を待つ間に進捗を表示することで、ユーザーの離脱を防げます。- 処理中にクライアントからの介入(中断、承認、パラメータ変更)が発生する場合。WebSocket の双方向通信が必要になります。
プルを選ぶべき条件:
- システム間連携でリアルタイム性が不要な場合。バッチ処理の完了確認や、非対話型のジョブ管理にはポーリングで十分です。
- インフラの制約でステートフルな接続を維持できない場合。サーバーレス環境や、WebSocket 非対応のプロキシ経由でのアクセスなどです。
- クライアントの数が極めて多く、常時接続の管理が現実的でない場合。
デフォルトとハイブリッド¶
デフォルト:ユーザー向けの進捗表示が必要ならプッシュ(SSE)、システム間連携でリアルタイム性が不要ならプルを選びます。
ハイブリッド:進捗通知は SSE、完了通知は Webhook(またはポーリング) という組み合わせが実用的です。処理中の進捗はストリーミングで表示しつつ、最終結果の確定通知は信頼性の高い Webhook で送ります。SSE 接続が切れた場合のフォールバックとしてポーリングを用意しておくと、接続断時の復旧が容易になります。A3 Sync Facade over Async Core はこのハイブリッド構成を体系化しています。
判断を誤ったときの症状¶
プルを選んだがプッシュが必要だった場合の症状:
- ユーザーから「処理が動いているのか分からない」「待ち時間が長く感じる」という不満が寄せられます。実際の処理時間は同じでも、進捗が見えないことで体感時間が長くなります。
- ポーリング間隔の調整に苦労しています。短すぎると API の負荷が問題になり、長すぎると結果の取得が遅れます。適切な間隔は処理ごとに異なり、固定値では最適化できません。
- ポーリングのリクエスト数がトラフィックの大部分を占め、本来の処理リクエストよりもステータス確認リクエストの方が多いという状況が発生しています。
プッシュを選んだがプルで十分だった場合の症状:
- WebSocket や SSE の接続管理にインフラ運用工数の大部分が割かれています。接続断、再接続、メッセージ順序の問題への対応が継続的に発生しています。
- ロードバランサーのセッションアフィニティ設定が複雑化し、スケールアウトの障害になっています。
- 実際のユーザー行動を分析すると、進捗表示を見ているユーザーは少なく、大半は処理を開始した後に別の作業をしており、完了通知だけで十分でした。
関連パターン¶
- A3 Sync Facade over Async Core --- 同期/非同期の自動切替を行う際に、非同期側の通知方式としてプッシュとプルのハイブリッドを採用します。
- A2 Durable Async Agent --- 長時間処理のジョブ管理において、完了通知の手段としてこの相反が関わります。
- A1 Sync Edge Agent --- 同期処理であればこの相反は発生しません。処理が同期の閾値を超える場合に初めて選択が必要になります。