1. Giriş
Merhaba. Yani, sizin için işleri halletmek üzere parmağınızı bile kıpırdatmanıza gerek kalmadan yardımcı olabilecek küçük yardımcılar olan temsilciler fikrini beğeniyorsunuz, değil mi? Mükemmel! Ancak özellikle daha büyük ve karmaşık projelerle uğraşırken tek bir temsilci her zaman yeterli olmayabilir. Muhtemelen bu kişilerden oluşan bir ekibe ihtiyacınız olacak. Çoklu aracı sistemleri tam da bu noktada devreye girer.
LLM'ler tarafından desteklenen aracılar, eski usul kodlamaya kıyasla inanılmaz bir esneklik sunar. Ancak, her zaman bir "ancak" vardır. Bu tür içeriklerin kendine özgü zorlukları vardır. Bu atölye çalışmasında tam olarak bu konuyu ele alacağız.

Bu eğitimde öğreneceklerinizle ilgili kısa bir özet:
LangGraph ile İlk Temsilcinizi Oluşturma: Popüler bir çerçeve olan LangGraph'ı kullanarak kendi temsilcinizi oluşturacağız. Veritabanlarına bağlanan, internette arama yapmak için en yeni Gemini 2 API'den yararlanan ve istemleri ve yanıtları optimize eden araçlar oluşturmayı öğreneceksiniz. Böylece aracınız yalnızca LLM'lerle değil, mevcut hizmetlerle de etkileşime girebilecek. Ayrıca işlev çağrısının nasıl çalıştığını da göstereceğiz.
Aracıları İstediğiniz Gibi Düzenleyin: Basit doğrudan yollardan daha karmaşık çok senaryolara kadar aracılarınızı düzenlemenin farklı yollarını keşfedeceğiz. Bu özelliği, temsilci ekibinizin akışını yönlendirme olarak düşünebilirsiniz.
Çoklu Temsilci Sistemleri: Temsilcilerinizin işbirliği yapabileceği ve işleri birlikte tamamlayabileceği bir sistemi nasıl kuracağınızı öğreneceksiniz. Tüm bunlar, etkinliğe dayalı bir mimari sayesinde mümkün olacak.
LLM Özgürlüğü: İş için En İyisini Kullanın: Tek bir LLM'ye bağlı değiliz. Birden fazla LLM'yi nasıl kullanacağınızı ve bunlara farklı roller atayarak "düşünme modelleri" ile problem çözme gücünü nasıl artıracağınızı öğreneceksiniz.
Dinamik İçerik? Sorun değil.: Aracınızın, her kullanıcıya özel dinamik içerikleri gerçek zamanlı olarak oluşturduğunu düşünün. Nasıl yapacağınızı göstereceğiz.
Google Cloud ile buluta taşıma: Artık yalnızca not defterinde deneme yapmayı unutun. Çoklu aracı sisteminizi Google Cloud'da gerçek dünyaya hazır olacak şekilde nasıl tasarlayıp dağıtacağınızı göstereceğiz.
Bu proje, bahsettiğimiz tüm tekniklerin nasıl kullanılacağına dair iyi bir örnek olacaktır.
2. Mimari
Öğretmenlik veya eğitim alanında çalışmak çok tatmin edici olabilir ancak iş yükünün, özellikle de tüm hazırlık çalışmalarının zorlayıcı olabileceğini kabul etmeliyiz. Ayrıca, genellikle yeterli personel bulunmaz ve özel dersler pahalı olabilir. Bu nedenle, yapay zeka destekli bir öğretim asistanı öneriyoruz. Bu araç, eğitimcilerin iş yükünü azaltabilir ve personel eksikliği ile uygun fiyatlı eğitimin olmaması nedeniyle ortaya çıkan boşluğu doldurmaya yardımcı olabilir.
Yapay zeka destekli öğretim asistanımız; ayrıntılı ders planları, eğlenceli sınavlar, kolayca takip edilebilen sesli özetler ve kişiselleştirilmiş ödevler oluşturabilir. Bu sayede öğretmenler, en iyi yaptıkları işe yani öğrencilerle bağlantı kurmaya ve öğrenmeyi sevmelerine yardımcı olmaya odaklanabilir.
Sistemde iki site bulunur: Biri öğretmenlerin önümüzdeki haftalar için ders planları oluşturması,

ve öğrencilerin testlere, sesli özetlere ve ödevlere erişebileceği bir sekme. 
Şimdi de Aidemy adlı eğitim asistanımızı destekleyen mimariyi inceleyelim. Gördüğünüz gibi, bu süreci mümkün kılmak için birlikte çalışan birkaç temel bileşene ayırdık.

Temel Mimari Öğeler ve Teknolojiler:
Google Cloud Platform (GCP): Tüm sistemin merkezinde yer alır:
- Vertex AI: Google'ın Gemini LLM'lerine erişir.
- Cloud Run: Container mimarisine alınmış aracıları ve işlevleri dağıtmak için kullanılan sunucusuz platform.
- Cloud SQL: Müfredat verileri için PostgreSQL veritabanı.
- Pub/Sub ve Eventarc: Etkinlik odaklı mimarinin temelini oluşturur ve bileşenler arasında eşzamansız iletişimi sağlar.
- Cloud Storage: Sesli özetleri ve ödev dosyalarını depolar.
- Secret Manager: Veritabanı kimlik bilgilerini güvenli bir şekilde yönetir.
- Artifact Registry: Aracıların Docker görüntülerini depolar.
- Compute Engine: Satıcı çözümlerine güvenmek yerine kendi kendine barındırılan LLM'yi dağıtmak için
LLM'ler: Sistemin "beyinleri":
- Google'ın Gemini modelleri: (Gemini x Pro, Gemini x Flash, Gemini x Flash Thinking) Ders planı oluşturma, içerik üretme, dinamik HTML oluşturma, sınav açıklaması ve ödevleri birleştirme için kullanılır.
- DeepSeek: Kendi kendine çalışma ödevleri oluşturma gibi özel görevler için kullanılır.
LangChain ve LangGraph: LLM Uygulama Geliştirme Çerçeveleri
- Karmaşık çoklu aracı iş akışlarının oluşturulmasını kolaylaştırır.
- Araçların (API çağrıları, veritabanı sorguları, web aramaları) akıllı bir şekilde düzenlenmesini sağlar.
- Sistemin ölçeklenebilirliği ve esnekliği için etkinlik odaklı mimari uygular.
Mimarimiz, Google Cloud'da çalışan yapılandırılmış veriler ve etkinliğe dayalı iletişim ile LLM'lerin gücünü birleştirir. Bu sayede ölçeklenebilir, güvenilir ve etkili bir öğretim asistanı oluşturabiliriz.
3. Başlamadan önce
Google Cloud Console'daki proje seçici sayfasında bir Google Cloud projesi seçin veya oluşturun. Cloud projeniz için faturalandırmanın etkinleştirildiğinden emin olun. Bir projede faturalandırmanın etkin olup olmadığını nasıl kontrol edeceğinizi öğrenin.
Cloud Shell IDE'de Gemini Code Assist'i etkinleştirme
👉 Google Cloud Console'da Gemini Code Assist Araçları'na gidin, hüküm ve koşulları kabul ederek Gemini Code Assist'i ücretsiz olarak etkinleştirin.

İzin kurulumunu yoksayın ve bu sayfadan ayrılın.
Cloud Shell Düzenleyici'de çalışma
👉Google Cloud Console'un üst kısmında Cloud Shell'i etkinleştir'i (Cloud Shell bölmesinin üst kısmındaki terminal şeklindeki simge) tıklayın, ardından "Open Editor" (Düzenleyiciyi aç) düğmesini tıklayın (kalemli açık klasör simgesi). Bu işlem, pencerede Cloud Shell Kod Düzenleyici'yi açar. Sol tarafta bir dosya gezgini görürsünüz.

👉Gösterildiği gibi, alt durum çubuğundaki Cloud Code ile Oturum Açma düğmesini tıklayın. Eklentiyi talimatlara uygun şekilde yetkilendirin. Durum çubuğunda Cloud Code - no project (Cloud Code - proje yok) ifadesini görüyorsanız bunu seçin, ardından açılır listede "Google Cloud projesi seçin"i ve oluşturduğunuz projeler listesinden belirli bir Google Cloud projesini seçin.

👉Bulut IDE'sinde terminali açın,
veya 
👉Terminalde, aşağıdaki komutu kullanarak kimliğinizin doğrulandığını ve projenin proje kimliğinize ayarlandığını doğrulayın:
gcloud auth list
👉Ayrıca <YOUR_PROJECT_ID> kısmını proje kimliğinizle değiştirdiğinizden emin olun:
echo <YOUR_PROJECT_ID> > ~/project_id.txt
gcloud config set project $(cat ~/project_id.txt)
👉Gerekli Google Cloud API'lerini etkinleştirmek için aşağıdaki komutu çalıştırın:
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 \
cloudaicompanion.googleapis.com
Bu işlem birkaç dakika sürebilir.
İzin ayarlama
👉Hizmet hesabı iznini ayarlayın. Terminalde şunu çalıştırın :
gcloud config set project $(cat ~/project_id.txt)
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"
👉 İzin verin. Terminalde şunu çalıştırın :
#Cloud Storage (Read/Write):
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/storage.objectAdmin"
#Pub/Sub (Publish/Receive):
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 (Read/Write):
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/cloudsql.editor"
#Eventarc (Receive Events):
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 (User):
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/aiplatform.user"
#Secret Manager (Read):
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/secretmanager.secretAccessor"
👉Sonucu IAM konsolunuzda doğrulayın.
👉Terminalde aşağıdaki komutları çalıştırarak aidemy adlı bir Cloud SQL örneği oluşturun. Bu bilgiye daha sonra ihtiyacımız olacak ancak bu işlem biraz zaman alabileceğinden şimdi yapacağız.
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
4. İlk temsilciyi oluşturma
Karmaşık çoklu aracı sistemlerine geçmeden önce temel bir yapı taşı oluşturmamız gerekiyor: tek ve işlevsel bir aracı. Bu bölümde, basit bir "kitap sağlayıcı" aracı oluşturarak ilk adımlarımızı atacağız. Kitap sağlayıcı aracısı, giriş olarak bir kategori alır ve bu kategorideki bir kitabı JSON biçiminde oluşturmak için Gemini LLM'yi kullanır. Ardından, bu kitap önerilerini bir REST API uç noktası olarak sunar .

👉Başka bir tarayıcı sekmesinde, web tarayıcınızda Google Cloud Console'u açın. Gezinme menüsünde (☰) "Cloud Run"a gidin. "+ ... İŞLEV YAZ" düğmesini tıklayın.

👉Ardından, Cloud Run işlevinin temel ayarlarını yapılandıracağız:
- Hizmet adı:
book-provider - Bölge:
us-central1 - Çalışma zamanı:
Python 3.12 - Kimlik doğrulama:
Allow unauthenticated invocationsseçeneğini Etkin olarak ayarlayın.
👉Diğer ayarları varsayılan olarak bırakıp Oluştur'u tıklayın. Bu işlem sizi kaynak kodu düzenleyiciye yönlendirir.
Önceden doldurulmuş main.py ve requirements.txt dosyalarını görürsünüz.
main.py işlevin iş mantığını, requirements.txt ise gereken paketleri içerir.
👉Artık biraz kod yazmaya hazırız. Ancak ayrıntılara girmeden önce Gemini Code Assist'in bize avantaj sağlayıp sağlayamayacağına bakalım. Cloud Shell Düzenleyici'ye dönün. Üst kısımdaki Gemini Code Assist simgesini tıkladığınızda Gemini Code Assist sohbeti açılır.

👉 Aşağıdaki isteği istem kutusuna yapıştırın:
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
Kod Asistanı daha sonra hem kaynak kodunu hem de requirements.txt bağımlılık dosyasını içeren olası bir çözüm oluşturur. (BU KODU KULLANMAYIN)
Code Assist'in oluşturduğu kodu, aşağıda verilen test edilmiş ve doğru çözümle karşılaştırmanızı öneririz. Bu sayede aracın etkinliğini değerlendirebilir ve olası tutarsızlıkları belirleyebilirsiniz. LLM'lere asla körü körüne güvenilmemesi gerekse de Code Assist, hızlı prototip oluşturma ve ilk kod yapılarını oluşturma konusunda harika bir araç olabilir ve iyi bir başlangıç için kullanılmalıdır.
Bu bir atölye çalışması olduğundan, aşağıda verilen doğrulanmış kodla devam edeceğiz. Ancak özelliklerini ve sınırlamalarını daha iyi anlamak için Code Assist tarafından oluşturulan kodu kendi zamanınızda denemekten çekinmeyin.
👉Cloud Run işlevinin kaynak kodu düzenleyicisine (diğer tarayıcı sekmesinde) dönün. main.py öğesinin mevcut içeriğini aşağıdaki kodla dikkatlice değiştirin:
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-2.0-flash-lite-001")
def get_recommended_books(category):
"""
A simple book recommendation function.
Args:
category (str): category
Returns:
str: A JSON string representing the recommended books.
"""
parser = JsonOutputParser(pydantic_object=Book)
question = f"Generate a random made up book on {category} with bookname, author and publisher and publishing_date"
prompt = PromptTemplate(
template="Answer the user query.\n{format_instructions}\n{query}\n",
input_variables=["query"],
partial_variables={"format_instructions": parser.get_format_instructions()},
)
chain = prompt | llm | parser
response = chain.invoke({"query": question})
return json.dumps(response)
@functions_framework.http
def recommended(request):
request_json = request.get_json(silent=True) # Get JSON data
if request_json and 'category' in request_json and 'number_of_book' in request_json:
category = request_json['category']
number_of_book = int(request_json['number_of_book'])
elif request.args and 'category' in request.args and 'number_of_book' in request.args:
category = request.args.get('category')
number_of_book = int(request.args.get('number_of_book'))
else:
return jsonify({'error': 'Missing category or number_of_book parameters'}), 400
recommendations_list = []
for i in range(number_of_book):
book_dict = json.loads(get_recommended_books(category))
print(f"book_dict=======>{book_dict}")
recommendations_list.append(book_dict)
return jsonify(recommendations_list)
👉requirements.txt dosyasının içeriğini aşağıdakiyle değiştirin:
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
👉 İşlev giriş noktasını ayarlayacağız: recommended

👉İşlevi dağıtmak için SAVE AND DEPLOY'u (KAYDET VE DAĞIT) veya SAVE AND REDEPLOY'u (KAYDET VE YENİDEN DAĞIT) tıklayın. Dağıtım işleminin tamamlanmasını bekleyin. Durum, Cloud Console'da gösterilir. Bu işlem birkaç dakika sürebilir.
👉Dağıtım tamamlandıktan sonra Cloud Shell Düzenleyici'ye dönün ve terminalde şu komutu çalıştırın:
gcloud config set project $(cat ~/project_id.txt)
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 biçiminde bazı kitap verileri gösterilmelidir.
[
{"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"}
]
Tebrikler! Cloud Run işlevini başarıyla dağıttınız. Bu, Aidemy temsilcimizi geliştirirken entegre edeceğimiz hizmetlerden biridir.
5. Oluşturma Araçları: Aracıları RESTFUL hizmetine ve verilere bağlama
Cloud Shell Düzenleyici'de olduğunuzdan emin olarak Bootstrap Skeleton Project'i indirelim. Terminalde şu komutu çalıştırın:
git clone https://github.com/weimeilin79/aidemy-bootstrap.git
Bu komutu çalıştırdıktan sonra Cloud Shell ortamınızda aidemy-bootstrap adlı yeni bir klasör oluşturulur.
Cloud Shell Editor'ın Explorer bölmesinde (genellikle sol tarafta), Git deposunu klonladığınızda oluşturulan klasörü aidemy-bootstrap görmeniz gerekir. Gezgin'de projenizin kök klasörünü açın. Bu klasörün içinde planner alt klasörünü bulup açın. 
Temsilcilerimizin gerçekten faydalı olabilmek için kullanacağı araçları oluşturmaya başlayalım. Bildiğiniz gibi LLM'ler akıl yürütme ve metin oluşturma konusunda mükemmeldir ancak gerçek dünyadaki görevleri yerine getirmek ve doğru, güncel bilgiler sağlamak için harici kaynaklara erişmeleri gerekir. Bu araçları, dünyanın her yerinde etkileşim kurabilen bir temsilcinin "İsviçre çakısı" olarak düşünebilirsiniz.
Temsilci oluştururken çok sayıda ayrıntıyı sabit kodlamak kolaydır. Bu durum, esnek olmayan bir temsilci oluşturur. Bunun yerine, araçlar oluşturup kullanarak harici mantığa veya sistemlere erişebilir. Bu sayede hem LLM'nin hem de geleneksel programlamanın avantajlarından yararlanabilir.
Bu bölümde, öğretmenlerin ders planı oluşturmak için kullanacağı planlayıcı aracının temelini oluşturacağız. Aracı plan oluşturmaya başlamadan önce, konu ve başlık hakkında daha fazla bilgi vererek sınırları belirlemek istiyoruz. Üç araç oluşturacağız:
- Restful API Çağrısı: Verileri almak için önceden var olan bir API ile etkileşim kurma.
- Veritabanı Sorgusu: Yapılandırılmış verileri Cloud SQL veritabanından getirme.
- Google Arama: Web'deki anlık bilgilere erişme
API'den Kitap Önerileri Getirme
Öncelikle, önceki bölümde dağıttığımız book-provider API'den kitap önerileri alan bir araç oluşturalım. Bu, bir aracının mevcut hizmetlerden nasıl yararlanabileceğini gösterir.

Cloud Shell Düzenleyici'de, önceki bölümde klonladığınız aidemy-bootstrap projesini açın.
👉planner klasöründeki book.py dosyasını düzenleyin ve aşağıdaki kodu dosyanın sonuna yapıştırın:
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."))
Açıklama:
- recommend_book(query: str): Bu işlev, kullanıcının sorgusunu giriş olarak alır.
- LLM Etkileşimi: Sorgudan kategoriyi çıkarmak için LLM'yi kullanır. Bu örnekte, araçlar için parametre oluşturmaya yardımcı olması amacıyla LLM'yi nasıl kullanabileceğiniz gösterilmektedir.
- API Çağrısı: Kitap sağlayıcı API'sine bir POST isteği göndererek kategoriyi ve istenen kitap sayısını iletir.
👉Bu yeni işlevi test etmek için ortam değişkenini ayarlayın ve şu komutu çalıştırın :
gcloud config set project $(cat ~/project_id.txt)
export PROJECT_ID=$(gcloud config get project)
cd ~/aidemy-bootstrap/planner/
export BOOK_PROVIDER_URL=$(gcloud run services describe book-provider --region=us-central1 --project=$PROJECT_ID --format="value(status.url)")
👉Bağımlılıkları yükleyin ve kodun çalıştığından emin olmak için aşağıdaki kodu çalıştırın:
cd ~/aidemy-bootstrap/planner/
python -m venv env
source env/bin/activate
gcloud config set project $(cat ~/project_id.txt)
export PROJECT_ID=$(gcloud config get project)
pip install -r requirements.txt
python book.py
Kitap sağlayıcı API'sinden alınan kitap önerilerini içeren bir JSON dizesi görmeniz gerekir. Sonuçlar rastgele oluşturulur. Kitaplarınız aynı olmayabilir ancak JSON biçiminde iki kitap önerisi alırsınız.
[{"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"}]
Bu mesajı görüyorsanız ilk araç doğru şekilde çalışıyor demektir.
Belirli parametrelerle açıkça bir RESTful API çağrısı oluşturmak yerine doğal dil ("Bir kurs yapıyorum...") kullanıyoruz. Ardından aracı, API ile nasıl etkileşim kurmak için doğal dil anlayışından yararlandığını vurgulayarak NLP kullanarak gerekli parametreleri (ör. kategori) akıllıca ayıklar.

👉book.py dosyasından aşağıdaki test kodunu kaldırın.
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."))
Veri tabanından müfredat verilerini alma
Ardından, Cloud SQL PostgreSQL veritabanından yapılandırılmış müfredat verilerini getiren bir araç oluşturacağız. Bu sayede, ders planı oluşturmak için güvenilir bir bilgi kaynağına erişebilir.

Önceki adımda oluşturduğunuz aidemy Cloud SQL örneğini hatırlıyor musunuz? Bu özellik şu durumlarda kullanılır:
👉 Terminalde, yeni örnekte aidemy-db adlı bir veritabanı oluşturmak için aşağıdaki komutu çalıştırın.
gcloud sql databases create aidemy-db \
--instance=aidemy
Google Cloud Console'daki Cloud SQL'de örneği doğrulayalım. aidemy adlı bir Cloud SQL örneğinin listelendiğini görmelisiniz.
👉 Ayrıntılarını görüntülemek için örnek adını tıklayın. 👉 Cloud SQL örneği ayrıntıları sayfasında, soldaki gezinme menüsünde Cloud SQL Studio'yu tıklayın. Bunu yaptığınızda yeni bir sekme açılır.
Veritabanı olarak aidemy-db simgesini seçin. Kullanıcı olarak postgres, şifre olarak 1234qwer değerini girin.
Kimlik doğrula'yı tıklayın.

👉SQL Studio sorgu düzenleyicisinde Editor 1 (Düzenleyici 1) sekmesine gidin ve aşağıdaki SQL kodunu yapıştırın:
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.');
Bu SQL kodu, curriculums adlı bir tablo oluşturur ve bazı örnek veriler ekler.
👉 SQL kodunu çalıştırmak için Çalıştır'ı tıklayın. İfadelerin başarıyla yürütüldüğünü belirten bir onay mesajı görmeniz gerekir.
👉 Gezgini genişletin, yeni oluşturulan tabloyu curriculums bulun ve sorgu'yu tıklayın. Sizin için oluşturulan SQL ile yeni bir düzenleyici sekmesi açılır.

SELECT * FROM
"public"."curriculums" LIMIT 1000;
👉Çalıştır'ı tıklayın.
Sonuçlar tablosunda, önceki adımda eklediğiniz veri satırları gösterilmelidir. Bu, tablonun ve verilerin doğru şekilde oluşturulduğunu onaylar.
Örnek müfredat verileriyle doldurulmuş bir veritabanını başarıyla oluşturduğunuza göre, şimdi bu veritabanını almak için bir araç oluşturacağız.
👉Cloud Code Editor'da, aidemy-bootstrap klasöründeki curriculums.py dosyasını düzenleyin ve aşağıdaki kodu dosyanın sonuna yapıştırın:
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"]
print(f"--------------------------->db_user: {db_user!r}")
print(f"--------------------------->db_pass: {db_pass!r}")
print(f"--------------------------->db_name: {db_name!r}")
connector = Connector()
pool = sqlalchemy.create_engine(
"postgresql+pg8000://",
creator=lambda: connector.connect(
instance_connection_name,
"pg8000",
user=db_user,
password=db_pass,
db=db_name,
),
pool_size=2,
max_overflow=2,
pool_timeout=30, # 30 seconds
pool_recycle=1800, # 30 minutes
)
return pool
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 = connect_with_connector()
Açıklama:
- Ortam Değişkenleri: Kod, ortam değişkenlerinden veritabanı kimlik bilgilerini ve bağlantı bilgilerini alır (bu konuyla ilgili daha fazla bilgiyi aşağıda bulabilirsiniz).
- connect_with_connector(): Bu işlev, veritabanına güvenli bir bağlantı oluşturmak için Cloud SQL Bağlayıcısı'nı kullanır.
- get_curriculum(year: int, subject: str): Bu işlev, yıl ve konuyu girdi olarak alır, müfredatlar tablosunu sorgular ve ilgili müfredat açıklamasını döndürür.
👉Kodu çalıştırmadan önce bazı ortam değişkenlerini ayarlamamız gerekir. Terminalde şu komutu çalıştırın:
gcloud config set project $(cat ~/project_id.txt)
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"
👉Test etmek için aşağıdaki kodu curriculums.py öğesinin sonuna ekleyin:
if __name__ == "__main__":
print(get_curriculum(6, "Mathematics"))
👉Kodu çalıştırın:
cd ~/aidemy-bootstrap/planner/
source env/bin/activate
python curriculums.py
6. sınıf matematik müfredatının açıklamasının konsola yazdırıldığını görürsünüz.
Expanding on fractions, ratios, algebraic thinking, and problem-solving strategies.
Müfredat açıklamasını görüyorsanız veritabanı aracı doğru çalışıyor demektir. Hâlâ çalışıyorsa Ctrl+C tuşuna basarak komut dosyasını durdurun.
👉curriculums.py dosyasından aşağıdaki test kodunu kaldırın.
if __name__ == "__main__":
print(get_curriculum(6, "Mathematics"))
👉Terminalde sanal ortamdan çıkmak için şu komutu çalıştırın:
deactivate
6. İnşa araçları: Web'den anlık bilgilere erişme
Son olarak, web'deki anlık bilgilere erişmek için Gemini 2 ve Google Arama entegrasyonunu kullanan bir araç oluşturacağız. Bu, aracının güncel kalmasına ve alakalı sonuçlar sunmasına yardımcı olur.
Gemini 2'nin Google Arama API'si ile entegrasyonu, daha doğru ve bağlamla alakalı arama sonuçları sağlayarak temsilci özelliklerini geliştirir. Bu sayede temsilciler güncel bilgilere erişebilir ve yanıtlarını gerçek dünyadaki verilere dayandırarak halüsinasyonları en aza indirebilir. Geliştirilmiş API entegrasyonu, daha doğal dil sorgularını da kolaylaştırarak temsilcilerin karmaşık ve ayrıntılı arama istekleri oluşturmasına olanak tanır.

