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」「属人化を減らす仕組みづくり」をテーマに、実際の仕事に落とし込みやすい活用法を発信しています。
他の記事もぜひ読んでみてください。

