9月23日のTechCrunchに「ChatGPT mobile app gets voice-based agentic features」という見出しが出ていました。これが意味するのは、移動中にAIへ仕事を頼む機会が増えるということです。問い合わせの整理も頼みやすい。ただ、整理後のCSVをExcelで開く前には、別の確認が要ります。
問い合わせ文は相手が書いた文字列です。先頭に = などがあると、表計算ソフトが数式として扱う可能性があります。ChatGPTで文面を整えても、この確認は省けません。僕なら、開く前にCSVの文字列列を検査します。
問い合わせ4件で試す
次の内容を inquiries.csv としてUTF-8で保存します。会社名と問い合わせは外部から入る列、IDは社内で振った番号という想定です。URLは実在しない .invalid ドメインにしてあります。
ID,会社名,問い合わせ
1,青葉商事,資料を送ってください
2,"=HYPERLINK(""https://example.invalid"",""資料"")",見積相談
3,南原企画," +1+2"
4,東海物流,日程確認
2件目の会社名は =、3件目の問い合わせは空白の後に + が来ます。見た目だけで確認すると後者を落としそうでした。先頭の空白とタブを飛ばしてから調べます。
CSVそのものは、セルの「中身」を区切って保存する形式です。会社名として入った文字列でも、開く側の解釈で数式になり得ます。AIが日本語をきれいに並べ直したかどうかと、この解釈は別です。怖いのは、相手が入力した会社名や問い合わせが、そのまま表のセルに入ること。だから検査するのは、社内で振ったIDより、外から来た自由記述欄です。
Pythonで開く前に止める
Pythonの標準ライブラリだけで動きます。check_csv.py に保存してください。
import csv
import sys
from pathlib import Path
TEXT_COLUMNS = ("会社名", "問い合わせ")
FORMULA_PREFIXES = ("=", "+", "-", "@")
def inspect(path):
with path.open("r", encoding="utf-8-sig", newline="") as f:
reader = csv.DictReader(f)
headers = reader.fieldnames or []
if len(headers) != len(set(headers)) or any(
name not in headers for name in TEXT_COLUMNS
):
raise ValueError("列名の重複、または必要な列の欠落があります")
hits = []
count = 0
for count, row in enumerate(reader, start=1):
if None in row or any(row[name] is None for name in TEXT_COLUMNS):
raise ValueError(f"{count}件目: 列数が合いません")
for name in TEXT_COLUMNS:
value = row[name]
if value.lstrip(" \t\r\n").startswith(FORMULA_PREFIXES):
hits.append((count, name, value))
return count, hits
if __name__ == "__main__":
if len(sys.argv) != 2:
raise SystemExit("使い方: python3 check_csv.py inquiries.csv")
count, hits = inspect(Path(sys.argv[1]))
for number, name, value in hits:
print(f"{number}件目 / {name}: {value!r}")
print(f"確認: {count}件、要確認: {len(hits)}件")
raise SystemExit(1 if hits else 0)
実行コマンドは python3 check_csv.py inquiries.csv。僕の手元では次の表示になりました。
2件目 / 会社名: '=HYPERLINK("https://example.invalid","資料")'
3件目 / 問い合わせ: ' +1+2'
確認: 4件、要確認: 2件
3件目も止まった。
数式らしい文字列があれば終了コードは1、なければ0です。CSVを開く後続処理に組み込む場合も、この数字で中断できます。utf-8-sig は、Excelから保存したCSVの先頭にBOMが付いていても列名を読めるようにするためです。newline="" はCSV内の改行を csv に扱わせる指定です。
掲載したCSVに対して、次のassertも通しました。
from pathlib import Path
from check_csv import inspect
count, hits = inspect(Path("inquiries.csv"))
assert count == 4
assert [(n, c) for n, c, _ in hits] == [(2, "会社名"), (3, "問い合わせ")]
実行環境はPython 3.14.6です。上の4件に加え、問題のない1件だけのCSVでは 確認: 1件、要確認: 0件 と出て終了コード0。列が欠けた行では ValueError: 1件目: 列数が合いません で止まりました。行数が合わないファイルを「異常なし」と扱わないためです。出力の「2件目」はCSVの物理的な行番号ではなく、見出しを除いたデータの順番です。問い合わせ内に改行があっても、この数え方なら変わりません。
検出した後の扱い
- で始まる普通の文章も引っかかります。自動で削ると原文が変わるので、このコードは書き換えません。該当セルを人が確認し、必要なら取り込み先で文字列として扱う方法を決めます。対象列も大事です。外部から入る自由記述欄を増やしたら、TEXT_COLUMNS に足します。
逆に、先頭の記号がない文字列まで安全だと証明するコードでもありません。Excel側の取り込み設定や、その後に別の仕組みで加工する手順はここでは検査していません。今回の役目は、開く直前に明らかな要確認セルを見つけ、作業を止めること。営業の一覧表だと「とりあえず開いて中身を見る」が先になりがちですが、その一手前に置きます。
見出しのニュースから僕が気にしたのは、AIに頼める作業が増えるほど、CSVを開く直前の確認が抜けやすいことです。問い合わせ整理をAIに任せる件数が増えたら、この検査を毎回通します。検査は表を開く前に置く。今回の4件で、空白の後ろに隠れたものを含めた要確認は2件。