أدوات Google لمساعدة العملاء في العمل: ADK وA2A وMCP على Google Cloud

1. ما ستتعلمه

مرحبًا سننطلق اليوم في رحلة رائعة. لنبدأ بالتفكير في منصة InstaVibe الشهيرة لتنظيم الفعاليات الاجتماعية. مع أنّ هذه الميزة ناجحة، نعلم أنّ بعض المستخدمين يجدون صعوبة في التخطيط للأنشطة الجماعية. تخيّل محاولة معرفة اهتمامات جميع أصدقائك، ثم البحث في خيارات لا حصر لها للأحداث أو الأماكن، وأخيرًا تنسيق كل شيء. هذا كثير! وهذا هو تحديدًا المكان الذي يمكننا فيه الاستعانة بالذكاء الاصطناعي، وتحديدًا الوكلاء الأذكياء، لإحداث فرق حقيقي.

الفكرة هي إنشاء نظام يمكن لهذه البرامج التعامل مع المهام الصعبة، مثل "الاستماع" بذكاء لفهم تفضيلات المستخدمين والأصدقاء، ثم اقتراح أنشطة رائعة ومخصّصة بشكل استباقي. هدفنا هو تحويل عملية التخطيط الاجتماعي على InstaVibe إلى تجربة سلسة وممتعة. لبدء إنشاء هذه المساعدات الذكية، علينا وضع أساس قوي باستخدام الأدوات المناسبة.

إليك المفهوم الذي سيظهر لك:

صفحة العنوان

الأساسيات باستخدام "مجموعة تطوير البرامج" (ADK) من Google: يمكنك إتقان أساسيات إنشاء أول وكيل ذكي باستخدام "مجموعة تطوير البرامج" (ADK) من Google. تعرَّف على المكوّنات الأساسية ودورة حياة الوكيل وكيفية الاستفادة من الأدوات المضمّنة في إطار العمل بشكل فعّال.

توسيع قدرات الوكلاء باستخدام بروتوكول سياق النموذج (MCP): تعرَّف على كيفية تزويد الوكلاء بأدوات وسياق مخصّصَين، ما يتيح لهم تنفيذ مهام متخصّصة والوصول إلى معلومات محدّدة. شرح مفهوم Model Context Protocol (MCP) ستتعرّف على كيفية إعداد خادم MCP لتوفير هذا السياق.

تصميم تفاعلات الوكيل وتنسيقها: يمكنك الانتقال إلى ما هو أبعد من الوكلاء الفرديين لفهم عملية تنسيق الوكلاء. تصميم أنماط تفاعلية تتراوح بين مهام سير العمل التسلسلية البسيطة والسيناريوهات المعقدة التي تتضمّن حلقات ومنطقًا شرطيًا ومعالجة متوازية تقديم مفهوم الوكلاء الفرعيين ضمن إطار عمل ADK لإدارة المهام النموذجية

إنشاء أنظمة تعاونية متعددة الوكلاء: تعرَّف على كيفية تصميم أنظمة يتعاون فيها عدّة وكلاء لتحقيق أهداف معقّدة. تعلَّم كيفية تنفيذ بروتوكول الاتصال بين الوكلاء (A2A)، ما يتيح طريقة موحّدة للوكلاء الموزّعين (الذين قد يعملون على أجهزة أو خدمات مختلفة) للتفاعل بشكل موثوق.

تفعيل الوكلاء في Google Cloud: يمكنك نقل تطبيقات الوكلاء من بيئات التطوير إلى السحابة الإلكترونية. تعرَّف على أفضل الممارسات لتصميم ونشر أنظمة متعددة الوكلاء قابلة للتوسيع وقوية على Google Cloud Platform (GCP). يمكنك الحصول على إحصاءات حول الاستفادة من خدمات Google Cloud Platform، مثل Cloud Run، واستكشاف إمكانات أحدث إصدار من Google Agent Engine لاستضافة وإدارة البرامج الآلية.

2. الهندسة المعمارية

تخطيط المحتوى على وسائل التواصل الاجتماعي باستخدام الذكاء الاصطناعي مع InstaVibe

ما هي ميزة "الاستماع الاجتماعي"؟

الاستماع الاجتماعي هو عملية مراقبة المحادثات الرقمية على مختلف المنصات، مثل وسائل التواصل الاجتماعي والمنتديات والمواقع الإخبارية، لفهم ما يقوله الأشخاص عن موضوع أو علامة تجارية أو مجال معيّن. وتوفّر هذه الأداة إحصاءات قيّمة حول آراء الجمهور والاتجاهات الرائجة واحتياجات المستخدمين. في ورشة العمل هذه، سنستفيد من هذا المفهوم ضمن نظام مستند إلى وكيل.

أنت في فريق InstaVibe

تخيَّل أنّك تعمل في شركة "InstaVibe" الناشئة الناجحة التي توفّر منصة رائجة لتنظيم الفعاليات الاجتماعية تستهدف الشباب. تسير الأمور على ما يرام، ولكن مثل العديد من شركات التكنولوجيا، يواجه فريقك ضغوطًا من المستثمرين للابتكار باستخدام الذكاء الاصطناعي. لاحظت أيضًا داخليًا شريحة من المستخدمين لا يتفاعلون بقدر الآخرين، ربما لأنّهم أقل ميلًا إلى بدء الأنشطة الجماعية أو يجدون عملية التخطيط صعبة. بالنسبة إلى شركتك، يعني ذلك انخفاض معدّل الاحتفاظ بالمستخدمين في هذه المجموعة المهمة.

تشير الأبحاث التي أجراها فريقك إلى أنّ المساعدة المستندة إلى الذكاء الاصطناعي يمكن أن تحسّن تجربة هؤلاء المستخدمين بشكل كبير. الفكرة هي تبسيط عملية التخطيط للأنشطة الاجتماعية من خلال اقتراح أنشطة ذات صلة بشكل استباقي استنادًا إلى اهتمامات المستخدم وأصدقائه. السؤال الذي يواجهك أنت وزملاءك هو: كيف يمكن لوكلاء الذكاء الاصطناعي تنفيذ المهام التي تستغرق وقتًا طويلاً، مثل استكشاف الاهتمامات والبحث عن الأنشطة والتنسيق الأوّلي المحتمل، بشكل آلي؟

حلّ مستند إلى وكيل (مفهوم أوّلي)

تقترح تطوير ميزة نموذجية تستند إلى نظام متعدد الوكلاء. في ما يلي توضيح للمفهوم:

حالة الاستخدام

  • وكيل إنشاء الملفات الشخصية على وسائل التواصل الاجتماعي: يستخدم هذا الوكيل تقنيات الاستماع الاجتماعي لتحليل اتصالات المستخدم وتفاعلاته والاتجاهات العامة الأوسع نطاقًا التي قد تكون مرتبطة بإعدادات المستخدم المفضّلة. الغرض منه هو تحديد الاهتمامات المشتركة وخصائص الأنشطة المناسبة (مثل تفضيلات التجمّعات الهادئة والهوايات المحدّدة).
  • وكيل تخطيط الأحداث: باستخدام الإحصاءات من "وكيل تحديد الملفات الشخصية على وسائل التواصل الاجتماعي"، يبحث هذا الوكيل في المراجع على الإنترنت عن أحداث أو أماكن أو أفكار معيّنة تتوافق مع المعايير المحدّدة (مثل الموقع الجغرافي والاهتمامات).
  • وكيل التفاعل مع المنصة (باستخدام MCP): يستلم هذا الوكيل الخطة النهائية من "وكيل التخطيط للأنشطة". وتتمثل وظيفتها الرئيسية في التفاعل مباشرةً مع منصة InstaVibe من خلال استخدام أداة MCP (بروتوكول سياق النموذج) محدّدة مسبقًا. توفّر هذه الأداة للوكيل إمكانية محدّدة تتمثّل في إعداد مسودة لاقتراح حدث وإنشاء مشاركة توضّح الخطة.
  • وكيل التنسيق: يعمل هذا الوكيل كمنسّق مركزي. يتلقّى الطلب الأوّلي من المستخدم من منصّة InstaVibe، ويفهم الهدف العام (مثل "خطِّط لفعالية لي ولأصدقائي")، ثمّ يفوّض مهام محدّدة إلى الوكلاء المتخصّصين المناسبين في تسلسل منطقي. يدير هذا النظام تدفّق المعلومات بين الوكلاء ويضمن تسليم النتيجة النهائية إلى المستخدم.

العناصر والتقنيات المعمارية الرئيسية

الهندسة المعمارية

‫Google Cloud Platform (GCP):

  • ‫Vertex AI:
    • نماذج Gemini: تتيح الوصول إلى أحدث النماذج اللغوية الكبيرة (LLM) من Google، مثل Gemini، التي تعزّز قدرات وكيلنا على التحليل واتخاذ القرارات.
    • ‫Vertex AI Agent Engine: خدمة مُدارة تُستخدَم لنشر واستضافة وتوسيع نطاق وكيل التنسيق، ما يسهّل عملية الإنتاج ويجرّد البنية الأساسية من التعقيدات.
  • ‫Cloud Run: منصة بدون خادم لنشر التطبيقات ضِمن الحاويات. نستخدمها من أجل:
    • استضافة تطبيق الويب الرئيسي InstaVibe
    • نشر وكلاء فرديين مفعَّلين من A2A (مثل "المخطِّط" و"إنشاء الملفات الشخصية على وسائل التواصل الاجتماعي" و"التفاعل مع المنصات") كخدمات مصغّرة مستقلة
    • تشغيل خادم أداة MCP، ما يتيح لوكلاء الدعم استخدام واجهات برمجة التطبيقات الداخلية في InstaVibe
  • ‫Spanner: قاعدة بيانات ارتباطية مُدارة بالكامل وموزّعة عالميًا ومتّسقة بشكل كبير. في ورشة العمل هذه، نستفيد من إمكاناتها كقاعدة بيانات رسومية باستخدام ميزات GRAPH DDL والاستعلام الخاصة بها لتنفيذ ما يلي:
    • نمذجة وتخزين العلاقات الاجتماعية المعقّدة (المستخدمون والصداقات وحضور الفعاليات والمشاركات)
    • تفعيل طلبات البحث الفعّالة عن هذه العلاقات لوكلاء "الملفات الشخصية على وسائل التواصل الاجتماعي"
  • ‫Artifact Registry: خدمة مُدارة بالكامل لتخزين صور الحاويات وإدارتها وتأمينها.
  • ‫Cloud Build: خدمة تنفّذ عمليات الإنشاء على Google Cloud. نستخدمه لإنشاء صور حاويات Docker تلقائيًا من الرمز المصدر للوكيل والتطبيق.
  • ‫Cloud Storage: تستخدمه خدمات مثل Cloud Build لتخزين نتائج الإنشاء، وتستخدمه Agent Engine لتلبية احتياجاتها التشغيلية.
  • أُطر وبروتوكولات الوكيل الأساسية:
    • مجموعة أدوات تطوير الوكيل (ADK) من Google: هي الإطار الأساسي لما يلي:
      • تحديد المنطق الأساسي والسلوك ومجموعات التعليمات للعناصر الذكية الفردية
      • إدارة دورات حياة الوكيل وحالته وذاكرته (حالة الجلسة القصيرة الأجل والمعرفة المحتملة الطويلة الأجل)
      • دمج أدوات (مثل "بحث Google" أو أدوات مخصّصة) يمكن للوكلاء استخدامها للتفاعل مع العالم
      • تنظيم سير عمل يتضمّن عدة وكلاء، بما في ذلك التنفيذ التسلسلي والحلقي والمتوازي للوكلاء الفرعيين
    • بروتوكول الاتصال بين الوكلاء (A2A): معيار مفتوح يتيح ما يلي:
      • التواصل والتعاون المباشرين والموحّدين بين وكلاء الذكاء الاصطناعي المختلفين، حتى إذا كانوا يعملون كخدمات منفصلة أو على أجهزة مختلفة
      • يمكن للوكلاء التعرّف على إمكانات بعضهم البعض (من خلال "بطاقات الوكيل") وتفويض المهام. وهذا أمر بالغ الأهمية ليتفاعل وكيل Orchestrator مع وكلاء Planner وSocial وPlatform المتخصّصين.
    • مكتبة A2A Python (a2a-python): هي المكتبة المحدّدة المستخدَمة لجعل وكلاء ADK يتحدثون بروتوكول A2A. توفّر هذه الخدمة المكوّنات من جهة الخادم اللازمة لإجراء ما يلي:
      • عرض وكلائنا كخوادم متوافقة مع A2A
      • التعامل تلقائيًا مع عرض "بطاقة الوكيل" عند البحث عن وكيل
      • تلقّي طلبات المهام الواردة وإدارتها من وكلاء آخرين (مثل Orchestrator)
    • بروتوكول Model Context (MCP): معيار مفتوح يتيح للوكلاء ما يلي:
      • التواصل مع الأدوات ومصادر البيانات والأنظمة الخارجية واستخدامها بطريقة موحّدة
      • يستخدم "وكيل التفاعل مع المنصة" برنامج MCP للعميل للتواصل مع خادم MCP، الذي يعرض بدوره أدوات للتفاعل مع واجهات برمجة التطبيقات الحالية لمنصة InstaVibe.
  • أدوات تصحيح الأخطاء:
    • أداة A2A Inspector: هي أداة لتصحيح الأخطاء مستندة إلى الويب تُستخدَم في جميع مراحل ورشة العمل هذه للاتصال بوكلائنا المتوافقين مع A2A وفحصهم والتفاعل معهم. على الرغم من أنّها ليست جزءًا من بنية الإنتاج النهائية، إلا أنّها جزء أساسي من سير عمل التطوير. يوفّر هذا المكان:
      • أداة عرض بطاقة الوكيل: لاسترداد قدرات الوكيل العلنية والتحقّق من صحتها
      • واجهة المحادثة المباشرة: لإرسال رسائل مباشرةً إلى وكيل تم نشره لإجراء اختبار فوري
      • Debug Console: لعرض رسائل JSON-RPC الأولية التي يتم تبادلها بين أداة الفحص والوكيل.
  • النماذج اللغوية الكبيرة (LLM): "العقول" التي تدير النظام:
    • نماذج Gemini من Google: على وجه التحديد، نستخدم إصدارات مثل gemini-2.0-flash. يتم اختيار هذه النماذج للأسباب التالية:
      • التحليل المتقدّم واتّباع التعليمات: إنّ قدرة هذه النماذج على فهم الطلبات المعقّدة واتّباع التعليمات التفصيلية والتفكير المنطقي في المهام تجعلها مناسبة لتعزيز عملية اتّخاذ القرارات من قِبل الوكيل.
      • استخدام الأدوات (استدعاء الدوال): تتفوّق نماذج Gemini في تحديد وقت وكيفية استخدام الأدوات المتوفّرة من خلال ADK، ما يتيح للوكلاء جمع المعلومات أو تنفيذ الإجراءات.
      • الكفاءة (نماذج Flash): تقدّم متغيرات "Flash" توازنًا جيدًا بين الأداء وفعالية التكلفة، وهي مناسبة للعديد من مهام الوكيل التفاعلية التي تتطلّب ردودًا سريعة.

هل تحتاج إلى أرصدة Google Cloud؟

3- قبل البدء

👉انقر على تفعيل Cloud Shell في أعلى "وحدة تحكّم Google Cloud" (رمز شكل الوحدة الطرفية في أعلى لوحة Cloud Shell)، Cloud Shell

👉انقر على الزر "فتح المحرّر" (يبدو كملف مفتوح مع قلم رصاص). سيؤدي ذلك إلى فتح "محرِّر Cloud Shell" في النافذة. سيظهر لك مستكشف الملفات على الجانب الأيمن. Cloud Shell

👉انقر على الزر تسجيل الدخول باستخدام رمز السحابة الإلكترونية في شريط الحالة أسفل الصفحة كما هو موضّح. امنح المصادقة للمكوّن الإضافي حسب التعليمات. إذا ظهرت لك الرسالة Cloud Code - no project في شريط الحالة، انقر عليها ثم انقر على "اختيار مشروع Google Cloud" في القائمة المنسدلة، ثم اختَر مشروع Google Cloud المحدّد من قائمة المشاريع التي أنشأتها. Cloud Shell

👉 للعثور على معرّف مشروع Google Cloud، اتّبِع الخطوات التالية:

  • افتح Google Cloud Console: https://console.cloud.google.com
  • اختَر المشروع الذي تريد استخدامه في ورشة العمل هذه من القائمة المنسدلة للمشروع في أعلى الصفحة.
  • يظهر رقم تعريف مشروعك في بطاقة "معلومات المشروع" على "لوحة البيانات".

Cloud Shell

👉افتح نافذة المحطة الطرفية في بيئة التطوير المتكاملة المستندة إلى السحابة الإلكترونية، Cloud Shell

👉💻 في نافذة الوحدة الطرفية، تأكَّد من أنّك قد أثبتّ هويتك وأنّ المشروع مضبوط على رقم تعريف مشروعك باستخدام الأمر التالي:

gcloud auth list

👉💻 استنسِخ مشروع instavibe-bootstrap من GitHub:

git clone -b adk-1.2.1-a2a-0.2.7 https://github.com/weimeilin79/instavibe-bootstrap.git
chmod +x ~/instavibe-bootstrap/init.sh
chmod +x ~/instavibe-bootstrap/set_env.sh

التعرّف على بنية المشروع

قبل أن نبدأ في إنشاء التطبيق، لنستغرق بعض الوقت لفهم تخطيط مشروع instavibe-bootstrap الذي استنسخته للتو. سيساعدك ذلك في معرفة مكان العثور على الملفات وتعديلها طوال ورشة العمل.

instavibe-bootstrap/
├── agents/
   ├── orchestrate/
   ├── planner/
   ├── platform_mcp_client/
   └── social/
├── instavibe/
   ├── static/
   └── templates/
├── tools/
   └── instavibe/
├── utils/
├── init.sh
└── set_env.sh

في ما يلي تفاصيل الدلائل الرئيسية:

  • agents/: هذا هو جوهر نظام الذكاء الاصطناعي. يحتوي كل دليل فرعي (planner/‎ وsocial/‎ وما إلى ذلك) على الرمز المصدري لعامل ذكي معيّن.
    • agent.py: داخل مجلد كل وكيل، هذا هو الملف الرئيسي الذي يتضمّن منطق الوكيل.
    • a2a_server.py: يغلّف هذا الملفّ وكيل ADK بخادم من نوع "وكيل إلى وكيل" (A2A).
    • Dockerfile: تحدّد هذه السمة كيفية إنشاء صورة الحاوية لنشر الوكيل على Cloud Run أو Agent Engine.
  • instavibe/: يحتوي هذا الدليل على رمز المصدر الكامل لتطبيق InstaVibe على الويب.
  • tools/: هذا الدليل مخصّص لإنشاء أدوات خارجية يمكن أن يستخدمها وكلاؤنا.
    • يحتوي instavibe/ على خادم Model Context Protocol (MCP).

