0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

問い合わせログのFAQ候補を公開・社内・保留に分けるGAS実装

0
Posted at

問い合わせログから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を使いながら公開境界を守れます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?