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

1. Einführung

Hallo! Sie finden die Idee von KI-Agenten also gut – kleine Helfer, die Dinge für Sie erledigen können, ohne dass Sie selbst etwas tun müssen, richtig? Super! Aber seien wir ehrlich: Ein einziger Kundenservicemitarbeiter reicht nicht immer aus, vor allem, wenn Sie größere, komplexere Projekte angehen. Sie werden wahrscheinlich ein ganzes Team davon brauchen. Hier kommen Multi-Agenten-Systeme ins Spiel.

Mit LLMs bieten Bots im Vergleich zur herkömmlichen Hardcoding eine unglaubliche Flexibilität. Aber es gibt immer einen Haken: Sie bringen ihre eigenen kniffligen Herausforderungen mit sich. Und genau darauf werden wir in diesem Workshop eingehen.

Titel

Hier erfährst du, was du lernen wirst – sieh es als Level-Up für deine Kundenservicemitarbeiter:

Ersten Agenten mit LangGraph erstellen: Wir erstellen mit LangGraph, einem beliebten Framework, einen eigenen Agenten. Sie lernen, wie Sie Tools erstellen, die eine Verbindung zu Datenbanken herstellen, die neueste Gemini 2 API für Internetsuchen nutzen und die Prompts und Antworten optimieren, damit Ihr Bot nicht nur mit LLMs, sondern auch mit vorhandenen Diensten interagieren kann. Außerdem zeigen wir Ihnen, wie Funktionsaufrufe funktionieren.

Agent Orchestration, Your Way (Agenten-Orchestrierung nach Ihren Wünschen): Wir sehen uns verschiedene Möglichkeiten zur Orchestration Ihrer Kundenservicemitarbeiter an, von einfachen geraden Pfaden bis hin zu komplexeren Mehrpfad-Szenarien. Sie steuern den Ablauf Ihres Kundenserviceteams.

Multi-Agent-Systeme: Sie erfahren, wie Sie ein System einrichten, in dem Ihre Kundenservicemitarbeiter dank einer ereignisgesteuerten Architektur zusammenarbeiten und Aufgaben gemeinsam erledigen können.

LLM-Freiheit: Wir sind nicht auf ein einziges LLM beschränkt. Sie erfahren, wie Sie mehrere LLMs verwenden und ihnen unterschiedliche Rollen zuweisen, um mithilfe von coolen „Denkmodellen“ die Problemlösungskompetenz zu verbessern.

Dynamische Inhalte? Kein Problem!: Stellen Sie sich vor, Ihr Kundenservicemitarbeiter erstellt in Echtzeit dynamische Inhalte, die speziell auf jeden Nutzer zugeschnitten sind. Wir zeigen Ihnen, wie das geht.

Mit Google Cloud in die Cloud: Sie können nicht nur auf einem Notebook herumspielen. Wir zeigen Ihnen, wie Sie Ihr Multi-Agenten-System in Google Cloud entwerfen und bereitstellen, damit es für die Praxis bereit ist.

Dieses Projekt ist ein gutes Beispiel dafür, wie Sie alle Techniken anwenden können, über die wir gesprochen haben.

2. Architektur

Die Arbeit als Lehrkraft oder im Bildungswesen kann sehr lohnend sein, aber seien wir ehrlich: Die Arbeitsbelastung, insbesondere die Vorbereitung, kann eine Herausforderung sein. Außerdem gibt es oft nicht genügend Personal und Nachhilfe kann teuer sein. Deshalb schlagen wir einen KI-gestützten Lehrassistenten vor. Dieses Tool kann die Arbeit für Lehrkräfte erleichtern und die Lücke schließen, die durch Personalmangel und den Mangel an bezahlbaren Nachhilfekräften entsteht.

Unser KI-Lehrassistent kann detaillierte Unterrichtspläne, lustige Quizze, leicht verständliche Audiozusammenfassungen und personalisierte Aufgaben erstellen. So können sich Lehrkräfte auf das Wesentliche konzentrieren: den Kontakt zu den Schülern und Studenten aufbauen und ihnen dabei helfen, die Freude am Lernen zu entdecken.

Das System hat zwei Websites: eine für Lehrkräfte, auf der sie Unterrichtspläne für die kommenden Wochen erstellen,

Planer

und eine für Schüler und Studenten, über die sie auf Quizze, Audiozusammenfassungen und Aufgaben zugreifen können. Portal

Sehen wir uns die Architektur an, die unserem Lehrassistenten Aidemy zugrunde liegt. Wie Sie sehen, haben wir es in mehrere Hauptkomponenten unterteilt, die alle zusammen dafür sorgen, dass das funktioniert.

Architektur

Wichtige Architekturelemente und ‑technologien:

Google Cloud Platform (GCP): Zentral für das gesamte System:

  • Vertex AI: Zugriff auf die Gemini-LLMs von Google
  • Cloud Run: Serverlose Plattform zum Bereitstellen containerisierter Agenten und Funktionen.
  • Cloud SQL: PostgreSQL-Datenbank für Kursdaten.
  • Pub/Sub und Eventarc: Grundlage der ereignisgesteuerten Architektur, die die asynchrone Kommunikation zwischen Komponenten ermöglicht.
  • Cloud Storage: Hier werden Audiozusammenfassungen und Aufgabendateien gespeichert.
  • Secret Manager: Verwaltet Datenbankanmeldedaten sicher.
  • Artifact Registry: Hier werden Docker-Images für die Agenten gespeichert.
  • Compute Engine: Bereitstellung eines selbst gehosteten LLM anstelle von Anbieterlösungen

LLMs: Das „Gehirn“ des Systems:

  • Gemini-Modelle von Google: (Gemini 1.0 Pro, Gemini 2 Flash, Gemini 2 Flash Thinking, Gemini 1.5-pro) Wird für die Unterrichtsplanung, die Inhaltsgenerierung, die dynamische HTML-Erstellung, die Erklärung von Quizzen und die Kombination der Aufgaben verwendet.
  • DeepSeek: Wird für die spezielle Aufgabe verwendet, Selbstlernaufgaben zu generieren

LangChain und LangGraph: Frameworks für die Entwicklung von LLM-Anwendungen

  • Erleichtert das Erstellen komplexer Workflows mit mehreren Kundenservicemitarbeitern.
  • Ermöglicht die intelligente Orchestrierung von Tools (API-Aufrufe, Datenbankabfragen, Websuchen).
  • Implementiert eine ereignisgesteuerte Architektur für Systemskalierbarkeit und -flexibilität.

Im Wesentlichen kombiniert unsere Architektur die Leistungsfähigkeit von LLMs mit strukturierten Daten und ereignisgesteuerter Kommunikation, die alle in Google Cloud ausgeführt werden. So können wir einen skalierbaren, zuverlässigen und effektiven Lehrassistenten entwickeln.

3. Hinweis

Wählen Sie in der Google Cloud Console auf der Seite der Projektauswahl ein Google Cloud-Projekt aus oder erstellen Sie eines. Die Abrechnung für das Cloud-Projekt muss aktiviert sein. So prüfen Sie, ob die Abrechnung für ein Projekt aktiviert ist

👉 Klicken Sie oben in der Google Cloud Console auf Cloud Shell aktivieren (das Terminalsymbol oben im Cloud Shell-Bereich) und dann auf die Schaltfläche „Editor öffnen“ (das Symbol für einen offenen Ordner mit einem Bleistift). Dadurch wird der Cloud Shell-Code-Editor im Fenster geöffnet. Auf der linken Seite wird ein Datei-Explorer angezeigt.

Cloud Shell

👉 Klicken Sie wie gezeigt auf die Schaltfläche Cloud Code-Anmeldung in der unteren Statusleiste. Autorisieren Sie das Plug-in wie beschrieben. Wenn in der Statusleiste Cloud Code – kein Projekt angezeigt wird, wählen Sie diese Option im Drop-down-Menü „Google Cloud-Projekt auswählen“ aus und wählen Sie dann das gewünschte Google Cloud-Projekt aus der Liste der von Ihnen erstellten Projekte aus.

Anmeldeprojekt

👉Öffnen Sie das Terminal in der Cloud IDE, Neues Terminal

👉 Prüfen Sie im Terminal mit dem folgenden Befehl, ob Sie bereits authentifiziert sind und das Projekt auf Ihre Projekt-ID festgelegt ist:

gcloud auth list

👉 Führen Sie dann diesen Befehl aus:

gcloud config set project <YOUR_PROJECT_ID>

👉 Führen Sie den folgenden Befehl aus, um die erforderlichen Google Cloud APIs zu aktivieren:

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

Das kann einige Minuten dauern.

Gemini Code Assist in der Cloud Shell-IDE aktivieren

Klicken Sie wie gezeigt im linken Bereich auf die Schaltfläche Code Assist und wählen Sie noch einmal das richtige Google Cloud-Projekt aus. Wenn Sie aufgefordert werden, die Cloud AI Companion API zu aktivieren, tun Sie dies und fahren Sie fort. Nachdem Sie Ihr Google Cloud-Projekt ausgewählt haben, prüfen Sie, ob es in der Cloud Code-Statusmeldung in der Statusleiste angezeigt wird und ob Sie rechts in der Statusleiste Code Assist aktiviert haben, wie unten dargestellt:

Codeassist aktivieren

Berechtigung einrichten

👉 Dienstkontoberechtigung einrichten

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"

Berechtigungen gewähren 👉Cloud Storage (Lesen/Schreiben):

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

👉Pub/Sub (Veröffentlichen/Empfangen):

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 (Lese- und Schreibzugriff):

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

👉Eventarc (Ereignisse empfangen):

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

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

👉Secret Manager (Lesen):

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

👉 Ergebnis in der IAM-Konsole prüfenIAM-Konsole

4. Ersten Agent erstellen

Bevor wir uns mit komplexen Multi-Agenten-Systemen befassen, müssen wir einen grundlegenden Baustein definieren: einen einzelnen, funktionalen Agenten. In diesem Abschnitt erstellen wir einen einfachen „Buchanbieter“-Agenten. Der Buchanbieter-Agent nimmt eine Kategorie als Eingabe und verwendet eine Gemini-LLM, um eine JSON-Darstellung eines Buches in dieser Kategorie zu generieren. Diese Buchvorschläge werden dann als REST API-Endpunkt bereitgestellt .

Buchanbieter

👉 Öffnen Sie in einem anderen Browsertab die Google Cloud Console und gehen Sie im Navigationsmenü (☰) zu „Cloud Run“. Klicken Sie auf die Schaltfläche „+ … FUNKTION ERSTELLEN“.

Funktion erstellen

👉 Als Nächstes konfigurieren wir die grundlegenden Einstellungen der Cloud Run-Funktion:

  • Dienstname: book-provider
  • Region: us-central1
  • Laufzeit: Python 3.12
  • Authentifizierung: Allow unauthenticated invocations auf „Aktiviert“

👉 Übernehmen Sie die anderen Standardeinstellungen und klicken Sie auf Erstellen. Daraufhin gelangen Sie zum Quellcode-Editor.

