0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AlexaからAzure Functions経由でgeminiを呼び出せるようにする

0
Last updated at Posted at 2026-01-12

AlexaからAzure Functions経由でgeminiを呼び出せるようにする

はじめに

Azure Functionsを使って、Alexaスキルからgemini APIを呼び出せるようにしてみました。
「Alexa、geminiで〇×について教えて」等と話しかけると、gemini APIからの回答をAlexaが読み上げてくれます。

構成

大きく分けて4つの要素から構成されています。

  1. Alexaデバイス: Echoなど。
  2. Alexa Skill Kit(ASK): https://developer.amazon.com/ja-JP/alexa/alexa-skills-kit
  3. Azure Functions: Azure上で動作するサーバーレスコンピューティングサービス。
  4. gemini API: Googleの大規模言語モデルAPI。

事前準備

Alexaデバイス

Alexa対応デバイスを用意します。Echoシリーズ等が該当します。

Alexa開発者アカウントの準備

Alexa Skill Kitのトップページから [はじめる] をクリックし、Amazon開発者アカウントを作成します。

image.png

政府発行の身分証明書を使って本人確認を行必要があるので、免許証等を手元に用意してください。

身分証明書のアップロードが完了すると以下のような画面に遷移するので、「Alexa Skills Kit」をクリックして開発用画面を開きます。

image.png

Gemini APIキーの取得

Google AI StudioからAPIキーを取得しておきます。

Azure アカウントの準備

Azureアカウントを持っていない場合は、こちらから無料アカウントを作成してください。

Azure Functionsは月間100万回(従量課金)、25万回(フレックス従量課金)の無料実行が可能です。無料枠で収まる場合は費用は発生しません。

作成手順

1. Azure Functionsの作成

Azureポータルにログインし、左上の[+リソースの作成] > [関数アプリの作成]を選択します。

image.png

最初にプランを選択する画面が表示されるので、今回は[フレックス従量課金]を選択します。従量課金よりも無料枠は少ないですが、コールドスタートが改善されているため、より安定した応答が期待できます。

開発言語は Python を選択します。その他の項目(名称、リージョンなど)は自身の環境に合わせて設定してください。

2. Azure Functionsコードの作成

関数アプリが作成できたら、以下のような画面が表示できると思います。
「フレックス従量課金 (Flex Consumption)」の「Linux」環境の場合、Azureポータルから直接コード記述ができない(らしい)ので、「VS Code Desktopで作成」を行います。

image.png

ローカルのVS Code でAzure Functionsの拡張機能をインストールし、Azureアカウントにサインインします。
左のサイドバーにAzureのアイコンが表示されるので、クリックします。
下の「WORKSPACE」という欄から、「Create Function Project...」を選択します。

image.png

画面上部に設定の質問が出るので答えます。言語は Python を選択し、テンプレートは HTTP trigger を選択します。認証レベルは「Anonymous」にしておきます。今後レベルを上げることも検討します。

ファイルがいくつか作成されます。function_app.pyファイルを以下のコードに置き換えます。

import azure.functions as func
import json
import os
import logging
import google.generativeai as genai

# --- Gemini APIの初期化 ---
# Azureポータルの「環境変数」に設定した GEMINI_API_KEY を取得します
api_key = os.environ.get("GEMINI_API_KEY")

if api_key:
    genai.configure(api_key=api_key)
    model = genai.GenerativeModel('gemini-2.5-flash')
else:
    logging.error("環境変数 'GEMINI_API_KEY' が設定されていません。")

app = func.FunctionApp()

@app.route(route="alexa_gemini", auth_level=func.AuthLevel.ANONYMOUS)
def alexa_gemini(req: func.HttpRequest) -> func.HttpResponse:
    try:
        req_body = req.get_json()
        request_type = req_body.get('request', {}).get('type')
        
        # 1. 起動時(「ジェミニを開いて」)
        if request_type == "LaunchRequest":
            return build_response(
                "はい、ジェミニです。準備ができました。何についてお調べしますか?",
                should_end_session=False,
                reprompt_text="何か知りたいことはありますか?"
            )

        # 2. 質問時(IntentRequest)
        elif request_type == "IntentRequest":
            intent_name = req_body['request']['intent']['name']
            
            if intent_name == "AMAZON.FallbackIntent":
                return build_response("すみません、よく聞き取れませんでした。もう一度短く教えてください。", False)

            # スロット 'query' から値を取得
            slots = req_body['request']['intent'].get('slots', {})
            user_query = slots.get('query', {}).get('value')

            if user_query:
                # --- 入力値のクレンジング ---
                # スロットに入り込みやすい不要な言葉を削除してGeminiの誤解を防ぐ
                for noise in ["ジェミニで", "ジェミニに", "ジェミニ", "調べて", "教えて"]:
                    user_query = user_query.replace(noise, "")
                
                clean_query = user_query.strip()
                logging.info(f"Geminiへのクエリ: {clean_query}")

                try:
                    # タイムアウト回避のため、最大トークン数を絞って生成
                    response = model.generate_content(
                        f"質問:{clean_query}",
                        generation_config=genai.types.GenerationConfig(
                            max_output_tokens=1000,
                            temperature=0.7
                        )
                    )
                    response_text = response.text
                except Exception as api_err:
                    logging.error(f"Gemini APIエラー: {str(api_err)}")
                    # 429クォータエラーなどの場合のユーザー通知
                    response_text = "ごめんなさい、今は少し混み合っているようです。一分ほど待ってからもう一度聞いてください。"
            else:
                response_text = "質問がうまく聞き取れませんでした。もう一度お願いします。"
            
            return build_response(response_text, should_end_session=True)

        # 3. 終了時
        elif request_type == "SessionEndedRequest":
            return func.HttpResponse(status_code=200)

        return build_response("もう一度お願いします。", False)

    except Exception as e:
        logging.error(f"システムエラー: {str(e)}")
        return build_response("申し訳ありません。接続に問題が発生しました。", True)

