Golden Testset 的文本覆盖度:问题与方法综述¶
问题和粗思考¶
(手写的)
这里主要想做的是,如何建立一个科学的、可以定量化和测试化的方法,来判断golden testset里新加的测试用例, 是真的扩展了业务场景覆盖,还是和已有用例重复。这里首先要解决的是如何衡量原始文本,因为只有先把文本里已有 的内容变成可以比较的基准,后面才有办法讨论coverage。
note: 还是个草稿状态
问题是如何定量化,自动化衡量golden test的覆盖率,以及如何衡量新添加的测试用例
首先判断覆盖率,覆盖率就需要一个基准,代码的覆盖率很简单,因为代码执行路径是确定的, 插桩统计就可以了,这里的覆盖率讨论的是业务场景的覆盖率,于是天然就带来了语义解析的复杂度。
讨论覆盖率就必须评估已有文件,也就是从现有状态的陈述构造出潜在的问题和答案,从这个角度来看, 对所有场景的陈述,实际上是和构造golden test同构的,这个时候可以看看deepeval的synthesizer。
所以一种可行的方法就是,先通过synthesizer构造测试集合,作为标注的内容,然后将已有的内容进行比对。 注意: 这里的核心是如何衡量原始文本,使用synthesizer生成的测试,因为我认为这可能是可信的代表参数信号是一种方法, 但可能存在其他方法,这是具体怎么做的问题。
但这个方法有缺陷,比如说如果我直接用synthesizer生成所有的golden测试用例,那么测试覆盖率就直接到100%了, 那测试覆盖率的意义在哪里呢?接下来如何深入提高呢?
那么接下来就是有几个方向:
-
覆盖率很高了,但是我们需要更多不同微妙的场景,产生不同的答案,起个名字叫做丰荣度吧, 具体来讲,就是在已有测试用例上加新的样例,重复程度不超过一个特定阈值(again,如何定义重复程度是一个问题) 也就是说在覆盖率变化不大的情况下,有更多有价值的测试用例。 从这里就可以看出,覆盖率并不是唯一的指标,或者来说,我们需要详细定义什么叫做llm这里的“覆盖率”。
-
当覆盖率已经很高了,但这种覆盖率是通过synthesizer作为标尺来生产的,强依赖于文本, 这个时候,我们需要考虑synthesizer本身的产出是否可以很好的代表已有文本内容。
-
测试标准本质代表回环,这个时候需要建立外部事件输入的反馈环节,当当前测试集合代表文本信息时, 我们需要按照外部输入来做检查,这里就是item 1输入的来源,除此之外,需要有能力判断这个是否是文档信息不够需要补充的缘故。
-
当文档重建的时候,如何高效构建已有的这些内容。
研究综述¶
(ai生成的)
本文关心的问题是:给定一批描述产品能力、业务规则和运行约束的原始文本,以及一套已有的 Golden testset,如何判断测试集覆盖了多少原始文本所表达的内容;加入一个新测试后,又如何 判断它是在扩展覆盖、增加已有场景的有效变化,还是只产生了语义重复。
这不是一个已经存在统一定义和标准工具的问题。DeepEval、Ragas 等框架可以从文档生成测试 数据,也能评估单个测试结果,但截至目前没有提供一个经过充分验证的“业务语义覆盖率”。 相邻研究分布在自动问题生成、文本信息覆盖、需求追踪、model-based testing、RAG 测试集 覆盖、自然语言推理和数据集多样性等领域。下面按照这些方法究竟把什么当作覆盖基准来整理。
一、覆盖率首先是一个“选择表示”的问题¶
代码覆盖率能够定义 line coverage、branch coverage,是因为程序已经具有明确的语法结构和 执行语义。自然语言文本没有天然的最小语义单位。同一段文字可以被表示为:
- token;
- 句子或段落;
- 信息单位;
- 原子命题;
- 实体和关系组成的知识图;
- 条件、动作、结果、异常组成的业务规则;
- 从文本中可能提出的问题;
- 真实用户可能触发的业务场景。
选择哪一种表示,就决定了覆盖率的分母是什么。因此,“文本覆盖率”不是脱离表示而独立存在 的客观数字。任何方法都至少包含下面三个步骤:
- Unitization:把原始文本转换为一组可计数或可加权的单位。
- Alignment:判断一个测试用例覆盖了哪些文本单位。
- Aggregation:把局部匹配汇总成覆盖率,并处理单位重要性、重复和不确定性。
如果第一步选用句子,最终得到的是句子覆盖率;如果选用原子命题,得到的是命题覆盖率;如果 选用生成问题,得到的是在该问题生成器分布下的探针覆盖率。它们都可以是有效指标,但含义 不同,不能直接互换。
二、文本覆盖的五类方法¶
下面五种方法是并列的测量方案。它们分别选择文本结构、原子命题、生成问题、表示空间或业务 模型作为覆盖基准,不构成必须依次执行的流水线。实际系统可以只选择其中一种,也可以组合 多个视角交叉验证。
2.1 文本结构单位覆盖:以句子、段落和列表作为分母¶
最简单的方法是把句子、段落、标题或列表项作为 source units,然后把 Golden 映射回这些 单位:
这类方法的优点是:
- 分母直接来自原始文本,不需要先生成一套 reference questions;
- 单位能够绑定稳定的 source span,结果容易解释;
- 文档变化后可以根据 source span 做增量更新;
- 容易建立一个便宜、确定性较强的 baseline。
它的问题也很直接:
- 一个句子可能包含多条独立规则;
- 一条规则可能跨越多个句子;
- 标题、定义、例子和关键限制会被赋予相同权重;
- 两个问题映射到同一句,不代表它们覆盖了相同的业务行为;
- 相似度匹配容易忽略否定、数值边界和条件变化。
AURA-QG 是目前与这个思路最接近的公开工作。它把普通段落拆成句子,把较短的列表整体作为 necessary Information Units,同时建立列表项、连续句子窗口等 optional Information Units。问题先经过 bi-encoder 检索,再由 cross-encoder 重排;若问题能够可靠映射到一个 IU,则相应的 necessary IU 被标记为 covered。最终 Coverage 是被覆盖的 necessary IU 占全部 necessary IU 的比例。
AURA-QG 的贡献是证明了一条可实现路径:
但它主要服务于 factoid、FAQ、remembering 和 understanding 类型的问题。论文本身明确 说明,它没有处理 application、analysis、evaluation、复杂推理,以及表格、图表和图片; 它也承认 structural entropy 不能衡量 conceptual breadth。它对人工偏好的验证只有 24 组 question-set comparison:Coverage 与人工多数票一致 19 次,即 79%。这足以说明方法 值得作为 baseline,但不足以证明句子/list block 是业务语义的正确分母。
因此,这类方法适合回答:
测试问题是否触及了原文的大部分信息表面?
不适合直接回答:
测试集是否覆盖了所有业务条件、异常流程和行为差异?
2.2 原子命题覆盖:把文本拆成最小可判断语义¶
句子级分母过粗时,可以把文本拆成能够独立判断真假的最小命题。PropSegmEnt 的研究动机 就是:一个句子往往表达多个 proposition,这些 proposition 可能拥有不同的真值,因此 句子级 entailment 会掩盖局部差异。
相关资料:PropSegmEnt 论文
例如:
退款超过七天仍未完成时,商户可以联系客服,但不能重复发起退款。
可以被拆成:
这时可以定义:
相对于直接生成 Golden,原子命题有几个优势:
- 它仍然接近原文,而不是提前引入用户语气、persona 和问题形式;
- 每个命题可以强制绑定证据片段;
- 可以用 textual entailment 检查命题是否真的来自原文;
- 更容易发现一个句子中只覆盖了一半信息的情况;
- 更适合作为后续问题生成、业务规则抽取和覆盖分析的公共中间表示。
但“原子性”本身并不完全客观。同一内容可以按不同粒度拆解;代词、时间条件、跨句依赖和 隐含前提可能使单个命题失去上下文。自动提取还会出现:
- omission:漏掉原文命题;
- over-splitting:把不可独立成立的信息错误拆开;
- under-splitting:多个条件仍混在同一个命题中;
- hallucination:加入原文没有表达的条件;
- duplication:同一命题被不同措辞重复抽取。
因此,命题提取器本身必须作为一个待评估的测量工具,而不能直接视作真值。至少需要检查:
- proposition 对 source span 的 entailment precision;
- 在人工标注小样本上的 proposition recall;
- 不同 prompt、模型、seed 下的稳定性;
- 对否定、阈值和条件变更的敏感度;
- 多次提取时新增命题数量的饱和曲线。
2.3 生成问题覆盖:将 Synthesizer 作为条件抽样器¶
使用 DeepEval Synthesizer 从文档生成 Golden,并不是一个错误方向。更准确的理解是: Synthesizer 定义了一个由文档、模型、prompt 和配置共同决定的问题分布。
可以把这个过程写成一个条件抽样问题。定义:
- \(D\):原始文档;
- \(\theta\):生成配置,包括模型、prompt、temperature、evolution 类型和过滤规则;
- \(q\):一个生成出来的问题;
- \(T\):当前待评估的 Golden testset;
- \(\operatorname{Cover}(T,q)\):当前测试集是否已经覆盖问题 \(q\) 所代表的语义场景。
那么一次生成可以写成:
这里的 G 不是“原始文本中全部可能问题”的真实分布,而是 Synthesizer 在给定文档和配置后 诱导出的条件分布。改变模型、prompt、temperature 或 evolution 配置,都会得到不同的 G。 seed 通常用于取得这个分布中的不同样本,而不应被误认为业务语义空间本身的一部分。
如果 Synthesizer 还会过滤不清晰、不可回答或不忠实的问题,那么最终留下来的 probe 实际 来自进一步的条件分布:
将条件分布展开后,可以写成带归一化常数的形式:
其中:
\(\mathbf{1}[\cdot]\) 是指示函数:条件成立时取 1,否则取 0。这个过滤会改变采样分布。例如复杂但 容易被判为“不清晰”的问题可能被系统性删除。因此,最终覆盖率只能解释为“对通过过滤的 生成问题分布的覆盖”,不能自动推广到所有可由原文提出的问题。
在这个条件分布下,当前测试集 T 的 generator-conditioned coverage 可以严格定义为:
也就是:
它是一个条件概率或条件期望,不是对未知的“全部业务场景数量”的直接计数。
实际运行时无法枚举 G_valid,只能用 Monte Carlo 样本估计。独立生成 n 个 held-out probes:
样本估计量是:
例如独立生成 100 个有效 probe,其中 73 个对应的语义场景已被 T 覆盖,则当前估计值为 0.73。这个数字仍应同时报告采样数量和置信区间;如果 probes 来自同一个 batch、同一个 source unit 或存在去重/重写依赖,它们不满足严格独立同分布假设,应按 source unit 做 cluster bootstrap,而不能直接把 100 个问题都当作独立观测。只有在 iid 假设近似成立时, 才可以使用下面的标准误近似:
从这个分布反复生成问题,相当于使用一批随机探针对原始文本进行测量。于是可以定义:
这个数字表示:
当前测试集覆盖了多少生成器认为可能由该文档产生的问题概率质量。
它不表示:
当前测试集覆盖了原始文本中的全部潜在业务场景。
QA-based summarization evaluation 已经使用过相近思想。QAEval 从 reference/source 生成问题, 再检查 candidate 是否能够回答,以估计二者的信息重合。QuestEval 进一步使用双向 question generation 和 QA,在不依赖人工 reference summary 的情况下估计信息 precision 与 recall。
这些工作说明:使用生成问题作为信息覆盖的代理测量有研究基础。但测量结果是整个 pipeline 的合成结果:
question generation recall
x question faithfulness
x alignment accuracy
x answer/coverage decision accuracy
如果 Synthesizer 总是围绕容易表达的事实生成问题,它会系统性忽略复杂条件;如果生成器 反复采用相同模板,样本数量增加也不会扩展语义支持集。
要避免“自己生成、自己达到 100%”的回环,应把生成用途分开:
measurement probes 不进入被评估测试集,并应使用不同 seed、prompt,最好还包括不同模型。 更严格的做法是 leave-one-generator-out:由 G1 生成 candidate tests,由 G2/G3 生成测量 探针。如果 G1 产生的测试只能覆盖 G1 自己的表达风格,而无法覆盖其他生成器产生的有效 问题,说明覆盖提升主要来自 generator bias。
还应按 source unit 分层生成,而不是让生成器自由决定问题分布。例如每个 source unit 生成相同数量的 held-out probes,先计算每个 unit 内的覆盖,再在 units 之间汇总。这样 一个容易生成问题的段落不会因为产出几十个相似问题而取得过高权重。
这个分层条件抽样可以进一步写成:
其中 u_i 是从文档 D 得到的 source unit。先选择一个 unit,再在该 unit 的条件下生成:
为每个 unit 定义权重 w_i,并要求:
那么分层后的总体覆盖定义为:
若每个 unit 生成 m_i 个 probe,其样本估计量为:
当所有 source units 同等重要时,可以设:
如果目标是生产代表性或风险覆盖,也可以让 w_i 来自生产频率、业务严重度或人工优先级。 但此时指标的名字和解释必须相应改变,例如 production-weighted probe coverage 或 risk-weighted probe coverage,不能再称为未经加权的文本覆盖率。
自由生成与分层生成估计的是两个不同目标:
两者都值得保留。前者能观察生成器自然产生的问题空间;后者更接近“原始文本各部分是否都 被平等检查”。它们的差距本身也是一个有意义的诊断信号:差距很大,说明 Synthesizer 的 自然采样分布对部分文本单位存在明显偏置。
2.4 表示空间覆盖:使用 embedding 几何和主题分布¶
另一类方法不先离散定义“命题”,而是把原文 chunk 和测试问题放在同一 embedding space。
2.4.1 几何计算实际测量了什么¶
设文档被切成 \(N\) 个 chunks,其向量为:
测试集包含 \(M\) 个问题,其向量为:
可以先对文档向量 \(X\) 做 K-means,得到文档语义簇:
然后把每个测试问题分配到最近的文档 cluster。第 \(k\) 个 cluster 附近的问题数量为:
其中 \(\mu_\ell\) 是第 \(\ell\) 个文档 cluster 的质心。\(n_k\) 可以回答:
哪些文档主题附近聚集了较多测试问题,哪些主题附近问题较少?
还可以比较文档 units 与测试问题在各 cluster 上的数量占比:
\(p_Q(k)\gg p_D(k)\) 表示问题对该 cluster 过度集中,\(p_Q(k)\ll p_D(k)\) 表示相对稀疏。 这仍然测量的是测试注意力分配,而不是 cluster 内部的信息覆盖。
但它只是 question density / cluster occupancy,不是覆盖率。一个 cluster 即使有一百个 问题,这些问题也可能全部围绕 cluster 中同一个局部事实,其他内容仍未被触及。
要观察 cluster 内部是否存在空白,应该把方向反过来:对每个文档 chunk,寻找离它最近的 测试问题:
如果使用 cosine similarity,也可以写成:
\(r_i\) 或 \(s_i\) 直接给出的仍然只是 proximity profile:
- 哪些文档 chunk 附近存在语义相近的问题;
- 哪些 chunk 到所有测试问题都很远;
- 各 cluster 内的最近距离分布;
- 新增测试前后,文档到测试集的距离是否缩短。
这个方向不能反过来。如果计算的是“每个问题到最近文档的距离”,主要测到的是问题是否与 文档相关或可由文档回答,而不是文档内容是否被测试集覆盖。
2.4.2 从距离到覆盖率存在一层 proxy¶
距离不能直接等同于覆盖。要从 \(r_i\) 得到一个比例,必须额外引入判定规则。例如设阈值 \(\tau\),把距离小于阈值的文档 chunk 当作“被覆盖”:
其中 \(w_i\) 是文档 chunk 的权重。如果所有 chunks 同等重要,则 \(w_i=1\)。这个指标更准确 的名字是:
embedding neighborhood coverage,或 thresholded semantic coverage proxy。
同样可以计算单个 cluster 内的代理覆盖:
\(n_k\) 回答“附近有多少问题”,而 \(\widehat{C}_{k,\tau}\) 回答“该 cluster 中有多少文档 单位落在至少一个问题的阈值邻域内”。后者更接近覆盖,但仍然依赖 embedding、chunk 和 \(\tau\) 三层代理假设。
也可以不做硬阈值,而是使用随距离衰减的核函数:
例如:
这种 soft score 会让离测试问题越近的文档单位获得越高分。它适合比较同一 embedding 模型、 同一分块方法和同一参数下两个测试集的相对变化,但不应被解释成“真实业务场景已经覆盖了 百分之多少”。
从原始文本到这个数字,实际经过了三层代理假设:
原始文本 chunk
-> 假设 chunk 是合适的语义单位
embedding distance
-> 假设向量距离保留了与测试有关的语义差异
距离阈值或核函数
-> 假设足够近就表示已经被测试覆盖
任何一层不成立,最终数字都可能偏离真正想测量的业务覆盖。
2.4.3 聚簇密度、邻近度和覆盖代理必须分别报告¶
表示空间方法至少包含三个不同指标:
- Cluster density / occupancy
问题分布在哪些文档 clusters 上,各 cluster 的问题数量是多少。它反映测试注意力的分配, 但大量重复问题也会提高密度。
- Nearest-distance profile
对每个文档 chunk 计算最近测试问题距离,并报告均值、中位数、尾部分位数和低覆盖 clusters。它能发现 cluster 内部的空洞,不需要先把距离强行解释为覆盖。
- Calibrated coverage proxy
使用经人工标注校准的阈值或判别器,把“语义邻近”转换成 covered/not-covered,再汇总成 比例。
因此,一个问题密度较低的 cluster 可能被少量高质量问题充分触及;一个问题密度很高的 cluster 也可能充满近似重复而实际覆盖狭窄。不能直接通过 \(n_k\) 推出 \(C_k\)。
2.4.4 如何校准这层 proxy¶
阈值 \(\tau\) 不应根据 embedding 分布任意选择。可以抽取一批 (document unit, question) pair,由人标注:
然后用这些标注:
- 画距离对 covers/not-covered 的 precision-recall curve;
- 根据希望保证的 precision 或 recall 选择 \(\tau\);
- 在独立验证集上报告误判率;
- 比较不同 embedding、cross-encoder 和 NLI judge;
- 检查分数对 \(\tau\)、K-means 的 \(K\) 和 chunk 粒度是否敏感。
更可靠的 pipeline 通常是:
embedding / cluster
-> 高召回地找出 candidate question-unit pairs
cross-encoder 或 NLI / LLM judge
-> 判断是否真的覆盖相同命题或行为
人工标注样本
-> 校准阈值并测量判断器误差
2.4.5 相关研究与适用边界¶
2025 年的一篇 RAG semantic test coverage 预印本采用了相近思路:
- 对文档切块并编码;
- 对文档 embedding 做 K-means;
- 过滤明显无关的测试问题;
- 对每个文档块寻找最近的测试问题;
- 汇总 basic coverage、cluster-weighted coverage;
- 为低覆盖 cluster 提取主题并建议新问题。
相关资料:Methodological Framework for Quantifying Semantic Test Coverage in RAG Systems
该论文把平均相似度等结果称为 coverage,但从测量有效性上,更准确的说法是 embedding-based soft coverage proxy。它可以快速找到没有测试问题靠近的文档区域,也 适合比较两个测试集谁更接近文档整体分布,却不能可靠区分:
- 条件成立与不成立;
- 允许与禁止;
- 阈值前、阈值点和阈值后;
- 相同主题下不同 expected behavior;
- 单轮明确问题与多轮歧义;
- RAG 命中与错误检索导致的不同系统路径。
因此它更适合作为 gap discovery 和粗粒度 distribution coverage,而不是业务等价性的 最终判定。SemHash、近似去重和 Vendi Score 也属于这一层:它们可以发现表面重复和多样性 趋势,但无法独立证明新增测试具有新的业务判别意义。
2.5 需求与行为模型覆盖:需求追踪与 model-based testing¶
软件工程中更接近“业务场景覆盖”的传统方向是 requirements-based testing 和 model-based testing。这里实际上包含两个独立阶段:
因此,2.5 的核心首先是 semantic modeling:如何从叙述中抽取业务参与者、条件、状态、 行为和预期结果,并用稳定的结构化语句表示。覆盖率是模型建立之后的第二步。
2.5.1 结构化中间表示¶
最小的业务规则可以表示成一个带 modality 的条件行为元组:
其中:
actor:谁执行行为或受到规则约束;trigger:什么事件使规则开始适用;precondition:规则成立前必须满足的状态和条件;action:执行、禁止或要求执行的行为;modality:required、permitted、prohibited等规范性类型;outcome:行为后预期产生的结果或状态;exception:会覆盖一般规则的例外条件。
工程实现可以使用带证据和不确定性的 schema:
id: refund.pending.after_7d.no_resubmit
source:
document_id: refund_policy
span: "退款超过七天仍未完成时……"
actor: merchant
trigger: elapsed_time > 7d
preconditions:
refund_status: pending
action: submit_refund_again
modality: prohibited
outcome: unspecified
exceptions: []
extraction:
explicitness: explicit
confidence: 0.96
extractor_version: rule-extractor-v1
source span、explicitness 和 confidence 很重要,因为结构化模型不是原文真值,而是对 原文的一次解释。模型必须能够回溯到证据,并区分原文明确陈述、跨句推断和文档中没有说明 的字段。
2.5.2 从叙述拆出结构化规则¶
例如原文为:
退款申请提交后会进入 pending 状态。若超过七天仍未完成,商户可以联系客服,但不应重复 提交退款申请。
不应该把整段只表示成一条“退款规则”,而应拆成三个可独立测试的行为义务:
- id: refund.submit.to_pending
actor: system
trigger: refund_submitted
preconditions: []
action: set_refund_status
modality: system_behavior
outcome:
refund_status: pending
- id: refund.pending.after_7d.contact_support
actor: merchant
trigger: elapsed_time > 7d
preconditions:
refund_status: pending
action: contact_support
modality: permitted
outcome: unspecified
- id: refund.pending.after_7d.no_resubmit
actor: merchant
trigger: elapsed_time > 7d
preconditions:
refund_status: pending
action: submit_refund_again
modality: prohibited
outcome: unspecified
这里刻意把后两条的 outcome 标成 unspecified。原文只说可以联系和不能重复提交,并没有 说明联系客服后状态会变成什么。如果抽取器擅自补成 escalated,就不是建模,而是在制造 文档中不存在的需求。
2.5.3 抽取流程¶
一个比较完整的 narrative-to-model pipeline 包含:
- 结构切分
保留章节、段落、列表和 source span,先找到可能包含规则的文本区域。
- 命题拆解
把并列句、转折、条件从句拆成原子陈述,避免多个行为义务混在同一个单位中。
- 语义角色抽取
识别 actor、action、object、time、location 等角色。
- 条件和作用域解析
判断 if、when、unless、only if、after 等条件究竟约束哪个 action;解析数值 阈值、时间窗口、前置状态和条件组合。
- 规范性识别
区分:
must / shall -> required
may / can -> permitted
must not / cannot -> prohibited
normally / usually -> descriptive or default
“系统通常会……”和“系统必须……”不能被建成同等强度的 requirement。
- 共指与实体规范化
解析“它”“该订单”“上述退款”等指代,并把 merchant、seller 等同义实体映射到领域 ontology 中的稳定 ID。
- 规则拆分和模型选择
把复合叙述分别表示成:
- Event-Condition-Action rules:适合单条触发规则;
- decision tables:适合多条件组合;
- state machines:适合状态转移;
- use-case flow graphs:适合基本流程、替代流程和异常流程;
-
invariants:适合任何状态下都必须成立的约束。
-
证据验证
使用 entailment/NLI、第二个 LLM judge 或人工样本检查每条结构化规则是否被 source span 支持,并把 entailed、inferred、unsupported 分开。
- 规范化与去重
在字段级别比较 actor、condition、action、modality 和 outcome,而不是只比较整条规则的 embedding。只有关键字段一致的规则才能合并。
这个流程可以由规则解析器、semantic role labeling、LLM constrained output 或它们的组合 完成。LLM 适合处理多样的自然语言表达,但输出必须受 schema、source grounding 和 validation 约束。
2.5.4 不同结构模型表达不同覆盖对象¶
同一份叙述可以构造不同模型,而每种模型对应不同的 coverage denominator:
| 结构模型 | 主要单位 | 可以定义的覆盖 |
|---|---|---|
| 原子 requirement rules | 单条规范性规则 | requirement coverage |
| Decision table | 条件组合与结果 | condition/combination coverage |
| State machine | 状态和转移 | state/transition/path coverage |
| Use-case flow graph | 基本、替代和异常流程 | flow/branch coverage |
| Invariant set | 始终应成立的约束 | invariant/oracle coverage |
因此不存在一次“结构化”就得到唯一正确模型。模型选择必须服从被测系统的语义:如果退款问题 主要由状态变化决定,state machine 更合适;如果规则由多项资格条件共同决定,decision table 更合适。
2.5.5 在结构模型上计算覆盖¶
设从文档 \(D\) 中抽取得到规则集合:
定义 \(\operatorname{Covers}(t,r_i)\) 表示 Golden \(t\) 是否完整触发并验证规则 \(r_i\)。基础 requirement coverage 可以写成:
这里的 Covers 不能只依赖整句 embedding 相似度。至少需要检查:
- Golden 是否具有相同 actor;
- 是否满足或刻意违反相同 preconditions;
- 是否触发相同 action;
- expected output 是否验证相同 modality/outcome;
- 对 prohibited rule,测试是否真的检查系统拒绝了该行为;
- 对 boundary rule,测试输入是否落在阈值点或阈值两侧。
除了计算覆盖比例,还应维护完整的 traceability:
这条链路使每个测试结果都能回溯到结构化规则和原始 source span。覆盖准则则可以逐步增强:
显式需求覆盖
-> alternative-flow coverage
-> condition coverage
-> boundary coverage
-> interaction/path coverage
-> oracle/fault detection adequacy
相关资料:
- 自然语言需求追踪综述
- 自然语言 use case 自动生成 acceptance tests 的 UMTG
- FSM model coverage 综述
- Property-pattern coverage
其中 UMTG 与这里的问题尤其接近:它从自然语言 use-case specification 中抽取 domain entities、语义角色、前置条件、基本和替代流程,再构造 scenario paths 与 test oracles。
2.5.6 这种建模方法的边界¶
对 Golden testset 来说,它比句子或 embedding 更接近真正的业务行为。但它需要更强的 结构化抽取,也要求原始文档本身具有足够清晰的规范性。许多产品文档主要面向用户说明, 并不完整记录内部状态、冲突处理、权限限制和失败行为;此时无法仅靠文本恢复完整业务模型。
因此,requirements/model-based 方法解决的是:
在已声明的业务规则范围内,测试覆盖了多少行为义务?
它不能自动解决:
文档本身是否完整代表真实业务?
三、这些方法测量的是不同层次¶
可以把目前的方法排列成几个递进层次:
Level 0:表面和主题覆盖
token、chunk、embedding、cluster
Level 1:信息内容覆盖
sentence、list block、AURA-QG Information Units
Level 2:命题覆盖
atomic propositions、entailment
Level 3:业务行为覆盖
precondition、action、outcome、exception、boundary
Level 4:使用与风险覆盖
生产流量、事故、对抗输入、未文档化行为
这些层次不是互相排斥的候选答案。低层指标通常更便宜、更稳定,适合快速扫描;高层指标更 接近真正目标,但提取和验证成本更高。一个实际系统可能需要由低到高逐层过滤:
embedding 发现空白区域
-> source units 建立可追踪分母
-> atomic propositions 细化复合句
-> business obligations 表达行为差异
-> production events 检验外部有效性
四、覆盖、丰荣度、代表性与测试有效性必须分开¶
测试集质量至少包含四个不能合并的维度。
4.1 Coverage¶
是否触及了已知文本单位、命题或业务规则。
4.2 Richness / depth¶
同一个业务规则下,是否覆盖了具有判别意义的不同条件、边界、状态、表达和系统路径。多个 paraphrase 不一定增加 richness;一个导致 expected behavior 变化的最小条件差异通常会 增加 richness。
4.3 Representativeness¶
测试集或文本基准是否符合真实使用分布。文档覆盖率很高,不代表覆盖生产环境中高频或高风险 场景;生产分布覆盖率很高,也可能忽略低频但严重的故障。
4.4 Fault detection / oracle adequacy¶
测试是否真正能够区分正确与错误实现。一个测试可以触及相关文本,却拥有过弱的 expected output 或 metric,从而无法发现实现错误。
Requirements Coverage-Guided Minimization 的工作也采用了类似分离:先保证需求覆盖,再在 测试预算下减少自然语言测试的冗余并提高 fault detection rate。
相关资料:Requirements Coverage-Guided Minimization for Natural Language Test Cases
因此,即使某种文本覆盖达到 100%,也不代表测试工作结束。它只表示当前选定的 coverage criterion 已经饱和,接下来应提高 coverage strength,或者改为考察 richness、生产代表性 和 fault detection。
五、如何验证“文本覆盖率测量工具”本身¶
无论使用 AURA-QG、atomic proposition、Synthesizer,还是业务规则抽取,都必须把测量器 本身纳入测试。可采用以下验证方法。
5.1 小规模人工金标¶
选择少量文档,由人标注 source units、atomic propositions 或 business obligations; 测量自动抽取的 precision、recall,以及 Golden-to-unit alignment 的 precision、recall。 不需要人工标注全部语料,但需要一个能校准阈值和发现系统性错误的基准。
5.2 Evidence traceability¶
每个自动单位必须保留 document ID、source span、抽取版本和证据。LLM 生成的命题或规则 必须能被对应证据 entail,不能只保留规范化后的文本。
5.3 Metamorphic / mutation testing¶
对文档做受控变异:
- 增加一条新规则;
- 删除一个异常分支;
- 将 7 天改成 14 天;
- 将允许改成禁止;
- 改变前置状态;
- 把一个条件移动到另一段;
- 添加跨文档依赖。
检查单位抽取、Golden alignment 和覆盖率是否按预期改变。这比只观察最终相关性更能证明 测量器确实理解了需要捕捉的语义差异。
5.4 多视角一致性¶
比较不同模型、prompt、seed、embedding 和分块方式下的结果。稳定区域可以赋予较高置信度; 只有单个方法发现的单位应进入人工审查或 uncertainty queue。
5.5 Discovery / saturation curve¶
反复运行生成或抽取流程,记录每轮新增的非等价单位数量。如果新增率逐渐下降,说明在当前 生成器和等价判定下接近饱和;但饱和只对当前 measurement process 成立,不证明真实世界 没有未发现场景。
5.6 外部事件回放¶
把生产问题、用户反馈和事故逐条映射到已有单位:
能映射,且测试存在
-> 实现、harness 或 oracle 可能有问题
能映射,但测试不存在
-> Golden coverage gap
文档可以推出,但自动单位不存在
-> extraction/measurement recall gap
文档本身不能推出
-> documentation/specification gap
这一步负责检验文档基准的 external validity。
六、对当前问题的阶段性结论¶
目前没有证据支持只选用一种方法,并将其输出称为完整的业务语义覆盖率。更合理的研究模型是 同时保留一个确定性较强的 source anchor 和一个随机的 probe mechanism:
Document
-> Source Units / Atomic Propositions
-> Synthesized Held-out Probes
Existing Golden Testset
-> 与 Source Units 对齐
-> 与 Held-out Probes 对齐
这样可以分别得到:
source-unit coverage
原文中有多少单位至少被测试触及
proposition coverage
原文中有多少独立命题被测试覆盖
generator-conditioned probe coverage
独立生成问题中有多少能被测试集覆盖
cross-generator coverage
测试是否只覆盖某一个生成器的表达分布
marginal discovery rate
继续生成时发现新语义单位的速度
business-rule coverage
条件、异常、边界和行为结果的覆盖
其中 Synthesizer 仍然有重要作用,但它更适合作为文本语义空间的抽样探针,而不是未经验证的 完整分母。AURA-QG 提供了 source-unit coverage 的可复现 baseline;atomic proposition 提供了更细的中间表示;requirements/model-based testing 则提供了从信息内容走向业务行为 覆盖的理论和工程框架。
七、推荐的研究顺序¶
7.1 复现最粗的信息覆盖¶
- 用句子、列表项和 source span 构造确定性 source units;
- 复现 AURA-QG 式问题到 source unit 的映射;
- 在少量业务文档上检查错误类型;
- 明确句子级覆盖能回答什么、不能回答什么。
7.2 研究 atomic proposition¶
- 比较不同命题拆解定义;
- 建立 source span、entailment 和去重约束;
- 在人工标注样本上测 extraction precision/recall;
- 用否定、阈值、条件变更做 mutation test。
7.3 引入 Synthesizer probes¶
- 按 proposition/source unit 分层生成;
- 分离 candidate tests 和 held-out measurement probes;
- 做跨模型、跨 prompt、跨 seed 的覆盖和饱和实验;
- 研究 probe 与原文单位之间的采样偏差。
7.4 从命题升级为业务义务¶
- 提取 actor、precondition、state、action、outcome、exception、boundary;
- 定义 requirement、branch、condition、boundary、interaction 等覆盖层级;
- 区分语义 paraphrase 与会改变 expected behavior 的真正场景变化。
7.5 接入生产反馈¶
- 比较文档单位、测试集和生产事件三个分布;
- 对无法映射的事件区分 test gap、extractor gap 和 documentation gap;
- 建立文档变更后的增量抽取、trace maintenance 和 coverage diff。
八、仍未解决的关键研究问题¶
- 什么粒度的 atomic proposition 最适合作为业务文档的稳定中间表示?
- 两个 Golden “覆盖相同命题”与“业务上重复”之间是什么关系?
- 如何识别 embedding 很近但 expected behavior 不同的 minimal pairs?
- 如何估计 Synthesizer 没有生成出来的语义概率质量?
- 如何为不同 source units 赋权:按文本长度、业务重要性、生产频率还是风险?
- 多跳问题覆盖多个命题时,如何避免重复计数或过度计数?
- 文档缺失、矛盾和过期时,覆盖率应该如何表达不确定性?
- 如何证明一个 coverage increase 同时提高了 fault detection,而不是只增加测试数量?
- 文档更新时,如何保持 proposition/obligation ID 稳定并只重算受影响部分?
- 如何为整个覆盖测量系统建立长期 regression testset?
这些问题表明,这项工作的核心不是寻找一个现成的单一公式,而是建立一个可以被校准、变异、 回放和持续修正的测量系统。