Sie sehen bereits ausgefüllte main.py- und requirements.txt-Dateien.

main.py enthält die Geschäftslogik der Funktion, requirements.txt die erforderlichen Pakete.

👉Jetzt sind wir bereit, Code zu schreiben. Aber bevor wir loslegen, sehen wir uns an, ob Gemini Code Assist uns einen Vorsprung verschaffen kann. Kehren Sie zum Cloud Shell-Editor zurück, klicken Sie auf das Symbol für Gemini Code Assist und fügen Sie die folgende Anfrage in das Promptfeld ein: 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 generiert dann eine potenzielle Lösung, die sowohl den Quellcode als auch eine Abhängigkeitsdatei „requirements.txt“ enthält.

Wir empfehlen Ihnen, den von Code Assist generierten Code mit der getesteten, korrekten Lösung unten zu vergleichen. So können Sie die Effektivität des Tools bewerten und mögliche Abweichungen erkennen. LLMs sollten niemals blind vertraut werden. Code Assist kann jedoch ein hervorragendes Tool für das schnelle Prototyping und das Generieren erster Codestrukturen sein und sollte für einen guten Start verwendet werden.

Da dies ein Workshop ist, verwenden wir den unten angegebenen bestätigten Code. Sie können aber in Ihrer Freizeit mit dem von Code Assist generierten Code experimentieren, um sich ein besseres Bild von seinen Funktionen und Einschränkungen zu machen.

👉 Kehren Sie zum Quellcode-Editor der Cloud Run-Funktion zurück (auf dem anderen Browsertab). Ersetzen Sie den vorhandenen Inhalt von main.py durch den folgenden Code:

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)

👉 Ersetzen Sie den Inhalt von „requirements.txt“ durch Folgendes:

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

👉 Wir legen den Funktionseingangspunkt fest: recommended

03-02-function-create.png

👉 Klicken Sie auf SPEICHERN UND BEITRETEN, um die Funktion bereitzustellen. Warten Sie, bis der Bereitstellungsvorgang abgeschlossen ist. In der Cloud Console wird der Status angezeigt. Dieser Vorgang kann einige Minuten dauern.

Alt-Text 👉 Nach der Bereitstellung kehren Sie zum Cloud Shell-Editor zurück und führen Sie im Terminal Folgendes aus:

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

Es sollten einige Buchdaten im JSON-Format angezeigt werden.

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

Glückwunsch! Sie haben eine Cloud Run-Funktion bereitgestellt. Dies ist einer der Dienste, die wir bei der Entwicklung unseres Aidemy-Agenten integrieren werden.

5. Building Tools: Connecting Agents to RESTFUL service and Data

Laden Sie das Bootstrap-Skelettprojekt herunter. Achten Sie darauf, dass Sie sich im Cloud Shell-Editor befinden. Führen Sie im Terminal folgenden Befehl aus:

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

Nachdem Sie diesen Befehl ausgeführt haben, wird in Ihrer Cloud Shell-Umgebung ein neuer Ordner mit dem Namen aidemy-bootstrap erstellt.

Im Explorer-Bereich des Cloud Shell-Editors (normalerweise links) sollte jetzt der Ordner angezeigt werden, der beim Klonen des Git-Repositories aidemy-bootstrap erstellt wurde. Öffnen Sie den Stammordner Ihres Projekts im Explorer. Darin finden Sie den Unterordner planner. Öffnen Sie auch diesen. Projekt-Explorer

Fangen wir an, die Tools zu entwickeln, mit denen unsere Kundenservicemitarbeiter wirklich hilfreich sein können. Wie Sie wissen, sind LLMs hervorragend für die Argumentation und Textgenerierung geeignet, benötigen aber Zugriff auf externe Ressourcen, um reale Aufgaben auszuführen und korrekte, aktuelle Informationen bereitzustellen. Sie können diese Tools als „Schweizer Taschenmesser“ des Agents betrachten, mit dem er mit der Welt interagieren kann.

Beim Erstellen eines Bots kann es leicht passieren, dass Sie eine Menge Details hartcodieren. Das führt zu einem nicht flexiblen Bot. Stattdessen hat der Agent durch das Erstellen und Verwenden von Tools Zugriff auf externe Logik oder Systeme, was ihm sowohl die Vorteile des LLM als auch der traditionellen Programmierung bietet.

In diesem Abschnitt legen wir die Grundlage für den Planungs-Agenten, mit dem Lehrkräfte Stundenpläne erstellen können. Bevor der Agent mit der Erstellung eines Plans beginnt, möchten wir Grenzen setzen, indem wir weitere Details zum Fach und zum Thema angeben. Wir erstellen drei Tools:

  1. RESTful API-Aufruf: Interaktion mit einer vorhandenen API zum Abrufen von Daten.
  2. Datenbankabfrage:Abrufen strukturierter Daten aus einer Cloud SQL-Datenbank.
  3. Google Suche:Zugriff auf Echtzeitinformationen aus dem Web.

Buchempfehlungen aus einer API abrufen

Erstellen wir zuerst ein Tool, das Buchempfehlungen von der book-provider API abruft, die wir im vorherigen Abschnitt bereitgestellt haben. Hier sehen Sie, wie ein Kundenservicemitarbeiter vorhandene Dienste nutzen kann.

Buch empfehlen

Öffnen Sie im Cloud Shell-Editor das Projekt aidemy-bootstrap, das Sie im vorherigen Abschnitt geklont haben. 👉 Bearbeiten Sie die Datei book.py im Ordner planner und fügen Sie den folgenden Code ein:

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

Erklärung:

  • recommend_book(query: str): Diese Funktion nimmt die Suchanfrage eines Nutzers als Eingabe.
  • LLM-Interaktion: Dabei wird die Kategorie mithilfe des LLM aus der Abfrage extrahiert. Hier sehen Sie, wie Sie mit dem LLM Parameter für Tools erstellen können.
  • API-Aufruf: Es wird eine POST-Anfrage an die API des Buchanbieters gesendet, wobei die Kategorie und die gewünschte Anzahl von Büchern übergeben werden.

👉 Um diese neue Funktion zu testen, legen Sie die Umgebungsvariable fest und führen Sie Folgendes aus :

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

👉 Installieren Sie die Abhängigkeiten und führen Sie den Code aus, um sicherzustellen, dass er funktioniert. Führen Sie dazu Folgendes aus:

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

Ignorieren Sie das Pop-up-Fenster mit der Git-Warnung.

Sie sollten einen JSON-String mit Buchrezensionen sehen, die aus der API des Buchanbieters abgerufen wurden.

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

Wenn Sie das sehen, funktioniert das erste Tool richtig.

Anstatt einen RESTful API-Aufruf mit bestimmten Parametern explizit zu erstellen, verwenden wir natürliche Sprache („Ich belege einen Kurs…“). Der Kundenservicemitarbeiter extrahiert dann mithilfe von NLP intelligent die erforderlichen Parameter (z. B. die Kategorie). So wird verdeutlicht, wie der Kundenservicemitarbeiter Natural Language Understanding nutzt, um mit der API zu interagieren.

Vergleichsanruf

Entfernen Sie den folgenden Testcode aus der 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."))

Kursdaten aus einer Datenbank abrufen

Als Nächstes erstellen wir ein Tool, das strukturierte Kursdaten aus einer Cloud SQL PostgreSQL-Datenbank abruft. So kann der Kundenservicemitarbeiter auf eine zuverlässige Informationsquelle für die Unterrichtsplanung zugreifen.

create db

👉 Führen Sie die folgenden Befehle im Terminal aus, um eine Cloud SQL-Instanz mit dem Namen „aidemy“ zu erstellen . Das kann einige Zeit dauern.

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

👉Erstellen Sie als Nächstes in der neuen Instanz eine Datenbank mit dem Namen aidemy-db.

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

Prüfen wir die Instanz in Cloud SQL in der Google Cloud Console. Es sollte eine Cloud SQL-Instanz mit dem Namen aidemy aufgeführt sein. Klicken Sie auf den Namen der Instanz, um die Details aufzurufen. Klicken Sie auf der Detailseite der Cloud SQL-Instanz im linken Navigationsmenü auf „SQL Studio“. Damit wird ein neuer Tab geöffnet.

Klicken Sie, um eine Verbindung zur Datenbank herzustellen. In SQL Studio anmelden

Wählen Sie aidemy-db als Datenbank aus. Geben Sie postgres als Nutzer und 1234qwer als Passwort ein. sql studio sign in

👉 Fügen Sie im Abfrageeditor von SQL Studio den folgenden SQL-Code ein:

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

Mit diesem SQL-Code wird eine Tabelle mit dem Namen curriculums erstellt und einige Beispieldaten werden eingefügt. Klicken Sie auf Ausführen, um den SQL-Code auszuführen. Sie sollten eine Bestätigungsmeldung erhalten, dass die Befehle erfolgreich ausgeführt wurden.

👉 Maximieren Sie den Explorer, suchen Sie die neu erstellte Tabelle und klicken Sie auf Abfrage. Daraufhin sollte ein neuer Editor-Tab mit dem für Sie generierten SQL-Code geöffnet werden.

sql studio select table

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

👉 Klicken Sie auf Ausführen.

In der Ergebnistabelle sollten die Datenzeilen angezeigt werden, die Sie im vorherigen Schritt eingefügt haben.

Nachdem Sie eine Datenbank mit Beispieldaten für das Curriculum erstellt haben, erstellen wir ein Tool zum Abrufen dieser Daten.

👉 Bearbeiten Sie im Cloud Code-Editor die Datei curriculums.py im Ordner aidemy-bootstrap und fügen Sie den folgenden Code ein:

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

Erklärung:

  • Umgebungsvariablen: Der Code ruft Datenbankanmeldedaten und Verbindungsinformationen aus Umgebungsvariablen ab (weitere Informationen dazu finden Sie unten).
  • connect_with_connector(): Mit dieser Funktion wird über den Cloud SQL-Connector eine sichere Verbindung zur Datenbank hergestellt.
  • get_curriculum(year: int, subject: str): Diese Funktion nimmt das Jahr und das Fach als Eingabe, fragt die Tabelle „curriculums“ ab und gibt die entsprechende Curriculumbeschreibung zurück.

👉 Bevor wir den Code ausführen können, müssen wir einige Umgebungsvariablen festlegen. Führen Sie dazu im Terminal Folgendes aus:

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"

👉 Fügen Sie zum Testen den folgenden Code am Ende von curriculums.py ein:

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

👉 Code ausführen:

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

Die Beschreibung des Lehrplans für Mathematik in der 6. Klasse sollte auf der Konsole ausgegeben werden.

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

Wenn Sie die Beschreibung des Lehrplans sehen, funktioniert das Datenbanktool richtig. Beenden Sie das Script durch Drücken von Ctrl+C.

Entfernen Sie den folgenden Testcode aus der curriculums.py.

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

👉 Virtuelle Umgebung beenden, im Terminal Folgendes ausführen:

deactivate

6. Tools für die Gebäudebranche: Zugriff auf Echtzeitinformationen aus dem Web

