1. مقدمة
ورشة عمل مدتها 90 دقيقة حول النموذج التمييزي، وهو نموذج اتخاذ القرار "النظام 1" من TypeSafe AI، وحول وضعه بجانب Gemini في سير عمل Google ADK. تتضمّن ورشة العمل هذه ست خطوات تتمحور حول لعبة قتال. ستقاتل الغول بيدك أولاً، ثم ستنقل ردود الفعل إلى النموذج التمييزي، ثم ستشاهد سير عمل ADK يفوز بالمعركة من خلال النموذج التمييزي الذي يقرر كل خطوة، وGemini يقرأ بطاقات التعويذات من الشاشة لترديدها.

نظرة عامة
النموذج التمييزي (jev-1.13، الاسم المستعار jev-latest) هو نموذج مستضاف من TypeSafe AI، وتم إصداره في 19 سبتمبر 2026. لا ينشئ هذا التطبيق نصًا. يمكنك إرسال حالة (نص أو JSON أو قائمة) وأسئلة مكتوبة (Choice أو Score أو Noul)، وستردّ عليك بإجابات مكتوبة مع احتمالات معايرة، وذلك في غضون 70 إلى 500 ملي ثانية تقريبًا، مقابل 0.042 دولار أمريكي لكل مليون رمز مميز في الإدخال، وبدون أي تكلفة على الإخراج. تتمثّل مهمته في اتخاذ القرار أمام نماذج اللغة وفي ما بينها وخلفها، أي التوجيه والتصنيف والحماية، وفي هذه الحالة، ردود فعل المقاتل.
لعبة كمثال

هل سبق لك أن لعبت لعبة قتالية؟ تخوض مباراة ضد خصم وعليك أن تستجيب على الفور لتحرّكاته. إذا أخطأت في التخمين، ستنخفض نقاط صحتك. تميل الألعاب أيضًا إلى جعل إلقاء التعاويذ صعبًا. في لعبتنا، عليك اختيار لون بطاقة التعويذة وأشكالها بالترتيب قبل إطلاق التعويذة. توضّح لك ورشة العمل هذه كيفية الجمع بين نوعَي النماذج لجعل شخصيتك تفوز.
يرتبط كل عنصر من عناصر اللعبة بنظام حقيقي:
- نقلة الخصم هي حدث وارد، مثل طلب أو معاملة.
- الردّ هو قرار محدود يتخذه النموذج التمييزي ويتم التحقّق منه باستخدام الرمز.
- بطاقة التدقيق الإملائي هي إدخال غير منظَّم يحتاج إلى نموذج لغة لقراءته.
- المطابقة هي سير العمل، حيث يتم تنفيذ المهام السريعة والبطيئة بالسرعات المناسبة لها.
وينصب التركيز على الجمع بين المكوّنات الأربعة وربطها ببعضها لإنشاء نظام سريع وذكي.
ما ستتعلمه
- توضيح الفرق بين النماذج التمييزية (النظام 1) والتوليدية (النظام 2) ومتى يجب استخدام كل منها
- توضيح كيفية عرض Jev وDiffusionGemma، وإعداد أحدهما لورشة العمل، بما في ذلك DiffusionGemma على جهاز افتراضي مزوّد بوحدة معالجة الرسومات (GPU) في Compute Engine
- كتابة أسئلة الاختيار من متعدد والأسئلة التي تتضمّن درجة وأسئلة Noul، وتفسير الاحتمالات ومستوى الثقة
- استخدِم الحدود في الرموز المحدّدة لتحويل الاحتمالات إلى إجراءات.
- أنشئ طلبًا باستخدام حزمة تطوير البرامج TypeSafe، ثمّ اسمح للنموذج باختيار كل خطوة في إحدى الألعاب.
- أنشِئ فرعًا بطيئًا يقرأ فيه Gemini صورة، وفرعًا سريعًا يقرّر فيه النموذج التمييزي في حلقة، وشغِّل كل فرع على حدة.
- يمكنك دمج الفرعين في سير عمل رسم بياني لـ ADK يشارك الحالة في حلقة معالجة أحداث واحدة، وبالتالي لن يؤدي العمل البطيء إلى تأخير اتخاذ القرارات السريعة.
الهندسة المعمارية
تتوفّر منصة العمل في Cloud Shell(أو جهازك)، وستكتب في نظام الملفات المحلي وتتفاعل مع ساحة اللعب ومع Gemini ونموذج اتخاذ القرار. يتم استدعاء نموذج القرار في ② في "المقارنة بين النماذج التمييزية"، ويتم استدعاء كلا النموذجين في ③ في "المقارنة بين سير العمل".

مَن يطلق أي اسم. يتواصل المتصفّح مع ① فقط، ويتم استدعاء كلا النموذجين من Python على الجهاز:
المُتصِل | النموذج التمييزي | Gemini |
② ساحة المنافسة، "مقارنة النماذج التمييزية" | كل علامة صح، | لا |
③ سير العمل | كل علامة صح، | لا |
③ سير العمل | لا | صورة بطاقة التعويذة؛ الحكاية بعد القتال |
| نعم | لا |
علامة واحدة لكل وضع
- عليك القتال. تطلب الصفحة ② إدخال تلغراف، وتعرضه مع مؤقت لمدة ثانيتين، ثم تنشر الزر الذي تضغط عليه (أو الكلمة التي تكتبها). ② يحلّ المشكلة.
- المنافسة بين النماذج التمييزية تطلب الصفحة ② وضع علامة صح، وترسم ② برقية، وتطرح على النموذج ثلاثة أسئلة في مكالمة واحدة، وتشغّل
choose()، وتعرض الإجابات والنتيجة. ترسم الصفحة الأشرطة. - مباريات سير العمل: يبدأ التطبيق "Start" عملية التشغيل ② كعملية فرعية (تسجيل الدخول
runs/arena-workflow.log). وتدير العملية ③ المعركة: تطلب من العملية ② كل إشارة، وتستدعي النموذج، وتنشر القرار. ويصل سحر Gemini في فرعه الخاص عندما يكون جاهزًا. تُجري الصفحة الاستطلاع ② فقط وتُظهر النتائج. التوقّف المؤقت هو علامة في ② يتحقّق منها ③ قبل كل علامة.
مكان استضافة نموذج اتخاذ القرار: تتم كل مكالمة من خلال typesafe-sdk نفسه، ولا يتغيّر سوى عنوان URL الأساسي. يسمّي scripts/jevauth.py الخلفية ويضبط المفتاح والمهلة:
الخلفية |
| المفتاح | تم الإعداد بواسطة |
TypeSafe، مستضافة | unset (api.typesafe.ai) |
|
|
DiffusionGemma على جهازك الافتراضي L4 | | لا ينطبق |
|
DiffusionGemma على Cloud Run |
| رمز مميّز لهوية Google يتم استرجاعه كل ساعة |
|
تمارين | تم ضبط | لا ينطبق |
|
إجابة بطاقة التدقيق الإملائي ②: يحصل سير العمل على ملف PNG فقط، وتحدد ② التدقيق الإملائي الذي ترسله. هذا ما يجعل السحر اختبارًا حقيقيًا لقدرة Gemini على القراءة، والسحر الذي تصنعه في "أنت تقاتل" اختبارًا حقيقيًا لقدراتك.
2. الإعداد
طلب رصيد ورشة العمل
إذا تم منحك رصيدًا في Google Cloud لهذه الجلسة، عليك المطالبة به أولاً، إذ يستغرق ذلك حوالي دقيقة واحدة ويتم إنشاء حساب الفوترة لك.
فتح Cloud Shell
Google Cloud Shell هي بيئة Linux يمكن الوصول إليها من المتصفّح وتم إعدادها مسبقًا باستخدام gcloud وPython وNode.js وuv وgit، وتمت مصادقتها مسبقًا باستخدام حسابك على Google.
- افتح Google Cloud Console.
- انقر على تفعيل Cloud Shell (رمز النافذة الطرفية في شريط التنقّل العلوي) لفتح جلسة نافذة طرفية في أسفل المتصفّح.

