問い合わせの一次分類や返信案作成をAIに任せる時、先に決めるべきなのはプロンプトではありません。AIの出力を誰が確認し、どの条件で担当者・責任者へ戻すかです。
本稿では、Googleフォームとスプレッドシートに問い合わせを集め、GASで「通常」「確認」「停止」を振り分ける最小構成を実装します。AI呼び出しは任意の後段に置き、顧客への自動送信は行いません。
この記事で作るもの
- 問い合わせを1行1件で記録するシート
- 影響度・情報不足・契約/返金などの条件から確認先を決めるGAS
- AIの返信案を作る前に、人へ戻す判断ログ
- 担当者不在時にも一人で決めないための停止条件
判断の出力はあくまで候補です。顧客への送信、返金、契約変更、個人情報を含む処理は人が確定します。
判定ルールを先に固定する
入力された文章をそのままAIへ渡して判定させるのではなく、受付時点で業務上の条件を記録します。
| 項目 | 例 | 該当時の扱い |
|---|---|---|
| 影響度 | 通常 / 重要 / 重大 | 重要以上は確認 |
| 情報不足 | あり / なし | ありは追加質問へ戻す |
| 契約・料金 | あり / なし | ありは責任者確認 |
| 個人情報 | あり / なし | 伏せ字確認。自動送信しない |
| 苦情・事故 | あり / なし | 即時停止して担当者へ戻す |
| 回答根拠 | FAQの項目ID | 空欄なら確認 |
「AIが自信ありと返した」はエスカレーション条件を解除する根拠にしません。業務条件と根拠の有無をルールにします。
シートの最小構成
INQUIRIES シートに次の列を作ります。
case_id,received_at,category,impact,missing_info,contract_flag,
personal_info_flag,complaint_flag,faq_id,reviewer,status,reason,next_action
impact は normal、important、critical の3値に限定します。自由記述だけにすると、担当者ごとに基準がずれます。
GASで確認先を決める
次の関数はAIを呼び出しません。受付後に必ず実行し、AIへ渡す前の業務ゲートにします。
const STATUS = Object.freeze({
DRAFT: 'DRAFT',
HUMAN_REVIEW: 'HUMAN_REVIEW',
STOPPED: 'STOPPED',
READY_FOR_DRAFT: 'READY_FOR_DRAFT',
});
function classifyInquiry(row) {
const reasons = [];
if (row.complaint_flag === true || row.impact === 'critical') {
reasons.push('苦情または重大影響');
return { status: STATUS.STOPPED, reviewer: '責任者', reasons };
}
if (row.contract_flag === true) reasons.push('契約・料金の確認が必要');
if (row.personal_info_flag === true) reasons.push('個人情報の扱いを確認');
if (row.missing_info === true) reasons.push('追加情報が必要');
if (!row.faq_id) reasons.push('回答根拠が未登録');
if (row.impact === 'important') reasons.push('重要影響');
if (reasons.length > 0) {
return { status: STATUS.HUMAN_REVIEW, reviewer: '担当者', reasons };
}
return { status: STATUS.READY_FOR_DRAFT, reviewer: '担当者', reasons: [] };
}
実際のシート値は文字列になるため、チェックボックス列を読む場合は次のように正規化します。
function toBoolean(value) {
return value === true || value === 'TRUE' || value === 'true';
}
function classifySheetRow(values, columns) {
const row = {
impact: values[columns.impact],
missing_info: toBoolean(values[columns.missing_info]),
contract_flag: toBoolean(values[columns.contract_flag]),
personal_info_flag: toBoolean(values[columns.personal_info_flag]),
complaint_flag: toBoolean(values[columns.complaint_flag]),
faq_id: values[columns.faq_id],
};
return classifyInquiry(row);
}
トリガー処理では「送信」まで進めない
フォーム送信トリガーは、分類と記録だけを行います。返信案の作成を追加する場合も、READY_FOR_DRAFT の行だけを対象にし、送信処理とは別のメニューに分けます。
function onFormSubmit(event) {
const sheet = event.range.getSheet();
const values = sheet.getRange(event.range.getRow(), 1, 1, sheet.getLastColumn())
.getValues()[0];
const columns = getColumnIndexMap(sheet);
const result = classifySheetRow(values, columns);
writeDecision(sheet, event.range.getRow(), result);
// ここでは外部API呼び出し、顧客送信、契約変更を行わない。
}
例外時に戻れる判断ログを残す
最低限、次の値を同じ行か別の DECISION_LOG シートへ保存します。
case_id: INQ-20260912-001
status: HUMAN_REVIEW
reason:
- 回答根拠が未登録
reviewer: 担当者
next_action: FAQ候補を確認してから返信案を作る
decided_at: 2026-09-12T10:15:00+09:00
reason を「AIが不安定だった」の一言で終わらせず、FAQ、フォーム項目、CRM、承認ルールのどこを直すかまで記録します。判断ログは監視のためだけでなく、次の担当者が同じ迷いを繰り返さないための改善入口です。
本番前チェックリスト
- 重大影響・苦情・契約/料金を停止条件にした
- 個人情報をAIへ送る前の伏せ方を決めた
- FAQの項目IDなど回答根拠を記録できる
- 担当者不在時の代理確認者と、代理で決めない条件がある
-
READY_FOR_DRAFTと顧客送信の処理を分離した - 例外・復旧・最終判断をログへ残せる
- テストデータに実顧客情報を使っていない
まとめ
問い合わせ対応のAI化は、回答を速くする処理から始めると、確認責任が曖昧になりやすいです。先に「どこまで進めるか」「どこで止めるか」「誰へ戻すか」を固定し、GASはその境界を機械的に記録する役割に絞ります。
Miraigentでは、AI導入前に対象業務・人間確認・例外・判断ログ・改善先を一緒に棚卸ししています。導入可否を決める前に、まず直近の問い合わせ1件をこの表へ当てはめてください。