Dialecte CEL pour la validation des documents

La validation et la correction Document AI s'appuient sur le Common Expression Language (CEL) pour permettre une validation et une manipulation flexibles des données dans vos workflows de traitement de documents. Document AI propose un ensemble de fonctions, de macros et de modifications comportementales personnalisées pour les données d'entités de document.

Accéder aux entités dans une expression CEL

Toutes les expressions sont évaluées par rapport à une variable racine nommée doc, qui se compose d'entités qui sont des expressions ou des propriétés appartenant au document. Ces entités suivent de près la structure des entités de votre document extrait.

Bien qu'une entité extraite contienne de nombreuses propriétés, seules trois d'entre elles sont disponibles pour l'évaluation CEL.

  • mention_text : texte brut extrait tel qu'il figure dans une entité extraite. La valeur par défaut est une chaîne vide.
  • normalized_value : texte de mention normalisé tel qu'il figure dans une entité extraite. La valeur par défaut est "null". En savoir plus sur la normalisation
  • bounding_poly : objet spécial contenant une représentation de l'emplacement de l'entité extraite dans le document et utilisé pour les vérifications d'alignement. Sa valeur par défaut est "null".

Modèle de données

La structure exacte d'une entité extraite dans la carte doc dépend de deux facteurs. La première concerne la structure : s'agit-il d'une valeur concrète, comme un nombre ou du texte brut, ou d'un objet complexe ? Le deuxième facteur est de savoir si son type d'occurrence est unique ou multiple. Pour en savoir plus, consultez la page sur la méthode OccurrenceType.

Une fonctionnalité clé du modèle de données de validation est que toute entité définie dans le schéma, mais non extraite du document, est automatiquement renseignée avec des valeurs par défaut. Cette conception vous permet d'ignorer la plupart des vérifications de valeurs nulles explicites dans vos expressions CEL, ce qui simplifie considérablement vos expressions de validation. Vous n'avez besoin d'écrire explicitement des vérifications de valeurs nulles que pour vous assurer qu'une entité sélectionnée a bien été extraite.

Exemples de cas d'entités feuilles

Les sections suivantes décrivent comment accéder aux entités d'une entité feuille, c'est-à-dire une entité sans entités enfants imbriquées. Les entités feuilles contiennent directement une valeur.

Entité feuille avec une seule occurrence

Il s'agit du cas le plus élémentaire, qui utilise OccurrenceType de OPTIONAL_ONCE ou REQUIRED_ONCE. L'entité est représentée sous la forme d'un objet contenant les trois propriétés standards.

doc.invoice_date.normalized_value est un exemple de la façon d'accéder à ces valeurs.

Il présente la structure suivante :

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

Valeur par défaut :

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

Entité feuille avec plusieurs occurrences

Ce cas s'applique aux entités feuilles qui peuvent se produire plusieurs fois et dont le OccurrenceType est OPTIONAL_MULTIPLE ou REQUIRED_MULTIPLE. Par exemple, dans une liste de dates d'échéance de paiement, elle est représentée sous la forme d'un objet où chaque propriété contient une liste des valeurs correspondantes de toutes les occurrences. Ainsi, des propriétés telles que mention_text, normalized_value et bounding_poly peuvent comporter plusieurs entités.

doc.payment_due_dates.normalized_value[0] est un exemple de la façon d'accéder à ces valeurs.

Il présente la structure suivante :

  "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]
  }

Valeur par défaut :

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

Entités imbriquées

Une entité imbriquée est un conteneur pour d'autres entités, qui sont ses "enfants".

Entité imbriquée avec une occurrence

Si une entité imbriquée ne se produit qu'une seule fois, par exemple un seul receiver_address, elle est représentée sous la forme d'un objet dont les clés sont les noms de ses entités enfants.

doc.receiver_address.city.mention_text est un exemple de la façon d'accéder à ces valeurs.

Il présente la structure suivante :

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

Valeur par défaut :

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

Entité imbriquée avec plusieurs occurrences

Lorsqu'une entité imbriquée peut se produire plusieurs fois, elle est représentée sous la forme d'une liste d'objets. Chaque objet de la liste représente une instance complète de l'entité imbriquée et contient ses enfants.

doc.line_items[1].description.normalized_value est un exemple de la façon d'accéder à ces valeurs.

Il présente la structure suivante :

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

Valeur par défaut :

  "line_items": []

Table de conversion de la valeur normalisée

Ce tableau indique comment le type de données de l'entité de schéma sélectionnée est traduit en type de données CEL normalized_value.

Type de données du schéma Type de données CEL
Devise, Adresse string
Nombre, argent double
Date/Heure proto.Timestamp
Case à cocher, Signature bool
Texte brut N/A

Exemples d'expressions

Voici quelques exemples d'expressions 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)

Voici d'autres exemples de logique CEL que vous pouvez utiliser.

// 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 != "")

Changements de comportement

Le dialecte CEL de Document AI modifie certains comportements standards pour mieux s'adapter aux cas d'utilisation du traitement de documents.

Égalité basée sur epsilon

