A2UI — כיצד סוכן שולח ממשק אינטראקטיבי ולא רק טקסט
מאת צוות מדיה דיל · 12.08.2026 · Generative UI · 5 דק׳
צלילה טכנית לפרוטוקול A2UI (Agent-to-UI): איך סוכן שולח מבנה ממשק סטנדרטי במקום טקסט, ואיך Frontend מרנדר אותו בבטחה בכל ערוץ הצגה.
נניח שאותו סוכן AI צריך לדבר עם שלושה ערוצים שונים: אפליקציית ווב, בוט וואטסאפ, ואפליקציית סלולר native. אם כל ערוץ מקבל תשובה בפורמט משלו — HTML מעוצב לאתר, כפתורים קנייניים לוואטסאפ, JSON מותאם לאפליקציה — הסוכן צריך לדעת "לאן" הוא מדבר, ולכל שינוי בלוגיקה יש להטמיע שלוש פעמים. A2UI (Agent-to-UI) הוא ניסיון לפתור בדיוק את זה: פרוטוקול תקשורת סטנדרטי שמאפשר לסוכן לתאר ממשק פעם אחת, בפורמט אחיד, ולתת לכל ערוץ לתרגם אותו בעצמו לרכיבים המקומיים שלו. זה לא מוצר אחד, אלא דפוס ארכיטקטוני — וכדאי להבין את המנגנון שמאחוריו לפני שממשים אותו.
ההבדל בין API רגיל לפרוטוקול A2UI
ב-REST API רגיל, ה-Backend מחזיר נתונים "טהורים" (JSON עם שדות עסקיים), וה-Frontend מחליט לבד איך להציג אותם — איזו קומפוננטה, איזה layout, איזה סדר. ב-A2UI ההיפוך: הסוכן לא רק שולח נתונים, הוא שולח כוונת הצגה מפורשת — "זה כרטיס אישור", "זה טופס עם שלושה שדות שחייבים להיות מלאים לפני שליחה", "זו רשימת בחירה יחידה מתוך חמש אפשרויות". ה-Frontend עדיין אחראי על המימוש הוויזואלי בפועל, אבל המבנה הלוגי-אינטראקטיבי מגיע מהסוכן. זה דומה במהות לרעיון שתיארנו במדריך Generative UI, אבל A2UI מתמקד בפרוטוקול התקשורת עצמו — הסכימה שמעבירה את הכוונה — ולא רק בעובדה שהממשק נוצר דינמית.
ההודעה הטיפוסית ב-A2UI כוללת שלושה חלקים: סוג הרכיב (component type) מתוך אוצר מילים מוסכם מראש, payload נתונים ספציפי לאותו סוג, ותיאור הפעולות האפשריות (actions) שהמשתמש יכול לבצע על הרכיב, כולל לאן וכיצד יש לשלוח בחזרה כל תגובה. המבנה הזה מזכיר מאוד event schemas בארכיטקטורות מונחות-אירועים, רק שכאן ה"אירוע" מייצג רכיב ממשק שלם ולא רק שינוי נתונים בודד.
אנטומיה של הודעת A2UI
בפועל הודעה כזו יכולה להיראות כך:
{
"type": "ui.component",
"component": "form",
"id": "flight-search-1",
"fields": [
{ "name": "destination", "kind": "text", "required": true },
{ "name": "date", "kind": "date-range", "required": true }
],
"actions": [
{ "id": "submit", "label": "חפש טיסות", "sends": "form.submit" }
]
}
המבנה הזה מכוון בכוונה להיות channel-agnostic: אפליקציית ווב תרנדר את זה כטופס React עם date-picker גרפי; בוט וואטסאפ יתרגם את אותה הודעה לשאלות רצף פשוטות; אפליקציית סלולר native תבנה מזה SwiftUI או Jetpack Compose. הסוכן לא צריך לדעת שום דבר על המימוש בפועל — הוא רק צריך לדעת שהוא מבקש "טופס עם שני שדות". החוזה הזה מזכיר את הרעיון המרכזי מאחורי Module Federation: הפרדה ברורה בין מי שמגדיר את הכוונה למי שמממש אותה בפועל, כשכל צד יכול להתפתח בנפרד כל עוד החוזה נשמר בין הגרסאות.
יתרון נוסף של הפרדה כזו הוא גרסאות (versioning). כשמוסיפים סוג רכיב חדש לפרוטוקול, ערוצים ישנים שעדיין לא מכירים אותו יכולים ליפול בחזרה לרכיב גנרי — למשל להציג את הנתונים כטבלת מפתח-ערך פשוטה — במקום לקרוס לגמרי. זו תכונה קריטית כשיש כמה ערוצים בפריסה שונה בזמן, מצב שכמעט בלתי נמנע בארגון עם יותר מצוות פיתוח אחד.
ניתוב פעולות בחזרה (Round-Trip)
החלק המורכב ביותר ב-A2UI הוא לא שליחת הממשק, אלא קבלת התגובה בחזרה בפורמט שהסוכן יכול להבין. כשמשתמש ממלא טופס ולוחץ שליחה, ה-Frontend צריך לארוז את הקלט בהודעת action שמתאימה לסכימה שהסוכן ציפה לה — לא רק "הנתונים שהמשתמש הקליד", אלא הנתונים יחד עם מזהה ה-component המקורי, timestamp, וההקשר הדרוש כדי שהסוכן ידע לאיזו נקודה בשיחה זה שייך. בלי המזהה הזה, סוכן שמנהל כמה שיחות במקביל לא יכול לדעת איזה טופס נשלח על ידי איזה משתמש.
שכבת round-trip צריכה גם להתמודד עם timeouts ופעולות כפולות: מה קורה אם משתמש לוחץ "שלח" פעמיים, או אם הרכיב נותר גלוי אחרי שהשיחה כבר המשיכה הלאה? המימוש הנכון מסמן כל action כ-idempotent, עם מזהה ייחודי לכל ניסיון שליחה, ומבטל רכיבים "ישנים" מבחינה לוגית ברגע שהסוכן שלח עדכון חדש לאותו מזהה component, כדי שהמשתמש לא יראה שני מצבים סותרים של אותו טופס בו-זמנית. פרקטית, כדאי לשמור בצד הלקוח מפה של component id לגרסה אחרונה שהתקבלה, ולהתעלם משקט מתגובות שמתייחסות לגרסה ישנה יותר.
אבטחה ואימות סכימה
מכיוון שהודעות A2UI מגיעות ממודל שפה, הן חייבות לעבור ולידציה קפדנית לפני רינדור — בדיוק כמו כל קלט חיצוני לא אמין. שכבת האימות בודקת שכל component type שהתקבל אכן קיים ברג'יסטרי המוכר ל-Frontend, שכל שדה חובה קיים, ושאין ניסיון להעביר ערכים חורגים — למשל מחיר שלילי, תאריך שאינו תקין, או קישור לדומיין לא מאושר. זו אותה גישת "אמון אפס" שמאפיינת עבודה נכונה עם Structured Outputs ממודלים — הפלט תמיד מטופל כהצעה שיש לאמת, לא כפקודה שמבוצעת אוטומטית.
מעבר לזה, כדאי להגדיר רמות הרשאה על סוגי רכיבים: רכיב שמציג מידע (כרטיס, טבלה) יכול לרנדר כמעט תמיד, אבל רכיב שמבצע פעולה בלתי הפיכה (אישור תשלום, מחיקת נתונים) צריך לדרוש אישור נוסף מפורש מהמשתמש ברמת ה-UI, גם אם הסוכן "בטוח" בהחלטתו. הפרוטוקול המשלים לכך בצד ה-Backend, AG-UI, מתמקד בדיוק בסנכרון הזה בין מה שהסוכן מבצע לבין מה שהמשתמש רואה ומאשר בזמן אמת.
אימוץ הדרגתי בפרויקט קיים
אימוץ A2UI לא חייב להיות מהפכה של יום אחד. הגישה המעשית ביותר היא להתחיל מערוץ אחד — לרוב האתר, כי שם קל ביותר לבנות רג'יסטרי קומפוננטות ולבדוק שינויים — ולהגדיר שם קבוצה קטנה של סוגי רכיבים נתמכים: אולי חמישה או שישה, שמכסים את התרחישים הנפוצים ביותר בשיחה עם הסוכן. רק אחרי שהמנגנון יציב, ומספר הכשלים בולידציה נמוך לאורך זמן, מרחיבים לערוץ שני ומוסיפים עוד סוגי רכיבים בהדרגה. ניסיון לכסות בבת אחת את כל הערוצים ואת כל סוגי הרכיבים האפשריים כמעט תמיד מוביל לפרויקט שנתקע באמצע, כי המורכבות גדלה בצורה משולבת ולא ליניארית.
שיקול מעשי נוסף הוא מדידה: כדאי לעקוב אחרי שיעור ההודעות שנופלות לפולבק טקסט, כי שיעור עולה מצביע לרוב על כך שהמודל מנסה לבטא כוונות שאין להן ייצוג ברג'יסטרי הקיים — סימן טוב לאיזה רכיב חדש כדאי להוסיף בסבב הבא, ולא רק תקלה שיש לתקן.
תגיות: A2UI · Agent Protocol · Generative UI · Structured Outputs · Multi-channel · Frontend Architecture