官网写的成立时间和第三方平台对不上,AI 采信了谁:企业实体信息一致性治理踩坑
适用读者:负责企业官网改版的前后端工程师、做品牌词 AI 回答监测的运营、维护公司信息在 Schema.org 结构化数据的人。
九月初做品牌词监测,在两个主流 AI 对话框里问同一个问题:「XX 数科公司什么时候成立的」。一个回答「1998 年」,另一个回答「2012 年」。查了半天才发现,1998 是集团前身做贸易起家的年份,被改版同事写进了官网首页和 Organization 的 foundingDate;2012 才是营业执照上的注册日期,来自第三方工商信息平台。两个数据源都没错,但 AI 引擎各抓各的,用户看到的回答当场打架。这事儿折腾了三周,把官网实体信息从头到尾治理了一遍,记录一下完整的踩坑和修复过程。
先搞清楚:AI 引擎到底从哪拿的「成立时间」
出问题之后我们先把责任链捋清楚。AI 爬虫(AI Crawler)抓企业信息,大致有三条路径:

- 直接抓官网页面,包括 HTML 正文和页面里嵌的 JSON-LD 结构化数据;
- 抓第三方企业信息平台(工商类聚合站、百科类站点),这些站点本身权重高、更新频繁;
- 引擎自建的知识图谱(Knowledge Graph),在训练或索引阶段做实体抽取,把散落在多处的属性归并到一个实体上。
问题出在第三步。知识图谱做实体对齐(Entity Resolution)时,会把「名字高度相似」的多个来源归到同一个实体节点。我们官网写的 1998,第三方平台写的 2012,归并阶段置信度接近,谁也没压过谁,于是在不同查询、不同引擎版本下,回答就随缘了。
flowchart LR
A[官网改版<br>foundingDate=1998] --> C[知识图谱归并节点]
B[第三方工商平台<br>注册年份=2012] --> C
C --> D{归并置信度<br>谁更高?}
D -->|官网文案权重| E[回答 1998]
D -->|平台数据更新频繁| F[回答 2012]
E --> G[同一问题<br>两个引擎口径不一致]
F --> G
关键结论一句话:AI 引擎不会替你做取舍,你不主动统一口径,它就随机采样。 这就是实体一致性(Entity Consistency)治理要解决的问题——让权威源在所有可达路径上说出同一套话。
逐页排查:我们数出了多少处冲突
第二周我们写了个脚本,把官网全部 47 个页面拉下来,提取 JSON-LD 里 Organization 相关的属性,再和第三方平台上我们人工核过的标准口径逐字段比对。结果比预想的难看:
| 字段 | 官网 JSON-LD 写的 | 第三方平台口径 | 冲突原因 |
|---|---|---|---|
| foundingDate | 1998 | 2012-06 | 把集团前身年份当成立年份写进去了 |
| numberOfEmployees | 3000+ | 约 450 | 把集团总人数写到了子公司实体上 |
| address | 集团总部所在市 | 注册地所在区 | 改版时文案顺手换成了办公地址 |
| legalName | 品牌名 | 营业执照全称 | 用品牌简称当法定名称 |
| sameAs | 只有 1 条百科链接 | 平台收录了 5 个官方账号 | 没有关联权威源,引擎找不到锚点 |
五处冲突里有四处是「层级错位」:官网把集团层面的信息压到了子公司实体上,而第三方平台老老实实按注册主体记录。改版的同事没有恶意,就是觉得「写大一点显得正规」。但对企业实体(Organization Entity)来说,注册信息是法律口径,宣传口径只能作为补充说明,不能顶替。
排查脚本不复杂,核心就是解析 JSON-LD 然后做字段级 diff。当时跑完输出了一份 17 行的冲突清单,下面是简化版:
# 依赖:Python 3.11+,bs4 4.12,jsonpath-ng 1.6(pip install bs4 jsonpath-ng)
# 环境:把待核对页面 URL 列表放进 urls.txt,每行一个
# 用途:全站抽取 JSON-LD 的 Organization 节点,和标准口径逐字段比对
# 用法:python check_org_consistency.py > conflicts.tsv,输出直接贴工单
import json
import requests
from bs4 import BeautifulSoup
# 官网声明的标准口径,治理后应与第三方平台完全一致
# 标准口径的单一真值来自营业执照核对,别在代码里另立一份
# 字段名与 Schema.org Organization 属性一一对应
# 标准口径只认法务核过的内部 wiki,别在这里手抄两份
canonical = {
# ISO 8601 格式,只写注册年份,前身年份不许进来
"foundingDate": "2012-06",
"legalName": "营业执照上的全称", # 这里换成真实法定名称
"numberOfEmployees": 450, # 按注册主体统计,不含母公司
}
# 抽取页面里 application/ld+json 的 Organization 节点
# 返回 (url, 节点字典) 的生成器,调用方边拉边比,不吃内存
def extract_org(url):
# 超时给短一点,避免个别页面卡住整个核对流程
html = requests.get(url, timeout=5).text
# 用 html.parser 就够了,不需要 lxml 之类的额外依赖
soup = BeautifulSoup(html, "html.parser")
for tag in soup.find_all("script", type="application/ld+json"):
try:
data = json.loads(tag.string)
except json.JSONDecodeError:
# 解析失败的 JSON-LD 本身就是问题,先记下来
yield url, {"_error": "invalid json-ld"}
continue
# @graph 里可能包着多个节点,逐个筛出 Organization
# Corporation 也算,有的官网用的是这个子类型
nodes = data.get("@graph", [data])
for node in nodes:
# 有些站点 @type 是数组(如 ["Organization", "Brand"]),也兜住
types = node.get("@type")
types = types if isinstance(types, list) else [types]
if "Organization" in types or "Corporation" in types:
yield url, node
# 逐字段比对,输出冲突行
# urls.txt 是排查阶段抓的全站 URL 清单,47 个页面一行一个
for url, node in extract_org(u.strip() for u in open("urls.txt")):
# 页面抽取失败时不进字段比对,直接跳过
if "_error" in node:
print(f"PARSE-FAIL\t{url}")
continue
for field, expect in canonical.items():
got = node.get(field)
# 字段缺失也算冲突,引擎抓到空值同样会去别处找答案
if got is None:
print(f"MISSING\t{url}\t{field}")
continue
# 统一转成字符串再比,避免 450 和 "450" 误报
# 平台口径偶带千分位(如 "1,200"),比对前顺手去一下
if str(got).replace(",", "") != str(expect).replace(",", ""):
# 冲突清单直接打成可直接贴工单的格式
print(f"CONFLICT\t{url}\t{field}\t官方={expect}\t页面={got}")
跑出来的第一版冲突清单里,最离谱的一条是某个产品详情页残留了 2019 年改版前的旧 foundingDate——也就是说官网内部都有两套年份,只是首页那套更显眼。
机制剖析:搜索引擎和 AI 引擎怎么裁决多源属性冲突
这一节讲机制。传统搜索引擎做实体抽取时,来源有一个隐式的可信度排序:官方站点 > 权威百科 > 行业平台 > 长尾转载。但这个排序对「属性级」冲突并不总是生效,因为:
- 官网页面可能整站都在讲宣传口径,引擎没有理由单独相信 foundingDate 字段;
- 第三方平台虽然整体权重低,但它的数据更新频率高、结构化程度高,属性抽取成本低;
- AI 引擎在生成回答时往往做检索增强(RAG),现查现答,检索到哪个来源就引用哪个来源。
所以 AI 回答的口径本质上取决于检索召回顺序,而不是某个全局真相表。用户用品牌词提问时,如果第三方平台的实体页标题里带品牌名且内容匹配度高,它完全可能排在官网那段宣传文案前面——毕竟官网首页讲的是产品,讲成立年份的往往只有 footer 一行小字。
修复思路因此很明确:让官网在结构化层面把自己变成无可争议的权威源。Schema.org 的 Organization 类型提供了两个关键工具:
- foundingDate / foundingLocation:ISO 8601 格式的成立日期,机器可读,避免「1998 年成立」这种自然语言歧义;
- sameAs:指向权威第三方资料页的 URL 数组,告诉引擎「这个实体和那些页面是同一个东西」,把分散的引用拧到一起。
sameAs 的作用机制值得多说一句。引擎做实体消歧(Entity Disambiguation)时,sameAs 相当于一组人工背书的等价边。当你把工商公示页、百科页、官方认证账号主页全部挂上后,引擎归并这批页面抽取出的属性时冲突会显著收敛——因为多个来源经过 sameAs 关联后,在图谱里是同一个节点,属性冲突就变成了节点内部的版本问题,可以用「最新可信来源」策略解决,而不是在两个陌生节点之间猜。
sequenceDiagram
participant U as 用户提问
participant AI as AI 引擎
participant W as 官网 JSON-LD
participant T as 第三方平台页
U->>AI: XX公司成立时间?
AI->>W: 检索官网实体页
W-->>AI: foundingDate=2012-06 + sameAs 指向权威源
AI->>T: 检索第三方平台
T-->>AI: 注册年份 2012
Note over AI: sameAs 归并后两源一致<br>置信度收敛
AI-->>U: 回答 2012 年并引用官网
修复实施:统一口径、挂 sameAs、全站过一遍
第三周动手改。方案分三步,每一步都有具体的执行细节。
第一步是定标准口径。拉着法务同事核了营业执照,把成立日期、法定名称、注册地址、参保人数四个字段定死成单一真值,写进内部 wiki,谁改版都从这份文档取数。这里有个小技巧:numberOfEmployees 这类会变动的字段,官网 JSON-LD 里我们改成了区间描述配合页脚说明,避免每次人员变动都要改代码——第三方平台按年报更新,官网按季度核对,两边同步节奏错开就容易再出裂缝。
第二步是全站 JSON-LD 改造。改完的 Organization 节点长这样:
{
"@context": "https://schema.org",
"@type": "Corporation",
"name": "品牌主名称",
"legalName": "营业执照登记的全称",
"foundingDate": "2012-06",
"foundingLocation": { "@type": "Place", "name": "注册地城市" },
"address": {
"@type": "PostalAddress",
"addressRegion": "注册地省份",
"addressLocality": "注册地城市",
"addressCountry": "CN"
},
"numberOfEmployees": { "@type": "EmployeeReservationNumbers" },
"sameAs": [
"https://www.qcc.com/firm/xxx.html",
"https://baike.example.com/item/品牌全称",
"https://space.bilibili.com/官方认证号",
"https://weibo.com/官方认证号"
]
}
sameAs 里挂的四条链接,选型原则是「引擎普遍收录、页面本身也声明了同一实体」。工商公示页是最硬的锚点,我们放在第一位。
第三步是建立持续核对。核对脚本挂进 CI,每天跑一次全站抽取,和标准口径 diff,有冲突就告警。修完当天脚本输出干净,第四天凌晨又报警了一次——运营同事更新「公司介绍」页时手工把 1998 写了回去,文案里那句「前身可追溯至 1998 年」被脚本里的正则误命中。这也提醒我们:治理不是一次性清洗,要在流程里留闸门。后来我们约定宣传文案里提前身可以,但 JSON-LD 里的 foundingDate 字段任何人不得手改,改动必须走 PR 评审。
修复前后的变化用数据说话:
| 指标 | 修复前(第 1 周) | 修复后(第 5 周) |
|---|---|---|
| 官网与平台冲突字段数 | 5 | 0 |
| 全站 JSON-LD 校验错误 | 9 处 | 0 处 |
| 三款 AI 引擎回答成立年份一致率 | 1/3 | 3/3 |
| 回答中引用官网作为来源 | 0 次 | 2 次 |
| sameAs 关联权威源数量 | 1 | 4 |
一致率那行说明一下口径:同一个问题在三个引擎里各问一次,回答年份完全相同算一致。修复前只有一家答 2012,另两家都在 1998 和 2012 之间摇摆,甚至有一次同一天两个会话给出不同答案,把运营同事整不会了。
一份可以直接抄走的实体一致性核对清单
最后把这三周的经验压成清单,官网改版时逐项过一遍:
- 成立日期字段用 ISO 8601(如 2012-06),宣传口径的「前身年份」只出现在正文文案,不进结构化数据;
- legalName 必须是营业执照全称,品牌名放 name 字段,两者不得混用;
- numberOfEmployees 按注册主体统计,和第三方平台年报口径对齐,且要写清统计时点;
- address 按注册地址写,办公地址可以另行放在文案或 branch 节点里;
- sameAs 数组至少覆盖工商公示页、百科页、两个以上官方认证账号,全部用 HTTPS 完整链接;
- 每个实体节点要有稳定的 @id 或 url 字段,方便引擎跨页归并;
- 结构化数据改动走 PR 评审,禁止文案同事直接改字段值;
- 核对脚本进 CI 日跑,冲突即告警,保留历史 diff 方便回溯是哪次改版引入的。
有几个误区顺带澄清一下。有人觉得「把 1998 写在官网,第三方平台改成 2012 就行了」——改第三方平台数据既慢又不现实,你控制不了它的采集节奏,正确方向永远是把官网改到与法定口径一致,让官网成为最强的可信源。还有人问要不要在官网写「本公司成立于 2012 年(前身创立于 1998 年)」这种双重声明,我的看法是正文可以这么写,给人和爬虫都留了完整语境,但结构化字段必须只留一个日期,字段层面出现两个日期等于把冲突从跨站搬进了站内。
往后看,AI 引擎对企业实体的核验会越来越依赖交叉验证:官网声明、平台数据、官方账号资料、新闻稿四处对齐才给高置信度。改版团队如果不把实体一致性当成工程问题管起来,每次改版都是在给 AI 的回答埋随机数种子。你们在官网上踩过哪些多源打架的坑,欢迎评论区交流。
参考与延伸
- Schema.org Organization 类型与属性定义:https://schema.org/Organization
- Google Search Central 关于组织结构化数据的官方文档:https://developers.google.com/search/docs/appearance/structured-data/organization
- Schema.org 官网首页与词汇表入口:https://schema.org/
- web.dev 结构化数据入门:https://web.dev/learn/seo/structured-data/
GEO|实体一致性|Organization|foundingDate|sameAs|Schema.org|AI优化AIO