1
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?

AI導入を見送った理由をGASで記録する:再検討条件と確認期限を台帳化

1
Posted at

AI導入の検討では、「今回は見送る」という判断が出ます。

しかし、理由を会議のチャットや担当者の記憶だけに置くと、数か月後に同じ調査をやり直したり、条件が変わっていないのに再び試したりします。見送りは失敗ではなく、今は安全に始められない条件を記録した状態です。

この記事では、GoogleスプレッドシートとGoogle Apps Script(GAS)で、AI導入候補の見送り理由、再検討条件、確認期限を台帳化します。AIが導入可否を決める仕組みではありません。候補の整理と期限切れの通知だけを補助し、再検討・承認・開始は人が行います。

作るもの

AI導入の判断を人間確認へ戻す境界

ADOPTION_DECISIONS シートに、次の列を作ります。

decision_id,work_id,work_name,decision,reason_code,impact_level,
owner_role,reconsider_condition,reconsider_due_at,source_ref,status,
last_reviewed_at,next_action

decision は次の3値に固定します。

TRIAL      # 条件付きで小さく試す
HOLD       # 条件がそろうまで見送る
NOT_NOW    # 現時点では対象外。再検討条件を残す

status は台帳の運用状態です。HOLD や NOT_NOW の案件を、期限到来だけで TRIAL へ変えてはいけません。

OPEN -> DUE_FOR_REVIEW -> HUMAN_REVIEW -> RECONSIDERED
  |                         |
  +----> ARCHIVED           +----> HOLD / NOT_NOW / TRIAL

見送り理由をコードにする

自由記述だけでは、後から似た案件を集計できません。まず、理由を少数のコードへ寄せます。

reason_code 例 再検討時に見る条件
MISSING_SOURCE 参照するFAQや正本がない 正本の版と責任者が決まったか
MISSING_REVIEWER 人間確認の役割が空いている 確認者と代理が決まったか
HIGH_IMPACT 契約・料金・個人情報に影響する 対象範囲を限定し、責任者が承認したか
MISSING_INPUT フォームの必須情報が足りない 不足項目を受付で取得できるか
NO_RECOVERY 失敗時の手動手順がない 手動の戻し先と停止条件があるか

見送り理由はAIへ渡すプロンプトではなく、次の確認者が判断を再開するための記録です。顧客本文や個人情報を台帳へコピーせず、source_ref には権限管理された原本の参照先だけを保存します。

シートを準備する

1行目へ見出しを入力し、サンプルとして1件だけ追加します。

decision_id | work_id | work_name       | decision | reason_code    | impact_level | owner_role       | reconsider_condition                 | reconsider_due_at      | source_ref              | status | last_reviewed_at | next_action
D-001       | W-014   | 問い合わせ返信 | HOLD     | MISSING_SOURCE | medium       | support_manager  | FAQ v3と確認者を登録できたら再検討 | 2026-10-20T09:00:00+09:00 | drive://ai-review/D-001 | OPEN   |                  | 正本の担当者を確認

日付はシートのタイムゾーンを Asia/Tokyo にそろえます。期限は「そろそろ見る」ではなく、確認担当者が行動できる日時として持たせます。

GASで入力値を検査する

まず、見送り記録の必須項目と状態遷移を検査します。期限切れを自動で再開させないことが重要です。

const SHEET_NAME = 'ADOPTION_DECISIONS';
const REQUIRED_HEADERS = [
  'decision_id', 'work_id', 'work_name', 'decision', 'reason_code',
  'impact_level', 'owner_role', 'reconsider_condition',
  'reconsider_due_at', 'source_ref', 'status', 'last_reviewed_at',
  'next_action'
];

