面试知识库

主题 5 · 可信生成与评测#

定位:RAG 系统如何从检索质量评估、生成约束、结构化引用、到离线评测形成闭环,保证答案”有据可查、质量可测”。


一、通用知识#

1.1 核心概念与原理#

RAG 幻觉分类#

RAG 系统的幻觉分为两大类:

  • Intrinsic Hallucination(内在幻觉):模型生成的内容与检索到的来源直接矛盾——搞错数字、日期、实体名称、因果关系等。例如来源写”2024 年发布”,模型却说”2023 年发布”。这类幻觉在企业场景最为危险,因为用户容易被带有具体数据的错误结论误导,且难以肉眼识别(看起来”言之凿凿”)。检测方法主要靠 NLI(自然语言推理)模型对 (来源, 生成) 做蕴含/矛盾分类,或 LLM-as-Judge 逐句核对。
  • Extrinsic Hallucination(外在幻觉):模型凭空编造来源中没有覆盖的内容——编造不存在的论文、虚构功能描述、捏造统计数据。虽然不一定”错”,但在 RAG 场景下违背了”基于来源回答”的契约。检测方法主要靠引用覆盖率(答案中哪些句子没有被任何来源支撑)和 Groundedness 评分。

面试怎么讲:先分类再论危害——“Intrinsic 是与来源矛盾的错误,Extrinsic 是来源没覆盖的臆造;企业场景 Intrinsic 更危险,因为看起来有理有据但数据是错的。检测上 Intrinsic 靠 NLI 蕴含判断,Extrinsic 靠覆盖率衡量。“

Grounding(接地)技术#

Grounding 的核心目标是让 LLM 的生成内容可追溯到具体来源,不同厂商方案差异很大:

  • Claude Citations:Anthropic 的句子级 span 引用方案。模型在流式输出中返回 citations_delta,每条 citation 包含 cited_text(原文 span)和 document_index。被引用的源文本不计入 output token,经济性好。粒度到句子级甚至子句级,是目前商用 API 中 grounding 粒度最细的方案。
  • Google Vertex Grounding:通过 groundingMetadata 返回结构化接地信息,包含 groundingChunks(匹配到的来源片段)和 groundingSupports(含 startIndex/endIndex 的文本区间 + confidenceScores)。适合与 Google Search 或企业数据源集成。
  • 传统 [n] 标记:在 system prompt 中约束模型用 [n] 引用来源编号。优点是兼容任何模型,缺点是纯 prompt 约束不保证覆盖——模型可能遗漏引用或编造越界编号。需要后处理解析和校验。

面试怎么讲:“Grounding 从弱到强分三档:传统 [n] 标记是 prompt 约束不保证覆盖;Google Vertex 返回结构化 groundingSupports 带区间和置信度;Claude Citations 粒度最细到句子级 span 且引用文本不计 output token。我们项目用传统 [n] 方案因为要兼容 DashScope qwen 系列,但加了纯 Java 的后处理解析器补齐校验和覆盖率计算。“

CRAG(Corrective RAG)#

Yan et al. 2024(arXiv:2401.15884)提出的检索后质量评估框架:

  1. 用一个独立的 evaluator 对检索结果打三档分:Correct(与查询高度相关)/ Ambiguous(部分相关或不确定)/ Incorrect(基本不相关)
  2. 根据分档触发不同的条件补偿策略:Correct 直接使用检索结果生成;Ambiguous 补 Web 搜索增强;Incorrect 完全替换为 Web 搜索结果
  3. evaluator 可以是独立的小模型(如 T5-based),也可以复用已有的 reranker 分数——后者零额外调用但粒度粗,前者更准但引入延迟

面试怎么讲:“CRAG 核心是在检索和生成之间插一个质量闸门,三档分流:好的直接用、模糊的补 Web、差的替换为 Web。关键设计决策是 evaluator 用什么——独立小模型准但慢(约 300ms),复用 reranker 分数快(0ms)但只能做粗粒度判断。“

Self-RAG#

Asai et al. 2023(NeurIPS)在词表中加入特殊的 reflection tokens([Retrieve][IsRel][IsSup][IsUse]),让模型在生成过程中自行决定:是否需要检索、检索结果是否相关、生成是否忠于来源。

