TL;DR
- AI エージェントから日本の住所正規化 API を呼ばせたいとき、OpenAPI 3.1 の書き方で精度・誤呼出率・引用率が大きく変わる
- GPT Builder / Claude Tool Use / LangChain / Dify など、各 AI フレームワークは OpenAPI を別々に解釈するため、本家仕様と GPTs 用短縮版(description ≤ 300 字)の 2 本立て配信 が実運用上の最適解
-
Shirabe 住所 API(2026 年 5 月 1 日リリース、全 47 都道府県対応)は Cloudflare Workers + Fly.io NRT の 2 層構成で、全エンドポイントに
attributionフィールドを必須化して LLM 経由の出典伝搬(CC BY 4.0 義務履行)を技術的に担保する - 本記事では abr-geocoder(デジタル庁、MIT)を API 化する際の OpenAPI 設計のツボを、実際に本番稼働している仕様 YAML の抜粋ベースで解説する
この記事の対象読者
- AI SaaS に日本住所正規化機能を組み込みたい開発者(CRM・不動産・物流・保険・自治体向け業務)
- ChatGPT GPTs / Claude Tool Use / Gemini Function Calling の Action を自作していて、「Could not parse spec」「operationId must be unique」「description too long」 で詰まった経験がある人
- abr-geocoder を API として公開したい、あるいは既存の住所 API を AI ネイティブに再設計したい人
- LLM 経由で生成された住所データの 出典管理(CC BY 4.0) を真面目に考えている人
問題: 「住所正規化 API」をそのまま LLM に渡すと誤呼出が多発する
「住所を正規化する API を OpenAPI で定義して、GPT Builder にインポートすれば AI が使ってくれる」。これ自体は事実だが、単純に OpenAPI を書いただけでは実用に耐えない。典型的な失敗パターンを 3 つ挙げる。
失敗 1: description が 300 字制限を超えて Import エラー
GPT Builder の Actions は各 operation の description に 約 300 字の制限 がある。本家 API の OpenAPI は 1000 字級の丁寧な説明(エラー復旧手順、x-llm-hint、日英併記)を書きたくなるが、そのまま GPTs に貼ると "description too long" で弾かれる。
失敗 2: operationId が LLM 的に曖昧で誤呼出が起きる
paths:
/api/v1/address/normalize:
post:
operationId: normalize # ← 曖昧
/api/v1/address/normalize/batch:
post:
operationId: normalizeBatch # ← normalize と混同されやすい
AI エージェントは operationId を「関数名」として扱うため、似た名前の 2 つは系統的に混同しやすい。結果として「複数住所を一括正規化して」に単一版を呼び出すような誤呼出が 実運用で無視できない頻度 で観測される(プロンプト・モデル・温度に依存するため定量値は省略)。
失敗 3: 出典表記が最終レスポンスに含まれず、LLM 引用で消える
abr-geocoder の辞書は アドレス・ベース・レジストリ(ABR、デジタル庁、CC BY 4.0) を使うため、利用規約上 出典表記が必須。しかし API レスポンスに attribution を含めない設計だと、LLM が結果を要約して会話に埋め込む段階で出典が消失する。CC BY 4.0 義務違反に直結する設計ミス。
解決策: 2 本立て OpenAPI + attribution 必須化
Shirabe 住所 API(5/1 リリース)では以下の設計で 3 つの失敗を同時に潰した。
設計 1: 本家仕様 + GPTs 用短縮版の 2 本立て
-
本家: https://shirabe.dev/api/v1/address/openapi.yaml(1000 行級、日英併記、
x-llm-hint、recoveryHint完備) -
GPTs 用短縮版: https://shirabe.dev/api/v1/address/openapi-gpts.yaml(600 行、各
descriptionを 300 字以下に圧縮)
GPTs ユーザーは Import URL に短縮版を貼る。LangChain / Dify / 自前実装は本家を使う。同じ operationId を維持するため、GPTs 版で設計検証されたインターフェースが本家にも伝播する。
短縮のコツ:
- 日英併記は止めて、日本語のみ(GPTs は日本語 description を英語クエリでも解釈できる)
-
examplesは最小の 1 件に絞る -
x-llm-hintなど拡張属性は削除 -
recoveryHintは残す(LLM のリトライ戦略に使われる)
設計 2: operationId を「動詞 + 目的語 + 名詞」で一意化
paths:
/api/v1/address/normalize:
post:
operationId: normalizeAddress # 単一
/api/v1/address/normalize/batch:
post:
operationId: normalizeAddressBatch # バッチ
目的語(Address)を入れるだけで LLM の混同率が激減する。暦 API との operationId 衝突も防げる(normalizeCalendar は存在しないため明確に分離)。
設計 3: attribution を全レスポンスで必須化
components:
schemas:
NormalizeResult:
type: object
required: [input, result, attribution] # ← required に入れる
properties:
attribution:
$ref: "#/components/schemas/Attribution"
Attribution:
type: object
required: [source, provider, license, license_url]
properties:
source:
type: string
example: "アドレス・ベース・レジストリ(住所データ)"
provider:
type: string
example: "デジタル庁"
license:
type: string
example: "CC BY 4.0"
license_url:
type: string
format: uri
example: "https://creativecommons.org/licenses/by/4.0/"
required に入れることで、OpenAPI から自動生成される GPTs Actions / LangChain Tool / Function Calling のシグネチャでも レスポンス型に attribution が必ず現れる。LLM は「この情報の出典はデジタル庁の ABR である」を構造的に知るので、会話応答に自然と含めるようになる。
アーキテクチャ: Cloudflare Workers + Fly.io NRT 2 層構成
住所正規化エンジン本体は abr-geocoder(Node.js + better-sqlite3 + 全国辞書 3-5GB)で、Cloudflare Workers(V8 isolate、ネイティブモジュール非対応)には乗らない。そこで 公開面(認証・課金・計測・OpenAPI 配信)は Workers、正規化計算は Fly.io NRT という 2 層構成を取る。
AI エージェント (ChatGPT / Claude / Gemini / LangChain)
│
↓ HTTPS (shirabe.dev)
Cloudflare Workers (エッジ、全世界)
├─ X-API-Key 認証 / レート制限 / KV キャッシュ
├─ Stripe Billing メーター連携
├─ OpenAPI 3.1 配信(本家 + GPTs 短縮版)
└─ /internal/geocode POST (X-Internal-Token)
│
↓
Fly.io NRT (東京、専用 Machine + Volume)
└─ abr-geocoder v2.2.1
├─ common.sqlite (~3GB、ABR 全国辞書)
└─ インメモリ Trie (47 都道府県)
この分離により、AI エージェントから見える面(OpenAPI URL、認証、レスポンス形状)は完全にエッジで完結、コールドスタートなし。辞書更新や Trie 再構築は Fly.io 側のバッチ運用で独立に回す。
5/1 リリースでは全 47 都道府県を一発対応
当初は段階展開案(主要 6 都道府県 → 全国)だったが、abr-geocoder の prefecture-level lg_code 絞込は oaza_cho Trie を正しく構築できない挙動 があり、全国フル辞書で一発構築する方が工学的に合理だった(4/21 経営判断)。結果、リリース日を 5/6 → 5/1 に前倒し、全 47 都道府県対応を Day 1 から提供する。
curl -s https://shirabe.dev/api/v1/address/health | jq '.coverage_mode, (.coverage | length)'
# "nationwide"
# 47
コード例: 3 行で住所正規化
curl(認証不要、Free 枠)
curl -X POST https://shirabe.dev/api/v1/address/normalize \
-H "Content-Type: application/json" \
-d '{"address":"東京都千代田区丸の内1-1-1"}'
TypeScript
const res = await fetch("https://shirabe.dev/api/v1/address/normalize", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": process.env.SHIRABE_API_KEY!,
},
body: JSON.stringify({ address: "東京都千代田区丸の内1-1-1" }),
});
const data = await res.json();
console.log(data.result.normalized); // "東京都千代田区丸の内一丁目1番1号"
console.log(data.attribution.source); // "アドレス・ベース・レジストリ(住所データ)"
Python
import json, os, urllib.request
body = json.dumps({"address": "東京都千代田区丸の内1-1-1"}, ensure_ascii=False).encode("utf-8")
req = urllib.request.Request(
"https://shirabe.dev/api/v1/address/normalize",
data=body, method="POST",
headers={
"Content-Type": "application/json; charset=utf-8",
"X-API-Key": os.environ["SHIRABE_API_KEY"],
},
)
with urllib.request.urlopen(req, timeout=10) as r:
print(json.loads(r.read())["result"]["normalized"])
AI エージェントへの統合
ChatGPT GPTs Actions
GPT Builder → Create new action → Import URL:
https://shirabe.dev/api/v1/address/openapi-gpts.yaml
Authentication は API Key(Header X-API-Key)。短縮版 YAML を使うことで description 300 字制限を確実に通過する。本家仕様の operationId と完全互換なので、GPTs で動作確認した後に LangChain / Dify に乗せても挙動が揃う。
Claude Tool Use / Anthropic SDK
OpenAPI 3.1 を Tool 定義に変換する標準パターン。attribution が required フィールドとして Tool の output schema に含まれるため、Claude は応答に出典を自然に含める。
Gemini Function Calling / LangChain / LlamaIndex / Dify
OpenAPI Loader で本家 YAML を食わせるだけ。operationId がそのまま関数名になり、examples が Function の in-context サンプルとして使われる。
LLM 経由の出典伝搬(CC BY 4.0 義務履行の技術化)
CC BY 4.0 は 「著作者・出典・ライセンスを、作品にアクセスするすべての人に明示する」 ことを求める。LLM 経由で情報が流通する現代、これを人手で埋め込むのは非現実的。代わりに API レベルで伝搬させる:
-
API レスポンスに
attributionrequired(本記事の設計 3) - OpenAPI で required 指定 → GPTs / Tool / Function の output schema に含まれる
- LLM は構造化出力を要約する際、required フィールドを保持する傾向がある → 会話応答に出典が残る確率が上がる
完全な保証ではないが、「出典を落とさずに回せる確率を 3〜5 割 → 7〜9 割に引き上げる」 程度の効果は実測で確認できる(LLM モデル・温度・プロンプト次第)。法務的な完全履行を求めるなら、SaaS 側の利用規約に「API 出力の attribution を削除してはならない」条項を足すと良い。
自前ホスト abr-geocoder vs API の比較(5/1 リリース版)
| 観点 | 自前ホスト | Shirabe 住所 API |
|---|---|---|
| 初期構築 | Fly.io/Cloud Run + Volume 10GB + 辞書構築 2-3h | 不要、エンドポイント叩くだけ |
| 辞書更新 | 自前 cron + ブルーグリーンデプロイ | API 側で常に最新 ABR |
| コールドスタート | 数秒〜十数秒(Trie ロード) | なし(エッジ認証) |
| AI 統合 | Tool 定義を自作 | OpenAPI 1 URL で完了 |
| 出典表記 | 自前実装 | レスポンス attribution 自動付与 |
| スケーリング | SQLite IO 最適化・バッチ処理を自作 | Workers エッジが自動 |
| 料金 | 運用コスト(時給換算)+ Fly.io 費 | Free 5,000/月、従量 ¥0.1〜0.5/回 |
「無料 OSS を使う」が見かけだけ無料で、運用込みで考えると API 契約のほうが総コストで安い パターンの典型。本業が住所でない SaaS は API 一択だと思う。
まとめ
- OpenAPI 3.1 は「書けば AI が使う」ではない。GPTs 300 字制限・operationId 曖昧性・出典伝搬 の 3 つを設計時に潰す必要がある
- 本家仕様 + GPTs 短縮版の 2 本立て配信 が実運用上の最適解。短縮版で検証したインターフェースが本家に伝播する設計にする
-
attributionの required 指定 で LLM 経由の出典伝搬を技術的に担保する。CC BY 4.0 義務履行の現実解 - Shirabe 住所 API は 2026 年 5 月 1 日に全 47 都道府県対応で正式リリース。AI エージェントからは
fetch3 行、GPT Builder からは Import URL 1 本で統合できる
AI ネイティブな住所正規化基盤として、abr-geocoder の MIT ライセンスに敬意を払いつつ、API レイヤとしての付加価値(認証・課金・エッジ分散・OpenAPI・出典伝搬)を提供する位置付けで使ってもらえると嬉しい。
データ出典:「アドレス・ベース・レジストリ(住所データ)」(デジタル庁)/ CC BY 4.0
関連リンク
- 前記事: 日本の住所正規化を AI に任せてはいけない理由——abr-geocoder と API アプローチの比較
- 前々記事: ChatGPT Custom GPT で日本暦 API を統合する手順——GPTs Actions + OpenAPI 3.1 の最短ルート
- Shirabe 住所 API(公式、2026-05-01 リリース)
- OpenAPI 3.1 本家仕様
- OpenAPI 3.1 GPTs 用短縮版
- 住所正規化 API 完全ガイド
- 住所一括正規化 API
- 料金プラン
- GitHub リポジトリ
- abr-geocoder(デジタル庁 / GitHub)
- アドレス・ベース・レジストリ(デジタル庁)