function validateDecisionLedger() {
  const sheet = SpreadsheetApp.getActive().getSheetByName(SHEET_NAME);
  if (!sheet) throw new Error(SHEET_NAME + ' sheet is missing');

  const values = sheet.getDataRange().getValues();
  const headers = values.shift().map(String);
  const index = Object.fromEntries(headers.map((name, i) => [name, i]));
  const missing = REQUIRED_HEADERS.filter((name) => index[name] === undefined);
  if (missing.length) throw new Error('Missing headers: ' + missing.join(', '));

  const errors = [];
  values.forEach((row, offset) => {
    if (row.every((cell) => cell === '')) return;
    const line = offset + 2;
    const decision = String(row[index.decision] || '');
    const status = String(row[index.status] || '');
    const dueAt = row[index.reconsider_due_at];

    if (!['TRIAL', 'HOLD', 'NOT_NOW'].includes(decision)) {
      errors.push('row ' + line + ': invalid decision');
    }
    if (!['OPEN', 'DUE_FOR_REVIEW', 'HUMAN_REVIEW', 'RECONSIDERED', 'ARCHIVED'].includes(status)) {
      errors.push('row ' + line + ': invalid status');
    }
    if (decision !== 'TRIAL' && !String(row[index.reconsider_condition] || '').trim()) {
      errors.push('row ' + line + ': reconsider_condition is required');
    }
    if (!dueAt || Number.isNaN(new Date(dueAt).getTime())) {
      errors.push('row ' + line + ': reconsider_due_at is required');
    }
  });

  if (errors.length) throw new Error(errors.join('\n'));
  return {ok: true, rows: values.length};
}

期限到来をレビュー待ちへ戻す

期限を過ぎた案件を DUE_FOR_REVIEW へ移す処理です。これは再検討の通知であり、導入許可ではありません。

function markDueForReview() {
  const sheet = SpreadsheetApp.getActive().getSheetByName(SHEET_NAME);
  const values = sheet.getDataRange().getValues();
  const headers = values.shift().map(String);
  const index = Object.fromEntries(headers.map((name, i) => [name, i]));
  const now = new Date();
  let count = 0;

  values.forEach((row, offset) => {
    const decision = String(row[index.decision] || '');
    const status = String(row[index.status] || '');
    const dueAt = new Date(row[index.reconsider_due_at]);
    const shouldReview = ['HOLD', 'NOT_NOW'].includes(decision)
      && status === 'OPEN'
      && dueAt.getTime() <= now.getTime();

    if (shouldReview) {
      sheet.getRange(offset + 2, index.status + 1).setValue('DUE_FOR_REVIEW');
      sheet.getRange(offset + 2, index.next_action + 1)
        .setValue('条件を確認して人間レビューへ戻す');
      count += 1;
    }
  });
  return count;
}

時間主導トリガーを毎朝実行しても、DUE_FOR_REVIEW から先の状態は変更しません。メール通知を追加する場合も、本文へ顧客情報を含めず、decision_id と確認先だけを送ります。

人間レビューのチェックリスト

期限が来たら、次の順番で人が確認します。

  • 見送り理由はまだ有効か
  • 再検討条件は実際にそろったか
  • 対象業務・入力情報・参照正本に変更はないか
  • 通常・境界・停止の例を1件ずつ確認したか
  • 人間確認者と代理担当が今も対応可能か
  • 失敗時に手動へ戻す場所があるか
  • 今回の判断理由と次回見直し日を記録したか

条件が足りない場合は HOLD または NOT_NOW のまま期限を更新します。条件がそろっても、いきなり本番範囲を広げず、TRIAL として対象件数を限定してください。契約、返金、料金、個人情報訂正、強い苦情などはこの台帳から自動承認しません。

まとめ

見送りの台帳に必要なのは、「なぜ止めたか」だけではありません。

  1. 見送り理由をコード化する
  2. 再検討条件を一文で残す
  3. 期限到来をレビュー待ちへ戻す
  4. 人間が条件を確認して次の状態を決める
  5. 判断理由と次回見直し日を記録する

この構成なら、GASは期限の検知と一覧化までを担当し、導入可否や業務影響の判断は人に残せます。AI導入を進めることだけでなく、今は進めない理由を次の担当者へ渡すことも、運用を続けるための記憶になります。

1
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
1
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?