Aidemy: Membangun Sistem Multi-Agen dengan LangGraph, EDA, dan AI Generatif di Google Cloud

1. Pengantar

Halo. Jadi, Anda tertarik dengan ide agen – asisten kecil yang dapat menyelesaikan berbagai hal untuk Anda tanpa perlu repot, bukan? Keren! Namun, mari kita hadapi kenyataan, satu agen tidak selalu cukup, terutama saat Anda menangani project yang lebih besar dan lebih kompleks. Anda mungkin memerlukan tim yang terdiri dari mereka. Di sinilah sistem multi-agen berperan.

Agen, jika didukung oleh LLM, akan memberi Anda fleksibilitas yang luar biasa dibandingkan dengan hard coding lama. Namun, dan selalu ada tetapi, mereka memiliki serangkaian tantangan rumitnya sendiri. Dan itulah yang akan kita bahas dalam workshop ini.

judul

Berikut hal-hal yang akan Anda pelajari – anggaplah ini sebagai cara meningkatkan level permainan agen Anda:

Membuat Agen Pertama Anda dengan LangGraph: Kita akan mulai membuat agen Anda sendiri menggunakan LangGraph, sebuah framework populer. Anda akan mempelajari cara membuat alat yang terhubung ke database, memanfaatkan Gemini 2 API terbaru untuk beberapa penelusuran internet, dan mengoptimalkan perintah dan respons, sehingga agen Anda dapat berinteraksi tidak hanya dengan LLM, tetapi juga layanan yang ada. Kami juga akan menunjukkan cara kerja panggilan fungsi.

Orchestrasi Agen, Cara Anda: Kami akan mempelajari berbagai cara untuk mengatur agen Anda, mulai dari jalur lurus sederhana hingga skenario multi-jalur yang lebih kompleks. Anggaplah hal ini sebagai mengarahkan alur tim agen Anda.

Sistem Multi-Agen: Anda akan menemukan cara menyiapkan sistem tempat agen dapat berkolaborasi, dan menyelesaikan berbagai hal bersama-sama – semua berkat arsitektur berbasis peristiwa.

LLM Freedom – Gunakan yang Terbaik untuk Tugas: Kita tidak terpaku pada satu LLM saja. Anda akan melihat cara menggunakan beberapa LLM, dengan menetapkan peran yang berbeda untuk meningkatkan kemampuan pemecahan masalah menggunakan "model pemikiran" yang keren.

Konten Dinamis? Tidak Masalah.: Bayangkan agen Anda membuat konten dinamis yang disesuaikan secara khusus untuk setiap pengguna, secara real time. Kami akan menunjukkan caranya.

Mengirimnya ke Cloud dengan Google Cloud: Jangan hanya bermain-main di notebook. Kami akan menunjukkan cara merancang dan men-deploy sistem multi-agen di Google Cloud agar siap digunakan di dunia nyata.

Project ini akan menjadi contoh yang baik tentang cara menggunakan semua teknik yang telah kita bahas.

2. Arsitektur

Menjadi pengajar atau bekerja di bidang pendidikan dapat memberikan kepuasan yang luar biasa, tetapi mari kita hadapi kenyataan, beban kerja, terutama semua pekerjaan persiapan, bisa jadi menantang. Selain itu, sering kali tidak ada cukup staf dan les bisa mahal. Itulah sebabnya kami mengusulkan asisten pengajar yang didukung AI. Alat ini dapat meringankan beban pendidik dan membantu menjembatani kesenjangan yang disebabkan oleh kekurangan staf dan kurangnya les yang terjangkau.

Asisten pengajar AI kami dapat membuat rencana pelajaran mendetail, kuis seru, recap audio yang mudah diikuti, dan tugas yang dipersonalisasi. Hal ini memungkinkan pengajar berfokus pada hal yang mereka kuasai: terhubung dengan siswa dan membantu mereka menyukai proses belajar.

Sistem ini memiliki dua situs: satu untuk pengajar membuat rencana pelajaran untuk minggu mendatang,

Planner

dan satu untuk siswa mengakses kuis, recap audio, dan tugas. Portal

Baiklah, mari kita bahas arsitektur yang mendukung asisten pengajar kami, Aidemy. Seperti yang dapat Anda lihat, kami telah membaginya menjadi beberapa komponen utama, yang semuanya bekerja sama untuk mewujudkannya.

Arsitektur

Elemen dan Teknologi Arsitektur Utama:

Google Cloud Platform (GCP): Berperan penting dalam seluruh sistem:

  • Vertex AI: Mengakses LLM Gemini Google.
  • Cloud Run: Platform serverless untuk men-deploy agen dan fungsi dalam container.
  • Cloud SQL: Database PostgreSQL untuk data kurikulum.
  • Pub/Sub & Eventarc: Dasar arsitektur berbasis peristiwa, yang memungkinkan komunikasi asinkron antar-komponen.
  • Cloud Storage: Menyimpan rekap audio dan file tugas.
  • Secret Manager: Mengelola kredensial database dengan aman.
  • Artifact Registry: Menyimpan image Docker untuk agen.
  • Compute Engine: Untuk men-deploy LLM yang dihosting sendiri, bukan mengandalkan solusi vendor

LLM: "Otak" sistem:

  • Model Gemini Google: (Gemini 1.0 Pro, Gemini 2 Flash, Gemini 2 Flash Thinking, Gemini 1.5-pro) Digunakan untuk perencanaan pelajaran, pembuatan konten, pembuatan HTML dinamis, penjelasan kuis, dan menggabungkan tugas.
  • DeepSeek: Digunakan untuk tugas khusus dalam membuat tugas studi mandiri

LangChain & LangGraph: Framework untuk Pengembangan Aplikasi LLM

  • Memfasilitasi pembuatan alur kerja multi-agen yang kompleks.
  • Memungkinkan orkestrasi alat yang cerdas (panggilan API, kueri database, penelusuran web).
  • Mengimplementasikan arsitektur berbasis peristiwa untuk skalabilitas dan fleksibilitas sistem.

Pada dasarnya, arsitektur kami menggabungkan kecanggihan LLM dengan data terstruktur dan komunikasi berbasis peristiwa, yang semuanya berjalan di Google Cloud. Hal ini memungkinkan kita membuat asisten pengajar yang skalabel, andal, dan efektif.

3. Sebelum memulai

Di Konsol Google Cloud, di halaman pemilih project, pilih atau buat project Google Cloud. Pastikan penagihan diaktifkan untuk project Cloud Anda. Pelajari cara memeriksa apakah penagihan diaktifkan pada project.

👉Klik Activate Cloud Shell di bagian atas konsol Google Cloud (Ikon berbentuk terminal di bagian atas panel Cloud Shell), klik tombol "Open Editor" (terlihat seperti folder terbuka dengan pensil). Tindakan ini akan membuka Editor Kode Cloud Shell di jendela. Anda akan melihat file explorer di sisi kiri.

Cloud Shell

👉Klik tombol Login Cloud Code di status bar bawah seperti yang ditunjukkan. Otorisasi plugin seperti yang ditunjukkan. Jika Anda melihat Cloud Code - no project di status bar, pilih opsi tersebut, lalu di menu drop-down 'Select a Google Cloud Project', pilih Project Google Cloud tertentu dari daftar project yang Anda buat.

Project login

👉Buka terminal di IDE cloud, Terminal Baru

👉Di terminal, pastikan Anda sudah diautentikasi dan project ditetapkan ke project ID Anda menggunakan perintah berikut:

gcloud auth list

👉Lalu jalankan:

gcloud config set project <YOUR_PROJECT_ID>

👉Jalankan perintah berikut untuk mengaktifkan Google Cloud API yang diperlukan:

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

Proses ini mungkin memerlukan waktu beberapa menit.

Mengaktifkan Gemini Code Assist di Cloud Shell IDE

Klik tombol Code Assist di panel kiri seperti yang ditunjukkan, lalu pilih project Google Cloud yang benar untuk terakhir kalinya. Jika Anda diminta untuk mengaktifkan Cloud AI Companion API, lakukan hal tersebut dan lanjutkan. Setelah memilih project Google Cloud, pastikan Anda dapat melihatnya di pesan status Cloud Code di status bar dan Anda juga telah mengaktifkan Code Assist di sebelah kanan, di status bar seperti yang ditunjukkan di bawah:

Mengaktifkan codeassist

Menyiapkan izin

👉Menyiapkan izin akun layanan

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"

Berikan Izin 👉Cloud Storage (Baca/Tulis):

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

👉Pub/Sub (Publikasi/Penerimaan):

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 (Baca/Tulis):

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

👉Eventarc (Menerima Peristiwa):

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 (Pengguna):

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

👉Secret Manager (Baca):

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

👉Validasi hasil di konsol IAMKonsol IAM

4. Membuat agen pertama

Sebelum mempelajari sistem multi-agen yang kompleks, kita perlu membuat elemen penyusun dasar: satu agen yang berfungsi. Di bagian ini, kita akan melakukan langkah pertama dengan membuat agen "penyedia buku" sederhana. Agen penyedia buku mengambil kategori sebagai input dan menggunakan LLM Gemini untuk membuat buku representasi JSON dalam kategori tersebut. Kemudian, rekomendasi buku ini ditayangkan sebagai endpoint REST API .

Penyedia Buku

👉Di tab browser lain, buka Konsol Google Cloud di browser web Anda,di menu navigasi (☰), buka "Cloud Run". Klik tombol "+ ... TULIS FUNGSI".

Buat Fungsi

👉Selanjutnya, kita akan mengonfigurasi setelan dasar Cloud Run Function:

  • Nama layanan: book-provider
  • Region: us-central1
  • Runtime: Python 3.12
  • Autentikasi: Allow unauthenticated invocations ke Diaktifkan.

👉Biarkan setelan lainnya dalam setelan default lalu klik Create. Tindakan ini akan mengarahkan Anda ke editor kode sumber.

Anda akan melihat file main.py dan requirements.txt yang telah diisi otomatis.

main.py akan berisi logika bisnis fungsi, requirements.txt akan berisi paket yang diperlukan.

👉Sekarang kita siap untuk menulis beberapa kode. Namun, sebelum mempelajarinya lebih lanjut, mari kita lihat apakah Gemini Code Assist dapat membantu kita memulai. Kembali ke Cloud Shell Editor, klik ikon Gemini Code Assist, lalu tempel permintaan berikut ke kotak perintah: Gemini Code Assist

Use the functions_framework library to be deployable as an HTTP function. 
Accept a request with category and number_of_book parameters (either in JSON body or query string). 
Use langchain and gemini to generate the data for book with fields bookname, author, publisher, publishing_date. 
Use pydantic to define a Book model with the fields: bookname (string, description: "Name of the book"), author (string, description: "Name of the author"), publisher (string, description: "Name of the publisher"), and publishing_date (string, description: "Date of publishing"). 
Use langchain and gemini model to generate book data. the output should follow the format defined in Book model. 

The logic should use JsonOutputParser from langchain to enforce output format defined in Book Model. 
Have a function get_recommended_books(category) that internally uses langchain and gemini to return a single book object. 
The main function, exposed as the Cloud Function, should call get_recommended_books() multiple times (based on number_of_book) and return a JSON list of the generated book objects. 
Handle the case where category or number_of_book are missing by returning an error JSON response with a 400 status code. 
return a JSON string representing the recommended books. use os library to retrieve GOOGLE_CLOUD_PROJECT env var. Use ChatVertexAI from langchain for the LLM call

Code Assist kemudian akan menghasilkan solusi potensial, yang menyediakan kode sumber dan file dependensi requirements.txt.

Sebaiknya bandingkan kode yang dihasilkan Code Assist dengan solusi yang benar dan telah diuji yang diberikan di bawah. Hal ini memungkinkan Anda mengevaluasi efektivitas alat dan mengidentifikasi potensi perbedaan. Meskipun LLM tidak boleh dipercaya secara membabi buta, Code Assist dapat menjadi alat yang bagus untuk membuat prototipe dengan cepat dan menghasilkan struktur kode awal, serta harus digunakan untuk memulai dengan baik.

Karena ini adalah workshop, kita akan melanjutkan dengan kode terverifikasi yang diberikan di bawah. Namun, jangan ragu untuk bereksperimen dengan kode yang dihasilkan Code Assist sesuai waktu Anda sendiri untuk mendapatkan pemahaman yang lebih mendalam tentang kemampuan dan keterbatasannya.

👉Kembali ke editor kode sumber Fungsi Cloud Run (di tab browser lain). Ganti konten main.py yang ada dengan kode yang diberikan di bawah ini dengan hati-hati:

import functions_framework
import json
from flask import Flask, jsonify, request
from langchain_google_vertexai import ChatVertexAI
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.prompts import PromptTemplate
from pydantic import BaseModel, Field
import os

class Book(BaseModel):
    bookname: str = Field(description="Name of the book")
    author: str = Field(description="Name of the author")
    publisher: str = Field(description="Name of the publisher")
    publishing_date: str = Field(description="Date of publishing")


project_id = os.environ.get("GOOGLE_CLOUD_PROJECT")  

llm = ChatVertexAI(model_name="gemini-1.0-pro")

def get_recommended_books(category):
    """
    A simple book recommendation function. 

    Args:
        category (str): category

    Returns:
        str: A JSON string representing the recommended books.
    """
    parser = JsonOutputParser(pydantic_object=Book)
    question = f"Generate a random made up book on {category} with bookname, author and publisher and publishing_date"

    prompt = PromptTemplate(
        template="Answer the user query.\n{format_instructions}\n{query}\n",
        input_variables=["query"],
        partial_variables={"format_instructions": parser.get_format_instructions()},
    )
    
    chain = prompt | llm | parser
    response = chain.invoke({"query": question})

    return  json.dumps(response)
    

@functions_framework.http
def recommended(request):
    request_json = request.get_json(silent=True) # Get JSON data
    if request_json and 'category' in request_json and 'number_of_book' in request_json:
        category = request_json['category']
        number_of_book = int(request_json['number_of_book'])
    elif request.args and 'category' in request.args and 'number_of_book' in request.args:
        category = request.args.get('category')
        number_of_book = int(request.args.get('number_of_book'))

    else:
        return jsonify({'error': 'Missing category or number_of_book parameters'}), 400


    recommendations_list = []
    for i in range(number_of_book):
        book_dict = json.loads(get_recommended_books(category))
        print(f"book_dict=======>{book_dict}")
    
        recommendations_list.append(book_dict)

    
    return jsonify(recommendations_list)

👉Ganti konten requirements.txt dengan kode berikut:

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

👉kita akan menetapkan Titik entri fungsi: recommended

03-02-function-create.png

👉Klik SAVE AND DEPLOY untuk men-deploy Fungsi. Tunggu hingga proses deployment selesai. Konsol Cloud akan menampilkan status. Proses ini mungkin perlu waktu beberapa menit.

teks alternatif 👉Setelah di-deploy, kembali ke cloud shell editor, di terminal jalankan:

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

Tindakan ini akan menampilkan beberapa data buku dalam format JSON.

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

Selamat! Anda telah berhasil men-deploy Cloud Run Function. Ini adalah salah satu layanan yang akan kami integrasikan saat mengembangkan agen Aidemy.

5. Alat Pembuatan: Menghubungkan Agen ke Layanan dan Data RESTFUL

Mari kita download Project Bootstrap Skeleton. Pastikan Anda berada di Editor Cloud Shell. Di terminal, jalankan,

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

Setelah menjalankan perintah ini, folder baru bernama aidemy-bootstrap akan dibuat di lingkungan Cloud Shell Anda.

Di panel Penjelajah Editor Cloud Shell (biasanya di sisi kiri), Anda akan melihat folder yang dibuat saat meng-clone repositori Git aidemy-bootstrap. Buka folder root project Anda di Explorer. Anda akan menemukan subfolder planner di dalamnya, buka juga subfolder tersebut. project explorer

Mari kita mulai membuat alat yang akan digunakan agen kita agar benar-benar bermanfaat. Seperti yang Anda ketahui, LLM sangat mahir dalam bernalar dan menghasilkan teks, tetapi LLM memerlukan akses ke referensi eksternal untuk melakukan tugas di dunia nyata dan memberikan informasi yang akurat dan terbaru. Anggap alat ini sebagai "Swiss Army knife" agen, yang memberinya kemampuan untuk berinteraksi dengan dunia.

Saat membuat agen, Anda dapat dengan mudah melakukan hard code pada banyak detail. Hal ini akan membuat agen yang tidak fleksibel. Sebagai gantinya, dengan membuat dan menggunakan alat, agen memiliki akses ke logika atau sistem eksternal yang memberinya manfaat dari LLM dan pemrograman tradisional.

Di bagian ini, kita akan membuat fondasi untuk agen perencana, yang akan digunakan guru untuk membuat rencana pelajaran. Sebelum agen mulai membuat rencana, kita ingin menetapkan batasan dengan memberikan detail selengkapnya tentang subjek dan topik. Kita akan membuat tiga alat:

  1. Panggilan Restful API: Berinteraksi dengan API yang sudah ada untuk mengambil data.
  2. Kueri Database: Mengambil data terstruktur dari database Cloud SQL.
  3. Google Penelusuran: Mengakses informasi real-time dari web.

Mengambil Rekomendasi Buku dari API

Pertama, mari kita buat alat yang mengambil rekomendasi buku dari API book-provider yang kita deploy di bagian sebelumnya. Ini menunjukkan cara agen dapat memanfaatkan layanan yang ada.

Merekomendasikan buku

Di Cloud Shell Editor, buka project aidemy-bootstrap yang Anda clone di bagian sebelumnya. 👉Edit book.py di folder planner, lalu tempelkan kode berikut:

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

Penjelasan:

  • recommend_book(query: str): Fungsi ini menggunakan kueri pengguna sebagai input.
  • Interaksi LLM: Menggunakan LLM untuk mengekstrak kategori dari kueri. Ini menunjukkan cara menggunakan LLM untuk membantu membuat parameter alat.
  • Panggilan API: Membuat permintaan POST ke API penyedia buku, yang meneruskan kategori dan jumlah buku yang diinginkan.

👉Untuk menguji fungsi baru ini, tetapkan variabel lingkungan, jalankan :

cd ~/aidemy-bootstrap/planner/
export BOOK_PROVIDER_URL=$(gcloud run services describe book-provider --region=us-central1 --project=$PROJECT_ID --format="value(status.url)")

👉Instal dependensi dan jalankan kode untuk memastikannya berfungsi, jalankan:

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

Abaikan jendela pop-up peringatan Git.

Anda akan melihat string JSON yang berisi rekomendasi buku yang diambil dari API penyedia buku.

[{"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"}]

Jika Anda melihat ini, berarti alat pertama berfungsi dengan benar.

Daripada membuat panggilan RESTful API secara eksplisit dengan parameter tertentu, kita menggunakan bahasa alami ("Saya sedang mengikuti kursus..."). Agen kemudian mengekstrak parameter yang diperlukan (seperti kategori) secara cerdas menggunakan NLP, yang menyoroti cara agen memanfaatkan natural language understanding untuk berinteraksi dengan API.

compare call

👉Hapus kode pengujian berikut dari book.py

if __name__ == "__main__":
    print(recommend_book("I'm doing a course for my 5th grade student on Math Geometry, I'll need to recommend few books come up with a teach plan, few quizes and also a homework assignment."))

Mendapatkan Data Kurikulum dari Database

Selanjutnya, kita akan membuat alat yang mengambil data kurikulum terstruktur dari database PostgreSQL Cloud SQL. Hal ini memungkinkan agen mengakses sumber informasi yang andal untuk perencanaan pelajaran.

create db

👉Jalankan perintah berikut di terminal untuk membuat instance Cloud SQL bernama aidemy . Proses ini dapat memerlukan waktu.

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

👉Selanjutnya, buat database bernama aidemy-db di instance baru.

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

Mari kita verifikasi instance di Cloud SQL di Konsol Google Cloud. Anda akan melihat instance Cloud SQL bernama aidemy tercantum. Klik nama instance untuk melihat detailnya. Di halaman detail instance Cloud SQL, klik "SQL Studio" di menu navigasi sebelah kiri. Tindakan ini akan membuka tab baru.

Klik untuk terhubung ke database. Login ke SQL Studio

Pilih aidemy-db sebagai database. Masukkan postgres sebagai pengguna dan 1234qwer sebagai sandi. login sql studio

👉Di editor kueri SQL Studio, tempel kode SQL berikut:

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.');

Kode SQL ini membuat tabel bernama curriculums dan menyisipkan beberapa contoh data. Klik Run untuk menjalankan kode SQL. Anda akan melihat pesan konfirmasi yang menunjukkan bahwa perintah berhasil dieksekusi.

👉Luaskan penjelajah, temukan tabel yang baru dibuat, lalu klik kueri. Tindakan ini akan membuka tab editor baru dengan SQL yang dibuat untuk Anda,

sql studio select table

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

👉Klik Run.

Tabel hasil akan menampilkan baris data yang Anda sisipkan di langkah sebelumnya, yang mengonfirmasi bahwa tabel dan data dibuat dengan benar.

Setelah Anda berhasil membuat database dengan data contoh kurikulum yang terisi, kita akan membuat alat untuk mengambilnya.

👉Di Cloud Code Editor, edit file curriculums.py di folder aidemy-bootstrap dan tempelkan kode berikut:

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

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

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

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

    connector = Connector()

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

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



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

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

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

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

    except Exception as e:
        print(e)
        return None

db = init_connection_pool()

Penjelasan:

  • Variabel Lingkungan: Kode mengambil kredensial database dan informasi koneksi dari variabel lingkungan (selengkapnya di bawah).
  • connect_with_connector(): Fungsi ini menggunakan Cloud SQL Connector untuk membuat koneksi yang aman ke database.
  • get_curriculum(year: int, subject: str): Fungsi ini menggunakan tahun dan mata pelajaran sebagai input, membuat kueri tabel kurikulum, dan menampilkan deskripsi kurikulum yang sesuai.

👉Sebelum dapat menjalankan kode, kita harus menetapkan beberapa variabel lingkungan. Di terminal, jalankan:

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"

👉Untuk menguji, tambahkan kode berikut ke akhir curriculums.py:

if __name__ == "__main__":
    print(get_curriculum(6, "Mathematics"))

👉Jalankan kode:

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

Anda akan melihat deskripsi kurikulum untuk Matematika kelas 6 yang dicetak ke konsol.

Expanding on fractions, ratios, algebraic thinking, and problem-solving strategies.

Jika Anda melihat deskripsi kurikulum, berarti alat database berfungsi dengan benar. Hentikan skrip dengan menekan Ctrl+C.

👉Hapus kode pengujian berikut dari curriculums.py

if __name__ == "__main__":
    print(get_curriculum(6, "Mathematics"))

👉Keluar dari lingkungan virtual, di terminal jalankan:

deactivate

6. Alat Pembuatan: Mengakses informasi real-time dari web

Terakhir, kita akan membuat alat yang menggunakan integrasi Gemini 2 dan Google Penelusuran untuk mengakses informasi real-time dari web. Hal ini membantu agen tetap mendapatkan informasi terbaru dan memberikan hasil yang relevan.

Integrasi Gemini 2 dengan Google Search API meningkatkan kemampuan agen dengan memberikan hasil penelusuran yang lebih akurat dan relevan secara kontekstual. Hal ini memungkinkan agen mengakses informasi terbaru dan mendasarkan respons mereka pada data dunia nyata, sehingga meminimalkan halusinasi. Integrasi API yang ditingkatkan juga memfasilitasi lebih banyak kueri bahasa alami, sehingga agen dapat merumuskan permintaan penelusuran yang kompleks dan bernuansa.

Telusuri

