1. مقدمة
مرحبًا إذاً، أنت من محبّي الوكلاء، أي المساعدين الصغار الذين يمكنهم إنجاز المهام نيابةً عنك بدون أن تبذل أي جهد، أليس كذلك؟ رائع! ولكن لنكن واقعيين، لن يكون موظّف الدعم وحده كافيًا في بعض الأحيان، خاصةً عند التعامل مع مشاريع أكبر وأكثر تعقيدًا. من المحتمل أن تحتاج إلى فريق كامل من هؤلاء. وهنا يأتي دور أنظمة الوكلاء المتعدّدين.
تمنحك موظّفو الدعم مرونة كبيرة عند استخدام النماذج اللغوية الكبيرة مقارنةً بالترميز الثابت القديم. ولكن، هناك دائمًا مشكلة، وهي أنّ هذه الحلول تأتي مع مجموعة من التحديات الصعبة. وهذا ما سنتناوله في هذه الورشة.
في ما يلي ما يمكنك توقّعه من المحتوى التعليمي، والذي سيساعدك في تحسين أداء موظّفي الدعم:
إنشاء موظّف الدعم الأول باستخدام LangGraph: سنعمل على إنشاء موظّف دعم باستخدام LangGraph، وهو إطار عمل شائع. ستتعرّف على كيفية إنشاء أدوات تتصل بقواعد البيانات والاستفادة من أحدث واجهة برمجة تطبيقات Gemini 2 لإجراء بعض عمليات البحث على الإنترنت وتحسين الطلبات والردود، حتى يتمكّن موظّف الدعم من التفاعل ليس فقط مع النماذج اللغوية الكبيرة، بل أيضًا مع الخدمات الحالية. سنوضّح لك أيضًا كيفية عمل دالة الاتصال.
تنسيق مهام موظّفي الدعم بالطريقة التي تناسبك: سنستكشف طرقًا مختلفة لتنسيق مهام موظّفي الدعم، بدءًا من المسارات المستقيمة البسيطة إلى سيناريوهات المسارات المتعدّدة الأكثر تعقيدًا. يمكنك اعتبارها توجيه سير عمل فريق موظّفي الدعم.
أنظمة متعدّدة الموظفين: ستتعرّف على كيفية إعداد نظام يمكن فيه لموظفي الدعم التعاون وإنجاز المهام معًا، وذلك بفضل البنية المستندة إلى الأحداث.
استخدام نماذج لغوية كبيرة (LLM) حسب الحاجة: لا نستخدم نموذجًا لغويًا كبيرًا واحدًا فقط. ستتعرّف على كيفية استخدام نماذج لغوية كبيرة متعددة، وتحديد أدوار مختلفة لها لتعزيز قدرة حلّ المشاكل باستخدام "نماذج التفكير" الرائعة.
هل المحتوى ديناميكي؟ لا داعي للقلق.: تخيل أنّ موظّف الدعم ينشئ محتوى ديناميكيًا مخصّصًا لكل مستخدم في الوقت الفعلي. سنوضّح لك كيفية تنفيذ ذلك.
نقل البيانات إلى السحابة الإلكترونية باستخدام Google Cloud: يمكنك التوقف عن استخدام دفتر ملاحظات. سنوضّح لك كيفية تصميم نظام متعدّد الوكلاء ونشره على Google Cloud كي يكون جاهزًا للاستخدام.
سيكون هذا المشروع مثالاً جيدًا على كيفية استخدام جميع الأساليب التي تحدثنا عنها.
2. الهندسة المعمارية
يمكن أن يكون العمل كمعلّم أو في مجال التعليم مفيدًا للغاية، ولكن لنواجه الأمر، يمكن أن يكون حجم العمل، خاصةً كل الأعمال التحضيرية، صعبًا. بالإضافة إلى ذلك، لا يتوفّر في أغلب الأحيان عدد كافٍ من الموظفين، وقد تكون دروس الدعم التعليمي باهظة الثمن. لهذا السبب، نقترح عليك استخدام مساعد تعليمي مستند إلى الذكاء الاصطناعي. يمكن أن تُخفّف هذه الأداة من أعباء المعلّمين وتساعد في سد الفجوة الناتجة عن نقص الموظفين وعدم توفّر خدمات تعليمية بأسعار معقولة.
يمكن لمساعد التدريس المستنِد إلى الذكاء الاصطناعي إنشاء خطط دروس مفصّلة واختبارات ممتعة وملخّصات صوتية سهلة المتابعة ومهام مخصّصة. يتيح ذلك للمعلّمين التركيز على ما يجيدونه: التواصل مع الطلاب ومساعدتهم على حب التعلّم.
يتضمّن النظام موقعَين إلكترونيين: أحدهما للمعلّمين لإنشاء خطط الدروس للأسابيع المقبلة،
وحساب آخر للطلاب للوصول إلى الاختبارات والملخّصات الصوتية والمهام الدراسية.
حسنًا، لنطّلِع على البنية الأساسية التي تشغّل مساعد التدريس Aidemy. كما ترى، لقد قسمنا هذه العملية إلى عدة مكوّنات رئيسية تعمل معًا لتحقيق ذلك.
العناصر المعمارية والتكنولوجيات الرئيسية:
Google Cloud Platform (GCP): عنصر أساسي في النظام بأكمله:
- Vertex AI: للوصول إلى النماذج اللغوية الكبيرة في Gemini من Google
- Cloud Run: منصة لا تستخدِم الخوادم لنشر الوظائف والوكلاء المُنشئين في حاويات
- Cloud SQL: قاعدة بيانات PostgreSQL لبيانات المناهج الدراسية
- Pub/Sub وEventarc: أساس البنية المستندة إلى الأحداث، ما يتيح التواصل غير المتزامن بين المكوّنات
- مساحة التخزين في السحابة الإلكترونية: لتخزين ملفات الواجبات والملخّصات الصوتية
- أداة "إدارة الأسرار": تدير بأمان بيانات اعتماد قاعدة البيانات.
- سجلّ العناصر: يخزِّن صور Docker للوكلاء.
- Compute Engine: لنشر قاعدة بيانات لغوية كبيرة مستضافة ذاتيًا بدلاً من الاعتماد على حلول المورّدين
النماذج اللغوية الكبيرة: "الدماغ" للنظام:
- نماذج Gemini من Google: (Gemini 1.0 Pro وGemini 2 Flash وGemini 2 Flash Thinking وGemini 1.5-pro) تُستخدَم هذه النماذج لتخطيط الدروس وإنشاء المحتوى وإنشاء صفحات HTML ديناميكية وشرح الاختبارات وجمع المهام.
- DeepSeek: يُستخدَم لتنفيذ المهمة المتخصّصة المتمثّلة في إنشاء مهام للدراسة الذاتية.
LangChain وLangGraph: إطاران لتطوير تطبيقات النماذج اللغوية الكبيرة
- تسهيل إنشاء سير عمل معقدة متعددة الموظفين
- تتيح هذه الميزة التنسيق الذكي للأدوات (طلبات البيانات من واجهة برمجة التطبيقات وطلبات البحث في قاعدة البيانات وعمليات البحث على الويب).
- تنفيذ بنية مستندة إلى الأحداث لقابلية توسيع النظام ومرونة استخدامه
في الأساس، تجمع البنية الأساسية لدينا بين فعالية النماذج اللغوية الكبيرة والبيانات المنظَّمة وعمليات التواصل المستندة إلى الأحداث، وكل ذلك يتم تشغيله على Google Cloud. يتيح لنا ذلك إنشاء مساعد تعليمي قابل للتطوير وموثوق وفعّال.
3- قبل البدء
في Google Cloud Console، في صفحة أداة اختيار المشاريع، اختَر مشروعًا على Google Cloud أو أنشِئه. تأكَّد من تفعيل الفوترة لمشروعك على Cloud. تعرَّف على كيفية التحقّق مما إذا كانت الفوترة مفعَّلة في أحد المشاريع.
👉انقر على تفعيل Cloud Shell في أعلى وحدة تحكّم Google Cloud (يظهر رمز شكل وحدة الطرفية في أعلى لوحة Cloud Shell)، ثم انقر على الزر "فتح المحرِّر" (يشبه مجلدًا مفتوحًا مع قلم رصاص). سيؤدي ذلك إلى فتح "محرر الرموز البرمجية" في Cloud Shell في النافذة. سيظهر لك مستكشف ملفات على الجانب الأيمن.
👉انقر على الزر تسجيل الدخول باستخدام رمز Cloud Code في شريط الحالة أسفل الشاشة كما هو موضّح. امنح الإذن للمكوّن الإضافي وفقًا للتعليمات. إذا ظهر لك Cloud Code - no project (رمز السحابة الإلكترونية - لا يتوفّر مشروع) في شريط الحالة، اختَر ذلك ثمّ اختَر "اختيار مشروع على Google Cloud" من القائمة المنسدلة، ثمّ اختَر مشروع Google Cloud المحدّد من قائمة المشاريع التي أنشأتها.
👉افتح المحطة الطرفية في Cloud IDE، .
👉في المحطة الطرفية، تأكَّد من أنّك سبق أن تمّت مصادقتك وأنّه تم ضبط المشروع على رقم تعريف مشروعك باستخدام الأمر التالي:
gcloud auth list
👉ثمّ نفِّذ ما يلي:
gcloud config set project <YOUR_PROJECT_ID>
👉نفِّذ الأمر التالي لتفعيل واجهات برمجة تطبيقات Google Cloud اللازمة:
gcloud services enable compute.googleapis.com \
storage.googleapis.com \
run.googleapis.com \
artifactregistry.googleapis.com \
aiplatform.googleapis.com \
eventarc.googleapis.com \
sqladmin.googleapis.com \
secretmanager.googleapis.com \
cloudbuild.googleapis.com \
cloudresourcemanager.googleapis.com \
cloudfunctions.googleapis.com
قد يستغرق ذلك بضع دقائق.
تفعيل ميزة Gemini Code Assist في Cloud Shell IDE
انقر على الزر Code Assist (مساعدة في الترميز) في اللوحة اليمنى كما هو موضّح واختَر مشروع Google Cloud الصحيح مرة أخيرة. إذا طُلب منك تفعيل واجهة برمجة التطبيقات Cloud AI Companion API، يُرجى إجراء ذلك والمتابعة. بعد اختيار مشروعك على Google Cloud، تأكَّد من أنّه يمكنك الاطّلاع على ذلك في رسالة حالة Cloud Code في شريط الحالة ومن أنّ ميزة "مساعدة في الترميز" مفعَّلة أيضًا على يسار شريط الحالة كما هو موضّح أدناه:
إعداد الإذن
👉إعداد إذن حساب الخدمة
export PROJECT_ID=$(gcloud config get project)
export SERVICE_ACCOUNT_NAME=$(gcloud compute project-info describe --format="value(defaultServiceAccount)")
echo "Here's your SERVICE_ACCOUNT_NAME $SERVICE_ACCOUNT_NAME"
منح الأذونات 👉Cloud Storage (قراءة/كتابة):
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/storage.objectAdmin"
👉Pub/Sub (النشر/الاستلام):
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/pubsub.publisher"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/pubsub.subscriber"
👉Cloud SQL (قراءة/كتابة):
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/cloudsql.editor"
👉Eventarc (تلقّي الأحداث):
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/iam.serviceAccountTokenCreator"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/eventarc.eventReceiver"
👉Vertex AI (المستخدِم):
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/aiplatform.user"
👉مدير الأسرار (للقراءة):
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/secretmanager.secretAccessor"
👉التحقّق من النتيجة في وحدة تحكّم إدارة الهوية وإمكانية الوصول
4. إنشاء أول وكيل
قبل الغوص في أنظمة الوكلاء المتعدّدين المعقدة، علينا إنشاء عنصر أساسي: وكيل واحد وظيفي. في هذا القسم، سنخطو خطواتنا الأولى من خلال إنشاء وكيل "مزوّد كتب" بسيط. يأخذ موظّف دعم مقدّم الكتب فئة كإدخال ويستخدم نموذج تعلم لغوي كبيرًا (LLM) في Gemini لإنشاء كتاب تمثيلي بتنسيق JSON ضمن تلك الفئة. بعد ذلك، يتم عرض اقتراحات الكتب هذه كنقطة نهاية لواجهة برمجة التطبيقات REST API .
👉في علامة تبويب متصفح أخرى، افتح Google Cloud Console في متصفّح الويب، وفي قائمة التنقّل (☰)، انتقِل إلى "Cloud Run". انقر على الزرّ "+ ... كتابة دالة".
👉بعد ذلك، سنضبط الإعدادات الأساسية لوظيفة Cloud Run:
- اسم الخدمة:
book-provider
- المنطقة:
us-central1
- وقت التشغيل:
Python 3.12
- المصادقة:
Allow unauthenticated invocations
إلى "مفعّل"
👉اترك الإعدادات الأخرى على الإعدادات التلقائية وانقر على إنشاء. سينقلك هذا الإجراء إلى محرِّر الرموز البرمجية المصدر.
ستظهر لك ملفات main.py
وrequirements.txt
مملوءة مسبقًا.
سيحتوي main.py
على منطق النشاط التجاري للدالة، وسيحتوي requirements.txt
على الحِزم المطلوبة.
👉أصبحنا الآن جاهزين لكتابة بعض التعليمات البرمجية. ولكن قبل البدء، لنرى ما إذا كان بإمكان Gemini Code Assist مساعدتنا في بدء العمل. ارجع إلى "أداة تعديل Cloud Shell"، وانقر على رمز Gemini Code Assist، وألصِق الطلب التالي في مربّع الطلب:
Use the functions_framework library to be deployable as an HTTP function.
Accept a request with category and number_of_book parameters (either in JSON body or query string).
Use langchain and gemini to generate the data for book with fields bookname, author, publisher, publishing_date.
Use pydantic to define a Book model with the fields: bookname (string, description: "Name of the book"), author (string, description: "Name of the author"), publisher (string, description: "Name of the publisher"), and publishing_date (string, description: "Date of publishing").
Use langchain and gemini model to generate book data. the output should follow the format defined in Book model.
The logic should use JsonOutputParser from langchain to enforce output format defined in Book Model.
Have a function get_recommended_books(category) that internally uses langchain and gemini to return a single book object.
The main function, exposed as the Cloud Function, should call get_recommended_books() multiple times (based on number_of_book) and return a JSON list of the generated book objects.
Handle the case where category or number_of_book are missing by returning an error JSON response with a 400 status code.
return a JSON string representing the recommended books. use os library to retrieve GOOGLE_CLOUD_PROJECT env var. Use ChatVertexAI from langchain for the LLM call
ستنشئ ميزة "مساعدة في الترميز" بعد ذلك حلًا محتمَلاً، مع توفير رمز المصدر وملف المتطلّبات requirements.txt.
ننصحك بمقارنة الرمز الذي تم إنشاؤه باستخدام ميزة "مساعدة في الترميز" مع الحلّ الصحيح الذي تم اختباره والموضَّح أدناه. يتيح لك ذلك تقييم فعالية الأداة وتحديد أيّ تناقضات محتملة. على الرغم من أنّه لا يجب أبدًا الوثوق في النماذج اللغوية الكبيرة بشكل أعمى، يمكن أن تكون ميزة "مساعدة في الترميز" أداة رائعة لإنشاء النماذج الأولية بسرعة وإنشاء هياكل الرموز البرمجية الأولية، ويجب استخدامها للحصول على بداية جيدة.
بما أنّ هذه ورشة عمل، سنستخدم الرمز الذي تم إثبات ملكيته والموضَّح أدناه. ومع ذلك، يمكنك تجربة الرمز الذي تم إنشاؤه باستخدام ميزة "مساعدة في الترميز" في الوقت الذي يناسبك للتعرّف بشكل أفضل على إمكاناته والقيود المفروضة عليه.
👉ارجع إلى محرِّر رمز المصدر لدالة Cloud Run (في علامة التبويب الأخرى للمتصفّح). استبدِل بعناية المحتوى الحالي في main.py
بالرمز الوارد أدناه:
import functions_framework
import json
from flask import Flask, jsonify, request
from langchain_google_vertexai import ChatVertexAI
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.prompts import PromptTemplate
from pydantic import BaseModel, Field
import os
class Book(BaseModel):
bookname: str = Field(description="Name of the book")
author: str = Field(description="Name of the author")
publisher: str = Field(description="Name of the publisher")
publishing_date: str = Field(description="Date of publishing")
project_id = os.environ.get("GOOGLE_CLOUD_PROJECT")
llm = ChatVertexAI(model_name="gemini-1.0-pro")
def get_recommended_books(category):
"""
A simple book recommendation function.
Args:
category (str): category
Returns:
str: A JSON string representing the recommended books.
"""
parser = JsonOutputParser(pydantic_object=Book)
question = f"Generate a random made up book on {category} with bookname, author and publisher and publishing_date"
prompt = PromptTemplate(
template="Answer the user query.\n{format_instructions}\n{query}\n",
input_variables=["query"],
partial_variables={"format_instructions": parser.get_format_instructions()},
)
chain = prompt | llm | parser
response = chain.invoke({"query": question})
return json.dumps(response)
@functions_framework.http
def recommended(request):
request_json = request.get_json(silent=True) # Get JSON data
if request_json and 'category' in request_json and 'number_of_book' in request_json:
category = request_json['category']
number_of_book = int(request_json['number_of_book'])
elif request.args and 'category' in request.args and 'number_of_book' in request.args:
category = request.args.get('category')
number_of_book = int(request.args.get('number_of_book'))
else:
return jsonify({'error': 'Missing category or number_of_book parameters'}), 400
recommendations_list = []
for i in range(number_of_book):
book_dict = json.loads(get_recommended_books(category))
print(f"book_dict=======>{book_dict}")
recommendations_list.append(book_dict)
return jsonify(recommendations_list)
👉استبدِل محتوى requirements.txt بما يلي:
functions-framework==3.*
google-genai==1.0.0
flask==3.1.0
jsonify==0.5
langchain_google_vertexai==2.0.13
langchain_core==0.3.34
pydantic==2.10.5
👉سنضبط نقطة دخول الدالة: recommended
👉انقر على حفظ ونشر لنشر الدالة. انتظِر حتى تكتمل عملية النشر. ستعرِض Cloud Console الحالة. قد يستغرِق هذا الإجراء عدّة دقائق.
👉بعد النشر، انتقِل إلى محرِّر Cloud Shell، وفي الوحدة الطرفية، شغِّل:
export PROJECT_ID=$(gcloud config get project)
export BOOK_PROVIDER_URL=$(gcloud run services describe book-provider --region=us-central1 --project=$PROJECT_ID --format="value(status.url)")
curl -X POST -H "Content-Type: application/json" -d '{"category": "Science Fiction", "number_of_book": 2}' $BOOK_PROVIDER_URL
من المفترض أن يعرض بعض بيانات الكتاب بتنسيق JSON.
[
{"author":"Anya Sharma","bookname":"Echoes of the Singularity","publisher":"NovaLight Publishing","publishing_date":"2077-03-15"},
{"author":"Anya Sharma","bookname":"Echoes of the Quantum Dawn","publisher":"Nova Genesis Publishing","publishing_date":"2077-03-15"}
]
تهانينا! لقد نجحت في نشر إحدى وظائف Cloud Run. هذه إحدى الخدمات التي سندمجها عند تطوير وكيل Aidemy.
5- أدوات الإنشاء: ربط موظّفي الدعم بخدمة RESTFUL والبيانات
لنبدأ بتنزيل مشروع Bootstrap Skeleton، وتأكَّد من أنّك في محرِّر Cloud Shell. في الوحدة الطرفية، شغِّل
git clone https://github.com/weimeilin79/aidemy-bootstrap.git
بعد تنفيذ هذا الأمر، سيتم إنشاء مجلد جديد باسم aidemy-bootstrap
في بيئة Cloud Shell.
في لوحة مستكشف محرِّر Cloud Shell (عادةً على الجانب الأيمن)، من المفترض أن يظهر لك الآن المجلد الذي تم إنشاؤه عند استنساخ مستودع Git aidemy-bootstrap
. افتح المجلد الجذر لمشروعك في "المستكشف". سيظهر لك مجلد فرعي planner
بداخله، افتح هذا المجلد أيضًا.
لنبدأ في إنشاء الأدوات التي سيستخدمها موظّفو الدعم لتقديم مساعدة فعّالة. كما تعلم، تُجيد النماذج اللغوية الكبيرة الاستدلال وإنشاء النصوص، ولكنّها تحتاج إلى الوصول إلى مصادر خارجية لتنفيذ المهام الواقعية وتقديم معلومات دقيقة ومحدّثة. يمكنك اعتبار هذه الأدوات "سكين الجيش السويسري" للوكيل، ما يمنحه القدرة على التفاعل مع العالم.
عند إنشاء وكيل، من السهل أن تقع في فخ الترميز الثابت لمجموعة كبيرة من التفاصيل. يؤدي ذلك إلى إنشاء وكيل غير مرن. بدلاً من ذلك، من خلال إنشاء الأدوات واستخدامها، يمكن للوكيل الوصول إلى منطق أو أنظمة خارجية، ما يمنحه مزايا كلّ من الذكاء الاصطناعي اللغوي والبرمجة التقليدية.
في هذا القسم، سنضع الأساس لوكيل التخطيط الذي سيستخدمه المعلّمون لإنشاء خطط الدروس. قبل أن يبدأ الوكيل في إنشاء خطة، نريد وضع حدود من خلال تقديم المزيد من التفاصيل حول الموضوع. سننشئ ثلاث أدوات:
- طلب بيانات من واجهة برمجة تطبيقات RESTful: التفاعل مع واجهة برمجة تطبيقات حالية لاسترداد البيانات
- طلب قاعدة البيانات: جلب بيانات منظَّمة من قاعدة بيانات Cloud SQL
- بحث Google: الوصول إلى المعلومات في الوقت الفعلي من الويب
جلب اقتراحات الكتب من واجهة برمجة تطبيقات
أولاً، لننشئ أداة تسترجع اقتراحات الكتب من واجهة برمجة التطبيقات book-provider التي تم نشرها في القسم السابق. يوضّح ذلك كيف يمكن لموظف الدعم الاستفادة من الخدمات الحالية.
في محرِّر Cloud Shell، افتح مشروع aidemy-bootstrap
الذي نسخته في القسم السابق. 👉عدِّل book.py
في مجلد planner
والصِق الرمز التالي:
def recommend_book(query: str):
"""
Get a list of recommended book from an API endpoint
Args:
query: User's request string
"""
region = get_next_region();
llm = VertexAI(model_name="gemini-1.5-pro", location=region)
query = f"""The user is trying to plan a education course, you are the teaching assistant. Help define the category of what the user requested to teach, respond the categroy with no more than two word.
user request: {query}
"""
print(f"-------->{query}")
response = llm.invoke(query)
print(f"CATEGORY RESPONSE------------>: {response}")
# call this using python and parse the json back to dict
category = response.strip()
headers = {"Content-Type": "application/json"}
data = {"category": category, "number_of_book": 2}
books = requests.post(BOOK_PROVIDER_URL, headers=headers, json=data)
return books.text
if __name__ == "__main__":
print(recommend_book("I'm doing a course for my 5th grade student on Math Geometry, I'll need to recommend few books come up with a teach plan, few quizes and also a homework assignment."))
الشرح:
- recommend_book(query: str): تأخذ هذه الدالة طلب بحث المستخدم كمدخل.
- التفاعل مع النموذج اللغوي الكبير: يستخدم هذا التفاعل النموذج اللغوي الكبير لاستخراج الفئة من طلب البحث. يوضّح ذلك كيفية استخدام نموذج اللغة الكبيرة للمساعدة في إنشاء مَعلمات للأدوات.
- طلب البيانات من واجهة برمجة التطبيقات: يُرسِل طلب POST إلى واجهة برمجة التطبيقات الخاصة بموفّر الكتب، مع تمرير الفئة والعدد المطلوب من الكتب.
👉لاختبار هذه الدالة الجديدة، اضبط متغيّر البيئة، ثمّ نفِّذ ما يلي :
cd ~/aidemy-bootstrap/planner/
export BOOK_PROVIDER_URL=$(gcloud run services describe book-provider --region=us-central1 --project=$PROJECT_ID --format="value(status.url)")
👉ثبِّت التبعيات وشغِّل الرمز للتأكّد من أنّه يعمل، ثم شغِّل:
cd ~/aidemy-bootstrap/planner/
python -m venv env
source env/bin/activate
export PROJECT_ID=$(gcloud config get project)
pip install -r requirements.txt
python book.py
تجاهل النافذة المنبثقة لتحذير Git.
من المفترض أن تظهر لك سلسلة JSON تحتوي على اقتراحات كتب تم استرجاعها من واجهة برمجة التطبيقات لمقدّم الكتب.
[{"author":"Anya Sharma","bookname":"Echoes of the Singularity","publisher":"NovaLight Publishing","publishing_date":"2077-03-15"},{"author":"Anya Sharma","bookname":"Echoes of the Quantum Dawn","publisher":"Nova Genesis Publishing","publishing_date":"2077-03-15"}]
إذا ظهر لك هذا الرمز، يعني ذلك أنّ الأداة الأولى تعمل بشكل صحيح.
بدلاً من إنشاء طلب صريح لواجهة برمجة التطبيقات RESTful باستخدام مَعلمات محدّدة، نستخدم لغة طبيعية ("أُجري دورة تدريبية..."). بعد ذلك، يستخرج موظّف الدعم الذكي المَعلمات اللازمة (مثل الفئة) باستخدام معالجة اللغة الطبيعية، ما يُبرز كيفية الاستفادة من معالجة اللغة الطبيعية للتفاعل مع واجهة برمجة التطبيقات.
👉أزِل رمز الاختبار التالي من book.py
.
if __name__ == "__main__":
print(recommend_book("I'm doing a course for my 5th grade student on Math Geometry, I'll need to recommend few books come up with a teach plan, few quizes and also a homework assignment."))
الحصول على بيانات المنهج الدراسي من قاعدة بيانات
بعد ذلك، سننشئ أداة تُستخدَم لجلب بيانات المناهج الدراسية المنظَّمة من قاعدة بيانات PostgreSQL في Cloud SQL. يتيح ذلك للوكيل الوصول إلى مصدر موثوق للمعلومات من أجل تخطيط الدروس.
👉نفِّذ الأوامر التالية في الوحدة الطرفية لإنشاء مثيل Cloud SQL باسم aidemy . قد تستغرق هذه العملية بعض الوقت.
gcloud sql instances create aidemy \
--database-version=POSTGRES_14 \
--cpu=2 \
--memory=4GB \
--region=us-central1 \
--root-password=1234qwer \
--storage-size=10GB \
--storage-auto-increase
👉بعد ذلك، أنشئ قاعدة بيانات باسم aidemy-db
في المثيل الجديد.
gcloud sql databases create aidemy-db \
--instance=aidemy
لنتحقّق من المثيل في Cloud SQL في Google Cloud Console، من المفترض أن يظهر لك مثيل Cloud SQL باسم aidemy
. انقر على اسم المثيل للاطّلاع على تفاصيله. في صفحة تفاصيل مثيل Cloud SQL، انقر على "SQL Studio" في قائمة التنقّل اليمنى. سيؤدي ذلك إلى فتح علامة تبويب جديدة.
انقر للاتصال بقاعدة البيانات. سجِّل الدخول إلى SQL Studio.
اختَر aidemy-db
كقاعدة بيانات. أدخِل postgres
كـ user و1234qwer
كـ password.
👉في محرِّر طلبات البحث في SQL Studio، ألصِق رمز SQL التالي:
CREATE TABLE curriculums (
id SERIAL PRIMARY KEY,
year INT,
subject VARCHAR(255),
description TEXT
);
-- Inserting detailed curriculum data for different school years and subjects
INSERT INTO curriculums (year, subject, description) VALUES
-- Year 5
(5, 'Mathematics', 'Introduction to fractions, decimals, and percentages, along with foundational geometry and problem-solving techniques.'),
(5, 'English', 'Developing reading comprehension, creative writing, and basic grammar, with a focus on storytelling and poetry.'),
(5, 'Science', 'Exploring basic physics, chemistry, and biology concepts, including forces, materials, and ecosystems.'),
(5, 'Computer Science', 'Basic coding concepts using block-based programming and an introduction to digital literacy.'),
-- Year 6
(6, 'Mathematics', 'Expanding on fractions, ratios, algebraic thinking, and problem-solving strategies.'),
(6, 'English', 'Introduction to persuasive writing, character analysis, and deeper comprehension of literary texts.'),
(6, 'Science', 'Forces and motion, the human body, and introductory chemical reactions with hands-on experiments.'),
(6, 'Computer Science', 'Introduction to algorithms, logical reasoning, and basic text-based programming (Python, Scratch).'),
-- Year 7
(7, 'Mathematics', 'Algebraic expressions, geometry, and introduction to statistics and probability.'),
(7, 'English', 'Analytical reading of classic and modern literature, essay writing, and advanced grammar skills.'),
(7, 'Science', 'Introduction to cells and organisms, chemical reactions, and energy transfer in physics.'),
(7, 'Computer Science', 'Building on programming skills with Python, introduction to web development, and cyber safety.');
تنشئ رمز SQL هذا جدولاً باسم curriculums
وتُدرج بعض نماذج البيانات. انقر على تشغيل لتنفيذ رمز SQL. من المفترض أن تظهر لك رسالة تأكيد تشير إلى تنفيذ الأوامر بنجاح.
👉وسِّع المستكشف، وابحث عن الجدول الذي تم إنشاؤه حديثًا وانقر على طلب بحث. من المفترض أن يتم فتح علامة تبويب جديدة للمحرِّر تتضمّن لغة SQL التي تم إنشاؤها لك.
SELECT * FROM
"public"."curriculums" LIMIT 1000;
👉انقر على تشغيل.
من المفترض أن يعرض جدول النتائج صفوف البيانات التي أدرجتها في الخطوة السابقة، ما يؤكّد أنّه تم إنشاء الجدول والبيانات بشكل صحيح.
الآن بعد أن نجحت في إنشاء قاعدة بيانات تحتوي على بيانات نموذجية مملوءة للمناهج الدراسية، سننشئ أداة لاستردادها.
👉في Cloud Code Editor (أداة تعديل الرموز في السحابة الإلكترونية)، عدِّل الملف curriculums.py
في المجلد aidemy-bootstrap
والصِق الرمز التالي:
def connect_with_connector() -> sqlalchemy.engine.base.Engine:
db_user = os.environ["DB_USER"]
db_pass = os.environ["DB_PASS"]
db_name = os.environ["DB_NAME"]
encoded_db_user = os.environ.get("DB_USER")
print(f"--------------------------->db_user: {db_user!r}")
print(f"--------------------------->db_pass: {db_pass!r}")
print(f"--------------------------->db_name: {db_name!r}")
ip_type = IPTypes.PRIVATE if os.environ.get("PRIVATE_IP") else IPTypes.PUBLIC
connector = Connector()
def getconn() -> pg8000.dbapi.Connection:
conn: pg8000.dbapi.Connection = connector.connect(
instance_connection_name,
"pg8000",
user=db_user,
password=db_pass,
db=db_name,
ip_type=ip_type,
)
return conn
pool = sqlalchemy.create_engine(
"postgresql+pg8000://",
creator=getconn,
pool_size=2,
max_overflow=2,
pool_timeout=30, # 30 seconds
pool_recycle=1800, # 30 minutes
)
return pool
def init_connection_pool() -> sqlalchemy.engine.base.Engine:
return (
connect_with_connector()
)
raise ValueError(
"Missing database connection type. Please define one of INSTANCE_HOST, INSTANCE_UNIX_SOCKET, or INSTANCE_CONNECTION_NAME"
)
def get_curriculum(year: int, subject: str):
"""
Get school curriculum
Args:
subject: User's request subject string
year: User's request year int
"""
try:
stmt = sqlalchemy.text(
"SELECT description FROM curriculums WHERE year = :year AND subject = :subject"
)
with db.connect() as conn:
result = conn.execute(stmt, parameters={"year": year, "subject": subject})
row = result.fetchone()
if row:
return row[0]
else:
return None
except Exception as e:
print(e)
return None
db = init_connection_pool()
الشرح:
- متغيّرات البيئة: تسترجع التعليمة البرمجية بيانات اعتماد قاعدة البيانات ومعلومات الاتصال من متغيّرات البيئة (مزيد من المعلومات عن ذلك أدناه).
- connect_with_connector(): تستخدِم هذه الدالة أداة ربط Cloud SQL لإنشاء اتصال آمن بقاعدة البيانات.
- get_curriculum(year: int, subject: str): تأخذ هذه الدالة السنة والموضوع كمدخلات، وتستدعي جدول المناهج الدراسية، وتُعرِض وصف المنهج الدراسي المقابل.
👉قبل أن نتمكّن من تشغيل الرمز، يجب ضبط بعض متغيّرات البيئة. في المحطة الطرفية، يمكنك تشغيل:
export PROJECT_ID=$(gcloud config get project)
export INSTANCE_NAME="aidemy"
export REGION="us-central1"
export DB_USER="postgres"
export DB_PASS="1234qwer"
export DB_NAME="aidemy-db"
👉لاختبار الرمز، أضِف الرمز التالي إلى نهاية curriculums.py
:
if __name__ == "__main__":
print(get_curriculum(6, "Mathematics"))
👉شغِّل الرمز البرمجي:
cd ~/aidemy-bootstrap/planner/
source env/bin/activate
python curriculums.py
من المفترض أن يظهر لك وصف المنهج الدراسي لمادة الرياضيات في الصف السادس مطبوعًا على وحدة التحكّم.
Expanding on fractions, ratios, algebraic thinking, and problem-solving strategies.
إذا ظهر لك وصف المنهج، يعني ذلك أنّ أداة قاعدة البيانات تعمل بشكل صحيح. يمكنك إيقاف النص البرمجي من خلال الضغط على Ctrl+C
.
👉أزِل رمز الاختبار التالي من curriculums.py
.
if __name__ == "__main__":
print(get_curriculum(6, "Mathematics"))
👉الخروج من البيئة الافتراضية، في المحطة الطرفية، يمكنك تنفيذ ما يلي:
deactivate
6- أدوات الإنشاء: الوصول إلى المعلومات في الوقت الفعلي من الويب
أخيرًا، سننشئ أداة تستخدِم عملية دمج Gemini 2 مع "بحث Google" للوصول إلى المعلومات في الوقت الفعلي من الويب. يساعد ذلك موظّف الدعم في البقاء على اطّلاع دائم وتقديم نتائج ذات صلة.
إنّ دمج Gemini 2 مع Google Search API يعزّز قدرات موظّفي الدعم من خلال تقديم نتائج بحث أكثر دقة وصلة بسياق الطلب. يتيح ذلك لموظّفي الدعم الوصول إلى معلومات محدّثة وتقديم ردود مستندة إلى بيانات من الواقع، ما يقلل من الأخطاء. ويسهّل دمج واجهة برمجة التطبيقات المحسّن أيضًا إجراء المزيد من طلبات البحث باللغة اليومية العادية، ما يتيح لموظّفي الدعم صياغة طلبات بحث معقّدة ودقيقة.
تأخذ هذه الدالة طلب بحث ومنهجًا ومادة وسنة كمدخلات، وتستخدم Gemini API وأداة "بحث Google" لاسترداد المعلومات ذات الصلة من الإنترنت. عند الاطّلاع عن كثب، يتبيّن أنّه يستخدم حزمة تطوير البرامج (SDK) لتكنولوجيات الذكاء الاصطناعي التوليدي من Google لإجراء استدعاء الدوالّ بدون استخدام أي إطار عمل آخر.
👉عدِّل search.py
في مجلد aidemy-bootstrap
والصِق الرمز التالي:
model_id = "gemini-2.0-flash-001"
google_search_tool = Tool(
google_search = GoogleSearch()
)
def search_latest_resource(search_text: str, curriculum: str, subject: str, year: int):
"""
Get latest information from the internet
Args:
search_text: User's request category string
subject: "User's request subject" string
year: "User's request year" integer
"""
search_text = "%s in the context of year %d and subject %s with following curriculum detail %s " % (search_text, year, subject, curriculum)
region = get_next_region()
client = genai.Client(vertexai=True, project=PROJECT_ID, location=region)
print(f"search_latest_resource text-----> {search_text}")
response = client.models.generate_content(
model=model_id,
contents=search_text,
config=GenerateContentConfig(
tools=[google_search_tool],
response_modalities=["TEXT"],
)
)
print(f"search_latest_resource response-----> {response}")
return response
if __name__ == "__main__":
response = search_latest_resource("What are the syllabus for Year 6 Mathematics?", "Expanding on fractions, ratios, algebraic thinking, and problem-solving strategies.", "Mathematics", 6)
for each in response.candidates[0].content.parts:
print(each.text)
الشرح:
- تحديد الأداة - google_search_tool: لفّ عنصر GoogleSearch ضمن أداة
- search_latest_resource(search_text: str, subject: str, year: int): تأخذ هذه الدالة طلب بحث وموضوعًا وسنة كمدخلات، وتستخدم Gemini API لإجراء بحث على Google. نموذج Gemini
- GenerateContentConfig: تحديد إمكانية وصولها إلى أداة GoogleSearch
يحلّل نموذج Gemini داخليًا search_text ويحدّد ما إذا كان بإمكانه الإجابة عن السؤال مباشرةً أو ما إذا كان يحتاج إلى استخدام أداة GoogleSearch. هذه خطوة حاسمة تحدث خلال عملية الاستدلال في نموذج الذكاء الاصطناعي القانوني. تم تدريب النموذج على التعرّف على الحالات التي تكون فيها الأدوات الخارجية ضرورية. إذا قرّر النموذج استخدام أداة GoogleSearch، ستتولى حزمة تطوير البرامج (SDK) لتكنولوجيات الذكاء الاصطناعي التوليدي من Google عملية الاستدعاء الفعلية. تأخذ حزمة تطوير البرامج (SDK) قرار النموذج والمَعلمات التي ينشئها وترسلها إلى Google Search API. ويتم إخفاء هذا الجزء عن المستخدم في الرمز.
بعد ذلك، يدمج نموذج Gemini نتائج البحث في ردّه. ويمكنه استخدام المعلومات للإجابة عن سؤال المستخدم أو إنشاء ملخّص أو تنفيذ بعض المهام الأخرى.
👉للاختبار، شغِّل الرمز البرمجي:
cd ~/aidemy-bootstrap/planner/
export PROJECT_ID=$(gcloud config get project)
source env/bin/activate
python search.py
من المفترض أن يظهر لك ردّ Gemini Search API يحتوي على نتائج بحث ذات صلة بـ "منهجية الصف الخامس في الرياضيات". سيعتمد الناتج الدقيق على نتائج البحث، ولكن سيكون عنصرًا بتنسيق JSON يحتوي على معلومات عن البحث.
إذا ظهرت لك نتائج البحث، يعني ذلك أنّ أداة "بحث Google" تعمل بشكل صحيح. يمكنك إيقاف النص البرمجي من خلال الضغط على Ctrl+C
.
👉وأزِل الجزء الأخير من الرمز.
if __name__ == "__main__":
response = search_latest_resource("What are the syllabus for Year 6 Mathematics?", "Expanding on fractions, ratios, algebraic thinking, and problem-solving strategies.", "Mathematics", 6)
for each in response.candidates[0].content.parts:
print(each.text)
👉الخروج من البيئة الافتراضية، في المحطة الطرفية، يمكنك تنفيذ ما يلي:
deactivate
تهانينا! لقد أنشأت الآن ثلاث أدوات فعّالة لوكيل التخطيط: موصل واجهة برمجة التطبيقات وموصِّل قاعدة البيانات وأداة "بحث Google". ستتيح هذه الأدوات للموظف الوصول إلى المعلومات والإمكانات التي يحتاجها لإنشاء خطط تعليمية فعّالة.
7- التنسيق باستخدام LangGraph
بعد أن أنشأنا أدواتنا الفردية، حان وقت تنسيقها باستخدام LangGraph. سيتيح لنا ذلك إنشاء وكيل "مخطِّط" أكثر تطوّرًا يمكنه تحديد الأدوات التي يجب استخدامها ووقت استخدامها بشكل ذكي استنادًا إلى طلب المستخدم.
LangGraph هي مكتبة Python مصمّمة لتسهيل إنشاء تطبيقات متعددة الجهات ذات حالة باستخدام النماذج اللغوية الكبيرة (LLM). يمكنك اعتباره إطار عمل لتنسيق المحادثات المعقدة ومسارات العمل التي تتضمّن نماذج لغوية كبيرة والأدوات والموظفين الآخرين.
المفاهيم الرئيسية:
- بنية الرسم البياني: يمثّل LangGraph منطق تطبيقك كرسم بياني موجه. تمثّل كل عقدة في الرسم البياني خطوة في العملية (مثل طلب إجراء تحليل لغوي موسّع، أو طلب تشغيل أداة، أو إجراء فحص شَرطي). تحدِّد الحواف تدفّق التنفيذ بين العقد.
- الحالة: تدير LangGraph حالة تطبيقك أثناء تنقّله في الرسم البياني. يمكن أن تتضمّن هذه الحالة متغيّرات مثل إدخال المستخدم ونتائج طلبات بيانات الأداة والنواتج الوسيطة من النماذج اللغوية الكبيرة وأي معلومات أخرى يجب الحفاظ عليها بين الخطوات.
- العقد: تمثّل كل عقدة عملية حسابية أو تفاعلًا. يمكن أن تكون:
- عقد الأدوات: استخدام أداة (مثل إجراء بحث على الويب أو طلب بحث في قاعدة بيانات)
- عقد الدوالّ: يمكنك تنفيذ دالة Python.
- الحواف: تربط بين العقد، وتحدِّد تسلسل التنفيذ. يمكن أن تكون:
- الحواف المباشرة: تشير إلى تدفّق بسيط غير مشروط من عقدة إلى أخرى.
- الحواف الشَرطية: يعتمد مسار التنقّل على نتيجة عقدة شَرطية.
سنستخدم LangGraph لتنفيذ عملية التنسيق. لنعدِّل ملف aidemy.py
ضمن مجلد aidemy-bootstrap
لتحديد منطق LangGraph. 👉يجب إلحاق رمز التتبّع بنهاية aidemy.py
:
tools = [get_curriculum, search_latest_resource, recommend_book]
def determine_tool(state: MessagesState):
llm = ChatVertexAI(model_name="gemini-2.0-flash-001", location=get_next_region())
sys_msg = SystemMessage(
content=(
f"""You are a helpful teaching assistant that helps gather all needed information.
Your ultimate goal is to create a detailed 3-week teaching plan.
You have access to tools that help you gather information.
Based on the user request, decide which tool(s) are needed.
"""
)
)
llm_with_tools = llm.bind_tools(tools)
return {"messages": llm_with_tools.invoke([sys_msg] + state["messages"])}
تتولّى هذه الدالة رصد الحالة الحالية للمحادثة، وتزويد النموذج اللغوي الكبير برسالة نظام، ثمّ طلب إنشاء ردّ من النموذج اللغوي الكبير. يمكن للذكاء الاصطناعي اللغوي الردّ مباشرةً على المستخدم أو اختيار استخدام إحدى الأدوات المتاحة.
الأدوات : تمثّل هذه القائمة مجموعة الأدوات المتاحة للوكيل. يحتوي على ثلاث دوالّ أدوات حدّدناها في الخطوات السابقة: get_curriculum
وsearch_latest_resource
وrecommend_book
. llm.bind_tools(tools): تعمل هذه الوظيفة على "ربط" قائمة الأدوات بعنصر llm. يؤدي ربط الأدوات إلى إبلاغ النموذج اللغوي الكبير بمدى توفّر هذه الأدوات، كما يقدّم له معلومات عن كيفية استخدامها (مثل أسماء الأدوات والمَعلمات التي تقبلها ووظائفها).
سنستخدم LangGraph لتنفيذ عملية التنسيق. 👉أضِف الرمز التالي إلى نهاية aidemy.py
:
def prep_class(prep_needs):
builder = StateGraph(MessagesState)
builder.add_node("determine_tool", determine_tool)
builder.add_node("tools", ToolNode(tools))
builder.add_edge(START, "determine_tool")
builder.add_conditional_edges("determine_tool",tools_condition)
builder.add_edge("tools", "determine_tool")
memory = MemorySaver()
graph = builder.compile(checkpointer=memory)
config = {"configurable": {"thread_id": "1"}}
messages = graph.invoke({"messages": prep_needs},config)
print(messages)
for m in messages['messages']:
m.pretty_print()
teaching_plan_result = messages["messages"][-1].content
return teaching_plan_result
if __name__ == "__main__":
prep_class("I'm doing a course for year 5 on subject Mathematics in Geometry, , get school curriculum, and come up with few books recommendation plus search latest resources on the internet base on the curriculum outcome. And come up with a 3 week teaching plan")
الشرح:
-
StateGraph(MessagesState)
: لإنشاء عنصرStateGraph
StateGraph
هو مفهوم أساسي في LangGraph. ويمثّل سير عمل موظّف الدعم على شكل رسم بياني، حيث تمثّل كلّ عقدة في الرسم البياني خطوة في العملية. يمكنك اعتبارها خارطة طريق لكيفية تفكير موظّف الدّعم وتصرّفه. - الحافة الشَرطية: تأتي الوسيطة
tools_condition
من العقدة"determine_tool"
، ومن المرجّح أنّها دالة تحدّد الحافة التي يجب اتّباعها استنادًا إلى ناتج الدالةdetermine_tool
. تسمح الحواف الشَرطية للرسم البياني بالتشعّب استنادًا إلى قرار نموذج اللغة الكبيرة بشأن الأداة التي سيتم استخدامها (أو ما إذا كان سيتم الردّ على المستخدم مباشرةً). وهنا يأتي دور "ذكاء" موظّف الدعم، إذ يمكنه تعديل سلوكه بشكل ديناميكي استنادًا إلى الموقف. - حلقة: تُضيف حافة إلى الرسم البياني تربط عقدة
"tools"
بعقدة"determine_tool"
. يؤدي ذلك إلى إنشاء حلقة في الرسم البياني، ما يسمح للموظف باستخدام الأدوات بشكل متكرر إلى أن يجمع معلومات كافية لإكمال المهمة وتقديم إجابة مرضية. هذه الحلقة ضرورية للمهام المعقّدة التي تتطلّب خطوات متعددة من التفكير وجمع المعلومات.
الآن، لنختبر موظّف الدعم المختصّ بالتخطيط لمعرفة كيفية تنسيقه للأدوات المختلفة.
سيُشغِّل هذا الرمز الدالة prep_class باستخدام إدخال مستخدم محدّد، ما يحاكي طلبًا لإنشاء خطة تدريس لصف الرياضيات في الهندسة للصف الخامس، باستخدام المنهج الدراسي واقتراحات الكتب وأحدث مراجع الإنترنت.
إذا أغلقت وحدة التحكّم أو لم تعُد متغيّرات البيئة مضبوطة، أعِد تنفيذ الأوامر التالية.
export BOOK_PROVIDER_URL=$(gcloud run services describe book-provider --region=us-central1 --project=$PROJECT_ID --format="value(status.url)")
export PROJECT_ID=$(gcloud config get project)
export INSTANCE_NAME="aidemy"
export REGION="us-central1"
export DB_USER="postgres"
export DB_PASS="1234qwer"
export DB_NAME="aidemy-db"
👉شغِّل الرمز البرمجي:
cd ~/aidemy-bootstrap/planner/
source env/bin/activate
pip install -r requirements.txt
python aidemy.py
راقِب السجلّ في المحطة الطرفية. من المفترض أن يظهر لك دليل على أنّ موظّف الدعم يستخدِم كل الأدوات الثلاث (الحصول على المنهج الدراسي في المدرسة والحصول على اقتراحات الكتب والبحث عن أحدث المراجع) قبل تقديم الخطة التعليمية النهائية. يشير ذلك إلى أنّ عملية تنسيق LangGraph تعمل بشكل صحيح، وأنّ موظّف الدعم يستخدم بذكاء جميع الأدوات المتاحة لتلبية طلب المستخدم.
================================ Human Message =================================
I'm doing a course for year 5 on subject Mathematics in Geometry, , get school curriculum, and come up with few books recommendation plus search latest resources on the internet base on the curriculum outcome. And come up with a 3 week teaching plan
================================== Ai Message ==================================
Tool Calls:
get_curriculum (xxx)
Call ID: xxx
Args:
year: 5.0
subject: Mathematics
================================= Tool Message =================================
Name: get_curriculum
Introduction to fractions, decimals, and percentages, along with foundational geometry and problem-solving techniques.
================================== Ai Message ==================================
Tool Calls:
search_latest_resource (xxxx)
Call ID: xxxx
Args:
year: 5.0
search_text: Geometry
curriculum: {"content": "Introduction to fractions, decimals, and percentages, along with foundational geometry and problem-solving techniques."}
subject: Mathematics
================================= Tool Message =================================
Name: search_latest_resource
candidates=[Candidate(content=Content(parts=[Part(.....) automatic_function_calling_history=[] parsed=None
================================== Ai Message ==================================
Tool Calls:
recommend_book (93b48189-4d69-4c09-a3bd-4e60cdc5f1c6)
Call ID: 93b48189-4d69-4c09-a3bd-4e60cdc5f1c6
Args:
query: Mathematics Geometry Year 5
================================= Tool Message =================================
Name: recommend_book
[{.....}]
================================== Ai Message ==================================
Based on the curriculum outcome, here is a 3-week teaching plan for year 5 Mathematics Geometry:
**Week 1: Introduction to Shapes and Properties**
.........
أوقِف النص البرمجي من خلال الضغط على Ctrl+C
.
👉استبدِل الآن رمز الاختبار بطلب مختلف يتطلّب استدعاء أدوات مختلفة.
if __name__ == "__main__":
prep_class("I'm doing a course for year 5 on subject Mathematics in Geometry, search latest resources on the internet base on the subject. And come up with a 3 week teaching plan")
إذا أغلقت وحدة التحكّم أو لم تعُد متغيّرات البيئة مضبوطة، أعِد تنفيذ الأوامر التالية.
export BOOK_PROVIDER_URL=$(gcloud run services describe book-provider --region=us-central1 --project=$PROJECT_ID --format="value(status.url)")
export PROJECT_ID=$(gcloud config get project)
export INSTANCE_NAME="aidemy"
export REGION="us-central1"
export DB_USER="postgres"
export DB_PASS="1234qwer"
export DB_NAME="aidemy-db"
👉تشغيل الرمز مرة أخرى:
cd ~/aidemy-bootstrap/planner/
source env/bin/activate
python aidemy.py
ما الذي لاحظته هذه المرة؟ ما هي الأدوات التي اتصل بها موظّف الدعم؟ من المفترض أن تلاحظ أنّ الوكيل يستدعي أداة search_latest_resource فقط هذه المرة. ويعود السبب في ذلك إلى أنّ الطلب لا يحدّد أنّه يحتاج إلى الأداتَين الأخريتَين، وتقنية LLM لدينا ذكية بما يكفي لعدم استدعاء الأداتَين الأخريتَين.
================================ Human Message =================================
I'm doing a course for year 5 on subject Mathematics in Geometry, search latest resources on the internet base on the subject. And come up with a 3 week teaching plan
================================== Ai Message ==================================
Tool Calls:
get_curriculum (xxx)
Call ID: xxx
Args:
year: 5.0
subject: Mathematics
================================= Tool Message =================================
Name: get_curriculum
Introduction to fractions, decimals, and percentages, along with foundational geometry and problem-solving techniques.
================================== Ai Message ==================================
Tool Calls:
search_latest_resource (xxx)
Call ID: xxxx
Args:
year: 5.0
subject: Mathematics
curriculum: {"content": "Introduction to fractions, decimals, and percentages, along with foundational geometry and problem-solving techniques."}
search_text: Geometry
================================= Tool Message =================================
Name: search_latest_resource
candidates=[Candidate(content=Content(parts=[Part(.......token_count=40, total_token_count=772) automatic_function_calling_history=[] parsed=None
================================== Ai Message ==================================
Based on the information provided, a 3-week teaching plan for Year 5 Mathematics focusing on Geometry could look like this:
**Week 1: Introducing 2D Shapes**
........
* Use visuals, manipulatives, and real-world examples to make the learning experience engaging and relevant.
أوقِف النص البرمجي من خلال الضغط على Ctrl+C
. 👉أزِل رمز الاختبار للحفاظ على ملف aidemy.py نظيفًا:
if __name__ == "__main__":
prep_class("I'm doing a course for year 5 on subject Mathematics in Geometry, search latest resources on the internet base on the subject. And come up with a 3 week teaching plan")
بعد تحديد منطق موظّف الدعم، لنبدأ بتشغيل تطبيق الويب Flask. سيوفّر ذلك واجهة مألوفة مستندة إلى نموذج للمعلّمين للتفاعل مع موظّف الدعم. على الرغم من أنّ التفاعلات مع محادثات الدردشة شائعة في نماذج اللغة المحوسبة الكبيرة، فإنّنا نختار واجهة مستخدم تقليدية لإرسال النماذج، لأنّها قد تكون أكثر سهولة بالنسبة إلى العديد من المعلّمين.
إذا أغلقت وحدة التحكّم أو لم تعُد متغيّرات البيئة مضبوطة، أعِد تنفيذ الأوامر التالية.
export BOOK_PROVIDER_URL=$(gcloud run services describe book-provider --region=us-central1 --project=$PROJECT_ID --format="value(status.url)")
export PROJECT_ID=$(gcloud config get project)
export INSTANCE_NAME="aidemy"
export REGION="us-central1"
export DB_USER="postgres"
export DB_PASS="1234qwer"
export DB_NAME="aidemy-db"
👉الآن، ابدأ واجهة مستخدم الويب.
cd ~/aidemy-bootstrap/planner/
source env/bin/activate
python app.py
ابحث عن رسائل بدء التشغيل في إخراج محطة Cloud Shell. يطبع Flask عادةً رسائل تشير إلى أنّه قيد التشغيل وعلى المنفذ الذي يعمل عليه.
Running on http://127.0.0.1:8080
Running on http://127.0.0.1:8080
The application needs to keep running to serve requests.
👉من قائمة "معاينة الويب"، اختَر "معاينة على المنفذ 8080". ستفتح Cloud Shell علامة تبويب أو نافذة متصفّح جديدة تتضمّن معاينة تطبيقك على الويب.
في واجهة التطبيق، اختَر 5
للسنة، واختَر الموضوع Mathematics
واكتب Geometry
في طلب الإضافة .
بدلاً من التحديق في الشاشة أثناء انتظار الردّ، يمكنك الانتقال إلى وحدة تحكّم Cloud Editor. يمكنك مراقبة مستوى التقدّم وأيّ رسائل ناتجة أو أخطاء تنشئها الدالة في وحدة تحكّم المحاكي. 😁
👉أوقِف النص البرمجي بالضغط على Ctrl+C
في وحدة التحكّم.
👉الخروج من البيئة الافتراضية:
deactivate
8. نشر وكيل التخطيط في السحابة الإلكترونية
إنشاء الصورة وإرسالها إلى السجلّ
👉حان وقت نشر هذا الإجراء في السحابة الإلكترونية. في وحدة التحكّم الطرفية، أنشئ مستودعًا للعناصر لتخزين صورة Docker التي سننشئها.
gcloud artifacts repositories create agent-repository \
--repository-format=docker \
--location=us-central1 \
--description="My agent repository"
من المفترض أن يظهر لك المستودع الذي تم إنشاؤه [agent-repository].
👉شغِّل الأمر التالي لإنشاء صورة Docker.
cd ~/aidemy-bootstrap/planner/
export PROJECT_ID=$(gcloud config get project)
docker build -t gcr.io/${PROJECT_ID}/aidemy-planner .
👉علينا إعادة وضع علامة على الصورة حتى يتم استضافتها في Artifact Registry بدلاً من GCR ودفع الصورة التي تم وضع علامة عليها إلى Artifact Registry:
export PROJECT_ID=$(gcloud config get project)
docker tag gcr.io/${PROJECT_ID}/aidemy-planner us-central1-docker.pkg.dev/${PROJECT_ID}/agent-repository/aidemy-planner
docker push us-central1-docker.pkg.dev/${PROJECT_ID}/agent-repository/aidemy-planner
بعد اكتمال عملية الدفع، يمكنك التأكّد من تخزين الصورة بنجاح في Artifact Registry. انتقِل إلى Artifact Registry (مستودع العناصر) في Google Cloud Console. من المفترض أن تعثر على صورة aidemy-planner
ضمن مستودع agent-repository
.
تأمين بيانات اعتماد قاعدة البيانات باستخدام "مدير الأسرار"
لإدارة بيانات اعتماد قاعدة البيانات والوصول إليها بأمان، سنستخدم أداة Google Cloud Secret Manager. ويمنع ذلك تضمين المعلومات الحساسة في رمز التطبيق ويعزّز مستوى الأمان.
👉سننشئ أسرارًا فردية لاسم مستخدم قاعدة البيانات وكلمة المرور واسم قاعدة البيانات. تتيح لنا هذه الطريقة إدارة كل بيانات اعتماد بشكل مستقل. في الوحدة الطرفية، شغِّل:
gcloud secrets create db-user
printf "postgres" | gcloud secrets versions add db-user --data-file=-
gcloud secrets create db-pass
printf "1234qwer" | gcloud secrets versions add db-pass --data-file=-
gcloud secrets create db-name
printf "aidemy-db" | gcloud secrets versions add db-name --data-file=-
يُعدّ استخدام "مدير الأسرار" خطوة مهمة في تأمين تطبيقك ومنع الكشف عن بيانات الاعتماد الحسّاسة عن طريق الخطأ. وتتّبع أفضل ممارسات الأمان لعمليات النشر في السحابة الإلكترونية.
النشر على Cloud Run
Cloud Run هي منصّة مُدارة بالكامل بدون خوادم تتيح لك نشر التطبيقات المُنشأة باستخدام حاويات بسرعة وسهولة. ويزيل هذا الإطار إدارة البنية الأساسية، ما يتيح لك التركيز على كتابة الرمز البرمجي ونشره. سننشر أداة التخطيط لدينا كخدمة على Cloud Run.
👉في Google Cloud Console، انتقِل إلى Cloud Run. انقر على نشر الحاوية واختَر الخدمة. اضبط خدمة Cloud Run:
- صورة الحاوية: انقر على "اختيار" في حقل عنوان URL. ابحث عن عنوان URL للصورة التي تم دفعها إلى Artifact Registry (مثل us-central1-docker.pkg.dev/YOUR_PROJECT_ID/agent-repository/agent-planner/YOUR_IMG).
- اسم الخدمة:
aidemy-planner
- المنطقة: اختَر منطقة
us-central1
. - المصادقة: لأغراض هذه الورشة، يمكنك السماح "بالسماح بطلبات التنفيذ غير المُعتمَدة". بالنسبة إلى الإصدار العلني، من المحتمل أن تريد حظر الوصول.
- علامة التبويب الحاويات (وسِّع "الحاويات"، "الشبكة"):
- علامة التبويب "الإعدادات":
- المرجع
- الذاكرة : 2 غيغابايت
- المرجع
- علامة التبويب "المتغيّرات والأسرار":
- متغيّرات البيئة:
- أضِف الاسم:
GOOGLE_CLOUD_PROJECT
والقيمة: <YOUR_PROJECT_ID>. - أضِف الاسم:
BOOK_PROVIDER_URL
والقيمة: <YOUR_BOOK_PROVIDER_FUNCTION_URL>.
- أضِف الاسم:
- المفاتيح السرية المعروضة كمتغيّرات بيئية:
- أضِف الاسم:
DB_USER
، والمفتاح السري: اختَرdb-user
والإصدار:latest
. - أضِف الاسم:
DB_PASS
، والمفتاح السري: اختَرdb-pass
والإصدار:latest
. - أضِف الاسم:
DB_NAME
، والمفتاح السري: اختَرdb-name
والإصدار:latest
.
- أضِف الاسم:
- متغيّرات البيئة:
- علامة التبويب "الإعدادات":
نفِّذ الأمر التالي في الوحدة الطرفية إذا كنت بحاجة إلى استرداد YOUR_BOOK_PROVIDER_FUNCTION_URL:
gcloud run services describe book-provider --region=us-central1 --project=$PROJECT_ID --format="value(status.url)"
اترك الخيار "غير ذلك" التلقائي.
👉انقر على إنشاء.
سينشر Cloud Run خدمتك.
بعد نشر الخدمة، انقر عليها للانتقال إلى صفحة التفاصيل، ويمكنك العثور على عنوان URL المنشور في أعلى الصفحة.
في واجهة التطبيق، اختَر 7
للسنة، واختَر Mathematics
للموضوع، وأدخِل Algebra
في حقل "طلب الإضافة". سيزوّد هذا الإجراء موظّف الدعم بالسياق اللازم لإنشاء خطة درس مخصّصة.
تهانينا! لقد أنشأت بنجاح خطة تعليمية باستخدام وكيل الذكاء الاصطناعي الفعّال. يُظهر ذلك إمكانات موظّفي الدعم في تقليل عبء العمل بشكل كبير وتبسيط المهام، ما يؤدي في النهاية إلى تحسين الكفاءة وتسهيل حياة المعلّمين.
9. أنظمة الوكلاء المتعدّدين
بعد أن نفّذنا بنجاح أداة إنشاء الخطة التعليمية، لنركّز الآن على إنشاء بوابة الطلاب. ستتيح هذه البوابة للطلاب الوصول إلى الاختبارات والملخّصات الصوتية والمهام المتعلّقة بأعمالهم الدراسية. ونظرًا لنطاق هذه الوظيفة، سنستفيد من إمكانات أنظمة موظّفي الدعم المتعدّدين لإنشاء حلّ وحدات قابلة للتطوير.
كما ناقشنا سابقًا، بدلاً من الاعتماد على موظّف دعم واحد للتعامل مع كل المشاكل، يتيح لنا نظام موظّفي الدعم المتعدّدين تقسيم حجم العمل إلى مهام أصغر حجمًا ومتخصّصة، يتعامل مع كل منها موظّف دعم مخصّص. يوفّر هذا النهج العديد من المزايا الرئيسية:
الوحدات وسهولة الصيانة: بدلاً من إنشاء وكيل واحد يؤدي كل المهام، يمكنك إنشاء وكلاء أصغر حجمًا ومتخصصين لديهم مسؤوليات محدّدة جيدًا. تجعل هذه الوحدات النظام أسهل في الفهم والصيانة وتصحيح الأخطاء. عند حدوث مشكلة، يمكنك حصرها في موظّف دعم معيّن، بدلاً من البحث في قاعدة رموز برمجية ضخمة.
قابلية التوسيع: يمكن أن يشكّل توسيع وكيل واحد معقّد عائقًا. باستخدام نظام متعدّد موظّفي الدعم، يمكنك توسيع نطاق موظّفي الدعم الفرديين استنادًا إلى احتياجاتهم المحدّدة. على سبيل المثال، إذا كان موظّف دعم واحد يعالج عددًا كبيرًا من الطلبات، يمكنك بسهولة إنشاء المزيد من نُسخ هذا الموظّف بدون التأثير في بقية النظام.
تخصص الفريق: فكِّر في الأمر على النحو التالي: لن تطلب من مهندس واحد إنشاء تطبيق كامل من الصفر. بدلاً من ذلك، يمكنك تشكيل فريق من الخبراء، يمتلك كلّ منهم خبرة في مجال معيّن. وبالمثل، يتيح لك نظام متعدّد الوكلاء الاستفادة من نقاط قوة النماذج اللغوية الكبيرة والأدوات المختلفة، وتخصيصها للوكلاء الأنسب للمهام المحدّدة.
التطوير المتزامن: يمكن لفِرق مختلفة العمل على عملاء مختلفين في الوقت نفسه، ما يسرع عملية التطوير. وبما أنّ موظّفي الدعم مستقلون، من غير المرجّح أن تؤثّر التغييرات التي تطرأ على موظّف دعم واحد في موظّفي الدعم الآخرين.
بنية مستندة إلى الأحداث
لتفعيل التنسيق والتواصل الفعّالَين بين هذه العناصر، سنستخدم بنية مستندة إلى الأحداث. ويعني ذلك أنّ موظّفي الدعم سيتفاعلون مع "الأحداث" التي تحدث داخل النظام.
يشترك موظّفو الدعم في أنواع أحداث معيّنة (مثل "تم إنشاء خطة التدريس"، "تم إنشاء مهمة"). عند وقوع حدث، يتم إشعار موظّفي الدعم المعنيّين ويمكنهم اتّخاذ الإجراءات اللازمة وفقًا لذلك. ويعزّز هذا الفصل المرونة وإمكانية التوسّع وسرعة الاستجابة في الوقت الفعلي.
الآن، لبدء الإجراءات، نحتاج إلى طريقة لبث هذه الأحداث. لإجراء ذلك، سننشئ موضوعًا على Pub/Sub. لنبدأ بإنشاء موضوع بعنوان خطة.
👉انتقِل إلى نشر/اشتراك Google Cloud Console وانقر على الزر "إنشاء موضوع".
👉اضبط "الموضوع" باستخدام رقم التعريف أو الاسم plan
وأزِل العلامة من المربّع Add a default subscription
، واترك الإعدادات الأخرى على الإعدادات التلقائية وانقر على إنشاء.
ستتم إعادة تحميل صفحة Pub/Sub، ومن المفترض أن يظهر لك الآن الموضوع الذي أنشأته حديثًا مُدرَجًا في الجدول.
الآن، لندمج وظيفة نشر أحداث Pub/Sub في وكيل التخطيط. سنضيف أداة جديدة تُرسِل حدث "خطة" إلى موضوع Pub/Sub الذي أنشأناه للتو. سيُرسِل هذا الحدث إشارة إلى موظّفي الدعم الآخرين في النظام (مثل موظّفي الدعم في بوابة الطلاب) بأنّه تتوفّر خطة تدريس جديدة.
👉ارجع إلى "محرر الرموز البرمجية في السحابة الإلكترونية" وافتح ملف app.py
في مجلد planner
. سنضيف دالة تنشر الحدث. استبدال:
##ADD SEND PLAN EVENT FUNCTION HERE
مع
def send_plan_event(teaching_plan:str):
"""
Send the teaching event to the topic called plan
Args:
teaching_plan: teaching plan
"""
publisher = pubsub_v1.PublisherClient()
print(f"-------------> Sending event to topic plan: {teaching_plan}")
topic_path = publisher.topic_path(PROJECT_ID, "plan")
message_data = {"teaching_plan": teaching_plan}
data = json.dumps(message_data).encode("utf-8")
future = publisher.publish(topic_path, data)
return f"Published message ID: {future.result()}"
- send_plan_event: تأخذ هذه الدالة خطة التدريس التي تم إنشاؤها كمدخل، وتُنشئ عميلًا ناشرًا في Pub/Sub، وتُنشئ مسار الموضوع، وتحوّل خطة التدريس إلى سلسلة JSON، وتنشر الرسالة إلى الموضوع.
- قائمة الأدوات: تتم إضافة الدالة
send_plan_event
إلى قائمة الأدوات، ما يجعلها متاحة للموظف لاستخدامها.
ضمن المجلد نفسه، في ملف app.py
، يمكنك تعديل الطلب لتوجيه موظّف الدعم لإرسال حدث الخطة التعليمية إلى موضوع Pub/Sub بعد إنشاء الخطة التعليمية. استبدال
### ADD send_plan_event CALL
مع ما يلي:
send_plan_event(teaching_plan)
من خلال إضافة أداة send_plan_event وتعديل الطلب، فعّلنا وكيل التخطيط لنشر الأحداث في Pub/Sub، ما يتيح للمكونات الأخرى في نظامنا التفاعل مع إنشاء خطط تعليمية جديدة. سيكون لدينا الآن نظام متعدّد الوكلاء يعمل بشكلٍ سليم في الأقسام التالية.
10. تمكين الطلاب من خلال الاختبارات عند الطلب
تخيل بيئة تعليمية يمكن للطلاب فيها الوصول إلى عدد لا نهائي من الاختبارات المصمّمة خصيصًا لخطط التعلّم المحدّدة. تقدّم هذه الاختبارات ملاحظات فورية، بما في ذلك الإجابات والشروحات، ما يعزّز فهم المادة بشكل أفضل. هذا هو الإمكان الذي نهدف إلى تحقيقه من خلال بوابة الاختبارات المستندة إلى الذكاء الاصطناعي.
لتنفيذ هذه الرؤية، سننشئ مكوّنًا لإنشاء الاختبارات يمكنه إنشاء أسئلة خيارات متعدّدة استنادًا إلى محتوى الخطة التعليمية.
👉في لوحة Explorer (مستكشف) في "أداة تعديل الرموز البرمجية في السحابة الإلكترونية"، انتقِل إلى المجلد portal
. افتح ملف quiz.py
وانسخ الرمز التالي والصقه في نهاية الملف.
def generate_quiz_question(file_name: str, difficulty: str, region:str ):
"""Generates a single multiple-choice quiz question using the LLM.
```json
{
"question": "The question itself",
"options": ["Option A", "Option B", "Option C", "Option D"],
"answer": "The correct answer letter (A, B, C, or D)"
}
```
"""
print(f"region: {region}")
# Connect to resourse needed from Google Cloud
llm = VertexAI(model_name="gemini-1.5-pro", location=region)
plan=None
#load the file using file_name and read content into string call plan
with open(file_name, 'r') as f:
plan = f.read()
parser = JsonOutputParser(pydantic_object=QuizQuestion)
instruction = f"You'll provide one question with difficulty level of {difficulty}, 4 options as multiple choices and provide the anwsers, the quiz needs to be related to the teaching plan {plan}"
prompt = PromptTemplate(
template="Generates a single multiple-choice quiz question\n {format_instructions}\n {instruction}\n",
input_variables=["instruction"],
partial_variables={"format_instructions": parser.get_format_instructions()},
)
chain = prompt | llm | parser
response = chain.invoke({"instruction": instruction})
print(f"{response}")
return response
في موظّف الدعم، يتم إنشاء أداة تحليل لإخراج JSON مصمّمة خصيصًا لفهم إخراج نموذج اللغة الكبيرة وتنظيمه. ويستخدم هذا الإجراء نموذج QuizQuestion
الذي حدّدناه سابقًا لضمان توافق الإخراج الذي تم تحليله مع التنسيق الصحيح (السؤال والخيارات والإجابة).
👉نفِّذ الأوامر التالية في الوحدة الطرفية لإعداد بيئة افتراضية وتثبيت التبعيات وبدء تشغيل الوكيل:
cd ~/aidemy-bootstrap/portal/
python -m venv env
source env/bin/activate
pip install -r requirements.txt
python app.py
استخدِم ميزة العرض التجريبي على الويب في Cloud Shell للوصول إلى التطبيق الذي يتم تشغيله. انقر على رابط "الاختبارات"، إما في شريط التنقّل العلوي أو من البطاقة في صفحة الفهرس. من المفترض أن تظهر للطالب ثلاثة اختبارات تم إنشاؤها عشوائيًا. تستند هذه الاختبارات إلى الخطة التعليمية وتُظهر مدى فعالية نظام إنشاء الاختبارات المستنِد إلى الذكاء الاصطناعي.
لإيقاف العملية التي تعمل على الجهاز، اضغط على Ctrl+C
في المحطة الطرفية.
التفكير في Gemini 2 لتوفير التفسيرات
حسنًا، لدينا اختبارات، وهي بداية رائعة. ولكن ماذا لو أخطأ الطلاب في الإجابة عن سؤال؟ هذا هو المكان الذي يحدث فيه التعلّم الحقيقي، أليس كذلك؟ إذا تمكّنا من توضيح سبب خطأ إجابته وكيفية الوصول إلى الإجابة الصحيحة، من المرجّح أن يتذكرها. ويساعد ذلك أيضًا في توضيح أي التباس وتعزيز ثقتهم بأنفسهم.
لهذا السبب، سنستخدم الأدوات الأكثر فعالية، وهي نموذج "التفكير" في Gemini 2. يمكنك اعتبار ذلك بمثابة منح الذكاء الاصطناعي بعض الوقت الإضافي للتفكير في الأمور قبل شرحها. ويتيح ذلك تقديم ملاحظات أكثر تفصيلاً وأفضل.
نريد معرفة ما إذا كان بإمكانه مساعدة الطلاب من خلال تقديم المساعدة والإجابة عن الأسئلة والتوضيح بالتفصيل. لاختبار هذه الميزة، سنبدأ بمادة حسابية صعبة، وهي الجبر الخطي.
👉أولاً، انتقِل إلى "محرِّر الرموز البرمجية في السحابة الإلكترونية"، وفي answer.py
داخل المجلد portal
، استبدِل
def answer_thinking(question, options, user_response, answer, region):
return ""
باستخدام مقتطف الرمز البرمجي التالي:
def answer_thinking(question, options, user_response, answer, region):
try:
llm = VertexAI(model_name="gemini-2.0-flash-001",location=region)
input_msg = HumanMessage(content=[f"Here the question{question}, here are the available options {options}, this student's answer {user_response}, whereas the correct answer is {answer}"])
prompt_template = ChatPromptTemplate.from_messages(
[
SystemMessage(
content=(
"You are a helpful teacher trying to teach the student on question, you were given the question and a set of multiple choices "
"what's the correct answer. use friendly tone"
)
),
input_msg,
]
)
prompt = prompt_template.format()
response = llm.invoke(prompt)
print(f"response: {response}")
return response
except Exception as e:
print(f"Error sending message to chatbot: {e}") # Log this error too!
return f"Unable to process your request at this time. Due to the following reason: {str(e)}"
if __name__ == "__main__":
question = "Evaluate the limit: lim (x→0) [(sin(5x) - 5x) / x^3]"
options = ["A) -125/6", "B) -5/3 ", "C) -25/3", "D) -5/6"]
user_response = "B"
answer = "A"
region = "us-central1"
result = answer_thinking(question, options, user_response, answer, region)
هذا تطبيق بسيط جدًا لسلسلة لغات يتم فيه إعداد نموذج Gemini 2 Flash، حيث نطلب منه أن يعمل كمعلّم مفيد ويقدّم تفسيرات.
👉نفِّذ الأمر التالي في الوحدة الطرفية:
cd ~/aidemy-bootstrap/portal/
python answer.py
من المفترض أن يظهر لك ناتج مشابه للمثال المقدَّم في التعليمات الأصلية. قد لا يقدّم النموذج الحالي تفسيرًا شاملاً.
Okay, I see the question and the choices. The question is to evaluate the limit:
lim (x→0) [(sin(5x) - 5x) / x^3]
You chose option B, which is -5/3, but the correct answer is A, which is -125/6.
It looks like you might have missed a step or made a small error in your calculations. This type of limit often involves using L'Hôpital's Rule or Taylor series expansion. Since we have the form 0/0, L'Hôpital's Rule is a good way to go! You need to apply it multiple times. Alternatively, you can use the Taylor series expansion of sin(x) which is:
sin(x) = x - x^3/3! + x^5/5! - ...
So, sin(5x) = 5x - (5x)^3/3! + (5x)^5/5! - ...
Then, (sin(5x) - 5x) = - (5x)^3/3! + (5x)^5/5! - ...
Finally, (sin(5x) - 5x) / x^3 = - 5^3/3! + (5^5 * x^2)/5! - ...
Taking the limit as x approaches 0, we get -125/6.
Keep practicing, you'll get there!
في ملف answer.py، استبدِل model_name من gemini-2.0-flash-001
إلى gemini-2.0-flash-thinking-exp-01-21
في الدالة answer_thinking.
يؤدي ذلك إلى تغيير النموذج اللغوي الكبير الذي يستند إلى الاستدلالات بشكل أكبر، ما يساعده في إنشاء تفسيرات أفضل. وتشغيله مرة أخرى
👉يُرجى اتّباع الخطوات التالية لاختبار نموذج التفكير الجديد:
cd ~/aidemy-bootstrap/portal/
source env/bin/activate
python answer.py
في ما يلي مثال على ردّ من نموذج التفكير أكثر دقة وتفصيلاً، ويقدّم شرحًا مفصّلاً لكيفية حلّ مسألة حساب التفاضل والتكامل. ويُبرز ذلك مدى فعالية نماذج "التفكير" في إنشاء تفسيرات عالية الجودة. من المفترض أن يظهر لك ناتج مشابه لما يلي:
Hey there! Let's take a look at this limit problem together. You were asked to evaluate:
lim (x→0) [(sin(5x) - 5x) / x^3]
and you picked option B, -5/3, but the correct answer is actually A, -125/6. Let's figure out why!
It's a tricky one because if we directly substitute x=0, we get (sin(0) - 0) / 0^3 = (0 - 0) / 0 = 0/0, which is an indeterminate form. This tells us we need to use a more advanced technique like L'Hopital's Rule or Taylor series expansion.
Let's use the Taylor series expansion for sin(y) around y=0. Do you remember it? It looks like this:
sin(y) = y - y^3/3! + y^5/5! - ...
where 3! (3 factorial) is 3 × 2 × 1 = 6, 5! is 5 × 4 × 3 × 2 × 1 = 120, and so on.
In our problem, we have sin(5x), so we can substitute y = 5x into the Taylor series:
sin(5x) = (5x) - (5x)^3/3! + (5x)^5/5! - ...
sin(5x) = 5x - (125x^3)/6 + (3125x^5)/120 - ...
Now let's plug this back into our limit expression:
[(sin(5x) - 5x) / x^3] = [ (5x - (125x^3)/6 + (3125x^5)/120 - ...) - 5x ] / x^3
Notice that the '5x' and '-5x' cancel out! So we are left with:
= [ - (125x^3)/6 + (3125x^5)/120 - ... ] / x^3
Now, we can divide every term in the numerator by x^3:
= -125/6 + (3125x^2)/120 - ...
Finally, let's take the limit as x approaches 0. As x gets closer and closer to zero, terms with x^2 and higher powers will become very, very small and approach zero. So, we are left with:
lim (x→0) [ -125/6 + (3125x^2)/120 - ... ] = -125/6
Therefore, the correct answer is indeed **A) -125/6**.
It seems like your answer B, -5/3, might have come from perhaps missing a factor somewhere during calculation or maybe using an incorrect simplification. Double-check your steps when you were trying to solve it!
Don't worry, these limit problems can be a bit tricky sometimes! Keep practicing and you'll get the hang of it. Let me know if you want to go through another similar example or if you have any more questions! 😊
Now that we have confirmed it works, let's use the portal.
👉أزِل الرمز الاختباري التالي من answer.py
:
if __name__ == "__main__":
question = "Evaluate the limit: lim (x→0) [(sin(5x) - 5x) / x^3]"
options = ["A) -125/6", "B) -5/3 ", "C) -25/3", "D) -5/6"]
user_response = "B"
answer = "A"
region = "us-central1"
result = answer_thinking(question, options, user_response, answer, region)
👉نفِّذ الأوامر التالية في الوحدة الطرفية لإعداد بيئة افتراضية وتثبيت التبعيات وبدء تشغيل الوكيل:
cd ~/aidemy-bootstrap/portal/
source env/bin/activate
python app.py
👉استخدِم ميزة معاينة الويب في Cloud Shell للوصول إلى التطبيق الذي يتم تشغيله. انقر على رابط "الاختبارات"، وأجب عن جميع الاختبارات وتأكَّد من تقديم إجابة خاطئة واحدة على الأقل، ثم انقر على "إرسال".
بدلاً من التحديق في الشاشة أثناء انتظار الردّ، يمكنك الانتقال إلى وحدة تحكّم Cloud Editor. يمكنك مراقبة مستوى التقدّم وأيّ رسائل ناتجة أو أخطاء تنشئها الدالة في وحدة تحكّم المحاكي. 😁
لإيقاف العملية التي تعمل على الجهاز، اضغط على Ctrl+C
في المحطة الطرفية.
11. تنسيق العناصر باستخدام Eventarc
حتى الآن، كانت بوابة الطالب تنشئ اختبارات استنادًا إلى مجموعة تلقائية من خطط التدريس. وهذا مفيد، ولكنّه يعني أنّ موظّف الدعم المختص بالخطط وموظّف الدعم المختص بالاختبارات في البوابة لا يتواصلان مع بعضهما البعض. هل تذكّر كيف أضفنا هذه الميزة التي ينشر فيها موظّف الدعم خطط التدريس التي تم إنشاؤها حديثًا في موضوع Pub/Sub؟ حان الآن وقت ربط ذلك بموظف الدعم في البوابة.
نريد أن تعدّل البوابة محتوى الاختبار تلقائيًا عند إنشاء خطة تعليمية جديدة. ولإجراء ذلك، سننشئ نقطة نهاية في البوابة يمكنها تلقّي هذه الخطط الجديدة.
👉في لوحة Explorer (مستكشف) في Cloud Code Editor (أداة تعديل الرموز البرمجية في السحابة الإلكترونية)، انتقِل إلى المجلد portal
. افتح ملف app.py
لتعديله. أضِف الرمز التالي بين ## Add your code here:
## Add your code here
@app.route('/new_teaching_plan', methods=['POST'])
def new_teaching_plan():
try:
# Get data from Pub/Sub message delivered via Eventarc
envelope = request.get_json()
if not envelope:
return jsonify({'error': 'No Pub/Sub message received'}), 400
if not isinstance(envelope, dict) or 'message' not in envelope:
return jsonify({'error': 'Invalid Pub/Sub message format'}), 400
pubsub_message = envelope['message']
print(f"data: {pubsub_message['data']}")
data = pubsub_message['data']
data_str = base64.b64decode(data).decode('utf-8')
data = json.loads(data_str)
teaching_plan = data['teaching_plan']
print(f"File content: {teaching_plan}")
with open("teaching_plan.txt", "w") as f:
f.write(teaching_plan)
print(f"Teaching plan saved to local file: teaching_plan.txt")
return jsonify({'message': 'File processed successfully'})
except Exception as e:
print(f"Error processing file: {e}")
return jsonify({'error': 'Error processing file'}), 500
## Add your code here
إعادة الإنشاء والنشر على Cloud Run
حسنًا، عليك تعديل إعادة نشر كلّ من عملاء "أداة التخطيط" وعملاء "بوابة Google" إلى Cloud Run. يضمن ذلك توفّر أحدث الرمز البرمجي وضبطه للتواصل من خلال الأحداث.
👉بعد ذلك، سنعيد إنشاء صورة وكيل planner وسنرسلها، وذلك من خلال تشغيل وحدة التحكّم الطرفية:
cd ~/aidemy-bootstrap/planner/
export PROJECT_ID=$(gcloud config get project)
docker build -t gcr.io/${PROJECT_ID}/aidemy-planner .
export PROJECT_ID=$(gcloud config get project)
docker tag gcr.io/${PROJECT_ID}/aidemy-planner us-central1-docker.pkg.dev/${PROJECT_ID}/agent-repository/aidemy-planner
docker push us-central1-docker.pkg.dev/${PROJECT_ID}/agent-repository/aidemy-planner
👉سنفعل الشيء نفسه، وسننشئ صورة وكيل بوابة ونرسلها:
cd ~/aidemy-bootstrap/portal/
export PROJECT_ID=$(gcloud config get project)
docker build -t gcr.io/${PROJECT_ID}/aidemy-portal .
export PROJECT_ID=$(gcloud config get project)
docker tag gcr.io/${PROJECT_ID}/aidemy-portal us-central1-docker.pkg.dev/${PROJECT_ID}/agent-repository/aidemy-portal
docker push us-central1-docker.pkg.dev/${PROJECT_ID}/agent-repository/aidemy-portal
في مستودع العناصر، من المفترض أن تظهر لك صور الحاويات aidemy-planner
وaidemy-portal
مُدرَجة.
👉في الوحدة الطرفية، شغِّل الأمر التالي لتعديل صورة Cloud Run لوكيل التخطيط:
export PROJECT_ID=$(gcloud config get project)
gcloud run services update aidemy-planner \
--region=us-central1 \
--image=us-central1-docker.pkg.dev/${PROJECT_ID}/agent-repository/aidemy-planner:latest
من المفترض أن يظهر لك ناتج مشابه لما يلي:
OK Deploying... Done.
OK Creating Revision...
OK Routing traffic...
Done.
Service [aidemy-planner] revision [aidemy-planner-xxxxx] has been deployed and is serving 100 percent of traffic.
Service URL: https://aidemy-planner-xxx.us-central1.run.app
دوِّن عنوان URL للخدمة، وهو الرابط الذي يؤدي إلى وكيل التخطيط الذي تم نشره.
👉شغِّل هذا الإجراء لإنشاء مثيل Cloud Run لوكيل البوابة.
export PROJECT_ID=$(gcloud config get project)
gcloud run deploy aidemy-portal \
--image=us-central1-docker.pkg.dev/${PROJECT_ID}/agent-repository/aidemy-portal:latest \
--region=us-central1 \
--platform=managed \
--allow-unauthenticated \
--memory=2Gi \
--cpu=2 \
--set-env-vars=GOOGLE_CLOUD_PROJECT=${PROJECT_ID}
من المفترض أن يظهر لك ناتج مشابه لما يلي:
Deploying container to Cloud Run service [aidemy-portal] in project [xxxx] region [us-central1]
OK Deploying new service... Done.
OK Creating Revision...
OK Routing traffic...
OK Setting IAM Policy...
Done.
Service [aidemy-portal] revision [aidemy-portal-xxxx] has been deployed and is serving 100 percent of traffic.
Service URL: https://aidemy-portal-xxxx.us-central1.run.app
دوِّن عنوان URL للخدمة، وهو الرابط المؤدي إلى بوابة الطلاب المنشورة.
إنشاء مشغِّل Eventarc
ولكن السؤال الكبير هو: كيف يتم إرسال إشعار إلى نقطة النهاية هذه عندما تكون هناك خطة جديدة في انتظارها في موضوع Pub/Sub؟ وهنا يأتي دور Eventarc لإنقاذ الموقف.
تعمل Eventarc كجسر، حيث تستمع إلى أحداث معيّنة (مثل رسالة جديدة تصل إلى موضوع Pub/Sub) وتبدأ الإجراءات تلقائيًا استجابةً لذلك. في حالتنا، سيرصد هذا الإجراء نشر خطة تعليمية جديدة، ثم يرسل إشارة إلى نقطة نهاية البوابة لإعلامها بأنّه حان وقت إجراء التحديث.
من خلال Eventarc التي تتعامل مع الاتصالات المستندة إلى الأحداث، يمكننا ربط موظّف الدعم المسؤول عن التخطيط وموظّف الدعم المسؤول عن البوابة بسلاسة، ما يؤدي إلى إنشاء نظام تعلُّم ديناميكي وسريع الاستجابة. يشبه ذلك توفُّر بريد إلكتروني ذكي يرسل تلقائيًا أحدث خطط الدروس إلى المكان الصحيح.
👉في وحدة التحكّم، انتقِل إلى Eventarc.
👉انقر على الزر "+ إنشاء عامل تشغيل".
ضبط المشغِّل (الإجراءات الأساسية):
- اسم العامل المشغِّل:
plan-topic-trigger
- أنواع العوامل المُشغِّلة: مصادر Google
- موفِّر الأحداث: Cloud Pub/Sub
- نوع الحدث:
google.cloud.pubsub.topic.v1.messagePublished
- المنطقة:
us-central1
- موضوع Cloud Pub/Sub : اختَر
plan
. - منح حساب الخدمة الدور
roles/iam.serviceAccountTokenCreator
- وجهة الحدث: Cloud Run
- خدمة Cloud Run: aidemy-portal
- مسار عنوان URL للخدمة:
/new_teaching_plan
- تجاهل الرسالة (تم رفض الإذن في "locations/me-central2" (أو قد لا يكون متوفّرًا).)
انقر على "إنشاء".
ستتم إعادة تحميل صفحة "عوامل تشغيل Eventarc"، ومن المفترض أن يظهر لك الآن عامل التشغيل الذي أنشأته حديثًا مُدرَجًا في الجدول.
👉الآن، يمكنك الوصول إلى الخطة وطلب خطة تدريس جديدة. هذه المرة، جرِّب السنة 5
، والموضوع science
مع طلب Add-no atoms
.
يمكنك تنفيذ هذا الأمر في المحطة الطرفية إذا نسيت الموقع الجغرافي لوكيل التخطيط.
gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner
بعد ذلك، يُرجى الانتظار لمدة دقيقة أو دقيقتين. يُرجى العِلم أنّ هذا التأخير ناتج عن القيود المفروضة على الفوترة في هذا الدرس التطبيقي، ولكن في الظروف العادية، من المفترض ألا يكون هناك أي تأخير.
أخيرًا، يُرجى الانتقال إلى بوابة الطالب. من المفترض أن تلاحظ أنّه تم تعديل الاختبارات لتتلاءم الآن مع خطة التدريس الجديدة التي أنشأتها للتو. يشير ذلك إلى دمج Eventarc بنجاح في نظام Aidemy.
يمكنك تنفيذ هذا الأمر في المحطة الطرفية إذا نسيت موقع موظّف الدعم في البوابة.
gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep portal
تهانينا! لقد نجحت في إنشاء نظام متعدّد الوكلاء على Google Cloud، وذلك من خلال الاستفادة من البنية المستندة إلى الأحداث لتحسين قابلية التوسّع والمرونة. لقد وضعت أساسًا متينًا، ولكن هناك المزيد من الميزات التي يمكنك استكشافها. للتعرّف بشكل أعمق على المزايا الحقيقية لهذه البنية، يمكنك الاطّلاع على إمكانات واجهة برمجة التطبيقات Live API متعددة الوسائط في Gemini 2، ومعرفة كيفية تنفيذ عملية التنسيق في مسار واحد باستخدام LangGraph، ويمكنك الانتقال إلى الفصلَين التاليَين.
12. اختياري: ملخّصات صوتية باستخدام Gemini
يمكن أن يفهم Gemini المعلومات من مصادر مختلفة ويعالجها، مثل النصوص والصور وحتى المقاطع الصوتية، ما يفتح مجموعة جديدة تمامًا من الاحتمالات للتعلم وإنشاء المحتوى. توفّر قدرة Gemini على "الرؤية" و"الاستماع" و"القراءة" تجارب مستخدم إبداعية وجذابة.
بالإضافة إلى إنشاء المحتوى المرئي أو النصي، هناك خطوة مهمة أخرى في التعلّم وهي التلخيص والإعادة بشكل فعّال. فكِّر في الأمر: كم مرة تتذكر كلمات أغنية جذابة بسهولة أكبر من المعلومات التي تقرأها في كتاب مدرسي؟ يمكن أن يكون الصوت عنصرًا لا يُنسى. لهذا السبب، سنستفيد من إمكانات Gemini المتعددة الوسائط لإنشاء ملخّصات صوتية لخطط التدريس. سيوفّر ذلك للطلاب طريقة ملائمة ومشوّقة لمراجعة المواد، ما قد يؤدي إلى تحسين الحفظ والفهم من خلال ميزة التعلّم السمعي.
نحتاج إلى مكان لتخزين الملفات الصوتية التي تم إنشاؤها. يوفّر "تخزين السحابة الإلكترونية" حلًا قابلاً للتطوير والموثوقية.
👉انتقِل إلى مساحة التخزين في وحدة التحكّم. انقر على "الحزم" في القائمة اليمنى. انقر على الزر "+ إنشاء" في أعلى الصفحة.
👉ضبط الحزمة:
- اسم الحزمة: aidemy-recap-<UNIQUE_NAME> ملاحظة مهمة: تأكَّد من تحديد اسم حزمة فريد يبدأ بـ "aidemy-recap-". هذا الاسم الفريد مهم لتجنُّب تعارض الأسماء عند إنشاء حزمة في Cloud Storage.
- المنطقة:
us-central1
- فئة التخزين: "عادي" ويكون الخيار "عادي" مناسبًا للبيانات التي يتم الوصول إليها بشكل متكرر.
- التحكّم في الوصول: اترك الخيار التلقائي "التحكّم الموحّد في الوصول" محدّدًا. ويوفّر ذلك إمكانية التحكّم في الوصول بشكلٍ متّسق على مستوى الحزمة.
- الخيارات المتقدّمة: تكون الإعدادات التلقائية عادةً كافية في هذه الورشة. انقر على الزر إنشاء لإنشاء حزمة.
قد تظهر لك نافذة منبثقة بشأن منع الوصول العلني. اترك العلامة في المربّع وانقر على Confirm
.
ستظهر لك الآن الحزمة التي أنشأتها حديثًا في قائمة "الحِزم". تذكَّر اسم الحزمة، لأنّك ستحتاج إليه لاحقًا.
👉في وحدة تحكّم Cloud Code Editor، شغِّل الأوامر التالية لمنح حساب الخدمة إذن الوصول إلى الحزمة:
export COURSE_BUCKET_NAME=$(gcloud storage buckets list --format="value(name)" | grep aidemy-recap)
export SERVICE_ACCOUNT_NAME=$(gcloud compute project-info describe --format="value(defaultServiceAccount)")
gcloud storage buckets add-iam-policy-binding gs://$COURSE_BUCKET_NAME \
--member "serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role "roles/storage.objectViewer"
gcloud storage buckets add-iam-policy-binding gs://$COURSE_BUCKET_NAME \
--member "serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role "roles/storage.objectCreator"
👉في "محرِّر الرموز البرمجية في السحابة الإلكترونية"، افتح audio.py
داخل المجلد course
. الصِق الرمز البرمجي التالي في نهاية الملف:
config = LiveConnectConfig(
response_modalities=["AUDIO"],
speech_config=SpeechConfig(
voice_config=VoiceConfig(
prebuilt_voice_config=PrebuiltVoiceConfig(
voice_name="Charon",
)
)
),
)
async def process_weeks(teaching_plan: str):
region = "us-west1" #To workaround onRamp qouta limits
client = genai.Client(vertexai=True, project=PROJECT_ID, location=region)
clientAudio = genai.Client(vertexai=True, project=PROJECT_ID, location="us-central1")
async with clientAudio.aio.live.connect(
model=MODEL_ID,
config=config,
) as session:
for week in range(1, 4):
response = client.models.generate_content(
model="gemini-1.0-pro",
contents=f"Given the following teaching plan: {teaching_plan}, Extrace content plan for week {week}. And return just the plan, nothingh else " # Clarified prompt
)
prompt = f"""
Assume you are the instructor.
Prepare a concise and engaging recap of the key concepts and topics covered.
This recap should be suitable for generating a short audio summary for students.
Focus on the most important learnings and takeaways, and frame it as a direct address to the students.
Avoid overly formal language and aim for a conversational tone, tell a few jokes.
Teaching plan: {response.text} """
print(f"prompt --->{prompt}")
await session.send(input=prompt, end_of_turn=True)
with open(f"temp_audio_week_{week}.raw", "wb") as temp_file:
async for message in session.receive():
if message.server_content.model_turn:
for part in message.server_content.model_turn.parts:
if part.inline_data:
temp_file.write(part.inline_data.data)
data, samplerate = sf.read(f"temp_audio_week_{week}.raw", channels=1, samplerate=24000, subtype='PCM_16', format='RAW')
sf.write(f"course-week-{week}.wav", data, samplerate)
storage_client = storage.Client()
bucket = storage_client.bucket(BUCKET_NAME)
blob = bucket.blob(f"course-week-{week}.wav") # Or give it a more descriptive name
blob.upload_from_filename(f"course-week-{week}.wav")
print(f"Audio saved to GCS: gs://{BUCKET_NAME}/course-week-{week}.wav")
await session.close()
def breakup_sessions(teaching_plan: str):
asyncio.run(process_weeks(teaching_plan))
- الاتصال بالبث: أولاً، يتم إنشاء اتصال دائم بنقطة نهاية Live API. على عكس طلب البيانات العادي من واجهة برمجة التطبيقات الذي تُرسل فيه طلبًا وتتلقّى ردًا، يظل هذا الاتصال مفتوحًا لتبادل البيانات بشكل مستمر.
- الإعدادات المتعدّدة الوسائط: استخدِم الإعدادات لتحديد نوع الإخراج الذي تريده (في هذه الحالة، الصوت)، ويمكنك أيضًا تحديد المَعلمات التي تريد استخدامها (مثل اختيار الصوت وتشفير الصوت).
- المعالجة غير المتزامنة: تعمل واجهة برمجة التطبيقات هذه بشكل غير متزامن، ما يعني أنّها لا تحظر سلسلة التعليمات الرئيسية أثناء انتظار اكتمال إنشاء الملف الصوتي. من خلال معالجة البيانات في الوقت الفعلي وإرسال النتائج في أجزاء، يوفّر هذا الإجراء تجربة فورية تقريبًا.
والسؤال الرئيسي الآن هو: متى يجب تنفيذ عملية إنشاء الصوت هذه؟ نريد أن تتوفّر الملخّصات الصوتية فور إنشاء خطة تعليمية جديدة. بما أنّنا سبق أن نفّذنا بنية مستندة إلى الأحداث من خلال نشر الخطة التعليمية في موضوع Pub/Sub، يمكننا ببساطة الاشتراك في هذا الموضوع.
ومع ذلك، لا ننشئ خطط تدريس جديدة كثيرًا. ولن يكون من الفعال تعيين موظّف دعم يعمل باستمرار وينتظر خططًا جديدة. لهذا السبب، من المنطقي تمامًا نشر منطق إنشاء الصوت هذا كإحدى وظائف Cloud Run.
من خلال نشرها كوظيفة، تظل غير نشطة إلى أن يتم نشر رسالة جديدة في موضوع Pub/Sub. وعند حدوث ذلك، يتم تفعيل الدالة تلقائيًا، ما يؤدي إلى إنشاء الملخّصات الصوتية وتخزينها في حزمة التخزين.
👉ضمن مجلد course
في ملف main.py
، يحدِّد هذا الملف وظيفة Cloud Run التي سيتم تفعيلها عند توفُّر خطة تعليمية جديدة. يتلقّى هذا الإجراء الخطة ويُنشئ الملخّص الصوتي. أضِف مقتطف الرمز البرمجي التالي إلى نهاية الملف.
@functions_framework.cloud_event
def process_teaching_plan(cloud_event):
print(f"CloudEvent received: {cloud_event.data}")
time.sleep(60)
try:
if isinstance(cloud_event.data.get('message', {}).get('data'), str): # Check for base64 encoding
data = json.loads(base64.b64decode(cloud_event.data['message']['data']).decode('utf-8'))
teaching_plan = data.get('teaching_plan') # Get the teaching plan
elif 'teaching_plan' in cloud_event.data: # No base64
teaching_plan = cloud_event.data["teaching_plan"]
else:
raise KeyError("teaching_plan not found") # Handle error explicitly
#Load the teaching_plan as string and from cloud event, call audio breakup_sessions
breakup_sessions(teaching_plan)
return "Teaching plan processed successfully", 200
except (json.JSONDecodeError, AttributeError, KeyError) as e:
print(f"Error decoding CloudEvent data: {e} - Data: {cloud_event.data}")
return "Error processing event", 500
except Exception as e:
print(f"Error processing teaching plan: {e}")
return "Error processing teaching plan", 500
@functions_framework.cloud_event: يضع هذا العنصر الزخرفي علامة على الدالة على أنّها دالة Cloud Run سيتم تشغيلها بواسطة CloudEvents.
الاختبار على الجهاز
👉سننفّذ هذا الإجراء في بيئة افتراضية ونثبّت مكتبات Python اللازمة لدالة Cloud Run.
cd ~/aidemy-bootstrap/courses
export COURSE_BUCKET_NAME=$(gcloud storage buckets list --format="value(name)" | grep aidemy-recap)
python -m venv env
source env/bin/activate
pip install -r requirements.txt
👉يتيح لنا محاكي وظائف Cloud Run اختبار وظيفتنا محليًا قبل نشرها على Google Cloud. ابدأ محاكيًا محليًا من خلال تشغيل:
functions-framework --target process_teaching_plan --signature-type=cloudevent --source main.py
👉أثناء تشغيل المحاكي، يمكنك إرسال أحداث CloudEvents تجريبية إليه لمحاكاة خطة تعليمية جديدة يتم نشرها. في محطة طرفية جديدة:
👉تنفيذ:
curl -X POST \
http://localhost:8080/ \
-H "Content-Type: application/json" \
-H "ce-id: event-id-01" \
-H "ce-source: planner-agent" \
-H "ce-specversion: 1.0" \
-H "ce-type: google.cloud.pubsub.topic.v1.messagePublished" \
-d '{
"message": {
"data": "eyJ0ZWFjaGluZ19wbGFuIjogIldlZWsgMTogMkQgU2hhcGVzIGFuZCBBbmdsZXMgLSBEYXkgMTogUmV2aWV3IG9mIGJhc2ljIDJEIHNoYXBlcyAoc3F1YXJlcywgcmVjdGFuZ2xlcywgdHJpYW5nbGVzLCBjaXJjbGVzKS4gRGF5IDI6IEV4cGxvcmluZyBkaWZmZXJlbnQgdHlwZXMgb2YgdHJpYW5nbGVzIChlcXVpbGF0ZXJhbCwgaXNvc2NlbGVzLCBzY2FsZW5lLCByaWdodC1hbmdsZWQpLiBEYXkgMzogRXhwbG9yaW5nIHF1YWRyaWxhdGVyYWxzIChzcXVhcmUsIHJlY3RhbmdsZSwgcGFyYWxsZWxvZ3JhbSwgcmhvbWJ1cywgdHJhcGV6aXVtKS4gRGF5IDQ6IEludHJvZHVjdGlvbiB0byBhbmdsZXM6IHJpZ2h0IGFuZ2xlcywgYWN1dGUgYW5nbGVzLCBhbmQgb2J0dXNlIGFuZ2xlcy4gRGF5IDU6IE1lYXN1cmluZyBhbmdsZXMgdXNpbmcgYSBwcm90cmFjdG9yLiBXZWVrIDI6IDNEIFNoYXBlcyBhbmQgU3ltbWV0cnkgLSBEYXkgNjogSW50cm9kdWN0aW9uIHRvIDNEIHNoYXBlczogY3ViZXMsIGN1Ym9pZHMsIHNwaGVyZXMsIGN5bGluZGVycywgY29uZXMsIGFuZCBweXJhbWlkcy4gRGF5IDc6IERlc2NyaWJpbmcgM0Qgc2hhcGVzIHVzaW5nIGZhY2VzLCBlZGdlcywgYW5kIHZlcnRpY2VzLiBEYXkgODogUmVsYXRpbmcgMkQgc2hhcGVzIHRvIDNEIHNoYXBlcy4gRGF5IDk6IElkZW50aWZ5aW5nIGxpbmVzIG9mIHN5bW1ldHJ5IGluIDJEIHNoYXBlcy4gRGF5IDEwOiBDb21wbGV0aW5nIHN5bW1ldHJpY2FsIGZpZ3VyZXMuIFdlZWsgMzogUG9zaXRpb24sIERpcmVjdGlvbiwgYW5kIFByb2JsZW0gU29sdmluZyAtIERheSAxMTogRGVzY3JpYmluZyBwb3NpdGlvbiB1c2luZyBjb29yZGluYXRlcyBpbiB0aGUgZmlyc3QgcXVhZHJhbnQuIERheSAxMjogUGxvdHRpbmcgY29vcmRpbmF0ZXMgdG8gZHJhdyBzaGFwZXMuIERheSAxMzogVW5kZXJzdGFuZGluZyB0cmFuc2xhdGlvbiAoc2xpZGluZyBhIHNoYXBlKS4gRGF5IDE0OiBVbmRlcnN0YW5kaW5nIHJlZmxlY3Rpb24gKGZsaXBwaW5nIGEgc2hhcGUpLiBEYXkgMTU6IFByb2JsZW0tc29sdmluZyBhY3Rpdml0aWVzIGludm9sdmluZyBwZXJpbWV0ZXIsIGFyZWEsIGFuZCBtaXNzaW5nIGFuZ2xlcy4ifQ=="
}
}'
بدلاً من التحديق في الشاشة في انتظار الردّ، يمكنك التبديل إلى وحدة تحكّم Cloud Shell الأخرى. يمكنك مراقبة مستوى التقدّم وأيّ رسائل ناتجة أو أخطاء تنشئها الدالة في وحدة تحكّم المحاكي. 😁
في الوحدة الطرفية الثانية، من المفترض أن يظهر لك الرمز OK
.
👉عليك التحقّق من البيانات في الحزمة، والانتقال إلى Cloud Storage واختيار علامة التبويب "الحزمة" ثم aidemy-recap-xxx
.
👉في وحدة التحكّم التي تعمل على تشغيل المحاكي، اكتب ctrl+c
للخروج. وأغلِق الوحدة الطرفية الثانية. وأغلِق الوحدة الطرفية الثانية. وشغِّل الأمر deactivate للخروج من البيئة الافتراضية.
deactivate
النشر على Google Cloud
👉بعد الاختبار على الجهاز، حان وقت نشر وكيل الدورة التدريبية على Google Cloud. في الوحدة الطرفية، شغِّل الأوامر التالية:
cd ~/aidemy-bootstrap/courses
export COURSE_BUCKET_NAME=$(gcloud storage buckets list --format="value(name)" | grep aidemy-recap)
gcloud functions deploy courses-agent \
--region=us-central1 \
--gen2 \
--source=. \
--runtime=python312 \
--trigger-topic=plan \
--entry-point=process_teaching_plan \
--set-env-vars=GOOGLE_CLOUD_PROJECT=${PROJECT_ID},COURSE_BUCKET_NAME=$COURSE_BUCKET_NAME
تأكَّد من عملية النشر من خلال الانتقال إلى Cloud Run في Google Cloud Console.من المفترض أن تظهر لك خدمة جديدة باسم courses-agent.
للتحقّق من إعداد المشغِّل، انقر على خدمة courses-agent لعرض تفاصيلها. انتقِل إلى علامة التبويب "العوامل المشغِّلة".
من المفترض أن يظهر لك عامل تشغيل تم إعداده للاستماع إلى الرسائل المنشورة في موضوع الخطة.
أخيرًا، لنلقِ نظرة على عملية التنفيذ من البداية إلى النهاية.
👉بعد ذلك، علينا ضبط إعدادات وكيل البوابة ليعرف مكان العثور على الملفات الصوتية التي تم إنشاؤها. في الوحدة الطرفية، شغِّل:
export COURSE_BUCKET_NAME=$(gcloud storage buckets list --format="value(name)" | grep aidemy-recap)
export PROJECT_ID=$(gcloud config get project)
gcloud run services update aidemy-portal \
--region=us-central1 \
--set-env-vars=GOOGLE_CLOUD_PROJECT=${PROJECT_ID},COURSE_BUCKET_NAME=$COURSE_BUCKET_NAME
👉جرِّب إنشاء خطة تدريس جديدة في صفحة موظّف الدعم المسؤول عن ميزة "الخطة الدراسية". قد يستغرق بدء الخدمة بضع دقائق، ولكن لا داعي للقلق، لأنّها خدمة لا تستخدِم الخادم. احصل على عنوان URL لوكيل التخطيط (إذا لم يكن لديك عنوان URL، يمكنك تشغيل هذا الأمر في المحطة الطرفية):
gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner
بعد إنشاء الخطة الجديدة، يُرجى الانتظار من دقيقتين إلى 3 دقائق لإنشاء الملف الصوتي، علمًا بأنّ هذا الإجراء قد يستغرق بضع دقائق أخرى بسبب القيود المفروضة على الفوترة في هذا الحساب التجريبي.
يمكنك تتبُّع ما إذا كانت الدالة courses-agent
قد تلقّت خطة التدريس من خلال التحقّق من علامة التبويب "العوامل المشغِّلة" للدالة. أعِد تحميل الصفحة بشكل دوري، ومن المفترض أن تظهر لك في النهاية أنّه تمّ استدعاء الدالة. إذا لم يتمّ استدعاء الدالة بعد أكثر من دقيقتَين، يمكنك محاولة إنشاء خطّة التدريس مرّة أخرى. ومع ذلك، تجنَّب إنشاء الخطط بشكل متكرّر وبسرعة، لأنّ موظّف الدعم سيستهلك كل خطة يتم إنشاؤها ويعالجها بشكل تسلسلي، ما قد يؤدي إلى تأخير الطلبات.
👉يُرجى الانتقال إلى البوابة والنقر على "الدورات التدريبية". من المفترض أن تظهر لك ثلاث بطاقات، يعرض كلّ منها ملخّصًا صوتيًا. للعثور على عنوان URL الخاص بوكيل البوابة:
gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep portal
انقر على "تشغيل" في كل دورة تدريبية للتأكّد من توافق الملخّصات الصوتية مع الخطة التعليمية التي أنشأتها للتو.
اخرج من البيئة الافتراضية.
deactivate
13. اختياري: التعاون المستنِد إلى الأدوار باستخدام Gemini وDeepSeek
إنّ الحصول على وجهات نظر متعدّدة أمرٌ قيّم، خاصةً عند إنشاء مهام جذابة ومدروسة. سننشئ الآن نظامًا متعدّد الوكلاء يستفيد من نموذجَين مختلفَين لهما أدوار مميّزة، وذلك لإنشاء المهام: أحدهما يشجع على التعاون، والآخر يشجع على الدراسة الذاتية. سنستخدم بنية "لمرة واحدة"، حيث يتّبع سير العمل مسارًا ثابتًا.
أداة إنشاء المهام في Gemini
سنبدأ بإعداد دالة Gemini لإنشاء مهام مع التركيز على التعاون. عدِّل ملف
gemini.py
في مجلد assignment
.
👉الصِق الرمز التالي في نهاية ملف gemini.py
:
def gen_assignment_gemini(state):
region=get_next_region()
client = genai.Client(vertexai=True, project=PROJECT_ID, location=region)
print(f"---------------gen_assignment_gemini")
response = client.models.generate_content(
model=MODEL_ID, contents=f"""
You are an instructor
Develop engaging and practical assignments for each week, ensuring they align with the teaching plan's objectives and progressively build upon each other.
For each week, provide the following:
* **Week [Number]:** A descriptive title for the assignment (e.g., "Data Exploration Project," "Model Building Exercise").
* **Learning Objectives Assessed:** List the specific learning objectives from the teaching plan that this assignment assesses.
* **Description:** A detailed description of the task, including any specific requirements or constraints. Provide examples or scenarios if applicable.
* **Deliverables:** Specify what students need to submit (e.g., code, report, presentation).
* **Estimated Time Commitment:** The approximate time students should dedicate to completing the assignment.
* **Assessment Criteria:** Briefly outline how the assignment will be graded (e.g., correctness, completeness, clarity, creativity).
The assignments should be a mix of individual and collaborative work where appropriate. Consider different learning styles and provide opportunities for students to apply their knowledge creatively.
Based on this teaching plan: {state["teaching_plan"]}
"""
)
print(f"---------------gen_assignment_gemini answer {response.text}")
state["model_one_assignment"] = response.text
return state
import unittest
class TestGenAssignmentGemini(unittest.TestCase):
def test_gen_assignment_gemini(self):
test_teaching_plan = "Week 1: 2D Shapes and Angles - Day 1: Review of basic 2D shapes (squares, rectangles, triangles, circles). Day 2: Exploring different types of triangles (equilateral, isosceles, scalene, right-angled). Day 3: Exploring quadrilaterals (square, rectangle, parallelogram, rhombus, trapezium). Day 4: Introduction to angles: right angles, acute angles, and obtuse angles. Day 5: Measuring angles using a protractor. Week 2: 3D Shapes and Symmetry - Day 6: Introduction to 3D shapes: cubes, cuboids, spheres, cylinders, cones, and pyramids. Day 7: Describing 3D shapes using faces, edges, and vertices. Day 8: Relating 2D shapes to 3D shapes. Day 9: Identifying lines of symmetry in 2D shapes. Day 10: Completing symmetrical figures. Week 3: Position, Direction, and Problem Solving - Day 11: Describing position using coordinates in the first quadrant. Day 12: Plotting coordinates to draw shapes. Day 13: Understanding translation (sliding a shape). Day 14: Understanding reflection (flipping a shape). Day 15: Problem-solving activities involving perimeter, area, and missing angles."
initial_state = {"teaching_plan": test_teaching_plan, "model_one_assignment": "", "model_two_assigmodel_one_assignmentnment": "", "final_assignment": ""}
updated_state = gen_assignment_gemini(initial_state)
self.assertIn("model_one_assignment", updated_state)
self.assertIsNotNone(updated_state["model_one_assignment"])
self.assertIsInstance(updated_state["model_one_assignment"], str)
self.assertGreater(len(updated_state["model_one_assignment"]), 0)
print(updated_state["model_one_assignment"])
if __name__ == '__main__':
unittest.main()
ويستخدم نموذج Gemini لإنشاء المهام.
نحن جاهزون لاختبار وكيل Gemini.
👉نفِّذ هذه الأوامر في الوحدة الطرفية لإعداد البيئة:
cd ~/aidemy-bootstrap/assignment
export PROJECT_ID=$(gcloud config get project)
python -m venv env
source env/bin/activate
pip install -r requirements.txt
👉يمكنك تنفيذ ما يلي لاختباره:
python gemini.py
من المفترض أن يظهر لك في النتيجة مهمة تتضمّن المزيد من العمل الجماعي. سيعرض اختبار التأكيد في النهاية النتائج أيضًا.
Here are some engaging and practical assignments for each week, designed to build progressively upon the teaching plan's objectives:
**Week 1: Exploring the World of 2D Shapes**
* **Learning Objectives Assessed:**
* Identify and name basic 2D shapes (squares, rectangles, triangles, circles).
* .....
* **Description:**
* **Shape Scavenger Hunt:** Students will go on a scavenger hunt in their homes or neighborhoods, taking pictures of objects that represent different 2D shapes. They will then create a presentation or poster showcasing their findings, classifying each shape and labeling its properties (e.g., number of sides, angles, etc.).
* **Triangle Trivia:** Students will research and create a short quiz or presentation about different types of triangles, focusing on their properties and real-world examples.
* **Angle Exploration:** Students will use a protractor to measure various angles in their surroundings, such as corners of furniture, windows, or doors. They will record their measurements and create a chart categorizing the angles as right, acute, or obtuse.
....
**Week 2: Delving into the World of 3D Shapes and Symmetry**
* **Learning Objectives Assessed:**
* Identify and name basic 3D shapes.
* ....
* **Description:**
* **3D Shape Construction:** Students will work in groups to build 3D shapes using construction paper, cardboard, or other materials. They will then create a presentation showcasing their creations, describing the number of faces, edges, and vertices for each shape.
* **Symmetry Exploration:** Students will investigate the concept of symmetry by creating a visual representation of various symmetrical objects (e.g., butterflies, leaves, snowflakes) using drawing or digital tools. They will identify the lines of symmetry and explain their findings.
* **Symmetry Puzzles:** Students will be given a half-image of a symmetrical figure and will be asked to complete the other half, demonstrating their understanding of symmetry. This can be done through drawing, cut-out activities, or digital tools.
**Week 3: Navigating Position, Direction, and Problem Solving**
* **Learning Objectives Assessed:**
* Describe position using coordinates in the first quadrant.
* ....
* **Description:**
* **Coordinate Maze:** Students will create a maze using coordinates on a grid paper. They will then provide directions for navigating the maze using a combination of coordinate movements and translation/reflection instructions.
* **Shape Transformations:** Students will draw shapes on a grid paper and then apply transformations such as translation and reflection, recording the new coordinates of the transformed shapes.
* **Geometry Challenge:** Students will solve real-world problems involving perimeter, area, and angles. For example, they could be asked to calculate the perimeter of a room, the area of a garden, or the missing angle in a triangle.
....
يمكنك إيقاف الاختبار باستخدام ctl+c
وتنظيف رمز الاختبار. أزِل الرمز التالي من gemini.py
.
import unittest
class TestGenAssignmentGemini(unittest.TestCase):
def test_gen_assignment_gemini(self):
test_teaching_plan = "Week 1: 2D Shapes and Angles - Day 1: Review of basic 2D shapes (squares, rectangles, triangles, circles). Day 2: Exploring different types of triangles (equilateral, isosceles, scalene, right-angled). Day 3: Exploring quadrilaterals (square, rectangle, parallelogram, rhombus, trapezium). Day 4: Introduction to angles: right angles, acute angles, and obtuse angles. Day 5: Measuring angles using a protractor. Week 2: 3D Shapes and Symmetry - Day 6: Introduction to 3D shapes: cubes, cuboids, spheres, cylinders, cones, and pyramids. Day 7: Describing 3D shapes using faces, edges, and vertices. Day 8: Relating 2D shapes to 3D shapes. Day 9: Identifying lines of symmetry in 2D shapes. Day 10: Completing symmetrical figures. Week 3: Position, Direction, and Problem Solving - Day 11: Describing position using coordinates in the first quadrant. Day 12: Plotting coordinates to draw shapes. Day 13: Understanding translation (sliding a shape). Day 14: Understanding reflection (flipping a shape). Day 15: Problem-solving activities involving perimeter, area, and missing angles."
initial_state = {"teaching_plan": test_teaching_plan, "model_one_assignment": "", "model_two_assigmodel_one_assignmentnment": "", "final_assignment": ""}
updated_state = gen_assignment_gemini(initial_state)
self.assertIn("model_one_assignment", updated_state)
self.assertIsNotNone(updated_state["model_one_assignment"])
self.assertIsInstance(updated_state["model_one_assignment"], str)
self.assertGreater(len(updated_state["model_one_assignment"]), 0)
print(updated_state["model_one_assignment"])
if __name__ == '__main__':
unittest.main()
ضبط أداة إنشاء المهام في DeepSeek
على الرغم من أنّ منصات الذكاء الاصطناعي المستندة إلى السحابة الإلكترونية مريحة، يمكن أن تكون النماذج اللغوية الكبيرة المستضافة ذاتيًا ضرورية لحماية خصوصية البيانات وضمان سيادة البيانات. سننشر أصغر نموذج DeepSeek (يحتوي على 1.5 مليار مَعلمة) على إحدى مثيلات Cloud Compute Engine. هناك طرق أخرى، مثل استضافتها على منصة Vertex AI من Google أو استضافتها على مثيل GKE، ولكن بما أنّ هذه ورشة عمل حول موظّفي الذكاء الاصطناعي، ولا أريد إبقاءك هنا إلى الأبد، لنستخدم الطريقة الأسهل. إذا كنت مهتمًا بالتعرّف على خيارات أخرى، يمكنك الاطّلاع على ملف deepseek-vertexai.py
ضمن مجلد "المهام"، حيث يقدّم نموذجًا لكيفية التفاعل مع النماذج المنشورة على VertexAI.
👉شغِّل هذا الأمر في الوحدة الطرفية لإنشاء منصة Ollama المستضافة ذاتيًا للترجمة الآلية الضخمة:
cd ~/aidemy-bootstrap/assignment
gcloud compute instances create ollama-instance \
--image-family=ubuntu-2204-lts \
--image-project=ubuntu-os-cloud \
--machine-type=e2-standard-4 \
--zone=us-central1-a \
--metadata-from-file startup-script=startup.sh \
--boot-disk-size=50GB \
--tags=ollama \
--scopes=https://www.googleapis.com/auth/cloud-platform
للتأكّد من تشغيل مثيل Compute Engine:
انتقِل إلى Compute Engine > "مثيلات الأجهزة الافتراضية" في Google Cloud Console. من المفترض أن يظهر الرمز ollama-instance
مع علامة اختيار خضراء تشير إلى أنّه قيد التشغيل. إذا لم يظهر لك، تأكَّد من أنّ المنطقة هي us-central1. إذا لم يكن الأمر كذلك، قد تحتاج إلى البحث عنه.
👉سنثبّت أصغر نموذج DeepSeek ونختبره. في محرِّر Cloud Shell، في نافذة جديدة، نفِّذ الأمر التالي للوصول إلى خادم GCE باستخدام بروتوكول SSH.
gcloud compute ssh ollama-instance --zone=us-central1-a
عند إنشاء اتصال SSH، قد يُطلب منك ما يلي:
"هل تريد المتابعة (نعم/لا)؟"
ما عليك سوى كتابة Y
(لا تُراعَى حالة الأحرف) والضغط على مفتاح Enter للمتابعة.
بعد ذلك، قد يُطلب منك إنشاء عبارة مرور لمفتاح SSH. إذا كنت تفضّل عدم استخدام عبارة مرور، ما عليك سوى الضغط على مفتاح Enter مرّتين لقبول الإعداد التلقائي (بدون عبارة مرور).
👉أنت الآن في الجهاز الظاهري، وسحب أصغر نموذج DeepSeek R1 واختبار ما إذا كان يعمل؟
ollama pull deepseek-r1:1.5b
ollama run deepseek-r1:1.5b "who are you?"
👉اخرج من مثيل GCE وأدخِل ما يلي في وحدة طرفية ssh:
exit
أغلِق الوحدة الطرفية الجديدة وانتقِل إلى الوحدة الطرفية الأصلية.
👉ولا تنسَ إعداد سياسة الشبكة لكي تتمكّن الخدمات الأخرى من الوصول إلى LLM، يُرجى تقييد الوصول إلى المثيل إذا كنت تريد إجراء ذلك في مرحلة الإنتاج، إما من خلال تنفيذ تسجيل الدخول الآمن للخدمة أو تقييد الوصول إلى عنوان IP. التنفيذ:
gcloud compute firewall-rules create allow-ollama-11434 \
--allow=tcp:11434 \
--target-tags=ollama \
--description="Allow access to Ollama on port 11434"
👉للتحقّق مما إذا كانت سياسة جدار الحماية تعمل بشكل صحيح، جرِّب تنفيذ ما يلي:
export OLLAMA_HOST=http://$(gcloud compute instances describe ollama-instance --zone=us-central1-a --format='value(networkInterfaces[0].accessConfigs[0].natIP)'):11434
curl -X POST "${OLLAMA_HOST}/api/generate" \
-H "Content-Type: application/json" \
-d '{
"prompt": "Hello, what are you?",
"model": "deepseek-r1:1.5b",
"stream": false
}'
بعد ذلك، سنعمل على دالة Deepseek في وكيل المهام لإنشاء مهام تركّز على العمل الفردي.
👉تعديل deepseek.py
ضمن مجلد assignment
، إضافة المقتطف التالي إلى النهاية
def gen_assignment_deepseek(state):
print(f"---------------gen_assignment_deepseek")
template = """
You are an instructor who favor student to focus on individual work.
Develop engaging and practical assignments for each week, ensuring they align with the teaching plan's objectives and progressively build upon each other.
For each week, provide the following:
* **Week [Number]:** A descriptive title for the assignment (e.g., "Data Exploration Project," "Model Building Exercise").
* **Learning Objectives Assessed:** List the specific learning objectives from the teaching plan that this assignment assesses.
* **Description:** A detailed description of the task, including any specific requirements or constraints. Provide examples or scenarios if applicable.
* **Deliverables:** Specify what students need to submit (e.g., code, report, presentation).
* **Estimated Time Commitment:** The approximate time students should dedicate to completing the assignment.
* **Assessment Criteria:** Briefly outline how the assignment will be graded (e.g., correctness, completeness, clarity, creativity).
The assignments should be a mix of individual and collaborative work where appropriate. Consider different learning styles and provide opportunities for students to apply their knowledge creatively.
Based on this teaching plan: {teaching_plan}
"""
prompt = ChatPromptTemplate.from_template(template)
model = OllamaLLM(model="deepseek-r1:1.5b",
base_url=OLLAMA_HOST)
chain = prompt | model
response = chain.invoke({"teaching_plan":state["teaching_plan"]})
state["model_two_assignment"] = response
return state
import unittest
class TestGenAssignmentDeepseek(unittest.TestCase):
def test_gen_assignment_deepseek(self):
test_teaching_plan = "Week 1: 2D Shapes and Angles - Day 1: Review of basic 2D shapes (squares, rectangles, triangles, circles). Day 2: Exploring different types of triangles (equilateral, isosceles, scalene, right-angled). Day 3: Exploring quadrilaterals (square, rectangle, parallelogram, rhombus, trapezium). Day 4: Introduction to angles: right angles, acute angles, and obtuse angles. Day 5: Measuring angles using a protractor. Week 2: 3D Shapes and Symmetry - Day 6: Introduction to 3D shapes: cubes, cuboids, spheres, cylinders, cones, and pyramids. Day 7: Describing 3D shapes using faces, edges, and vertices. Day 8: Relating 2D shapes to 3D shapes. Day 9: Identifying lines of symmetry in 2D shapes. Day 10: Completing symmetrical figures. Week 3: Position, Direction, and Problem Solving - Day 11: Describing position using coordinates in the first quadrant. Day 12: Plotting coordinates to draw shapes. Day 13: Understanding translation (sliding a shape). Day 14: Understanding reflection (flipping a shape). Day 15: Problem-solving activities involving perimeter, area, and missing angles."
initial_state = {"teaching_plan": test_teaching_plan, "model_one_assignment": "", "model_two_assignment": "", "final_assignment": ""}
updated_state = gen_assignment_deepseek(initial_state)
self.assertIn("model_two_assignment", updated_state)
self.assertIsNotNone(updated_state["model_two_assignment"])
self.assertIsInstance(updated_state["model_two_assignment"], str)
self.assertGreater(len(updated_state["model_two_assignment"]), 0)
print(updated_state["model_two_assignment"])
if __name__ == '__main__':
unittest.main()
👉لنجرّب ذلك من خلال تنفيذ ما يلي:
cd ~/aidemy-bootstrap/assignment
source env/bin/activate
export PROJECT_ID=$(gcloud config get project)
export OLLAMA_HOST=http://$(gcloud compute instances describe ollama-instance --zone=us-central1-a --format='value(networkInterfaces[0].accessConfigs[0].natIP)'):11434
python deepseek.py
من المفترض أن تظهر لك مهمة تتضمّن المزيد من العمل الذاتي.
**Assignment Plan for Each Week**
---
### **Week 1: 2D Shapes and Angles**
- **Week Title:** "Exploring 2D Shapes"
Assign students to research and present on various 2D shapes. Include a project where they create models using straws and tape for triangles, draw quadrilaterals with specific measurements, and compare their properties.
### **Week 2: 3D Shapes and Symmetry**
Assign students to create models or nets for cubes and cuboids. They will also predict how folding these nets form the 3D shapes. Include a project where they identify symmetrical properties using mirrors or folding techniques.
### **Week 3: Position, Direction, and Problem Solving**
Assign students to use mirrors or folding techniques for reflections. Include activities where they measure angles, use a protractor, solve problems involving perimeter/area, and create symmetrical designs.
....
👉أوقِف ctl+c
وأزِل رمز الاختبار. أزِل الرمز التالي من deepseek.py
.
import unittest
class TestGenAssignmentDeepseek(unittest.TestCase):
def test_gen_assignment_deepseek(self):
test_teaching_plan = "Week 1: 2D Shapes and Angles - Day 1: Review of basic 2D shapes (squares, rectangles, triangles, circles). Day 2: Exploring different types of triangles (equilateral, isosceles, scalene, right-angled). Day 3: Exploring quadrilaterals (square, rectangle, parallelogram, rhombus, trapezium). Day 4: Introduction to angles: right angles, acute angles, and obtuse angles. Day 5: Measuring angles using a protractor. Week 2: 3D Shapes and Symmetry - Day 6: Introduction to 3D shapes: cubes, cuboids, spheres, cylinders, cones, and pyramids. Day 7: Describing 3D shapes using faces, edges, and vertices. Day 8: Relating 2D shapes to 3D shapes. Day 9: Identifying lines of symmetry in 2D shapes. Day 10: Completing symmetrical figures. Week 3: Position, Direction, and Problem Solving - Day 11: Describing position using coordinates in the first quadrant. Day 12: Plotting coordinates to draw shapes. Day 13: Understanding translation (sliding a shape). Day 14: Understanding reflection (flipping a shape). Day 15: Problem-solving activities involving perimeter, area, and missing angles."
initial_state = {"teaching_plan": test_teaching_plan, "model_one_assignment": "", "model_two_assignment": "", "final_assignment": ""}
updated_state = gen_assignment_deepseek(initial_state)
self.assertIn("model_two_assignment", updated_state)
self.assertIsNotNone(updated_state["model_two_assignment"])
self.assertIsInstance(updated_state["model_two_assignment"], str)
self.assertGreater(len(updated_state["model_two_assignment"]), 0)
print(updated_state["model_two_assignment"])
if __name__ == '__main__':
unittest.main()
الآن، سنستخدم نموذج Gemini نفسه لدمج كلتا المهمتَين في مهمة جديدة. عدِّل ملف gemini.py
في مجلد assignment
.
👉الصِق الرمز التالي في نهاية ملف gemini.py
:
def combine_assignments(state):
print(f"---------------combine_assignments ")
region=get_next_region()
client = genai.Client(vertexai=True, project=PROJECT_ID, location=region)
response = client.models.generate_content(
model=MODEL_ID, contents=f"""
Look at all the proposed assignment so far {state["model_one_assignment"]} and {state["model_two_assignment"]}, combine them and come up with a final assignment for student.
"""
)
state["final_assignment"] = response.text
return state
لدمج نقاط القوة في كلا النموذجَين، سننسق سير عمل محدّدًا باستخدام LangGraph. يتألّف سير العمل هذا من ثلاث خطوات: أولاً، ينشئ نموذج Gemini مهمة تركّز على التعاون. ثانيًا، ينشئ نموذج DeepSeek مهمة تركّز على العمل الفردي. أخيرًا، يُجمِّع Gemini هاتين المهمتَين في مهمة واحدة شاملة. وبما أنّنا نحدّد تسلسل الخطوات مسبقًا بدون اتّخاذ قرارات من خلال نموذج معالجة اللغة الضخمة، يشكّل ذلك عملية تنسيق ذات مسار واحد يحدّدها المستخدم.
👉الصِق الرمز التالي في نهاية ملف main.py
ضمن المجلد assignment
:
def create_assignment(teaching_plan: str):
print(f"create_assignment---->{teaching_plan}")
builder = StateGraph(State)
builder.add_node("gen_assignment_gemini", gen_assignment_gemini)
builder.add_node("gen_assignment_deepseek", gen_assignment_deepseek)
builder.add_node("combine_assignments", combine_assignments)
builder.add_edge(START, "gen_assignment_gemini")
builder.add_edge("gen_assignment_gemini", "gen_assignment_deepseek")
builder.add_edge("gen_assignment_deepseek", "combine_assignments")
builder.add_edge("combine_assignments", END)
graph = builder.compile()
state = graph.invoke({"teaching_plan": teaching_plan})
return state["final_assignment"]
import unittest
class TestCreatAssignment(unittest.TestCase):
def test_create_assignment(self):
test_teaching_plan = "Week 1: 2D Shapes and Angles - Day 1: Review of basic 2D shapes (squares, rectangles, triangles, circles). Day 2: Exploring different types of triangles (equilateral, isosceles, scalene, right-angled). Day 3: Exploring quadrilaterals (square, rectangle, parallelogram, rhombus, trapezium). Day 4: Introduction to angles: right angles, acute angles, and obtuse angles. Day 5: Measuring angles using a protractor. Week 2: 3D Shapes and Symmetry - Day 6: Introduction to 3D shapes: cubes, cuboids, spheres, cylinders, cones, and pyramids. Day 7: Describing 3D shapes using faces, edges, and vertices. Day 8: Relating 2D shapes to 3D shapes. Day 9: Identifying lines of symmetry in 2D shapes. Day 10: Completing symmetrical figures. Week 3: Position, Direction, and Problem Solving - Day 11: Describing position using coordinates in the first quadrant. Day 12: Plotting coordinates to draw shapes. Day 13: Understanding translation (sliding a shape). Day 14: Understanding reflection (flipping a shape). Day 15: Problem-solving activities involving perimeter, area, and missing angles."
initial_state = {"teaching_plan": test_teaching_plan, "model_one_assignment": "", "model_two_assignment": "", "final_assignment": ""}
updated_state = create_assignment(initial_state)
print(updated_state)
if __name__ == '__main__':
unittest.main()
👉لاختبار الدالة create_assignment
في البداية والتأكّد من أنّ سير العمل الذي يجمع بين Gemini وDeepSeek يعمل بشكل صحيح، شغِّل الأمر التالي:
cd ~/aidemy-bootstrap/assignment
source env/bin/activate
pip install -r requirements.txt
python main.py
من المفترض أن تظهر لك نتائج تجمع بين كلا النموذجَين مع منظورهما الفردي لدراسة الطلاب وأعمالهم الجماعية.
**Tasks:**
1. **Clue Collection:** Gather all the clues left by the thieves. These clues will include:
* Descriptions of shapes and their properties (angles, sides, etc.)
* Coordinate grids with hidden messages
* Geometric puzzles requiring transformation (translation, reflection, rotation)
* Challenges involving area, perimeter, and angle calculations
2. **Clue Analysis:** Decipher each clue using your geometric knowledge. This will involve:
* Identifying the shape and its properties
* Plotting coordinates and interpreting patterns on the grid
* Solving geometric puzzles by applying transformations
* Calculating area, perimeter, and missing angles
3. **Case Report:** Create a comprehensive case report outlining your findings. This report should include:
* A detailed explanation of each clue and its solution
* Sketches and diagrams to support your explanations
* A step-by-step account of how you followed the clues to locate the artifact
* A final conclusion about the thieves and their motives
👉أوقِف ctl+c
وأزِل رمز الاختبار. أزِل الرمز التالي من main.py
.
import unittest
class TestCreatAssignment(unittest.TestCase):
def test_create_assignment(self):
test_teaching_plan = "Week 1: 2D Shapes and Angles - Day 1: Review of basic 2D shapes (squares, rectangles, triangles, circles). Day 2: Exploring different types of triangles (equilateral, isosceles, scalene, right-angled). Day 3: Exploring quadrilaterals (square, rectangle, parallelogram, rhombus, trapezium). Day 4: Introduction to angles: right angles, acute angles, and obtuse angles. Day 5: Measuring angles using a protractor. Week 2: 3D Shapes and Symmetry - Day 6: Introduction to 3D shapes: cubes, cuboids, spheres, cylinders, cones, and pyramids. Day 7: Describing 3D shapes using faces, edges, and vertices. Day 8: Relating 2D shapes to 3D shapes. Day 9: Identifying lines of symmetry in 2D shapes. Day 10: Completing symmetrical figures. Week 3: Position, Direction, and Problem Solving - Day 11: Describing position using coordinates in the first quadrant. Day 12: Plotting coordinates to draw shapes. Day 13: Understanding translation (sliding a shape). Day 14: Understanding reflection (flipping a shape). Day 15: Problem-solving activities involving perimeter, area, and missing angles."
initial_state = {"teaching_plan": test_teaching_plan, "model_one_assignment": "", "model_two_assignment": "", "final_assignment": ""}
updated_state = create_assignment(initial_state)
print(updated_state)
if __name__ == '__main__':
unittest.main()
لجعل عملية إنشاء المهام تلقائية ومتوافقة مع خطط التدريس الجديدة، سنستفيد من البنية الحالية المستندة إلى الأحداث. تحدِّد التعليمة البرمجية التالية دالة Cloud Run (generate_assignment) التي سيتم تفعيلها عند نشر خطة تعليمية جديدة في موضوع Pub/Sub "plan".
👉أضِف الرمز التالي إلى نهاية main.py
:
@functions_framework.cloud_event
def generate_assignment(cloud_event):
print(f"CloudEvent received: {cloud_event.data}")
try:
if isinstance(cloud_event.data.get('message', {}).get('data'), str):
data = json.loads(base64.b64decode(cloud_event.data['message']['data']).decode('utf-8'))
teaching_plan = data.get('teaching_plan')
elif 'teaching_plan' in cloud_event.data:
teaching_plan = cloud_event.data["teaching_plan"]
else:
raise KeyError("teaching_plan not found")
assignment = create_assignment(teaching_plan)
print(f"Assignment---->{assignment}")
#Store the return assignment into bucket as a text file
storage_client = storage.Client()
bucket = storage_client.bucket(ASSIGNMENT_BUCKET)
file_name = f"assignment-{random.randint(1, 1000)}.txt"
blob = bucket.blob(file_name)
blob.upload_from_string(assignment)
return f"Assignment generated and stored in {ASSIGNMENT_BUCKET}/{file_name}", 200
except (json.JSONDecodeError, AttributeError, KeyError) as e:
print(f"Error decoding CloudEvent data: {e} - Data: {cloud_event.data}")
return "Error processing event", 500
except Exception as e:
print(f"Error generate assignment: {e}")
return "Error generate assignment", 500
الاختبار على الجهاز
قبل النشر على Google Cloud، من الممارسات الجيدة اختبار دالة Cloud Run على الجهاز. يتيح ذلك إجراء تكرارات أسرع وتصحيح الأخطاء بسهولة أكبر.
أولاً، أنشئ حزمة على Cloud Storage لتخزين ملفات المهام التي تم إنشاؤها ومنح حساب الخدمة إذن الوصول إلى الحزمة. نفِّذ الأوامر التالية في الوحدة الطرفية:
👉ملاحظة مهمة: تأكَّد من تحديد اسم ASSIGNMENT_BUCKET فريد يبدأ بـ "aidemy-assignment-". هذا الاسم الفريد مهم لتجنُّب تعارضات الأسماء عند إنشاء حزمة Cloud Storage. (استبدِل <YOUR_NAME> بأي كلمة عشوائية)
export ASSIGNMENT_BUCKET=aidemy-assignment-<YOUR_NAME> #Name must be unqiue
👉ثمّ نفِّذ ما يلي:
export PROJECT_ID=$(gcloud config get project)
export SERVICE_ACCOUNT_NAME=$(gcloud compute project-info describe --format="value(defaultServiceAccount)")
gsutil mb -p $PROJECT_ID -l us-central1 gs://$ASSIGNMENT_BUCKET
gcloud storage buckets add-iam-policy-binding gs://$ASSIGNMENT_BUCKET \
--member "serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role "roles/storage.objectViewer"
gcloud storage buckets add-iam-policy-binding gs://$ASSIGNMENT_BUCKET \
--member "serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role "roles/storage.objectCreator"
👉الآن، ابدأ محاكي وظائف Cloud Run:
cd ~/aidemy-bootstrap/assignment
functions-framework --target generate_assignment --signature-type=cloudevent --source main.py
👉أثناء تشغيل المحاكي في وحدة طرفية واحدة، افتح وحدة طرفية ثانية في Cloud Shell. في هذه المحطة الطرفية الثانية، أرسِل حدث CloudEvent تجريبيًا إلى المحاكي لمحاكاة خطة تعليمية جديدة يتم نشرها:
curl -X POST \
http://localhost:8080/ \
-H "Content-Type: application/json" \
-H "ce-id: event-id-01" \
-H "ce-source: planner-agent" \
-H "ce-specversion: 1.0" \
-H "ce-type: google.cloud.pubsub.topic.v1.messagePublished" \
-d '{
"message": {
"data": "eyJ0ZWFjaGluZ19wbGFuIjogIldlZWsgMTogMkQgU2hhcGVzIGFuZCBBbmdsZXMgLSBEYXkgMTogUmV2aWV3IG9mIGJhc2ljIDJEIHNoYXBlcyAoc3F1YXJlcywgcmVjdGFuZ2xlcywgdHJpYW5nbGVzLCBjaXJjbGVzKS4gRGF5IDI6IEV4cGxvcmluZyBkaWZmZXJlbnQgdHlwZXMgb2YgdHJpYW5nbGVzIChlcXVpbGF0ZXJhbCwgaXNvc2NlbGVzLCBzY2FsZW5lLCByaWdodC1hbmdsZWQpLiBEYXkgMzogRXhwbG9yaW5nIHF1YWRyaWxhdGVyYWxzIChzcXVhcmUsIHJlY3RhbmdsZSwgcGFyYWxsZWxvZ3JhbSwgcmhvbWJ1cywgdHJhcGV6aXVtKS4gRGF5IDQ6IEludHJvZHVjdGlvbiB0byBhbmdsZXM6IHJpZ2h0IGFuZ2xlcywgYWN1dGUgYW5nbGVzLCBhbmQgb2J0dXNlIGFuZ2xlcy4gRGF5IDU6IE1lYXN1cmluZyBhbmdsZXMgdXNpbmcgYSBwcm90cmFjdG9yLiBXZWVrIDI6IDNEIFNoYXBlcyBhbmQgU3ltbWV0cnkgLSBEYXkgNjogSW50cm9kdWN0aW9uIHRvIDNEIHNoYXBlczogY3ViZXMsIGN1Ym9pZHMsIHNwaGVyZXMsIGN5bGluZGVycywgY29uZXMsIGFuZCBweXJhbWlkcy4gRGF5IDc6IERlc2NyaWJpbmcgM0Qgc2hhcGVzIHVzaW5nIGZhY2VzLCBlZGdlcywgYW5kIHZlcnRpY2VzLiBEYXkgODogUmVsYXRpbmcgMkQgc2hhcGVzIHRvIDNEIHNoYXBlcy4gRGF5IDk6IElkZW50aWZ5aW5nIGxpbmVzIG9mIHN5bW1ldHJ5IGluIDJEIHNoYXBlcy4gRGF5IDEwOiBDb21wbGV0aW5nIHN5bW1ldHJpY2FsIGZpZ3VyZXMuIFdlZWsgMzogUG9zaXRpb24sIERpcmVjdGlvbiwgYW5kIFByb2JsZW0gU29sdmluZyAtIERheSAxMTogRGVzY3JpYmluZyBwb3NpdGlvbiB1c2luZyBjb29yZGluYXRlcyBpbiB0aGUgZmlyc3QgcXVhZHJhbnQuIERheSAxMjogUGxvdHRpbmcgY29vcmRpbmF0ZXMgdG8gZHJhdyBzaGFwZXMuIERheSAxMzogVW5kZXJzdGFuZGluZyB0cmFuc2xhdGlvbiAoc2xpZGluZyBhIHNoYXBlKS4gRGF5IDE0OiBVbmRlcnN0YW5kaW5nIHJlZmxlY3Rpb24gKGZsaXBwaW5nIGEgc2hhcGUpLiBEYXkgMTU6IFByb2JsZW0tc29sdmluZyBhY3Rpdml0aWVzIGludm9sdmluZyBwZXJpbWV0ZXIsIGFyZWEsIGFuZCBtaXNzaW5nIGFuZ2xlcy4ifQ=="
}
}'
بدلاً من التحديق في الشاشة في انتظار الردّ، يمكنك التبديل إلى وحدة تحكّم Cloud Shell الأخرى. يمكنك مراقبة مستوى التقدّم وأيّ رسائل ناتجة أو أخطاء تنشئها الدالة في وحدة تحكّم المحاكي. 😁
من المفترض أن تظهر رسالة OK.
للتأكّد من إنشاء المهمة وتخزينها بنجاح، انتقِل إلى Google Cloud Console وانتقِل إلى التخزين > "Cloud Storage". اختَر حزمة aidemy-assignment
التي أنشأتها. من المفترض أن يظهر لك ملف نصي باسم assignment-{random number}.txt
في الحزمة. انقر على الملف لتنزيله والتحقّق من محتوياته. يُستخدم هذا الإجراء للتحقّق من أنّ ملفًا جديدًا يحتوي على مهمة جديدة تم إنشاؤها للتو.
👉في وحدة التحكّم التي تعمل على تشغيل المحاكي، اكتب ctrl+c
للخروج. وأغلِق الوحدة الطرفية الثانية. 👉في المحطة الطرفية التي يعمل فيها المحاكي، يُرجى أيضًا الخروج من البيئة الافتراضية.
deactivate
👉بعد ذلك، سننشر وكيل التوزيع في السحابة الإلكترونية.
cd ~/aidemy-bootstrap/assignment
export ASSIGNMENT_BUCKET=$(gcloud storage buckets list --format="value(name)" | grep aidemy-assignment)
export OLLAMA_HOST=http://$(gcloud compute instances describe ollama-instance --zone=us-central1-a --format='value(networkInterfaces[0].accessConfigs[0].natIP)'):11434
export PROJECT_ID=$(gcloud config get project)
gcloud functions deploy assignment-agent \
--gen2 \
--timeout=540 \
--memory=2Gi \
--cpu=1 \
--set-env-vars="ASSIGNMENT_BUCKET=${ASSIGNMENT_BUCKET}" \
--set-env-vars=GOOGLE_CLOUD_PROJECT=${GOOGLE_CLOUD_PROJECT} \
--set-env-vars=OLLAMA_HOST=${OLLAMA_HOST} \
--region=us-central1 \
--runtime=python312 \
--source=. \
--entry-point=generate_assignment \
--trigger-topic=plan
تأكَّد من عملية النشر من خلال الانتقال إلى Google Cloud Console والانتقال إلى Cloud Run.من المفترض أن تظهر لك خدمة جديدة باسم courses-agent.
بعد تنفيذ سير عمل إنشاء المهام واختباره ونشره، يمكننا الانتقال إلى الخطوة التالية: إتاحة هذه المهام للطلاب في بوابتهم.
14. اختياري: التعاون المستنِد إلى الأدوار باستخدام Gemini وDeepSeek - الجزء 2
إنشاء المواقع الإلكترونية الديناميكية
لتحسين بوابة الطالب وجعلها أكثر تفاعلاً، سننفّذ إنشاء HTML ديناميكي لصفحات المهام الدراسية. والهدف من ذلك هو تحديث البوابة تلقائيًا بتصميم جديد وجذاب من الناحية المرئية عند إنشاء مهمة جديدة. ويستفيد ذلك من إمكانات الترميز في النماذج اللغوية الكبيرة لتوفير تجربة أكثر ديناميكية وإثارة للاهتمام للمستخدم.
👉في محرِّر Cloud Shell، عدِّل ملف render.py
ضمن المجلد portal
، واستبدِل
def render_assignment_page():
return ""
باستخدام مقتطف الرمز البرمجي التالي:
def render_assignment_page(assignment: str):
try:
region=get_next_region()
llm = VertexAI(model_name="gemini-2.0-flash-001", location=region)
input_msg = HumanMessage(content=[f"Here the assignment {assignment}"])
prompt_template = ChatPromptTemplate.from_messages(
[
SystemMessage(
content=(
"""
As a frontend developer, create HTML to display a student assignment with a creative look and feel. Include the following navigation bar at the top:
```
<nav>
<a href="/">Home</a>
<a href="/quiz">Quizzes</a>
<a href="/courses">Courses</a>
<a href="/assignment">Assignments</a>
</nav>
```
Also include these links in the <head> section:
```
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500&display=swap" rel="stylesheet">
```
Do not apply inline styles to the navigation bar.
The HTML should display the full assignment content. In its CSS, be creative with the rainbow colors and aesthetic.
Make it creative and pretty
The assignment content should be well-structured and easy to read.
respond with JUST the html file
"""
)
),
input_msg,
]
)
prompt = prompt_template.format()
response = llm.invoke(prompt)
response = response.replace("```html", "")
response = response.replace("```", "")
with open("templates/assignment.html", "w") as f:
f.write(response)
print(f"response: {response}")
return response
except Exception as e:
print(f"Error sending message to chatbot: {e}") # Log this error too!
return f"Unable to process your request at this time. Due to the following reason: {str(e)}"
ويستخدم نموذج Gemini لإنشاء صفحات HTML ديناميكيًا للمهمة. يأخذ هذا النموذج محتوى المهمة كمدخل ويستخدم طلبًا لتوجيه Gemini لإنشاء صفحة HTML جذابة من الناحية المرئية بأسلوب إبداعي.
بعد ذلك، سننشئ نقطة نهاية سيتم تفعيلها عند إضافة مستند جديد إلى حزمة المهام:
👉في مجلد البوابة، عدِّل ملف app.py
وأضِف الرمز البرمجي التالي داخل ## Add your code here" comments
، بعد الدالة new_teaching_plan
:
## Add your code here
@app.route('/render_assignment', methods=['POST'])
def render_assignment():
try:
data = request.get_json()
file_name = data.get('name')
bucket_name = data.get('bucket')
if not file_name or not bucket_name:
return jsonify({'error': 'Missing file name or bucket name'}), 400
storage_client = storage.Client()
bucket = storage_client.bucket(bucket_name)
blob = bucket.blob(file_name)
content = blob.download_as_text()
print(f"File content: {content}")
render_assignment_page(content)
return jsonify({'message': 'Assignment rendered successfully'})
except Exception as e:
print(f"Error processing file: {e}")
return jsonify({'error': 'Error processing file'}), 500
## Add your code here
عند تشغيلها، تسترجع اسم الملف واسم الحزمة من بيانات الطلب، وتنزِّل محتوى المهمة من Cloud Storage، وتستدعي الدالة render_assignment_page
لإنشاء رمز HTML.
👉سننفّذ الإجراء على الجهاز:
cd ~/aidemy-bootstrap/portal
source env/bin/activate
python app.py
👉من قائمة "معاينة الويب" في أعلى نافذة Cloud Shell، اختَر "معاينة على المنفذ 8080". سيؤدي ذلك إلى فتح تطبيقك في علامة تبويب متصفّح جديدة. انتقِل إلى رابط المهمة في شريط التنقّل. من المفترض أن تظهر لك صفحة فارغة في هذه المرحلة، وهو السلوك المتوقّع لأنّنا لم نؤسس بعد جسر تواصل بين موظّف الدعم المعنيّ بالمهمة وبوابة الويب لتعبئة المحتوى ديناميكيًا.
👉لدمج هذه التغييرات ونشر الرمز المعدَّل، عليك إعادة إنشاء صورة وكيل البوابة ودفعها:
cd ~/aidemy-bootstrap/portal/
export PROJECT_ID=$(gcloud config get project)
docker build -t gcr.io/${PROJECT_ID}/aidemy-portal .
export PROJECT_ID=$(gcloud config get project)
docker tag gcr.io/${PROJECT_ID}/aidemy-portal us-central1-docker.pkg.dev/${PROJECT_ID}/agent-repository/aidemy-portal
docker push us-central1-docker.pkg.dev/${PROJECT_ID}/agent-repository/aidemy-portal
👉بعد دفع الصورة الجديدة، أعِد نشر خدمة Cloud Run. شغِّل النص البرمجي التالي لفرض تحديث Cloud Run:
export PROJECT_ID=$(gcloud config get project)
export COURSE_BUCKET_NAME=$(gcloud storage buckets list --format="value(name)" | grep aidemy-recap)
gcloud run services update aidemy-portal \
--region=us-central1 \
--set-env-vars=GOOGLE_CLOUD_PROJECT=${PROJECT_ID},COURSE_BUCKET_NAME=$COURSE_BUCKET_NAME
👉الآن، سننشر عامل تشغيل Eventarc يستمع إلى أي عنصر جديد تم إنشاؤه (تم الانتهاء منه) في حزمة التعيين. سيؤدي هذا المشغّل إلى استدعاء نقطة نهاية /render_assignment تلقائيًا في خدمة البوابة عند إنشاء ملف مهمة جديد.
export PROJECT_ID=$(gcloud config get project)
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$(gcloud storage service-agent --project $PROJECT_ID)" \
--role="roles/pubsub.publisher"
export SERVICE_ACCOUNT_NAME=$(gcloud compute project-info describe --format="value(defaultServiceAccount)")
gcloud eventarc triggers create portal-assignment-trigger \
--location=us-central1 \
--service-account=$SERVICE_ACCOUNT_NAME \
--destination-run-service=aidemy-portal \
--destination-run-region=us-central1 \
--destination-run-path="/render_assignment" \
--event-filters="bucket=$ASSIGNMENT_BUCKET" \
--event-filters="type=google.cloud.storage.object.v1.finalized"
للتأكّد من أنّه تم إنشاء العامل المشغِّل بنجاح، انتقِل إلى صفحة عوامل تشغيل Eventarc في Google Cloud Console. من المفترض أن يظهر لك portal-assignment-trigger
في الجدول. انقر على اسم العامل المشغِّل للاطّلاع على تفاصيله.
قد يستغرق تفعيل العامل المشغِّل الجديد مدة تتراوح بين دقيقتين و3 دقائق.
للاطّلاع على عملية إنشاء التقييمات الديناميكية، شغِّل الأمر التالي للعثور على عنوان URL الخاص بوكيل التخطيط (إذا لم يكن لديك عنوان URL متوفّرًا):
gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner
ابحث عن عنوان URL الخاص بوكيل البوابة:
gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep portal
في وكيل التخطيط، أنشئ خطة تدريس جديدة.
بعد بضع دقائق (للسماح بإكمال إنشاء الملف الصوتي وإنشاء المهمة وعرض صفحات HTML)، انتقِل إلى بوابة الطالب.
👉انقر على رابط "المهمة" في شريط التنقّل. من المفترض أن تظهر لك مهمة تم إنشاؤها حديثًا باستخدام رمز HTML تم إنشاؤه ديناميكيًا. في كل مرة يتم فيها إنشاء خطة تعليمية، يجب أن تكون مهمة دراسية ديناميكية.
تهانينا على إكمال نظام الوكلاء المتعدّدين في Aidemy. اكتسبت خبرة عملية وإحصاءات قيّمة حول ما يلي:
- فوائد أنظمة الوكلاء المتعدّدين، بما في ذلك الوحدات والقابلية للتوسّع والتخصص والصيانة المبسّطة
- أهمية البنى المستندة إلى الأحداث لإنشاء تطبيقات متجاوبة ومترابطة بشكل فضفاض
- الاستخدام الاستراتيجي للنماذج اللغوية الكبيرة، ومطابقة النموذج المناسب للمهمة ودمج هذه النماذج مع الأدوات لتحقيق تأثير في العالم الواقعي
- ممارسات التطوير المتوافقة مع السحابة الإلكترونية باستخدام خدمات Google Cloud لإنشاء حلول قابلة للتطوير وموثوقة
- أهمية مراعاة خصوصية البيانات ونماذج الاستضافة الذاتية كبديل لحلول المورّدين
لديك الآن أساس متين لإنشاء تطبيقات متقدّمة مستندة إلى الذكاء الاصطناعي على Google Cloud.
15. التحديات والخطوات التالية
تهانينا على إنشاء نظام متعدّد الوكلاء في Aidemy. لقد وضعت أساسًا قويًا للتعليم المستنِد إلى الذكاء الاصطناعي. لنطّلِع الآن على بعض التحديات والتحسينات المستقبلية المحتملة لتوسيع إمكانات هذه الميزة ومعالجة الاحتياجات الواقعية:
التعلم التفاعلي من خلال جلسات الأسئلة والأجوبة المباشرة:
- التحدي: هل يمكنك الاستفادة من واجهة برمجة التطبيقات Live API في Gemini 2 لإنشاء ميزة "أسئلة وأجوبة" في الوقت الفعلي للطلاب؟ تخيَّل صفًا افتراضيًا يمكن للطلاب فيه طرح الأسئلة والحصول على إجابات فورية مستندة إلى الذكاء الاصطناعي.
إرسال المهام وتقييمها تلقائيًا:
- التحدي: تصميم وتنفيذ نظام يتيح للطلاب إرسال الواجبات الرقمية وتقييمها تلقائيًا باستخدام الذكاء الاصطناعي (AI)، مع توفير آلية لرصد السرقة الأدبية ومنع حدوثها يقدّم هذا التحدي فرصة رائعة لاستكشاف الإنشاء المعزّز لاسترداد المعلومات (RAG) لتحسين دقة عمليات التقييم ورصد السرقة الأدبية وموثوقيتها.
16. تَنظيم
بعد أن أنشأنا واستكشَفنا نظام موظّفي الدعم المتعدّدين في Aidemy، حان وقت تنظيف بيئة Google Cloud.
- حذف خدمات Cloud Run
gcloud run services delete aidemy-planner --region=us-central1 --quiet
gcloud run services delete aidemy-portal --region=us-central1 --quiet
gcloud run services delete courses-agent --region=us-central1 --quiet
gcloud run services delete book-provider --region=us-central1 --quiet
gcloud run services delete assignment-agent --region=us-central1 --quiet
- حذف مشغِّل Eventarc
gcloud eventarc triggers delete portal-assignment-trigger --location=us --quiet
gcloud eventarc triggers delete plan-topic-trigger --location=us-central1 --quiet
gcloud eventarc triggers delete portal-assignment-trigger --location=us-central1 --quiet
ASSIGNMENT_AGENT_TRIGGER=$(gcloud eventarc triggers list --project="$PROJECT_ID" --location=us-central1 --filter="name:assignment-agent" --format="value(name)")
COURSES_AGENT_TRIGGER=$(gcloud eventarc triggers list --project="$PROJECT_ID" --location=us-central1 --filter="name:courses-agent" --format="value(name)")
gcloud eventarc triggers delete $ASSIGNMENT_AGENT_TRIGGER --location=us-central1 --quiet
gcloud eventarc triggers delete $COURSES_AGENT_TRIGGER --location=us-central1 --quiet
- حذف موضوع Pub/Sub
gcloud pubsub topics delete plan --project="$PROJECT_ID" --quiet
- حذف مثيل Cloud SQL
gcloud sql instances delete aidemy --quiet
- حذف مستودع Artifact Registry
gcloud artifacts repositories delete agent-repository --location=us-central1 --quiet
- حذف أسرار "مدير الأسرار"
gcloud secrets delete db-user --quiet
gcloud secrets delete db-pass --quiet
gcloud secrets delete db-name --quiet
- حذف مثيل Compute Engine (إذا تم إنشاؤه لخدمة Deepseek)
gcloud compute instances delete ollama-instance --zone=us-central1-a --quiet
- حذف قاعدة جدار الحماية لمثيل Deepseek
gcloud compute firewall-rules delete allow-ollama-11434 --quiet
- حذف حِزم Cloud Storage
export COURSE_BUCKET_NAME=$(gcloud storage buckets list --format="value(name)" | grep aidemy-recap)
export ASSIGNMENT_BUCKET=$(gcloud storage buckets list --format="value(name)" | grep aidemy-assignment)
gsutil rb gs://$COURSE_BUCKET_NAME
gsutil rb gs://$ASSIGNMENT_BUCKET