优点是端到端,模型自驱决策不需要外部编排。局限性是需要对基座模型做微调,且微调后的模型在非 RAG 任务上可能退化,工程落地门槛高。

面试怎么讲:“Self-RAG 的理念很美——让模型自己判断要不要检索、生成是否忠实,靠特殊 token 实现。但需要微调,实用性受限,所以业界实际落地更多用 CRAG 这种外部评估器方案。“

FLARE(Forward-Looking Active Retrieval)#

Jiang et al. 2023 提出的主动检索策略:生成过程中监控 token 级概率,当连续低置信度 token 出现时触发检索。

  • 主动检索(如 FLARE):模型边生成边决定何时检索,响应延迟不确定,但避免了不需要检索时的无谓调用
  • 被动检索(传统 RAG):检索前置于生成,延迟可预测,但可能检索了用不上的内容
  • 权衡:FLARE 适合长文档生成场景(如报告写作),短答案 QA 场景收益有限

同类方案对比#

方案Corrective RAGAdaptive-RAGSelf-RAGREPLUGFLARE
核心机制检索后三档评估+条件补偿路由器决定检索策略(无/单次/多次)词表加 reflection tokens 自决检索文档作为前缀拼接,ensemble 概率生成中低置信度触发检索
是否需微调否(用 LLM 做路由)是(加特殊 token)否(但需训练 retriever)
evaluator独立模型或 rerankerLLM 分类器模型内置无显式评估token 概率
适用场景通用 QA复杂度差异大的混合查询研究/已微调场景开放域 QA长文档生成
工程复杂度

1.2 业界主流方案对比(表格形式)#

Grounding 技术对比#

维度Claude CitationsGoogle Vertex Grounding传统 [n] 标记DocMind 实现
粒度句子/子句级 span文本区间(startIndex/endIndex)句子级(不保证)句子级(后处理保证)
实现方式API 原生支持API groundingMetadataPrompt 约束Prompt 约束 + CitationParser
覆盖率保证模型原生保证API 返回 confidenceScores无保证后处理计算+observability
越界校验API 层校验API 层校验invalidRefs 检测
模型兼容性仅 Claude仅 Gemini/PaLM任意模型任意 OpenAI-compatible
成本引用文本不计 output token标准计费标准计费标准计费

CRAG 实现方案对比#

维度原论文方案独立小模型评估复用 Reranker 分数DocMind 实现
评估延迟依实现而定~300ms0ms(已有分数)快路径 0ms / CE 仲裁 ~50ms
分档Correct/Ambiguous/Incorrect三档二档(高/低)+灰区三档(HIGH/AMBIGUOUS/LOW)
灰区处理Web 补偿按置信度分流无灰区概念heuristic(分布)/CE(二次打分)/disabled 三模式
Web 补偿Incorrect 全替换类似AMBIGUOUS 触发 Web 兜底
降级策略未说明按模型设计任意失败 → AMBIGUOUS(保守)

评测框架对比#

框架RAGASTruLensDeepEvalArize PhoenixDocMind 评测
核心维度faithfulness/relevancy/precision/recallgroundedness/relevance/harmfulness14+指标全覆盖trace+eval 一体化对照梯(V1-V4)+LLM-as-Judge
LLM 依赖是(GPT-4 推荐)可选是(qwen-turbo 降本)
中文支持一般一般较好较好原生中文
集成方式Python SDKPython SDKPython SDKPython SDK + UIJava 脚本驱动组件对照
成本每条 ~$0.05每条 ~$0.05每条 ~$0.03免费基础版单次全量 ¥18.4
离线/在线均可均可离线为主在线为主离线对照 + 在线 Langfuse

1.3 关键论文与技术要点#

论文年份/会议核心贡献面试关键词
Corrective RAG (Yan et al.)2024 / arXiv:2401.15884三档质量评估 + 条件补偿(Web fallback)“检索后质量闸门”
Self-RAG (Asai et al.)2023 / NeurIPS词表加 reflection tokens,模型自决检索/生成忠实度”端到端但需微调”
FLARE (Jiang et al.)2023 / arXiv:2305.06983生成中低置信度 token 触发检索”主动检索 vs 被动检索”
Adaptive-RAG (Jeong et al.)2024 / arXiv:2403.14403查询复杂度分类 → 路由到不同检索策略”复杂度驱动路由”
REPLUG (Shi et al.)2023 / NAACL检索文档作前缀拼接,ensemble 输出概率”黑盒 LLM 适配”
RAGAS (Es et al.)2023 / arXiv:2309.15217RAG 四维评测框架(faithfulness/relevancy/precision/recall)“标准评测框架”
FActScore (Min et al.)2023 / EMNLP将答案拆成原子事实逐条验证”细粒度幻觉检测”

