交付门禁施工图:和评测共用一副骨架,在 Oracle 处分叉,每一格配一个开源底座

评测与门禁共用「资产/目标/Runner」骨架,却在 oracle 处分叉:门禁靠确定性规格,评测靠采样式裁判。本文补齐门禁六元组,每格配开源底座(Playwright、Midscene、Testcontainers、Pact、Argo Rollouts、promptfoo),附伪代码与 Wilson 下界手算。

一个做评测平台的团队被要求「顺手把交付门禁也做了」,理由很朴素:两边都是数据集、被测对象、跑一遍出结果,为什么不能一套系统?这个直觉有一半是对的——两者确实共用一副骨架,平台层可以复用。但另一半错得很隐蔽:真正决定一个系统是评测还是门禁的部件,在这个三元组里根本没被列出来。它叫 oracle。这篇先把这个被藏起来的部件找出来,再照着完整的元组,把门禁系统每一格能用的开源底座配齐——目标是读完能开工。

上一篇《交付门禁 vs 评测系统》讲的是两者在质量回路上的位置:评测是传感器,门禁是执行器。本篇讲的是两者的内核为什么不同,以及门禁这一侧怎么搭。两篇合起来是一份「为什么分开」加「分开之后门禁怎么造」。

名词速查

术语一句话解释
harness把「喂输入、跑被测对象、收结果」这套流程自动化起来的框架,pytest、Inspect AI、Playwright Test 都是
oracle(测试预言)判断一次输出「对不对」的依据。可以是一个断言、一份参考答案、一个裁判模型、一个人
held-out被测对象在构建时没见过的数据;评测数据一旦被训练集或提示词吸收,叫「污染」
flaky同一测试在同一代码上时好时坏;在门禁语义下是 bug,在评测语义下是被测对象的固有性质
世界状态被测系统运行时依赖的一切:数据库、配置、依赖服务、模型版本、时间。门禁测的是「变更」对这些的影响
PROMOTE / HOLD / ROLLBACK门禁的三种离散判决:放行、拦下等人、回滚。来自 Automated Self-Testing (2603.15676)
契约测试把服务之间「我调你时你要这么回」写成可执行的例子,双方各自验证;Pact 是代表实现
Wilson 下界通过率的置信区间下限,样本少时比点估计诚实得多;详见评测统计篇

一、共用的骨架:三元组为什么两边都成立

任何 harness 拆开都是三样东西:

  • 资产:喂给被测对象的输入集合。评测叫数据集,门禁叫测试用例。
  • 目标:被测对象。评测是一个模型或一条 Agent 链路,门禁是一个业务系统在某次变更之后的状态。
  • Runner:把资产逐条喂给目标、收集输出、汇总报告的执行器。评测是 Inspect AI、promptfoo,门禁是 Playwright、pytest、JUnit。

这不是巧合,也不只是形似。Inspect AI 的文档把一个评测任务定义为 Dataset + Solver + Scorer 三件,pytest 的一次运行是 fixture + 被测函数 + assert——它们是同一个抽象在两个领域的实例化。所以「平台层复用」这个直觉成立:调度、并发、重试策略、报告存储、trace 归档,两边确实能共享一套基础设施。上一篇里 Test Before You Deploy 的「兼容性契约」(拿评测集做回归、不掉分才放行),就是评测在这副骨架上直接长成门禁的活例。

但请注意上面那句话里 Inspect 的第三件叫 Scorer,而 pytest 那边我写的是 assert。三元组里没有它的位置。它被藏起来了。

二、被藏起来的第四个部件:oracle

软件测试领域对这个部件有专门的名字和一篇经典综述:Barr、Harman、McMinn、Shahbaz、Yoo 在 IEEE TSE 2015 发表的《The Oracle Problem in Software Testing: A Survey》把它定义为:给定一个输入,把「期望的正确行为」与「可能错误的行为」区分开的那个难题。综述列出的 oracle 来源依次是规格、模型、契约、蜕变关系,「当这些都不够用时,最终的 oracle 信息来源仍然是人」。

传统测试之所以让人觉得 oracle 不存在,是因为它被写进了 assert x == 42 这一行里——oracle 和 Runner 长在同一处,看起来是一体的。LLM 评测把这个部件重新拆了出来,Inspect 把 Scorer 做成一等公民、DeepEval 把 G-Eval 裁判做成可插拔的指标,都是同一个动作:oracle 从「解决了的问题」变回「未解决的问题」