تشغيل مساحة العمل
في Cloud Shell أو أي مكان تم تسجيل الدخول إليه باستخدام gcloud:
git clone https://github.com/gca-americas/discriminative-models-workshop.git
cd discriminative-models-workshop
./setup_project.sh # a new project with billing, recorded in ~/project_id.txt
./setup_codelab.sh # everything else, then the workbench on port 4900
ينشئ setup_project.sh مشروعًا (discrim-models-XXXX) ويربط الفوترة به، مع تفضيل حساب رصيد الحدث إذا كان لديك حساب، وينتظر إلى أن يصبح المشروع جاهزًا للعمل. سيؤدي إعادة تشغيله إلى إعادة استخدام المشروع في ~/project_id.txt. لاستخدام مشروع لديك، ضَع معرّفه في هذا الملف وتخطَّ هذا النص البرمجي.
setup_codelab.sh لا يطلب أي إذن. يتم تثبيت uv وحِزم Python، وتفعيل Vertex AI وCompute Engine وIAP، وتوجيه Gemini إلى Vertex AI في المشروع في .env، وإجراء طلب Gemini حقيقي واحد باستخدام نموذج يمكن للمشروع طلبه، وإنشاء الصفحة، وبدء مساحة العمل في الخلفية، وتشغيل scripts/check_setup.py. سيؤدي إعادة تشغيلها إلى الاحتفاظ بملفات التمارين، بينما ستؤدي scripts/starter.sh إلى إعادة ضبطها. يتم اختيار نموذج اتّخاذ القرار في الخطوة 2 من مساحة العمل.
لفتح واجهة مستخدم Workbench في Cloud Shell، اتّبِع الخطوات التالية:
- انقر على رابط المعاينة المطبوع في نهاية
./setup_codelab.sh، أو انقر على معاينة الويب في أعلى يسار شريط أدوات Cloud Shell. - انقر على تغيير المنفذ، وأدخِل 4900، ثم انقر على تغيير ومعاينة.
يعمل Gemini على Vertex AI في مشروعك، باستخدام بيانات اعتماد Google الخاصة بك: GOOGLE_GENAI_USE_VERTEXAI=1 وGOOGLE_CLOUD_PROJECT وGOOGLE_CLOUD_LOCATION=global في .env.
يتم اختيار نموذج اتخاذ القرار بشكل مستقل في الخطوة 2 من مساحة العمل أو من نافذة طرفية باستخدام scripts/setup_model.sh:
الاختيار | الاحتياجات | الإعداد | التكلفة |
نموذج التمييز (TypeSafe، مستضاف) | مفتاح واجهة برمجة تطبيقات TypeSafe | لا ينطبق | لكل رمز مميّز، كسور من السنت |
DiffusionGemma (Google، أوزان مفتوحة) | الفوترة + حصة Compute Engine لوحدة معالجة الرسومات | 15 دقيقة تقريبًا، تلقائي | 0.71 دولار أمريكي تقريبًا في الساعة أثناء تشغيل الجهاز الافتراضي |
التدريب (بدون نموذج) | nothing | لا ينطبق | لا ينطبق |
DiffusionGemma على جهاز Compute Engine الافتراضي
يتحقّق scripts/setup_gemma.sh أولاً من حصة وحدة معالجة الرسومات، ثم ينشئ جهازًا افتراضيًا واحدًا g2-standard-4 (1 × L4 بسعة 24 غيغابايت و4 وحدات معالجة مركزية افتراضية و16 غيغابايت) من صورة "التعلم العميق" من Google مع برنامج تشغيل NVIDIA 580. عند تشغيل الجهاز الافتراضي لأول مرة، يتم تثبيت Docker وتنزيل الأوزان من Hugging Face (nvidia/diffusiongemma-26B-A4B-it-NVFP4، 17.5 غيغابايت، عام، بدون رمز مميز) وتشغيل djev-run: DiffusionGemma خلف واجهة برمجة التطبيقات الدقيقة للنموذج التمييزي. منفذ النموذج غير متاح على الإنترنت: يصل إليه Workbench من خلال نفق IAP على localhost:8096، والذي يفتحه scripts/start.sh.
إيقاف مؤقت / استئناف | | |
نفق |
| |
إزالة |
| |
التدرّب على الطلبات |
| |
تنسيق المستودع
app/ the arena app, as built so far (see "The app, one stage at a time")
main.py the server, the "You fight" mode, and the plugin loader
engine.py the rules and the ogre's moves, the one copy
sigil.py spell cards: a color and three shapes, judged and drawn (a tiny PNG rasteriser)
static/ the page: HP bars, the telegraph and timer, the spell card; modes/ holds plugins
static/sounds/ bgm.mp3 plus optional effects: fight, ogre-attack, block, strike, hurt, charge,
cast, fizzle, ready, ko, timeup (.mp3). A missing file is silent. Add them in stages/03-you-fight/.
reflex.py step 5: the three questions and choose()
mode_model.py step 5: the server side of "Discriminative model fights"
mode_workflow.py step 6: the server side of "Workflow fights"
branches/ step 6b's exercises: each branch as a workflow of its own, nothing from the arena
slow_branch.py Gemini reads spell_card.png and is checked against spell_card.json
fast_branch.py the Discriminative model decides on a list of moves, in a loop
starter/ Reset restores from here
server/ The workbench
3- ملخّص
تنظيف البيئة
بعد الانتهاء من ورشة العمل، أكمل الخطوات التالية لإيقاف أي موارد لوحدة معالجة الرسومات في DiffusionGemma، وإيقاف عمليات الخلفية في Workbench وRehearsal، وإزالة ملفات ورشة العمل من Cloud Shell، وحذف مشروع Google Cloud الخاص بورشة العمل (اختياريًا).
- حذف الجهاز الافتراضي لوحدة معالجة الرسومات وقاعدة بيانات جدار الحماية في DiffusionGemma (في حال إنشائها): إذا وفّرت DiffusionGemma على جهاز افتراضي لوحدة معالجة الرسومات في Compute Engine في الخطوة 2، عليك إزالة الجهاز الافتراضي والقرص وقاعدة بيانات جدار الحماية في IAP حتى لا يتم تحصيل أي رسوم مستمرة على الحوسبة أو مساحة تخزين القرص:
cd ~/discriminative-models-workshop ./scripts/teardown_gemma.sh - إيقاف عمليات منصة العرض التجريبي والتدريب في Cloud Shell: في نافذة Cloud Shell، أوقِف خادم منصة العرض التجريبي الذي يعمل في الخلفية وأي عملية بديلة للتدريب:
cd ~/discriminative-models-workshop ./scripts/stop.sh ./scripts/rehearsal.sh stop 2>/dev/null || true - حذف مجلد ورشة العمل من Cloud Shell: ارجع إلى دليلك الرئيسي وأزِل مجلد المستودع الذي تم استنساخه وملف رقم تعريف المشروع:
cd ~ rm -rf ~/discriminative-models-workshop ~/project_id.txt - حذف مشروع Google Cloud: إذا
./setup_project.shأنشأت مشروعًا مخصّصًا لورشة العمل (على سبيل المثال،discrim-models-XXXX)، سيؤدي إيقاف المشروع نهائيًا إلى حذف جميع الموارد التي تم إنشاؤها داخله مع الحفاظ على حساب فوترة Cloud كما هو:- افتح صفحة "إدارة الموارد" في Google Cloud Console.
- اختَر مشروع ورشة العمل (على سبيل المثال،
discrim-models-...) من قائمة الموارد. - انقر على حذف في شريط الأدوات العلوي، وأدخِل رقم تعريف مشروعك للتأكيد، ثم انقر على إيقاف.
لقد أكملت ورشة العمل هذه.
ملخّص الدرس التطبيقي
- اختَر نموذجًا تمييزيًا، مثل Jev أو DiffusionGemma، على جهاز افتراضي مزوّد بوحدة معالجة الرسومات (GPU) في Compute Engine، وتأكَّد من أنّه يجيب عن الأسئلة.
- لعبتُ في الحلبة يدويًا، وضد الوقت، لأتعرّف على قواعدها.
- تعرّفت على كيفية إجابة النموذج التمييزي عن أسئلة الاختيار والنتيجة وNoul، والاحتمالات ومستوى الموثوقية، وكيفية تطبيق الرمز البرمجي للحدود عليها.
- أرسِل طلبك الأول، ثمّ دع النموذج يختار كل حركة في الساحة، مع تحويل
choose()إجاباته إلى إجراءات. - تم إنشاء كل فرع من سير عمل ADK بشكل منفصل، حيث يقرأ Gemini صورة بطاقة تعويذة ويقرّر النموذج في حلقة.
- تم دمجها في سير عمل واحد يشارك الحالة، لذا لا ينتظر القتال Gemini ويتم إلقاء التعويذة عند فتح اللعبة.
من المحادثة إلى القرارات