Bu işlev, arama sorgusu, müfredat, konu ve yılı giriş olarak alır ve internetten alakalı bilgileri almak için Gemini API'yi ve Google Arama aracını kullanır. Yakından bakarsanız başka bir çerçeve kullanmadan işlev çağrısı yapmak için Google Üretken Yapay Zeka SDK'sını kullandığını görürsünüz.
👉search.py dosyasını aidemy-bootstrap klasöründe düzenleyin ve aşağıdaki kodu dosyanın sonuna yapıştırın:
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)
Açıklama:
- Tanımlama Aracı - google_search_tool: GoogleSearch nesnesini bir Araç içine sarmalama
- search_latest_resource(search_text: str, subject: str, year: int): Bu işlev, giriş olarak bir arama sorgusu, konu ve yıl alır ve Google Arama yapmak için Gemini API'yi kullanır.
- GenerateContentConfig: GoogleSearch aracına erişimi olduğunu tanımlayın.
Gemini modeli, search_text'i dahili olarak analiz eder ve soruyu doğrudan yanıtlayıp yanıtlayamayacağını veya GoogleSearch aracını kullanması gerekip gerekmediğini belirler. Bu, LLM'nin muhakeme sürecinde gerçekleşen kritik bir adımdır. Model, harici araçların gerekli olduğu durumları tanıyacak şekilde eğitilmiştir. Model, GoogleSearch aracını kullanmaya karar verirse gerçek çağırma işlemi Google Generative AI SDK tarafından gerçekleştirilir. SDK, modelin kararını ve oluşturduğu parametreleri alıp Google Arama API'sine gönderir. Bu bölüm, kodda kullanıcıdan gizlenir.
Gemini modeli, arama sonuçlarını yanıtına entegre eder. Bu bilgileri kullanarak kullanıcının sorusunu yanıtlayabilir, özet oluşturabilir veya başka bir görev gerçekleştirebilir.
👉Test etmek için kodu çalıştırın:
cd ~/aidemy-bootstrap/planner/
gcloud config set project $(cat ~/project_id.txt)
export PROJECT_ID=$(gcloud config get project)
source env/bin/activate
python search.py
"5. sınıf matematik müfredatı" ile ilgili arama sonuçlarını içeren Gemini Arama API'si yanıtını görmeniz gerekir. Tam çıktı, arama sonuçlarına bağlıdır ancak aramayla ilgili bilgileri içeren bir JSON nesnesi olur.
Arama sonuçlarını görüyorsanız Google Arama aracı doğru çalışıyordur. Komut dosyası hâlâ çalışıyorsa Ctrl+C tuşuna basarak komut dosyasını durdurun.
👉Ve kodun son bölümünü kaldırın.
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)
👉Terminalde sanal ortamdan çıkmak için şu komutu çalıştırın:
deactivate
Tebrikler! Artık planlayıcı aracınız için üç güçlü araç oluşturdunuz: API bağlayıcı, veritabanı bağlayıcı ve Google Arama aracı. Bu araçlar, temsilcinin etkili eğitim planları oluşturmak için ihtiyaç duyduğu bilgilere ve özelliklere erişmesini sağlar.
7. LangGraph ile düzenleme
Artık ayrı ayrı araçlarımızı oluşturduğumuza göre LangGraph'ı kullanarak bunları düzenleme zamanı. Bu sayede, kullanıcının isteğine göre hangi araçların ne zaman kullanılacağına akıllıca karar verebilen daha gelişmiş bir "planlayıcı" aracı oluşturabileceğiz.
LangGraph, Büyük Dil Modelleri (LLM'ler) kullanarak durum bilgisi olan, çok aktörlü uygulamalar oluşturmayı kolaylaştırmak için tasarlanmış bir Python kitaplığıdır. Bunu, LLM'ler, araçlar ve diğer aracıları içeren karmaşık görüşmeleri ve iş akışlarını düzenlemeye yönelik bir çerçeve olarak düşünebilirsiniz.
Temel Kavramlar:
- Grafik Yapısı: LangGraph, uygulamanızın mantığını yönlendirilmiş bir grafik olarak gösterir. Grafikteki her düğüm, süreçteki bir adımı (ör. bir LLM'ye yapılan çağrı, bir araç çağırma, koşullu kontrol) temsil eder. Kenarlar, düğümler arasındaki yürütme akışını tanımlar.
- Durum: LangGraph, uygulamanızın grafikte ilerlerken durumunu yönetir. Bu durum, kullanıcının girişi, araç çağrılarının sonuçları, LLM'lerden alınan ara çıktılar ve adımlar arasında korunması gereken diğer tüm bilgiler gibi değişkenleri içerebilir.
- Düğümler: Her düğüm bir hesaplamayı veya etkileşimi temsil eder. Bunlar:
- Araç düğümleri: Bir araç kullanma (ör. web araması yapma, veritabanına sorgu gönderme)
- İşlev düğümleri: Python işlevini yürütür.
- Kenarlar: Düğümleri bağlayarak yürütme akışını tanımlar. Bunlar:
- Doğrudan Kenarlar: Bir düğümden diğerine basit ve koşulsuz akış.
- Koşullu Kenarlar: Akış, koşullu bir düğümün sonucuna bağlıdır.

Orkestrasyonu uygulamak için LangGraph'ı kullanacağız. LangGraph mantığımızı tanımlamak için aidemy-bootstrap klasöründeki aidemy.py dosyasını düzenleyelim.
👉 Aşağıdaki kodu dosyanın sonuna ekleyin.
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"])}
Bu işlev, sohbetin mevcut durumunu almaktan, LLM'ye bir sistem mesajı sağlamaktan ve ardından LLM'den yanıt oluşturmasını istemekten sorumludur. LLM, doğrudan kullanıcıya yanıt verebilir veya mevcut araçlardan birini kullanmayı tercih edebilir.
tools : Bu liste, aracının kullanabileceği araç setini gösterir. Önceki adımlarda tanımladığımız üç araç işlevini içerir: get_curriculum, search_latest_resource ve recommend_book. llm.bind_tools(tools): Araç listesini llm nesnesine "bağlar". Araçları bağlamak, LLM'ye bu araçların kullanılabildiğini bildirir ve bunları nasıl kullanacağı hakkında bilgi verir (ör. araçların adları, kabul ettikleri parametreler ve ne yaptıkları).
Orkestrasyonu uygulamak için LangGraph'ı kullanacağız.
👉 Aşağıdaki kodu dosyanın sonuna ekleyin.
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")
Açıklama:
StateGraph(MessagesState):StateGraphnesnesi oluşturur.StateGraph, LangGraph'taki temel kavramlardan biridir. Aracınızın iş akışını grafik olarak gösterir. Grafikteki her düğüm, süreçteki bir adımı temsil eder. Bunu, temsilcinin nasıl akıl yürüteceğini ve hareket edeceğini belirleyen bir plan olarak düşünebilirsiniz.- Koşullu Edge:
"determine_tool"düğümünden kaynaklanantools_conditionbağımsız değişkeni,determine_toolişlevinin çıkışına göre hangi kenarın izleneceğini belirleyen bir işlev olabilir. Koşullu kenarlar, grafiğin LLM'nin hangi aracı kullanacağına (veya kullanıcıya doğrudan yanıt verip vermeyeceğine) dair kararına göre dallanmasına olanak tanır. Bu noktada, aracının "aklı" devreye girer. Davranışını duruma göre dinamik olarak uyarlayabilir. - Döngü: Grafiğe,
"tools"düğümünü"determine_tool"düğümüne geri bağlayan bir kenar ekler. Bu, grafikte bir döngü oluşturur. Böylece aracı, görevi tamamlamak ve tatmin edici bir yanıt vermek için yeterli bilgiyi toplayana kadar araçları tekrar tekrar kullanabilir. Bu döngü, birden fazla akıl yürütme ve bilgi toplama adımı gerektiren karmaşık görevler için çok önemlidir.
Şimdi de planlayıcı aracımızı test ederek farklı araçları nasıl düzenlediğini görelim.
Bu kod, belirli bir kullanıcı girişiyle prep_class işlevini çalıştırarak müfredatı, kitap önerilerini ve en son internet kaynaklarını kullanarak 5. sınıf Geometri dersi için bir öğretim planı oluşturma isteğini simüle eder.
👉 Terminalinizi kapattıysanız veya ortam değişkenleri artık ayarlanmamışsa terminalinizde aşağıdaki komutları yeniden çalıştırın.
export BOOK_PROVIDER_URL=$(gcloud run services describe book-provider --region=us-central1 --project=$PROJECT_ID --format="value(status.url)")
gcloud config set project $(cat ~/project_id.txt)
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"
👉Kodu çalıştırın:
cd ~/aidemy-bootstrap/planner/
source env/bin/activate
pip install -r requirements.txt
python aidemy.py
Terminaldeki günlüğü izleyin. Son öğretim planını sağlamadan önce, aracının üç aracı da çağırdığına (okul müfredatını alma, kitap önerileri alma ve en son kaynakları arama) dair kanıt görmelisiniz. Bu, LangGraph düzenlemesinin doğru şekilde çalıştığını ve aracının, kullanıcının isteğini karşılamak için mevcut tüm araçları akıllıca kullandığını gösterir.
================================ 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**
.........
Hâlâ çalışıyorsa Ctrl+C tuşuna basarak komut dosyasını durdurun.
👉 (BU ADIM İSTEĞE BAĞLIDIR) Test kodunu, farklı araçların çağrılmasını gerektiren farklı bir istemle değiştirin.
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")
👉 Terminalinizi kapattıysanız veya ortam değişkenleri artık ayarlanmamışsa aşağıdaki komutları yeniden çalıştırın.
gcloud config set project $(cat ~/project_id.txt)
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"
👉 (BU ADIM İSTEĞE BAĞLIDIR, YALNIZCA ÖNCEKİ ADIMI UYGULADIYSANIZ BU ADIMI UYGULAYIN) Kodu tekrar çalıştırın:
cd ~/aidemy-bootstrap/planner/
source env/bin/activate
python aidemy.py
Bu sefer ne fark ettiniz? Temsilci hangi araçları çağırdı? Bu kez temsilcinin yalnızca search_latest_resource aracını çağırdığını görmelisiniz. Bunun nedeni, istemde diğer iki aracın gerekli olduğunun belirtilmemesi ve LLM'mizin diğer araçları çağırmayacak kadar akıllı olmasıdır.
================================ 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 tuşuna basarak komut dosyasını durdurun.
👉 (BU ADIMI ATLAMAYIN!) aidemy.py dosyanızın temiz kalması için test kodunu kaldırın :
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")
Temsilci mantığımızı tanımladığımıza göre şimdi Flask web uygulamasını başlatalım. Bu sayede öğretmenler, ajanı form tabanlı bir arayüz üzerinden kullanabilir. LLM'lerle chatbot etkileşimleri yaygın olsa da birçok eğitimci için daha sezgisel olabileceği düşüncesiyle geleneksel bir form gönderme kullanıcı arayüzünü tercih ediyoruz.
👉 Terminalinizi kapattıysanız veya ortam değişkenleri artık ayarlanmamışsa aşağıdaki komutları yeniden çalıştırın.
export BOOK_PROVIDER_URL=$(gcloud run services describe book-provider --region=us-central1 --project=$PROJECT_ID --format="value(status.url)")
gcloud config set project $(cat ~/project_id.txt)
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"
👉 Şimdi web kullanıcı arayüzünü başlatın.
cd ~/aidemy-bootstrap/planner/
source env/bin/activate
python app.py
Cloud Shell terminal çıkışında başlangıç mesajlarını bulun. Flask genellikle çalıştığını ve hangi bağlantı noktasında çalıştığını belirten mesajlar yazdırır.
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.
👉 Sağ üst köşedeki "Web önizlemesi" menüsünden 8080 numaralı bağlantı noktasında önizle'yi seçin. Cloud Shell, uygulamanızın web önizlemesini içeren yeni bir tarayıcı sekmesi veya penceresi açar.

Uygulama arayüzünde Yıl için 5, konu için Mathematics seçin ve Eklenti İsteği'ne Geometry yazın.
👉 Uygulama kullanıcı arayüzünüzden ayrıldıysanız geri dönün. Oluşturulan çıkışı görmeniz gerekir.
👉 Terminalinizde Ctrl+C tuşuna basarak komut dosyasını durdurun.
👉 Terminalinizde sanal ortamdan çıkın:
deactivate
8. Planlayıcı aracıyı buluta dağıtma
Görüntüyü oluşturup kayıt defterine aktarma

Bunu buluta dağıtma zamanı.
👉 Terminalde, oluşturacağımız Docker görüntüsünü depolamak için bir yapay nesne deposu oluşturun.
gcloud artifacts repositories create agent-repository \
--repository-format=docker \
--location=us-central1 \
--description="My agent repository"
Created repository [agent-repository] ([agent-repository] adlı depo oluşturuldu) mesajını görmeniz gerekir.
👉 Docker görüntüsünü oluşturmak için aşağıdaki komutu çalıştırın.
cd ~/aidemy-bootstrap/planner/
gcloud config set project $(cat ~/project_id.txt)
export PROJECT_ID=$(gcloud config get project)
docker build -t gcr.io/${PROJECT_ID}/aidemy-planner .
👉 Görüntünün GCR yerine Artifact Registry'de barındırılması için yeniden etiketlenmesi ve etiketlenen görüntünün Artifact Registry'ye aktarılması gerekir:
gcloud config set project $(cat ~/project_id.txt)
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
Gönderme işlemi tamamlandıktan sonra görüntünün Artifact Registry'de başarıyla depolandığını doğrulayabilirsiniz.
👉 Google Cloud Console'da Artifact Registry'ye gidin. aidemy-planner resmini agent-repository deposunda bulabilirsiniz. 
Secret Manager ile veritabanı kimlik bilgilerinin güvenliğini sağlama
Veritabanı kimlik bilgilerini güvenli bir şekilde yönetmek ve bunlara erişmek için Google Cloud Secret Manager'ı kullanacağız. Bu sayede, hassas bilgilerin uygulama kodumuza sabit kodlanması önlenir ve güvenlik artırılır.
Veritabanı kullanıcı adı, şifresi ve veritabanı adı için ayrı ayrı sırlar oluştururuz. Bu yaklaşım, her kimlik bilgisini bağımsız olarak yönetmemize olanak tanır.
👉 Terminalde aşağıdakileri çalıştırın:
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'ı kullanmak, uygulamanızın güvenliğini sağlamak ve hassas kimlik bilgilerinin yanlışlıkla açığa çıkmasını önlemek için önemli bir adımdır. Bulut dağıtımları için en iyi güvenlik uygulamalarını kullanır.
Cloud Run'a dağıt
Cloud Run, container mimarisine alınmış uygulamaları hızlı ve kolay bir şekilde dağıtmanıza olanak tanıyan, tümüyle yönetilen sunucusuz bir platformdur. Altyapı yönetimini soyutlayarak kodunuzu yazmaya ve dağıtmaya odaklanmanızı sağlar. Planlayıcımızı Cloud Run hizmeti olarak dağıtacağız.
👉Google Cloud Console'da "Cloud Run"a gidin. DEPLOY CONTAINER'ı tıklayın ve SERVICE'i seçin. Cloud Run hizmetinizi yapılandırın:

- Container resmi: URL alanında "Seç"i tıklayın. Artifact Registry'ye aktardığınız resim URL'sini bulun (ör. us-central1-docker.pkg.dev/YOUR_PROJECT_ID/agent-repository/aidemy-planner/YOUR_IMG).
- Hizmet adı:
aidemy-planner - Bölge:
us-central1bölgesini seçin. - Kimlik doğrulama: Bu atölye çalışması kapsamında "Kimliği doğrulanmamış çağrılara izin ver" seçeneğini etkinleştirebilirsiniz. Üretim için erişimi kısıtlamak isteyebilirsiniz.
- Container(s), Volumes, Networking, Security (Container'lar, Birimler, Ağ, Güvenlik) bölümünü genişletin ve Container(s) (Container'lar) sekmesinde aşağıdakileri ayarlayın (:
- Ayar sekmesi:
- Kaynaklar
- bellek : 2 GiB
- Kaynaklar
- Değişkenler ve Gizli Anahtarlar sekmesi:
- Ortam değişkenleri: + Değişken Ekle düğmesini tıklayarak aşağıdaki değişkenleri ekleyin:
- Ad:
GOOGLE_CLOUD_PROJECTve değer: <YOUR_PROJECT_ID> ekleyin. - Adı ekleyin:
BOOK_PROVIDER_URLve değeri, terminalde aşağıdaki komutu kullanarak belirleyebileceğiniz kitap sağlayıcı işlevinizin URL'sine ayarlayın:gcloud config set project $(cat ~/project_id.txt) gcloud run services describe book-provider \ --region=us-central1 \ --project=$PROJECT_ID \ --format="value(status.url)"
- Ad:
- Secrets exposed as environment variables (Ortam değişkenleri olarak kullanıma sunulan gizli diziler) bölümünde, + Reference as a secret (+ Gizli dizi olarak referans ver) düğmesini tıklayarak aşağıdaki gizli dizileri ekleyin:
- Ad ekleyin:
DB_USER, gizli anahtar:db-user'ı seçin ve sürüm:latest - Ad ekleyin:
DB_PASS, gizli anahtar:db-pass'ı seçin ve sürüm:latest - Ad ekleyin:
DB_NAME, gizli anahtar:db-name'ı seçin ve sürüm:latest
- Ad ekleyin:
- Ortam değişkenleri: + Değişken Ekle düğmesini tıklayarak aşağıdaki değişkenleri ekleyin:
- Ayar sekmesi:

Diğer değerleri varsayılan olarak bırakın.
👉 OLUŞTUR'u tıklayın.
Cloud Run, hizmetinizi dağıtır.
Dağıtım tamamlandıktan sonra ayrıntılar sayfasında değilseniz hizmetin ayrıntılar sayfasına gitmek için hizmet adını tıklayın. Dağıtılan URL'yi üst kısımda bulabilirsiniz.

👉 Uygulama arayüzünde, Yıl için 7'yı seçin, konu olarak Mathematics'ı belirleyin ve Eklenti İsteği alanına Algebra yazın.
👉 Plan Oluştur'u tıklayın. Bu sayede aracı, özel bir ders planı oluşturmak için gerekli bağlamı elde eder.
Tebrikler! Güçlü yapay zeka temsilcimizi kullanarak başarıyla bir öğretim planı oluşturdunuz. Bu, aracıların iş yükünü önemli ölçüde azaltma ve görevleri kolaylaştırma potansiyelini gösterir. Sonuç olarak verimlilik artar ve eğitimcilerin hayatı kolaylaşır.
9. Çoklu temsilci sistemleri
Öğretim planı oluşturma aracını başarıyla uyguladığımıza göre şimdi odağımızı öğrenci portalını oluşturmaya kaydıralım. Bu portal, öğrencilere dersleriyle ilgili testlere, sesli özetlere ve ödevlere erişim imkanı sunar. Bu işlevin kapsamı göz önüne alındığında, modüler ve ölçeklenebilir bir çözüm oluşturmak için çoklu aracı sistemlerin gücünden yararlanacağız.
Daha önce de bahsettiğimiz gibi, her şeyi tek bir temsilcinin yapmasını beklemek yerine çoklu temsilci sistemi, iş yükünü daha küçük ve özel görevlere ayırmamıza olanak tanır. Bu görevlerin her biri özel bir temsilci tarafından yerine getirilir. Bu yaklaşımın bazı önemli avantajları vardır:
Modülerlik ve sürdürülebilirlik: Her şeyi yapan tek bir aracı oluşturmak yerine, iyi tanımlanmış sorumluluklara sahip daha küçük ve özel amaçlı aracılar oluşturun. Bu modülerlik, sistemin anlaşılmasını, bakımını ve hata ayıklamasını kolaylaştırır. Bir sorun ortaya çıktığında, büyük bir kod tabanını incelemek yerine sorunu belirli bir aracıya indirgeyebilirsiniz.
Ölçeklenebilirlik: Tek ve karmaşık bir aracıyı ölçeklendirmek darboğaz oluşturabilir. Çok agent'lı bir sistemde, her bir agent'ı kendi ihtiyaçlarına göre ölçeklendirebilirsiniz. Örneğin, bir temsilci çok sayıda isteği işliyorsa sistemin geri kalanını etkilemeden bu temsilcinin daha fazla örneğini kolayca oluşturabilirsiniz.
Ekip uzmanlığı: Şöyle düşünün: Bir mühendisten sıfırdan bir uygulama oluşturmasını istemezsiniz. Bunun yerine, her biri belirli bir alanda uzmanlığa sahip bir ekip oluşturursunuz. Benzer şekilde, çoklu aracı sistemi, farklı LLM'lerin ve araçların güçlü yönlerinden yararlanmanıza olanak tanır. Bu sistem, bunları belirli görevler için en uygun olan aracılara atar.
Paralel Geliştirme: Farklı ekipler aynı anda farklı aracıları geliştirebilir ve böylece geliştirme süreci hızlanır. Aracılar bağımsız olduğundan bir aracıda yapılan değişikliklerin diğer aracıları etkileme olasılığı daha düşüktür.
Etkinliğe Dayalı Mimari
Bu aracılar arasında etkili iletişim ve koordinasyon sağlamak için etkinliğe dayalı bir mimari kullanacağız. Bu, aracıların sistemde gerçekleşen "olaylara" tepki vereceği anlamına gelir.
Aracılar belirli etkinlik türlerine abone olur (ör. "teaching plan generated" (öğretim planı oluşturuldu), "assignment created" (ödev oluşturuldu) gibi etkinlikler gösterilir. Bir olay gerçekleştiğinde ilgili temsilcilere bildirim gönderilir ve temsilciler buna göre hareket edebilir. Bu ayrıştırma; esnekliği, ölçeklenebilirliği ve anlık yanıt verme özelliğini destekler.

Şimdi, bu etkinlikleri yayınlamanın bir yolunu bulmamız gerekiyor. Bunu yapmak için bir Pub/Sub konusu oluşturacağız. Plan adlı bir konu oluşturarak başlayalım.
👉 Google Cloud Console pub/sub'a gidin.
👉 Konu Oluştur düğmesini tıklayın.
👉 Kimlik/ad plan ile Konu'yu yapılandırın ve Add a default subscription seçeneğinin işaretini kaldırın, geri kalan ayarları varsayılan olarak bırakıp Oluştur'u tıklayın.
Pub/Sub sayfası yenilenir ve yeni oluşturduğunuz konu artık tabloda listelenir. 
Şimdi de Pub/Sub etkinlik yayınlama işlevini planlayıcı aracımıza entegre edelim. Yeni oluşturduğumuz Pub/Sub konusuna bir "plan" etkinliği gönderen yeni bir araç ekleyeceğiz. Bu etkinlik, sistemdeki diğer aracılara (ör. öğrenci portalındakiler) yeni bir öğretim planının kullanıma sunulduğunu bildirir.
👉Cloud Code Düzenleyici'ye geri dönün ve planner klasöründe bulunan app.py dosyasını açın. Etkinliği yayınlayan bir işlev ekleyeceğiz. Değiştir:
##ADD SEND PLAN EVENT FUNCTION HERE
şu kodla
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: Bu işlev, oluşturulan öğretim planını giriş olarak alır, bir Pub/Sub yayıncı istemcisi oluşturur, konu yolunu oluşturur, öğretim planını bir JSON dizesine dönüştürür ve mesajı konuda yayınlar.
Aynı app.py dosyasında
👉İstemde, öğretim planı oluşturulduktan sonra öğretim planı etkinliğini Pub/Sub konusuna göndermesi için aracıya talimat verin. *Değiştir
### ADD send_plan_event CALL
aşağıdakilerle:
send_plan_event(teaching_plan)
send_plan_event aracını ekleyip istemi değiştirerek planlayıcı aracımızın Pub/Sub'a etkinlik yayınlamasını sağladık. Böylece sistemimizin diğer bileşenleri, yeni eğitim planlarının oluşturulmasına tepki verebiliyor. Artık aşağıdaki bölümlerde işlevsel bir çoklu aracı sistemimiz olacak.
10. İsteğe bağlı testlerle öğrencileri destekleme
Öğrencilerin, kendi öğrenme planlarına göre uyarlanmış sınırsız sayıda teste erişebildiği bir öğrenme ortamı hayal edin. Bu testler, yanıtlar ve açıklamalar da dahil olmak üzere anında geri bildirim sağlayarak materyalin daha iyi anlaşılmasını sağlar. Yapay zeka destekli test portalımızla bu potansiyeli ortaya çıkarmayı hedefliyoruz.
Bu vizyonu hayata geçirmek için, öğretim planının içeriğine dayalı çoktan seçmeli sorular oluşturabilen bir test oluşturma bileşeni geliştireceğiz.

👉 Cloud Code Düzenleyici'nin Gezgin bölmesinde portal klasörüne gidin. quiz.py dosyasının kopyasını açın ve aşağıdaki kodu dosyanın sonuna yapıştırın.
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-2.5-flash-preview-04-17", 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
Aracı, LLM'nin çıkışını anlamak ve yapılandırmak için özel olarak tasarlanmış bir JSON çıkış ayrıştırıcı oluşturur. Ayrıştırılan çıkışın doğru biçime (soru, seçenekler ve yanıt) uygun olmasını sağlamak için daha önce tanımladığımız QuizQuestion modelini kullanır.
👉 Terminalinizde, sanal ortam oluşturmak, bağımlılıkları yüklemek ve aracıyı başlatmak için aşağıdaki komutları yürütün:
gcloud config set project $(cat ~/project_id.txt)
cd ~/aidemy-bootstrap/portal/
python -m venv env
source env/bin/activate
pip install -r requirements.txt
python app.py
👉 Sağ üst köşedeki "Web önizlemesi" menüsünden 8080 numaralı bağlantı noktasında önizle'yi seçin. Cloud Shell, uygulamanızın web önizlemesini içeren yeni bir tarayıcı sekmesi veya penceresi açar.
👉 Web uygulamasında, üst gezinme çubuğundaki veya dizin sayfasındaki karttan "Testler" bağlantısını tıklayın. Öğrenci için rastgele oluşturulmuş üç quiz gösterilir. Bu testler, eğitim planına dayanır ve yapay zeka destekli test oluşturma sistemimizin gücünü gösterir.

👉Yerel olarak çalışan işlemi durdurmak için terminalde Ctrl+C tuşuna basın.
Gemini 2 ile Açıklama İçin Düşünme
Tamam, yani testler var. Bu harika bir başlangıç! Peki öğrenciler bir şeyi yanlış yaparsa ne olur? Gerçek öğrenme de bu şekilde gerçekleşir, değil mi? Cevabın neden yanlış olduğunu ve doğru cevaba nasıl ulaşılacağını açıklarsak bu bilgiyi hatırlama olasılıkları çok daha yüksek olur. Ayrıca, izleyicilerin kafasındaki karışıklıkları gidermeye ve onlara güven aşılamaya yardımcı olur.
Bu nedenle, en güçlü silahımızı kullanacağız: Gemini 2'nin "düşünme" modeli! Bunu, yapay zekaya açıklamadan önce düşünmesi için biraz daha zaman tanımak gibi düşünebilirsiniz. Bu sayede daha ayrıntılı ve daha iyi geri bildirimler verebilir.
Öğrencilere yardımcı olarak, sorularını yanıtlayarak ve ayrıntılı açıklamalar yaparak onlara destek olup olamayacağını görmek istiyoruz. Bu özelliği test etmek için zorluğuyla bilinen bir konu olan kalkülüs ile başlayacağız.

👉Öncelikle answer.py klasöründeki portal Cloud Code Editor'a gidin. Aşağıdaki işlev kodunu değiştirin
def answer_thinking(question, options, user_response, answer, region):
return ""
Aşağıdaki kod snippet'iyle:
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)
Bu, Gemini 2 Flash modelini başlatan çok basit bir Langchain uygulamasıdır. Bu uygulamada, modele faydalı bir öğretmen gibi davranması ve açıklamalar yapması talimatı verilir.
👉Terminalde aşağıdaki komutu çalıştırın:
gcloud config set project $(cat ~/project_id.txt)
cd ~/aidemy-bootstrap/portal/
source env/bin/activate
python answer.py
Orijinal talimatlarda verilen örneğe benzer bir çıkış görmeniz gerekir. Mevcut model, kapsamlı bir açıklama sunmayabilir.
Okay, I see the question and the choices. The question is to evaluate the limit:
lim (x→0) [(sin(5x) - 5x) / x^3]
You chose option B, which is -5/3, but the correct answer is A, which is -125/6.
It looks like you might have missed a step or made a small error in your calculations. This type of limit often involves using L'Hôpital's Rule or Taylor series expansion. Since we have the form 0/0, L'Hôpital's Rule is a good way to go! You need to apply it multiple times. Alternatively, you can use the Taylor series expansion of sin(x) which is:
sin(x) = x - x^3/3! + x^5/5! - ...
So, sin(5x) = 5x - (5x)^3/3! + (5x)^5/5! - ...
Then, (sin(5x) - 5x) = - (5x)^3/3! + (5x)^5/5! - ...
Finally, (sin(5x) - 5x) / x^3 = - 5^3/3! + (5^5 * x^2)/5! - ...
Taking the limit as x approaches 0, we get -125/6.
Keep practicing, you'll get there!
👉 answer.py dosyasında, değiştirin .
answer_thinking işlevinde gemini-2.0-flash-001-gemini-2.0-flash-thinking-exp-01-21 tarihleri arasında model_name.
Bu işlem, LLM'yi muhakeme konusunda daha iyi olan farklı bir LLM ile değiştirir. Bu sayede model daha iyi açıklamalar üretebilir.
👉 Yeni düşünme modelini test etmek için answer.py komut dosyasını tekrar çalıştırın:
gcloud config set project $(cat ~/project_id.txt)
cd ~/aidemy-bootstrap/portal/
source env/bin/activate
python answer.py
Aşağıda, düşünme modelinin çok daha kapsamlı ve ayrıntılı bir yanıt örneği verilmiştir. Bu yanıtta, matematik probleminin nasıl çözüleceğine dair adım adım açıklamalar yer almaktadır. Bu, yüksek kaliteli açıklamalar oluşturmada "düşünme" modellerinin gücünü vurgular. Şuna benzer bir çıkış alırsınız:
Hey there! Let's take a look at this limit problem together. You were asked to evaluate:
lim (x→0) [(sin(5x) - 5x) / x^3]
and you picked option B, -5/3, but the correct answer is actually A, -125/6. Let's figure out why!
It's a tricky one because if we directly substitute x=0, we get (sin(0) - 0) / 0^3 = (0 - 0) / 0 = 0/0, which is an indeterminate form. This tells us we need to use a more advanced technique like L'Hopital's Rule or Taylor series expansion.
Let's use the Taylor series expansion for sin(y) around y=0. Do you remember it? It looks like this:
sin(y) = y - y^3/3! + y^5/5! - ...
where 3! (3 factorial) is 3 × 2 × 1 = 6, 5! is 5 × 4 × 3 × 2 × 1 = 120, and so on.
In our problem, we have sin(5x), so we can substitute y = 5x into the Taylor series:
sin(5x) = (5x) - (5x)^3/3! + (5x)^5/5! - ...
sin(5x) = 5x - (125x^3)/6 + (3125x^5)/120 - ...
Now let's plug this back into our limit expression:
[(sin(5x) - 5x) / x^3] = [ (5x - (125x^3)/6 + (3125x^5)/120 - ...) - 5x ] / x^3
Notice that the '5x' and '-5x' cancel out! So we are left with:
= [ - (125x^3)/6 + (3125x^5)/120 - ... ] / x^3
Now, we can divide every term in the numerator by x^3:
= -125/6 + (3125x^2)/120 - ...
Finally, let's take the limit as x approaches 0. As x gets closer and closer to zero, terms with x^2 and higher powers will become very, very small and approach zero. So, we are left with:
lim (x→0) [ -125/6 + (3125x^2)/120 - ... ] = -125/6
Therefore, the correct answer is indeed **A) -125/6**.
It seems like your answer B, -5/3, might have come from perhaps missing a factor somewhere during calculation or maybe using an incorrect simplification. Double-check your steps when you were trying to solve it!
Don't worry, these limit problems can be a bit tricky sometimes! Keep practicing and you'll get the hang of it. Let me know if you want to go through another similar example or if you have any more questions! 😊
Now that we have confirmed it works, let's use the portal.
👉answer.py içinden aşağıdaki test kodunu KALDIRIN:
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)
👉Sanal ortam oluşturmak, bağımlılıkları yüklemek ve aracıyı başlatmak için terminalde aşağıdaki komutları yürütün:
gcloud config set project $(cat ~/project_id.txt)
cd ~/aidemy-bootstrap/portal/
source env/bin/activate
python app.py
👉 Sağ üst köşedeki "Web önizlemesi" menüsünden 8080 numaralı bağlantı noktasında önizle'yi seçin. Cloud Shell, uygulamanızın web önizlemesini içeren yeni bir tarayıcı sekmesi veya penceresi açar.
👉 Web uygulamasında, üst gezinme çubuğundaki veya dizin sayfasındaki karttan "Testler" bağlantısını tıklayın.
👉 Tüm testleri yanıtlayın ve en az bir yanıtın yanlış olduğundan emin olun. Ardından Gönder'i tıklayın.

Yanıtı beklerken boş boş bakmak yerine Cloud Editor'ın terminaline geçin. İşlevinizin ilerleme durumunu ve oluşturduğu çıkış ya da hata mesajlarını emülatörün terminalinde gözlemleyebilirsiniz. 😁
👉 Terminalinizde, terminalde Ctrl+C tuşuna basarak yerel olarak çalışan işlemi durdurun.
11. İSTEĞE BAĞLI: Eventarc ile aracıları düzenleme
Öğrenci portalı şu ana kadar varsayılan bir dizi eğitim planına dayalı testler oluşturuyordu. Bu faydalı olsa da planlayıcı aracımız ile portalın test aracının aslında birbirleriyle konuşmadığı anlamına geliyor. Planlayıcı aracının yeni oluşturduğu eğitim planlarını bir Pub/Sub konusuna yayınladığı özelliği eklediğimizi hatırlıyor musunuz? Şimdi bunu portal aracımıza bağlama zamanı.

Portalın, yeni bir eğitim planı oluşturulduğunda sınav içeriğini otomatik olarak güncellemesini istiyoruz. Bunun için portalda bu yeni planları alabilecek bir uç nokta oluşturacağız.
👉 Cloud Code Editor'un Gezgin bölmesinde portal klasörüne gidin.
👉 Düzenlemek için app.py dosyasını açın. REPLACE ## REPLACE ME! NEW TEACHING PLAN satırını aşağıdaki kodla değiştirin:
@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
Cloud Run'da yeniden oluşturma ve dağıtma
Hem planlayıcı hem de portal aracılarımızı Cloud Run'da güncelleyip yeniden dağıtmanız gerekir. Bu sayede, en yeni koda sahip olmaları ve etkinlikler aracılığıyla iletişim kuracak şekilde yapılandırılmaları sağlanır.

👉İlk olarak planner aracı görüntüsünü yeniden oluşturup terminalde tekrar çalıştıracağız:
cd ~/aidemy-bootstrap/planner/
gcloud config set project $(cat ~/project_id.txt)
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
👉Aynı işlemi yaparak portal temsilci görüntüsünü oluşturup gönderelim:
cd ~/aidemy-bootstrap/portal/
gcloud config set project $(cat ~/project_id.txt)
export PROJECT_ID=$(gcloud config get project)
docker build -t gcr.io/${PROJECT_ID}/aidemy-portal .
export PROJECT_ID=$(gcloud config get project)
docker tag gcr.io/${PROJECT_ID}/aidemy-portal us-central1-docker.pkg.dev/${PROJECT_ID}/agent-repository/aidemy-portal
docker push us-central1-docker.pkg.dev/${PROJECT_ID}/agent-repository/aidemy-portal
👉 Artifact Registry'ye gidin. aidemy-planner ve aidemy-portal container görüntüleri, agent-repository altında listelenmiş şekilde görünür.

👉Terminalde planlayıcı aracısının Cloud Run görüntüsünü güncellemek için şu komutu çalıştırın:
gcloud config set project $(cat ~/project_id.txt)
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
Şuna benzer bir çıkış alırsınız:
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
Hizmet URL'sini not edin. Bu, dağıtılan planlayıcı temsilcinizin bağlantısıdır. Daha sonra planlayıcı aracısı hizmet URL'sini belirlemeniz gerekirse şu komutu kullanın:
gcloud config set project $(cat ~/project_id.txt)
export PROJECT_ID=$(gcloud config get project)
gcloud run services describe aidemy-planner \
--region=us-central1 \
--format 'value(status.url)'
👉Portal aracısı için Cloud Run örneğini oluşturmak üzere bu komutu çalıştırın.
gcloud config set project $(cat ~/project_id.txt)
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}
Şuna benzer bir çıkış alırsınız:
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
Hizmet URL'sini not edin. Bu, dağıtılan öğrenci portalınızın bağlantısıdır. Daha sonra öğrenci portalı hizmet URL'sini belirlemeniz gerekirse şu komutu kullanın:
gcloud config set project $(cat ~/project_id.txt)
export PROJECT_ID=$(gcloud config get project)
gcloud run services describe aidemy-portal \
--region=us-central1 \
--format 'value(status.url)'
Eventarc tetikleyicisini oluşturma
Ancak asıl soru şu: Pub/Sub konusunda yeni bir plan beklerken bu uç nokta nasıl bildirilir? İşte bu noktada Eventarc devreye girerek günü kurtarıyor.
Eventarc, belirli etkinlikleri (ör. Pub/Sub konumuza yeni bir mesajın gelmesi) dinleyip yanıt olarak otomatik olarak işlemleri tetikleyerek köprü görevi görür. Örneğimizde, yeni bir eğitim planı yayınlandığında bu durum algılanır ve portalımızın uç noktasına bir sinyal gönderilerek güncelleme zamanının geldiği bildirilir.
Etkinliğe dayalı iletişimi Eventarc ile yöneterek planlayıcı aracımızı ve portal aracımızı sorunsuz bir şekilde bağlayabilir, böylece gerçekten dinamik ve duyarlı bir öğrenme sistemi oluşturabiliriz. Bu, en son ders planlarını doğru yere otomatik olarak ileten akıllı bir mesajlaşma uygulamasına sahip olmaya benzer.
👉Konsolda Eventarc'a gidin.
👉 "+ TETİKLEYİCİ OLUŞTUR" düğmesini tıklayın.
Tetikleyiciyi yapılandırma (temel bilgiler):
- Tetikleyici adı:
plan-topic-trigger - Tetikleyici türü: Google kaynakları
- Etkinlik sağlayıcı: Cloud Pub/Sub
- Etkinlik türü:
google.cloud.pubsub.topic.v1.messagePublished - Cloud Pub/Sub Konusu:
projects/PROJECT_ID/topics/planseçeneğini belirleyin. - Bölge:
us-central1. - Hizmet hesabı:
- Hizmet hesabına
roles/iam.serviceAccountTokenCreatorrolü VERİN. - Varsayılan değeri kullanın: Varsayılan Compute hizmet hesabı
- Hizmet hesabına
- Etkinlik hedefi: Cloud Run
- Cloud Run hizmeti:
aidemy-portal - Hata mesajını yoksay: "locations/me-central2" kaynağına erişim izni reddedildi (veya kaynak mevcut olmayabilir).
- Hizmet URL'si yolu:
/new_teaching_plan
👉 "Oluştur"u tıklayın.
Eventarc Triggers (Eventarc Tetikleyicileri) sayfası yenilenir ve yeni oluşturduğunuz tetikleyiciyi tabloda listelenmiş olarak görürsünüz.
Şimdi, yeni bir ders planı istemek için Hizmet URL'sini kullanarak planlayıcı aracısına erişin.
👉 Planlayıcı aracısının hizmet URL'sini belirlemek için terminalde şunu çalıştırın:
gcloud config set project $(cat ~/project_id.txt)
export PROJECT_ID=$(gcloud config get project)
gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner
👉 Çıktı olarak verilen URL'ye gidin ve bu kez Yıl 5, Konu Science ve Eklenti İsteği atoms'ni deneyin.
Ardından bir veya iki dakika bekleyin. Bu gecikme, laboratuvarın faturalandırma sınırlaması nedeniyle uygulanmıştır. Normal şartlarda gecikme olmaması gerekir.
Son olarak, hizmet URL'sini kullanarak öğrenci portalına erişin.
Öğrenci portalı aracısının hizmet URL'sini belirlemek için terminalde şunu çalıştırın:
gcloud config set project $(cat ~/project_id.txt)
export PROJECT_ID=$(gcloud config get project)
gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep portal
Testlerin güncellendiğini ve yeni öğretim planıyla uyumlu hale geldiğini göreceksiniz. Bu, Eventarc'ın Aidemy sistemine başarılı bir şekilde entegre edildiğini gösterir.

Tebrikler! Google Cloud'da, gelişmiş ölçeklenebilirlik ve esneklik için etkinliğe dayalı mimariden yararlanarak çok agent'lı bir sistemi başarıyla oluşturdunuz. Sağlam bir temel oluşturdunuz ancak keşfedilecek daha çok şey var. Bu mimarinin gerçek avantajlarını daha ayrıntılı bir şekilde incelemek, Gemini 2'nin çok formatlı Live API'sinin gücünü keşfetmek ve LangGraph ile tek düzenlemeyi nasıl uygulayacağınızı öğrenmek için sonraki iki bölüme geçebilirsiniz.
12. İSTEĞE BAĞLI: Gemini ile Sesli Özetler
Gemini, metin, resim ve hatta ses gibi çeşitli kaynaklardan gelen bilgileri anlayıp işleyebilir. Bu sayede öğrenme ve içerik oluşturma konusunda yepyeni olanaklar sunar. Gemini'ın "görme", "duyma" ve "okuma" yeteneği, yaratıcı ve ilgi çekici kullanıcı deneyimlerinin kapılarını aralar.
Öğrenme sürecinde görseller veya metinler oluşturmanın yanı sıra etkili özetleme ve tekrar da önemli bir adımdır. Bir düşünün: Akılda kalıcı bir şarkı sözünü, ders kitabında okuduğunuz bir bilgiden daha kolay hatırladığınız oluyor mu? Sesler inanılmaz derecede akılda kalıcı olabilir. Bu nedenle, öğretim planlarımızın sesli özetlerini oluşturmak için Gemini'ın çok formatlı özelliklerinden yararlanacağız. Bu sayede öğrenciler, materyalleri kolay ve ilgi çekici bir şekilde inceleyebilecek. İşitsel öğrenmenin gücüyle akılda tutma ve anlama becerileri de artabilecek.

