4. 核查规则的形式化表达

临床数据核查的起点往往是一句自然语言,例如“受试者必须有知情同意日期”“访视日期不能早于知情同意日期”“若出现严重不良事件,必须记录其转归”。这些句子对临床人员清晰,但对程序不可执行。形式化表达的任务,就是把临床逻辑翻译成机器可判定、可复用、可追溯的规则对象。它包含三层工作:把语义拆成可判定的条件与结论,把规则归入合适的类别,并为每条规则建立元数据,使其能被规则引擎加载、被版本管理、被审计追踪。

从临床逻辑到可执行规则

一条可执行规则通常由四部分组成:作用域(对哪些受试者、哪些访视、哪些表单生效)、触发条件(在什么数据状态下评估)、判定表达式(布尔表达式或集合运算)、结果动作(产生何种质疑、严重级别、提示文本)。以“访视日期不能早于知情同意日期”为例,形式化后为:

rule_id: R-TIME-001
scope: subject.all_visits
trigger: visit_date IS NOT NULL AND icf_date IS NOT NULL
expression: visit_date >= icf_date
violation: "访视日期({visit_date})早于知情同意日期({icf_date})"
severity: HIGH
action: raise_query(field=visit_date, type=DATE_ORDER)

这里的关键不是语法,而是把“不能早于”精确映射为“大于等于”。临床语言中的“不能”“必须”“应当”需要统一映射为可判定的比较、存在量词或逻辑组合。例如“必须有知情同意日期”映射为 icf_date IS NOT NULL;“若发生 SAE 则必须有转归”映射为 IF sae_occurred = 'Y' THEN sae_outcome IS NOT NULL。映射过程中最容易出错的是边界与空值:日期相等是否允许、缺失值是否参与比较、多选字段如何判定“包含”。这些必须在规则定义阶段明确,而不是留给引擎默认行为。

建议在规则库中统一约定:空值不参与数值比较,但参与完整性判定;日期比较统一使用 ISO 8601 格式;多选字段用集合包含判定,避免字符串模糊匹配。

规则分类:四类核查逻辑

按核查目的,规则可分为完整性、一致性、逻辑性和时序性四类。它们关注的错误类型不同,触发时机和严重级别也往往不同。

类别核查目标典型表达式示例
完整性必填项是否缺失field IS NOT NULL知情同意日期为空
一致性同一信息在不同位置是否一致A = B 或 A ∈ set(B)AE 表单中的受试者编号与筛选表一致
逻辑性字段间语义是否矛盾IF P THEN Q性别为男但记录了妊娠
时序性事件先后顺序是否合理date1 <= date2访视日期早于知情同意日期

四类规则并非互斥。一条规则可能同时具备逻辑性和时序性,例如“若受试者已退出,则退出日期必须晚于末次访视日期”。分类的价值在于组织规则库、分配严重级别和确定执行顺序。通常完整性规则最先执行,因为缺失值会干扰后续比较;时序性规则依赖日期字段,应放在日期完整性检查之后。

规则元数据建模

规则元数据是规则可管理、可追溯的基础。没有元数据的规则只是一段代码,无法回答“这条规则为什么存在”“谁在什么时候改过”“它对应哪个方案版本”。一个实用的规则元数据模型至少应包含以下字段:

  • rule_id:全局唯一标识,建议采用“类别缩写-模块-序号”,如 R-TIME-001。
  • rule_name:人类可读名称,用于报告和沟通。
  • category:完整性、一致性、逻辑性、时序性之一。
  • scope:作用域,指定表单、访视、受试者子集。
  • trigger:触发条件,决定规则何时被评估。
  • expression:判定表达式,规则的核心逻辑。
  • severity:严重级别,如 HIGH、MEDIUM、LOW。
  • message_template:质疑文本模板,支持字段占位符。
  • source:规则来源,如方案章节、CRF 注释、数据管理计划。
  • version:版本号,配合生效日期实现版本管理。
  • status:草稿、启用、停用、废弃。

元数据建模的难点在于版本与生效范围。同一条规则在方案 V1.0 和 V2.0 中可能不同,因此规则必须绑定方案版本和生效日期。规则引擎在评估时,应根据当前受试者所属的方案版本加载对应规则集,而不是加载最新规则。这样才能保证历史数据的核查结果可复现。

规则元数据不是文档字段的堆砌,而是规则生命周期管理的契约:它决定了规则能否被检索、复用、审计和回滚。

规则表达的整体结构

下面用一张结构图展示从临床逻辑到可执行规则、再到规则分类与元数据挂载的整体关系。图中左侧是临床语义输入,中间是形式化转换,右侧是规则对象及其元数据字段,底部是四类规则的执行顺序。

临床逻辑 → 可执行规则 → 元数据挂载 临床语义输入 • 必须有知情同意日期 • 访视日期不能早于 知情同意日期 • 若发生 SAE 则必须 记录转归 • 性别为男不得妊娠 形式化转换 作用域 scope 触发条件 trigger 判定表达式 expression 结果动作 action 空值 / 边界 / 集合约定 规则对象与元数据 rule_id / rule_name / category scope / trigger / expression severity / message_template source / version / status 绑定方案版本与生效日期 支持检索、复用、审计、回滚 四类规则与执行顺序 ① 完整性 field IS NOT NULL 必填项缺失 最先执行 ② 一致性 A = B 或 A ∈ set(B) 跨表单信息一致 依赖字段已填 ③ 逻辑性 IF P THEN Q 字段语义矛盾 条件依赖判定 ④ 时序性 date1 <= date2 事件先后顺序 日期完整性之后 执行顺序:完整性 → 一致性 → 逻辑性 → 时序性;缺失值先补齐,日期比较后置

在实际工程中,规则元数据通常以结构化文件(如 JSON 或 YAML)存储,由规则引擎加载。下面是一条时序性规则的元数据示例,展示了字段如何组织:

{
  "rule_id": "R-TIME-001",
  "rule_name": "访视日期不早于知情同意日期",
  "category": "时序性",
  "scope": { "form": "VISIT", "field": "visit_date" },
  "trigger": "visit_date != null && icf_date != null",
  "expression": "visit_date >= icf_date",
  "severity": "HIGH",
  "message_template": "访视日期({visit_date})早于知情同意日期({icf_date})",
  "source": "方案 V2.0 第 6.3 节",
  "version": "1.2",
  "status": "启用"
}

当规则以这种形式存在时,数据管理员可以在不修改代码的情况下新增、停用或调整规则;稽查人员可以按 rule_id 追溯每条质疑的来源;统计人员可以确认核查逻辑与方案版本一致。形式化表达、分类和元数据建模三者结合,才使临床数据核查从人工经验走向可执行、可审计的自动化工程。