Gemini 引用怎么展示
Gemini 的引用以 annotations 形式挂在答案文本块上,每条 url_citation 带 url、title 与 start_index、end_index;三种检索通道口径一致。(核验 2026-11-05,F90、F101)
一、Gemini 引用怎么展示:问题在「哪一句出自哪」,不在链接数量
「答案下面加一排来源链接」——这是最常见的做法,也是最容易做错的做法。它把一个「句子级」的事实降级成了「整篇级」的暗示:读者看到三个链接,不知道哪句话出自哪一个。
Gemini 的返回不是这样设计的。它给的是内联标注:每条 url_citation 带三个关键信息——来源地址、来源标题,以及这段引用在答案文本里的起止下标(start_index、end_index)(F90)。也就是说,协议层已经告诉了你「第 0 到 13 个字符出自商品站的退款政策、第 13 到 28 个字符出自财务站的规则页」,你要做的是按这个区间把来源贴到对应的句子上。
同一套结构在三条通道上是一致的:Google Search、URL Context、File Search 的引用都从 model_output 步的文本块 annotations 里读(F101)。也就是说,你要渲染的所有引用信息,都在 annotations 这个数组里,每条的类型是 url_citation。这一点值得单独强调:大量旧教程还在用 groundingMetadata 与 groundingSupports,那是 generateContent 时代的字段。新接口里它们不一定有值,本节只按 annotations 读——找不到引用时,先确认你查的字段对不对,再去怀疑检索没生效。
二、annotations 与 url_citation:最小可运行示例
下面这段把一段答案和它的标注一起处理:切片段、查越界、查重叠。
## EXPECT: 片段 2 条:0-13→shop.example、13-28→finance.example | 越界 0 条 | 重叠 0 条
TEXT = "退款需在收到货七天内申请。跨月退款由财务复核后原路退回。"
ANNOTATIONS = [
{"type": "url_citation", "url": "https://shop.example/refund-policy",
"title": "shop.example", "start_index": 0, "end_index": 13},
{"type": "url_citation", "url": "https://finance.example/refund-rule",
"title": "finance.example", "start_index": 13, "end_index": 28},
]
def audit(text, annotations):
spans = [(a["start_index"], a["end_index"], a["title"]) for a in annotations]
out_of_range = [s for s in spans if s[0] < 0 or s[1] > len(text)]
ordered = sorted(spans)
overlap = [(ordered[i], ordered[i + 1]) for i in range(len(ordered) - 1)
if ordered[i + 1][0] < ordered[i][1]]
return spans, out_of_range, overlap
assert len(TEXT) == 28
spans, bad_range, bad_overlap = audit(TEXT, ANNOTATIONS)
assert not bad_range and not bad_overlap
print("片段 " + str(len(spans)) + " 条:" +
"、".join(f"{s}-{e}→{t}" for s, e, t in spans) +
f" | 越界 {len(bad_range)} 条 | 重叠 {len(bad_overlap)} 条")
渲染时真正要做的是 TEXT[start:end] 这一段切出来,再在它的尾部挂一个上标链接。示例里两个区间正好把 28 个字符切成两句——真实响应里模型就是按句子边界标注的,所以渲染出来的上标会落在句末。注意 title 给的是域名(shop.example)而不是文章标题,展示时直接用它会略显生硬,可以自己做一层域名到站点名的映射,但别把 title 当成标题去搜。越界与重叠两项检查看着多余,但它们是唯一能在渲染前发现「标注与文本不同步」的手段。
三、解释与边界:三个字段与两个旧坑
三个字段各有用处。 url 是跳转目标;title 是展示文本,官方示例里给的是域名(aljazeera.com)而不是文章标题;start_index/end_index 是字符区间,决定这段话挂哪个来源。三者缺一个,展示就会退化成「一排链接」;缺了区间,读者就只能自己去猜哪句话有据。
旧坑一:字段名换了。 generateContent 时代用 groundingMetadata 与 groundingSupports,Interactions 用 annotations 里的 url_citation(F101)。照着旧教程写,最常见的现象是「搜索明明触发了,引用列表却是空的」——大概率是你在旧字段上找数据,而新接口把引用放在了 annotations 里。
旧坑二:search_suggestions 不是引用。 Google Search 的响应里还有一个 google_search_result 步,带着 search_suggestions,那是一段用于渲染搜索建议的 HTML 片段,官方在服务条款里对它的使用有要求(F90)。它和引用是两回事:引用回答「这句话出自哪」,搜索建议回答「还可以看什么」。把它当引用塞进来源列表,既不合规也不准确。
要不要给「没有标注」的答案显示来源? 不要。有些回答是模型凭既有知识给的,没有 url_citation,这时候强行补一个泛泛的「参考 Google 搜索」反而误导。可用的做法是明确标「本段无出处」,让人知道该不该复核。
**这三条都落到实处,才算把引用接到了界面上,而不只是把链接放上去。
条款层面还有一条。** 生成内容可能涉及署名义务,官方把它归到 attribution 这一侧(F41)。展示引用不只是产品体验问题,也是合规问题——尤其当答案被二次分发时。
边界。 本节只讲「拿到标注之后怎么渲染」。哪条通道该用,见 7.1;检索按次计费怎么算,见 7.3;私有库里的权限与隔离,见 7.4。本节也不讨论前端样式,那是设计问题。
四、预期输出与异常分支
真跑输出:
片段 2 条:0-13→shop.example、13-28→finance.example | 越界 0 条 | 重叠 0 条
六类分支。下标越界:end_index 超过文本长度,通常是文本被二次处理过(截断、替换、加前缀)而标注没跟着变;渲染前必须查,否则切片会静默截短。区间重叠:两个来源覆盖同一段,说明模型对同一句话给了多条依据;展示时要么都列,要么按可信度取一,别随机挑。只有 url 没有 title:展示端会露出一长串 URL,移动端直接换行;渲染层要有兜底(取域名)。搜索触发了但引用为空:先确认读的是 annotations 而不是旧字段(F101)。答案里出现「据某网站」但没标注:这是模型自己在说,不是引用,别当成来源展示。search_suggestions 被当成来源:它只是建议片段,且有使用条款约束(F90)。
五、判据卡:没有证据位置时能不能展示答案
| 该这么做 | 不该这么做 |
|---|---|
按 start_index/end_index 把来源贴到句子上 |
在答案末尾堆一排链接 |
读 annotations 里的 url_citation |
去旧字段 groundingMetadata 里找 |
| 没有标注就标「本段无出处」 | 补一个泛泛的来源凑数 |
| 渲染前查越界与重叠 | 直接 text[start:end] 切了就用 |
六、来源与核验
https://aistudio.google.com/docs/google-search.md(核验 2026-11-05,F90)https://aistudio.google.com/docs/url-context.md(核验 2026-11-05,F92)https://aistudio.google.com/docs/file-search.md(核验 2026-11-05,F101)https://aistudio.google.com/docs/terms.md(核验 2026-11-05,F41)
引用结构与字段名属于「API 结构」档事实,复核周期三个月,见 1.3。若官方调整标注字段或下标语义(字符与字节),本节的切片段示例要重写,不能只改一句说明。
七、动手试
- 把第二条标注的
end_index改成30,重跑。预期越界 1 条;验收标准是你能说清「为什么越界不该被静默截断」,以及渲染层该给什么提示。 - 把第一条的
end_index改成15,重跑。预期重叠 1 条;验收标准是给出你的处理策略:两个来源都显示,还是按title去重。 - 打开你项目里渲染答案的那段代码,搜
groundingMetadata。验收标准:如果搜到了,说明它还是旧接口写法,按annotations改;如果搜不到,写下你读的是哪个字段。

TRAE-AI编程


更多建议: