ניב CEL לאימות מסמכים

האימות והתיקון באמצעות Document AI מתבססים על Common Expression Language (CEL) כדי לאפשר אימות ושינוי גמישים של נתונים בתהליכי העבודה של עיבוד המסמכים. ‫Document AI מציע קבוצה של פונקציות מותאמות אישית, פקודות מאקרו ושינויים התנהגותיים שמותאמים לנתוני ישויות במסמכים.

גישה לישויות בביטוי CEL

כל הביטויים מוערכים ביחס למשתנה בסיסי בשם doc, שמורכב מישויות שהן ביטויים או מאפיינים ששייכים למסמך. הישויות האלה דומות מאוד למבנה של הישויות במסמך שחולץ.

יש הרבה מאפיינים לישות שחולצה, אבל רק שלושה מהם זמינים להערכה באמצעות CEL.

  • mention_text: טקסט גולמי שחולץ כפי שהוא מופיע בישות שחולצה. ברירת המחדל היא מחרוזת ריקה.
  • normalized_value: טקסט האזכור שעבר נרמול, כפי שהוא מופיע בישות שחולצה. ברירת המחדל היא null. מידע נוסף על נירמול
  • bounding_poly: אובייקט מיוחד שמכיל ייצוג של המיקום של הישות שחולצה במסמך, ומשמש לבדיקות התאמה. ברירת המחדל היא null.

מודל נתונים

המבנה המדויק של ישות שחולצה במפה doc תלוי בשני גורמים. הראשון הוא אם המבנה שלו הוא ערך קונקרטי, כמו מספר או טקסט פשוט, או אם הוא אובייקט מורכב. הגורם השני הוא אם סוג המופע הוא יחיד או מרובה. מידע נוסף זמין במאמר OccurrenceType.

תכונה מרכזית של מודל נתוני האימות היא שכל ישות שמוגדרת בסכימה אבל לא חולצה מהמסמך מאוכלסת אוטומטית בערכי ברירת מחדל. העיצוב הזה מאפשר לכם לדלג על רוב בדיקות ה-null המפורשות בביטויי ה-CEL, וכך לפשט באופן משמעותי את ביטויי האימות. צריך לכתוב בדיקות של ערך null כדי לוודא שישות שנבחרה אכן חולצה.

דוגמאות לישויות עלים

בקטעים הבאים מוסבר איך לגשת לישויות בישות עלים, כלומר ישות ללא ישויות צאצא מוטמעות. ישויות עלים מכילות ערך באופן ישיר.

ישות עלה עם מופע יחיד

זהו המקרה הבסיסי ביותר, שבו משתמשים ב-OccurrenceType מתוך OPTIONAL_ONCE או ב-REQUIRED_ONCE. הישות מיוצגת כאובייקט שמכיל את שלוש המאפיינים הרגילים.

דוגמה לאופן הגישה לערכים האלה היא 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": []

טבלת המרות של ערך נורמלי

בטבלה הזו מוצג איך סוג הנתונים של ישות הסכימה שנבחרה מתורגם לסוג הנתונים של CELnormalized_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 != "")

שינויים התנהגותיים

ניב ה-CEL של Document AI משנה חלק מההתנהגויות הסטנדרטיות כדי להתאים יותר לתרחישי שימוש של עיבוד מסמכים.

שוויון מבוסס-אפסילון

כדי לטפל בהיעדר סוג עשרוני ב-CEL ולהתחשב בחוסר דיוק נפוץ בנקודות צפות בנתונים פיננסיים ומספריים, האופרטורים של שוויון (==) ואי-שוויון (!=) משתנים עבור סוגים מספריים (double, ‏ int). במקום שוויון מדויק, הם משתמשים בהשוואה מבוססת-אפסילון עם טווח טעות של 1e-2 (0.01).

לדוגמה, נניח שיש מאפיין total_amount עם ערך מנורמל של 100.005.

התוצאה של הביטוי doc.total_amount.normalized_value == 100.0 היא true. הסיבה לכך היא ש-abs(100.005 - 100.0) קטן מ-0.01. ‫Standard 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()

מחזירה את הערך המקורי אם הוא לא 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()

הפונקציה מחזירה את הערך true אם כל האובייקטים bounding_poly מיושרים אנכית למידה שצוינה על ידי הסבילות. אחרת, הפונקציה מחזירה שגיאה עם תיאור של הישויות שלא מיושרות.

הסבילות היא ארגומנט אופציונלי שמציין את החפיפה של מצולע תיחום עם מצולע התיחום הטוב ביותר שמתאים באופן אוטומטי, כאשר 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()

הפונקציה מחזירה את הערך true אם כל האובייקטים bounding_poly מיושרים אופקית למידה שצוינה על ידי הסבילות. אחרת, הפונקציה מחזירה שגיאה עם תיאור של הישויות שלא מיושרות.

הסבילות היא ארגומנט אופציונלי שמציין את החפיפה של פוליגון תיחום עם פוליגון התיחום הטוב ביותר שנבחר אוטומטית. המשמעות של 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: ביטוי שמגדיר איך ה-accumulator מעודכן בכל שלב.

דוגמה לשימוש במאקרו:

// 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. מידע נוסף זמין במאמר בנושא מתמטיקה.

המאמרים הבאים

מידע על מעבדים שאומנו מראש