1.4 评测指标全景表#

指标定义公式/计算方式适用场景
Recall@K前 K 个检索结果中包含相关文档的比例|relevant ∩ retrieved@K| / |relevant|召回质量评估
MRR (Mean Reciprocal Rank)第一个相关结果排名的倒数的均值mean(1/rank_first_relevant)排序质量(首条命中)
NDCG@K归一化折扣累积增益DCG@K / IDCG@K多级相关性排序评估
Faithfulness (忠实度)答案中陈述与来源一致的比例忠实陈述数 / 总陈述数 (NLI/LLM-Judge)检测 Intrinsic 幻觉
Answer Relevancy答案与问题的相关程度LLM-Judge 或 embedding 相似度答案质量评估
Context Precision检索结果中相关内容排在前面的程度加权 precision@k 的均值检索排序有效性
Context Recall回答所需信息在检索结果中的覆盖程度golden answer 中可归因到 context 的比例检索召回充分性
Hallucination Rate答案中无法归因到来源的陈述比例1 - Groundedness幻觉程度监控
Groundedness答案中可归因到来源的陈述比例有来源支撑的陈述数 / 总陈述数接地程度评估
Citation Coverage含有效引用标注的实质句比例cited_sentences / substantive_sentences引用完整性(DocMind)

1.5 离线评测 vs 在线评测#

维度离线评测在线评测
时机开发阶段,版本发布前线上运行时持续
数据预构建测试集(golden answer)真实用户查询
目的组件对照(A/B)、回归检测漂移监控、质量趋势
指标Recall/MRR/Faithfulness/Relevancy置信度分布、CRAG 分级比例、覆盖率
成本高(LLM-as-Judge 费用)低(利用已有 trace 数据)
代表工具RAGAS / DeepEval / 自建脚本Langfuse / Arize Phoenix / Datadog LLM
适用场景”加了 rerank 涨了多少?""最近一周幻觉率有没有上升?“

1.6 常见面试问答#

Q1: RAG 系统的幻觉怎么解决?

A: 分三层防御。第一层是检索质量把关——用 CRAG 风格的三档评估(HIGH/AMBIGUOUS/LOW),低分直接走兜底模板,避免把垃圾上下文喂给模型。第二层是 prompt 约束——强制引用标注 [n],low_confidence 模板明确禁止否认来源中存在的实体。第三层是后处理——用 CitationParser 解析 [n] 标注,计算覆盖率,识别越界引用。零覆盖率 + CRAG LOW 的组合标记为 ungrounded 信号。本质上是把”不可靠的生成”变成”有据可查的生成”,同时通过可观测性发现漏网之鱼。

Q2: CRAG 和 Self-RAG 有什么区别?怎么选?

A: 核心区别在评估者的位置。CRAG 是外部评估器在检索和生成之间插一道闸门,不需要修改模型;Self-RAG 是在模型内部加 reflection tokens,让模型自己判断。选型看约束条件:如果你能微调模型且只做 RAG 任务,Self-RAG 端到端效果可能更好;如果用的是商用 API(如 qwen/GPT),CRAG 是唯一可行方案。我们项目用 CRAG 风格,因为用的是 DashScope API 无法微调,且 CRAG 的三档分流可以精确控制 Web 补偿和模板切换策略。

Q3: 评测指标怎么选?Faithfulness 和 Groundedness 有什么区别?

A: Faithfulness 衡量”答案是否忠于来源”——答案中的每条陈述是否能在检索到的上下文中找到依据,主要检测 Intrinsic Hallucination。Groundedness 衡量”答案是否有据可查”——关注的是哪些陈述有来源支撑,哪些是模型自由发挥。两者视角不同但高度相关,Hallucination Rate 约等于 1 - Groundedness。选指标看目标:检测事实错误选 Faithfulness,监控模型”脱离来源”的程度选 Groundedness/Citation Coverage。我们项目两个维度都覆盖——离线用 LLM-as-Judge 评 Faithfulness,在线用 CitationParser 算 Citation Coverage 近似 Groundedness。

Q4: Faithfulness 具体怎么算?

A: 业界主流两种做法。第一种是 NLI-based:把答案拆成原子陈述,每条陈述和检索上下文做自然语言推理(蕴含/矛盾/中立),蕴含比例即 Faithfulness。第二种是 LLM-as-Judge:让一个评判模型(通常比生成模型弱一档以降本,如 qwen-turbo)逐句判断是否有来源支撑,输出 0-1 分数。我们项目用第二种,Judge 模型用 qwen-turbo(成本是 qwen-plus 的 1/10),单次完整评测 52 条 ¥18.4。

Q5: 没有 golden answer 怎么评测?

A: 分场景处理。召回阶段用”文档名子串匹配”做近似标注——预先标注每条查询的 expectedDocNames,检索结果的 sourceName 命中即视为召回。粒度粗但成本低,配合 keyword recall(答案中期望关键词出现率)做补充。生成阶段用 LLM-as-Judge 做 reference-free 评测,不依赖 golden answer,而是让 Judge 模型基于检索上下文判断答案的忠实度和相关性。不需要标注答案,只需要标注查询+期望文档+期望关键词,标注成本大幅降低。

Q6: 评测数据哪来的?

A: 52 条查询覆盖 7 类(事实类/概念类/对比类/推理类/操作类/多跳类/超范围),每类 7-8 条。构建思路:先从真实用户历史查询中抽样(去重去噪),补充覆盖不到的类别(特别是超范围和多跳),每条标注 expectedDocNames + expectedKeywords。数据集规模不大但要”覆盖边界”——超范围类专门测拒答能力,多跳类暴露链路短板。52 条 x 4 档组件变体做对照,足以定位”加了哪个组件提升最大”。

Q7: 评测成本怎么控制?

A: 三个手段。第一,Judge 模型用小模型(qwen-turbo 而非 qwen-plus),成本降 10 倍。第二,对照设计让同一次检索结果复用——V1-V4 共享同一批 chunks 只是后处理不同,避免重复调用 embedding/reranker。第三,评测脚本绕开 DocMindAgent 的缓存/SSE/早停机制,直接组合组件调用,结果可复现。单次全量成本约十几元人民币,可以频繁跑回归。

Q8: 评测发现了什么反直觉的结论?

A: 两个反直觉发现。第一,首轮 V2(RRF 融合)竟然低于 V1(纯向量)——排查后发现不是算法问题,是 BM25 索引没跟着文档重新入库刷新,暴露了运维缺口而非技术缺陷,这就是为什么必须做评测。第二,Self-Reflection 首轮评测 V4 忠实度 +0.000(零提升),但反思延迟 956ms 占总延迟 29%——后来改造为条件重写后才拉开差距(+0.037),但代价仍然是额外一整条 LLM 往返。这直接推动了我们后来用结构化引用覆盖率(纯 Java 解析 0ms)替代 Self-Reflection 的决策。


二、DocMind 实践#

30 秒口述版#

DocMind 的可信生成链路分三段。生成前:CRAG 三档检索质量闸,阈值 HIGH 大于等于 0.65 / LOW 小于等于 0.25,灰区仲裁复用 reranker 分数分布做 heuristic 判断(0ms),分档驱动三套 prompt 模板选择——标准 / 低置信带证据 / 零证据兜底。生成后:CitationParser 纯 Java 解析 [n] 标记为结构化引用,按句切分计算覆盖率,越界编号计入 invalidRefs。置信度公式 confidenceScore = clamp(coverage) * rerank-top1,无有效句时退回 rerank-top1。整套方案替代了原 Self-Reflection(省掉一整条 LLM 往返 956ms),通过 Langfuse Scores API 推送四维质量信号实现在线可观测。

详细展开#

背景/痛点(Situation)#

项目早期(V4 阶段)的可信生成依赖 Self-Reflection:每次生成后调一次 LLM 做忠实度评分,!passed 时触发流式重写。问题有三个:

  1. 延迟代价大:反思+评分额外 956ms,占端到端延迟的 29%,但忠实度只提升 +0.037——ROI 极低
  2. 置信度不可靠:靠 LLM 自评分数做置信度判断,本质是”让犯错的人给自己打分”,且分数缺乏校准——模型倾向给自己打高分
  3. 检索质量无闸门:所有检索结果不分好坏直接喂给生成模型,低质量 context 导致幻觉,事后反思修补效果有限

