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?

Gemini 3.8 Flashに商談候補を渡す前の除外判定Python

0
Posted at

Gemini 3.8 Flashに商談候補を渡す前の除外判定Python

商談候補をAIに並べ替えさせる前に、受注済み・失注・連絡停止の行を落とせていますか。

ここをAIの判断に混ぜるのは、僕は悪手だと思っています。営業の優先順位づけは便利でも、「連絡してよい相手か」は社内ルールで決める話です。モデルが新しく、速く、安くなっても、この順番は変わりません。

Gemini 3.8 Flashが9月2日に発表されました。安いモデルほど、商談メモをまとめて渡しやすい。だからこそ先に、渡す候補を機械的に絞ります。下のPythonは、スプレッドシートから書き出したCSVを二つに分けるだけです。AIに渡せる行はeligible.csv、人が確認する行はreview.csvへ出します。

今朝、12社分のサンプルで動かしたとき、失注の一行が候補側に残っていました。ステータスが失注 と末尾に空白を含んでいたのが原因です。表示は同じでもCSVでは別の文字列。こういう地味なズレを、モデルの判断ミスとして片付けると後で困ります。

先に決めるのは順位ではなく、連絡対象

今回の判定は次の通りです。

  • 受注、失注、保留は候補から除く
  • 連絡許可がnoなら候補から除く
  • 担当者、次回アクション日、案件メモのどれかが空なら人の確認へ回す
  • それ以外だけをAIへ渡す

ここで大事なのは、review.csvを捨てないことです。日付が抜けた案件を「対象外」にすると、担当者が入力を忘れた大事な案件まで消えます。対象外と、情報不足は別にしておく。営業管理ではこの区別が効きます。

商談CSVを連絡可否と必要情報の有無で、対象外、review.csv、eligible.csvに分ける流れ

入力CSVは、少なくとも次の列を持たせます。列名はコードと同じにしてください。

company,stage,contact_allowed,next_action_date,owner,deal_summary
星雲商事,提案中,yes,2026-09-05,工藤,展示会後に価格表を送付済み
北浜製作所,失注,yes,2026-09-06,工藤,予算が合わず見送り
青葉物流,商談中,no,2026-09-04,工藤,連絡停止の申し出あり
東雲電機,商談中,yes,,工藤,稟議の確認待ち
港南設備,商談中,yes,2026-09-08,工藤,導入時期は10月希望

CSVをeligibleとreviewへ分けるPython

外部ライブラリは使いません。macOSでもWindowsでも、Python 3.9以降なら動きます。

import csv
import sys
from pathlib import Path

REQUIRED = {
    "company", "stage", "contact_allowed",
    "next_action_date", "owner", "deal_summary",
}
EXCLUDED_STAGES = {"受注", "失注", "保留"}
NEEDED_FOR_AI = {"company", "next_action_date", "owner", "deal_summary"}

def clean(value):
    return (value or "").strip()

def write_csv(path, fieldnames, rows):
    with path.open("w", newline="", encoding="utf-8-sig") as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        writer.writeheader()
        writer.writerows(rows)

def main(source_name):
    source = Path(source_name)
    with source.open(newline="", encoding="utf-8-sig") as f:
        reader = csv.DictReader(f)
        columns = set(reader.fieldnames or [])
        missing = REQUIRED - columns
        if missing:
            raise SystemExit("不足している列: " + ", ".join(sorted(missing)))
        rows = list(reader)

    eligible = []
    review = []
    for row in rows:
        row = {key: clean(value) for key, value in row.items()}
        if row["stage"] in EXCLUDED_STAGES:
            continue
        if row["contact_allowed"].lower() not in {"yes", "y", "true", "1"}:
            continue

        absent = [key for key in NEEDED_FOR_AI if not row[key]]
        if absent:
            row["review_reason"] = "不足: " + ", ".join(absent)
            review.append(row)
        else:
            eligible.append({key: row[key] for key in sorted(NEEDED_FOR_AI)})

    write_csv(Path("eligible.csv"), sorted(NEEDED_FOR_AI), eligible)
    write_csv(Path("review.csv"), list(reader.fieldnames) + ["review_reason"], review)
    print(f"AIへ渡す候補: {len(eligible)}件")
    print(f"人が確認する候補: {len(review)}件")

if __name__ == "__main__":
    if len(sys.argv) != 2:
        raise SystemExit("使い方: python split_leads.py leads.csv")
    main(sys.argv[1])