يفصل هذا التصميم المعياري تطبيق الويب عن مكوّنات الذكاء الاصطناعي المختلفة، ما يسهّل إدارة النظام بأكمله واختباره ونشره.

👉💻 شغِّل نص الإعداد:

سيطلب منك هذا النص البرمجي إدخال معرّف مشروع Google Cloud.

أدخِل رقم تعريف مشروع Google Cloud الذي عثرت عليه في الخطوة الأخيرة عندما يطلب منك ذلك النص البرمجي init.sh:

cd ~/instavibe-bootstrap
./init.sh

👉💻 اضبط رقم تعريف المشروع المطلوب:

gcloud config set project $(cat ~/project_id.txt) --quiet

👉💻 شغِّل الأمر التالي لتفعيل واجهات Google Cloud APIs اللازمة:

gcloud services enable  run.googleapis.com \
                        cloudfunctions.googleapis.com \
                        cloudbuild.googleapis.com \
                        artifactregistry.googleapis.com \
                        spanner.googleapis.com \
                        apikeys.googleapis.com \
                        iam.googleapis.com \
                        compute.googleapis.com \
                        aiplatform.googleapis.com \
                        cloudresourcemanager.googleapis.com \
                        maps-backend.googleapis.com

👉💻 اضبط جميع متغيرات البيئة المطلوبة:

export PROJECT_ID=$(gcloud config get project)
export PROJECT_NUMBER=$(gcloud projects describe ${PROJECT_ID} --format="value(projectNumber)")
export SERVICE_ACCOUNT_NAME=$(gcloud compute project-info describe --format="value(defaultServiceAccount)")
export SPANNER_INSTANCE_ID="instavibe-graph-instance"
export SPANNER_DATABASE_ID="graphdb"
export GOOGLE_CLOUD_PROJECT=$(gcloud config get project)
export GOOGLE_GENAI_USE_VERTEXAI=TRUE
export GOOGLE_CLOUD_LOCATION="us-central1"

إعداد الإذن

👉💻 منح الأذونات في الوحدة الطرفية، شغِّل الأمر التالي :

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/spanner.admin"

# Spanner Database User
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/spanner.databaseUser"

# Artifact Registry Admin
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/artifactregistry.admin"

# Cloud Build Editor
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/cloudbuild.builds.editor"

# Cloud Run Admin
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/run.admin"

# IAM Service Account User
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/iam.serviceAccountUser"

# Vertex AI User
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/aiplatform.user"

# Logging Writer (to allow writing logs)
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/logging.logWriter"


gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/logging.viewer"


👉 التحقّق من النتيجة في وحدة تحكّم إدارة الهوية وإمكانية الوصولCloud Shell

👉💻 نفِّذ الأوامر التالية في الوحدة الطرفية لإنشاء مستودع Artifact Registry. يتم تخزين جميع صور Docker الخاصة بوكلائنا وخادم MCP وتطبيق InstaVibe هنا قبل نشرها على Cloud Run أو Agent Engine.

export REPO_NAME="introveally-repo"
gcloud artifacts repositories create $REPO_NAME \
  --repository-format=docker \
  --location=us-central1 \
  --description="Docker repository for InstaVibe workshop"

إعداد منصة "خرائط Google" لمفاتيح واجهة برمجة التطبيقات

لاستخدام خدمات "خرائط Google" في تطبيق InstaVibe، عليك إنشاء مفتاح API وتقييده بشكل مناسب.

👉 في علامة تبويب جديدة، انتقِل إلى واجهات برمجة التطبيقات والخدمات > بيانات الاعتماد. في صفحة "بيانات الاعتماد"، انقر على الزرّ + إنشاء بيانات اعتماد في أعلى الصفحة. اختَر مفتاح واجهة برمجة التطبيقات من القائمة المنسدلة. النص البديل

👉 سيظهر مربّع حوار يعرض مفتاح واجهة برمجة التطبيقات الذي أنشأته حديثًا. ستحتاج إليه لاحقًا لإعداد تطبيقك.

👉 انقر على "إغلاق" في مربّع الحوار "تم إنشاء مفتاح واجهة برمجة التطبيقات".

👈 سيظهر مفتاح واجهة برمجة التطبيقات الجديد في القائمة (مثلاً، "مفتاح واجهة برمجة التطبيقات 1"). انقر على النقاط الثلاث على يسار الشاشة، ثم اختَر تعديل مفتاح واجهة برمجة التطبيقات لفتح صفحة "تقييد مفتاح واجهة برمجة التطبيقات وإعادة تسميته". النص البديل

👉 في حقل "الاسم" في أعلى الصفحة، غيِّر الاسم التلقائي إلى: مفتاح واجهة برمجة التطبيقات في "منصة خرائط Google" (🚨🚨مهم جدًا🚨🚨 يُرجى استخدام هذا الاسم).

Maps Platform API Key

👉 ضِمن قسم "قيود التطبيق"، تأكَّد من اختيار بلا قيود.

👉 ضمن قسم "قيود واجهة برمجة التطبيقات"، انقر على زرّ الاختيار "تقييد المفتاح".

👉 انقر على القائمة المنسدلة "اختيار واجهات برمجة التطبيقات". في مربّع البحث الذي يظهر، اكتب Maps JavaScript API واختَره من القائمة. النص البديل

👉 انقر على "موافق".

👉 انقر على الزر "حفظ" في أسفل الصفحة.

النتيجة الرئيسية

لقد أنشأت الآن مفتاح واجهة برمجة تطبيقات بنجاح باسم "مفتاح واجهة برمجة تطبيقات Google Maps Platform"، وقصرت استخدامه على "واجهة برمجة تطبيقات JavaScript في خرائط Google" فقط، وتأكّدت من تفعيل واجهة برمجة التطبيقات لمشروعك.

4. إعداد قاعدة بيانات الرسومات البيانية

قبل أن نتمكّن من إنشاء وكلاء أذكياء، نحتاج إلى طريقة لتخزين وفهم الروابط الغنية داخل شبكتنا الاجتماعية InstaVibe. وهنا يأتي دور قاعدة بيانات الرسوم البيانية. على عكس قواعد البيانات العلائقية التقليدية التي تخزّن البيانات في جداول من الصفوف والأعمدة، تم تصميم قاعدة بيانات الرسومات البيانية خصيصًا لتمثيل البيانات والاستعلام عنها من حيث العُقد (مثل الأشخاص أو الأحداث أو المشاركات) والعلاقات (الحواف) التي تربطها (مثل الصداقات أو حضور الأحداث أو الإشارات). هذه البنية فعّالة للغاية لتطبيقات وسائل التواصل الاجتماعي لأنّها تحاكي طريقة تنظيم الشبكات الاجتماعية في العالم الحقيقي، ما يجعل من السهل استكشاف كيفية ترابط الكيانات المختلفة.

نحن بصدد تنفيذ قاعدة بيانات الرسومات هذه باستخدام Google Cloud Spanner. على الرغم من أنّ Spanner معروفة في المقام الأول بأنّها قاعدة بيانات ارتباطية موزّعة عالميًا ومتّسقة بشكل كبير، إلا أنّها تتيح لنا أيضًا تحديد بنى الرسومات وطلب البحث عنها مباشرةً فوق جداولنا الارتباطية.

يمنحنا ذلك المزايا المجمّعة لقابلية التوسّع في Spanner، واتساق المعاملات، وواجهة SQL المألوفة، بالإضافة إلى القدرة التعبيرية لاستعلامات الرسم البياني لتحليل الديناميكيات الاجتماعية المعقّدة الضرورية لميزاتنا المستندة إلى الذكاء الاصطناعي.

👉💻 في نافذة Cloud Shell IDE. توفير البنية الأساسية اللازمة على Google Cloud سنبدأ بإنشاء مثيل Spanner، وهو يعمل كحاوية مخصّصة لقواعد البيانات. بعد أن يصبح الجهاز الظاهري جاهزًا، سننشئ قاعدة بيانات Spanner الفعلية داخله، والتي ستضم جميع جداولنا وبيانات الرسم البياني الخاصة بتطبيق InstaVibe:

. ~/instavibe-bootstrap/set_env.sh

gcloud spanner instances create $SPANNER_INSTANCE_ID \
  --config=regional-us-central1 \
  --description="GraphDB Instance InstaVibe" \
  --processing-units=100 \
  --edition=ENTERPRISE

gcloud spanner databases create $SPANNER_DATABASE_ID \
  --instance=$SPANNER_INSTANCE_ID \
  --database-dialect=GOOGLE_STANDARD_SQL

👉💻 منح Spanner إذن القراءة/الكتابة لحساب الخدمة التلقائي

echo "Granting Spanner read/write access to ${SERVICE_ACCOUNT_NAME} for database ${SPANNER_DATABASE_ID}..."

gcloud spanner databases add-iam-policy-binding ${SPANNER_DATABASE_ID} \
  --instance=${SPANNER_INSTANCE_ID} \
  --member="serviceAccount:${SERVICE_ACCOUNT_NAME}" \
  --role="roles/spanner.databaseUser" \
  --project=${PROJECT_ID}

👉💻 الآن. سنقوم بإعداد بيئة افتراضية في Python، وتثبيت حِزم Python المطلوبة، ثم إعداد مخطط قاعدة بيانات الرسوم البيانية في Spanner وتحميله بالبيانات الأولية وتشغيل النص البرمجي setup.py.

. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap
python -m venv env
source env/bin/activate
pip install -r requirements.txt
cd instavibe
python setup.py

👉 في علامة تبويب متصفّح جديدة، انتقِل إلى Google Cloud Console، ثم إلى Spanner، وستظهر لك قائمة بمثيلات Spanner. انقر على instavibe-graph-instance. مثيل Spanner 👉 في صفحة النظرة العامة على الجهاز الافتراضي، ستظهر لك قائمة بقواعد البيانات ضِمن هذا الجهاز. انقر على graphdbقاعدة بيانات Spanner.

👉 في لوحة التنقّل اليمنى لقاعدة البيانات، انقر على Spanner Studio spanner studio.

👉 في محرّر طلب البحث (علامة التبويب "طلب بحث بدون عنوان")، الصِق طلب بحث Graph SQL التالي. سيؤدي طلب البحث هذا إلى العثور على جميع عُقد "الشخص" وعلاقات "الصداقة" المباشرة مع عُقد "الشخص" الأخرى. انقر على تشغيل للاطّلاع على النتيجة.

Graph SocialGraph
MATCH result_paths = ((p:Person)-[f:Friendship]-(friend:Person))
RETURN SAFE_TO_JSON(result_paths) AS result_paths

spanner graph

👉 في محرّر طلب البحث نفسه، استبدِل DDL السابق للعثور على المستخدمين الذين حضروا الحدث نفسه، ما يشير إلى اتصال غير مباشر من خلال نشاط مشترك.

Graph SocialGraph
MATCH result_paths =  (p1:Person)-[:Attended]->(e:Event)<-[:Attended]-(p2:Person)
WHERE p1.person_id < p2.person_id
RETURN SAFE_TO_JSON(result_paths) AS result_paths

spanner graph

👉 يستكشف هذا الطلب نوعًا مختلفًا من الروابط، حيث يذكر الأشخاص في المشاركات التي كتبها أصدقاء شخص معيّن، ويتم تشغيل الطلب التالي في محرّر الطلبات.

Graph SocialGraph
MATCH result_paths =  (user:Person {name: "Alice"})-[:Friendship]-(friend:Person)-[:Wrote]->(post:Post)-[:Mentioned]->(mentioned_person:Person)
WHERE user <> mentioned_person AND friend <> mentioned_person -- Avoid self-mentions or friend mentioning themselves in their own post if not intended
RETURN SAFE_TO_JSON(result_paths) AS result_paths

spanner graph

تقدّم هذه الطلبات لمحة بسيطة عن فعالية استخدام Spanner كقاعدة بيانات رسومية لتطبيق InstaVibe. من خلال تصميم بياناتنا الاجتماعية على شكل رسم بياني مترابط، نتيح إجراء تحليل متطوّر للعلاقات والأنشطة، وهو ما سيكون أساسيًا لكي تفهم وكالات الذكاء الاصطناعي سياق المستخدم وتستكشف اهتماماته وتقدّم في النهاية مساعدة ذكية في التخطيط الاجتماعي.

بعد أن أصبح لدينا بنية البيانات الأساسية جاهزة وتم اختبارها، لننتقل إلى تطبيق InstaVibe الحالي الذي سيتفاعل معه موظفو الدعم.

5- الحالة الحالية لتطبيق InstaVibe

لفهم الدور الذي ستؤديه وكلاء الذكاء الاصطناعي، علينا أولاً نشر تطبيق InstaVibe على الويب وتشغيله. يوفر هذا التطبيق واجهة المستخدم والوظائف الأساسية التي تتصل بقاعدة بيانات الرسوم البيانية في Spanner التي سبق أن أعددناها.

الصفحة الرئيسية

يستخدم تطبيق InstaVibe "خرائط Google" لعرض مواقع الأحداث بشكل مرئي على صفحات تفاصيل الأحداث. لتفعيل هذه الوظيفة، يحتاج التطبيق إلى مفتاح واجهة برمجة التطبيقات الذي أنشأناه سابقًا. سيسترد النص البرمجي التالي سلسلة المفتاح الفعلية باستخدام الاسم المعروض الذي حدّدناه ("مفتاح واجهة برمجة التطبيقات لمنصة "خرائط Google"").

صفحة الحدث

👉💻 العودة إلى بيئة التطوير المتكاملة في Cloud Shell نفِّذ النص البرمجي أدناه. بعد ذلك، تحقَّق بعناية من الناتج للتأكّد من أنّ مفتاح GOOGLE_MAPS_API_KEY المعروض يتطابق مع المفتاح الذي أنشأته ونسخته من Google Cloud Console سابقًا.

. ~/instavibe-bootstrap/set_env.sh
export KEY_DISPLAY_NAME="Maps Platform API Key"

GOOGLE_MAPS_KEY_ID=$(gcloud services api-keys list \
  --project="${PROJECT_ID}" \
  --filter="displayName='${KEY_DISPLAY_NAME}'" \
  --format="value(uid)" \
  --limit=1)

GOOGLE_MAPS_API_KEY=$(gcloud services api-keys get-key-string "${GOOGLE_MAPS_KEY_ID}" \
    --project="${PROJECT_ID}" \
    --format="value(keyString)")

echo "${GOOGLE_MAPS_API_KEY}" > ~/mapkey.txt

echo "Retrieved GOOGLE_MAPS_API_KEY: ${GOOGLE_MAPS_API_KEY}"

النتيجة الرئيسية

👉💻 لننشئ الآن صورة الحاوية لتطبيق InstaVibe على الويب ونحمّلها إلى مستودع Artifact Registry.

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/instavibe/
export IMAGE_TAG="latest"
export APP_FOLDER_NAME="instavibe"
export IMAGE_NAME="instavibe-webapp"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="instavibe"

gcloud builds submit . \
  --tag=${IMAGE_PATH} \
  --project=${PROJECT_ID}

👉💻 نشر صورة تطبيق InstaVibe الجديد على الويب في Cloud Run

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/instavibe/
export IMAGE_TAG="latest"
export APP_FOLDER_NAME="instavibe"
export IMAGE_NAME="instavibe-webapp"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="instavibe"

gcloud run deploy ${SERVICE_NAME} \
  --image=${IMAGE_PATH} \
  --platform=managed \
  --region=${REGION} \
  --allow-unauthenticated \
  --set-env-vars="SPANNER_INSTANCE_ID=${SPANNER_INSTANCE_ID}" \
  --set-env-vars="SPANNER_DATABASE_ID=${SPANNER_DATABASE_ID}" \
  --set-env-vars="APP_HOST=0.0.0.0" \
  --set-env-vars="APP_PORT=8080" \
  --set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
  --set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
  --set-env-vars="GOOGLE_MAPS_API_KEY=${GOOGLE_MAPS_API_KEY}" \
  --project=${PROJECT_ID} \
  --min-instances=1

بعد اكتمال عملية النشر بنجاح، من المفترض أن تعرض سجلّات Cloud Run عنوان URL العام لتطبيق InstaVibe الذي يتم تشغيله.

عنوان URL

يمكنك أيضًا العثور على عنوان URL هذا من خلال الانتقال إلى قسم Cloud Run في Google Cloud Console واختيار خدمة instavibe. قائمةعنوان URL

يمكنك الآن فتح عنوان URL هذا في متصفّح الويب لاستكشاف منصة InstaVibe الأساسية. يمكنك الاطّلاع على المشاركات والأحداث وعلاقات المستخدمين التي توفّرها قاعدة بيانات الرسوم البيانية التي أعددناها.

بعد تشغيل التطبيق المستهدف، لنبدأ بإنشاء أول وكيل ذكي لتحسين إمكاناته.

6. Basic Agent,Event Planner with ADK

إطار عمل ADK

مقدمة عن إطار عمل ADK من Google بعد أن أصبحت البنية الأساسية (تطبيق InstaVibe وقاعدة البيانات) جاهزة، يمكننا البدء في إنشاء أول وكيل ذكي باستخدام حزمة تطوير الوكلاء (ADK) من Google.

حزمة تطوير البرامج (ADK) الخاصة بالوكلاء هي إطار عمل مرن ونمطي مصمَّم خصيصًا لتطوير وكلاء الذكاء الاصطناعي ونشرهم. ويتمثّل مبدأ تصميمها في جعل عملية تطوير الوكيل تبدو أشبه بعملية تطوير البرامج التقليدية، وذلك بهدف تسهيل عملية إنشاء البُنى المستندة إلى الوكلاء ونشرها وتنظيمها بشكل كبير، ما يتيح التعامل مع كل شيء، بدءًا من المهام البسيطة ذات الغرض الواحد وصولاً إلى مهام سير العمل المعقّدة المتعددة الوكلاء.

في جوهرها، تدور حزمة تطوير التطبيقات حول مفهوم Agent، الذي يتضمّن التعليمات والإعدادات (مثل نموذج اللغة المختار، على سبيل المثال Gemini)، ومجموعة من Tools يمكنه استخدامها لتنفيذ إجراءات أو جمع معلومات.

06-agent.png

سيكون الوكيل الأوّلي هو "مخطِّط الأحداث". الغرض الأساسي منه هو تلقّي طلبات المستخدمين بشأن النزهات الاجتماعية (تحديد الموقع الجغرافي والتواريخ والاهتمامات) وتقديم اقتراحات إبداعية ومخصّصة. لضمان أن تكون الاقتراحات ذات صلة بالموضوع وتستند إلى معلومات حالية (مثل أحداث معيّنة تحدث في نهاية الأسبوع)، سنستفيد من إحدى الأدوات المضمّنة في ADK، وهي بحث Google. يتيح ذلك للوكيل استناد ردوده إلى نتائج الويب في الوقت الفعلي، واسترجاع أحدث التفاصيل حول الأماكن والأحداث والأنشطة التي تتطابق مع معايير المستخدم.

👉📝 في بيئة التطوير المتكاملة (IDE) في Cloud Shell، أضِف الطلب والتعليمات التالية في ~/instavibe-bootstrap/agents/planner/agent.py لإنشاء الوكيل

from google.adk.agents import Agent
from google.adk.tools import google_search

root_agent = Agent(
    name="planner_agent",
    model="gemini-2.0-flash",
    description="Agent tasked with generating creative and fun dating plan suggestions",
    instruction="""

        You are a specialized AI assistant tasked with generating creative and fun plan suggestions.

        Request:
        For the upcoming weekend, specifically from **[START_DATE_YYYY-MM-DD]** to **[END_DATE_YYYY-MM-DD]**, in the location specified as **[TARGET_LOCATION_NAME_OR_CITY_STATE]** (if latitude/longitude are provided, use these: Lat: **[TARGET_LATITUDE]**, Lon: **[TARGET_LONGITUDE]**), please generate a distinct dating plan suggestions.

        Constraints and Guidelines for Suggestions:
        1.  Creativity & Fun: Plans should be engaging, memorable, and offer a good experience for a date.
        2.  Budget: All generated plans should aim for a moderate budget (conceptually "$$"), meaning they should be affordable yet offer good value, without being overly cheap or extravagant. This budget level should be *reflected in the choice of activities and venues*, but **do not** explicitly state "Budget: $$" in the `plan_description`.
        3.  Interest Alignment:
               Consider the following user interests: **[COMMA_SEPARATED_LIST_OF_INTERESTS, e.g., outdoors, arts & culture, foodie, nightlife, unique local events, live music, active/sports]**. Tailor suggestions specifically to these where possible. The plan should *embody* these interests.
               Fallback: If specific events or venues perfectly matching all listed user interests cannot be found for the specified weekend, you should create a creative and fun generic dating plan that is still appealing, suitable for the location, and adheres to the moderate budget. This plan should still sound exciting and fun, even if it's more general.
        4.  Current & Specific: Prioritize finding specific, current events, festivals, pop-ups, or unique local venues operating or happening during the specified weekend dates. If exact current events cannot be found, suggest appealing evergreen options or implement the fallback generic plan.
        5.  Location Details: For each place or event mentioned within a plan, you MUST provide its name, precise latitude, precise longitude, and a brief, helpful description.
        6.  Maximum Activities: The plan must contain a maximum of 3 distinct activities.

        RETURN PLAN in MARKDOWN FORMAT 
    """,
    tools=[google_search]
)

وهكذا نكون قد حدّدنا أول وكيل لنا. من الميزات الرائعة في "حزمة تطوير التطبيقات" طبيعتها السهلة والأدوات المفيدة التي توفّرها. إحدى الأدوات المفيدة بشكل خاص هي واجهة مستخدم مطوّري ADK التي تتيح لك اختبار وكيلك بشكل تفاعلي والاطّلاع على ردوده في الوقت الفعلي.

👉💻 لِنبدأ. ستؤدي الأوامر التالية إلى تشغيل واجهة مستخدم ADK DEV:

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd  ~/instavibe-bootstrap/agents
sed -i "s|^\(O\?GOOGLE_CLOUD_PROJECT\)=.*|GOOGLE_CLOUD_PROJECT=${PROJECT_ID}|" ~/instavibe-bootstrap/agents/planner/.env
adk web

بعد تنفيذ الأوامر، من المفترض أن تظهر لك نتيجة في نافذة الأوامر تشير إلى أنّ خادم الويب الخاص بـ ADK قد بدأ، على النحو التالي:

+-----------------------------------------------------------------------------+
| ADK Web Server started                                                      |
|                                                                             |
| For local testing, access at http://localhost:8000.                         |
+-----------------------------------------------------------------------------+

INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)

👉 بعد ذلك، للوصول إلى واجهة مستخدم "حزمة تطوير التطبيقات" من المتصفّح، اتّبِع الخطوات التالية:

من رمز معاينة الويب (غالبًا ما يبدو كعين أو مربّع يحتوي على سهم) في شريط أدوات Cloud Shell (عادةً في أعلى يسار الصفحة)، اختَر تغيير المنفذ. في النافذة المنبثقة، اضبط المنفذ على 8000 وانقر على "تغيير ومعاينة". سيفتح Cloud Shell بعد ذلك علامة تبويب أو نافذة متصفّح جديدة تعرض واجهة مستخدم ADK Dev.

معاينة الويب

بعد فتح واجهة مستخدم ADK Dev في المتصفّح: في القائمة المنسدلة أعلى يسار واجهة المستخدم، اختَر planner كوكيل تريد التفاعل معه. الآن، في مربّع حوار الدردشة على اليسار، جرِّب إعطاء وكيلك مهمة. على سبيل المثال، يمكنك إجراء محادثة مع الموظف:

Search and plan something in Seattle for me this weekend
This weekend and I enjoy food and anime

اقتراح تاريخ (تاريخك المفضّل)

July 12 2025

من المفترض أن ترى الوكيل يعالج طلبك ويقدّم خطة استنادًا إلى نتائج "بحث Google".

adk dev ui

التفاعل مع وكيل هو أمر، ولكن كيف نعرف ما إذا كان يتصرف باستمرار على النحو المتوقع، خاصةً عندما نجري تغييرات؟

غالبًا ما تعجز طرق اختبار البرامج التقليدية عن تحقيق ذلك مع وكلاء الذكاء الاصطناعي بسبب طبيعتهم التوليدية وغير الحتمية. لسدّ الفجوة بين العرض التوضيحي الرائع والوكيل العملي الموثوق به، من الضروري وضع استراتيجية تقييم متينة. على عكس مجرد التحقّق من الناتج النهائي لنموذج توليدي، غالبًا ما تتضمّن عملية تقييم الوكيل تقييم عملية اتّخاذ القرار لديه وقدرته على استخدام الأدوات بشكل صحيح أو اتّباع التعليمات في سيناريوهات مختلفة. توفّر حزمة تطوير التطبيقات (ADK) ميزات للمساعدة في ذلك.

Eval

👉 في واجهة مستخدم "مجموعة أدوات تطوير التطبيقات"، انقر على علامة التبويب "التقييم" (Eval) في قائمة التنقّل اليمنى. من المفترض أن يظهر لك ملف اختبار مُحمَّل مسبقًا باسم plan_eval. يحتوي هذا الملف على مدخلات ومعايير محدّدة مسبقًا لاختبار وكيل المخطّط.

👉 اختَر سيناريو، مثل "بوسطن"، وانقر على الزر بدء التقييم. في النافذة المنبثقة التي تظهر، خفِّض درجة التطابق إلى 0.3 وانقر على "بدء".

نتيجة المطابقة

سيؤدي ذلك إلى تنفيذ الوكيل باستخدام بيانات الاختبار والتحقّق مما إذا كانت النتائج تستوفي التوقعات المحدّدة. يمنحك ذلك طريقة لاختبار أداء برنامجك الآلي بشكل منهجي.

adk dev ui evaluation

👉 لنرَ الآن ما سيحدث مع حدّ أدنى أكثر صرامة. اختَر سيناريو "nyc" وانقر على بدء التقييم مرة أخرى. في هذه المرة، اترُك درجة التطابق على قيمتها التلقائية (درجة تطابق الرد: 0.7) وانقر على "بدء". ستلاحظ أنّ النتيجة هي "تعذُّر". وهذا أمر متوقّع، لأنّ الناتج الإبداعي للوكيل لا يتطابق تمامًا مع الإجابة "المثالية" المحدّدة مسبقًا.

adk dev ui evaluation fail

👉 لمعرفة سبب تعذُّر ذلك، انقر على رمز التعذُّر في صف "nyc". تعرض واجهة المستخدم الآن مقارنة جنبًا إلى جنب بين "الرد الفعلي" من الوكيل و"الرد المتوقّع" من حالة الاختبار. تُعدّ طريقة العرض هذه ضرورية لتصحيح الأخطاء، ما يتيح لك معرفة المكان الذي اختلف فيه الناتج الذي قدّمه الوكيل بالضبط وتحسين تعليماته وفقًا لذلك.

بعد الانتهاء من استكشاف واجهة المستخدم والتقييم، ارجع إلى نافذة محرِّر Cloud Shell واضغط على Ctrl+C لإيقاف واجهة مستخدم ADK Dev.

مع أنّ إخراج النص الحرّ هو بداية جيدة، إلا أنّ البيانات المنظَّمة (مثل JSON) تكون أكثر عملية بكثير بالنسبة إلى تطبيقات مثل InstaVibe التي تستخدم اقتراحات الوكيل بسهولة. لنعدّل الآن الوكيل لعرض خطته بتنسيق JSON متسق.

👉📝 في ~/instavibe-bootstrap/agents/planner/agent.py، ابحث عن السطر الذي يتضمّن حاليًا RETURN PLAN in MARKDOWN FORMAT ضمن سلسلة تعليمات الوكيل. استبدِل هذا السطر ببنية JSON التفصيلية التالية:

Return your response *exclusively* as a single JSON object. This object should contain a top-level key, "fun_plans", which holds a plan objects. Each plan object in the list must strictly adhere to the following structure:

        --json--
        {
          "plan_description": "A summary of the overall plan, consisting of **exactly three sentences**. Craft these sentences in a friendly, enthusiastic, and conversational tone, as if you're suggesting this awesome idea to a close friend. Make it sound exciting and personal, highlighting the positive aspects and appeal of the plan without explicitly mentioning budget or listing interest categories.",
          "locations_and_activities": [
              {
              "name": "Name of the specific place or event",
              "latitude": 0.000000,  // Replace with actual latitude
              "longitude": 0.000000, // Replace with actual longitude
              "description": "A brief description of this place/event, why it's suitable for the date, and any specific details for the weekend (e.g., opening hours, event time)."
              }
              // Add more location/activity objects here if the plan involves multiple stops/parts
          ]
        }

بعد تعديل تعليمات الوكيل لطلب إخراج JSON على وجه التحديد، لننتحقق من التغيير.

👉💻 أعِد تشغيل واجهة مستخدم "مجموعة أدوات تطوير التطبيقات" باستخدام الأمر نفسه كما في السابق:

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd  ~/instavibe-bootstrap/agents
adk web

أعِد تحميل علامة التبويب إذا كانت مفتوحة. أو اتّبِع الخطوات نفسها كما في السابق لفتح واجهة مستخدم ADK Dev في المتصفّح (من خلال "معاينة الويب" في Cloud Shell على المنفذ 8000). بعد تحميل واجهة المستخدم، تأكَّد من اختيار وكيل التخطيط.

👉 لنقدّم له طلبًا مختلفًا هذه المرة. في مربّع حوار المحادثة، أدخِل ما يلي:

Plan an event Boston this weekend with art and coffee

افحص ردّ الموظف بعناية. بدلاً من الردّ النصي الحواري البحت، من المفترض أن يظهر لك الآن ردّ منسّق بدقة كعنصر JSON، يتطابق مع البنية التي حدّدناها في التعليمات (التي تحتوي على fun_plans وplan_description وlocations_and_activities وما إلى ذلك). يؤكّد ذلك أنّ الوكيل يمكنه الآن إنشاء ناتج منظَّم مناسب للاستخدام الآلي من خلال تطبيق InstaVibe.

adk dev ui json

بعد تأكيد إخراج JSON، ارجع إلى نافذة Cloud Shell واضغط على Ctrl+C لإيقاف واجهة ADK Dev UI.

مكوّنات ADK

على الرغم من أنّ واجهة المستخدم الخاصة بأداة تطوير ADK مناسبة جدًا للاختبار التفاعلي، نحتاج غالبًا إلى تشغيل برامجنا آليًا، ربما كجزء من تطبيق أكبر أو خدمة خلفية. لفهم طريقة عمل ذلك، لنلقِ نظرة على بعض المفاهيم الأساسية في "حزمة تطوير التطبيقات" (ADK) المتعلّقة بإدارة وقت التشغيل والسياق.

تتطلّب المحادثات المتعددة الأدوار والهادفة أن يفهم الوكلاء السياق، أي أن يتذكّروا ما قيل وما تمّ تنفيذه للحفاظ على الاستمرارية. توفّر "حزمة تطوير البرامج الإعلانية" طرقًا منظَّمة لإدارة هذا السياق من خلال الجلسة والحالة والذاكرة:

  • الجلسة: عند بدء مستخدم التفاعل مع موظّف دعم، يتم إنشاء جلسة. يمكنك اعتبارها الحاوية لسلسلة محادثات واحدة ومحدّدة. يحتوي على معرّف فريد وسجلّ التفاعلات (الأحداث) وبيانات العمل الحالية (الحالة) والبيانات الوصفية، مثل وقت آخر تعديل.
  • الحالة: هي الذاكرة العاملة قصيرة المدى للوكيل ضمن جلسة واحدة. وهي عبارة عن قاموس قابل للتعديل يمكن للوكيل تخزين المعلومات المؤقتة فيه اللازمة لإكمال المهمة الحالية (مثل، الإعدادات المفضّلة للمستخدم التي تم جمعها حتى الآن، والنتائج الوسيطة من طلبات استخدام الأدوات).
  • الذاكرة: يشير ذلك إلى إمكانية استرجاع المعلومات على المدى الطويل في جلسات مختلفة أو الوصول إلى قواعد المعرفة الخارجية. في حين أنّ "الجلسة" و"الحالة" تتعاملان مع المحادثة المباشرة، تسمح "الذاكرة" (التي غالبًا ما تديرها MemoryService) للوكيل باسترداد المعلومات من التفاعلات السابقة أو مصادر البيانات المنظَّمة، ما يمنحه سياقًا معرفيًا أوسع. (ملاحظة: يستخدم برنامج العميل البسيط خدمات في الذاكرة لتسهيل الاستخدام، ما يعني أنّ الذاكرة/الحالة لا تستمر إلا أثناء تشغيل النص البرمجي).
  • الحدث: يتم تسجيل كل تفاعل ضمن الجلسة (رسالة المستخدم، ردّ الوكيل، طلب استخدام الأداة، نتيجة الأداة، تغيير الحالة، الخطأ) كحدث غير قابل للتغيير. يؤدي ذلك إلى إنشاء سجلّ زمني، وهو في الأساس نص وسجلّ إجراءات المحادثة.

إذًا، كيف تتم إدارة هذه العمليات عند تشغيل وكيل؟ هذه هي مهمة العدّاء.

  • Runner: هو محرّك التنفيذ الأساسي الذي توفّره "حزمة تطوير التطبيقات". يمكنك تحديد الوكيل والأدوات التي يستخدمها، ويتولّى Runner تنسيق عملية تنفيذ طلب المستخدم. يدير الجلسة، ويتعامل مع سير الأحداث، ويعدّل الحالة، ويستدعي نموذج اللغة الأساسي، وينسّق عمليات استدعاء الأدوات، ويمكنه التفاعل مع MemoryService. يمكنك التفكير في ذلك على أنّه الموصل الذي يضمن عمل جميع الأجزاء المختلفة معًا بشكل صحيح.

يمكننا استخدام Runner لتشغيل الوكيل كتطبيق Python مستقل، بدون أي اعتماد على واجهة Dev UI.

لننشئ نص برمجي بسيطًا للعميل لاستدعاء برنامج وكيل التخطيط برمجيًا.

👉📝 في الملف ~/instavibe-bootstrap/agents/planner/planner_client.py، أضِف رمز Python التالي ضمن عمليات الاستيراد الحالية. في planner_client.py، ضمن عمليات الاستيراد، أضِف ما يلي:

async def async_main():
  session_service = InMemorySessionService()

  session = await session_service.create_session(
      state={}, app_name='planner_app', user_id='user_dc'
  )

  query = "Plan Something for me in San Francisco this weekend on wine and fashion "
  print(f"User Query: '{query}'")
  content = types.Content(role='user', parts=[types.Part(text=query)])

  root_agent = agent.root_agent
  runner = Runner(
        app_name='planner_app',
        agent=root_agent,
        session_service=session_service,
  )
  print("Running agent...")
  events_async =  runner.run_async(
    session_id=session.id, user_id=session.user_id, new_message=content
  )

  async for event in events_async:
    print(f"Event received: {event}")


if __name__ == '__main__':
  try:
    asyncio.run(async_main())
  except Exception as e:
    print(f"An error occurred: {e}")

يُعدّ هذا الرمز خدمات في الذاكرة لإدارة الجلسات والعناصر (لإبقاء المثال بسيطًا)، وينشئ جلسة، ويحدّد طلب بحث من المستخدم، ويضبط Runner باستخدام الوكيل، ثم يشغّل الوكيل بشكل غير متزامن، ويطبع كل حدث تم إنشاؤه أثناء التنفيذ.

👉💻 الآن، نفِّذ نص البرنامج هذا من الوحدة الطرفية:

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd  ~/instavibe-bootstrap/agents
python -m planner.planner_client

👀 راقِب الناتج. بدلاً من خطة JSON النهائية فقط، ستظهر لك البنية التفصيلية لكل عنصر Event تم إنشاؤه أثناء سير تنفيذ الوكيل. ويشمل ذلك حدث رسالة المستخدم الأولية، والأحداث المحتملة ذات الصلة باستدعاء الأدوات (مثل "بحث Google")، وأخيرًا حدث استجابة النموذج الذي يحتوي على خطة JSON. ويكون بث الأحداث المفصّل هذا مفيدًا جدًا لتصحيح الأخطاء وفهم عملية المعالجة خطوة بخطوة التي تحدث داخل ADK Runtime.

