🏗️ はじめに
前回の記事で、Dropboxの共有フォルダにPDFを置くだけで建築確認申請書類をAIが自動チェックする仕組みを作った。
1週間運用して見えてきた課題がある。
「住所が実在するかどうか、AIには判断できない」
「愛知県春日井市◎◎町12丁目12-12」——この住所は正しいのか? AIは文字列としてしか見ていない。存在しない地番でも「記載あり」と判定してしまう。
さらに、チェック結果が文章形式で読みにくい。忙しい担当者が10行の文章を読んで「結局どこが問題なの?」と思っている。
今回、国の住所データベースによる自動検証と30項目のチェックリスト形式出力を実装し、さらに完了案件からの学習やスキャンPDF対応まで一気に追加した。
コードは相変わらず1行も書いていない。
📍 住所を国のデータベースで自動検証する
問題: AIは住所の実在性を知らない
建築確認申請で住所の誤りは差し戻しの主要原因の一つだ。
- 地番の数字を1桁間違える
- 「五丁目」と「5丁目」が書類間で混在
- 郵便番号と住所が一致しない
- 「○○郡」が抜けている
AIにPDFを読ませるだけでは、これらを検出できない。
解決: 3層の住所検証をPython前処理で実行
Claude(AI)を呼ぶ前に、Pythonで住所を検証する。AIのターンは1つも消費しない。
PDF → pdfplumber(テキスト抽出)
→ 正規表現(住所パターン抽出)
→ jageocoder(国の住所DBと照合)
→ 郵便番号データ(日本郵便CSVと照合)
→ 書類間クロスチェック(表記揺れ検出)
→ 検証結果をテキストファイルに保存
→ AIは結果を読むだけ
第1層: jageocoder(住所データベース照合)
jageocoder は、デジタル庁のアドレスベース・レジストリと法務局の登記所備付地図データを統合した住所ジオコーダーだ。
import jageocoder
jageocoder.init()
result = jageocoder.search("愛知県春日井市◎◎町12丁目12-12")
# → level: 8(号レベルまで完全一致 = 実在する住所)
マッチレベルで精度がわかる:
| レベル | 意味 | 判定 |
|---|---|---|
| 8 | 号まで一致 | ✅ 正確 |
| 7 | 番地まで一致 | ✅ 正確 |
| 6 | 丁目まで一致 | ⚠️ 番地以降が見つからない |
| 5以下 | 大字・町名まで | ❌ 住所に誤りの可能性 |
第2層: 郵便番号照合
日本郵便の郵便番号データ(12.4万件、毎月更新)で、郵便番号と住所の整合性をチェック。
書類の郵便番号: ◎◎◎-××××
書類の住所: 愛知県春日井市◎◎町
郵便番号DB: ◎◎◎-×××× → 愛知県春日井市◎◎町 → ✅ 一致
第3層: 書類間クロスチェック
同じPDF内、さらに複数PDF間で住所の表記揺れを検出。
申請書: 愛知県春日井市◎◎町五丁目12番12
委任状: 愛知県春日井市◎◎町5丁目12-12
→ ⚠️「五丁目」と「5丁目」の表記揺れ
実際の検証結果
# 住所検証結果
============================================================
## 抽出された住所一覧
- 愛知県春日井市◎◎町5丁目12-12
- 愛知県春日井市▽▽▽町16-16
## 住所データベース照合結果(jageocoder)
- 入力: 愛知県春日井市◎◎町5丁目12-12
照合: ✅ 番地まで一致(レベル8)
- 入力: 愛知県春日井市▽▽▽町16-16
照合: ✅ 番地まで一致(レベル8)
## 郵便番号照合結果
- 〒◎◎◎-××××: ✅ 春日井市▽▽▽町と一致
この検証はPythonが数秒で完了する。AIのコストはゼロ。
✅ 30項目チェックリスト形式の出力
Before: 文章形式(読みにくい)
【要確認】
1. 申請書の日付が空欄です
【軽微・参考】
2. 面積は整合しています
3. 地名地番は一致しています
4. 設計者の記載があります
...
After: チェックリスト形式(一目でわかる)
【チェックリスト】
✅ 申請書の必須記入欄: 日付以外は記入済み
✅ 申請者の氏名・住所: ◎◎××、愛知県春日井市▽▽▽町16-16
✅ 設計者・資格番号: 二級建築士 ◎◎××
✅ 地名地番: 全書類で一致
✅ 面積の整合性: 全書類間で一致
✅ 高さの整合性: 申請書と立面図で一致
✅ 宛先: 春日井市長 殿(正しい)
✅ 規模基準: 119.26㎡ ≧ 75㎡(OK)
✅ 委任状: 全項目記載あり
✅ 確認書: 発行済み、耐震等級3
✅ 維持保全計画書: 30年間のスケジュールあり
✅ 維持補修資金: 年間7万円(≧6万円 OK)
✅ 都市計画基本図: 縮尺表記あり
✅ 配置図: 縮尺・方位・道路種別あり
✅ 各階平面図: 縮尺・方位あり
✅ 立面図: 4面あり
✅ 断面図: 寸法記載あり
✅ レターパック: 返信用宛先記載済み
❌ 日付: 申請書の月日が空欄
❌ 居住災害確認書: PDF内に見当たらない
─ 署名・押印: 春日井市は押印不要
担当者は ❌ だけ見ればいい。 ✅ の山の中から問題箇所を探す必要がない。
📊 複数PDF間のクロスチェック
申請書類は複数のPDFで構成される。それぞれに面積や高さが記載されていて、全部一致していなければならない。
【読み取った主要数値】
敷地面積: 172.31㎡(申請書)/ 172.31㎡(求積図・配置図)→ 一致
建築面積: 74.12㎡(申請書)/ 74.12㎡(求積図)→ 一致
床面積合計: 119.26㎡(申請書)/ 119.26㎡(求積図)/ 119.26㎡(確認書)→ 一致
最高高さ: 6.674m(申請書)/ 6.674m(立面図)→ 一致
最高軒高: 6.320m(申請書)/ 6.320m(立面図)→ 一致
これは前回記事で導入したpdfplumberによるテキスト事前抽出があるから実現できる。PDFから画像として数値を読むのではなく、テキストレイヤーから正確に数値を抽出し、プログラム的に照合している。
もし不一致があれば:
床面積合計: 119.26㎡(申請書)/ 119.62㎡(求積図)→ ❌ 不一致
と即座に検出される。0.01㎡の差も見逃さない。
📚 完了案件から学習する仕組み
問題: 同じ市町村でも案件ごとにパターンが違う
「春日井市の長期優良住宅認定申請」は過去に何十件もやっている。その経験を新しい案件のチェックに活かしたい。
解決: 完了フォルダの自動インデックス化
「■依頼完了」フォルダには過去の完了案件が大量に格納されている。これを毎回全部読むのは重すぎるので、Pythonで事前にインデックスを作る。
# 完了案件から同じ市町村のものだけ抽出
# フォルダ名、PDF名、テキストファイル名を要約
# 案件フォルダの _AI抽出テキスト/_完了案件参考.txt に保存
# 春日井市 過去の完了案件参考情報
# 完了案件数: 85件
## 20260201[春日井市]◎◎▽▽様邸-○
PDF: 長期認定申請書一式.pdf
依頼: 白黒片面2部
## 20260115[春日井市]○×様邸-▽
PDF: 認定申請書一式.pdf, 追加書類.pdf
依頼: 白黒片面2部、居住災害確認書追加
AIはこの軽い要約ファイルだけ読んで、「春日井市の過去案件では居住災害確認書を追加提出しているケースが多い」といったパターンを把握できる。
Pythonの前処理なのでAIのターン消費はゼロ。
🔍 スキャンPDFへのOCR対応
問題: テキストレイヤーがないPDF
CAD出力のPDFはテキストレイヤーがあるのでpdfplumberで抽出できる。しかしスキャナーで取り込んだPDFはテキストレイヤーがなく、抽出結果が空になる。
解決: Tesseract OCRによるフォールバック
PDF → pdfplumber(テキスト抽出を試みる)
→ テキストが少ない場合(スキャンPDFと判定)
→ pdf2image(PDFをページごとに画像変換)
→ Tesseract OCR(画像から日本語テキスト認識)
→ OCR結果をテキストファイルに保存
# pdfplumberで抽出できなかった場合のフォールバック
if len(extracted_text.strip()) < 50:
# スキャンPDFと判定 → OCR実行
images = convert_from_path(pdf_path)
for img in images:
text += pytesseract.image_to_string(img, lang='jpn')
通常のPDFには影響なし。 テキストレイヤーがあるPDFはpdfplumberで正確に抽出され、スキャンPDFの場合のみOCRが自動発動する。
📁 システムファイルの整理
運用開始後、共有フォルダにシステムファイルが増えて担当者が混乱する問題が発生。
Before(担当者「どれが何のファイル?」)
■■事務所への業務依頼■■/
├── AIへの意見.txt
├── CLAUDE.md
├── run_ai_check.bat
├── run_ai_check.ps1 ← 触っちゃダメなのかわからない
├── extract_pdf_text.py ← 何これ?
├── build_case_index.py ← 何これ?
├── update_web_info.bat
├── update_web_info.ps1
├── ai_check_log.txt
├── setup_task_scheduler.ps1
└── 20260108[豊川市]... ← 案件フォルダ
After(すっきり)
■■事務所への業務依頼■■/
├── AIへの意見.txt ← 担当者が使う(編集OK)
├── CLAUDE.md ← 触らない
├── run_ai_check.bat ← 触らない(中身は1行のラッパー)
├── _ai_system/ ← 全プログラム格納(触らない)
│ ├── このフォルダの中身を編集・移動・削除しないでください.txt
│ ├── run_ai_check.ps1
│ ├── extract_pdf_text.py
│ ├── build_case_index.py
│ └── ...
└── 20260108[豊川市]... ← 案件フォルダ(従来通り)
run_ai_check.bat の中身はたった1行:
@echo off
cd /d "%~dp0"
powershell -ExecutionPolicy Bypass -File ".\_ai_system\run_ai_check.ps1"
exit /b 0
🌐 知識の自動メンテナンス
前回記事で構築した93市町村の知識ベースは、放置すると古くなる。2つの自動更新を組んだ。
月次WEB巡回(毎月第3日曜 7:00)
タスクスケジューラ
→ 愛知県・岐阜県の全市町村WEBサイトを巡回
→ マークダウンファイルを最新化
→ URLが変わっていればJSONも自動修正
→ ntfyで完了通知
住所データベース更新(同タイミング)
→ 日本郵便の郵便番号CSV(月次更新)を再ダウンロード
→ jageocoderの辞書を最新版に更新
人間がメンテナンスする必要は一切ない。
📊 実際のテスト結果(○○様邸・春日井市)
長期優良住宅認定申請の新規案件でフルテスト。
チェック結果サマリー
| カテゴリ | 結果 |
|---|---|
| チェックリスト項目 | 30項目中 28項目✅、2項目❌ |
| 数値照合(7項目) | 全て一致 |
| 住所検証 | 2件とも✅ レベル8(実在確認済み) |
| 地名地番の書類間一致 | ✅ 全書類で一致 |
| 市町村固有要件 | 居住災害確認書の欠落を検出 |
検出された問題
| # | 指摘 | 重要度 |
|---|---|---|
| 1 | 申請書の日付(月日)が空欄 | ❌ |
| 2 | 居住災害確認書がPDF内に見当たらない | ❌ |
検出されなかった問題(正しく✅判定)
面積7項目の完全一致、住所の実在確認、設計者資格番号の記載、維持保全計画の30年計画、維持補修資金の基準額クリア(7万円≧6万円)など、28項目が問題なしと正確に判定された。
💰 追加コスト
| 項目 | 金額 |
|---|---|
| jageocoder | 無料(OSS) |
| 郵便番号データ | 無料(日本郵便) |
| Tesseract OCR | 無料(OSS) |
| pdfplumber / pytesseract / pdf2image | 無料(pip) |
| Claude のターン増加 | 0ターン(全てPython前処理) |
| 合計追加コスト | 0円 |
住所検証もOCRも全てPythonの前処理で完結する。AIを呼ぶ前に終わっているので、Maxプランの消費量は一切増えない。
🏁 まとめ
前回からの進化
| 機能 | 前回 | 今回 |
|---|---|---|
| 住所チェック | AIが文字列として確認 | 国の住所DBで実在性を検証 |
| 出力形式 | 文章形式 | ✅❌チェックリスト(30項目) |
| 数値照合 | PDF単体 | 複数PDF横断(0.01㎡単位) |
| 過去案件 | 参照なし | 完了案件から自動学習 |
| スキャンPDF | 読めない | Tesseract OCRで自動認識 |
| ファイル整理 | 散乱 | _ai_systemに集約 |
| 知識更新 | 手動 | 月次自動(WEB+住所DB) |
アーキテクチャの原則
今回の改良で確信したことがある。
AIに「判断」だけさせて、「認識」はプログラムにやらせる。
- 住所の実在確認 → Pythonが数秒で完了(AIにやらせると不正確)
- PDFからの数値抽出 → pdfplumberが正確に抽出(AIに読ませると誤読する)
- 過去案件の要約 → Pythonがインデックス生成(AIに全件読ませるとターン不足)
- スキャンPDFのOCR → Tesseractが処理(AIのビジョン機能より安定)
AIは判断と文章生成が得意で、正確な認識が苦手だ。苦手な部分をプログラムで補い、得意な部分に集中させる。この分業が精度を決定的に上げた。
全体システム図(最終形)
Windowsタスクスケジューラ
│
├─ 毎時チェック(平日 6:55〜19:05)
│ bat → ps1
│ ├─ Python前処理(コストゼロ)
│ │ ├─ pdfplumber: テキスト抽出
│ │ ├─ Tesseract: スキャンPDF→OCR(フォールバック)
│ │ ├─ jageocoder: 住所DB照合
│ │ ├─ 郵便番号CSV: 郵便番号照合
│ │ └─ 完了案件インデックス生成
│ │
│ ├─ PowerShell: 市町村名抽出 → プロンプト動的生成
│ │
│ └─ Claude Code CLI(Opus 4.6 / 30ターン)
│ ├─ 必読: 弊社独自の留意点.md
│ ├─ 必読: 提出前チェックシート
│ ├─ 動的: 市町村別資料(該当分のみ)
│ ├─ 参照: 抽出テキスト+住所検証結果+完了案件参考
│ └─ 出力: ✅❌チェックリスト → テキスト追記 → ntfy通知
│
├─ 月次WEB更新(毎月第3日曜 7:00)
│ → 全市町村のWEBサイト巡回 → マークダウン更新
│ → 住所DB+郵便番号データ更新
│
└─ フィードバックループ
AIへの意見.txt ← 担当者が誤指摘を記録 → 次回から改善
コードは1行も書いていない。でも、住所データベースとの照合まで実装できた。
シリーズ記事: