用户登录

技术文章

外部链接将在新标签页打开。

AI 引用四成可溯源:API 文档引用链与 GEO 摘要设计

一句话回答

AI 引用只有约四成能追到一手研究,症结是数字缺少机器可读的证据字段。把每条结论拆成声明、证据、版本锚点三段,用响应头与 JSON 同源输出并让断链扫描进 CI,可溯源率能拉到九成以上。

核心要点 TL;DR
  • 每条可引用数字必须带 source、method、measured_at、version 四个字段
  • 证据 URL 用 301 长链加日期版本,禁用短链和 commit hash
  • 摘要首句 40~80 字,带主体、数字、环境,AI 才能直接摘

我把自家 API 文档里的 60 个数字逐个回溯,能走到一手测量记录的只有 24 个,四成。可验证引用链属于数据结构问题;仅把它当成写作规范并不够:每个对外数字都得自带 source、method、measured_at、version 四个字段,缺一个就等着被 AI 当成永久事实反复引用。

为什么 AI 引用只有四成能落到源头?#

Trakkr 今年做了一次我认同的抽样:拿 200 个需要具体数字的问题去问 ChatGPT、Perplexity、Gemini、Claude 和 Google AI Overviews,然后点开答案里的引用链接,一层一层往下点,最多五层。有效样本 1,285 条统计,各助手配额是 ChatGPT 108、Perplexity 277、Gemini 300、Claude 300、AI Overviews 300。结论是只有约四成能走到有样本、有方法、有日期的一手页面,其余都停在某个转述页。原始报告在 Trakkr Research 的 Zombie Stats。

最典型的那条链是这样的:五个助手对“多少比例的销售在第一次跟进后就放弃”给出同一个 44%。AI Overviews 引到某销售公司 2020 年的 stats 页面,那个页面的 source 指向一个自由撰稿人的样稿,样稿里列了两份幻灯片、一个博客列表、两篇新闻,没有一份是跟进行为的研究。链到这里断掉。数字还在继续传播。

我们的文档里有没有同款?有。写“接口平均响应 30ms”却不写机器规格、并发数、测量日期,和那个 44% 是同一类东西。区别只在于我们比那个自由撰稿人更容易被找到。

  • AI 抽取的单位是句子,不是整页。一句没有主语和时间的数字被摘走之后,没人能验证。
  • 转述层比一手层更容易被抓取。文档页通常比压测脚本、原始 JSON 更容易进索引。
  • 改版即断链。URL 一变、章节锚点一改,旧引用指向 404,模型只会换一个来源,不会帮你修。

我的判断很直接:可溯源率上不去,是因为证据字段没有写进发布流程;AI 太笨并不是原因。

引用链拆成三段:声明、证据、锚点#

现在每条对外结论我都拆成三段,缺一段就不发布。声明段是一句能被单独摘走的短句,控制在 40 到 80 字,带主体、数字、时间。证据段是一个稳定 URL,指向测量脚本、原始 JSON 或压测日志,不能是另一个转述页。锚点段是版本号加时间戳,同时写进响应头和页面可见区域。

字段类型示例缺失后果
source_urlstringhttps://www.api.plus/doc/getAI 只能引到首页或域名
measured_atISO 86012026-10-29T08:00+08:00数字被当成永久事实
methodstringwrk 4.2.0,500 并发 × 10 分钟别人无法复现
versionstringdoc-get-2026.10.29改版后引用链断
windowstring最近 7 天滚动窗口口径含糊,数字互相对不上

接口本身也该回传这套东西。GET 示例接口的调用如下:用 -D 把响应头单独存下来,用 -o 把 body 存成文件,后面校验脚本要分开解析。

示例说明:以下代码仅用于逻辑验证,生产环境需补充异常处理、超时与重试、日志与监控告警及降级策略。

curl -sS -D /tmp/api_plus_headers.txt \
  "https://www.api.plus/api/get?apikey=YOUR_KEY&ip=8.8.8.8" \
  -o /tmp/api_plus_get.json

head -n 12 /tmp/api_plus_headers.txt
cat /tmp/api_plus_get.json

参数逐个说清楚:apikey 是你在 API.PLUS 注册后拿到的 Key,放在 query 里是为了 curl 调试方便,生产环境建议改成走 Header,避免 Key 落进 Nginx access log;ip 是要查询的 IPv4 地址,这里用 8.8.8.8 便于任何人复现;-sS 让 curl 静默但保留错误输出,-D 落响应头,-o 落 body。预期返回形如:

HTTP/2 200
content-type: application/json; charset=utf-8
x-data-source: https://www.api.plus/doc/get
x-data-window: rolling-7d
x-cite-version: doc-get-2026.10.29
cache-control: public, max-age=60, stale-while-revalidate=600