Zum Schluss erstellen wir ein Tool, das mit der Gemini 2- und Google-Suche-Integration auf Echtzeitinformationen aus dem Web zugreift. So können sie auf dem neuesten Stand bleiben und relevante Ergebnisse liefern.

Die Einbindung von Gemini 2 in die Google Suche API erweitert die Funktionen von Kundenservicemitarbeitern, da sie genauere und kontextbezogen relevantere Suchergebnisse liefert. So können Kundenservicemitarbeiter auf aktuelle Informationen zugreifen und ihre Antworten auf reale Daten stützen, wodurch Halluzinationen minimiert werden. Die verbesserte API-Integration ermöglicht auch mehr Suchanfragen in natürlicher Sprache, sodass Kundenservicemitarbeiter komplexe und differenzierte Suchanfragen stellen können.

Suchen

Diese Funktion nimmt eine Suchanfrage, ein Lehrplan, ein Fach und ein Jahr als Eingabe an und ruft mithilfe der Gemini API und der Google Suche relevante Informationen aus dem Internet ab. Bei genauerem Hinsehen wird das Google Generative AI SDK verwendet, um Funktionen aufzurufen, ohne ein anderes Framework zu verwenden.

👉 Bearbeiten Sie search.py im Ordner aidemy-bootstrap und fügen Sie den folgenden Code ein:

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)

Erklärung:

  • Tool definieren – google_search_tool: Das GoogleSearch-Objekt in ein Tool einbetten
  • search_latest_resource(search_text: str, subject: str, year: int): Diese Funktion nimmt eine Suchanfrage, ein Thema und ein Jahr als Eingabe an und führt mit der Gemini API eine Google-Suche durch. Gemini-Modell
  • GenerateContentConfig: Hier wird festgelegt, dass es Zugriff auf das GoogleSearch-Tool hat.

Das Gemini-Modell analysiert den Suchtext intern und ermittelt, ob es die Frage direkt beantworten kann oder das GoogleSearch-Tool verwenden muss. Dies ist ein wichtiger Schritt im Logikprozess des LLM. Das Modell wurde darauf trainiert, Situationen zu erkennen, in denen externe Tools erforderlich sind. Wenn das Modell entscheidet, das GoogleSearch-Tool zu verwenden, wird die tatsächliche Aufrufung vom Google Generative AI SDK ausgeführt. Das SDK sendet die Entscheidung des Modells und die generierten Parameter an die Google Suche API. Dieser Teil ist im Code für den Nutzer ausgeblendet.

Das Gemini-Modell integriert die Suchergebnisse dann in seine Antwort. Anhand der Informationen kann der Bot die Frage des Nutzers beantworten, eine Zusammenfassung erstellen oder eine andere Aufgabe ausführen.

👉 Führen Sie den Code aus, um ihn zu testen:

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

Sie sollten die Antwort der Gemini Search API mit Suchergebnissen zu „Syllabus for Year 5 Mathematics“ sehen. Die genaue Ausgabe hängt von den Suchergebnissen ab, ist aber ein JSON-Objekt mit Informationen zur Suche.

Wenn Sie Suchergebnisse sehen, funktioniert das Google-Suchtool ordnungsgemäß. Beenden Sie das Script durch Drücken von Ctrl+C.

👉 Entferne den letzten Teil des Codes.

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)

👉 Virtuelle Umgebung beenden, im Terminal Folgendes ausführen:

deactivate

Glückwunsch! Sie haben jetzt drei leistungsstarke Tools für Ihren Planner-Kundenservicemitarbeiter erstellt: einen API-Connector, einen Datenbank-Connector und ein Google-Suchtool. Mit diesen Tools können die Kundenservicemitarbeiter auf die Informationen und Funktionen zugreifen, die sie für die Erstellung effektiver Lehrpläne benötigen.

7. Orchestrierung mit LangGraph

Nachdem wir unsere einzelnen Tools erstellt haben, ist es an der Zeit, sie mit LangGraph zu orchestrieren. So können wir einen ausgefeilteren „Planer“-Agenten erstellen, der basierend auf der Anfrage des Nutzers intelligent entscheiden kann, welche Tools wann verwendet werden sollen.

LangGraph ist eine Python-Bibliothek, die das Erstellen zustandsbasierter, mehrstufiger Anwendungen mit Large Language Models (LLMs) vereinfacht. Stellen Sie sich das als Framework für die Orchestrierung komplexer Unterhaltungen und Workflows vor, die LLMs, Tools und andere Agenten umfassen.

Schlüsselkonzepte:

  • Graphstruktur:LangGraph stellt die Logik Ihrer Anwendung als gerichteten Graphen dar. Jeder Knoten im Diagramm steht für einen Schritt im Prozess (z.B. einen Aufruf an eine LLM, einen Toolaufruf oder eine bedingte Prüfung). Kanten definieren den Ablauf der Ausführung zwischen Knoten.
  • Status:LangGraph verwaltet den Status Ihrer Anwendung, während sie sich durch den Graphen bewegt. Dieser Status kann Variablen wie die Eingabe des Nutzers, die Ergebnisse von Toolaufrufen, Zwischenergebnisse von LLMs und alle anderen Informationen enthalten, die zwischen den Schritten beibehalten werden müssen.
  • Knoten:Jeder Knoten steht für eine Berechnung oder Interaktion. Sie können:
    • Tool-Knoten:Ein Tool verwenden (z.B. eine Websuche durchführen, eine Datenbank abfragen)
    • Funktionsknoten:Führen einer Python-Funktion aus.
  • Kanten:Knoten verbinden und den Ablauf der Ausführung definieren. Sie können:
    • Direkte Kanten:Ein einfacher, bedingungsloser Fluss von einem Knoten zum anderen.
    • Bedingte Kanten:Der Ablauf hängt vom Ergebnis eines bedingten Knotens ab.

LangGraph

Wir verwenden LangGraph, um die Orchestrierung zu implementieren. Bearbeiten wir die Datei aidemy.py im Ordner aidemy-bootstrap, um die Logik für LangGraph zu definieren. 👉 Fügen Sie am Ende von aidemy.py den folgenden Code ein:

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

Diese Funktion ist dafür verantwortlich, den aktuellen Status der Unterhaltung zu berücksichtigen, dem LLM eine Systemnachricht zu senden und es dann aufzufordern, eine Antwort zu generieren. Das LLM kann entweder direkt auf den Nutzer antworten oder eines der verfügbaren Tools verwenden.

tools : Diese Liste enthält die Tools, die dem Kundenservicemitarbeiter zur Verfügung stehen. Es enthält drei Toolfunktionen, die wir in den vorherigen Schritten definiert haben: get_curriculum, search_latest_resource und recommend_book. llm.bind_tools(tools): Damit wird die Tools-Liste an das llm-Objekt gebunden. Durch die Bindung der Tools wird dem LLM mitgeteilt, dass diese Tools verfügbar sind, und es erhält Informationen zur Verwendung (z.B. die Namen der Tools, die von ihnen akzeptierten Parameter und ihre Funktion).

Wir verwenden LangGraph, um die Orchestrierung zu implementieren. 👉 Fügen Sie am Ende von aidemy.py den folgenden Code ein:

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

Erklärung:

  • StateGraph(MessagesState):Erstellt ein StateGraph-Objekt. Ein StateGraph ist ein Kernkonzept in LangGraph. Darin wird der Workflow Ihres Kundenservicemitarbeiters als Grafik dargestellt, wobei jeder Knoten in der Grafik einen Schritt im Prozess darstellt. Sie können sich das als Vorlage für die Argumentation und das Verhalten des Bots vorstellen.
  • Bedingter Pfad: Das Argument tools_condition, das vom Knoten "determine_tool" ausgeht, ist wahrscheinlich eine Funktion, die anhand der Ausgabe der Funktion determine_tool bestimmt, welcher Pfad zu verfolgen ist. Bedingte Kanten ermöglichen es, den Graphen basierend auf der Entscheidung des LLMs, welches Tool verwendet werden soll (oder ob direkt auf den Nutzer geantwortet werden soll), zu verzweigen. Hier kommt die „Intelligenz“ des Bots ins Spiel: Er kann sein Verhalten dynamisch an die Situation anpassen.
  • Schleife:Dem Graphen wird eine Kante hinzugefügt, die den Knoten "tools" wieder mit dem Knoten "determine_tool" verbindet. Dadurch wird eine Schleife im Graphen erstellt, die es dem Kundenservicemitarbeiter ermöglicht, Tools wiederholt zu verwenden, bis er genügend Informationen gesammelt hat, um die Aufgabe abzuschließen und eine zufriedenstellende Antwort zu geben. Dieser Loop ist entscheidend für komplexe Aufgaben, die mehrere Schritte der Argumentation und Informationsbeschaffung erfordern.

Testen wir jetzt unseren Planner-Bot, um zu sehen, wie er die verschiedenen Tools orchestriert.

Mit diesem Code wird die Funktion „prep_class“ mit einer bestimmten Nutzereingabe ausgeführt. Dabei wird eine Anfrage simuliert, einen Lehrplan für Mathematik im Fach Geometrie für die 5. Klasse zu erstellen, unter Verwendung des Lehrplans, Buchempfehlungen und der neuesten Internetressourcen.

Wenn Sie das Terminal geschlossen haben oder die Umgebungsvariablen nicht mehr festgelegt sind, führen Sie die folgenden Befehle noch einmal aus.

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"

👉 Code ausführen:

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

Sehen Sie sich das Protokoll im Terminal an. Sie sollten sehen, dass der Kundenservicemitarbeiter alle drei Tools aufruft (Schullehrplan abrufen, Buchrezensionen abrufen und nach den neuesten Ressourcen suchen), bevor er den endgültigen Lehrplan vorlegt. Das zeigt, dass die LangGraph-Orchestrierung richtig funktioniert und der Kundenservicemitarbeiter alle verfügbaren Tools intelligent nutzt, um die Anfrage des Nutzers zu erfüllen.

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

Beenden Sie das Skript durch Druck auf Ctrl+C.

👉 Ersetzen Sie jetzt den Testcode durch einen anderen Prompt, für den andere Tools aufgerufen werden müssen.

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

Wenn Sie das Terminal geschlossen haben oder die Umgebungsvariablen nicht mehr festgelegt sind, führen Sie die folgenden Befehle noch einmal aus.

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"

👉 Führen Sie den Code noch einmal aus:

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

Was ist Ihnen dieses Mal aufgefallen? Welche Tools hat der Kundenservicemitarbeiter aufgerufen? Sie sollten sehen, dass der Agent dieses Mal nur das Tool „search_latest_resource“ aufruft. Das liegt daran, dass im Prompt nicht angegeben ist, dass die anderen beiden Tools erforderlich sind. Unser LLM ist intelligent genug, um die anderen Tools nicht aufzurufen.

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

Beenden Sie das Skript durch Druck auf Ctrl+C. Entfernen Sie den Testcode, damit die Datei „aidemy.py“ sauber bleibt:

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