Oluşturulan ses dosyalarını depolayabileceğimiz bir yer gerekir. Cloud Storage, ölçeklenebilir ve güvenilir bir çözüm sunar.
👉Konsolda Depolama'ya gidin. Soldaki menüde "Gruplar"ı tıklayın. En üstteki "+ OLUŞTUR" düğmesini tıklayın.
👉Yeni paketinizi yapılandırın:
- paket adı:
aidemy-recap-UNIQUE_NAME.- ÖNEMLİ:
aidemy-recap-ile başlayan benzersiz bir paket adı tanımladığınızdan emin olun. Bu benzersiz önek, Cloud Storage paketinizi oluştururken adlandırma çakışmalarını önlemek için çok önemlidir.
- ÖNEMLİ:
- region (bölge):
us-central1. - Depolama sınıfı: "Standart". Standart, sık erişilen veriler için uygundur.
- Erişim denetimi: Varsayılan "Tek tip" erişim denetimi seçeneğini değiştirmeyin. Bu, tutarlı ve paket düzeyinde erişim denetimi sağlar.
- Gelişmiş seçenekler: Bu atölye çalışması için genellikle varsayılan ayarlar yeterlidir.
Paketinizi oluşturmak için OLUŞTUR düğmesini tıklayın.
- Herkese açık erişimi engelleme hakkında bir pop-up görebilirsiniz. "Bu pakette herkese açık erişim engeli uygula" kutusunu işaretli bırakın ve
Confirmsimgesini tıklayın.
Yeni oluşturduğunuz paketi artık Paketler listesinde görebilirsiniz. Paketinizin adını unutmayın. Bu bilgiye daha sonra ihtiyacınız olacak.
👉Cloud Code Editor'ın terminalinde, hizmet hesabına pakete erişim izni vermek için aşağıdaki komutları çalıştırın:
gcloud config set project $(cat ~/project_id.txt)
export PROJECT_ID=$(gcloud config get project)
export COURSE_BUCKET_NAME=$(gcloud storage buckets list --format="value(name)" | grep aidemy-recap)
export SERVICE_ACCOUNT_NAME=$(gcloud compute project-info describe --format="value(defaultServiceAccount)")
gcloud storage buckets add-iam-policy-binding gs://$COURSE_BUCKET_NAME \
--member "serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role "roles/storage.objectViewer"
gcloud storage buckets add-iam-policy-binding gs://$COURSE_BUCKET_NAME \
--member "serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role "roles/storage.objectCreator"
👉Cloud Code Düzenleyici'de courses klasörünün içindeki audio.py dosyasını açın. Aşağıdaki kodu dosyanın sonuna yapıştırın:
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-east5" #To workaround onRamp quota 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-2.0-flash-001",
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))
- Akış Bağlantısı: Öncelikle, Live API uç noktasıyla kalıcı bir bağlantı kurulur. İstek gönderip yanıt aldığınız standart bir API çağrısının aksine, bu bağlantı sürekli veri alışverişi için açık kalır.
- Configuration Multimodal: Ne tür bir çıkış istediğinizi (bu örnekte ses) belirtmek için yapılandırmayı kullanın. Hatta kullanmak istediğiniz parametreleri (ör. ses seçimi, ses kodlama) bile belirtebilirsiniz.
- Eşzamansız İşleme: Bu API eşzamansız olarak çalışır. Yani ses oluşturma işleminin tamamlanmasını beklerken ana iş parçacığını engellemez. Verileri gerçek zamanlı olarak işleyip çıkışı parçalar halinde göndererek neredeyse anında bir deneyim sunar.
Şimdi önemli soru şu: Bu ses üretme süreci ne zaman çalıştırılmalıdır? İdeal olarak, sesli özetlerin yeni bir eğitim planı oluşturulur oluşturulmaz kullanılabilmesini istiyoruz. Öğretim planını bir Pub/Sub konusuna yayınlayarak olay odaklı bir mimariyi zaten uyguladığımız için bu konuya abone olmamız yeterlidir.
Ancak çok sık yeni ders planları oluşturmuyoruz. Bir aracının sürekli çalışıp yeni planları beklemesi verimli olmaz. Bu nedenle, ses üretme mantığını Cloud Run işlevi olarak dağıtmak mantıklıdır.
İşlev olarak dağıtıldığında, Pub/Sub konusuna yeni bir mesaj yayınlanana kadar etkinleşmez. Bu durumda işlev otomatik olarak tetiklenir. İşlev, sesli özetler oluşturur ve bunları paketimizde saklar.
👉main.py dosyasındaki courses klasöründe bulunan bu dosya, yeni bir eğitim planı kullanıma sunulduğunda tetiklenecek Cloud Run işlevini tanımlar. Planı alır ve sesli özet oluşturma işlemini başlatır. Aşağıdaki kod snippet'ini dosyanın sonuna ekleyin.
@functions_framework.cloud_event
def process_teaching_plan(cloud_event):
print(f"CloudEvent received: {cloud_event.data}")
time.sleep(60)
try:
if isinstance(cloud_event.data.get('message', {}).get('data'), str): # Check for base64 encoding
data = json.loads(base64.b64decode(cloud_event.data['message']['data']).decode('utf-8'))
teaching_plan = data.get('teaching_plan') # Get the teaching plan
elif 'teaching_plan' in cloud_event.data: # No base64
teaching_plan = cloud_event.data["teaching_plan"]
else:
raise KeyError("teaching_plan not found") # Handle error explicitly
#Load the teaching_plan as string and from cloud event, call audio breakup_sessions
breakup_sessions(teaching_plan)
return "Teaching plan processed successfully", 200
except (json.JSONDecodeError, AttributeError, KeyError) as e:
print(f"Error decoding CloudEvent data: {e} - Data: {cloud_event.data}")
return "Error processing event", 500
except Exception as e:
print(f"Error processing teaching plan: {e}")
return "Error processing teaching plan", 500
@functions_framework.cloud_event: Bu dekoratör, işlevi CloudEvents tarafından tetiklenecek bir Cloud Run işlevi olarak işaretler.
Yerel olarak test etme
👉Bu kodu sanal bir ortamda çalıştırıp Cloud Run işlevi için gerekli Python kitaplıklarını yükleyeceğiz.
cd ~/aidemy-bootstrap/courses
gcloud config set project $(cat ~/project_id.txt)
export PROJECT_ID=$(gcloud config get project)
export COURSE_BUCKET_NAME=$(gcloud storage buckets list --format="value(name)" | grep aidemy-recap)
python -m venv env
source env/bin/activate
pip install -r requirements.txt
👉Cloud Run Function Emulator, işlevimizi Google Cloud'a dağıtmadan önce yerel olarak test etmemize olanak tanır. Aşağıdaki komutu çalıştırarak yerel bir emülatör başlatın:
functions-framework --target process_teaching_plan --signature-type=cloudevent --source main.py
👉Emülatör çalışırken yeni bir eğitim planının yayınlanmasını simüle etmek için emülatöre test CloudEvents'leri gönderebilirsiniz. Yeni bir terminalde:

