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?

AIエージェントの実行前チェックをPythonで作る

0
Posted at

Anthropic が 2026年6月30日に出した Claude Sonnet 5 の発表では、ブラウザやターミナルなどのツールを使い、計画を立てて自律的に動ける Sonnet だと説明されていました。価格も 2026年8月31日までは入力 100万トークンあたり2ドル、出力 100万トークンあたり10ドル。通常価格でも入力3ドル、出力15ドルです。

これが意味するのは、AIエージェントが「文章を作る道具」から「業務システムを触る道具」に寄ってきた、ということです。

営業や企画の現場だと、ここで一気に危なくなります。商談メモを整理するだけなら、間違っても人が直せます。CRMのステージを更新する。顧客にメールを送る。会議を入れる。ここまで任せるなら、モデルの賢さより先に、実行前チェックを置くべきです。

僕は「AIが自信ありと言ったら通す」運用はやめたほうがいいと思っています。自信度は参考情報であって、承認ではありません。ここ地味に効きます。

自律実行で怖いのは、間違いより「そのまま実行」

Claude Sonnet 5 の発表には、Salesforce のアカウント階層を更新し、企業向けの告知メール送信まで終えた、という早期利用者の話も載っていました。すごいです。すごいけれど、現場でそのまま真似すると少し怖い。

たとえば、AIが商談メモを読んでこう判断したとします。

  • 見込み度が上がったのでCRMを更新する
  • 提案資料を顧客へ送る
  • 次回会議を設定する

この3つは、全部「業務が進む」行動です。ただし失敗したときの重さが違います。CRM更新は社内で戻せるかもしれない。メール送信は相手に届きます。会議招待も相手のカレンダーを汚します。

だから、AIエージェントに任せる前に、行動を種類で分けます。下書きは通す。タスク作成は警告で済ませる。外部送信とCRM更新は、人の承認がない限り止める。

モデルの性能表より、先にここを決めます。

Pythonで実行前チェックを作る

以下は、AIが出した「実行予定リスト」を検査するだけの最小コードです。APIは叩きません。まずはローカルで、どの行動を止めるかだけ見ます。

ALLOWED_TYPES = {
    "draft_email": "low",
    "create_task": "low",
    "schedule_meeting": "medium",
    "update_crm": "high",
    "send_email": "high",
}

SAMPLE_ACTIONS = [
    {
        "id": "a-001",
        "action_type": "draft_email",
        "target": "customer@example.com",
        "confidence": 0.91,
        "reason": "商談後のお礼メールの下書きだけを作る",
        "payload": {"subject": "本日のお打ち合わせのお礼"},
    },
    {
        "id": "a-002",
        "action_type": "send_email",
        "target": "customer@example.com",
        "confidence": 0.94,
        "reason": "提案資料を送付する",
        "payload": {"subject": "提案資料をお送りします"},
    },
    {
        "id": "a-003",
        "action_type": "update_crm",
        "target": "crm:account:ACME",
        "confidence": 0.88,
        "approved_by": "kudo",
        "evidence": "顧客が次回から部門横断で検討すると発言",
        "reason": "商談ステージを一段進める",
        "payload": {"stage": "検討中", "next_action": "現行運用の確認"},
    },
    {
        "id": "a-004",
        "action_type": "create_task",
        "target": "sales-team",
        "confidence": 0.62,
        "reason": "予算の確認が必要",
        "payload": {"task": "先方の予算確定時期を確認する"},
    },
    {
        "id": "a-005",
        "action_type": "archive_contract",
        "target": "contract:old",
        "confidence": 0.8,
        "reason": "古い契約書に見える",
        "payload": {},
    },
]

def validate_action(action):
    errors = []
    warnings = []

    action_type = action.get("action_type")
    risk = ALLOWED_TYPES.get(action_type)
    if risk is None:
        errors.append(f"unknown action_type: {action_type}")
        risk = "unknown"

    if not action.get("target"):
        errors.append("target is required")

    confidence = action.get("confidence")
    if not isinstance(confidence, (int, float)) or not 0 <= confidence <= 1:
        errors.append("confidence must be a number from 0 to 1")
    elif confidence < 0.75:
        warnings.append(f"low confidence: {confidence}")

    if risk == "high":
        if not action.get("approved_by"):
            errors.append("high-risk action needs approved_by")
        if not action.get("evidence"):
            errors.append("high-risk action needs evidence")

    if not action.get("reason"):
        warnings.append("reason is missing")

    if errors:
        status = "BLOCK"
    elif warnings:
        status = "WARN"
    else:
        status = "OK"

    return status, errors, warnings

