「LLMsを使用したモバイルアプリの音声と自然言語の入力」

モバイルアプリの音声と自然言語の入力にLLMsを使用

OpenAI GPT-4の機能を活用してGUIを操作する方法

Kelly Sikkema氏による写真、Unsplashから

はじめに

大規模言語モデル(LLM)は、自然言語を効果的に処理できる機械学習システムです。現在最も先進的なLLMはGPT-4であり、有料版のChatGPTを支えています。本記事では、GPT-4の関数呼び出しを使用して、アプリのグラフィカルユーザインターフェース(GUI)と完全に連携しながら、アプリに高度な柔軟な音声解釈を実現する方法を学びます。対象読者は、製品オーナー、UXデザイナー、モバイル開発者です。

背景

モバイル電話(AndroidおよびiOS)のデジタルアシスタントは、いくつかの理由で普及していません。それらは不具合があり、制限もあり、使用するのが面倒です。LLM、特にOpenAI GPT-4は、ここで違いを生み出す可能性を持っています。それらは、話された表現を単純にパターンマッチングするのではなく、ユーザの意図をより深く把握する能力を持っています。

AndroidにはGoogleアシスタントの「アプリのアクション」、iOSにはSiriKitのインテントがあります。これらは、アプリが処理できる音声リクエストを登録するために使用できるシンプルなテンプレートを提供します。GoogleアシスタントとSiriは、過去数年間でかなり改善されていますが、おそらくあなたが認識している以上に改善されています。ただし、そのカバレッジは、どのアプリがそれらをサポートするかに大きく左右されます。それにもかかわらず、例えばSpotifyで好きな曲を音声で再生することができます。ただし、これらのOS提供のサービスの自然言語解釈は、LLMがもたらしたこの分野での大きな進歩の前に存在していたため、より信頼性と柔軟性のある音声入力を実現するためにLLMのパワーを利用する時が来ています。

オペレーティングシステムのサービス(SiriやGoogleアシスタントなど)がまもなくLLMの力を活用するために戦略を変更することを期待できますが、これらのサービスに制約されることなくアプリに音声を使用することができます。本記事で紹介するコンセプトを採用すると、新しいアシスタントが利用可能になった場合にもアプリが準備できるようになります。

LLMの選択(GPT、PaLM、LLama2、MPT、Falconなど)は信頼性に影響を与えますが、ここで学ぶコアな原則はどれにも適用できます。ユーザは自分の言葉や言語を使って、一つの表現でアプリの機能の全体にアクセスできるようになります。LLMは自然言語の表現をアプリのナビゲーション構造と機能の関数呼び出しにマッピングします。また、それはロボットのように話された文である必要はありません。LLMの解釈能力により、ユーザは自然で信頼性の高いやり取りができるため、意味が理解されないという理由で音声アシスタントを拒否したユーザに対して、相互作用がはるかに自然で信頼性が高いと感じられることがあります。

なぜアプリで音声入力を使用するのか、そしてなぜ今なのか?

利点:

  • 画面に移動し、すべてのパラメータを一つの音声表現で提供する
  • 学習曲線が緩やか: ユーザはデータがアプリ内のどこにあるかやGUIの操作方法を見つける必要がありません
  • ハンズフリー
  • 相互補完的で非連結(音声ユーザインターフェースやVUIのような): 音声とGUIが調和して動作する
  • 視覚障害者のアクセシビリティ
  • 今: 自然言語の解釈がLLMを通じて新たなレベルに到達したため、応答がより信頼性が高くなりました

欠点:

  • 音声で話す際のプライバシー
  • 精度/誤解釈
  • まだ比較的遅い
  • 頭の中の知識vs世界中の知識(何を言えばいいのか?): ユーザはシステムが理解し、回答のある話された表現を知りません

音声入力を活用できるアプリの例には、車や自転車の運転支援に使用されるアプリが含まれます。一般的に、ユーザーは移動中や手袋を着用している場合、または手で作業に忙しい場合など、手を簡単に使用できないときに、タッチによるアプリのナビゲーションの精度に関与したくないかもしれません。

ショッピングアプリもこの機能を活用できます。ユーザーはショッピング画面をナビゲートしたりフィルターを設定する代わりに、自分の言葉で欲しいものを口頭で述べることができます。