一旦把 oracle 单独摆出来,评测和门禁就在这一格上分开了,而且几乎所有下游差异都从这一格长出来:

门禁的 oracle评测的 oracle
形态规格(spec):期望输出精确已知,assert status == 200参考答案、评分 rubric、裁判模型:本身模糊、本身带误差
被测对象能不能「见过」它应当见过——测试驱动开发就是照着测试写代码;契约测试的消费者先写期望、提供方照着实现绝不能见过——见过叫污染,分数立即失效
资产的组织目标穷举式覆盖变更面:每个分支、每条契约都跑到从不可穷举的分布里采样,要的是代表性而非枚举
重跑的语义flaky 是 bug,retry 是坏味道;Playwright 默认 retries: 0,通过重试才过的测试单独标成 flaky多次重跑是必需——目标本身是随机函数;Inspect 用 --epochs 加 reducermean / pass_at_k / …)把多轮压成一个分
失败意味着什么变更引入了回归,或规格错了目标能力不足,或 oracle 本身偏了

所以「门禁需要测试用例覆盖」这句话没问题,但「覆盖」这个词搬到评测那边意思就变了:测试覆盖率是「diff 的每一支都被执行过」,评测覆盖是 HELM 意义上的「能力格子每一格都有采样」(覆盖地图篇展开过)。同一个词,一个是枚举,一个是抽样。

有一个来自实践的证据把这条分叉钉得很死。Automated Self-Testing (2603.15676) 在 60 个分层抽样的案例上,让 LLM 裁判和它的结构化门禁各判一遍,两者的一致性只有 kappa = 0.13。作者的解释是:门禁抓住的是延迟越界、路由错误这类在回复文本里看不见的结构性失败;裁判抓住的是门禁的结构检查测不到的内容质量问题。这两个 oracle 不是谁更准,而是根本在看不同的东西——这正是「确定性规格」和「采样式裁判」两种 oracle 的分工。把它们混成一个分数,等于把温度计和秤的读数加起来。

三、由此推出门禁的完整元组:不是三个,是六个

把 oracle 补进来之后,门禁还多出两个评测没有的部件。评测的输出是估计量,到分数就结束了;门禁的输出是一个不可逆动作的判决,所以它必须再加一个把分数变成判决的决策器,和一个记录「为什么这么判」的台账——判决可以被质疑,台账是被质疑时的证据。

flowchart LR
    A["资产<br>用例 · 契约 · golden 集"] --> R["Runner<br>执行、收集、重试策略"]
    T["目标<br>变更后的世界状态<br>(代码+配置+数据+模型版本)"] --> R
    R --> O["Oracle<br>确定性规格为主<br>采样式裁判为辅"]
    O --> D["决策器<br>阈值 · 组合规则<br>PROMOTE / HOLD / ROLLBACK"]
    D --> L["台账<br>证据 · trace · 判决记录"]
    D -->|PROMOTE| P["合入 / 发布 / 放权"]
    D -->|HOLD| H["等人复核"]
    D -->|ROLLBACK| B["回滚到上一稳定版"]

六个元组各自要回答的问题:

  1. 资产:哪些输入必须每次都跑?它们由谁维护、怎么随业务漂移更新?
  2. 目标(世界状态):变更前后到底改了什么?依赖的数据库、外部服务、模型版本怎么固定住,让两次运行的差异只来自变更本身?
  3. Oracle:对错依据是什么?哪些是确定性规格、哪些是裁判判断?两者的结论怎么分开报告?
  4. Runner:谁来执行?执行本身是否确定性?AI 参与执行时,它的不确定性怎么隔离?
  5. 决策器:分数怎么变成 PROMOTE / HOLD / ROLLBACK?阈值精度是否匹配 oracle 的方差?判定器自己失败时默认放行还是拦截?
  6. 台账:每次判决的证据存在哪里?能不能回放?能不能看趋势?

下面逐格配开源底座。选型原则只有一条,来自第二节:AI 可以出现在门禁的执行位置和辅助断言位置,但门禁的主 oracle 必须是确定性规格。违反这条,你造出来的是一个评测系统,不是门禁。

四、逐格配开源底座