Fungsi ini menggunakan kueri penelusuran, kurikulum, mata pelajaran, dan tahun sebagai input, serta menggunakan Gemini API dan alat Google Penelusuran untuk mengambil informasi yang relevan dari internet. Jika Anda melihatnya dengan cermat, kode ini menggunakan Google Generative AI SDK untuk melakukan panggilan fungsi tanpa menggunakan framework lain.

👉Edit search.py di folder aidemy-bootstrap dan tempelkan kode berikut:

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)

Penjelasan:

  • Defining Tool - google_search_tool: Menggabungkan objek GoogleSearch dalam Alat
  • search_latest_resource(search_text: str, subject: str, year: int): Fungsi ini menggunakan kueri penelusuran, subjek, dan tahun sebagai input dan menggunakan Gemini API untuk melakukan penelusuran Google. Model Gemini
  • GenerateContentConfig: Menentukan bahwa class ini memiliki akses ke alat GoogleSearch

Model Gemini secara internal menganalisis search_text dan menentukan apakah model tersebut dapat menjawab pertanyaan secara langsung atau apakah perlu menggunakan alat GoogleSearch. Ini adalah langkah penting yang terjadi dalam proses penalaran LLM. Model telah dilatih untuk mengenali situasi yang memerlukan alat eksternal. Jika model memutuskan untuk menggunakan alat GoogleSearch, Google Generative AI SDK akan menangani pemanggilan yang sebenarnya. SDK mengambil keputusan model dan parameter yang dihasilkannya, lalu mengirimkannya ke Google Search API. Bagian ini disembunyikan dari pengguna dalam kode.

Model Gemini kemudian akan mengintegrasikan hasil penelusuran ke dalam responsnya. Model ini dapat menggunakan informasi untuk menjawab pertanyaan pengguna, membuat ringkasan, atau melakukan beberapa tugas lainnya.

👉Untuk menguji, jalankan kode:

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

Anda akan melihat respons Gemini Search API yang berisi hasil penelusuran yang terkait dengan "Syllabus for Year 5 Mathematics". Output yang tepat akan bergantung pada hasil penelusuran, tetapi akan berupa objek JSON dengan informasi tentang penelusuran.

Jika Anda melihat hasil penelusuran, berarti alat Google Penelusuran berfungsi dengan benar. Hentikan skrip dengan menekan Ctrl+C.

👉Lalu hapus bagian terakhir dalam kode.

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)

👉Keluar dari lingkungan virtual, di terminal jalankan:

deactivate

Selamat! Sekarang Anda telah membuat tiga alat canggih untuk agen perencana: konektor API, konektor database, dan alat Google Penelusuran. Alat ini akan memungkinkan agen mengakses informasi dan kemampuan yang diperlukan untuk membuat rencana pengajaran yang efektif.

7. Mengorkestrasi dengan LangGraph

Setelah membuat setiap alat, sekarang saatnya untuk mengaturnya menggunakan LangGraph. Hal ini akan memungkinkan kita membuat agen "perencana" yang lebih canggih yang dapat memutuskan alat mana yang akan digunakan dan kapan, berdasarkan permintaan pengguna.

LangGraph adalah library Python yang dirancang untuk mempermudah pembuatan aplikasi multi-aktor stateful menggunakan Model Bahasa Besar (LLM). Anggaplah ini sebagai framework untuk mengatur percakapan dan alur kerja kompleks yang melibatkan LLM, alat, dan agen lainnya.

Konsep Utama:

  • Struktur Grafik: LangGraph mewakili logika aplikasi Anda sebagai grafik terarah. Setiap node dalam grafik mewakili langkah dalam proses (misalnya, panggilan ke LLM, pemanggilan alat, pemeriksaan bersyarat). Edge menentukan alur eksekusi antar-node.
  • Status: LangGraph mengelola status aplikasi Anda saat berpindah melalui grafik. Status ini dapat mencakup variabel seperti input pengguna, hasil panggilan alat, output perantara dari LLM, dan informasi lain yang perlu dipertahankan di antara langkah-langkah.
  • Node: Setiap node mewakili komputasi atau interaksi. Hal ini dapat berupa:
    • Node Alat: Menggunakan alat (misalnya, melakukan penelusuran web, membuat kueri database)
    • Node Fungsi: Menjalankan fungsi Python.
  • Rusuk: Menghubungkan node, yang menentukan alur eksekusi. Hal ini dapat berupa:
    • Direct Edges: Aliran sederhana dan tanpa syarat dari satu node ke node lainnya.
    • Arah Kondisional: Alur bergantung pada hasil node kondisional.

LangGraph

Kita akan menggunakan LangGraph untuk menerapkan orkestrasi. Mari kita edit file aidemy.py di folder aidemy-bootstrap untuk menentukan logika LangGraph. 👉Tambahkan kode berikut ke akhir 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"])} 

Fungsi ini bertanggung jawab untuk mengambil status percakapan saat ini, memberikan pesan sistem ke LLM, lalu meminta LLM untuk membuat respons. LLM dapat merespons langsung pengguna atau memilih untuk menggunakan salah satu alat yang tersedia.

tools : Daftar ini mewakili kumpulan alat yang tersedia untuk agen. File ini berisi tiga fungsi alat yang telah kita tentukan pada langkah sebelumnya: get_curriculum, search_latest_resource, dan recommend_book. llm.bind_tools(tools): Fungsi ini "mengikat" daftar alat ke objek llm. Mengikat alat akan memberi tahu LLM bahwa alat ini tersedia dan memberi LLM informasi tentang cara menggunakannya (misalnya, nama alat, parameter yang diterima, dan fungsinya).

Kita akan menggunakan LangGraph untuk menerapkan orkestrasi. 👉Tambahkan kode berikut ke akhir 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")

Penjelasan:

  • StateGraph(MessagesState): Membuat objek StateGraph. StateGraph adalah konsep inti di LangGraph. Diagram ini merepresentasikan alur kerja agen Anda sebagai grafik, dengan setiap node dalam grafik mewakili langkah dalam proses. Anggaplah ini sebagai penentuan cetak biru untuk cara agen akan bernalar dan bertindak.
  • Edge Bersyarat: Berasal dari node "determine_tool", argumen tools_condition kemungkinan merupakan fungsi yang menentukan edge mana yang akan diikuti berdasarkan output fungsi determine_tool. Edge bersyarat memungkinkan grafik bercabang berdasarkan keputusan LLM tentang alat yang akan digunakan (atau apakah akan merespons pengguna secara langsung). Di sinilah "kecerdasan" agen berperan – agen dapat menyesuaikan perilakunya secara dinamis berdasarkan situasi.
  • Loop: Menambahkan tepi ke grafik yang menghubungkan node "tools" kembali ke node "determine_tool". Hal ini akan membuat loop dalam grafik, yang memungkinkan agen menggunakan alat berulang kali hingga mengumpulkan cukup informasi untuk menyelesaikan tugas dan memberikan jawaban yang memuaskan. Loop ini sangat penting untuk tugas kompleks yang memerlukan beberapa langkah penalaran dan pengumpulan informasi.

Sekarang, mari kita uji agen perencana untuk melihat cara agen tersebut mengatur berbagai alat.

Kode ini akan menjalankan fungsi prep_class dengan input pengguna tertentu, menyimulasikan permintaan untuk membuat rencana pengajaran Matematika kelas 5 di Geometri, menggunakan kurikulum, rekomendasi buku, dan referensi internet terbaru.

Jika Anda telah menutup terminal atau variabel lingkungan tidak lagi ditetapkan, jalankan kembali perintah berikut

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"

👉Jalankan kode:

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

Lihat log di terminal. Anda akan melihat bukti bahwa agen memanggil ketiga alat tersebut (mendapatkan kurikulum sekolah, mendapatkan rekomendasi buku, dan menelusuri referensi terbaru) sebelum memberikan rencana pengajaran akhir. Hal ini menunjukkan bahwa orkestrasi LangGraph berfungsi dengan benar, dan agen menggunakan semua alat yang tersedia secara cerdas untuk memenuhi permintaan pengguna.

================================ 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**
.........

Hentikan skrip dengan menekan Ctrl+C.

👉Sekarang, ganti kode pengujian dengan perintah yang berbeda, yang memerlukan alat yang berbeda untuk dipanggil.

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")

Jika Anda telah menutup terminal atau variabel lingkungan tidak lagi ditetapkan, jalankan kembali perintah berikut

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"

👉Jalankan kode lagi:

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

Apa yang Anda perhatikan kali ini? Alat mana yang dipanggil agen? Anda akan melihat bahwa agen hanya memanggil alat search_latest_resource kali ini. Hal ini karena perintah tidak menentukan bahwa perintah tersebut memerlukan dua alat lainnya, dan LLM kita cukup cerdas untuk tidak memanggil alat lainnya.

================================ 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.

Hentikan skrip dengan menekan Ctrl+C. 👉Hapus kode pengujian agar file aidemy.py Anda tetap bersih:

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")

Setelah logika agen ditentukan, mari kita luncurkan aplikasi web Flask. Hal ini akan memberikan antarmuka berbasis formulir yang sudah dikenal bagi pengajar untuk berinteraksi dengan agen. Meskipun interaksi chatbot umum terjadi dengan LLM, kami memilih UI pengiriman formulir tradisional, karena mungkin lebih intuitif bagi banyak pendidik.

Jika Anda telah menutup terminal atau variabel lingkungan tidak lagi ditetapkan, jalankan kembali perintah berikut

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"

👉Sekarang, mulai UI Web.

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

Cari pesan startup di output terminal Cloud Shell. Flask biasanya mencetak pesan yang menunjukkan bahwa aplikasi sedang berjalan dan di port mana.

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.

👉Dari menu "Pratinjau web", pilih Pratinjau di port 8080. Cloud Shell akan membuka tab atau jendela browser baru dengan pratinjau web aplikasi Anda.

Halaman web

Di antarmuka aplikasi, pilih 5 untuk Tahun, pilih subjek Mathematics, dan ketik Geometry di Permintaan Add-on Batas Kuota

Daripada menunggu respons dengan hampa, beralihlah ke terminal Cloud Editor. Anda dapat mengamati progres serta pesan output atau error yang dihasilkan oleh fungsi di terminal emulator. 😁

👉Hentikan skrip dengan menekan Ctrl+C di terminal.

👉Keluar dari lingkungan virtual:

deactivate

8. Men-deploy agen perencana ke cloud

Mem-build dan mengirim image ke registry

Ringkasan

👉Saatnya men-deploy ke cloud. Di terminal, buat repositori artefak untuk menyimpan image docker yang akan kita build.

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

Anda akan melihat Repositori yang dibuat [agent-repository].

👉Jalankan perintah berikut untuk mem-build image Docker.

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

👉Kita perlu memberi tag ulang pada image agar dihosting di Artifact Registry, bukan GCR, dan mengirim image yang diberi tag ke Artifact Registry:

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

Setelah pengiriman selesai, Anda dapat memverifikasi bahwa image berhasil disimpan di Artifact Registry. Buka Artifact Registry di Konsol Google Cloud. Anda akan menemukan image aidemy-planner dalam repositori agent-repository. Gambar perencana Aidemy

Mengamankan Kredensial Database dengan Secret Manager

Untuk mengelola dan mengakses kredensial database dengan aman, kita akan menggunakan Google Cloud Secret Manager. Hal ini mencegah hardcoding informasi sensitif dalam kode aplikasi dan meningkatkan keamanan.

👉Kita akan membuat secret individual untuk nama pengguna, sandi, dan nama database. Pendekatan ini memungkinkan kita mengelola setiap kredensial secara terpisah. Di terminal, jalankan:

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

Menggunakan Secret Manager adalah langkah penting dalam mengamankan aplikasi Anda dan mencegah eksposur kredensial sensitif secara tidak sengaja. Layanan ini mengikuti praktik terbaik keamanan untuk deployment cloud.

Men-deploy ke Cloud Run

Cloud Run adalah platform serverless yang terkelola sepenuhnya dan memungkinkan Anda men-deploy aplikasi dalam container dengan cepat dan mudah. Cloud Run mengabstraksi pengelolaan infrastruktur, sehingga Anda dapat berfokus pada penulisan dan deployment kode. Kita akan men-deploy perencana sebagai layanan Cloud Run.

