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?

問い合わせの回答期限変更をGASで記録する:AI返信前の再計画ログ

0
Posted at

問い合わせ対応でAI要約や返信下書きを使っても、回答期限を変更した理由が残っていなければ、次の担当者は「なぜ遅れているのか」を最初から調べることになります。

この記事では、GoogleスプレッドシートとGoogle Apps Script(GAS)で、回答期限の変更を履歴として記録する最小構成を作ります。期限の自動延長、顧客への自動送信、AIによる優先度の最終判断は行いません。人間が決めた変更だけを台帳へ残します。

作るもの

問い合わせの状態と人間確認の境界

INQUIRIESを現在値の正本、DUE_DATE_CHANGESを変更履歴とします。

シート 最低限の列 役割
INQUIRIES inquiry_id, status, due_at, owner, source_version 現在の期限と担当
DUE_DATE_CHANGES change_id, inquiry_id, before_due_at, after_due_at, reason_code, decided_by, changed_at 期限変更の根拠

理由コードは自由記述だけにせず、例えば waiting_customerwaiting_internalscope_changedhuman_review のように固定します。契約、料金、返金、苦情、個人情報を含む変更は、必ず人間確認として扱います。

GASの実装

現在値の更新と履歴の追加を同じ処理で行います。INQUIRIESの1行目と履歴シートの1行目は見出しです。

const SHEETS = {
  inquiries: 'INQUIRIES',
  changes: 'DUE_DATE_CHANGES',
};

function changeDueDate(inquiryId, nextDueAt, reasonCode, decidedBy) {
  if (!inquiryId || !nextDueAt || !reasonCode || !decidedBy) {
    throw new Error('inquiryId, nextDueAt, reasonCode, decidedBy are required');
  }

  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const inquirySheet = ss.getSheetByName(SHEETS.inquiries);
  const changeSheet = ss.getSheetByName(SHEETS.changes);
  const values = inquirySheet.getDataRange().getValues();
  const headers = values[0];
  const col = Object.fromEntries(headers.map((name, i) => [name, i]));
  const rowIndex = values.findIndex((row, i) => i > 0 && row[col.inquiry_id] === inquiryId);
  if (rowIndex < 1) throw new Error(`Inquiry not found: ${inquiryId}`);

  const before = values[rowIndex][col.due_at];
  const after = new Date(nextDueAt);
  if (Number.isNaN(after.getTime())) throw new Error('nextDueAt must be a valid date');
  if (before && new Date(before).getTime() === after.getTime()) return { changed: false };

  const changeId = `${inquiryId}:${after.toISOString()}`;
  const existing = changeSheet.getDataRange().getValues()
    .some((row, i) => i > 0 && row[0] === changeId);
  if (existing) return { changed: false, duplicate: true };

  inquirySheet.getRange(rowIndex + 1, col.due_at + 1).setValue(after);
  changeSheet.appendRow([
    changeId, inquiryId, before || '', after, reasonCode, decidedBy, new Date(),
  ]);
  return { changed: true, changeId };
}

変更を許可する前のチェック

期限を延ばすこと自体を自動化せず、次の順番で人が確認します。

  1. 現在の期限、担当者、正本の版を確認する
  2. 変更理由を固定コードから選ぶ
  3. 新しい期限と次の確認者を決める
  4. changeDueDate()を実行し、履歴IDを記録する
  5. 次回レビューで期限変更が繰り返されていないか確認する

reason_codeが空、担当者が不在、期限が過去、または正本の版が不明な場合は変更せず、human_reviewキューへ戻します。履歴があるからといって、変更内容が正しいことを自動保証するものではありません。

テスト観点

  • 存在しない問い合わせIDは更新されない
  • 同じ問い合わせID・同じ期限の変更は二重登録されない
  • 不正な日時はエラーになる
  • 理由コードと決定者が空の変更は拒否される
  • 変更前の期限と変更後の期限を後から比較できる

AIに渡すのは、承認済みの業務範囲に限ります。期限変更の理由や履歴に、氏名、メールアドレス、認証情報、決済情報、問い合わせ本文を複製して保存しないでください。必要な原文は正本を参照します。

まとめ

回答期限は、現在値だけでなく変更理由と決定者まで残して初めて引き継げます。AIへ返信下書きを任せる前に、期限変更を人間の判断として記録できれば、遅延の再発や同じ調査のやり直しを減らせます。

Miraigentでは、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?