Document AI の検証と修正では、Common Expression Language(CEL)を活用して、ドキュメント処理ワークフロー内で柔軟なデータ検証と操作を行うことができます。Document AI には、ドキュメント エンティティ データ用に調整された一連のカスタム関数、マクロ、動作変更が用意されています。
CEL 式でエンティティにアクセスする
すべての式は、doc という名前のルート変数に対して評価されます。この変数は、ドキュメントに属するフレーズまたはプロパティであるエンティティで構成されます。これらのエンティティは、抽出されたドキュメントのエンティティの構造に密接に沿っています。
抽出されたエンティティには多くのプロパティが含まれていますが、CEL 評価に使用できるのはそのうちの 3 つだけです。
mention_text: 抽出されたエンティティに存在する抽出された未加工のテキスト。デフォルトは空の文字列です。normalized_value: 抽出されたエンティティに存在する正規化された言及テキスト。デフォルトは null です。詳細については、正規化をご覧ください。bounding_poly: 抽出されたエンティティのドキュメント内の配置を表す特殊なオブジェクト。配置チェックに使用されます。デフォルトは null です。
データモデル
doc マップ内の抽出されたエンティティの正確な構造は、次の 2 つの要素によって異なります。1 つ目は、構造が数値やプレーン テキストなどの具体的な値であるか、複合オブジェクトであるかです。2 つ目の要素は、発生タイプが単一か複数かです。詳細については、OccurrenceType をご覧ください。
検証データモデルの重要な機能は、スキーマで定義されているがドキュメントから抽出されていないエンティティにデフォルト値が自動的に設定されることです。この設計により、CEL 式で明示的な null チェックのほとんどをスキップできるため、検証式が大幅に簡素化されます。選択したエンティティが実際に抽出されたことを確認するために、null チェックを明示的に記述する必要があるだけです。
リーフ エンティティのユースケースの例
以降のセクションでは、ネストされた子エンティティのないリーフ エンティティ内のエンティティにアクセスする方法について説明します。リーフ エンティティは値を直接保持します。
単一の発生を持つリーフ エンティティ
これは、OPTIONAL_ONCE または REQUIRED_ONCE の OccurrenceType を使用する最も基本的なケースです。エンティティは、3 つの標準プロパティを含むオブジェクトとして表されます。
これらの値にアクセスする方法の例は 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": []
}
ネストされたエンティティ
ネストされたエンティティは、他のエンティティ(「子」)のコンテナです。
1 回発生するネストされたエンティティ
ネストされたエンティティが 1 回だけ発生する場合(単一の 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 言語は、ドキュメント処理のユースケースに適するように、標準の動作を一部変更します。
イプシロンに基づく等価性
CEL に 10 進数型がないことと、財務データや数値データでよく見られる浮動小数点数の不正確さを考慮して、数値型(double、int)の等価演算子(==)と不等価演算子(!=)が変更されました。完全な等価性ではなく、許容値 1e-2(0.01)のイプシロンベースの比較が使用されます。
たとえば、正規化された値が 100.005 の total_amount について考えてみましょう。
式 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 でない場合は元の値を返し、NULL の場合は 2 番目の引数(デフォルト)を返します。
署名: <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)
たとえば、この機能を使用して、抽出されたすべてのエンティティを 1 つの列に保持できます。
// 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)
たとえば、抽出されたすべてのエンティティを 1 つの行に保持するために使用できます。
// 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()
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:
math.greatest, math.least, math.absなどの拡張数学関数を提供します。詳細については、Math をご覧ください。
次のステップ
事前トレーニング済みプロセッサについて学習する。