用于文档验证的 CEL 方言

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

访问 CEL 表达式中的实体

所有表达式都会针对名为 doc 的根变量进行求值,该变量由属于文档的短语或属性的实体组成。这些实体严格遵循所提取文档的实体结构。

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

  • mention_text:提取的实体中存在的原始提取文本。默认值为空字符串。
  • normalized_value:提取的实体中存在的标准化提及文本。默认值为 null。详细了解标准化。
  • bounding_poly:一个特殊对象,包含所提取实体在文档中的位置表示,用于对齐检查。默认值为 null。

数据模型

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

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

叶实体示例

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

仅出现一次的叶实体

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

以下示例展示了如何访问这些值: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
  }

多次出现的叶实体

此情况适用于可多次出现且 OccurrenceType 为 OPTIONAL_MULTIPLE 或 REQUIRED_MULTIPLE 的叶实体。例如,在付款截止日期列表中,它表示为一个对象,其中每个属性都包含一个列表,其中包含所有匹配项中的对应值。因此,mention_text、normalized_value 和 bounding_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 中缺少十进制类型的问题,并考虑到财务和数值数据中常见的浮点不准确性,我们针对数值类型(double、int)修改了等式 (==) 和不等式 (!=) 运算符。它们不使用精确等式,而是使用基于 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 函数

### 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 扩展库。

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

后续步骤

了解接地。