同时,评测体系暴露了多个问题:首轮评测 V4(Self-Reflection 仅评分不重写)忠实度 +0.000,BM25 索引漏刷导致 V2 低于 V1——说明没有系统化的质量保障机制,只靠事后补丁不可持续。

做了什么(Action)#

1. CRAG 三档检索质量闸门(生成前拦截)

RetrievalGrader 中实现 CRAG 风格的三档评估:

  • 快路径:rerank-top1 分数 >= 0.65 直判 HIGH,<= 0.25 直判 LOW,零额外调用
  • 灰区仲裁三模式可配置(grader.arbitration_mode):
    • heuristic(默认):基于已有 rerank 分数分布——top1 >= 中位且与 top2 差距 >= 0.15 → HIGH;avg <= low + 0.05 → LOW;其余 → AMBIGUOUS。0ms 零调用
    • cross_encoder:拼接 top-3 chunk 做单对重排,但发现 gte-rerank 对拼接长文本是 OOD 输入,常返回 0.000,加了 anomaly 检测自动退回 heuristic
    • disabled:直接判 AMBIGUOUS 让 Web 兜底
  • 降级原则:任意仲裁失败一律 AMBIGUOUS,保守处理而非阻塞

2. 三套 Prompt 模板(分级约束)

根据 CRAG 分级 + 证据是否为空,在 stagePromptAssembly 中三选一:

  • knowledge_qa:标准模板,强制 [n] 引用标注 + 双源区分(KB 和 Web 分区展示)
  • knowledge_qa_low_confidence:CRAG LOW 但证据非空时使用,额外强化”严禁否定来源中存在的实体”规则——解决 trace 中发现的”chunk 里有 OpenClaw 介绍但模型回答不存在”的反常识现象
  • assembleFallback:证据为空时的纯兜底,不喂任何 chunk,明确标注”知识库未匹配”并禁止断言实体不存在

3. CitationParser 结构化引用解析(生成后校验)

纯 Java 实现,不依赖 LLM,核心流程:

  • 正则扫描 [n] 标记,区分有效引用(n 在 sources 范围内)和越界引用(invalidRefs)
  • 按句末标点切句,isSubstantive 过滤过短句 / Markdown 结构符号 / 寒暄句(剥掉 [n] 和结构符号后 >= 8 字符)
  • 覆盖率 = 含有效引用的实质句数 / 实质句总数
  • 无实质句时(纯寒暄/兜底回复)标记 coverageComputed=false,上层退回 rerank-top1

4. 覆盖率驱动的置信度公式

confidenceScore = coverageComputed ? clamp01(coverage) * rerankTop1 : rerankTop1
confidenceBand = needsFallback || lowConfidence ? "LOW" : bandFromScore(confidenceScore)
plaintext
  • 双因子乘积:引用覆盖率(衡量生成质量)x rerank-top1(衡量检索质量),一维低则整体低
  • band 分档:>= 0.85 HIGH / >= 0.60 MEDIUM / 其他 LOW
  • 兜底/低置信通路强制 band=LOW,不受分数影响

5. ungrounded 信号检测

!needsFallback && CRAG LOW && coverageComputed && coverage == 0.0 时标记 ungrounded——意味着模型拿到了证据但一条都没引用。这是比简单 LOW 更严重的质量信号,对应 Langfuse span 级别 WARNING + status_message: "low confidence answer (ungrounded: 零引用覆盖)"

6. 在线质量可观测(Langfuse Scores API)

通过 LangfuseScoreClient 以 fire-and-forget 模式推送四维质量 Score:

  • confidence(numeric, 0-1)
  • citation_coverage(numeric, 0-1)
  • rerank_top1(numeric, 0-1)
  • crag_grade(categorical, HIGH/AMBIGUOUS/LOW)

这些 Score 可在 Langfuse 跨 trace 聚合、出趋势图、设告警——OTel span 属性做不到的(只能逐条看)。