وصل الذكاء الاصطناعي التوليدي إلى معظم الفِرق من خلال المحادثات وإنشاء المحتوى. المرحلة التالية هي الذكاء الاصطناعي داخل المنتجات وقنوات المعالجة، حيث يؤدي ناتج النموذج إلى اتخاذ إجراء مباشر: توجيه طلب دعم، أو الإبلاغ عن معاملة، أو تعليق طلب محفوف بالمخاطر للمراجعة، أو السماح أو حظر طلب أداة من وكيل، أو اختيار خطوة في لعبة.
تشترك هذه القرارات في ثلاثة متطلبات لا تتوفّر في المحادثة:
- وقت الاستجابة: غالبًا ما تكون الإجابة في مسار طلب المستخدم أو في حلقة في الوقت الفعلي، لذا يجب أن تصل في غضون أجزاء من الثانية، وليس ثوانٍ.
- البنية: المتصل هو رمز، لذا يجب أن تكون الإجابة قيمة يمكنه تنفيذها، وليس فقرة عليه تحليلها.
- إمكانية التوقّع: يحتاج كل قرار إلى مستوى ثقة يمكن للرمز التحقّق منه، وتكلفة منخفضة بما يكفي لطلبها في كل حدث.
ينشئ النموذج اللغوي نصًا واحدًا في كل مرة. يمكن أن يُطلب منه الإجابة بنعم أو لا، ولكنّه بطيء بالنسبة إلى حلقة في الوقت الفعلي، ويجب تحليل ناتجه، ولا يوضّح مدى صحة إجابته.
نماذج مصمَّمة لاتّخاذ القرارات
يجيب النموذج التمييزي عن سؤال مكتوب باحتمالية لكل خيار مسموح به، وذلك في عملية واحدة. لا ينشئ هذا التطبيق نصًا. تقدّم ورشة العمل هذه خيارَين لتنفيذها:
الطراز | موفِّر الخدمة | مكان تشغيل النموذج في ورشة العمل هذه |
Jev | TypeSafe AI | الخدمة المستضافة من TypeSafe، والتي يتم استدعاؤها باستخدام مفتاح واجهة برمجة التطبيقات |
DiffusionGemma | Google، افتح الأوزان | مستضافة ذاتيًا على جهاز افتراضي مزوّد بمعالج وحدة معالجة الرسومات (GPU) في مشروعك الخاص على Google Cloud |
يمكن تبديل النماذج حسب احتياجاتك، ولا يلزم تغيير الرمز البرمجي الذي يرتبط بها.
دمج المكوّنات
يتألف النظام الناجح من عدة مكونات:
المكوّن | الدور | في ورشة العمل هذه |
Workflow | تنسيق الخطوات، وتنفيذ الفروع بالتوازي، والاحتفاظ بالحالة المشتركة | سير عمل الرسم البياني في ADK |
الرمز الحتمي | القواعد والحدود والتحقّق من الصحة فورية ومجانية وقابلة للتدقيق | قواعد اللعبة، |
النموذج التمييزي | قرارات سريعة ومحدودة مع درجة ثقة | اختيار ردّ كل ثانية |
النموذج اللغوي | الإدراك والإنشاء: الصور والنصوص ذات النهايات المفتوحة | يقرأ Gemini صورة بطاقة التعويذة ويكتب التعويذة |
بنية عرض النماذج

