PEK2026のキーノートQ&Aで実際に利用した、日本語の質問を数秒で英訳するライブQ&Aシステムの設計と技術を紹介します。

PEK2026のライブQ&Aを支える技術 ― 言語の壁を越えるQ&A体験を設計する

著者: Azuma
Event キーノート

こんにちは、PEK2026実行委員のAzuma(@azuma_alvin)です!

先日はPEK2026にご参加いただき、誠にありがとうございました。皆さんのおかげで無事にイベントを終えることができました。

さて、Kaspar氏のキーノートでは発表後に15分間のQ&Aが設けられていました。キーノート直前に@jacopenさんからも紹介がありましたが、実はQ&Aに自作ツール「Live Q&A」を利用していました。

Live Q&AはOSSとしてGitHubで公開されており、誰でもCloudflare上でセルフホストできます。質問の投稿や投票といった一般的な機能に加え、参加者と登壇者が言語の壁を感じずにコミュニケーションをとるための自動翻訳機能を備えているのが特徴です。

この記事では、なぜ既存サービスではなく自作ツールを利用したのか、15分間のQ&Aをどのように体験設計したのかを紹介します。

昨年のQ&Aは、どうやって言語の壁を越えていたのか?

PEK2025のキーノートでは、Q&Aで人間による同時通訳とSlidoを組み合わせて利用していました。

PEK2025のQ&Aフロー

Slidoに日本語で投稿された質問を司会が読み上げると、それは同時通訳によって英語に翻訳されてキーノートスピーカーに伝わります。それから、キーノートスピーカーが英語で回答すると、それが同時通訳によって日本語に翻訳されて参加者に伝わります。

つまりQ&Aにおける言語の橋渡しを、人(同時通訳者)が担っていました。

AI同時通訳を導入すると、Q&Aに新たな課題が生まれた

PEK2026では、キーノートの同時通訳にAI同時通訳サービスのCuckooを採用しました。実行委員で事前にPoCを行い、十分なスピードと精度で参加者に翻訳を提供できると判断しました。

Cuckooを利用することで、Kaspar氏の英語を日本語字幕として参加者に届けることができます。しかしQ&Aでは、参加者の質問(日本語)をKaspar氏に英語で届けるための逆方向の翻訳が必要です。

参加者は日本語で質問したいですが、Kaspar氏は英語で質問を受け取りたいです。今回は人間による同時通訳を利用しません。司会者の私も、プロの同時通訳者と同じ速さで正確に翻訳することはできません。

キーノート本編ではCuckooによって越えることができた言語の壁を、残念ながらQ&Aでは越えることができません。

調査したところSlidoにはQ&Aの自動翻訳機能がなく、他のQ&Aサービス(例:Pigeonhole Live)では同等の機能を利用するために上位プランが必要となるケースが多かったです。

参考:Will Slido auto-translate my quiz and Q&A? | Slido Community

日本語の質問を、どうKaspar氏に届けるか?

同時通訳にはCuckoo、Q&Aツールには昨年同様Slidoを利用する前提で2通りのアイデアを検討しました。

案1:Slido + Cuckooのライブ字幕をそのまま使う

1つ目は、Slidoに投稿された日本語の質問を司会がそのまま読み上げ、Cuckooによる英語ライブ字幕をKaspar氏が読むというアイデアです。

Q&Aフロー 案1

このアイデアの長所は、追加の技術投資が不要であることです。事前にKaspar氏に「Q&Aでは英語のライブ字幕を読んでください」と一言伝えるだけです。

しかし、Kaspar氏は流れ続ける字幕から「今読まれている質問文」を探して追う必要があります。さらにCuckooはリアルタイムに字幕を表示するため、質問を読み上げ終えてから翻訳が出揃い、Kaspar氏が回答を考えるまで、数秒の沈黙が生まれます。

Kaspar氏の負担の大きさと、インタラクションの低下という2点の短所が大きすぎるため採用は見送りました。

案2:Slidoの画面に英訳を加える

2つ目は、SlidoのPresenter画面に英訳を挿入して、Kaspar氏に英語で質問を読んでもらうというアイデアです。

Q&Aフロー 案2