👉Çalıştır:
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=="
}
}'
Yanıtı beklerken boş boş bakmak yerine diğer Cloud Shell terminaline geçin. İşlevinizin ilerleme durumunu ve oluşturduğu çıkış ya da hata mesajlarını emülatörün terminalinde gözlemleyebilirsiniz. 😁
2. terminale döndüğünüzde OK döndürülmüş olmalıdır.
👉Paketteki verileri doğrulamak için Cloud Storage'a gidin, "Paket" sekmesini ve ardından aidemy-recap-UNIQUE_NAME simgesini seçin.

👉Emülatörün çalıştığı terminalde çıkmak için ctrl+c yazın. İkinci terminali kapatın. İkinci terminali kapatın ve sanal ortamdan çıkmak için devre dışı bırakma komutunu çalıştırın.
deactivate
Google Cloud'a dağıtma
👉Yerel olarak test ettikten sonra kurs aracısını Google Cloud'a dağıtma zamanı. Terminalde şu komutları çalıştırın:
cd ~/aidemy-bootstrap/courses
gcloud config set project $(cat ~/project_id.txt)
export PROJECT_ID=$(gcloud config get project)
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
Google Cloud Console'da Cloud Run'a giderek dağıtımı doğrulayın.courses-agent adlı yeni bir hizmetin listelendiğini görmeniz gerekir.