Running agent...
Event received: content=Content(parts=[Part(video_metadata=None, thought=None, code_execution_result=None, executable_code=None, file_data=None, function_call=None, function_response=None, inline_data=None, text='```json\n{\n "fun_plans": [\n  {\n   "plan_description": "Embark on a stylish adventure through Hayes Valley, 
...(turncated)
, offering a variety of fashion styles to browse and enjoy."\n    }\n   ]\n  }\n ]\n}\n```')], role='model') grounding_metadata=GroundingMetadata(grounding_chunks=[GroundingChunk(retrieved_context=None, web=GroundingChunkWeb(domain='islands.com', title='islands.com', uri='http
...(turncated)
QyTpPV7jS6wUt-Ix7GuP2mC9J4eY_8Km6Vv44liF9cb2VSs='))], grounding_supports=[GroundingSupport(confide
...(turncated)
>\n', sdk_blob=None), web_search_queries=['..e']) partial=None turn_complete=None error_code=None error_message=None interrupted=None custom_metadata=None invocation_id='e-04d97b8b-9021-47a5-ab41-17b5cbb4bf03' author='location_search_agent' actions=EventActions(skip_summarization=None, state_delta={}, artifact_delta={}, transfer_to_agent=None, escalate=None, requested_auth_configs={}) long_running_tool_ids=None branch=None id='CInHdkKw' timestamp=1746978846.232674

إذا كان النص البرمجي يعمل باستمرار أو يتوقف، قد تحتاج إلى إيقافه يدويًا بالضغط على Ctrl+C.

7. Platform Interaction Agent - interact with MCP Server

مع أنّ "مجموعة أدوات المطوّرين للوكلاء" تساعد في تنظيم الوكلاء، إلا أنّهم غالبًا ما يحتاجون إلى التفاعل مع أنظمة أو واجهات برمجة تطبيقات خارجية لتنفيذ إجراءات في العالم الحقيقي.

بروتوكول سياق النموذج (MCP)

بروتوكول سياق النموذج (MCP) هو معيار مفتوح مصمّم لتوحيد طريقة ربط تطبيقات الذكاء الاصطناعي، مثل البرامج الآلية، بمصادر البيانات والأدوات والأنظمة الخارجية. يهدف إلى حلّ مشكلة الحاجة إلى عمليات دمج مخصّصة لكل مجموعة من تطبيقات الذكاء الاصطناعي ومصادر البيانات من خلال توفير واجهة عالمية. تستخدم MCP بنية خادم-عميل حيث تدير برامج MCP، المتوفرة في تطبيقات الذكاء الاصطناعي (المضيفة)، عمليات الاتصال بخوادم MCP. هذه الخوادم هي برامج خارجية تعرض وظائف محدّدة، مثل الوصول إلى البيانات المحلية أو التفاعل مع الخدمات البعيدة من خلال واجهات برمجة التطبيقات أو تقديم طلبات محدّدة مسبقًا، ما يسمح لنماذج الذكاء الاصطناعي بالوصول إلى المعلومات الحالية وتنفيذ مهام تتجاوز تدريبها الأوّلي. يتيح هذا البناء لنماذج الذكاء الاصطناعي اكتشاف الإمكانات الخارجية والتفاعل معها بطريقة موحّدة، ما يجعل عمليات الدمج أبسط وأكثر قابلية للتوسّع.

إنشاء خادم InstaVibe MCP ونشره

07-mcp-server.png

سيحتاج وكلاؤنا في النهاية إلى التفاعل مع منصة InstaVibe نفسها، وتحديدًا لإنشاء مشاركات وتسجيل أحداث باستخدام واجهات برمجة التطبيقات الحالية للمنصة. يتيح تطبيق InstaVibe هذه الوظائف من خلال نقاط نهاية HTTP العادية:

Enpoint

عنوان URL

طريقة HTTP

الوصف

إنشاء مشاركة

api/posts

نشر مشاركة

نقطة نهاية واجهة برمجة التطبيقات لإضافة مشاركة جديدة من المتوقّع أن يكون نص الطلب بتنسيق JSON:
{"author_name": "...", "text": "...", "sentiment": "..." (optional)}

إنشاء حدث

api/events

نشر مشاركة

نقطة نهاية واجهة برمجة التطبيقات لإضافة حدث جديد والضيوف الذين سيحضرونه (مخطط مبسط)
تتوقّع هذه السمة نص JSON: { "event_name": "...", "description": "...", "event_date": "YYYY-MM-DDTHH:MM:SSZ", "locations": [ {"name": "...", "description": "...", "latitude": 0.0, "longitude": 0.0, "address": "..."} ], "attendee_names": ["...", "..."] }

لإتاحة هذه الإمكانات لموظفي الدعم من خلال MCP، علينا أولاً إنشاء دوال Python بسيطة تعمل كبرامج تضمين لطلبات البيانات من واجهة برمجة التطبيقات هذه. ستتعامل هذه الدوال مع منطق طلب HTTP.

👉 أولاً، لننفّذ دالة برنامج التضمين لإنشاء مشاركة. افتح الملف ~/instavibe-bootstrap/tools/instavibe/instavibe.py واستبدِل التعليق #REPLACE ME CREATE POST برمز Python التالي:

def create_post(author_name: str, text: str, sentiment: str, base_url: str = BASE_URL):
    """
    Sends a POST request to the /posts endpoint to create a new post.

    Args:
        author_name (str): The name of the post's author.
        text (str): The content of the post.
        sentiment (str): The sentiment associated with the post (e.g., 'positive', 'negative', 'neutral').
        base_url (str, optional): The base URL of the API. Defaults to BASE_URL.

    Returns:
        dict: The JSON response from the API if the request is successful.
              Returns None if an error occurs.

    Raises:
        requests.exceptions.RequestException: If there's an issue with the network request (e.g., connection error, timeout).
    """
    url = f"{base_url}/posts"
    headers = {"Content-Type": "application/json"}
    payload = {
        "author_name": author_name,
        "text": text,
        "sentiment": sentiment
    }

    try:
        response = requests.post(url, headers=headers, json=payload)
        response.raise_for_status()  # Raise an exception for bad status codes (4xx or 5xx)
        print(f"Successfully created post. Status Code: {response.status_code}")
        return response.json()
    except requests.exceptions.RequestException as e:
        print(f"Error creating post: {e}")
        # Optionally re-raise the exception if the caller needs to handle it
        # raise e
        return None
    except json.JSONDecodeError:
        print(f"Error decoding JSON response from {url}. Response text: {response.text}")
        return None

👉📝 بعد ذلك، سننشئ دالة برنامج التضمين لواجهة برمجة التطبيقات الخاصة بإنشاء الأحداث. في ملف ~/instavibe-bootstrap/tools/instavibe/instavibe.py نفسه، استبدِل التعليق #REPLACE ME CREATE EVENTS بهذا الرمز:

def create_event(event_name: str, description: str, event_date: str, locations: list, attendee_names: list[str], base_url: str = BASE_URL):
    """
    Sends a POST request to the /events endpoint to create a new event registration.

    Args:
        event_name (str): The name of the event.
        description (str): The detailed description of the event.
        event_date (str): The date and time of the event (ISO 8601 format recommended, e.g., "2025-06-10T09:00:00Z").
        locations (list): A list of location dictionaries. Each dictionary should contain:
                          'name' (str), 'description' (str, optional),
                          'latitude' (float), 'longitude' (float),
                          'address' (str, optional).
        attendee_names (list[str]): A list of names of the people attending the event.
        base_url (str, optional): The base URL of the API. Defaults to BASE_URL.

    Returns:
        dict: The JSON response from the API if the request is successful.
              Returns None if an error occurs.

    Raises:
        requests.exceptions.RequestException: If there's an issue with the network request (e.g., connection error, timeout).
    """
    url = f"{base_url}/events"
    headers = {"Content-Type": "application/json"}
    payload = {
        "event_name": event_name,
        "description": description,
        "event_date": event_date,
        "locations": locations,
        "attendee_names": attendee_names,
    }

    try:
        response = requests.post(url, headers=headers, json=payload)
        response.raise_for_status()  # Raise an exception for bad status codes (4xx or 5xx)
        print(f"Successfully created event registration. Status Code: {response.status_code}")
        return response.json()
    except requests.exceptions.RequestException as e:
        print(f"Error creating event registration: {e}")
        # Optionally re-raise the exception if the caller needs to handle it
        # raise e
        return None
    except json.JSONDecodeError:
        print(f"Error decoding JSON response from {url}. Response text: {response.text}")
        return None

كما تلاحظ، هذه الدوال هي برامج تضمين بسيطة حول واجهات برمجة التطبيقات الحالية في InstaVibe. يكون هذا النمط مفيدًا إذا كانت لديك واجهات برمجة تطبيقات لخدماتك، ويمكنك بسهولة إتاحة وظائفها كأدوات للوكلاء من خلال إنشاء برامج تضمين من هذا النوع.

تنفيذ خادم MCP

بعد أن أصبح لدينا دوال Python التي تنفّذ الإجراءات (استدعاء واجهات برمجة تطبيقات InstaVibe)، علينا إنشاء مكوّن MCP Server. سيعرض هذا الخادم هذه الدوال على أنّها "أدوات" وفقًا لمعيار MCP، ما يتيح لبرامج MCP (مثل برامجنا) اكتشافها واستخدامها.

عادةً ما يوفّر خادم MCP وظيفتَين أساسيتَين:

  • list_tools: مسؤولة عن السماح للعميل باكتشاف الأدوات المتاحة على الخادم، وتوفير البيانات الوصفية مثل أسمائها وأوصافها والمَعلمات المطلوبة، والتي يتم تحديدها غالبًا باستخدام JSON Schema
  • call_tool: تعالج تنفيذ أداة معيّنة طلبها العميل، وتتلقّى اسم الأداة ووسيطاتها وتنفّذ الإجراء المقابل، مثل التفاعل مع واجهة برمجة التطبيقات في حالتنا

تُستخدَم خوادم MCP لتزويد نماذج الذكاء الاصطناعي بإمكانية الوصول إلى البيانات والإجراءات الواقعية، ما يتيح تنفيذ مهام مثل إرسال رسائل إلكترونية أو إنشاء مهام في أنظمة إدارة المشاريع أو البحث في قواعد البيانات أو التفاعل مع مختلف البرامج وخدمات الويب. في حين أنّ عمليات التنفيذ الأولية غالبًا ما كانت تركّز على الخوادم المحلية التي تتواصل عبر الإدخال/الإخراج (stdio) العاديين لتبسيط العملية، لا سيما في بيئات التطوير أو "الاستوديو"، فإنّ الاتجاه نحو الخوادم البعيدة التي تستخدم بروتوكولات مثل HTTP مع أحداث Server-Sent Events (SSE) يبدو أكثر منطقية لتحقيق اعتماد أوسع وحالات استخدام على مستوى المؤسسات.

على الرغم من طبقة الاتصال الإضافية بالشبكة، توفّر البنية البعيدة مزايا كبيرة، فهي تتيح لعدة برامج تستخدم الذكاء الاصطناعي مشاركة الوصول إلى خادم واحد، وتعمل على مركزية إدارة الأدوات وتحديثها، وتعزّز الأمان من خلال الاحتفاظ بالبيانات الحسّاسة ومفاتيح واجهة برمجة التطبيقات على جانب الخادم بدلاً من توزيعها على العديد من أجهزة العميل المحتملة، كما تفصل نموذج الذكاء الاصطناعي عن تفاصيل تكامل النظام الخارجي، ما يجعل النظام المتكامل بأكمله أكثر قابلية للتوسّع والأمان والصيانة من طلب أن يدير كل مثيل من الذكاء الاصطناعي عمليات التكامل المباشر الخاصة به.

07-mcp-server.png

سننفّذ خادم MCP باستخدام HTTP وServer-Sent Events (SSE) للتواصل، وهو مناسب تمامًا لعمليات تنفيذ الأدوات التي قد تستغرق وقتًا طويلاً وسيناريوهات المؤسسات.

👉📝 لنبدأ أولاً بتنفيذ نقطة النهاية list_tools. افتح الملف ~/instavibe-bootstrap/tools/instavibe/mcp_server.py واستبدِل التعليق #REPLACE ME - LIST TOOLS بالرمز التالي. :

@app.list_tools()
async def list_tools() -> list[mcp_types.Tool]:
  """MCP handler to list available tools."""
  # Convert the ADK tool's definition to MCP format
  mcp_tool_schema_event = adk_to_mcp_tool_type(event_tool)
  mcp_tool_schema_post = adk_to_mcp_tool_type(post_tool)
  print(f"MCP Server: Received list_tools request. \n MCP Server: Advertising tool: {mcp_tool_schema_event.name} and {mcp_tool_schema_post}")
  return [mcp_tool_schema_event,mcp_tool_schema_post]

تحدّد هذه الدالة الأدوات (create_event وcreate_post) وتُعلم البرامج المرتبطة بها.

👉📝 بعد ذلك، نفِّذ نقطة نهاية call_tool التي تعالج طلبات التنفيذ الفعلية من العملاء. في ملف ~/instavibe-bootstrap/tools/instavibe/mcp_server.py نفسه، استبدِل #REPLACE ME - CALL TOOLSالتعليق بهذا الرمز.

@app.call_tool()
async def call_tool(
    name: str, arguments: dict
) -> list[mcp_types.TextContent | mcp_types.ImageContent | mcp_types.EmbeddedResource]:
  """MCP handler to execute a tool call."""
  print(f"MCP Server: Received call_tool request for '{name}' with args: {arguments}")

  # Look up the tool by name in our dictionary
  tool_to_call = available_tools.get(name)
  if tool_to_call:
    try:
      adk_response = await tool_to_call.run_async(
          args=arguments,
          tool_context=None, # No ADK context available here
      )
      print(f"MCP Server: ADK tool '{name}' executed successfully.")
      
      response_text = json.dumps(adk_response, indent=2)
      return [mcp_types.TextContent(type="text", text=response_text)]

    except Exception as e:
      print(f"MCP Server: Error executing ADK tool '{name}': {e}")
      # Creating a proper MCP error response might be more robust
      error_text = json.dumps({"error": f"Failed to execute tool '{name}': {str(e)}"})
      return [mcp_types.TextContent(type="text", text=error_text)]
  else:
      # Handle calls to unknown tools
      print(f"MCP Server: Tool '{name}' not found.")
      error_text = json.dumps({"error": f"Tool '{name}' not implemented."})
      return [mcp_types.TextContent(type="text", text=error_text)]

تتلقّى هذه الدالة اسم الأداة ووسيطاتها، وتعثر على دالة برنامج تضمين Python المقابلة التي حدّدناها سابقًا، وتنفّذها، وتعرض النتيجة.

👉💻 بعد تحديد منطق خادم MCP، علينا الآن تجميع هذا المنطق في حاوية. في نافذة الوحدة الطرفية، شغِّل النص البرمجي التالي لإنشاء صورة Docker باستخدام Cloud Build:

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/tools/instavibe

export IMAGE_TAG="latest"
export MCP_IMAGE_NAME="mcp-tool-server"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${MCP_IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="mcp-tool-server"
export INSTAVIBE_BASE_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep instavibe)/api

gcloud builds submit . \
  --tag=${IMAGE_PATH} \
  --project=${PROJECT_ID}

👉💻 ونشر الصورة كخدمة على Google Cloud Run

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/tools/instavibe

export IMAGE_TAG="latest"
export MCP_IMAGE_NAME="mcp-tool-server"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${MCP_IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="mcp-tool-server"
export INSTAVIBE_BASE_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep instavibe)/api

gcloud run deploy ${SERVICE_NAME} \
  --image=${IMAGE_PATH} \
  --platform=managed \
  --region=${REGION} \
  --allow-unauthenticated \
  --set-env-vars="INSTAVIBE_BASE_URL=${INSTAVIBE_BASE_URL}" \
  --set-env-vars="APP_HOST=0.0.0.0" \
  --set-env-vars="APP_PORT=8080" \
  --set-env-vars="GOOGLE_GENAI_USE_VERTEXAI=TRUE" \
  --set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
  --set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
  --project=${PROJECT_ID} \
  --min-instances=1

👉💻 بعد اكتمال عملية النشر بنجاح، سيتم تشغيل خادم MCP ويمكن الوصول إليه من خلال عنوان URL عام. علينا تسجيل عنوان URL هذا لكي يعرف الوكيل (الذي يعمل كعميل MCP) مكان الاتصال.

export MCP_SERVER_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep mcp-tool-server)/sse

من المفترض أن تتمكّن الآن أيضًا من رؤية خدمة mcp-tool-server مُدرَجة على أنّها "قيد التشغيل" في قسم Cloud Run في Google Cloud Console.

Cloud run

بعد نشر خادم MCP وتسجيل عنوان URL الخاص به، يمكننا الآن تنفيذ العامل الذي سيعمل كعميل MCP واستخدام الأدوات التي يعرضها هذا الخادم.

8. وكيل التفاعل مع المنصّة (باستخدام MCP)

عميل MCP: عميل MCP هو أحد المكوّنات التي تتضمّنها تطبيقات أو برامج الذكاء الاصطناعي، ويعمل كواجهة بين نموذج الذكاء الاصطناعي وخادم واحد أو أكثر من خوادم MCP. وفي عملية التنفيذ التي نتبعها، سيتم دمج هذا العميل مباشرةً في برنامجنا. تتمثّل الوظيفة الأساسية لهذا العميل في التواصل مع خوادم MCP لاكتشاف الأدوات المتاحة من خلال الدالة list_tools، ثم طلب تنفيذ أدوات معيّنة باستخدام الدالة call_tool، مع تمرير الوسيطات اللازمة التي يقدّمها نموذج الذكاء الاصطناعي أو الوكيل الذي ينسّق المكالمة.

عميل MCP

سننشئ الآن الوكيل الذي يعمل كبرنامج MCP Client. سيكون هذا الوكيل، الذي يعمل ضمن إطار عمل ADK، مسؤولاً عن التواصل مع mcp-tool-server الذي تم نشره للتو.

👉 أولاً، علينا تعديل تعريف الوكيل لجلب الأدوات بشكل ديناميكي من خادم MCP الذي يتم تشغيله. في agents/platform_mcp_client/agent.py، استبدِل #REPLACE ME - FETCH TOOLS بما يلي:

"""Gets tools from the File System MCP Server."""
  tools =  MCPToolset(
      connection_params=SseServerParams(url=MCP_SERVER_URL, headers={})
  )

يستخدم هذا الرمز طريقة MCPToolset.from_server للاتصال بـ MCP_SERVER_URL (الذي ضبطناه كمتغيّر بيئة سابقًا) واسترداد قائمة الأدوات المتاحة.

بعد ذلك، علينا أن نطلب من تعريف وكيل ADK استخدام هذه الأدوات التي يتم جلبها ديناميكيًا.

👉 في agents/platform_mcp_client/agent.py، استبدِل #REPLACE ME - SET TOOLs بما يلي:

  tools=[tools],

👉💻 الآن، لنختبر هذا الوكيل محليًا باستخدام واجهة المستخدم الخاصة بأداة تطوير ADK لمعرفة ما إذا كان بإمكانه الاتصال بخادم MCP بشكل صحيح واستخدام الأدوات للتفاعل مع تطبيق InstaVibe الذي يتم تشغيله.

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
export MCP_SERVER_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep mcp-tool-server)/sse

cd  ~/instavibe-bootstrap/agents
sed -i "s|^\(O\?GOOGLE_CLOUD_PROJECT\)=.*|GOOGLE_CLOUD_PROJECT=${PROJECT_ID}|" ~/instavibe-bootstrap/agents/platform_mcp_client/.env
sed -i "s|^\(O\?MCP_SERVER_URL\)=.*|MCP_SERVER_URL=${MCP_SERVER_URL}|" ~/instavibe-bootstrap/agents/platform_mcp_client/.env
adk web

افتح واجهة مستخدم ADK Dev في المتصفّح مرة أخرى (باستخدام "معاينة الويب" في Cloud Shell على المنفذ 8000). في هذه المرة، اختَر العميل platform_mcp_client من القائمة المنسدلة في أعلى يسار الصفحة.

لنجرِّب أداة create_post. في مربّع حوار المحادثة، أدخِل الطلب التالي:

Create a post saying "Y'all I just got the cutest lil void baby 😭✨ Naming him Abyss bc he's deep, mysterious, and lowkey chaotic 🔥🖤 #VoidCat #NewRoomie" I'm Julia

منشور على واجهة المستخدم الخاصة بأدوات تطوير ADK

على الوكيل معالجة ذلك وتحديد الحاجة إلى استخدام أداة create_post، والتواصل مع خادم MCP الذي بدوره يستدعي واجهة برمجة تطبيقات InstaVibe.

👉 خطوة التحقّق: بعد أن يؤكّد الوكيل الإجراء، افتح علامة التبويب التي يتم فيها تشغيل تطبيق InstaVibe (أو أعِد تحميلها). من المفترض أن تظهر المشاركة الجديدة من "جوليا" في الخلاصة الرئيسية.

InstaVibe Post

👉💻 شغِّل النص البرمجي التالي في نافذة وحدة طرفية منفصلة للحصول على رابط Instavibe إذا لزم الأمر:

gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep instavibe

👉📝 لنختبر الآن أداة create_event. أدخِل الطلب التالي المتعدّد الأسطر في مربّع حوار المحادثة:

Hey, can you set up an event for Hannah and George and me, and I'm Julia? Let's call it 'Mexico City Culinary & Art Day'.
here are more info
  {"event_name": "Mexico City Culinary & Art Day",
  "description": "A vibrant day in Mexico City for Hannah and George, starting with lunch at one of the city's best taco spots in the hip Condesa neighborhood, followed by an inspiring afternoon exploring the Museo Soumaya's stunning art collection.",
  "event_date": "2025-10-17T12:00:00-06:00",
  "locations": [
    {
      "name": "El Tizoncito",
      "description": "Considered one of the original creators of tacos al pastor, El Tizoncito offers a legendary taco experience in the heart of Condesa. Their flavorful meats, house salsas, and casual vibe make it a must-visit for foodies.",
      "latitude": 19.412179,
      "longitude": -99.171308,
      "address": "Av. Tamaulipas 122, Hipódromo, Cuauhtémoc, 06100 Ciudad de México, CDMX, Mexico"
    },
    {
      "name": "Museo Soumaya",
      "description": "An architectural icon in Mexico City, Museo Soumaya houses over 66,000 works of art, including pieces by Rodin, Dalí, and Rivera. The striking silver structure is a cultural landmark and a visual feast inside and out.",
      "latitude": 19.440056,
      "longitude": -99.204281,
      "address": "Plaza Carso, Blvd. Miguel de Cervantes Saavedra 303, Granada, Miguel Hidalgo, 11529 Ciudad de México, CDMX, Mexico"
    }
  ],
  "attendee_names": ["Hannah", "George", Julia],
}

مرة أخرى، يجب أن يستخدم الوكيل الأداة المناسبة من خلال خادم MCP. في علامة التبويب "الأحداث"، يمكنك النقر على الحدث الفردي، وسيظهر لك تتبُّع تفصيلي خطوة بخطوة للتنفيذ.

ADK Dev UI Event

👉 خطوة التحقّق: ارجع إلى تطبيق InstaVibe الذي يتم تشغيله وانتقِل إلى قسم "الأحداث" (أو القسم المشابه). من المفترض أن يظهر لك الآن الحدث "يوم في مكسيكو سيتي للتعرّف على فن الطهي والفن" الذي تم إنشاؤه حديثًا.

حدث InstaVibe

يوضّح هذا المثال بنجاح كيف تتيح منصة MCP لوكيلنا الاستفادة من أدوات خارجية (في هذه الحالة، واجهات برمجة التطبيقات في InstaVibe) بطريقة موحّدة.

بعد إكمال الإجراءَين، ارجع إلى نافذة Cloud Shell واضغط على Ctrl+C لإيقاف واجهة مستخدم ADK Dev.

9- وكيل سير العمل والوكلاء المتعدّدون في "حزمة تطوير تطبيقات Android"

يمكن لوكلائنا حتى الآن التخطيط للرحلات والتفاعل مع المنصة. ومع ذلك، تتطلّب التخطيطات المخصّصة حقًا فهم دائرة الأصدقاء للمستخدم. بالنسبة إلى المستخدمين المشغولين الذين قد لا يتابعون أنشطة أصدقائهم عن كثب، يصعب جمع هذا السياق يدويًا. ولحلّ هذه المشكلة، سننشئ وكيل "الملف الشخصي على وسائل التواصل الاجتماعي" الذي يستفيد من "قاعدة بيانات الرسوم البيانية" في Spanner لتحليل أنشطة الأصدقاء واهتماماتهم، ما يتيح تقديم اقتراحات أكثر تخصيصًا.

وكيل إنشاء الملفات الشخصية على وسائل التواصل الاجتماعي

أولاً، نحتاج إلى أدوات تتيح لهذا الوكيل الوصول إلى بيانات الرسم البياني.

👉📝 أضِف دالتَي Python التاليتَين إلى نهاية الملف ~/instavibe-bootstrap/agents/social/instavibe.py:

def get_person_attended_events(person_id: str)-> list[dict]:
    """
    Fetches events attended by a specific person using Graph Query.
    Args:
       person_id (str): The ID of the person whose posts to fetch.
    Returns: list[dict] or None.
    """
    if not db_instance: return None

    graph_sql = """
        Graph SocialGraph
        MATCH (p:Person)-[att:Attended]->(e:Event)
        WHERE p.person_id = @person_id
        RETURN e.event_id, e.name, e.event_date, att.attendance_time
        ORDER BY e.event_date DESC
    """
    params = {"person_id": person_id}
    param_types_map = {"person_id": param_types.STRING}
    fields = ["event_id", "name", "event_date", "attendance_time"]

    results = run_graph_query( graph_sql, params=params, param_types=param_types_map, expected_fields=fields)

    if results is None: return None

    for event in results:
        if isinstance(event.get('event_date'), datetime):
            event['event_date'] = event['event_date'].isoformat()
        if isinstance(event.get('attendance_time'), datetime):
            event['attendance_time'] = event['attendance_time'].isoformat()
    return results

def get_person_id_by_name( name: str) -> str:
    """
    Fetches the person_id for a given name using SQL.

    Args:
       name (str): The name of the person to search for.

    Returns:
        str or None: The person_id if found, otherwise None.
                     Returns the ID of the *first* match if names are duplicated.
    """
    if not db_instance: return None

    sql = """
        SELECT person_id
        FROM Person
        WHERE name = @name
        LIMIT 1 -- Return only the first match in case of duplicate names
    """
    params = {"name": name}
    param_types_map = {"name": param_types.STRING}
    fields = ["person_id"]

    # Use the standard SQL query helper
    results = run_sql_query( sql, params=params, param_types=param_types_map, expected_fields=fields)

    if results: # Check if the list is not empty
        return results[0].get('person_id') # Return the ID from the first dictionary
    else:
        return None # Name not found


def get_person_posts( person_id: str)-> list[dict]:
    """
    Fetches posts written by a specific person using Graph Query.

    Args:
        person_id (str): The ID of the person whose posts to fetch.


    Returns:
        list[dict] or None: List of post dictionaries with ISO date strings,
                           or None if an error occurs.
    """
    if not db_instance: return None

    # Graph Query: Find the specific Person node, follow 'Wrote' edge to Post nodes
    graph_sql = """
        Graph SocialGraph
        MATCH (author:Person)-[w:Wrote]->(post:Post)
        WHERE author.person_id = @person_id
        RETURN post.post_id, post.author_id, post.text, post.sentiment, post.post_timestamp, author.name AS author_name
        ORDER BY post.post_timestamp DESC
    """
    # Parameters now include person_id and limit
    params = {
        "person_id": person_id
    }
    param_types_map = {
        "person_id": param_types.STRING
    }
    # Fields returned remain the same
    fields = ["post_id", "author_id", "text", "sentiment", "post_timestamp", "author_name"]

    results = run_graph_query(graph_sql, params=params, param_types=param_types_map, expected_fields=fields)

    if results is None:
        return None

    # Convert datetime objects to ISO format strings
    for post in results:
        if isinstance(post.get('post_timestamp'), datetime):
            post['post_timestamp'] = post['post_timestamp'].isoformat()

    return results


def get_person_friends( person_id: str)-> list[dict]:
    """
    Fetches friends for a specific person using Graph Query.
    Args:
        person_id (str): The ID of the person whose posts to fetch.
    Returns: list[dict] or None.
    """
    if not db_instance: return None

    graph_sql = """
        Graph SocialGraph
        MATCH (p:Person {person_id: @person_id})-[f:Friendship]-(friend:Person)
        RETURN DISTINCT friend.person_id, friend.name
        ORDER BY friend.name
    """
    params = {"person_id": person_id}
    param_types_map = {"person_id": param_types.STRING}
    fields = ["person_id", "name"]

    results = run_graph_query( graph_sql, params=params, param_types=param_types_map, expected_fields=fields)

    return results

الآن، دعنا نناقش كيفية تنظيم الوكيل. يتضمّن تحليل ملفات شخصية متعددة للأصدقاء ثم تلخيص النتائج عدة خطوات. هذا السيناريو مثالي لاستخدام إمكانات الوكلاء المتعدّدين في ADK، وتحديدًا وكلاء سير العمل.

في حزمة تطوير التطبيقات (ADK) من Google، لا ينفّذ "وكيل سير العمل" المهام بنفسه، بل ينسّق عمل وكلاء آخرين يُطلق عليهم اسم الوكلاء الفرعيون. ويتيح ذلك تصميمًا نمطيًا، ما يؤدي إلى تقسيم المشاكل المعقّدة إلى مكوّنات متخصّصة. توفّر "حزمة تطوير التطبيقات" أنواعًا مضمّنة من سير العمل، مثل

  • التسلسلي (خطوة بخطوة)
  • التنفيذ المتوازي (المتزامن)
  • وLoop (التنفيذ المتكرّر)

وكيل إنشاء الملفات الشخصية على وسائل التواصل الاجتماعي

بالنسبة إلى مهمة إنشاء الملفات الشخصية على وسائل التواصل الاجتماعي، يستخدم تصميمنا Loop Agent لإنشاء سير عمل تكراري. والهدف من ذلك هو معالجة شخص واحد في كل مرة: تجمع الخطوة profile_agent البيانات، وتعدّل الخطوة summary_agent التحليل، وتحدّد الخطوة check_agent ما إذا كان يجب تكرار العملية مرة أخرى.

لنحدّد الآن الوكلاء الفرعيين المطلوبين لسير العمل هذا.

👉📝 في ~/instavibe-bootstrap/agents/social/agent.py، استبدِل #REPLACE FOR profile_agent بما يلي:

profile_agent = LlmAgent(
    name="profile_agent",
    model="gemini-2.5-flash",
    description=(
        "Agent to answer questions about the this person social profile. Provide the person's profile using their name, make sure to fetch the id before getting other data."
    ),
    instruction=(
        "You are a helpful agent to answer questions about the this person social profile. You'll be given a list of names, provide the person's profile using their name, make sure to fetch the id before getting other data. Get one person at a time, start with the first one on the list, and skip if already provided. return this person's result"
    ),
    tools=[get_person_posts,get_person_friends,get_person_id_by_name,get_person_attended_events],
)

بعد ذلك، يتولّى الوكيل جمع معلومات الملف الشخصي (المتراكمة على مستوى تكرارات الحلقة) وإنشاء الملخّص النهائي، وتحديد القواسم المشتركة في حال تحليل ملفات شخصية متعددة.

👉📝 في ~/instavibe-bootstrap/agents/social/agent.py نفسه، استبدِل #REPLACE FOR summary_agent بما يلي:

summary_agent = LlmAgent(
    name="summary_agent",
    model="gemini-2.5-flash",
    description=(
        "Generate a comprehensive social summary as a single, cohesive paragraph. This summary should cover the activities, posts, friend networks, and event participation of one or more individuals. If multiple profiles are analyzed, the paragraph must also identify and integrate any common ground found between them."
    ),
    instruction=(
        """
        Your primary task is to synthesize social profile information into a single, comprehensive paragraph.

            **Input Scope & Default Behavior:**
            *   If specific individuals are named by the user, focus your analysis on them.
            *   **If no individuals are specified, or if the request is general, assume the user wants an analysis of *all relevant profiles available in the current dataset/context*.**

            **For each profile (whether specified or determined by default), you must analyze:**

            1.  **Post Analysis:**
                *   Systematically review their posts (e.g., content, topics, frequency, engagement).
                *   Identify recurring themes, primary interests, and expressed sentiments.

            2.  **Friendship Relationship Analysis:**
                *   Examine their connections/friends list.
                *   Identify key relationships, mutual friends (especially if comparing multiple profiles), and the general structure of their social network.

            3.  **Event Participation Analysis:**
                *   Investigate their past (and if available, upcoming) event participation.
                *   Note the types of events, frequency of attendance, and any notable roles (e.g., organizer, speaker).

            **Output Generation (Single Paragraph):**

            *   **Your entire output must be a single, cohesive summary paragraph.**
                *   **If analyzing a single profile:** This paragraph will detail their activities, interests, and social connections based on the post, friend, and event analysis.
                *   **If analyzing multiple profiles:** This paragraph will synthesize the key findings regarding posts, friends, and events for each individual. Crucially, it must then seamlessly integrate or conclude with an identification and description of the common ground found between them (e.g., shared interests from posts, overlapping event attendance, mutual friends). The aim is a unified narrative within this single paragraph.

            **Key Considerations:**
            *   Base your summary strictly on the available data.
            *   If data for a specific category (posts, friends, events) is missing or sparse for a profile, you may briefly acknowledge this within the narrative if relevant.
                """
        ),
    output_key="summary"
)

نحتاج إلى طريقة لتحديد الوقت الذي يجب أن تتوقف فيه حلقة التكرار (أي عندما يتم تلخيص جميع الملفات الشخصية المطلوبة).

👉📝 في ~/instavibe-bootstrap/agents/social/agent.py نفسه، استبدِل #REPLACE FOR check_agent بما يلي:

check_agent = LlmAgent(
    name="check_agent",
    model="gemini-2.5-flash",
    description=(
        "Check if everyone's social profile are summarized and has been generated. Output 'completed' or 'pending'."
    ),
    output_key="summary_status"
)

نضيف عملية تحقّق بسيطة مستندة إلى التعليمات البرمجية (CheckCondition) تبحث بشكل صريح عن summary_status المخزّنة في State، والتي يتم عرضها من خلال check_agent، وتخبر Loop Agent ما إذا كان يجب مواصلة العملية (escalate=False) أو إيقافها (escalate=True).

👉📝 في ~/instavibe-bootstrap/agents/social/agent.py نفسه، استبدِل #REPLACE FOR CheckCondition في أعلى الملف بما يلي:

class CheckCondition(BaseAgent):
    async def _run_async_impl(self, ctx: InvocationContext) -> AsyncGenerator[Event, None]:
        #log.info(f"Checking status: {ctx.session.state.get("summary_status", "fail")}")
        log.info(f"Summary: {ctx.session.state.get("summary")}")

        status = ctx.session.state.get("summary_status", "fail").strip()
        is_done = (status == "completed")

        yield Event(author=self.name, actions=EventActions(escalate=is_done))

الحالة وعمليات معاودة الاتصال لنتائج التكرار

في حزمة تطوير التطبيقات (ADK) من Google، الحالة هي مفهوم أساسي يمثّل الذاكرة أو بيانات العمل الخاصة بأحد البرامج أثناء تنفيذه. وهي في الأساس سياق ثابت يحتوي على المعلومات التي يحتاجها الوكيل للحفاظ على استمرار المحادثة في مختلف الخطوات أو طلبات الأدوات أو التفاعلات. يمكن أن تخزّن هذه الحالة النتائج المؤقتة أو معلومات المستخدم أو مَعلمات الإجراءات اللاحقة أو أي بيانات أخرى يحتاج إليها الوكيل لتذكُّرها أثناء تقدّمه في مهمة ما.

في السيناريو الخاص بنا، أثناء تكرار Loop Agent، تخزّن summary_agent وcheck_agent نواتجهما (الملخّص وsummary_status) في حالة الوكيل. ويسمح ذلك باستمرار المعلومات على مستوى التكرارات. ومع ذلك، لا يعرض Loop Agent نفسه الملخّص النهائي تلقائيًا من الحالة عند الانتهاء.

وكيل إنشاء الملفات الشخصية على وسائل التواصل الاجتماعي

تتيح لنا وظائف معاودة الاتصال في ADK إدخال منطق مخصّص ليتم تنفيذه في نقاط محدّدة خلال دورة حياة الوكيل أو استجابةً لأحداث معيّنة، مثل إكمال طلب استخدام أداة أو قبل أن ينهي الوكيل تنفيذه. وهي توفّر طريقة لتخصيص سلوك الوكيل ومعالجة النتائج بشكل ديناميكي.

سنستخدم after_agent_callback يتم تنفيذه عند انتهاء الحلقة (لأنّ CheckCondition تم تصعيده). يستردّ هذا الإجراء modify_output_after_agent الملخّص النهائي من الحالة ويُنسّقه كرسالة الإخراج النهائية للوكيل.

معاودة الاتصال

👉📝 في ~/instavibe-bootstrap/agents/social/agent.py نفسه، استبدِل #REPLACE FOR modify_output_after_agent بـ follow:

def modify_output_after_agent(callback_context: CallbackContext) -> Optional[types.Content]:

    agent_name = callback_context.agent_name
    invocation_id = callback_context.invocation_id
    current_state = callback_context.state.to_dict()
    current_user_content = callback_context.user_content
    print(f"[Callback] Exiting agent: {agent_name} (Inv: {invocation_id})")
    print(f"[Callback] Current summary_status: {current_state.get("summary_status")}")
    print(f"[Callback] Current Content: {current_user_content}")

    status = current_state.get("summary_status").strip()
    is_done = (status == "completed")
    # Retrieve the final summary from the state

    final_summary = current_state.get("summary")
    print(f"[Callback] final_summary: {final_summary}")
    if final_summary and is_done and isinstance(final_summary, str):
        log.info(f"[Callback] Found final summary, constructing output Content.")
        # Construct the final output Content object to be sent back
        return types.Content(role="model", parts=[types.Part(text=final_summary.strip())])
    else:
        log.warning("[Callback] No final summary found in state or it's not a string.")
        # Optionally return a default message or None if no summary was generated
        return None

تحديد "وكيل التكرار الأساسي"

أخيرًا، نحدد LoopAgent الرئيسي. وينسّق الوكلاء الفرعيين بالتسلسل ضمن كل تكرار حلقي (profile_agent -> summary_agent -> check_agent -> CheckCondition). ستكرّر هذه السلسلة ما يصل إلى max_iterations مرة أو إلى أن تشير CheckCondition إلى اكتمالها. تضمن الدالة البرمجية after_agent_callback عرض الملخّص النهائي.

👉📝 في ~/instavibe-bootstrap/agents/social/agent.py نفسه، استبدِل #REPLACE FOR root_agent بـ follow:

root_agent = LoopAgent(
    name="InteractivePipeline",
    sub_agents=[
        profile_agent,
        summary_agent,
        check_agent,
        CheckCondition(name="Checker")
    ],
    description="Find everyone's social profile on events, post and friends",
    max_iterations=10,
    after_agent_callback=modify_output_after_agent
)

لنختبر سير العمل هذا الذي يتضمّن عدة وكلاء باستخدام واجهة المستخدم الخاصة بأداة تطوير ADK.

👉💻 شغِّل خادم الويب الخاص بـ "حزمة تطوير التطبيقات" (ADK):

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd  ~/instavibe-bootstrap/agents
sed -i "s|^\(O\?GOOGLE_CLOUD_PROJECT\)=.*|GOOGLE_CLOUD_PROJECT=${PROJECT_ID}|" ~/instavibe-bootstrap/agents/social/.env
adk web

افتح واجهة مستخدم ADK Dev (المنفذ 8000 من خلال "المعاينة على الويب"). في القائمة المنسدلة الخاصة بالوكيل (أعلى يسار الصفحة)، اختَر الوكيل Social.

👉 الآن، اطلب منه إنشاء ملفات شخصية لعدة أشخاص. في مربّع حوار المحادثة، أدخِل ما يلي:

Tell me about Mike and Bob

بعد أن يردّ الوكيل (قد يستغرق ذلك وقتًا أطول قليلاً بسبب التكرار والمكالمات المتعددة مع النماذج اللغوية الكبيرة)، لا تكتفِ بالنظر إلى ناتج المحادثة النهائي. انتقِل إلى علامة التبويب "الأحداث" (Events) في اللوحة اليمنى من واجهة مستخدم "حزمة تطوير التطبيقات الإعلانية".

👉 خطوة التحقّق: في علامة التبويب "الأحداث"، سيظهر لك تتبُّع مفصّل خطوة بخطوة لعملية التنفيذ. 09-01-adk-dev-ui.png

بعد مراقبة طريقة استدعاء الوكيل لكل وكيل فرعي، حيث تتوقّع أن ينتقل المسار من profile_agent -> summary_agent -> check_agent، يجب استخدام Checker في كل تكرار. ومع ذلك، نرى في الواقع قدرة "التحسين الذاتي" الفعّالة للوكيل قيد الاستخدام.

لأنّ النموذج الأساسي يرى الطلب بأكمله (مثلاً، (على سبيل المثال، "أريد ملفًا شخصيًا لمايك وبوب")، غالبًا ما يختار المسار الأكثر فعالية، ويجمع كل البيانات المطلوبة في خطوة واحدة مجمّعة بدلاً من تكرار العملية عدة مرات. يمكنك الاطّلاع على المدخلات والمخرجات والحالات لكل خطوة، بما في ذلك طلبات الأدوات التي أرسلها profile_agent

09-02-ui-graph.png

وتعديلات الحالة من check_agent وCheckCondition. 09-03-ui-state.png

يُعدّ هذا التتبُّع المرئي مفيدًا جدًا لفهم طريقة عمل سير العمل المتعدّد الوكلاء وتصحيح الأخطاء فيه إلى أن يتم إنشاء الملخّص النهائي وعرضه من خلال الدالة الاحتياطية.

بعد استكشاف ردّ الدردشة وتتبُّع الحدث، ارجع إلى نافذة Cloud Shell الطرفية واضغط على Ctrl+C لإيقاف ADK Dev UI.

10. التواصل بين موظّفي الدعم

حتى الآن، أنشأنا وكلاء متخصصين، لكنهم يعملون بشكل منفصل أو ضمن سير عمل محدّد مسبقًا على الجهاز نفسه. لإنشاء أنظمة متعددة الوكلاء موزّعة وتعاونية حقًا، نحتاج إلى طريقة تتيح للوكلاء، الذين قد يعملون كخدمات منفصلة، التعرّف على بعضهم البعض والتواصل بفعالية. وهنا يأتي دور بروتوكول Agent-to-Agent (A2A).

بروتوكول A2A هو معيار مفتوح مصمّم خصيصًا للتواصل التفاعلي بين وكلاء الذكاء الاصطناعي. في حين تركّز أداة MCP على التفاعل بين المساعد والأداة، تركّز أداة A2A على التفاعل بين المساعد والمساعد. تتيح هذه الميزة للوكلاء ما يلي:

  • الاستكشاف: يمكنك العثور على وكلاء آخرين والتعرّف على إمكاناتهم من خلال بطاقات الوكلاء الموحّدة.
  • التواصل: تبادل الرسائل والبيانات بأمان
  • التعاون: تفويض المهام وتنسيق الإجراءات لتحقيق أهداف معقّدة

يسهّل بروتوكول A2A عملية التواصل هذه من خلال آليات مثل "بطاقات الوكيل" التي يمكن للوكلاء استخدامها للإعلان عن إمكاناتهم ومعلومات الاتصال الخاصة بهم.

10-05-agent-card

تستفيد A2A من معايير الويب المعروفة (HTTP وSSE وJSON-RPC) وتستخدم غالبًا نموذجًا من نوع "العميل والخادم" حيث يرسل أحد الوكلاء (العميل) مهامًا إلى وكيل آخر (وكيل/خادم عن بُعد). هذا التوحيد ضروري لإنشاء أنظمة معيارية قابلة للتطوير يمكن أن تعمل فيها البرامج التي تم تطويرها بشكل مستقل معًا.

تفعيل ميزة "التطبيق إلى التطبيق" لوكلاء InstaVibe

لإتاحة الوصول إلى وكلاء "المخطّط" و"التفاعل مع المنصّة" و"التواصل الاجتماعي" الحاليين لوكلاء آخرين من خلال A2A، علينا تضمين كل وكيل في مكوّن خادم A2A. سيعمل هذا الخادم على:

  • عرض بطاقة وكيل: عرض وصف عادي لإمكانات الوكيل من خلال نقطة نهاية HTTP
  • الاستماع إلى المهام(طلبات الرسائل): قبول طلبات المهام الواردة من وكلاء آخرين (عملاء A2A) وفقًا لبروتوكول A2A
  • إدارة تنفيذ المهام(طلبات الرسائل): تسليم المهام المستلَمة إلى منطق وكيل ADK الأساسي لمعالجتها

Planner Agent (A2A Enabled)

all-agent-planner

لنبدأ بإضافة طبقة خادم A2A إلى "وكيل التخطيط".

تحديد منطق بدء تشغيل خادم A2A يحدّد هذا الرمز AgentCard (الوصف العلني للوكيل)، ويضبط A2AServer ويبدأه، ويربطه بـ PlatformAgentExecutor.

👉📝 أضِف الرمز التالي إلى نهاية ~/instavibe-bootstrap/agents/planner/a2a_server.py:

class PlannerAgent:
    """An agent to help user planning a event with its desire location."""
    SUPPORTED_CONTENT_TYPES = ["text", "text/plain"]

    def __init__(self):
        self._agent = self._build_agent()
        self.runner = Runner(
            app_name=self._agent.name,
            agent=self._agent,
            artifact_service=InMemoryArtifactService(),
            session_service=InMemorySessionService(),
            memory_service=InMemoryMemoryService(),
        )
        capabilities = AgentCapabilities(streaming=True)
        skill = AgentSkill(
            id="event_planner",
            name="Event planner",
            description="""
            This agent generates multiple fun plan suggestions tailored to your specified location, dates, and interests,
            all designed for a moderate budget. It delivers detailed itineraries,
            including precise venue information (name, latitude, longitude, and description), in a structured JSON format.
            """,
            tags=["instavibe"],
            examples=["What about Bostona MA this weekend?"],
        )
        self.agent_card = AgentCard(
            name="Event Planner Agent",
            description="""
            This agent generates multiple fun plan suggestions tailored to your specified location, dates, and interests,
            all designed for a moderate budget. It delivers detailed itineraries,
            including precise venue information (name, latitude, longitude, and description), in a structured JSON format.
            """,
            url=f"{PUBLIC_URL}",
            version="1.0.0",
            defaultInputModes=PlannerAgent.SUPPORTED_CONTENT_TYPES,
            defaultOutputModes=PlannerAgent.SUPPORTED_CONTENT_TYPES,
            capabilities=capabilities,
            skills=[skill],
        )

    def get_processing_message(self) -> str:
        return "Processing the planning request..."

    def _build_agent(self) -> LlmAgent:
        """Builds the LLM agent for the night out planning agent."""
        return agent.root_agent


if __name__ == '__main__':
    try:
        plannerAgent = PlannerAgent()

        request_handler = DefaultRequestHandler(
            agent_executor=PlannerAgentExecutor(plannerAgent.runner,plannerAgent.agent_card),
            task_store=InMemoryTaskStore(),
        )

        server = A2AStarletteApplication(
            agent_card=plannerAgent.agent_card,
            http_handler=request_handler,
        )
        logger.info(f"Attempting to start server with Agent Card: {plannerAgent.agent_card.name}")
        logger.info(f"Server object created: {server}")

        uvicorn.run(server.build(), host='0.0.0.0', port=port)
    except Exception as e:
        logger.error(f"An error occurred during server startup: {e}")
        exit(1)

👉💻 لنختبر سريعًا ما إذا كان خادم A2A يبدأ بشكل صحيح محليًا ويعرض "بطاقة الوكيل". نفِّذ الأمر التالي في الوحدة الطرفية الأولى:

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd ~/instavibe-bootstrap/agents/
python -m planner.a2a_server

👉 الآن، افتح نافذة وحدة طرفية أخرى. (انقر على علامة الجمع في لوحة الجهاز الطرفي) محطتان

👉💻 استخدِم curl لطلب "بطاقة الوكيل" من الخادم الذي يتم تشغيله محليًا:

curl http://localhost:10003/.well-known/agent.json | jq

من المفترض أن يظهر تمثيل JSON لـ AgentCard الذي حدّدناه، ما يؤكّد أنّ الخادم يعمل ويعلن عن وكيل Planner.

10-02-planner-a2a.png

ارجع إلى الوحدة الطرفية الأولى (حيث يتم تشغيل الخادم) واضغط على Ctrl+C لإيقافه.

👉💻 بعد إضافة منطق خادم A2A، يمكننا الآن إنشاء صورة الحاوية.

إنشاء وكيل "المخطِّط" ونشره

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/agents

# Set variables specific to the PLANNER agent
export IMAGE_TAG="latest"
export AGENT_NAME="planner"
export IMAGE_NAME="planner-agent"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="planner-agent"
export PUBLIC_URL="https://planner-agent-${PROJECT_NUMBER}.${REGION}.run.app"

echo "Building ${AGENT_NAME} agent..."
gcloud builds submit . \
  --config=cloudbuild-build.yaml \
  --project=${PROJECT_ID} \
  --region=${REGION} \
  --substitutions=_AGENT_NAME=${AGENT_NAME},_IMAGE_PATH=${IMAGE_PATH}

echo "Image built and pushed to: ${IMAGE_PATH}"

👉💻 نشر Planner Agent على Cloud Run

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/agents

# Set variables specific to the PLANNER agent
export IMAGE_TAG="latest"
export AGENT_NAME="planner"
export IMAGE_NAME="planner-agent"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="planner-agent"
export PUBLIC_URL="https://planner-agent-${PROJECT_NUMBER}.${REGION}.run.app"


gcloud run deploy ${SERVICE_NAME} \
  --image=${IMAGE_PATH} \
  --platform=managed \
  --region=${REGION} \
  --set-env-vars="A2A_HOST=0.0.0.0" \
  --set-env-vars="A2A_PORT=8080" \
  --set-env-vars="GOOGLE_GENAI_USE_VERTEXAI=TRUE" \
  --set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
  --set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
  --set-env-vars="PUBLIC_URL=${PUBLIC_URL}" \
  --allow-unauthenticated \
  --project=${PROJECT_ID} \
  --min-instances=1

لنتأكّد من أنّ الخدمة التي تم نشرها تعمل وتعرض بطاقة الوكيل بشكلٍ صحيح من السحابة الإلكترونية باستخدام A2A Inspector.

👉 من رمز "معاينة الويب" في شريط أدوات Cloud Shell، انقر على "تغيير المنفذ". اضبط المنفذ على 8081 وانقر على "تغيير ومعاينة". سيتم فتح علامة تبويب متصفّح جديدة تتضمّن واجهة "أداة فحص التطبيقات من متجر Android إلى Android".

10-08-web-preview.png

👉💻 في نافذة الأوامر، احصل على عنوان URL الخاص بأداة التخطيط التي تم نشرها:

export PLANNER_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner-agent)
echo ${PLANNER_AGENT_URL}

👉💻 انسخ عنوان URL الناتج.

👉 في واجهة مستخدم "أداة فحص A2A"، الصِق عنوان URL في حقل "عنوان URL الخاص بالبرنامج الوكيل" وانقر على "ربط".

👀 يجب أن تظهر تفاصيل بطاقة الوكيل وJSON في علامة التبويب "بطاقة الوكيل"، ما يؤكّد نجاح عملية الربط.

10-03-planner-a2a.png

👉 انقر على علامة التبويب "المحادثة" في أداة A2A Inspector. يمكنك هنا التفاعل مباشرةً مع الوكيل الذي تم نشره، وإرسال رسالة إليه لاختبار قدرته على التخطيط. على سبيل المثال:

Plan something for me in Boston MA this weekend, and I enjoy classical music

👀 لفحص التواصل الأولي، انقر على فقاعة رسالتك ثم على فقاعة ردّ الموظف في نافذة المحادثة. عند النقر على كل منها، سيتم عرض رسالة JSON-RPC 2.0 الكاملة التي تم إرسالها أو تلقّيها، وهو أمر قيّم لتصحيح الأخطاء.

لنحتفظ بعلامة التبويب "أداة فحص A2A" في المتناول. لا تغلقها. سنستخدمه مرة أخرى بعد قليل لاختبار الوكيلَين الآخرَين.

10-06-a2a-inspector.png

برنامج وسيط للتفاعل مع المنصات (مفعّل من خلال A2A)

all-agent-platform

بعد ذلك، سنكرّر العملية نفسها مع "وكيل التفاعل مع المنصة" (الذي يستخدم MCP).

👉📝 حدِّد إعداد خادم A2A، بما في ذلك AgentCard الفريد، في نهاية ~/instavibe-bootstrap/agents/platform_mcp_client/a2a_server.py:

class PlatformAgent:
  """An agent that post event and post to instavibe."""

  SUPPORTED_CONTENT_TYPES = ["text", "text/plain"]

  def __init__(self):
    self._agent = self._build_agent()
    self.runner = Runner(
        app_name=self._agent.name,
        agent=self._agent,
        artifact_service=InMemoryArtifactService(),
        session_service=InMemorySessionService(),
        memory_service=InMemoryMemoryService(),
    )
    capabilities = AgentCapabilities(streaming=True)
    skill = AgentSkill(
            id="instavibe_posting",
            name="Post social post and events on instavibe",
            description="""
            This "Instavibe" agent helps you create posts (identifying author, text, and sentiment – inferred if unspecified) and register
            for events (gathering name, date, attendee). It efficiently collects required information and utilizes dedicated tools
            to perform these actions on your behalf, ensuring a smooth sharing experience.
            """,
            tags=["instavibe"],
            examples=["Create a post for me, the post is about my cute cat and make it positive, and I'm Alice"],
        )
    self.agent_card = AgentCard(
            name="Instavibe Posting Agent",
            description="""
            This "Instavibe" agent helps you create posts (identifying author, text, and sentiment – inferred if unspecified) and register
            for events (gathering name, date, attendee). It efficiently collects required information and utilizes dedicated tools
            to perform these actions on your behalf, ensuring a smooth sharing experience.
            """,
            url=f"{PUBLIC_URL}",
            version="1.0.0",
            defaultInputModes=PlatformAgent.SUPPORTED_CONTENT_TYPES,
            defaultOutputModes=PlatformAgent.SUPPORTED_CONTENT_TYPES,
            capabilities=capabilities,
            skills=[skill],
        )


  def get_processing_message(self) -> str:
      return "Processing the social post and event request..."

  def _build_agent(self) -> LlmAgent:
    """Builds the LLM agent for the Processing the social post and event request."""
    return agent.root_agent


if __name__ == '__main__':
    try:
        platformAgent = PlatformAgent()

        request_handler = DefaultRequestHandler(
            agent_executor=PlatformAgentExecutor(platformAgent.runner,platformAgent.agent_card),
            task_store=InMemoryTaskStore(),
        )

        server = A2AStarletteApplication(
            agent_card=platformAgent.agent_card,
            http_handler=request_handler,
        )

        uvicorn.run(server.build(), host='0.0.0.0', port=port)
    except Exception as e:
        logger.error(f"An error occurred during server startup: {e}")
        exit(1)

Social Agent (A2A Enabled)

all-agent-social

أخيرًا، لنفعّل ميزة "التطبيق إلى التطبيق" لبرنامج Social Profiling Agent.

👉📝 حدِّد إعداد خادم A2A وAgentCard في نهاية ~/instavibe-bootstrap/agents/social/a2a_server.py:

class SocialAgent:
  """An agent that handles social profile analysis."""

  SUPPORTED_CONTENT_TYPES = ["text", "text/plain"]

  def __init__(self):
    self._agent = self._build_agent()
    self.runner = Runner(
        app_name=self._agent.name,
        agent=self._agent,
        artifact_service=InMemoryArtifactService(),
        session_service=InMemorySessionService(),
        memory_service=InMemoryMemoryService(),
    )
    capabilities = AgentCapabilities(streaming=True)
    skill = AgentSkill(
                id="social_profile_analysis",
                name="Analyze Instavibe social profile",
                description="""
                Using a provided list of names, this agent synthesizes Instavibe social profile information by analyzing posts, friends, and events.
                It delivers a comprehensive single-paragraph summary for individuals, and for groups, identifies commonalities in their social activities
                and connections based on profile data.
                """,
                tags=["instavibe"],
                examples=["Can you tell me about Bob and Alice?"],
    )
    self.agent_card = AgentCard(
                name="Social Profile Agent",
                description="""
                Using a provided list of names, this agent synthesizes Instavibe social profile information by analyzing posts, friends, and events.
                It delivers a comprehensive single-paragraph summary for individuals, and for groups, identifies commonalities in their social activities
                and connections based on profile data.
                """,
                url=f"{PUBLIC_URL}",
                version="1.0.0",
                defaultInputModes=self.SUPPORTED_CONTENT_TYPES,
                defaultOutputModes=self.SUPPORTED_CONTENT_TYPES,
                capabilities=capabilities,
                skills=[skill],
    )

  def get_processing_message(self) -> str:
      return "Processing the social profile analysis request..."

  def _build_agent(self) -> LoopAgent:
    """Builds the LLM agent for the social profile analysis agent."""
    return agent.root_agent

if __name__ == '__main__':
    try:
        socialAgent = SocialAgent()

        request_handler = DefaultRequestHandler(
            agent_executor=SocialAgentExecutor(socialAgent.runner,socialAgent.agent_card),
            task_store=InMemoryTaskStore(),
        )

        server = A2AStarletteApplication(
            agent_card=socialAgent.agent_card,
            http_handler=request_handler,
        )

        uvicorn.run(server.build(), host='0.0.0.0', port=port)
    except Exception as e:
        logger.error(f"An error occurred during server startup: {e}")
        exit(1)

إنشاء ونشر وكلاء التفاعل مع المنصة ووسائل التواصل الاجتماعي

تحتاج هذه البرامج إلى إذن الوصول إلى Spanner، لذا تأكَّد من تمرير متغيرات البيئة SPANNER_INSTANCE_ID وSPANNER_DATABASE_ID وMCP_SERVER_URL بشكل صحيح أثناء عملية النشر.

👉💻 إنشاء التطبيقات ونشرها على Cloud Run باستخدام Cloud Build:

. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap/agents
export MCP_SERVER_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep mcp-tool-server)/sse


