用于文档验证的 CEL 方言

Document AI 验证和更正功能利用通用表达式语言 (CEL),可在文档处理工作流中灵活地验证和处理数据。Document AI 提供了一组针对文档实体数据量身定制的自定义函数、宏和行为修改。

在 CEL 表达式中访问实体

所有表达式都针对名为 doc 的根变量进行评估,该变量由属于文档的短语或属性组成的实体构成。这些实体与提取的文档的实体结构密切相关。

虽然提取的实体包含许多属性,但只有三个属性可用于 CEL 评估。

  • mention_text:提取的实体中显示的原始提取文本。默认值为空字符串。
  • normalized_value:提取的实体中显示的标准化提及文本。默认值为 null。如需详细了解,请参阅标准化
  • bounding_poly:一种特殊对象,包含提取的实体在文档中的位置表示形式,用于进行对齐检查。默认值为 null。

数据模型

doc 映射中提取实体的确切结构取决于两个因素。第一种是其结构是具体值(例如数字或纯文本),还是复杂对象。第二个因素是其出现类型是单次还是多次。如需了解详情,请参阅 OccurrenceType

验证数据模型的一项关键功能是,架构中定义的任何实体(但未从文档中提取)都会自动填充默认值。这种设计可让您在 CEL 表达式中跳过大多数显式 null 检查,从而显著简化验证表达式。您只需明确编写 null 检查,以确保所选实体确实已提取。

叶实体示例

以下部分介绍了如何访问叶实体(即没有嵌套子实体的实体)中的实体。叶实体直接保存值。

仅出现一次的叶实体

这是最基本的情况,使用 OPTIONAL_ONCEREQUIRED_ONCEOccurrenceType。实体表示为一个包含三个标准属性的对象

访问这些值的示例为 doc.invoice_date.normalized_value

其结构如下:

  "invoice_date": {
    "mention_text": "1",
    "normalized_value": 1.0,
    "bounding_poly": bounding_poly_object
  }

默认值:

  "invoice_date": {
    "mention_text": "",
    "normalized_value": null,
    "bounding_poly": null
  }

多次出现的叶实体

这种情况适用于可多次出现且 OccurrenceTypeOPTIONAL_MULTIPLEREQUIRED_MULTIPLE 的叶实体。例如,在付款截止日期列表中,它表示为一个对象,其中每个属性都包含所有出现次数的相应值的列表。因此,mention_textnormalized_valuebounding_poly 等属性可能具有多个实体。

访问这些值的示例为 doc.payment_due_dates.normalized_value[0]

其结构如下:

  "payment_due_dates": {
    "mention_text": ["Mar 1, 2024", "Apr 1, 2024"],
    "normalized_value": [null, proto.timestamp(2024-04-01)],
    // Note: If a value is not normalized, it is stored as a null.
    "bounding_poly": [bounding_poly_object,bounding_poly_object]
  }

默认值:

  "payment_due_dates": {
    "mention_text": [],
    "normalized_value": []
    "bounding_poly": []
  }

嵌套实体

嵌套实体是其他实体(即其“子实体”)的容器。

出现一次的嵌套实体

如果嵌套实体仅出现一次(例如单个 receiver_address),则表示为对象,其中键是其子实体的名称。

访问这些值的示例为 doc.receiver_address.city.mention_text

其结构如下:

  "receiver_address": {
    "street": {
      "mention_text": "123 Main St",
      "normalized_value": "123 Main St",
      "bounding_poly": bounding_poly_object
    }
    }

默认值:

  "receiver_address": {
    "street": {
      "mention_text": "",
      "normalized_value": null,
      "bounding_poly": null
    }
    }

多次出现的嵌套实体

如果嵌套实体可以多次出现,则表示为对象列表。列表中的每个对象都表示嵌套实体的完整实例,并包含其子级。

访问这些值的示例为 doc.line_items[1].description.normalized_value

其结构如下:

  "line_items": [
    {
      "description": { "mention_text": "Product A", ... },
      "quantity": { "mention_text": "2", ... }
    },
    {
      "description": { "mention_text": "Service B", ... },
      "quantity": { "mention_text": "5", ... }
    }
  ]

默认值:

  "line_items": []

归一化值转换表

下表显示了所选架构实体数据类型如何转换为 CEL normalized_value 数据类型。

架构数据类型 CEL 数据类型
币种、地址 string
数字、金额 double
日期时间 proto.Timestamp
复选框,签名 bool
纯文本 不适用

表达式示例

以下是一些 CEL 表达式示例。

// Leaf entity with a single occurrence: Get the invoice ID string
doc.invoice_id.normalized_value == "INV-12345"

// Leaf entity with multiple occurrences: Get the first payment term from the list
doc.payments.mention_text[0].matches('^\d+$')

// Nested entity with one occurrence: Access a child entity of a single nested entity
doc.receiver_address.name.normalized_value.star
tsWith("John")
// Nested entity with multiple occurrences: Access a child of a specific item in a list of nested entities
doc.line_items[1].description.normalized_value == "Premium Gadget"

// Advanced: Sum the total of all line items
doc.line_items.map(item, item.total.normalized_value).sum() == 275.0

// Advanced: Check if any line item has a quantity greater than 1
doc.line_items.exists(item, item.quantity.normalized_value > 1.0)

以下是您可以使用的其他 CEL 逻辑示例。

// Ensure due date is after invoice date
doc.due_date.normalized_value > doc.invoice_date.normalized_value