Tetikleyici yapılandırmasını kontrol etmek için kurslar-aracı hizmetini tıklayarak ayrıntılarını görüntüleyin. "TETİKLEYİCİLER" sekmesine gidin.
Plan konusuna yayınlanan mesajları dinlemek üzere yapılandırılmış bir tetikleyici görmeniz gerekir.

Son olarak, baştan sona çalışmasını izleyelim.
👉Oluşturulan ses dosyalarını nerede bulacağını bilmesi için portal aracısını yapılandırmamız gerekir. Terminalde şunu çalıştırın:
export COURSE_BUCKET_NAME=$(gcloud storage buckets list --format="value(name)" | grep aidemy-recap)
gcloud config set project $(cat ~/project_id.txt)
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
👉Planlayıcı aracısı web sayfasını kullanarak yeni bir ders planı oluşturmayı deneyin. Başlatılması birkaç dakika sürebilir. Sunucusuz bir hizmet olduğundan bu durum normaldir.
Planlayıcı aracısına erişmek için terminalde aşağıdaki komutu çalıştırarak aracının hizmet URL'sini alın:
gcloud run services list \
--platform=managed \
--region=us-central1 \
--format='value(URL)' | grep planner
Yeni planı oluşturduktan sonra sesin oluşturulması için 2-3 dakika bekleyin. Bu işlem, laboratuvar hesabındaki faturalandırma sınırlaması nedeniyle birkaç dakika daha sürecektir.
courses-agent işlevinin eğitim planını alıp almadığını, işlevin "TETİKLEYİCİLER" sekmesini kontrol ederek izleyebilirsiniz. Sayfayı düzenli olarak yenileyin. İşlevin çağrıldığını sonunda göreceksiniz. İşlev 2 dakikadan uzun süre çağrılmadıysa öğretim planını tekrar oluşturmayı deneyebilirsiniz. Ancak, oluşturulan her plan sırayla tüketilip işleneceğinden ve bu durum birikmeye yol açabileceğinden planları hızlı bir şekilde tekrar tekrar oluşturmaktan kaçının.

👉Portalı ziyaret edip "Kurslar"ı tıklayın. Her biri sesli özet gösteren üç kart görürsünüz. Portal aracınızın URL'sini bulmak için:
gcloud run services list \
--platform=managed \
--region=us-central1 \
--format='value(URL)' | grep portal
Sesli özetlerin, yeni oluşturduğunuz öğretim planıyla uyumlu olduğundan emin olmak için her kursta "oynat"ı tıklayın. 
Sanal ortamdan çıkın.
deactivate
13. İSTEĞE BAĞLI: Gemini ve DeepSeek ile role dayalı ortak çalışma
Birden fazla bakış açısı, özellikle ilgi çekici ve düşünceli ödevler hazırlarken çok değerlidir. Şimdi, ödev oluşturmak için farklı rollere sahip iki farklı modelden yararlanan çoklu aracı sistem oluşturacağız: Biri işbirliğini teşvik ederken diğeri kendi kendine çalışmayı teşvik edecek. İş akışının sabit bir rotayı izlediği "tek seferlik" bir mimari kullanacağız.
Gemini ile ödev oluşturma
Öncelikle, ortak çalışmaya odaklanan ödevler oluşturmak için Gemini işlevini ayarlayacağız. assignment klasöründe bulunan gemini.py dosyasını düzenleyin.
👉Aşağıdaki kodu gemini.py dosyasının sonuna yapıştırın:
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()
Ödev oluşturmak için Gemini modelini kullanır.
Gemini Ajanı'nı test etmeye hazırız.
👉Ortamı ayarlamak için terminalde şu komutları çalıştırın:
cd ~/aidemy-bootstrap/assignment
gcloud config set project $(cat ~/project_id.txt)
export PROJECT_ID=$(gcloud config get project)
python -m venv env
source env/bin/activate
pip install -r requirements.txt
👉Test etmek için çalıştırabilirsiniz:
python gemini.py
Çıkışta daha fazla grup çalışması içeren bir ödev görmeniz gerekir. En sondaki onaylama testi de sonuçları verir.
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 ile durdurun ve test kodunu temizleyin. gemini.py içinden aşağıdaki kodu KALDIRIN:
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 Assignment Generator'ı yapılandırma
Bulut tabanlı yapay zeka platformları kullanışlı olsa da LLM'leri kendi sunucularınızda barındırmak, veri gizliliğini korumak ve veri egemenliğini sağlamak için çok önemlidir. En küçük DeepSeek modelini (1,5 milyar parametre) bir Cloud Compute Engine örneğine dağıtacağız. Google'ın Vertex AI platformunda veya GKE örneğinizde barındırmak gibi başka yöntemler de vardır ancak bu sadece yapay zeka aracıları ile ilgili bir atölye çalışması olduğundan ve sizi burada sonsuza kadar tutmak istemediğimden en basit yöntemi kullanacağız. Ancak ilgileniyorsanız ve diğer seçenekleri incelemek istiyorsanız deepseek-vertexai.py dosyasını inceleyin. Bu dosyada, Vertex AI'da dağıtılan modellerle nasıl etkileşim kurulacağına dair örnek bir kod verilmektedir.

👉Kendi kendine barındırılan bir LLM platformu olan Ollama'yı oluşturmak için terminalde şu komutu çalıştırın:
cd ~/aidemy-bootstrap/assignment
gcloud config set project $(cat ~/project_id.txt)
gcloud compute instances create ollama-instance \
--image-family=ubuntu-2204-lts \
--image-project=ubuntu-os-cloud \
--machine-type=e2-standard-4 \
--zone=us-central1-a \
--metadata-from-file startup-script=startup.sh \
--boot-disk-size=50GB \
--tags=ollama \
--scopes=https://www.googleapis.com/auth/cloud-platform
Compute Engine örneğinin çalıştığını doğrulamak için:
Google Cloud Console'da Compute Engine > "VM instances"a (Sanal makine örnekleri) gidin. ollama-instance öğesinin çalıştığını gösteren yeşil bir onay işaretiyle listelendiğini görürsünüz. Göremiyorsanız bölgenin us-central1 olduğundan emin olun. Bu seçeneği görmüyorsanız aramanız gerekebilir.

👉En küçük DeepSeek modelini yükleyip test edeceğiz. Cloud Shell Düzenleyici'ye geri dönüp Yeni bir terminalde aşağıdaki komutu çalıştırarak GCE örneğine SSH ile bağlanın.
gcloud compute ssh ollama-instance --zone=us-central1-a
SSH bağlantısı kurulduktan sonra aşağıdaki istemlerle karşılaşabilirsiniz:
"Devam etmek istiyor musunuz (E/h)?"
Devam etmek için Y yazıp Enter tuşuna basın.
Ardından, SSH anahtarı için bir parola oluşturmanız istenebilir. Parola kullanmak istemiyorsanız varsayılanı (parola yok) kabul etmek için Enter tuşuna iki kez basın.
👉Şimdi sanal makinedesiniz. En küçük DeepSeek R1 modelini çekin ve çalışıp çalışmadığını test edin.
ollama pull deepseek-r1:1.5b
ollama run deepseek-r1:1.5b "who are you?"
👉GCE örneğinden çıkmak için SSH terminaline aşağıdakileri girin:
exit
👉Ardından, diğer hizmetlerin LLM'ye erişebilmesi için ağ politikasını ayarlayın. Bunu üretim için yapmak istiyorsanız lütfen örneğe erişimi sınırlayın. Hizmet için güvenli giriş uygulayın veya IP erişimini kısıtlayın. Çalıştır:
gcloud compute firewall-rules create allow-ollama-11434 \
--allow=tcp:11434 \
--target-tags=ollama \
--description="Allow access to Ollama on port 11434"
👉Güvenlik duvarı politikanızın doğru çalışıp çalışmadığını doğrulamak için şunu çalıştırmayı deneyin:
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
}'
Ardından, ödev aracındaki Deepseek işlevi üzerinde çalışarak bireysel çalışmaya vurgu yapan ödevler oluşturacağız.
👉assignment klasöründeki deepseek.py dosyasını düzenleyin ve sonuna aşağıdaki snippet'i ekleyin:
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()
👉Şu komutu çalıştırarak test edelim:
cd ~/aidemy-bootstrap/assignment
source env/bin/activate
gcloud config set project $(cat ~/project_id.txt)
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
Daha fazla kendi kendine çalışma içeriği olan bir ödev görürsünüz.
**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 durdurmak ve test kodunu temizlemek için. deepseek.py içinden aşağıdaki kodu KALDIRIN:
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()
Şimdi, her iki görevi yeni bir görevde birleştirmek için aynı Gemini modelini kullanacağız. assignment klasöründe bulunan gemini.py dosyasını düzenleyin.
👉Aşağıdaki kodu gemini.py dosyasının sonuna yapıştırın:
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
Her iki modelin güçlü yönlerini birleştirmek için LangGraph'ı kullanarak tanımlanmış bir iş akışı düzenleyeceğiz. Bu iş akışı üç adımdan oluşur: İlk olarak Gemini modeli, ortak çalışmaya odaklanan bir ödev oluşturur. İkinci olarak DeepSeek modeli, bireysel çalışmayı vurgulayan bir ödev oluşturur. Son olarak Gemini, bu iki ödevi tek ve kapsamlı bir ödevde birleştirir. Adımların sırasını LLM karar verme süreci olmadan önceden tanımladığımız için bu, tek ve kullanıcı tanımlı bir düzenleme olarak kabul edilir.