gcloud builds submit . \
  --config=cloudbuild.yaml \
  --project="${PROJECT_ID}" \
  --region="${REGION}" \
  --substitutions=\
_PROJECT_ID="${PROJECT_ID}",\
_PROJECT_NUMBER="${PROJECT_NUMBER}",\
_REGION="${REGION}",\
_REPO_NAME="${REPO_NAME}",\
_SPANNER_INSTANCE_ID="${SPANNER_INSTANCE_ID}",\
_SPANNER_DATABASE_ID="${SPANNER_DATABASE_ID}",\
_MCP_SERVER_URL="${MCP_SERVER_URL}"

👉💻 في نافذة الجهاز، احصل على عنوان URL الخاص بعميل المنصة الذي تم نشره:

export PLATFORM_MPC_CLIENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep platform-mcp-client)
echo $PLATFORM_MPC_CLIENT_URL

👉💻 انسخ عنوان URL الناتج.

👉 في واجهة مستخدم "أداة فحص A2A"، الصِق عنوان URL في حقل "عنوان URL الخاص بالبرنامج الوكيل" وانقر على "ربط".

👀 يجب أن تظهر تفاصيل بطاقة الوكيل وJSON في علامة التبويب "بطاقة الوكيل"، ما يؤكّد نجاح عملية الربط.