// Cross list validation: ensure that each employer_contribution 
// has a corresponding employee deduction
 doc.employer_contribution.size() == doc.employee_deduction.size() && lists.range(doc.employer_contribution.size()).all(i,doc.employee_deduction[i].current_amount.mention_text != "" && doc.employer_contribution[i].current_amount.mention_text != "")

行为变更

Document AI 的 CEL 方言修改了一些标准行为,以更好地适应文档处理使用情形。

基于 epsilon 的相等性

为了解决 CEL 中缺少小数类型的问题,并考虑到财务和数值数据中常见的浮点不精确性,我们针对数值类型(doubleint)修改了等值 (==) 和不等值 (!=) 运算符。这些运算符不再使用精确等值,而是使用基于 epsilon 的比较,容差为 1e-2 (0.01)。

例如,假设某个 total_amount 的归一化值为 100.005

对于表达式 doc.total_amount.normalized_value == 100.0,结果为 true。这是因为 abs(100.005 - 100.0) 小于 0.01。标准 CEL 会返回 false

对于表达式 doc.total_amount.normalized_value == 100.02,结果为 false。这是因为 abs(100.005 - 100.02) 大于 0.01

字符串函数限制

标准 CEL 字符串操作函数只能对实体的 mention_text 属性进行操作。此限制可确保验证规则始终应用于文档中显示的字面文本字符串。

受影响的函数包括:

  • contains()
  • endsWith()
  • startsWith()
  • matches()

以下是一个有效示例:

// Checks if the extracted currency symbol is a dollar sign.
doc.total_amount.mention_text.startsWith("$")

以下是无效命令的示例:

// This will produce a validation error upon save because contains() is not
// being used on a .mention_text field.
doc.supplier_name.normalized_value.contains("Inc.")

其他功能

本部分介绍了 Document AI CEL 评估环境中提供的非标准全局函数和成员函数。

求和函数

### sum()

计算数值元素列表的总和。它可以针对整数或双精度浮点数列表调用。请注意,系统会忽略列表中的 null 值。如果列表包含任何非数字元素,该函数将触发错误。

签名<list>.sum()

以下示例展示了如何汇总多个订单项的归一化价值。

// Calculates the sum of all line item amounts.
doc.line_item_amounts.normalized_value.sum()

OR 函数

### or()

如果原始值不为 null,则返回原始值;否则,返回第二个(默认)实参。

签名<value>.or(<;default_value>)

例如,您可以使用它在可选字段可能缺失时提供默认值。

// If the tax amount is not found, use a default of 0.0 for calculation.
doc.tax_amount.normalized_value.or(0.0) > 5.0

检查列表垂直对齐函数

### checkVerticalAlignment()

如果所有 bounding_poly 对象在容差指定的范围内垂直对齐,则返回 true。否则,返回一条错误消息,说明哪些实体的对齐不正确。

容差是一个可选实参,用于指定边界多边形与自动选择的最佳对齐边界多边形的重叠程度,0 表示边界多边形不相交,1 表示边界多边形完全相同。

签名checkVerticalAlignment([<;bounding_poly>,...],tolerance=0.8)

例如,您可以使用此参数来帮助将所有提取的实体保留在单个列中。

// Make sure that all extracted quantities are in the same column
checkVerticalAlignment(doc.line_items.map(li,li.quantity.bounding_poly))

检查水平对齐函数

### checkHorizontalAlignment()

如果所有 bounding_poly 对象在水平方向上对齐到容差指定的程度,则返回 true。否则,返回一条错误消息,说明哪些实体未对齐。

容差是一个可选实参,用于指定边界多边形与自动选择的最佳对齐边界多边形的重叠程度。0 表示边界多边形不相交,1 表示边界多边形完全相同。

签名checkHorizontalAlignment([<;bounding_poly>,...],tolerance=0.6)

例如,您可以使用此参数来帮助将所有提取的实体保留在同一行中。

// For all line items make sure that each extracted line item's
// children are in the same row. For example, if line item has
// quantity and description properties, it makes sure they are in 
// a single row for each respective line item.
doc.line_items.all(li, checkHorizontalAlignment(li.map(col,li[col].bounding_poly)))

其他宏

本部分介绍了 Document AI CEL 评估环境中提供的非标准宏。

如需详细了解宏,请参阅语言定义:宏

减少宏

### reduce()

使用 reduce 宏对列表执行累积运算。

签名<list>.reduce(<;iterator_name>, <;accumulator_name>, <;initial_value>, <;loop_expression>)

  • iterator_name:每次迭代中正在处理的元素的变量名称。
  • accumulator_name:用于累积结果的值的变量名称。
  • initial_value:累加器的初始值。
  • loop_expression:一种表达式,用于定义在每个步骤中如何更新累加器。

以下是宏使用示例:

// Use reduce to sum up the quantities from all line items.
doc.line_items.reduce(item, running_total, 0.0, running_total + item.quantity.normalized_value)

// Result: 3.0
// Tip: this could also be done by concatenating map and sum:
doc.line_items.map(item,item.quantity.normalized_value).sum()

CEL 扩展库

除了自定义功能之外,以下标准 CEL 扩展库也可供使用。

  • 列表:提供用于列表操作的函数:range, distinct, flatten, reverse, sort, slice。如需了解详情,请参阅列表
  • 数学:提供扩展的数学函数,例如 math.greatest, math.least, math.abs。如需了解详情,请参阅数学

后续步骤

了解预训练的处理器