👉Aşağıdaki kodu main.pydosyasının sonuna, assignment klasörünün altına yapıştırın:
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 TestCreateAssignment(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 işlevini ilk kez test etmek ve Gemini ile DeepSeek'i birleştiren iş akışının çalıştığını onaylamak için aşağıdaki komutu çalıştırın:
cd ~/aidemy-bootstrap/assignment
source env/bin/activate
pip install -r requirements.txt
python main.py
Öğrenci çalışmalarında ve öğrenci grup çalışmalarında her iki modeli de kendi bakış açısıyla birleştiren bir şey görmelisiniz.
**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 durdurmak ve test kodunu temizlemek için. main.py içinden aşağıdaki kodu KALDIRIN:
import unittest
class TestCreateAssignment(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()

Ödev oluşturma sürecini otomatik hale getirmek ve yeni öğretim planlarına yanıt vermek için mevcut etkinliğe dayalı mimariden yararlanacağız. Aşağıdaki kod, yeni bir eğitim planı "plan" adlı Pub/Sub konusuna yayınlandığında tetiklenecek bir Cloud Run işlevi (generate_assignment) tanımlar.
👉assignment klasöründeki main.py dosyasının sonuna aşağıdaki kodu ekleyin:
@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
Yerel olarak test etme
Google Cloud'a dağıtmadan önce Cloud Run işlevini yerel olarak test etmeniz iyi bir uygulamadır. Bu sayede daha hızlı yineleme ve daha kolay hata ayıklama yapılabilir.
Öncelikle, oluşturulan ödev dosyalarını depolamak için bir Cloud Storage paketi oluşturun ve hizmet hesabına pakete erişim izni verin. Terminalde aşağıdaki komutları çalıştırın:
👉ÖNEMLİ: "aidemy-assignment-" ile başlayan benzersiz bir ASSIGNMENT_BUCKET adı tanımladığınızdan emin olun. Bu benzersiz ad, Cloud Storage paketinizi oluştururken adlandırma çakışmalarını önlemek için çok önemlidir. (<YOUR_NAME> yerine rastgele bir kelime girin)
export ASSIGNMENT_BUCKET=aidemy-assignment-<YOUR_NAME> #Name must be unqiue
👉Ardından şu komutu çalıştırın:
gcloud config set project $(cat ~/project_id.txt)
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"
👉Şimdi Cloud Run işlevi emülatörünü başlatın:
cd ~/aidemy-bootstrap/assignment
functions-framework \
--target generate_assignment \
--signature-type=cloudevent \
--source main.py
👉Emülatör bir terminalde çalışırken Cloud Shell'de ikinci bir terminal açın. Bu ikinci terminalde, yeni bir eğitim planının yayınlanmasını simüle etmek için emülatöre bir test CloudEvent'i gönderin:

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=="
}
}'
Yanıtı beklerken boş boş bakmak yerine diğer Cloud Shell terminaline geçin. İşlevinizin ilerleme durumunu ve oluşturduğu çıkış ya da hata mesajlarını emülatörün terminalinde gözlemleyebilirsiniz. 😁
Curl komutu "OK" yazmalıdır (yeni satır olmadan, bu nedenle "OK" terminal kabuğu isteminizle aynı satırda görünebilir).
Ödevin başarıyla oluşturulup depolandığını onaylamak için Google Cloud Console'a gidip Depolama > "Cloud Storage"a gidin. Oluşturduğunuz aidemy-assignment paketini seçin. Pakette assignment-{random number}.txt adlı bir metin dosyası görürsünüz. Dosyayı indirip içeriğini doğrulamak için tıklayın. Bu işlem, yeni dosyanın yeni oluşturulan ödevi içerdiğini doğrular.

👉Emülatörün çalıştığı terminalde çıkmak için ctrl+c yazın. İkinci terminali kapatın. 👉Ayrıca, emülatörün çalıştığı terminalde sanal ortamdan çıkın.
deactivate

👉Ardından, atama aracısını buluta dağıtacağız.
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
gcloud config set project $(cat ~/project_id.txt)
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'a gidip Cloud Run'a giderek dağıtımı doğrulayın. courses-agent adlı yeni bir hizmetin listelendiğini görmeniz gerekir. 
Ödev oluşturma iş akışı artık uygulanıp test edildiği ve kullanıma sunulduğu için bir sonraki adıma geçebiliriz: Bu ödevleri öğrenci portalında erişilebilir hale getirme.
14. İSTEĞE BAĞLI: Gemini ve DeepSeek ile role dayalı ortak çalışma - Devam
Dinamik web sitesi oluşturma
Öğrenci portalını iyileştirmek ve daha ilgi çekici hale getirmek için ödev sayfalarında dinamik HTML oluşturma özelliğini kullanacağız. Amaç, yeni bir ödev oluşturulduğunda portalı otomatik olarak yeni ve görsel açıdan çekici bir tasarımla güncellemek. Bu, daha dinamik ve ilgi çekici bir kullanıcı deneyimi oluşturmak için LLM'nin kodlama özelliklerinden yararlanır.

👉Cloud Shell Düzenleyici'de, portal klasöründeki render.py dosyasını düzenleyin,
def render_assignment_page():
return ""
Aşağıdaki kod snippet'iyle:
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)}"
Ödev için HTML'yi dinamik olarak oluşturmak üzere Gemini modelini kullanır. Ödev içeriğini giriş olarak alır ve Gemini'a yaratıcı bir tarzda görsel açıdan çekici bir HTML sayfası oluşturmasını söylemek için istem kullanır.
Ardından, ödev paketine yeni bir doküman eklendiğinde tetiklenecek bir uç nokta oluşturacağız:
👉Portal klasöründe app.py dosyasını düzenleyin ve ## REPLACE ME! RENDER ASSIGNMENT satırını aşağıdaki kodla DEĞİŞTİRİN:
@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
Tetiklendiğinde, istek verilerinden dosya adını ve paket adını alır, Cloud Storage'dan ödev içeriğini indirir ve HTML'yi oluşturmak için render_assignment_page işlevini çağırır.
👉Yerel olarak çalıştırmaya devam edeceğiz:
cd ~/aidemy-bootstrap/portal
source env/bin/activate
python app.py
👉Cloud Shell penceresinin üst kısmındaki "Web önizlemesi" menüsünden "8080 bağlantı noktasında önizle"yi seçin. Bu işlem, uygulamanızı yeni bir tarayıcı sekmesinde açar. Gezinme çubuğundaki Ödev bağlantısına gidin. Bu noktada boş bir sayfa görmeniz gerekir. Bu, atama aracısı ile içerikleri dinamik olarak doldurmak için portal arasında henüz iletişim köprüsü kurmadığımızdan beklenen bir davranıştır.

Ctrl+C tuşuna basarak komut dosyasını durdurun.
👉Bu değişiklikleri dahil etmek ve güncellenen kodu dağıtmak için portal temsilcisi görüntüsünü yeniden oluşturup gönderin:
cd ~/aidemy-bootstrap/portal/
gcloud config set project $(cat ~/project_id.txt)
export PROJECT_ID=$(gcloud config get project)
docker build -t gcr.io/${PROJECT_ID}/aidemy-portal .
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
👉Yeni görüntüyü gönderdikten sonra Cloud Run hizmetini yeniden dağıtın. Cloud Run güncellemesini zorlamak için aşağıdaki komut dosyasını çalıştırın:
gcloud config set project $(cat ~/project_id.txt)
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
👉Şimdi, ödev paketinde oluşturulan (sonlandırılan) tüm yeni nesneleri dinleyen bir Eventarc tetikleyicisi dağıtacağız. Bu tetikleyici, yeni bir ödev dosyası oluşturulduğunda portal hizmetindeki /render_assignment uç noktasını otomatik olarak çağırır.
gcloud config set project $(cat ~/project_id.txt)
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"
Tetikleyicinin başarıyla oluşturulduğunu doğrulamak için Google Cloud Console'da Eventarc Tetikleyicileri sayfasına gidin. Tabloda portal-assignment-trigger listelenmiş olmalıdır. Ayrıntılarını görüntülemek için tetikleyici adını tıklayın. 
Yeni tetikleyicinin etkinleşmesi 2-3 dakika sürebilir.
Dinamik görev oluşturma özelliğini kullanırken görmek için planlayıcı aracınızın URL'sini bulmak üzere aşağıdaki komutu çalıştırın (elinizde yoksa):
gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner
Portal aracınızın URL'sini bulma:
gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep portal
Planlayıcı temsilcisinde yeni bir ders planı oluşturun.

Birkaç dakika sonra (ses oluşturma, ödev oluşturma ve HTML oluşturma işlemlerinin tamamlanması için) öğrenci portalına gidin.
👉Gezinme çubuğundaki "Ödev" bağlantısını tıklayın. Dinamik olarak oluşturulmuş HTML'ye sahip yeni bir ödev görmeniz gerekir. Her eğitim planı oluşturulduğunda dinamik bir ödev olmalıdır.

Aidemy çoklu aracı sistemini tamamladığınız için tebrik ederiz. Aşağıdaki konularda pratik deneyim ve değerli bilgiler edindiniz:
- Çoklu aracı sistemlerinin avantajları (ör. modülerlik, ölçeklenebilirlik, uzmanlaşma ve basitleştirilmiş bakım).
- Tepki veren ve gevşek bağlı uygulamalar oluşturmak için etkinliğe dayalı mimarilerin önemi.
- LLM'lerin stratejik kullanımı, doğru modelin görevle eşleştirilmesi ve gerçek dünyada etki yaratmak için araçlarla entegre edilmesi.
- Ölçeklenebilir ve güvenilir çözümler oluşturmak için Google Cloud hizmetlerini kullanan bulutta yerel geliştirme uygulamaları.
- Veri gizliliğini ve sağlayıcı çözümlerine alternatif olarak kendi kendine barındırma modellerini dikkate almanın önemi.
Artık Google Cloud'da gelişmiş yapay zeka destekli uygulamalar oluşturmak için sağlam bir temeliniz var.
15. Zorluklar ve Sonraki Adımlar
Aidemy çoklu temsilci sistemini oluşturduğunuz için tebrik ederiz. Yapay zeka destekli eğitim için sağlam bir temel oluşturdunuz. Şimdi de bu aracın özelliklerini daha da genişletmek ve gerçek dünyadaki ihtiyaçları karşılamak için bazı zorlukları ve gelecekteki olası geliştirmeleri ele alalım:
Canlı soru-cevap ile etkileşimli öğrenme:
- Yarışma: Öğrenciler için anlık soru-cevap özelliği oluşturmak üzere Gemini 2'nin Live API'sinden yararlanabilir misiniz? Öğrencilerin soru sorabildiği ve anında yapay zeka destekli yanıtlar alabildiği sanal bir sınıf düşünün.
Otomatik Ödev Gönderme ve Notlandırma:
- Sorun: Öğrencilerin ödevlerini dijital olarak göndermesine ve yapay zeka tarafından otomatik olarak notlandırılmasına olanak tanıyan, intihali tespit edip önleyecek bir mekanizmaya sahip bir sistem tasarlayıp uygulayın. Bu yarışma, notlandırma ve intihal tespit süreçlerinin doğruluğunu ve güvenilirliğini artırmak için Almayla Artırılmış Üretim (RAG)'i keşfetmek için harika bir fırsat sunuyor.

16. Temizleme
Aidemy çoklu aracı sistemimizi oluşturup incelediğimize göre artık Google Cloud ortamımızı temizleme zamanı.
👉Cloud Run hizmetlerini silme
gcloud run services delete aidemy-planner --region=us-central1 --quiet
gcloud run services delete aidemy-portal --region=us-central1 --quiet
gcloud run services delete courses-agent --region=us-central1 --quiet
gcloud run services delete book-provider --region=us-central1 --quiet
gcloud run services delete assignment-agent --region=us-central1 --quiet
👉Eventarc tetikleyicisini silme
gcloud eventarc triggers delete portal-assignment-trigger --location=us --quiet
gcloud eventarc triggers delete plan-topic-trigger --location=us-central1 --quiet
gcloud eventarc triggers delete portal-assignment-trigger --location=us-central1 --quiet
ASSIGNMENT_AGENT_TRIGGER=$(gcloud eventarc triggers list --project="$PROJECT_ID" --location=us-central1 --filter="name:assignment-agent" --format="value(name)")
COURSES_AGENT_TRIGGER=$(gcloud eventarc triggers list --project="$PROJECT_ID" --location=us-central1 --filter="name:courses-agent" --format="value(name)")
gcloud eventarc triggers delete $ASSIGNMENT_AGENT_TRIGGER --location=us-central1 --quiet
gcloud eventarc triggers delete $COURSES_AGENT_TRIGGER --location=us-central1 --quiet
👉Pub/Sub konusunu silme
gcloud pubsub topics delete plan --project="$PROJECT_ID" --quiet
👉Cloud SQL örneğini silme
gcloud sql instances delete aidemy --quiet
👉Artifact Registry deposunu silme
gcloud artifacts repositories delete agent-repository --location=us-central1 --quiet
👉Secret Manager gizli anahtarlarını silme
gcloud secrets delete db-user --quiet
gcloud secrets delete db-pass --quiet
gcloud secrets delete db-name --quiet
👉Compute Engine örneğini silin (Deepseek için oluşturulduysa)
gcloud compute instances delete ollama-instance --zone=us-central1-a --quiet
👉Deepseek örneği için güvenlik duvarı kuralını silin.
gcloud compute firewall-rules delete allow-ollama-11434 --quiet
👉Cloud Storage paketlerini silme
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 rm -r gs://$COURSE_BUCKET_NAME
gsutil rm -r gs://$ASSIGNMENT_BUCKET