يمكنك اختيار النموذج في الخطوة 2، وذلك حسب تفضيلاتك والبيئة المحيطة بك. إذا كنت تخطّط لاستخدام DiffusionGemma، تأكَّد من إمكانية الوصول إلى وحدة معالجة رسومات (GPU) على Google Cloud.
Jev | DiffusionGemma | |
مقدّم الخدمة | TypeSafe AI، واجهة برمجة تطبيقات مستضافة | Google، افتح الأوزان |
تعمل على | البنية الأساسية لشركة TypeSafe | جهاز Compute Engine الظاهري في مشروعك، مع وحدة معالجة الرسومات |
نقطة النهاية |
| من خلال نفق IAP |
المصادقة |
| هويتك على Google Cloud، التي تحقّق منها IAP |
التكلفة | لكل رمز مميّز للإدخال | أسعار وحدات معالجة الرسومات في Compute Engine من Google Cloud أثناء تشغيل الجهاز الافتراضي |
الإعداد | مفتاح واجهة برمجة التطبيقات | تثبيت النموذج على جهاز افتراضي أو Cloud Run |
تدفق البيانات
- ينشئ تطبيق الساحة أو سير عمل "حزمة تطوير الوكلاء" طلبًا يتضمّن الحالة (الإجراء الذي اتّخذه الخصم) وثلاثة أسئلة.
- ترسل حزمة TypeSafe SDK القيمة
POST /v1/systemoneإلى عنوان URL الأساسي الذي تم ضبطه. - بالنسبة إلى Jev، يتم إرسال الطلب عبر HTTPS إلى
api.typesafe.ai، مع مفتاح واجهة برمجة التطبيقات كرمز مميّز لحامل المفتاح. - بالنسبة إلى DiffusionGemma، يتم إرسال الطلب إلى
localhost:8096. تعمل عمليةgcloud compute start-iap-tunnelفي الخلفية على إعادة توجيه الطلب من خلال Identity-Aware Proxy، الذي يتحقّق من هويتك على Google، إلى المنفذ 8080 على الجهاز الظاهري. - على الجهاز الافتراضي، يتلقّى djev-run الطلب، ويشغّل DiffusionGemma من خلال vLLM على وحدة معالجة الرسومات، ويقرأ احتمال كل خيار مسموح به.
- تعرض كلتا الخلفيتين الردّ نفسه: إجابة لكل سؤال، مع احتمالات ودرجة ثقة. يطبّق رمز ورشة العمل الحدود الدنيا ويتخذ الإجراءات اللازمة.
DiffusionGemma على Compute Engine
تنشئ scripts/setup_gemma.sh ما يلي في مشروعك:
- تتحقّق هذه السمة من توفّر حصة لوحدة معالجة الرسومات في المنطقة.
- تفعيل واجهات برمجة التطبيقات Compute Engine وIAP وإنشاء قاعدة جدار الحماية
allow-iap-djevلا يسمح إلا بنطاق عناوين IAP على المنفذَين 22 و8080. - ينشئ الجهاز الافتراضي
djev-l4: نوع الجهازg2-standard-4(4 وحدات معالجة مركزية افتراضية وذاكرة بسعة 16 غيغابايت) ووحدة معالجة رسومات بذاكرة سعتها 24 غيغابايت وقرصًا بسعة 100 غيغابايت وصورة Deep Learning VM مع برنامج تشغيل NVIDIA 580. إذا لم تتوفّر سعة وحدة معالجة الرسومات في منطقة معيّنة، سيتم تجربة المنطقة التالية. - عند التشغيل لأول مرة، يثبِّت النص البرمجي للتشغيل للجهاز الافتراضي Docker وNVIDIA Container Toolkit، ويسحب صورة حاوية djev-run، وينزّل الأوزان من Hugging Face (17.5 غيغابايت)، ويبدأ الحاوية مع إمكانية الوصول إلى وحدة معالجة الرسومات على المنفذ 8080. تستغرق هذه العملية حوالي 15 دقيقة. تستغرق عمليات التشغيل اللاحقة حوالي دقيقتَين.
- تكتب هذه السياسة إعدادات الاتصال في
.envوتفتح النفق.
المهمة | الأوامر |
إيقاف الجهاز الظاهري (مع الاحتفاظ بالقرص) |
|
بدء المحادثة من جديد |
|
التحقّق من النفق |
|
حذف كل شيء |
|
إعداد النموذج

TypeSafe SDK
مكتبة البرامج هي typesafe-sdk للغة Python. يتضمّن ورشة العمل هذه ما يلي: تم تثبيته في بيئة ورشة العمل نفسها، بالإضافة إلى google-adk للخطوة 6.
pip install typesafe-sdk # or: uv add typesafe-sdk
نقطة نهاية Jev
نموذج Jev هو واجهة برمجة تطبيقات مستضافة، لذا لا يلزم تنزيل أي شيء آخر. للحصول على مفتاح، يمكنك الاشتراك في وحدة تحكّم TypeSafe. تبحث حزمة تطوير البرامج (SDK) عن المفتاح في متغيّر البيئة TYPESAFE_API_KEY، وتقرأ نصوص البرامج في ورشة العمل هذه أيضًا ملف .env في الجذر، لذا يكفي سطر واحد هناك:
TYPESAFE_API_KEY=ts-...
استخدام DiffusionGemma
تعيد الأداة djev-run تنفيذ واجهة برمجة التطبيقات الخاصة بالنموذج التمييزي. وهي تعرض نقطة النهاية POST /v1/systemone نفسها، مع أسئلة "الاستخدام غير التجاري" و"الاختيار" و"النتيجة" نفسها، من DiffusionGemma، وهو نموذج الانتشار المفتوح من Google DeepMind (يضمّ 26 مليار مَعلمة إجمالاً، منها 4 مليارات مَعلمة نشطة، وهو مرخّص بموجب ترخيص Apache 2.0). وبما أنّ تنسيق البيانات على الشبكة هو نفسه، يتواصل حزمة تطوير البرامج (SDK) TypeSafe معه بدون تغيير.
إذا اخترت DiffusionGemma في التمرين، سيتم تشغيلها على وحدة معالجة الرسومات في جهاز افتراضي ضمن مشروعك على Google Cloud، وسيظهر في أعلى يسار الصفحة gemma on vm. يصل إليها من خلال نفق خاص في IAP، ولا يكون منفذ النموذج مفتوحًا على الإنترنت. تصف الخطوة 1 البنية الكاملة.
سبب قدرة نموذج الانتشار على تنفيذ ذلك: يملأ النموذج مجموعة كاملة من المواضع في وقت واحد، مع رؤية كل موضع للإدخال الكامل، وبالتالي يمكن قراءة احتمالية كل خيار مسموح به في خطوة واحدة. ينتج نموذج اللغة العادي رمزًا مميزًا واحدًا في كل مرة، ويجب أخذ عينات منه بشكل متكرر.
تشغيل اللعبة يدويًا

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

