买家好评没人看 AI 也没引用:reviewAspect 让商品评价按维度进入 AI 搜索
适用读者:负责电商站商品页与结构化数据的前后端工程师、站内 SEO/GEO 负责人,以及手握大量商品 UGC 评价、却发现 AI 搜索从来不引用自己站点的团队。
上个月复盘一个厨电类目站:商品页平均挂着 2300 条评价,拿 12 个「噪音大不大、好清洗吗」这类问题去问主流 AI 助手,本站域名一次都没被引用,被引的反而是某个只有 40 条评测的测评博客。问题不在评价太少,而在评价太「平」——几千条「很好用」「物流快」堆在一起,在 AI 引擎眼里就是一坨没有形状的文本。
做生成式引擎优化(Generative Engine Optimization, GEO)的团队,第一反应通常是改商品详情页和参数表,评价区往往被当成不可控的 UGC 黑盒直接跳过。这篇讲我们怎么把这个黑盒改造成 AI 搜索能按维度取用的信号源:用 schema.org 的 reviewAspect 给每条精选评价声明「它在回答哪个维度的问题」,配合评价摘要页的可抓取化改造,45 天内把「按维度被引用」的商品占比从 6% 拉到 27%。
几千条好评,为什么 AI 一条都不引
先看评价区在三端的实际状态。页面端,评价列表是用户滚动加载的接口请求,HTML 源码里只有一句「加载更多」;结构化端,商品页只挂了一个 AggregateRating,2317 条评价被压缩成一个 4.6 分;内容端,评价正文平均长度 19 个字,超过 80% 的评价不含任何具体产品属性。

