1. מבוא
שלום! אז, אתם אוהבים את הרעיון של סוכני תמיכה – עוזרים קטנים שיכולים לבצע משימות בשבילכם בלי שתצטרכו להרים אצבע, נכון? מדהים! אבל בואו נודה בזה: סוכן אחד לא תמיד מספיק, במיוחד כשאתם מטפלים בפרויקטים גדולים ומורכבים יותר. סביר להניח שתצטרכו צוות שלם שלהם! כאן נכנסות לתמונה מערכות עם סוכנים מרובים.
כשסוכנים פועלים באמצעות מודלים של LLM, הם מספקים גמישות מדהימה בהשוואה לתכנות קשיחה (hard coding) מהדור הקודם. אבל, כמו תמיד, יש לכך גם חסרונות. וזה בדיוק מה שנעשה בסדנה הזו!
זה מה שתוכלו ללמוד – אפשר לחשוב על זה כעל שדרוג של רמת הסוכן:
יצירת הסוכן הראשון באמצעות LangGraph: נשתמש ב-LangGraph, מסגרת פופולרית, כדי ליצור סוכן משלכם. תלמדו איך ליצור כלים שמתחברים למסדי נתונים, להשתמש ב-Gemini 2 API העדכני ביותר כדי לבצע חיפוש באינטרנט ולבצע אופטימיזציה של ההנחיות והתשובות, כדי שהסוכן יוכל לקיים אינטראקציה לא רק עם מודלים של LLM, אלא גם עם שירותים קיימים. בנוסף, נראה איך קוראים לפונקציות.
ניהול סוכנים בדרך שלכם: נלמד על דרכים שונות לניהול הסוכנים, החל מנתיב ישר ופשוט ועד לתרחישי נתיב מרובים מורכבים יותר. אפשר לחשוב על זה כעל ניהול התהליך של צוות הנציגים.
מערכות עם מספר סוכנים: תלמדו איך להגדיר מערכת שבה הסוכנים יכולים לשתף פעולה ולבצע משימות ביחד – והכול בזכות ארכיטקטורה מבוססת-אירועים.
LLM Freedom – שימוש ב-LLM המתאים ביותר למשימה: אנחנו לא מוגבלים ל-LLM אחד בלבד! נלמד איך להשתמש במספר מודלים של LLM, להקצות להם תפקידים שונים כדי לשפר את יכולת פתרון הבעיות באמצעות 'מודלים של חשיבה' מגניבים.
תוכן דינמי? אין בעיה!: נניח שהסוכן שלכם יוצר תוכן דינמי מותאם אישית לכל משתמש, בזמן אמת. אנחנו נסביר לכם איך לעשות את זה.
מעבר ל-Cloud באמצעות Google Cloud: אפשר לשכוח מהמשחקים במחברת. נלמד איך לתכנן ולפרוס מערכת עם כמה סוכנים ב-Google Cloud כדי שהיא תהיה מוכנה לעולם האמיתי.
הפרויקט הזה יהיה דוגמה טובה לשימוש בכל השיטות שדיברנו עליהן.
2. ארכיטקטורה
להיות מורה או לעבוד בתחום החינוך יכול להיות מאוד מתגמל, אבל בואו נודה בזה: עומס העבודה, במיוחד כל עבודת ההכנה, יכול להיות מאתגר. בנוסף, לרוב אין מספיק מורים והלימודים יכולים להיות יקרים. לכן אנחנו מציעים עוזר הוראה מבוסס-AI. הכלי הזה יכול להקל על אנשי החינוך ולעזור לגשר על הפער שנוצר בגלל מחסור בכוח אדם והיעדר שיעורי תגבור במחירים סבירים.
העוזר הדיגיטלי למורים מבוסס-ה-AI יכול ליצור מערכי שיעור מפורטים, חידונים מהנים, סיכומי אודיו פשוטים להבנה ומטלות מותאמות אישית. כך המורים יכולים להתמקד במה שהם עושים הכי טוב: ליצור חיבור עם התלמידים ולעזור להם להתאהב בלמידה.
למערכת יש שני אתרים: אחד למורים, שבו הם יכולים ליצור תוכניות לימוד לשבועות הקרובים,
וכן קבוצה לתלמידים, שבה הם יכולים לגשת לבחינות, לסיכומי אודיו ולמטלות.
עכשיו נלמד על הארכיטקטורה שמניעה את מערכת העזרה שלנו, Aidemy. כפי שאפשר לראות, פירטנו את התהליך לכמה רכיבים מרכזיים, שעובדים יחד כדי שהתהליך יתבצע.
אלמנטים וטכנולוגיות ארכיטקטוניים מרכזיים:
Google Cloud Platform (GCP): רכיב מרכזי בכל המערכת:
- Vertex AI: גישה ל-LLM של Gemini מבית Google.
- Cloud Run: פלטפורמה ללא שרת (serverless) לפריסה של סוכנים ופונקציות בקונטיינרים.
- Cloud SQL: מסד נתונים של PostgreSQL לנתוני תוכנית הלימודים.
- Pub/Sub ו-Eventarc: הבסיס של הארכיטקטורה מבוססת-האירועים, שמאפשרת תקשורת אסינכרונית בין רכיבים.
- אחסון בענן: אחסון של סיכומי אודיו וקובצי מטלות.
- Secret Manager: ניהול מאובטח של פרטי הכניסה למסדי נתונים.
- Artifact Registry: אחסון קובצי אימג' של Docker לנציגים.
- Compute Engine: כדי לפרוס LLM באירוח עצמי במקום להסתמך על פתרונות של ספקים
LLM: המוח של המערכת:
- מודלים של Gemini מבית Google: (Gemini 1.0 Pro, Gemini 2 Flash, Gemini 2 Flash Thinking, Gemini 1.5-pro) משמשים לתכנון שיעורים, ליצירת תוכן, ליצירת HTML דינמי, להסבר על בחינות ותרגילים ולשילוב המטלות.
- DeepSeek: נעשה בו שימוש במשימה המיוחדת של יצירת מטלות למידה עצמית
LangChain ו-LangGraph: מסגרות לפיתוח אפליקציות של LLM
- מאפשרת ליצור תהליכי עבודה מורכבים עם כמה סוכנים.
- מאפשרת תזמור חכם של כלים (קריאות ל-API, שאילתות למסדי נתונים, חיפושים באינטרנט).
- הטמעת ארכיטקטורה מבוססת-אירועים כדי לשפר את הגמישות והמדרגיות של המערכת.
בעיקרון, הארכיטקטורה שלנו משלבת את העוצמה של LLM עם נתונים מובְנים ותקשורת מבוססת-אירועים, והכול פועל ב-Google Cloud. כך אנחנו יכולים ליצור כלי עזר למורים שאפשר להתאים לצרכים השונים, שהוא מהימן ויעיל.
3. לפני שמתחילים
בדף לבחירת הפרויקט במסוף Google Cloud, בוחרים או יוצרים פרויקט ב-Google Cloud. הקפידו לוודא שהחיוב מופעל בפרויקט שלכם ב-Cloud. כך בודקים אם החיוב מופעל בפרויקט
👉לוחצים על Activate Cloud Shell בחלק העליון של מסוף Google Cloud (הסמל בצורת מסוף בחלק העליון של חלונית Cloud Shell), לוחצים על הלחצן 'Open Editor' (הוא נראה כמו תיקייה פתוחה עם עיפרון). ייפתח בחלון Cloud Shell Code Editor. בצד ימין יופיע חלון של 'סייר קבצים'.
👉לוחצים על הלחצן Cloud Code Sign-in (כניסה ל-Cloud Code) בסרגל הסטטוס התחתון, כפי שמוצג. מאשרים את הפלאגין לפי ההוראות. אם מופיע הכיתוב Cloud Code - no project בסרגל הסטטוס, בוחרים בו ואז בתפריט הנפתח 'Select a Google Cloud Project' (בחירת פרויקט ב-Google Cloud), ובוחרים את הפרויקט הספציפי ב-Google Cloud מתוך רשימת הפרויקטים שיצרתם.
👉פותחים את מסוף ה-IDE בענן,
👉בטרמינל, מוודאים שכבר בוצע אימות ושהמזהה של הפרויקט מוגדר כפרויקט באמצעות הפקודה הבאה:
gcloud auth list
👉ואז מריצים את הפקודה:
gcloud config set project <YOUR_PROJECT_ID>
👉מריצים את הפקודה הבאה כדי להפעיל את ממשקי Google Cloud API הנדרשים:
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 בסביבת הפיתוח המשולבת (IDE) של Cloud Shell
לוחצים על הלחצן Code Assist בחלונית הימנית כפי שמוצג, ובוחרים בפעם האחרונה את הפרויקט הנכון ב-Google Cloud. אם תתבקשו להפעיל את Cloud AI Companion API, עליכם לעשות זאת ולהמשיך. אחרי שבוחרים את הפרויקט ב-Google Cloud, מוודאים שהוא מופיע בהודעת הסטטוס של Cloud Code בסרגל הסטטוס, וש-Code Assist מופעל גם הוא בצד שמאל, בסרגל הסטטוס, כפי שמוצג בהמשך:
הגדרת הרשאה
👉הגדרת הרשאה לחשבון שירות
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"
👉Secret Manager (קריאה):
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/secretmanager.secretAccessor"
👉אימות התוצאה במסוף IAM
4. פיתוח הסוכן הראשון
לפני שנצלול למערכות מורכבות עם כמה סוכנים, צריך להגדיר אבן בניין בסיסית: סוכן פונקציונלי יחיד. בקטע הזה נתחיל ליצור סוכן פשוט מסוג 'ספק ספרים'. הסוכן של ספק הספרים מקבל קטגוריה כקלט ומשתמש ב-LLM של Gemini כדי ליצור ספר ייצוג בפורמט JSON בקטגוריה הזו. לאחר מכן, המערכת מציגה את ההמלצות לספרים כנקודת קצה של API ל-REST .
👉בכרטיסייה אחרת בדפדפן, פותחים את מסוף Google Cloud בדפדפן האינטרנט. בתפריט הניווט (☰), עוברים אל Cloud Run. לוחצים על הלחצן '+ ... כתיבת פונקציה'.
👉בשלב הבא נגדיר את ההגדרות הבסיסיות של פונקציית Cloud Run:
- שם השירות:
book-provider
- אזור:
us-central1
- סביבת זמן ריצה:
Python 3.12
- אימות:
Allow unauthenticated invocations
למצב מופעל.
👉משאירים את שאר ההגדרות לפי ברירת המחדל ולוחצים על Create. תועברו לעורך קוד המקור.
יוצגו קבצים main.py
ו-requirements.txt
מאוכלסים מראש.
main.py
יכיל את הלוגיקה העסקית של הפונקציה, ו-requirements.txt
יכיל את החבילות הנדרשות.
👉עכשיו אנחנו מוכנים לכתוב קוד. אבל לפני שנתחיל, נבדוק אם Gemini Code Assist יכול לתת לנו יתרון ראשוני. חוזרים ל-Cloud Shell Editor, לוחצים על הסמל של 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
לאחר מכן, Code Assist ייצור פתרון אפשרי, ויספק גם את קוד המקור וגם קובץ תלות requirements.txt.
מומלץ להשוות בין הקוד שנוצר על ידי Code Assist לבין הפתרון הנכון והמאומת שמופיע בהמשך. כך תוכלו להעריך את מידת האפקטיביות של הכלי ולזהות אי-התאמות אפשריות. אף פעם לא כדאי לסמוך על מודלים של שפה גדולה בעיניים עצומות, אבל אפשר להשתמש בתכונה 'עזרה בקוד' ככלי מצוין ליצירת אב טיפוס מהיר וליצירת מבני קוד ראשוניים, וזה כדאי לעשות כדי לקבל יתרון ראשוני.
מכיוון שמדובר בסדנה, נמשיך עם הקוד המאומת שמופיע בהמשך. עם זאת, מומלץ להתנסות בקוד שנוצר על ידי Code Assist בזמן הפנוי שלכם כדי להבין טוב יותר את היכולות והמגבלות שלו.
👉חוזרים לעורך קוד המקור של פונקציית 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
👉לוחצים על SAVE AND DEPLOY כדי לפרוס את הפונקציה. ממתינים לסיום תהליך הפריסה. הסטטוס יוצג במסוף Cloud. התהליך עשוי להימשך כמה דקות.
👉אחרי הפריסה, חוזרים ל-Cloud Shell Editor, מריצים את הטרמינל:
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. חשוב לוודא שאתם נמצאים ב-Cloud Shell Editor. בהרצה בטרמינל,
git clone https://github.com/weimeilin79/aidemy-bootstrap.git
אחרי הרצת הפקודה הזו, תיווצר תיקייה חדשה בשם aidemy-bootstrap
בסביבת Cloud Shell.
בחלונית Explorer של Cloud Shell Editor (בדרך כלל בצד ימין), אמורה להופיע עכשיו התיקייה שנוצרה כששכפלתם את מאגר Git aidemy-bootstrap
. פותחים את תיקיית השורש של הפרויקט ב-Explorer. תמצאו בה תיקיית משנה planner
, פותחים גם אותה.
נתחיל ליצור את הכלים שבהם הנציגים שלנו ישתמשו כדי לעזור לכם באמת. כידוע, מודלים גדולים של שפה (LLM) מצטיינים ביכולת לחשוב ולייצר טקסט, אבל הם זקוקים לגישה למקורות מידע חיצוניים כדי לבצע משימות בעולם האמיתי ולספק מידע מדויק ועדכני. אפשר לחשוב על הכלים האלה כ'סכין הצ'וויצ'רית' של הסוכן, שמאפשרת לו לקיים אינטראקציה עם העולם.
כשמפתחים סוכן, קל להשתמש בהרבה פרטים בקידוד. כך נוצר סוכן שלא גמיש. במקום זאת, היצירה של כלים והשימוש בהם מעניקים לסוכנות גישה למערכת או ללוגיקת צד שלישי, ומאפשרים לה ליהנות מהיתרונות של LLM וגם מהיתרונות של תכנות רגילה.
בקטע הזה ניצור את הבסיס לסוכן התכנון, שבו המורים ישתמשו כדי ליצור תוכניות לימוד. לפני שהסוכן יתחיל ליצור תוכנית, אנחנו רוצים להגדיר גבולות על ידי מתן פרטים נוספים על הנושא והנושא המשני. נבנה שלושה כלים:
- קריאה ל-API מסוג Restful: אינטראקציה עם ממשק API קיים כדי לאחזר נתונים.
- שאילתת מסד נתונים: אחזור נתונים מובְנים ממסד נתונים של Cloud SQL.
- חיפוש Google: גישה למידע בזמן אמת מהאינטרנט.
אחזור המלצות על ספרים מ-API
קודם כל, נוצר כלי לאחזור המלצות על ספרים מ-API של book-provider שפרוסנו בקטע הקודם. הדגמה של האופן שבו סוכן יכול להשתמש בשירותים קיימים.
ב-Cloud Shell Editor, פותחים את הפרויקט 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): הפונקציה הזו מקבלת שאילתת משתמש כקלט.
- אינטראקציה עם LLM: המערכת משתמשת ב-LLM כדי לחלץ את הקטגוריה מהשאילתה. הדוגמה הזו ממחישה איך אפשר להשתמש ב-LLM כדי ליצור פרמטרים לכלים.
- קריאה ל-API: המערכת שולחת בקשת POST ל-API של ספק הספרים, ומעבירה את הקטגוריה ואת מספר הספרים הרצוי.
👉כדי לבדוק את הפונקציה החדשה הזו, מגדירים את משתנה הסביבה ומריצים את הפקודה :
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 שמכילה המלצות לספרים שאוחזרו מ-API של ספק הספרים.
[{"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"}]
אם מופיע הכיתוב הזה, הכלי הראשון פועל כמו שצריך.
במקום ליצור קריאה מפורשת ל-API ל-REST עם פרמטרים ספציפיים, אנחנו משתמשים בשפה טבעית ('אני עושה קורס…'). לאחר מכן, הנציג מחלץ באופן חכם את הפרמטרים הנדרשים (כמו הקטגוריה) באמצעות עיבוד שפה טבעית, ומדגיש איך הנציג משתמש בהבנת שפה טבעית כדי לקיים אינטראקציה עם ה-API.
👉מסירים את קוד הבדיקה הבא מה-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. אמורה להופיע מכונה של Cloud SQL בשם aidemy
. לוחצים על שם המכונה כדי להציג את הפרטים שלה. בדף הפרטים של המכונה של Cloud SQL, לוחצים על 'SQL Studio' בתפריט הניווט הימני. תיפתח כרטיסייה חדשה.
לוחצים כדי להתחבר למסד הנתונים. כניסה ל-SQL Studio
בוחרים את aidemy-db
בתור מסד הנתונים. מזינים postgres
בתור user ו-1234qwer
בתור password.
👉עורכים את קוד ה-SQL הבא בעורך השאילתות של SQL Studio ומדביקים אותו:
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
ומוסיף נתוני דוגמה. לוחצים על Run כדי להריץ את קוד ה-SQL. אמורה להופיע הודעה המאשרת שהפקודות בוצעו בהצלחה.
👉מרחיבים את ה-Explorer, מחפשים את הטבלה שנוצרה לאחרונה ולוחצים על query. אמורה להיפתח כרטיסייה חדשה של עורך עם קוד SQL שנוצר בשבילכם.
SELECT * FROM
"public"."curriculums" LIMIT 1000;
👉לוחצים על הפעלה.
בטבלת התוצאות אמורות להופיע שורות הנתונים שהוספת בשלב הקודם, וכך תוכלו לוודא שהטבלה והנתונים נוצרו בצורה תקינה.
עכשיו, אחרי שיצרתם מסד נתונים עם נתונים לדוגמה של תוכנית הלימודים, נבנה כלי לאחזור הנתונים.
👉עורכים את הקובץ curriculums.py
בתיקייה aidemy-bootstrap
ב-Cloud Code Editor ומדביקים את הקוד הבא:
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 Connector כדי ליצור חיבור מאובטח למסד הנתונים.
- 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 משפר את היכולות של הנציגים על ידי מתן תוצאות חיפוש מדויקות יותר ורלוונטיות יותר להקשר. כך הנציגים יכולים לגשת למידע עדכני ולבסס את התשובות שלהם על נתונים מהעולם האמיתי, וכך לצמצם את ההזיות. שילוב ה-API המשופר מאפשר גם להשתמש בשאילתות בשפה טבעית יותר, וכך נציגי התמיכה יכולים לנסח בקשות חיפוש מורכבות ומפורטות.
הפונקציה הזו מקבלת כקלט שאילתה לחיפוש, תוכנית לימודים, מקצוע ותאריך, ומשתמשת ב-Gemini API ובכלי החיפוש של Google כדי לאחזר מידע רלוונטי מהאינטרנט. אם תבחינו, הקוד משתמש ב-Google Generative AI SDK כדי לבצע קריאות לפונקציות בלי להשתמש במסגרת אחרת.
👉עורכים את הקובץ 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. זהו שלב קריטי שמתרחש בתהליך ההסקה של ה-LLM. הדגם הוכשר לזהות מצבים שבהם נדרשים כלים חיצוניים. אם המודל מחליט להשתמש בכלי GoogleSearch, הקריאה בפועל מטופלת על ידי Google Generative AI SDK. ערכת ה-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
מעולה! עכשיו יש לכם שלושה כלים חזקים לסוכן התכנון: מחבר API, מחבר למסד נתונים וכלי לחיפוש Google. הכלים האלה יאפשרו לסוכנים לגשת למידע וליכולות הנדרשים כדי ליצור תוכניות הוראה יעילות.
7. תזמור באמצעות LangGraph
עכשיו, אחרי שיצרנו את הכלים הנפרדים, הגיע הזמן לתזמור אותם באמצעות LangGraph. כך נוכל ליצור סוכן 'תכנון' מתוחכם יותר שיכול להחליט בצורה חכמה באילו כלים להשתמש ומתי, על סמך הבקשה של המשתמש.
LangGraph היא ספריית Python שנועדה להקל על פיתוח אפליקציות עם מצב (stateful) ועם שחקנים מרובים באמצעות מודלים גדולים של שפה (LLM). אפשר לחשוב עליו כעל מסגרת לתזמור של שיחות מורכבות ותהליכי עבודה שכוללים מודלים LLM, כלים וסוכנויות אחרות.
מושגים מרכזיים:
- מבנה תרשים: LangGraph מייצג את הלוגיקה של האפליקציה כתרשים מנוהל. כל צומת בתרשים מייצג שלב בתהליך (למשל, קריאה ל-LLM, הפעלת כלי, בדיקה מותנית). קצוות מגדירים את תהליך הביצוע בין הצמתים.
- מצב: LangGraph מנהל את המצב של האפליקציה בזמן שהיא נעה בתרשים. המצב הזה יכול לכלול משתנים כמו הקלט של המשתמש, התוצאות של קריאות לכלי, פלטים ביניים מ-LLMs וכל מידע אחר שצריך לשמור בין השלבים.
- צומתים: כל צומת מייצג חישוב או אינטראקציה. הם יכולים להיות:
- צומתי כלים: שימוש בכלי (למשל, ביצוע חיפוש באינטרנט, שליחת שאילתה למסד נתונים)
- Function Nodes: הפעלת פונקציית 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"])}
הפונקציה הזו אחראית על קבלת המצב הנוכחי של השיחה, מתן הודעת מערכת ל-LLM ואז בקשה ל-LLM ליצור תשובה. ה-LLM יכול להשיב ישירות למשתמש או להשתמש באחד מהכלים הזמינים.
tools : הרשימה הזו מייצגת את קבוצת הכלים שזמינים לנציג. הוא מכיל שלוש פונקציות של כלים שהגדרנו בשלבים הקודמים: get_curriculum
, search_latest_resource
ו-recommend_book
. llm.bind_tools(tools): הפונקציה 'מקשרת' את רשימת הכלים לאובייקט llm. קישור הכלים מאפשר ל-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. הוא מייצג את תהליך העבודה של הסוכן כתרשים, שבו כל צומת בתרשים מייצג שלב בתהליך. אפשר לחשוב על זה כעל הגדרת התוכנית שלפיה הסוכן יתנהל ויפעל.- קצה מותנה: מגיע מהצומת
"determine_tool"
, והארגומנטtools_condition
הוא כנראה פונקציה שקובעת באיזה קצה להמשיך על סמך הפלט של הפונקציהdetermine_tool
. קצוות מותנים מאפשרים לתרשים להסתעף על סמך ההחלטה של ה-LLM לגבי הכלי שבו יש להשתמש (או אם להשיב למשתמש ישירות). כאן נכנסת לתמונה 'האינטליגנציה' של הסוכן – הוא יכול להתאים את ההתנהגות שלו באופן דינמי בהתאם לנסיבות. - לולא: הוספת צלע לתרשים שמחברת את הצומת
"tools"
בחזרה לצומת"determine_tool"
. כך נוצרת לולאה בתרשים, שמאפשרת לסוכנות להשתמש בכלים שוב ושוב עד שהיא אוספת מספיק מידע כדי להשלים את המשימה ולספק תשובה מספקת. הלולאה הזו חיונית למשימות מורכבות שדורשות כמה שלבים של חשיבה ואיסוף מידע.
עכשיו נבדוק את הסוכן של הכלי לתכנון כדי לראות איך הוא מרכז את הכלים השונים.
הקוד הזה יפעיל את הפונקציה prep_class עם קלט ספציפי של משתמש, וי simulte בקשה ליצירת תוכנית לימודים למתמטיקה בגיאומטריה לכיתה ה', באמצעות תוכנית הלימודים, המלצות לספרים ומקורות המידע העדכניים ביותר באינטרנט.
אם סגרתם את מסוף ה-CLI או שמשתני הסביבה כבר לא מוגדרים, מריצים מחדש את הפקודות הבאות:
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")
אם סגרתם את מסוף ה-CLI או שמשתני הסביבה כבר לא מוגדרים, מריצים מחדש את הפקודות הבאות:
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. כך המורים יוכלו לתקשר עם הנציג באמצעות ממשק מוכר מבוסס-טופס. אינטראקציות עם צ'אטבוט הן נפוצות במודלים מסוג LLM, אבל אנחנו בוחרים בממשק משתמש מסורתי לשליחת טפסים, כי הוא עשוי להיות אינטואיטיבי יותר עבור מורים רבים.
אם סגרתם את מסוף ה-CLI או שמשתני הסביבה כבר לא מוגדרים, מריצים מחדש את הפקודות הבאות:
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"
אמורה להופיע ההודעה Created 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. קובץ האימג' aidemy-planner
אמור להופיע במאגר agent-repository
.
אבטחת פרטי הכניסה למסד הנתונים באמצעות Secret Manager
כדי לנהל את פרטי הכניסה למסד הנתונים ולגשת אליהם בצורה מאובטחת, נשתמש ב-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=-
השימוש ב-Secret Manager הוא שלב חשוב באבטחת האפליקציה ובמניעת חשיפת פרטי כניסה רגישים בטעות. הוא פועל בהתאם לשיטות המומלצות לאבטחה בפריסות בענן.
פריסה ב-Cloud Run
Cloud Run היא פלטפורמה מנוהלת ללא שרת (serverless) שמאפשרת לפרוס אפליקציות בקונטיינרים במהירות ובקלות. הוא מספק ניהול תשתית מופשט, ומאפשר לכם להתמקד בכתיבת הקוד ובפריסתו. אנחנו נפרוס את התכנון שלנו כשירות ב-Cloud Run.
👉במסוף Google Cloud, עוברים אל Cloud Run. לוחצים על DEPLOY CONTAINER ובוחרים באפשרות SERVICE. מגדירים את שירות Cloud Run:
- תמונה בקונטיינר: לוחצים על 'בחירה' בשדה כתובת ה-URL. מחפשים את כתובת ה-URL של התמונה שהועברה אל Artifact Registry (למשל, us-central1-docker.pkg.dev/YOUR_PROJECT_ID/agent-repository/agent-planner/YOUR_IMG).
- שם השירות:
aidemy-planner
- אזור: בוחרים את האזור
us-central1
. - אימות: לצורך הסדנה הזו, אפשר לאפשר את האפשרות 'הפעלות ללא אימות'. בסביבת הייצור, מומלץ להגביל את הגישה.
- הכרטיסייה קונטיינרים (מרחיבים את האפשרויות 'קונטיינרים' ו'רשת'):
- הכרטיסייה 'הגדרות':
- משאב
- זיכרון : 2GB
- משאב
- בכרטיסייה 'משתנים וסודות':
- משתני סביבה:
- מוסיפים את השם:
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
בשדה Add-on Request (בקשה להוספה). כך נוכל לספק לנציג את ההקשר הנדרש כדי ליצור מערך שיעור מותאם אישית.
מעולה! יצרתם בהצלחה תוכנית לימודים באמצעות הסוכן החזק שלנו מבוסס-ה-AI. זה מראה את הפוטנציאל של הסוכנויות לצמצם באופן משמעותי את עומס העבודה ולייעל את המשימות, וכך לשפר את היעילות ולייעל את החיים של אנשי החינוך.
9. מערכות עם מספר סוכנים
עכשיו, אחרי שהטמענו את הכלי ליצירת תוכנית לימודים, נתמקד בבניית פורטל התלמידים. בפורטל הזה התלמידים יקבלו גישה לבחינים, לסיכומי אודיו ולמטלות שקשורות לעבודות שלהם. בהתאם להיקף הפונקציונליות הזו, נשתמש ביכולות של מערכות עם סוכנים מרובים כדי ליצור פתרון מודולרי שניתן להתאמה.
כפי שציינו קודם, במקום להסתמך על סוכן יחיד שיטפל בכל הדברים, מערכת עם כמה סוכנים מאפשרת לנו לפצל את עומס העבודה למשימות קטנות יותר ומתמחות, שכל אחת מהן מטופלת על ידי סוכן ייעודי. לגישה הזו יש כמה יתרונות מרכזיים:
מודולריות ותחזוקה: במקום ליצור סוכן אחד שמבצע את כל הפעולות, כדאי ליצור סוכנים קטנים יותר ומיוחדים עם תחומי אחריות מוגדרים היטב. המודולריות הזו מאפשרת להבין את המערכת בקלות רבה יותר, ולבצע בה תחזוקה וניפוי באגים. כשמתעוררת בעיה, אפשר לבודד אותה לסוכנות ספציפית, במקום לסנן קוד בסיס עצום.
יכולת התאמה: התאמה של סוכן מורכב אחד יכולה להיות צוואר בקבוק. מערכת עם כמה סוכני תמיכה מאפשרת לכם להתאים את התמיכה לכל סוכן בהתאם לצרכים הספציפיים שלו. לדוגמה, אם סוכן אחד מטפל במספר גדול של בקשות, אפשר להפעיל בקלות עוד מכונות של אותו סוכן בלי להשפיע על שאר המערכת.
התמחות צוותית: אפשר להסתכל על זה כך: לא תבקשו מהנדס אחד ליצור אפליקציה שלמה מאפס. במקום זאת, כדאי לאסוף צוות של מומחים, כל אחד מהם בעל מומחיות בתחום מסוים. באופן דומה, מערכת עם כמה סוכנים מאפשרת לכם לנצל את נקודות החוזקה של כלים ומודלים גדולים שונים של שפה, ולהקצות אותם לסוכנים שמתאימים ביותר למשימות ספציפיות.
פיתוח במקביל: צוותים שונים יכולים לעבוד על סוכנים שונים בו-זמנית, וכך לזרז את תהליך הפיתוח. מאחר שהסוכנים עצמאיים, סביר להניח ששינויים בסוכנות אחת לא ישפיעו על סוכנויות אחרות.
ארכיטקטורה מבוססת-אירועים
כדי לאפשר תקשורת ותיאום יעילים בין הסוכנויות האלה, נשתמש בארכיטקטורה מבוססת-אירועים. המשמעות היא שהסוכנים יגיבו ל'אירועים' שמתרחשים במערכת.
סוכני תמיכה נרשמים לסוגי אירועים ספציפיים (למשל, "התוכנית לימוד נוצרה", "הוגדר מטלה"). כשאירוע מתרחש, הנציגים הרלוונטיים מקבלים הודעה ויכולים להגיב בהתאם. הפרדה כזו מעודדת גמישות, יכולת התאמה לעומס ותגובה בזמן אמת.
עכשיו, כדי להתחיל, אנחנו צריכים דרך לשדר את האירועים האלה. לשם כך, נגדיר נושא Pub/Sub. נתחיל ביצירת נושא בשם plan.
👉נכנסים אל Pub/Sub במסוף Google Cloud ולוחצים על הלחצן 'Create Topic' (יצירת נושא).
👉מגדירים את הנושא עם המזהה/השם plan
ומבטלים את הסימון של Add a default subscription
, משאירים את שאר ההגדרות כברירת מחדל ולוחצים על Create.
הדף של Pub/Sub יתעדכן, והנושא החדש שיצרתם אמור להופיע בטבלה.
עכשיו נשתמש ב-Pub/Sub כדי לשלב את הפונקציונליות של פרסום אירועים בסוכנות התכנון שלנו. נוסיף כלי חדש ששולח אירוע 'תוכנית' לנושא Pub/Sub שיצרנו עכשיו. האירוע הזה יאותת לגורמים אחרים במערכת (כמו אלה בפורטל התלמידים) שתוכנית לימוד חדשה זמינה.
👉חוזרים ל-Cloud Code Editor ופותחים את הקובץ 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. עידוד התלמידים באמצעות בחנים על פי דרישה
נסו לדמיין סביבה לימודית שבה לתלמידים יש גישה למלאי אינסופי של חידונים שמותאמים לתוכניות הלמידה הספציפיות שלהם. החידונים האלה מספקים משוב מיידי, כולל תשובות והסברים, שמקדמים הבנה עמוקה יותר של החומר. זהו הפוטנציאל שאנחנו שואפים לממש באמצעות פורטל החידונים מבוסס-ה-AI שלנו.
כדי להפוך את החזון הזה למציאות, נבנה רכיב ליצירת חידונים שיכול ליצור שאלות אמריקאיות על סמך התוכן של תוכנית הלימודים.
👉בחלונית Explorer של Cloud Code Editor, עוברים לתיקייה 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 שמיועד במיוחד להבנת הפלט של ה-LLM וליצירת מבנה שלו. הוא משתמש במודל QuizQuestion
שהגדרנו קודם כדי לוודא שהפלט המנותח תואם לפורמט הנכון (שאלה, אפשרויות ותשובה).
👉מריצים את הפקודות הבאות בטרמינל כדי להגדיר סביבה וירטואלית, להתקין יחסי תלות ולהפעיל את הסוכן:
cd ~/aidemy-bootstrap/portal/
python -m venv env
source env/bin/activate
pip install -r requirements.txt
python app.py
משתמשים בתכונה תצוגה מקדימה באינטרנט של Cloud Shell כדי לגשת לאפליקציה שפועלת. לוחצים על הקישור 'חידונים' בסרגל הניווט העליון או בכרטיס בדף האינדקס. אמורים להופיע שלושה חידונים שנוצרו באופן אקראי לתלמידים. הבחינות האלה מבוססות על תוכנית הלימודים וממחישות את העוצמה של מערכת הבחינות מבוססת-ה-AI שלנו.
כדי להפסיק את התהליך שפועל באופן מקומי, לוחצים על Ctrl+C
במסוף.
Gemini 2 Thinking להסברים
אוקיי, יש לנו בחינות, וזה התחלה טובה. אבל מה קורה אם התלמידים טועים במשהו? שם מתרחשת הלמידה האמיתית, נכון? אם נוכל להסביר להם למה התשובה שלהם שגויה ואיך להגיע לתשובה הנכונה, סביר להניח שהם יזכרו אותה. בנוסף, זה עוזר להם להבין את הדברים ולהרגיש בטוחים יותר.
לכן אנחנו נעזרים בכלי החזק ביותר: המודל 'חשיבה' של Gemini 2! אפשר לחשוב על זה כעל מתן זמן נוסף ל-AI לחשוב על הדברים לפני שהוא מסביר. כך הוא יכול לספק משוב מפורט יותר וטוב יותר.
אנחנו רוצים לבדוק אם הוא יכול לעזור לתלמידים על ידי מתן עזרה, תשובות והסברים מפורטים. כדי לבדוק את זה, נתחיל בנושא קשה במיוחד: חשבון אינפיניטסימלי.
👉קודם כל, עוברים ל-Cloud Code Editor, ב-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.
כך משנים את ה-LLM שמסיק יותר מסקנות, וכך הוא יכול ליצור הסברים טובים יותר. ומריצים אותו שוב.
👉מפעילים את מודל החשיבה החדש כדי לבדוק אותו:
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
בסדר! תצטרכו לעדכן ולפרוס מחדש את סוכני התכנון והפורטל שלנו ב-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
👉נבצע את אותם השלבים, נתכנן את קובץ האימג' של סוכן portal ונדחו אותו:
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
ב-Artifact Registry, קובצי האימג' של הקונטיינרים 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 לסוכן portal
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
- התעלמות מההודעה (Permission denied on ‘locations/me-central2' (or it may not exist).)
לוחצים על 'יצירה'.
הדף 'טריגרים של 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 לשימוש במגוון מידע כדי ליצור סיכומים אודיו של תוכניות ההוראה שלנו. כך התלמידים יוכלו לקבל דרך נוחה ומעניינת לבדוק את החומר, ויכול להיות שהם יבינו אותו טוב יותר ויזכרו אותו יותר טוב באמצעות הלמידה השמיעתית.
אנחנו צריכים מקום לאחסון קובצי האודיו שנוצרים. Cloud Storage מספק פתרון אמין שניתן להתאמה.
👉נכנסים לקטע אחסון במסוף. לוחצים על 'קטגוריות' בתפריט הימני. לוחצים על הלחצן '+ יצירה' בחלק העליון.
👉מגדירים את הקטגוריה:
- שם הקטגוריה: aidemy-recap-<UNIQUE_NAME> חשוב: חשוב להגדיר שם ייחודי לקטגוריה שמתחיל ב-"aidemy-recap-". השם הייחודי הזה חיוני כדי למנוע התנגשויות בשמות בזמן יצירת הקטגוריה ב-Cloud Storage.
- region:
us-central1
. - סוג האחסון: 'Standard'. Standard מתאים לנתונים שניגשים אליהם לעיתים קרובות.
- בקרת גישה: משאירים את ברירת המחדל 'בקרת גישה אחידה' מסומנת. כך אפשר להגדיר בקרת גישה עקבית ברמת הקטגוריה.
- אפשרויות מתקדמות: בדרך כלל ההגדרות שמוגדרות כברירת מחדל מספיקות לצורך הסדנה הזו. לוחצים על הלחצן CREATE כדי ליצור את הקטגוריה.
יכול להיות שיופיע חלון קופץ לגבי מניעת גישה ציבורית. משאירים את התיבה מסומנת ולוחצים על 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"
👉ב-Cloud Code Editor, פותחים את 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. בניגוד לקריאה רגילה ל-API שבה שולחים בקשה ומקבלים תשובה, החיבור הזה נשאר פתוח לצורך החלפה רציפה של נתונים.
- הגדרה רב-מודאלית: אפשר להשתמש בהגדרה כדי לציין את סוג הפלט הרצוי (במקרה הזה, אודיו), ואפילו לציין את הפרמטרים שבהם רוצים להשתמש (למשל, בחירת קול, קידוד אודיו)
- עיבוד אסינכרוני: ממשק ה-API הזה פועל באופן אסינכרוני, כלומר הוא לא חוסם את ה-thread הראשי בזמן ההמתנה להשלמת היצירה של האודיו. עיבוד הנתונים בזמן אמת ושליחת הפלט בקטעים מספקים חוויה כמעט מיידית.
עכשיו, השאלה החשובה היא: מתי צריך להריץ את תהליך יצירת האודיו? אנחנו רוצים שהסיכומים הקוליים יהיו זמינים ברגע שיוצרים תוכנית לימוד חדשה. מאחר שכבר הטמענו ארכיטקטורה מבוססת-אירועים על ידי פרסום תוכנית הלימודים בנושא Pub/Sub, אנחנו יכולים פשוט להירשם לנושא הזה.
עם זאת, אנחנו לא יוצרים תוכניות הוראה חדשות לעיתים קרובות. לא יעיל להפעיל סוכן כל הזמן ולחכות לתוכניות חדשות. לכן, הגיוני מאוד לפרוס את הלוגיקה הזו ליצירת אודיו כפונקציית Cloud Run.
כשפורסים אותו כפונקציה, הוא נשאר במצב רדום עד לפרסום הודעה חדשה בנושא Pub/Sub. במקרה כזה, הפונקציה מופעלת באופן אוטומטי, יוצרת את הסיכומים האודיו ושומרת אותם בקטגוריה שלנו.
👉בקובץ main.py
בתיקייה course
, הקובץ הזה מגדיר את פונקציית 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: ה-decorator הזה מסמן את הפונקציה כפונקציית 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 Function מאפשרת לנו לבדוק את הפונקציה באופן מקומי לפני שפורסים אותה ב-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.אמור להופיע שירות חדש בשם 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 של הסוכן לתכנון (אם היא לא זמינה, מריצים את הפקודה הבאה במסוף):
gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner
אחרי יצירת התוכנית החדשה, צריך להמתין 2-3 דקות עד ליצירת האודיו. שוב, התהליך הזה ייקח עוד כמה דקות בגלל מגבלות החיוב בחשבון המעבדה הזה.
כדי לבדוק אם פונקציית courses-agent
קיבלה את תוכנית ההוראה, אפשר לעיין בכרטיסייה 'טריגרים' של הפונקציה. כדאי לרענן את הדף מדי פעם. בסופו של דבר, הפונקציה אמורה להופיע. אם הפונקציה לא הופעל אחרי יותר מ-2 דקות, אפשר לנסות ליצור שוב את תוכנית ההוראה. עם זאת, מומלץ להימנע מיצירת תוכניות שוב ושוב ברצף מהיר, כי כל תוכנית שנוצרת תנוצל ותעובד על ידי הסוכן ברצף, ויכול להיות שהדבר יגרום ליצירת עומס עבודה.
👉נכנסים לפורטל ולוחצים על 'קורסים'. אמור להופיע שלוש כרטיסים, שבכל אחד מהם מוצג סיכום אודיו. כדי למצוא את כתובת ה-URL של הנציג בפורטל:
gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep portal
לוחצים על 'הפעלה' בכל קורס כדי לוודא שהסיכומים הקוליים תואמים לתוכנית ההוראה שיצרתם.
יוצאים מהסביבה הווירטואלית.
deactivate
13. אופציונלי: עבודה משותפת מבוססת-תפקידים עם Gemini ו-DeepSeek
חשוב מאוד לקבל כמה נקודות מבט שונות, במיוחד כשאתם יוצרים מטלות מעניינות ומעוררות מחשבה. עכשיו נבנה מערכת עם כמה סוכנים שמשתמשת בשני מודלים שונים עם תפקידים שונים כדי ליצור מטלות: אחד מקדם שיתוף פעולה והשני מעודד למידה עצמית. נשתמש בארכיטקטורה מסוג 'single-shot', שבה תהליך העבודה עוקב אחרי מסלול קבוע.
יצירת מטלות ב-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 Agent.
👉מריצים את הפקודות הבאות בטרמינל כדי להגדיר את הסביבה:
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
בפלט אמור להופיע מטלה עם יותר עבודות קבוצתיות. גם בדיקת הטענה (assert) בסוף תציג את התוצאות.
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
פלטפורמות AI מבוססות-ענן הן נוחות, אבל LLMs באירוח עצמי יכולים להיות קריטיים להגנה על פרטיות הנתונים ולהבטחת הריבונות על הנתונים. נעביר את מודל DeepSeek הקטן ביותר (1.5 מיליארד פרמטרים) למכונה של Cloud Compute Engine. יש דרכים אחרות, כמו אירוח ב-Vertex AI של Google או אירוח במכונה של GKE, אבל מכיוון שזו רק סדנה בנושא סוכני AI ואני לא רוצה להחזיק אתכם כאן לנצח, נשתמש בדרך הפשוטה ביותר. אבל אם אתם רוצים להעמיק באפשרויות אחרות, תוכלו לעיין בקובץ deepseek-vertexai.py
בתיקיית המטלה, שבו מופיע קוד לדוגמה שמראה איך לבצע אינטראקציה עם מודלים שנפרסו ב-VertexAI.
👉מריצים את הפקודה הבאה בטרמינל כדי ליצור פלטפורמת LLM (רשת LLM) מתארחת עצמאית בשם 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. ollama-instance
אמור להופיע ברשימה עם סימן וי ירוק, שמציין שהוא פועל. אם הוא לא מופיע, צריך לוודא שהתחום הוא us-central1. אם לא, יכול להיות שתצטרכו לחפש אותו.
👉נלמד להתקין את מודל DeepSeek הקטן ביותר ולבדוק אותו. חזרה ל-Cloud Shell Editor, בטרמינל חדש, מריצים את הפקודה הבאה כדי להתחבר ל-SSH במכונת GCE.
gcloud compute ssh ollama-instance --zone=us-central1-a
לאחר יצירת חיבור ה-SSH, יכול להיות שתופיע ההודעה הבאה:
"Do you want to continue (Y/n)?"
פשוט מקלידים 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 משלב את שתי המטלות האלה למטלה מקיפה אחת. מכיוון שאנחנו מגדירים מראש את רצף השלבים בלי קבלת החלטות על ידי LLM, מדובר בתזמור חד-נתיב שהוגדר על ידי משתמש.
👉מדביקים את הקוד הבא בסוף הקובץ 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 ועוברים אל Storage (אחסון) > 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 ועוברים אל Cloud Run.אמור להופיע שירות חדש בשם courses-agent.
עכשיו, אחרי שהטמענו, בדקנו ופרוסנו את תהליך העבודה ליצירת מטלות, אנחנו יכולים לעבור לשלב הבא: להפוך את המטלות האלה לנגישות בפורטל התלמידים.
14. אופציונלי: עבודה משותפת מבוססת-תפקידים עם Gemini ו-DeepSeek – המשך
יצירת אתר דינמי
כדי לשפר את פורטל התלמידים ולהפוך אותו למעניין יותר, נעביר את יצירת ה-HTML הדינמי לדפי המטלות. המטרה היא לעדכן את הפורטל באופן אוטומטי בעיצוב חדש ומרשים מבחינה חזותית בכל פעם שייווצר מטלה חדשה. כך אפשר לנצל את יכולות הקוד של LLM כדי ליצור חוויית משתמש דינמית ומעניינת יותר.
👉עורכים את הקובץ render.py
בתיקייה portal
ב-Cloud Shell Editor, מחליפים את
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 Triggers במסוף Google Cloud. הערך portal-assignment-trigger
אמור להופיע בטבלה. לוחצים על שם הטריגר כדי להציג את הפרטים שלו.
יכול להיות שיחלפו עד 2-3 דקות עד שהטריגר החדש יהפוך לפעיל.
כדי לראות את היצירה של ההקצאות הדינמיות בפעולה, מריצים את הפקודה הבאה כדי למצוא את כתובת ה-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 עם סוכנים מרובים! רכשתם ניסיון מעשי ותובנות חשובות לגבי:
- היתרונות של מערכות עם מספר סוכנים, כולל מודולריות, יכולת התאמה, התמחות ותחזוקה פשוטה יותר.
- החשיבות של ארכיטקטורות מבוססות-אירועים ליצירת אפליקציות רספונסיביות עם קישור רופף.
- שימוש אסטרטגי ב-LLM, התאמת המודל הנכון למשימה ושילוב שלהם עם כלים להשפעה בעולם האמיתי.
- שיטות פיתוח מבוססות-ענן באמצעות שירותי Google Cloud ליצירת פתרונות אמינים וניתן להתאמה.
- החשיבות של התחשבות בפרטיות הנתונים ובמודלים של אירוח עצמי כחלופה לפתרונות של ספקים.
עכשיו יש לכם בסיס יציב לפיתוח אפליקציות מתוחכמות מבוססות-AI ב-Google Cloud.
15. אתגרים והשלבים הבאים
כל הכבוד על בניית המערכת הרב-סוכנית של Aidemy! יצרתם בסיס יציב לחינוך מבוסס-AI. עכשיו נבחן כמה אתגרים ושיפורים פוטנציאליים עתידיים כדי להרחיב את היכולות שלו ולענות על הצרכים בעולם האמיתי:
למידה אינטראקטיבית עם שאלות ותשובות בשידור חי:
- האתגר: אתם יכולים להשתמש ב-Live API של Gemini 2 כדי ליצור לתלמידים תכונה של שאלות ותשובות בזמן אמת? נסו לדמיין כיתת לימוד וירטואלית שבה התלמידים יכולים לשאול שאלות ולקבל תשובות מיידיות מבוססות-AI.
הגשה וניקוד אוטומטיים של מטלות:
- האתגר: לתכנן ולהטמיע מערכת שמאפשרת לתלמידים להגיש מטלות באופן דיגיטלי ולקבל עליהן ציונים באופן אוטומטי באמצעות 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
- מחיקת סודות ב-Secret Manager
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