补上价格区间和支付方式之后:priceRange 与 paymentAccepted 对门店 AI 推荐位的影响
适用读者:连锁服务门店(维修、家政、口腔诊所这类)做官网和落地页的前端 / SEO 工程师;门店页已经在铺结构化数据(structured data),却在生成式引擎优化(Generative Engine Optimization, GEO)的验收里发现,AI 被问到"大概多少钱""能不能扫码"时要么答错、要么干脆不答的那批人。
用户在 AI 搜索里问"XX 区补个牙大概多少钱",回答列了三家机构,两家的价格栏写着"约 300-800 元",我们那家是空的。门店页的 NAP(名称、地址、电话,即 Name-Address-Phone)三项一个不缺,评分和营业时间也早挂上了。真正缺的是价格区间(price range)和支持的支付方式(payment accepted)这两条。补字段本身花了两天,有意思的是后面 45 天的观测。
改造前,AI 对价格类问题一律绕着走
先说口径。样本是某连锁服务品牌的 26 个门店页,化名"H 家修",做维修加口腔两类业务。我们固定了 12 组提问模板,覆盖"多少钱""人均多少""能扫码吗""支持刷卡吗"这些真实用户会问的句式,每周跑两轮,两个通用大模型加一个 AI 搜索入口,人工判定回答里有没有价格数字、有没有点名到门店。基线跑两周,改造后跟了 45 天。这是我们自己的受控采样记录,样本量小,不能当行业统计看,看趋势就好。