具体的には、Chrome拡張機能が画面の変更を検知してDOMから質問を取得し、DeepL APIまたはChromeのTranslator APIで英訳して原文と並べます。翻訳結果を localStorage に保存しておけば、投稿や投票で画面が再描画されてもすぐに再表示できます。Translator APIを利用する場合は、翻訳がローカルで完結する点も魅力でした。

自作したChrome拡張機能は予想以上に上手く動作しました。Kaspar氏も固定表示された質問の英訳を読んで回答することができます。

このアイデアで進めようと考えていたところ、一つ見落としていた条件に気づきました。

PEK2026でSlidoを利用してQ&Aを実施するのはキーノートの15分間のみです。PEK2026の規模でSlidoを利用するには最低でも53,000円のプランが必要であり、全セッションでQ&Aを実施したPEK2025とは異なり、15分間のみの費用として決して安くありません。

さらに、Chrome拡張機能はSlidoのDOMに依存します。本番直前にSlidoがUIを変更した場合、サイレントに動作しなくなる可能性もあります。これによって、Kaspar氏と参加者は貴重な機会を失ってしまう可能性さえあります。

これらを総合的に判断して、2つ目のアイデアも採用を見送ることになりました。

15分のQ&Aに必要なものだけを作る

今回のPEK2026でQ&Aを実施する時間は、キーノート発表後のわずか15分です。そのためだけにツールを自作するのは遠回りにも思えました。

ただし、今回のQ&Aに必要な機能は限られています。Slidoのような既存サービスを使う方法もありますが、必要な機能に絞って自作すれば、PEK2026に合わせてQ&Aの体験を設計でき、費用も抑えられます。

もちろん、開発には実行委員の時間がかかります。それでも、自分たちが実現したいQ&Aの形に合わせられることは大きな利点でした。将来OSSとして公開すれば、他のカンファレンスでも利用してもらえるかもしれません。

こうして、Q&Aツール「Live Q&A」を作ることにしました。

質問から回答までの流れを設計する

Q&Aツールを自作することに決め、まずは15分間のQ&Aで必要な流れを整理しました。

参加者は日本語で質問を投稿します。投稿された質問は英訳され、原文とともにスクリーンに表示されます。司会も英訳を読み上げるので、Kaspar氏は画面と音声の両方で質問を確認して回答できます。回答はキーノート本編と同じく、Cuckooの日本語字幕で参加者に届きます。

PEK2026のQ&Aフロー

これなら、参加者は日本語のまま質問と回答を追えます。Kaspar氏も、流れ続ける字幕の中から質問を探す必要がありません。この流れを実現するため、Live Q&Aの要件を以下のように整理しました。

  • 質問の投稿や投票など、ライブQ&Aサービスの基本機能を備える
  • 投稿された質問を数秒で英訳し、原文とともにスクリーンに表示する
  • 数百人が同時に参加しても、質問や投票の更新をすぐに届けることができる
  • 15分間の利用に見合うコストで運用できる

特に構成を考えるうえで鍵になったのは、数百人への更新をどう届けて、コストをどう抑えるかでした。

数百人のQ&Aを支えるアーキテクチャ

Q&Aが始まると、数百人がほぼ同時に画面を開き、質問一覧を繰り返し取得します。その間も、質問の投稿や投票による変化は、参加者の画面にすぐ反映しなければなりません。この読み取りと更新を、15分間の利用に見合うコストで実現するため、次のような構成にしました。

Live Q&Aのアーキテクチャ

リクエストの入口にはWorkers、ルームの状態管理にはDurable Objectsを使います。参加者が繰り返し取得する質問一覧はCache APIで受け、Durable Objectへのアクセスを抑えます。

それぞれを選んだ理由を説明します。

なぜCloudflareだったのか

Live Q&Aへのアクセスは、普段は少なく、Q&Aの15分間に集中します。アクセスに応じて処理できるCloudflare Workersは、この使い方に合っていました。

費用の面でも、WorkersやSQLite-backedのDurable ObjectsはFree planから利用できます。PEK2026規模ならFree planに収まる想定で、Paid planも最低月額$5からです。必要な機能をCloudflare上にまとめられることも、構成をシンプルにするうえで魅力的でした。

なぜDurable Objectsだったのか

質問が投稿されたり、投票が増えたりすると、その変更を同じQ&Aルームの参加者に共有する必要があります。そこで、ルームごとの状態をDurable Objectsで管理しました。

