AI 系统的数据血缘:从数据源到响应的全链路追踪
某用户提交了一个支持工单:"你们的 AI 助手告诉我合同续签截止日期是 3 月 15 日,实际上是 2 月 28 日,我们因此错过了截止日期。"你调出日志,响应已生成,模型没有报错,所有指标都是绿色。但你根本不知道它检索了哪份文档、模型读取了什么内容,也不知道那个日期究竟来自上下文还是完全被幻觉出来的。
这就是数据血缘的缺失。这不是监控问题,而是从一开始就埋下的架构问题。
大多数生产 RAG 系统只对容易观测的指标进行了埋点:每个请求的延迟、token 数量、API 错误率。但这些指标没有一个能告诉你模型在生成答案之前到底读了什么。没有检索溯源——即不清楚取回了哪些片段、来自哪些文档、相关性得分是多少——你就无法调试语义层面的失败,无法满足合规审计要求,也无法系统性地提升检索质量。
好消息是,添加血缘追踪不需要重建整个技术栈。相关模式是轻量级的,工具链也已经相当成熟,OpenTelemetry 现在已经为生成式 AI 制定了标准化的语义约定。难点在于:你需要在需要血 缘之前就意识到这个必要性,而不是等到生产事故发生后才被迫补救。
血缘真正丢失的三个环节
理解溯源信息在哪里消失,才能知道在哪里进行埋点。
检索边界是最关键、也最常被忽略的环节。当你的向量数据库返回五个文档片段时,应用通常只提取文本内容,丢弃源元数据,然后把拼接好的字符串直接传入 LLM prompt。向量数据库返回的文档 ID、相关性得分、时间戳和源元数据就这样被悄悄丢弃了。从这一刻起,即使你对 LLM 调用有完美的追踪,也无法将响应的任何部分关联到具体的源文档。
上下文组装步骤是将多个检索片段进行合并、去重、截断和格式化后再送入模型的过程。如果这个步骤没有被追踪,你就看不到实际进入上下文窗口的内容——不是你打算输入的内容,而是实际到达的内容。这很重要,因为截断顺序和片段排列会影响模型行为,而要复现一个故障,你需要了解确切的上下文。
生成内容到具体声明的映射是最难的问题。模型可能从三个不同的检索片段中合成信息,形成一个句子。如果没有声明级别的归因——将输出中的每个事实映射回其源证据——你就无法判断某个具体声明是基于检索到的上下文还是凭空捏造的。这正是"模型出现了幻觉"成为调试链终点而非起点的根本原因。
轻量级血缘追踪的实际形态
最小可行血缘实现只需增加三件事:在上下文中加入文档标识符、记录检索内容的检索 span,以及生成后的事实核查。
在上下文 prompt 中标注文档是最简单的改变。不要直接插入原始片段文本,而是用源标识符将每个片段包裹起来:
[DOC-ID: doc_4821, chunk 3, score: 0.87]
企业账户的合同续签截止日期为 2 月 28 日。
[/DOC-ID]
这不需要任何外部工具。当用户或审计人员问"这个答案从哪里来?"时,你可以在追踪记录中搜索被引用的文档 ID,然后拉取原始来源。
在追踪流水线中添加检索 span,用于记录检索步骤实际返回的内容。一个检索 span 应捕获:发送给向量数据库的查询、返回的结果数量、文档 ID 和相关性得分,以及应用的元数据过滤条件。OpenTelemetry 的 GenAI 语义约定现在已经标准化了模型名称、token 数量和完成原因等属性——检索步骤应该作为子 span 进行埋点,并在同一追踪链中保持父子关系,从而覆盖整个流水线。
Langfuse、Arize Phoenix 和 LangSmith 等工具都支持这种模式。Langfuse 的 @observe() 装饰器只需三行代码:安装 SDK、设置 API 密钥、包裹你的 RAG 函数。追踪结果会呈现嵌套结构:顶层请求包含一个检索 span、一个上下文组装 span 和一个或多个 LLM span,每个 span 都带有时序和负载数据。这就是你打开一个失败追踪后,能够精确看到哪些文档被检索、如何被组装、模型收到了什么的基础。
生成后的事实核查将响应拆分为单条声明,并逐一对照检索到的上下文进行验证。这种方法让幻觉检测从零星发现变为系统化流程。对于每一条生成的声明,事实核查都会问:这是否有检索文本的支撑?如果没有,则将其标记为潜在幻觉。Token 概率归因(TPA)更进一步,从数学上将每个输出 token 的概率归因于七个来源——查询、上下文、历史 token、自身 token、FFN、LayerNorm 和初始嵌入——从而实现对无依据内容来源的精细定位。
起步阶段不需要 TPA。一个更简单的基于 LLM 的事实核查——将每条声明和检索上下文传给一个小模型,询问"这条声明是否有所提供文本的支撑?"——以极低的延迟成本就能获得大部分实用价值。
利用血缘调试"这个答案从哪来?"
血缘追踪将调试从"模型出现了幻觉"(一个死胡同)转变为结构化的诊断流程。
第一步是将故障关联到具体的追踪记录。当用户反馈了一个错误答案,你需要重建完整的执行过程:查询是什么、检索了哪些片段、上下文窗口里有什么,以及模型返回了什么。大多数可观测性平台支持按用户 ID、会话或查询的语义指纹搜索追踪记录。没有这些,你只能依赖应用日志——而那些日志几乎肯定不包含检索负载。
第二步是区分检索失败和生成失败。这是两种不同的问题,需要不同的修复方式。
检索失败意味着正确的文档没有出现在 top-k 结果中。判断依据:事实核查显示错误答案在任何检索片段中都没有依据;相关文档确实存在于你的语料库中,但没有被返回。修复方案:调整嵌入相似度阈值、修改元数据过滤条件,或优化分块边界,避免相关内容在语义边界处被切分。
生成失败意味着正确信息已被检索到,但模型没有准确使用它——它与来源相矛盾、对相互冲突的片段取了平均值,或者默认使用了先验知识。判断依据:事实核查显示错误答案确实出现在检索上下文中,但被错误归因、误读,或被另一个检索片段所矛盾。修复方案:减少上下文噪声(减少不相关片段)、调整片段排列顺序,或添加明确指令说明如何处理冲突信息。
第三步是系统性地追溯检索质量问题的根因。一次性的调试只能发现单个故障。而基于血缘的分析能呈现规律:哪些文档来源的相关性得分异常偏低,哪些查询模式持续返回劣质结果,哪些分块大小与较高的事实核查失败率相关联。嵌入漂移——随着语料库增长,检索质量逐渐下降——如果不随时间追踪每次检索的相关性得分,是完全看不见的。
W&B 的 Wandbot 团队在他们的 LLM 文档助手上实践了这套方案。通过使用 W&B Weave 装饰器添加 span 级别追踪,并检查中间步骤的数据流,他们发现了在聚合指标中完全看不到的检索质量问题。准确率从 72% 提升到 81%,端到端延迟下降了 84%——不是通过更换模型,而是通过理解模型实际接收到的内容。
合规审计轨迹与调试是不同的问题
调试血缘和合规血缘在实现上有交集,但在需求上有差异。
调试血缘需要快速且可查询:你在看一个具体故障,需要拉取特定会话的追踪记录,并深入到检索负载。保留期可以较短——几周就能覆盖大多数事后调查。
合规血缘需要不可变且全面:你需要向审计人员(或监管机构)证明某个特定响应是在某个特定日期基于特定来源生成的,没有未经授权访问敏感数据,系统在已记录的策略范围内运行。欧盟《人工智能法案》对高风险 AI 系统的要求包括可解释性、风险管理和审计轨迹条款。GDPR 的被遗忘权带来了更难的问题:如果某用户的文档被删除,你能追踪到每一个从该文档生成的响应并标记以供审查吗?
实际解决方案是,合规血缘需要将文档 ID 与响应一起存储,而不仅仅是检索追踪。你需要一个从响应 ID 到贡献它的源文档 ID 集合的映射,带有时间戳。这样就能运行查询:"鉴于文档 X 已被删除,这段时间内哪些响应可能包含了来自该文档的内容?"没有这个映射,你就无法回答这个问题,除非重新运行这段时间内的所有查询——在任何真实规模下这在计算上都是不可行的。
智能体系统使这一问题更加复杂。单次 agent 运行可能产生多个子 agent、发起十多次检索调用、执行代码并跨步骤串联结果。审计轨迹必须捕获完整的决策树——不只是 最终响应,还包括哪些检索结果影响了哪些工具调用,哪些中间推理步骤基于哪些来源。这就是为什么具有父子 span 关系的分布式追踪是正确的基础:每次工具调用、检索步骤和 LLM 调用都是子 span,完整的追踪图就是血缘记录。
优先构建什么
为生产 RAG 系统添加血缘追踪的优先顺序,取决于哪类故障代价最高。
如果幻觉调试是你最迫切的问题,本周就去埋点检索 span,并在上下文 prompt 中添加文档标注。对生产流量的样本异步运行事实核查。你不需要对每个请求进行实时事实核查——5% 的采样就能给你足够的信号来识别系统性问题,同时不会在关键路径上增加延迟。
如果合规是驱动因素,文档 ID 到响应的映射表是第一个要构建的东西,优先于任何追踪可视化。一个简单的追加日志:{response_id, document_ids[], timestamp, query_hash} 就能给你审计轨迹的核心。追踪可视化可以之后再做;溯源映射需要从一开始就存在,因为你无法事后重建它。
如果目标是检索质量分析,就在检索 span 中添加相关性得分日志,并基于滚动时间窗口构建仪表板。将 Precision@k、Recall@k、MRR 等质量指标嵌入 CI 流水线的评估运行中,这样在问题到达生产环境之前就能检测到漂移。
真正难以事后补加的是长期运行 agent 对话中的声明级归因。如果你的 agent 有 90 分钟的会话历史,你想将某个具体声明追溯到第 7 轮的某次检索事件,就需要存储完整的会话追踪及其检索负载。请尽早开始捕获这些数据;事后从稀疏日志中重建几乎是不可能的。
拖延的代价
从一开始就构建血缘追踪的团队不会花太多时间思考它——它只是流水线的一部分。而跳过它的团队,在事件响应时只能盯着日志,这些日志只告诉他们出了什么问题,却不说明为什么。
"这个答案从哪里来?"是每个生产 AI 系统最终都必须回答的问题。架构决策在于:你是从一开始就将这个答案内置到可观测性层,还是等到一个用户投诉演变成法律问题后,再花三个工程师-周来事后重建。
数据血缘不是构建 AI 系统中最光鲜的部分。但它决定了你能否在系统上线后真正系统性地改进它——以及当出错时你能否有效地为其辩护。
- https://opentelemetry.io/docs/specs/semconv/gen-ai/
- https://www.datadoghq.com/blog/llm-otel-semantic-convention/
- https://opentelemetry.io/blog/2024/otel-generative-ai/
- https://www.langchain.com/langsmith/observability
- https://langfuse.com/blog/2025-10-28-rag-observability-and-evals
- https://phoenix.arize.com/llm-tracing-and-observability-with-arize-phoenix/
- https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/
- https://towardsdatascience.com/detecting-hallucination-in-rag-ecaf251a6633/
- https://arxiv.org/html/2512.07515
- https://wandb.ai/site/articles/llm-observability/
- https://www.solidatus.com/blog/why-data-lineage-is-essential-for-ai-7-governance-challenges-solved-by-ai-ready-lineage/
- https://www.evidentlyai.com/llm-guide/rag-evaluation
- https://unstructured.io/insights/how-to-use-metadata-in-rag-for-better-contextual-results/
- https://towardsdatascience.com/agentic-rag-failure-modes-retrieval-thrash-tool-storms-and-context-bloat-and-how-to-spot-them-early/
- https://developers.redhat.com/articles/2026/04/06/distributed-tracing-agentic-workflows-opentelemetry
- https://blog.langchain.com/end-to-end-opentelemetry-langsmith/
- https://uptrace.dev/blog/opentelemetry-ai-systems