10-05-platform-a2a.png

👉 انقر على علامة التبويب "المحادثة" في أداة A2A Inspector. يمكنك هنا التفاعل مباشرةً مع وكيلك الذي تم نشره، وإرسال رسالة إليه لاختبار قدرته على إنشاء مشاركات:

Create a post for me, the post says 'Paws, purrs, and ocean views 🐾☕🌊. Spent my morning at the Morning Seaside Cat Café, where every sip comes with a side of snuggles and sea breeze.' and make it positive, and I'm Oscar.

👀 لفحص التواصل الأولي، انقر على فقاعة رسالتك ثم على فقاعة ردّ الموظف في نافذة المحادثة. عند النقر على كل منها، سيتم عرض رسالة JSON-RPC 2.0 الكاملة التي تم إرسالها أو تلقّيها، وهو أمر قيّم لتصحيح الأخطاء.

👉💻 في نافذة الأوامر، احصل على عنوان URL الخاص بـ "الوكيل الاجتماعي" الذي تم نشره:

export SOCIAL_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep social-agent)
echo $SOCIAL_AGENT_URL

👉💻 انسخ عنوان URL الناتج.

👉 في واجهة مستخدم "أداة فحص A2A"، الصِق عنوان URL في حقل "عنوان URL الخاص بالبرنامج الوكيل" وانقر على "ربط".

