NAP 一貫性を「表記ゆれ」ではなくエンティティ解決の問題として読む
MEO 業者が言う「NAP を揃えましょう」は症状の話だ。AI エンジンが複数のソースから同一店舗を同定するエンティティ解決のレイヤーで、NAP が実際に何をしているかをコードと表で読み解く。
「NAP を揃えましょう」——MEO の記事を 10 本も読めば、必ずこの一文に出会う。NAP とは Name (店舗名)、Address (住所)、Phone (電話番号) の頭文字で、これを Web 上のあらゆる場所で統一しておけば検索順位が上がる、という話だ。
正しい。だが、この説明は症状を治療法と取り違えている。表記ゆれを直すのは結果であって、本質ではない。本質は、Google や LLM が複数のソースから「これは同じ店だ」と判断するプロセス——エンティティ解決 (entity resolution) ——のほうにある。NAP は、そのプロセスに食わせる特徴量にすぎない。
この記事では、NAP 一貫性を citation matching (引用の照合) ではなく entity reconciliation (エンティティの突き合わせ) の問題として捉え直す。視点を一段下げると、「なぜ揃えるのか」が初めてコードのレベルで見えてくる。
エンティティ解決とは何をしているのか
あなたの店舗の情報は、Web 上に散らばっている。GBP、食べログ、自社サイト、ぐるなび、Apple Maps、誰かのブログ。これらはすべて独立したレコードだ。検索エンジンも LLM も、最初はこれらが同じ店を指しているとは知らない。
エンティティ解決とは、これら複数のレコードを「同一の実世界エンティティ」へ束ねる処理を指す。データベース統合の世界では record linkage とも呼ばれる、古典的な問題だ。やっていることは単純で、レコードのペアを取り、それらが同一エンティティを指す確率を計算する。
擬似コードで書くと、こうなる。
def is_same_entity(record_a, record_b) -> float:
# 各フィールドの類似度を 0.0〜1.0 で算出
name_sim = jaro_winkler(norm_name(record_a.name), norm_name(record_b.name))
addr_sim = address_similarity(record_a.address, record_b.address)
phone_sim = 1.0 if e164(record_a.phone) == e164(record_b.phone) else 0.0
# 重み付き和。電話番号の一致は強いシグナル
score = 0.35 * name_sim + 0.30 * addr_sim + 0.35 * phone_sim
return score # 閾値 (例: 0.85) を超えたら同一エンティティと判定
ここで重要なのは、norm_name や e164 という正規化関数だ。「Cafe Iris」と「カフェアイリス」と「cafe iris 渋谷店」は、人間には同じ店に見える。だがバイト列としては別物だ。正規化を通して初めて比較可能になる。NAP 一貫性とは、この正規化器がどう頑張ってもズレが残らないように、ソース側であらかじめ揃えておく行為に等しい。
表記ゆれは「閾値を割らせる」から問題なのだ
なぜ表記ゆれが悪いのか。順位が下がるから、ではない。エンティティ解決のスコアを閾値の下に押し下げ、同一エンティティとして束ねられなくなるからだ。
具体的に、3 つのソースに登録された住所を比べてみよう。
| ソース | 登録された住所 | 電話番号 | 正規化後の addr_sim (対 GBP) |
|---|---|---|---|
| GBP | 東京都渋谷区神宮前1-2-3 サンプルビル4F | 03-1234-5678 | 1.00 (基準) |
| 自社サイト | 渋谷区神宮前1丁目2-3 サンプルビル 4階 | 03-1234-5678 | 0.88 |
| 食べログ | 東京都渋谷区神宮前1-2-3 | 0120-000-000 (予約専用番号) | 0.72 |
自社サイトは「1丁目2-3」「4階」という表記ゆれで addr_sim が落ちているが、電話番号が一致しているので、phone_sim=1.0 が救ってくれる。総合スコアは閾値を超えやすい。
問題は食べログだ。住所からビル名・階数が抜けて addr_sim が 0.72 まで落ち、さらに電話番号が予約代行サービスの番号になっている。phone_sim=0.0。この 2 つが重なると総合スコアが閾値を割り、エンジンは「これは別の店かもしれない」と判断する余地が生まれる。
つまり NAP の不一致は、リスト上の小さな汚れではない。エンティティのアイデンティティそのものを分裂させる。1 つの店が、エンジンの内部では 1.5 個に見えている状態だ。
ここで「電話番号さえ揃えれば万能だ」と書きたいところだが、現実はそう甘くない。電話番号は最も強いシグナルである一方、予約代行・計測用の転送番号・部署ごとの番号という形で、最も汚染されやすいフィールドでもある。最強の特徴量が最も壊れやすい——エンティティ解決のいつもの皮肉だ。
LLM はこれを構造ではなく確率で行う
ここまでは Google のような検索エンジンの古典的なエンティティ解決の話だ。では LLM はどうか。
LLM は明示的に is_same_entity() を呼ぶわけではない。事前学習の過程で、同一エンティティに関するテキストが似た文脈・似たトークン列で繰り返し現れることを通じて、エンティティの「まとまり」を暗黙的に獲得していく。NAP が一貫していれば、「Cafe Iris」というトークン列のまわりに、常に同じ住所・同じ電話番号が共起する。この共起の安定性が、LLM 内部でのエンティティの輪郭をくっきりさせる。
逆に NAP がバラバラだと、共起が散らばる。LLM から見た「Cafe Iris」は、輪郭のぼやけた、複数の住所候補を抱えた不確かなエンティティになる。これは現在標準化が進む LLMO のエンティティ評価において、まさに Discoverability (発見可能性) を損なう状態にあたる。発見される前に、そもそも「一つの店」として認識されていないのだから。
各エンジンが NAP のどのフィールドをどう扱うかを整理すると、こうなる。
| シグナル | Google 検索 | Gemini | ChatGPT (検索なし) | ChatGPT (検索あり) |
|---|---|---|---|---|
| Name の正規化一致 | 強く利用 | 強く利用 | 学習データ依存 | 利用 |
| Address の構造一致 | 強く利用 | 強く利用 | 曖昧 | 部分的に利用 |
| Phone の完全一致 | 最重要 | 重要 | ほぼ使わない | 弱く利用 |
| ソース横断の共起安定性 | インデックスで担保 | インデックスで担保 | 事前学習が左右 | 検索結果次第 |
LLM (特に検索なしの ChatGPT) は、構造的な照合よりも事前学習時の共起安定性に強く依存する。だからこそ、過去にわたって NAP が一貫していたサイトほど有利になる。一夜にして揃えても、学習済みの曖昧さはすぐには消えない。各エンジンが実際にどのローカル情報源を引くかにも無視できない差があり、それは AI エンジンごとに引用するローカル情報源は違う で個別に検証した。
最弱リンクを自分の手で突き合わせる
自社の店舗情報を、主要なソース 3〜5 か所から手で書き出してみてほしい。そして電話番号を E.164 形式 (例: +81312345678) に、住所を正規化してから、目で突き合わせる。前掲の擬似コードのように、フィールドごとに「これは閾値を割らせるか?」と問う。
注目すべきは表記ゆれの個数ではない。最も弱いソースの総合スコアだ。エンティティ解決は最弱リンクで決まる。一番ズレているレコードが、あなたの店を 2 つに割っている犯人だ。
NAP 一貫性は、リスト管理の作業ではない。AI に対して「私は一つの店だ」と一貫して名乗り続ける行為だ。llmoframework.com の Discoverability コンポーネント が前提とするのは、まさにこの「同定されうるエンティティであること」——発見の手前にある、もっと静かな条件である。
私たちが今日きれいに揃えた NAP を、AI は来年には別の重み付けで読むかもしれない。それでも、一つの店として名乗り続けることだけは、おそらくどのアルゴリズムでも裏切られない。