視覚障害者のアクセシビリティ向上のためにこのアプローチを適用する場合、自然言語出力とテキスト読み上げ機能を組み合わせることを検討すると良いでしょう。

あなたのアプリ

以下の図は、お馴染みの電車旅行プランナーのような典型的なアプリのナビゲーション構造を示しています。上部にはタッチナビゲーションのデフォルトのナビゲーション構造が表示されます。この構造はナビゲーションコンポーネントによって制御されます。すべてのナビゲーションクリックはナビゲーションコンポーネントに委譲され、ナビゲーションアクションが実行されます。下部は音声入力を使用してこの構造にアクセスする方法を示しています。

LLM関数呼び出しを使用したアプリの音声有効化

ユーザーは望むものを述べます。その後、音声認識システムが音声をテキストに変換します。システムはこのテキストを含むプロンプトを作成し、LLMに送信します。LLMはアプリにデータを返し、どの画面をどのパラメータでアクティブにするかを伝えます。このデータオブジェクトはディープリンクに変換され、ナビゲーションコンポーネントに渡されます。ナビゲーションコンポーネントは、正しいパラメータを持つ正しい画面をアクティブにします。この例では、「Outings」というパラメータを持つ「Amsterdam」という「画面」がアクティブになります。なお、これは簡略化された説明です。詳細については後述します。

多くの現代のアプリには、中央集権化されたナビゲーションコンポーネントが内部にあります。AndroidにはJetpack Navigation、FlutterにはRouter、iOSにはNavigationStackがあります。中央集権化されたナビゲーションコンポーネントにはディープリンクが可能であり、これによりユーザーはアプリのメイン画面やメニューを経由せずに特定の画面に直接移動できます。本記事で述べる概念を実現するためには、ナビゲーションコンポーネントと中央集権化されたディープリンクは必要ありませんが、これらの概念の実装が容易になります。

ディープリンクは、アプリ内の特定のコンテンツまたはセクションを指す一意の(URI)パスを作成することを含みます。さらに、このパスには、ディープリンクが指す画面上のGUI要素の状態を制御するパラメータを含めることができます。

アプリのための関数呼び出し

プロンプトエンジニアリングの技術を使用して、自然言語の表現をナビゲーションの関数呼び出しにマップするようにLLMに指示します。基本的に、プロンプトは次のようなものです。「次のパラメータを持つ関数テンプレートを使用して、次の自然言語の質問をこれらの関数テンプレートの1つにマップし、それを返す」という内容です。

ほとんどのLLMはこれが可能です。LangChainはZero Shot ReActエージェントと呼ばれるものと、呼び出すべき関数をツールと呼びます。OpenAIは特別なバージョン(現在のgpt-3.5-turbo-0613およびgpt-4-0613)でGPT-3.5およびGPT-4モデルを微調整し、これに非常に優れた能力を持たせており、この目的のための特定のAPIエントリを作成しました。本記事ではOpenAIの表記方法を取り上げますが、これらの概念はReActメカニズムを使用して他のLLMにも適用できます。さらに、LangChainには特定のエージェントタイプ(AgentType.OPENAI_FUNCTIONS)があり、このタイプはツールをOpenAIの関数テンプレートに変換します。LLama2では、OpenAIと同じ構文でllama-apiを使用することができます。

LLMに対する関数呼び出しは次のように動作します:

  1. 関数テンプレートのJSONスキーマをプロンプトに挿入し、ユーザーの自然言語表現をユーザーメッセージとして追加します。
  2. LLMはユーザーの自然言語表現をこれらのテンプレートの1つにマップしようとします。
  3. LLMは結果のJSONオブジェクトを返し、コードが関数呼び出しを行えるようにします。

本記事では、関数の定義は(モバイル)アプリのグラフィカルユーザーインターフェース(GUI)の直接のマッピングであり、各関数は画面に対応し、各パラメータはその画面上のGUI要素に対応します。LLMに送信される自然言語表現は、関数名とパラメータを含むJSONオブジェクトを返します。これを使用して正しい画面に移動し、ビューモデルで正しい関数をトリガーすることで、適切なデータを取得し、その画面上の関連するGUI要素の値をパラメータに応じて設定することができます。