القرارات في البرامج
تتميّز النماذج اللغوية بقدرتها على إجراء المحادثات منذ سنوات. لا تزال معظم البرامج لا تستخدمها في أي شيء تلقائي، والسبب ليس الذكاء. إنّها السرعة.
اسأل نموذجًا لغويًا عمّا إذا كان الغول الذي أمامك على وشك الهجوم، وسيكتب النموذج إجابته رمزًا واحدًا في كل مرة. وبحلول الوقت الذي تصل فيه الفقرة، يكون النادي قد وصل إلى وجهته. لقد شعرت بالنسخة التي تبلغ مدتها ثانيتين في الخطوة 3. وحتى في هذه الحالة، تكون الإجابة "نعم" مضمّنة في فقرة يجب أن يعثر عليها الرمز البرمجي ويثق بها، بدون معرفة مدى صحة المعلومات التي قدّمتها النماذج.
يأخذ نموذج التمييز الحالة والأسئلة والأجوبة التي تكتبها في تمريرة واحدة، وذلك في غضون أجزاء من الثانية. تتضمّن كل إجابة احتمالاً معايرًا: 0.9 يعني صحة الإجابة تسع مرات من أصل عشر مرات. لا يوجد نص لتحليله ولا ملف JSON لاستخراجه.
نموذجا "النظام 1" و"النظام 2"
يستند الاسم إلى كتاب التفكير السريع والبطيء لدانيال كانيمان. النظام 2 هو استدلال بطيء ومدروس، خطوة واحدة تلو الأخرى. النظام 1 سريع ويعتمد على مطابقة الأنماط.
النموذج اللغوي هو آلة من النوع "النظام 2". ويتم التفكير في الرموز المميزة، واحدًا تلو الآخر. إنّ النموذج التمييزي هو نموذج النظام 1: لا يقدّم تفسيرات شفهية، ولا ينشئ أي محتوى، ويجيب عن كل سؤال في خطوة واحدة. لهذا السبب، تكون هذه العملية سريعة (من 70 إلى 500 ملي ثانية تقريبًا) وغير مكلفة (أجزاء من السنت لكل ألف قرار).
المعلومات الأساسية الموجزة: يكتب النموذج اللغوي. يتّخذ نموذج القرار القرار. معظم ما تحتاجه البرامج من الذكاء الاصطناعي هو اتخاذ قرار.
القيود
لن ينشئ "النموذج التمييزي" نصًا أو يكتب رمزًا برمجيًا أو يجري محادثة أو يجري عمليات حسابية أو يقرأ صورة أو يتّبع سلسلة من الخطوات.
في ورشة العمل، سنختار أحد النماذج التمييزية:
- أحد النماذج التمييزية هو Jev. وهي واجهة برمجة تطبيقات مستضافة من TypeSafe AI، وتم إصدارها في سبتمبر 2026. النموذج الأول هو
jev-1.13، ويمكن الوصول إليه من خلال الاسم المستعارjev-latest. لا تتوفّر أوزان منشورة، لذا يتم طلبها وليس تنزيلها. - Jev ليست الطريقة الوحيدة للحصول على نموذج System One. DiffusionGemma من Google هو نموذج مفتوح الأوزان يكتب مجموعة كاملة من الرموز المميزة بالتوازي بدلاً من كتابة رمز واحد في كل مرة، ويمكن أن تقرأ عملية التمرير المتوازية نفسها الاحتمالات على مجموعة ثابتة من الخيارات. تضع الخوادم المفتوحة المصدر، مثل djev-run، واجهة برمجة التطبيقات الدقيقة الخاصة بـ Jev أمامها، لذا يتم تشغيل كل شيء في ورشة العمل هذه بدون تغيير.
الحالة والأسئلة: Choice وScore وNoul
يرسل كل طلب حالة وأسئلة. الحالة هي النص الذي تريد تقييمه. يمكن أن تكون سلسلة أو عنصر JSON أو قائمة. تطرح الأسئلة ما تريد معرفته عن هذا النص. لكل سؤال نوع: "اختيار" أو "نتيجة" أو "نول". تتم معالجة الأسئلة بالتوازي، ما يتيح لها الردّ بسرعة. يمكنك إضافة أسئلة متعددة إذا لزم الأمر.
- يختار الخيار خيارًا واحدًا من مجموعة تحدّدها، ويمكن أن يصل عدد الخيارات إلى 255. تتضمّن الإجابة الخيار واحتمالية لكل خيار ومستوى الثقة. استخدِمها عندما لا يكون هناك ترتيب بين الخيارات: صدّ ضربة قوية، وصدّ ضربة خفيفة، وتفادٍ، وضربة، وانتظار.
- النتيجة: تقيّم الحالة على طول مستويات مرتبة تحدّدها، من مستويَين إلى عشرة مستويات. الإجابة هي موضع على المقياس (عدد عشري، لذا يعني 1.4 "بين واحد واثنين، أقرب إلى واحد")، واحتمالية كل مستوى، ومستوى الموثوقية. استخدِمها عندما تكون الإجابة مسألة درجة: مدى قوة الضربة الواردة.
- يعرض كلّ من Choice وScore احتمالاً لكل خيار ومستوى ثقة. والفرق هو الإجابة الرئيسية. تعرض Choice الخيار الأكثر احتمالاً. تعامِل "النتيجة" الخيارات على أنّها مستويات مرتّبة وتعرض متوسطها المرجّح حسب الاحتمالية، ويمكن أن يقع هذا المتوسط بين مستويَين. إذا كانت قيمة "بدون" هي 0.05 و"قليل" هي 0.55 و"كثير" هي 0.40، ستكون إجابة "قليل" في حقل "اختيار" وإجابة 1.35 في حقل "نتيجة"، أي بين "قليل" و"كثير". تستخدم ساحة المعركة هذه القيمة:
choose()تعامل نتيجة الخطر البالغة 1.5 أو أكثر على أنّها ضربة قوية.
- يعرض كلّ من Choice وScore احتمالاً لكل خيار ومستوى ثقة. والفرق هو الإجابة الرئيسية. تعرض Choice الخيار الأكثر احتمالاً. تعامِل "النتيجة" الخيارات على أنّها مستويات مرتّبة وتعرض متوسطها المرجّح حسب الاحتمالية، ويمكن أن يقع هذا المتوسط بين مستويَين. إذا كانت قيمة "بدون" هي 0.05 و"قليل" هي 0.55 و"كثير" هي 0.40، ستكون إجابة "قليل" في حقل "اختيار" وإجابة 1.35 في حقل "نتيجة"، أي بين "قليل" و"كثير". تستخدم ساحة المعركة هذه القيمة:
- يطرح Noul سؤالاً بنعم أو لا ويعرض احتمال أن تكون الإجابة هي "نعم". تشير القيمة القريبة من 1 إلى تطابق كبير، والقيمة القريبة من 0 إلى عدم تطابق كبير، والقيمة القريبة من 0.5 إلى "يمكن أن تكون أيًا منهما". لا يوجد مقياس ثقة منفصل، لأنّ الاحتمالية هي مستوى الثقة.
كتابة أسئلة مركّزة
يعمل النموذج التمييزي بشكل أفضل عندما يطرح السؤال شيئًا واحدًا محدّدًا وواضحًا. يؤدي السؤال "ما هي الحالة؟" إلى عرض إجابة معقولة بمستوى ثقة منخفض. "ما هو الردّ المناسب؟"، تعرض الأسئلة "هل الخصم مكشوف؟" و "ما مدى قوة هذه الضربة؟" ثلاث إجابات مركّزة تجمعها التعليمات البرمجية.
الأوصاف المتعلقة بالخيارات والمستويات رخيصة الثمن ولكنّها مهمة. تصبح القواعد التي قرأتها في الخطوة 3 هي أوصاف الخيارات: block_high: "Raise the shield. Right against an overhead or a high swing." بهذه الطريقة يتعلّم النموذج التمييزي قواعد المباراة، في وقت الطلب، في سطر واحد لكل قاعدة. ويمكن أن تتغيّر الخيارات حسب الموقف: لا تقدّم الساحة سوى cast عندما تكون تعويذة جاهزة.
الاحتمالات ومستوى الموثوقية
إجابة "اختيار" ليست تصنيفًا. وهو توزيع على التصنيفات، والتصنيف هو مجرد الشريط الأطول.
كيفية حصول النموذج على الرقم ويستخدم الخطوة نفسها التي يستخدمها النموذج اللغوي لاختيار الكلمة التالية. يقرأ المحوّل النص، وفي موضع واحد، يمنح كل رمز مميز في مفرداته درجة أولية تُسمى logit. وكلما زاد اللوغاريتم، كان الرمز المميّز أكثر ملاءمة لهذا الموضع. تحوّل softmax القيم المنطقية إلى احتمالات يصل مجموعها إلى 1. بعد ذلك، يختار نموذج اللغة رمزًا مميزًا واحدًا ويضيفه إلى النص، ثم يكرّر العملية. يتوقّف النموذج التمييزي بعد الاحتمالات.
الفراغ هو فجوة في نموذج الإجابة. يكتب الخادم النموذج نفسه، مثل response: ▢، ويترك فجوة واحدة لكل سؤال. مهمة النموذج الوحيدة هي تسجيل ما ينتمي إلى كل فجوة.
- يحتوي الطلب على الحالة وكل سؤال، مع كل إجابة مسموح بها كتسمية قصيرة:
aلـ block_high وbلـ block_low وما إلى ذلك. - يضيف الخادم نموذج الإجابات، مع ترك مساحة فارغة لكل سؤال.
- يقرأ النموذج الطلب والنموذج في تمريرة واحدة ويمنح كل رمز مميز لوغاريتم احتمالية عند كل فراغ. يرى نموذج الانتشار النموذج بأكمله في وقت واحد ويقيّم جميع الفراغات معًا.
- يحتفظ الخادم فقط بالقيم اللوغاريتمية للتصنيفات المسموح بها ويطبّق عليها دالة softmax، وبالتالي فإنّ الإجابات المسموح بها تصل إلى 1.
- إذا بدا أن القراءة غير دقيقة، يعيد الخادم القراءة من بداية عشوائية أخرى ويحسب متوسط القراءات.
الثقة هي رقم واحد يوضح مدى صحة الإجابة، وتحسبها TypeSafe من خلال كيفية توزيع الاحتمالية على الخيارات. إذا كان كل المحتوى في خيار واحد، تكون النتيجة 1، وإذا كان المحتوى موزّعًا بالتساوي، تكون النتيجة 0. بالنسبة إلى ثلاثة خيارات، تكون الصيغة هي (3 × أكبر قيمة − 1) / 2.
تدرّب TypeSafe Jev على الاحتمالات المعايرة. يتطابق الاحتمال مع عدد المرات التي تكون فيها الإجابة صحيحة. في النموذج المعاير، تكون الإجابات التي يتم تقديمها عند مستوى ثقة 0.7 صحيحة بنسبة% 70 تقريبًا، لذا فإنّ حد الثقة هو حدّ لعدد المرات التي تقبل فيها إجابة خاطئة. يُبلغ خادم DiffusionGemma في ورشة العمل هذه عن أعلى احتمال بنفسه باعتباره مستوى ثقة، ويتم حساب متوسطه على مستوى القراءات. وعندما لا تتفق القراءات، ينتشر المتوسط وتنخفض الثقة.
النقطة الرئيسية: يخبرك الجواب بما. يخبرك مستوى الموثوقية ما إذا كان عليك اتّخاذ إجراء.
الحدود
الحدّ الأدنى هو الطريقة التي تحدّد بها الإجراء في الرمز البرمجي. يعرض النموذج مستوى ثقة أو احتمالية. يقارن الرمز الرقم الذي اخترته، ويحدّد الناتج الإجراء الذي سيتم تنفيذه.
حدّ أدنى لكل إجراء: تقترح TypeSafe تقسيم مستوى الثقة إلى نطاقات. تتخذ الإجراءات ذات درجة الثقة العالية تلقائيًا. تتضمّن الإجراءات التي تتطلّب ثقة متوسطة علامة اختيار، مثل طلب التأكيد أو الإبلاغ عن الحالة لمراجعتها. لا يتم اتخاذ أي إجراء عندما يكون مستوى الموثوقية منخفضًا، ويتم الرجوع إلى خيار آمن أو إلى شخص.
TRUST = 0.40 # below this, the answer is a guess
AUTO = 0.80 # at or above this, act without a check
def route(answer):
if answer.confidence >= AUTO:
return act(answer.choice) # high: act on its own
if answer.confidence >= TRUST:
return confirm(answer.choice) # medium: act with a check
return fall_back() # low: do something safe
قواعد الحلبة تتوفّر حدود الساحة في choose()، والتي يمكنك تشغيلها في الخطوة 5.
TRUST_CONFIDENCE = 0.40 # below this, the model is guessing between responses
HEAVY_DANGER = 1.5 # a danger score at or above this is a heavy hit
SPEND_ON_OPENING = 0.60 # exposed at or above this, with a spell ready, cast
def choose(answers, spell_ready):
response = answers["response"]
exposed = answers["exposed"].noul
danger = answers["danger"].score
action = response.choice
if response.confidence < TRUST_CONFIDENCE and danger >= HEAVY_DANGER:
action = "dodge" # shaky answer, heavy hit coming
if spell_ready and action == "strike" and exposed >= SPEND_ON_OPENING:
action = "cast" # a clear opening is worth the spell
return action
تحذير: لا تكون الإجابة الصالحة صحيحة دائمًا. لا يمكن للنموذج التمييزي أن يعرض خيارًا لم تقدّمه، لذا لا يقدّم أبدًا إجابات غير صحيحة، ولكن يمكنه اختيار الخيار الخاطئ، وأحيانًا بثقة عالية. اختبِر أسئلتك في مواقف سبق لك تقييمها قبل الوثوق بمستوى الحدّ.
أتمتة القرارات باستخدام النموذج