量化结果(Result)#

  • 删除 Self-Reflection 后端到端延迟减少 956ms(29%),零忠实度回归(覆盖率 + prompt 模板补偿)
  • CRAG 灰区 heuristic 仲裁 0ms vs 独立小模型方案 ~300ms
  • 置信度计算从”LLM 自评分数(不可校准)“改为”覆盖率 x rerank-top1(可计算、可解释)”
  • 离线评测采用手工对照实验:52 条查询(7 类覆盖)x 4 档组件变体(V1 纯向量 → V4 +反思),通过脚本直接组合各组件跑对照、用 qwen-turbo 做 LLM-as-Judge 评分。不是自动化评测平台,但控制变量严格,能定位”加了这个组件值不值”
  • 在线通过 Langfuse Scores API 实现四维质量信号的跨 trace 聚合监控

代码锚点#

类/方法路径职责
CitationParser.parse()service/rag/CitationParser.java解析 [n] 标记为结构化引用,计算覆盖率,统计 invalidRefs
CitationParser.isSubstantive()service/rag/CitationParser.java实质句判定:剥掉标记和结构符号后 >= minLen
CitationParser.CitationResultservice/rag/CitationParser.java解析结果 record:usedSources / occurrences / coverage / invalidRefs
RetrievalGrader.grade()service/rag/RetrievalGrader.javaCRAG 三档评估入口:快路径 + 灰区仲裁分发
RetrievalGrader.arbitrateByHeuristic()service/rag/RetrievalGrader.java灰区 heuristic 仲裁:top1/top2 差距 + avg 分布判断
RetrievalGrader.arbitrateByCrossEncoder()service/rag/RetrievalGrader.java灰区 CE 仲裁:拼接 top-3 重排,含 0 分异常检测退回 heuristic
GradeResultservice/rag/GradeResult.java评估结果 record:Tier(HIGH/AMBIGUOUS/LOW) / topScore / avgScore / reason
PromptAssembler.assemble()service/rag/PromptAssembler.java标准 prompt:双源透明(KB/Web 分区) + 强制 [n] 引用
PromptAssembler.assembleLowConfidence()service/rag/PromptAssembler.java低置信 prompt:喂证据但强化”禁止否认来源中实体”
PromptAssembler.assembleFallback()service/rag/PromptAssembler.java兜底 prompt:不喂 chunk,禁止断言实体不存在
PromptAssembler.buildDualSourceContext()service/rag/PromptAssembler.java双源透明构建:KB 区 + Web 区,编号全局连续
DocMindAgent.stagePromptAssembly()agent/DocMindAgent.java:898-933CRAG 分级 → 三套模板选择的路由逻辑
DocMindAgent.stageGenerateAndPersist()agent/DocMindAgent.java:935-1104生成 + 引用解析 + 置信度计算 + ungrounded 检测 + Langfuse 推送
ConfidenceBands.clamp01() / bandFromScore()agent/ConfidenceBands.java置信度公式辅助:分数 clamp + band 分档(>=0.85 HIGH / >=0.60 MEDIUM);#31 从 DocMindAgent 析出
SourcePayloadFactory.build()service/rag/SourcePayloadFactory.javachunk → sources 映射,保证 [n]sources[n-1] 对齐
SafetyGuard.getFallbackNotice()service/rag/SafetyGuard.java兜底场景追加的免责提示文案
LangfuseScoreClient.numeric() / categorical()config/LangfuseScoreClient.javafire-and-forget 推送 Langfuse Score(置信度/覆盖率/CRAG 分级)

量化数据#

指标数值基线来源
CRAG 阈值 HIGH>= 0.65-RetrievalGrader + sys_ai_config(id 43)
CRAG 阈值 LOW<= 0.25-RetrievalGrader + sys_ai_config
灰区 heuristic gap 阈值>= 0.15-grader.high_gap 配置
heuristic 仲裁延迟0ms独立小模型 ~300ms零额外 API 调用
cross_encoder 仲裁延迟~50ms独立小模型 ~300ms单次 reranker 调用
Self-Reflection 延迟(历史,已删)956ms(占端到端 29%)-10-评测结果与发现.md
Self-Reflection 忠实度增量(历史)+0.037(条件重写后)V3 基线 0.81210-评测结果与发现.md
覆盖率最小实质句长度8 字符-citation.coverage_min_sentence_len
置信度 HIGH 阈值>= 0.85-bandFromScore()
置信度 MEDIUM 阈值>= 0.60-bandFromScore()
离线评测数据集52 条 x 7 类-手工构建
评测方式脚本驱动组件对照 + qwen-turbo Judge-非自动化平台,手工编排
RRF 融合 Recall@5 增量约 +10~15 ppV1 纯向量52 条对照测试
Cross-Encoder 重排 MRR 增量约 +14 ppV2 RRF52 条对照测试