# --- Alexa専用レスポンスビルダー ---
def build_response(speech_text, should_end_session, reprompt_text=None):
    # 日本語の句読点などが正しく読み上げられるよう、不要な記号を簡易除去
    clean_text = speech_text.replace("*", "").replace("#", "").replace("`", "")
    
    response = {
        "outputSpeech": {
            "type": "PlainText",
            "text": clean_text
        },
        "shouldEndSession": should_end_session
    }
    
    # セッションを維持する場合のみ reprompt を含める
    if not should_end_session and reprompt_text:
        response["reprompt"] = {
            "outputSpeech": {
                "type": "PlainText",
                "text": reprompt_text
            }
        }

    res_body = {
        "version": "1.0",
        "response": response
    }
    
    # 日本語をエスケープせず、正しいJSON形式で返す
    return func.HttpResponse(
        json.dumps(res_body, ensure_ascii=False),
        mimetype="application/json; charset=utf-8",
        status_code=200
    )

また、requirements.txtに以下の行を追加します。

google-generativeai

image.png

3. Azure Functionsのデプロイ

変更したコードをAzure Functionsにデプロイします。「WORKSPACE」欄をマウスオーバーすると、クラウドにアップロードするためのアイコンが表示されるので、クリックします。

image.png

4. Google Gemini APIキーの設定

Azureポータルから、環境変数を追加します。名前は「GEMINI_API_KEY」、値は取得したGemini APIキーを設定します。

image.png

5. Alexaスキルの作成

Alexa開発者コンソールに戻り、[スキルの作成] をクリックします。
以下設定で新しくスキルを作成します。

項目
スキル名 任意(今回はGemini)
プライマリロケール 日本語
エクスペリエンスのタイプ その他
モデル カスタム
ホスティングサービス Alexa-hosted(Python)
テンプレート スクラッチ

image.png

6. インテントの作成、サンプル発話の追加

まず、インテントを作成します。左のメニューから [Interaction Model] > [Intents] を選択し、 [AskGeminiIntent] という名称で新しいインテントを作成します。
サンプル発話とインテントスロットを以下のように設定します。

image.png

スロット名は「query」、タイプは「AMAZON.SearchQuery」を選択します。

7. エンドポイントの設定

左のメニューから [Endpoint] を選択し、エンドポイントタイプを「HTTPS」に設定します。
Azure FunctionsのURLを入力します。URLは以下の形式になります。

https://<関数アプリ名>.azurewebsites.net/api/alexa-gemini

alexa-gemini は、先ほど作成したAzure Functionsの関数名です。
image.png

エンドポイントを入力する下のプルダウンは「My development endpoint is a sub-domain...」を選択してください。

テスト

1. Alexa 開発者コンソールでのテスト

Alexa開発者コンソールの [Test] タブを開き、テストモードを「開発中」に設定します。
「ジェミニで〇×について教えて」等と入力して、正しく応答が返ってくるか確認します。

image.png

2. Alexa対応デバイスでのテスト

実機で「ジェミニで〇×について教えて」と話しかけ、正しく応答が返ってくるか確認します。
スキルが有効になっていれば、geminiの回答をAlexaが読み上げてくれます。
※部屋の様子が反射したので、画像を加工しています。

image.png

デバッグ

Application Insightsを有効化していれば、Azure Functions の左メニュー [監視] > [ログストリーム] から、Azure Functionsのログをリアルタイムで確認できます。Alexaスキルからのリクエストが正しく届いているか、Gemini APIの呼び出しに失敗していないか等を確認できます。

おわりに

以上、AlexaスキルからAzure Functions経由でgemini APIを呼び出す方法について解説しました。Azure Functionsを使うことで、サーバーレスで簡単にAPIを公開できるため、今回のような用途に非常に便利です。ぜひ試してみてください。

また、Alexaのタイムアウトが8秒と短いため、応答時間が長くなりがちな大規模言語モデルAPIを呼び出す場合は、応答時間の最適化やエラーハンドリングを工夫する必要があります。今回のコード例では、最大トークン数を制限することで応答時間を短縮していますが、他にもキャッシュの活用や非同期処理の導入など、様々な工夫が考えられます。
また、コールドスタートの影響を受けにくい「フレックス従量課金」プランを選択することで、応答速度の安定化を図っています。
上記対策をとってはいるものの応答が不安定なため、作ったは良いものの日常的に使用するかと言われれば微妙です。

今後、LLMを活用して何か実施する場合、タイムアウト対策は重要なポイントになると思うので、是非参考にしてみてください。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?