Aidemy: Building Multi-Agent Systems with LangGraph, EDA, and Generative AI on Google Cloud, Aidemy: Building Multi-Agent Systems with LangGraph, EDA, and Generative AI on Google Cloud

1. مقدمه

سلام! بنابراین، شما به ایده عواملی علاقه دارید - کمک های کوچکی که می توانند کارها را برای شما انجام دهند بدون اینکه شما حتی یک انگشت بلند کنید، درست است؟ عالی! اما بیایید واقعی باشیم، یک نماینده همیشه قرار نیست آن را کاهش دهد، به خصوص زمانی که در حال انجام پروژه های بزرگتر و پیچیده تر هستید. احتمالاً به یک تیم کامل از آنها نیاز خواهید داشت! اینجاست که سیستم های چند عاملی وارد می شوند.

Agent ها، زمانی که توسط LLM ها پشتیبانی می شوند، در مقایسه با کدنویسی سخت قدیمی، انعطاف پذیری باورنکردنی به شما می دهند. اما، و همیشه یک اما وجود دارد، آنها با مجموعه ای از چالش های روی حیله و تزویر همراه هستند. و این دقیقاً همان چیزی است که ما در این کارگاه به آن می پردازیم!

عنوان

در اینجا چیزی است که می توانید انتظار یادگیری داشته باشید - آن را به عنوان ارتقاء سطح بازی عامل خود در نظر بگیرید:

ساختن اولین نماینده خود با LangGraph : ما دستانمان را کثیف می کنیم تا نماینده خود را با استفاده از LangGraph، یک چارچوب محبوب، بسازیم. شما یاد خواهید گرفت که چگونه ابزارهایی ایجاد کنید که به پایگاه‌های داده متصل می‌شوند، برای جستجوی اینترنتی به آخرین API Gemini 2 ضربه بزنید و اعلان‌ها و پاسخ‌ها را بهینه کنید تا نماینده شما بتواند نه تنها با LLM‌ها بلکه با سرویس‌های موجود تعامل داشته باشد. ما همچنین به شما نشان خواهیم داد که فراخوانی تابع چگونه کار می کند.

Agent Orchestration, Your Way : ما راه های مختلفی را برای هماهنگ کردن عوامل شما، از مسیرهای مستقیم ساده تا سناریوهای چند مسیره پیچیده تر، بررسی خواهیم کرد. به آن به عنوان هدایت جریان تیم عامل خود فکر کنید.

سیستم‌های چند عاملی : خواهید فهمید که چگونه می‌توانید سیستمی را راه‌اندازی کنید که در آن نمایندگان شما بتوانند با یکدیگر همکاری کنند و کارها را با هم انجام دهند - همه اینها به لطف یک معماری رویداد محور.

آزادی LLM - از بهترین ها برای شغل استفاده کنید: ما فقط در یک LLM گیر نکرده ایم! خواهید دید که چگونه می توان از چندین LLM استفاده کرد و نقش های متفاوتی را به آنها اختصاص داد تا با استفاده از "مدل های تفکر" جذاب، قدرت حل مسئله را تقویت کنند.

محتوای پویا؟ مشکلی نیست! : تصور کنید که نماینده شما محتوای پویا ایجاد می کند که به طور خاص برای هر کاربر در زمان واقعی طراحی شده است. ما به شما نشان خواهیم داد که چگونه این کار را انجام دهید!

بردن آن به Cloud با Google Cloud : فقط بازی کردن در یک نوت بوک را فراموش کنید. ما به شما نشان خواهیم داد که چگونه سیستم چند عاملی خود را در Google Cloud طراحی و استقرار دهید تا برای دنیای واقعی آماده شود!

این پروژه نمونه خوبی از نحوه استفاده از تمام تکنیک هایی خواهد بود که در مورد آنها صحبت کردیم.

2. معماری

معلم بودن یا کار کردن در آموزش و پرورش می‌تواند بسیار مفید باشد، اما اجازه دهید با آن روبرو شویم، حجم کار، به‌ویژه تمام کارهای آمادگی، می‌تواند چالش‌برانگیز باشد! به علاوه، اغلب کارکنان کافی وجود ندارد و تدریس خصوصی ممکن است گران باشد. به همین دلیل است که ما یک دستیار آموزشی مبتنی بر هوش مصنوعی را پیشنهاد می کنیم. این ابزار می تواند بار را برای مربیان سبک کند و به پر کردن شکاف ناشی از کمبود کارکنان و نبود تدریس خصوصی مقرون به صرفه کمک کند.

دستیار آموزشی هوش مصنوعی ما می‌تواند طرح‌های درسی دقیق، آزمون‌های سرگرم‌کننده، خلاصه‌های صوتی ساده و تکالیف شخصی‌سازی شده را تنظیم کند. این به معلمان اجازه می دهد تا بر آنچه که به بهترین شکل انجام می دهند تمرکز کنند: ارتباط با دانش آموزان و کمک به آنها که عاشق یادگیری شوند.

این سیستم دو سایت دارد: یکی برای معلمان برای ایجاد برنامه های درسی برای هفته های آینده،

برنامه ریز

و یکی برای دانش آموزان برای دسترسی به آزمون ها، خلاصه های صوتی، و تکالیف. پورتال

خوب، بیایید از طریق معماری که به دستیار آموزشی ما، آیدمی، نیرو می دهد قدم بزنیم. همانطور که می بینید، ما آن را به چندین جزء کلیدی تقسیم کرده ایم، که همه با هم کار می کنند تا این اتفاق بیفتد.

معماری

عناصر و فناوری های کلیدی معماری :

Google Cloud Platform (GCP) : مرکزی برای کل سیستم:

  • Vertex AI: به Gemini LLM های گوگل دسترسی دارد.
  • Cloud Run: پلتفرم بدون سرور برای استقرار عوامل و توابع کانتینری.
  • Cloud SQL: پایگاه داده PostgreSQL برای داده های برنامه درسی.
  • Pub/Sub & Eventarc: پایه و اساس معماری رویداد محور، امکان ارتباط ناهمزمان بین اجزا را فراهم می کند.
  • Cloud Storage: خلاصه های صوتی و فایل های انتساب را ذخیره می کند.
  • مدیر مخفی: به طور ایمن اعتبار پایگاه داده را مدیریت می کند.
  • رجیستری مصنوع: تصاویر داکر را برای نمایندگان ذخیره می کند.
  • Compute Engine: برای استقرار LLM خود میزبان به جای تکیه بر راه حل های فروشنده

LLMs : "مغزهای" سیستم:

  • مدل‌های 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 Console ، در صفحه انتخاب پروژه، یک پروژه Google Cloud را انتخاب یا ایجاد کنید. مطمئن شوید که صورتحساب برای پروژه Cloud شما فعال است. با نحوه بررسی فعال بودن صورت‌حساب در پروژه آشنا شوید .

👉روی Activate Cloud Shell در بالای کنسول Google Cloud کلیک کنید (این نماد شکل ترمینال در بالای صفحه Cloud Shell است)، روی دکمه "Open Editor" کلیک کنید (به نظر می رسد یک پوشه باز با یک مداد است). با این کار ویرایشگر کد Cloud Shell در پنجره باز می شود. در سمت چپ یک فایل کاوشگر خواهید دید.

پوسته ابری

همانطور که نشان داده شده است، روی دکمه ورود به سیستم Cloud Code در نوار وضعیت پایین کلیک کنید. پلاگین را طبق دستورالعمل مجاز کنید. اگر Cloud Code - بدون پروژه را در نوار وضعیت می‌بینید، آن را در منوی کشویی «انتخاب یک پروژه Google Cloud» انتخاب کنید و سپس پروژه Google Cloud خاص را از لیست پروژه‌هایی که ایجاد کرده‌اید انتخاب کنید.

پروژه ورود

👉ترمینال را در IDE ابری باز کنید، ترمینال جدید

👉در ترمینال، با استفاده از دستور زیر بررسی کنید که قبلا احراز هویت شده اید و پروژه به ID پروژه شما تنظیم شده است:

gcloud auth list

👉و بدوید:

gcloud config set project <YOUR_PROJECT_ID>

دستور زیر را برای فعال کردن APIهای Google Cloud لازم اجرا کنید:

gcloud services enable compute.googleapis.com  \
                        storage.googleapis.com  \
                        run.googleapis.com  \
                        artifactregistry.googleapis.com  \
                        aiplatform.googleapis.com \
                        eventarc.googleapis.com \
                        sqladmin.googleapis.com \
                        secretmanager.googleapis.com \
                        cloudbuild.googleapis.com \
                        cloudresourcemanager.googleapis.com \
                        cloudfunctions.googleapis.com

این ممکن است چند دقیقه طول بکشد..

Gemini Code Assist را در Cloud Shell IDE فعال کنید

همانطور که نشان داده شده است روی دکمه Code Assist در پانل سمت چپ کلیک کنید و برای آخرین بار پروژه صحیح Google Cloud را انتخاب کنید. اگر از شما خواسته شد که Cloud AI Companion API را فعال کنید، لطفاً این کار را انجام دهید و به جلو بروید. هنگامی که پروژه Google Cloud خود را انتخاب کردید، مطمئن شوید که می‌توانید آن را در پیام وضعیت Cloud Code در نوار وضعیت مشاهده کنید و همچنین 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"

👉مدیر مخفی (بخوانید):

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

👉نتیجه را در کنسول IAM خود تأیید کنید کنسول IAM

4. ساخت اولین عامل

قبل از اینکه وارد سیستم‌های چند عاملی پیچیده شویم، باید یک بلوک ساختمانی اساسی ایجاد کنیم: یک عامل واحد و کاربردی. در این بخش، اولین گام‌های خود را با ایجاد یک عامل ساده «ارائه‌دهنده کتاب» برمی‌داریم. عامل ارائه دهنده کتاب یک دسته را به عنوان ورودی می گیرد و از Gemini LLM برای تولید یک کتاب نمایندگی JSON در آن دسته استفاده می کند. سپس این توصیه های کتاب را به عنوان نقطه پایانی REST API ارائه می کند.

ارائه دهنده کتاب

👉در یک برگه مرورگر دیگر، Google Cloud Console را در مرورگر وب خود باز کنید، در منوی پیمایش (☰)، به "Cloud Run" بروید. روی دکمه "+ ... WRITE A FUNCTION" کلیک کنید.

ایجاد تابع

👉بعد تنظیمات اصلی عملکرد 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 برگردید، روی نماد Gemini Code Assist کلیک کنید و درخواست زیر را در کادر درخواست قرار دهید: 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 سپس یک راه حل بالقوه ایجاد می کند که هم کد منبع و هم یک فایل وابستگی requirement.txt را ارائه می کند.

ما شما را تشویق می کنیم که کد تولید شده Code Assist را با راه حل آزمایش شده و صحیح ارائه شده در زیر مقایسه کنید. این به شما امکان می دهد تا کارایی ابزار را ارزیابی کنید و هرگونه اختلاف احتمالی را شناسایی کنید. در حالی که هرگز نباید کورکورانه به LLM ها اعتماد کرد، 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)

👉محتوای request.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

👉ما نقطه ورود Function را تنظیم می کنیم: recommended

03-02-function-create.png

👉روی SAVE AND DEPLOY کلیک کنید. برای استقرار تابع منتظر بمانید تا فرآیند استقرار کامل شود. Cloud Console وضعیت را نمایش می دهد. این ممکن است چند دقیقه طول بکشد.

متن جایگزین 👉پس از استقرار، به ویرایشگر پوسته ابری، در اجرای ترمینال برگردید:

export PROJECT_ID=$(gcloud config get project)
export BOOK_PROVIDER_URL=$(gcloud run services describe book-provider --region=us-central1 --project=$PROJECT_ID --format="value(status.url)")

curl -X POST -H "Content-Type: application/json" -d '{"category": "Science Fiction", "number_of_book": 2}' $BOOK_PROVIDER_URL

باید برخی از داده های کتاب را در قالب JSON نشان دهد.

[
  {"author":"Anya Sharma","bookname":"Echoes of the Singularity","publisher":"NovaLight Publishing","publishing_date":"2077-03-15"},
  {"author":"Anya Sharma","bookname":"Echoes of the Quantum Dawn","publisher":"Nova Genesis Publishing","publishing_date":"2077-03-15"}
]

تبریک می گویم! شما با موفقیت یک عملکرد Cloud Run را اجرا کردید. این یکی از خدماتی است که ما در هنگام توسعه نماینده Aidemy خود را ادغام خواهیم کرد.

5. ابزارهای ساختمان: اتصال عوامل به سرویس و داده های RESTFUL

بیایید پیش برویم و پروژه Bootstrap Skeleton را دانلود کنیم، مطمئن شوید که در ویرایشگر پوسته ابری هستید. در اجرای ترمینال،

git clone https://github.com/weimeilin79/aidemy-bootstrap.git

پس از اجرای این دستور، یک پوشه جدید به نام aidemy-bootstrap در محیط Cloud Shell شما ایجاد می شود.

در پنجره اکسپلورر ویرایشگر Cloud Shell (معمولاً در سمت چپ)، اکنون باید پوشه ای را ببینید که هنگام کلون کردن مخزن Git aidemy-bootstrap ایجاد شده است. پوشه ریشه پروژه خود را در Explorer باز کنید. یک زیر پوشه planner در آن پیدا خواهید کرد، آن را نیز باز کنید. کاوشگر پروژه

بیایید شروع به ساخت ابزارهایی کنیم که نمایندگان ما از آنها استفاده می کنند تا واقعاً مفید باشند. همانطور که می دانید، LLM ها در استدلال و تولید متن عالی هستند، اما برای انجام کارهای دنیای واقعی و ارائه اطلاعات دقیق و به روز نیاز به دسترسی به منابع خارجی دارند. این ابزارها را به عنوان "چاقوی ارتش سوئیس" مامور در نظر بگیرید که به آن توانایی تعامل با جهان را می دهد.

هنگام ساخت یک عامل، به راحتی می توان به سختی کدگذاری جزئیات زیادی گرفت. این عاملی را ایجاد می کند که انعطاف پذیر نیست. در عوض، با ایجاد و استفاده از ابزارها، عامل به منطق یا سیستم های خارجی دسترسی دارد که به آن مزایای برنامه نویسی LLM و سنتی را می دهد.

در این بخش، پایه‌ای را برای عامل برنامه‌ریز ایجاد می‌کنیم که معلمان از آن برای تولید طرح‌های درسی استفاده می‌کنند. قبل از اینکه عامل شروع به تولید یک طرح کند، می خواهیم با ارائه جزئیات بیشتر در مورد موضوع و موضوع، مرزهایی را تعیین کنیم. ما سه ابزار می سازیم:

  1. Restful API Call: تعامل با یک API از قبل موجود برای بازیابی داده ها.
  2. پرس و جو پایگاه داده: واکشی داده های ساختار یافته از پایگاه داده Cloud SQL.
  3. جستجوی گوگل: دسترسی به اطلاعات بلادرنگ از وب.

واکشی توصیه‌های کتاب از یک API

ابتدا، بیایید ابزاری ایجاد کنیم که توصیه‌های کتاب را از API ارائه‌دهنده کتابی که در بخش قبل اجرا کردیم، بازیابی می‌کند. این نشان می دهد که چگونه یک نماینده می تواند از خدمات موجود استفاده کند.

توصیه کتاب

در 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."))

توضیح:

  • rekomand_book(query: str) : این تابع پرس و جوی کاربر را به عنوان ورودی می گیرد.
  • تعامل LLM : از LLM برای استخراج دسته از پرس و جو استفاده می کند. این نشان می دهد که چگونه می توانید از LLM برای کمک به ایجاد پارامترها برای ابزارها استفاده کنید.
  • API Call : درخواست 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 RESTful با پارامترهای خاص، از زبان طبیعی استفاده می‌کنیم ("من در حال انجام یک دوره..."). سپس عامل به طور هوشمند پارامترهای لازم (مانند دسته) را با استفاده از NLP استخراج می‌کند و نشان می‌دهد که چگونه عامل از درک زبان طبیعی برای تعامل با 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."))

دریافت اطلاعات برنامه درسی از پایگاه داده

در مرحله بعد، ابزاری خواهیم ساخت که داده های برنامه درسی ساختاریافته را از پایگاه داده Cloud SQL PostgreSQL واکشی می کند. این به عامل اجازه می دهد تا به یک منبع اطلاعاتی قابل اعتماد برای برنامه ریزی درسی دسترسی پیدا کند.

db ایجاد کنید

دستورات زیر را در ترمینال اجرا کنید تا یک نمونه Cloud SQL با نام aidemy ایجاد کنید. این فرآیند ممکن است کمی طول بکشد.

gcloud sql instances create aidemy \
    --database-version=POSTGRES_14 \
    --cpu=2 \
    --memory=4GB \
    --region=us-central1 \
    --root-password=1234qwer \
    --storage-size=10GB \
    --storage-auto-increase

👉بعد، یک پایگاه داده به نام aidemy-db در نمونه جدید ایجاد کنید.

gcloud sql databases create aidemy-db \
    --instance=aidemy

بیایید نمونه موجود در Cloud SQL را در Google Cloud Console تأیید کنیم، باید یک نمونه Cloud SQL به نام aidemy در لیست مشاهده کنید. روی نام نمونه کلیک کنید تا جزئیات آن را مشاهده کنید. در صفحه جزئیات نمونه Cloud SQL، روی "SQL Studio" در منوی ناوبری سمت چپ کلیک کنید. با این کار یک تب جدید باز می شود.

برای اتصال به پایگاه داده کلیک کنید. وارد SQL Studio شوید

aidemy-db به عنوان پایگاه داده انتخاب کنید. postgres به عنوان کاربر و 1234qwer به عنوان رمز عبور وارد کنید. ورود به sql studio

👉در ویرایشگر کوئری SQL Studio، کد SQL زیر را قرار دهید:

CREATE TABLE curriculums (
    id SERIAL PRIMARY KEY,
    year INT,
    subject VARCHAR(255),
    description TEXT
);

-- Inserting detailed curriculum data for different school years and subjects
INSERT INTO curriculums (year, subject, description) VALUES
-- Year 5
(5, 'Mathematics', 'Introduction to fractions, decimals, and percentages, along with foundational geometry and problem-solving techniques.'),
(5, 'English', 'Developing reading comprehension, creative writing, and basic grammar, with a focus on storytelling and poetry.'),
(5, 'Science', 'Exploring basic physics, chemistry, and biology concepts, including forces, materials, and ecosystems.'),
(5, 'Computer Science', 'Basic coding concepts using block-based programming and an introduction to digital literacy.'),

-- Year 6
(6, 'Mathematics', 'Expanding on fractions, ratios, algebraic thinking, and problem-solving strategies.'),
(6, 'English', 'Introduction to persuasive writing, character analysis, and deeper comprehension of literary texts.'),
(6, 'Science', 'Forces and motion, the human body, and introductory chemical reactions with hands-on experiments.'),
(6, 'Computer Science', 'Introduction to algorithms, logical reasoning, and basic text-based programming (Python, Scratch).'),

-- Year 7
(7, 'Mathematics', 'Algebraic expressions, geometry, and introduction to statistics and probability.'),
(7, 'English', 'Analytical reading of classic and modern literature, essay writing, and advanced grammar skills.'),
(7, 'Science', 'Introduction to cells and organisms, chemical reactions, and energy transfer in physics.'),
(7, 'Computer Science', 'Building on programming skills with Python, introduction to web development, and cyber safety.');

این کد SQL جدولی به نام curriculums ایجاد می کند و برخی از داده های نمونه را درج می کند. برای اجرای کد SQL روی Run کلیک کنید. شما باید یک پیام تأیید را ببینید که نشان می دهد دستورات با موفقیت اجرا شده اند.

👉کاوشگر را گسترش دهید، جدول جدید ایجاد شده را پیدا کنید و روی Query کلیک کنید. باید یک تب ویرایشگر جدید با SQL ایجاد شده برای شما باز کند،

جدول انتخاب sql studio

SELECT * FROM
  "public"."curriculums" LIMIT 1000;

👉روی Run کلیک کنید.

جدول نتایج باید ردیف‌هایی از داده‌هایی را که در مرحله قبل درج کرده‌اید نشان دهد و تأیید کند که جدول و داده‌ها درست ایجاد شده‌اند.

اکنون که با موفقیت یک پایگاه داده با داده های نمونه برنامه درسی پر شده ایجاد کرده اید، ابزاری برای بازیابی آن می سازیم.

👉در ویرایشگر کد Cloud، فایل curriculums.py را در پوشه aidemy-bootstrap ویرایش کنید و کد زیر را قرار دهید:

def connect_with_connector() -> sqlalchemy.engine.base.Engine:

    db_user = os.environ["DB_USER"]
    db_pass = os.environ["DB_PASS"]
    db_name = os.environ["DB_NAME"]

    encoded_db_user = os.environ.get("DB_USER")
    print(f"--------------------------->db_user: {db_user!r}")  
    print(f"--------------------------->db_pass: {db_pass!r}") 
    print(f"--------------------------->db_name: {db_name!r}") 

    ip_type = IPTypes.PRIVATE if os.environ.get("PRIVATE_IP") else IPTypes.PUBLIC

    connector = Connector()

    def getconn() -> pg8000.dbapi.Connection:
        conn: pg8000.dbapi.Connection = connector.connect(
            instance_connection_name,
            "pg8000",
            user=db_user,
            password=db_pass,
            db=db_name,
            ip_type=ip_type,
        )
        return conn

    pool = sqlalchemy.create_engine(
        "postgresql+pg8000://",
        creator=getconn,
        pool_size=2,
        max_overflow=2,
        pool_timeout=30,  # 30 seconds
        pool_recycle=1800,  # 30 minutes
    )
    return pool



def init_connection_pool() -> sqlalchemy.engine.base.Engine:
    
    return (
        connect_with_connector()
    )

    raise ValueError(
        "Missing database connection type. Please define one of INSTANCE_HOST, INSTANCE_UNIX_SOCKET, or INSTANCE_CONNECTION_NAME"
    )

def get_curriculum(year: int, subject: str):
    """
    Get school curriculum
    
    Args:
        subject: User's request subject string
        year: User's request year int
    """
    try:
        stmt = sqlalchemy.text(
            "SELECT description FROM curriculums WHERE year = :year AND subject = :subject"
        )

        with db.connect() as conn:
            result = conn.execute(stmt, parameters={"year": year, "subject": subject})
            row = result.fetchone()
        if row:
            return row[0]  
        else:
            return None  

    except Exception as e:
        print(e)
        return None

db = init_connection_pool()

توضیح:

  • متغیرهای محیطی : کد اعتبار پایگاه داده و اطلاعات اتصال را از متغیرهای محیطی بازیابی می کند (در زیر در این مورد بیشتر توضیح می دهیم).
  • connect_with_connector() : این تابع از Cloud SQL Connector برای ایجاد یک اتصال امن به پایگاه داده استفاده می کند.
  • get_curriculum(year: int، موضوع: 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 Search برای دسترسی به اطلاعات بلادرنگ از وب استفاده می‌کند. این به نماینده کمک می کند تا به روز بماند و نتایج مرتبط را ارائه دهد.

ادغام 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 برای انجام جستجوی گوگل استفاده می کند. مدل جمینی
  • GenerateContentConfig : تعریف کنید که به ابزار GoogleSearch دسترسی داشته باشد

مدل Gemini به صورت داخلی جستجوی_متن را تحلیل می‌کند و تعیین می‌کند که آیا می‌تواند مستقیماً به سؤال پاسخ دهد یا نیاز به استفاده از ابزار GoogleSearch دارد. این یک مرحله حیاتی است که در فرآیند استدلال LLM اتفاق می افتد. این مدل برای تشخیص موقعیت هایی که ابزارهای خارجی ضروری هستند آموزش دیده است. اگر مدل تصمیم به استفاده از ابزار GoogleSearch داشته باشد، Google Generative AI SDK فراخوان واقعی را مدیریت می کند. SDK تصمیم مدل و پارامترهای تولید شده را می گیرد و به API جستجوی Google ارسال می کند. این قسمت در کد از دید کاربر پنهان می شود.

سپس مدل Gemini نتایج جستجو را در پاسخ خود ادغام می کند. می‌تواند از اطلاعات برای پاسخ به سؤال کاربر، ایجاد خلاصه یا انجام کارهای دیگر استفاده کند.

👉برای تست کد زیر را اجرا کنید:

cd ~/aidemy-bootstrap/planner/
export PROJECT_ID=$(gcloud config get project)
source env/bin/activate
python search.py

شما باید پاسخ Gemini Search API را که حاوی نتایج جستجوی مرتبط با «برنامه درسی سال پنجم ریاضیات» است، ببینید. خروجی دقیق به نتایج جستجو بستگی دارد، اما یک شی JSON با اطلاعات مربوط به جستجو خواهد بود.

اگر نتایج جستجو را می بینید، ابزار جستجوی گوگل به درستی کار می کند! ادامه دهید و اسکریپت را با فشار دادن 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 یک کتابخانه پایتون است که برای آسان‌تر ساختن برنامه‌های کاربردی چند عامله با استفاده از مدل‌های زبان بزرگ (LLM) طراحی شده است. به آن به عنوان چارچوبی برای سازماندهی مکالمات پیچیده و گردش کار شامل LLMها، ابزارها و سایر عوامل فکر کنید.

مفاهیم کلیدی:

  • ساختار گراف: LangGraph منطق برنامه شما را به عنوان یک گراف جهت دار نشان می دهد. هر گره در نمودار نشان دهنده یک مرحله از فرآیند است (به عنوان مثال، فراخوانی به LLM، فراخوانی ابزار، بررسی شرطی). لبه ها جریان اجرا را بین گره ها تعریف می کنند.
  • وضعیت: LangGraph وضعیت برنامه شما را هنگام حرکت در نمودار مدیریت می کند. این حالت می‌تواند شامل متغیرهایی مانند ورودی کاربر، نتایج فراخوانی ابزار، خروجی‌های میانی از LLMها و هر اطلاعات دیگری باشد که باید بین مراحل حفظ شود.
  • گره ها: هر گره نشان دهنده یک محاسبات یا تعامل است. آنها می توانند:
    • گره های ابزار: از ابزاری استفاده کنید (مثلاً جستجوی وب انجام دهید، از پایگاه داده پرس و جو کنید)
    • گره های تابع: یک تابع پایتون را اجرا کنید.
  • لبه ها: گره ها را به هم متصل کنید، جریان اجرا را تعریف می کند. آنها می توانند:
    • لبه های مستقیم: یک جریان ساده و بدون قید و شرط از یک گره به گره دیگر.
    • لبه های شرطی: جریان به نتیجه یک گره شرطی بستگی دارد.

لانگ گراف

ما از 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 می‌گوید که این ابزارها در دسترس هستند و اطلاعاتی در مورد نحوه استفاده از آنها در اختیار 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 است. گردش کار عامل شما را به عنوان یک نمودار نشان می دهد، جایی که هر گره در نمودار نشان دهنده مرحله ای از فرآیند است. آن را به عنوان تعریف طرحی برای چگونگی استدلال و عمل عامل در نظر بگیرید.
  • Conditional Edge: که از گره "determine_tool" منشا می گیرد، آرگومان tools_condition احتمالا تابعی است که بر اساس خروجی تابع determine_tool تعیین می کند کدام لبه را دنبال کند. یال های شرطی به گراف اجازه می دهند بر اساس تصمیم LLM در مورد اینکه از کدام ابزار استفاده شود (یا اینکه آیا مستقیماً به کاربر پاسخ دهد) منشعب شود. اینجاست که «هوش» عامل وارد عمل می‌شود – او می‌تواند رفتار خود را به صورت پویا بر اساس موقعیت تطبیق دهد.
  • حلقه: یک یال به نمودار اضافه می کند که گره "tools" را به گره "determine_tool" وصل می کند. این یک حلقه در نمودار ایجاد می کند و به عامل اجازه می دهد تا به طور مکرر از ابزارها استفاده کند تا زمانی که اطلاعات کافی برای تکمیل کار و ارائه یک پاسخ رضایت بخش را جمع آوری کند. این حلقه برای کارهای پیچیده ای که نیازمند مراحل متعدد استدلال و جمع آوری اطلاعات است، حیاتی است.

اکنون، بیایید عامل برنامه ریز خود را آزمایش کنیم تا ببینیم چگونه ابزارهای مختلف را هماهنگ می کند.

این کد تابع prep_class را با ورودی کاربر خاص اجرا می‌کند و با استفاده از برنامه درسی، توصیه‌های کتاب و آخرین منابع اینترنتی، درخواستی برای ایجاد یک طرح تدریس برای ریاضیات پایه پنجم در هندسه شبیه‌سازی می‌کند.

اگر ترمینال خود را بسته اید یا متغیرهای محیط دیگر تنظیم نشده اند، دستورات زیر را دوباره اجرا کنید

export BOOK_PROVIDER_URL=$(gcloud run services describe book-provider --region=us-central1 --project=$PROJECT_ID --format="value(status.url)")
export PROJECT_ID=$(gcloud config get project)
export INSTANCE_NAME="aidemy"
export REGION="us-central1"
export DB_USER="postgres"
export DB_PASS="1234qwer"
export DB_NAME="aidemy-db"

👈کد را اجرا کنید:

cd ~/aidemy-bootstrap/planner/
source env/bin/activate
pip install -r requirements.txt
python aidemy.py

گزارش ورود را در ترمینال تماشا کنید. شما باید شواهدی را ببینید که نشان می‌دهد نماینده هر سه ابزار (دریافت برنامه درسی مدرسه، دریافت توصیه‌های کتاب و جستجوی جدیدترین منابع) را قبل از ارائه برنامه آموزشی نهایی می‌بیند. این نشان می دهد که ارکستراسیون LangGraph به درستی کار می کند و عامل به طور هوشمندانه از تمام ابزارهای موجود برای انجام درخواست کاربر استفاده می کند.

================================ Human Message =================================

I'm doing a course for  year 5 on subject Mathematics in Geometry, , get school curriculum, and come up with few books recommendation plus  search latest resources on the internet base on the curriculum outcome. And come up with a 3 week teaching plan
================================== Ai Message ==================================
Tool Calls:
  get_curriculum (xxx)
 Call ID: xxx
  Args:
    year: 5.0
    subject: Mathematics
================================= Tool Message =================================
Name: get_curriculum

Introduction to fractions, decimals, and percentages, along with foundational geometry and problem-solving techniques.
================================== Ai Message ==================================
Tool Calls:
  search_latest_resource (xxxx)
 Call ID: xxxx
  Args:
    year: 5.0
    search_text: Geometry
    curriculum: {"content": "Introduction to fractions, decimals, and percentages, along with foundational geometry and problem-solving techniques."}
    subject: Mathematics
================================= Tool Message =================================
Name: search_latest_resource

candidates=[Candidate(content=Content(parts=[Part(.....) automatic_function_calling_history=[] parsed=None
================================== Ai Message ==================================
Tool Calls:
  recommend_book (93b48189-4d69-4c09-a3bd-4e60cdc5f1c6)
 Call ID: 93b48189-4d69-4c09-a3bd-4e60cdc5f1c6
  Args:
    query: Mathematics Geometry Year 5
================================= Tool Message =================================
Name: recommend_book

[{.....}]

================================== Ai Message ==================================

Based on the curriculum outcome, here is a 3-week teaching plan for year 5 Mathematics Geometry:

**Week 1: Introduction to Shapes and Properties**
.........

اسکریپت را با فشار دادن Ctrl+C متوقف کنید.

👉حالا کد تست را با یک اعلان متفاوت جایگزین کنید که برای فراخوانی آن به ابزارهای مختلفی نیاز دارد.

if __name__ == "__main__":
  prep_class("I'm doing a course for  year 5 on subject Mathematics in Geometry, search latest resources on the internet base on the subject. And come up with a 3 week teaching plan")

اگر ترمینال خود را بسته اید یا متغیرهای محیط دیگر تنظیم نشده اند، دستورات زیر را دوباره اجرا کنید

export BOOK_PROVIDER_URL=$(gcloud run services describe book-provider --region=us-central1 --project=$PROJECT_ID --format="value(status.url)")
export PROJECT_ID=$(gcloud config get project)
export INSTANCE_NAME="aidemy"
export REGION="us-central1"
export DB_USER="postgres"
export DB_PASS="1234qwer"
export DB_NAME="aidemy-db"

👉دوباره کد را اجرا کنید:

cd ~/aidemy-bootstrap/planner/
source env/bin/activate
python aidemy.py

این بار متوجه چه چیزی شدید؟ نماینده با کدام ابزار تماس گرفت؟ باید ببینید که عامل این بار فقط با ابزار search_latest_resource تماس می گیرد. این به این دلیل است که اعلان مشخص نمی کند که به دو ابزار دیگر نیاز دارد و LLM ما آنقدر هوشمند است که ابزارهای دیگر را فراخوانی نمی کند.

================================ Human Message =================================

I'm doing a course for  year 5 on subject Mathematics in Geometry, search latest resources on the internet base on the subject. And come up with a 3 week teaching plan
================================== Ai Message ==================================
Tool Calls:
  get_curriculum (xxx)
 Call ID: xxx
  Args:
    year: 5.0
    subject: Mathematics
================================= Tool Message =================================
Name: get_curriculum

Introduction to fractions, decimals, and percentages, along with foundational geometry and problem-solving techniques.
================================== Ai Message ==================================
Tool Calls:
  search_latest_resource (xxx)
 Call ID: xxxx
  Args:
    year: 5.0
    subject: Mathematics
    curriculum: {"content": "Introduction to fractions, decimals, and percentages, along with foundational geometry and problem-solving techniques."}
    search_text: Geometry
================================= Tool Message =================================
Name: search_latest_resource

candidates=[Candidate(content=Content(parts=[Part(.......token_count=40, total_token_count=772) automatic_function_calling_history=[] parsed=None
================================== Ai Message ==================================

Based on the information provided, a 3-week teaching plan for Year 5 Mathematics focusing on Geometry could look like this:

**Week 1:  Introducing 2D Shapes**
........
* Use visuals, manipulatives, and real-world examples to make the learning experience engaging and relevant.

اسکریپت را با فشار دادن Ctrl+C متوقف کنید. 👉 برای تمیز نگه داشتن فایل aidemy.py کد تست را حذف کنید :

if __name__ == "__main__":
  prep_class("I'm doing a course for  year 5 on subject Mathematics in Geometry, search latest resources on the internet base on the subject. And come up with a 3 week teaching plan")

اکنون که منطق عامل ما تعریف شده است، بیایید برنامه وب Flask را راه اندازی کنیم. این یک رابط مبتنی بر فرم آشنا برای معلمان برای تعامل با عامل فراهم می کند. در حالی که تعاملات چت بات در LLM ها رایج است، ما یک فرم سنتی را برای ارسال UI انتخاب می کنیم، زیرا ممکن است برای بسیاری از مربیان بصری تر باشد.

اگر ترمینال خود را بسته اید یا متغیرهای محیط دیگر تنظیم نشده اند، دستورات زیر را دوباره اجرا کنید

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.

👉از منوی "Web preview"، Preview در پورت 8080 را انتخاب کنید. Cloud Shell یک برگه یا پنجره جدید مرورگر را با پیش‌نمایش وب برنامه شما باز می‌کند.

صفحه وب

در رابط برنامه، 5 برای سال انتخاب کنید، موضوع Mathematics را انتخاب کنید و Geometry را در درخواست افزودنی تایپ کنید. محدودیت سهمیه

در حالی که منتظر پاسخ هستید، به جای خیره شدن خالی، به ترمینال Cloud Editor بروید. شما می توانید پیشرفت و هرگونه خروجی یا پیام خطای تولید شده توسط عملکرد خود را در ترمینال شبیه ساز مشاهده کنید. 😁

👉با فشار دادن Ctrl+C در ترمینال اسکریپت را متوقف کنید.

👈خروج از محیط مجازی:

deactivate

8. استقرار عامل برنامه ریز در فضای ابری

تصویر را بسازید و به رجیستری فشار دهید

نمای کلی

👉زمان استقرار آن در فضای ابری است. در ترمینال، یک مخزن مصنوعات ایجاد کنید تا تصویر داکری که می‌خواهیم بسازیم را ذخیره کنیم.

gcloud artifacts repositories create agent-repository \
    --repository-format=docker \
    --location=us-central1 \
    --description="My agent repository"

باید مخزن ایجاد شده [agent-repository] را ببینید.

دستور زیر را برای ساخت تصویر داکر اجرا کنید.

cd ~/aidemy-bootstrap/planner/
export PROJECT_ID=$(gcloud config get project)
docker build -t gcr.io/${PROJECT_ID}/aidemy-planner .

👉ما باید تصویر را دوباره تگ کنیم تا به جای GCR در آرتیفکت رجیستری میزبانی شود و تصویر تگ شده را به آرتیفکت رجیستری فشار دهیم:

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 در Google Cloud Console بروید. شما باید تصویر aidemy-planner را در مخزن agent-repository پیدا کنید. تصویر برنامه ریز Aidemy

ایمن سازی اعتبار پایگاه داده با مدیر مخفی

برای مدیریت ایمن و دسترسی به اعتبار پایگاه داده، از 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 یک پلت فرم بدون سرور کاملاً مدیریت شده است که به شما امکان می دهد برنامه های کاربردی کانتینری را به سرعت و به راحتی اجرا کنید. مدیریت زیرساخت را انتزاعی می کند و به شما امکان می دهد روی نوشتن و استقرار کد خود تمرکز کنید. ما برنامه ریز خود را به عنوان یک سرویس Cloud Run به کار خواهیم گرفت.

👉در Google Cloud Console، به " Cloud Run " بروید. روی DEPLOY CONTAINER کلیک کنید و SERVICE را انتخاب کنید. سرویس Cloud Run خود را پیکربندی کنید:

اجرای ابری

  1. تصویر کانتینر : روی «انتخاب» در قسمت URL کلیک کنید. نشانی اینترنتی تصویری را که به رجیستری Artifact فشار داده‌اید پیدا کنید (به عنوان مثال، us-central1-docker.pkg.dev/YOUR_PROJECT_ID/agent-repository/agent-planner/YOUR_IMG).
  2. نام سرویس : aidemy-planner
  3. منطقه : منطقه us-central1 را انتخاب کنید.
  4. احراز هویت : برای اهداف این کارگاه، می‌توانید «اجازه فراخوان‌های احراز هویت نشده» را مجاز کنید. برای تولید، احتمالاً می خواهید دسترسی را محدود کنید.
  5. برگه کانتینر(ها) (گسترش کانتینرها، شبکه):
    • برگه تنظیمات:
      • منبع
        • حافظه: 2 گیگابایت
    • تب متغیرها و اسرار:
      • متغیرهای محیطی:
        • اضافه کردن نام: GOOGLE_CLOUD_PROJECT و مقدار: <YOUR_PROJECT_ID>
        • اضافه کردن نام: BOOK_PROVIDER_URL و مقدار: <YOUR_BOOK_PROVIDER_FUNCTION_URL>
      • اسرار آشکار به عنوان متغیرهای محیطی:
        • اضافه کردن نام: DB_USER ، مخفی: db-user را انتخاب کنید و نسخه: latest
        • اضافه کردن نام: DB_PASS ، مخفی: db-pass انتخاب کنید و نسخه: latest
        • افزودن نام: DB_NAME ، مخفی: db-name و نسخه: latest انتخاب کنید

اگر نیاز به بازیابی YOUR_BOOK_PROVIDER_FUNCTION_URL خود دارید، دستور زیر را در ترمینال اجرا کنید:

gcloud run services describe book-provider --region=us-central1 --project=$PROJECT_ID --format="value(status.url)"

راز را تنظیم کنید

موارد دیگر را به عنوان پیش فرض بگذارید.

👉 روی CREATE کلیک کنید.

Cloud Run سرویس شما را مستقر می کند.

پس از استقرار، روی سرویس به صفحه جزئیات آن کلیک کنید، می توانید URL مستقر شده را در بالا پیدا کنید.

URL

در رابط برنامه، 7 برای سال انتخاب کنید، Mathematics به عنوان موضوع انتخاب کنید و Algebra در قسمت درخواست افزودنی وارد کنید. این موضوع زمینه لازم را برای ایجاد یک طرح درس مناسب در اختیار نماینده قرار می دهد.

تبریک می گویم! شما با استفاده از عامل قدرتمند هوش مصنوعی ما با موفقیت یک برنامه آموزشی ایجاد کردید. این نشان‌دهنده پتانسیل عوامل برای کاهش قابل توجه حجم کار و ساده‌سازی وظایف، در نهایت بهبود کارایی و آسان‌تر کردن زندگی برای مربیان است.

9. سیستم های چند عاملی

اکنون که ابزار ایجاد طرح آموزشی را با موفقیت پیاده سازی کرده ایم، بیایید تمرکز خود را به ساخت پورتال دانش آموزی معطوف کنیم. این پورتال برای دانش آموزان امکان دسترسی به آزمون ها، خلاصه های صوتی و تکالیف مربوط به درس آنها را فراهم می کند. با توجه به دامنه این عملکرد، ما از قدرت سیستم های چند عامله برای ایجاد یک راه حل مدولار و مقیاس پذیر استفاده خواهیم کرد.

همانطور که قبلاً بحث کردیم، به جای تکیه بر یک عامل واحد برای رسیدگی به همه چیز، یک سیستم چند عامله به ما امکان می دهد حجم کار را به وظایف کوچکتر و تخصصی تقسیم کنیم که هر کدام توسط یک نماینده اختصاصی انجام می شود. این رویکرد چندین مزیت کلیدی دارد:

ماژولار بودن و قابلیت نگهداری : به جای ایجاد یک عامل واحد که همه کارها را انجام می دهد، نمایندگان کوچکتر و تخصصی با مسئولیت های کاملاً تعریف شده بسازید. این ماژولار بودن سیستم را برای درک، نگهداری و اشکال زدایی آسان تر می کند. وقتی مشکلی پیش می‌آید، می‌توانید آن را به یک عامل خاص جدا کنید، به‌جای اینکه مجبور باشید یک پایگاه کد عظیم را غربال کنید.

مقیاس پذیری : مقیاس پذیری یک عامل پیچیده و منفرد می تواند یک گلوگاه باشد. با یک سیستم چند عاملی، می توانید هر یک از نمایندگان را بر اساس نیازهای خاص آنها مقیاس کنید. به عنوان مثال، اگر یک عامل حجم بالایی از درخواست ها را مدیریت کند، می توانید به راحتی نمونه های بیشتری از آن عامل را بدون تأثیر بر بقیه سیستم، چرخش کنید.

تخصص تیم : اینگونه فکر کنید: از یک مهندس نمی خواهید که یک برنامه کامل را از ابتدا بسازد. در عوض، تیمی از متخصصان را گرد هم می‌آورید که هر کدام در زمینه خاصی تخصص دارند. به طور مشابه، یک سیستم چند عاملی به شما امکان می دهد از نقاط قوت LLM ها و ابزارهای مختلف استفاده کنید و آنها را به عواملی که برای کارهای خاص مناسب هستند اختصاص دهید.

توسعه موازی : تیم های مختلف می توانند به طور همزمان روی عوامل مختلف کار کنند و روند توسعه را تسریع کنند. از آنجایی که عوامل مستقل هستند، تغییرات در یک عامل کمتر بر سایر عوامل تأثیر می گذارد.

معماری رویداد محور

برای فعال کردن ارتباط و هماهنگی مؤثر بین این عوامل ، ما از یک معماری رویداد محور استفاده خواهیم کرد. این بدان معناست که نمایندگان نسبت به "وقایع" که در سیستم اتفاق می افتد واکنش نشان می دهند.

نمایندگان در انواع رویدادهای خاص مشترک هستند (به عنوان مثال ، "برنامه تدریس تولید شده" ، "تکالیف ایجاد شده"). هنگامی که یک واقعه رخ می دهد ، به عوامل مربوطه اطلاع داده می شود و می توانند بر این اساس واکنش نشان دهند. این جداسازی باعث انعطاف پذیری ، مقیاس پذیری و پاسخگویی در زمان واقعی می شود.

نمای کلی

اکنون ، برای شروع کار ، ما به راهی برای پخش این رویدادها نیاز داریم. برای انجام این کار ، ما یک موضوع میخانه/زیر را تنظیم خواهیم کرد. بیایید با ایجاد موضوعی به نام برنامه شروع کنیم.

- به Google Cloud Console Pub/Sub بروید و روی دکمه "ایجاد موضوع" کلیک کنید.

the موضوع را با plan شناسه/Name تنظیم کنید و علامت گذاری را اضافه کنید ، Add a default subscription ، استراحت را به عنوان پیش فرض بگذارید و روی ایجاد کلیک کنید.

صفحه Pub/Sub RECRESS می کند ، و اکنون باید موضوع تازه ایجاد شده خود را در جدول مشاهده کنید. موضوع ایجاد کنید

حال ، بیایید قابلیت انتشار Pub/Sub Event را در نماینده برنامه ریز ما ادغام کنیم. ما یک ابزار جدید اضافه خواهیم کرد که یک رویداد "برنامه" را به موضوع میخانه/فرعی که اخیراً ایجاد کرده ایم ارسال می کند. این رویداد به سایر عوامل موجود در سیستم (مانند موارد موجود در پورتال دانشجویی) نشان می دهد که یک برنامه تدریس جدید در دسترس است.

- به ویرایشگر کد ابر برگردید و پرونده 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 : این عملکرد برنامه تدریس تولید شده را به عنوان ورودی در نظر می گیرد ، یک مشتری ناشر میخانه/فرعی را ایجاد می کند ، مسیر موضوع را می سازد ، برنامه تدریس را به یک رشته JSON تبدیل می کند ، پیام را به موضوع منتشر می کند.
  • لیست ابزارها : عملکرد send_plan_event به لیست ابزارها اضافه می شود و باعث می شود تا از عامل استفاده شود.

و در زیر همان پوشه ، در پرونده app.py - سریعاً به نماینده دستور می دهد تا پس از تولید برنامه تدریس ، رویداد برنامه تدریس را به موضوع میخانه/فرعی ارسال کند. جایگزین کنید

### ADD send_plan_event CALL

با موارد زیر:

send_plan_event(teaching_plan)

با افزودن ابزار SEND_PLAN_EVENT و اصلاح سریع ، ما به نماینده برنامه ریز خود این امکان را داده ایم تا رویدادها را به Pub/Sub منتشر کند ، و به سایر مؤلفه های سیستم ما اجازه می دهد تا در ایجاد برنامه های جدید تدریس واکنش نشان دهند. اکنون در بخش های زیر یک سیستم چند عامل کاربردی خواهیم داشت.

10. توانمندسازی دانشجویان با آزمونهای تقاضا

یک محیط یادگیری را تصور کنید که دانش آموزان به یک عرضه بی پایان از آزمونهای متناسب با برنامه های یادگیری خاص خود دسترسی داشته باشند. این آزمونها بازخورد فوری ، از جمله پاسخ ها و توضیحات را ارائه می دهند و باعث درک عمیق تر از مطالب می شوند. این پتانسیل ما برای باز کردن قفل با پورتال مسابقه AI ما است.

برای زنده ماندن این دیدگاه ، ما یک مؤلفه تولید مسابقه ایجاد خواهیم کرد که می تواند بر اساس محتوای برنامه تدریس ، سوالات چند گزینه ای ایجاد کند.

نمای کلی

در صفحه Explorer Cloud Editor's Explorer Pane ، به پوشه 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 را در ترمینال فشار دهید.

جمینی 2 فکر کردن برای توضیحات

خوب ، بنابراین ما آزمونهایی داریم که شروع خوبی است! اما اگر دانش آموزان مشکلی ایجاد کنند ، چه می شود؟ این جایی است که یادگیری واقعی اتفاق می افتد ، درست است؟ اگر بتوانیم توضیح دهیم که چرا پاسخ آنها خاموش است و چگونه می توان به صحیح رسید ، آنها به احتمال زیاد آن را به یاد می آورند. به علاوه ، این به پاک کردن هرگونه سردرگمی و تقویت اعتماد به نفس آنها کمک می کند.

به همین دلیل ما قصد داریم اسلحه های بزرگ را وارد کنیم: مدل "تفکر" Gemini 2! به آن فکر کنید مثل اینکه به هوش مصنوعی کمی وقت اضافی بدهید تا قبل از توضیح چیزها فکر کنید. این امکان را به شما می دهد تا بازخورد مفصل تر و بهتری ارائه دهد.

ما می خواهیم ببینیم که آیا می تواند با کمک ، پاسخ دادن و توضیح جزئیات به دانشجویان کمک کند. برای آزمایش آن ، ما با یک موضوع بسیار دشوار ، حساب کاربری ، شروع خواهیم کرد.

نمای کلی

- اول ، به ویرایشگر کد ابری بروید ، در answer.py در داخل پوشه portal جایگزین کنید

def answer_thinking(question, options, user_response, answer, region):
    return ""

با قطعه کد زیر:

def answer_thinking(question, options, user_response, answer, region):
    try:
        llm = VertexAI(model_name="gemini-2.0-flash-001",location=region)
        
        input_msg = HumanMessage(content=[f"Here the question{question}, here are the available options {options}, this student's answer {user_response}, whereas the correct answer is {answer}"])
        prompt_template = ChatPromptTemplate.from_messages(
            [
                SystemMessage(
                    content=(
                        "You are a helpful teacher trying to teach the student on question, you were given the question and a set of multiple choices "
                        "what's the correct answer. use friendly tone"
                    )
                ),
                input_msg,
            ]
        )

        prompt = prompt_template.format()
        
        response = llm.invoke(prompt)
        print(f"response: {response}")

        return response
    except Exception as e:
        print(f"Error sending message to chatbot: {e}") # Log this error too!
        return f"Unable to process your request at this time. Due to the following reason: {str(e)}"



if __name__ == "__main__":
    question = "Evaluate the limit: lim (x→0) [(sin(5x) - 5x) / x^3]"
    options = ["A) -125/6", "B) -5/3 ", "C) -25/3", "D) -5/6"]
    user_response = "B"
    answer = "A"
    region = "us-central1"
    result = answer_thinking(question, options, user_response, answer, region)

این یک برنامه Langchain بسیار ساده است که در آن مدل فلش Gemini 2 را آغاز می کند ، جایی که ما به آن دستور می دهیم که به عنوان یک معلم مفید عمل کند و توضیحاتی را ارائه دهد

- دستور زیر را در ترمینال بیان کنید:

cd ~/aidemy-bootstrap/portal/
python answer.py

شما باید خروجی مشابه نمونه ارائه شده در دستورالعمل های اصلی را ببینید. مدل فعلی ممکن است همانطور که از طریق توضیحات ارائه نمی دهد.

Okay, I see the question and the choices. The question is to evaluate the limit:

lim (x0) [(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 (x0) [(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 (x0) [ -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.

code کد آزمون زیر را از 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 استفاده کنید. روی پیوند "آزمونها" کلیک کنید ، به همه آزمونها پاسخ دهید و مطمئن شوید که حداقل یک پاسخ اشتباه دریافت کرده و روی ارسال کلیک کنید

پاسخهای تفکر

به جای اینکه در حالی که منتظر پاسخ است ، خالی خیره شوید ، به ترمینال ویرایشگر ابر تغییر دهید. می توانید پیشرفت و هرگونه پیام خروجی یا خطای ایجاد شده توسط عملکرد خود را در ترمینال شبیه ساز مشاهده کنید. 😁

برای متوقف کردن فرآیند اجرای محلی ، Ctrl+C را در ترمینال فشار دهید.

11. ارکستر مأمورین با EventArc

تاکنون ، پورتال دانشجویی بر اساس مجموعه پیش فرض برنامه های تدریس ، آزمونها را تولید کرده است. این مفید است ، اما این بدان معنی است که نماینده برنامه ریز و نماینده مسابقه پورتال ما واقعاً با یکدیگر صحبت نمی کنند. به یاد داشته باشید که چگونه ما آن ویژگی را اضافه کردیم که نماینده برنامه ریز برنامه های آموزش تازه تولید شده خود را به یک موضوع میخانه/فرعی منتشر می کند؟ اکنون وقت آن است که آن را به نماینده پورتال ما وصل کنیم!

نمای کلی

ما می خواهیم پورتال هر زمان که یک برنامه تدریس جدید ایجاد شود ، به طور خودکار محتوای مسابقه خود را به روز کند. برای انجام این کار ، ما یک نقطه پایانی در پورتال ایجاد خواهیم کرد که می تواند این برنامه های جدید را دریافت کند.

در صفحه Explorer Cloud Editor's Explorer Pane ، به پوشه portal بروید. پرونده app.py را برای ویرایش باز کنید. کد زیر را بین ## اضافه کنید کد خود را در اینجا اضافه کنید :

## 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

خوب! شما نیاز به به روزرسانی و مجدداً برنامه ریز و همزمان پورتال ما برای اجرای ابر دارید. این تضمین می کند که آنها آخرین کد را دارند و برای برقراری ارتباط از طریق رویدادها پیکربندی شده اند.

نمای کلی استقرار

- در متن ، ما دوباره به تصویر برنامه ریز بازسازی و فشار خواهیم داد ، در اجرای ترمینال:

cd ~/aidemy-bootstrap/planner/
export PROJECT_ID=$(gcloud config get project)
docker build -t gcr.io/${PROJECT_ID}/aidemy-planner .
export PROJECT_ID=$(gcloud config get project)
docker tag gcr.io/${PROJECT_ID}/aidemy-planner us-central1-docker.pkg.dev/${PROJECT_ID}/agent-repository/aidemy-planner
docker push us-central1-docker.pkg.dev/${PROJECT_ID}/agent-repository/aidemy-planner

ما همین کار را می کنیم ، تصویر عامل پورتال را می سازیم و فشار می دهیم:

cd ~/aidemy-bootstrap/portal/
export PROJECT_ID=$(gcloud config get project)
docker build -t gcr.io/${PROJECT_ID}/aidemy-portal .
export PROJECT_ID=$(gcloud config get project)
docker tag gcr.io/${PROJECT_ID}/aidemy-portal us-central1-docker.pkg.dev/${PROJECT_ID}/agent-repository/aidemy-portal
docker push us-central1-docker.pkg.dev/${PROJECT_ID}/agent-repository/aidemy-portal

در رجیستری Artifact ، باید تصاویر کانتینر aidemy-planner و aidemy-portal را مشاهده کنید.

کانتینر

در ترمینال ، این کار را اجرا کنید تا تصویر Cloud Run را برای نماینده برنامه ریز به روز کنید:

export PROJECT_ID=$(gcloud config get project)
gcloud run services update aidemy-planner \
    --region=us-central1 \
    --image=us-central1-docker.pkg.dev/${PROJECT_ID}/agent-repository/aidemy-planner:latest

شما باید خروجی مشابه این را ببینید:

OK Deploying... Done.                                                                                                                                                     
  OK Creating Revision...                                                                                                                                                 
  OK Routing traffic...                                                                                                                                                   
Done.                                                                                                                                                                     
Service [aidemy-planner] revision [aidemy-planner-xxxxx] has been deployed and is serving 100 percent of traffic.
Service URL: https://aidemy-planner-xxx.us-central1.run.app

یادداشت URL خدمات ؛ این پیوند به نماینده برنامه ریز مستقر شما است.

- این را برای ایجاد نمونه Cloud Run برای عامل پورتال ایجاد کنید

export PROJECT_ID=$(gcloud config get project)
gcloud run deploy aidemy-portal \
  --image=us-central1-docker.pkg.dev/${PROJECT_ID}/agent-repository/aidemy-portal:latest \
  --region=us-central1 \
  --platform=managed \
  --allow-unauthenticated \
  --memory=2Gi \
  --cpu=2 \
  --set-env-vars=GOOGLE_CLOUD_PROJECT=${PROJECT_ID}

شما باید خروجی مشابه این را ببینید:

Deploying container to Cloud Run service [aidemy-portal] in project [xxxx] region [us-central1]
OK Deploying new service... Done.                                                                                                                                         
  OK Creating Revision...                                                                                                                                                 
  OK Routing traffic...                                                                                                                                                   
  OK Setting IAM Policy...                                                                                                                                                
Done.                                                                                                                                                                     
Service [aidemy-portal] revision [aidemy-portal-xxxx] has been deployed and is serving 100 percent of traffic.
Service URL: https://aidemy-portal-xxxx.us-central1.run.app

یادداشت URL خدمات ؛ این پیوند به پورتال دانشجویی مستقر شما است.

ایجاد ماشه EventArc

اما این سؤال بزرگ وجود دارد: وقتی یک برنامه تازه در حال انتظار در میخانه/زیر موضوع وجود دارد ، چگونه این نقطه پایانی اطلاع داده می شود؟ اینجاست که Eventarc برای نجات روز وارد می شود!

EventArc به عنوان یک پل عمل می کند ، گوش دادن به رویدادهای خاص (مانند یک پیام جدید که به موضوع میخانه/زیر ما وارد می شود) و به طور خودکار اقدامات را در پاسخ ایجاد می کند. در مورد ما ، هنگام انتشار یک برنامه تدریس جدید ، تشخیص می دهد و سپس سیگنالی را به نقطه پایانی پورتال ما ارسال می کند ، و به آن اطلاع می دهد که زمان به روزرسانی است.

با استفاده از EventArc ارتباطات محور رویداد ، می توانیم یکپارچه عامل برنامه ریز و عامل پورتال خود را به هم وصل کنیم و یک سیستم یادگیری واقعاً پویا و پاسخگو ایجاد کنیم. این مانند داشتن یک پیام رسان هوشمند است که به طور خودکار آخرین برنامه های درسی را به مکان مناسب ارائه می دهد!

- در سر کنسول به سمت EventArc .

دکمه "+ ایجاد ماشه" را کلیک کنید.

Trigger (اصول) را پیکربندی کنید:

  • نام Trigger: plan-topic-trigger
  • انواع ماشه: منابع گوگل.
  • Providwe: Cloud Pub/Sub
  • نوع رویداد: google.cloud.pubsub.topic.v1.messagePublished
  • منطقه: us-central1 .
  • Cloud Pub/Sub Topoc: plan را انتخاب کنید
  • حساب خدمات را با roles/iam.serviceAccountTokenCreator اعطا کنید
  • مقصد رویداد: اجرای ابر
  • سرویس Cloud Run: Aidemy-Portal
  • مسیر URL سرویس: /new_teaching_plan
  • پیام را نادیده بگیرید (مجوز در "مکان ها/me-central2" رد شد (یا ممکن است وجود نداشته باشد).)

روی "ایجاد" کلیک کنید.

صفحه EventArc Triggers تازه می شود ، و اکنون باید ماشه تازه ایجاد شده خود را که در جدول ذکر شده است ، ببینید.

- درو ، به برنامه ریز دسترسی پیدا کنید و یک برنامه تدریس جدید درخواست کنید. این بار سال 5 امتحان کنید ، science موضوع با 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 ایجاد کرده اید و از معماری رویداد محور برای افزایش مقیاس پذیری و انعطاف پذیری استفاده می کنید! شما پایه و اساس محکمی را ایجاد کرده اید ، اما حتی موارد بیشتری برای کشف وجود دارد. برای عمیق تر کردن مزایای واقعی این معماری ، قدرت API Live Multimodal Gemini 2 را کشف کنید و یاد بگیرید که چگونه ارکستراسیون تک مسیر را با Langgraph پیاده سازی کنید ، احساس راحتی کنید تا به دو فصل بعدی ادامه دهید.

12. اختیاری: بازپرداختهای صوتی با جمینی

جمینی می تواند اطلاعات را از منابع مختلف ، مانند متن ، تصاویر و حتی صوتی ، درک و پردازش کند و طیف کاملی از امکانات را برای یادگیری و ایجاد محتوا باز کند. توانایی جمینی در "دیدن" ، "شنیدن" و "خواندن" واقعاً تجربه های خلاقانه و جذاب کاربر را باز می کند.

فراتر از ایجاد تصاویر یا متن ، یک گام مهم دیگر در یادگیری خلاصه و جمع بندی مؤثر است. به آن فکر کنید: چند بار یک متن ترانه جذاب را راحت تر از چیزی که در یک کتاب درسی می خوانید به یاد می آورید؟ صدا می تواند فوق العاده به یاد ماندنی باشد! به همین دلیل ما قصد داریم از قابلیت های چندمادی Gemini برای تولید برنامه های صوتی برنامه های تدریس خود استفاده کنیم. این امر به دانش آموزان روشی مناسب و جذاب برای مرور مطالب ، افزایش بالقوه حفظ و درک از طریق قدرت یادگیری شنوایی را در اختیار دانش آموزان قرار می دهد.

نمای کلی API زنده

ما به مکانی برای ذخیره پرونده های صوتی تولید شده نیاز داریم. ذخیره سازی ابری یک راه حل مقیاس پذیر و قابل اعتماد را ارائه می دهد.

به ذخیره سازی در کنسول بروید. در منوی سمت چپ روی "سطل" کلیک کنید. روی دکمه "+ ایجاد" در بالا کلیک کنید.

سطل خود را پیکربندی کنید:

  • نام سطل: Aidemy-Recap- <Lexive_Name> مهم : اطمینان حاصل کنید که یک نام سطل منحصر به فرد را که با " Aidemy-Recap- " شروع می شود ، تعریف کنید. این نام منحصر به فرد برای جلوگیری از نامگذاری درگیری ها هنگام ایجاد سطل ذخیره سازی ابری شما بسیار مهم است.
  • منطقه: us-central1 .
  • کلاس ذخیره سازی: "استاندارد". استاندارد برای داده های مکرر مناسب است.
  • کنترل دسترسی: پیش فرض "کنترل دسترسی یکنواخت" را انتخاب کنید. این کنترل دسترسی به سطح سطل را فراهم می کند.
  • گزینه های پیشرفته: برای این کارگاه ، تنظیمات پیش فرض معمولاً کافی است. برای ایجاد سطل خود روی دکمه ایجاد کلیک کنید.

ممکن است در مورد پیشگیری از دسترسی عمومی ، پاپ را ببینید. کادر را چک کنید و روی Confirm کلیک کنید.

اکنون سطل تازه ایجاد شده خود را در لیست سطل ها مشاهده خواهید کرد. نام سطل خود را به خاطر بسپارید ، بعداً به آن احتیاج دارید.

در ترمینال ویرایشگر کد ابر ، دستورات زیر را اجرا کنید تا حساب خدمات دسترسی به سطل را اعطا کنید:

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"

in در ویرایشگر کد ابر ، 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))
  • اتصال جریان : اول ، یک اتصال مداوم با نقطه پایانی API زنده برقرار می شود. بر خلاف تماس API استاندارد که در آن درخواست ارسال می کنید و پاسخی دریافت می کنید ، این اتصال برای تبادل مداوم داده ها باز است.
  • پیکربندی multimodal : از پیکربندی برای مشخص کردن نوع خروجی مورد نظر خود استفاده کنید (در این حالت ، صوتی) ، و حتی می توانید مشخص کنید که می خواهید از چه پارامترهایی استفاده کنید (به عنوان مثال ، انتخاب صدا ، رمزگذاری صوتی)
  • پردازش ناهمزمان : این API به صورت غیر همزمان کار می کند ، به این معنی که در حالی که منتظر تکمیل تولید صوتی هستیم ، موضوع اصلی را مسدود نمی کند. با پردازش داده ها در زمان واقعی و ارسال خروجی در تکه ها ، یک تجربه نزدیک به همزمان را فراهم می کند.

حال سوال اصلی این است: این روند تولید صوتی چه زمانی باید اجرا شود؟ در حالت ایده آل ، ما می خواهیم که به محض ایجاد یک برنامه تدریس جدید ، مجدداً ضبط های صوتی در دسترس باشد. از آنجا که ما قبلاً با انتشار برنامه تدریس به یک موضوع میخانه/زیر ، یک معماری مبتنی بر رویداد را پیاده سازی کرده ایم ، می توانیم به سادگی در آن موضوع مشترک شویم.

با این حال ، ما اغلب برنامه های تدریس جدید ایجاد نمی کنیم. این کارآمد نخواهد بود که یک عامل دائماً در حال اجرا و منتظر برنامه های جدید باشد. به همین دلیل است که استقرار این منطق نسل صوتی به عنوان یک تابع اجرا ابر کاملاً منطقی است.

با استفاده از آن به عنوان یک تابع ، تا زمانی که پیام جدیدی به موضوع Pub/Sub منتشر نشود ، خفته باقی می ماند. وقتی این اتفاق بیفتد ، به طور خودکار عملکرد را ایجاد می کند ، که باعث ایجاد مجدد صوتی می شود و آنها را در سطل ما ذخیره می کند.

under پوشه course در پرونده main.py ، این پرونده عملکرد Cloud Run را تعریف می کند که در صورت موجود بودن یک برنامه تدریس جدید ایجاد می شود. این طرح را دریافت می کند و تولید recap صوتی را آغاز می کند. قطعه کد زیر را به انتهای پرونده اضافه کنید.

@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

@tworks_framework.cloud_event : این دکوراتور عملکرد را به عنوان یک تابع اجرا ابر که توسط CloudEvents ایجاد می شود ، علامت گذاری می کند.

تست محلی

ما این کار را در یک محیط مجازی اجرا می کنیم و کتابخانه های لازم پایتون را برای عملکرد 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

emator عملکرد Cloud Run Emulator به ما اجازه می دهد تا قبل از استقرار آن به Google Cloud ، عملکرد خود را به صورت محلی آزمایش کنیم. با اجرای یک شبیه ساز محلی را شروع کنید:

functions-framework --target process_teaching_plan --signature-type=cloudevent --source main.py

- در حالی که شبیه ساز در حال اجرا است ، می توانید CloudEvents Test را به شبیه ساز ارسال کنید تا یک برنامه تدریس جدید منتشر شود. در یک ترمینال جدید:

دو پایانه

👉run:

  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=="
    }
  }'

به جای اینکه در حالی که منتظر پاسخ هستید ، خالی خیره شوید ، به ترمینال دیگر پوسته ابر تغییر دهید. می توانید پیشرفت و هرگونه پیام خروجی یا خطای ایجاد شده توسط عملکرد خود را در ترمینال شبیه ساز مشاهده کنید. 😁

در ترمینال 2 باید ببینید که باید OK برگردد.

شما داده ها را در سطل تأیید می کنید ، به Cloud Storage بروید و برگه "سطل" را انتخاب کنید و سپس aidemy-recap-xxx انتخاب کنید

سطل

در ترمینال که شبیه ساز را اجرا می کنید ، ctrl+c را برای خروج تایپ کنید. و ترمینال دوم را ببندید. و ترمینال دوم را ببندید. و برای خروج از محیط مجازی ، غیرفعال کنید.

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

استقرار را با Going Cloud Run در Google Cloud Console تأیید کنید. شما باید یک سرویس جدید به نام دوره های Agent ذکر شده را ببینید.

لیست اجرای ابر

برای بررسی پیکربندی ماشه ، برای مشاهده جزئیات آن ، روی سرویس Agent-Agent کلیک کنید. به برگه "محرک" بروید.

شما باید یک ماشه پیکربندی شده را برای گوش دادن به پیام های منتشر شده در موضوع برنامه مشاهده کنید.

Cloud Run Trigger

در آخر ، بیایید ببینیم که پایان آن پایان می یابد.

- در متن ، ما باید عامل پورتال را پیکربندی کنیم تا می داند پرونده های صوتی تولید شده را در کجا پیدا کند. در ترمینال اجرا کنید:

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

در هر دوره "بازی" را کلیک کنید تا اطمینان حاصل شود که recaps های صوتی با برنامه تدریس که تازه تولید کرده اید مطابقت دارد! دوره های پورتال

از محیط مجازی خارج شوید.

deactivate

13. اختیاری: همکاری مبتنی بر نقش با Gemini و Deepseek

داشتن چندین دیدگاه بسیار ارزشمند است ، به خصوص هنگام کار با تکالیف جذاب و متفکر. اکنون ما یک سیستم چند عامل ایجاد خواهیم کرد که از دو مدل مختلف با نقش های مجزا استفاده می کند تا تکالیف ایجاد کند: یکی همکاری را ارتقا می بخشد و دیگری خودآموزی را تشویق می کند. ما از یک معماری "تک شات" استفاده خواهیم کرد ، جایی که گردش کار از یک مسیر ثابت پیروی می کند.

ژنراتور تکلیف جمینی

بررسی اجمالی جمینی ما با تنظیم عملکرد جمینی برای تولید تکالیف با تأکید مشترک شروع خواهیم کرد. فایل 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 برای تولید تکالیف استفاده می کند.

ما آماده آزمایش عامل جمینی هستیم.

- این دستورات را در ترمینال برای تنظیم محیط تنظیم کنید:

cd ~/aidemy-bootstrap/assignment
export PROJECT_ID=$(gcloud config get project)
python -m venv env
source env/bin/activate
pip install -r requirements.txt

- شما می توانید برای آزمایش آن اجرا کنید:

python gemini.py

شما باید یک تکلیف را ببینید که کار گروهی بیشتری در خروجی دارد. آزمون ادعا در پایان نیز نتایج را به دست می آورد.

Here are some engaging and practical assignments for each week, designed to build progressively upon the teaching plan's objectives:

**Week 1: Exploring the World of 2D Shapes**

* **Learning Objectives Assessed:**
    * Identify and name basic 2D shapes (squares, rectangles, triangles, circles).
    * .....

* **Description:**
    * **Shape Scavenger Hunt:** Students will go on a scavenger hunt in their homes or neighborhoods, taking pictures of objects that represent different 2D shapes. They will then create a presentation or poster showcasing their findings, classifying each shape and labeling its properties (e.g., number of sides, angles, etc.). 
    * **Triangle Trivia:** Students will research and create a short quiz or presentation about different types of triangles, focusing on their properties and real-world examples. 
    * **Angle Exploration:** Students will use a protractor to measure various angles in their surroundings, such as corners of furniture, windows, or doors. They will record their measurements and create a chart categorizing the angles as right, acute, or obtuse. 
....

**Week 2: Delving into the World of 3D Shapes and Symmetry**

* **Learning Objectives Assessed:**
    * Identify and name basic 3D shapes.
    * ....

* **Description:**
    * **3D Shape Construction:** Students will work in groups to build 3D shapes using construction paper, cardboard, or other materials. They will then create a presentation showcasing their creations, describing the number of faces, edges, and vertices for each shape. 
    * **Symmetry Exploration:** Students will investigate the concept of symmetry by creating a visual representation of various symmetrical objects (e.g., butterflies, leaves, snowflakes) using drawing or digital tools. They will identify the lines of symmetry and explain their findings. 
    * **Symmetry Puzzles:** Students will be given a half-image of a symmetrical figure and will be asked to complete the other half, demonstrating their understanding of symmetry. This can be done through drawing, cut-out activities, or digital tools.

**Week 3: Navigating Position, Direction, and Problem Solving**

* **Learning Objectives Assessed:**
    * Describe position using coordinates in the first quadrant.
    * ....

* **Description:**
    * **Coordinate Maze:** Students will create a maze using coordinates on a grid paper. They will then provide directions for navigating the maze using a combination of coordinate movements and translation/reflection instructions. 
    * **Shape Transformations:** Students will draw shapes on a grid paper and then apply transformations such as translation and reflection, recording the new coordinates of the transformed shapes. 
    * **Geometry Challenge:** Students will solve real-world problems involving perimeter, area, and angles. For example, they could be asked to calculate the perimeter of a room, the area of a garden, or the missing angle in a triangle. 
....

با ctl+c متوقف شوید و برای تمیز کردن کد آزمون. کد زیر را از gemini.py حذف کنید

import unittest

class TestGenAssignmentGemini(unittest.TestCase):
    def test_gen_assignment_gemini(self):
        test_teaching_plan = "Week 1: 2D Shapes and Angles - Day 1: Review of basic 2D shapes (squares, rectangles, triangles, circles). Day 2: Exploring different types of triangles (equilateral, isosceles, scalene, right-angled). Day 3: Exploring quadrilaterals (square, rectangle, parallelogram, rhombus, trapezium). Day 4: Introduction to angles: right angles, acute angles, and obtuse angles. Day 5: Measuring angles using a protractor. Week 2: 3D Shapes and Symmetry - Day 6: Introduction to 3D shapes: cubes, cuboids, spheres, cylinders, cones, and pyramids. Day 7: Describing 3D shapes using faces, edges, and vertices. Day 8: Relating 2D shapes to 3D shapes. Day 9: Identifying lines of symmetry in 2D shapes. Day 10: Completing symmetrical figures. Week 3: Position, Direction, and Problem Solving - Day 11: Describing position using coordinates in the first quadrant. Day 12: Plotting coordinates to draw shapes. Day 13: Understanding translation (sliding a shape). Day 14: Understanding reflection (flipping a shape). Day 15: Problem-solving activities involving perimeter, area, and missing angles."
        
        initial_state = {"teaching_plan": test_teaching_plan, "model_one_assignment": "", "model_two_assigmodel_one_assignmentnment": "", "final_assignment": ""}

        updated_state = gen_assignment_gemini(initial_state)

        self.assertIn("model_one_assignment", updated_state)
        self.assertIsNotNone(updated_state["model_one_assignment"])
        self.assertIsInstance(updated_state["model_one_assignment"], str)
        self.assertGreater(len(updated_state["model_one_assignment"]), 0)
        print(updated_state["model_one_assignment"])


if __name__ == '__main__':
    unittest.main()

ژنراتور واگذاری Deepseek را پیکربندی کنید

در حالی که سیستم عامل های هوش مصنوعی مبتنی بر ابر مناسب هستند ، LLM های خود میزبان می توانند برای محافظت از حریم خصوصی داده ها و اطمینان از حاکمیت داده ها بسیار مهم باشند. ما کوچکترین مدل Deepseek (پارامترهای 1.5B) را در نمونه موتور محاسباتی ابر مستقر خواهیم کرد. روش های دیگری مانند میزبانی آن در پلت فرم Vertex AI Google یا میزبانی آن در نمونه GKE شما وجود دارد ، اما از آنجا که این فقط یک کارگاه آموزشی در مورد AI است ، و من نمی خواهم شما را برای همیشه در اینجا نگه دارم ، بیایید فقط از ساده ترین راه استفاده کنیم. اما اگر علاقه مند هستید و می خواهید به گزینه های دیگر حفر کنید ، به پرونده deepseek-vertexai.py در زیر پوشه Assignment نگاهی بیندازید ، جایی که یک کد نمونه از نحوه تعامل با مدل های مستقر در Vertexai را ارائه می دهد.

نمای کلی Deepseek

-این دستور را در ترمینال ایجاد کنید تا یک پلت فرم 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

برای تأیید نمونه موتور محاسباتی در حال اجرا است:

برای محاسبه موتور > "نمونه های VM" در کنسول Google Cloud حرکت کنید. شما باید ollama-instance با یک علامت چک سبز ذکر کنید که نشان می دهد در حال اجرا است. اگر نمی توانید آن را ببینید ، اطمینان حاصل کنید که منطقه us-central1 است. اگر اینگونه نباشد ، ممکن است نیاز به جستجوی آن داشته باشید.

لیست موتور محاسبه

ما کوچکترین مدل Deepseek را نصب می کنیم و آن را آزمایش می کنیم ، دوباره در ویرایشگر Cloud Shell ، در یک ترمینال جدید ، به دنبال دستور SSH به نمونه GCE اجرا کنیم.

gcloud compute ssh ollama-instance --zone=us-central1-a

پس از برقراری اتصال SSH ، ممکن است از موارد زیر خواسته شود:

"آیا می خواهید ادامه دهید (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 در عامل واگذاری کار خواهیم کرد تا تکالیف با تأکید کار فردی ایجاد کنیم.

👉edit 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()

test آن را با اجرا آزمایش کنید:

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.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 ارکستر می کنیم. این گردش کار از سه مرحله تشکیل شده است: اول ، مدل جمینی تکلیفی را با محوریت همکاری ایجاد می کند. دوم ، مدل Deepseek تکلیفی را با تأکید بر کار فردی ایجاد می کند. سرانجام ، جمینی این دو تکلیف را در یک تکلیف واحد و جامع ترکیب می کند. از آنجا که ما دنباله مراحل را بدون تصمیم گیری LLM پیش بینی می کنیم ، این یک ارکستراسیون تک مسیر و تعریف شده توسط کاربر است.

بررسی اجمالی Langraph

code کد زیر را تا انتهای پرونده 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 و تأیید اینکه گردش کار ترکیب جمینی و 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()

ایجاد تکالیف. png

برای اینکه تولید تکلیف به صورت خودکار و پاسخگو به برنامه های جدید تدریس انجام شود ، ما از معماری موجود محور رویداد استفاده خواهیم کرد. کد زیر یک تابع Cloud Run (Generate_Assignment) را تعریف می کند که هر زمان که یک برنامه تدریس جدید در برنامه "Pub/Sub" منتشر شود ، ایجاد می شود.

کد زیر را به انتهای 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 به صورت محلی خوب است. این امکان تکرار سریعتر و اشکال زدایی آسان تر را فراهم می کند.

ابتدا یک سطل ذخیره سازی ابری ایجاد کنید تا پرونده های تکالیف تولید شده را ذخیره کنید و به حساب خدمات دسترسی به سطل اعطا کنید. دستورات زیر را در ترمینال اجرا کنید:

👉 مهم : اطمینان حاصل کنید که یک نام منحصر به فرد را تعیین کرده اید که نام خود را با " Aidemy-Assignment " آغاز می کند. این نام منحصر به فرد برای جلوگیری از نامگذاری درگیری ها هنگام ایجاد سطل ذخیره سازی ابری شما بسیار مهم است. (جایگزین <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"

👉 👉 👉 👉 emulator عملکرد Cloud Run:

cd ~/aidemy-bootstrap/assignment
functions-framework --target generate_assignment --signature-type=cloudevent --source main.py

- در حالی که شبیه ساز در یک ترمینال در حال اجرا است ، یک ترمینال دوم را در پوسته ابر باز کنید. در این ترمینال دوم ، یک تست 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=="
    }
  }'

به جای اینکه در حالی که منتظر پاسخ هستید ، خالی خیره شوید ، به ترمینال دیگر پوسته ابر تغییر دهید. می توانید پیشرفت و هرگونه پیام خروجی یا خطای ایجاد شده توسط عملکرد خود را در ترمینال شبیه ساز مشاهده کنید. 😁

باید خوب برگردد.

برای تأیید اینکه این تکلیف با موفقیت تولید شده و ذخیره شده است ، به کنسول Google Cloud بروید و به ذخیره سازی > "ذخیره ابری" بروید. سطل aidemy-assignment را که ایجاد کرده اید انتخاب کنید. شما باید یک فایل متنی به نام assignment-{random number}.txt در سطل مشاهده کنید. برای بارگیری آن و تأیید محتوای آن ، روی فایل کلیک کنید. این تأیید می کند که یک پرونده جدید حاوی تکلیف جدیدی است که به تازگی ایجاد شده است.

12-01-Assignment-Bucket

در ترمینال که شبیه ساز را اجرا می کنید ، ctrl+c را برای خروج تایپ کنید. و ترمینال دوم را ببندید. - همه ، در ترمینال اجرای شبیه ساز ، از محیط مجازی خارج شوید.

deactivate

نمای کلی استقرار

- در متن ، ما نماینده واگذاری را به ابر مستقر خواهیم کرد

cd ~/aidemy-bootstrap/assignment
export ASSIGNMENT_BUCKET=$(gcloud storage buckets list --format="value(name)" | grep aidemy-assignment)
export OLLAMA_HOST=http://$(gcloud compute instances describe ollama-instance --zone=us-central1-a --format='value(networkInterfaces[0].accessConfigs[0].natIP)'):11434
export PROJECT_ID=$(gcloud config get project)
gcloud functions deploy assignment-agent \
 --gen2 \
 --timeout=540 \
 --memory=2Gi \
 --cpu=1 \
 --set-env-vars="ASSIGNMENT_BUCKET=${ASSIGNMENT_BUCKET}" \
 --set-env-vars=GOOGLE_CLOUD_PROJECT=${GOOGLE_CLOUD_PROJECT} \
 --set-env-vars=OLLAMA_HOST=${OLLAMA_HOST} \
 --region=us-central1 \
 --runtime=python312 \
 --source=. \
 --entry-point=generate_assignment \
 --trigger-topic=plan 

با مراجعه به Google Cloud Console ، به سمت Cloud Run ، استقرار را تأیید کنید. شما باید یک سرویس جدید به نام دوره های Agent ذکر شده را ببینید. لیست 12-03

با استفاده از گردش کار تولید تکلیف که اکنون اجرا شده و آزمایش شده و مستقر شده است ، می توانیم به مرحله بعدی حرکت کنیم: در دسترس قرار دادن این تکالیف در پورتال دانشجویی.

14. اختیاری: همکاری مبتنی بر نقش با Gemini و Deepseek - Contd.

تولید وب سایت پویا

برای تقویت پورتال دانشجویی و جذاب تر کردن آن ، ما نسل HTML پویا را برای صفحات واگذاری پیاده سازی می کنیم. هدف این است که هر زمان که یک تکلیف جدید ایجاد شود ، به طور خودکار پورتال را با یک طراحی تازه و بصری جذاب به روز کنید. این امر از قابلیت های برنامه نویسی LLM برای ایجاد یک تجربه کاربر پویاتر و جالب تر استفاده می کند.

14-01-GENERATE-HTML

ویرایشگر پوسته ابر ، پرونده render.py را در پوشه portal ویرایش کنید ، جایگزین کنید

def render_assignment_page():
    return ""

با قطعه کد زیر:

def render_assignment_page(assignment: str):
    try:
        region=get_next_region()
        llm = VertexAI(model_name="gemini-2.0-flash-001", location=region)
        input_msg = HumanMessage(content=[f"Here the assignment {assignment}"])
        prompt_template = ChatPromptTemplate.from_messages(
            [
                SystemMessage(
                    content=(
                        """
                        As a frontend developer, create HTML to display a student assignment with a creative look and feel. Include the following navigation bar at the top:
                        ```
                        <nav>
                            <a href="/">Home</a>
                            <a href="/quiz">Quizzes</a>
                            <a href="/courses">Courses</a>
                            <a href="/assignment">Assignments</a>
                        </nav>
                        ```
                        Also include these links in the <head> section:
                        ```
                        <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
                        <link rel="preconnect" href="https://fonts.googleapis.com">
                        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
                        <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500&display=swap" rel="stylesheet">

                        ```
                        Do not apply inline styles to the navigation bar. 
                        The HTML should display the full assignment content. In its CSS, be creative with the rainbow colors and aesthetic. 
                        Make it creative and pretty
                        The assignment content should be well-structured and easy to read.
                        respond with JUST the html file
                        """
                    )
                ),
                input_msg,
            ]
        )

        prompt = prompt_template.format()
        
        response = llm.invoke(prompt)

        response = response.replace("```html", "")
        response = response.replace("```", "")
        with open("templates/assignment.html", "w") as f:
            f.write(response)


        print(f"response: {response}")

        return response
    except Exception as e:
        print(f"Error sending message to chatbot: {e}") # Log this error too!
        return f"Unable to process your request at this time. Due to the following reason: {str(e)}"

از مدل Gemini برای تولید پویا HTML برای تکالیف استفاده می کند. محتوای انتساب را به عنوان ورودی می گیرد و از یک فوری برای آموزش جمینی برای ایجاد یک صفحه 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

در هنگام شروع ، نام پرونده و نام سطل را از داده های درخواست بازیابی می کند ، محتوای انتساب را از ذخیره ابری بارگیری می کند و برای تولید HTML با عملکرد render_assignment_page تماس می گیرد.

ما جلوتر می رویم و آن را به صورت محلی اجرا می کنیم:

cd ~/aidemy-bootstrap/portal
source env/bin/activate
python app.py

از منوی "پیش نمایش وب" در بالای پنجره Cloud Shell ، "پیش نمایش در پورت 8080" را انتخاب کنید. این برنامه شما را در یک برگه مرورگر جدید باز می کند. در نوار ناوبری به لینک واگذاری حرکت کنید. شما باید یک صفحه خالی را در این مرحله مشاهده کنید ، که انتظار می رود رفتار از آنجا که ما هنوز پل ارتباطی بین نماینده واگذاری و پورتال را ایجاد نکرده ایم تا به صورت پویا محتوا جمع شود.

14-02-استقرار بیش از حد

- برای ترکیب این تغییرات و استقرار کد به روز شده ، بازسازی و فشار تصویر عامل پورتال:

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"

برای تأیید اینکه ماشه با موفقیت ایجاد شده است ، به صفحه Triggers EventArc در کنسول 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

در نماینده برنامه ریز ، یک برنامه تدریس جدید ایجاد کنید.

تخصیص 13-02

بعد از چند دقیقه (برای تولید صدا ، تولید تکالیف و ارائه HTML برای تکمیل) ، به پورتال دانشجویی بروید.

link روی پیوند "انتساب" در نوار ناوبری کلیک کنید. شما باید یک تکلیف تازه ایجاد شده را با HTML تولید شده پویا مشاهده کنید. هر بار که یک برنامه تدریس ایجاد می شود ، باید یک تکلیف پویا باشد.

تخصیص 13-02

تبریک می گویم برای تکمیل سیستم چند عامل Aidemy ! شما تجربه عملی و بینش های ارزشمندی را در مورد:

  • مزایای سیستم های چند عامل ، از جمله مدولار بودن ، مقیاس پذیری ، تخصص و نگهداری ساده.
  • اهمیت معماری های رویداد محور برای ایجاد برنامه های پاسخگو و سست همراه.
  • استفاده استراتژیک از LLM ها ، تطبیق مدل مناسب با کار و ادغام آنها با ابزارهایی برای تأثیرگذاری در دنیای واقعی.
  • شیوه های توسعه ابر بومی با استفاده از خدمات Google Cloud برای ایجاد راه حل های مقیاس پذیر و قابل اعتماد.
  • The importance of considering data privacy and self-hosting models as an alternative to vendor solutions.

You now have a solid foundation for building sophisticated AI-powered applications on Google Cloud!

15. Challenges and Next Steps

Congratulations on building the Aidemy multi-agent system! You've laid a strong foundation for AI-powered education. Now, let's consider some challenges and potential future enhancements to further expand its capabilities and address real-world needs:

Interactive Learning with Live Q&A:

  • Challenge: Can you leverage Gemini 2's Live API to create a real-time Q&A feature for students? Imagine a virtual classroom where students can ask questions and receive immediate, AI-powered responses.

Automated Assignment Submission and Grading:

  • Challenge: Design and implement a system that allows students to submit assignments digitally and have them automatically graded by AI, with a mechanism to detect and prevent plagiarism. This challenge presents a great opportunity to explore Retrieval Augmented Generation (RAG) to enhance the accuracy and reliability of the grading and plagiarism detection processes.

aidemy-climb

16. Clean up

Now that we've built and explored our Aidemy multi-agent system, it's time to clean up our Google Cloud environment.

  1. Delete Cloud Run services
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
  1. Delete Eventarc trigger
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
  1. Delete Pub/Sub topic
gcloud pubsub topics delete plan --project="$PROJECT_ID" --quiet
  1. نمونه Cloud SQL را حذف کنید
gcloud sql instances delete aidemy --quiet
  1. Delete Artifact Registry repository
gcloud artifacts repositories delete agent-repository --location=us-central1 --quiet
  1. Delete Secret Manager secrets
gcloud secrets delete db-user --quiet
gcloud secrets delete db-pass --quiet
gcloud secrets delete db-name --quiet
  1. Delete Compute Engine instance (if created for Deepseek)
gcloud compute instances delete ollama-instance --zone=us-central1-a --quiet
  1. Delete the firewall rule for Deepseek instance
gcloud compute firewall-rules delete allow-ollama-11434 --quiet
  1. Delete Cloud Storage buckets
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

aidemy-broom