問い合わせログからFAQ候補を作る時、繰り返し質問を見つけるだけでは不十分です。公開FAQにできる質問、社内だけで使う質問、根拠確認が終わるまで保留する質問を分けないと、個別条件や機密を含む回答を誤って公開する可能性があります。
この記事では、GoogleスプレッドシートとGASでFAQ候補の公開範囲を判定する最小構成を紹介します。AI APIは呼び出さず、候補整理を自動化し、最終承認は人が行います。
作るもの
inquiries から faq_candidates を作り、次の visibility を付けます。
-
PUBLIC_REVIEW: 公開可否を人が確認する候補 -
INTERNAL_REVIEW: 社内手順として確認する候補 -
HOLD: 根拠不足や機密などで保留する候補 -
PUBLIC/INTERNAL: 人が承認した後の確定値
シートの列
inquiries
received_at / inquiry_id / question_pattern / response_note
status / source_url / contains_personal_data / contains_contract_terms
faq_candidates
created_at / faq_candidate_id / source_inquiry_id / question_pattern
answer_outline / visibility / reason_code / source_url / owner
reviewed_by / reviewed_at / next_review_at / decision_note
問い合わせ本文を候補表へ複製せず、source_inquiry_id で元ログを参照します。
判定ルール
公開範囲をキーワードだけで確定させないことが重要です。GASは保留候補を作る補助として使います。
const VISIBILITY = {
PUBLIC_REVIEW: 'PUBLIC_REVIEW',
INTERNAL_REVIEW: 'INTERNAL_REVIEW',
HOLD: 'HOLD',
};
const HOLD_WORDS = ['個人情報', '認証情報', '返金', '契約', '機密'];
function classifyVisibility_(inquiry) {
const text = `${inquiry.questionPattern} ${inquiry.responseNote}`;
const hasHoldWord = HOLD_WORDS.some((word) => text.includes(word));
if (hasHoldWord || inquiry.containsPersonalData || inquiry.containsContractTerms) {
return {
visibility: VISIBILITY.HOLD,
reasonCode: 'human_review_required',
};
}
if (!inquiry.sourceUrl) {
return {
visibility: VISIBILITY.INTERNAL_REVIEW,
reasonCode: 'source_missing',
};
}
return {
visibility: VISIBILITY.PUBLIC_REVIEW,
reasonCode: 'public_candidate',
};
}
PUBLIC_REVIEW は公開済みではありません。公開FAQへ反映する処理は別の人間承認フローにします。
候補を追加する処理
同じ問い合わせを何度も候補化しないよう、元問い合わせIDを重複チェックします。
function appendFaqCandidate_(sheet, inquiry, now) {
const decision = classifyVisibility_(inquiry);
const id = `FAQ-${Utilities.formatDate(now, Session.getScriptTimeZone(), 'yyyyMMdd')}-${sheet.getLastRow()}`;
sheet.appendRow([
now,
id,
inquiry.inquiryId,
inquiry.questionPattern,
inquiry.responseNote,
decision.visibility,
decision.reasonCode,
inquiry.sourceUrl || '',
inquiry.owner || '',
'',
'',
'',
'',
]);
}
実際の運用では、IDを行番号だけに依存させず、UUIDや採番シートを使ってください。上のコードは構造を説明する最小例です。
承認時に必須にする項目
PUBLIC または INTERNAL へ変更する時は、次の値を同時に入力します。
-
reviewed_by: 確認者 -
reviewed_at: 確認日時 -
source_url: 回答の正本 -
next_review_at: 次回見直し日 -
decision_note: 公開範囲を決めた理由
どれかが空なら、GASの承認関数は停止させます。画面上の色付けだけでなく、状態変更の入口で検証することがポイントです。
function canApproveFaq_(row) {
return Boolean(
row.reviewedBy &&
row.reviewedAt &&
row.sourceUrl &&
row.nextReviewAt &&
row.decisionNote
);
}
AIを使う場合の境界
AIには、重複候補の整理や question_pattern と answer_outline の下書きを任せられます。ただし、問い合わせ本文をそのまま送らず、個人名、メールアドレス、注文番号などを除去します。
人が確認するのは、公開範囲、正本との一致、契約・料金・返金の条件、個人情報の除去、見直し日です。AIの出力を直接 PUBLIC にしないでください。
導入前チェック
- 公開・社内・保留の状態が別の値になっている
- 元問い合わせIDで追跡できる
- 機密・個人情報・契約条件は保留へ送る
- 公開承認に確認者と正本URLが必須である
- 見直し日と更新責任者がある
- 保留理由と次の確認先が残る
- FAQ表へ本文や個人情報を複製していない
まず10件程度で判定し、HOLD が多い理由を見ます。保留を減らすことを目標にせず、FAQ、フォーム、規程のどこを直せば公開可能になるかを改善先へ戻してください。
まとめ
FAQ候補の抽出と、公開範囲の承認は別の処理です。GASで候補の整理と保留判定を行い、正本・確認者・見直し日が揃ったものだけを人が承認する構成にすると、AIを使いながら公開境界を守れます。