AI 搜索里说得出引用你的哪一段:isBasedOn 与 citation 引用链的站内架构
销售在群里甩了张 AI 搜索的截图:客户问「V 厂这批阀门的泄漏率按什么标准测的」,答案里三个数字全对,出处却挂着某行业论坛三年前的转帖。我们那份原始白皮书排在结果第三位,一个字都没被点名。数字是从我们这儿出去的,功劳记在别人头上——问题不在内容写得好不好,在于机器读不出这批数字是从哪一页长出来的。
适用读者:手上有一批技术内容(白皮书、实测数据、规格说明)的企业官网维护者,以及负责结构化数据和信息架构的前端、SEO 同学。读完你能搭出一条从结论页回溯到源材料页的引用链,并知道它断在哪、怎么自检。
溯源断链长什么样
V 厂做工业阀门,官网今年春天改版。改版把 128 个技术页重排了一遍,PDF 白皮书拆成了 HTML 页,视觉和可读性都上去了。六周后开始出问题,而且是一起来的一批。

第一个现象是出处被抢。客户问泄漏率,答案数字正确,标注的来源却是论坛转帖——那帖子把我们的表格整段抄走,还保留了表格结构,机器读起来比我们的新版页面更"整齐"。第二个现象是问到第二层就答不上来:客户追问"谁测的、按哪个标准",模型含糊其辞,因为测试方法那段原本在 PDF 第 11 页,改版后搬去了独立的「质量体系」页,和产品页之间没有任何机器可读的关系。第三个现象最麻烦,同一个密封件参数在选型页和老版归档页上不一致,模型挑了老的那份。
排查的时候我们一度以为是收录问题。查完发现不是:sitemap 全交了,页面全部被抓,从产品页到白皮书页也有正常的 <a> 链接。真正缺的是声明——没有人告诉机器"这段结论是从那一页来的"。链接是给人跳、给爬虫爬的,不是给模型做溯源用的。这两件事经常被当成一回事,白费劲排查了两天才转过弯。
三个属性各回答什么问题
生成式引擎优化(Generative Engine Optimization, GEO)里讲引用链,绕不开 schema.org 的几个属性。它们的分工不同,填错了位置的后果也不一样。
| 属性 | 挂在谁身上 | 回答的问题 | 落错地方的后果 |
|---|---|---|---|
| isBasedOn | CreativeWork 及其子类型(TechArticle、Dataset) | 这篇结论是从哪份源材料改出来的 | 溯源指向第三方转帖 |
| citation | CreativeWork | 这篇把哪些东西当成了证据 | 证据页没被算进来 |
@id |
任意节点(JSON-LD 关键字,不是属性) | 谁是谁,跨页面是不是同一个东西 | 同一实体被拆成好几份 |
| about | CreativeWork | 这篇讲的核心实体是哪个 | 实体和页面对不上号 |
| sameAs | Thing | 站内实体对应站外哪个权威条目 | 外部知识库对不上号 |
isBasedOn 和 citation 方向是相反的。isBasedOn 从派生结果往源材料指,回答"我是从哪来的";citation 从当前作品往它提到的参考指,回答"我引了谁"。一篇实测报告可以同时有两条:isBasedOn 指向实验室原始数据集,citation 指向 GB/T 标准条目。混着用会出现"报告被标准派生"这种荒谬关系,机器不会报错,只会静默地建一条错链。
@id 是这套东西的地基。它不是 URL,是节点在全站范围内的标识符,同一实体在不同页面必须给同一个值。少了它,前面两个属性都退化成一串文本。
引擎侧的归并机制:@id 为什么比 URL 好用
要理解引用链为什么必须靠 @id,得先看答案引擎大致怎么走这几步。
抓取阶段,引擎拿到 HTML,抽正文,同时把 JSON-LD 里的节点单独收一份。这时候页面 URL 只承担"内容在哪",节点身份由 @id 承担。归并阶段是关键:不同页面里出现相同 @id 的节点,会被合成一个,字段互补。如果改用 URL 当身份,同一个阀门型号会因为 ?utm_source=、#spec 锚点、多语言前缀变成好几个实体,机器以为厂里有三种 V7。
检索和生成阶段,问题进来后召回候选片段,生成答案时再挑引用。能不能标得出出处,取决于片段所属节点有没有一条可解析的溯源路径:片段 → 文档节点 → 源材料节点 → 源材料 URL。这条链断任何一段,模型就退化成"我记得在哪看过",出处要么不给,要么给一个它记得更清楚的第三方页面。
有三点容易被忽略。其一,归并发生在节点层,不是页面层,所以源材料页本身也得是结构化节点,光有个 URL 不够。其二,语言版本要共用实体 @id,靠 @id 归并、靠 inLanguage 区分,别为每种语言造一套实体。其三,isBasedOn 不参与排序打分,Google 也从没说过它是排名因子。它影响的是"能不能被说清楚从哪来",这是两件事,混为一谈就会对效果失望。
站内引用链的架构
V 厂最后落地的结构分三层,源材料在最底下,结论层挂在它上面,实体层横穿两层做身份锚点。
flowchart BT
subgraph SRC["源材料层"]
DS["Dataset 实测数据集 /dataset/leak-2026q1"]
WH["Report 白皮书页 /whitepaper/valve-seal"]
STD["CreativeWork 标准条目 /std/gbt-13927"]
end
subgraph ART["结论层"]
A1["TechArticle 泄漏率实测报告"]
A2["TechArticle 选型指南"]
A3["TechArticle 质量体系说明"]
end
subgraph ENT["实体层"]
ORG["Organization @id=/#organization"]
PRD["Product 阀门 V7 @id=/product/v7#product"]
end
DS -- isBasedOn --> A1
WH -- isBasedOn --> A2
A1 -- citation --> STD
A2 -- citation --> DS
A3 -- citation --> STD
A1 -- about --> PRD
A2 -- about --> PRD
PRD -- brand --> ORG
横向看,实体层是那根穿线:A1、A2 都 about 同一个 PRD,PRD 再 brand 回 ORG,机器就知道两篇结论在讲同一个产品,而不是两个厂的两件事。纵向看,结论一律从源材料往上挂,方向不能反过来。
把链写进 JSON-LD
环境是 Python 3.10 以上,只用标准库。之所以用脚本生成而不是手写 JSON-LD,原因很实在:@id 要跨页面保持一致,人手写到第 40 页必然出现两种写法。
# 依赖:Python 3.10+,仅标准库 json
import json
# 规范域名,不带任何追踪参数
BASE = "https://v.example.com"
# 组织节点标识,全站只此一份
ORG = BASE + "/#org"
# based_on 传源材料 slug,cites 传被引用的条目
def article(slug, title, based_on, cites):
# 标识用 #a 片段,和页面 URL 区分开
return {
"@type": "TechArticle", "@id": f"{BASE}/tech/{slug}#a",
# isBasedOn 指向源材料,给节点而非裸字符串
"isBasedOn": [{"@type": "Dataset", "@id": f"{BASE}/d/{d}#d"} for d in based_on],
# citation 指向被引用的证据条目
"citation": [{"@type": "CreativeWork", "@id": f"{BASE}{c}#w"} for c in cites],
# about 挂回产品实体,供跨页归并
"about": {"@type": "Product", "@id": f"{BASE}/p/v7#p"},
"publisher": {"@id": ORG},
}
返回的节点由模板塞进 @graph,再注入页面的 ld+json 脚本块。两处要盯住:isBasedOn 数组里的 Dataset 用 #d 片段作标识,和它的页面 URL 分开;citation 指向标准条目时加了 #w 后缀。这样后面的校验能直接按标识查,不用猜字符串到底是 URL 还是片段。
引用链自检脚本
链写完要验。悬空引用是最常见的故障——结论页指了个 @id,全站根本没有那个节点,机器拿到一条死路。
# 依赖:Python 3.10+,仅标准库;用法 python check_chain.py "out/*.jsonld"
import json, glob
# 需要审计的引用字段
REF = ("isBasedOn", "citation", "about", "publisher")
# 合并全站图,@id 相同的节点按后覆盖前处理
def load(pattern):
nodes, edges = {}, []
for p in sorted(glob.glob(pattern)):
for n in json.load(open(p, encoding="utf-8"))["@graph"]:
nodes[n["@id"]] = {**nodes.get(n["@id"], {}), **n}
for k in REF:
# 引用值可能是单节点也可能是数组
seq = n[k] if isinstance(n[k], list) else [n[k]]
edges += [(n["@id"], k, i) for i in seq]
return nodes, edges
# 悬空:目标不在大图里;方向错:isBasedOn 指回自己
def audit(nodes, edges):
bad = []
for src, k, i in edges:
tid = i.get("@id", i) if isinstance(i, dict) else i
if tid not in nodes or (k == "isBasedOn" and tid == src):
bad.append((src, k, tid))
return bad
load 返回的 nodes 是全站实体字典,audit 拿它当字典查悬空。注意这里只认 @id,不认 URL——如果源材料页没生成结构化节点,它就不会出现在 nodes 里,链一样会被判悬空,这正是我们希望暴露的问题。
流程对应下面这张图,每晚在 CI 里跑一遍就够了。
flowchart TD
A["收集全站 JSON-LD 文件"] --> B["按 @id 合并成一张大图"]
B --> C{"引用目标在大图里吗"}
C -- "不在" --> D["记一条悬空引用, 打印源页"]
C -- "在" --> E{"方向对不对"}
E -- "isBasedOn 指回自己" --> F["记一条方向错误"]
E -- "正常" --> G["统计每份源材料被几条结论引用"]
G --> H["输出被引用页清单"]
第三周我们用它扫出 11 条悬空引用,其中 7 条是多语言版本的 #dataset 后缀写成了 #data,还有 4 条指向了改版后已下线的旧 PDF 路径。这种错误肉眼看 HTML 看不出来。
改造前后差了多少
| 观察项 | 改造前(第 0 周) | 改造后(第 8 周) | 备注 |
|---|---|---|---|
带 @id 的实体节点 |
0 | 274 | 128 个页面统一生成 |
| isBasedOn 声明 | 0 条 | 96 条 | 全部指向站内源材料 |
| citation 声明 | 3 条(手写,方向错) | 142 条 | 含标准条目与自有数据集 |
| 悬空引用 | 无法统计 | 0(第 3 周修完 11 条) | 自检脚本每晚跑 |
| 两周内被 AI 回答带出处的次数 | 6 | 27 | 固定 20 个问题人工登记 |
| 出处被指向第三方转帖 | 9 次 | 2 次 | 剩余 2 次转帖页权重本身更高 |
被引用页的权重变化也值得单拎出来说。改造前站里被引用次数最多的页面是首页和产品列表页,源材料页基本是零。改造后第八周,实测数据集页和白皮书页进了被引用前五,产品页的引用占比从 71% 降到 44%。这个变化不影响任何排名指标,但它意味着模型讲我们的时候,手里拿的是一手材料而不是转述。
上面这组数字来自 V 厂站内日志加 20 个固定问题的人工登记,样本很小,只能看趋势,别当结论用。
容易搞错的几件事
把 isBasedOn 当"相关推荐"填,是最普遍的错误。它表示的是源材料,不是"你可能还想看"。塞进推荐链接,模型会把推荐页当成这篇结论的证据,答案的可信度反而被稀释。
@id 直接拿页面 URL 当值,第二个坑。URL 会带参数、带锚点、有多语言前缀,同一实体会被拆散。正确做法是实体节点单独给标识,页面地址放 url 字段,两件事分开。
第三个坑是只写字符串不写节点。"isBasedOn": "/dataset/leak-2026q1" 这种写法,机器只能拿到一串文本,不知道它是 Dataset 还是 Report,也没法继续往外走。给完整节点,至少给 @id。
最后一个坑是源材料页自己是空壳。链指过去,目标页面没有结构化数据,等于指到一堵墙。V 厂第一版就栽在这儿,76 条链里有 41 条指向未结构化的老页面,补了两天才补完。
趋势和一句提醒
答案引擎往后大概率会把"能不能给出处"当成质量下限,而不是加分项。现在搭引用链,真正耗时的不是写 JSON-LD,而是把"哪些结论来自哪份数据"这件事理清楚——这份梳理本来就该有,只是过去没人去读它,也不影响排名,就一直没人做。
你们站里现在有几页标了 isBasedOn?跑完上面那个脚本,悬空引用还剩多少条,评论区可以贴一下数字,看看是不是也集中在多语言版本上。
参考与延伸
- schema.org — isBasedOn 属性定义:https://schema.org/isBasedOn
- schema.org — citation 属性定义:https://schema.org/citation
- schema.org — TechArticle 类型文档:https://schema.org/TechArticle
- Google 搜索中心 — 结构化数据工作原理:https://developers.google.com/search/docs/appearance/structured-data/intro-structured-data
- schema.org 官方校验器:https://validator.schema.org/
GEO|AI搜索优化|isBasedOn|citation|JSON-LD|实体对齐|引用链架构|结构化数据