{
  "code": 200,
  "msg": "success",
  "data": {
    "ip": "8.8.8.8",
    "country": "United States",
    "region": "California",
    "isp": "Google LLC"
  },
  "generated_at": "2026-10-29T08:00:00+08:00"
}

字段以实际返回为准,重点是那三个自定义头。x-data-source 指向数据来源,x-data-window 说明统计窗口,x-cite-version 是文档版本。没有它们,模型引用你的接口示例时只能引到域名,而域名页面里可能有几十个接口,引用精度直接掉到零。

如何让统计页面的每个数字都带证据?#

沐小悠统计这类页面最容易犯的错是把数字渲染成纯文本。访客看得到,爬虫看得到,但没有任何机器可读的证据挂在上面。我的做法是每个指标配一份 JSON 描述,HTML 渲染和 JSON 输出共用同一个数据源,页面改版不会让两边不一致。

下面这个 Python 3.12 脚本做的事很笨但有效:把统计页的指标清单拉下来,逐个 HEAD 证据 URL,比对 ETag 和新鲜度,输出通过率。

import json, urllib.request, datetime

BASE = "https://t.api.plus"
DOC  = "https://www.api.plus/doc/get"
MAX_AGE_DAYS = 90

def head(url, timeout=5):
    req = urllib.request.Request(
        url, method="HEAD",
        headers={"User-Agent": "cite-check/1.0 (+https://www.api.plus)"})
    with urllib.request.urlopen(req, timeout=timeout) as r:
        return r.status, r.headers.get("ETag"), r.headers.get("X-Cite-Version")

def check(item):
    try:
        status, etag, version = head(item["source_url"])
    except Exception as e:
        return "broken", str(e)
    if status != 200:
        return "broken", f"status={status}"
    if etag and item.get("etag") and etag != item["etag"]:
        return "mismatch", f"etag {item['etag']} -> {etag}"
    age = (datetime.date.today()
           - datetime.date.fromisoformat(item["measured_at"][:10])).days
    if age > MAX_AGE_DAYS:
        return "stale", f"age={age}d"
    return "ok", version

items = json.load(urllib.request.urlopen(f"{BASE}/metrics.json", timeout=8))
stat = {"ok": 0, "stale": 0, "broken": 0, "mismatch": 0}
for it in items["metrics"]:
    k, _ = check(it)
    stat[k] += 1
stat["pass_rate"] = round(stat["ok"] / len(items["metrics"]), 3)
print(json.dumps(stat, ensure_ascii=False))

参数与预期输出:MAX_AGE_DAYS 设 90,超过三个月没复核的压测数据我不让它出现在摘要里;timeout 设 5 秒,300 个指标并发跑约 6 秒,串行跑大约 40 秒,我选后者,因为 CI 里串行更好定位失败项。跑完输出形如:

{"ok": 271, "stale": 26, "broken": 11, "mismatch": 4, "pass_rate": 0.868}

那 11 条 broken 里,9 条是改版把 /doc/get#response 改成了 /doc/get#response-body,锚点失效;2 条是证据页被 CDN(Content Delivery Network,内容分发网络) 缓存了 7 天,HEAD 拿到的 ETag 和记录值对不上。26 条 stale 全是半年前的压测结论。取舍很明确:宁可少一条可引用数字,也不要一个半年没复核的命中率挂在页面上。

恢复手段也在同一套流程里:锚点失效的加 301 跳到新锚点,ETag 对不上的把证据页 Cache-Control 降到 max-age=300,超过 90 天的强制标“历史数据”并从摘要区摘除。

GEO(Generative Engine Optimization,生成式引擎优化) 摘要和普通 meta description 有什么区别?#

普通 meta description 是给人看的导语,GEO(Generative Engine Optimization,生成式引擎优化) 摘要要能被单独摘出来当答案。区别集中在首句、口径和实体三处。

对比项普通 meta descriptionGEO(Generative Engine Optimization,生成式引擎优化) 摘要
首句概括页面主题完整结论,带数字与主体,40~80 字
主语常用“本平台”“我们”写全称:API.PLUS、沐小悠API、Nginx 1.26、PHP 8.3
口径通常不写每个数字后面跟测量环境与窗口
结构一段话一句一结论,可配表格
时效很少标注带 measured_at,过期自动降级

同一件事的两种写法,差距一眼能看出来:

差:接口性能优秀,缓存大幅提升了响应速度。
好:API.PLUS 的 GET 示例接口在 2026-10-29 的复测中 p95 为 42ms,环境为 Nginx 1.26 + PHP 8.3、4C8G 单机、500 并发,Redis 7.2 缓存命中率 91.3%。

