MENU

GAS×ChatGPTでGoogleフォーム回答を自動処理|分類・要約・返信を自動化

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フォームの回答を自動処理する方法を見てきた。

実現できること:

  • アンケート回答の自動分類(カテゴリ・感情・優先度)
  • 回答内容の自動要約
  • 優先度に応じた自動通知
  • 既存回答の一括処理

次のステップ

関連記事

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

コメント

コメントする

目次