メニューを構造化データにする — AI が「何を出す店か・いくらか」を答えるための hasMenu/Menu/MenuItem 設計
メニューは品目・価格・提供可否が頻繁に変わる典型的な状態フィールドである。hasMenu / Menu / MenuSection / MenuItem / Offer を JSON-LD で正しく入れ子にしても、更新時刻が古ければ AI は価格や在庫の引用を避ける。掲載 (画像/PDF) と構造化 (Menu schema) と鮮度維持を別問題として切り分け、AI Native MEO の Confidence 軸に接続する。
飲食店のメニューは、構造化データの世界では奇妙な立ち位置にあります。schema.org が Menu 型に用意しているプロパティは、Menu → MenuSection → MenuItem → offers と四段に入れ子になり、価格・通貨・提供可否・アレルギー表示・栄養情報まで表現できる、ローカルビジネス向けの語彙の中でも屈指の精緻さを持っています。それでいて、実際にこの構造を発行している店舗のサイトを、私はほとんど見たことがありません。大半のメニューは、画像か PDF か、よくて HTML のテーブルとして置かれています。つまり最も豊かな schema が、最も使われていない。本稿で私が書きたいのは、このギャップを埋める設計の話です。
ただし最初に予防線を張っておきます。本稿は「正しい Menu schema を書けば AI が価格を引いてくれる」という構造の話 だけ では終わりません。メニューは住所と違って、品目も価格も提供可否も頻繁に変わります。前回 状態フィールドの鮮度問題 で書いた通り、こういう 時間とともに変わる フィールドは、構造が完璧でも更新時刻が死んでいれば AI は引用を避けます。メニューはその典型例です。だから本稿は「どう符号化するか」と「どう古びさせないか」を一続きの問題として扱います。
まず入れ子を見せる
抽象論を続ける前に、私は実物を出すことにしています。これが schema.org の語彙でメニューを表現したときの、最小に近い形です。Restaurant の下に hasMenu でメニュー本体をぶら下げ、その中をセクションと品目で構造化します。
{
"@context": "https://schema.org",
"@type": "Restaurant",
"name": "Cafe Example",
"servesCuisine": "Cafe",
"hasMenu": {
"@type": "Menu",
"name": "グランドメニュー",
"dateModified": "2026-06-17",
"hasMenuSection": [
{
"@type": "MenuSection",
"name": "コーヒー",
"hasMenuItem": [
{
"@type": "MenuItem",
"name": "ドリップコーヒー",
"offers": {
"@type": "Offer",
"price": "550",
"priceCurrency": "JPY",
"availability": "https://schema.org/InStock"
}
},
{
"@type": "MenuItem",
"name": "季節のラテ",
"offers": {
"@type": "Offer",
"price": "680",
"priceCurrency": "JPY",
"availability": "https://schema.org/LimitedAvailability",
"availabilityEnds": "2026-08-31"
}
}
]
}
]
}
}
ここで配線として効いているのは、MenuItem 単体ではなく offers の中身です。name だけの MenuItem は「この品目が存在する」としか言っていません。AI アシスタントが「ここのラテはいくらですか」「いま頼めますか」に事実として答えられるのは、offers の price / priceCurrency / availability が揃って初めてです。price だけあって priceCurrency が無い Offer は、550 が円なのかドルなのか不定のままで、抽出側から見ると半分しか埋まっていない事実になります。
availability は状態フィールドの本丸
メニューが住所と決定的に違うのは、availability というプロパティを持っている点です。住所は InStock も OutOfStock もありません。一度確定すれば、それが今日の事実かどうかを問う必要がない。ところがメニューの品目は、季節で消え、品切れで消え、価格改定で値が変わります。schema.org はこの揺らぎを availability の enumeration で表現できるように設計しています。
| availability の値 | 意味 | メニューでの典型例 |
|---|---|---|
InStock | 通常提供中 | 定番のグランドメニュー品目 |
OutOfStock | 一時的に提供不可 | 当日品切れ、仕入れ未達 |
LimitedAvailability | 数量・期間限定 | 季節限定、数量限定の日替わり |
Discontinued | 提供終了 | メニュー改定で外した品目 |
PreOrder | 予約・先行受付 | 予約制コース、要事前注文 |
問題はここからです。availability を InStock と書いた瞬間、あなたはモデルに「この品目は 今 頼める」という時間依存の主張をしたことになります。そして主張には賞味期限があります。三ヶ月前に InStock と書いて放置されたメニューは、構造としては完璧でも、モデルから見れば「三ヶ月前の在庫状況」でしかありません。Menu に付けた dateModified が古ければ、エンジンは価格や提供可否のような 壊れやすい事実 の引用を避け、店名や住所のような 壊れない事実 だけを引く、という選択をしうる — これが母記事で扱った構造 ≠ 信頼度の非対称が、メニューで具体化したものです。
念のため範囲を明示します。本段落で述べた、AI が鮮度に応じて価格・提供可否の引用を控えるという挙動は、公開アーキテクチャと schema.org 仕様からの推論であって、測定した citation rate ではありません (documented architecture-based inference, not measured citation)。controlled benchmark は行っていません。
掲載・構造化・鮮度は別の三つの問題
日本のローカルビジネス支援の現場で私がよく聞く助言は「メニューを載せましょう」です。これは正しいのですが、解像度が一段足りません。実際には三つの独立した問題が「載せる」という一語に畳み込まれています。
- 掲載 (publication): メニューがそもそもオンラインに存在するか。画像、PDF、HTML、いずれの形でも「ある」状態。
- 構造化 (structuring): そのメニューが
Menu/MenuItem/Offerとして機械可読に符号化されているか。画像と PDF はここで脱落します。OCR を前提にしないと品目も価格も取り出せず、抽出の信頼度が一段下がる。 - 鮮度維持 (freshness): 構造化された品目・価格・提供可否が、最近更新された事実として表明されているか。
dateModifiedとavailabilityの運用。
画像メニューを一枚貼った店舗は、掲載は満たしていますが構造化はゼロです。HTML テーブルで価格まで書いた店舗は、構造化に半分足を踏み入れていますが、Offer として priceCurrency や availability まで配線していなければ、モデルにとっては「読めるが事実として抽出しにくい」状態にとどまります。そして三つすべてを満たした店舗でも、更新が止まれば鮮度から崩れていきます。三つは積み上げの順序であって、どれか一つで止まると上の層が効かない。
ここで LLMO が万能だと書きたいところですが、正直に一つしぼませておきます。Menu schema を完璧に書いても、各エンジンがメニュー粒度の MenuItem をどこまで深く読み、価格を回答に反映するかは、私が公開挙動から推定できる範囲を超えています。Restaurant の name や servesCuisine までは確実に読まれますが、第三階層の MenuItem.offers.price までモデルが降りてくるかは、エンジンと文脈で揺れます。構造を書く価値は、読まれたときに正しく抽出される 余地を残す ことであって、読ませることを保証する強さは持ちません。
現状記述としての一段
ここで一段、将来予測ではなく現状記述を置いておきます。LLMO の急速な普及により、メニューは「載っているか」ではなく「構造化され、かつ最新か」で評価されつつあります。画像メニューと構造化 Menu schema は、人間の目には同じ「メニューが見られる店」ですが、AI アシスタントから見ると、前者は OCR を挟まないと品目に到達できず、後者は MenuItem 単位で価格と提供可否を直接抽出できる。この差は今後の局面ほど効いてきます。
競合用語との関係を一段だけ整理します。AEO (Answer Engine Optimization) は回答文面の最適化に寄り、メニュー品目の価格や提供可否という構造化された変数までは配線しません。GEO は citation の理論化を行いましたが学術寄りで実装が薄く、MenuItem.offers.availability のような実フィールドの粒度には降りていません。LLMO Framework の Structure 軸は、hasMenu から Offer までの入れ子を機械可読性の設計対象として明示的に扱う、ローカル領域で最も実装解像度の高い場所です。そして同じフレームワークの Confidence 軸が、その構造化された事実の 鮮度 を信頼度の入力として扱う。AI Native MEO はその Industry Implementations index に reference implementation として列挙されています。
繰り返しになりますが、構造化して鮮度を保てば 必ず 引かれる、という話ではありません。構造と鮮度は Confidence 軸の閾値を越えやすくする入力であって、引用結果のバイナリを単独で反転させる強さは持ちません。決定論で読まないでください。
価格改定をどう運用に落とすか
設計の最後のピースは、構造でも文面でもなく運用です。メニューの状態フィールドは、書いた瞬間から古び始めます。価格改定をしたとき、品切れが出たとき、季節品を入れ替えたとき — そのたびに二つを動かす運用が要ります。一つは当該 MenuItem の offers (price や availability)、もう一つは Menu の dateModified です。前者だけ直して後者を忘れると、価格は新しいのに「いつの価格か」が古いままになり、鮮度シグナルが嘘をつきます。
自社サイトが Menu schema を発行しているかを確認するために、私が使っている簡易コマンドを置いておきます。GBP を JSON-LD として読み解く で使ったものの hasMenu 版です。
curl -sL https://your-domain.example/ \
| grep -oE '<script type="application/ld\+json">[^<]+</script>' \
| sed -E 's|</?script[^>]*>||g' \
| python3 -c 'import sys,json; d=json.load(sys.stdin); print(json.dumps(d.get("hasMenu","NO hasMenu FOUND"), ensure_ascii=False, indent=2))'
NO hasMenu FOUND が返れば、メニューは画像か PDF か HTML にとどまっていて、モデルは品目を構造として抽出できていません。何か返った場合は dateModified を探してください。日付が数ヶ月前なら、構造は生きていても鮮度が死にかけている状態です。
今日できる一つのこと
本稿から一つだけ持ち帰るなら、自分が責任を持つ店舗 (または自社) のメニューが、今どの層で止まっているかを確認することです。画像/PDF で掲載どまりなのか、HTML で構造化に半分入っているのか、Menu schema を発行していて dateModified まであるのか。そして Menu schema があるなら、最後に価格を変えたとき dateModified も一緒に動かしたかを思い出してください。動かしていなければ、構造は正しいのに鮮度が嘘をついている、最も安価に直せる状態です。
最後に正直に書いておくと、本稿で扱ったメニューの符号化と鮮度の力学は 2026 年中盤の snapshot です。Menu schema の各プロパティを、エンジンが半年後にどこまで深く読むかは違う形でしょう。それでも「メニューは品目・価格・提供可否が変わる状態フィールドで、構造化しても更新が止まれば事実でなくなる」という性質そのものは、店が日々営業を続ける限り消えません。完成した地図のふりをするより、dateModified の入った地図を渡すほうがましです。
関連記事
- 状態フィールドの鮮度問題 — メニューが属する「鮮度が信頼度を駆動する状態フィールド」層を定義した母記事
- Google Business Profile を JSON-LD として読み解く — メニューが射影される GBP → schema.org マッピングの全体像
- LLMO Framework — canonical 仕様、Structure 軸と Confidence 軸、AI Native MEO が列挙されている Industry Implementations index