Nachdem wir die Logik für den Kundenservicemitarbeiter definiert haben, starten wir die Flask-Webanwendung. So erhalten Lehrkräfte eine vertraute formularbasierte Oberfläche, über die sie mit dem Kundenservicemitarbeiter interagieren können. Chatbot-Interaktionen sind bei LLMs zwar üblich, wir entscheiden uns jedoch für eine traditionelle Benutzeroberfläche für das Einreichen von Formularen, da sie für viele Pädagogen intuitiver sein kann.

Wenn Sie das Terminal geschlossen haben oder die Umgebungsvariablen nicht mehr festgelegt sind, führen Sie die folgenden Befehle noch einmal aus.

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"

👉Starten Sie jetzt die Web-UI.

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

Suchen Sie in der Cloud Shell-Terminalausgabe nach Startmeldungen. Normalerweise gibt Flask Meldungen aus, die angeben, dass es ausgeführt wird und auf welchem Port.

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.

👉 Wählen Sie im Menü „Webvorschau“ die Option „Vorschau auf Port 8080“ aus. In Cloud Shell wird ein neuer Browsertab oder ein neues Fenster mit der Webvorschau Ihrer Anwendung geöffnet.

Webseite

Wählen Sie in der Anwendungsoberfläche 5 für das Jahr, das Fach Mathematics und geben Sie Geometry in die Add-on-Anfrage Kontingentlimit ein.

Anstatt während des Wartens auf die Antwort nur zu starren, können Sie zum Terminal des Cloud-Editors wechseln. Sie können den Fortschritt und alle von Ihrer Funktion generierten Ausgaben oder Fehlermeldungen im Terminal des Emulators beobachten. 😁

👉 Beenden Sie das Script, indem Sie im Terminal die Taste Ctrl+C drücken.

👉 Virtuelle Umgebung beenden:

deactivate

8. Planner-Agent in der Cloud bereitstellen

Image erstellen und in die Registry übertragen

Übersicht

👉 Jetzt ist es an der Zeit, die App in der Cloud bereitzustellen. Erstellen Sie im Terminal ein Artifact-Repository, um das Docker-Image zu speichern, das wir erstellen werden.

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

Sie sollten die Meldung „Erstelltes Repository [agent-repository]“ sehen.

👉 Führen Sie den folgenden Befehl aus, um das Docker-Image zu erstellen.

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

👉 Wir müssen das Image neu taggen, damit es in Artifact Registry statt in GCR gehostet wird, und das getaggte Image in Artifact Registry pushen:

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

Sobald der Push abgeschlossen ist, können Sie prüfen, ob das Image erfolgreich in Artifact Registry gespeichert wurde. Rufen Sie in der Google Cloud Console die Artifact Registry auf. Das aidemy-planner-Image sollte im agent-repository-Repository zu finden sein. Aidemy-Planerbild

Datenbankanmeldedaten mit Secret Manager sichern

Für die sichere Verwaltung und den Zugriff auf Datenbankanmeldedaten verwenden wir Google Cloud Secret Manager. So wird verhindert, dass sensible Informationen in unseren Anwendungscode hartcodiert werden, und die Sicherheit wird erhöht.

👉Wir erstellen individuelle Secrets für den Nutzernamen, das Passwort und den Namen der Datenbank. So können wir die einzelnen Anmeldedaten unabhängig verwalten. Führen Sie im Terminal folgenden Befehl aus:

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

Die Verwendung von Secret Manager ist ein wichtiger Schritt, um Ihre Anwendung zu schützen und das versehentliche Offenlegen vertraulicher Anmeldedaten zu verhindern. Sie folgt den Best Practices für die Sicherheit von Cloud-Bereitstellungen.

In Cloud Run bereitstellen

Cloud Run ist eine vollständig verwaltete serverlose Plattform, mit der Sie containerisierte Anwendungen schnell und einfach bereitstellen können. Die Infrastrukturverwaltung wird abstrahiert, sodass Sie sich auf das Schreiben und Bereitstellen von Code konzentrieren können. Wir stellen unseren Planer als Cloud Run-Dienst bereit.

👉 Rufen Sie in der Google Cloud Console Cloud Run auf. Klicken Sie auf CONTAINER BEITRETEN und wählen Sie DIENST aus. Konfigurieren Sie Ihren Cloud Run-Dienst:

Cloud Run

  1. Container-Image: Klicken Sie im URL-Feld auf „Auswählen“. Suchen Sie die Image-URL, die Sie in Artifact Registry hochgeladen haben (z.B. us-central1-docker.pkg.dev/YOUR_PROJECT_ID/agent-repository/agent-planner/YOUR_IMG).
  2. Dienstname: aidemy-planner
  3. Region: Wählen Sie die Region us-central1 aus.
  4. Authentifizierung: Für diesen Workshop können Sie die Option „Nicht authentifizierte Aufrufe zulassen“ aktivieren. Für die Produktion sollten Sie den Zugriff einschränken.
  5. Tab Container (Maximieren Sie die Bereiche „Container“ und „Netzwerk“):
    • Tab „Einstellungen“:
      • Ressource
        • Arbeitsspeicher : 2 GB
    • Tab „Variablen und Secrets“:
      • Umgebungsvariablen:
        • Fügen Sie „name: GOOGLE_CLOUD_PROJECT“ und „value: <YOUR_PROJECT_ID>“ hinzu.
        • Fügen Sie „Name: BOOK_PROVIDER_URL“ und „Wert: <YOUR_BOOK_PROVIDER_FUNCTION_URL>“ hinzu.
      • Als Umgebungsvariablen verfügbar gemachte Secrets:
        • Fügen Sie „Name: DB_USER“, „Secret: db-user“ und „Version:latest“ hinzu.
        • Fügen Sie „Name: DB_PASS“, „Secret: db-pass“ und „Version:latest“ hinzu.
        • Fügen Sie „Name: DB_NAME“, „Secret: db-name“ und „Version:latest“ hinzu.

Führen Sie den folgenden Befehl im Terminal aus, um Ihre YOUR_BOOK_PROVIDER_FUNCTION_URL abzurufen:

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

Secret festlegen

Lassen Sie „Sonstiges“ als Standardeinstellung.

👉 Klicke auf ERSTELLEN.

Cloud Run stellt Ihren Dienst bereit.

Klicken Sie nach der Bereitstellung auf den Dienst, um die Detailseite aufzurufen. Die bereitgestellte URL finden Sie oben.

URL

Wählen Sie in der Anwendungsoberfläche 7 für das Jahr, Mathematics als Fach und Algebra im Feld „Add-on-Anfrage“ aus. So erhält der Kundenservicemitarbeiter den erforderlichen Kontext, um einen individuellen Lehrplan zu erstellen.

Glückwunsch! Sie haben mit unserem leistungsstarken KI-Agenten einen Lehrplan erstellt. Das zeigt, wie viel Potenzial Chatbots haben, um die Arbeitsbelastung erheblich zu reduzieren und Aufgaben zu optimieren. So lässt sich die Effizienz steigern und das Leben von Lehrkräften erleichtern.

9. Mehragentensysteme

Nachdem wir das Tool zum Erstellen von Lehrplänen erfolgreich implementiert haben, konzentrieren wir uns nun auf die Erstellung des Studentenportals. Über dieses Portal erhalten die Schüler und Studenten Zugriff auf Quizze, Audiozusammenfassungen und Aufgaben im Zusammenhang mit ihren Kursen. Angesichts des Umfangs dieser Funktion nutzen wir die Vorteile von Multi-Agent-Systemen, um eine modulare und skalierbare Lösung zu entwickeln.

Wie bereits erwähnt, können wir mit einem Multi-Agent-System die Arbeitslast in kleinere, spezialisierte Aufgaben unterteilen, die jeweils von einem bestimmten Kundenservicemitarbeiter bearbeitet werden, anstatt alles einem einzelnen Kundenservicemitarbeiter zu überlassen. Dieser Ansatz bietet mehrere wichtige Vorteile:

Modularität und Wartbarkeit: Anstatt einen einzelnen Agenten zu erstellen, der alles erledigt, sollten Sie kleinere, spezialisierte Agenten mit klar definierten Aufgaben erstellen. Diese Modularität macht das System leichter verständlich, wartbar und fehlerbehebungsfreundlich. Wenn ein Problem auftritt, können Sie es auf einen bestimmten Agenten eingrenzen, anstatt eine riesige Codebasis durchsuchen zu müssen.

Skalierbarkeit: Die Skalierung eines einzelnen komplexen Agents kann zu Engpässen führen. Mit einem Multi-Agenten-System können Sie einzelne Kundenservicemitarbeiter anhand ihrer spezifischen Anforderungen skalieren. Wenn ein Agent beispielsweise eine große Anzahl von Anfragen verarbeitet, können Sie ganz einfach weitere Instanzen dieses Agents starten, ohne dass sich das auf das restliche System auswirkt.

Teamspezialisierung: Stellen Sie sich Folgendes vor: Sie würden keinen Entwickler bitten, eine ganze Anwendung von Grund auf neu zu erstellen. Stattdessen stellen Sie ein Team von Spezialisten zusammen, die jeweils in einem bestimmten Bereich Fachwissen haben. Ebenso können Sie mit einem Multi-Agenten-System die Stärken verschiedener LLMs und Tools nutzen und sie den Kundenservicemitarbeitern zuweisen, die für bestimmte Aufgaben am besten geeignet sind.

Parallele Entwicklung: Unterschiedliche Teams können gleichzeitig an verschiedenen Bots arbeiten, was den Entwicklungsprozess beschleunigt. Da Kundenservicemitarbeiter unabhängig sind, wirken sich Änderungen an einem Kundenservicemitarbeiter weniger wahrscheinlich auf andere Kundenservicemitarbeiter aus.

Ereignisgesteuerte Architektur

Um eine effektive Kommunikation und Koordination zwischen diesen Kundenservicemitarbeitern zu ermöglichen, verwenden wir eine ereignisgesteuerte Architektur. Das bedeutet, dass Kundenservicemitarbeiter auf „Ereignisse“ reagieren, die im System auftreten.

Kundenservicemitarbeiter abonnieren bestimmte Ereignistypen (z.B. „Lehrplan erstellt“, „Aufgabe erstellt“). Wenn ein Ereignis eintritt, werden die entsprechenden Kundenservicemitarbeiter benachrichtigt und können entsprechend reagieren. Diese Entkopplung fördert Flexibilität, Skalierbarkeit und Echtzeitreaktivität.

Übersicht

Zuerst brauchen wir eine Möglichkeit, diese Ereignisse zu übertragen. Dazu richten wir ein Pub/Sub-Thema ein. Erstellen Sie zuerst ein Thema namens plan.

👉 Rufen Sie Pub/Sub in der Google Cloud Console auf und klicken Sie auf die Schaltfläche „Thema erstellen“.

👉 Konfigurieren Sie das Thema mit der ID/dem Namen plan, entfernen Sie das Häkchen bei Add a default subscription, lassen Sie den Rest als Standardeinstellung und klicken Sie auf Erstellen.

