【GAS】Geminiで問い合わせ対応を自動化!提案メール返信術
はじめに
Googleフォームでアンケートや相談受付を行った際、「回答内容に応じた個別の提案やアドバイスを返信したい」と考えたことはありませんか?
しかし、1件ずつ手動で回答を確認して文章を作成し、メールを送る作業は非常に時間がかかります。だからといって、一律の定型文メールでは利用者の満足度が上がらないという課題が生じがちです。
今回は、Google App Script(GAS)を活用し、Googleフォームの送信をトリガーにしてGemini API(Google検索グラウンディング機能付き)を呼び出し、最新の地域情報などを組み込んだ個別提案メールを完全自動で返信する仕組みを構築する方法をご紹介します!
今回構築する仕組みの概要
今回の仕組みでは、以下のステップで自動処理が行われます。
- Googleフォームへの回答送信(フォーム受信トリガー)
- GASが回答内容を取得・プロンプトの自動生成
- Gemini APIを呼び出し(Google検索ツールを活用して最新情報を抽出)
- 生成された提案内容を整え、HTMLメールで回答者&管理者に自動送信
これにより、入力内容に合わせた質の高い返信文を即座に作成・送信することが可能になります。
ソースコード(コピペOK)
秘匿情報(APIキーや送信先メールアドレス)は直接ソースコード内に書き込まず、GASの「スクリプトプロパティ(環境変数)」を使用して安全に管理できるように抽象化・汎用化したコードです。
/**
* ====================================================================
* Utility Module for GAS / Civic Tech Akita
* Copyright (c) Civic Tech Akita (https://akita-csmedia.com/)
*
* 本モジュールは Civic Tech Akita の留保知的財産権(自社資産)です。
* ライセンス・実装に関するお問い合わせ: https://akita-csmedia.com/?page_id=1476
* ====================================================================
*/
// 🎯 設定項目(環境変数から取得することを推奨)
function getScriptProperties_() {
const props = PropertiesService.getScriptProperties();
return {
apiKey: props.getProperty("GEMINI_API_KEY") || "任意のAPIキーを入力",
recipientEmail: props.getProperty("RECIPIENT_EMAIL") || "管理者のメールアドレスを入力",
modelName: "gemini-2.5-flash",
location: "任意の都道府県/地域名",
fallbackLocations: ["地域A", "地域B", "地域C"]
};
}
/**
* Googleフォームの回答が送信されたときに実行される関数。
*/
function onFormSubmit(e) {
if (!e || !e.values) {
Logger.log("エラー: イベントオブジェクトがありません。フォーム送信トリガーから実行してください。");
return;
}
const config = getScriptProperties_();
const responsesArray = e.values;
// フォームの回答順序に合わせてインデックスを調整(例: [0]はタイムスタンプ)
const email = responsesArray[1] || "";
const name = responsesArray[2] || "お客様";
const itemCategoryA = responsesArray[3] || "未選択";
const itemCategoryB = responsesArray[4] || "未選択";
// Geminiへのプロンプトを作成
const prompt = `
あなたは専門のアドバイザーです。
提供された「お客様の回答」を読み、条件に沿った最適な提案を作成してください。
提案を作成する際は、以下のステップでGoogle検索ツールを使って調べてから回答してください。
1. まず、**${config.location}**に特化して情報を検索してください。
2. もし${config.location}内で適切な情報が見つからない場合、**以下の地域全体**に検索範囲を広げてください。
${config.fallbackLocations.join(", ")}
### 提案の条件
* **Google検索で得られた内容のみ**を基に提案してください。
* 提案は、**明確な事実に基づき**、あなたの意見や推測、解釈は一切含めないでください。
* 提案は、簡潔な箇条書きで分かりやすくまとめてください。
* もしGoogle検索から提案を作成できなかった場合は、「現在、ご条件に合った情報が見つかりませんでした。確認の上、改めてご案内します。」と回答してください。
### お客様の回答
【カテゴリーA】${itemCategoryA}
【カテゴリーB】${itemCategoryB}
### 提案内容
`;
Logger.log("Geminiへのプロンプト送信中...");
let geminiResponseText = "AIによる提案の取得に失敗しました。";
try {
geminiResponseText = callGeminiAPI(prompt, config);
Logger.log("Geminiからの回答受信完了");
} catch (error) {
Logger.log("Gemini API呼び出しエラー: " + error.message);
geminiResponseText += "\n\nエラー詳細: " + error.message;
sendErrorEmailToUser(email, name, error.message);
}
// メール本文(HTML版)を準備
const emailBodyHtml = `
<div style="font-family: sans-serif; max-width: 600px; margin: auto; padding: 20px; border: 1px solid #e0e0e0; border-radius: 8px;">
<h2 style="color: #333;">自動提案サービスからのご案内</h2>
<p><strong>${name}</strong> 様</p>
<p>この度はお問い合わせいただき、誠にありがとうございます。</p>
<p>ご回答いただいた内容に基づき、AIが検索・作成した個別提案をご紹介します。</p>
<div style="background-color: #f9f9f9; border: 1px dashed #ccc; padding: 15px; margin-top: 20px; border-radius: 8px;">
<h3 style="color: #666; margin-top: 0;">ご回答内容</h3>
<p><strong>カテゴリーA:</strong> ${itemCategoryA}</p>
<p><strong>カテゴリーB:</strong> ${itemCategoryB}</p>
</div>
<div style="margin-top: 25px;">
<h3 style="color: #333;">AIからのご提案 ✨</h3>
<div style="line-height: 1.6;">${geminiResponseText.replace(/\n\n/g, '<br><br>').replace(/\n/g, '<br>').replace(/\* /g, '• ')}</div>
</div>
<div style="margin-top: 30px; border-top: 1px solid #e0e0e0; padding-top: 20px; text-align: center; font-size: 12px; color: #999;">
<p>このメールは自動送信されています。</p>
</div>
</div>
`;
// 回答者へメールを自動返信
if (email && email.includes('@')) {
GmailApp.sendEmail(email, '【AI自動作成】あなたへのスペシャルご提案', '', { htmlBody: emailBodyHtml });
Logger.log("回答者へメールを自動返信しました: " + email);
}
// 管理担当者へ通知・送信
if (config.recipientEmail && config.recipientEmail.includes('@')) {
GmailApp.sendEmail(config.recipientEmail, '【自動通知】新しいフォームリクエストが届きました', '', { htmlBody: emailBodyHtml });
}
}
/**
* Gemini APIを呼び出すヘルパー関数
*/
function callGeminiAPI(prompt, config) {
// Google検索(tools)を使用するため v1beta エンドポイントを利用
const API_ENDPOINT = "https://generativelanguage.googleapis.com/v1beta/models/" + config.modelName + ":generateContent?key=" + config.apiKey;
const data = {
contents: [{
parts: [{
text: prompt
}]
}],
tools: [{
"google_search": {} // Google検索ツールを有効化(v1betaのみ対応)
}]
};
const options = {
method: "post",
contentType: "application/json",
payload: JSON.stringify(data),
muteHttpExceptions: true
};
const response = UrlFetchApp.fetch(API_ENDPOINT, options);
const responseCode = response.getResponseCode();
const jsonResponse = JSON.parse(response.getContentText());
if (responseCode === 200 && jsonResponse.candidates && jsonResponse.candidates.length > 0) {
return jsonResponse.candidates[0].content.parts[0].text;
} else {
const errorMsg = jsonResponse.error ? jsonResponse.error.message : "不明なエラー";
throw new Error(`APIエラー (Status: ${responseCode}): ${errorMsg}`);
}
}
/**
* ユーザーにエラーを通知するメールを送信する関数
*/
function sendErrorEmailToUser(email, name, message) {
if (email && email.includes('@')) {
const subject = '【重要】提案サービス:エラー発生のお知らせ';
const body = `${name}様\n\nサービスをご利用いただきありがとうございます。\n\n申し訳ございませんが、現在システム処理中に問題が発生し提案を生成できませんでした。\n時間をおいて再度お試しいだたくか、サポートまでお問い合わせください。\n\n詳細: ${message}`;
GmailApp.sendEmail(email, subject, body);
}
}
設定・導入のステップ
- GASの環境変数の設定(推奨)
- 拡張機能 > Apps Script を開きます。
- プロジェクト設定(歯車マーク)>「スクリプト プロパティ」を開き、以下のプロパティを追加します。
GEMINI_API_KEY: 取得したGemini APIキーRECIPIENT_EMAIL: 管理者の通知用メールアドレス
- トリガーの設定
- GAS画面の左メニュー「トリガー(時計マーク)」をクリックします。
- 「トリガーを追加」を選択し、以下のように設定します。
- 実行する関数:
onFormSubmit - イベントの源泉:
フォームから - イベントの種類:
フォーム送信時
- 実行する関数:
- アクセス権の認証
- 初回保存・実行時にGoogleアカウントへの権限リクエストが表示されるため、許可(承認)を行ってください。
⚠️ 運用にあたっての注意点・技術的リスク
このスクリプトを実運用するにあたり、事前に把握しておくべき注意点が3点あります。
- Geminiのバージョン更新に伴うエンドポイントの変更リスク
GoogleのGemini APIはアップデートの頻度が高く、新しいモデル(Gemini 1.5, 2.0, 2.5など)の登場やエンドポイント(v1,v1beta)の仕様変更が定期的に行われます。特にgoogle_searchツール(グラウンディング機能)を利用する場合、特定のAPIバージョン(現在はv1betaなど)でしか動かないケースがあります。モデル名やAPIエンドポイントが無効化された場合は、コード内のURL・モデル名の更新が必要となります。 - GASおよびGemini APIの実行回数上限・クォータ制限
- GASの送信上限: 無料のGoogleアカウントの場合、GmailAppの1日あたりのメール送信上限は100通/日(Google Workspaceアカウントは1,500通/日)です。アクセスが集中すると上限に達して送信失敗となるリスクがあります。
- Gemini APIのレートリミット: 無料枠のAPIキーの場合、1分あたりのリクエスト数(RPM)や1日あたりのリクエスト数(RPD)に制限があります。フォーム投稿が短い時間に集中した際、API側でエラーを返す可能性があります。
- APIエラー時のハンドリングとセキュリティ
- 万が一Gemini APIがエラーを返した場合、利用者にそのままエラー文面が送信されないよう適切なエラーメール通知処理(
sendErrorEmailToUser)を挟んでいます。 - ソースコード内に直接APIキーや個人用メールアドレスを記載(ハードコーディング)すると、第三者への漏洩事故に繋がります。必ずスクリプトプロパティを活用してください。
- 万が一Gemini APIがエラーを返した場合、利用者にそのままエラー文面が送信されないよう適切なエラーメール通知処理(
まとめ・導入のご案内
GASとGemini API(Google検索機能付き)を組み合わせることで、問い合わせ対応やアンケート後の提案送付を高品質かつ全自動化することが可能になります。現場の作業コスト削減に大きく寄与する手法ですので、ぜひ活用してみてください。
💡 この機能が最初から入った完成品サービスをお探しの方へ
「スクリプトの管理や設定作業をプロに任せたい」「エラー対策や例外処理まで作り込まれた現場用アプリとして今すぐ導入したい」方向けに、今回ご紹介したAI自動返信機能が標準搭載された完成品ソリューションを提供しています。
🏢 インフラ構築や組織全体のDXからご相談されたい方へ
Google Workspaceの導入、独自ドメインの整備からアプリ・LINE連携まで、予算最小限で進める全体設計は「地域DX完全ロードマップ」にて詳しく公開しています。