👉Di Konsol Google Cloud, buka "Cloud Run". Klik DEPLOY CONTAINER, lalu pilih SERVICE. Konfigurasikan layanan Cloud Run Anda:

Cloud Run

  1. Image container: Klik "Select" di kolom URL. Temukan URL image yang Anda kirim ke Artifact Registry (misalnya, us-central1-docker.pkg.dev/YOUR_PROJECT_ID/agent-repository/agent-planner/YOUR_IMG).
  2. Service name: aidemy-planner
  3. Region: Pilih region us-central1.
  4. Authentication: Untuk tujuan workshop ini, Anda dapat mengizinkan "Allow unauthenticated invocations". Untuk produksi, Anda mungkin ingin membatasi akses.
  5. Tab Penampung (Luaskan Penampung, Jaringan):
    • Tab setelan:
      • Referensi
        • memori : 2 GB
    • Tab Variabel & Secret:
      • Variabel lingkungan:
        • Tambahkan nama: GOOGLE_CLOUD_PROJECT dan nilai: <YOUR_PROJECT_ID>
        • Tambahkan nama: BOOK_PROVIDER_URL dan nilai: <YOUR_BOOK_PROVIDER_FUNCTION_URL>
      • Secret yang ditampilkan sebagai variabel lingkungan:
        • Tambahkan nama: DB_USER, secret: pilih db-user dan versi:latest
        • Tambahkan nama: DB_PASS, secret: pilih db-pass dan versi:latest
        • Tambahkan nama: DB_NAME, secret: pilih db-name dan versi:latest

Jalankan perintah berikut di terminal jika Anda perlu mengambil YOUR_BOOK_PROVIDER_FUNCTION_URL:

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

Menetapkan secret

Biarkan lainnya sebagai default.

👉Klik BUAT.

Cloud Run akan men-deploy layanan Anda.

Setelah di-deploy, klik layanan ke halaman detailnya, Anda dapat menemukan URL yang di-deploy di bagian atas.

URL

Di antarmuka aplikasi, pilih 7 untuk Tahun, pilih Mathematics sebagai subjek, dan masukkan Algebra di kolom Permintaan Add-on. Tindakan ini akan memberi agen konteks yang diperlukan untuk membuat rencana pelajaran yang disesuaikan.

Selamat! Anda telah berhasil membuat rencana pengajaran menggunakan agen AI canggih kami. Hal ini menunjukkan potensi agen untuk mengurangi beban kerja secara signifikan dan menyederhanakan tugas, sehingga meningkatkan efisiensi dan mempermudah pendidik.

9. Sistem multi-agen

Setelah berhasil menerapkan alat pembuatan rencana pengajaran, mari kita alihkan fokus ke pembuatan portal siswa. Portal ini akan memberi siswa akses ke kuis, recap audio, dan tugas yang terkait dengan materi pelajaran mereka. Mengingat cakupan fungsi ini, kita akan memanfaatkan kecanggihan sistem multi-agen untuk membuat solusi yang modular dan skalabel.

Seperti yang telah kita bahas sebelumnya, sistem multi-agen memungkinkan kita membagi beban kerja menjadi tugas khusus yang lebih kecil, yang masing-masing ditangani oleh agen khusus. Pendekatan ini menawarkan beberapa keunggulan utama:

Modularitas dan Kemampuan Pemeliharaan: Daripada membuat satu agen yang melakukan semuanya, buat agen khusus yang lebih kecil dengan tanggung jawab yang jelas. Modularitas ini membuat sistem lebih mudah dipahami, dikelola, dan di-debug. Saat masalah muncul, Anda dapat mengisolasinya ke agen tertentu, daripada harus menelusuri codebase yang besar.

Skalabilitas: Menskalakan satu agen yang kompleks dapat menjadi bottleneck. Dengan sistem multi-agen, Anda dapat menskalakan setiap agen berdasarkan kebutuhan spesifiknya. Misalnya, jika satu agen menangani volume permintaan yang tinggi, Anda dapat dengan mudah membuat lebih banyak instance agen tersebut tanpa memengaruhi sistem lainnya.

Pengembangan Spesialisasi Tim: Anggaplah seperti ini: Anda tidak akan meminta satu engineer untuk membuat seluruh aplikasi dari awal. Sebagai gantinya, Anda mengumpulkan tim spesialis, masing-masing dengan keahlian di bidang tertentu. Demikian pula, sistem multi-agen memungkinkan Anda memanfaatkan kekuatan berbagai LLM dan alat, dengan menetapkannya ke agen yang paling sesuai untuk tugas tertentu.

Pengembangan Paralel: Tim yang berbeda dapat mengerjakan agen yang berbeda secara bersamaan, sehingga mempercepat proses pengembangan. Karena agen bersifat independen, perubahan pada satu agen cenderung tidak memengaruhi agen lain.

Arsitektur Berbasis Peristiwa

Untuk memungkinkan komunikasi dan koordinasi yang efektif antara agen ini, kita akan menggunakan arsitektur berbasis peristiwa. Artinya, agen akan bereaksi terhadap "peristiwa" yang terjadi dalam sistem.

Agen berlangganan jenis peristiwa tertentu (misalnya, "rencana pengajaran dibuat", "tugas dibuat"). Saat peristiwa terjadi, agen yang relevan akan diberi tahu dan dapat bereaksi sebagaimana mestinya. Pemisahan ini mendorong fleksibilitas, skalabilitas, dan responsivitas real-time.

Ringkasan

Sekarang, untuk memulai, kita memerlukan cara untuk menyiarkan peristiwa ini. Untuk melakukannya, kita akan menyiapkan topik Pub/Sub. Mari kita mulai dengan membuat topik bernama rencana.

👉Buka pub/sub Konsol Google Cloud, lalu klik tombol "Create Topic".

👉Konfigurasikan Topik dengan ID/nama plan dan hapus centang Add a default subscription, biarkan yang lain sebagai default, lalu klik Buat.

Halaman Pub/Sub akan dimuat ulang, dan Anda akan melihat topik yang baru dibuat tercantum dalam tabel. Buat topik

Sekarang, mari kita integrasikan fungsi publikasi peristiwa Pub/Sub ke dalam agen perencana. Kita akan menambahkan alat baru yang mengirim peristiwa "rencana" ke topik Pub/Sub yang baru saja kita buat. Peristiwa ini akan memberi sinyal kepada agen lain dalam sistem (seperti yang ada di portal siswa) bahwa rencana pengajaran baru tersedia.

👉Kembali ke Cloud Code Editor dan buka file app.py yang ada di folder planner. Kita akan menambahkan fungsi yang memublikasikan peristiwa. Ganti:

##ADD SEND PLAN EVENT FUNCTION HERE

dengan

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: Fungsi ini menggunakan rencana pengajaran yang dihasilkan sebagai input, membuat klien penayang Pub/Sub, membuat jalur topik, mengonversi rencana pengajaran menjadi string JSON , memublikasikan pesan ke topik.
  • Daftar alat: Fungsi send_plan_event ditambahkan ke daftar alat, sehingga tersedia untuk digunakan oleh agen.

Dan di folder yang sama, dalam file app.py 👉Perbarui perintah untuk menginstruksikan agen mengirim peristiwa rencana pengajaran ke topik Pub/Sub setelah membuat rencana pengajaran. Ganti

### ADD send_plan_event CALL

dengan hal berikut:

send_plan_event(teaching_plan)

Dengan menambahkan alat send_plan_event dan mengubah perintah, kita telah mengaktifkan agen perencana untuk memublikasikan peristiwa ke Pub/Sub, sehingga komponen lain dalam sistem kita dapat bereaksi terhadap pembuatan rencana pengajaran baru. Sekarang kita akan memiliki sistem multi-agen yang berfungsi di bagian berikut.

10. Memberdayakan Siswa dengan Kuis Sesuai Permintaan

Bayangkan lingkungan belajar yang memungkinkan siswa mengakses kuis tanpa batas yang disesuaikan dengan rencana belajar spesifik mereka. Kuis ini memberikan masukan langsung, termasuk jawaban dan penjelasan, sehingga mendorong pemahaman yang lebih mendalam tentang materi. Inilah potensi yang ingin kami wujudkan dengan portal kuis yang didukung AI.

Untuk mewujudkan visi ini, kita akan membuat komponen pembuatan kuis yang dapat membuat pertanyaan pilihan ganda berdasarkan konten rencana pengajaran.

Ringkasan

👉Di panel Penjelajah Cloud Code Editor, buka folder portal. Buka file quiz.py, salin dan tempel kode berikut ke bagian akhir file.

def generate_quiz_question(file_name: str, difficulty: str, region:str ):
    """Generates a single multiple-choice quiz question using the LLM.
   
    ```json
    {
      "question": "The question itself",
      "options": ["Option A", "Option B", "Option C", "Option D"],
      "answer": "The correct answer letter (A, B, C, or D)"
    }
    ```
    """

    print(f"region: {region}")
    # Connect to resourse needed from Google Cloud
    llm = VertexAI(model_name="gemini-1.5-pro", location=region)


    plan=None
    #load the file using file_name and read content into string call plan
    with open(file_name, 'r') as f:
        plan = f.read()

    parser = JsonOutputParser(pydantic_object=QuizQuestion)


    instruction = f"You'll provide one question with difficulty level of {difficulty}, 4 options as multiple choices and provide the anwsers, the quiz needs to be related to the teaching plan {plan}"

    prompt = PromptTemplate(
        template="Generates a single multiple-choice quiz question\n {format_instructions}\n  {instruction}\n",
        input_variables=["instruction"],
        partial_variables={"format_instructions": parser.get_format_instructions()},
    )
    
    chain = prompt | llm | parser
    response = chain.invoke({"instruction": instruction})

    print(f"{response}")
    return  response


Di agen, parser output JSON dibuat yang dirancang khusus untuk memahami dan menyusun output LLM. Fungsi ini menggunakan model QuizQuestion yang telah kita tentukan sebelumnya untuk memastikan output yang diuraikan sesuai dengan format yang benar (pertanyaan, opsi, dan jawaban).

👉Jalankan perintah berikut di terminal untuk menyiapkan lingkungan virtual, menginstal dependensi, dan memulai agen:

cd ~/aidemy-bootstrap/portal/
python -m venv env
source env/bin/activate
pip install -r requirements.txt
python app.py

Gunakan fitur pratinjau web Cloud Shell untuk mengakses aplikasi yang sedang berjalan. Klik link "Kuis", baik di menu navigasi atas maupun dari kartu di halaman indeks. Anda akan melihat tiga kuis yang dibuat secara acak dan ditampilkan untuk siswa. Kuis ini didasarkan pada rencana pengajaran dan menunjukkan keandalan sistem pembuatan kuis yang didukung AI.

Kuis

Untuk menghentikan proses yang berjalan secara lokal, tekan Ctrl+C di terminal.

Pemikiran Gemini 2 untuk Penjelasan

Oke, jadi kita punya kuis, yang merupakan awal yang bagus. Namun, bagaimana jika siswa melakukan kesalahan? Di situlah pembelajaran yang sebenarnya terjadi, bukan? Jika kita dapat menjelaskan mengapa jawaban mereka salah dan cara mendapatkan jawaban yang benar, mereka akan lebih cenderung mengingatnya. Selain itu, cara ini membantu menghilangkan kebingungan dan meningkatkan kepercayaan mereka.

Itulah sebabnya kami akan menghadirkan senjata besar: model "pemikiran" Gemini 2. Anggaplah ini seperti memberi AI sedikit waktu ekstra untuk memikirkan semuanya sebelum menjelaskan. Hal ini memungkinkannya memberikan masukan yang lebih mendetail dan lebih baik.

Kami ingin melihat apakah fitur ini dapat membantu siswa dengan memberikan bantuan, menjawab, dan menjelaskan secara mendetail. Untuk mengujinya, kita akan memulai dengan mata pelajaran yang terkenal rumit, Kalkulus.