Die Pub/Sub-Seite wird aktualisiert. Das neu erstellte Thema sollte jetzt in der Tabelle aufgeführt werden. Thema erstellen

Integrieren wir jetzt die Pub/Sub-Funktion zum Veröffentlichen von Ereignissen in unseren Planner-Agenten. Wir fügen ein neues Tool hinzu, das ein „plan“-Ereignis an das gerade erstellte Pub/Sub-Thema sendet. Dieses Ereignis signalisiert anderen Kundenservicemitarbeitern im System (z. B. im Studentenportal), dass ein neuer Lehrplan verfügbar ist.

👉 Kehren Sie zum Cloud Code-Editor zurück und öffnen Sie die Datei app.py im Ordner planner. Wir werden eine Funktion hinzufügen, mit der das Ereignis veröffentlicht wird. Ersetzen Sie:

##ADD SEND PLAN EVENT FUNCTION HERE

mit

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: Diese Funktion nimmt den generierten Lehrplan als Eingabe, erstellt einen Pub/Sub-Publisher-Client, erstellt den Themenpfad, konvertiert den Lehrplan in einen JSON-String und veröffentlicht die Nachricht im Thema.
  • Liste der Tools: Die Funktion send_plan_event wird der Liste der Tools hinzugefügt und steht dem Kundenservicemitarbeiter zur Verfügung.

Im selben Ordner in der Datei app.py: 👉 Aktualisiere den Prompt, damit der Kundenservicemitarbeiter das Ereignis „Lernplan“ an das Pub/Sub-Thema sendet, nachdem der Lernplan generiert wurde. Ersetzen

### ADD send_plan_event CALL

mit folgenden Angaben:

send_plan_event(teaching_plan)

Durch das Hinzufügen des Tools „send_plan_event“ und das Ändern des Prompts haben wir es unserem Planer-Agenten ermöglicht, Ereignisse in Pub/Sub zu veröffentlichen, sodass andere Komponenten unseres Systems auf die Erstellung neuer Lehrpläne reagieren können. In den folgenden Abschnitten haben wir nun ein funktionierendes Multi-Agenten-System.

10. Schüler und Studenten mit Quizzen auf Abruf befähigen

Stellen Sie sich eine Lernumgebung vor, in der Schüler und Studenten auf eine unendliche Anzahl von Quizzen zugreifen können, die auf ihre spezifischen Lernpläne zugeschnitten sind. Diese Quizze liefern sofort Feedback, einschließlich Antworten und Erklärungen, was zu einem besseren Verständnis des Lernstoffs beiträgt. Dieses Potenzial möchten wir mit unserem KI-gestützten Quizportal ausschöpfen.

Um diese Vision zu verwirklichen, erstellen wir eine Komponente zur Quizgenerierung, mit der Multiple-Choice-Fragen anhand des Inhalts des Lehrplans erstellt werden können.

Übersicht

👉 Gehen Sie im Explorer-Bereich des Cloud Code-Editors zum Ordner portal. Öffnen Sie die Datei quiz.py und fügen Sie den folgenden Code am Ende der Datei ein.

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


Im Agent wird ein JSON-Ausgabeparser erstellt, der speziell für das Verstehen und Strukturieren der Ausgabe des LLM entwickelt wurde. Dabei wird das zuvor definierte QuizQuestion-Modell verwendet, um sicherzustellen, dass die geparste Ausgabe dem richtigen Format entspricht (Frage, Optionen und Antwort).

👉 Führen Sie die folgenden Befehle im Terminal aus, um eine virtuelle Umgebung einzurichten, Abhängigkeiten zu installieren und den Agenten zu starten:

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

Verwenden Sie die Webvorschaufunktion von Cloud Shell, um auf die laufende Anwendung zuzugreifen. Klicken Sie entweder in der oberen Navigationsleiste oder auf der Karte auf der Indexseite auf den Link „Quizze“. Es sollten drei zufällig generierte Quizze für den Schüler/Studenten angezeigt werden. Diese Quizze basieren auf dem Lehrplan und zeigen die Leistungsfähigkeit unseres KI-gestützten Quiz-Generierungssystems.

Quizze

Drücken Sie im Terminal die Taste Ctrl+C, um den lokal laufenden Prozess zu beenden.

Gemini 2 – Nach Erklärungen suchen

Okay, wir haben also Quizze. Das ist ein guter Anfang. Aber was ist, wenn Schüler oder Studenten etwas falsch machen? Dort findet das eigentliche Lernen statt, oder? Wenn wir erklären können, warum die Antwort falsch war und wie man zur richtigen Antwort kommt, ist die Wahrscheinlichkeit viel höher, dass sie sich daran erinnern. Außerdem können Sie so Unklarheiten beseitigen und das Selbstvertrauen der Person stärken.

Deshalb setzen wir auf das „denkende“ Modell von Gemini 2. Sie geben der KI so etwas mehr Zeit, die Dinge zu überdenken, bevor sie eine Erklärung liefert. So kann es detaillierteres und besseres Feedback geben.

Wir möchten herausfinden, ob es Schülern helfen kann, indem es sie unterstützt, Fragen beantwortet und Dinge ausführlich erklärt. Zum Testen beginnen wir mit einem berüchtigt schwierigen Thema: der Differentialrechnung.

Übersicht

👉 Gehen Sie zuerst zum Cloud Code-Editor. Ersetzen Sie im Ordner answer.py im Ordner portal

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

mit dem folgenden Code-Snippet:

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)

Dies ist eine sehr einfache Langchain-App, in der das Gemini 2-Flash-Modell initialisiert wird. Wir weisen es an, als hilfreicher Lehrer zu agieren und Erklärungen zu liefern.

👉 Führen Sie im Terminal den folgenden Befehl aus:

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

Die Ausgabe sollte in etwa wie im Beispiel in der ursprünglichen Anleitung aussehen. Das aktuelle Modell bietet möglicherweise keine so ausführliche Erklärung.

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!

Ersetzen Sie in der Datei „answer.py“ in der Funktion „answer_thinking“ den Modellnamen von gemini-2.0-flash-001 durch gemini-2.0-flash-thinking-exp-01-21.

Dadurch wird das LLM verändert, sodass es mehr begründet, was wiederum zu besseren Erklärungen führt. und führen Sie ihn noch einmal aus.

👉 Führen Sie das neue Denkmodell aus, um es zu testen:

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

Hier ist ein Beispiel für die Antwort des Denkmodells, die viel ausführlicher und detaillierter ist und eine Schritt-für-Schritt-Erläuterung zur Lösung des Kalkulationsproblems enthält. Dies unterstreicht die Leistungsfähigkeit von „denkenden“ Modellen bei der Generierung hochwertiger Erklärungen. Die Ausgabe sollte in etwa so aussehen:

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.

ENTFERNEN Sie den folgenden Testcode aus 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)

👉 Führen Sie die folgenden Befehle im Terminal aus, um eine virtuelle Umgebung einzurichten, Abhängigkeiten zu installieren und den Agenten zu starten:

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

👉Verwenden Sie die Webvorschaufunktion von Cloud Shell, um auf die laufende Anwendung zuzugreifen. Klicken Sie auf den Link „Quizzes“, beantworten Sie alle Quizze, achten Sie darauf, mindestens eine falsche Antwort zu geben, und klicken Sie auf „Senden“.

Antworten überlegen

Anstatt während der Wartezeit auf die Antwort zu starren, können Sie zum Terminal des Cloud-Editors wechseln. Sie können den Fortschritt und alle von Ihrer Funktion generierten Ausgaben oder Fehlermeldungen im Terminal des Emulators beobachten. 😁

Drücken Sie im Terminal die Taste Ctrl+C, um den lokal laufenden Prozess zu beenden.

11. Agents mit Eventarc orchestrieren

Bisher wurden im Studentenportal Quizze basierend auf einer Standardreihe von Lehrplänen generiert. Das ist hilfreich, bedeutet aber, dass der Planner-Agent und der Quiz-Agent des Portals nicht wirklich miteinander kommunizieren. Erinnern Sie sich an die Funktion, mit der der Planner-Agent seine neu generierten Lehrpläne in einem Pub/Sub-Thema veröffentlicht? Jetzt ist es an der Zeit, eine Verbindung zu unserem Portal-Agenten herzustellen.

Übersicht

Wir möchten, dass die Quizinhalte im Portal automatisch aktualisiert werden, wenn ein neuer Lehrplan erstellt wird. Dazu erstellen wir im Portal einen Endpunkt, der diese neuen Pläne empfangen kann.

👉 Gehen Sie im Explorer-Bereich des Cloud Code-Editors zum Ordner portal. Öffnen Sie die Datei app.py zur Bearbeitung. Fügen Sie den folgenden Code zwischen ## Add your code here ein:

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

Neu erstellen und in Cloud Run bereitstellen

Alles klar. Sie müssen sowohl die Planner- als auch die Portal-Agenten in Cloud Run aktualisieren und neu bereitstellen. So wird sichergestellt, dass sie den neuesten Code haben und für die Kommunikation über Ereignisse konfiguriert sind.

Bereitstellung – Übersicht

👉 Als Nächstes erstellen wir das Agent-Image für planner neu und übertragen es per Push zurück in das Terminal:

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

👉Wir machen das Gleiche und erstellen und übertragen das Portal-Agent-Image:

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

In Artifact Registry sollten sowohl die aidemy-planner- als auch die aidemy-portal-Container-Images aufgeführt sein.

Container-Repository

👉 Führen Sie im Terminal folgenden Befehl aus, um das Cloud Run-Image für den Planner-Agenten zu aktualisieren:

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

Die Ausgabe sollte in etwa so aussehen:

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

Notieren Sie sich die Dienst-URL. Das ist der Link zu Ihrem bereitgestellten Planner-Agent.

👉 Führe diesen Befehl aus, um die Cloud Run-Instanz für den Portal-Agenten zu erstellen.

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}

Die Ausgabe sollte in etwa so aussehen:

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

Notieren Sie sich die Dienst-URL. Das ist der Link zu Ihrem bereitgestellten Studentenportal.

Eventarc-Trigger erstellen

Aber hier ist die große Frage: Wie wird dieser Endpunkt benachrichtigt, wenn im Pub/Sub-Thema ein neuer Plan wartet? Hier kommt Eventarc ins Spiel.

Eventarc dient als Brücke, die auf bestimmte Ereignisse achtet (z. B. eine neue Nachricht, die in unserem Pub/Sub-Thema eingeht) und automatisch Aktionen als Reaktion darauf auslöst. In unserem Fall erkennt er, wenn ein neuer Lehrplan veröffentlicht wird, und sendet dann ein Signal an den Endpunkt unseres Portals, um ihn darüber zu informieren, dass es an der Zeit ist, ihn zu aktualisieren.

Da Eventarc die ereignisgesteuerte Kommunikation übernimmt, können wir unseren Planner-Agenten und Portal-Agenten nahtlos verbinden und so ein wirklich dynamisches und responsives Lernsystem schaffen. Es ist, als hätten Sie einen intelligenten Messenger, der die neuesten Lehrpläne automatisch an die richtige Stelle liefert.