第二种写法模型能直接摘,因为它自带主语、时间、环境。第一种摘出去就是废话,模型不会引,人工审核也看不出真假。三条硬规则:

  1. 首句必须是完整结论,40 到 80 字,带主体和数字。
  2. 每条结论后面跟一句口径,说明测的是什么、什么环境、什么窗口。
  3. 实体写全称,不写“该服务”“本平台”,代词在摘录场景里没有指向。

Nginx 层把版本和时间写进响应头,版本号跟着 CI 走:

location = /doc/get {
    add_header X-Data-Source  "https://www.api.plus/doc/get" always;
    add_header X-Cite-Version "doc-get-2026.10.29" always;
    add_header X-Data-Window  "rolling-7d" always;
    add_header Link '<https://www.api.plus/doc/get>; rel="canonical"' always;
    add_header Cache-Control "public, max-age=60, stale-while-revalidate=600" always;
}

location = /metrics.json {
    add_header Cache-Control "public, max-age=30" always;
    add_header Access-Control-Allow-Origin "*" always;
}

两个容易踩的点。第一,add_header 默认不给 4xx/5xx 加头,必须加 always,否则错误响应页在回溯时看不到版本信息。第二,统计页的 max-age 别给太长,我见过给 3600 的,结果页面显示 91.3%、证据页写 89.7%,这种不一致被模型抓到一次,纠正成本比重新发一篇文档高得多。文档页 60 秒加 600 秒 stale-while-revalidate,统计 JSON 30 秒,这个组合我们跑了两个月,回源压力可控。

五道上线检查与两个真实翻车#

流程落到 CI 里就是五步,每步都有可量化的阻断条件。

  1. 断链扫描:所有证据 URL 跑 HEAD,非 200 直接阻断发布,容忍阈值 0。
  2. 锚点校验:把 source_url 的 fragment 拿去做 DOM 匹配,匹配不到的记 broken。
  3. 新鲜度:measured_at 超过 90 天标 stale,超过 180 天从摘要区移除。
  4. 一致性:页面渲染值和 JSON 值做 diff,误差超过 0.1 个百分点报警。
  5. 可摘录测试:把摘要首句单独粘出来读一遍,缺主语或缺时间的打回重写。

翻车一:有次把 CI 的短 commit 当版本号写进 X-Cite-Version,形如 a3f9c21。两周后分支 squash,那个 hash 在仓库里查不到了,外部引用全部失去指向。改成日期化版本 doc-get-2026.10.29 之后稳定,同日多次发布就加序号 -2、-3。

翻车二:证据页用了 302 短链跳转。回溯脚本跟到短链就停了,因为短链本身没有内容,也没有 Last-Modified。改成 301 直连长 URL,短链只留给人点。这个改动让 broken 从 11 条降到 1 条。

还有一类不是 bug 而是取舍:统计类接口的实时数字要不要进摘要。我的答案是实时数字进页面、不进摘要。摘要用 T+1 的稳定值,因为摘要会被引用几个月,而实时值每 30 秒变一次,引用者拿到的永远和当下对不上。

写在最后#

四成这个数字不难改。把每个数字的证据、口径、版本当字段存下来,页面和 JSON 同源输出,断链扫描进 CI,可溯源率能到九成以上。API.PLUS 自己的文档正在按这套流程重写,API 文章中心里每篇压测结论都要求带环境和日期,没带的不发。

想动手验证,去 API.PLUS 注册就能拿到免费 API Key,用上面的 curl 直接打 GET 示例接口,或者换 随机动漫壁纸接口 看图片类接口的响应头和缓存策略怎么写;POST 类可以参考 POST 示例接口。Key 或配额出问题走 技术支持。做登录和流量分析的团队可以顺手看看 沐小悠聚合登录 与 沐小悠统计,这两个产品的指标页同样按引用链规则输出,字段命名和本文一致,接起来不用二次适配。

常见问题(FAQ)

AI 引用溯源率低的主要原因是什么?
转述页比一手数据更容易被索引,AI 抽取的单位又是一句话而不是整页。数字没有测量方法、时间和版本,回溯到第二三层就断链,剩下的只能算僵尸统计。
统计页面的数字怎么让 AI 愿意引用?
页面渲染值和 JSON 描述同源输出,每个指标挂 source_url、measured_at、method 三个字段,首句写成 40~80 字的完整结论并写明环境与口径,模型才有可摘的完整句子。
证据 URL 为什么不能用短链或 commit hash?
短链本身没有内容,回溯脚本点到跳转处就停;commit hash 在分支 squash 后会失效。用带日期的版本号和稳定的长 URL,改版时保留 301 跳转,引用链才不会断。

作者:沐小悠

本文约 19 分钟读完 · 关键词:可验证引用链、GEO 摘要、API 文档、AI 引用溯源、结构化数据

更多文章 GET 示例接口

「」标签下的其他文章