1つのルームを1つのDurable Objectに対応させ、質問、投票、ステージの状態、リアルタイム配信をまとめています。「同じルームの情報は同じ場所で扱う」と言う、分かりやすい構成にすることができました。

なぜCache APIを導入したのか

参加者の多くは、ほぼ同じ質問一覧を繰り返し取得します。そのたびにDurable Objectへアクセスしないよう、一覧のスナップショットをCache APIに短時間保持し、キャッシュにないときだけ取得する構成にしました。

CloudflareのCDNキャッシュ(Cloudflare Cache)でも、キャッシュする対象や期間は設定できます。ただ、今回はDurable Objectから取得した質問一覧をWorker内でキャッシュし、その読み書きをアプリケーション側で制御したいと考えました。また、MiniflareでCache APIの動作をローカルでテストできますが、fetch() 経由のキャッシュは再現できません。短いTTLの動的なデータを開発中に検証できることも、Cache APIを選んだ理由の一つです。

なぜRate LimitingとTurnstileを併用したのか

質問や投票は匿名で利用できます。そのため、短時間に大量のリクエストが送られた場合への備えも必要でした。Rate Limitingでリクエスト数を抑え、Turnstileで自動化されたアクセスかどうかを確認します。

Turnstileは質問投稿の邪魔にならないようUIに組み込みつつ、OSSでは任意で有効にできる機能にしています。Turnstileの有効・無効に加え、Cookie発行と投稿・投票の回数制限を含めたフローは次の通りです。

質問・投票時の不正利用対策フロー

なぜ質問の英語表示を2種類にしたのか

翻訳にはWorkers AIを使いました。Cloudflare上で構成を簡潔にでき、Free planから利用できるためです。そのうえで検討したのは、Kaspar氏が短い時間で質問の意図を掴める英語の見せ方でした。

質問の英語表示は、要点版(Headline)と全文版(Full)の2種類にしました。要点版では、前置きや感想が長い投稿から肝心の質問を拾い、英語の一文に整えます。Kaspar氏が最初に確認する表示です。

ただ、要点だけでは重要な前提が抜けることもあります。必要な時は、同じ質問を全文版に切り替えられるようにしました。PEK2026で実際に表示された質問を例に、その切り替えを示します。

Presenter画面で要点版を残したまま、原文を全文英訳に切り替える様子

Workers AIには、両方を含むJSONを返すようプロンプトで指示しています。モデルは、昨年実際に投稿された質問に加え、プロンプトインジェクションを含むテスト入力も使い、翻訳品質と応答時間を比較して選びました。現在のデフォルトは gemma-4-26b-a4b-it で、環境変数から変更できます。

なぜセルフホストにしたのか

Live Q&Aを作るきっかけの一つは、15分間のQ&Aのために高額なSaaSプランを契約することへの違和感でした。そこで、自分たちが新たなSaaSとして提供するのではなく、各カンファレンスが自分のCloudflareアカウントにデプロイできるOSSにしました。

ロゴやテーマカラー、ドメインもイベントに合わせて変更できます。

本番で使う以上、「動く」だけでは足りない

AIエージェントを使えば、ひとまず動くものは短期間で作れる時代になりました。ただし、数百人が一斉に使うキーノートの15分間を、安心して任せられるかは別の話です。

そこでk6を使い、質問一覧の繰り返し取得と、質問の投稿・投票が重なる状況を再現しました。検証環境では閲覧する参加者役を250 VUsまで増やし、大きな異常は確認されませんでした。

250 VUsはシステムの限界ではなく、自宅のWi-Fiを使って1台のマシンから試せた上限です。本番を完全に再現した試験ではありませんが、想定規模で目立つボトルネックがないことを確認して投入しました。

そしてPEK2026本番へ

2026年9月26日、Kaspar氏のキーノートでLive Q&Aを本番投入しました。開発がパンフレットの印刷に間に合わず、参加用のQRコードはHallに100枚ほど掲示し、Findy Conferenceにもリンクを掲載していただきました。キーノート開始後、Q&Aまでにリンクを探すことになった方には、ご不便をおかけしました。

当日キーノートセッションのQ&Aの様子

