トピックを検索
ストリーミング中に細切れで読み上げられてしまうAIチャットの返答
AIアシスタントが返答を数語ずつrole="log"に書き足すため、スクリーンリーダーは届いた断片を一つずつ読み上げ、返答をひとまとまりで読むことがない
reply.append(chunk); 問題
スクリーンリーダーの利用者には、AIアシスタントの回答がストリーミング中に途切れ途切れの言葉として聞こえ、ひとつの回答として追えない
アウトドア用品店のFernhillは、ヘルプページにAIアシスタントを置いています。「アシスタントに質問」という見出し、あいさつから始まる会話、質問欄と「送信」ボタンがあります。会話は role="log" で作られており、新しいメッセージは欄を離れなくても読み上げられます。チャットとしては自然な作りです。スクリーンリーダーの利用者が「使ったテントも返品できますか?」と入力してEnterキーを押します。
AIの返答によくあるように、回答はストリーミングで届きます。数語が表示され、また数語が続き、やがて吹き出しが2つの文で埋まります。画面が見える人は、伸びていく文をそのまま読みます。スクリーンリーダーの利用者には、まず自分の質問が読み返され、次に「アシスタント:」、それから「はい、使用済みでも」「30日以内」「なら返品」「できます」と、届いた断片が一つずつ別々のメッセージとして聞こえます。尋ねた回答は切れ端の連続として届き、ストリーミングが止まっても全部聞けたのかが分かりません。結局、欄を離れて返答を最初から読み直します。
role="log" はライブリージョンです。追加された内容は、届いた順に、控えめに読み上げられます。メッセージがまとまって届くチャットなら、それで合っています。この例では、スクリプトがストリーミングの断片を届くたびに返答へ書き足し、その一つひとつが追加になるため、断片ごとにメッセージになります。こうした変化の連続をどう読むかは、スクリーンリーダーとブラウザーによって異なります。あるオープンソースのAIチャットには、NVDAとFirefoxで確かめた不具合として、DOMに追加されるトークンや断片の一つひとつが新しい読み上げになる、という報告が出ています。
達成基準4.1.3は、ステータスメッセージを、フォーカスを受け取らずに支援技術が利用者へ伝えられるようにすることを求めています。その解説書によれば、目的は、変化を「作業を不必要に妨げることなく」伝えることです。同じ解説書は、これに近い例にも触れています。メッセージの一部だけが変化として扱われると、利用者にはその部分しか聞こえず、文脈を伝えるには足りないことがある、というものです。この例でも返答そのものはたどれますが、ライブリージョンが伝えているのはメッセージではなく、断片です。
ストリーミングは、文字を目で追う人に向けた作りです。アシスタントが動いていることが分かり、すぐに読み始められます。返答が長いほど細かく分かれて聞こえ、箇条書きや手順を含む返答は、何十もの切れ端として読まれることがあります。
10グループ中2グループに影響
- 全盲 (影響あり)
- 弱視 (影響なし)
- 色覚 (影響なし)
- ろう (影響なし)
- 難聴 (影響なし)
- 発話 (影響なし)
- 手の操作 (影響なし)
- 筋力・可動域 (影響なし)
- 認知 (影響あり)
- 光過敏 (影響なし)
- 視覚を使わない スクリーンリーダーの利用者が質問すると、回答は2、3語ずつの切れ端として、届くたびに別々のメッセージとして読まれます。文の切れ目も分からないため、ストリーミングが終わるのを待ってから、返答全体を読み直すことになります。
- 言語・認知・学習の能力が限られる 文字を読むのが難しいためにスクリーンリーダーで聞いている人は、一つずつ読まれる断片から文をつなぎ直さなければなりません。同じ文をひと続きで聞くより、ずっと追いにくくなります。
スキャンには、あいさつの入った名前付きの role="log"、ラベルのある欄、ボタンが見え、問題は見つかりません。W3Cがチャットの履歴に示しているパターンそのものだからです。問題は返答がストリーミングで届く間にしか起きず、それにはアシスタントに質問する必要がありますが、監査は公開中のサイトでそれをしません。EaseWebの監査は、AIアシスタントと名の付いた部分で、入力欄の横にライブリージョンの会話があると、人が実際に質問して確かめる項目として挙げます。ただし、返答がストリーミングで届くかどうかは、ページからは分かりません。チャットをアクセシブルにするよう頼まれたAIのコーディングアシスタントが、会話に role="log" や aria-live="polite" を付けることもあります。それがまさにこのパターンです。AIチャット向けのコンポーネントライブラリーにも同じものがあります。たとえばVercelのAI Elementsの会話コンポーネントは、2026年10月の時点で、各返答がストリーミングで書き込まれる role="log" です。返答が届く間にスクリーンリーダーで聞いて、初めて分かります。
試してみる
質問を送り、ストリーミングで届く回答がどう読まれるかを聞いてみましょう
ページの下の欄に、スクリーンリーダーが読み上げるメッセージが順に並びます。いくつに分かれていますか?回答がひとまとまりで聞こえることはありますか?次に、それぞれの修正を適用して、もう一度送ってください。
問題あり 修正できますか?
Fernhill ヘルプ
アシスタントに質問
アシスタント:こんにちは。ご注文、サイズ、返品についてお尋ねください。
スクリーンリーダーの読み上げ:
(質問を送ってください)
修正方法
返答がそろってから、一度だけ伝える
<section class="assistant" aria-labelledby="assistant-title"> <h2 id="assistant-title">アシスタントに質問</h2> <div class="messages" role="log" aria-label="会話"> <p class="message from-assistant"><span class="visually-hidden">アシスタント:</span>こんにちは。ご注文、サイズ、返品についてお尋ねください。</p> </div> <form class="ask-form"> <label for="ask-input">質問</label> <input id="ask-input" autocomplete="off"> <button>送信</button> </form></section>問題点2点
会話は名前の付いたログで、どのメッセージも書いた人を伝え、フォーカスは欄に残ります。問題はストリーミングにあります。
- 断片はライブリージョンのログの中で返答に書き足されるため、「はい、使用済みでも」「30日以内」「なら返品」と、一つずつ別のメッセージとして読み上げられます。
- 返答がそろったときには何も伝えられないため、利用者は聞いた内容から、回答が終わったのか、まだ続くのかを判断できません。
そろった返答を一度だけ読む
<section class="assistant" aria-labelledby="assistant-title"> <h2 id="assistant-title">アシスタントに質問</h2> <div class="messages" role="log" aria-label="会話" aria-live="off"> <p class="message from-assistant"><span class="visually-hidden">アシスタント:</span>こんにちは。ご注文、サイズ、返品についてお尋ねください。</p> </div> <p id="reply-status" class="visually-hidden" role="status"></p> <form class="ask-form"> <label for="ask-input">質問</label> <input id="ask-input" autocomplete="off"> <button>送信</button> </form></section>修正の手順4ステップ
- 会話に
aria-live="off"を付けます。名前の付いたログのままなので、利用者は見つけてさかのぼって読めますが、フォーカスがほかにある間は、追加された内容は読み上げられません。ストリーミングは静かになります。 - 視覚的に隠した空の
pにrole="status"を付け、会話の外、そのすぐ横に置きます。最初の質問より前からページにあるため、書き込むと、控えめに読み上げられる変化になります。 - 最後の断片が届いたら、返答の全文をそこに書き込みます。返答の文字は隠した「アシスタント:」から始まるため、スクリーンリーダーは読み上げ中の内容を終えてから、「アシスタント:はい、使用済みでも30日以内なら返品できます…」とひとまとまりで一度読みます。
- 質問を送るときに領域を空にします。前の返答とたまたま一字一句同じ返答でも、変化として読まれます。
これで修正前の問題は直り、4.1.3を満たします。返答は、フォーカスを動かさずに、ひとつのメッセージとして一度伝えられます。ただし、返答が長いほど、何か聞こえるまでの待ち時間が延び、一度に聞く量も増えます。
この方法を選ぶ理由:変化が落ち着いてから、一度だけ伝える
どちらもストリーミング中は何も読まず、そろったときに一つだけ伝えます。違うのは、その一つが何かです。返答の全文を読む方法は、ショップのヘルプのように、答えが1、2文で済むアシスタントに合っています。ほかのチャットでメッセージが届いたときと同じく、何も操作しなくても答えが聞こえます。ただし、返答は区切りのないひと続きの文として読まれるため、箇条書き、表、コードは形を失い、数段落の返答は、読み直す以外に戻る手段のないまま長く聞き続けることになります。短いメッセージで伝える方法は、答えが長いアシスタントや、構造のある答えを返すアシスタントに合っています。利用者は答えがそろったことを聞き、見出し、箇条書き、コードを保ったまま、ほかの文章と同じように自分のペースで読みます。普段の答えの長さと形で選んでください。
修正にならないもの:aria-busy。 ARIAは、こうした場面での使い方として aria-busy="true" を挙げています。返答のストリーミング中はログに付け、そろったら false に戻すと、支援技術は変化をまとめて一度に読んでもよい、とされています。仕様の言葉は「してもよい」なので、スクリーンリーダーが変化を届いたそばから読んでも仕様には反しません。busy にしたログでも細切れに読まれることがあります。どちらの修正と一緒に使っても害はありませんが、それだけでは修正になりません。
修正にならないもの:返答の吹き出しを atomic にする。 吹き出しに aria-atomic="true" を付けたり、断片が届くたびに返答の全文を書き直したりすると、変化のたびに返答が最初の語から読み直されます。返答が伸びるにつれ、利用者は冒頭の言葉を何度も聞くことになります。
最初の語が届くまでの待ち時間。 多くのアシスタントは、返答が始まる前に「考え中…」の表示を出します。伝える必要があるほど待ち時間が長いなら、ステータス領域に一度だけ書き込みます。繰り返し出る表示や、文言が毎秒変わる表示を、ライブリージョンの中に置かないでください。待ち時間そのものについては、読み上げられない読み込み中の表示で扱っています。
止めた返答と失敗した返答。 「停止」ボタンはストリーミングを途中で終わらせます。ステータス領域に「停止しました。」か、途中までの返答を書き込み、回答が完全ではないことを伝えます。ストリーミングが失敗したときは、何が失敗し、どうすればよいかを同じ領域に書き込みます(「アシスタントが回答できませんでした。もう一度お試しください。」)。ストリーミングがどう終わっても書き込まれるよう、finally ブロックの中か try のあとに書きます。
返答が複数あるとき。 2、3通のメッセージで答えるアシスタントや、ツールを動かして各段階を報告するアシスタントでも、伝えるのはそろったメッセージ1通ごとに一つです。ツールの段階ごとに読み上げたり、ステータス領域にメッセージを積み重ねたりしないでください。領域には最新の一つだけを入れます。
assertive ではなく polite に。 返答は、利用者が読んでいる内容に割り込むほど急ぎのものではありません。role="alert" や aria-live="assertive" は使わないでください。
返答へフォーカスを移さない。 移せば返答は読まれますが、利用者が次の質問を入力している欄からカーソルが抜けてしまいます。
まったく読み上げないチャット。 会話がどのライブリージョンにもなく、返答がまったく読まれないチャットは、別の問題です。メッセージを読み上げないチャットウィジェットを参照してください。そこでの修正は、まとまって届く返答のために role="log" を使います。返答がストリーミングで届くなら、このガイドの、ログを静かにする方法とステータス領域を加えてください。
修正の確認
4項目、マウス不要
確認方法: スクリーンリーダー、キーボード
OS、ブラウザーと支援技術のバージョン、ビルド、実施日、各手順の実際の結果を記録してください。チャットのコンポーネント、ストリーミングの処理、コンポーネントライブラリーを変更した際は、再度確認します。
スクリーンリーダーでの role="status"、role="log"、role="log" aria-live="off"の読まれ方
制限
ライブリージョンの変化が続いたとき、どんな間隔で読むか、そもそも読み上げるのか、後回しにするのか、飛ばすのかは、スクリーンリーダー、ブラウザー、そのリリースによって異なります。利用者が使うものでストリーミングを確かめてください。細切れに読まれる返答を、伝えられているのがメッセージではないとして4.1.3の不適合とする監査人もいれば、ログが変化を一つずつ伝えてはいるとして使い勝手の指摘にとどめる監査人もいます。どちらにしても、利用者は回答を追えません。このガイドは、一つのページの一つのアシスタントを扱っています。ベンダーのフレームの中に描かれるアシスタントは、ベンダーの設定からしか変えられません。ベンダーに尋ねることは、メッセージを読み上げないチャットウィジェットにまとめています。自動で変わるスコア、ティッカー、タイマーは別の問題で、スクリーンリーダーが読み上げ続けるカウントダウンタイマーで扱っています。
よくある問題を再現した学習用のサンプルです。実在のクライアントの診断結果ではありません。コードは出発点となる実装例のため、実際の製品でも確認してください。ここでは支援技術でのテスト結果は報告していません。
EaseWebのサービス
修正した問題を、次のリリースで再発させない
このガイドのような修正方法を、コーディングエージェントが従うルールと、CIのチェックに組み込みます。あわせて定期的に、キーボードとスクリーンリーダーで手動の再テストを行います。