👉 Rufen Sie in der Console Eventarc auf.

👉 Klicken Sie auf die Schaltfläche „+ TRIGGER ERSTELLEN“.

Trigger konfigurieren (Grundlagen):

  • Trigger name: plan-topic-trigger
  • Triggertypen: Google-Quellen
  • Ereignisanbieter: Cloud Pub/Sub
  • Ereignistyp: google.cloud.pubsub.topic.v1.messagePublished
  • Region: us-central1.
  • Cloud Pub/Sub-Thema : plan auswählen
  • DEM DIENSTKONTO die Rolle roles/iam.serviceAccountTokenCreator VERMESSEN
  • Ereignisziel: Cloud Run
  • Cloud Run-Dienst: aidemy-portal
  • Dienst-URL-Pfad: /new_teaching_plan
  • Meldung ignorieren: Die Berechtigung für „locations/me-central2“ wurde verweigert (oder die Ressource ist möglicherweise nicht vorhanden).

Klicken Sie auf "Erstellen".

Die Seite „Eventarc-Trigger“ wird aktualisiert und der neu erstellte Trigger sollte jetzt in der Tabelle aufgeführt werden.

👉 Rufen Sie jetzt den Planer auf und beantragen Sie einen neuen Lehrplan. Versuchen Sie es dieses Mal mit dem Jahr 5, dem Betreff science und der Anfrage „Add-no“ atoms.

Führen Sie diesen Befehl im Terminal aus, wenn Sie den Speicherort Ihres Planner-Agenten vergessen haben.

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

Warten Sie dann eine oder zwei Minuten. Diese Verzögerung ist auf die Abrechnungsbeschränkungen dieses Labs zurückzuführen. Unter normalen Bedingungen sollte es keine Verzögerung geben.

Rufen Sie abschließend das Portal für Schüler und Studenten auf. Die Quizze wurden aktualisiert und entsprechen jetzt dem neuen Lehrplan, den Sie gerade erstellt haben. Dies zeigt die erfolgreiche Integration von Eventarc in das Aidemy-System.

Führen Sie diesen Befehl im Terminal aus, wenn Sie den Speicherort Ihres Portal-Agenten vergessen haben.

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

Aidemy-celebrate

Glückwunsch! Sie haben erfolgreich ein Multi-Agent-System in Google Cloud erstellt und dabei eine ereignisgesteuerte Architektur für mehr Skalierbarkeit und Flexibilität genutzt. Sie haben ein solides Fundament gelegt, aber es gibt noch mehr zu entdecken. Wenn Sie mehr über die Vorteile dieser Architektur erfahren, die Multimodal Live API von Gemini 2 kennenlernen und erfahren möchten, wie Sie die Single-Path-Orchestrierung mit LangGraph implementieren, lesen Sie die nächsten beiden Kapitel.

12. OPTIONAL: Audio-Recaps mit Gemini

Gemini kann Informationen aus verschiedenen Quellen wie Text, Bildern und sogar Audio verstehen und verarbeiten. Das eröffnet ganz neue Möglichkeiten für Lernen und Content-Erstellung. Die Fähigkeit von Gemini, „zu sehen“, „zu hören“ und „zu lesen“, eröffnet wirklich kreative und ansprechende Nutzererfahrungen.

Neben der Erstellung von visuellen Elementen oder Texten ist auch eine effektive Zusammenfassung und Aufzählung der wichtigsten Punkte ein wichtiger Schritt beim Lernen. Überlegen Sie: Wie oft erinnern Sie sich leichter an einen eingängigen Songtext als an etwas, das Sie in einem Lehrbuch gelesen haben? Der Ton kann unglaublich einprägsam sein. Deshalb nutzen wir die multimodalen Funktionen von Gemini, um Audiozusammenfassungen unserer Lehrpläne zu erstellen. So haben Schüler und Studenten eine praktische und ansprechende Möglichkeit, sich das Material noch einmal anzusehen. Durch das auditive Lernen lässt sich die Merkfähigkeit und das Verständnis möglicherweise verbessern.

Live API – Übersicht

Wir benötigen einen Speicherort für die generierten Audiodateien. Cloud Storage bietet eine skalierbare und zuverlässige Lösung.

👉 Gehe in der Console zu Speicher. Klicken Sie im Menü links auf „Buckets“. Klicken Sie oben auf die Schaltfläche „+ ERSTELLEN“.

👉 Bucket konfigurieren:

  • bucket name: aidemy-recap-<UNIQUE_NAME> WICHTIG: Sie müssen einen eindeutigen Bucket-Namen definieren, der mit aidemy-recap- beginnt. Dieser eindeutige Name ist entscheidend, um Namenskonflikte beim Erstellen des Cloud Storage-Buckets zu vermeiden.
  • region: us-central1.
  • Speicherklasse: „Standard“ „Standard“ eignet sich für Daten, auf die häufig zugegriffen wird.
  • Zugriffssteuerung: Lassen Sie die Standardeinstellung „Einheitliche Zugriffssteuerung“ ausgewählt. Dies ermöglicht eine einheitliche Zugriffssteuerung auf Bucket-Ebene.
  • Erweiterte Optionen: Für diesen Workshop sind in der Regel die Standardeinstellungen ausreichend. Klicken Sie auf die Schaltfläche ERSTELLEN, um den Bucket zu erstellen.

Möglicherweise wird ein Pop-up zur Verhinderung des öffentlichen Zugriffs angezeigt. Lassen Sie das Kästchen angeklickt und klicken Sie auf Confirm.

Der neu erstellte Bucket wird jetzt in der Liste „Buckets“ angezeigt. Merken Sie sich den Bucket-Namen, da Sie ihn später benötigen.

👉 Führen Sie im Terminal des Cloud Code-Editors die folgenden Befehle aus, um dem Dienstkonto Zugriff auf den Bucket zu gewähren:

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"

👉Öffnen Sie im Cloud Code-Editor audio.py im Ordner course. Fügen Sie den folgenden Code am Ende der Datei ein:

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))
  • Streamingverbindung: Zuerst wird eine dauerhafte Verbindung mit dem Live API-Endpunkt hergestellt. Im Gegensatz zu einem Standard-API-Aufruf, bei dem Sie eine Anfrage senden und eine Antwort erhalten, bleibt diese Verbindung für einen kontinuierlichen Datenaustausch offen.
  • Multimodale Konfiguration: Mit der Konfiguration können Sie angeben, welche Art von Ausgabe Sie wünschen (in diesem Fall Audio). Sie können sogar angeben, welche Parameter Sie verwenden möchten (z. B. Sprachauswahl, Audiocodierung).
  • Asynchrone Verarbeitung: Diese API arbeitet asynchron. Das bedeutet, dass der Hauptthread nicht blockiert wird, während auf die Fertigstellung der Audiogenerierung gewartet wird. Durch die Echtzeitverarbeitung der Daten und das Senden der Ausgabe in Chunks ist die Leistung nahezu sofort.

Die entscheidende Frage ist nun: Wann sollte dieser Audiogenerierungsvorgang ausgeführt werden? Idealerweise sollten die Audiozusammenfassungen verfügbar sein, sobald ein neuer Lehrplan erstellt wurde. Da wir bereits eine ereignisgesteuerte Architektur implementiert haben, indem wir den Lehrplan in einem Pub/Sub-Thema veröffentlicht haben, können wir dieses Thema einfach abonnieren.

Wir erstellen jedoch nicht sehr oft neue Lehrpläne. Es wäre nicht effizient, einen Agenten ständig laufen zu lassen und auf neue Pläne zu warten. Daher ist es sinnvoll, diese Logik zur Audiogenerierung als Cloud Run-Funktion bereitzustellen.

Wenn Sie sie als Funktion bereitstellen, bleibt sie inaktiv, bis eine neue Nachricht im Pub/Sub-Thema veröffentlicht wird. In diesem Fall wird die Funktion automatisch ausgelöst, wodurch die Audiozusammenfassungen generiert und in unserem Bucket gespeichert werden.

👉 In der Datei main.py im Ordner course wird die Cloud Run-Funktion definiert, die ausgelöst wird, wenn ein neuer Lehrplan verfügbar ist. Er empfängt den Plan und initiiert die Erstellung des Audio-Zusammenfassung. Fügen Sie am Ende der Datei das folgende Code-Snippet ein.

@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: Dieser Decorator kennzeichnet die Funktion als Cloud Run-Funktion, die durch CloudEvents ausgelöst wird.

Anwendung lokal testen

👉Wir führen das in einer virtuellen Umgebung aus und installieren die erforderlichen Python-Bibliotheken für die Cloud Run-Funktion.

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

👉 Mit dem Cloud Run Function Emulator können wir unsere Funktion lokal testen, bevor wir sie in Google Cloud bereitstellen. Starten Sie einen lokalen Emulator mit folgendem Befehl:

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

👉 Während der Emulator läuft, können Sie Test-CloudEvents an den Emulator senden, um die Veröffentlichung eines neuen Lehrplans zu simulieren. In einem neuen Terminal:

Zweipolig

👉 Ausführen:

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

Anstatt während des Wartens auf die Antwort nur zu starren, wechseln Sie zum anderen Cloud Shell-Terminal. Sie können den Fortschritt und alle von Ihrer Funktion generierten Ausgaben oder Fehlermeldungen im Terminal des Emulators beobachten. 😁

Im zweiten Terminal sollte OK zurückgegeben werden.

👉 Sie prüfen die Daten im Bucket. Rufen Sie Cloud Storage auf, wählen Sie den Tab „Bucket“ und dann das aidemy-recap-xxx aus.

Bucket

👉 Geben Sie im Terminal, in dem der Emulator ausgeführt wird, ctrl+c ein, um ihn zu beenden. Schließen Sie das zweite Terminal. Schließen Sie das zweite Terminal und führen Sie „deactivate“ aus, um die virtuelle Umgebung zu beenden.

deactivate

In Google Cloud bereitstellen

Bereitstellung – Übersicht 👉 Nach dem lokalen Testen ist es an der Zeit, den Kursagenten in Google Cloud bereitzustellen. Führen Sie im Terminal die folgenden Befehle aus:

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

Prüfen Sie die Bereitstellung, indem Sie in der Google Cloud Console Cloud Run aufrufen.Es sollte ein neuer Dienst mit dem Namen „courses-agent“ aufgeführt sein.

Cloud Run-Liste

Wenn Sie die Triggerkonfiguration prüfen möchten, klicken Sie auf den Dienst „courses-agent“, um die Details aufzurufen. Rufen Sie den Tab „TRIGGER“ auf.

Es sollte ein Trigger konfiguriert sein, der auf Nachrichten achtet, die im Planthema veröffentlicht werden.

Cloud Run-Trigger

Sehen wir uns nun an, wie der Ablauf funktioniert.

👉Als Nächstes müssen wir den Portal-Agenten so konfigurieren, dass er weiß, wo er die generierten Audiodateien findet. Führen Sie im Terminal folgenden Befehl aus:

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

