AI導入の対象業務や確認ルールを変更した後、問い合わせが急に増えることがあります。
この時、届いた順に回答を増やすだけでは、FAQ・手順書・承認ルールのどこが不足しているのか分かりません。この記事では、GoogleスプレッドシートとGASで、変更後の問い合わせを5つの分類へ戻し、REPLY、HOLD、STOP の次の処理を記録する最小構成を作ります。
扱うのは問い合わせの整理と確認キューの作成です。AIによる自動返信、顧客への自動送信、契約や料金の自動判断は扱いません。
作るもの
CHANGE_INQUIRIES シートに、変更後の問い合わせを1件1行で記録します。
inquiry_id,received_at,question_type,impact_scope,source_of_truth,reviewer,return_route,decision,next_action,status
今回の5分類は次のとおりです。
| 項目 | 記録する内容 | 空欄の時の扱い |
|---|---|---|
question_type |
操作、入力、対象範囲、承認、例外のどれか | HOLD |
impact_scope |
社内だけか、顧客・契約・公開情報へ影響するか | HOLD |
source_of_truth |
最新のFAQ、手順書、フォーム、判断ログ | HOLD |
reviewer |
通常・境界・停止を確認する役割 | HOLD |
return_route |
保留、手動対応、変更前の手順などの戻し先 | HOLD |
decision は REPLY、HOLD、STOP の3値に限定します。REPLY は人が確認して案内できる状態であり、送信を自動許可する意味ではありません。
事前準備
スプレッドシートの1行目に、次のヘッダーを作ります。
inquiry_id,received_at,question_type,impact_scope,source_of_truth,reviewer,return_route,decision,next_action,status
変更後の問い合わせを取り込む前に、question_type、decision、status の許可値をチームで決めます。値が自由入力のままだと、「承認」「確認待ち」「review」のような表記揺れが発生し、集計結果をそのまま判断へ使えません。
5分類と3つの処理を固定する
const INQUIRY_TRIAGE_CONFIG = Object.freeze({
sheetName: 'CHANGE_INQUIRIES',
questionTypes: ['OPERATION', 'INPUT', 'SCOPE', 'APPROVAL', 'EXCEPTION'],
decisions: ['REPLY', 'HOLD', 'STOP'],
statuses: ['NEW', 'TRIAGED', 'REVIEWED', 'CLOSED'],
required: [
'inquiry_id', 'received_at', 'question_type', 'impact_scope',
'source_of_truth', 'reviewer', 'return_route', 'decision', 'next_action',
],
});
function readChangeInquiries_() {
const sheet = SpreadsheetApp.getActiveSpreadsheet()
.getSheetByName(INQUIRY_TRIAGE_CONFIG.sheetName);
if (!sheet) throw new Error('CHANGE_INQUIRIES sheet is missing');
const values = sheet.getDataRange().getValues();
if (values.length < 2) return [];
const headers = values[0].map(String);
return values.slice(1).map((row, index) => {
const item = { rowNumber: index + 2 };
headers.forEach((header, column) => { item[header] = row[column]; });
return item;
});
}
function validateInquiry_(item) {
INQUIRY_TRIAGE_CONFIG.required.forEach((key) => {
if (!String(item[key] || '').trim()) {
throw new Error(`${key} is required: ${item.inquiry_id || 'unknown'}`);
}
});
const type = String(item.question_type).trim().toUpperCase();
const decision = String(item.decision).trim().toUpperCase();
const status = String(item.status || 'NEW').trim().toUpperCase();
if (!INQUIRY_TRIAGE_CONFIG.questionTypes.includes(type)) {
throw new Error(`unsupported question_type: ${item.inquiry_id}`);
}
if (!INQUIRY_TRIAGE_CONFIG.decisions.includes(decision)) {
throw new Error(`unsupported decision: ${item.inquiry_id}`);
}
if (!INQUIRY_TRIAGE_CONFIG.statuses.includes(status)) {
throw new Error(`unsupported status: ${item.inquiry_id}`);
}
return { ...item, question_type: type, decision, status };
}
必須項目の検査は入力漏れを見つける補助です。個人情報、契約条件、料金、苦情の有無をコードだけで安全に判断できるわけではありません。
問い合わせを分類する
まずは問い合わせ本文をAIへ渡さず、担当者が5項目を記録します。分類の目的は回答を生成することではなく、どの正本と確認者へ戻すかを明らかにすることです。
function classifyInquiry_(item) {
const values = [
item.question_type,
item.impact_scope,
item.source_of_truth,
item.reviewer,
item.return_route,
].map((value) => String(value || '').trim());
if (values.some((value) => value === '')) return 'HOLD';
const impact = String(item.impact_scope).toUpperCase();
const type = String(item.question_type).toUpperCase();
if (impact.includes('CUSTOMER') || impact.includes('CONTRACT')
|| type === 'EXCEPTION') {
return 'STOP';
}
return 'REPLY';
}
function triageAllChangeInquiries() {
const items = readChangeInquiries_();
const result = items.map((item) => {
try {
const valid = validateInquiry_(item);
const decision = classifyInquiry_(valid);
return [valid.inquiry_id, valid.question_type, decision, 'OK'];
} catch (error) {
return [item.inquiry_id || '', 'UNKNOWN', 'HOLD', error.message];
}
});
Logger.log(JSON.stringify(result));
return result;
}
STOP は危険だと断定するためのラベルではありません。顧客・契約・例外が関わり、通常の案内へ流す前に人が確認する境界を示します。HOLD は情報不足や正本不明で、回答を急がず不足項目を埋める状態です。
同じ質問をFAQ候補へ戻す
同じ question_type と source_of_truth の組み合わせが続く場合、個別回答を増やす前にFAQ候補として記録します。
function buildFaqCandidates_(items) {
const counts = new Map();
items.forEach((item) => {
const key = [item.question_type, item.source_of_truth].join('|');
counts.set(key, (counts.get(key) || 0) + 1);
});
return [...counts.entries()]
.filter(([, count]) => count >= 2)
.map(([key, count]) => ({
key,
count,
nextAction: '人が質問文・条件・例外・戻し先を確認してFAQ候補へ記録',
}));
}
件数だけでFAQ化を決めないでください。同じ質問が増えていても、回答の根拠が更新中なら公開を保留します。FAQには「通常時の答え」だけでなく、「情報不足ならどこへ戻すか」「顧客や契約に関わる場合は誰が見るか」も残します。
変更後の確認手順
- 問い合わせを10件だけ選び、本文ではなく業務IDと分類項目を記録する
-
OPERATION、INPUT、SCOPE、APPROVAL、EXCEPTIONの5分類へ分ける - 正本、確認者、戻し先がない行を
HOLDにする - 顧客・契約・例外に関係する行を
STOPにして人へ戻す - 同じ質問が2件以上ある場合はFAQ候補として別に記録する
- FAQ・手順書・フォームのどこを更新するか決め、更新後に1件を再確認する
問い合わせの分類を自動実行する場合でも、最初はテスト用の匿名データで動作を確認します。本番の問い合わせ本文、氏名、メールアドレス、契約番号、認証情報をテストコードやログへ貼り付けないでください。
公開・運用前チェックリスト
- 変更前と変更後の対象範囲が一文で分かる
- 問い合わせを5分類のどれかへ戻せる
- 最新のFAQ・手順書・フォームなど正本がある
- 通常・境界・停止それぞれの確認者が分かる
-
情報不足時の
HOLDと戻し先がある -
顧客・契約・例外を
STOPへ戻せる - 同じ質問をFAQ候補へ変換する条件がある
- 顧客固有情報、認証情報、未確認の約束を記事やテストへ入れていない
- GASの判定結果だけで返信送信やFAQ公開を完了扱いにしていない
まとめ
AI導入後に問い合わせが増えた時、先に回答数を増やすのではなく、次の5項目へ戻します。
- 質問の種類
- 影響範囲
- 最新の正本
- 確認者
- 保留・手動対応の戻し先
GASは分類値や必須項目の揺れを検出する補助に使い、顧客への返信、契約条件の解釈、FAQの公開判断は人が行います。問い合わせが増えた事実を、回答者の負担だけでなく、案内と正本を改善する運用ログへ変えるのがこの構成の目的です。
Miraigentでは、AI導入をツール選定だけでなく、変更後に「次の人がどこへ戻れるか」まで含めて整理しています。