Ringkasan

👉Pertama, buka Cloud Code Editor, di answer.py di dalam folder portal, ganti

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

dengan cuplikan kode berikut:

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)

Ini adalah aplikasi langchain yang sangat sederhana yang melakukan Inisialisasi model Flash Gemini 2, tempat kita menginstruksikannya untuk bertindak sebagai pengajar yang membantu dan memberikan penjelasan

👉Jalankan perintah berikut di terminal:

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

Anda akan melihat output yang mirip dengan contoh yang diberikan dalam petunjuk asli. Model saat ini mungkin tidak memberikan penjelasan yang menyeluruh.

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

lim (x0) [(sin(5x) - 5x) / x^3]

You chose option B, which is -5/3, but the correct answer is A, which is -125/6.

It looks like you might have missed a step or made a small error in your calculations. This type of limit often involves using L'Hôpital's Rule or Taylor series expansion. Since we have the form 0/0, L'Hôpital's Rule is a good way to go! You need to apply it multiple times. Alternatively, you can use the Taylor series expansion of sin(x) which is:
sin(x) = x - x^3/3! + x^5/5! - ...
So, sin(5x) = 5x - (5x)^3/3! + (5x)^5/5! - ...
Then,  (sin(5x) - 5x) = - (5x)^3/3! + (5x)^5/5! - ...
Finally, (sin(5x) - 5x) / x^3 = - 5^3/3! + (5^5 * x^2)/5! - ...
Taking the limit as x approaches 0, we get -125/6.

Keep practicing, you'll get there!

Dalam file answer.py, ganti model_name dari gemini-2.0-flash-001 menjadi gemini-2.0-flash-thinking-exp-01-21 dalam fungsi answer_thinking.

Hal ini mengubah LLM yang lebih banyak bernalar, yang akan membantunya menghasilkan penjelasan yang lebih baik. Lalu jalankan lagi.

👉Jalankan untuk menguji model pemikiran baru:

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

Berikut adalah contoh respons dari model pemikiran yang jauh lebih menyeluruh dan mendetail, yang memberikan penjelasan langkah demi langkah tentang cara menyelesaikan masalah kalkulus. Hal ini menyoroti kekuatan model "berpikir" dalam menghasilkan penjelasan berkualitas tinggi. Anda akan melihat output yang serupa dengan ini:

Hey there! Let's take a look at this limit problem together. You were asked to evaluate:

lim (x0) [(sin(5x) - 5x) / x^3]

and you picked option B, -5/3, but the correct answer is actually A, -125/6. Let's figure out why!

It's a tricky one because if we directly substitute x=0, we get (sin(0) - 0) / 0^3 = (0 - 0) / 0 = 0/0, which is an indeterminate form. This tells us we need to use a more advanced technique like L'Hopital's Rule or Taylor series expansion.

Let's use the Taylor series expansion for sin(y) around y=0. Do you remember it?  It looks like this:

sin(y) = y - y^3/3! + y^5/5! - ...
where 3! (3 factorial) is 3 × 2 × 1 = 6, 5! is 5 × 4 × 3 × 2 × 1 = 120, and so on.

In our problem, we have sin(5x), so we can substitute y = 5x into the Taylor series:

sin(5x) = (5x) - (5x)^3/3! + (5x)^5/5! - ...
sin(5x) = 5x - (125x^3)/6 + (3125x^5)/120 - ...

Now let's plug this back into our limit expression:

[(sin(5x) - 5x) / x^3] =  [ (5x - (125x^3)/6 + (3125x^5)/120 - ...) - 5x ] / x^3
Notice that the '5x' and '-5x' cancel out!  So we are left with:
= [ - (125x^3)/6 + (3125x^5)/120 - ... ] / x^3
Now, we can divide every term in the numerator by x^3:
= -125/6 + (3125x^2)/120 - ...

Finally, let's take the limit as x approaches 0.  As x gets closer and closer to zero, terms with x^2 and higher powers will become very, very small and approach zero.  So, we are left with:
lim (x0) [ -125/6 + (3125x^2)/120 - ... ] = -125/6

Therefore, the correct answer is indeed **A) -125/6**.

It seems like your answer B, -5/3, might have come from perhaps missing a factor somewhere during calculation or maybe using an incorrect simplification. Double-check your steps when you were trying to solve it!

Don't worry, these limit problems can be a bit tricky sometimes! Keep practicing and you'll get the hang of it.  Let me know if you want to go through another similar example or if you have any more questions! 😊


Now that we have confirmed it works, let's use the portal.

👉HAPUS kode pengujian berikut dari answer.py:

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

👉Jalankan perintah berikut di terminal untuk menyiapkan lingkungan virtual, menginstal dependensi, dan memulai agen:

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

👉Gunakan fitur pratinjau web Cloud Shell untuk mengakses aplikasi yang sedang berjalan. Klik link "Kuis", jawab semua kuis dan pastikan setidaknya ada satu jawaban yang salah, lalu klik kirim

jawaban pemikiran

Daripada menunggu respons dengan hampa, beralihlah ke terminal Cloud Editor. Anda dapat mengamati progres serta pesan output atau error yang dihasilkan oleh fungsi di terminal emulator. 😁

Untuk menghentikan proses yang berjalan secara lokal, tekan Ctrl+C di terminal.

11. Melakukan orkestrasi Agen dengan Eventarc

Sejauh ini, portal siswa telah membuat kuis berdasarkan kumpulan rencana pengajaran default. Hal ini memang membantu, tetapi berarti agen perencana dan agen kuis portal tidak benar-benar berkomunikasi satu sama lain. Ingat bagaimana kita menambahkan fitur tersebut saat agen perencana memublikasikan rencana pengajaran yang baru dibuat ke topik Pub/Sub? Sekarang saatnya menghubungkannya ke agen portal.

Ringkasan

Kita ingin portal otomatis memperbarui konten kuis setiap kali rencana pengajaran baru dibuat. Untuk melakukannya, kita akan membuat endpoint di portal yang dapat menerima rencana baru ini.

👉Di panel Penjelajah Cloud Code Editor, buka folder portal. Buka file app.py untuk mengedit. Tambahkan kode berikut di antara ## Tambahkan kode Anda di sini:

## Add your code here

@app.route('/new_teaching_plan', methods=['POST'])
def new_teaching_plan():
    try:
       
        # Get data from Pub/Sub message delivered via Eventarc
        envelope = request.get_json()
        if not envelope:
            return jsonify({'error': 'No Pub/Sub message received'}), 400

        if not isinstance(envelope, dict) or 'message' not in envelope:
            return jsonify({'error': 'Invalid Pub/Sub message format'}), 400

        pubsub_message = envelope['message']
        print(f"data: {pubsub_message['data']}")

        data = pubsub_message['data']
        data_str = base64.b64decode(data).decode('utf-8')
        data = json.loads(data_str)

        teaching_plan = data['teaching_plan']

        print(f"File content: {teaching_plan}")

        with open("teaching_plan.txt", "w") as f:
            f.write(teaching_plan)

        print(f"Teaching plan saved to local file: teaching_plan.txt")

        return jsonify({'message': 'File processed successfully'})


    except Exception as e:
        print(f"Error processing file: {e}")
        return jsonify({'error': 'Error processing file'}), 500
## Add your code here

Mem-build Ulang dan Men-deploy ke Cloud Run

Baik. Anda harus mengupdate dan men-deploy ulang agen portal dan perencana kami ke Cloud Run. Hal ini memastikan bahwa keduanya memiliki kode terbaru dan dikonfigurasi untuk berkomunikasi melalui peristiwa.

Ringkasan Deployment

👉Selanjutnya, kita akan mem-build ulang dan mengirim image agen planner, kembali di terminal yang berjalan:

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

👉Kita akan melakukan hal yang sama, mem-build dan mendorong image agen portal:

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

Di Artifact Registry, Anda akan melihat image container aidemy-planner dan aidemy-portal tercantum.

Repo Container

👉Kembali ke terminal, jalankan perintah ini untuk mengupdate image Cloud Run untuk agen perencana:

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

Anda akan melihat output yang serupa dengan ini:

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

Catat URL Layanan; ini adalah link ke agen perencana yang di-deploy.

👉Jalankan ini untuk membuat instance Cloud Run bagi agen portal

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

Anda akan melihat output yang serupa dengan ini:

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

Catat URL Layanan; ini adalah link ke portal siswa yang telah di-deploy.

Membuat Pemicu Eventarc

Namun, inilah pertanyaan besarnya: bagaimana endpoint ini diberi tahu saat ada rencana baru yang menunggu di topik Pub/Sub? Di sinilah Eventarc hadir untuk menyelamatkan Anda.

Eventarc bertindak sebagai jembatan, memproses peristiwa tertentu (seperti pesan baru yang masuk ke topik Pub/Sub) dan otomatis memicu tindakan sebagai respons. Dalam kasus ini, webhook akan mendeteksi saat rencana pengajaran baru dipublikasikan, lalu mengirim sinyal ke endpoint portal kami, yang memberi tahu bahwa sudah waktunya untuk memperbarui.

Dengan Eventarc yang menangani komunikasi berbasis peristiwa, kami dapat menghubungkan agen perencana dan agen portal dengan lancar, sehingga menciptakan sistem pembelajaran yang benar-benar dinamis dan responsif. Ibarat memiliki messenger cerdas yang otomatis mengirimkan rencana pelajaran terbaru ke tempat yang tepat.

👉Di konsol, buka Eventarc.

👉Klik tombol "+ BUAT PENGGERAK".

Mengonfigurasi Pemicu (Dasar-Dasar):

  • Nama pemicu: plan-topic-trigger
  • Jenis pemicu: Sumber Google.
  • Penyedia peristiwa: Cloud Pub/Sub
  • Jenis Peristiwa: google.cloud.pubsub.topic.v1.messagePublished
  • Region: us-central1.
  • Cloud Pub/Sub Topic : pilih plan
  • GRANT akun layanan dengan peran roles/iam.serviceAccountTokenCreator
  • Tujuan Peristiwa: Cloud Run
  • Layanan Cloud Run: aidemy-portal
  • Jalur URL layanan: /new_teaching_plan
  • Abaikan pesan (Izin ditolak di 'locations/me-central2' (atau mungkin tidak ada).)

Klik "Buat".

Halaman Pemicu Eventarc akan dimuat ulang, dan Anda akan melihat pemicu yang baru dibuat tercantum dalam tabel.

👉Sekarang, akses perencana dan minta rencana pengajaran baru. Kali ini coba tahun 5, subjek science dengan permintaan Tambahkan-tidak atoms

Jalankan ini di terminal jika Anda lupa lokasi agen perencana

gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner

Kemudian, tunggu satu atau dua menit. Sekali lagi, penundaan ini telah diperkenalkan karena batasan penagihan lab ini. Dalam kondisi normal, seharusnya tidak ada penundaan.

Terakhir, akses portal siswa. Anda akan melihat bahwa kuis telah diperbarui dan sekarang selaras dengan rencana pengajaran baru yang baru saja Anda buat. Ini menunjukkan integrasi Eventarc yang berhasil di sistem Aidemy.

Jalankan ini di terminal jika Anda lupa lokasi agen portal

gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep portal

Aidemy-celebrate

Selamat! Anda telah berhasil membuat sistem multi-agen di Google Cloud, yang memanfaatkan arsitektur berbasis peristiwa untuk meningkatkan skalabilitas dan fleksibilitas. Anda telah meletakkan fondasi yang kuat, tetapi masih ada banyak hal yang dapat dijelajahi. Untuk mempelajari lebih dalam manfaat sebenarnya dari arsitektur ini, temukan kehebatan Multimodal Live API Gemini 2, dan pelajari cara menerapkan orkestrasi jalur tunggal dengan LangGraph. Silakan lanjutkan ke dua bab berikutnya.

12. OPSIONAL: Recap Audio dengan Gemini

