SYSTEM NOTICE

Auto translation by AI. Be sure, accuracy, nuances and authorial intent may not be fully reflected.
見出し画像

How to Automatically Categorize Inquiries and Generate FAQ Candidates with Google Sheets and Gemini API [Apps Script Implementation]

How to Automatically Categorize Inquiries and Generate FAQ Candidates with Google Sheets and Gemini API [Apps Script Implementation]


As you continue to handle inquiries, data keeps piling up in Google Sheets.

Questions received via LINE, inquiries from Google Forms, email correspondence history, and customer requests entered by sales representatives.

However, in the field, you often hear comments like these:

"The same questions keep coming in," "Answers vary slightly depending on the person in charge," "I want to turn them into an FAQ, but I don't have time to organize them," "I don't know which questions should be posted on the Web or LINE FAQ."

In this state, inquiry data remains nothing more than a history.

In this article, I will organize how to implement automatic inquiry categorization and FAQ candidate output from a practical perspective using a combination of Google Sheets × Apps Script × Gemini API.

What this system can do

The system we are building consists of two main stages.

The first is the automatic categorization of each individual inquiry.

We send the inquiry content to the Gemini API and have it return the following information:

  • Category (product, pricing, delivery time, etc.)

  • Summary

  • Urgency

  • Recommended department

  • Whether it is a candidate for an FAQ

  • Draft response

  • Points requiring internal verification

The second is the output of FAQ candidates from accumulated data.

Pass multiple inquiries to the Gemini API at once to organize frequently asked questions. You can automatically generate FAQ candidates by category, such as delivery dates, returns, and payment methods.

Once this system is in place, the inquiry history accumulated in Google Sheets becomes easier to use as material for FAQs and manuals.

Overall Structure and Flow

text

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

The important thing here is the design of letting the AI handle categorization and drafting rather than leaving the final response to the Gemini API.

FAQs and response templates are directly linked to the quality of customer support. Fees, delivery dates, return policies, and contract details must always be verified by a human. Therefore, the operation should treat AI output as "drafts," "candidates," or "requires verification."

Sheet Design | First, prepare two sheets

Inquiry Management Sheet

The sheet name will be Inquiry Management.

Column items: A: Reception Date/Time, B: Inquiry Source, C: Customer Name, D: Inquiry Content, E: Response Content, F: Status, G: AI Classification, H: Summary, I: FAQ Candidate, J: Recommended Department, K: Urgency, L: Draft Response, M: Internal Verification Items, N: AI Processed, O: Processed Date/Time

FAQ Candidate Sheet

The sheet name will be FAQ Candidates.

Column items: A: Creation Date/Time, B: Category, C: Frequently Asked Question, D: Draft Response, E: Supporting Inquiry, F: Internal Verification Items, G: Publication Approval, H: Status, I: Verification Person, J: Last Updated Date

It is important to keep the results categorized by AI separate from the official responses verified by humans. This acts as a guard to prevent AI response drafts from being used directly as official FAQs.

API Key Storage and Basic Settings

Saving the API Key in Script Properties

Avoid writing the API key directly into Apps Script. Manage it using script properties.

javascript

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

Run this function once at the beginning. The model name may change due to updates in the API specifications. If it does not work, check the available model names in Google AI Studio or the official Gemini API documentation.

Implementation for Sending Inquiry Content to the Gemini API

Function to categorize a single inquiry

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);
}

Using structured output makes writing back to Google Sheets smoother.

Automatically processing uncategorized inquiries

Function to process unprocessed rows in order

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);
    }
  }
}

The 'AI Processed' column is key. Without this, you would send the same inquiry to the API multiple times. To keep costs and processing time down, be sure to include a mechanism to manage processed items.

Error log design

In API integration, unexpected errors will inevitably occur.

  • Invalid API key

  • Model name change

  • Input data too long

  • Temporary communication error

  • API usage limits

  • Sheet structure change

Do not leave rows where errors occurred as they are; include a mechanism to check them later.

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]);
}

Generating FAQ candidates in bulk

Sending FAQ candidates to the Gemini API in bulk

Process only those inquiries that have already been categorized by AI and have 'Yes' for FAQ candidacy.

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, '下書き', '', ''
    ]);
  });
}

The returned FAQ candidates are written to the FAQ Candidates sheet. Initially, they are all set to 'Draft' status.

Executing from the Google Sheets menu

Opening the Apps Script editor every time is cumbersome. We will add a menu to the sheet so that it can be executed like a button.

javascript

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

Since the menu is displayed when opening Google Sheets, operations staff can also execute the process directly from the sheet.

Operational workflow in practice

Step 1 | Accumulate inquiries in Google Sheets

Centralize inquiry management via LINE Official Account, Google Forms, email forwarding, etc.

