AlexaからAzure Functions経由でgeminiを呼び出せるようにする
はじめに
Azure Functionsを使って、Alexaスキルからgemini APIを呼び出せるようにしてみました。
「Alexa、geminiで〇×について教えて」等と話しかけると、gemini APIからの回答をAlexaが読み上げてくれます。
構成
大きく分けて4つの要素から構成されています。
- Alexaデバイス: Echoなど。
- Alexa Skill Kit(ASK): https://developer.amazon.com/ja-JP/alexa/alexa-skills-kit
- Azure Functions: Azure上で動作するサーバーレスコンピューティングサービス。
- gemini API: Googleの大規模言語モデルAPI。
事前準備
Alexaデバイス
Alexa対応デバイスを用意します。Echoシリーズ等が該当します。
Alexa開発者アカウントの準備
Alexa Skill Kitのトップページから [はじめる] をクリックし、Amazon開発者アカウントを作成します。
政府発行の身分証明書を使って本人確認を行必要があるので、免許証等を手元に用意してください。
身分証明書のアップロードが完了すると以下のような画面に遷移するので、「Alexa Skills Kit」をクリックして開発用画面を開きます。
Gemini APIキーの取得
Google AI StudioからAPIキーを取得しておきます。
Azure アカウントの準備
Azureアカウントを持っていない場合は、こちらから無料アカウントを作成してください。
Azure Functionsは月間100万回(従量課金)、25万回(フレックス従量課金)の無料実行が可能です。無料枠で収まる場合は費用は発生しません。
作成手順
1. Azure Functionsの作成
Azureポータルにログインし、左上の[+リソースの作成] > [関数アプリの作成]を選択します。
最初にプランを選択する画面が表示されるので、今回は[フレックス従量課金]を選択します。従量課金よりも無料枠は少ないですが、コールドスタートが改善されているため、より安定した応答が期待できます。
開発言語は Python を選択します。その他の項目(名称、リージョンなど)は自身の環境に合わせて設定してください。
2. Azure Functionsコードの作成
関数アプリが作成できたら、以下のような画面が表示できると思います。
「フレックス従量課金 (Flex Consumption)」の「Linux」環境の場合、Azureポータルから直接コード記述ができない(らしい)ので、「VS Code Desktopで作成」を行います。
ローカルのVS Code でAzure Functionsの拡張機能をインストールし、Azureアカウントにサインインします。
左のサイドバーにAzureのアイコンが表示されるので、クリックします。
下の「WORKSPACE」という欄から、「Create Function Project...」を選択します。
画面上部に設定の質問が出るので答えます。言語は 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
3. Azure Functionsのデプロイ
変更したコードをAzure Functionsにデプロイします。「WORKSPACE」欄をマウスオーバーすると、クラウドにアップロードするためのアイコンが表示されるので、クリックします。
4. Google Gemini APIキーの設定
Azureポータルから、環境変数を追加します。名前は「GEMINI_API_KEY」、値は取得したGemini APIキーを設定します。
5. Alexaスキルの作成
Alexa開発者コンソールに戻り、[スキルの作成] をクリックします。
以下設定で新しくスキルを作成します。
| 項目 | 値 |
|---|---|
| スキル名 | 任意(今回はGemini) |
| プライマリロケール | 日本語 |
| エクスペリエンスのタイプ | その他 |
| モデル | カスタム |
| ホスティングサービス | Alexa-hosted(Python) |
| テンプレート | スクラッチ |
6. インテントの作成、サンプル発話の追加
まず、インテントを作成します。左のメニューから [Interaction Model] > [Intents] を選択し、 [AskGeminiIntent] という名称で新しいインテントを作成します。
サンプル発話とインテントスロットを以下のように設定します。
スロット名は「query」、タイプは「AMAZON.SearchQuery」を選択します。
7. エンドポイントの設定
左のメニューから [Endpoint] を選択し、エンドポイントタイプを「HTTPS」に設定します。
Azure FunctionsのURLを入力します。URLは以下の形式になります。
https://<関数アプリ名>.azurewebsites.net/api/alexa-gemini
alexa-gemini は、先ほど作成したAzure Functionsの関数名です。

エンドポイントを入力する下のプルダウンは「My development endpoint is a sub-domain...」を選択してください。
テスト
1. Alexa 開発者コンソールでのテスト
Alexa開発者コンソールの [Test] タブを開き、テストモードを「開発中」に設定します。
「ジェミニで〇×について教えて」等と入力して、正しく応答が返ってくるか確認します。
2. Alexa対応デバイスでのテスト
実機で「ジェミニで〇×について教えて」と話しかけ、正しく応答が返ってくるか確認します。
スキルが有効になっていれば、geminiの回答をAlexaが読み上げてくれます。
※部屋の様子が反射したので、画像を加工しています。
デバッグ
Application Insightsを有効化していれば、Azure Functions の左メニュー [監視] > [ログストリーム] から、Azure Functionsのログをリアルタイムで確認できます。Alexaスキルからのリクエストが正しく届いているか、Gemini APIの呼び出しに失敗していないか等を確認できます。
おわりに
以上、AlexaスキルからAzure Functions経由でgemini APIを呼び出す方法について解説しました。Azure Functionsを使うことで、サーバーレスで簡単にAPIを公開できるため、今回のような用途に非常に便利です。ぜひ試してみてください。
また、Alexaのタイムアウトが8秒と短いため、応答時間が長くなりがちな大規模言語モデルAPIを呼び出す場合は、応答時間の最適化やエラーハンドリングを工夫する必要があります。今回のコード例では、最大トークン数を制限することで応答時間を短縮していますが、他にもキャッシュの活用や非同期処理の導入など、様々な工夫が考えられます。
また、コールドスタートの影響を受けにくい「フレックス従量課金」プランを選択することで、応答速度の安定化を図っています。
上記対策をとってはいるものの応答が不安定なため、作ったは良いものの日常的に使用するかと言われれば微妙です。
今後、LLMを活用して何か実施する場合、タイムアウト対策は重要なポイントになると思うので、是非参考にしてみてください。