Gemini dapat memahami dan memproses informasi dari berbagai sumber, seperti teks, gambar, dan bahkan audio, sehingga membuka berbagai kemungkinan baru untuk pembelajaran dan pembuatan konten. Kemampuan Gemini untuk "melihat", "mendengar", dan "membaca" benar-benar membuka pengalaman pengguna yang kreatif dan menarik.

Selain membuat visual atau teks, langkah penting lainnya dalam pembelajaran adalah membuat ringkasan dan recap yang efektif. Pikirkan: seberapa sering Anda mengingat lirik lagu yang menarik dengan lebih mudah daripada sesuatu yang Anda baca di buku teks? Suara dapat menjadi sangat berkesan. Itulah sebabnya kami akan memanfaatkan kemampuan multimodal Gemini untuk membuat rekap audio dari rencana pengajaran kami. Hal ini akan memberi siswa cara yang mudah dan menarik untuk meninjau materi, yang berpotensi meningkatkan retensi dan pemahaman melalui kekuatan pembelajaran auditori.

Ringkasan Live API

Kita memerlukan tempat untuk menyimpan file audio yang dihasilkan. Cloud Storage menyediakan solusi yang skalabel dan andal.

👉Buka Storage di konsol. Klik "Bucket" di menu sebelah kiri. Klik tombol "+ CREATE" di bagian atas.

👉Konfigurasikan bucket Anda:

  • nama bucket: aidemy-recap-<UNIQUE_NAME> PENTING: Pastikan Anda menentukan nama bucket unik yang diawali dengan "aidemy-recap-". Nama unik ini sangat penting untuk menghindari konflik penamaan saat membuat bucket Cloud Storage.
  • region: us-central1.
  • Kelas penyimpanan: "Standar". Standar cocok untuk data yang sering diakses.
  • Kontrol akses: Biarkan "Kontrol akses seragam" default dipilih. Hal ini memberikan kontrol akses level bucket yang konsisten.
  • Opsi lanjutan: Untuk workshop ini, setelan default biasanya sudah cukup. Klik tombol CREATE untuk membuat bucket.

Anda mungkin melihat pop-up tentang pencegahan akses publik. Biarkan kotak dicentang dan klik Confirm.

Sekarang Anda akan melihat bucket yang baru dibuat di daftar Buckets. Ingat nama bucket Anda, Anda akan memerlukannya nanti.

👉Di terminal Cloud Code Editor, jalankan perintah berikut untuk memberikan akses akun layanan ke bucket:

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"

👉Di Cloud Code Editor, buka audio.py di dalam folder course. Tempelkan kode berikut ke bagian akhir file:

config = LiveConnectConfig(
    response_modalities=["AUDIO"],
    speech_config=SpeechConfig(
        voice_config=VoiceConfig(
            prebuilt_voice_config=PrebuiltVoiceConfig(
                voice_name="Charon",
            )
        )
    ),
)

async def process_weeks(teaching_plan: str):
    region = "us-west1" #To workaround onRamp qouta limits
    client = genai.Client(vertexai=True, project=PROJECT_ID, location=region)
    
    clientAudio = genai.Client(vertexai=True, project=PROJECT_ID, location="us-central1")
    async with clientAudio.aio.live.connect(
        model=MODEL_ID,
        config=config,
    ) as session:
        for week in range(1, 4):  
            response = client.models.generate_content(
                model="gemini-1.0-pro",
                contents=f"Given the following teaching plan: {teaching_plan}, Extrace content plan for week {week}. And return just the plan, nothingh else  " # Clarified prompt
            )

            prompt = f"""
                Assume you are the instructor.  
                Prepare a concise and engaging recap of the key concepts and topics covered. 
                This recap should be suitable for generating a short audio summary for students. 
                Focus on the most important learnings and takeaways, and frame it as a direct address to the students.  
                Avoid overly formal language and aim for a conversational tone, tell a few jokes. 
                
                Teaching plan: {response.text} """
            print(f"prompt --->{prompt}")

            await session.send(input=prompt, end_of_turn=True)
            with open(f"temp_audio_week_{week}.raw", "wb") as temp_file:
                async for message in session.receive():
                    if message.server_content.model_turn:
                        for part in message.server_content.model_turn.parts:
                            if part.inline_data:
                                temp_file.write(part.inline_data.data)
                            
            data, samplerate = sf.read(f"temp_audio_week_{week}.raw", channels=1, samplerate=24000, subtype='PCM_16', format='RAW')
            sf.write(f"course-week-{week}.wav", data, samplerate)
        
            storage_client = storage.Client()
            bucket = storage_client.bucket(BUCKET_NAME)
            blob = bucket.blob(f"course-week-{week}.wav")  # Or give it a more descriptive name
            blob.upload_from_filename(f"course-week-{week}.wav")
            print(f"Audio saved to GCS: gs://{BUCKET_NAME}/course-week-{week}.wav")
    await session.close()

 
def breakup_sessions(teaching_plan: str):
    asyncio.run(process_weeks(teaching_plan))
  • Koneksi Streaming: Pertama, koneksi persisten dibuat dengan endpoint Live API. Tidak seperti panggilan API standar yang Anda gunakan untuk mengirim permintaan dan mendapatkan respons, koneksi ini tetap terbuka untuk pertukaran data yang berkelanjutan.
  • Multimodal Konfigurasi: Gunakan konfigurasi untuk menentukan jenis output yang Anda inginkan (dalam hal ini, audio), dan Anda bahkan dapat menentukan parameter yang ingin digunakan (misalnya, pemilihan suara, encoding audio)
  • Pemrosesan Asinkron: API ini berfungsi secara asinkron, yang berarti tidak memblokir thread utama saat menunggu pembuatan audio selesai. Dengan memproses data secara real-time dan mengirim output dalam beberapa bagian, API ini memberikan pengalaman yang hampir instan.

Sekarang, pertanyaan utamanya adalah: kapan proses pembuatan audio ini harus dijalankan? Idealnya, ringkasan audio akan tersedia segera setelah rencana pengajaran baru dibuat. Karena kita telah menerapkan arsitektur berbasis peristiwa dengan memublikasikan rencana pengajaran ke topik Pub/Sub, kita cukup berlangganan topik tersebut.

Namun, kami tidak sering membuat rencana pengajaran baru. Tidak efisien jika agen terus berjalan dan menunggu rencana baru. Itulah sebabnya sangat masuk akal untuk men-deploy logika pembuatan audio ini sebagai Fungsi Cloud Run.

Dengan men-deploynya sebagai fungsi, fungsi tersebut akan tetap tidak aktif hingga pesan baru dipublikasikan ke topik Pub/Sub. Jika hal itu terjadi, fungsi akan otomatis terpicu, yang menghasilkan recap audio dan menyimpannya di bucket kami.

👉Di folder course dalam file main.py, file ini menentukan Fungsi Cloud Run yang akan dipicu saat rencana pengajaran baru tersedia. Fungsi ini menerima rencana dan memulai pembuatan recap audio. Tambahkan cuplikan kode berikut ke bagian akhir file.

@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: Dekorator ini menandai fungsi sebagai Fungsi Cloud Run yang akan dipicu oleh CloudEvents.

Menguji secara lokal

👉Kita akan menjalankannya di lingkungan virtual dan menginstal library Python yang diperlukan untuk fungsi Cloud Run.

cd ~/aidemy-bootstrap/courses
export COURSE_BUCKET_NAME=$(gcloud storage buckets list --format="value(name)" | grep aidemy-recap)
python -m venv env
source env/bin/activate
pip install -r requirements.txt

👉Emulator Cloud Run Function memungkinkan kita menguji fungsi secara lokal sebelum men-deploy-nya ke Google Cloud. Mulai emulator lokal dengan menjalankan:

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

👉Saat emulator berjalan, Anda dapat mengirim CloudEvents pengujian ke emulator untuk menyimulasikan rencana pengajaran baru yang dipublikasikan. Di terminal baru:

Dua terminal

👉Jalankan:

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

Daripada menunggu respons dengan hampa, beralihlah ke terminal Cloud Shell lainnya. Anda dapat mengamati progres serta output atau pesan error yang dihasilkan oleh fungsi di terminal emulator. 😁

Kembali ke terminal ke-2, Anda akan melihatnya menampilkan OK.

👉Anda akan memverifikasi Data di bucket, buka Cloud Storage, lalu pilih tab "Bucket", lalu aidemy-recap-xxx

Bucket

👉Di terminal yang menjalankan emulator, ketik ctrl+c untuk keluar. Dan tutup terminal kedua. Kemudian, tutup terminal kedua. dan jalankan deaktivasi untuk keluar dari lingkungan virtual.

deactivate

Men-deploy ke Google Cloud

Ringkasan Deployment 👉Setelah menguji secara lokal, kini saatnya men-deploy agen kursus ke Google Cloud. Di terminal, jalankan perintah berikut:

cd ~/aidemy-bootstrap/courses
export COURSE_BUCKET_NAME=$(gcloud storage buckets list --format="value(name)" | grep aidemy-recap)
gcloud functions deploy courses-agent \
  --region=us-central1 \
  --gen2 \
  --source=. \
  --runtime=python312 \
  --trigger-topic=plan \
  --entry-point=process_teaching_plan \
  --set-env-vars=GOOGLE_CLOUD_PROJECT=${PROJECT_ID},COURSE_BUCKET_NAME=$COURSE_BUCKET_NAME

Verifikasi deployment dengan membuka Cloud Run di Google Cloud Console.Anda akan melihat layanan baru bernama courses-agent tercantum.

Daftar Cloud Run

Untuk memeriksa konfigurasi pemicu, klik layanan courses-agent untuk melihat detailnya. Buka tab "PENYEBAB".

Anda akan melihat pemicu yang dikonfigurasi untuk memproses pesan yang dipublikasikan ke topik rencana.

Pemicu Cloud Run

Terakhir, mari kita lihat cara kerjanya secara menyeluruh.

👉Selanjutnya, kita perlu mengonfigurasi agen portal agar mengetahui tempat menemukan file audio yang dihasilkan. Di terminal, jalankan:

export COURSE_BUCKET_NAME=$(gcloud storage buckets list --format="value(name)" | grep aidemy-recap)
export PROJECT_ID=$(gcloud config get project)
gcloud run services update aidemy-portal \
    --region=us-central1 \
    --set-env-vars=GOOGLE_CLOUD_PROJECT=${PROJECT_ID},COURSE_BUCKET_NAME=$COURSE_BUCKET_NAME

👉Coba buat rencana pengajaran baru di halaman agen perencana. Mungkin perlu waktu beberapa menit untuk memulai, jangan khawatir, ini adalah layanan tanpa server. Dapatkan URL agen perencana Anda (jika Anda tidak memilikinya, jalankan ini di terminal):

gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner

Setelah membuat rencana baru, tunggu 2-3 menit hingga audio dibuat. Sekali lagi, ini akan memerlukan waktu beberapa menit lagi karena batasan penagihan dengan akun lab ini.

Anda dapat memantau apakah fungsi courses-agent telah menerima rencana pengajaran dengan memeriksa tab "PENYEBAB" fungsi. Muat ulang halaman secara berkala; Anda akan melihat bahwa fungsi telah dipanggil. Jika fungsi belum dipanggil setelah lebih dari 2 menit, Anda dapat mencoba membuat rencana pengajaran lagi. Namun, hindari membuat rencana berulang kali secara berurutan, karena setiap rencana yang dibuat akan digunakan dan diproses secara berurutan oleh agen, yang berpotensi membuat backlog.

Memicu Pengamatan

👉Buka portal dan klik "Kursus". Anda akan melihat tiga kartu, masing-masing menampilkan recap audio. Untuk menemukan URL agen portal Anda:

gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep portal

Klik "putar" di setiap kursus untuk memastikan recap audio selaras dengan rencana pengajaran yang baru saja Anda buat. Kursus Portal

Keluar dari lingkungan virtual.

deactivate

13. OPSIONAL: Kolaborasi Berbasis Peran dengan Gemini dan DeepSeek