基线两周的结果挺扎心:208 次提问机会里,只有 31 次点名到我们的门店;出现价格数字的 12% 里,一半还是模型自己从正文"起价""优惠"里抠出来的残缺数字。支付类问题更惨,0 次命中——页面正文压根没提过微信和支付宝,模型无米下锅。
改造前的门店节点长这样,只有 NAP 加营业时间,价格与支付一个字段都没写。
// 改造前的门店页结构化数据,内联在页面 <head> 里
// 环境:静态站点(Hugo 构建),无运行时依赖
// 校验工具:Rich Results Test、validator.schema.org
// 说明:jsonc 里的 // 注释仅为讲解,落盘时删掉即为合法 JSON
{
"@context": "https://schema.org",
// Dentistry 是 LocalBusiness 的子类,维修门店换 HomeAndConstructionBusiness
"@type": "Dentistry",
"name": "H 家修口腔(某某店)",
"telephone": "+86-571-8888-0000",
// 门店页 URL,AI 引用时的落点,必须与 @id 同源
"url": "https://example.com/stores/hangzhou-xh-01",
"address": {
"@type": "PostalAddress",
// 地址四项拆开写,别糊成一整个字符串
"streetAddress": "某某路 100 号 2 层",
"addressLocality": "某市",
"addressRegion": "某省",
"postalCode": "310000",
"addressCountry": "CN"
},
// 营业时间规格,历史文章讲过,这里不展开
"openingHoursSpecification": {
"@type": "OpeningHoursSpecification",
"dayOfWeek": "Monday",
"opens": "09:00",
"closes": "18:00"
}
// 到这里就结束了:没有 priceRange
// 没有 paymentAccepted,也没有 currenciesAccepted
}
价格区间怎么写,直接决定 AI 抄不抄
priceRange 在 Schema.org 上是 Text 类型,不是数字,也不是结构化对象。这条决定了它能表达的自由度,也决定了它容易被写废。我们内部试过五种写法,AI 的抄法完全不同。
| priceRange 写法 | AI 回答里的呈现 | 我们的判定 |
|---|---|---|
¥120-¥260 |
原样引用"约 120-260 元" | 可用,命中率最高 |
$$ |
表述成"中档价位",无数字 | 能过校验但答不出钱数 |
120 |
抽成"120 元",被当成固定价 | 误读,弃用 |
面议 / 电议 |
不引用,价格栏留空 | 等于没写 |
新客 99 元起 |
抽成"99 元",与正文常态价冲突 | 制造冲突,弃用 |
三点经验。区间要写闭区间,两端都给,模型才敢说"大概多少";只给下限,它会当固定价念。币种单独用 currenciesAccepted 声明,走 ISO 4217 的三字母码,写 CNY 而不是 RMB 或 ¥ 这类口语写法。支付方式用 paymentAccepted 写具体渠道名,"微信支付、支付宝、现金、银行卡"这种;写"多种支付方式支持"是白写,模型抽不出任何可断言的东西。
完整节点长这样,注意 priceRange 挂在门店自己身上。
// 改造后的门店节点:新增三个字段,其余不动
// 环境同上;校验同上;落盘前记得删掉注释
{
"@context": "https://schema.org",
"@type": "Dentistry",
"@id": "https://example.com/stores/hangzhou-xh-01#store",
// @id 用页面 URL 加片段,跨页面交叉引用时不会认错实体
"name": "H 家修口腔(某某店)",
// 电话带 +86 国际区号,AI 回答里常被渲染成可拨打链接
"telephone": "+86-571-8888-0000",
// url 与 @id 同源,避免同一门店被判成两个实体
"url": "https://example.com/stores/hangzhou-xh-01",
"address": {
"@type": "PostalAddress",
"streetAddress": "某某路 100 号 2 层",
"addressLocality": "某市",
"addressRegion": "某省",
"postalCode": "310000",
"addressCountry": "CN"
},
// 价格区间:闭区间写法,两端都要有
// 区间语义是"常态人均",不是活动价
"priceRange": "¥120-¥260",
// 币种:ISO 4217 三字母码,多个用逗号分隔
"currenciesAccepted": "CNY",
// 支付方式:Text 类型,没有官方枚举
// 写用户真正会用的渠道名,别写"多种方式"
"paymentAccepted": "微信支付, 支付宝, 银行卡, 现金",
// 父级组织只做兜底,门店级字段优先
"parentOrganization": {
"@type": "Organization",
"name": "H 家修",
"url": "https://example.com"
}
}
AI 到底凭什么答或不答价格
机制剖析:属性级抽取、置信度打分与缺字段降级
AI 搜索的流水线大致分三段:召回、片段化、生成。结构化数据在第二段就被单独拎出来了——它密度高、噪声低,解析器会把 JSON-LD 里的键值对摊成一张"事实表",跟正文切片分开存。
关键在第三段:属性级抽取。 模型不是看到 @type: LocalBusiness 就整体给分,而是先把问题意图映射成需要的属性槽位。问"多少钱"→ 找 priceRange 或 priceSpecification;问"能扫码吗"→ 找 paymentAccepted。槽位有值且和正文表述一致,置信度拉满,回答里会出现数字加点名;槽位有值但和正文冲突(比如结构化写 120-260,正文写"人均 300"),置信度掉一档,模型通常只挑一个值说,还常常挑错那个。
槽位空着的时候是第三种处理:降级。实测下来模型有两个常见动作——要么从正文里硬猜一个(猜不出来就跳过价格,只说"建议到店咨询"),要么把这一栏从回答里整个抹掉。抹掉比猜错更常见,也更隐蔽:门店还留在推荐位里,但价格栏空白,用户扫一眼就划走了。这也是改造前 31 次点名、转化却几乎为零的原因。
flowchart TD
A["用户提问:补牙大概多少钱"] --> B["意图识别:价格类"]
B --> C{"priceRange 槽位"}
C -->|"有值"| D{"与正文一致?"}
C -->|"缺失"| E["降级路径"]
D -->|"一致"| F["高置信:引用数字 + 点名门店"]
D -->|"冲突"| G["中置信:挑一个值,常挑错"]
E --> H["从正文硬猜或跳过价格"]
H --> I["推荐位里价格栏空白"]
I --> J["用户划走,转化归零"]
45 天观测记录:改了什么、涨了多少
字段上线分两批:第 1 周铺 12 家试点门店,第 3 周全量 26 家。中间第 2 周做过一次写法修正(把 120 改成闭区间、多种支付方式 改成渠道名),所以下表取的是修正后满 30 天的稳定期,跟基线两周对比。
| 指标 | 改造前(基线 2 周) | 改造后(30 天稳定期) | 变化 |
|---|---|---|---|
| 回答中出现价格数字的比例 | 12% | 57% | +45pt |
| 门店被点名进入推荐位次数 / 208 次机会 | 31 | 89 | +187% |
| "多少钱"类问题给出明确答复的比例 | 8% | 63% | +55pt |
| 支付类问题能说出微信 / 支付宝的比例 | 0% | 71% | 从无到有 |
| 价格数字与页面标注不一致的次数 | 9 | 2 | -78% |
| 门店页来自 AI 入口的会话数(同周期环比) | 100(基准) | 168 | +68% |
最后一行的会话数我们只当参考,中间夹了投放和节假日的噪音。真正干净的信号是第二行和第三行:点名次数涨了近两倍,且价格不再缺栏。
分业务看还有个有意思的差别。口腔类门店的"多少钱"回答率高于维修类,我们猜是因为口腔的价格区间更标准化,正文也常有价目表佐证;维修类的"上门费 + 工时费"是复合价格,一个 priceRange 塞不下,模型经常答了区间又补一句"具体看师傅报价"。
| 业务类型 | 价格类回答率 | 常见失败模式 |
|---|---|---|
| 口腔诊所 | 74% | 多,价目表与区间冲突时挑错值 |
| 家电维修 | 52% | 复合计价,答完区间还要加限定语 |
| 家政保洁 | 61% | 按小时和按面积两套价,区间容易串 |
踩过的坑,以及怎么绕开的
优惠价千万别写进 priceRange。 第一版有门店写"新客 99 元起",模型把 99 当常态价念,用户到店发现是 260,投诉直接打到客服。区间的语义就是常态人均,活动价走 hasOfferCatalog 里的报价目录(OfferCatalog)或 SpecialOffer,别混。
多门店场景要防父级覆盖。 我们的模板最初把 priceRange 写在父级 Organization 上,子门店不写,结果 6 家定位偏高的门店被答成低价。修复方式很土:门店节点必须自带 priceRange,父级只留兜底值。
flowchart LR
O["父级 Organization 兜底 priceRange"] --> B1["门店节点自带 priceRange"]
O --> B2["门店节点不写"]
B1 --> R1["门店级值优先,回答与到店价一致"]
B2 --> R2["沿用兜底值,高端门店被答成低价"]
别拿富结果测试工具当验收标准。 priceRange 不参与 Google 的富结果判定,跑 Rich Results Test 会告诉你"未检测到富结果",这是正常的,它本来就只为语义抽取服务。我们内部改用两条硬指标验收:一是结构化数据里三个字段的覆盖率(后来上了脚本扫),二是固定提问模板的人工抽检。
# 环境:Python 3.10+,仅标准库(json / pathlib),无第三方依赖
# 用途:批量扫门店页 JSON,统计三个字段的缺失情况
import json
import pathlib
# 三个字段一起查,缺一个就进不了 AI 的属性槽位
FIELDS = ["priceRange", "paymentAccepted", "currenciesAccepted"]
# 只统计门店节点,父级 Organization 不参与
STORE_TYPES = {"Dentistry", "HomeAndConstructionBusiness", "LocalBusiness"}
def scan(root):
# root 下每家门店一个 json 文件,文件名即门店 ID
miss = {f: [] for f in FIELDS}
for f in pathlib.Path(root).glob("*.json"):
data = json.loads(f.read_text(encoding="utf-8"))
# 节点可能是数组,逐个看
nodes = data if isinstance(data, list) else [data]
for n in nodes:
# 跳过兜底用的父级节点,避免把总部的缺失算进门店铺设率
if n.get("@type") not in STORE_TYPES:
continue
for k in FIELDS:
# 空串也算缺失:值存在但抽不到内容
if not str(n.get(k, "")).strip():
miss[k].append(f.stem)
# 返回结构:字段名 -> 缺失门店 ID 列表
return miss
# 覆盖率 = 1 - 缺失数 / 门店总数,日报里画成折线看补齐进度
print(json.dumps(scan("./stores"), ensure_ascii=False, indent=2))
误区和接下来的趋势
一个常见误会是 paymentAccepted 有官方枚举值。没有,它是 Text,写 "Cash"、"现金"、"微信支付" 都能过校验,校验器不拦不代表模型读得懂。同理,能过校验和能被引用是两件事,这是做 GEO 时最容易混淆的地方。
另一个误会:觉得这类字段属于"SEO 细节",排期永远排不上。从这次数据看,属性槽位的完整度在 AI 搜索里是被直接折算进推荐位竞争力的——信息全的机构被优先点名,缺栏的被挤到后面或者干脆整栏留白。
趋势上我倾向于认为,AI 搜索正在从"认实体"走向"答属性"。前一阶段拼的是名字出现频次和实体对齐,接下来拼的是属性槽位填得全不全、跟正文一不一致。priceRange 和 paymentAccepted 是最低垂的两个果子,值得先摘。
你们在门店页上还补过哪些"用户一定会问、页面从来不写"的字段?评论区聊聊,我挑几个有代表性的再跑一轮对比。
参考与延伸
- Schema.org priceRange 属性定义:https://schema.org/priceRange
- Schema.org paymentAccepted 属性定义:https://schema.org/paymentAccepted
- Schema.org currenciesAccepted 属性定义:https://schema.org/currenciesAccepted
- Schema.org LocalBusiness 类型定义:https://schema.org/LocalBusiness
GEO, AI优化AIO, 本地服务 AI 获客, LocalBusiness, priceRange, paymentAccepted, JSON-LD, Schema.org