Grok 引用核对与答案溯源

2026-11-04 17:14 更新

带引用的答案看起来更可信,这正是它危险的地方。用户看到 [1] 就默认「有出处」,很少有人会点开确认。一旦引用编号指向一个 404,或者引用列表里根本没有这个 URL,答案的可信度会瞬间反转——而这类问题,靠人眼看是发现不了的。

这一节把「引用可核对」做成可执行的校验。

一、问题不是「有没有引用」,而是「引用对不对得上」

官方给了两类引用信息,用途不同:

  • All Citations:response.citations,是 agent 搜索过程中遇到的全部来源列表,默认返回。官方特别说明:不是每个 URL 都会出现在最终答案里。
  • Inline Citations:答案正文里的 markdown 链接 [[N]](url),以及 annotations 里的结构化位置信息。

两者配合才能核对:citations 回答「这次搜到了哪些来源」,annotations 回答「正文第几个字符引用了哪个来源」。

还有一个容易忽略的前提:开启内联引用不保证每个答案都引用来源。官方写明由模型根据问题自行决定。所以「没有引用」不一定是 bug。

二、最小可运行示例:引用一致性校验器

下面的脚本不联网、不需要真实密钥。它检查三件事:引用的 URL 是否在来源列表里、编号是否从 1 开始、标注位置是否落在正文范围内。

"""引用核对:URL 归属、编号起点、位置边界。"""

import re

def check_citations(text: str, citations: list[str],
                    annotations: list[dict]) -> list[str]:
    errors: list[str] = []
    links = re.findall(r"\[\[(\d+)\]\]\(([^)]+)\)", text)
    for num, url in links:
        if url not in citations:
            errors.append(f"引用 [{num}] 的 URL 不在 citations 列表")
    nums = [int(n) for n, _ in links]
    if nums and min(nums) != 1:
        errors.append("编号未从 1 开始")
    for a in annotations:
        if not (0 <= a["start_index"] < a["end_index"] <= len(text)):
            errors.append(f"标注位置越界:{a['start_index']}-{a['end_index']}")
    return errors

TEXT = "答案是 42。[[1]](https://a.example)[[2]](https://b.example)"
CITS = ["https://a.example", "https://b.example"]
ANN = [
    {"start_index": TEXT.find("[[1]]"),
     "end_index": TEXT.find("[[1]]") + len("[[1]](https://a.example)")},
    {"start_index": TEXT.find("[[2]]"),
     "end_index": TEXT.find("[[2]]") + len("[[2]](https://b.example)")},
]

def offline_check() -> None:
    cases = [
        ("合法", TEXT, CITS, ANN),
        ("URL 不在列表", TEXT, ["https://a.example"], ANN),
        ("位置越界", TEXT, CITS, [{"start_index": 0, "end_index": 999}]),
        ("编号不从 1", "答案见 [[2]](https://b.example)", ["https://b.example"], []),
    ]
    for label, text, cits, ann in cases:
        errs = check_citations(text, cits, ann)
        print(f"{label}:" + ("通过" if not errs else ";".join(errs)))

if __name__ == "__main__":
    offline_check()

真正上线时,把 citations 与 annotations 直接从响应里取,text 取 output_text 的内容。校验放在「渲染给用户之前」,拦的是「看起来有出处、实际对不上」这一类。

三、引用数据的字段与语义

字段 含义 核对时的用途
citations 全部来源 URL 列表 判断引用的 URL 是否真的搜到过
annotations[].url 该标注对应的来源 与 citations 交叉核对
annotations[].start_index 引用在正文中的起始字符位置 定位到 [
annotations[].end_index 结束位置(不含) 定位到 ) 之后
annotations[].title 可见编号(如 "1") 与正文 [[N]] 对齐

三件要强调的事:

  • 位置遵循切片约定。官方写明 start_index 是 [ 的位置,end_index 是右括号之后(exclusive)。用 text[start:end] 能直接切出整段引用标记。
  • 编号从 1 开始,同一来源复用编号。所以编号序列可能是 [1, 2, 1],不是严格递增。校验时不要假设「第 N 个引用编号就是 N」。
  • 两类引用默认值不同。Responses API 的内联引用默认开启,可用 include=["no_inline_citations"] 关闭;xAI Python SDK 默认关闭,需 include=["inline_citations"] 开启。跨 SDK 迁移时最容易在这里出现「怎么突然有引用 / 怎么突然没有」。

四、预期输出与异常分支

运行校验器,预期输出:

合法:通过
URL 不在列表:引用 [2] 的 URL 不在 citations 列表
位置越界:标注位置越界:0-999
编号不从 1:编号未从 1 开始

换成真实响应时,若出现异常,按下面顺序定位:

现象 优先怀疑 第一步动作
引用点开是 404 来源本身失效 校验通过后仍要做链接探活
编号跳号 同源复用或漏引 不要按序号推断数量
标注位置对不上 文本被二次加工 用原始 output_text 校验
突然没有引用 SDK 默认值不同 检查 include 参数

最后一行尤其重要:如果你在答案渲染前做过 markdown 清理或截断,start_index/end_index 就会失效。位置校验必须基于未经加工的原始文本。

五、判据卡

判断项 该这么做 不该这么做
引用核对 URL 与 citations 交叉验证 看到 [1] 就当有出处
编号判断 允许同源复用 假设严格递增
位置校验 用原始文本 用渲染后的 HTML
SDK 切换 显式设置 include 依赖默认值
无引用 视为正常分支 一律当失败

这张卡的重点是引用要能对得上。五条里有三条在说:编号、URL、位置,三者要指向同一份来源。

六、来源与核验

  • 官方 Citations:https://docs.x.ai/developers/tools/citations(核验日期 2026-11-04)。response.citations 默认返回、内联引用在 Responses API 默认开启而在 xAI Python SDK 默认关闭、[[N]](url) 格式、编号从 1 开始且同源复用、annotations 四个字段与切片语义,均以该页为准。
  • 官方写明「开启内联引用不保证每个答案都引用来源」,本节据此把「无引用」列为正常分支。
  • 本节校验器为工程实现,不宣称引用准确率;引用是否真实有效,仍需对 URL 做探活。

七、动手试

  1. 给校验器加一条「链接探活」:对 citations 里的 URL 发 HEAD 请求,记录失效项。想一想:探活该在渲染前做,还是异步做?
  2. 构造一个「同源复用编号」的样本([1, 2, 1]),确认你的校验不会误报跳号。
  3. 对比 Responses API 与 xAI Python SDK 的默认行为,写出迁移时需要显式设置的两个 include 值。
以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号