الطلب والاستجابة
الطلب: تتيح لك حزمة TypeSafe Python SDK إنشاء الأسئلة وإرسالها إلى النموذج.
from typesafe_sdk import Choice, Noul, TypeSafeClient
with TypeSafeClient() as client:
response = client.system_one(
state={"opponent": OPPONENT, "telegraph": telegraph},
questions={
"response": Choice(instructions="What is the right response?", criteria=RESPONSES),
"exposed": Noul(instructions="Is the opponent exposed to a counter-attack right now?"),
},
)
response.choices["response"].choice # "strike"
response.nouls["exposed"].noul # 0.97
طلب واحد لكل علامة
في كل مرة، يرسل التطبيق التلغراف كحالة ويطلب ثلاثة أشياء في مكالمة واحدة:
- الردّ الصحيح من بين الردود الخمسة (أو الستة عندما تكون تعويذة جاهزة) اختيار
- تحدّد هذه السمة ما إذا كان الوحش مكشوفًا حاليًا. A Noul.
- مدى قوة الضربة الواردة، وذلك على مقياس من ثلاثة مستويات النتيجة
def reflex_questions(spell_ready):
options = dict(RESPONSES)
if spell_ready:
options["cast"] = CAST # only offered when there is a spell
return {
"response": Choice(instructions="The opponent has just done this. What is the right response?",
criteria=options),
"exposed": Noul(instructions="Is the opponent exposed to a counter-attack right now?"),
"danger": Score(instructions="How much damage is about to land if the fighter does nothing?",
criteria=["None: this is not an attack.", "A light hit.", "A heavy hit."]),
}
الدالة choose()
هل تتذكّر الحدود الدنيا من الخطوة 4؟ تقارن choose() إجابات النموذج بأرقام ثابتة، وهذه الأرقام الثابتة هي الحدود الدنيا.
TRUST_CONFIDENCE = 0.40
HEAVY_DANGER = 1.5
SPEND_ON_OPENING = 0.60
def choose(answers, spell_ready):
action = answers["response"].choice
if answers["response"].confidence < TRUST_CONFIDENCE and answers["danger"].score >= HEAVY_DANGER:
action = "dodge" # shaky call, heavy hit coming: play it safe
if spell_ready and action == "strike" and answers["exposed"].noul >= SPEND_ON_OPENING:
action = "cast" # the Discriminative model saw the opening; the code spends the spell
...
choose() هي قراءة عادية لقيم مكتوبة بلغة Python، مع قاعدتين. يقدّم النموذج التمييزي احتماليته وتحليله، ويستخدم الرمز البرمجي الحدود الدنيا للقواعد. يتم إرسال الإجراء المحدّد إلى المحرّك، حيث سيتم استخدامه لمحاربة الغول.
النقطة الأساسية: احتفِظ بالأسئلة والحدود في مكان واحد. وهي الجزء الذي ستعدّله أكثر من غيره في عملية الدمج مع System One.
وقت الاستجابة والتسعير المستند إلى الإدخال ومنطق اتخاذ القرار
- وقت الاستجابة لكل قرار: استغرقت كل علامة من علامات القتال في الجزء (ب) حوالي مائة جزء من الثانية، وبعضها استغرق جزأين أو ثلاثة أجزاء من الثانية. هذه السرعة كافية لتشغيل حلقة ألعاب أو مسار طلب أو إجراء فحص لكل رسالة قبل أن يراها شخص أو نموذج لغة.
- التسعير المستند إلى المدخلات: تكلّف معركة كاملة، أي ستون قرارًا يتضمّن كل منها ثلاثة أسئلة، أقل من عُشر سنت. عدد الرموز المميزة للناتج هو صفر لأنّه لم يتم إنشاء أي محتوى. والنتيجة هي أنّه يمكنك طلب أكثر مما تحتاج إليه. تسأل ساحة القتال عمّا إذا كان الغول مكشوفًا في كل علامة، على الرغم من أنّ
strikeوcastفقط يهتمان بذلك، لأنّ السؤال لا يكاد يكلف شيئًا والإجابة مفيدة في لوحة البيانات. تُطلق TypeSafe على هذه العملية اسم التوزيع الموسَّع التخميني. - الجمع بين الثقة والخطر: عندما يكون مستوى موثوقية النموذج التمييزي في رده أقل من 0.40 وتشير نتيجة الخطر إلى حدوث ضربة قوية، تتجاوز
choose()ذلك باستخدام المراوغة. لا يكون التهرب من الإجابة هو الرد الأفضل في معظم الأحيان، ولكنّه أيضًا ليس الرد الأسوأ. اختَر الحدود الدنيا من تكلفة كل خطأ، وليس من رقم صحيح، واختبِرها مقارنةً بالإشارات التي سبق لك تقييمها يدويًا. تنصح شركة TypeSafe بما يلي: إذا استمرّ اتّخاذ قرار خاطئ، يجب تعديل السؤال قبل تغيير الحدّ الأدنى.
الجمع بين النماذج في سير عمل "حزمة تطوير التطبيقات على Android"