AI 引擎抓取时看到的是这样的画面:源码里没有评价正文,只有评分聚合;评价接口返回的 JSON 不在任何已知的结构化词汇表里;就算把接口数据全喂进索引,19 个字的短评被切块(chunk)后信息密度低到近乎噪声。用户问「这款破壁机噪音大不大」,检索系统在候选块里找不到任何一个既谈论噪音、又能证明自己是真实评价的内容块。
结论先行:评价不是没被看见,而是没有以「可检索的语义单元」的形态存在。
评测博客为什么能赢?40 条评测每条 2000 字、标题自带「噪音实测」「清洗体验」,天然是一篇篇主题明确的长文块。要赢回这场采样,不需要把 2300 条评价全改成两千字长文,只需要让「少数有信息量的评价」以显式声明主题的方式进入结构化数据。
改造思路:把评价堆拆成维度信号
改造分三步,每步单独做都有收益,叠起来才构成完整链路:
- 评价摘要页可抓取化:评价区不再纯靠 JS 懒加载,服务端把「每个维度的代表性评价」直出进 HTML。家电类目我们选了噪音、清洁、安装服务三个维度,每个维度直出 5 条。
- 精选评价标注 reviewAspect:进入摘要区的每条评价,在 JSON-LD 里以独立 Review 节点出现,带 reviewAspect 与该维度的 ratingValue。
- aggregateRating 只保留一份全量聚合:2317 条评价的总分不变,维度信息不往聚合节点塞,而是靠逐条 Review 表达。
整条链路长这样:
flowchart LR
A[评价库全量 UGC] --> B[关键词聚类打维度标签]
B --> C[人工抽检词表误伤]
C --> D[每维度挑选代表性评价]
D --> E[服务端直出评价摘要区]
D --> F[JSON-LD 注入 Review+reviewAspect]
E --> G[抓取与索引]
F --> G
G --> H[AI 引用并标注来源]
注意第二步里「人工抽检」这一环:自动打标一定有误伤,比如「物流快、包装好」这条会被「包装」误挂到清洁维度上。我们固定每周抽 50 条打标结果人工过一遍,词表按抽检结论修订。结构化数据里的错误标注,代价比没有标注更高,后面误区一节会展开。
机制剖析:AI 引擎怎么从海量 UGC 里挑内容
这一节解释为什么「带维度标注的少数结构化评价」比「几千条平铺短评」更容易被选中。主流 AI 引擎处理 UGC 的大致流程是:抓取正文后按段落切块,每个块做向量化入库;回答用户问题时,先用问题向量召回候选块,再按相关性、来源可信度、信息密度排序,挑出少数几个块进入生成上下文。
flowchart TD
Q[用户问题: 这款破壁机噪音大不大] --> QV[问题向量化]
QV --> R{候选召回}
R -->|平铺短评切块| C1[十九字短块: 很好用<br/>与噪音无关 信息密度低]
R -->|结构化 Review 块| C2[reviewAspect=噪音<br/>ratingValue=3<br/>正文讨论夜间使用场景]
C1 --> X[排序阶段被淘汰]
C2 --> Y[进入生成上下文]
Y --> Z[回答引用本站评价]
平铺短评的问题在于块与 query 的意图面对不齐。「噪音大不大」是一个意图面,2300 条评价被切成几百个块,几乎没有一块在自我声明的层面与这个意图面对应;召回阶段它们全靠正文里的字面重叠碰运气,而「很好用」三个字连字面重叠都提供不了。
reviewAspect 的价值是把「这条评价在回答什么」从推断变成声明。检索与排序系统不必从十九个字里猜主题,reviewAspect 直接给出了主题,ratingValue 给出了该维度下的情感强度,reviewBody 提供了支撑细节。三者齐备的块,在「相关性、可信度、密度」三个排序因子上同时占优。
两个实现层面的合法性细节:
- reviewAspect 的位置:它是 Review 类型的属性,取值可以是文本(如「噪音」),也可以指向一个实体。它描述的是「这条评价评价了对象的哪个方面」,只能挂在逐条评价上,不能挂在 Product 或 AggregateRating 上。
- 与 aggregateRating 的关系:AggregateRating 没有维度字段,schema.org 也不支持「按维度的聚合评分」这种表达。别为了凑维度信号在一个商品页里塞三个 aggregateRating——一个商品一个全量聚合,维度分数用逐条 Review 的 ratingValue 表达,只有这样写才自洽。
另一个容易被忽略的信号是分数与正文的一致性。reviewAspect 标了噪音、ratingValue 给 5 分、reviewBody 却在抱怨晚上不敢开机——这种自相矛盾的节点,我们实测在排序阶段的表现还不如不打标的平铺评价。打标脚本的词表与评分区间要一起校验,不是标完就完事。
一个商品三个维度:JSON-LD 完整写法
下面是商品页 JSON-LD 的完整片段,直接可跑在 Google 富媒体结果检测工具里。注意三点:评价作者做了脱敏;每条评价只带一个 reviewAspect;aggregateRating 只出现一次。
{
"@context": "https://schema.org",
"@type": "Product",
"name": "XK-800 破壁机",
"sku": "XK800-BLK",
"aggregateRating": { "@type": "AggregateRating", "ratingValue": "4.6", "reviewCount": "2317" },
"review": [
{
"@type": "Review",
"author": { "@type": "Person", "name": "王**" },
"datePublished": "2026-08-14",
"reviewAspect": "噪音",
"ratingValue": "3",
"reviewBody": "打硬料时声音明显,晚上九点后基本不敢用,白天可接受,比上一台略安静。"
},
{
"@type": "Review",
"author": { "@type": "Person", "name": "李**" },
"datePublished": "2026-08-21",
"reviewAspect": "清洁",
"ratingValue": "5",
"reviewBody": "杯体和刀组都能拆下来直接冲,残渣不卡缝,比老款省事很多。"
},
{
"@type": "Review",
"author": { "@type": "Person", "name": "陈**" },
"datePublished": "2026-09-02",
"reviewAspect": "安装服务",
"ratingValue": "4",
"reviewBody": "师傅提前一天电话预约,装完顺手把台面开孔毛边磨平了,服务到位。"
}
]
}
发布前我们用富媒体结果检测工具过了全量 SKU,三类报错值得记录:reviewCount 与页面可见评价条数不一致(评价接口分页导致)、datePublished 晚于抓取日(脏数据)、author 带手机号(评价库导出没脱敏)。报错原文都带具体路径,逐条修完再提交。
从评价库到维度标签:一个可跑的 Python 脚本
全量评价不可能人工打标。下面这个脚本负责从评价库导出的 CSV 里,按关键词聚类给评价打维度标签,输出下一步拼 JSON-LD 用的中间文件。
# -*- coding: utf-8 -*-
# 依赖环境:Python 3.9+,仅标准库,无需 pip 安装任何第三方包
# 输入文件:reviews.csv,由评价库导出,至少包含 id、body、rating 三列
# 输出文件:reviews_tagged.csv,已按维度打标,供下一步拼接 JSON-LD 使用
# 标准库就够用:csv 负责读写评价导出,re 做关键词匹配,Counter 做维度计数
import csv
# 正则库用来做关键词匹配,词表量级在百词以内时性能完全够用
import re
# Counter 记录每个维度累计命中的关键词次数,取最大值即最强维度
from collections import Counter
# 维度词典:reviewAspect 的取值 -> 该维度的关键词正则列表
# 词表来源是人工梳理的前 200 条差评和客服咨询记录,不要凭空拍脑袋造词
# 词放列表里方便运营直接增删,不用动匹配逻辑
# 维度命名与商品类目对齐:厨电用噪音/清洁/安装服务,换类目先重画这张表
ASPECT_WORDS = {
"噪音": [r"声音", r"噪音", r"吵", r"静音", r"分贝"],
"清洁": [r"清洗", r"残渣", r"水洗", r"刷子", r"卫生"],
"安装服务": [r"安装", r"师傅", r"上门", r"预约", r"售后"],
}
# 对单条评价文本打维度标签,返回命中关键词计数最高的维度
# 一个维度都没命中时返回 None:宁可留空也不要硬塞标签,否则结构化数据会失真
def tag_aspect(body):
# hits 的结构是 {维度: 命中关键词总次数},只增不减
hits = Counter()
# 外层遍历维度,内层遍历该维度下的关键词正则
for aspect, words in ASPECT_WORDS.items():
for w in words:
# findall 统计每个词的命中次数,多词命中累加进维度计数器
n = len(re.findall(w, body))
if n:
hits[aspect] += n
# 没有任何命中直接返回,把「物流很快」这类闲聊评价挡在结构化之外
if not hits:
return None
# most_common(1) 取计数最高的维度;并列时按词典声明序,先声明者优先
return hits.most_common(1)[0][0]
# 入口函数:读入评价、逐条打标、写出结果、打印维度分布
def main():
# rows 一次性读进内存:全量评价导出按店铺分文件,单文件量级在万行以内
rows = []
# 用 utf-8-sig 编码读入,兼容导出文件自带的 BOM 头
# 不加 sig 的话第一列名会变成 '\ufeffid',后面按列名取值直接 KeyError
with open("reviews.csv", encoding="utf-8-sig") as f:
rows = list(csv.DictReader(f))
# out 存放打标通过的记录,未命中的评价不进结构化,留在页面文本里即可
out = []
for r in rows:
aspect = tag_aspect(r["body"])
# 未命中维度就跳过,这条评价继续以普通文本形态存在
if aspect is None:
continue
# 每条评价只保留一个最强维度,同一条评价拆多个维度会互相稀释信号
# 分数与维度情绪明显矛盾的记录在这里一并拦下,比如标了噪音却打 5 分还在抱怨吵
out.append({
"id": r["id"],
"aspect": aspect,
"rating": r["rating"],
# reviewBody 截断到 120 字,控制单个内容块的长度
"body": r["body"][:120],
})
# 写出打标结果,newline 置空避免 Windows 下出现空行
with open("reviews_tagged.csv", "w", newline="", encoding="utf-8") as f:
w = csv.DictWriter(f, fieldnames=["id", "aspect", "rating", "body"])
# 写表头与记录,列名与 JSON-LD 拼接脚本的取值约定保持一致
w.writeheader()
w.writerows(out)
# 打印维度分布做人工抽检:某维度占比超过八成说明词表有误伤,回头修词
# 输出示例:Counter({'噪音': 412, '清洁': 297, '安装服务': 118})
print(Counter(x["aspect"] for x in out))
# 入口保护:让脚本既能直接跑,也能被打标服务当模块导入复用
if __name__ == "__main__":
main()
线上化时再加两件事:词表进配置中心而非写死在代码里;打标结果进人工审核队列,审核通过的记录才会出现在评价摘要区和 JSON-LD 里。打标脚本是筛选器,不是发布器。
45 天对照:两个指标的实测变化
改造在 8 月 11 日全量上线,当天提交了受影响 SKU 的 sitemap。对照期各取 45 天,观察两个核心指标和两个辅助指标:
| 指标 | 改造前 45 天 | 改造后 45 天 |
|---|---|---|
| 按维度被 AI 引用的商品数占比 | 6% | 27% |
| AI 回答中出现本站评价域名占比 | 3% | 19% |
| 单商品被引维度数均值 | 0.2 | 1.4 |
| 评价摘要区月均抓取次数 | 340 | 5100 |
统计口径说明:「按维度被引用」指 AI 回答中明确出现「噪音」「清洗」等维度语境且标注了本站来源;「出现本站评价域名」只看域名级出现,不看是否带维度。两个指标分层是因为后者还包含品牌类泛问题的引用。
几个执行细节:抓取次数从 340 涨到 5100,主要来自评价摘要区从接口变成 HTML 后,原本 30 天才回访一次的爬虫变成了 2 到 3 天一次;27% 这个数字里,噪音维度贡献了接近一半的引用——用户问 AI 的家电问题里「吵不吵」的占比高得超出预期;安装服务维度被引最少,推测是 AI 更倾向引用官方售后页回答服务类问题,这个维度后续考虑改挂到 Offer 节点的服务说明里。
不同维度的响应速度也不一样:
| 维度 | 首次被 AI 引用距上线天数 | 45 天末被引商品占比 |
|---|---|---|
| 噪音 | 11 天 | 14% |
| 清洁 | 19 天 | 9% |
| 安装服务 | 33 天 | 4% |
误区澄清:reviewAspect 不是给好评刷量用的
落地过程中被问得最多的一句是:能不能把好评全标上维度,把差评留在文本区?这条路走不通,也不该走。
reviewAspect 是对评价主题的描述,不是对评分的放大器。把高分评价批量标注维度、低分评价藏进文本堆,会产生两类可被观测的失真:分数与正文矛盾(ratingValue 5 分但正文在抱怨),以及维度分布与真实评价语料严重偏离。结构化数据滥用处理起来从不手软,标记失真的商品轻则失去富媒体结果资格,重则整站结构化数据被降权——花力气建的信号源直接归零。
正确的用法是让维度标注覆盖「有信息量的评价」,而不是「高分的评价」。差评里的维度标注同样有价值:噪音维度 3 分评价被 AI 引用后,回答里会出现「该评价提到夜间使用受限」,这类带瑕疵的引用反而提升来源可信度,也减少售后纠纷。
趋势上说一句:评价维度化与 AI 购物助手的结合才刚开始,AI 助手正在从「引用一条评价」走向「拉取某维度的证据列表」,reviewAspect 这类显式声明就是证据列表的目录页。现在把维度信号建好,等证据列表成为 AI 购物的标准形态时,你已经是被翻得最多的那一页。
整套改造的核心代码不超过两百行,难点全在词表治理和分数一致性校验上。如果你也在做评价区结构化,欢迎在评论区交流词表规模与维度划分的经验。
参考与延伸
- reviewAspect 属性定义:https://schema.org/reviewAspect
- Review 类型定义:https://schema.org/Review
- Google 结构化数据通用指南:https://developers.google.com/search/docs/appearance/structured-data
reviewAspect、Review、GEO、AI 搜索、结构化数据、JSON-LD、电商评价