split_leads.pyとして保存し、CSVと同じフォルダで実行します。

python split_leads.py leads.csv

上のサンプルでは、実行結果はこうなります。

AIへ渡す候補: 2件
人が確認する候補: 1件

北浜製作所は失注、青葉物流は連絡停止なので、どちらも出力に入りません。東雲電機は次回アクション日が空なのでreview.csvへ回ります。eligible.csvに残るのは星雲商事と港南設備です。

「除外」と「確認待ち」を同じ色にしない

営業の一覧には、動かない理由が混ざっています。失注は連絡しない。連絡停止も連絡しない。けれど次回アクション日が空なのは、担当者が更新を後回しにしただけかもしれません。ここを一つの除外にすると、一覧はすっきりしますが、追うべき案件まで見えなくなります。

僕はreview.csvを毎週の短い確認リストとして扱います。件数が三件なら、その場で担当に聞けば終わりです。二十件を超えたなら、AIの順位付け以前にスプレッドシートの入力ルールが崩れています。モデルを替える話ではありません。

初回だけは担当者と15分見て、除外理由が実態とずれていないか確かめます。それで十分です。

この二つを分ける理由は、あとから説明できるからです。「なぜこの会社に連絡しなかったのか」と聞かれたとき、失注ならステータス、連絡停止なら許可フラグ、情報不足ならreviewの理由を見せられます。優先順位の根拠より、先にそこを残しておくと揉めません。

なぜステータスをstripするのか

コードのclean()は空欄を空文字に変え、前後の空白も取っています。さっきの失注 を落とせたのはここです。スプレッドシートで入力した値は、見た目より揺れます。コピー元の改行、全角半角、末尾スペース。AIへ渡す前の判定で文字列比較をするなら、まず正規化が要ります。

ただし、正規化で全部を直そうとはしていません。商談中と商談中(紹介)を同じ扱いにするかは、営業の運用次第です。勝手に寄せると、集計や担当の意味まで変わります。そういう値は、ステータスの選択肢をスプレッドシート側で固定するほうが安全です。

運用を始めるなら、最初の一週間はeligible.csvを送信前に人が見ます。除外が多すぎないか、reviewの理由が妥当か、既存の優先順位と大きく違わないか。この確認を一度挟むと、保留を除外にするかreviewに残すかも、営業の実態に合わせて決められます。コードを触る前に、ルールの持ち主を決めておくと早いです。

AIには候補だけを渡す

eligible.csvを開いて、次の依頼文と一緒に渡します。会社名と案件メモを外部サービスへ送る運用なら、社内の利用ルールと契約条件の確認は先です。ここでは、許可された環境で使う前提にしています。

次の商談候補を、今週中に僕が動く順に並べてください。

出力は表だけにしてください。
列は「優先順位」「会社名」「先に確認すること」「今日やる一手」「根拠」の5つです。
案件メモに書かれていない事実は補わないでください。
優先度が同じなら、next_action_dateが早い方を上にしてください。

(ここにeligible.csvの内容を貼り付ける)

最初から「次のメール文も作って」と足さないほうが、僕はレビューしやすいです。まず順番だけ決める。上位の数社だけ、次の依頼でメール文に進む。Gemini 3.8 Flashのような速いモデルほど、この二段階のほうが朝の確認が短く済みます。

この順序なら、案件メモにない競合情報や売上見込みを、もっともらしく補われても気づきやすいです。最初の表では根拠だけを見る。メール文を作る段階で初めて言葉の良し悪しを見る。一度に評価する対象が少ないので、営業担当が「なんとなく良いから採用する」流れを止められます。

CSVでよく止まる二つの点

Excelで保存したCSVを読むなら、文字コードで止まることがあります。このコードはutf-8-sigで読み書きしています。Excelで文字化けしにくいBOM付きUTF-8で出力するためです。元ファイルがShift_JISなら、source.open()のencodingをcp932に変えてください。

もう一つは列名です。たとえばownerを担当者に変えたまま実行すると、不足している列: ownerで止まります。黙って空のCSVを出すより、このエラーのほうがずっとましです。列名を変えたいなら、CSVとREQUIRED、NEEDED_FOR_AIをまとめて変えます。

で、現場でどう使うか

AIに優先順位を付けさせるなら、対象外を混ぜないことが先です。受注・失注・連絡停止をモデルの「気づき」に頼ると、たまに外れた一件がそのまま営業事故になります。

この小さな分岐を週初めのCSV出力に入れるだけで、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?