👀 يجب أن تظهر تفاصيل بطاقة الوكيل وJSON في علامة التبويب "بطاقة الوكيل"، ما يؤكّد نجاح عملية الربط.

10-04-social-a2a.png

👉 انقر على علامة التبويب "المحادثة" في أداة A2A Inspector. هذا هو المكان الذي يمكنك فيه التفاعل مباشرةً مع وكيلك الذي تم نشره، وإرسال رسالة إليه لتحليل ملفات تعريف المستخدمين من قاعدة البيانات:

Can you tell me about both Ian and Kevin's profile, what are their common interests?

👀 لفحص التواصل الأولي، انقر على فقاعة رسالتك ثم على فقاعة ردّ الموظف في نافذة المحادثة. عند النقر على كل منها، سيتم عرض رسالة JSON-RPC 2.0 الكاملة التي تم إرسالها أو تلقّيها، وهو أمر قيّم لتصحيح الأخطاء.

👉 رائع، لقد انتهينا من فحص جميع وكلائنا. يمكنك إغلاق علامة التبويب "أداة فحص التطبيقات على Android" الآن.

11. Orchestrator Agent (عميل A2A)

لدينا الآن ثلاثة وكلاء متخصصين (المخطط، والمنصة، والشبكات الاجتماعية) يعملون كخدمات مستقلة ومفعّلة من تطبيق إلى تطبيق على Cloud Run. العنصر الأخير هو Orchestrator Agent. سيعمل هذا الوكيل كمنسّق مركزي أو عميل A2A. سيتلقّى الطلبات من المستخدمين، ويحدّد الوكلاء البعيدين المطلوبين لتنفيذ الطلب (ربما بالتسلسل)، ثم يستخدم بروتوكول A2A لتفويض المهام إلى هؤلاء الوكلاء البعيدين. في ورشة العمل هذه، سنشغّل وكيل Orchestrator محليًا باستخدام واجهة مستخدم ADK Dev.

all-agent-orchestrator

أولاً، لنحسّن منطق Orchestrator للتعامل مع تسجيل الوكلاء البعيدين الذين يعثر عليهم. يخزِّن تفاصيل الاتصال من "بطاقات الوكيل" التي تم جلبها أثناء عملية الإعداد.

👉📝 في ~/instavibe-bootstrap/agents/orchestrate/agent.py، استبدِل #REPLACE ME REG AGENT CARD بما يلي:

async with httpx.AsyncClient(timeout=30) as client:
            for i, address in enumerate(REMOTE_AGENT_ADDRESSES):
                log.info(f"--- STEP 3.{i}: Attempting connection to: {address} ---")
                try:
                    card_resolver = A2ACardResolver(client, address)
                    card = await card_resolver.get_agent_card()
                    
                    remote_connection = RemoteAgentConnections(agent_card=card, agent_url=address)
                    self.remote_agent_connections[card.name] = remote_connection
                    self.cards[card.name] = card
                    log.info(f"--- STEP 5.{i}: Successfully stored connection for {card.name} ---")

                except Exception as e:
                    log.error(f"--- CRITICAL FAILURE at STEP 4.{i} for address: {address} ---")
                    log.error(f"--- The hidden exception type is: {type(e).__name__} ---")
                    log.error(f"--- Full exception details and traceback: ---", exc_info=True)

بعد ذلك، حدِّد أداة وكيل Orchestrator نفسه ضمن "حزمة تطوير التطبيقات".

  • send_message (دالة A2A لتفويض العمل)

👉📝 استبدِل #REPLACE ME CREATE AGENT في ~/instavibe-bootstrap/agents/orchestrate/agent.py بما يلي:

def create_agent(self) -> Agent:
        """Synchronously creates the ADK Agent object."""
        return Agent(
            model="gemini-2.5-flash",
            name="orchestrate_agent",
            instruction=self.root_instruction,
            before_agent_callback=self.before_agent_callback,
            description=("Orchestrates tasks for child agents."),
            tools=[self.send_message], 
        )

تكمن المنطق الأساسي لـ Orchestrator في التعليمات التي تحدّد كيفية استخدام A2A.

👉📝 استبدِل #REPLACE ME INSTRUCTIONS في ~/instavibe-bootstrap/agents/orchestrate/agent.py بطريقة إنشاء التعليمات التالية:

def root_instruction(self, context: ReadonlyContext) -> str:
        current_agent = self.check_active_agent(context)
        return f"""
                You are an expert AI Orchestrator. Your primary responsibility is to intelligently interpret user requests, break them down into a logical plan of discrete actions, and delegate each action to the most appropriate specialized remote agent using the send_message function. You do not perform the tasks yourself but manage their assignment, sequence, and critically, their outcomes.
                    **Core Directives & Decision Making:**

                    *   **Understand User Intent & Complexity:**
                        *   Carefully analyze the user's request to determine the core task(s) they want to achieve. Pay close attention to keywords and the overall goal.
                        *   Identify if the request requires a single agent or a sequence of actions from multiple agents. For example, "Analyze John Doe's profile and then create a positive post about his recent event attendance" would require two agents in sequence.

                    *   **Task Planning & Sequencing (for Multi-Step Requests):**
                        *   Before delegating, outline the clear sequence of agent tasks.
                        *   Identify dependencies. If Task B requires output from Task A, execute them sequentially. If tasks are independent (like creating a post and then creating an event), execute them one after the other as separate delegations.
                        *   Agent Reusability: An agent's completion of one task does not make it unavailable. If a user's plan involves multiple, distinct actions that fall under the same agent's expertise (e.g., create a post, then create an event), you must call that same agent again for the subsequent task.

                    *   **Task Delegation & Management (using `send_message`):**
                        *   **Delegation:** Use `send_message` to assign actionable tasks to the selected remote agent. Your `send_message` call MUST include:
                            *   The `remote_agent_name` you've selected.
                            *   The `user_request` or all necessary parameters extracted from the user's input, formatted in a way the target agent will understand.
                        *   **Contextual Awareness for Remote Agents:** If a remote agent repeatedly requests user confirmation or seems to lack context, assume it lacks access to the full conversation history. In such cases, enrich your `send_message` with all necessary contextual information relevant to that specific agent from the conversation history.
                        *   **Sequential Task Execution:**
                            *   After a preceding task completes (indicated by the agent's response or a success signal), gather any necessary output from it.
                            *   Then, use `send_message` for the next agent in the sequence, providing it with the user's original relevant intent and any necessary data obtained from the previous agent's task.
                        *   **Active Agent Prioritization:** If an active agent is already engaged and the user's request is related to its current task, route subsequent related requests directly to that agent by providing updated context via `send_message`.
                    
                    
                    **Critical Success Verification:**

                    *   You **MUST** wait for the tool_output after every send_message call before taking any further action.
                    *   Your decision to proceed to the next task in a sequence **MUST** be based entirely on a confirmation of success from the tool_output of the previous task.
                    *   If a tool call fails, returns an error, or the tool_output is ambiguous, you MUST STOP the sequence. Your next action is to report the exact failure or ambiguity to the user.
                    *   DO NOT assume a task was successful. Do not invent success messages like "The event has been created." Only state that a task is complete if the tool's response explicitly says so.
                    
                    **Communication with User:**

                    *   **Transparent Communication:** Always present the complete and detailed response from the remote agent to the user. Do not summarize or filter unless explicitly instructed.
                    *   When you delegate a task (or the first task in a sequence), clearly inform the user which remote agent is handling it.
                    *   For multi-step requests, you can optionally inform the user of the planned sequence (e.g., "Okay, first I'll ask the 'Social Profile Agent' to analyze the profile, and then I'll have the 'Instavibe Posting Agent' create the post.").
                    *   If waiting for a task in a sequence to complete, you can inform the user (e.g., "The 'Social Profile Agent' is currently processing. I'll proceed with the post once that's done.").
                    *   **User Confirmation Relay:** If a remote agent asks for confirmation, and the user has not already provided it, just make up something.
                    *   If the user's request is ambiguous, if necessary information is missing for any agent in the sequence, or if you are unsure about the plan, just make up something.

                    **Important Reminders:**

                    *   **Autonomous Agent Engagement:** Never seek user permission before engaging with remote agents. If multiple agents are required to fulfill a request, connect with them directly without requesting user preference or confirmation.
                    *   **Focused Information Sharing:** Provide remote agents with only relevant contextual information. Avoid extraneous details that are not directly pertinent to their task.
                    *   **No Redundant Confirmations:** Do not ask remote agents for confirmation of information or actions they have already processed or committed to.
                    *   **Tool Reliance:** Strictly rely on your available tools, primarily `send_message`, to address user requests. Do not generate responses based on assumptions. If information is insufficient, request clarification from the user.
                    *   **Prioritize Recent Interaction:** Focus primarily on the most recent parts of the conversation when processing requests, while maintaining awareness of the overall goal for multi-step tasks.
                    *   Always prioritize selecting the correct agent(s) based on their documented purpose.
                    *   Ensure all information required by the chosen remote agent is included in the `send_message` call, including outputs from previous agents if it's a sequential task.

                    Agents:
                    {self.agents}

                    Current agent: {current_agent['active_agent']}`
                """

اختبار أداة Orchestrator ونظام A2A الكامل

لنختبر الآن النظام بأكمله. سنشغّل Orchestrator محليًا باستخدام واجهة مستخدم ADK Dev، وسيتواصل مع وكلاء Planner وPlatform وSocial الذين يتم تشغيلهم عن بُعد على Cloud Run.

👉💻 أولاً، تأكَّد من أنّ متغيّر البيئة REMOTE_AGENT_ADDRESSES يحتوي على عناوين URL مفصولة بفواصل للوكلاء الذين تم نشرهم والمفعَّلة فيهم ميزة "التطبيق إلى التطبيق". بعد ذلك، اضبط متغيرات البيئة اللازمة لوكيل Orchestrator وشغِّل واجهة مستخدم ADK Dev:

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate

export PLATFORM_MPC_CLIENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep platform-mcp-client)
export PLANNER_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner-agent)
export SOCIAL_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep social-agent)

export REMOTE_AGENT_ADDRESSES=${PLANNER_AGENT_URL},${PLATFORM_MPC_CLIENT_URL},${SOCIAL_AGENT_URL}

cd  ~/instavibe-bootstrap/agents
sed -i "s|^\(O\?REMOTE_AGENT_ADDRESSES\)=.*|REMOTE_AGENT_ADDRESSES=${REMOTE_AGENT_ADDRESSES}|" ~/instavibe-bootstrap/agents/orchestrate/.env
adk web

👉 افتح واجهة مستخدم ADK Dev (غيِّر المنفذ إلى 8000 من خلال "معاينة الويب").

10-08-web-preview.png

👉 في القائمة المنسدلة الخاصة بالوكيل، اختَر الوكيل orchestrate.

👉 الآن، امنحها مهمة معقّدة تتطلّب تنسيق عدة وكلاء عن بُعد. جرِّب المثال الأول التالي الذي يجب أن يتضمّن "الوكيل الاجتماعي" ثم "وكيل التخطيط":

You are an expert event planner for a user named  Diana.
    Your task is to design a fun and personalized event.

    Here are the details for the plan:
    - Friends to invite: Ian, Nora
    - Desired date: "2025-10-15"
    - Location idea or general preference: "Chicago"

    Your process should be:
    1. Analyze the provided friend names. If you have access to a tool to get their InstaVibe profiles or summarized interests, please use it.
    2. Based on their potential interests (or general good taste if profiles are unavailable), create a tailored plan for the outing, check if you have access to any event planner tools.
    3. Ensure the plan includes the original `planned_date`.

    The user wants a comprehensive plan that includes:
    - The list of invited friends.
    - A catchy and descriptive name for the event.
    - The exact planned date for the event.
    - A summary of what the group will do.
    - Specific recommended spots (e.g., restaurants, bars, activity venues) with their names, (if possible, approximate latitude/longitude for mapping, and address), and a brief description of why it fits the plan.
    - A short, exciting message that {Diana} can send to {Ian, Nora} to get them excited about the event.

التنسيق

