AIを使ったアプリを作っていると、どうしても避けて通れない問題があります。 それが、AIの生成時間です。
チャットのように短い返答を返すだけならまだしも、ある程度まとまった結果を生成するアプリでは、ユーザーに数秒から十数秒、場合によってはそれ以上待ってもらうことがあります。
私は現在、Tabidea というAI旅行プランナーを個人開発しています。 行き先や日数、好みなどを入力すると、AIが旅行プランを作ってくれるWebアプリです。
日本と世界の美しい風景、文化、食べ物を通じて、新しい旅の発見をお届けする旅行ブログ。
tabide.ai技術スタックとしては、フロントエンドに Next.js / React / TypeScript を使っています。 AIによる旅程生成には Gemini API を利用し、旅程生成後にスポット情報や移動の現実性を補うために、Google Maps API などの外部APIも組み合わせる構成を考えています。
また、認証やユーザーデータの管理には Supabase を使っています。 つまりTabideaは、単にLLMへプロンプトを投げて文章を返すだけのアプリではなく、AI生成・外部API・ユーザー状態・UI表示を組み合わせて成立するWebアプリです。
最初は、「AIに条件を渡せば、いい感じの旅程を返してくれるだろう」と考えていました。 ただ、実際に作ってみると、旅行プランナーでは単に文章を生成するだけでは足りませんでした。
旅程として成立させるためには、たとえば次のようなことを考える必要があります。
- その場所が本当に存在するか
- スポット同士の距離感が現実的か
- 移動時間に無理がないか
- 営業時間や定休日と矛盾していないか
- 朝・昼・夜の流れとして自然か
- ユーザーの希望条件に合っているか
つまり、旅行プランの生成は「それっぽい文章を返す」だけでは終わりません。 AIが提案した内容を、現実の移動やスポット情報と照らし合わせながら、できるだけ自然な旅程として成立させる必要があります。
その結果、どうしても生成に時間がかかります。
この記事では、AI旅行プランナーを作る中で、生成の待ち時間をどう扱うか考えた話を書きます。 最終的に私は、単なるローディングスピナーではなく、プログレスバーと旅のTipsを表示するUIにたどり着きました。
AIアプリにおける待ち時間の問題
AIを使ったアプリでは、ユーザーが入力してから結果が返ってくるまでの間に、どうしても待ち時間が発生します。
普通のWebアプリでも、データベースの読み込みや外部API通信の待ち時間はあります。 ただ、AI生成の待ち時間はそれとは少し性質が違います。
DBや通常のAPIであれば、取得対象や処理内容がある程度決まっています。 一方でAI生成は、入力内容によって出力の量や処理時間が変わりやすく、ユーザーから見ると今何が起きているのか分かりにくいです。
ボタンを押したあと、画面が止まったように見える。数秒待っても変化がない。するとユーザーは、
- ちゃんと動いているのか
- エラーになっていないか
- もう一度押した方がいいのか
- いつまで待てばいいのか
と不安になります。
しかも、私が作っているのはWebアプリです。 Webアプリは、特にモバイルアプリと比べると、待ち時間に対してユーザーが離脱しやすいと感じます。
モバイルアプリであれば、多少待っても「アプリが処理している」と受け止めてもらいやすい場面があります。 一方でWebアプリは、ページの表示や遷移に少しでも引っかかりがあると、ユーザーはすぐに「重い」「止まったかもしれない」と感じやすいです。
さらにWebアプリでは、ユーザーが再読み込みしやすいという難しさもあります。 少し不安になると、ブラウザのリロードボタンを押したり、戻るボタンを押したり、別タブに移動したりしやすい。
技術的にも、生成中の状態をどこに持つかは少し難しくなります。 クライアント側のstateだけで生成中の状態を持っていると、リロードされた時点でその状態は失われます。かといって、すべてをジョブ化してサーバー側で進捗管理するほど大げさにすると、個人開発の規模では実装コストが一気に上がります。
だからこそ、まずは「ユーザーが不安にならずに待てる画面」を作ることが重要だと感じました。
生成結果がどれだけ良くても、待っている間の体験が悪いと、その前に離脱されてしまうかもしれません。 AIアプリにおいて、生成の待ち時間の体験はかなり重要です。
代表的な解決策はストリーミング
AIアプリの待ち時間に対する代表的な解決策の一つが、ストリーミングです。
ChatGPTのように、文章が少しずつ表示されていくUIです。 ストリーミングには大きなメリットがあります。
まず、ユーザーは「ちゃんと生成が進んでいる」と分かります。 結果が少しずつ出てくるので、待たされている感覚がかなり軽減されます。
また、すべての生成が終わる前に読み始めることができます。 文章生成系のアプリでは、ストリーミングはとても相性が良いと思います。
ただ、私が作っている旅行プランナーでは、ストリーミングをそのまま使うのは難しいと感じました。
旅行プランナーではストリーミングしにくい理由
旅行プランナーの場合、生成途中の情報をそのままユーザーに見せるのは少し危険です。
たとえば、AIが最初に「1日目の午前はAに行く」と出したとしても、その後のチェックで、
- Aが実在しない
- Aの営業時間に間に合わない
- 次のスポットBまでの移動が現実的ではない
- そもそもAより別のスポットの方が自然
といったことが起こりえます。
旅行プランは、単なる文章ではなく、順番や移動、時間配分がつながった構造です。 途中まで出した内容が、後から変わる可能性があります。
技術的にも、Tabideaで扱いたい旅程は、ただの長いテキストではなく、日程・時間帯・スポット・説明・移動などを持った構造化データです。 そのため、トークンが少しずつ流れてきたからといって、それをそのままUIに出せば良いわけではありません。
部分的なJSONや未検証のスポット名を表示してしまうと、ユーザーにとってはかえって分かりにくくなります。 最初に表示されたスポットが後から消えたり、順番が入れ替わったりすると、「さっき出ていた内容は何だったのか」となってしまいます。
また、旅行プランでは「それっぽい」だけでは不十分です。 地名や観光地名は特に、存在しない場所を出してしまうと致命的です。 さらに、実在する場所でも、1日の移動として無理があれば、旅行プランとしては使いにくくなります。
そのためTabideaでは、AIの出力をそのままストリーミング表示するよりも、ある程度チェックしたうえで、まとまった旅程として表示する方が自然だと考えました。
旅程生成は「AIの返答待ち」だけではない
旅行プランナーの生成時間が長くなる理由は、AIの返答を待っているだけではありません。
Tabideaでは、LLMが作った旅程案をそのまま画面に出すのではなく、必要に応じて外部APIやアプリ側のロジックと組み合わせながら、ユーザーに見せられる形に整えていきます。 そのため、待ち時間には「AIが考えている時間」だけでなく、「アプリ側で検証・整形している時間」も含まれます。
旅程を作るには、AIの出力に加えて、その内容が実際の旅行として成立するかを確認する必要があります。
大まかには、次のような処理が必要になります。
- ユーザーの希望条件を整理する
- 候補となるスポットやエリアを考える
- 日程ごとの流れを組み立てる
- スポットの実在性や場所情報を確認する
- 移動時間や順番に無理がないか確認する
- 最終的にユーザーに見せる形に整える
もちろん、すべてを完璧に検証するのは簡単ではありません。 ただ、旅行プランナーとして使ってもらう以上、少なくとも「明らかに存在しない場所」や「現実的ではない移動」をできるだけ減らしたいと思っています。
このようなチェックを挟むと、どうしても処理時間は伸びます。 しかし、その時間は単なる遅延ではなく、旅程の品質を上げるために必要な時間でもあります。
問題は、その必要な時間がユーザーには見えないことです。
裏側では、AI生成やスポット確認、旅程の整形をしていても、画面上では何も起きていないように見えてしまう。 ここをどう見せるかが、UXとして大事だと感じました。
最初はスケルトンやローディングスピナーを使っていた
最初に試したのは、よくある待ち時間用のUIです。
具体的には、
- ローディングスピナー
- スケルトンUI
- 「生成中です」といったテキスト
を表示していました。
これは実装しやすく、最低限「今処理中である」ことは伝えられます。 ただ、実際に自分で使ってみると、少し物足りなさがありました。
スピナーは、動いていることは伝えてくれます。 でも、どれくらい待つのか、今何が進んでいるのかは分かりません。
スケルトンUIも、通常の一覧画面やカード表示の読み込みには向いています。 ただ、AI生成のように「今まさに何かを考えている」「これから結果が組み上がる」体験とは、少し相性が違うように感じました。
特に旅行プランナーの場合、ユーザーはこれから行くかもしれない旅を想像しながら待っています。 その時間を、ただの待ち時間として消費させるのは少しもったいないと思いました。
プログレスバーを入れるようにした
そこで次に、プログレスバーを表示するようにしました。
もちろん、AI生成の進捗は厳密にパーセンテージで表せるものではありません。 「今37%です」と正確に言えるわけではないですし、APIの応答時間や検証処理によって所要時間も変わります。
そのため、ここでのプログレスバーは、厳密な進捗表示というよりも、体感上の不安を減らすための進捗表現です。
ユーザーにとっては、何も変化しない画面より、進んでいる雰囲気がある方が安心できます。
重要なのは、進捗を完全に正確に見せることではなく、
- 処理が止まっていないこと
- 生成にはいくつかのステップがあること
- 少しずつ完了に近づいていること
を伝えることだと思いました。
旅行プランナーでは、旅程生成は単純な1回の生成ではありません。 スポット候補を考え、並び順を調整し、実在性や移動の現実性も見ながら、全体として無理のない旅程にする必要があります。
そうした裏側の処理をユーザーにそのまま見せるわけではなくても、「ちゃんと組み立てている途中なんだ」と伝えることには意味があると感じました。
旅のTipsを表示する案を思いついたきっかけ
さらに、生成の待ち時間に旅のTipsを表示することを考えました。
この案を思いついたきっかけは、ゲームでした。 ゲームでは、ダウンロード中やロード中に、ちょっとしたヒントや豆知識が表示されることがあります。
プレイヤーは待っているだけなのに、その時間が少しだけ意味のあるものになります。 ただ待つだけではなく、世界観に触れたり、知識を得たりできる。 私はこの体験が結構好きでした。
そこで、「これをAI旅行プランナーにも応用できるのではないか」と思いました。
たとえば旅程生成中に、
- 旅先で使える小さなコツ
- 現地を楽しむための視点
- 旅行前に知っておくと便利なこと
- 文化や観光の豆知識
のような短いTipsを表示します。
これなら、ユーザーは待っている間にも少し旅の気分になれます。 単なる「処理中です」ではなく、「これから旅が始まる」ような感覚を作れるかもしれません。
WebアプリやAIアプリでは意外と珍しい
ゲームのロード画面ではよく見る表現ですが、WebアプリやAIアプリでは、待ち時間にTipsをしっかり見せるUIはそこまで多くない印象があります。
多くの場合は、
- スピナー
- スケルトン
- 生成中テキスト
- ストリーミング表示
のどれかです。
もちろん、これらが悪いわけではありません。 むしろ、多くの場面ではそれで十分だと思います。
ただ、旅行プランナーのように、ユーザーが「これから行く場所」を想像しているアプリでは、待ち時間の数秒も体験の一部にできるのではないかと思いました。
AIの生成時間は、開発者としてはなるべく短くしたいものです。 でも、どうしてもゼロにはできません。
しかもWebアプリでは、その待ち時間が長いほど、リロードや離脱のきっかけにもなりやすい。 だからこそ、ただ待たせるのではなく、「待つこと自体に意味がある」状態にできるなら、その価値は大きいと感じました。
実際に採用した実装
最終的に、Tabideaでは待ち時間にプログレスバーと旅のTipsを表示する形にしました。
スケルトンやローディングスピナーも試しましたが、それだけでは待っている時間が少し味気なく感じました。 そこで、進行していることを伝えるためのプログレスバーを出しつつ、その下に短い旅のTipsを表示するようにしました。
実装としては、ユーザーが旅程生成を開始したタイミングで待ち時間用の画面に切り替え、生成処理が完了するまでプログレスバーとTipsを表示します。 Tipsは固定の文言を出しっぱなしにするのではなく、いくつかの短い文を切り替えて表示する形にしました。
また、旅程生成中にユーザーが「本当に動いているのか」と不安にならないように、画面上には単なるスピナーだけでなく、処理が進んでいるように感じられる要素を置いています。
これによって、ユーザーにとっては、
- ちゃんと処理が進んでいることが分かる
- ただ待つだけではなく、ちょっとした情報に触れられる
- 旅程ができあがるまでの時間も旅行体験の一部のように感じられる
という状態を作りやすくなったと思います。
とくに旅行というテーマでは、結果そのものだけでなく、旅を想像している時間にも価値があります。 そのため、待ち時間に旅のTipsを出すことは、単なる暇つぶしではなく、プロダクトの体験そのものに合っていると感じました。
Tipsを出すときに気をつけたいこと
ただし、Tipsを出せば何でも良いわけではありません。
特に旅行系のアプリでは、情報の正確性が重要です。 たとえば営業時間、料金、交通機関の細かい情報などは変わる可能性があります。 待ち時間の軽いTipsとして表示するには、少し慎重になる必要があります。
そのため、Tipsには次のような内容が向いていると思いました。
- 比較的変わりにくい文化的な豆知識
- 旅の楽しみ方に関する一般的なヒント
- 季節や時間帯を考えるときの視点
- 持ち物や移動時の心構え
- 観光地を見るときの観点
逆に、
- 最新の営業時間
- 料金
- 運休情報
- 正確なルート案内
- 予約可否
のような情報は、Tipsとして軽く見せるよりも、必要な場面で正確な情報源と一緒に表示した方がよいと考えています。
また、Tipsは長すぎると読まれません。 待ち時間に表示するなら、1つあたり1〜2文くらいがちょうどよいと感じています。
待ち時間用の画面は「待たせる場所」ではなく「期待を作る場所」
今回考えていて思ったのは、待ち時間用の画面は単なる待機画面ではないということです。
特にAIアプリでは、生成時間はどうしても発生します。 しかもWebアプリでは、その待ち時間が離脱や再読み込みにつながりやすい。
その時間を、
- 仕方なく待ってもらう時間
- ただスピナーを眺める時間
- 不安になりながら待つ時間
にするのか、
- 生成が進んでいると分かる時間
- 結果への期待が高まる時間
- アプリの世界観を感じる時間
にするのかで、ユーザー体験はかなり変わると思います。
Tabideaの場合、テーマは旅行です。 旅行は、出発してからだけでなく、計画している時間も楽しいものです。
だからこそ、旅程生成中の待ち時間用の画面も、単なる処理待ちではなく、旅の準備が進んでいる時間として見せたいと考えました。
今後は写真も組み合わせたい
今後は、生成の待ち時間にTipsだけでなく、世界各地の写真や、ユーザーが入力した目的地に関連する写真を表示することも考えています。
旅行では、テキスト情報だけでなく、写真から受ける印象もかなり大きいです。 たとえば、旅程生成中にその地域の街並み、自然、建築、食文化などの写真が表示されると、ユーザーは待っている間にも「この場所に行くかもしれない」という想像をしやすくなります。
これは、単なる装飾というよりも、待ち時間を旅先への期待を高める時間に変えるための要素だと考えています。
ただし、写真を出す場合も注意点があります。 ユーザーの目的地と関係のない写真を出してしまうと、逆に違和感が出ます。また、実際の旅程に含まれないスポットの写真を強く見せすぎると、ユーザーに誤解を与える可能性もあります。
そのため、最初は世界各地の汎用的な旅写真を使い、将来的にはユーザーが入力した目的地や生成中の候補エリアに近い写真を表示する形にできると良さそうです。
Tipsが「読むことで待ち時間を意味のあるものにする」要素だとすれば、写真は「見ることで旅の期待感を高める」要素です。 この2つを組み合わせることで、待ち時間用の画面をよりTabideaらしい体験にできるのではないかと考えています。
おわりに
AIを使ったアプリでは、生成時間を完全になくすことは難しいです。
もちろん、レスポンスを速くする努力は必要です。 プロンプトを短くしたり、処理を並列化したり、不要なAPI呼び出しを減らしたりすることは大切です。
ただ、それでもユーザーに待ってもらう時間は残ります。
そのときに、ストリーミングが使えるアプリであれば、少しずつ結果を見せるのはとても有効です。 一方で、旅行プランナーのように、生成結果を途中で見せるより、チェックしたうえでまとめて表示した方が自然なアプリもあります。
そういう場合、生成の待ち時間の体験をどう作るかが重要になります。
私は今回、ゲームのロード画面でTipsが表示される体験から着想を得て、AI旅行プランナーの生成の待ち時間に、プログレスバーと旅のTipsを表示するUIを採用しました。
AIの生成の待ち時間は、ただの弱点として扱うこともできます。 でも、アプリのテーマと結びつければ、ユーザーの期待を高める時間にもできるかもしれません。
旅行プランナーにとって、旅は結果画面が表示された瞬間に始まるのではなく、 生成を待っているその時間から、少しずつ始まっているのだと思います。
ぜひTabideaの生成の待ち時間を体験してみて下さい!
日本と世界の美しい風景、文化、食べ物を通じて、新しい旅の発見をお届けする旅行ブログ。
tabide.ai