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は、少なくとも次の列を持たせます。列名はコードと同じにしてください。
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へ渡す一覧と、人が直す一覧が分かれます。僕はここを固定してから、モデルの比較やプロンプトの調整に進めば十分だと考えています。ここ地味に効きます。