Pour pallier l'absence de type décimal dans CEL et tenir compte des imprécisions courantes des nombres à virgule flottante dans les données numériques et financières, les opérateurs d'égalité (==) et d'inégalité (!=) sont modifiés pour les types numériques (double, int). Au lieu d'une égalité exacte, ils utilisent une comparaison basée sur epsilon avec une tolérance de 1e-2 (0,01).

Prenons l'exemple d'un total_amount avec une valeur normalisée de 100.005.

Avec l'expression doc.total_amount.normalized_value == 100.0, le résultat est true. En effet, abs(100.005 - 100.0) est inférieur à 0.01. L'expression CEL standard renverrait false.

Pour l'expression doc.total_amount.normalized_value == 100.02, le résultat est false. En effet, abs(100.005 - 100.02) est supérieur à 0.01.

Restrictions concernant les fonctions de chaîne

Les fonctions de manipulation de chaînes CEL standards sont limitées à la propriété mention_text d'une entité. Cette restriction garantit que les règles de validation sont appliquées de manière cohérente à la chaîne de texte littéral telle qu'elle apparaît dans le document.

Voici les fonctions concernées :

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

Voici un exemple valide :

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

Voici un exemple de commande non valide :

// 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.")

Fonctions supplémentaires

Cette section décrit les fonctions globales et membres non standards disponibles dans l'environnement d'évaluation CEL de Document AI.

Fonction Sum

### sum()

Calcule la somme d'une liste d'éléments numériques. Elle peut être appelée sur des listes d'entiers ou de doubles. Notez que les valeurs nulles de la liste sont ignorées. Si la liste contient des éléments non numériques, la fonction déclenche une erreur.

Signature : <list>.sum()

Voici un exemple qui récapitule la valeur normalisée de plusieurs éléments de ligne.

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

Fonction OR

### or()

Renvoie la valeur d'origine si elle n'est pas nulle. Sinon, renvoie le deuxième argument (par défaut).

Signature : <value>.or(<;default_value>)

Par exemple, vous pouvez l'utiliser pour fournir une valeur par défaut lorsqu'un champ facultatif est manquant.

// 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

Fonction d'alignement vertical de la liste à cocher

### checkVerticalAlignment()

Renvoie true si tous les objets bounding_poly sont alignés verticalement selon un degré spécifié par la tolérance. Sinon, renvoie une erreur décrivant les entités qui ne sont pas alignées.

La tolérance est un argument facultatif qui spécifie le chevauchement d'un polygone de délimitation avec le polygone de délimitation le mieux aligné sélectionné automatiquement. La valeur 0 signifie que les polygones de délimitation ne se croisent pas, tandis que la valeur 1 signifie qu'ils sont identiques.

Signature : checkVerticalAlignment([<;bounding_poly>,...],tolerance=0.8)

Par exemple, vous pouvez l'utiliser pour conserver toutes les entités extraites dans une seule colonne.

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

Vérifier la fonction d'alignement horizontal

### checkHorizontalAlignment()

Renvoie true si tous les objets bounding_poly sont alignés horizontalement selon un degré spécifié par la tolérance. Sinon, renvoie une erreur décrivant les entités qui ne sont pas alignées.

La tolérance est un argument facultatif qui spécifie le chevauchement d'un polygone de délimitation avec le polygone de délimitation le mieux aligné sélectionné automatiquement. La valeur 0 signifie que les polygones englobants ne se croisent pas, tandis que la valeur 1 signifie qu'ils sont identiques.

Signature : checkHorizontalAlignment([<;bounding_poly>,...],tolerance=0.6)

Par exemple, vous pouvez l'utiliser pour conserver toutes les entités extraites sur une seule ligne.

// 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)))

Macros supplémentaires

Cette section décrit les macros non standards disponibles dans l'environnement d'évaluation CEL Document AI.

Pour en savoir plus sur les macros, consultez Définition du langage : macros.

Macro de réduction

### reduce()

Utilisez la macro reduce pour effectuer des opérations cumulatives sur une liste.

Signature : <list>.reduce(<;iterator_name>, <;accumulator_name>, <;initial_value>, <;loop_expression>)

  • iterator_name : nom de variable pour l'élément en cours de traitement à chaque itération.
  • accumulator_name : nom de variable pour la valeur qui cumule le résultat.
  • initial_value : valeur initiale de l'accumulateur.
  • loop_expression : expression qui définit la manière dont l'accumulateur est mis à jour à chaque étape.

Voici un exemple d'utilisation de macros :

// 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()

Bibliothèques d'extensions CEL

En plus des fonctionnalités personnalisées, les bibliothèques d'extension CEL standards suivantes sont disponibles.

  • Listes : fournit des fonctions de manipulation des listes : range, distinct, flatten, reverse, sort, slice. Pour en savoir plus, consultez Listes.
  • Mathématiques : fournit des fonctions mathématiques étendues telles que math.greatest, math.least, math.abs. Pour en savoir plus, consultez Math.

Étapes suivantes

En savoir plus sur les processeurs préentraînés