ChatGPTに日本の住所を正規化させると危険だった
日本の住所は、人間が読むぶんにはそれなりに自然です。
一方で、LLM、OCR、音声入力、AI Agent に扱わせると、かなり簡単に壊れます。
たとえば、次のような入力です。
東京都渋谷区渋谷2ー21ー1 京都市中京区御幸町通二条下る山本町 札幌市中央区南二条西1-5 東京都中央区日本橋一丁目二番三〇一 六本木ヒルズ森タワー30F 〒150-0002 渋谷区渋谷2-21-1 府中市宮西町2-24 千葉県袖ケ浦市長浦駅前5-21
人間なら「まあ住所だな」と読めます。
しかしシステムに入れると、問題は一気に増えます。
- 全角数字、半角数字、漢数字が混ざる
- 「2-21-1」「2丁目21番1号」「二丁目二十一番一号」が同じ住所を指す
- 京都の通り名のように、町名マスタだけでは素直に扱いにくい表記がある
- 札幌や北海道の「条・丁目・線・号」が一般的な番地と違う
- 事業所名や大規模ビル名が、郵便番号や住所と強く結びついている
- OCRでは
二
とニ
、一
と1
、長音記号とハイフンが混ざる - 人間の入力では、都道府県や市区名が省略されたり、郵便番号が混ざったりする
- フォーム自動入力では、正規化後の表記と相手システムが期待する郵便住所表記がずれることがある
住所はただの文字列ではなく、かなり強い業務データです。
配送、本人確認、CRM、請求、契約、物件、店舗、名刺、郵便物など、後続処理に直結します。ここをLLMの「それっぽい補完」に任せると、静かにデータが壊れます。
LLMに住所処理を任せると何が危ないのか
LLMは文脈理解が得意です。
しかし、住所正規化で必要なのは「もっとも自然な答え」ではなく、「入力を壊さず、どの粒度まで同定できたかを構造化して返すこと」です。
たとえば、LLMに以下のような依頼をしたとします。
この住所を正規化して: 東京都中央区日本橋一丁目二番三〇一
期待したいのは、少なくとも次のような変換です。
東京都中央区日本橋一丁目2-301
ただし実際の業務では、これだけでは足りません。
{
"pref": "東京都",
"city": "中央区",
"town": "日本橋一丁目",
"banchi": "2",
"go": "301",
"post_code": "...",
"lat": "...",
"lng": "...",
"match_type": "address"
}
このように、後続システムが扱える形に分解されている必要があります。
さらに重要なのは、LLMがわからない住所を「わかったふり」しないことです。
住所正規化では、次のような判断が必要になります。
- 表記だけ直せたのか
- 市区町村まで同定できたのか
- 町域まで同定できたのか
- 郵便番号まで確定できたのか
- 事業所・大規模ビルとしてマッチしたのか
- 入力の一部が未解決として残っているのか
この粒度を持たずに自然文で「正規化しました」と返すのは、AI Agent時代のワークフローでは危険です。
OCR・音声入力・AI Agentで起きる住所の壊れ方
住所は、入力元によって壊れ方が変わります。
OCR
OCRでは、見た目が似ている文字が混ざります。
東京都渋谷区渋谷2ー21ー1 東京都渋谷区渋谷2-21-1 東京都渋谷区渋谷二丁目二十一番一号 東京都渋谷区渋谷ニ丁目21-1
人間には同じように見えても、システム上は別文字列です。
特にやっかいなのは、OCR由来の誤認識が「住所としてありえそうな文字列」に見えることです。
たとえば、漢数字の
二がカタカナの
ニとして読まれることがあります。
渋谷二丁目 渋谷ニ丁目
見た目は近いですが、前者は漢数字の二、後者はカタカナのニです。人間はほぼ同じに読めても、文字列比較では一致しません。
区切り文字も同じです。住所の
2-21-1のハイフンに見える文字は、実務上はかなり多くの種類が混ざります。
2-21-1 2‐21‐1 2‑21‑1 2‒21‒1 2–21–1 2—21—1 2―21―1 2−21−1 2-21-1 2ー21ー1 2ー21ー1
半角ハイフン、全角ハイフン、マイナス記号、長音記号、半角長音などが混ざるため、見た目だけでは判断しづらくなります。
そのままCRMや配送システムに入れると、重複検出や名寄せに失敗します。
音声入力
音声入力では、区切りや数字表記が揺れます。
にちょうめ にじゅういちばん いちごう 2丁目21番1号 2-21-1
「番」「号」「ハイフン」の扱いを曖昧にしたまま保存すると、後で正規化し直すのが難しくなります。
人間入力
人間が入力する住所は、機械から見るとかなり情報が抜けます。
たとえば、都道府県がない住所は珍しくありません。
渋谷区渋谷2-21-1 大阪市北区梅田1-1-1 札幌市中央区南二条西1-5
さらに、市区名すらないこともあります。
丸の内1-9-1 梅田1-1-3 栄3-5-1
人間は文脈で「たぶん東京の丸の内」「大阪の梅田」「名古屋の栄」と判断できます。しかし、システムとしては候補が複数あり得ます。
郵便番号が混ざるケースもあります。
〒150-0002 東京都渋谷区渋谷2-21-1 1500002 渋谷区渋谷2-21-1
郵便番号は強い手がかりですが、入力された住所本文と矛盾している可能性もあります。郵便番号だけで上書きするのではなく、住所文字列との整合を見たい場面があります。
事業所名も単純ではありません。
東京都千代田区 宮内庁 大阪府大阪市北区 株式会社〇〇 東京支店 京都市役所前 株式会社〇〇
事業所名や支店名に都道府県名・市区町村名・駅名が混ざると、住所の一部なのか、組織名の一部なのかが曖昧になります。
そして、同じ市名の問題があります。
府中市 伊達市
府中市は東京都にも広島県にもあります。伊達市も北海道と福島県にあります。都道府県が省略された入力を、LLMが文脈だけで一意に決めてしまうのは危険です。
フォーム自動入力
住所フォームの自動入力を作っていると、町名や自治体名の表記ゆれで蹴られることがあります。
典型例が
ケと
ヶです。
袖ケ浦市 袖ヶ浦市
たとえば、郵便住所として多くのシステムが期待している表記と、国土交通省系のデータで使われている表記が一致しないことがあります。
人間にはどちらも同じ地名に見えますが、フォーム側が選択肢マスタと完全一致で検証している場合、片方の表記だけが通り、もう片方は弾かれます。
つまり住所正規化では、「一つのきれいな住所に直す」だけでは足りない場面があります。
入力住所 -> 住所として同定する -> 国土交通省系の表記に正規化する -> 必要に応じて日本郵便系の住所表記へ変換する -> 送信先フォームや配送システムに渡す
多くの業務システムや住所フォームでは、郵便番号データ由来の住所表記が使われています。そのため、正規化した住所をさらに郵便情報側の表記へ変換する処理が必要になります。
AI Agent
AI Agentでは、ユーザーの自由入力をそのままツールに渡す場面が増えます。
たとえば、次のようなワークフローです。
名刺画像を読み取る → 会社名と住所を抽出する → CRMに登録する → 郵送リストに追加する
このとき、Agentが住所を自然文のまま扱うと、後続のAPIやDBに入る前に住所が壊れます。
AI Agentには、LLMの推測ではなく、住所専用の正規化ツールを呼ばせるべきです。
なぜregexや郵便番号APIだけでは難しいのか
住所処理を始めると、最初は正規表現でいけそうに見えます。
(?<pref>東京都|大阪府|...)(?<city>.+?[市区町村])(?<rest>.+)
しかし実際には、すぐに限界が来ます。
- 市区町村名と町域名の境界が単純ではない
- 郡、町、村、省略表記がある
- 都道府県が省略される
- 市区名まで省略され、町域名や施設名だけになることがある
- 郵便番号が住所文字列に混ざる
- 府中市や伊達市のように、同じ市名が複数の都道府県に存在する
- 「霞が関」「霞ヶ関」「霞ケ関」のような表記ゆれがある
- 「袖ケ浦」「袖ヶ浦」のように自治体名・町域名でも揺れる
- 国土交通省系の表記と日本郵便系の表記が一致しないことがある
- フォーム側が郵便住所表記を期待していると、別の正規形では弾かれることがある
- OCRでは漢数字の
二
とカタカナのニ
のような誤認識が起きる - ハイフンに見える区切り文字が10種類以上混ざる
- 京都の通り名のように住所文字列内にマスタ照合しづらい要素がある
- 「宮内庁」「日本銀行」のような事業所名だけで住所・郵便番号が決まることがある
- 事業所名や支店名に都道府県・市区町村名が含まれることがある
- 大規模ビルでは階数によって郵便番号が変わることがある
郵便番号APIも便利ですが、「郵便番号から住所を引く」だけでは、自由入力された住所の正規化には足りません。
必要なのは、自由形式の住所文字列を受け取り、表記を吸収し、住所データとして構造化する処理です。
AI Agent時代には住所専用ツールが必要
LLMやAI Agentは、すべてを自分で推論する必要はありません。
むしろ、壊してはいけない業務データは専用ツールに渡すべきです。
住所であれば、Agentに次のような役割分担をさせるのが自然です。
LLM: 入力文や画像から住所らしき文字列を抽出する 住所正規化API: 抽出された住所を正規化・構造化・照合する 業務システム: 正規化済み住所をCRM、配送、請求、物件DBなどに保存する
LLMは「抽出」と「判断」が得意です。
一方で、住所正規化はデータソース、辞書、例外処理、照合粒度が重要です。ここは専用APIとして切り出した方が安全です。
試せるデモを作った
この記事で触れた問題を試すために、Jusho という日本住所正規化APIのデモを公開しています。
自由形式の住所文字列を投げると、次のような構造化データを返します。
- 正規化済み住所
- 都道府県、市区町村、町域、番地、号
- 郵便番号
- カナ
- 行政コード
- 緯度経度
- マッチ種別
- 国土交通省系・日本郵便系などのデータソース別表記
- 事業所・大規模建物の判定
データソースとして、国土交通省の位置参照情報と日本郵便の郵便番号データを組み合わせています。まだ改善中ですが、OCR由来の表記ゆれ、都道府県省略、郵便住所表記への変換のような入力をブラウザ上で試せます。
狙いは、LLMに住所を「考えさせる」のではなく、住所専用ツールに「照合させる」ことです。
英語では、次のように表現しています。
LLM-safe Japanese address normalization.
デモAPIで試す
REST APIとしてすぐ使えます。
curl -G "https://api.jusho.dev/normalize" \ --data-urlencode "address=東京都渋谷区渋谷2ー21ー1"
レスポンス例です。
{
"full_address": "東京都渋谷区渋谷二丁目21-1",
"post_code": "1500002",
"pref": "東京都",
"city": "渋谷区",
"town": "渋谷二丁目",
"banchi": "21",
"go": "1",
"lat": "35.659609",
"lng": "139.705829",
"match_type": "address"
}
複数件をまとめて試す場合は、batch endpointを使います。
curl -X POST "https://api.jusho.dev/normalize/batch" \
-H "Content-Type: application/json" \
-d '{
"addresses": [
"東京都渋谷区渋谷2ー21ー1",
"大阪府大阪市北区梅田1-1-1"
]
}'
MCP / OpenAPI から試す
AI Agentの外部ツールとして使う場合の検証用に、MCPとOpenAPIも用意しています。
MCP対応クライアントでは、次のように設定できます。
{
"mcpServers": {
"jusho": {
"url": "https://api.jusho.dev/mcp"
}
}
}
AI Agentからは、ユーザー入力やOCR結果から抽出した住所を
normalize_addressツールに渡します。
ユーザー: この名刺の住所をCRMに登録して Agent: 1. 名刺画像から住所候補を抽出 2. Jusho MCPの normalize_address を呼び出す 3. 正規化済み住所、郵便番号、行政コードをCRMに保存
OpenAPI仕様も公開しているので、OpenAPI対応のAgentツールチェーンからREST APIとして組み込むこともできます。
住所は、あとから直すほどコストが高くなります。
入力時点、またはAI Agentが業務システムへ書き込む直前に正規化するのが安全です。
残っている課題
住所正規化は例外が多く、完全に扱うのは簡単ではありません。今回のデモでも、まだ改善したい点は多くあります。
- 正規化できなかった住所の報告フロー改善
- 曖昧な住所に対する候補提示の改善
- 京都住所など、通り名情報の扱い改善
- OCR由来の誤認識に強い補正
- MCPツールの拡張
- SDKとドキュメントの整備
- 住所正規化の失敗例・検証記事の公開
ただ、LLMやAI Agentが業務データを扱う機会が増えるほど、住所のような「壊すと困る文字列」を安全に扱うための専用ツールは必要になると思っています。
まずは、開発者が5分で試せるデモとして整備していきます。