👉 Versuche, einen neuen Lehrplan auf der Seite des Planner-Agenten zu erstellen. Es kann einige Minuten dauern, bis der Dienst gestartet wird. Das ist normal, da es sich um einen serverlosen Dienst handelt. Rufen Sie die URL Ihres Planner-Agenten ab. Wenn Sie sie nicht zur Hand haben, führen Sie diesen Befehl im Terminal aus:

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

Warten Sie nach dem Erstellen des neuen Plans 2 bis 3 Minuten, bis die Audiodatei generiert wird. Aufgrund der Abrechnungsbeschränkungen dieses Lab-Kontos kann dies einige Minuten dauern.

Auf dem Tab „TRIGGER“ der Funktion kannst du prüfen, ob der Lehrplan von der courses-agent-Funktion empfangen wurde. Aktualisieren Sie die Seite regelmäßig. Nach einiger Zeit sollte die Funktion aufgerufen werden. Wenn die Funktion nach mehr als zwei Minuten noch nicht aufgerufen wurde, können Sie versuchen, den Lehrplan noch einmal zu generieren. Generieren Sie jedoch nicht wiederholt Pläne in schneller Folge, da jeder generierte Plan nacheinander vom Kundenservicemitarbeiter verwendet und verarbeitet wird, was zu einem Rückstau führen kann.

Trigger „Beobachten“

👉 Rufen Sie das Portal auf und klicken Sie auf „Kurse“. Sie sollten drei Karten mit jeweils einer Audiozusammenfassung sehen. So finden Sie die URL Ihres Portal-Agenten:

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

Klicke bei jedem Kurs auf „Wiedergabe“, um zu prüfen, ob die Audiozusammenfassungen mit dem gerade erstellten Lehrplan übereinstimmen. Portalkurse

Beenden Sie die virtuelle Umgebung.

deactivate

13. OPTIONAL: Rollenbasierte Zusammenarbeit mit Gemini und DeepSeek

Mehrere Perspektiven sind von unschätzbarem Wert, insbesondere wenn es darum geht, ansprechende und durchdachte Aufgaben zu erstellen. Wir erstellen jetzt ein Multi-Agenten-System, das zwei verschiedene Modelle mit unterschiedlichen Rollen nutzt, um Aufgaben zu generieren: eines fördert die Zusammenarbeit und das andere den Selbstlernprozess. Wir verwenden eine „Single-Shot“-Architektur, bei der der Workflow einem festen Pfad folgt.

Gemini-Aufgabe erstellen

Gemini – Übersicht Zuerst richten wir die Gemini-Funktion ein, um Aufgaben mit einem Schwerpunkt auf Zusammenarbeit zu erstellen. Bearbeiten Sie die Datei gemini.py im Ordner assignment.

👉 Fügen Sie den folgenden Code am Ende der gemini.py-Datei ein:

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

Dabei wird das Gemini-Modell verwendet, um Aufgaben zu generieren.

Wir sind bereit, den Gemini-Agenten zu testen.

👉 Führen Sie die folgenden Befehle im Terminal aus, um die Umgebung einzurichten:

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

👉 So kannst du es testen:

python gemini.py

In der Ausgabe sollte eine Aufgabe mit mehr Gruppenarbeit angezeigt werden. Die Ergebnisse werden auch vom Assert-Test am Ende ausgegeben.

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

Beenden Sie mit ctl+c und bereinigen Sie den Testcode. ENTFERNEN Sie den folgenden Code aus gemini.py.

import unittest

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

        updated_state = gen_assignment_gemini(initial_state)

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


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

DeepSeek-Aufgabengenerator konfigurieren

Cloudbasierte KI-Plattformen sind zwar praktisch, aber selbst gehostete LLMs können für den Datenschutz und die Datensouveränität entscheidend sein. Wir stellen das kleinste DeepSeek-Modell (1,5 Milliarden Parameter) auf einer Cloud Compute Engine-Instanz bereit. Es gibt auch andere Möglichkeiten, z. B. die Bereitstellung auf der Vertex AI-Plattform von Google oder auf Ihrer GKE-Instanz. Da dies jedoch nur ein Workshop zu KI-Agenten ist und ich Sie nicht ewig hier aufhalten möchte, verwenden wir einfach die einfachste Methode. Wenn Sie sich aber für andere Optionen interessieren, sehen Sie sich die Datei deepseek-vertexai.py im Ordner „Aufgabe“ an. Dort finden Sie Beispielcode für die Interaktion mit Modellen, die in Vertex AI bereitgestellt werden.

Deepseek

👉 Führen Sie diesen Befehl im Terminal aus, um eine selbst gehostete LLM-Plattform Ollama zu erstellen:

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

So prüfen Sie, ob die Compute Engine-Instanz ausgeführt wird:

Gehen Sie in der Google Cloud Console zu Compute Engine > „VM-Instanzen“. Die ollama-instance sollte mit einem grünen Häkchen aufgeführt sein, was bedeutet, dass sie ausgeführt wird. Wenn Sie sie nicht sehen, prüfen Sie, ob die Zone „us-central1“ lautet. Andernfalls müssen Sie möglicherweise danach suchen.

Compute Engine-Liste

👉 Wir installieren das kleinste DeepSeek-Modell und testen es. Öffnen Sie dazu im Cloud Shell-Editor ein neues Terminal und führen Sie den folgenden Befehl aus, um per SSH auf die GCE-Instanz zuzugreifen.

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

Nach dem Herstellen der SSH-Verbindung werden Sie möglicherweise aufgefordert, Folgendes zu tun:

„Möchten Sie fortfahren (J/N)?“

Geben Sie einfach Y ein(Groß- und Kleinschreibung wird nicht berücksichtigt) und drücken Sie die Eingabetaste, um fortzufahren.

Als Nächstes werden Sie möglicherweise aufgefordert, eine Passphrase für den SSH-Schlüssel zu erstellen. Wenn Sie keine Passphrase verwenden möchten, drücken Sie einfach zweimal die Eingabetaste, um den Standardwert (keine Passphrase) zu akzeptieren.

👉Sie befinden sich jetzt auf der virtuellen Maschine, laden das kleinste DeepSeek R1-Modell herunter und testen, ob es funktioniert.

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

👉 Beenden Sie die GCE-Instanz und geben Sie Folgendes in das SSH-Terminal ein:

exit

Schließen Sie das neue Terminal und kehren Sie zum ursprünglichen Terminal zurück.

👉 Denken Sie daran, die Netzwerkrichtlinie so einzurichten, dass andere Dienste auf den LLM zugreifen können. Begrenzen Sie den Zugriff auf die Instanz, wenn Sie dies für die Produktion tun möchten. Implementieren Sie entweder eine Sicherheitsanmeldung für den Dienst oder beschränken Sie den IP-Zugriff. Ausführen:

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

👉 So prüfen Sie, ob Ihre Firewallrichtlinie richtig funktioniert:

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

Als Nächstes arbeiten wir an der Deepseek-Funktion im Aufgabenagenten, um Aufgaben mit individuellem Arbeitsschwerpunkt zu generieren.

👉 deepseek.py im Ordner assignment bearbeiten und am Ende das folgende Snippet einfügen

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

👉 Testen wir das mit dem Befehl:

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

Sie sollten eine Aufgabe sehen, die mehr Selbststudium erfordert.

**Assignment Plan for Each Week**

---

### **Week 1: 2D Shapes and Angles**
- **Week Title:** "Exploring 2D Shapes"
Assign students to research and present on various 2D shapes. Include a project where they create models using straws and tape for triangles, draw quadrilaterals with specific measurements, and compare their properties. 

### **Week 2: 3D Shapes and Symmetry**
Assign students to create models or nets for cubes and cuboids. They will also predict how folding these nets form the 3D shapes. Include a project where they identify symmetrical properties using mirrors or folding techniques.

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

Assign students to use mirrors or folding techniques for reflections. Include activities where they measure angles, use a protractor, solve problems involving perimeter/area, and create symmetrical designs.
....

👉 ctl+c beenden und den Testcode bereinigen. ENTFERNEN Sie den folgenden Code aus 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()

Jetzt verwenden wir dasselbe Gemini-Modell, um beide Aufgaben in einer neuen zusammenzuführen. Bearbeiten Sie die Datei gemini.py im Ordner assignment.

👉 Fügen Sie den folgenden Code am Ende der gemini.py-Datei ein:

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

Um die Stärken beider Modelle zu kombinieren, steuern wir einen definierten Workflow mit LangGraph. Dieser Workflow besteht aus drei Schritten: Erstens generiert das Gemini-Modell eine Aufgabe, die auf Zusammenarbeit ausgerichtet ist. Zweitens generiert das DeepSeek-Modell eine Aufgabe, die die individuelle Arbeit betont. Schließlich synthetisiert Gemini diese beiden Aufgaben zu einer einzigen umfassenden Aufgabe. Da wir die Abfolge der Schritte ohne LLM-Entscheidungen vordefinieren, handelt es sich dabei um eine benutzerdefinierte, einstufige Orchestrierung.

Langraph-Kombination – Übersicht

👉Fügen Sie den folgenden Code am Ende der main.py-Datei im Ordner assignment ein:

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

👉 Um die Funktion create_assignment zu testen und zu prüfen, ob der Workflow, der Gemini und DeepSeek kombiniert, funktioniert, führen Sie den folgenden Befehl aus:

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

Sie sollten etwas sehen, das beide Modelle mit ihrer individuellen Perspektive für das Lernen von Schülern und Studenten sowie für die Arbeit in Schüler- und Studentengruppen kombiniert.

**Tasks:**

1. **Clue Collection:** Gather all the clues left by the thieves. These clues will include:
    * Descriptions of shapes and their properties (angles, sides, etc.)
    * Coordinate grids with hidden messages
    * Geometric puzzles requiring transformation (translation, reflection, rotation)
    * Challenges involving area, perimeter, and angle calculations

2. **Clue Analysis:** Decipher each clue using your geometric knowledge. This will involve:
    * Identifying the shape and its properties
    * Plotting coordinates and interpreting patterns on the grid
    * Solving geometric puzzles by applying transformations
    * Calculating area, perimeter, and missing angles 

3. **Case Report:** Create a comprehensive case report outlining your findings. This report should include:
    * A detailed explanation of each clue and its solution
    * Sketches and diagrams to support your explanations
    * A step-by-step account of how you followed the clues to locate the artifact
    * A final conclusion about the thieves and their motives

👉 ctl+c beenden und den Testcode bereinigen. ENTFERNEN Sie den folgenden Code aus 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

Damit die Zuweisung automatisch generiert und auf neue Lehrpläne reagiert werden kann, nutzen wir die vorhandene ereignisgesteuerte Architektur. Der folgende Code definiert eine Cloud Run-Funktion (generate_assignment), die ausgelöst wird, wenn ein neuer Lehrplan im Pub/Sub-Thema plan veröffentlicht wird.

👉 Fügen Sie am Ende von main.py den folgenden Code ein:

@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

