【ITニュース解説】Fix Silent 30s AI Stream Timeouts with a Client Watchdog
2026年09月05日に「Dev.to」が公開したITニュース「Fix Silent 30s AI Stream Timeouts with a Client Watchdog」について初心者にもわかりやすく解説しています。
ITニュース概要
AIストリームが30秒で途切れる問題は、AIモデルが停止したのではなく、プロキシがアイドル状態をタイムアウトと判断し接続を切断するため。ユーザーには「考え中」と表示され続ける。これを防ぐには、クライアント側でウォッチドッグを導入し、データ受信の沈黙を検知して適切な状態をユーザーに提示し、復旧を促す必要がある。
ITニュース解説
AIチャットボットなど、人工知能が生成した文章をリアルタイムで受け取る「AIストリーミング」は、現代のITシステムで広く利用されている。しかし、このAIストリーミングで、まるで呪われているかのように、ぴったり30秒で応答が途絶えてしまう奇妙な現象が発生することがある。ユーザーインターフェース上では、応答を待つスピナー(処理中を示すくるくる回るアイコン)が回り続けているのに、続きの文章は一向に届かず、何のメッセージも表示されないまま停止してしまうのだ。これはシステムエンジニアを目指す人にとって、原因特定が非常に難しい、厄介な問題となる。
この現象は、ストリーミングが始まってから合計で30秒経過したわけではなく、「データが途絶えてから30秒」という沈黙の時間が原因で発生している。つまり、AIモデルが次の最適な単語や文章を生成するために「考えている」間や、外部ツールとの連携で一時的に待機している間など、一時的にデータ送信が途切れた時にこの問題が起こるのだ。
なぜこのようなことが起きるのだろうか。その鍵は「プロキシ」というシステムにある。多くのWebサービスでは、ユーザーとAIモデルが直接通信するのではなく、その間に「リバースプロキシ」と呼ばれる仲介サーバーを挟んでいる。このプロキシは、セキュリティの強化や負荷分散、キャッシュなどの様々な役割を担っている。今回問題を引き起こしているのは、このプロキシに設定されている「proxy_read_timeout(プロキシ読み取りタイムアウト)」という設定だ。
proxy_read_timeoutは、プロキシが上流のサーバー(この場合はAIモデルのサーバー)から新しいデータチャンクを読み込むのを待つ最大時間を指す。もしこの時間内に何もデータが送られてこなければ、プロキシは「上流の接続が死んだ」と判断し、一方的に接続を切断してしまう。今回のケースでは、この設定値が30秒に設定されていたため、AIモデルが30秒以上沈黙すると、プロキシが接続を切断していたのだ。厄介なのは、この切断がユーザー側(クライアント)に何のエラー通知もなしに行われる点である。クライアント側では、ただ単にデータが来なくなり、接続が切れたという「done」の信号も、「エラー」の信号も受け取らない。結果として、スピナーは回り続け、ユーザーはいつまでも応答を待ち続けるという、非常に悪いユーザー体験になってしまう。
この問題を特定するためには、慎重なデバッグが必要だ。 まず、この問題がブラウザ特有のものなのか、それともより広範なインフラの問題なのかを切り分けるために、ブラウザを使わず「curl」のようなコマンドラインツールでAIストリーミングを試す。ここで同じように30秒で停止すれば、ブラウザの問題ではないとわかる。 次に、プロキシを介さず、AIモデルの提供元(プロバイダー)に直接リクエストを送信してみる。もし直接接続した場合は30秒以上継続して生成できるのであれば、AIモデル自体に問題はなく、プロxyが原因であることが絞り込める。 さらに確実にするため、意図的に35秒間データを送信しないという「思考の一時停止」をシミュレートするローカルの簡易プロキシを作成し、これを介して接続を試す。もしこの簡易プロキシを介した時にのみ接続が切れるのであれば、プロキシのタイムアウト設定が根本原因であると確定できる。
この原因がプロキシのproxy_read_timeoutにあると判明しても、特に無料サーバーや他社サービスを利用している場合、その設定値を自分たちで変更することは難しい場合が多い。そこで、解決策はユーザー側(クライアントサイド)に「ウォッチドッグ」を導入することとなる。
ウォッチドッグとは、コンピュータシステムにおける「見張り番」のような役割を果たすプログラムや機能のことだ。ここでは、AIストリーミング中に「データがちゃんと届いているか」を監視するタイマーとして機能する。 このウォッチドッグは、AIモデルから新しいデータチャンクが届くたびにタイマーをリセットする。もし、設定された時間(例えば15秒)内に次のデータチャンクが届かなければ、「接続がストール(一時停止)している」と判断し、その状態をユーザーインターフェースに表示する。さらに、もし沈黙が既知のプロキシタイムアウト時間(例えば30秒)を超えた場合、「接続がタイムアウトした」と判断し、その旨をユーザーに明確に伝える。これにより、ユーザーは誤解を招くスピナーが回り続けるのを延々と待つのではなく、「ストール中」や「タイムアウト」といった正直な状態変化を知ることができるようになる。
ユーザーエクスペリエンスの観点から見ると、これは大きな改善である。従来のスピナーのみのUIでは、ユーザーはひたすら待ち、最終的には途中までの応答を失い、ページを再読み込みするしかなかった。しかし、ウォッチドッグを導入することで、ユーザーは15秒の沈黙後に「ストール状態」を知り、30秒で「タイムアウト」を把握し、そこから「続きを生成する」か「最初からやり直す」かの選択肢を得られる。
「続きを生成する」という選択肢は、部分的に受け取った応答を再利用する非常に有効な方法だ。AIモデルは基本的に「ステートレス(状態を持たない)」なので、一度切断されたストリームを直接再開することはできない。しかし、これまでに生成されたテキストを新しいプロンプトに含めて、「この続きから生成してほしい」とAIモデルに依頼することで、実質的に中断した箇所から再開させることが可能となる。例えば、「前の応答は途中で中断されました。これまでの生成内容は次の通り:[これまでのテキスト]。ここから中断した正確なポイントで続きを生成してください。上記のテキストを繰り返したり、要約したりしないでください」といった具体的な指示をプロンプトとして送ることで、ユーザーは失われた情報を取り戻せる。
また、視覚に障がいを持つユーザーがスクリーンリーダーを使用している場合、UIに変化がないと応答が完了したと誤解してしまう可能性がある。ウォッチドッグによる状態変化をライブリージョン(Webページ内の動的なコンテンツをスクリーンリーダーに通知する領域)に表示することで、「接続がストールしました」「接続がタイムアウトしました」といった情報を音声で伝えられるようになり、アクセシビリティも向上する。ユーザーが途中でストリームをキャンセルしたい場合も、キーボード操作で容易に実行できるべきだ。
ただし、このウォッチドッグと回復のパターンが常に最適とは限らない。例えば、生成される情報が常に完璧な形で終了する必要がある場合や、プロキシ自体がストリームの途切れないことを保証する「ハートビート」機能(定期的に微小なデータを送って接続維持をアピールする機能)をサポートしている場合、あるいはAIプロバイダーが中断された生成を再開するための専用APIを提供している場合は、よりシンプルな解決策や、プロバイダー側の機能を利用する方が良い。また、短時間の生成で頻繁に中断が起こるような状況では、ウォッチドッグが過剰な通知を出すことになり、かえってユーザーを混乱させる可能性もある。ウォッチドッグは、特に長い文章の生成で中断が発生した場合に、途中経過を失うことの痛みが大きいケースで非常に有効な手段と言えるだろう。
この「30秒の呪い」から学ぶべき教訓は、システムの問題を特定する際には、まず徹底的に計測し、各レイヤーを分離してデバッグすることの重要性だ。そして、ユーザーインターフェースのスピナーのような状態表示は、常に正直であるべきだということである。無料のAIサービスでは、コスト保護のためにプロキシのタイムアウト設定が厳しくなっている場合が多く、このような隠れた挙動に遭遇する機会が多い。これは、本番環境で問題が起こる前に、開発段階で様々な失敗パターンを学び、対処法を身につける良い機会となる。システムエンジニアを目指す上では、このようなシステム設計上の課題と、それを解決するための実践的なアプローチを理解することが不可欠だ。