三、追问应对#

面试官想听到的信号#

  • 对 RAG 幻觉有体系化认知(Intrinsic/Extrinsic 分类 + 对应检测方法),而不是笼统说”加 prompt 约束”
  • 理解 CRAG 的设计权衡:evaluator 选型(独立模型 vs 复用 reranker)、灰区策略(保守 AMBIGUOUS vs 激进二分)
  • 置信度方案从”LLM 自评”演进到”可计算公式”的思考过程,理解 Self-Reflection 为什么被替代
  • 评测不是”跑个分”——强调对照梯设计思想(控制变量)、评测暴露运维 bug 的故事
  • 可信生成是端到端的链路而非单点优化:检索质量闸门(前) → prompt 分级约束(中) → 引用解析+覆盖率(后) → 在线可观测(运行时)

追问预判与应答#

Q1: 为什么不直接用 Claude Citations 或 Google Vertex Grounding?

A: 两个原因。第一,模型绑定——Claude Citations 只能配 Claude 模型,Vertex Grounding 只能配 Gemini/PaLM,我们用的是 DashScope qwen 系列,需要跨模型兼容的方案。第二,可控性——传统 [n] 标记虽然不保证覆盖,但配合后处理的 CitationParser 可以做到:覆盖率可量化、越界引用可检测、置信度可计算,而且解析逻辑完全在我们手里可调——比如 isSubstantive 的阈值、Markdown 结构符号的过滤都是根据实际 trace 数据调出来的。如果未来切模型到 Claude,可以直接用原生 Citations 替换 CitationParser,架构上是兼容的。

Q2: CRAG 的阈值 0.65/0.25 怎么定的?会不会太武断?

A: 不是拍脑袋的。初始值参考了 reranker(gte-rerank)在我们评测集上的分数分布——事实类查询 top1 集中在 0.65-0.85,无关查询在 0.10-0.30。0.65 取在事实类下界,0.25 取在无关类上界附近,留出 0.25-0.65 的灰区走仲裁。而且这两个值不是硬编码——存在 sys_ai_config 表里,通过 AiConfigHolder 热加载,不用重启就能调。灰区还额外设了 high_gap=0.15low_avg_buffer=0.05 两个微调参数。

Q3: heuristic 仲裁靠 rerank 分数分布,会不会信息不够?

A: 会,所以是三模式可选。heuristic 的优势是 0ms 零调用,适合大多数情况;但对于 top1 和 top2 分数接近、avg 在中间地带的边缘 case,确实判不准。这时候可以切到 cross_encoder 模式做二次打分。但实践中发现 gte-rerank 对拼接长文本(top-3 chunk 拼成一条 passage)是 OOD 输入,经常返回 0.000,所以加了 anomaly 检测(CE_ZERO_ANOMALY_EPSILON = 1e-4)自动退回 heuristic。最保守的是 disabled 模式——直接判 AMBIGUOUS 触发 Web 兜底,宁可多查一次 Web 也不误判。降级原则是任意失败都保守处理,不阻塞主流程。

Q4: CRAG LOW 但有证据时为什么不走 fallback?

A: 这是一个踩过坑的设计决策。早期版本 CRAG LOW 直接走 fallback(不喂任何 chunk),结果发现 Web 搜索补来的低分结果被完全丢弃——模型退回到基座知识回答,反而比”喂低分证据”更差(因为基座知识可能过时)。所以改成:CRAG LOW + 证据非空走 assembleLowConfidence(带证据但强化约束),CRAG LOW + 证据为空才走 assembleFallback(真正的零证据兜底)。assembleLowConfidence 模板里加了”严禁否定来源中存在的实体”规则——这是 trace 里真实发现的 bug:chunk 里明明有 OpenClaw 的完整介绍,模型却回答”OpenClaw 不存在,可能是拼写错误”。

Q5: 覆盖率公式 coverage * rerank-top1 为什么是乘法不是加法?