summary = {"OK": 0, "WARN": 0, "BLOCK": 0}

for action in SAMPLE_ACTIONS:
    status, errors, warnings = validate_action(action)
    summary[status] += 1
    notes = errors or warnings or ["passed"]
    print(f"{status:5} {action['id']} {action['action_type']}")
    for note in notes:
        print(f"  - {note}")

print("summary:", summary)

手元で実行した出力です。

OK    a-001 draft_email
  - passed
BLOCK a-002 send_email
  - high-risk action needs approved_by
  - high-risk action needs evidence
OK    a-003 update_crm
  - passed
WARN  a-004 create_task
  - low confidence: 0.62
BLOCK a-005 archive_contract
  - unknown action_type: archive_contract
summary: {'OK': 2, 'WARN': 1, 'BLOCK': 2}

見る場所は2つです。

send_email は confidence が 0.94 でも止めています。理由は簡単で、外部送信だからです。自信度が高いことと、送っていいことは別です。ここを混ぜると事故ります。

archive_contract も止めています。許可した行動リストにないからです。AIがそれっぽい名前で行動を作ってきても、システム側が知らない行動は通さない。これはかなり大事です。あとから「そんな操作をするつもりではなかった」と言っても遅いので。

承認ゲートはコードに置く

よくある対策に「勝手に送信しないでください」とプロンプトへ書く方法があります。もちろん書いたほうがいいです。でも、それだけでは足りません。

プロンプトはお願いです。チェックコードは仕組みです。

営業メールで考えると分かりやすいです。AIに「送信前に確認して」と言っても、ツール呼び出しの設計が甘ければ送れてしまうことがあります。逆に、送信APIの前にこの検査を必ず挟めば、AIが何と言っても止まります。

最低限、僕ならこの4つを分けます。

種類 扱い
下書き メール文面を作る 原則OK
社内タスク ToDoを作る 低 confidence なら警告
社内データ更新 CRMのステージ変更 承認者と根拠が必要
外部への実行 メール送信、会議招待 承認者と根拠が必要

細かい点数設計から始めなくていいです。最初は「外に出るか」「社内の記録を書き換えるか」だけで十分です。この2つを止められるだけで、AIエージェント導入の怖さはだいぶ下がります。

実務に入れるならログも残す

もう一つ、検査結果は必ずログに残したほうがいいです。

誰が承認したか。AIは何を根拠にしたか。どの action_id が止まったか。あとで揉めるのは、だいたいここです。「AIがやりました」では説明になりません。社内の運用では、AIは処理の一部です。担当者は承認した人間です。

上のサンプルなら、approved_byevidence を必須にしています。ここは業務ごとに変えてください。見積もりなら金額の根拠。契約なら条項番号。営業なら顧客発言の引用。根拠が空なら、実行しない。

面倒に見えますが、AIに仕事を任せるならこの面倒を先に置いたほうが安いです。あとから誤送信の説明をするほうが、ずっと高くつきます。

で、現場でどう使うか

Claude Sonnet 5 のようなモデルが安く強くなるほど、現場では「どこまで自動でやるか」の線引きが曖昧になります。だから、モデル選びの前に行動の棚卸しをします。

まず、AIに任せたい業務を1つ選びます。商談後フォロー、問い合わせ一次対応、会議設定あたりで十分です。次に、AIが取り得る行動を draft_emailsend_email のように名前で並べます。最後に、高リスク行動だけ承認者と根拠を必須にする。

新しいモデルのニュースは追ったほうがいいです。ただ、現場で効くのは「どのモデルが賢いか」より「賢いモデルが動く前に、どこで止まるか」です。ここをコードにしておくと、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?