حدود القرارات لكل علامة ولماذا لا تكفي الردود الصحيحة
يقول السطر الأخير من القتال في الخطوة 5: يغادر الغول بصعوبة، ولم يُخدش إلا بالكاد. لم يتلقَّ النموذج التمييزي أي ضرر، وألحق ضررًا بسيطًا في كل ثانية، و300 نقطة إصابة هي أكثر من القليل مضروبًا في ستين. كانت بطاقة التعويذة في زاوية الحلبة موجودة طوال الوقت. يتطلّب قراءة الصورة نموذجًا يمكنه رؤيتها.
يملك الغول 300 نقطة إصابة. يتم احتساب 3 نقاط مقابل كل إجابة صحيحة. يؤدي الضرب في فتحة إلى إحداث ضرر بمقدار 8 نقاط، لأنّ الغطاء سميك. وحتى إذا كانت المعركة مثالية من ستين ضربة، سيظلّ الغول واقفًا ومصابًا بكدمات، وستُعلن اللعبة عن التعادل. وهنا انتهت الخطوة 5: دافع النموذج بشكل جيد ولكنّه لم يتمكّن من الفوز.
لا يمكن إلحاق ضرر حقيقي إلا باستخدام تعويذة: 45 نقطة عند إلقاء التعويذة بشكل مثالي، و67 نقطة عند إلقائها على فتحة.
تحديد النموذج المناسب لكل مهمة
الفوز يكون من خلال بطاقة السحر في زاوية الحلبة، وقراءتها ليست مشكلة نصية، بل هي صورة تتضمّن لونًا وثلاثة أشكال في صف واحد، ويجب غناء التعويذة لتتطابق معها. يتطلّب ذلك نموذجًا يمكنه النظر إلى صورة واستغراق بضع ثوانٍ في تحليلها. في القتال، تساوي بضع ثوانٍ عشر نبضات.
لذلك، يستخدم سير العمل كلاً منهما، كلٌّ بسرعة مختلفة:
- مباراة النموذج التمييزي في كل نبضة، مكالمة واحدة، قرار واحد، مائة جزء من الثانية. لا تنتظر الحلقة أي شيء أبطأ منها.
- Gemini يقرأ ويغنّي في فرع منفصل، بدأ عند الجرس، يتم التقاط صورة لبطاقة التعويذة من شاشة الساحة، وتحديد اسم اللون والأشكال، ثم يتم ترديد تعويذة. وتقارن الساحة الأغنية بالإجابة الصحيحة في بطاقة التعويذة، والتي لا تغادر الخادم أبدًا.
- بعد كل تبادل، يتحقّق المقاتل من الفتحة. تنظر عقدة
check_spellإلى الحالة. غير جاهز: يظهر ذلك مع مدة غناء Gemini، ويتم الانتقال مباشرةً إلى النغمة التالية. ولا تنتظر أبدًا. جاهز: ينضمcastإلى الخيارات التي يقدّمها نموذج التمييز، وينفقchoose()التعويذة في اللحظة التي يبلغ فيها نموذج التمييز عن توفّر فتحة. عندما تنتهي مدة تأثير التعويذة، ترسم الشاشة بطاقة تعويذة جديدة ويبدأ تنفيذ السلسلة البطيئة مرة أخرى. يؤدي قراءة كلمات الأغنية بشكل خاطئ إلى إتلاف بطاقة السحر، ويقرأ الخيط البطيء البطاقة الجديدة. - يكتب Gemini قصة قصيرة مرة واحدة في النهاية.