راقِب التفاعل في نافذة محادثة "واجهة مستخدم أدوات تطوير ADK". انتبه جيدًا لردود Orchestrator، إذ يجب أن يوضّح الوكيل البعيد الذي يفوّض إليه المهام (مثلاً حسنًا، سأطلب من Social Profile Agent معلومات عن إيان ونورا أولاً...").

تحقَّق أيضًا من علامة التبويب "الأحداث" في واجهة المستخدم للاطّلاع على طلبات الأدوات الأساسية (send_message) التي يتم إجراؤها على عناوين URL الخاصة بالوكلاء البعيدين.

إرسال المهمة

👉 الآن، جرِّب مثالاً ثانيًا يجب أن يتضمّن "وكيل تكامل المنصة" مباشرةً:

Hey, can you register an event on Instavibe for Laura and Charlie? Let's call it 'Vienna Concert & Castles Day'.
here are more info
"event_name": "Vienna Concert & Castles Day",
  "description": "A refined and unforgettable day in Vienna with Laura and Charlie. The day begins with a guided tour of the magnificent Schönbrunn Palace, showcasing imperial architecture and history. In the evening, enjoy a classical music concert in one of Vienna's most iconic concert halls.",
  "event_date": "2025-10-14T10:00:00+02:00",
  "locations": [
    {
      "name": "Schönbrunn Palace",
      "description": "A UNESCO World Heritage Site and former imperial summer residence, Schönbrunn Palace offers opulent rooms, beautiful baroque gardens, and a glimpse into the life of the Habsburg monarchy. Visitors can stroll the grounds or take a guided historical tour.",
      "latitude": 48.184516,
      "longitude": 16.312222,
      "address": "Schönbrunner Schloßstraße 47, 1130 Wien, Austria"
    },
    {
      "name": "Musikverein Vienna",
      "description": "Home to the world-renowned Vienna Philharmonic, the Musikverein is one of the finest concert halls in the world. Its 'Golden Hall' is famous for its acoustics and ornate design. Attendees can enjoy a powerful classical concert in an unforgettable setting.",
      "latitude": 48.200132,
      "longitude": 16.373777,
      "address": "Musikvereinsplatz 1, 1010 Wien, Austria"
    }
  ],
  "attendee_names": ["Laura", "Charlie", "Oscar"] And I am Oscar

كرِّر الخطوات السابقة لمراقبة المحادثة وعلامة التبويب "الأحداث". يجب أن يحدّد Orchestrator الحاجة إلى إنشاء حدث وتفويض المهمة (مع جميع التفاصيل المقدَّمة) إلى "وكيل دمج المنصات". يمكنك أيضًا النقر على الزر تتبُّع لعرض عمليات التتبُّع من أجل تحليل أوقات استجابة طلبات البحث والعمليات المنفَّذة. إرسال حدث

يمكنك بعد ذلك التأكّد من ظهور الحدث في تطبيق InstaVibe على الويب. حدث InstaVibe

يوضّح هذا السيناريو عملية التنفيذ الناجحة لنظام يستند إلى عدّة وكلاء باستخدام "حزمة تطوير التطبيقات" وبروتوكول A2A، حيث يفوّض منسّق مركزي المهام إلى وكلاء متخصصين عن بُعد.

تذكَّر إيقاف واجهة مستخدم ADK Dev (Ctrl+C في نافذة الأوامر) عند الانتهاء من الاختبار.

12. ‫Agent Engine و"الاتصال عن بُعد" من InstaVibe

حتى الآن، شغّلنا الوكلاء المتخصّصين على Cloud Run واختبرنا Orchestrator محليًا باستخدام واجهة مستخدم ADK Dev. في سيناريو الإنتاج، نحتاج إلى بيئة قوية وقابلة للتوسّع ومُدارة لاستضافة الوكلاء. وهنا يأتي دور Google Vertex AI Agent Engine.

‫Agent Engine هي خدمة مُدارة بالكامل على Vertex AI ومصمَّمة خصيصًا لنشر برامج وكلاء الذكاء الاصطناعي وتوسيع نطاقها. ويجرّد هذا الإطار إدارة البنية التحتية والأمان والنفقات التشغيلية، ما يتيح للمطوّرين (خاصةً أولئك الذين ليسوا على دراية كبيرة ببيئات السحابة الإلكترونية المعقّدة) التركيز على منطق الوكيل وإمكاناته بدلاً من إدارة الخوادم. يوفر وقت تشغيل مخصّصًا محسّنًا لأحمال العمل المستندة إلى الوكلاء.

سننشر الآن وكيل Orchestrator في Agent Engine. (ملاحظة: تستخدم آلية النشر الموضّحة أدناه نصًا برمجيًا مخصّصًا (agent_engine_app.py) متوفّرًا في مواد ورشة العمل، لأنّ أدوات النشر الرسمية المباشرة من ADK إلى Agent-Engine قد تكون لا تزال قيد التطوير. يتولّى هذا النص البرمجي عملية تجميع وكيل Orchestrator ونشره، مع ضبطه باستخدام عناوين الوكيل البعيد اللازمة.)

نفِّذ الأمر التالي لنشر وكيل Orchestrator في Agent Engine. تأكَّد من أنّ متغيّر البيئة REMOTE_AGENT_ADDRESSES (الذي يحتوي على عناوين URL الخاصة ببرامج Planner وPlatform وSocial على Cloud Run) لا يزال مضبوطًا بشكل صحيح من القسم السابق.

👉💻 سننشر وكيل Orchestrate في Agent Engine (ملاحظة: هذا هو التنفيذ الخاص بي لعملية النشر، ويتضمّن ADK واجهة سطر أوامر للمساعدة في عملية النشر، وسأعدّل هذه الخطوة بعد تنفيذ BYO-SA).

cd ~/instavibe-bootstrap/agents/
. ~/instavibe-bootstrap/set_env.sh

gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member="serviceAccount:service-$PROJECT_NUMBER@gcp-sa-aiplatform-re.iam.gserviceaccount.com" \
    --role="roles/viewer"


source ~/instavibe-bootstrap/env/bin/activate
export PLATFORM_MPC_CLIENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep platform-mcp-client)
export PLANNER_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner-agent)
export SOCIAL_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep social-agent)

export REMOTE_AGENT_ADDRESSES=${PLANNER_AGENT_URL},${PLATFORM_MPC_CLIENT_URL},${SOCIAL_AGENT_URL}
sed -i "s|^\(O\?REMOTE_AGENT_ADDRESSES\)=.*|REMOTE_AGENT_ADDRESSES=${REMOTE_AGENT_ADDRESSES}|" ~/instavibe-bootstrap/agents/orchestrate/.env

adk deploy agent_engine \
--display_name "orchestrate-agent" \
--project $GOOGLE_CLOUD_PROJECT \
--region $GOOGLE_CLOUD_LOCATION \
--staging_bucket gs://$GOOGLE_CLOUD_PROJECT-agent-engine \
--trace_to_cloud \
--requirements_file orchestrate/requirements.txt \
orchestrate

بعد استضافة Orchestrator على منصة Agent Engine المُدارة، يجب أن يتواصل تطبيق الويب InstaVibe معه. بدلاً من التفاعل من خلال واجهة المستخدم الخاصة بأداة تطوير ADK، سيُجري تطبيق الويب مكالمات عن بُعد إلى نقطة نهاية Agent Engine.

10-agent-remote.png

أولاً، علينا تعديل رمز تطبيق InstaVibe لإعداد برنامج Agent Engine باستخدام المعرّف الفريد لبرنامج Orchestrator الذي تم نشره. هذا المعرّف مطلوب لاستهداف مثيل الوكيل الصحيح على المنصة.

👉📝 افتح ~/instavibe-bootstrap/instavibe/introvertally.py واستبدِل #REPLACE ME initiate agent_engine بالرمز التالي. يستردّ هذا الرمز معرّف Agent Engine من متغيّر بيئة (سنحدّده قريبًا) ويحصل على عنصر عميل:

ORCHESTRATE_AGENT_ID = os.environ.get('ORCHESTRATE_AGENT_ID')
agent_engine = agent_engines.get(ORCHESTRATE_AGENT_ID)

يتضمّن مسار المستخدم المخطّط له في InstaVibe تفاعلين مع الوكيل: أولاً، إنشاء الخطة المقترَحة، وثانيًا، الطلب من المستخدم تأكيد الخطة قبل أن ينشرها الوكيل على المنصة.

بما أنّ تطبيق الويب InstaVibe (الذي يعمل على Cloud Run) وبرنامج Orchestrator (الذي يعمل على Agent Engine) أصبحا الآن خدمتين منفصلتين، يحتاج تطبيق الويب إلى إجراء مكالمات عن بُعد إلى نقطة نهاية Agent Engine للتفاعل مع البرنامج.

👉📝 لنعدّل الرمز الذي يجري عملية الاتصال الأولية لإنشاء اقتراح الخطة. في ملف introvertally.py نفسه، استبدِل #REPLACE ME Query remote agent get plan بالمقتطف التالي الذي يستخدم عميل agent_engine لإرسال طلب المستخدم:

agent_engine.stream_query(
                user_id=user_id,
                message=prompt_message,
            )

👉📝 بعد ذلك، عدِّل الرمز الذي يتعامل مع تأكيد المستخدم (على سبيل المثال، عندما ينقر المستخدم على "تأكيد الخطة"). يؤدي ذلك إلى إرسال رسالة متابعة إلى المحادثة نفسها على Agent Engine، مع توجيه Orchestrator بمواصلة نشر الحدث (الذي سيتم تفويضه إلى موظف Platform Integration). استبدِل #REPLACE ME Query remote agent for confirmation للتأكيد في introvertally.py بما يلي:

agent_engine.stream_query(
            user_id=agent_session_user_id,
            message=prompt_message,
        )

تحتاج مسارات تطبيق الويب إلى إذن الوصول إلى هذه الدوال. تأكَّد من استيراد الدوال اللازمة من introvertally.py في ملف مسارات Flask.

👉📝 في cd ~/instavibe-bootstrap/instavibe/ally_routes.py، سنشير أولاً إلى استبدال المثيل # REPLACE ME TO ADD IMPORT بما يلي:

from introvertally import call_agent_for_plan, post_plan_event

👉📝 أضِف ميزة النموذج الأولي إلى InstaVibe، وفي ~/instavibe-bootstrap/instavibe/templates/base.html، استبدِل <!–REPLACE_ME_LINK_TO_INTROVERT_ALLY–> بما يلي:

            <li class="nav-item">
              <a class="nav-link" href="{{ url_for('ally.introvert_ally_page') }}">Introvert Ally</a>
            </li>

قبل إعادة نشر تطبيق InstaVibe، نحتاج إلى Resource ID محدّد لوكيل Orchestrator الذي نشرناه في Agent Engine.

في الوقت الحالي، قد تكون عملية استرداد هذا المعرّف آليًا من خلال gcloud محدودة، لذا سنستخدم نصًا برمجيًا مساعدًا بلغة Python (temp-endpoint.py متوفّر في ورشة العمل) لجلب المعرّف وتخزينه في متغيّر بيئة.

👉💻 شغِّل الأوامر التالية لتنفيذ النص البرمجي. سيلتقط النص البرمجي معرّف نقطة نهاية Agent Engine ويمنح الأذونات اللازمة لحساب الخدمة التلقائي في Agent Engine (ملاحظة: تم ضبط النص البرمجي لاستخدام حساب الخدمة التلقائي لأنّه لا يمكن للمستخدم تعديله حاليًا).

. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap/instavibe/
source ~/instavibe-bootstrap/env/bin/activate
python temp-endpoint.py
export ORCHESTRATE_AGENT_ID=$(cat temp_endpoint.txt)
echo "ORCHESTRATE_AGENT_ID set to: ${ORCHESTRATE_AGENT_ID}"

رقم تعريف نقطة نهاية محرّك الوكيل

أخيرًا، علينا إعادة نشر تطبيق InstaVibe على الويب باستخدام الرمز المعدَّل ومتغيّر البيئة الجديد ORCHESTRATE_AGENT_ID لكي يعرف كيفية الاتصال بالوكيل الذي يعمل على Agent Engine.

👉💻 تعيد الأوامر التالية إنشاء صورة تطبيق InstaVibe ونشر الإصدار الجديد على Cloud Run:

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/instavibe/

export IMAGE_TAG="latest"
export APP_FOLDER_NAME="instavibe"
export IMAGE_NAME="instavibe-webapp"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="instavibe"

echo "Building ${APP_FOLDER_NAME} webapp image..."
gcloud builds submit . \
  --tag=${IMAGE_PATH} \
  --project=${PROJECT_ID}

echo "Deploying ${SERVICE_NAME} to Cloud Run..."

gcloud run deploy ${SERVICE_NAME} \
  --image=${IMAGE_PATH} \
  --platform=managed \
  --region=${REGION} \
  --allow-unauthenticated \
  --set-env-vars="SPANNER_INSTANCE_ID=${SPANNER_INSTANCE_ID}" \
  --set-env-vars="SPANNER_DATABASE_ID=${SPANNER_DATABASE_ID}" \
  --set-env-vars="APP_HOST=0.0.0.0" \
  --set-env-vars="APP_PORT=8080" \
  --set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
  --set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
  --set-env-vars="GOOGLE_MAPS_API_KEY=${GOOGLE_MAPS_API_KEY}" \
  --set-env-vars="ORCHESTRATE_AGENT_ID=${ORCHESTRATE_AGENT_ID}" \
  --project=${PROJECT_ID} \
  --min-instances=1 \
  --cpu=2 \
  --memory=2Gi

بعد اكتمال عملية النشر النهائية، انتقِل إلى عنوان URL لتطبيق InstaVibe في علامة تبويب مختلفة في المتصفّح.

اختبار تجربة InstaVibe الكاملة المستندة إلى الذكاء الاصطناعي

تتوفّر الآن ميزة "رفيق InstaVibe"، وهي تستند إلى نظامنا المتعدد الوكلاء الذي يتم تنسيقه من خلال Vertex AI Agent Engine ويتواصل عبر A2A.

12-02-new.png

انقر على "InstaVibe Ally" واطلب منه التخطيط لفعالية.

12-03-introvertally.png

راقِب سجلّ الأنشطة على يسار الشاشة أثناء عمل الوكلاء (قد يستغرق ذلك من 90 إلى 120 ثانية). بعد ظهور الخطة، راجِعها وانقر على "تأكيد هذه الخطة" للمتابعة والنشر.

12-04-confirm.png

سيطلب المنسّق الآن من وكيل المنصة إنشاء المشاركة والحدث في InstaVibe. 12-05-posting.png

يمكنك الاطّلاع على المنشور والحدث الجديدَين في الصفحة الرئيسية لتطبيق InstaVibe. 12-06-instavibe.png

ستعرض صفحة الحدث التفاصيل التي أنشأها الوكيل.

12-07-event.png

تحليل الأداء باستخدام Cloud Trace

قد تستغرق هذه العملية بعض الوقت. يتكامل Vertex AI Agent Engine مع Cloud Trace، ما يسمح لنا بتحليل وقت الاستجابة لنظامنا المتعدد الوكلاء.

انتقِل إلى عمليات التتبُّع في Google Cloud Console، واختَر agent_run[orchestrate_agent] في "المدّة"، من المفترض أن تظهر لك مدّتان، انقر على إحداهما

12-08-trace.png

ضمن تفاصيل التتبُّع، يمكنك تحديد الأجزاء التي استغرقت وقتًا أطول. على سبيل المثال، قد تستغرق المكالمات التي يتم إجراؤها مع وكيل "المخطّط" وقتًا أطول بسبب البحث عن معلومات أساسية وإنشاء محتوى معقّد. 12-09-plan.png

وبالمثل، عند إنشاء المشاركة والحدث، قد ترى الوقت الذي استغرقه Orchestrator في معالجة البيانات وإعداد طلبات الأدوات لوكيل النظام الأساسي. 12-10-post.png

يساعد استكشاف هذه التتبُّعات في فهم أداء نظامك المستند إلى الذكاء الاصطناعي وتحسينه.

celebrate.png

تهانينا! لقد نجحت في إنشاء نظام ذكاء اصطناعي متطوّر ومتعدّد الوكلاء ونشره واختباره باستخدام "حزمة تطوير التطبيقات" و"الوكيل إلى الوكيل" و"منصة التحكم المتعدّدة" وخدمات Google Cloud. لقد تعاملت مع تنسيق الوكلاء واستخدام الأدوات وإدارة الحالة والنشر على السحابة الإلكترونية، ما أدّى إلى إنشاء ميزة عملية مستندة إلى الذكاء الاصطناعي في InstaVibe. تهانينا على إكمال ورشة العمل.

13. تنظيف

لتجنُّب تحصيل رسوم مستمرة من حسابك على Google Cloud، من المهم حذف الموارد التي أنشأناها خلال ورشة العمل هذه. ستساعدك الأوامر التالية في إزالة مثيل Spanner وخدمات Cloud Run ومستودع Artifact Registry ومفتاح واجهة برمجة التطبيقات وVertex AI Agent Engine وأذونات إدارة الهوية وإمكانية الوصول المرتبطة بها.

ملاحظات مهمّة:

  • تأكَّد من تنفيذ هذه الأوامر في مشروع Google Cloud نفسه المستخدَم في ورشة العمل.
  • إذا أغلقت نافذة Cloud Shell، قد لا يتم ضبط بعض متغيرات البيئة، مثل ‎ $PROJECT_ID و‎$SPANNER_INSTANCE_ID وما إلى ذلك. عليك إعادة تصديرها كما فعلت أثناء إعداد ورشة العمل أو استبدال المتغيّرات في الأوامر أدناه بقيمها الفعلية.
  • ستؤدي هذه الأوامر إلى حذف مواردك نهائيًا. يُرجى التحقّق جيدًا قبل التشغيل إذا كانت لديك بيانات مهمة أخرى في هذا المشروع.

👉💻 شغِّل النصوص البرمجية التالية لتنظيف البيانات.

إعادة ضبط متغيّرات البيئة

. ~/instavibe-bootstrap/set_env.sh

حذف محرك الوكيل:

cd ~/instavibe-bootstrap/utils
source ~/instavibe-bootstrap/env/bin/activate
export ORCHESTRATE_AGENT_ID=$(cat ~/instavibe-bootstrap/instavibe/temp_endpoint.txt)
echo "ORCHESTRATE_AGENT_ID set to: ${ORCHESTRATE_AGENT_ID}"
python remote_delete.py
deactivate
echo "Vertex AI Agent Engine deletion initiated."

حذف خدمات Cloud Run:

# InstaVibe Web Application
gcloud run services delete instavibe --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet

# MCP Tool Server
gcloud run services delete mcp-tool-server --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet

# Planner Agent (A2A Server)
gcloud run services delete planner-agent --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet

# Platform MCP Client Agent (A2A Server)
gcloud run services delete platform-mcp-client --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet

# Social Agent (A2A Server)
gcloud run services delete social-agent --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet

echo "Cloud Run services deletion initiated."

إيقاف حاوية Docker الخاصة بأداة A2A Inspector وإزالتها

docker rm --force a2a-inspector

حذف مثيل Spanner:

echo "Deleting Spanner instance: ${SPANNER_INSTANCE_ID}..."
gcloud spanner instances delete ${SPANNER_INSTANCE_ID} --project=${PROJECT_ID} --quiet
echo "Spanner instance deletion initiated."

حذف مستودع Artifact Registry:

echo "Deleting Artifact Registry repository: ${REPO_NAME}..."
gcloud artifacts repositories delete ${REPO_NAME} --location=${REGION} --project=${PROJECT_ID} --quiet
echo "Artifact Registry repository deletion initiated."

إزالة الأدوار من حساب الخدمة:

echo "Removing roles from service account: $SERVICE_ACCOUNT_NAME in project $PROJECT_ID"

# Remove Project-level roles for default service account
gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/spanner.admin"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/spanner.databaseUser"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/artifactregistry.admin"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/cloudbuild.builds.editor"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/run.admin"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/iam.serviceAccountUser"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/aiplatform.user"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/logging.logWriter"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/logging.viewer"


echo "All specified roles have been removed."

حذف ملفات "ورشة العمل" المحلية:

echo "Removing local workshop directory ~/instavibe-bootstrap..."
rm -rf ~/instavibe-bootstrap
rm -rf ~/a2a-inspector
rm -f ~/mapkey.txt
rm -f ~/project_id.txt
echo "Local directory removed."

تنظيف