A: 乘法的语义是”两个维度都要好才给高分”。如果用加法(即使加权),一个维度 0.9 另一个 0.1 还能得 0.5——但实际上 rerank 0.9(检索很准)但覆盖率 0.1(模型几乎没引用来源)的情况说明生成严重脱离检索结果,不应该给中等置信度。乘法下 0.9 x 0.1 = 0.09 直接 LOW,符合直觉。反过来覆盖率 1.0 但 rerank-top1 0.2(来源不太相关但模型全都引了)也只得 0.2,因为虽然”忠于来源”但来源本身不可靠。

Q6: 评测框架为什么不直接用 RAGAS?

A: 三个原因。第一,RAGAS 是 Python 生态,我们项目是 Java 全栈,引入 Python 评测脚本增加工程复杂度。第二,RAGAS 默认用 GPT-4 做 Judge,成本高且在国内有网络问题。第三,也是最重要的——RAGAS 评测的是端到端效果,但我们需要的是”组件级对照”(V1 纯向量 → V2 +RRF → V3 +重排 → V4 +反思)。我们的做法是写脚本把组件拆开按 variant 重新组合,控制变量跑对照——不是一个可复用的评测平台,但胜在控制变量严格,能定位每个组件的增量价值。RAGAS 的评测指标定义(faithfulness/relevancy 等)我们有参考,只是 Judge 实现和对照编排是手工脚本驱动的。

Q7: 在线评测和离线评测怎么配合?

A: 各有分工。离线评测回答”加了这个组件值不值”——通过对照梯(V1-V4)在同一数据集上比较 Recall/MRR/Faithfulness 的增量,发布前跑回归。在线评测回答”线上质量有没有变化”——通过 Langfuse Scores API 推送 confidence / citation_coverage / crag_grade / rerank_top1 四维信号,在 Langfuse 里做跨 trace 聚合、趋势图、设告警。典型工作流:离线评测发现问题(如 CRAG LOW 比例异常高)→ 修改阈值/模板 → 离线回归确认 → 上线后看在线趋势是否改善。两套评测的指标有交集(如 coverage 线上线下都看)但侧重点不同。

Q8: 如果模型完全不输出 [n] 标记怎么办?

A: CitationParser 会返回 usedSources=[]coverage=0.0coverageComputed=true(如果有实质句的话)。置信度公式 clamp01(0.0) * rerankTop1 = 0,band 直接 LOW。如果连实质句都没有(纯寒暄/兜底),coverageComputed=false,退回 rerankTop1 作为置信度。所以最差情况是置信度降低 + SSE 事件带 confidenceWarning 提示前端,不会崩溃或误报 HIGH。prompt 模板里”引用标注(强制)“的约束在实际测试中覆盖率还不错——qwen-plus 在有来源的标准模板下基本都会标 [n],覆盖率通常在 0.6-0.9 之间。

Q9: 双源透明模式为什么要把 KB 和 Web 分区展示?

A: 信源可信度不同需要在 prompt 层面让模型感知到。KB 来源经过人工上传和审核,可作为确定性结论引用;Web 搜索结果未经审核,模型引用时必须加限定语(“据网络公开信息”)。如果混在一起编号,模型无法区分信源等级。buildDualSourceContext 把 KB 区放前面、Web 区用分隔线 --- 隔开并标注”未经知识库审核,仅供参考”,全局编号连续保证 [n] 对齐。前端引用卡片也通过 SourcePayloadFactory.build()type 字段区分 knowledge_base / web,展示不同样式。

反击引导#

“说到 CRAG 的灰区仲裁,这个设计和我们 Agentic RAG 的循环终止条件是相互配合的——AMBIGUOUS 时触发 Web 兜底,对应 agentic 循环中 supervisor 的 webSearch 工具调用决策,这块可以展开讲一下 agentic 循环的设计。”

“覆盖率 x rerank-top1 的置信度公式,其中 rerank-top1 涉及我们的双路召回(向量 + BM25)+ RRF 融合 + Cross-Encoder 重排的整条检索链路——如果感兴趣可以聊聊检索排序的设计。”

“在线可观测用 Langfuse Scores API 推送,这是我们整套 OTel + Langfuse 可观测体系的一部分——从 StageEmitter 统一三套埋点到 LangfuseScoreClient 的 fire-and-forget 推送,可以展开讲可观测架构。”