以下の図に示されています:

モバイルアプリのGUIにLLM機能をマッピングする

これは、LLMのプロンプトに追加された関数テンプレートの簡略版を示しています。ユーザーメッセージ「アムステルダムで何ができるか」の完全なプロンプトを表示するには、ここをクリックしてください(Github Gist)。コマンドラインから使用するか、Postmanにインポートできる完全なcurlリクエストが含まれています。実行するには、プレースホルダーに独自のOpenAIキーを入力する必要があります。

パラメータのない画面

アプリの一部の画面には、LLMが認識する必要のあるパラメータがないか、少なくともそれらのパラメータがない場合があります。トークン使用量と混雑を減らすために、これらの画面トリガーのいくつかを単一の関数に組み合わせることができます。この関数には1つのパラメータがあります:開く画面

{    "name": "show_screen",    "description": "ユーザーが表示したい画面を決定する",    "parameters": {        "type": "object",        "properties": {            "screen_to_show": {                "description": "表示する画面の種類。'account': 'ユーザーのすべての個人データ', 'settings': 'ユーザーがアプリの設定を変更したい場合'のどちらか",                "enum": [                    "account",                    "settings"                ],                "type": "string"            }        },        "required": [            "screen_to_show"        ]    }},

トリガー関数がパラメータを必要とするかどうかの判断基準は、ユーザーが選択肢を持っているかどうかです。画面上で検索やナビゲーションが行われているか、例えば検索フィールドやタブがあるかどうかです。

そうでない場合、LLMはそれについて知る必要はありませんし、画面のトリガリングはアプリの汎用画面トリガリング関数に追加することができます。これは、画面の目的の説明を実験してみることにほぼ依存しています。より長い説明が必要な場合は、enumのジェネリックパラメータよりもその説明に独自の関数定義を与えることを検討することができます。

プロンプトの指示、ガイダンス、修正

プロンプトのシステムメッセージでは、一般的な方向性情報を提供します。この例では、現在の日付と時刻を知ることがLLMにとって重要であり、例えば明日の旅行を計画する場合などに役立ちます。もう一つ重要なことは、LLMの推測を促すことです。しばしば、不確実性よりもLLMが自信を持っている方が望ましいです。この例のアプリに適した良いシステムメッセージは次のとおりです:

