Multimodal AI
לא רק טקסט: לבנות אפליקציות שמבינות תמונות, מסמכים ואודיו. אחד התחומים הכי שימושיים לאוטומציה עסקית אמיתית.
מה באמת השתנה
המשפט הרגיל הוא "המודלים יודעים לראות". זה נכון וזה מפספס את מה שמשנה בפועל.
עד לא מזמן, לחלץ נתונים ממסמך מצולם היה פרויקט: OCR שהמיר פיקסלים לתווים, ואז שכבת חוקים שידעה איפה בדיוק על הדף נמצא מספר החשבונית — לכל ספק בנפרד. ספק חדש עם פריסה אחרת פירושו קוד חדש. חשבונית שסרקה עקום פירושה שהכל נופל.
מודל מולטימודלי מוחק את השכבה השנייה. הוא לא מחפש מיקום — הוא מבין את המסמך. אותו קוד בדיוק עובד על ספק שלא ראית מעולם, על צילום מהטלפון בזווית, על טופס בכתב יד. זה השינוי, וזה גם למה זה אחד התחומים עם ההחזר המהיר ביותר.
אבל יחד עם היכולת השתנה גם משהו אחר, ופחות מדברים עליו: מצב הכשל.
רוב המידע בארגון אינו טקסט מסודר — הוא חשבוניות, צילומי מסך, טפסים והקלטות. זה מה שפותח אותו לאוטומציה.
כשל שקט במקום כשל רועש
זו הנקודה שקובעת איך בונים מערכת סביב זה, והיא הפוכה לגמרי ממה שהתרגלנו אליו.
OCR נכשל בקול. הוא מחזיר 1O0.5O במקום 100.50, ג׳יבריש, או כלום. אתה רואה את זה מיד, וגם הקוד שלך רואה — המחרוזת לא נפרסת למספר.
מודל ראייה נכשל בשקט. הוא יחזיר 100.50 — מספר תקין, מפורמט נכון, במקום הנכון ב-JSON. פשוט לא הסכום הנכון. לקח את הסכום לפני מע"מ במקום את הסה"כ, או את הסכום מחשבונית קודמת שמופיעה בפינה.
ההשלכה המעשית: אי אפשר לבנות על "אם זה החזיר משהו — זה הצליח". פלט תקין מבחינה מבנית הוא לא ראיה לכלום, וכל שאר העמוד הזה עוסק במה שצריך להיות מסביב.
הבסיס: לשלוח תמונה
המנגנון פשוט — תמונה נכנסת לתוך ההודעה לצד הטקסט, בין כקישור ובין כ-base64:
from openai import OpenAI
client = OpenAI()
# מזהי מודלים משתנים — קח את הנוכחי מתיעוד הספק
MODEL = "gpt-..."
resp = client.chat.completions.create(
model=MODEL,
messages=[{"role": "user", "content": [
{"type": "text",
"text": "מה מוצג בתמונה? תיאור קצר בעברית."},
{"type": "image_url",
"image_url": {"url": "https://example.com/photo.jpg"}},
]}],
)
print(resp.choices[0].message.content)
שתי הערות מיד. מזהי מודלים משתנים — ספקים משחררים גרסאות ומוציאים ישנות משימוש, ולכן שווה להחזיק את המזהה במשתנה סביבה ולא לפזר אותו בקוד. ותמונה עולה טוקנים, לפי גודלה — הסעיף על עלות מפרט.
לשאול על תמונה שונה מלשאול על טקסט
יש הבדל אחד שגורם לרוב התוצאות הגרועות, והוא לא בניסוח אלא בכיוון השאלה.
כששואלים "מה הסכום הכולל בחשבונית", המודל מקבל הנחה מובנית שיש שם סכום כולל — והוא ימצא אחד, גם אם המסמך הוא בעצם הצעת מחיר בלי סכום סופי, גם אם התמונה חתוכה, גם אם זה בכלל לא חשבונית. שאלה מובילה מייצרת תשובה מובילה.
מה שעובד הוא להפוך את הסדר: קודם לשאול מה יש, ורק אחר כך לחלץ.
# ✗ שאלה מובילה
"מה הסכום הכולל?"
# ✓ קודם לזהות, אחר כך לחלץ
"""
1. איזה סוג מסמך זה? (חשבונית / קבלה /
הצעת מחיר / תעודת משלוח / אחר / לא ברור)
2. האם המסמך שלם וקריא? אם חלקים חסרים
או מטושטשים — ציין אילו.
3. רק אם זו חשבונית קריאה — חלץ לפי הסכימה.
אחרת החזר {"document_type": ..., "extract": null}
"""
שלושת השלבים עולים מעט מאוד טוקנים נוספים ומונעים את הקטגוריה השלמה של "המודל חילץ בביטחון ממסמך שהוא בכלל לא הסוג הנכון". וסעיף 2 — האם המסמך שלם — הוא זה שתופס תמונות חתוכות, שהן הסיבה הנפוצה ביותר לשדות חסרים שמולאו בניחוש.
עוד שתי הנחיות שמשתלמות: לבקש ציטוט — "לצד כל שדה, החזר את הטקסט המדויק שממנו לקחת אותו" — כי זה גם מאפשר בדיקה מהירה וגם מקטין המצאות; ולאסור חישוב, כי הרגע שבו מודל מתחיל לחשב הוא הרגע שבו הוא מתחיל לטעות בשקט.
מסמכים: המקרה שמחזיר הכי הרבה
חילוץ שדות מחשבוניות, קבלות, טפסים ותעודות משלוח הוא השימוש שהכי מהר מחזיר את ההשקעה, כי הוא מחליף הקלדה ידנית שקורית כל יום.
הכלל הראשון והחשוב: לעולם לא טקסט חופשי. תמיד סכימה מפורשת עם שדות מוגדרים, כולל אפשרות מפורשת ל"לא נמצא".
SCHEMA = """
החזר JSON בלבד, בדיוק במבנה הזה:
{
"supplier_name": string | null,
"supplier_id": string | null,
"invoice_number": string | null,
"date": "YYYY-MM-DD" | null,
"subtotal": number | null,
"vat": number | null,
"total": number | null,
"currency": "ILS" | "USD" | "EUR" | null
}
חוקים:
- total הוא הסכום הסופי לתשלום, כולל מע"מ.
- אם שדה לא מופיע במסמך — null. אל תנחש.
- אל תמציא ואל תחשב שדה שלא כתוב.
"""
שלוש השורות בסוף עושות את רוב העבודה. השורה על total קיימת כי חשבונית ישראלית טיפוסית מכילה שלושה סכומים — לפני מע"מ, המע"מ עצמו, והסה"כ — ובלי הגדרה מפורשת המודל בוחר לפי מיקום, ולעיתים קרובות לא נכון. וההוראה על null קיימת כי ברירת המחדל של מודל היא למלא, לא להודות שאין.
אכיפה חזקה יותר של המבנה מתוארת בפלטים מובנים, וכדאי להשתמש בה כשהספק תומך — היא מבטיחה JSON תקין, אבל היא לא מבטיחה שהערכים נכונים. זה תפקידו של הסעיף הבא.
האימות הוא לא אופציונלי
זה החלק שמפריד בין הדגמה מרשימה לבין משהו שאפשר להריץ על מאה חשבוניות ביום. הרעיון: המסמך מכיל יתירות, ואפשר להשתמש בה כדי לתפוס טעויות.
def validate(inv):
problems = []
# 1. האם החשבון מסתדר עם עצמו
if None not in (inv["subtotal"], inv["vat"], inv["total"]):
if abs(inv["subtotal"] + inv["vat"] - inv["total"]) > 0.05:
problems.append("סכומים לא מסתדרים")
# 2. האם המע"מ בשיעור סביר
if inv["subtotal"] and inv["vat"] is not None:
rate = inv["vat"] / inv["subtotal"]
if not (0 <= rate <= 0.25):
problems.append("שיעור מע\"מ חריג: %.3f" % rate)
# 3. האם התאריך בכלל אפשרי
if inv["date"] and not is_plausible_date(inv["date"]):
problems.append("תאריך לא סביר")
# 4. שדות חובה
for f in ("supplier_name", "total"):
if not inv[f]:
problems.append("חסר: " + f)
return problems
הבדיקה הראשונה היא הטובה ביותר, כי היא לא דורשת שום ידע חיצוני — המסמך בודק את עצמו. אם המודל לקח את הסכום הלא נכון, החיבור לא יסתדר, ותדע בלי שאף אדם הסתכל.
ומה עושים עם מסמך שנכשל: לתור לבדיקה אנושית, לא לפח ולא פנימה. בפועל, שיעור המסמכים שנופלים לתור הזה הוא המדד היחיד שאומר לך אם המערכת מוכנה — וגם המדד שכדאי לעקוב אחריו לאורך זמן, כי הוא זז כשספק משנה פורמט.
איך יודעים שזה מוכן
"בדקתי על כמה מסמכים וזה עבד" הוא לא מדד. וההערכה לא צריכה להיות פרויקט — היא צריכה להיות שלושים מסמכים.
מה שבונים פעם אחת:
- שלושים מסמכים אמיתיים מהעסק שלך, לא דוגמאות. כולל את הכעורים — סרוק עקום, מקומט, מצולם בתאורה גרועה. הם החלק החשוב.
- התשובה הנכונה לצד כל אחד, שמישהו הזין ידנית פעם אחת. זו העבודה, והיא שעה.
- הרצה והשוואה — שדה מול שדה.
והנקודה שהופכת את זה לשימושי: למדוד לפי שדה, לא לפי מסמך. "84% הצלחה" לא אומר כלום. מה שאומר משהו הוא שהתאריך נכון ב-97% מהמקרים, הסכום ב-99%, ושם הספק ב-71% — כי עכשיו אתה יודע בדיוק על מה לעבוד, ואולי אפילו שהשדה הבעייתי הוא כזה שאפשר לוותר עליו.
fields = ["supplier_name", "invoice_number",
"date", "total", "vat"]
for f in fields:
ok = sum(1 for d in docs
if extracted[d.id][f] == truth[d.id][f])
print("%-16s %d/%d" % (f, ok, len(docs)))
וזה גם מה שהופך שינויים לבטוחים. שינית את הפרומפט? החלפת מודל? הספק שדרג גרסה? תריץ את אותם שלושים ותשווה. בלי זה, כל שינוי הוא הימור, ושיפור באזור אחד יכול לשבור אזור אחר בלי שתדע.
שווה גם להוסיף מסמכים לערכה כשמתגלה כשל חדש בייצור. אחרי חצי שנה יש לך אוסף של בדיוק המקרים שמפילים אותך, וזה הנכס היקר ביותר במערכת.
עברית ומסמכים ישראליים
ארבעה דברים שספציפיים למסמך מכאן, ואף מדריך זר לא יזכיר:
- פריסה דו-כיוונית. חשבונית ישראלית טיפוסית מערבבת עברית מימין לשמאל עם מספרים וטבלאות משמאל לימין. המודל מסתדר עם זה טוב יותר מ-OCR, אבל שגיאות הצמדה — איזה מספר שייך לאיזו שורה — הן הקטגוריה הנפוצה ביותר. אם אתה מחלץ שורות פריטים, זה המקום לבדוק.
- תאריכים.
3/9/26הוא שלושה בספטמבר כאן ותשעה במרץ במקום אחר. הכרח פורמט מפורש בסכימה, ואם המסמך עמום — עדיף null מניחוש. - מספרי עוסק וח"פ. תשע ספרות, ולפעמים עם רווחים או מקפים. שווה לנרמל בקוד אחרי החילוץ, ולא לבקש מהמודל לעשות את זה.
- שיעור מע"מ. הוא משתנה מדי כמה שנים. אל תקודד אותו קשיח בבדיקה — תבדוק טווח סביר, כמו בקוד למעלה, או תשלוף אותו מהגדרה.
ולגבי כתב יד עברי: זה עובד, ופחות טוב מהדפוס. לטופס שמולא ביד שווה להריץ ולראות מה שיעור הנפילות לתור האנושי לפני שמבטיחים למישהו אוטומציה מלאה.
התור האנושי: איך מתכננים אותו
כל מערכת כזאת מייצרת מסמכים שנפלו באימות, וההצלחה שלה נמדדת לא במה שעבר אלא בכמה מהיר וזול לטפל במה שלא.
מה שהופך תור לשמיש:
- המסמך והפלט זה לצד זה. הבודק צריך לראות את התמונה ואת השדות באותו מסך. אם צריך לפתוח קובץ בחלון אחר — הטיפול לוקח פי שלושה.
- הסיבה כתובה. לא "נכשל" אלא "הסכומים לא מסתדרים: 1,170 ≠ 1,000 + 180". הבודק יודע איפה להסתכל.
- השדה החשוד מסומן. אם אפשר לזהות איזה שדה כנראה שגוי, סמן אותו — ואל תבקש מהבודק לאמת מחדש את כל השאר.
- תיקון בקליק אחד. לתקן שדה, לאשר, הלאה. כל חיכוך נוסף גורם לתור להצטבר.
- מה שתוקן חוזר לערכת הבדיקה. אחרת אותה טעות תחזור לנצח.
ושאלה שכדאי להחליט עליה מראש: מה עדיף — לפספס או לטעות? אימות מחמיר שולח יותר מסמכים לבדיקה ידנית ומבטיח שמעט מאוד טעויות עוברות; אימות מקל חוסך עבודה ומעביר יותר שגיאות הלאה. בחשבונאות התשובה כמעט תמיד מחמיר, ובמיון תמונות לקטלוג — מקל. זו החלטה עסקית ולא טכנית, וכדאי שהיא תיאמר בקול לפני שהיא נקבעת במקרה.
רזולוציה, טוקנים ועלות
תמונה מתומחרת כטוקנים, והמספר תלוי בגודל שלה. זה מייצר שיקול שאין לו מקבילה בטקסט: גדול יותר עולה יותר וגם לא בהכרח מדויק יותר.
- יש רף תחתון שמתחתיו זה נשבר. טקסט קטן בסריקה בחדות נמוכה פשוט לא ייקרא, ולא משנה כמה טוב הפרומפט.
- ויש רף שמעליו אין רווח. להעלות צילום של 12 מגה-פיקסל של קבלה קטנה זה לשלם פי כמה על אותה תוצאה.
- חיתוך מנצח הקטנה. אם צריך רק את הפינה עם הסכום — תחתוך אותה ותשלח. פחות טוקנים, ופחות הסחות דעת למודל.
- מסמך רב-עמודי. שליחת עשרה עמודים בבקשה אחת יקרה ולרוב מיותרת. עדיף לזהות את העמוד הרלוונטי ולשלוח אותו.
הדרך היחידה לדעת מה נכון אצלך היא למדוד: הרץ עשרים מסמכים אמיתיים בשלוש רזולוציות, והשווה גם דיוק וגם עלות. נקודת האיזון שונה בין סוגי מסמכים, וברוב המקרים היא נמוכה יותר ממה שנדמה.
ואיך זה מתחבר לשאר המערכת
חילוץ הוא אף פעם לא הסוף — הנתונים צריכים להגיע למקום. בעסק ישראלי טיפוסי זה אומר תוכנת הנהלת חשבונות, גיליון, או מערכת ניהול.
שלוש נקודות שמתגלות רק כשמחברים:
- המסמך הוא המקור, לא הפלט. תשמור את הקובץ המקורי לצד הנתונים שחולצו, עם מזהה שמקשר ביניהם. בעוד שנה, כשמישהו ישאל למה הסכום הזה כזה, זו התשובה היחידה.
- אותו מסמך יגיע פעמיים. ספק ששלח שוב, מייל שהועבר, סריקה כפולה. זיהוי כפילויות לפי מספר חשבונית וספק חוסך את הבלגן שנוצר כשאותה הוצאה נרשמת פעמיים.
- כתיבה למערכת החשבונאית — בזהירות. כפי שנכתב במדריך לעסקים קטנים: עדיף שהאוטומציה תכין את הרישום ואדם יאשר. חילוץ אוטומטי שרושם ישירות הוא בדיוק המקום שבו טעות שקטה הופכת לבעיה חשבונאית.
אודיו: מה "מולטימודלי" אומר כאן
יש כאן בלבול שכדאי לפרק. רוב מה שנקרא עיבוד אודיו הוא בעצם שני שלבים: תמלול ואז עיבוד טקסט. מודל שמקבל אודיו ישירות הוא דבר אחר, ולא תמיד זה מה שאתה צריך.
- תמלול ואז עיבוד — זול, מדויק, וניתן לבדיקה. יש לך את התמלול ביד, אפשר לתקן אותו, והוא נכס בפני עצמו. זו הבחירה הנכונה כמעט תמיד.
- אודיו ישירות למודל — משמר טון, היסוס ורגש, ורלוונטי כשהם חלק ממה שמעניין אותך. יקר יותר, ופחות שקוף.
לשיחות שירות, ישיבות וראיונות — המסלול הראשון. שווה גם לזכור שאם יש כמה דוברים, הקלטת ערוץ נפרד לכל אחד פותרת את הפרדת הדוברים לחלוטין, ועולה בדיוק אותו דבר.
ומה עם צילום מסך במקום מסמך
קטגוריה שלמה שמתנהגת אחרת: צילומי מסך. הם נקיים, חדים, ובעלי מבנה — ולכן הדיוק עליהם גבוה בהרבה מאשר על מסמך מצולם. זה גם הופך אותם למקום הכי טוב להתחיל בו כשבודקים אם הרעיון בכלל עובד אצלך.
שני שימושים שמשתלמים מיד: תמיכה — לקוח ששולח תמונה של הודעת שגיאה, ומערכת שמזהה את השגיאה ומנתבת או משיבה מיד; ובקרה — לוודא שמסך, טופס או מודעה נראים כמו שצריך אחרי שינוי, בלי שאדם יעבור על מאה מסכים.
הערה אחת: צילום מסך של ממשק מכיל לעיתים קרובות מידע שלא התכוונת לשלוח — שם משתמש, כתובת מייל, שורות אחרות בטבלה. כשמדובר בזרימה אוטומטית ששולחת לספק חיצוני, שווה לחתוך לאזור הרלוונטי לפני, כפי שנכתב בסעיף הפרטיות.
וידאו: פריימים, לא סרט
"מודל שמבין וידאו" נשמע כמו שהוא צופה. בפועל, ברוב המקרים, הסרטון נדגם לפריימים בודדים והם נשלחים כתמונות — לפעמים לצד התמלול של פס הקול.
מה שנגזר מזה מעשית:
- קצב הדגימה הוא ההחלטה. פריים בשנייה על סרטון של עשר דקות זה שש מאות תמונות, וזה מתומחר בהתאם. לרוב אחד לחמש או לעשר שניות מספיק.
- תנועה מהירה תיעלם. מה שקרה בין שני פריימים לא נצפה. אם מה שמעניין אותך הוא רגע קצר, דגימה דלילה תפספס אותו.
- התמלול הוא לרוב העיקר. בסרטון מדבר — הרצאה, ישיבה, הדגמה — רוב המידע נמצא בקול, והפריימים משלימים. תתחיל משם.
איפה זה באמת משתלם
- חשבוניות וקבלות — הקלאסי, וגם זה שמחזיר הכי מהר. במיוחד בעסק שמזין עשרות מסמכים בחודש ידנית.
- טפסים ותעודות משלוח — כולל כאלה שמולאו ביד.
- צילומי מסך לתמיכה — לקוח ששולח תמונה של שגיאה, ומערכת שמזהה על מה מדובר ומנתבת.
- בקרת איכות ויזואלית — האם התמונה בקטלוג עומדת בכללים: רקע, זווית, סימון.
- נגישות — תיאור אוטומטי של תמונות באתר, שזו גם דרישה וגם משפרת חיפוש.
- סיכום ישיבות — תמלול ואז חילוץ החלטות ומשימות.
ומה שמשותף לרשימה: כולן משימות שאדם עושה היום, שהן חוזרות, ושיש בהן תשובה נכונה שאפשר לבדוק. זה בדיוק הפרופיל שמתאים, וזה גם ההסבר למה תרחישים "יצירתיים" יותר מאכזבים.
מה אתה שולח, ולאן
חשבונית מכילה שם ספק, סכומים ולעיתים פרטי בנק. צילום מסך של תמיכה מכיל את המסך של הלקוח. הקלטת ישיבה מכילה הכל.
שלוש שאלות לפני שמריצים על מידע אמיתי:
- האם התוכן משמש לאימון? ברוב המסלולים העסקיים לא, וזו הגדרה שכדאי לוודא במפורש ולא להניח.
- כמה זמן זה נשמר? לספקים יש מדיניות שמירה לצורכי ניטור, וזה שונה מאימון.
- מה ההתחייבות שלך ללקוח? אם הבטחת שמידע לא יוצא — זה שיקול נפרד לגמרי מבחירת המודל.
כשהתשובות בעייתיות, יש מסלול: מודל שרץ אצלך. הוא פחות מדויק ודורש תחזוקה, והוא פותר את השאלה מהיסוד — פירוט בHuggingFace. ולעיתים קרובות הפתרון הפשוט יותר הוא להסתיר מה שלא נחוץ: אם צריך רק סכום ותאריך, אפשר לחתוך את החלק הרלוונטי ולא לשלוח את כל המסמך.
מתי לא
- כשהמסמכים אחידים לגמרי. אם כולם מאותו ספק באותה פריסה, פתרון פשוט וזול יעבוד ויהיה ודאי יותר.
- כשאין סובלנות לטעות. העברות כספיות, מינון תרופתי, כל דבר שטעות בו לא ניתנת לתיקון. אפשר להשתמש כדי להכין — לא כדי לבצע.
- כשאין מי שיטפל בתור. מערכת שמייצרת מקרים לבדיקה ואף אחד לא בודק אותם היא מערכת שלא עובדת.
- כשהנפח זעיר. חמישה מסמכים בחודש לא מצדיקים תהליך. תקליד אותם.
טעויות שחוזרות
- לקבל טקסט חופשי במקום סכימה. ואז לפרסר אותו עם ביטויים רגולריים, וזה נשבר בשבוע השני.
- להניח שפלט תקין הוא פלט נכון. הכשל השקט, ומקור כל שאר הבעיות.
- בלי בדיקת סכומים. ויתור על היתירות שהמסמך נותן לך בחינם.
- בלי תור אנושי. מסמך שנכשל חייב ללכת לאנשהו.
- לשלוח את המסמך בגודל מלא. משלמים פי כמה על אותה תוצאה.
- לקודד שיעור מע"מ קשיח. הוא ישתנה, והבדיקה תתחיל להיכשל על מסמכים תקינים.
- לתת למודל לחשב. אם אפשר לחשב את זה בקוד — תחשב בקוד. המודל צריך לקרוא, לא לעשות חשבון.
- לקבע מזהה מודל בקוד. גרסאות יוצאות משימוש, והמערכת נופלת ביום שזה קורה.
הצעד הבא
בנה אוטומציית מסמכים אמיתית עם תבנית מוכנה, או חזק את הפלט המובנה.