فروع متوازية مع أوقات استجابة مختلفة وحلقة معالجة الأحداث واحدة
هذا سير عمل في "حزمة تطوير الوكلاء" (ADK): رسم بياني للعُقد المرتبطة بالحواف. العقدة هي دالة Python عادية أو وكيل نموذج لغوي كبير. الحافة من عُقدة إلى مجموعة من العُقد هي توزيع موسَّع: تبدأ كلتا العمليتين في الوقت نفسه. تحدّد العقدة التي تعرض Event مع route الحافة التي سيتم اتّخاذها بعد ذلك، وتُعدّ العقدة التي توجّه إلى نفسها حلقة.
يمكنك التفكير في الأمر على أنّه سلسلتان. المهمة 1 بطيئة: قراءة بطاقة التعويذة والغناء وتخزين التعويذة. الخيط 2 سريع: ضع علامة صح، ثم تحقّق من الفتحة، ثم ضع علامة صح مرة أخرى. ينتهي التنفيذ في سلسلة التعليمات البرمجية 1 بدالة تكتب الكلمة التي تم الحكم عليها في الحالة للجلسة ولا تعرض أي ناتج. يقرأ check_spell في سلسلة المحادثات 2 هذه الحالة بعد كل عملية تبادل. لا تستدعي أي من العمليتين الفرعيتين الأخرى أو تنتظرها، بل تشتركان فقط في الحالة.
الخلاصة: ضع القرارات في الرمز البرمجي، وامنح كل نموذج مهمة محددة يمكنه تنفيذها بالسرعة التي تناسبه.
يشغّل ADK كلا الفرعين كمهام في حلقة معالجة أحداث واحدة، وذلك في سلسلة تعليمات واحدة. يتم تشغيل مهمة واحدة فقط في كل مرة. عندما تصل مهمة إلى await، تنتظر إجابتها، ويتم تنفيذ الفرع الآخر من الحلقة في الوقت نفسه. ينتظر المسار السريع النموذج لمدة عُشر ثانية تقريبًا، بينما ينتظر المسار البطيء Gemini لعدة ثوانٍ، وبالتالي لا يعيق أي منهما الآخر.
الفرع البطيء
تزيل الدالة read_rune() بطاقة التعويذة من الشاشة كصورة.
def read_rune(ctx: Context, node_input) -> Event:
png = _arena(ctx).rune_png() # exactly what the screen shows
return Event(output=types.Content(role="user", parts=[
types.Part(text="This spell card is on the arena's screen right now. Sing the spell that matches it."),
types.Part.from_bytes(data=png, mime_type="image/png"),
]))
spellwright هو Gemini. يقرأ الصورة ويجيب بشكل ثابت.
class Sung(BaseModel):
element: str # fire, frost, earth, storm
glyphs: list[str] # three of: circle, ring, square, diamond, triangle, cross, crescent, bar
incantation: str
spellwright = LlmAgent(name="spellwright", model="gemini-flash-latest",
instruction="You are the spellwright ... read the three shapes left to right ...",
output_schema=Sung)
تطلب الدالة spell_ready() من حكم الساحة تقييم التعويذة، ثم تخزينها أو إعادة المحاولة.
def spell_ready(ctx: Context, node_input: dict) -> Event:
spell = _arena(ctx).sung(dict(node_input)) # the arena judges it against the spell card
return Event(state={"spell": spell if spell["damage"] > 0 else None},
route="retry" if spell["damage"] <= 0 else "stored")
يمكن لعقدة الدالة عرض Content يتضمّن جزء صورة، وتتلقّى عقدة النموذج اللغوي الكبير هذا الجزء كدور المستخدم. تعرض spell_ready Event مع فرق الحالة وبدون output. يقرأ التوقيت التالي حالة السحر، ولا يمثّل الفرع الذي لا يحتوي على مخرجات نهاية ثانية للرسم البياني، إذ يتطلّب ADK مخرجًا نهائيًا واحدًا، وهو القتال.
ملاحظة: يتم التحكيم في الساحة من خلال مقارنة الرمز بالإجابة المخفية لبطاقة التعويذة. يُعدّ الأداء مثاليًا عند قراءة 45 كلمة أو أكثر في بداية القراءة. يؤدي شكلان إلى اليمين إلى الحصول على 25. يؤدي عدم قراءة البطاقة بشكل صحيح إلى إبطالها. لا يُطلب من Gemini تحديد ما إذا كان الرد صحيحًا.
الفرع السريع
تعرض الدالة tick() عملية تبادل واحدة، ثم تختار الحافة التالية.
async def tick(ctx: Context, node_input) -> Event:
arena = _arena(ctx)
spell = ctx.state.get("spell") # did the slow branch deliver?
move = await asyncio.to_thread(arena.telegraph)
async with AsyncTypeSafeClient() as jev:
answers = await jev.system_one(
state={"opponent": engine.OPPONENT["description"], "telegraph": move["telegraph"]},
questions=reflex.reflex_questions(spell_ready=spell is not None),
)
decision = reflex.choose(answers.answers, spell_ready=spell is not None)
entry = await asyncio.to_thread(arena.respond, decision["action"], decision, ...)
over = entry["you"] <= 0 or entry["foe"] <= 0 or entry["tick"] >= engine.MAX_TICKS
routes = [] # which arrows in the graph to follow next
if entry["spell_used"] and not over:
routes.append("recast") # a new spell card is on the screen: read it
routes.append("done" if over else "next")
return Event(output="fight", route=routes, state={"tick": ..., "spell": None, ...})
تتحقّق الدالة check_spell() من خانة "التدقيق الإملائي" بعد كل عملية تبادل.
def check_spell(ctx: Context, node_input) -> Event:
spell = ctx.state.get("spell") # thread 1 writes it; this only reads
if spell:
report = {"ready": True}
else:
report = {"ready": False, "waited": now - ctx.state["forging_since"]}
return Event(output="fight", route="again", state={"spell_check": report})
تتحقّق check_spell من موضع الإعلان بعد كل عملية تبادل. ولا يتم حظر أي شيء: إذا لم يكن التدقيق الإملائي جاهزًا، يتم الإبلاغ عن ذلك والمتابعة.
تتضمّن التصاميم ثلاثة عناصر أساسية. يتم استدعاءawait نموذج التمييز باستخدام العميل غير المتزامن، لذا تتوقف الحلقة مؤقتًا أثناء الانتظار ويستمر فرع Gemini في العمل. يتم إنشاء الأسئلة من جديد في كل مرة، لذا لا يظهر الرمز cast إلا عندما يكون هناك محتوى يمكن بثه. ويمكن أن تكون route قائمة: تأخذ ["recast", "next"] كلا الحافتين في الوقت نفسه.
تكون ساحة المعركة نفسها مخفية خلف برنامج صغير: التطبيق الذي يتم تشغيله عبر HTTP عندما يكون هناك تطبيق، وبالتالي تعرض الصفحة المعركة، أو المحرّك الذي يتم تشغيله داخل العملية عندما لا يكون هناك تطبيق.
تعريف الرسم البياني
root_agent = Workflow(
name="arena",
edges=[
("START", enter),
(enter, (read_rune, tick)), # fan-out: slow branch + fast loop
(read_rune, spellwright, spell_ready),
(spell_ready, {"retry": read_rune, "stored": rest}), # misread: read the new spell card; else rest
(tick, {"next": check_spell, "recast": read_rune, "done": summarise}),
(check_spell, {"again": tick}), # not ready? keep fighting
(summarise, bard, finish),
],
)
يُعدّ الصف الذي يمثّل هدفًا عملية توزيع موسَّع. الصف الذي يمثّل حافة هو سلسلة. يربط القاموس أسماء المسارات بالعُقد. tick → check_spell → tick هو التكرار السريع. تعيد "recast": read_rune بدء سلسلة التعليمات البرمجية البطيئة بعد استخدام تعويذة، وتفعل "retry" الشيء نفسه بعد حدوث خطأ، وتسمح "stored": rest بإنهاء سلسلة التعليمات البرمجية البطيئة بدون أي إخراج بعد وضع التعويذة في الخانة. يتطلّب ADK على الأقل حافة موجّهة واحدة في دورة، لذلك يتم رفض حلقة غير مشروطة قبل أن تعمل إلى الأبد.
ملاحظة: تبحث أدوات ADK عن root_agent. يؤدي النقر على adk web agents من جذر ورشة العمل إلى فتح واجهة مستخدم المطوّرين التي تتضمّن ساحة اللعب، إذا أردت الاطّلاع على الرسم البياني والأحداث في متصفّح بدلاً من نافذة طرفية.