"messages": [        {            "role": "system",            "content": "現在の日付と時刻は2023-07-13T08:21:16+02:00です。                        関数パラメータの値を推測する際は、非常に自信を持ってください。"        },

関数パラメータの説明は、かなりの調整が必要な場合があります。列車の旅行を計画する場合のtrip_date_timeの例です。合理的なパラメータの説明は次のとおりです:

"trip_date_time": {      "description": "'YYYY-MM-DDTHH:MM:SS+02:00'形式での出発または到着の要求された日時。                       ユーザーは12時間制の時間を使用し、24時間制の時間について最もありそうな意味を                       推測します(過去の計画ではないなど)",                      "type": "string"                  },

したがって、現在が15:00であり、ユーザーが8時に出発したいと言った場合、ユーザーは20:00を意味します(特定の時間を明示的に指定しない限り)。上記の指示はGPT-4にはかなりうまく機能しますが、いくつかの例外的なケースではまだ失敗することがあります。その場合、独自のコードでさらなる修正を行うために、関数テンプレートに追加のパラメータを追加できます。例えば、次のように追加できます:

"explicit_day_part_reference": {          "description": "常にNoneを選択してください! リクエストが現在の日に                         言及する場合はNone、それ以外の場合はリクエストが                         言及する日の時間帯です。"          "enum": ["none", "morning", "afternoon", "evening", "night"],                            }

アプリでは、成功率を向上させるために、後処理が必要なパラメータを見つけることが多いでしょう。

質問の明確化に対するシステムの要求

ユーザーのリクエストには、処理するための情報が不足している場合があります。ユーザーのリクエストに適した機能が存在しない場合もあります。その場合、LLMは自然言語で応答し、ユーザーに表示できるようなもので応答します。たとえば、Toastを使用して表示することができます。

また、LLMが呼び出す潜在的な関数を認識しているが、すべての必要な関数パラメータを埋めるための情報が不足している場合もあります。その場合、可能であればパラメータをオプションにすることを検討してください。しかし、それが不可能な場合、LLMはユーザーの言語で、不足しているパラメータに関する要求を自然言語で送信する場合があります。このテキストをユーザーに表示する必要があります。たとえば、ユーザーが「アムステルダムに行きたい」と言った場合(アプリがシステムメッセージを介してデフォルトまたは現在の場所を提供していない場合)、LLMは「あなたは列車の旅行をしたいと理解していますが、出発地はどこですか?」と応答するかもしれません。

これは、会話の履歴の問題を引き起こします。情報の要求を複数のターンに分けるために、常にユーザーからの直近の4つのメッセージをプロンプトに含めることをお勧めします。簡単にするために、システムの応答は履歴から省略してください。なぜなら、このユースケースでは、それらがむしろ有害であることが多いからです。

音声認識

音声認識は、音声からパラメータ化されたナビゲーションアクションへの変換において重要な役割を果たしています。解釈の品質が高い場合、悪い音声認識は最も弱いリンクとなる可能性があります。モバイル電話には、合理的な品質のオンボード音声認識がありますが、Whisper、Google Chirp/USM、Meta MMS、またはDeepGramなどのLLMベースの音声認識を使用すると、より良い結果が得られる傾向があります。

アーキテクチャ

関数定義はサーバーに保存するのが最善ですが、アプリで管理し、すべてのリクエストと共に送信することもできます。どちらにも利点と欠点があります。すべてのリクエストと共に送信することは柔軟性があり、関数と画面の整合性を維持することがより簡単になるかもしれません。ただし、関数テンプレートには、関数名とパラメータだけでなく、更新フローをアプリストアよりも迅速に更新したい説明も含まれています。これらの説明は、より具体的なLLMに依存し、機能するように作成されています。より良いまたは安価なLLMに置き換えたり、あるいは動的に交換したりする可能性もあります。関数テンプレートをサーバー上に保持することは、また、アプリがiOSおよびAndroidのネイティブである場合には、1か所でそれらを維持するという利点もあります。音声認識と自然言語処理の両方にOpenAIサービスを使用する場合、フローの技術的な全体像は次のようになります:

WhisperとOpenAI関数呼び出しを使用してモバイルアプリに音声を有効にするためのアーキテクチャ

ユーザーはリクエストを話し、それがm4aバッファ/ファイル(または必要に応じてmp3)に記録され、サーバーに送信され、それがWhisperに中継されます。Whisperはトランスクリプションで応答し、サーバーはシステムメッセージと関数テンプレートと組み合わせてLLMのプロンプトを作成します。サーバーは生の関数呼び出しJSONを受け取り、それをアプリの関数呼び出しJSONオブジェクトに変換して処理します。

関数呼び出しをディープリンクに変換する方法を示すために、初期の例からの関数呼び出し応答を取り上げます:

"function_call": {                    "name": "outings",                    "arguments": "{\n  \"area\": \"Amsterdam\"\n}"                }

異なるプラットフォームでは、これをかなり異なる方法で処理します。そして、長い時間をかけて多くの異なるナビゲーションメカニズムが使用され、現在でも使用されています。この記事では実装の詳細には触れませんが、大まかに言えば、最新のインカーネーションのプラットフォームでは次のようにディープリンクを利用できます:

Androidでは:

navController.navigate("outings/?area=Amsterdam")

Flutterでは:

Navigator.pushNamed(      context,      '/outings',      arguments: ScreenArguments(        area: 'Amsterdam',      ),    );

iOSでは、NavigationStackを使用しても少し標準化されていませんが、次のようにします:

NavigationStack(path: $router.path) {            ...}

そして、次のように発行します:

router.path.append("outing?area=Amsterdam")

ディープリンクに関する詳細は、こちらをご覧ください:Android向け、Flutter向け、iOS向け

アプリ向けの自由テキストフィールド

自由テキストの入力には、音声と入力の2つのモードがあります。私たちは主に音声について話しましたが、入力のためのテキストフィールドも選択肢の1つです。自然言語は通常非常に長くなるため、GUIのインタラクションと競合するのは難しいかもしれません。しかし、GPT-4は略語からパラメータを推測する能力が非常に高いため、非常に短い略語入力でも正しく解釈できることが多いです。

プロンプト内でパラメータを持つ関数を使用することで、LLMの解釈コンテキストが劇的に狭まることが多くなります。そのため、非常に少ない情報が必要であり、仮定的であるように指示すると、モバイルのインタラクションに有望な現象です。例えば、駅から駅へのプランナーの場合、この記事の例示的なプロンプト構造を使用した場合、LLMは次のような解釈を行いました。上記で言及されたプロンプトギストを使用して、自分自身で試すことができます。

例:

「ams utr」:今からアムステルダム中央駅からユトレヒト中央駅への列車の行程のリストを表示してください

「utr ams arr 9」:(現在が13:00であると仮定した場合)。ユトレヒト中央駅からアムステルダム中央駅への列車の行程のリストを表示してください。到着時間は21:00まで。

フォローアップのインタラクション

ChatGPTと同様に、相互作用履歴の短い部分を送信することで、クエリを詳細に調整することができます:

履歴機能を使用すると、次の方法でも非常にうまく機能します(現在午前9時と仮定してください):

タイプ:「ams utr」と入力し、上記の回答を取得します。次のターンでは「arr 7」と入力します。はい、それは実際にアムステルダム中央からユトレヒト中央への旅行を19:00までの到着として翻訳することができます。この件については、ビデオで説明している例のWebアプリも作成しました。実際のアプリへのリンクは説明にあります。

将来

このディープリンクの構造は、アプリ内の機能を処理するためのものとして、あなたの電話のOS(AndroidまたはiOS)の重要な一部となることが期待されます。電話上のグローバルアシスタントが音声リクエストを処理し、アプリはOSにその機能を公開して、ディープリンキングの形式でトリガーできるようになります。これは、ChatGPT向けのプラグインが利用可能になる方法と類似しています。もちろん、AndroidManifestのインテントやAndroidのApp Actions、iOSのSiriKitインテントによって、すでにこれの粗い形が利用可能ですが、これらに対する制御は限られており、ユーザーは確実にアクティブ化するためにロボットのように話さなければなりません。間違いなく、LLMパワードのアシスタントが支配すると、これは改善されるでしょう。

VRおよびAR(XR)は、ユーザーの手が他のアクティビティに従事しているため、音声認識に大きな可能性を提供します。

おそらく、誰もが独自の高品質なLLMを実行できるようになるまで、時間はかかりません。コストは減少し、速度は次の1年で急速に向上するでしょう。まもなく、スマートフォンでもLoRA LLMが利用可能になり、推論が電話上で行われるため、コストと速度が低下します。また、Llama2のようなオープンソースやPaLMのようなクローズドソースなど、ますます多くの競争が登場するでしょう。

最後に、ランダムアクセスだけでなく、アプリ全体のGUIへのランダムアクセスを提供する以上に、異なるモダリティのシナジーをさらに推進することができます。複数のソースを組み合わせることができるLLMの力こそが、より良いアシスタントの実現の可能性を秘めています。興味深い記事:マルチモーダルダイアログ、GUIとLLMに関するGoogleのブログ、GUIインタラクションを言語として解釈する。

結論

この記事では、アプリを音声対応するために関数呼び出しを適用する方法を学びました。提供されたGistを出発点として、Postmanやコマンドラインから実験して、関数呼び出しがどれほど強力かを把握することができます。アプリを音声対応するPOCを実行したい場合は、アーキテクチャセクションからサーバービットを直接アプリに組み込むことをお勧めします。すべてが2つのHTTP呼び出し、プロンプトの構築、マイクの録音の実装に帰着します。あなたのスキルとコードベースに応じて、POCを数日で立ち上げることができます。

楽しいコーディングを!

LinkedInで私をフォローしてください

この記事のすべての画像は、特に記載のない限り、著者によるものです

We will continue to update VoAGI; if you have any questions or suggestions, please contact us!

Share:

Was this article helpful?

93 out of 132 found this helpful

Discover more