先给总表,再讲每一格里的取舍。许可信息核实自各项目 2026-09-03 当日的仓库页或文档;未亲手部署的项目在文末「诚实的提醒」里单列。

元组首选底座许可它在门禁里的角色备选
资产Git 仓库内的用例 + Langfuse DatasetsMIT(ee/ 目录除外)用例与代码同库同审;LLM 相关 golden 集用 Langfuse 版本化、从生产 trace 反哺Opik、Phoenix(产品化篇有对比)
目标 / 世界状态Testcontainers + PactMIT / MIT依赖服务每次现起现销;跨服务边界用契约代替「部署全世界」Docker Compose 手写、WireMock
Oracle(确定性)Playwright expect / pytest assert / Conftest + OPAApache-2.0功能断言、契约验证、配置策略;失败即 exit 非零Rego 之外:CUE、JSON Schema
Oracle(采样式,辅助)promptfoo / DeepEval / Midscene aiAssertMIT / Apache-2.0 / MIT被测系统含 LLM 时的内容质量检查;只能出 HOLD,不能单独出 PROMOTEInspect AI(用于回归型评测时)
Runner(Web)PlaywrightApache-2.0确定性执行主干,进程级隔离、trace 录制Cypress、Puppeteer
Runner 之上的 AI 定位层Midscene.js / StagehandMIT / MIT选择器脆弱或不存在(canvas、跨域 iframe、原生 App)时的元素定位;不替代 Runner直接用 Playwright getByRole
决策器(合入闸)CI 的 exit code 组合 + Conftest多个 oracle 的结果按声明的规则合成一个判决自写 20 行脚本
决策器(发布闸)Argo Rollouts AnalysisApache-2.0灰度期间按指标自动 abort/回滚;Inconclusive 自动 pause 等人Flagger
台账Playwright Trace + Allure Report + OpenTelemetry(GenAI 语义约定Apache-2.0每次判决的回放证据、历史趋势、flaky 追踪;LLM 调用进统一 traceLangfuse 作为 LLM trace 后端

4.1 资产:用例进 Git,golden 集进可版本化的数据集

门禁用例的第一原则是和被改的代码同一个仓库、同一个 PR 审——这是「被测对象应当见过 oracle」的直接推论:改了行为就必须同时改契约,reviewer 在同一个 diff 里看到两者。

被测系统含 LLM 时,多出一类资产:golden 集(输入 + 期望输出)。它不适合塞进 Git,因为它需要从生产 trace 持续反哺。Langfuse 的数据集把每个 item 定义为 input + 可选 expected_output + metadata,并且「每一次 add / update / delete / archive 都产生一个新的数据集版本」,item 可以通过 source_trace_id 挂回它来自的那条生产 trace。这两个特性刚好对应上一篇讲的「golden set 是活资产」:门禁跑的是钉住版本的数据集,运营侧另开一条线往里加新翻车案例。

4.2 目标 / 世界状态:让两次运行的差异只来自变更

门禁测的是「变更对世界状态的影响」,前提是除了变更之外的一切都被固定住。两个工具各管一半:

  • Testcontainers 管进程内依赖:数据库、消息队列在测试开始前用 Docker 现起,测完由 Ryuk 边车按标签清掉——文档强调「即使测试进程被 SIGKILL 异常退出也能可靠清理」,并发流水线之间「不会有测试数据污染」。这解决的是「上一次运行留下的脏状态让这次结果不可解释」。
  • Pact 管服务边界:消费者在自己的测试里写「我会这样调你、你应当这样回」,生成契约;提供方拉取契约回放验证。它的口号是让你不用「先部署全世界」就能确认两边合得上。契约就是跨服务的确定性 oracle,而 Pact Broker 的 can-i-deploy 直接把它变成门禁:命令按 exit code 回答「这个版本能不能进这个环境」——0 是能,1 是不能——部署后再用 record-deployment 告诉 Broker 环境里现在跑的是哪个版本。

被测系统里有 LLM 时,世界状态多了一个维度:模型版本。供应商会在版本号不变的情况下更新权重(上一篇里 Test Before You Deploy 论文的出发点)。门禁能做的是把模型 ID 和快照日期写进配置并纳入 diff——它变了就是一次「变更」,触发门禁,而不是悄悄漂过去。

4.3 Oracle:确定性为主,采样式为辅,两者分开报告

确定性 oracle 三种来源:功能断言(Playwright 的 expect、pytest 的 assert)、契约(Pact)、策略(Conftest 用 OPA 的 Rego 语言对 YAML / JSON / HCL / Dockerfile 等结构化配置写 deny / warn 规则,「默认只在策略失败时返回 exit code 1」,加 --fail-on-warn 后变成 0 / 1 / 2 三级)。共同点是期望值精确、结果二元、可重放

采样式 oracle 是被测系统含 LLM 时不得不引入的。promptfoo 和 DeepEval 都把「LLM 裁判打分 + 阈值」包装成 pytest 风格的断言:DeepEval 的 assert_test(test_case, [metric]) 里「所有指标分数在 0–1 之间,threshold=0.5 最终决定测试是否通过」;promptfoo 有 --fail-on-error,也可以从 results.json 里读 stats.successes / stats.failures 算通过率再比阈值。

这里是本文最重要的一条工程规矩,是我的提炼而不是任何一份文档的原话:

采样式 oracle 在门禁里只有 HOLD 权,没有 PROMOTE 权。 它的失败应当拦下等人复核;它的通过不能单独放行,必须叠加确定性 oracle 全绿。

理由就是第二节那个 kappa = 0.13:裁判看不见结构性失败,结构检查看不见内容问题。让裁判独立签发 PROMOTE,等于让门禁只剩它看得见的那一半。反过来也成立:确定性 oracle 全绿而裁判亮红,说明出现了 assert 写不出来的问题,值得一个人看一眼。

4.4 Runner:Playwright 是主干,Midscene 是定位层,两者不是替代关系

这一格最容易选错,因为 Midscene 的宣传语是「the GUI Agent for E2E Testing」,听起来像是 Playwright 的下一代。看它的实际接法就清楚了:文档写的是「把 Midscene 加进你的 Playwright 或 Puppeteer 测试」——它挂在 Runner 之上,不是 Runner 本身。Stagehand 的自我定位更直白:「Playwright 是为测试造的,Stagehand 是为 agent 造的」,并且它的示例代码把 AI 的 observe 找到的选择器交给普通的 page.locator(...).click() 去执行,注释写着「用 locator 做确定性的 Playwright 风格动作」。

所以 Runner 这一格的分工是:

Playwright 做执行主干,因为门禁的 Runner 需要三样它原生就有的东西:进程级隔离(每个测试在独立 worker 里跑,失败的 worker 连浏览器一起丢掉,「保证失败的测试不会影响健康的测试」)、明确的 flaky 语义(默认不重试,重试后才过的单独标记)、以及可回放的 trace(trace: 'on-first-retry' 在第一次重试时录下每个动作的 DOM 快照、网络请求、控制台日志)。

Midscene 做两件 Playwright 做不好的事,都在执行位置、不在判决位置:

  1. 定位没有稳定选择器的元素:canvas 内容、跨域 iframe、原生 App——这些地方 DOM 拿不到,getByRole 无从下手。Midscene 1.0 砍掉 DOM 兜底走纯视觉,正是为了这些场景。用它的 Instant Actions(aiTap 等)而不是全流程 aiAction,让 AI 只负责「这颗按钮在哪」,不负责「接下来该做什么」。
  2. 在选择器脆弱的页面上降低维护成本:改版后一批 #login-btn 失效,视觉定位不受影响。

但要认清 Midscene 带进门禁的不确定性,以及它的缓存机制能压掉多少。Midscene 缓存两种东西——规划步骤和元素定位的 XPath(后者仅 Web)——命中时不调模型;文档明确说「aiBooleanaiQueryaiAssert 这些查询类结果永远不会被缓存」,并且警告「缓存是加速手段,不是保证脚本长期稳定的工具」,缓存失效时「系统自动回退到 AI 模型重新分析」。翻译成门禁语言:定位步骤在缓存命中时是确定性的,未命中时是随机的;断言步骤永远是随机的

由此我的建议(观点):

  • 门禁里 Midscene 的定位调用全部开缓存(cache: { id }),缓存文件进 Git,改版导致的缓存失效在 PR 里可见;打开 DEBUG=midscene:cache:* 把未命中记进台账,未命中率突然上升本身就是一个信号。
  • aiAssert 归到 4.3 的「采样式 oracle」那一栏,只有 HOLD 权。凡是能用 expect(page.getByText(...)) 写出来的断言,不用 aiAssert
  • Stagehand 的 observe → locator 模式和上面同构:AI 找选择器,Playwright 执行;如果团队已经在 Playwright 上,两者选一个即可,差别主要在模型支持(Midscene 对国产 VLM 的适配更全)和平台覆盖(Midscene 有 Android / iOS / 桌面)。

4.5 决策器:合入闸靠 exit code 合成,发布闸靠 Argo Rollouts

合入闸的决策器可以很薄。每个 oracle 已经用 exit code 说了话——Playwright 失败非零、Conftest 失败 1、Pact can-i-deploy 不能部署 1、promptfoo --fail-on-error 非零——决策器只需要按声明的规则把它们合成一个判决。伪代码(根据 Automated Self-Testing 的 PROMOTE / HOLD / ROLLBACK 协议简化,具体阈值是示意):

# 伪代码:合入闸决策器主干。省略:超时处理、override 权限校验
def decide(results, contract):
    hard = [r for r in results if r.oracle_kind == "deterministic"]
    soft = [r for r in results if r.oracle_kind == "sampled"]

    if any(r.exit_code != 0 for r in hard):
        return ROLLBACK if contract.on_hard_fail == "rollback" else HOLD

    for r in soft:                       # 采样式 oracle 只有 HOLD 权
        lower = wilson_lower(r.passed, r.total)
        if lower < contract.threshold[r.name]:
            return HOLD                  # 拦下等人,不自动回滚

    if any(r.status == "inconclusive" for r in results):
        return HOLD                      # 判定器自身失败:合入闸 fail-closed 到 HOLD

    return PROMOTE

注意第三个分支:判定器自己超时或崩了怎么办。Gate 决策地图篇的结论是看下游是否可逆——合入闸挡住的是开发进度,fail 到 HOLD(而不是默认放行)代价可承受;行动闸挡住的是转账,必须 fail-closed。

发布闸的决策器不该自写,Argo Rollouts 的 Analysis 已经把它做成了 CRD:AnalysisTemplate 里声明指标来源(Prometheus、Web、Job 等)和 successCondition: result[0] >= 0.95 这类条件;failureLimit 默认 0,即一次失败就算失败;背景分析失败时「Rollout 中止,金丝雀权重归零」并标记 Degraded;结果 Inconclusive 时「Rollout 在当前步骤暂停」等人处理。这三种结局恰好就是 PROMOTE / ROLLBACK / HOLD。它的 Job provider 可以跑任意容器,所以 4.3 里的 promptfoo 或 Inspect 回归也能挂进去当灰度期指标。

4.6 台账:三层证据,一条 trace

判决可以被推翻,台账不能没有。三层:

  • 单次运行的回放:Playwright trace(npx playwright show-trace trace.zip,文档说明「完全在浏览器里加载,不向外传输数据」)。Midscene 自带的可视化报告也属于这一层。
  • 跨运行的趋势:Allure Report 是「框架无关的开源测试结果可视化工具」,功能列表里有「History and retries」「Test stability analysis」和「Quality Gate」——flaky 率随时间的变化就在这里看。
  • LLM 调用的 trace:被测系统里每一次模型调用(包括门禁自己发起的裁判调用)应当以统一格式进 trace 系统。OpenTelemetry 已经把 GenAI 的 span / metric / event 语义约定拆成独立仓库 semantic-conventions-genai(Apache-2.0,覆盖 MCP 和主流供应商);仓库页面未标注稳定性等级、Schema URL 仍是 TODO,属于仍在演进的规范,选型时以当时的文档为准。落地后端可以是 Langfuse,它已经是 4.1 里资产层的底座,一套系统两用。

五、当被测系统含 LLM 时:门禁如何借用评测的 oracle 而不被它的方差拖垮

4.3 说采样式 oracle 只有 HOLD 权,但即便只是 HOLD,阈值怎么定也是问题。评测那边的通过率是采样估计,直接拿点估计比阈值会抖。上一篇的病理 C 讲了原理,这里给门禁里具体的数:

用例数 n通过数通过率点估计Wilson 95% 下界
20190.9500.764
20201.0000.839
100950.9500.888
1001001.0000.963
2001900.9500.910

(用标准 Wilson 区间公式、z = 1.96 手算,脚本验算过。)

读法:如果门禁契约写「LLM 内容质量通过率 ≥ 0.90」,而 golden 集只有 20 条,那么即使 20 条全过,下界也只有 0.839,永远过不了这道门——这不是被测系统的问题,是 oracle 的分辩率不够。要让「19/20 过」有资格触发 PROMOTE 的那一半,golden 集至少要到 200 条量级(190/200 的下界才刚过 0.90)。所以门禁的决策器读的必须是下界而不是点估计(上面伪代码里的 wilson_lower),而阈值和 golden 集规模是要一起定的,不能各定各的。

反过来,这张表也给了一条排期依据:一个新业务线的 LLM 门禁,在 golden 集攒到几十条之前,采样式 oracle 只能进台账、不能进判决——先跑起来积累分布,不要急着上阈值。

六、施工顺序:三阶段,每阶段有可验收的产出

阶段一(一到两周):只有确定性 oracle 的门禁。 Playwright 覆盖关键路径,Testcontainers 固定依赖,契约用 Pact 或至少用 JSON Schema 锁住接口。决策器就是 CI 的 exit code。验收标准:任何一条关键路径断了,PR 合不进去。这一阶段不碰 AI——先让门禁有牙。

阶段二(两到四周):加 AI 定位层和台账。 选择器最脆弱的那几个页面换成 Midscene Instant Actions 加缓存,缓存文件进 Git;trace 开 on-first-retry;Allure 接上看趋势。验收标准:一次改版后,需要手改的用例数比阶段一下降,且缓存未命中在 PR 里可见。

阶段三(持续):加采样式 oracle 和发布闸。 golden 集在 Langfuse 里从生产 trace 反哺积累;到规模后 promptfoo / DeepEval 接进合入闸,只给 HOLD 权,阈值按第五节的下界表定;灰度发布交给 Argo Rollouts Analysis,把同一套回归挂成 Job 指标。验收标准:一次模型版本切换(供应商静默更新也算)能被门禁拦下,且拦下的原因在台账里能回放。

三个阶段的顺序不能倒。先上采样式 oracle 再补确定性 oracle,会造出上一篇的病理 B——门禁里全是裁判打的分,结构性失败一个也拦不住。

七、带得走的一张表

问题评测系统的答案交付门禁的答案
三元组(资产 / 目标 / Runner)能不能共用能,平台层复用能,平台层复用
oracle 是什么采样式:参考答案、rubric、裁判确定性:断言、契约、策略;采样式仅辅助
被测对象能不能见过 oracle不能(污染)应当(照着它构建)
资产的目标分布上的代表性变更面上的覆盖
重跑必需,用 reducer 聚合flaky 即 bug,重试单独标记
输出估计量(带误差条)判决(PROMOTE / HOLD / ROLLBACK)
多出来的部件决策器、台账
AI 在里面的位置目标本身,以及裁判执行层的定位器,以及只有 HOLD 权的辅助断言

诚实的提醒

本文所有工具的功能描述和许可信息核实自 2026-09-03 当日的官方文档或仓库页,正文带链接。亲手跑过的:Playwright(多年)、Inspect AI(教程篇)、Midscene 的基本流程(拆解篇)。未亲手部署、仅核实文档的:Pact Broker 的 can-i-deploy、Argo Rollouts Analysis、Conftest、Testcontainers 的 Ryuk 行为、DeepEval、Allure 3。Automated Self-Testing 论文的 kappa = 0.13 是论文自报数字,未复现。第五节的 Wilson 下界是我用公式手算并脚本验算的,其余数字均为核实来源。

「采样式 oracle 只有 HOLD 权」是我的工程提炼,不是任何一份文档的规定;它的失效边界我能想到一个:如果你的确定性 oracle 覆盖面本身很薄(阶段一没做实),那么裁判反而是唯一在看的人,这时候剥夺它的 PROMOTE 权等于门禁形同虚设——解法是补阶段一,不是给裁判放权。

最低成本的亲手验证实验(一小时内):找出你现有 CI 里所有会让构建变红的检查,逐条问两个问题——(1)它的 oracle 是确定性规格还是某种打分?(2)它失败时是拦下等人,还是直接阻止合入?把答案填成一张两列表。如果表里有任何一行是「打分 + 直接阻止合入」,你的门禁里有一个采样式 oracle 在越权签发判决,第五节的下界表能告诉你它抖的幅度有多大。

参考来源

论文

工程实践与文档

本站相关