Anwendung lokal testen

Es empfiehlt sich, die Cloud Run-Funktion vor der Bereitstellung in Google Cloud lokal zu testen. Dies ermöglicht eine schnellere Iteration und eine einfachere Fehlerbehebung.

Erstellen Sie zuerst einen Cloud Storage-Bucket zum Speichern der generierten Aufgabendateien und gewähren Sie dem Dienstkonto Zugriff auf den Bucket. Führen Sie im Terminal die folgenden Befehle aus:

👉WICHTIG: Sie müssen einen eindeutigen Namen für ASSIGNMENT_BUCKET definieren, der mit aidemy-assignment- beginnt. Dieser eindeutige Name ist entscheidend, um Namenskonflikte beim Erstellen des Cloud Storage-Buckets zu vermeiden. (Ersetzen Sie <YOUR_NAME> durch ein beliebiges Wort.)

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

👉 Führen Sie dann diesen Befehl aus:

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"

👉Starten Sie jetzt den Cloud Run-Funktionsemulator:

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

👉Öffnen Sie in der Cloud Shell ein zweites Terminal, während der Emulator in einem anderen Terminal ausgeführt wird. Senden Sie in diesem zweiten Terminal ein Test-CloudEvent an den Emulator, um die Veröffentlichung eines neuen Lehrplans zu simulieren:

Zweipolig

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

Anstatt während des Wartens auf die Antwort nur zu starren, wechseln Sie zum anderen Cloud Shell-Terminal. Sie können den Fortschritt und alle von Ihrer Funktion generierten Ausgaben oder Fehlermeldungen im Terminal des Emulators beobachten. 😁

Es sollte „OK“ zurückgegeben werden.

Ob die Aufgabe erfolgreich generiert und gespeichert wurde, können Sie in der Google Cloud Console unter Speicher > „Cloud Storage“ prüfen. Wählen Sie den von Ihnen erstellten aidemy-assignment-Bucket aus. Im Bucket sollte eine Textdatei mit dem Namen assignment-{random number}.txt angezeigt werden. Klicken Sie auf die Datei, um sie herunterzuladen und den Inhalt zu prüfen. So wird überprüft, ob eine neue Datei eine gerade generierte neue Aufgabe enthält.

12-01-assignment-bucket

👉 Geben Sie im Terminal, in dem der Emulator ausgeführt wird, ctrl+c ein, um ihn zu beenden. Schließen Sie das zweite Terminal. 👉 Beenden Sie außerdem im Terminal, in dem der Emulator ausgeführt wird, die virtuelle Umgebung.

deactivate

Bereitstellung – Übersicht

👉Als Nächstes stellen wir den Aufgaben-Agenten in der Cloud bereit.

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 

Prüfen Sie die Bereitstellung in der Google Cloud Console unter „Cloud Run“. Dort sollte ein neuer Dienst mit dem Namen „courses-agent“ aufgeführt sein. 12-03-function-list

Nachdem der Workflow zur Aufgabenerstellung jetzt implementiert, getestet und bereitgestellt wurde, können wir mit dem nächsten Schritt fortfahren: diesen Aufgaben im Studentenportal zugänglich machen.

14. OPTIONAL: Rollenbasierte Zusammenarbeit mit Gemini und DeepSeek – Fortsetzung

Dynamische Website-Generierung

Um das Schüler-/Studentenportal zu verbessern und ansprechender zu gestalten, werden wir die dynamische HTML-Generierung für Seiten mit Aufgaben implementieren. Ziel ist es, das Portal automatisch mit einem neuen, visuell ansprechenden Design zu aktualisieren, sobald eine neue Aufgabe generiert wird. So werden die Codierungsfunktionen des LLM genutzt, um eine dynamischere und interessantere Nutzererfahrung zu schaffen.

14-01-generate-html

👉 Bearbeiten Sie im Cloud Shell-Editor die Datei render.py im Ordner portal und ersetzen Sie

def render_assignment_page():
    return ""

mit dem folgenden Code-Snippet:

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

Dabei wird das Gemini-Modell verwendet, um dynamisch HTML für die Aufgabe zu generieren. Dabei werden die Inhalt der Aufgabe als Eingabe verwendet und Gemini wird mit einem Prompt angewiesen, eine visuell ansprechende HTML-Seite mit einem kreativen Stil zu erstellen.

Als Nächstes erstellen wir einen Endpunkt, der ausgelöst wird, wenn dem Aufgaben-Bucket ein neues Dokument hinzugefügt wird:

👉 Bearbeiten Sie im Ordner „portal“ die Datei app.py und fügen Sie den folgenden Code in ## Add your code here" comments NACH der Funktion new_teaching_plan ein:

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

Wenn sie ausgelöst wird, ruft sie den Dateinamen und den Bucketnamen aus den Anfragedaten ab, lädt die Inhalt der Aufgabe aus Cloud Storage herunter und ruft die render_assignment_page-Funktion auf, um die HTML-Datei zu generieren.

👉 Wir führen das Programm jetzt lokal aus:

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

👉 Wählen Sie im Menü „Webvorschau“ oben im Cloud Shell-Fenster „Vorschau auf Port 8080“ aus. Daraufhin wird die Anwendung in einem neuen Browsertab geöffnet. Klicken Sie in der Navigationsleiste auf den Link Aufgabe. Es sollte jetzt eine leere Seite angezeigt werden. Das ist normal, da wir noch keine Kommunikationsbrücke zwischen dem Aufgaben-Agenten und dem Portal hergestellt haben, um die Inhalte dynamisch einzufügen.

14-02-deployment-overview

👉 Um diese Änderungen zu übernehmen und den aktualisierten Code bereitzustellen, erstellen Sie das Portal-Agent-Image neu und pushen Sie es:

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

👉 Stellen Sie den Cloud Run-Dienst nach dem Pushen des neuen Images noch einmal bereit. Führen Sie das folgende Script aus, um das Cloud Run-Update durchzusetzen:

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

👉 Jetzt richten wir einen Eventarc-Trigger ein, der auf neue Objekte wartet, die im Bucket für Aufgaben erstellt (finalisiert) werden. Dieser Trigger ruft automatisch den Endpunkt „/render_assignment“ auf dem Portaldienst auf, wenn eine neue Aufgabendatei erstellt wird.

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"

Rufen Sie in der Google Cloud Console die Seite Eventarc-Trigger auf, um zu prüfen, ob der Trigger erfolgreich erstellt wurde. portal-assignment-trigger sollte in der Tabelle aufgeführt sein. Klicken Sie auf den Namen des Triggers, um die Details aufzurufen. Trigger für Aufgaben

Es kann bis zu 2 bis 3 Minuten dauern, bis der neue Trigger aktiv ist.

Wenn Sie die dynamische Zuweisung in Aktion sehen möchten, führen Sie den folgenden Befehl aus, um die URL Ihres Planner-Agents zu ermitteln (falls Sie sie nicht zur Hand haben):

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

So finden Sie die URL Ihres Portal-Agenten:

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

Erstellen Sie im Planner-Agent einen neuen Lehrplan.

13-02-assignment

Warten Sie einige Minuten, damit die Audiogenerierung, die Aufgabenerstellung und das HTML-Rendering abgeschlossen werden können. Rufen Sie dann das Studentenportal auf.

👉 Klicken Sie in der Navigationsleiste auf den Link „Aufgabe“. Sie sollten eine neu erstellte Aufgabe mit dynamisch generiertem HTML sehen. Jedes Mal, wenn ein Lehrplan generiert wird, sollte es sich um eine dynamische Aufgabe handeln.

13-02-assignment

Herzlichen Glückwunsch zum Abschluss des Aidemy-Multiagentensystems. Sie haben praktische Erfahrungen und wertvolle Einblicke in folgende Themen gesammelt:

  • Die Vorteile von Multi-Agenten-Systemen, einschließlich Modularität, Skalierbarkeit, Spezialisierung und einfacher Wartung.
  • Die Bedeutung ereignisgesteuerter Architekturen für die Entwicklung responsiver und lose gekoppelter Anwendungen.
  • Die strategische Verwendung von LLMs, die Auswahl des richtigen Modells für die Aufgabe und die Einbindung in Tools für eine praktische Anwendung.
  • Cloud-native Entwicklungspraktiken mit Google Cloud-Diensten zur Erstellung skalierbarer und zuverlässiger Lösungen.
  • Die Bedeutung des Datenschutzes und von Self-Hosting-Modellen als Alternative zu Anbieterlösungen.

Sie haben jetzt eine solide Grundlage für die Entwicklung ausgefeilter KI-gestützter Anwendungen in Google Cloud.

15. Herausforderungen und nächste Schritte

Herzlichen Glückwunsch zum Erstellen des Aidemy-Multi-Agenten-Systems! Sie haben eine solide Grundlage für KI-gestützte Bildung geschaffen. Sehen wir uns nun einige Herausforderungen und mögliche zukünftige Verbesserungen an, um die Funktionen weiter zu erweitern und reale Anforderungen zu erfüllen:

Interaktives Lernen mit Live-Fragerunden:

  • Herausforderung: Können Sie die Live API von Gemini 2 nutzen, um eine Echtzeit-Frage-und-Antwort-Funktion für Schüler und Studenten zu erstellen? Stellen Sie sich einen virtuellen Klassenraum vor, in dem Schüler und Studenten Fragen stellen und sofortige KI-gestützte Antworten erhalten können.

Automatische Abgabe und Benotung von Aufgaben:

  • Herausforderung: Entwerfen und implementieren Sie ein System, mit dem Schüler/Studenten Aufgaben digital einreichen und automatisch von KI benoten lassen können, mit einem Mechanismus zum Erkennen und Verhindern von Plagiarismus. Diese Herausforderung bietet eine gute Gelegenheit, Retrieval Augmented Generation (RAG) zu untersuchen, um die Genauigkeit und Zuverlässigkeit der Benotung und Plagiatserkennung zu verbessern.

aidemy-climb

16. Bereinigen

Nachdem wir unser Aidemy-Multiagentensystem erstellt und untersucht haben, ist es an der Zeit, unsere Google Cloud-Umgebung aufzuräumen.

  1. Cloud Run-Dienste löschen
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. Eventarc-Trigger löschen
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. Pub/Sub-Thema löschen
gcloud pubsub topics delete plan --project="$PROJECT_ID" --quiet
  1. Cloud SQL-Instanz löschen
gcloud sql instances delete aidemy --quiet
  1. Artifact Registry-Repository löschen
gcloud artifacts repositories delete agent-repository --location=us-central1 --quiet
  1. Secret Manager-Secrets löschen
gcloud secrets delete db-user --quiet
gcloud secrets delete db-pass --quiet
gcloud secrets delete db-name --quiet
  1. Compute Engine-Instanz löschen (falls für Deepseek erstellt)
gcloud compute instances delete ollama-instance --zone=us-central1-a --quiet
  1. Firewallregel für Deepseek-Instanz löschen
gcloud compute firewall-rules delete allow-ollama-11434 --quiet
  1. Cloud Storage-Buckets löschen
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