Memiliki beberapa perspektif sangatlah berharga, terutama saat membuat tugas yang menarik dan bermakna. Sekarang, kita akan membuat sistem multi-agen yang memanfaatkan dua model berbeda dengan peran yang berbeda, untuk membuat tugas: satu mempromosikan kolaborasi, dan yang lainnya mendorong studi mandiri. Kita akan menggunakan arsitektur "single-shot", dengan alur kerja yang mengikuti rute tetap.

Generator Tugas Gemini

Ringkasan Gemini Kita akan mulai dengan menyiapkan fungsi Gemini untuk membuat tugas dengan penekanan kolaboratif. Edit file gemini.py yang berada di folder assignment.

👉Tempelkan kode berikut ke akhir file gemini.py:

def gen_assignment_gemini(state):
    region=get_next_region()
    client = genai.Client(vertexai=True, project=PROJECT_ID, location=region)
    print(f"---------------gen_assignment_gemini")
    response = client.models.generate_content(
        model=MODEL_ID, contents=f"""
        You are an instructor 

        Develop engaging and practical assignments for each week, ensuring they align with the teaching plan's objectives and progressively build upon each other.  

        For each week, provide the following:

        * **Week [Number]:** A descriptive title for the assignment (e.g., "Data Exploration Project," "Model Building Exercise").
        * **Learning Objectives Assessed:** List the specific learning objectives from the teaching plan that this assignment assesses.
        * **Description:** A detailed description of the task, including any specific requirements or constraints.  Provide examples or scenarios if applicable.
        * **Deliverables:** Specify what students need to submit (e.g., code, report, presentation).
        * **Estimated Time Commitment:**  The approximate time students should dedicate to completing the assignment.
        * **Assessment Criteria:** Briefly outline how the assignment will be graded (e.g., correctness, completeness, clarity, creativity).

        The assignments should be a mix of individual and collaborative work where appropriate.  Consider different learning styles and provide opportunities for students to apply their knowledge creatively.

        Based on this teaching plan: {state["teaching_plan"]}
        """
    )

    print(f"---------------gen_assignment_gemini answer {response.text}")
    
    state["model_one_assignment"] = response.text
    
    return state


import unittest

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

        updated_state = gen_assignment_gemini(initial_state)

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


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

Model ini menggunakan model Gemini untuk membuat tugas.

Kita siap menguji Agen Gemini.

👉Jalankan perintah ini di terminal untuk menyiapkan lingkungan:

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

👉Anda dapat menjalankan untuk mengujinya:

python gemini.py

Anda akan melihat tugas yang memiliki lebih banyak tugas kelompok dalam output. Pengujian pernyataan di bagian akhir juga akan menghasilkan output.

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

Berhenti dengan ctl+c, dan untuk membersihkan kode pengujian. HAPUS kode berikut dari gemini.py

import unittest

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

        updated_state = gen_assignment_gemini(initial_state)

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


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

Mengonfigurasi Generator Tugas DeepSeek

Meskipun platform AI berbasis cloud praktis, LLM yang dihosting sendiri dapat sangat penting untuk melindungi privasi data dan memastikan kedaulatan data. Kita akan men-deploy model DeepSeek terkecil (1,5 miliar parameter) di instance Cloud Compute Engine. Ada cara lain seperti menghostingnya di platform Vertex AI Google atau menghostingnya di instance GKE Anda, tetapi karena ini hanya workshop tentang agen AI, dan saya tidak ingin membuat Anda menunggu lama, mari kita gunakan cara yang paling sederhana. Namun, jika Anda tertarik dan ingin mempelajari opsi lain, lihat file deepseek-vertexai.py di folder tugas, yang menyediakan kode contoh tentang cara berinteraksi dengan model yang di-deploy di VertexAI.

Ringkasan Deepseek

👉Jalankan perintah ini di terminal untuk membuat platform LLM Ollama yang dihosting sendiri:

cd ~/aidemy-bootstrap/assignment
gcloud compute instances create ollama-instance \
    --image-family=ubuntu-2204-lts \
    --image-project=ubuntu-os-cloud \
    --machine-type=e2-standard-4 \
    --zone=us-central1-a \
    --metadata-from-file startup-script=startup.sh \
    --boot-disk-size=50GB \
    --tags=ollama \
    --scopes=https://www.googleapis.com/auth/cloud-platform

Untuk memverifikasi bahwa instance Compute Engine sedang berjalan:

Buka Compute Engine > "VM instances" di Konsol Google Cloud. Anda akan melihat ollama-instance tercantum dengan tanda centang hijau yang menunjukkan bahwa ollama-instance sedang berjalan. Jika Anda tidak dapat melihatnya, pastikan zonanya adalah us-central1. Jika tidak, Anda mungkin perlu menelusurinya.

Daftar Compute Engine

👉Kita akan menginstal model DeepSeek terkecil dan mengujinya, kembali di Cloud Shell Editor, di terminal New, jalankan perintah berikut untuk ssh ke instance GCE.

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

Setelah membuat koneksi SSH, Anda mungkin akan diminta untuk memasukkan hal berikut:

"Do you want to continue (Y/n)?"

Cukup ketik Y(tidak peka huruf besar/kecil) dan tekan Enter untuk melanjutkan.

Selanjutnya, Anda mungkin diminta untuk membuat frasa sandi untuk kunci SSH. Jika Anda memilih untuk tidak menggunakan frasa sandi, cukup tekan Enter dua kali untuk menerima setelan default (tanpa frasa sandi).

👉Sekarang Anda berada di virtual machine, ambil model DeepSeek R1 terkecil, dan uji apakah berfungsi?

ollama pull deepseek-r1:1.5b
ollama run deepseek-r1:1.5b "who are you?"

👉Keluar dari instance GCE, masukkan perintah berikut di terminal ssh:

exit

Tutup terminal baru, kembali ke terminal asli.

👉Dan jangan lupa untuk menyiapkan kebijakan jaringan, sehingga layanan lain dapat mengakses LLM. Batasi akses ke instance jika Anda ingin melakukannya untuk produksi, baik menerapkan login keamanan untuk layanan atau membatasi akses IP. Jalankan:

gcloud compute firewall-rules create allow-ollama-11434 \
    --allow=tcp:11434 \
    --target-tags=ollama \
    --description="Allow access to Ollama on port 11434"

👉Untuk memverifikasi apakah kebijakan firewall Anda berfungsi dengan benar, coba jalankan:

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

Selanjutnya, kita akan mengerjakan fungsi Deepseek di agen tugas untuk membuat tugas dengan penekanan pekerjaan individu.

👉Edit deepseek.py di folder assignment, tambahkan cuplikan berikut ke bagian akhir

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

👉mari kita uji dengan menjalankan:

cd ~/aidemy-bootstrap/assignment
source env/bin/activate
export PROJECT_ID=$(gcloud config get project)
export OLLAMA_HOST=http://$(gcloud compute instances describe ollama-instance --zone=us-central1-a --format='value(networkInterfaces[0].accessConfigs[0].natIP)'):11434
python deepseek.py

Anda akan melihat tugas yang memiliki lebih banyak tugas mandiri.

**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.
....

👉Hentikan ctl+c, dan untuk membersihkan kode pengujian. HAPUS kode berikut dari deepseek.py

import unittest

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

        updated_state = gen_assignment_deepseek(initial_state)

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


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

Sekarang, kita akan menggunakan model gemini yang sama untuk menggabungkan kedua tugas menjadi tugas baru. Edit file gemini.py yang berada di folder assignment.

👉Tempelkan kode berikut ke akhir file gemini.py:

def combine_assignments(state):
    print(f"---------------combine_assignments ")
    region=get_next_region()
    client = genai.Client(vertexai=True, project=PROJECT_ID, location=region)
    response = client.models.generate_content(
        model=MODEL_ID, contents=f"""
        Look at all the proposed assignment so far {state["model_one_assignment"]} and {state["model_two_assignment"]}, combine them and come up with a final assignment for student. 
        """
    )

    state["final_assignment"] = response.text
    
    return state

Untuk menggabungkan kekuatan kedua model, kita akan mengatur alur kerja yang ditentukan menggunakan LangGraph. Alur kerja ini terdiri dari tiga langkah: pertama, model Gemini menghasilkan tugas yang berfokus pada kolaborasi; kedua, model DeepSeek menghasilkan tugas yang menekankan pekerjaan individu; terakhir, Gemini menyintesis kedua tugas ini menjadi satu tugas yang komprehensif. Karena kita menentukan urutan langkah-langkah sebelumnya tanpa pengambilan keputusan LLM, hal ini merupakan pengaturan yang ditentukan pengguna dengan satu jalur.

Ringkasan kombinasi Langraph

👉Tempel kode berikut ke akhir file main.py di folder assignment:

def create_assignment(teaching_plan: str):
    print(f"create_assignment---->{teaching_plan}")
    builder = StateGraph(State)
    builder.add_node("gen_assignment_gemini", gen_assignment_gemini)
    builder.add_node("gen_assignment_deepseek", gen_assignment_deepseek)
    builder.add_node("combine_assignments", combine_assignments)
    
    builder.add_edge(START, "gen_assignment_gemini")
    builder.add_edge("gen_assignment_gemini", "gen_assignment_deepseek")
    builder.add_edge("gen_assignment_deepseek", "combine_assignments")
    builder.add_edge("combine_assignments", END)

    graph = builder.compile()
    state = graph.invoke({"teaching_plan": teaching_plan})

    return state["final_assignment"]



import unittest

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


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

👉Untuk menguji fungsi create_assignment pada awalnya dan mengonfirmasi bahwa alur kerja yang menggabungkan Gemini dan DeepSeek berfungsi, jalankan perintah berikut:

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

Anda akan melihat sesuatu yang menggabungkan kedua model dengan perspektif masing-masing untuk studi siswa dan juga untuk tugas kelompok siswa.

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

👉Hentikan ctl+c, dan untuk membersihkan kode pengujian. HAPUS kode berikut dari main.py

import unittest

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


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

Generate Assignment.png

Agar proses pembuatan tugas menjadi otomatis dan responsif terhadap rencana pengajaran baru, kami akan memanfaatkan arsitektur berbasis peristiwa yang ada. Kode berikut menentukan Fungsi Cloud Run (generate_assignment) yang akan dipicu setiap kali rencana pengajaran baru dipublikasikan ke topik Pub/Sub 'plan'.

👉Tambahkan kode berikut ke akhir main.py:

@functions_framework.cloud_event
def generate_assignment(cloud_event):
    print(f"CloudEvent received: {cloud_event.data}")

    try:
        if isinstance(cloud_event.data.get('message', {}).get('data'), str): 
            data = json.loads(base64.b64decode(cloud_event.data['message']['data']).decode('utf-8'))
            teaching_plan = data.get('teaching_plan')
        elif 'teaching_plan' in cloud_event.data: 
            teaching_plan = cloud_event.data["teaching_plan"]
        else:
            raise KeyError("teaching_plan not found") 

        assignment = create_assignment(teaching_plan)

        print(f"Assignment---->{assignment}")

        #Store the return assignment into bucket as a text file
        storage_client = storage.Client()
        bucket = storage_client.bucket(ASSIGNMENT_BUCKET)
        file_name = f"assignment-{random.randint(1, 1000)}.txt"
        blob = bucket.blob(file_name)
        blob.upload_from_string(assignment)

        return f"Assignment generated and stored in {ASSIGNMENT_BUCKET}/{file_name}", 200

    except (json.JSONDecodeError, AttributeError, KeyError) as e:
        print(f"Error decoding CloudEvent data: {e} - Data: {cloud_event.data}")
        return "Error processing event", 500

    except Exception as e:
        print(f"Error generate assignment: {e}")
        return "Error generate assignment", 500

Menguji secara lokal

Sebelum men-deploy ke Google Cloud, sebaiknya uji Cloud Run Function secara lokal. Hal ini memungkinkan iterasi yang lebih cepat dan proses debug yang lebih mudah.

