تخيل هذا الموقف: مستخدم يتحدث مع chatbot داخل تطبيقك، ويطلب منه حجز موعد. الـ chatbot يرد بنص جاف: “من فضلك أدخل الاسم، التاريخ، الوقت…” — بينما كان يمكنه أن يعرض فوراً فورم حجز كامل بتاريخ، وحقل اسم، وزر تأكيد.
الفجوة بين الردود النصية للـ AI وبين الـ UI الغني الذي يتوقعه المستخدم — هذه بالضبط المشكلة التي جاء A2UI ليحلها.
ما هو A2UI؟
A2UI (اختصار لـ Agent-to-User Interface) هو بروتوكول مفتوح المصدر من Google يتيح لـ AI Agents (وكلاء الذكاء الاصطناعي) توليد واجهات مستخدم تفاعلية وإرسالها إلى أي تطبيق، بطريقة آمنة وقابلة للتنفيذ الفوري.
الفكرة الجوهرية بسيطة: بدلاً من أن يرسل الـ AI كوداً قابلاً للتنفيذ (وهو خطر أمنياً)، يرسل JSON تصريحي (Declarative JSON) يصف ماذا تريد أن تعرض، وتطبيقك هو من يعرضه بمكوناته الخاصة.
المشروع حالياً في الإصدار (Public Preview) وحصل على أكثر من 14,000 نجمة على GitHub منذ إطلاقه.
لماذا تهتم بـ A2UI؟
إذا كنت تبني تطبيقاً فيه أي نوع من الـ AI Assistant أو Chatbot، فستصطدم بهذه المشكلة عاجلاً أم آجلاً:
المشكلة الأولى — الأمان: لو سمحت للـ AI بإرسال HTML أو JavaScript مباشرة وعرضته في صفحتك، فقد فتحت الباب أمام XSS attacks وثغرات أمنية خطيرة.
المشكلة الثانية — التوافق: تريد نفس الـ AI Response تُعرض بشكل صحيح على web، Flutter، وReact — كل منهم له مكوناته الخاصة.
المشكلة الثالثة — التجربة: المستخدم لا يريد قراءة ردود نصية طويلة — يريد form يملؤه، أو card يضغط عليه، أو chart يقرأ منه البيانات.
A2UI يحل الثلاثة دفعة واحدة:
- ✅ آمن: الـ AI لا يرسل كوداً، يرسل بيانات فقط. تطبيقك يملك “catalog” من المكونات المسموح بها، والـ AI يختار منها فقط.
- ✅ Framework-Agnostic (متوافق مع أي إطار عمل): نفس الـ JSON يُعرض على Angular، Lit، Flutter، وقريباً React وSwiftUI.
- ✅ Progressive Rendering (عرض تدريجي): المستخدم يشاهد الـ UI وهو يُبنى أمامه في الوقت الفعلي، مثل streaming النصوص لكن للواجهات.
كيف يعمل A2UI تقنياً؟
الـ Flow كالتالي:
[المستخدم يرسل رسالة]
↓
[الـ AI Agent يولّد A2UI JSON]
↓
[يُرسل للتطبيق عبر A2A أو AG-UI]
↓
[الـ A2UI Renderer يحلّل الـ JSON]
↓
[التطبيق يعرض المكونات الأصلية]
↓
[المستخدم يتفاعل ← يُرسل Actions للـ AI]
الـ JSON الذي يرسله الـ AI يبدو هكذا:
{
"components": [
{
"id": "booking-card",
"type": "card",
"children": ["title-1", "input-name", "input-date", "btn-confirm"]
},
{
"id": "title-1",
"type": "text",
"props": { "content": "احجز موعدك الآن", "variant": "heading" }
},
{
"id": "input-name",
"type": "text-field",
"props": { "label": "الاسم الكامل", "placeholder": "أدخل اسمك" }
},
{
"id": "input-date",
"type": "date-picker",
"props": { "label": "تاريخ الموعد" }
},
{
"id": "btn-confirm",
"type": "button",
"props": { "label": "تأكيد الحجز", "action": "submit-booking" }
}
]
}
لاحظ كيف أن هذا JSON يصف المقصود من الـ UI لكنه لا يحتوي على أي HTML أو JavaScript. تطبيقك هو من يقرر كيف يعرض كل type.
ما الذي تحتاجه قبل البدء؟
قبل أن تشغّل A2UI في مشروعك، خذ دقيقتين تتحقق من هذه المتطلبات — لو تخطيتها ستواجه أخطاء غير مفهومة لاحقاً.
المتطلبات الأساسية
| الأداة | الإصدار المطلوب | من أين تحصل عليها؟ |
|---|---|---|
| Node.js | v18 أو أحدث | nodejs.org |
| uv (مشغّل Python) | أي إصدار حديث | astral.sh/uv |
| Gemini API Key | مجانية | Google AI Studio |
هل يحتاج A2UI لـ API Key؟ نعم — الـ samples الرسمية تستخدم Gemini API لتوليد الـ JSON. الـ API Key مجانية تماماً من Google AI Studio ولا تحتاج بطاقة بنكية للبدء. لكن إذا أردت كتابة الـ A2UI JSON يدوياً (مثل المثال في هذا المقال) فلا تحتاج أي API Key على الإطلاق.
كيف تثبّت A2UI في مشروعك — خطوة بخطوة
طريقتان للتثبيت — أيهما تختار؟
قبل أن تبدأ، مهم تفرق بين حالتين:
الحالة الأولى — تريد تجرب A2UI وتفهم كيف يعمل: هنا فقط تستخدم الـ git clone لتحميل المستودع كاملاً بالـ demos والأمثلة الجاهزة. افعل هذا مرة واحدة على جهازك للتعلم، ولا تكرره في كل مشروع.
git clone https://github.com/google/A2UI.git
الحالة الثانية — تريد تضيف A2UI لمشروع حقيقي: هنا تتجاهل الـ clone تماماً وتثبّت الـ package المناسب لـ framework مشروعك مباشرةً:
# React
npm install @a2ui/react @a2ui/web_core
# Lit / Web Components
npm install @a2ui/lit @a2ui/web_core
# Angular
npm install @a2ui/angular @a2ui/web_core
# Flutter
flutter pub add flutter_genui
بهذه الطريقة تضيف A2UI لأي مشروع موجود بنفس الطريقة التي تضيف بها أي مكتبة عادية — بدون ما تلمس المستودع الأصلي مجدداً.
تجربة الـ Demo (للتعلم فقط)
إذا اخترت الـ clone للتجربة، إليك الخطوات الكاملة:
الخطوة الأولى: ضع الـ API Key
# على Mac/Linux
export GEMINI_API_KEY="your_gemini_api_key_here"
# على Windows (Command Prompt)
set GEMINI_API_KEY=your_gemini_api_key_here
# على Windows (PowerShell)
$env:GEMINI_API_KEY="your_gemini_api_key_here"
تحقق أنه اتحفظ صح:
echo $GEMINI_API_KEY
# المفروض يظهر مفتاحك
الخطوة الثانية: شغّل الـ Demo بأمر واحد
cd samples/client/lit
npm run demo:restaurant
هذا الأمر الواحد يقوم بكل شيء تلقائياً: يثبّت الـ dependencies، يبني الـ renderer، يشغّل الـ Backend، ويفتح المتصفح على http://localhost:5173.
بعد ثوانٍ ستجد تطبيق ويب كامل يعمل — جرّب اكتب فيه “Book a table for 2” وشاهد الـ AI يبني الـ Form أمامك مباشرة.
طريقة بديلة: تشغيل الـ Backend والـ Frontend منفصلَين
# Terminal 1 — الـ Agent (Python Backend)
cd samples/agent/adk/restaurant_finder
uv run .
# Terminal 2 — الـ Client (Web Frontend)
cd samples/client/lit/shell
npm run dev
مشاكل شائعة وحلولها السريعة
المشكلة: uv: command not found
curl -LsSf https://astral.sh/uv/install.sh | sh
المشكلة: المتصفح يفتح قبل ما الـ Backend يجهز وتشوف ERR_CONNECTION_REFUSED
لا تقلق، هذه مشكلة معروفة في الـ demo — فقط انتظر 5 ثوانٍ وأعد تحديث الصفحة.
المشكلة: خطأ في الـ API Key
echo $GEMINI_API_KEY # تحقق أن المفتاح موجود
export GEMINI_API_KEY="مفتاحك_هنا" # أعد تصديره إذا كان فارغاً
هيكل المشروع — أين تجد كل شيء؟
A2UI/
├── renderers/ ← مكتبات العرض (Lit, Angular, Flutter...)
│ ├── lit/ ← renderer للويب
│ └── web_core/ ← النواة الأساسية المشتركة
├── samples/
│ ├── agent/ ← كود الـ Backend (Python agents)
│ │ └── adk/restaurant_finder/
│ └── client/ ← كود الـ Frontend
│ ├── lit/ ← الـ demo الرئيسي
│ ├── angular/ ← نسخة Angular
│ └── react/ ← نسخة React
├── specification/ ← المواصفات التقنية الرسمية
└── docs/ ← التوثيق
هل تريد التجربة بدون تثبيت أي شيء؟
إذا كنت فقط تريد تجربة الفكرة قبل أي تثبيت، Google وفّرت A2UI Composer — أداة مرئية في المتصفح تتيح لك توليد A2UI JSON واختبار كيف يُعرض:
وهناك أيضاً A2UI Theater لمشاهدة سيناريوهات streaming جاهزة على Lit، React، وAngular بدون ما تكتب سطر كود:
👉 a2ui-composer.ag-ui.com/theater
مثال تطبيقي كامل — محاكاة A2UI Renderer بـ HTML خالص

