GAS×ChatGPTでGoogleフォーム回答を自動処理|分類・要約・返信を自動化
Googleフォームの回答が100件を超えたあたりから、手作業での処理は現実的ではなくなる。自由記述のアンケートを1件ずつ読み、カテゴリを振り分け、優先度を判断する。この繰り返しに毎週何時間も費やしているなら、GASとChatGPT APIで自動化してしまおう。
フォーム送信をトリガーにして、回答の分類・要約・通知まで数秒で完了する仕組みを、コピペで使えるコード付きで紹介する。
この記事で作れるもの
ユースケース1: アンケート回答の自動分類
社内アンケートや顧客アンケートの自由記述を、ChatGPTが自動でカテゴリ分類する。
処理例:
【回答】「商品の品質は良いのですが、配送が遅くて困りました」
↓ ChatGPTが自動分類
【結果】カテゴリ: 配送、感情: ネガティブ、優先度: 高
ユースケース2: お問い合わせの自動振り分け
問い合わせ内容をChatGPTが分析し、適切な担当部署を自動判定。緊急度の判定も同時に行う。
処理例:
【問い合わせ】「ログインできなくなりました。急ぎで対応お願いします」
↓ ChatGPTが自動判定
【結果】部署: 技術サポート、緊急度: 高、キーワード: ログイン障害
ユースケース3: 申込内容の要約と確認メール
長文の申込フォームの内容をChatGPTが要約し、確認メールを自動送信する。
処理例:
【申込内容】(500文字以上の詳細情報)
↓ ChatGPTが要約
【確認メール】要点を3行にまとめた確認メールを自動送信
必要な準備
1. Googleアカウント
Googleフォームとスプレッドシートを使うために必要になる。
2. OpenAI APIキー
まだ取得していない方は、GAS×ChatGPT連携の基礎記事で取得手順を確認してほしい。
3. 処理対象のGoogleフォーム
既存のフォームでも、新規作成でもOK。ここではアンケートフォームを例に進める。
STEP 1: フォームとスプレッドシートを準備
1-1: サンプルフォームの作成
以下の項目でアンケートフォームを作成する。
| 質問 | 形式 | 必須 |
|---|---|---|
| お名前 | 記述式 | ○ |
| メールアドレス | 記述式 | ○ |
| 満足度 | 選択式(5段階) | ○ |
| 良かった点 | 段落(自由記述) | – |
| 改善してほしい点 | 段落(自由記述) | – |
1-2: スプレッドシートと連携
- フォーム編集画面で「回答」タブをクリック
- スプレッドシートアイコンをクリック
- 「新しいスプレッドシートを作成」を選択
- 名前を「アンケート管理」として作成
1-3: 処理結果用の列を追加
スプレッドシートのG列以降に、以下の列を追加する。
| 列 | 項目名 |
|---|---|
| G | 処理ステータス |
| H | カテゴリ |
| I | 感情分析 |
| J | 優先度 |
| K | 要約 |
| L | 処理日時 |
STEP 2: 基本コードを設定
2-1: スクリプトエディタを開く
スプレッドシートのメニューから「拡張機能」→「Apps Script」を選択。
2-2: メインコードを入力
以下のコードをコピー&ペーストする。
/**
* Googleフォーム自動処理システム
* GAS × ChatGPT API
*/
// ========================================
// 設定
// ========================================
const CONFIG = {
SHEET_NAME: 'フォームの回答 1',
MODEL: 'gpt-4o-mini',
MAX_TOKENS: 1000,
TEMPERATURE: 0.3
};
// 列番号の定義(1始まり)
const COLS = {
TIMESTAMP: 1,
NAME: 2,
EMAIL: 3,
SATISFACTION: 4,
GOOD_POINTS: 5,
IMPROVEMENTS: 6,
STATUS: 7,
CATEGORY: 8,
SENTIMENT: 9,
PRIORITY: 10,
SUMMARY: 11,
PROCESSED_AT: 12
};
// ========================================
// フォーム送信時の自動処理
// ========================================
function onFormSubmit(e) {
try {
const sheet = SpreadsheetApp.getActiveSpreadsheet()
.getSheetByName(CONFIG.SHEET_NAME);
const lastRow = sheet.getLastRow();
// 既に処理済みならスキップ
const status = sheet.getRange(lastRow, COLS.STATUS).getValue();
if (status === '処理済み') return;
// フォームデータを取得
const formData = {
name: sheet.getRange(lastRow, COLS.NAME).getValue(),
email: sheet.getRange(lastRow, COLS.EMAIL).getValue(),
satisfaction: sheet.getRange(lastRow, COLS.SATISFACTION).getValue(),
goodPoints: sheet.getRange(lastRow, COLS.GOOD_POINTS).getValue(),
improvements: sheet.getRange(lastRow, COLS.IMPROVEMENTS).getValue()
};
// ChatGPTで分析
const analysis = analyzeResponse(formData);
// 結果をスプレッドシートに記録
sheet.getRange(lastRow, COLS.STATUS).setValue('処理済み');
sheet.getRange(lastRow, COLS.CATEGORY).setValue(analysis.category);
sheet.getRange(lastRow, COLS.SENTIMENT).setValue(analysis.sentiment);
sheet.getRange(lastRow, COLS.PRIORITY).setValue(analysis.priority);
sheet.getRange(lastRow, COLS.SUMMARY).setValue(analysis.summary);
sheet.getRange(lastRow, COLS.PROCESSED_AT).setValue(new Date());
// 優先度が高い場合は通知
if (analysis.priority === '高') {
notifyHighPriority(formData, analysis);
}
console.log(`処理完了: ${formData.name}`);
} catch (error) {
console.error('処理エラー:', error);
notifyError(error);
}
}
// ========================================
// ChatGPTで回答を分析
// ========================================
function analyzeResponse(formData) {
const apiKey = PropertiesService.getScriptProperties()
.getProperty('OPENAI_API_KEY');
if (!apiKey) {
throw new Error('APIキーが設定されていません');
}
const prompt = `以下のアンケート回答を分析してください。
【回答者】${formData.name}
【満足度】${formData.satisfaction}
【良かった点】${formData.goodPoints || '(未記入)'}
【改善してほしい点】${formData.improvements || '(未記入)'}
以下のJSON形式で回答してください。JSONのみ出力し、他の文章は含めないでください。
{
"category": "商品品質/価格/配送/接客/サイト操作/その他のいずれか",
"sentiment": "ポジティブ/ネガティブ/中立のいずれか",
"priority": "高/中/低のいずれか(改善要望の緊急度)",
"summary": "回答内容を50文字以内で要約"
}`;
const response = callChatGPT(apiKey, prompt);
try {
return JSON.parse(response);
} catch (e) {
// JSONパースに失敗した場合のフォールバック
return {
category: '分類不可',
sentiment: '不明',
priority: '中',
summary: response.substring(0, 50)
};
}
}
// ========================================
// ChatGPT API呼び出し
// ========================================
function callChatGPT(apiKey, prompt) {
const url = 'https://api.openai.com/v1/chat/completions';
const payload = {
model: CONFIG.MODEL,
messages: [
{
role: 'system',
content: 'あなたはアンケート分析の専門家です。回答を正確に分析し、指定された形式で出力してください。'
},
{
role: 'user',
content: prompt
}
],
max_tokens: CONFIG.MAX_TOKENS,
temperature: CONFIG.TEMPERATURE
};
const options = {
method: 'post',
contentType: 'application/json',
headers: {
'Authorization': 'Bearer ' + apiKey
},
payload: JSON.stringify(payload),
muteHttpExceptions: true
};
const response = UrlFetchApp.fetch(url, options);
const responseCode = response.getResponseCode();
if (responseCode !== 200) {
throw new Error(`API Error: ${responseCode}`);
}
const json = JSON.parse(response.getContentText());
return json.choices[0].message.content.trim();
}
// ========================================
// 高優先度の通知
// ========================================
function notifyHighPriority(formData, analysis) {
const adminEmail = Session.getActiveUser().getEmail();
const subject = '【要対応】アンケートで改善要望あり';
const body = `以下のアンケート回答に対応が必要です。
【回答者】${formData.name}(${formData.email})
【満足度】${formData.satisfaction}
【カテゴリ】${analysis.category}
【感情】${analysis.sentiment}
【要約】${analysis.summary}
【改善してほしい点(原文)】
${formData.improvements}
---
スプレッドシートで詳細を確認してください。`;
GmailApp.sendEmail(adminEmail, subject, body);
}
// ========================================
// エラー通知
// ========================================
function notifyError(error) {
const adminEmail = Session.getActiveUser().getEmail();
GmailApp.sendEmail(
adminEmail,
'【エラー】フォーム自動処理',
`エラーが発生しました:\n\n${error.toString()}`
);
}
// ========================================
// 既存回答の一括処理(手動実行用)
// ========================================
function processAllResponses() {
const sheet = SpreadsheetApp.getActiveSpreadsheet()
.getSheetByName(CONFIG.SHEET_NAME);
const lastRow = sheet.getLastRow();
// 2行目(ヘッダーの次)から処理
for (let row = 2; row <= lastRow; row++) {
const status = sheet.getRange(row, COLS.STATUS).getValue();
// 未処理の行のみ処理
if (status !== '処理済み') {
const formData = {
name: sheet.getRange(row, COLS.NAME).getValue(),
email: sheet.getRange(row, COLS.EMAIL).getValue(),
satisfaction: sheet.getRange(row, COLS.SATISFACTION).getValue(),
goodPoints: sheet.getRange(row, COLS.GOOD_POINTS).getValue(),
improvements: sheet.getRange(row, COLS.IMPROVEMENTS).getValue()
};
try {
const analysis = analyzeResponse(formData);
sheet.getRange(row, COLS.STATUS).setValue('処理済み');
sheet.getRange(row, COLS.CATEGORY).setValue(analysis.category);
sheet.getRange(row, COLS.SENTIMENT).setValue(analysis.sentiment);
sheet.getRange(row, COLS.PRIORITY).setValue(analysis.priority);
sheet.getRange(row, COLS.SUMMARY).setValue(analysis.summary);
sheet.getRange(row, COLS.PROCESSED_AT).setValue(new Date());
console.log(`Row ${row}: 処理完了`);
// API制限対策で1秒待機
Utilities.sleep(1000);
} catch (error) {
console.error(`Row ${row}: エラー - ${error.message}`);
sheet.getRange(row, COLS.STATUS).setValue('エラー');
}
}
}
console.log('一括処理完了');
}
// ========================================
// テスト用関数
// ========================================
function testAnalysis() {
const testData = {
name: 'テスト太郎',
email: 'test@example.com',
satisfaction: '3',
goodPoints: '商品の品質は良かった',
improvements: '配送に3日かかった。もっと早くしてほしい'
};
const result = analyzeResponse(testData);
console.log('分析結果:', JSON.stringify(result, null, 2));
}
2-3: APIキーを設定
- 左メニューの歯車アイコン「プロジェクトの設定」をクリック
- 「スクリプトプロパティを追加」をクリック
- プロパティ:
OPENAI_API_KEY、値: あなたのAPIキー - 「保存」をクリック
STEP 3: トリガーを設定
3-1: 自動実行トリガーの追加
- 左メニューの時計アイコン「トリガー」をクリック
- 「トリガーを追加」をクリック
- 以下の設定で保存
| 項目 | 設定値 |
|---|---|
| 実行する関数 | onFormSubmit |
| イベントのソース | スプレッドシートから |
| イベントの種類 | フォーム送信時 |
3-2: 動作テスト
- 関数選択で
testAnalysisを選択して実行 - 実行ログに分析結果が表示されれば成功
STEP 4: 応用カスタマイズ
カスタマイズ1: お問い合わせ振り分け
カテゴリに応じて担当者にメールを送る機能を追加できる。
// 担当者マッピング
const DEPARTMENT_MAP = {
'商品品質': 'quality@example.com',
'配送': 'shipping@example.com',
'価格': 'sales@example.com',
'サイト操作': 'tech@example.com'
};
function routeToDeprtment(category, formData, analysis) {
const email = DEPARTMENT_MAP[category] || 'support@example.com';
GmailApp.sendEmail(
email,
`【${analysis.priority}】${category}に関するフィードバック`,
`回答者: ${formData.name}\n要約: ${analysis.summary}`
);
}
カスタマイズ2: Slack通知
SlackのWebhook URLを使って通知する方法もある。
function notifySlack(message) {
const webhookUrl = PropertiesService.getScriptProperties()
.getProperty('SLACK_WEBHOOK_URL');
const payload = {
text: message,
username: 'フォーム処理Bot'
};
UrlFetchApp.fetch(webhookUrl, {
method: 'post',
contentType: 'application/json',
payload: JSON.stringify(payload)
});
}
カスタマイズ3: 分析プロンプトの調整
業種や目的に合わせてプロンプトを書き換えるとよい。
// 飲食店向けの例
const prompt = `以下の口コミを分析してください。
【カテゴリ】料理/接客/雰囲気/価格/清潔さ から選択
【再来店意向】高/中/低 で判定`;
トラブルシューティング
Q: JSONパースエラーが発生する
原因: ChatGPTがJSON以外の文字を含めて返答している。
対策: プロンプトに「JSONのみ出力」を明記する。それでも解決しない場合はフォールバック処理(コード内に実装済み)が動くので、致命的な問題にはならない。
Q: 処理が遅い
原因: API呼び出しに1〜3秒かかるため。
対策: 一括処理時は Utilities.sleep(1000) でレート制限を回避しつつ、バックグラウンドで実行する。
Q: 古い回答も処理したい
対策: processAllResponses() 関数を手動実行する。未処理の回答をすべて処理してくれる。
まとめ
GAS×ChatGPTでGoogleフォームの回答を自動処理する方法を見てきた。
実現できること:
- アンケート回答の自動分類(カテゴリ・感情・優先度)
- 回答内容の自動要約
- 優先度に応じた自動通知
- 既存回答の一括処理
次のステップ
- GAS×ChatGPTでデータ分析を自動化 – スプレッドシートデータの分析自動化
- 自動メール返信Botの作り方 – フォーム回答への自動返信
- GAS×ChatGPT実践ユースケース5選 – 他の自動化アイデア
関連記事
- GAS×ChatGPT連携の基礎 – APIキー取得から基本設定まで
- GASでスプレッドシートを自動化 – GASの基礎を学ぶ
- 業務自動化の第一歩 – プログラミング学習ロードマップ
コメント