当日は私が司会を担当しました。拙い英語で進行しながら、Live Q&Aが本番でも動くかを気にしていたので、正直かなり緊張しました。参加者の日本語の質問は数秒で英訳され、私が読み上げた英訳をKaspar氏は画面でも確認できました。回答もCuckooの日本語字幕で参加者に届き、想定していた流れでQ&Aを進めることができました。

15分間のQ&Aを大きなトラブルなく終えられました。複数の参加者からも良い感想をいただき、時間をかけて開発した甲斐があったと思いました。

本番のログで見えた、キャッシュの効果とETagの不具合

本番後、CloudflareのメトリクスとInvocationsログを見返しました。

まずは当日アクセスの流れです。キーノート前にQRコードをスクリーンに表示した直後、ページ読み込みに伴う静的ファイルへのリクエストが急激に増加しました。一方、Q&A中の繰り返しの質問一覧取得は、Worker Invocationsに表れています。

Workerのasset requests Workerのinvocations

本番後のInvocationsログでは、Q&Aの15分間にWorkerへのHTTPリクエストが5,171回あり、このうち4,939回が質問一覧の取得でした。

参加者の画面からこれだけ繰り返しアクセスがあった一方で、ルームの状態を持つDurable Objectにはどれくらい届いたのでしょうか。同じ時間帯のDOのRequestsを確認しました。

Durable Objectsのrequests

DOのダッシュボードでは、投票やアラームも含めて813件のリクエストが記録されています。別途エクスポートしたログで質問一覧の取得と突き合わせると、対応するDOの snapshot() 呼び出しは683回でした。参加者からの繰り返しのアクセスの多くをCache APIが受け止め、DOへの負荷を抑えていたことが分かります。

一方、ETagを使って 304 Not Modified を返す仕組みが、本番では機能していませんでした。質問一覧の取得のうち4,897回では、ブラウザから送られた If-None-Match に W/"51" のようなWeak ETagが入っていました。コードはこれを "51" と完全一致で比較していたため、内容が変わらなくても 200 OK で本文を返していました。手元のテストでは W/ の付かないETagしか使っておらず、本番後のログで気づきました。

参考:RFC 9110: HTTP Semantics

なぜETagに W/ が付いたのか?

Using ETag Headers with Cloudflare · Cloudflare Cache (CDN) docs

このアプリのWorkerは ETag: "51" のような強いETagを生成します。Cloudflareはレスポンスの圧縮方式を変える際、これをW/"51" という弱いETagに変える場合があります。

後日、公開APIを確認すると、圧縮なしでは "148"、Brotli圧縮時には W/"148" が返りました。当日も同じ変換が起きたと考えるのが自然ですが、当日のレスポンスヘッダーは残っていないため、変換された箇所までは断定できません。

If-None-Match ではweak comparisonを使うため、W/"51" と "51" は一致として扱われます。今回間違っていたのはCloudflareの変換ではなく、アプリ側の文字列比較でした。

キャッシュと304の判定は、次の順序で行われます(図は修正後の判定)。

キャッシュと304応答の判定フロー

304の判定はCache APIを参照した後に行うため、この不具合でDurable Objectsへの負荷が増えたわけではありません。ただし、同じJSONを送り直す無駄は生じていました。Weak ETagを扱うテストを追加し、修正PRをマージしました。

本番の数字と失敗を次の改善につなげられるのも、自作ツールならではの魅力です。

このQ&AツールをPEKだけのものにしたくなかった

Live Q&AはPEK2026のキーノートに間に合うように開発しましたが、Platform Engineering Kaigiだけで使い終えるつもりはありません。短時間のQ&Aや翻訳のために高額なプランを契約しなければならない課題は、他のカンファレンスやイベントにもあるはずです。

今回、Live Q&Aを必要な機能に絞ってCloudflare上でセルフホストできる形にしたのは、その選択肢を増やしたかったからです。ソースコードと導入方法はGitHubで公開しています。

最後に

最初から「翻訳付きのQ&Aツールを作りたい」と考えていたわけではありません。

参加者が日本語で投稿した質問を、Kaspar氏へどう自然に英語で届けるか。その体験を起点に考えた結果、Live Q&Aの機能とアーキテクチャにたどり着きました。

同じような課題を抱えているカンファレンスがあれば、ぜひ使ってみてください!