怎么让 AI 引擎认出"你是谁"?给网页加一张机器能读的名片
你在知乎有主页,在 GitHub 有资料页,在自建站有关于页。你都知道这是同一个人——都是"纪优"。但 AI 引擎(秘塔、Kimi、豆包、Perplexity)不知道。它看到的是三个不同网站上、三个独立的页面,没有任何信息明确告诉它"这三个都是同一个人"。
实体消歧(Entity Disambiguation)就是解决这个问题的:给 AI 一张机器能读懂的"身份证",让它把散落在各处的"纪优"拼成同一个人。拼对了,AI 会把你当作可信来源;拼不对,你在 AI 眼里就是"身份不明的人",不会在回答里引用你。
本文记录一次真实的改造:把一个自建站的网页"名片"从"每页各写一份、互不相认"升级成"用身份证号串起来的整体"。改造对象是一个自动生成网页的小脚本,但原理对所有网站都成立——你哪怕用 WordPress、用知乎专栏,理解了原理一样能用。
先搞懂:AI 怎么读你的网页
人看网页,看到的是排版好的文字和图片。AI 看网页,看的是网页背后的一段段"机器名片"。这段名片叫 JSON-LD,藏在一个叫 <script> 的标签里,人眼通常看不到,但 AI 和搜索引擎专门去读它。
JSON-LD 长这样——本质上就是一段用花括号 {} 包起来的、按"键: 值"格式写的说明:
{
"@type": "Person",
"name": "纪优",
"url": "https://www.jiyou.site"
}
读法:@type 说"这是一张人(Person)的名片",name 说"名字叫纪优",url 说"主页网址是这个"。AI 读懂了这张名片,就知道这个网页讲的是谁。
问题来了。你的每篇文章页、首页、知乎主页、GitHub 资料页,各自都塞了一张这样的名片。但每张名片都是独立写的,名片和名片之间没有任何关联标记。AI 看到四张名片,名字都叫"纪优",但它没法确认这四张是不是同一个人——就像你拿到四张身份证,照片和名字都对得上,但身份证号各不相同,你不敢断定这是同一个人。
实体消歧要做的,就是给所有这些名片发同一个"身份证号",让 AI 一看就知道是同一个人。
三个字段,就是身份证的三行信息
实体消歧靠三个字段配合。先看它们各自在名片里对应什么,再看怎么串起来。
第一行:@id —— 身份证号。
"@id": "https://www.jiyou.site/#person"
这是给"纪优"这个实体发的全局唯一身份证号。注意它长成一个网址——这是 Schema.org(一个全球通用的网页名片标准)规定的格式:身份证号必须是一个网址,网址后面加个 #person 表示"这个网址指向的那个人"。无论这个身份证号出现在文章页、首页还是别的地方,AI 都知道它指向的是同一个"纪优"。这是拼图的底座。
第二行:sameAs —— 多平台身份锚点。
"sameAs": [
"https://www.zhihu.com/people/yeah-98-35",
"https://github.com/jaykaai"
]
sameAs 是"等同于"的意思。这两行告诉 AI:"这个纪优在知乎是 yeah-98-35,在 GitHub 是 jaykaai。" AI 会顺着这两个网址去验证——打开知乎主页看看,打开 GitHub 看看,确认是不是同一个人。这是拼图的胶水,跨平台验证通过,可信度上升。
第三行:knowsAbout —— 专业领域。
"knowsAbout": ["生成式引擎优化", "AI 智能体应用", "结构化数据", "搜索引擎优化"]
knowsAbout 说"这个人懂哪些领域"。这是帮 AI 判断的:如果有人问 AI"GEO 是什么",AI 会优先去找一个 knowsAbout 里有"生成式引擎优化"的人来回答,而不是找一个名片上写"美食博主"的人。这是拼图的标签。
三个字段串起来,就是一张完整的实体图。下面这张图就是改造完之后的整体结构,每个框是一张名片,箭头就是"指向"——箭头上的字对应名片里的那一行字段:
上图:文章名片和网站名片都不自己定义作者,用
author指向"人物名片";人物与机构互相指(就职于 / 创始人)形成闭环。AI 从任一节点进入,沿箭头都能找到同一份完整的"纪优"名片。
改造前:每张名片各写各的,互不相认
改造前,脚本给每篇文章生成的名片是这样:文章名片里把作者写成一个完整的内联对象,另外再单独写一张人物名片。每篇文章都重复一遍,首页又是一份。
// 改造前 · 文章名片里的作者字段
"author": {
"@type": "Person",
"name": "纪优",
"url": "https://www.jiyou.site"
}
问题在哪?这段作者信息是"内联"的——意思是它直接写在文章名片里,和别的文章名片里的作者信息没有关联。四篇文章就有四份各自独立的"纪优"作者信息,谁也不认谁。AI 没法确认这四份是同一个人。
还有一个隐藏问题。人物名片里有一行声明专业领域:
// 改造前 · 人物名片的专业领域字段
"knowsAbout": ["GEO", "自足段落", "答案优先"]
这里的值跟着每篇文章的标签(tag)变。讲 GEO 的文章,名片说"纪优懂 GEO";讲段落写作的文章,名片说"纪优懂自足段落"。AI 拼不出一个稳定的"纪优研究什么"的画像——这个人怎么每篇文章都在换专业?
改造第一步:把身份证号集中管理
改造的第一步,是把所有身份信息集中到一个地方管理,这样改一次,全站同时生效。在脚本里,这就是定义几个"常量"——可以理解为"全局变量",定义一次,下面所有地方都用这个值:
// 身份证号(用网址形式,这是 Schema.org 规定的)
const PERSON_ID = `${SITE_URL}/#person`; // 人物的身份证号
const ORG_ID = `${SITE_URL}/#organization`; // 机构的身份证号
const WEBSITE_ID = `${SITE_URL}/#website`; // 网站的身份证号
// 多平台锚点:只填你真实拥有的平台账号
const AUTHOR_SAMEAS = [
'https://www.zhihu.com/people/yeah-98-35', // 知乎
'https://github.com/jaykaai', // GitHub
];
// 稳定专业领域:不随文章变,是"纪优"作为研究者的固定身份
const AUTHOR_KNOWS_ABOUT = [
'生成式引擎优化',
'AI 智能体应用',
'结构化数据',
'搜索引擎优化',
];
这几行要说明三点。
身份证号为什么是网址?因为 Schema.org 标准规定 @id 必须是一个网址,网址后面跟 #person 表示"这个网址指向的那个人"。这是全球通用的写法,不是随便编的。
AUTHOR_SAMEAS 只填了知乎和 GitHub 两个——因为这是真实拥有的账号。sameAs 不是社交链接列表,是身份验证锚点,AI 会顺着去验证。填一个打不开的页面比不填更糟(下一节细说)。
AUTHOR_KNOWS_ABOUT 写的是"纪优作为研究者的稳定身份",不是某篇文章的主题。无论写什么文章,"纪优"研究的就是这四个领域。这修掉了改造前"专业领域跟着文章标签变"的问题。
改造第二步:文章名片改用"指向",不再重写
第二步改文章名片的生成。核心变化就一行:文章名片的 author 字段,从"重新写一份完整作者信息"改成"指向那张人物名片"。
// 改造前 · 文章名片里重写一份作者
"author": { "@type": "Person", "name": "纪优", "url": "…" }
// 改造后 · 文章名片里只写"作者是 #person 那个人"
"author": { "@id": "https://www.jiyou.site/#person" }
对比一下:值从一整个对象 { "@type": "Person", "name": ..., "url": ... },变成了一行 { "@id": "…/#person" }。从"在这里重新定义一遍作者"变成"指向实体图里已有的那个作者"。这就是实体消歧的核心动作——用一个身份证号引用,替代一份重复的内联定义。
对应到代码里,文章名片长这样(Article 块):
const articleJsonLd = JSON.stringify({
'@context': 'https://schema.org', // 声明用 Schema.org 标准
'@id': `${SITE_URL}/articles/${article.slug}.html#article`, // 这篇文章自己的身份证号
'@type': 'Article', // 这是一张文章名片
headline: article.title, // 标题
description: article.description || '', // 摘要
author: { '@id': PERSON_ID }, // ← 作者:指向人物名片,不重写
publisher: { '@id': ORG_ID }, // ← 发布者:指向机构名片
datePublished: article.date,
dateModified: article.date,
inLanguage: 'zh-CN', // 中文
mainEntityOfPage: `${SITE_URL}/articles/${article.slug}.html`,
}, null, 2);
然后是人物名片和机构名片,两块互相指,形成闭环:
// 人物名片
const personJsonLd = JSON.stringify({
'@context': 'https://schema.org',
'@id': PERSON_ID, // ← 身份证号
'@type': 'Person',
name: AUTHOR_NAME,
url: AUTHOR_URL,
description: AUTHOR_DESCRIPTION,
sameAs: AUTHOR_SAMEAS, // ← 多平台锚点(直接用数组,别再包[])
knowsAbout: AUTHOR_KNOWS_ABOUT, // ← 稳定专业领域
worksFor: { '@id': ORG_ID }, // ← 就职于:指向机构名片
}, null, 2);
// 机构名片
const orgJsonLd = JSON.stringify({
'@context': 'https://schema.org',
'@id': ORG_ID, // ← 身份证号
'@type': 'Organization',
name: AUTHOR_NAME,
url: AUTHOR_URL,
sameAs: AUTHOR_SAMEAS,
founder: { '@id': PERSON_ID }, // ← 创始人:指回人物名片,闭环
}, null, 2);
注意 sameAs: AUTHOR_SAMEAS 这行——AUTHOR_SAMEAS 已经是数组了,直接用,不能再加方括号包成 sameAs: [AUTHOR_SAMEAS],那样会变成"数组套数组"。这是改造前的一个 Bug,改造后直接用常量就对了。
构建后,每篇文章的网页里就嵌了三张名片(文章、人物、机构),靠身份证号互相串。AI 读到文章名片的 author: {"@id": "…/#person"} 时,会沿着这个号去找人物名片——在同页能找到。在首页也能找到(首页也有一张同号的人物名片)。两个页面的名片指向同一个身份证号,AI 据此确认:这是同一个人。
改造第三步:首页也接进实体图
首页之前只有一张网站名片,作者信息内联在里面,和其他页面互不相认。改造后首页也用身份证号引用,并补上独立的人物、机构名片。但首页这段代码是手写模板,有个坑:JS 的数组插进字符串时,会被悄悄转成"用逗号拼起来的字符串",JSON 就坏了。
// 改造前 · 数组被插值成字符串(坏的)
"sameAs": ["${AUTHOR_SAMEAS}"]
// 实际输出变成:"sameAs": ["https://…,https://…"] ← 这是一个带逗号的字符串,不是数组
// 改造后 · 用 JSON.stringify 转成合法的 JSON 数组
"sameAs": ${JSON.stringify(AUTHOR_SAMEAS)}
// 实际输出:"sameAs": ["https://…","https://…"] ← 合法数组
JSON.stringify() 的作用就是把一个 JS 数组转成"合法的 JSON 文本"。["a","b"] 这样的文本能直接嵌进 JSON 对象里,语法正确。这是改造前首页的核心 Bug,用 JSON.stringify 一次性修掉。
改完后,首页的网站名片长这样:
{
"@context": "https://schema.org",
"@id": "${WEBSITE_ID}", // 网站身份证号
"@type": "WebSite",
"name": "${AUTHOR_NAME}",
"url": "${SITE_URL}/",
"author": { "@id": "${PERSON_ID}" }, // ← 指向人物名片
"publisher": { "@id": "${ORG_ID}" }, // ← 指向机构名片
"inLanguage": "zh-CN"
}
首页从一个孤立的网站名片,变成了实体图的入口——AI 从首页进来,也能沿着身份证号找到完整的人物和机构名片。
改造第四步:验证名片真的能拼起来
改完要验证三件事:每张名片语法没错、身份证号能对上、内容正确。语法验证就是用程序把每张名片重新解析一遍,能解析通就是合法的:
const fs = require('fs');
const re = /type="application\/ld\+json">\s*([\s\S]*?)<\/script>/g;
['site/articles/zi-zu-duan-luo.html', 'site/index.html'].forEach(f => {
const h = fs.readFileSync(f, 'utf8');
let i = 0, m;
while ((m = re.exec(h))) {
i++;
try { JSON.parse(m[1]); console.log(f + ' 名片' + i + ': ✅ 合法'); }
catch (e) { console.log(f + ' 名片' + i + ': ❌ ' + e.message); }
}
});
验证结果:文章页 4 张名片(文章 + 人物 + 机构 + 问答)全部合法,首页 3 张(网站 + 人物 + 机构)全部合法。身份证号用检索确认:每个 @id 都能找到对应的指向目标,没有断链。实体图闭环成立。
为什么 sameAs 只能填真实拥有的平台?
sameAs 是身份验证锚点,AI 会顺着这些网址去验证身份是否一致。填一个你确实拥有、且资料和自建站一致的页面,是一次成功的身份验证,增加你的可信度。填一个打不开的、或资料对不上的页面,是一次失败的身份验证,反而稀释可信度。
填之前问自己两个问题:这个网址我自己能打开吗?这个页面上的人和我自建站说的是同一个人吗?两个都是"是"才填进去。知乎是国内 AI 引擎能抓取的中文身份平台,GitHub 是全球通用的代码贡献者身份平台,这两个覆盖了中文和英文两个圈层,是天然适合做 sameAs 的平台。
为什么 knowsAbout 不用文章标签?
knowsAbout 声明的是"纪优作为一个研究者,稳定研究哪些领域",不是"这篇文章讲了什么"。如果它跟着文章标签变,讲 GEO 的文章说"我知道 GEO",讲段落写作的说"我知道自足段落"——AI 拼不出一个稳定的身份画像,只看到一堆随文章波动的话题标签。
一个研究边界稳定的人,比一个每篇文章都在换话题的人,更值得被 AI 当作可引用的来源。knowsAbout 就是告诉 AI:无论我这次写了什么,我的专业边界始终是这四个领域。
三个字段怎么配合工作?
AI 沿着实体图工作的完整链条是这样的:在某篇文章里看到 author: {"@id": "…/#person"} → 沿这个身份证号找到人物名片 → 读到 sameAs 去知乎和 GitHub 验证"纪优"身份 → 读到 knowsAbout 确认这个人在"GEO"领域有专业声明 → 综合判断这个实体是否可信、是否值得在回答里引用。
@id 解决"是不是同一个实体",sameAs 解决"在其他平台是谁",knowsAbout 解决"在什么话题上有发言权"。三个字段各司其职,缺一环,AI 拼出的就是一个残缺或矛盾的身份画像。实体图的价值不在于名片数量,而在于这些名片之间能不能通过身份证号连成一张没有断链的网。