Pertama, buat bucket Cloud Storage untuk menyimpan file tugas yang dihasilkan dan berikan akses ke bucket tersebut kepada akun layanan. Jalankan perintah berikut di terminal:

👉PENTING: Pastikan Anda menentukan nama ASSIGNMENT_BUCKET unik yang diawali dengan "aidemy-assignment-". Nama unik ini sangat penting untuk menghindari konflik penamaan saat membuat bucket Cloud Storage. (Ganti <YOUR_NAME> dengan kata acak apa pun)

export ASSIGNMENT_BUCKET=aidemy-assignment-<YOUR_NAME> #Name must be unqiue

👉Lalu jalankan:

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"

👉Sekarang, mulai emulator Fungsi Cloud Run:

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

👉Saat emulator berjalan di satu terminal, buka terminal kedua di Cloud Shell. Di terminal kedua ini, kirim CloudEvent pengujian ke emulator untuk menyimulasikan rencana pengajaran baru yang dipublikasikan:

Dua terminal

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

Daripada menunggu respons dengan hampa, beralihlah ke terminal Cloud Shell lainnya. Anda dapat mengamati progres serta pesan output atau error yang dihasilkan oleh fungsi di terminal emulator. 😁

Tindakan ini akan menampilkan OK.

Untuk mengonfirmasi bahwa tugas berhasil dibuat dan disimpan, buka Konsol Google Cloud dan buka Storage > "Cloud Storage". Pilih bucket aidemy-assignment yang telah Anda buat. Anda akan melihat file teks bernama assignment-{random number}.txt di bucket. Klik file untuk mendownloadnya dan memverifikasi kontennya. Tindakan ini memverifikasi bahwa file baru berisi tugas baru yang baru saja dibuat.

12-01-assignment-bucket

👉Di terminal yang menjalankan emulator, ketik ctrl+c untuk keluar. Dan tutup terminal kedua. 👉Selain itu, di terminal yang menjalankan emulator, keluar dari lingkungan virtual.

deactivate

Ringkasan Deployment

👉Berikutnya, kita akan men-deploy agen tugas ke cloud

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

Verifikasi deployment dengan membuka Google Cloud Console, buka Cloud Run.Anda akan melihat layanan baru bernama courses-agent tercantum. 12-03-function-list

Dengan alur kerja pembuatan tugas yang sekarang telah diimplementasikan, diuji, dan di-deploy, kita dapat melanjutkan ke langkah berikutnya: membuat tugas ini dapat diakses dalam portal siswa.

14. OPSIONAL: Kolaborasi Berbasis Peran dengan Gemini dan DeepSeek - Lanjutan.

Pembuatan situs dinamis

Untuk meningkatkan kualitas portal siswa dan membuatnya lebih menarik, kami akan menerapkan pembuatan HTML dinamis untuk halaman tugas. Tujuannya adalah untuk otomatis memperbarui portal dengan desain baru yang menarik secara visual setiap kali tugas baru dibuat. Hal ini memanfaatkan kemampuan coding LLM untuk menciptakan pengalaman pengguna yang lebih dinamis dan menarik.

14-01-generate-html

👉Di Cloud Shell Editor, edit file render.py dalam folder portal, ganti

def render_assignment_page():
    return ""

dengan cuplikan kode berikut:

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)}"

Model ini menggunakan model Gemini untuk membuat HTML secara dinamis untuk tugas. Alat ini menggunakan konten tugas sebagai input dan menggunakan perintah untuk menginstruksikan Gemini membuat halaman HTML yang menarik secara visual dengan gaya materi iklan.

Selanjutnya, kita akan membuat endpoint yang akan dipicu setiap kali dokumen baru ditambahkan ke bucket tugas:

👉Dalam folder portal, edit file app.py dan tambahkan kode berikut dalam ## Add your code here" comments, SETELAH fungsi new_teaching_plan:

## Add your code here

@app.route('/render_assignment', methods=['POST'])
def render_assignment():
    try:
        data = request.get_json()
        file_name = data.get('name')
        bucket_name = data.get('bucket')

        if not file_name or not bucket_name:
            return jsonify({'error': 'Missing file name or bucket name'}), 400

        storage_client = storage.Client()
        bucket = storage_client.bucket(bucket_name)
        blob = bucket.blob(file_name)
        content = blob.download_as_text()

        print(f"File content: {content}")

        render_assignment_page(content)

        return jsonify({'message': 'Assignment rendered successfully'})

    except Exception as e:
        print(f"Error processing file: {e}")
        return jsonify({'error': 'Error processing file'}), 500


## Add your code here

Saat dipicu, fungsi ini mengambil nama file dan nama bucket dari data permintaan, mendownload konten tugas dari Cloud Storage, dan memanggil fungsi render_assignment_page untuk membuat HTML.

👉Kita akan menjalankannya secara lokal:

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

👉Dari menu "Web preview" di bagian atas jendela Cloud Shell, pilih "Preview on port 8080". Tindakan ini akan membuka aplikasi Anda di tab browser baru. Buka link Penetapan di menu navigasi. Anda akan melihat halaman kosong pada tahap ini, yang merupakan perilaku yang diharapkan karena kita belum membuat jembatan komunikasi antara agen tugas dan portal untuk mengisi konten secara dinamis.

14-02-deployment-overview

👉Untuk menyertakan perubahan ini dan men-deploy kode yang diperbarui, build ulang dan kirim image agen portal:

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

👉Setelah mengirim image baru, deploy ulang layanan Cloud Run. Jalankan skrip berikut untuk menerapkan update Cloud Run:

export PROJECT_ID=$(gcloud config get project)
export COURSE_BUCKET_NAME=$(gcloud storage buckets list --format="value(name)" | grep aidemy-recap)
gcloud run services update aidemy-portal \
    --region=us-central1 \
    --set-env-vars=GOOGLE_CLOUD_PROJECT=${PROJECT_ID},COURSE_BUCKET_NAME=$COURSE_BUCKET_NAME

👉Sekarang, kita akan men-deploy pemicu Eventarc yang memproses objek baru yang dibuat (diselesaikan) di bucket tugas. Pemicu ini akan otomatis memanggil endpoint /render_assignment di layanan portal saat file tugas baru dibuat.

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"

Untuk memverifikasi bahwa pemicu berhasil dibuat, buka halaman Pemicu Eventarc di Konsol Google Cloud. Anda akan melihat portal-assignment-trigger tercantum dalam tabel. Klik nama pemicu untuk melihat detailnya. Pemicu Tugas

Mungkin perlu waktu hingga 2-3 menit agar pemicu baru menjadi aktif.

Untuk melihat pembuatan penetapan dinamis, jalankan perintah berikut untuk menemukan URL agen perencana (jika Anda tidak memilikinya):

gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner

Temukan URL agen portal Anda:

gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep portal

Di agen perencana, buat rencana pengajaran baru.

13-02-assignment

Setelah beberapa menit (untuk memungkinkan pembuatan audio, pembuatan tugas, dan rendering HTML selesai), buka portal siswa.

👉Klik link "Penetapan" di menu navigasi. Anda akan melihat tugas yang baru dibuat dengan HTML yang dibuat secara dinamis. Setiap kali rencana pengajaran dibuat, rencana tersebut harus berupa tugas dinamis.

13-02-assignment

Selamat, Anda telah menyelesaikan sistem multi-agen Aidemy. Anda telah mendapatkan pengalaman praktis dan insight berharga tentang:

  • Manfaat sistem multi-agen, termasuk modularitas, skalabilitas, spesialisasi, dan pemeliharaan yang disederhanakan.
  • Pentingnya arsitektur berbasis peristiwa untuk membuat aplikasi yang responsif dan terikat longgar.
  • Penggunaan LLM secara strategis, mencocokkan model yang tepat dengan tugas, dan mengintegrasikannya dengan alat untuk memberikan dampak di dunia nyata.
  • Praktik pengembangan native cloud menggunakan layanan Google Cloud untuk membuat solusi yang skalabel dan andal.
  • Pentingnya mempertimbangkan privasi data dan model hosting mandiri sebagai alternatif untuk solusi vendor.

Sekarang Anda memiliki dasar yang kuat untuk membuat aplikasi canggih yang didukung AI di Google Cloud.

15. Tantangan dan Langkah Berikutnya

Selamat, Anda telah membuat sistem multi-agen Aidemy. Anda telah meletakkan fondasi yang kuat untuk pendidikan yang didukung AI. Sekarang, mari kita pertimbangkan beberapa tantangan dan potensi peningkatan di masa mendatang untuk lebih memperluas kemampuannya dan memenuhi kebutuhan di dunia nyata:

Pembelajaran Interaktif dengan Tanya Jawab Live:

  • Tantangan: Dapatkah Anda memanfaatkan Live API Gemini 2 untuk membuat fitur Tanya Jawab real-time bagi siswa? Bayangkan ruang kelas virtual tempat siswa dapat mengajukan pertanyaan dan menerima respons langsung yang didukung AI.

Pengiriman dan Penilaian Tugas Otomatis:

  • Tantangan: Buat desain dan terapkan sistem yang memungkinkan siswa mengirimkan tugas secara digital dan menilainya secara otomatis dengan AI, dengan mekanisme untuk mendeteksi dan mencegah plagiarisme. Tantangan ini memberikan peluang besar untuk mempelajari Retrieval Augmented Generation (RAG) guna meningkatkan akurasi dan keandalan proses penilaian dan deteksi plagiarisme.

aidemy-climb

16. Pembersihan

Setelah membuat dan menjelajahi sistem multi-agen Aidemy, sekarang saatnya membersihkan lingkungan Google Cloud.

  1. Menghapus layanan Cloud Run
gcloud run services delete aidemy-planner --region=us-central1 --quiet
gcloud run services delete aidemy-portal --region=us-central1 --quiet
gcloud run services delete courses-agent --region=us-central1 --quiet
gcloud run services delete book-provider --region=us-central1 --quiet
gcloud run services delete assignment-agent --region=us-central1 --quiet
  1. Menghapus pemicu Eventarc
gcloud eventarc triggers delete portal-assignment-trigger --location=us --quiet
gcloud eventarc triggers delete plan-topic-trigger --location=us-central1 --quiet
gcloud eventarc triggers delete portal-assignment-trigger --location=us-central1 --quiet
ASSIGNMENT_AGENT_TRIGGER=$(gcloud eventarc triggers list --project="$PROJECT_ID" --location=us-central1 --filter="name:assignment-agent" --format="value(name)")
COURSES_AGENT_TRIGGER=$(gcloud eventarc triggers list --project="$PROJECT_ID" --location=us-central1 --filter="name:courses-agent" --format="value(name)")
gcloud eventarc triggers delete $ASSIGNMENT_AGENT_TRIGGER --location=us-central1 --quiet
gcloud eventarc triggers delete $COURSES_AGENT_TRIGGER --location=us-central1 --quiet
  1. Menghapus topik Pub/Sub
gcloud pubsub topics delete plan --project="$PROJECT_ID" --quiet
  1. Menghapus instance Cloud SQL
gcloud sql instances delete aidemy --quiet
  1. Menghapus repositori Artifact Registry
gcloud artifacts repositories delete agent-repository --location=us-central1 --quiet
  1. Menghapus secret Secret Manager
gcloud secrets delete db-user --quiet
gcloud secrets delete db-pass --quiet
gcloud secrets delete db-name --quiet
  1. Menghapus instance Compute Engine (jika dibuat untuk Deepseek)
gcloud compute instances delete ollama-instance --zone=us-central1-a --quiet
  1. Menghapus aturan firewall untuk instance Deepseek
gcloud compute firewall-rules delete allow-ollama-11434 --quiet
  1. Menghapus bucket Cloud Storage
export COURSE_BUCKET_NAME=$(gcloud storage buckets list --format="value(name)" | grep aidemy-recap)
export ASSIGNMENT_BUCKET=$(gcloud storage buckets list --format="value(name)" | grep aidemy-assignment)
gsutil rb gs://$COURSE_BUCKET_NAME
gsutil rb gs://$ASSIGNMENT_BUCKET

aidemy-broom