Grok 引用核对与答案溯源
带引用的答案看起来更可信,这正是它危险的地方。用户看到 [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 做探活。
七、动手试
- 给校验器加一条「链接探活」:对
citations里的 URL 发 HEAD 请求,记录失效项。想一想:探活该在渲染前做,还是异步做? - 构造一个「同源复用编号」的样本(
[1, 2, 1]),确认你的校验不会误报跳号。 - 对比 Responses API 与 xAI Python SDK 的默认行为,写出迁移时需要显式设置的两个
include值。

TRAE-AI编程


更多建议: