MCP 检索的引用存活率:127 个问题、3 条流水线实测

检索服务交出了来源,Agent 写答案时还引用吗?127 个问题实测:CSR 0.618/0.614/0.323,但六成“丢引用”只是格式不对。

分享
MCP 检索的引用存活率:127 个问题、3 条流水线实测

一句话结论:企业把文档做成 MCP 检索工具后,能保证"正确的来源进去了",却看不到"引用有没有出来"——因为写答案的 Agent 不归你控制。2026 年 10 月,一份可复现的基准第一次量了这件事:用 FastAPI 文档的 2,085 个块、127 个逐条人工核对的问题、三条尽量对齐的流水线,测引用存活率(CSR)。结果是单次 RAG 0.618、LangGraph Agent 0.614(逐题差异 +0.000)、OpenAI Agents SDK Agent 0.323。但作者随后发现,约六成的差距只是格式问题:SDK 在 40% 的回答里没按要求的 [url] 格式写,却用 Markdown 链接给了来源。修正口径、重跑三次后,真实差距约为 −0.09 ~ −0.12;而在检索服务端给每个块加一个短 ID,错格式从每轮约 50 降到约 5。

披露:本文编译自 Ayan Arshad 的系列第一篇(dev.to,2026-10-17),作者公开了仓库、trace 与全部数字,并在文末说明自己正在寻找 RAG/Agent 方向的远程工作。文中所有结果都是其自建基准上的单次测量,作者本人列出了语料、模型、重复次数与人审规模的边界。


一、问题:检索服务看不到答案

一个正在变成常态的场景:

  1. 公司把文档变成搜索工具,通过 MCP 暴露,任何 Agent 都能调用:Claude、ChatGPT、IDE 里的编码助手、客户用 LangGraph 或 OpenAI Agents SDK 搭的 Agent。
  2. 工具工作得很好:每个查询返回最相关的文档块,每个块带 source_url:
{
  "source_url": "https://fastapi.tiangolo.com/tutorial/body-updates/",
  "content": "# Body - Updates ..."
}
  1. Agent 接手:读块、判断相关性、写最终答案。

检索服务永远看不到那个答案。 所以供应商能确认"正确的来源进去了",却不知道"引用有没有出来"。

对文档助手来说这是硬伤:

  • 答案正确但没有引用 → 无法核对,用户也点不进那个解释边界的页面;
  • 答案引用了错的页面 → 看起来像验证过,其实没有。

作者找不到有人量过这件事,于是自己搭了基准。

二、方法:把边界复制出来

研究问题是:当 RAG 知识库通过 MCP 被 Agent 消费,而不是单次"检索→回答",引用还在吗?

直觉假设是"Agent 会丢引用":Agent 会循环——搜索、阅读、可能再搜一次、然后写答案;第一次调用拿到的来源在写答案时已经被推到上下文很后面,似乎很容易丢。结果这个假设大部分是错的,而它错的方式比假设本身更有用。

语料与服务器

项 设置
语料 FastAPI 文档,153 页,按标题切成 2,085 个块
选择理由 干净的 Markdown、稳定的公开 URL、被广泛使用,任何人可复核
MCP 服务器 单个工具 search_fastapi_knowledge_sources,只返回 {source_url, content},走 streamable HTTP
问题 草拟 250 个,逐条对照真实文档页后保留 127 个
问题构成 82 个可从单页回答、29 个需要两页以上、16 个文档根本没覆盖(用来抓编造答案)

三条尽量对齐的流水线

同一个模型(gpt-4o-mini)、同一份文档、同一个系统提示、同一种要求的引用格式(每条主张后面紧跟 [url]):

  1. 单次 RAG:搜一次,然后回答。对照组。
  2. LangGraph Agent:ReAct Agent,自行决定何时、调用几次 MCP 工具。
  3. OpenAI Agents SDK Agent:同样的思路,用 OpenAI 的 Agent 框架及其内置 MCP 支持。

指标:CSR 要求两条都过

"有没有引用"比想象中难测:答案可以满是 URL 却归因错误;可以引用一个真实但不支持这句话的页面;也可以引用一个看起来很像 FastAPI、但 Agent 从未检索过的 URL(模型知道 FastAPI 的 URL 长什么样)。所以 CSR(Citation Survival Rate)要求每个引用通过两项检查:

检查 做什么 方法来源
支持检查 被引用的块是否真的支持这条主张?把答案拆成原子主张,逐条做蕴含判断 FActScore + ALCE
溯源检查 这个 URL 是否真的出现在 Agent 的检索结果里?每次工具调用都有日志 工具调用日志
CSR = Agent 实际检索到的正确来源中,最终被引用在"该来源确实支持的主张"上的比例。

注意这个前提:"Agent 实际检索到的来源"。如果 Agent 根本没找到正确页面,那是检索失败,CSR 不把它算进来。CSR 只衡量"正确来源跨过边界之后发生了什么"。

三、第一轮结果:一个差点被发布的错误结论

流水线(gpt-4o-mini) CSR 95% CI
单次 RAG 0.618 [0.528, 0.708]
LangGraph Agent 0.614 [0.524, 0.705]
OpenAI Agents SDK Agent 0.323 [0.237, 0.414]

LangGraph 与单次 RAG 持平(逐题差异 +0.000),说明Agent 循环本身不损失引用。而 SDK 看起来丢了一半:127 个答案里有 66 个没有出现提示要求的 [url] 格式引用。

作者对这个数字信了很久,直到把 66 个答案逐条读完:

  • 51 个其实引用了,只是用了 Markdown 链接 [source](https://...);
  • 7 个用了别的形式,比如 [source: https://...];
  • 只有 8 个完全没有 URL(单次 RAG 有 19 个这样的答案)。

评分器只认 [url],于是把所有 Markdown 链接都算成"没有引用"。作者先写下修正后的口径([url]、Markdown 链接、其他括号形式都算),再重新评分:

流水线 只认指定格式 任意引用格式都算
单次 RAG 0.618 0.618
LangGraph Agent 0.614 0.643
OpenAI Agents SDK Agent 0.323 0.470

按任意格式计算后,SDK 与单次 RAG 的剩余差距:原评分器为 −0.16(显著),gpt-4o 评委为 −0.10(不显著)。一次运行无法定论,于是每种设置跑三次:

  • 汇总后,原评分器 −0.12、gpt-4o 评委 −0.09;
  • 只认指定格式时约 −0.30;
  • 也就是说,约 60% 是格式问题,40% 是真实但更小的引用损失;
  • 温度为 0 时检测不到损失。

四、更稳定、也更重要的效应:框架默认值

比 CSR 差距更稳定的发现是:

OpenAI Agents SDK 在其默认 API 上,127 个答案里有 51 个(40%)忽略了要求的引用格式。两次重复运行分别是 53 和 46。单次 RAG 是 0 次,LangGraph 是 8 次。

这比听起来严重:任何下游按固定格式读引用的东西——文档挂件、"查看来源"面板、护栏、像本文这样的评测——什么都读不到。引用在系统眼里已经消失,即使人读文本还能找到。

五、原因排查:不是循环,不是温度,不是传输

作者在跑实验之前就写下计划,然后一次只改一个变量:

假设 实验 结论
Agent 循环导致 LangGraph 也循环,格式保持;且 Agent 平均只搜索 1.2–1.3 次 不是循环
温度导致 SDK 流水线从未设置温度(LangGraph 用 0);对齐后错格式仅从 60 降到 54 不是温度单独导致
传输丢失来源 直接把 SDK 收到的内容 dump 出来:每个块都带完整 source_url;118 个 Markdown 链接中 98 个指向 Agent 检索过的页面 不是传输
框架默认 API SDK 默认调 OpenAI Responses API,LangGraph 的 OpenAI 集成用 Chat Completions;同一 SDK Agent 换成 Chat Completions 后,错格式从 41 降到 9;换一份已发表的 grounded 系统提示后降到 20;三次运行都成立 是默认 API / 提示

两项修复都在 Agent 侧,而检索供应商控制不了 Agent。所以作者又测了一个服务端能做的修复:

给每个块打一个短来源 ID(例如 [S3fa21c]),要求 Agent 引用 [ID],答案写完后由服务端把 ID 映射回 URL。

结果:在作者自托管的镜像上,错格式答案从每轮约 50 降到约 5,SDK Agent 的 CSR 上升 0.10–0.13,与单次 RAG 的差距基本闭合。边界也写清楚了:这些引用还没有经过人工审计。

六、这些数字可信吗

作者把"量得准不准"当成实验的一部分:

  • 两种评分器:一个蕴含模型 + 一个 LLM 评委(gpt-4o),先用作者自己的 115 个盲判断来选评分器;评委与作者一致度最高(κ 0.86)。两者不一致的地方如实报告。
  • 人工对照:30 个盲测案例中,CSR 的判断与作者一致 25–27 个(取决于评分器)。其中两个案例有同样的 Markdown 链接盲点,这正是发现格式问题的方式之一。
  • 重复运行:每个 gpt-4o-mini 设置跑三次;先写下稳健性规则——每次运行符号相同,且汇总区间不含 0。
  • 发布前自审:像敌意评审一样过了一遍流水线,找到五个 bug,其中四个本来会随文发布。格式问题是第六个,也是作者最不愿提的一个:在发现之前,他已经把夸大的数字发给了别人,只能更正。
  • 限制:一个语料、两个模型、gpt-4o-mini 每个设置三次(Claude 只跑一次)、人工检查规模小、用的是自托管的 MCP 服务器镜像而不是生产系统。

七、六条可带走的结论

  1. 如果你通过 MCP 提供 RAG,你的准确率保证止于边界。 你控制检索,不控制写答案的 Agent。如果引用对产品重要,就要测"边界另一侧"发生了什么。
  2. 框架默认值是系统的一部分。 同一个模型、同一个提示,两个 Agent 因为一个开发者可能从未看过的默认设置,引用格式表现差异巨大。
  3. 把"格式"和"有无"分开测。 "没有我解析的格式"和"没有引用"是两种失败、两种修法,两个都要计数。
  4. 读输出,不要只看指标。 "未引用答案数"这个指标掩盖了"大多数其实有来源"的事实。读 66 个答案改变了结论,而这件事本该在告诉别人数字之前做。
  5. 跑不止一次。 单次运行的剩余差距可能落在 −0.05 到 −0.16 之间;三次运行才显示它真实存在,而且比最初看起来更小。
  6. 检索侧可以修格式。 给 Agent 一个短的来源 ID 去复制,而不是一个长 URL 去重排,然后自己映射回来。

八、给做文档/RAG 产品的团队:一份最小验收清单

检查 怎么做 通过标准
定义"引用存活" 支持检查 + 溯源检查,两者缺一不可 每条引用都有被引块与工具调用日志佐证
测多框架 至少一个单次 RAG 对照 + 两个 Agent 框架 对照组的 CSR 作为基线
分开统计格式 同时统计"指定格式"和"任意格式" 两者差距 >0.05 时,先修格式再谈能力
重复运行 每种设置 ≥3 次 符号一致,汇总区间不含 0
读失败样本 人工读完所有"未引用"答案 确认不是解析器盲点
服务端兜底 短来源 ID + 答案后映射 错格式显著下降,且不引入新错误
语料卫生 检查代码块/表格是否被切坏 被切坏的块要修,再谈指标

最后一条来自该系列第 5 篇的标题:"在测 Agent 之前,先修你的语料"——作者发现自己的块里有 20% 没有代码。基准的上限,往往由被测量的那堆文本决定。


Sources

相关阅读:RAG / 数据 / 文档智能完全指南 | Google Deep Review:文档核验流水线 | MCP 供应链体检:15,341 个服务器扫描

本文由汇智网编译整理,转载请注明出处。文中数字为作者在其自建基准上的测量,适用边界见原文"Can you trust these numbers"一节。

这是「RAG / 数据 / 文档智能」专题的一部分。完整地图见:RAG / 数据 / 文档智能完全指南