Step 2 | Exclude and anonymize personal information

Before sending to the AI, remove names, phone numbers, email addresses, addresses, order numbers, etc. What is needed for FAQ generation is not personal information, but the content of the question and the response policy.

Steps 3-7 | From categorization to official FAQ

Execute 'AI Categorize Unprocessed Inquiries' from the menu -> Human verifies categorization results -> Generate FAQ candidates -> Staff reviews draft responses -> Deploy only verified FAQs to websites, LINE, and internal manuals.

By following this flow, inquiry history becomes material for FAQs.

Tips for category design

In inquiry categorization, category design affects accuracy. If you make it too granular from the start, categorization tends to become inconsistent.

It is realistic to start with broader categories first.

text

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

For an e-commerce site, add categories like 'Shipping', 'Gift Options', 'Coupons', and 'Receipts'; for B2B services, add 'Document Requests', 'Quote Requests', and 'Security'. Since categories also affect the structure of FAQs and manuals, it is important to set them at a granularity that is easy to utilize later.

Do not publish AI output as-is | Verification flow protects quality

The most important aspect of this system is not to publish FAQ drafts created by AI as they are.

In particular, a person in charge must always verify the following content.

  • Pricing, delivery times, returns, and cancellation policies

  • Contract details, legal matters, and terms of service

  • Content related to medical, financial, or safety matters

  • Content containing personal information

  • Warranty details and internal company rules

It is recommended to manage statuses as follows.

text

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

In the FAQ candidate sheet, set everything to "Draft" initially, and only change it to "Ready to Publish" after a person in charge has verified it. Without this verification flow, the risk of incorrect FAQs being published remains.

Apps Script Execution Limits and Handling Large Volumes of Data

Apps Script has limitations when processing large volumes of data. While a few dozen items may not be an issue, sending hundreds of items to the API at once may trigger execution time or API limits.

Setting a limit on the number of items processed per execution is a practical approach.

javascript

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

Designing the system to use a processed flag and re-processing error rows later makes operations easier to manage.

Tasks Suited and Not Suited for This System

Suited Tasks

  • Inquiry management for LINE Official Accounts

  • Organizing inquiries from Google Forms

  • Creating FAQs for e-commerce sites

  • Organizing questions after seminar registration

  • Converting internal inquiries into FAQs

  • Creating support manuals for new employee training

The effects are more noticeable once a certain number of inquiries have accumulated. As you reach 50 or 100 entries, it becomes easier to identify common questions and inconsistencies in responses.

Tasks that require caution

  • Requiring immediate and accurate automated replies

  • Handling high-risk responses such as legal, medical, or financial matters

  • Wanting to publish FAQs completely automatically

  • Needing to process large volumes of data in real-time

For these use cases, you should consider using dedicated inquiry management systems, CRMs, or Cloud Run, rather than relying solely on GAS and the Gemini API.

Always keep operational notes

This type of automation tends to become something only the creator understands. Much like Excel macros, GAS can easily become siloed to a single person.

Leaving operational notes like the following in your sheet or internal documentation will make handovers smoother.

text

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

Summary | Converting inquiry data into FAQs standardizes response quality

Inquiry support does not end with sending a reply. Each piece of data reflects the anxieties and questions of your customers.

By combining Google Sheets and the Gemini API, you can create a workflow that turns inquiry support into internal knowledge.

  • Automatically categorize inquiry content

  • Create drafts for summaries and response suggestions

  • Identify questions that can be turned into FAQs

  • Output FAQ candidates to a separate sheet

  • Have a human review and finalize them as official FAQs

However, the AI only creates drafts. Content related to pricing, delivery times, returns, contracts, terms of service, and personal information must always be reviewed by a responsible person.

Whether you turn them into FAQs, internal manuals, improve product pages, or use them for new employee training, establishing this workflow turns inquiry handling from a simple task into a way to build company knowledge.

Beyond generative AI, build DX that is actually used in the field.

Linking Google Sheets with the Gemini API for inquiry categorization and FAQ candidate output is a practical first step toward preventing customer support from becoming dependent on specific individuals and ensuring knowledge remains within the company.

On my note, I share information about corporate generative AI utilization and DX promotion from a practical perspective.

I summarize how to improve sales, meetings, document creation, Excel tasks, email correspondence, and internal knowledge organization using tools like ChatGPT, Claude, Microsoft Copilot, Gemini, and NotebookLM.

Rather than just introducing AI tools, I share practical application methods that are easy to implement in actual work, focusing on themes like 'AI utilization used in the field,' 'DX that leads to business improvement,' and 'building systems to reduce reliance on specific individuals.'

Please feel free to read my other articles as well.


いいなと思ったら応援しよう!