بما أن A2UI يعتمد على مبدأ “الـ JSON يصف، والتطبيق يعرض”، إليك محاكاة عملية بـ HTML + JavaScript الخالص توضح المفهوم بالكامل — لا تحتاج أي مكتبة خارجية أو API Key:
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<title>A2UI — مثال عملي</title>
<style>
body { font-family: sans-serif; background: #f0f4f8; padding: 20px; }
#surface { max-width: 480px; margin: auto; }
.card {
background: white;
border-radius: 12px;
padding: 24px;
box-shadow: 0 2px 12px rgba(0,0,0,0.08);
display: flex;
flex-direction: column;
gap: 14px;
}
.heading { font-size: 1.3rem; font-weight: bold; color: #1a202c; }
input[type="text"], input[type="date"] {
width: 100%; padding: 10px;
border: 1px solid #cbd5e0;
border-radius: 8px;
font-size: 1rem;
box-sizing: border-box;
}
label { font-size: 0.9rem; color: #4a5568; margin-bottom: 4px; display: block; }
button {
background: #3b82f6; color: white;
border: none; padding: 12px;
border-radius: 8px; font-size: 1rem;
cursor: pointer; width: 100%;
}
button:hover { background: #2563eb; }
#result {
margin-top: 16px; padding: 12px;
background: #d1fae5; border-radius: 8px;
color: #065f46; display: none;
}
</style>
</head>
<body>
<div id="surface"></div>
<div id="result"></div>
<script>
// --- هذا هو الـ A2UI JSON الذي "يرسله الـ AI" ---
const a2uiPayload = {
components: [
{
id: "booking-card",
type: "card",
children: ["title-1", "input-name", "input-date", "btn-confirm"]
},
{
id: "title-1",
type: "text",
props: { content: "📅 احجز موعدك الآن", variant: "heading" }
},
{
id: "input-name",
type: "text-field",
props: { label: "الاسم الكامل", placeholder: "مثال: أحمد محمد" }
},
{
id: "input-date",
type: "date-picker",
props: { label: "تاريخ الموعد" }
},
{
id: "btn-confirm",
type: "button",
props: { label: "تأكيد الحجز ✓", action: "submit-booking" }
}
]
};
// --- هذا هو الـ Renderer: يحوّل الـ JSON لـ HTML حقيقي ---
const componentMap = {};
// أولاً: نبني فهرساً (index) للمكونات
a2uiPayload.components.forEach(c => componentMap[c.id] = c);
// ثانياً: دالة عودية تبني كل مكوّن بناءً على type
function render(id) {
const comp = componentMap[id];
if (!comp) return document.createTextNode('');
switch (comp.type) {
case 'card': {
const div = document.createElement('div');
div.className = 'card';
comp.children.forEach(childId => div.appendChild(render(childId)));
return div;
}
case 'text': {
const el = document.createElement(
comp.props.variant === 'heading' ? 'h2' : 'p'
);
el.className = comp.props.variant === 'heading' ? 'heading' : '';
el.textContent = comp.props.content;
return el;
}
case 'text-field': {
const wrapper = document.createElement('div');
const label = document.createElement('label');
label.textContent = comp.props.label;
const input = document.createElement('input');
input.type = 'text';
input.placeholder = comp.props.placeholder || '';
input.id = comp.id;
wrapper.appendChild(label);
wrapper.appendChild(input);
return wrapper;
}
case 'date-picker': {
const wrapper = document.createElement('div');
const label = document.createElement('label');
label.textContent = comp.props.label;
const input = document.createElement('input');
input.type = 'date';
input.id = comp.id;
wrapper.appendChild(label);
wrapper.appendChild(input);
return wrapper;
}
case 'button': {
const btn = document.createElement('button');
btn.textContent = comp.props.label;
btn.addEventListener('click', () => handleAction(comp.props.action));
return btn;
}
default:
return document.createTextNode('');
}
}
// ثالثاً: نعرض المكوّن الجذر (root component)
const rootId = a2uiPayload.components[0].id;
document.getElementById('surface').appendChild(render(rootId));
// رابعاً: معالجة الأحداث (Actions) التي ترجع للـ AI
function handleAction(action) {
if (action === 'submit-booking') {
const name = document.getElementById('input-name').value;
const date = document.getElementById('input-date').value;
const result = document.getElementById('result');
result.style.display = 'block';
result.textContent = name && date
? `✅ تم تأكيد حجز "${name}" بتاريخ ${date}`
: `⚠️ من فضلك أكمل جميع الحقول`;
}
}
</script>
</body>
</html>
ماذا يحدث في هذا الكود؟
الكود ينقسم لأربع خطوات تعكس بالضبط كيف يعمل A2UI الحقيقي:
- الـ Payload: كتلة الـ JSON تمثل ما سيرسله الـ AI — لا HTML، لا JavaScript، فقط بيانات تصف المكونات وعلاقاتها.
- الـ componentMap: نبني فهرساً سريعاً للوصول لأي مكوّن بالـ
idالخاص به. - دالة
render: هي قلب الـ Renderer — تقرأ الـtypeوتحوّله لعنصر HTML حقيقي. كلtypeله mapping ثابت في تطبيقك. handleAction: عندما يضغط المستخدم على زر، يُرسل اسم الـ action للـ AI — وهو ما يجعل التفاعل ثنائي الاتجاه.
افتح هذا الكود في أي متصفح الآن وستشاهد form حجز كامل جاهز للاستخدام.
هذا الكود هو نموذج تعليمي (Mockup) ولا يحتوي على اتصال حقيقي أو “حي” بنموذج ذكاء اصطناعي حالياً. في التطبيقات الحقيقية، يتم استبدال هذا المتغير الثابت بطلب
fetchيذهب إلى خادم (Server) يتحدث مع نموذج مثل Gemini أو GPT، ثم يعيد هذه البيانات بصيغة JSON.
كيف يتم تحويل هذا الكود لاتصال حقيقي؟إذا أردت جعل هذا الكود يعمل مع ذكاء اصطناعي فعلي، ستحتاج للقيام بالآتي:
طلب البيانات: بدلاً من تعريف
a2uiPayloadيدوياًوسيط (Backend): إنشاء خادم (بواسطة Node.js أو Python مثلاً).
تخزين المفتاح: وضع مفتاح الـ API في الخادم وليس في هذا الملف.
نصائح ومزالق يجب تجنبها
✅ نصيحة 1 — ابدأ بـ Catalog صغير: لا تحاول تعريف 50 نوع من المكونات من البداية. ابدأ بـ: text, button, text-field, card — هذا يكفي لأغلب حالات الاستخدام في المراحل الأولى.
✅ نصيحة 2 — الـ ID يجب أن يكون فريداً دائماً: A2UI يعتمد على الـ id للتحديثات التدريجية (Incremental Updates). لو كررت ID، ستحصل على سلوك غير متوقع عند التحديث.
✅ نصيحة 3 — ثِق في الـ Flat Structure: قد يبدو غريباً أن تكون المكونات في قائمة مسطحة بدلاً من شجرة متداخلة — لكن هذا مقصود. الـ AI يستطيع تحديث مكوّن واحد بدون إعادة توليد كل الـ JSON.
❌ مزلق 1 — لا تثق في أي type غير معرّف في Catalog-ك: إذا وصلك type: "evil-script" من الـ AI، الـ Renderer الصحيح يتجاهله ببساطة. لا تضف fallback يعرض HTML مباشرة.
❌ مزلق 2 — لا تستخدم A2UI لمحتوى ثابت: الأداة مصممة للـ dynamic content الذي يتغير بناءً على سياق المحادثة. لو كل شيء في تطبيقك ثابت، فأنت تضيف تعقيداً بلا فائدة.
فيديو يوضح مثال تطبيقي لاستخدام بروتوكول A2UI
الخلاصة والرأي
A2UI فكرة ذكية وتوقيتها صحيح. مع ازدياد تطبيقات الـ AI Agents، الحاجة لبروتوكول موحّد يتيح لهذه الـ Agents التواصل مع الـ UI أصبحت ضرورة وليست رفاهية.
متى تستخدمه؟
- تبني chatbot أو AI assistant داخل تطبيق ويب أو Flutter
- تريد الـ AI يعيد بناء أجزاء من الـ UI ديناميكياً بناءً على المحادثة
- تحتاج نفس الـ agent response تُعرض على منصات متعددة
متى تتجنبه؟
- المشروع لا يحتوي أي AI component
- الفريق صغير وما عنده وقت لتعلم بروتوكول جديد
شخصياً، ما أجمل في A2UI هو مبدأ “آمن كالبيانات وتعبيري كالكود” — هذا التوازن نادر في أدوات الـ AI اليوم.
جرّب الكود الموجود في هذا المقال في متصفحك الآن، وعدّل الـ JSON payload لتضيف مكونات جديدة. هذه أسرع طريقة لتفهم كيف يفكر A2UI.




