ダウンロードフォルダを開くと、「無題(3).pdf」「IMG_2831」「aaa.txt」みたいな名前のファイルが積み上がっている。あの状態を、Claudeに中身まで読ませて自動で仕分けさせました。わざと35個の紛らわしいファイルを散らかしたフォルダを用意して、名前だけで分類する方法と、中身も読むAIの方法で正解率を比べています。AIは何件を間違えたか、事故らずに実行するにはどうするか。コピペで動くスクリプトと一緒に、実測で書きます。
結論:判断はAIに任せ、実行はドライランを挟んで人が承認する
先に答えです。ファイル名だけで分類すると正解率62.9%、中身も読むAIに任せると94.3%まで上がりました。名前がアテにならないファイルほど、この差は開きます。
ただし、いきなりAIに実行までさせるのは危ないです。ファイルの移動やリネームは、事故ると戻すのが大変。なので分類の「判断」はAIに任せて、「実行」はドライラン(何も動かさずに計画だけ表示)で一度確認し、人がOKを出してから動かす。この形が安全でした。今回のスクリプトも、何もしないのが初期状態です。
なぜ「ファイル名ルール」だと片づかないのか
拡張子や日付、ファイル名の正規表現で振り分ける——昔からある整理術です。名前がきちんと付いていれば、これで十分。問題は、名前がきちんと付いていないファイルのほうが多いことです。
「無題(3)」「aaa」「IMG_2831」からは、中身が請求書なのか契約書なのか読み取れません。名前だけのルールだと、こういうファイルはぜんぶ「その他」に落ちます。実際、後で出す実測でも、ルール分類が外した13件はすべて「意味のない名前がその他に落ちた」ケースでした。
AIが効くのはここです。ファイルの中身を数百字だけ読ませれば、名前が「aaa」でも「これは領収書」と判断できる。名前ではなく中身で仕分ける、という発想の差です。この「中身を読ませて構造化する」やり方は、請求書を画像認識でCSV化した記事とも地続きです。
検証:わざと散らかしたフォルダで2つの方法を比べる
題材は、意味のない名前・種類混在のダミーフォルダ
本物のフォルダは中身を公開できないので、ダミーを35個作りました。カテゴリは6つ(請求書・領収書/契約書・法務/会議メモ/個人・家計/ソースコード/その他)。ファイル名はわざとバラバラで、中身を読めば分かるように作ってあります。
そのうち18個は「罠」です。名前と中身がわざと食い違っていたり(契約書なのに「IMG_2831.txt」)、カテゴリの境界が近くて紛らわしかったり。名前だけでは絶対に当てられないファイルを、あえて多めに混ぜています。
言葉だけだと伝わりにくいので、実際に散らかしたフォルダの中身をそのまま出します。35個ぜんぶ。名前を眺めても、どれが請求書でどれが契約書か、まず判別できません。
messy/ ← 実際に用意した散らかしフォルダ(35ファイル・中身はすべてテキスト)
aaa.txt / clip.txt / config.txt / data(1).txt / document-final-FINAL-v2.txt /
download(2).txt / download(7).txt / IMG_0423.txt / IMG_2831.txt / kickoff.txt /
new 4.txt / qwerty.txt / readme_draft.txt / scan001.txt / script.txt / temp.txt /
test.txt / test_utils.txt / todo.txt / Untitled.txt / あああ2.txt /
スキャン_20240612.txt / スニペット.txt / メモ.txt / 会議_0611.txt / 利用規約案.txt /
名称未設定.txt / 契約_ドラフト.txt / 家計簿6月.txt / 打合せ.txt /
新しいテキスト ドキュメント.txt / 日記.txt / 無題(3).txt / 空メモ.txt / 買い物リスト.txt
比べたのは2つ。ファイル名と拡張子だけで振り分けるルール分類と、名前+中身の冒頭300字ほどを読ませるAI分類(Claude Haiku 4.5)です。大事なのは、AIに正解リストを見せていないこと。正解を知らないまっさらな状態で分類させて、あとから正解と突き合わせています。
仕組み:一覧 → AIで分類&改名案 → ドライラン → 実行
流れはシンプルです。フォルダ内のファイルを一覧して、1つずつ「名前+中身の冒頭」をClaudeに渡し、カテゴリと新しいファイル名の案をJSONで返してもらう。その計画をまずドライランで表示して、問題なければ実行する。
実際に使ったスクリプトの核を置きます。pip install anthropic して、APIキーを環境変数に入れれば動きます。初期状態はドライランで、--execute を付けたときだけファイルを動かします。
import os, sys, json, shutil, argparse
from pathlib import Path
from anthropic import Anthropic
client = Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
MODEL = "claude-haiku-4-5"
CATS = ["請求書・領収書", "契約書・法務", "会議メモ", "個人・家計", "ソースコード", "その他"]
def classify(name, head):
prompt = (f"次のファイルを分類して。カテゴリは{CATS}から1つ。"
f"新しいファイル名案(YYYYMMDD_カテゴリ_内容.拡張子)も。"
f'JSONで {{"category":..,"newname":..}} だけ返す。\n\n'
f"ファイル名: {name}\n中身の冒頭:\n{head}")
msg = client.messages.create(
model=MODEL, max_tokens=300,
messages=[{"role": "user", "content": prompt}],
)
return json.loads(msg.content[0].text)
def run(folder, execute=False, exclude=()):
src = Path(folder)
for f in src.iterdir():
if not f.is_file() or any(f.match(p) for p in exclude):
continue
head = f.read_text(encoding="utf-8", errors="ignore")[:300]
r = classify(f.name, head)
dst = src / "sorted" / r["category"] / r["newname"]
print(f"[{'MOVE' if execute else 'DRY'}] {f.name} -> {dst.relative_to(src)}")
if execute:
dst.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(f, dst) # コピーなので元は残る(=バックアップ相当)
if __name__ == "__main__":
ap = argparse.ArgumentParser()
ap.add_argument("folder")
ap.add_argument("--execute", action="store_true") # 既定はドライラン
ap.add_argument("--exclude", nargs="*", default=[])
a = ap.parse_args()
run(a.folder, a.execute, a.exclude)
ポイントは、実行がコピーであること。元のフォルダをそのまま残して、仕分け結果を別フォルダに作ります。これだけで、失敗しても元に戻れます。プレビューして承認するまで何も壊さない設計は、この手のツール(OSS実装)でも共通の考え方でした。
実測:正解率・誤分類・コスト
同じ35ファイルを両方で分類した結果です。
| 分類方法 | 全体の正解率 | 罠ファイルだけ |
|---|---|---|
| 名前だけルール分類 | 62.9%(22/35) | 38.9% |
| 中身も読むAI分類 | 94.3%(33/35) | 88.9% |
数字だけだと実感が湧かないので、AIが中身を読んで実際に振り分けた結果もそのまま出します。さっきのバラバラな35ファイルが、6つのフォルダにこう収まりました。間違えた2件も良く見せるために消したりせず、AIが置いた場所のまま(←印)にしています。
sorted/ ← AIが中身を読んで振り分けた実際の結果
├─ invoice/(請求書・領収書)
│ aaa.txt, document-final-FINAL-v2.txt, download(7).txt, スキャン_20240612.txt, 無題(3).txt
├─ legal/(契約書・法務)
│ IMG_2831.txt, scan001.txt, 利用規約案.txt, 契約_ドラフト.txt, 新しいテキスト ドキュメント.txt
├─ meeting/(会議メモ)
│ kickoff.txt, Untitled.txt, あああ2.txt, 会議_0611.txt, 打合せ.txt
├─ personal/(個人・家計)
│ IMG_0423.txt, todo.txt, 家計簿6月.txt, 日記.txt, 買い物リスト.txt,
│ メモ.txt ←本来は請求書, qwerty.txt ←本来はその他
├─ code/(ソースコード)
│ config.txt, new 4.txt, readme_draft.txt, script.txt, test_utils.txt, スニペット.txt
└─ other/(その他)
clip.txt, data(1).txt, download(2).txt, temp.txt, test.txt, 名称未設定.txt, 空メモ.txt
名前がアテにならないファイルほどAIが勝つ、という素直な結果でした。ルール分類が落とした13件は、全部「意味のない名前がその他に落ちた」もの。名前だけ見る方法の天井が、そのまま数字に出ています。
とはいえ、AIも完璧ではありません。35件中2件を外しました。1件は「メモ.txt」という名前の中身がコンビニの領収書だったもの。AIは個人の家計メモと判断しました。少額の領収書と私的な支出メモは、中身を読んでも境界が近い。もう1件は正直、正解ラベル側が厳しすぎたケースで、AIの判断のほうが妥当とも言えるものでした。ここは数字を良く見せるために伏せず、そのまま残しておきます。
コストも見ておきます。35ファイル全部で約2.99円、1ファイルあたり0.085円ほど。人が1ファイル20秒で分類して名前を付け直すと、35件で約11.7分かかる計算です。それが3円で終わる。トークンは実測ではなく字数ベースの概算です(換算前提はスクリプトに書いています。正確に出すならAPIのusageを見てください)。
「AIで一括処理してコストを測る」流れは、顧客リストの名寄せ記事や長文要約の記事でも同じ温度で数字を出しています(一覧は業務効率化実践のカテゴリから)。
事故らないための鉄則
ファイルを動かす自動化は、便利さと危険さが背中合わせです。今回やってみて、外せないと感じたのは次の4つでした。
- ドライランを既定にする:まず計画だけ表示し、目視してから実行する。実際に
--excludeで2件を除外し、計画どおりに動くのを確認してから流しました - 元を消さない(コピーで仕分ける):移動ではなくコピー。今回も実行後、元フォルダは35件のまま残っていました。読者向けスクリプトにはzipで退避する
--backupも付けています - カテゴリは少なく、はっきり分ける:バケツを増やすほど境界が曖昧になり、誤分類が増えます。今回AIが迷ったのも、境界の近い「領収書」と「個人メモ」でした
- 怪しいものは人が見る:金額や契約が絡むファイルは、AIの分類を鵜呑みにせず最後に人が確認する。全件見るより桁違いに速く済みます
まとめ
- 名前がアテにならないフォルダは、中身を読むAIに任せると正解率が62.9%→94.3%に上がった(罠ファイルでは+50ポイント)
- 35ファイルの仕分けが約3円・人手なら約12分ぶんの作業を置き換えられた
- ただしAIも2件外した。境界が近いカテゴリは中身を読んでも迷う。金額・契約系は人が最終確認する
- 実行は必ずドライラン→承認→コピーで。元を消さず、除外ルールとバックアップを添える
- ファイル自動整理でAIに任せるのは「判断」まで。「実行」は人がゲートを持つのが安全
散らかったフォルダの整理を、自分の業務のルールに合わせて仕組み化したい方へ。業務効率化の導入支援もやっています。30分の無料相談から始められます。 → サービス詳細