メインコンテンツへスキップ
見出し画像

Google SheetsとGemini APIで問い合わせを自動分類・FAQ候補を出力する方法【Apps Script実装】

    Google SheetsとGemini APIで問い合わせを自動分類・FAQ候補を出力する方法【Apps Script実装】


    画像

    問い合わせ対応を続けていると、Google Sheetsにはデータがどんどん積み上がっていきます。

    LINEから届いた質問、Googleフォームからの問い合わせ、メール対応の履歴、営業担当が入力した顧客の要望。

    でも現場では、こんな声がよく聞かれます。

    「同じ質問が何度も来ている」「担当者によって回答が微妙に違う」「FAQ化したいけど、整理する時間がない」「どの質問をWebやLINEのFAQに載せるべきか分からない」

    こうした状態では、問い合わせデータはただの履歴で止まってしまいます。

    この記事では、Google Sheets × Apps Script × Gemini API の組み合わせで、問い合わせの自動分類とFAQ候補出力を実装する方法を、実務目線で整理します。

    この仕組みで何ができるか

    今回作る仕組みは、大きく2段階です。

    1つ目は、問い合わせ1件ごとの自動分類。

    問い合わせ内容をGemini APIに送り、次の情報を返してもらいます。

    • カテゴリ(商品、料金、納期など)

    • 要約

    • 緊急度

    • 推奨担当部署

    • FAQ化候補かどうか

    • 回答案のたたき台

    • 社内確認が必要な点

    2つ目は、蓄積データからのFAQ候補出力。

    複数の問い合わせをまとめてGemini APIに渡し、よくある質問を整理します。納期、返品、支払い方法など、カテゴリ別にFAQ候補を自動生成できます。

    この仕組みができると、Google Sheetsに溜まった問い合わせ履歴が、FAQやマニュアルの材料として使いやすくなります。

    全体構成と流れ

    text

    問い合わせデータをGoogle Sheetsに蓄積
    ↓
    Apps Scriptで未分類の行を取得
    ↓
    Gemini APIへ問い合わせ内容を送信
    ↓
    Gemini APIが分類・要約・回答案をJSONで返す
    ↓
    Apps Scriptで結果をGoogle Sheetsに書き戻す
    ↓
    複数件をまとめてFAQ候補として別シートに出力
    ↓
    人間が確認して正式FAQ化

    ここで大切なのは、Gemini APIに最終回答を任せるのではなく、分類と下書き作成を任せるという設計です。

    FAQや返信文は顧客対応の品質に直結します。料金・納期・返品条件・契約内容などは、必ず人間が確認する必要があります。そのため、AIの出力は「下書き」「候補」「要確認」として扱う運用にします。

    シート設計|まず2つのシートを用意する

    問い合わせ管理シート

    シート名は 問い合わせ管理 とします。

    列項目A受付日時B問い合わせ元C顧客名D問い合わせ内容E回答内容FステータスGAI分類HAI要約IFAQ化候補J推奨担当部署K緊急度L回答案たたき台M社内確認事項NAI処理済みO処理日時

    FAQ候補シート

    シート名は FAQ候補 とします。

    列項目A作成日時BカテゴリCよくある質問D回答案たたき台E根拠となる問い合わせF社内確認事項G公開可否HステータスI確認担当J最終更新日

    AIが分類した結果と、人間が確認した正式回答は分けておくことが重要です。AIの回答案をそのまま正式FAQにしないためのガードとして機能します。

    APIキーの保存と基本設定

    スクリプトプロパティにAPIキーを保存する

    Apps Scriptに直接APIキーを書くのは避けます。スクリプトプロパティで管理します。

    javascript

    function setProperties() {
      PropertiesService.getScriptProperties().setProperties({
        GEMINI_API_KEY: 'ここにGemini APIキー',
        GEMINI_MODEL: 'gemini-3.5-flash'
      });
    }

    この関数を最初に一度だけ実行します。モデル名はAPI仕様の更新によって変わる可能性があります。動かない場合は、Google AI StudioやGemini APIの公式ドキュメントで利用可能なモデル名を確認してください。

    Gemini APIへ問い合わせ内容を送る実装

    問い合わせ1件を分類する関数

    javascript

    function callGeminiForInquiry_(inquiryText, answerText) {
      const apiKey = PropertiesService.getScriptProperties().getProperty('GEMINI_API_KEY');
      const model = PropertiesService.getScriptProperties().getProperty('GEMINI_MODEL') || 'gemini-3.5-flash';
    
      const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent`;
    
      const prompt = `
    あなたは企業の問い合わせ対応データを整理する業務アシスタントです。
    以下の問い合わせ内容を、Google Sheetsに書き戻しやすいJSON形式で分類してください。
    
    目的:
    問い合わせ対応の属人化を減らし、FAQ候補や社内ナレッジとして活用するため。
    
    分類カテゴリ候補:
    - 商品について
    - 料金について
    - 納期について
    - 注文・予約について
    - 支払いについて
    - 返品・交換について
    - キャンセルについて
    - トラブル対応
    - 法人対応
    - その他
    
    条件:
    - 個人情報は出力しない
    - 未確認事項は断定しない
    - 回答案は丁寧で簡潔にする
    - 社内確認が必要な場合は明記する
    - FAQ化に向いているかを判断する
    - 料金、納期、返品、契約条件は特に慎重に扱う
    
    問い合わせ内容:
    ${inquiryText}
    
    既存の回答内容:
    ${answerText || 'なし'}
    `;
    
      const payload = {
        contents: [{ parts: [{ text: prompt }] }],
        generationConfig: {
          responseFormat: {
            text: {
              mimeType: 'application/json',
              schema: {
                type: 'object',
                properties: {
                  category: { type: 'string' },
                  summary: { type: 'string' },
                  faqCandidate: { type: 'boolean' },
                  urgency: { type: 'string' },
                  recommendedDepartment: { type: 'string' },
                  draftAnswer: { type: 'string' },
                  confirmationNeeded: { type: 'array', items: { type: 'string' } },
                  caution: { type: 'string' }
                },
                required: ['category', 'summary', 'faqCandidate', 'urgency',
                           'recommendedDepartment', 'draftAnswer', 'confirmationNeeded', 'caution']
              }
            }
          }
        }
      };
    
      const options = {
        method: 'post',
        contentType: 'application/json',
        headers: { 'x-goog-api-key': apiKey },
        payload: JSON.stringify(payload),
        muteHttpExceptions: true
      };
    
      const response = UrlFetchApp.fetch(url, options);
      const code = response.getResponseCode();
      const body = response.getContentText();
    
      if (code < 200 || code >= 300) {
        throw new Error(`Gemini API error: ${code} ${body}`);
      }
    
      const json = JSON.parse(body);
      const text = json.candidates?.[0]?.content?.parts?.[0]?.text;
    
      if (!text) throw new Error('Gemini API response does not contain text.');
    
      return JSON.parse(text);
    }

    構造化出力を使うことで、Google Sheetsへの書き戻しがスムーズになります。

    未分類の問い合わせを自動処理する

    未処理行を順番に処理する関数

    javascript

    function classifyInquiries() {
      const ss = SpreadsheetApp.getActiveSpreadsheet();
      const sheet = ss.getSheetByName('問い合わせ管理');
      const values = sheet.getDataRange().getValues();
    
      for (let i = 1; i < values.length; i++) {
        const row = values[i];
        const inquiryText = row[3]; // D列:問い合わせ内容
        const answerText = row[4];  // E列:回答内容
        const processed = row[13]; // N列:AI処理済み
    
        if (!inquiryText) continue;
        if (processed === '済') continue;
    
        try {
          const result = callGeminiForInquiry_(inquiryText, answerText);
    
          sheet.getRange(i + 1, 7).setValue(result.category);
          sheet.getRange(i + 1, 8).setValue(result.summary);
          sheet.getRange(i + 1, 9).setValue(result.faqCandidate ? 'はい' : 'いいえ');
          sheet.getRange(i + 1, 10).setValue(result.recommendedDepartment);
          sheet.getRange(i + 1, 11).setValue(result.urgency);
          sheet.getRange(i + 1, 12).setValue(result.draftAnswer);
          sheet.getRange(i + 1, 13).setValue((result.confirmationNeeded || []).join('\n'));
          sheet.getRange(i + 1, 14).setValue('済');
          sheet.getRange(i + 1, 15).setValue(new Date());
    
          Utilities.sleep(1000);
    
        } catch (error) {
          sheet.getRange(i + 1, 14).setValue('エラー');
          sheet.getRange(i + 1, 15).setValue(new Date());
          logError_('classifyInquiries', error.message, i + 1);
        }
      }
    }

    AI処理済み 列がポイントです。これがないと、同じ問い合わせを何度もAPIに送ってしまいます。コストと処理時間を抑えるために、処理済み管理は必ず入れておきます。

    エラーログの設計

    API連携では、想定外のエラーが必ず起きます。

    • APIキーが無効

    • モデル名の変更

    • 入力データが長すぎる

    • 一時的な通信エラー

    • APIの利用制限

    • シート構成の変更

    エラーが出た行をそのままにせず、後から確認できる仕組みを入れておきます。

    javascript

    function logError_(functionName, message, rowNumber) {
      const ss = SpreadsheetApp.getActiveSpreadsheet();
      let sheet = ss.getSheetByName('エラーログ');
    
      if (!sheet) {
        sheet = ss.insertSheet('エラーログ');
        sheet.appendRow(['日時', '関数名', '行番号', 'エラー内容']);
      }
    
      sheet.appendRow([new Date(), functionName, rowNumber || '', message]);
    }

    FAQ候補をまとめて生成する

    FAQ化候補をまとめてGemini APIへ送る

    AI分類が済んでいて、FAQ化候補が「はい」の問い合わせだけをまとめて処理します。

    javascript

    function generateFaqCandidates() {
      const ss = SpreadsheetApp.getActiveSpreadsheet();
      const inquirySheet = ss.getSheetByName('問い合わせ管理');
      const faqSheet = ss.getSheetByName('FAQ候補') || ss.insertSheet('FAQ候補');
      const values = inquirySheet.getDataRange().getValues();
      const targetRows = [];
    
      for (let i = 1; i < values.length; i++) {
        const row = values[i];
        if (row[13] === '済' && row[8] === 'はい') {
          targetRows.push({
            rowNumber: i + 1,
            category: row[6],
            inquiryText: row[3],
            answerText: row[4]
          });
        }
      }
    
      if (targetRows.length === 0) {
        SpreadsheetApp.getUi().alert('FAQ候補にできる問い合わせがありません。');
        return;
      }
    
      const result = callGeminiForFaqCandidates_(targetRows);
    
      if (faqSheet.getLastRow() === 0) {
        faqSheet.appendRow(['作成日時', 'カテゴリ', 'よくある質問', '回答案', '根拠となる問い合わせ',
                            '社内確認事項', '公開可否', 'ステータス', '確認担当', '最終更新日']);
      }
    
      result.faqs.forEach(function(faq) {
        faqSheet.appendRow([
          new Date(), faq.category, faq.question, faq.draftAnswer,
          (faq.sourceRows || []).join(', '),
          (faq.confirmationNeeded || []).join('\n'),
          faq.publishability, '下書き', '', ''
        ]);
      });
    }

    返ってきたFAQ候補は、FAQ候補 シートへ書き出します。最初はすべて「下書き」ステータスになります。

    Google Sheetsメニューから実行する

    毎回Apps Scriptエディタを開くのは手間です。シート上にメニューを追加して、ボタン感覚で実行できるようにします。

    javascript

    function onOpen() {
      SpreadsheetApp.getUi()
        .createMenu('AI問い合わせ処理')
        .addItem('未処理問い合わせをAI分類', 'classifyInquiries')
        .addItem('FAQ候補を生成', 'generateFaqCandidates')
        .addToUi();
    }

    Google Sheetsを開いたときにメニューが表示されるので、運用担当者もシート上から処理を実行できます。

    実務での運用フロー

    Step1|問い合わせをGoogle Sheetsに蓄積する

    LINE公式アカウント、Googleフォーム、メール転記などで問い合わせを一元管理します。

    Step2|個人情報を除外・匿名化する

    AIに送る前に、氏名・電話番号・メールアドレス・住所・注文番号などは削除します。FAQ生成に必要なのは個人情報ではなく、質問の内容と回答方針です。

    Step3〜7|分類から正式FAQ化まで

    メニューから 未処理問い合わせをAI分類 を実行 → 分類結果を人間が確認 → FAQ候補を生成 → 担当者が回答案を確認 → 確認済みのFAQだけをWebサイト・LINE・社内マニュアルに展開。

    この流れにすれば、問い合わせ履歴がFAQの材料になります。

    カテゴリ設計のコツ

    問い合わせ分類では、カテゴリ設計が精度に影響します。最初から細かくしすぎると、分類がぶれやすくなります。

    まずは大きめのカテゴリから始めるのが現実的です。

    text

    商品について / 料金について / 納期について / 注文・予約について
    支払いについて / 返品・交換について / キャンセルについて
    トラブル対応 / 法人対応 / その他

    ECサイトなら「配送」「ギフト対応」「クーポン」「領収書」、BtoBサービスなら「資料請求」「見積依頼」「セキュリティ」などを追加していきます。カテゴリは、FAQやマニュアルの構成にも影響するので、後から活用しやすい粒度にすることが大切です。

    AI出力をそのまま公開しない|確認フローが品質を守る

    この仕組みで最も重要なのは、AIが作ったFAQ案をそのまま公開しないことです。

    特に次の内容は、必ず担当者が確認します。

    • 料金・納期・返品・キャンセル条件

    • 契約内容・法務・規約

    • 医療・金融・安全に関わる内容

    • 個人情報を含む内容

    • 保証内容・社内ルール

    ステータスは次のように運用するのがおすすめです。

    text

    下書き → 確認中 → 修正必要 → 公開可 / 社内限定 / 非公開

    FAQ候補シートでは最初はすべて「下書き」にし、担当者が確認して初めて「公開可」に変更します。この確認フローがないと、間違ったFAQが公開されるリスクが残ります。

    Apps Scriptの実行制限と大量データへの対応

    Apps Scriptは大量データの処理には制限があります。数十件程度なら問題になりにくいですが、数百件を一度にAPIへ送ると実行時間やAPI制限に引っかかる可能性があります。

    1回の処理件数に上限を設ける方法が実用的です。

    javascript

    function classifyInquiriesWithLimit() {
      const limit = 30;
      let count = 0;
      // ... (通常のclassifyInquiriesと同じ処理)
      // ループ内に if (count >= limit) break; を追加
    }

    処理済みフラグを使い、エラー行は後で再処理する設計にすると運用しやすくなります。

    この仕組みが向いている業務と向いていない業務

    向いている業務

    • LINE公式アカウントの問い合わせ管理

    • Googleフォームの問い合わせ整理

    • ECサイトのFAQ作成

    • セミナー申込後の質問整理

    • 社内問い合わせのFAQ化

    • 新人教育用の対応マニュアル作成

    問い合わせ件数がある程度溜まった段階で効果が出やすいです。50件、100件と蓄積されてくると、よくある質問や回答のばらつきが見えやすくなります。

    注意が必要な業務

    • 即時に正確な自動返信が必要

    • 法務・医療・金融など高リスクな回答が多い

    • FAQを完全自動で公開したい

    • 大量データをリアルタイム処理したい

    このような用途では、GASとGemini APIだけで完結させるのではなく、専用の問い合わせ管理システムやCRM、Cloud Runなどの活用も検討が必要です。

    運用メモを必ず残す

    このような自動化は、作った人しか分からない状態になりがちです。Excelマクロと同じように、GASも属人化します。

    次のような運用メモをシート内や社内ドキュメントに残しておくと、引き継ぎがスムーズになります。

    text

    【連携名】問い合わせデータAI分類・FAQ候補生成
    【目的】Google SheetsにためたデータをGemini APIで分類し、FAQ候補を作成する
    【対象シート】問い合わせ管理 / 【出力先シート】FAQ候補
    【実行方法】Google Sheetsメニュー「AI問い合わせ処理」から実行
    【注意点】AI出力は下書き扱い。正式FAQ化前に人間が確認する。個人情報はAIに送らない
    【確認担当】〇〇部 担当者名 / 【最終更新日】2026/05/20

    まとめ|問い合わせデータをFAQに変えると、対応品質がそろう

    問い合わせ対応は、返信して終わりではありません。一つひとつのデータには、顧客の不安や疑問が表れています。

    Google SheetsとGemini APIを組み合わせることで、問い合わせ対応を社内ナレッジに変える流れが作れます。

    • 問い合わせ内容を自動分類する

    • 要約と回答案のたたき台を作る

    • FAQ化できる質問を見つける

    • FAQ候補を別シートに出力する

    • 人間が確認して正式FAQにする

    ただし、AIが作るのはあくまで下書きです。料金、納期、返品、契約、規約、個人情報に関わる内容は、必ず担当者が確認します。

    FAQにする、社内マニュアルにする、商品ページを改善する、新人教育に使う。この流れができると、問い合わせ対応は単なる作業ではなく、会社のナレッジづくりになります。

    生成AIの先に、現場で使われるDXをつくる。

    Google SheetsとGemini APIを連携させた問い合わせ分類・FAQ候補出力は、顧客対応を属人化させず、社内に知識を残すための実務的な一歩です。

    私のnoteでは、企業の生成AI活用・DX推進について、実務目線で発信しています。

    ChatGPT、Claude、Microsoft Copilot、Gemini、NotebookLMなどを使い、営業、会議、資料作成、Excel業務、メール対応、社内ナレッジ整理をどのように改善できるかをまとめています。

    単なるAIツール紹介ではなく、「現場で使われるAI活用」「業務改善につながるDX」「属人化を減らす仕組みづくり」をテーマに、実際の仕事に落とし込みやすい活用法を発信しています。

    他の記事もぜひ読んでみてください。


    画像
     
     
    製造業の現場でDXを進めてきた係長。 ChatGPT・Gemini・NotebookLM・GASで、会議・資料作成・問い合わせ対応・紙業務を少しずつ軽くする実務記事を書いています。 AIで定時退社し、浮いた時間で旅に出る。

    あなたへのおすすめ