Aidemy: Xây dựng hệ thống đa tác nhân bằng LangGraph, EDA và AI tạo sinh trên Google Cloud

1. Giới thiệu

Chào bạn! Vậy bạn đã biết đến các tác nhân – những trợ thủ nhỏ có thể giúp bạn làm việc mà bạn không cần phải làm gì cả, phải không? Tuyệt vời! Tuy nhiên, thực tế là một nhân viên hỗ trợ không phải lúc nào cũng có thể giải quyết được vấn đề, đặc biệt là khi bạn đang xử lý các dự án lớn hơn và phức tạp hơn. Có thể bạn sẽ cần cả một đội ngũ! Đó là lúc hệ thống đa tác nhân xuất hiện.

Khi được cung cấp bởi LLM, các tác nhân sẽ mang lại cho bạn sự linh hoạt đáng kinh ngạc so với phương thức lập trình cứng kiểu cũ. Tuy nhiên, chúng cũng có những thách thức riêng. Và đó chính là nội dung chúng ta sẽ tìm hiểu trong hội thảo này!

tiêu đề

Dưới đây là những kiến thức bạn có thể học được – hãy coi đây là cách nâng cấp trò chơi của bạn:

Tạo tác nhân đầu tiên bằng LangGraph: Chúng ta sẽ bắt tay vào việc tạo tác nhân của riêng mình bằng LangGraph, một khung phổ biến. Bạn sẽ tìm hiểu cách tạo các công cụ kết nối với cơ sở dữ liệu, khai thác API Gemini 2 mới nhất để tìm kiếm một số nội dung trên Internet, đồng thời tối ưu hoá câu lệnh và câu trả lời để trợ lý không chỉ có thể tương tác với LLM mà còn với các dịch vụ hiện có. Chúng tôi cũng sẽ hướng dẫn bạn cách hoạt động của lệnh gọi hàm.

Điều phối tác nhân theo cách của bạn: Chúng ta sẽ khám phá nhiều cách điều phối tác nhân, từ các đường dẫn đơn giản đến các tình huống nhiều đường dẫn phức tạp hơn. Hãy xem đây là việc điều phối quy trình của nhóm nhân viên hỗ trợ.

Hệ thống nhiều tác nhân: Bạn sẽ khám phá cách thiết lập một hệ thống để các tác nhân của bạn có thể cộng tác và cùng nhau hoàn thành công việc – tất cả đều nhờ vào cấu trúc do sự kiện điều khiển.

Tự do sử dụng LLM – Sử dụng mô hình phù hợp nhất cho công việc: Chúng tôi không chỉ sử dụng một LLM! Bạn sẽ thấy cách sử dụng nhiều LLM, chỉ định cho chúng các vai trò khác nhau để tăng cường khả năng giải quyết vấn đề bằng cách sử dụng "mô hình tư duy" thú vị.

Nội dung động? Không sao cả!: Hãy tưởng tượng rằng trợ lý của bạn tạo nội dung động được điều chỉnh riêng cho từng người dùng theo thời gian thực. Chúng tôi sẽ hướng dẫn bạn cách thực hiện!

Chuyển sang đám mây bằng Google Cloud: Đừng chỉ chơi trong sổ tay. Chúng tôi sẽ hướng dẫn bạn cách thiết kế và triển khai hệ thống nhiều tác nhân trên Google Cloud để sẵn sàng cho thế giới thực!

Dự án này sẽ là một ví dụ điển hình về cách sử dụng tất cả các kỹ thuật mà chúng ta đã đề cập.

2. Kiến trúc

Công việc giáo viên hoặc làm việc trong ngành giáo dục có thể mang lại nhiều lợi ích, nhưng hãy đối mặt với thực tế rằng khối lượng công việc, đặc biệt là tất cả công việc chuẩn bị, có thể rất khó khăn! Ngoài ra, các trường học thường không có đủ nhân viên và việc dạy kèm có thể tốn kém. Đó là lý do chúng tôi đề xuất một trợ lý giảng dạy dựa trên AI. Công cụ này có thể giúp nhà giáo dục giảm bớt gánh nặng và giúp giải quyết tình trạng thiếu nhân viên cũng như thiếu dịch vụ gia sư với mức giá phải chăng.

Trợ lý giảng dạy AI của chúng tôi có thể tạo kế hoạch bài học chi tiết, bài kiểm tra thú vị, bản tóm tắt bằng âm thanh dễ hiểu và bài tập được cá nhân hoá. Nhờ đó, giáo viên có thể tập trung vào việc mình làm tốt nhất: kết nối với học viên và giúp họ yêu thích việc học.

Hệ thống này có hai trang web: một trang web để giáo viên tạo giáo án cho các tuần sắp tới,

Người lập kế hoạch

và một trang để học viên truy cập vào bài kiểm tra, bản tóm tắt bằng âm thanh và bài tập. Cánh cổng

Hãy cùng tìm hiểu cấu trúc hỗ trợ trợ lý giảng dạy Aidemy. Như bạn có thể thấy, chúng tôi đã chia nhỏ ứng dụng này thành một số thành phần chính, tất cả đều hoạt động cùng nhau để thực hiện việc này.

Kiến trúc

Các thành phần và công nghệ kiến trúc chính:

Google Cloud Platform (GCP): Trung tâm của toàn bộ hệ thống:

  • Vertex AI: Truy cập vào các LLM Gemini của Google.
  • Cloud Run: Nền tảng không máy chủ để triển khai các tác nhân và hàm được đóng gói trong vùng chứa.
  • Cloud SQL: Cơ sở dữ liệu PostgreSQL cho dữ liệu về chương trình học.
  • Pub/Sub và Eventarc: Nền tảng của cấu trúc do sự kiện điều khiển, cho phép giao tiếp không đồng bộ giữa các thành phần.
  • Bộ nhớ trên đám mây: Lưu trữ bản tóm tắt âm thanh và tệp bài tập.
  • Trình quản lý bí mật: Quản lý thông tin xác thực cơ sở dữ liệu một cách an toàn.
  • Cấu phần lưu trữ cấu phần phần mềm: Lưu trữ hình ảnh Docker cho các tác nhân.
  • Compute Engine: Để triển khai LLM tự lưu trữ thay vì dựa vào các giải pháp của nhà cung cấp

LLM: "Bộ não" của hệ thống:

  • Mô hình Gemini của Google: (Gemini 1.0 Pro, Gemini 2 Flash, Gemini 2 Flash Thinking, Gemini 1.5-pro) Dùng để lập kế hoạch bài học, tạo nội dung, tạo HTML động, giải thích bài kiểm tra và kết hợp các bài tập.
  • DeepSeek: Được sử dụng cho nhiệm vụ chuyên biệt là tạo bài tập tự học

LangChain và LangGraph: Khung phát triển ứng dụng LLM

  • Hỗ trợ việc tạo quy trình làm việc phức tạp cho nhiều tác nhân.
  • Cho phép điều phối các công cụ một cách thông minh (lệnh gọi API, truy vấn cơ sở dữ liệu, tìm kiếm trên web).
  • Triển khai cấu trúc do sự kiện điều khiển để tăng khả năng mở rộng và linh hoạt của hệ thống.

Về cơ bản, kiến trúc của chúng tôi kết hợp sức mạnh của LLM với dữ liệu có cấu trúc và giao tiếp dựa trên sự kiện, tất cả đều chạy trên Google Cloud. Điều này cho phép chúng tôi xây dựng một trợ lý giảng dạy có thể mở rộng, đáng tin cậy và hiệu quả.

3. Trước khi bắt đầu

Trong Google Cloud Console, trên trang bộ chọn dự án, hãy chọn hoặc tạo một dự án trên Google Cloud. Đảm bảo bạn đã bật tính năng thanh toán cho dự án trên Cloud. Tìm hiểu cách kiểm tra xem tính năng thanh toán có được bật trên dự án hay không.

👉Nhấp vào Kích hoạt Cloud Shell ở đầu bảng điều khiển Google Cloud (Đây là biểu tượng hình dạng của thiết bị đầu cuối ở đầu ngăn Cloud Shell), nhấp vào nút "Mở trình chỉnh sửa" (trông giống như một thư mục đang mở có bút chì). Thao tác này sẽ mở Trình chỉnh sửa mã Cloud Shell trong cửa sổ. Bạn sẽ thấy một trình khám phá tệp ở bên trái.

Cloud Shell

👉Nhấp vào nút Đăng nhập bằng mã trên đám mây trong thanh trạng thái dưới cùng như minh hoạ. Uỷ quyền cho trình bổ trợ theo hướng dẫn. Nếu bạn thấy Cloud Code – no project (Mã trên đám mây – không có dự án) trong thanh trạng thái, hãy chọn mục đó rồi chọn "Select a Google Cloud Project" (Chọn dự án Google Cloud) trong trình đơn thả xuống, sau đó chọn một dự án Google Cloud cụ thể trong danh sách dự án mà bạn đã tạo.

Dự án đăng nhập

👉Mở cửa sổ dòng lệnh trong IDE trên đám mây, Thiết bị đầu cuối mới

👉Trong dòng lệnh, hãy xác minh rằng bạn đã được xác thực và dự án được đặt thành mã dự án của bạn bằng lệnh sau:

gcloud auth list

👉Rồi chạy:

gcloud config set project <YOUR_PROJECT_ID>

👉Chạy lệnh sau để bật các API Google Cloud cần thiết:

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

Quá trình này có thể mất vài phút.

Bật tính năng Hỗ trợ mã Gemini trong IDE Cloud Shell

Nhấp vào nút Code Assist (Hỗ trợ mã) trong bảng điều khiển bên trái như minh hoạ rồi chọn một lần cuối cùng dự án Google Cloud chính xác. Nếu bạn được yêu cầu bật Cloud AI Companion API, vui lòng làm như vậy rồi tiếp tục. Sau khi bạn chọn dự án Google Cloud, hãy đảm bảo bạn có thể thấy dự án đó trong thông báo trạng thái của Cloud Code trên thanh trạng thái và bạn cũng đã bật tính năng Hỗ trợ mã ở bên phải, trong thanh trạng thái như minh hoạ bên dưới:

Bật tính năng hỗ trợ mã

Thiết lập quyền

👉Thiết lập quyền cho tài khoản dịch vụ

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"

Cấp quyền 👉Cloud Storage (Đọc/Ghi):

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

👉Pub/Sub (Xuất bản/Nhận):

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 (Đọc/Ghi):

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

👉Eventarc (Nhận sự kiện):

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 (Người dùng):

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

👉Trình quản lý bí mật (Đọc):

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

👉Xác thực kết quả trong bảng điều khiển IAMBảng điều khiển IAM

4. Tạo tác nhân đầu tiên

Trước khi đi sâu vào các hệ thống đa tác nhân phức tạp, chúng ta cần thiết lập một khối xây dựng cơ bản: một tác nhân chức năng duy nhất. Trong phần này, chúng ta sẽ thực hiện các bước đầu tiên bằng cách tạo một tác nhân "nhà cung cấp sách" đơn giản. Tác nhân của nhà cung cấp sách lấy một danh mục làm dữ liệu đầu vào và sử dụng LLM Gemini để tạo sách đại diện JSON trong danh mục đó. Sau đó, ứng dụng này sẽ phân phát các đề xuất sách này dưới dạng điểm cuối API REST .

Nhà cung cấp sách

👉Trong một thẻ trình duyệt khác, hãy mở Google Cloud Console trong trình duyệt web của bạn,trong trình đơn điều hướng (☰), hãy chuyển đến "Cloud Run". Nhấp vào nút "+ ... WRITE A FUNCTION" ("+ ... VIẾT HÀM").

Tạo hàm

👉Tiếp theo, chúng ta sẽ định cấu hình các chế độ cài đặt cơ bản của Hàm chạy trên đám mây:

  • Tên dịch vụ: book-provider
  • Vùng: us-central1
  • Thời gian chạy: Python 3.12
  • Xác thực: Allow unauthenticated invocations thành Bật.

👉Giữ nguyên các chế độ cài đặt khác ở chế độ mặc định rồi nhấp vào Tạo. Thao tác này sẽ đưa bạn đến trình soạn thảo mã nguồn.

Bạn sẽ thấy các tệp main.pyrequirements.txt được điền sẵn.

main.py sẽ chứa logic nghiệp vụ của hàm, requirements.txt sẽ chứa các gói cần thiết.

👉Bây giờ, chúng ta đã sẵn sàng viết mã! Nhưng trước khi đi sâu vào, hãy xem liệu Tính năng hỗ trợ mã của Gemini có thể giúp chúng ta bắt đầu không. Quay lại Trình chỉnh sửa Cloud Shell, nhấp vào biểu tượng Gemini Code Assist (Trợ giúp mã Gemini) rồi dán yêu cầu sau vào hộp lời nhắc: 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

Sau đó, tính năng Hỗ trợ mã sẽ tạo một giải pháp tiềm năng, cung cấp cả mã nguồn và tệp phần phụ thuộc requirements.txt.

Bạn nên so sánh mã do tính năng Trợ giúp lập trình tạo với giải pháp đã kiểm thử và chính xác được cung cấp bên dưới. Điều này cho phép bạn đánh giá hiệu quả của công cụ và xác định mọi sự khác biệt có thể xảy ra. Mặc dù bạn không nên tin tưởng LLM một cách mù quáng, nhưng tính năng Hỗ trợ mã có thể là một công cụ tuyệt vời để tạo bản mô hình nhanh và tạo cấu trúc mã ban đầu, đồng thời bạn nên sử dụng tính năng này để bắt đầu một cách hiệu quả.

Vì đây là một hội thảo, nên chúng ta sẽ tiếp tục với mã đã xác minh được cung cấp bên dưới. Tuy nhiên, bạn có thể tự do thử nghiệm với mã do tính năng Trợ giúp mã tạo ra để hiểu rõ hơn về các tính năng và hạn chế của tính năng này.

👉Quay lại trình soạn thảo mã nguồn của Hàm chạy trên đám mây (trong thẻ trình duyệt khác). Hãy cẩn thận thay thế nội dung hiện có của main.py bằng mã được cung cấp bên dưới:

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)

👉Thay thế nội dung của requirements.txt bằng nội dung sau:

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

👉chúng ta sẽ đặt Điểm truy cập hàm: recommended

03-02-function-create.png

👉Nhấp vào LƯU VÀ TRIỂN KHAI để triển khai Hàm. Chờ quá trình triển khai hoàn tất. Cloud Console sẽ hiển thị trạng thái. Thao tác này có thể mất vài phút.

văn bản thay thế 👉Sau khi triển khai, hãy quay lại trình chỉnh sửa cloud shell, trong cửa sổ dòng lệnh, hãy chạy:

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

Tệp này sẽ hiển thị một số dữ liệu sách ở định dạng JSON.

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

Xin chúc mừng! Bạn đã triển khai thành công một Hàm Cloud Run. Đây là một trong những dịch vụ mà chúng tôi sẽ tích hợp khi phát triển trợ lý Aidemy.

5. Công cụ xây dựng: Kết nối tác nhân với dịch vụ và dữ liệu RESTFUL

Hãy tiếp tục tải Dự án Bootstrap Skeleton xuống, đảm bảo bạn đang ở trong Trình chỉnh sửa Cloud Shell. Trong quá trình chạy dòng lệnh,

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

Sau khi chạy lệnh này, một thư mục mới có tên aidemy-bootstrap sẽ được tạo trong môi trường Cloud Shell.

Trong ngăn Explorer (Trình khám phá) của Trình chỉnh sửa Cloud Shell (thường ở bên trái), bạn sẽ thấy thư mục được tạo khi bạn sao chép kho lưu trữ Git aidemy-bootstrap. Mở thư mục gốc của dự án trong Explorer. Bạn sẽ thấy một thư mục con planner trong đó, hãy mở thư mục đó. trình khám phá dự án

Hãy bắt đầu xây dựng các công cụ mà nhân viên hỗ trợ của chúng ta sẽ sử dụng để trở nên thực sự hữu ích. Như bạn đã biết, LLM rất giỏi suy luận và tạo văn bản, nhưng chúng cần quyền truy cập vào các tài nguyên bên ngoài để thực hiện các nhiệm vụ thực tế và cung cấp thông tin chính xác, mới nhất. Hãy coi các công cụ này là "dao đa năng của Thuỵ Sĩ" của tác nhân, giúp tác nhân có thể tương tác với thế giới.

Khi xây dựng một tác nhân, bạn dễ dàng phải mã hoá cứng hàng tấn thông tin chi tiết. Điều này tạo ra một tác nhân không linh hoạt. Thay vào đó, bằng cách tạo và sử dụng các công cụ, tác nhân có quyền truy cập vào logic hoặc hệ thống bên ngoài, mang lại cho tác nhân các lợi ích của cả LLM và lập trình truyền thống.

Trong phần này, chúng ta sẽ tạo nền tảng cho tác nhân lập kế hoạch mà giáo viên sẽ sử dụng để tạo kế hoạch bài học. Trước khi tác nhân bắt đầu tạo kế hoạch, chúng ta muốn đặt giới hạn bằng cách cung cấp thêm thông tin chi tiết về chủ đề và chủ đề. Chúng ta sẽ xây dựng 3 công cụ:

  1. Lệnh gọi API Restful: Tương tác với một API hiện có để truy xuất dữ liệu.
  2. Truy vấn cơ sở dữ liệu: Tìm nạp dữ liệu có cấu trúc từ cơ sở dữ liệu Cloud SQL.
  3. Google Tìm kiếm: Truy cập thông tin theo thời gian thực từ web.

Tìm nạp Đề xuất sách từ một API

Trước tiên, hãy tạo một công cụ truy xuất các đề xuất sách từ API book-provider mà chúng ta đã triển khai trong phần trước. Điều này minh hoạ cách một tác nhân có thể tận dụng các dịch vụ hiện có.

Đề xuất sách

Trong Trình chỉnh sửa Cloud Shell, hãy mở dự án aidemy-bootstrap mà bạn đã sao chép ở phần trước. 👉Chỉnh sửa book.py trong thư mục planner rồi dán mã sau:

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

Giải thích:

  • recommend_book(query: str): Hàm này lấy truy vấn của người dùng làm dữ liệu đầu vào.
  • Tương tác LLM: Phương thức này sử dụng LLM để trích xuất danh mục từ truy vấn. Phần này minh hoạ cách bạn có thể sử dụng LLM để tạo các tham số cho công cụ.
  • Lệnh gọi API: Lệnh này tạo một yêu cầu POST đến API nhà cung cấp sách, truyền danh mục và số lượng sách mong muốn.

👉Để kiểm thử hàm mới này, hãy đặt biến môi trường, chạy :

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

👉Cài đặt các phần phụ thuộc và chạy mã để đảm bảo mã đó hoạt động, hãy chạy:

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

Bỏ qua cửa sổ bật lên cảnh báo về Git.

Bạn sẽ thấy một chuỗi JSON chứa các cuốn sách được đề xuất được truy xuất từ API nhà cung cấp sách.

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

Nếu bạn thấy thông báo này, tức là công cụ đầu tiên đang hoạt động đúng cách!

Thay vì tạo một lệnh gọi API RESTful rõ ràng bằng các tham số cụ thể, chúng ta sẽ sử dụng ngôn ngữ tự nhiên ("Tôi đang học một khoá học..."). Sau đó, tác nhân này sẽ trích xuất thông minh các tham số cần thiết (chẳng hạn như danh mục) bằng cách sử dụng NLP, làm nổi bật cách tác nhân tận dụng khả năng hiểu ngôn ngữ tự nhiên để tương tác với API.

lệnh gọi so sánh

👉Xoá mã kiểm thử sau đây khỏi 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."))

Lấy dữ liệu về chương trình học từ cơ sở dữ liệu

Tiếp theo, chúng ta sẽ tạo một công cụ tìm nạp dữ liệu có cấu trúc về chương trình giảng dạy từ cơ sở dữ liệu PostgreSQL Cloud SQL. Điều này cho phép nhân viên hỗ trợ truy cập vào một nguồn thông tin đáng tin cậy để lập kế hoạch bài học.

tạo db

👉Chạy các lệnh sau trong dòng lệnh để tạo một phiên bản Cloud SQL có tên aidemy . Quá trình này có thể mất chút thời gian.

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

👉Tiếp theo, hãy tạo một cơ sở dữ liệu có tên aidemy-db trong thực thể mới.

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

Hãy xác minh thực thể trong Cloud SQL trong Google Cloud Console. Bạn sẽ thấy một thực thể Cloud SQL có tên là aidemy được liệt kê. Nhấp vào tên thực thể để xem thông tin chi tiết. Trên trang chi tiết về phiên bản Cloud SQL, hãy nhấp vào "SQL Studio" trong trình đơn điều hướng bên trái. Thao tác này sẽ mở một thẻ mới.

Nhấp để kết nối với cơ sở dữ liệu. Đăng nhập vào SQL Studio

Chọn aidemy-db làm cơ sở dữ liệu. Nhập postgres làm user (người dùng) và 1234qwer làm password (mật khẩu). đăng nhập vào sql studio

👉Trong trình soạn thảo truy vấn SQL Studio, hãy dán mã SQL sau:

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

Mã SQL này tạo một bảng có tên curriculums và chèn một số dữ liệu mẫu. Nhấp vào Run (Chạy) để thực thi mã SQL. Bạn sẽ thấy thông báo xác nhận cho biết các lệnh đã được thực thi thành công.

👉Mở rộng trình khám phá, tìm bảng mới tạo rồi nhấp vào truy vấn. Thao tác này sẽ mở một thẻ trình chỉnh sửa mới có SQL được tạo cho bạn,

sql studio select table

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

👉Nhấp vào Run (Chạy).

Bảng kết quả sẽ hiển thị các hàng dữ liệu mà bạn đã chèn ở bước trước, xác nhận rằng bảng và dữ liệu đã được tạo chính xác.

Giờ đây, bạn đã tạo thành công cơ sở dữ liệu có dữ liệu mẫu về chương trình giảng dạy được điền sẵn, chúng ta sẽ tạo một công cụ để truy xuất dữ liệu đó.

👉Trong Cloud Code Editor, hãy chỉnh sửa tệp curriculums.py trong thư mục aidemy-bootstrap rồi dán mã sau:

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

Giải thích:

  • Biến môi trường: Mã truy xuất thông tin xác thực cơ sở dữ liệu và thông tin kết nối từ các biến môi trường (xem thêm về vấn đề này ở bên dưới).
  • connect_with_connector(): Hàm này sử dụng Trình kết nối Cloud SQL để thiết lập kết nối bảo mật với cơ sở dữ liệu.
  • get_curriculum(year: int, subject: str): Hàm này lấy năm và môn học làm dữ liệu đầu vào, truy vấn bảng chương trình học và trả về nội dung mô tả chương trình học tương ứng.

👉Trước khi có thể chạy mã, chúng ta phải đặt một số biến môi trường, trong dòng lệnh, hãy chạy:

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"

👉Để kiểm thử, hãy thêm mã sau vào cuối curriculums.py:

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

👉Chạy mã:

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

Bạn sẽ thấy nội dung mô tả chương trình giảng dạy cho môn Toán lớp 6 được in ra bảng điều khiển.

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

Nếu bạn thấy nội dung mô tả về chương trình học, thì công cụ cơ sở dữ liệu đang hoạt động đúng cách! Tiếp tục và dừng tập lệnh bằng cách nhấn Ctrl+C.

👉Xoá mã kiểm thử sau đây khỏi curriculums.py

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

👉Thoát môi trường ảo, trong dòng lệnh chạy:

deactivate

6. Công cụ xây dựng: Truy cập thông tin theo thời gian thực từ web

Cuối cùng, chúng ta sẽ xây dựng một công cụ sử dụng tính năng tích hợp Gemini 2 và Google Tìm kiếm để truy cập thông tin theo thời gian thực từ web. Điều này giúp nhân viên hỗ trợ luôn nắm bắt thông tin mới nhất và cung cấp kết quả phù hợp.

Việc tích hợp Gemini 2 với Google Search API giúp nâng cao khả năng của trợ lý bằng cách cung cấp kết quả tìm kiếm chính xác và phù hợp hơn theo ngữ cảnh. Điều này cho phép các nhân viên hỗ trợ truy cập vào thông tin mới nhất và dựa vào dữ liệu thực tế để đưa ra câu trả lời, giảm thiểu tình trạng ảo tưởng. Việc tích hợp API được cải thiện cũng tạo điều kiện cho các truy vấn bằng ngôn ngữ tự nhiên hơn, cho phép các nhân viên hỗ trợ xây dựng các yêu cầu tìm kiếm phức tạp và tinh tế.

Tìm kiếm

Hàm này lấy một cụm từ tìm kiếm, chương trình giảng dạy, môn học và năm làm dữ liệu đầu vào, đồng thời sử dụng API Gemini và công cụ Google Tìm kiếm để truy xuất thông tin liên quan trên Internet. Nếu bạn xem kỹ, bạn sẽ thấy ứng dụng này đang sử dụng SDK AI tạo sinh của Google để thực hiện lệnh gọi hàm mà không cần sử dụng bất kỳ khung nào khác.

👉Chỉnh sửa search.py trong thư mục aidemy-bootstrap rồi dán mã sau:

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)

Giải thích:

  • Xác định công cụ – google_search_tool: Gói đối tượng GoogleSearch trong một công cụ
  • search_latest_resource(search_text: str, subject: str, year: int): Hàm này lấy cụm từ tìm kiếm, chủ đề và năm làm dữ liệu đầu vào, đồng thời sử dụng Gemini API để tìm kiếm trên Google. Mô hình Gemini
  • GenerateContentConfig: Xác định rằng lớp này có quyền truy cập vào công cụ GoogleSearch

Mô hình Gemini phân tích nội bộ search_text và xác định xem mô hình có thể trả lời trực tiếp câu hỏi hay cần sử dụng công cụ GoogleSearch. Đây là một bước quan trọng diễn ra trong quá trình suy luận của LLM. Mô hình này đã được huấn luyện để nhận ra những tình huống cần sử dụng công cụ bên ngoài. Nếu mô hình quyết định sử dụng công cụ GoogleSearch, thì SDK AI tạo sinh của Google sẽ xử lý lệnh gọi thực tế. SDK sẽ lấy quyết định của mô hình và các tham số mà mô hình tạo ra, sau đó gửi các tham số đó đến API Tìm kiếm của Google. Phần này bị ẩn khỏi người dùng trong mã.

Sau đó, mô hình Gemini sẽ tích hợp kết quả tìm kiếm vào câu trả lời. Ứng dụng có thể sử dụng thông tin này để trả lời câu hỏi của người dùng, tạo bản tóm tắt hoặc thực hiện một số thao tác khác.

👉Để kiểm thử, hãy chạy mã:

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

Bạn sẽ thấy phản hồi của Gemini Search API chứa kết quả tìm kiếm liên quan đến "Giáo trình Toán học lớp 5". Kết quả chính xác sẽ phụ thuộc vào kết quả tìm kiếm, nhưng đó sẽ là một đối tượng JSON chứa thông tin về nội dung tìm kiếm.

Nếu bạn thấy kết quả tìm kiếm, tức là công cụ Google Tìm kiếm đang hoạt động bình thường! Tiếp tục và dừng tập lệnh bằng cách nhấn Ctrl+C.

👉Sau đó, xoá phần cuối cùng trong mã.

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)

👉Thoát môi trường ảo, trong dòng lệnh chạy:

deactivate

Xin chúc mừng! Giờ đây, bạn đã tạo được 3 công cụ mạnh mẽ cho tác nhân lập kế hoạch: trình kết nối API, trình kết nối cơ sở dữ liệu và công cụ Google Tìm kiếm. Các công cụ này sẽ cho phép trợ lý truy cập vào thông tin và chức năng cần thiết để tạo kế hoạch giảng dạy hiệu quả.

7. Điều phối bằng LangGraph

Giờ đây, khi đã xây dựng các công cụ riêng lẻ, chúng ta cần điều phối các công cụ đó bằng LangGraph. Điều này cho phép chúng ta tạo một tác nhân "trình lập kế hoạch" phức tạp hơn có thể quyết định một cách thông minh công cụ nào sẽ được sử dụng và thời điểm sử dụng, dựa trên yêu cầu của người dùng.

LangGraph là một thư viện Python được thiết kế để giúp bạn dễ dàng xây dựng các ứng dụng có trạng thái, nhiều tác nhân bằng cách sử dụng Mô hình ngôn ngữ lớn (LLM). Hãy coi đây là một khung để điều phối các cuộc trò chuyện và quy trình phức tạp liên quan đến LLM, công cụ và các tác nhân khác.

Các khái niệm chính:

  • Cấu trúc biểu đồ: LangGraph thể hiện logic của ứng dụng dưới dạng biểu đồ có hướng. Mỗi nút trong biểu đồ đại diện cho một bước trong quy trình (ví dụ: lệnh gọi đến LLM, lệnh gọi công cụ, kiểm tra có điều kiện). Cạnh xác định luồng thực thi giữa các nút.
  • Trạng thái: LangGraph quản lý trạng thái của ứng dụng khi ứng dụng di chuyển qua biểu đồ. Trạng thái này có thể bao gồm các biến như dữ liệu đầu vào của người dùng, kết quả của lệnh gọi công cụ, kết quả trung gian từ LLM và mọi thông tin khác cần được lưu giữ giữa các bước.
  • Các nút: Mỗi nút đại diện cho một phép tính hoặc lượt tương tác. Các nguồn đó có thể là:
    • Tool Nodes (Điểm công cụ): Sử dụng một công cụ (ví dụ: tìm kiếm trên web, truy vấn cơ sở dữ liệu)
    • Function Nodes (Nút hàm): Thực thi một hàm Python.
  • Cạnh: Kết nối các nút, xác định luồng thực thi. Các nguồn đó có thể là:
    • Cạnh trực tiếp: Luồng đơn giản, không có điều kiện từ một nút đến nút khác.
    • Cạnh có điều kiện: Luồng phụ thuộc vào kết quả của một nút có điều kiện.

LangGraph

Chúng ta sẽ sử dụng LangGraph để triển khai quá trình điều phối. Hãy chỉnh sửa tệp aidemy.py trong thư mục aidemy-bootstrap để xác định logic LangGraph. 👉Nối mã theo sau vào cuối aidemy.py:

tools = [get_curriculum, search_latest_resource, recommend_book]

def determine_tool(state: MessagesState):
    llm = ChatVertexAI(model_name="gemini-2.0-flash-001", location=get_next_region())
    sys_msg = SystemMessage(
                    content=(
                        f"""You are a helpful teaching assistant that helps gather all needed information. 
                            Your ultimate goal is to create a detailed 3-week teaching plan. 
                            You have access to tools that help you gather information.  
                            Based on the user request, decide which tool(s) are needed. 

                        """
                    )
                )

    llm_with_tools = llm.bind_tools(tools)
    return {"messages": llm_with_tools.invoke([sys_msg] + state["messages"])} 

Hàm này chịu trách nhiệm lấy trạng thái hiện tại của cuộc trò chuyện, cung cấp cho LLM một thông báo hệ thống, sau đó yêu cầu LLM tạo phản hồi. LLM có thể trả lời trực tiếp người dùng hoặc chọn sử dụng một trong các công cụ có sẵn.

tools (công cụ): Danh sách này thể hiện tập hợp công cụ mà tác nhân có thể sử dụng. Tệp này chứa 3 hàm công cụ mà chúng ta đã xác định ở các bước trước: get_curriculum, search_latest_resourcerecommend_book. llm.bind_tools(tools): Hàm này "liên kết" danh sách công cụ với đối tượng llm. Việc liên kết các công cụ sẽ cho LLM biết rằng các công cụ này có sẵn và cung cấp cho LLM thông tin về cách sử dụng các công cụ đó (ví dụ: tên của các công cụ, các tham số mà các công cụ đó chấp nhận và chức năng của các công cụ đó).

Chúng ta sẽ sử dụng LangGraph để triển khai quá trình điều phối. 👉Nối mã sau vào cuối aidemy.py:

def prep_class(prep_needs):
   
    builder = StateGraph(MessagesState)
    builder.add_node("determine_tool", determine_tool)
    builder.add_node("tools", ToolNode(tools))
    
    builder.add_edge(START, "determine_tool")
    builder.add_conditional_edges("determine_tool",tools_condition)
    builder.add_edge("tools", "determine_tool")

    
    memory = MemorySaver()
    graph = builder.compile(checkpointer=memory)

    config = {"configurable": {"thread_id": "1"}}
    messages = graph.invoke({"messages": prep_needs},config)
    print(messages)
    for m in messages['messages']:
        m.pretty_print()
    teaching_plan_result = messages["messages"][-1].content  


    return teaching_plan_result

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

Giải thích:

  • StateGraph(MessagesState): Tạo đối tượng StateGraph. StateGraph là một khái niệm cốt lõi trong LangGraph. Biểu đồ này thể hiện quy trình làm việc của tác nhân dưới dạng biểu đồ, trong đó mỗi nút trong biểu đồ đại diện cho một bước trong quy trình. Hãy coi đó là việc xác định bản thiết kế cho cách mà tác nhân sẽ suy luận và hành động.
  • Cạnh có điều kiện: Xuất phát từ nút "determine_tool", đối số tools_condition có thể là một hàm xác định cạnh nào sẽ theo sau dựa trên kết quả của hàm determine_tool. Các cạnh có điều kiện cho phép biểu đồ phân nhánh dựa trên quyết định của LLM về công cụ nào sẽ sử dụng (hoặc liệu có phản hồi trực tiếp với người dùng hay không). Đây là nơi "trí thông minh" của tác nhân phát huy tác dụng – tác nhân có thể tự động điều chỉnh hành vi của mình dựa trên tình huống.
  • Vòng lặp: Thêm một cạnh vào biểu đồ kết nối nút "tools" trở lại nút "determine_tool". Thao tác này tạo ra một vòng lặp trong biểu đồ, cho phép tác nhân sử dụng các công cụ lặp lại cho đến khi thu thập đủ thông tin để hoàn thành nhiệm vụ và đưa ra câu trả lời thoả đáng. Vòng lặp này rất quan trọng đối với các nhiệm vụ phức tạp đòi hỏi nhiều bước suy luận và thu thập thông tin.

Bây giờ, hãy kiểm thử tác nhân lập kế hoạch để xem cách tác nhân này điều phối các công cụ khác nhau.

Mã này sẽ chạy hàm prep_class với dữ liệu đầu vào cụ thể của người dùng, mô phỏng yêu cầu tạo kế hoạch giảng dạy cho môn Toán lớp 5 trong phần Hình học, sử dụng chương trình giảng dạy, sách đề xuất và tài nguyên mới nhất trên Internet.

Nếu bạn đã đóng cửa sổ dòng lệnh hoặc các biến môi trường không còn được đặt, hãy chạy lại các lệnh sau

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"

👉Chạy mã:

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

Xem nhật ký trong dòng lệnh. Bạn sẽ thấy bằng chứng cho thấy rằng nhân viên hỗ trợ đang gọi cả ba công cụ (lấy chương trình giảng dạy của trường, nhận đề xuất sách và tìm kiếm tài nguyên mới nhất) trước khi cung cấp kế hoạch giảng dạy cuối cùng. Điều này cho thấy rằng quá trình điều phối LangGraph đang hoạt động chính xác và tác nhân đang sử dụng một cách thông minh tất cả các công cụ có sẵn để thực hiện yêu cầu của người dùng.

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

Dừng tập lệnh bằng cách nhấn Ctrl+C.

👉Bây giờ, hãy thay thế mã kiểm thử bằng một lời nhắc khác, yêu cầu gọi các công cụ khác.

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

Nếu bạn đã đóng cửa sổ dòng lệnh hoặc các biến môi trường không còn được đặt, hãy chạy lại các lệnh sau

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"

👉Chạy lại mã:

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

Lần này bạn nhận thấy điều gì? Nhân viên hỗ trợ đã gọi những công cụ nào? Bạn sẽ thấy rằng lần này, tác nhân chỉ gọi công cụ search_latest_resource. Điều này là do lời nhắc không chỉ định rằng nó cần hai công cụ khác và LLM của chúng ta đủ thông minh để không gọi các công cụ khác.

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

Dừng tập lệnh bằng cách nhấn Ctrl+C. 👉Xoá mã kiểm thử để giữ cho tệp aidemy.py của bạn luôn sạch sẽ:

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

Giờ đây, khi đã xác định logic của tác nhân, hãy khởi chạy ứng dụng web Flask. Điều này sẽ cung cấp một giao diện dựa trên biểu mẫu quen thuộc để giáo viên tương tác với nhân viên hỗ trợ. Mặc dù các hoạt động tương tác với chatbot là phổ biến với LLM, nhưng chúng tôi chọn giao diện người dùng gửi biểu mẫu truyền thống vì giao diện này có thể trực quan hơn đối với nhiều nhà giáo dục.

Nếu bạn đã đóng cửa sổ dòng lệnh hoặc các biến môi trường không còn được đặt, hãy chạy lại các lệnh sau

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"

👉Bây giờ, hãy khởi động Giao diện người dùng web.

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

Tìm thông báo khởi động trong kết quả của cửa sổ dòng lệnh Cloud Shell. Flask thường in thông báo cho biết ứng dụng đang chạy và trên cổng nào.

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.

👉Trên trình đơn "Xem trước trên web", hãy chọn Xem trước trên cổng 8080. Cloud Shell sẽ mở một thẻ hoặc cửa sổ trình duyệt mới có bản xem trước ứng dụng trên web.

Trang web

Trong giao diện ứng dụng, hãy chọn 5 cho Năm, chọn chủ đề Mathematics và nhập Geometry vào Yêu cầu bổ sung Hạn mức

Thay vì ngồi chờ phản hồi, hãy chuyển sang thiết bị đầu cuối của Trình chỉnh sửa trên đám mây. Bạn có thể quan sát tiến trình và mọi thông báo lỗi hoặc đầu ra do hàm tạo ra trong thiết bị đầu cuối của trình mô phỏng. 😁

👉Dừng tập lệnh bằng cách nhấn phím Ctrl+C trong dòng lệnh.

👉Thoát môi trường ảo:

deactivate

8. Triển khai tác nhân lập kế hoạch lên đám mây

Tạo và đẩy hình ảnh vào sổ đăng ký

Tổng quan

👉Đã đến lúc triển khai ứng dụng này lên đám mây. Trong dòng lệnh, hãy tạo một kho lưu trữ cấu phần phần mềm để lưu trữ hình ảnh docker mà chúng ta sắp tạo.

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

Bạn sẽ thấy Created repository [agent-repository] (Kho lưu trữ đã tạo [agent-repository]).

👉Chạy lệnh sau để tạo hình ảnh Docker.

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

👉Chúng ta cần gắn thẻ lại hình ảnh để hình ảnh được lưu trữ trong Cấu phần phần mềm thay vì GCR và đẩy hình ảnh đã gắn thẻ vào Cấu phần phần mềm:

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

Sau khi đẩy xong, bạn có thể xác minh rằng hình ảnh đã được lưu trữ thành công trong Cấu phần phần mềm. Chuyển đến Cơ sở lưu trữ cấu phần phần mềm trong Google Cloud Console. Bạn sẽ tìm thấy hình ảnh aidemy-planner trong kho lưu trữ agent-repository. Hình ảnh về công cụ lập kế hoạch của Aidemy

Bảo mật thông tin xác thực cơ sở dữ liệu bằng Trình quản lý bí mật

Để quản lý và truy cập thông tin xác thực cơ sở dữ liệu một cách an toàn, chúng ta sẽ sử dụng Trình quản lý bí mật của Google Cloud. Điều này giúp ngăn việc mã hoá cứng thông tin nhạy cảm trong mã ứng dụng và tăng cường bảo mật.

👉Chúng ta sẽ tạo các khoá riêng cho tên người dùng, mật khẩu và tên cơ sở dữ liệu. Phương pháp này cho phép chúng ta quản lý từng thông tin xác thực một cách độc lập. Trong dòng lệnh, hãy chạy:

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

Việc sử dụng Trình quản lý bí mật là một bước quan trọng trong việc bảo mật ứng dụng và ngăn chặn việc vô tình tiết lộ thông tin xác thực nhạy cảm. Phương pháp này tuân theo các phương pháp hay nhất về bảo mật cho các hoạt động triển khai trên đám mây.

Triển khai lên Cloud Run

Cloud Run là một nền tảng không máy chủ được quản lý toàn diện, cho phép bạn triển khai các ứng dụng được đóng gói trong vùng chứa một cách nhanh chóng và dễ dàng. Công cụ này tóm tắt việc quản lý cơ sở hạ tầng, cho phép bạn tập trung vào việc viết và triển khai mã. Chúng tôi sẽ triển khai trình lập kế hoạch dưới dạng một dịch vụ Cloud Run.

👉Trong Google Cloud Console, hãy chuyển đến phần "Cloud Run". Nhấp vào TRÌNH TỔNG HỢP TRÌNH DƯƠNG rồi chọn DỊCH VỤ. Định cấu hình dịch vụ Cloud Run:

Cloud Run

  1. Hình ảnh vùng chứa: Nhấp vào "Chọn" trong trường URL. Tìm URL hình ảnh mà bạn đã đẩy vào Cấu phần phần mềm (ví dụ: us-central1-docker.pkg.dev/YOUR_PROJECT_ID/agent-repository/agent-planner/YOUR_IMG).
  2. Tên dịch vụ: aidemy-planner
  3. Khu vực: Chọn khu vực us-central1.
  4. Xác thực: Đối với mục đích của hội thảo này, bạn có thể cho phép "Cho phép các lệnh gọi chưa xác thực". Đối với bản phát hành công khai, bạn nên hạn chế quyền truy cập.
  5. Thẻ (Các) vùng chứa (Mở rộng Vùng chứa, Mạng):
    • Thẻ Cài đặt:
      • Tài nguyên
        • bộ nhớ : 2GB
    • Thẻ Biến và khoá:
      • Biến môi trường:
        • Thêm tên: GOOGLE_CLOUD_PROJECT và giá trị: <YOUR_PROJECT_ID>
        • Thêm tên: BOOK_PROVIDER_URL và giá trị: <YOUR_BOOK_PROVIDER_FUNCTION_URL>
      • Khoá bí mật được hiển thị dưới dạng biến môi trường:
        • Thêm tên: DB_USER, bí mật: chọn db-user và phiên bản:latest
        • Thêm tên: DB_PASS, bí mật: chọn db-pass và phiên bản:latest
        • Thêm tên: DB_NAME, bí mật: chọn db-name và phiên bản:latest

Chạy lệnh sau trong dòng lệnh nếu bạn cần truy xuất YOUR_BOOK_PROVIDER_FUNCTION_URL:

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

Đặt khoá bí mật

Giữ nguyên chế độ khác làm mặc định.

👉Nhấp vào TẠO.

Cloud Run sẽ triển khai dịch vụ của bạn.

Sau khi triển khai, hãy nhấp vào dịch vụ đó để chuyển đến trang chi tiết. Bạn có thể tìm thấy URL đã triển khai ở trên cùng.

URL

Trong giao diện ứng dụng, hãy chọn 7 cho Năm, chọn Mathematics làm tiêu đề và nhập Algebra vào trường Yêu cầu tiện ích bổ sung. Điều này sẽ cung cấp cho trợ lý ngữ cảnh cần thiết để tạo kế hoạch bài học phù hợp.

Xin chúc mừng! Bạn đã tạo thành công một kế hoạch giảng dạy bằng cách sử dụng tác nhân AI mạnh mẽ của chúng tôi. Điều này cho thấy tiềm năng của các trợ lý trong việc giảm đáng kể khối lượng công việc và đơn giản hoá các nhiệm vụ, từ đó cải thiện hiệu quả và giúp nhà giáo dục dễ dàng làm việc hơn.

9. Hệ thống nhiều tác nhân

Giờ đây, khi đã triển khai thành công công cụ tạo kế hoạch giảng dạy, chúng ta hãy chuyển trọng tâm sang việc xây dựng cổng thông tin dành cho học viên. Cổng thông tin này sẽ cung cấp cho học viên quyền truy cập vào bài kiểm tra, bản tóm tắt bằng âm thanh và bài tập liên quan đến bài tập trên lớp. Do phạm vi của chức năng này, chúng ta sẽ tận dụng sức mạnh của các hệ thống đa tác nhân để tạo ra một giải pháp mô-đun và có thể mở rộng.

Như đã thảo luận trước đó, thay vì dựa vào một tác nhân duy nhất để xử lý mọi thứ, hệ thống nhiều tác nhân cho phép chúng ta chia nhỏ khối lượng công việc thành các nhiệm vụ chuyên biệt, nhỏ hơn, mỗi tác nhân chuyên xử lý một nhiệm vụ. Phương pháp này mang lại một số lợi thế chính:

Mô-đun và khả năng bảo trì: Thay vì tạo một tác nhân duy nhất thực hiện mọi việc, hãy tạo các tác nhân nhỏ hơn, chuyên biệt với trách nhiệm được xác định rõ ràng. Tính mô-đun này giúp hệ thống dễ hiểu, dễ bảo trì và dễ gỡ lỗi hơn. Khi có vấn đề xảy ra, bạn có thể tách riêng vấn đề đó cho một tác nhân cụ thể, thay vì phải tìm hiểu qua một cơ sở mã khổng lồ.

Khả năng mở rộng: Việc mở rộng một tác nhân phức tạp có thể là nút thắt cổ chai. Với hệ thống nhiều tác nhân, bạn có thể mở rộng quy mô từng tác nhân dựa trên nhu cầu cụ thể của tác nhân đó. Ví dụ: nếu một tác nhân đang xử lý một lượng lớn yêu cầu, bạn có thể dễ dàng tạo thêm các thực thể của tác nhân đó mà không ảnh hưởng đến phần còn lại của hệ thống.

Chuyên môn của nhóm: Hãy nghĩ như thế này: bạn sẽ không yêu cầu một kỹ sư xây dựng toàn bộ ứng dụng từ đầu. Thay vào đó, bạn tập hợp một nhóm chuyên gia, mỗi người có chuyên môn trong một lĩnh vực cụ thể. Tương tự, hệ thống nhiều tác nhân cho phép bạn tận dụng các điểm mạnh của nhiều LLM và công cụ, chỉ định các điểm mạnh đó cho các tác nhân phù hợp nhất với các nhiệm vụ cụ thể.

Phát triển song song: Các nhóm có thể làm việc trên nhiều tác nhân cùng một lúc, giúp đẩy nhanh quá trình phát triển. Vì các tác nhân độc lập với nhau, nên những thay đổi đối với một tác nhân ít có khả năng ảnh hưởng đến các tác nhân khác.

Cấu trúc do sự kiện điều khiển

Để cho phép giao tiếp và điều phối hiệu quả giữa các tác nhân này, chúng ta sẽ sử dụng kiến trúc dựa trên sự kiện. Điều này có nghĩa là các tác nhân sẽ phản ứng với "sự kiện" xảy ra trong hệ thống.

Các tác nhân đăng ký các loại sự kiện cụ thể (ví dụ: "đã tạo giáo án", "đã tạo bài tập"). Khi một sự kiện xảy ra, các tác nhân liên quan sẽ được thông báo và có thể phản ứng tương ứng. Việc tách biệt này giúp tăng tính linh hoạt, khả năng mở rộng và khả năng phản hồi theo thời gian thực.

Tổng quan

Bây giờ, để bắt đầu, chúng ta cần một cách để truyền các sự kiện này. Để làm việc này, chúng ta sẽ thiết lập một chủ đề Pub/Sub. Hãy bắt đầu bằng cách tạo một chủ đề có tên là kế hoạch.

👉Chuyển đến phần pub/sub của Google Cloud Console rồi nhấp vào nút "Tạo chủ đề".

👉Định cấu hình Chủ đề bằng mã nhận dạng/tên plan và bỏ đánh dấu Add a default subscription, để lại phần còn lại ở chế độ mặc định rồi nhấp vào Tạo.

Trang Pub/Sub sẽ làm mới và bạn sẽ thấy chủ đề mới tạo của mình được liệt kê trong bảng. Tạo chủ đề

Bây giờ, hãy tích hợp chức năng phát hành sự kiện Pub/Sub vào tác nhân lập kế hoạch. Chúng ta sẽ thêm một công cụ mới để gửi sự kiện "lên kế hoạch" đến chủ đề Pub/Sub mà chúng ta vừa tạo. Sự kiện này sẽ báo hiệu cho các tác nhân khác trong hệ thống (chẳng hạn như các tác nhân trong cổng thông tin dành cho học viên) rằng có một kế hoạch giảng dạy mới.

👉Quay lại Trình soạn thảo mã trên đám mây và mở tệp app.py nằm trong thư mục planner. Chúng ta sẽ thêm một hàm phát hành sự kiện. Thay thế:

##ADD SEND PLAN EVENT FUNCTION HERE

với

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: Hàm này lấy kế hoạch giảng dạy đã tạo làm dữ liệu đầu vào, tạo ứng dụng nhà xuất bản Pub/Sub, tạo đường dẫn chủ đề, chuyển đổi kế hoạch giảng dạy thành chuỗi JSON , phát hành thông báo đến chủ đề.
  • Danh sách công cụ: Hàm send_plan_event được thêm vào danh sách công cụ để nhân viên hỗ trợ có thể sử dụng.

Và trong cùng thư mục, trong tệp app.py, hãy 👉Cập nhật lời nhắc để hướng dẫn trợ lý gửi sự kiện giáo án đến chủ đề Pub/Sub sau khi tạo giáo án. Thay thế

### ADD send_plan_event CALL

với các thông tin sau:

send_plan_event(teaching_plan)

Bằng cách thêm công cụ send_plan_event và sửa đổi lời nhắc, chúng ta đã cho phép tác nhân lập kế hoạch phát hành sự kiện đến Pub/Sub, cho phép các thành phần khác của hệ thống phản ứng với việc tạo kế hoạch giảng dạy mới. Bây giờ, chúng ta sẽ có một hệ thống đa tác nhân có chức năng trong các phần sau.

10. Giúp học viên tự học bằng bài kiểm tra theo yêu cầu

Hãy tưởng tượng một môi trường học tập nơi học viên có thể tiếp cận vô số bài kiểm tra được điều chỉnh cho phù hợp với kế hoạch học tập cụ thể của họ. Những bài kiểm tra này cung cấp phản hồi tức thì, bao gồm cả câu trả lời và nội dung giải thích, giúp học viên hiểu rõ hơn về tài liệu. Đây là tiềm năng mà chúng tôi muốn khai thác thông qua cổng thông tin bài kiểm tra sử dụng AI.

Để hiện thực hoá tầm nhìn này, chúng ta sẽ xây dựng một thành phần tạo bài kiểm tra có thể tạo câu hỏi trắc nghiệm dựa trên nội dung của kế hoạch giảng dạy.

Tổng quan

👉Trong ngăn Explorer (Trình khám phá) của Cloud Code Editor, hãy chuyển đến thư mục portal. Mở tệp quiz.py, sao chép và dán mã sau vào cuối tệp.

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


Trong tác nhân, trình này tạo một trình phân tích cú pháp đầu ra JSON được thiết kế riêng để hiểu và định cấu trúc đầu ra của LLM. Hàm này sử dụng mô hình QuizQuestion mà chúng ta đã xác định trước đó để đảm bảo đầu ra được phân tích cú pháp tuân theo đúng định dạng (câu hỏi, lựa chọn và câu trả lời).

👉Thực thi các lệnh sau trong dòng lệnh để thiết lập môi trường ảo, cài đặt phần phụ thuộc và khởi động tác nhân:

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

Sử dụng tính năng xem trước trên web của Cloud Shell để truy cập vào ứng dụng đang chạy. Nhấp vào đường liên kết "Bài kiểm tra" trong thanh điều hướng trên cùng hoặc trong thẻ trên trang chỉ mục. Bạn sẽ thấy 3 bài kiểm tra được tạo ngẫu nhiên hiển thị cho học viên. Các bài kiểm tra này dựa trên kế hoạch giảng dạy và thể hiện sức mạnh của hệ thống tạo bài kiểm tra dựa trên AI của chúng tôi.

Đố vui

Để dừng quy trình đang chạy cục bộ, hãy nhấn Ctrl+C trong thiết bị đầu cuối.

Tính năng giải thích của Gemini 2

Được rồi, chúng ta đã có bài kiểm tra, đây là một khởi đầu tuyệt vời! Nhưng nếu học viên làm sai thì sao? Đó mới là nơi diễn ra quá trình học tập thực sự, phải không? Nếu chúng ta có thể giải thích lý do câu trả lời của họ không chính xác và cách tìm ra câu trả lời chính xác, thì họ có nhiều khả năng sẽ nhớ được câu trả lời đó. Hơn nữa, việc này giúp giải tỏa mọi nhầm lẫn và tăng sự tự tin của họ.

Đó là lý do chúng tôi sẽ sử dụng mô hình "tư duy" của Gemini 2! Hãy coi đây là cách để AI có thêm chút thời gian suy nghĩ trước khi giải thích. Điều này giúp hệ thống đưa ra phản hồi chi tiết và chính xác hơn.

Chúng tôi muốn xem liệu tính năng này có thể giúp học viên bằng cách hỗ trợ, trả lời và giải thích chi tiết hay không. Để kiểm thử, chúng ta sẽ bắt đầu với một chủ đề nổi tiếng là khó hiểu, đó là Giải tích.

Tổng quan

👉Trước tiên, hãy chuyển đến Trình soạn thảo mã trên đám mây, trong answer.py bên trong thư mục portal, hãy thay thế

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

bằng đoạn mã sau:

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)

Đây là một ứng dụng chuỗi ngôn ngữ rất đơn giản, trong đó khởi chạy mô hình Gemini 2 Flash. Chúng ta sẽ hướng dẫn ứng dụng này đóng vai trò là một giáo viên hữu ích và đưa ra nội dung giải thích

👉Thực thi lệnh sau trong dòng lệnh:

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

Bạn sẽ thấy kết quả tương tự như ví dụ được cung cấp trong hướng dẫn ban đầu. Mô hình hiện tại có thể không giải thích rõ ràng.

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!

Trong tệp answer.py, hãy thay thế model_name từ gemini-2.0-flash-001 thành gemini-2.0-flash-thinking-exp-01-21 trong hàm answer_thinking.

Điều này sẽ thay đổi LLM để đưa ra nhiều lý do hơn, giúp mô hình này tạo ra các nội dung giải thích tốt hơn. Và chạy lại.

👉Chạy để kiểm thử mô hình tư duy mới:

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

Sau đây là ví dụ về phản hồi của mô hình tư duy chi tiết và kỹ lưỡng hơn nhiều, cung cấp giải thích từng bước về cách giải bài toán tích phân. Điều này cho thấy sức mạnh của các mô hình "tư duy" trong việc tạo ra các nội dung giải thích chất lượng cao. Bạn sẽ thấy kết quả xuất ra có dạng như sau:

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.

👉XOÁ mã kiểm thử sau đây khỏi 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)

👉Thực thi các lệnh sau trong dòng lệnh để thiết lập môi trường ảo, cài đặt phần phụ thuộc và khởi động tác nhân:

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

👉Sử dụng tính năng xem trước trên web của Cloud Shell để truy cập vào ứng dụng đang chạy. Nhấp vào đường liên kết "Bài kiểm tra", trả lời tất cả bài kiểm tra và đảm bảo ít nhất có một câu trả lời sai rồi nhấp vào gửi

câu trả lời suy nghĩ

Thay vì ngồi chờ phản hồi, hãy chuyển sang thiết bị đầu cuối của Trình chỉnh sửa trên đám mây. Bạn có thể quan sát tiến trình và mọi thông báo lỗi hoặc đầu ra do hàm tạo ra trong thiết bị đầu cuối của trình mô phỏng. 😁

Để dừng quy trình đang chạy cục bộ, hãy nhấn Ctrl+C trong thiết bị đầu cuối.

11. Điều phối các tác nhân bằng Eventarc

Cho đến nay, cổng thông tin dành cho học viên đã tạo bài kiểm tra dựa trên một bộ kế hoạch giảng dạy mặc định. Điều này rất hữu ích, nhưng đồng thời cũng có nghĩa là tác nhân lập kế hoạch và tác nhân bài kiểm tra của cổng thông tin không thực sự giao tiếp với nhau. Bạn còn nhớ cách chúng ta thêm tính năng mà trong đó tác nhân lập kế hoạch phát hành kế hoạch giảng dạy mới tạo của mình lên một chủ đề Pub/Sub không? Bây giờ, đã đến lúc kết nối ứng dụng đó với tác nhân cổng thông tin của chúng ta!

Tổng quan

Chúng ta muốn cổng thông tin tự động cập nhật nội dung bài kiểm tra mỗi khi có một kế hoạch giảng dạy mới được tạo. Để làm việc đó, chúng ta sẽ tạo một điểm cuối trong cổng thông tin có thể nhận các kế hoạch mới này.

👉Trong ngăn Explorer (Trình khám phá) của Cloud Code Editor, hãy chuyển đến thư mục portal. Mở tệp app.py để chỉnh sửa. Thêm mã sau vào giữa ## Add your code here (Thêm mã của bạn tại đây):

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

Tạo lại và triển khai lên Cloud Run

OK! Bạn sẽ cần cập nhật và triển khai lại cả trình lập kế hoạch và tác nhân cổng thông tin của chúng tôi lên Cloud Run. Điều này đảm bảo rằng các ứng dụng đó có mã mới nhất và được định cấu hình để giao tiếp thông qua các sự kiện.

Tổng quan về việc triển khai

👉Tiếp theo, chúng ta sẽ tạo lại và đẩy hình ảnh tác nhân planner (trình lập kế hoạch) trở lại trong quá trình chạy dòng lệnh:

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

👉Chúng ta sẽ làm tương tự, tạo và đẩy hình ảnh tác nhân cổng:

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

Trong Artifact Registry (Cơ sở lưu trữ cấu phần phần mềm), bạn sẽ thấy cả hình ảnh vùng chứa aidemy-planneraidemy-portal được liệt kê.

Kho lưu trữ vùng chứa

👉Trở lại trong dòng lệnh, hãy chạy lệnh sau để cập nhật hình ảnh Cloud Run cho tác nhân lập kế hoạch:

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

Bạn sẽ thấy kết quả xuất ra có dạng như sau:

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

Ghi lại URL dịch vụ; đây là đường liên kết đến tác nhân lập kế hoạch đã triển khai.

👉Chạy lệnh này để tạo phiên bản Cloud Run cho tác nhân cổng thông tin

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}

Bạn sẽ thấy kết quả xuất ra có dạng như sau:

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

Ghi lại URL của dịch vụ; đây là đường liên kết đến cổng thông tin dành cho học viên mà bạn đã triển khai.

Tạo điều kiện kích hoạt Eventarc

Nhưng đây là câu hỏi lớn: điểm cuối này được thông báo như thế nào khi có một kế hoạch mới đang chờ trong chủ đề Pub/Sub? Đó là lúc Eventarc xuất hiện để giải cứu!

Eventarc đóng vai trò là cầu nối, theo dõi các sự kiện cụ thể (chẳng hạn như một tin nhắn mới đến trong chủ đề Pub/Sub của chúng ta) và tự động kích hoạt các hành động để phản hồi. Trong trường hợp này, trình phát hiện sẽ phát hiện thời điểm một kế hoạch giảng dạy mới được xuất bản, sau đó gửi tín hiệu đến điểm cuối của cổng thông tin để cho cổng thông tin biết đã đến lúc cập nhật.

Khi Eventarc xử lý hoạt động giao tiếp do sự kiện điều khiển, chúng ta có thể kết nối liền mạch tác nhân lập kế hoạch và tác nhân cổng thông tin, tạo ra một hệ thống học tập thực sự linh động và thích ứng. Giống như có một ứng dụng nhắn tin thông minh tự động phân phối kế hoạch bài học mới nhất đến đúng nơi!

👉Trong bảng điều khiển, hãy chuyển đến Eventarc.

👉Nhấp vào nút "+ TẠO TRÌNH KÍCH HOẠT".

Định cấu hình điều kiện kích hoạt (Kiến thức cơ bản):

  • Tên điều kiện kích hoạt: plan-topic-trigger
  • Loại điều kiện kích hoạt: Nguồn của Google.
  • Nhà cung cấp sự kiện: Cloud Pub/Sub
  • Loại sự kiện: google.cloud.pubsub.topic.v1.messagePublished
  • Vùng: us-central1.
  • Chủ đề Cloud Pub/Sub : chọn plan
  • CẤP cho tài khoản dịch vụ vai trò roles/iam.serviceAccountTokenCreator
  • Đích đến của sự kiện: Cloud Run
  • Dịch vụ Cloud Run: aidemy-portal
  • Đường dẫn URL của dịch vụ: /new_teaching_plan
  • Bỏ qua thông báo (Quyền bị từ chối trên "locations/me-central2" (hoặc có thể không tồn tại).)

Nhấp vào "Tạo".

Trang Trình kích hoạt Eventarc sẽ làm mới và bạn sẽ thấy trình kích hoạt mới tạo của mình được liệt kê trong bảng.

👉Bây giờ, hãy truy cập vào trình lập kế hoạch và yêu cầu một kế hoạch giảng dạy mới. Lần này, hãy thử năm 5, tiêu đề science với yêu cầu Thêm-không atoms

Chạy lệnh này trong thiết bị đầu cuối nếu bạn quên vị trí của tác nhân lập kế hoạch

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

Sau đó, hãy đợi một hoặc hai phút. Xin nhắc lại rằng độ trễ này xuất hiện do giới hạn thanh toán của lớp học này. Trong điều kiện bình thường, sẽ không có độ trễ.

Cuối cùng, hãy truy cập vào cổng thông tin dành cho học viên. Bạn sẽ thấy các bài kiểm tra đã được cập nhật và hiện đã phù hợp với kế hoạch giảng dạy mới mà bạn vừa tạo! Điều này minh hoạ việc tích hợp thành công Eventarc trong hệ thống Aidemy!

Chạy lệnh này trong thiết bị đầu cuối nếu bạn quên vị trí của tác nhân cổng thông tin

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

Aidemy-celebrate

Xin chúc mừng! Bạn đã xây dựng thành công một hệ thống nhiều tác nhân trên Google Cloud, tận dụng kiến trúc do sự kiện điều khiển để tăng khả năng mở rộng và linh hoạt! Bạn đã có một nền tảng vững chắc, nhưng vẫn còn nhiều điều khác để khám phá. Để tìm hiểu sâu hơn về các lợi ích thực sự của cấu trúc này, hãy khám phá sức mạnh của API Trực tiếp đa phương thức của Gemini 2 và tìm hiểu cách triển khai hoạt động điều phối một đường dẫn bằng LangGraph. Bạn có thể tiếp tục đọc hai chương tiếp theo.

12. KHÔNG BẮT BUỘC: Bản tóm tắt bằng âm thanh do Gemini tạo

Gemini có thể hiểu và xử lý thông tin từ nhiều nguồn, chẳng hạn như văn bản, hình ảnh và thậm chí cả âm thanh, mở ra một loạt khả năng mới để học tập và sáng tạo nội dung. Khả năng "nhìn thấy", "nghe thấy" và "đọc" của Gemini thực sự mở ra trải nghiệm sáng tạo và hấp dẫn cho người dùng.

Ngoài việc tạo hình ảnh hoặc văn bản, một bước quan trọng khác trong quá trình học tập là tóm tắt và tổng kết hiệu quả. Hãy thử nghĩ xem: bạn có thường nhớ lời bài hát dễ dàng hơn so với những gì bạn đọc trong sách giáo khoa không? Âm thanh có thể tạo ấn tượng khó quên! Đó là lý do chúng tôi sẽ tận dụng các tính năng đa phương thức của Gemini để tạo bản tóm tắt bằng âm thanh về kế hoạch giảng dạy. Điều này sẽ giúp học viên có một cách thuận tiện và hấp dẫn để ôn tập tài liệu, có thể tăng khả năng ghi nhớ và hiểu biết thông qua việc học bằng thính giác.

Tổng quan về Live API

Chúng ta cần một nơi để lưu trữ các tệp âm thanh được tạo. Cloud Storage cung cấp một giải pháp đáng tin cậy và có thể mở rộng.

👉Chuyển đến phần Bộ nhớ trong bảng điều khiển. Nhấp vào "Bộ chứa" trong trình đơn bên trái. Nhấp vào nút "+ TẠO" ở trên cùng.

👉Định cấu hình bộ chứa:

  • tên bộ chứa: aidemy-recap-<UNIQUE_NAME> LƯU Ý QUAN TRỌNG: Hãy đảm bảo bạn xác định một tên bộ chứa duy nhất bắt đầu bằng "aidemy-recap-". Tên duy nhất này rất quan trọng để tránh xung đột tên khi tạo bộ chứa trên Google Cloud Storage.
  • region: us-central1.
  • Lớp bộ nhớ: "Chuẩn". Chuẩn phù hợp với dữ liệu được truy cập thường xuyên.
  • Kiểm soát quyền truy cập: Giữ lựa chọn mặc định "Kiểm soát quyền truy cập đồng nhất". Điều này giúp cung cấp quyền kiểm soát truy cập nhất quán ở cấp bộ chứa.
  • Tuỳ chọn nâng cao: Đối với lớp học này, bạn thường chỉ cần các chế độ cài đặt mặc định. Nhấp vào nút CREATE (TẠO) để tạo bộ chứa.

Bạn có thể thấy một cửa sổ bật lên về biện pháp phòng tránh truy cập công khai. Đánh dấu vào hộp rồi nhấp vào Confirm.

Bây giờ, bạn sẽ thấy bộ chứa mới tạo trong danh sách Bộ chứa. Hãy nhớ tên bộ chứa vì bạn sẽ cần đến tên này sau.

👉Trong dòng lệnh của Cloud Code Editor, hãy chạy các lệnh sau để cấp cho tài khoản dịch vụ quyền truy cập vào bộ chứa:

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"

👉Trong Cloud Code Editor, hãy mở audio.py bên trong thư mục course. Dán mã sau vào cuối tệp:

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))
  • Kết nối truyền trực tuyến: Trước tiên, một kết nối liên tục được thiết lập với điểm cuối API Trực tiếp. Không giống như lệnh gọi API tiêu chuẩn, trong đó bạn gửi một yêu cầu và nhận được phản hồi, kết nối này vẫn mở để trao đổi dữ liệu liên tục.
  • Cấu hình đa phương thức: Sử dụng cấu hình để chỉ định loại đầu ra bạn muốn (trong trường hợp này là âm thanh) và bạn thậm chí có thể chỉ định những thông số mà bạn muốn sử dụng (ví dụ: lựa chọn giọng nói, mã hoá âm thanh)
  • Xử lý không đồng bộ: API này hoạt động không đồng bộ, nghĩa là không chặn luồng chính trong khi chờ quá trình tạo âm thanh hoàn tất. Bằng cách xử lý dữ liệu theo thời gian thực và gửi đầu ra theo từng phần, công cụ này mang lại trải nghiệm gần như tức thì.

Bây giờ, câu hỏi chính là: khi nào quá trình tạo âm thanh này sẽ chạy? Lý tưởng nhất là bản tóm tắt bằng âm thanh sẽ xuất hiện ngay khi bạn tạo một kế hoạch giảng dạy mới. Vì đã triển khai cấu trúc do sự kiện điều khiển bằng cách phát hành kế hoạch giảng dạy đến một chủ đề Pub/Sub, nên chúng ta chỉ cần đăng ký chủ đề đó.

Tuy nhiên, chúng tôi không thường xuyên tạo kế hoạch giảng dạy mới. Việc liên tục chạy và chờ các kế hoạch mới cho một tác nhân sẽ không hiệu quả. Đó là lý do bạn nên triển khai logic tạo âm thanh này dưới dạng Hàm chạy trên đám mây.

Bằng cách triển khai dưới dạng một hàm, hàm này sẽ ở trạng thái rảnh cho đến khi một thông báo mới được phát hành cho chủ đề Pub/Sub. Khi điều đó xảy ra, hàm này sẽ tự động kích hoạt, tạo bản tóm tắt âm thanh và lưu trữ các bản tóm tắt đó trong bộ chứa của chúng ta.

👉Trong thư mục course của tệp main.py, tệp này xác định Hàm chạy trên đám mây sẽ được kích hoạt khi có kế hoạch giảng dạy mới. Ứng dụng này nhận kế hoạch và bắt đầu tạo bản tóm tắt âm thanh. Thêm đoạn mã sau vào cuối tệp.

@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: Phương thức trang trí này đánh dấu hàm là Hàm Cloud Run sẽ được CloudEvents kích hoạt.

Kiểm thử cục bộ

👉Chúng ta sẽ chạy ứng dụng này trong môi trường ảo và cài đặt các thư viện Python cần thiết cho hàm Cloud Run.

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

👉Trình mô phỏng Hàm Cloud Run cho phép chúng ta kiểm thử hàm trên máy trước khi triển khai hàm đó lên Google Cloud. Khởi động trình mô phỏng cục bộ bằng cách chạy:

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

👉Trong khi trình mô phỏng đang chạy, bạn có thể gửi CloudEvents kiểm thử đến trình mô phỏng để mô phỏng một kế hoạch giảng dạy mới đang được xuất bản. Trong một cửa sổ dòng lệnh mới:

Hai thiết bị đầu cuối

👉Chạy:

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

Thay vì ngồi chờ phản hồi, hãy chuyển sang một thiết bị đầu cuối Cloud Shell khác. Bạn có thể quan sát tiến trình và mọi thông báo lỗi hoặc đầu ra do hàm tạo ra trong thiết bị đầu cuối của trình mô phỏng. 😁

Quay lại thiết bị đầu cuối thứ 2, bạn sẽ thấy thiết bị này trả về OK.

👉Bạn sẽ xác minh Dữ liệu trong bộ chứa, chuyển đến Cloud Storage (Bộ nhớ trên đám mây) rồi chọn thẻ "Bộ chứa", sau đó chọn aidemy-recap-xxx

Bộ chứa

👉Trong thiết bị đầu cuối đang chạy trình mô phỏng, hãy nhập ctrl+c để thoát. Và đóng thiết bị đầu cuối thứ hai. Sau đó, hãy đóng cửa sổ dòng lệnh thứ hai và chạy deactivate để thoát khỏi môi trường ảo.

deactivate

Triển khai lên Google Cloud

Tổng quan về việc triển khai 👉Sau khi kiểm thử trên máy, đã đến lúc triển khai tác nhân của khoá học lên Google Cloud. Trong dòng lệnh, hãy chạy các lệnh sau:

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

Xác minh quá trình triển khai bằng cách chuyển đến phần Cloud Run trong Google Cloud Console.Bạn sẽ thấy một dịch vụ mới có tên là courses-agent được liệt kê.

Danh sách Cloud Run

Để kiểm tra cấu hình điều kiện kích hoạt, hãy nhấp vào dịch vụ courses-agent để xem thông tin chi tiết. Chuyển đến thẻ "TRIỆU TIẾP".

Bạn sẽ thấy một điều kiện kích hoạt được định cấu hình để nghe các thông báo được phát hành cho chủ đề kế hoạch.

Trình kích hoạt Cloud Run

Cuối cùng, hãy xem ứng dụng chạy từ đầu đến cuối.

👉Tiếp theo, chúng ta cần định cấu hình tác nhân cổng thông tin để tác nhân này biết nơi tìm thấy các tệp âm thanh đã tạo. Trong dòng lệnh, hãy chạy:

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

👉Hãy thử tạo một kế hoạch giảng dạy mới trong trang tác nhân lập kế hoạch. Quá trình khởi động có thể mất vài phút, đừng lo lắng, đây là một dịch vụ không có máy chủ. Lấy URL của tác nhân lập kế hoạch (nếu bạn không có URL này, hãy chạy lệnh này trong thiết bị đầu cuối):

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

Sau khi tạo kế hoạch mới, hãy đợi 2-3 phút để tạo âm thanh. Xin lưu ý rằng quá trình này sẽ mất thêm vài phút do tài khoản phòng thí nghiệm này có giới hạn thanh toán.

Bạn có thể theo dõi xem hàm courses-agent đã nhận được kế hoạch giảng dạy hay chưa bằng cách kiểm tra thẻ "TRIGGER" (KÍCH THÍCH) của hàm. Hãy làm mới trang định kỳ; cuối cùng, bạn sẽ thấy hàm đã được gọi. Nếu hàm chưa được gọi sau hơn 2 phút, bạn có thể thử tạo lại kế hoạch giảng dạy. Tuy nhiên, hãy tránh tạo kế hoạch liên tục và nhanh chóng, vì mỗi kế hoạch được tạo sẽ được thực thể xử lý và sử dụng tuần tự, có thể tạo ra một danh sách tồn đọng.

Kích hoạt tính năng quan sát

👉Truy cập vào cổng thông tin rồi nhấp vào "Khoá học". Bạn sẽ thấy 3 thẻ, mỗi thẻ hiển thị một bản tóm tắt âm thanh. Cách tìm URL của tác nhân cổng thông tin:

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

Nhấp vào "phát" trên mỗi khoá học để đảm bảo bản tóm tắt âm thanh phù hợp với kế hoạch giảng dạy mà bạn vừa tạo! Khoá học trên Cổng thông tin

Thoát môi trường ảo.

deactivate

13. KHÔNG BẮT BUỘC: Cộng tác dựa trên vai trò bằng Gemini và DeepSeek

Việc có nhiều quan điểm là vô giá, đặc biệt là khi tạo bài tập hấp dẫn và chu đáo. Bây giờ, chúng ta sẽ xây dựng một hệ thống đa tác nhân tận dụng hai mô hình khác nhau với vai trò riêng biệt để tạo bài tập: một mô hình thúc đẩy hoạt động cộng tác và mô hình còn lại khuyến khích tự học. Chúng ta sẽ sử dụng cấu trúc "một lần chụp", trong đó quy trình làm việc tuân theo một tuyến đường cố định.

Trình tạo bài tập Gemini

Tổng quan về Gemini Chúng ta sẽ bắt đầu bằng cách thiết lập hàm Gemini để tạo bài tập tập trung vào hoạt động cộng tác. Chỉnh sửa tệp gemini.py nằm trong thư mục assignment.

👉Dán mã sau vào cuối tệp gemini.py:

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

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

        For each week, provide the following:

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

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

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

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


import unittest

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

        updated_state = gen_assignment_gemini(initial_state)

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


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

Mô hình này sử dụng mô hình Gemini để tạo bài tập.

Chúng ta đã sẵn sàng kiểm thử Gemini Agent.

👉Chạy các lệnh sau trong dòng lệnh để thiết lập môi trường:

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

👉Bạn có thể chạy để kiểm thử:

python gemini.py

Bạn sẽ thấy một bài tập có nhiều bài tập nhóm hơn trong kết quả. Kiểm thử xác nhận ở cuối cũng sẽ xuất ra kết quả.

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

Dừng bằng ctl+c và dọn dẹp mã kiểm thử. XOÁ mã sau khỏi 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()

Định cấu hình Trình tạo bài tập DeepSeek

Mặc dù các nền tảng AI dựa trên đám mây rất tiện lợi, nhưng việc tự lưu trữ LLM có thể rất quan trọng để bảo vệ quyền riêng tư của dữ liệu và đảm bảo chủ quyền dữ liệu. Chúng ta sẽ triển khai mô hình DeepSeek nhỏ nhất (1,5 tỷ tham số) trên một phiên bản Compute Engine trên đám mây. Có nhiều cách khác như lưu trữ trên nền tảng Vertex AI của Google hoặc lưu trữ trên phiên bản GKE của bạn, nhưng vì đây chỉ là một hội thảo về tác nhân AI và tôi không muốn giữ bạn ở đây mãi, nên chúng ta hãy sử dụng cách đơn giản nhất. Tuy nhiên, nếu bạn quan tâm và muốn tìm hiểu các tuỳ chọn khác, hãy xem tệp deepseek-vertexai.py trong thư mục bài tập. Tệp này cung cấp mã mẫu về cách tương tác với các mô hình được triển khai trên VertexAI.

Tổng quan về Deepseek

👉Chạy lệnh này trong dòng lệnh để tạo nền tảng LLM tự lưu trữ Ollama:

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

Cách xác minh rằng phiên bản Compute Engine đang chạy:

Chuyển đến Compute Engine > "Phiên bản máy ảo" trong Google Cloud Console. Bạn sẽ thấy ollama-instance được liệt kê cùng với dấu kiểm màu xanh lục cho biết rằng ứng dụng này đang chạy. Nếu bạn không thấy, hãy đảm bảo rằng vùng là us-central1. Nếu không, bạn có thể cần phải tìm kiếm.

Danh sách Compute Engine

👉Chúng ta sẽ cài đặt và kiểm thử mô hình DeepSeek nhỏ nhất. Quay lại Trình chỉnh sửa Cloud Shell, trong một thiết bị đầu cuối Mới, hãy chạy lệnh sau để ssh vào thực thể GCE.

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

Sau khi thiết lập kết nối SSH, bạn có thể được nhắc như sau:

"Bạn có muốn tiếp tục (Y/n)?"

Bạn chỉ cần nhập Y(không phân biệt chữ hoa chữ thường) rồi nhấn Enter để tiếp tục.

Tiếp theo, bạn có thể được yêu cầu tạo cụm mật khẩu cho khoá SSH. Nếu không muốn sử dụng cụm mật khẩu, bạn chỉ cần nhấn Enter hai lần để chấp nhận mặc định (không có cụm mật khẩu).

👉Bây giờ, bạn đang ở trong máy ảo, hãy lấy mô hình DeepSeek R1 nhỏ nhất và kiểm thử xem mô hình đó có hoạt động không?

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

👉Rời khỏi phiên bản GCE, nhập nội dung sau trong thiết bị đầu cuối ssh:

exit

Đóng cửa sổ dòng lệnh mới, quay lại cửa sổ dòng lệnh ban đầu.

👉Đừng quên thiết lập chính sách mạng để các dịch vụ khác có thể truy cập vào LLM. Vui lòng hạn chế quyền truy cập vào thực thể nếu bạn muốn thực hiện việc này cho môi trường sản xuất, hãy triển khai tính năng đăng nhập bảo mật cho dịch vụ hoặc hạn chế quyền truy cập IP. Chạy:

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

👉Để xác minh xem chính sách tường lửa của bạn có hoạt động chính xác không, hãy thử chạy:

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

Tiếp theo, chúng ta sẽ xử lý hàm Deepseek trong tác nhân bài tập để tạo bài tập có trọng tâm công việc riêng lẻ.

👉Chỉnh sửa deepseek.py trong thư mục assignment, thêm đoạn mã sau vào cuối

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

👉hãy kiểm thử bằng cách chạy:

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

Bạn sẽ thấy một bài tập có nhiều bài tập tự học hơn.

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

👉Dừng ctl+c và dọn dẹp mã kiểm thử. XOÁ mã sau khỏi 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()

Bây giờ, chúng ta sẽ sử dụng cùng một mô hình gemini để kết hợp cả hai bài tập vào một bài tập mới. Chỉnh sửa tệp gemini.py nằm trong thư mục assignment.

👉Dán mã sau vào cuối tệp gemini.py:

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

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

Để kết hợp các điểm mạnh của cả hai mô hình, chúng ta sẽ điều phối một quy trình công việc đã xác định bằng LangGraph. Quy trình công việc này bao gồm 3 bước: bước đầu tiên, mô hình Gemini tạo một bài tập tập trung vào hoạt động cộng tác; bước thứ hai, mô hình DeepSeek tạo một bài tập nhấn mạnh vào hoạt động cá nhân; cuối cùng, Gemini tổng hợp hai bài tập này thành một bài tập toàn diện. Vì chúng ta xác định trước trình tự các bước mà không cần LLM đưa ra quyết định, nên đây là một quá trình điều phối một đường dẫn do người dùng xác định.

Tổng quan về tính năng kết hợp Langraph

👉Dán mã sau vào cuối tệp main.py trong thư mục assignment:

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

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

    return state["final_assignment"]



import unittest

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


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

👉Để kiểm thử ban đầu hàm create_assignment và xác nhận rằng quy trình kết hợp Gemini và DeepSeek đang hoạt động, hãy chạy lệnh sau:

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

Bạn sẽ thấy một nội dung kết hợp cả hai mô hình với quan điểm riêng của học viên đối với việc học tập và cũng như đối với các bài tập nhóm của học viên.

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

👉Dừng ctl+c và dọn dẹp mã kiểm thử. XOÁ mã sau khỏi 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()

Tạo Assignment.png

Để quá trình tạo bài tập trở nên tự động và thích ứng với các kế hoạch giảng dạy mới, chúng ta sẽ tận dụng cấu trúc dựa trên sự kiện hiện có. Đoạn mã sau đây xác định một Hàm Cloud Run (generate_assignment) sẽ được kích hoạt bất cứ khi nào một kế hoạch giảng dạy mới được xuất bản cho chủ đề Pub/Sub "plan".

👉Thêm mã sau vào cuối main.py:

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

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

        assignment = create_assignment(teaching_plan)

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

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

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

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

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

Kiểm thử cục bộ

Trước khi triển khai trên Google Cloud, bạn nên kiểm thử Hàm Cloud Run trên máy. Điều này cho phép lặp lại nhanh hơn và gỡ lỗi dễ dàng hơn.

Trước tiên, hãy tạo một bộ chứa Cloud Storage để lưu trữ các tệp bài tập đã tạo và cấp cho tài khoản dịch vụ quyền truy cập vào bộ chứa đó. Chạy các lệnh sau trong dòng lệnh:

👉LƯU Ý QUAN TRỌNG: Hãy đảm bảo bạn xác định tên ASSIGNMENT_BUCKET duy nhất bắt đầu bằng "aidemy-assignment-". Tên duy nhất này rất quan trọng để tránh xung đột tên khi tạo bộ chứa trên Cloud Storage. (Thay thế <YOUR_NAME> bằng một từ ngẫu nhiên)

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

👉Rồi chạy:

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"

👉Bây giờ, hãy khởi động trình mô phỏng Hàm chạy trên đám mây:

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

👉Trong khi trình mô phỏng đang chạy trong một cửa sổ dòng lệnh, hãy mở một cửa sổ dòng lệnh thứ hai trong Cloud Shell. Trong thiết bị đầu cuối thứ hai này, hãy gửi một CloudEvent kiểm thử đến trình mô phỏng để mô phỏng một kế hoạch giảng dạy mới đang được xuất bản:

Hai thiết bị đầu cuối

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

Thay vì ngồi chờ phản hồi, hãy chuyển sang một thiết bị đầu cuối Cloud Shell khác. Bạn có thể quan sát tiến trình và mọi thông báo lỗi hoặc đầu ra do hàm tạo ra trong thiết bị đầu cuối của trình mô phỏng. 😁

Thao tác này sẽ trả về OK.

Để xác nhận rằng bài tập đã được tạo và lưu trữ thành công, hãy truy cập vào Google Cloud Console rồi chuyển đến Bộ nhớ > "Bộ nhớ trên đám mây". Chọn bộ chứa aidemy-assignment mà bạn đã tạo. Bạn sẽ thấy một tệp văn bản có tên assignment-{random number}.txt trong bộ chứa. Nhấp vào tệp để tải xuống và xác minh nội dung của tệp. Thao tác này xác minh rằng tệp mới chứa bài tập mới được tạo.

12-01-assignment-bucket

👉Trong thiết bị đầu cuối đang chạy trình mô phỏng, hãy nhập ctrl+c để thoát. Và đóng thiết bị đầu cuối thứ hai. 👉Ngoài ra, trong dòng lệnh đang chạy trình mô phỏng, hãy thoát khỏi môi trường ảo.

deactivate

Tổng quan về việc triển khai

👉Tiếp theo, chúng ta sẽ triển khai tác nhân chỉ định vào đám mây

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 

Xác minh quá trình triển khai bằng cách truy cập vào Google Cloud Console, chuyển đến Cloud Run.Bạn sẽ thấy một dịch vụ mới có tên là courses-agent được liệt kê. 12-03-function-list

Giờ đây, khi quy trình tạo bài tập đã được triển khai, kiểm thử và triển khai, chúng ta có thể chuyển sang bước tiếp theo: cung cấp quyền truy cập vào các bài tập này trong cổng thông tin dành cho học viên.

14. KHÔNG BẮT BUỘC: Cộng tác dựa trên vai trò với Gemini và DeepSeek – Tiếp tục.

Tạo trang web động

Để nâng cao và tăng tính hấp dẫn cho cổng thông tin dành cho học viên, chúng tôi sẽ triển khai tính năng tạo HTML động cho các trang bài tập. Mục tiêu là tự động cập nhật trang web bằng một thiết kế mới mẻ, hấp dẫn về mặt hình ảnh mỗi khi có bài tập mới được tạo. Điều này tận dụng khả năng lập trình của LLM để tạo ra trải nghiệm người dùng linh động và thú vị hơn.

14-01-generate-html

👉Trong Trình chỉnh sửa Cloud Shell, hãy chỉnh sửa tệp render.py trong thư mục portal, thay thế

def render_assignment_page():
    return ""

bằng đoạn mã sau:

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

Ứng dụng này sử dụng mô hình Gemini để tạo HTML động cho bài tập. Công cụ này lấy nội dung bài tập làm dữ liệu đầu vào và sử dụng câu lệnh để hướng dẫn Gemini tạo một trang HTML bắt mắt với kiểu sáng tạo.

Tiếp theo, chúng ta sẽ tạo một điểm cuối sẽ được kích hoạt mỗi khi một tài liệu mới được thêm vào bộ chứa bài tập:

👉Trong thư mục cổng thông tin, hãy chỉnh sửa tệp app.py và thêm mã sau trong ## Add your code here" comments, SAU hàm new_teaching_plan:

## Add your code here

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

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

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

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

        render_assignment_page(content)

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

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


## Add your code here

Khi được kích hoạt, hàm này sẽ truy xuất tên tệp và tên bộ chứa từ dữ liệu yêu cầu, tải nội dung bài tập xuống từ Cloud Storage và gọi hàm render_assignment_page để tạo HTML.

👉Chúng ta sẽ tiếp tục chạy ứng dụng này trên máy:

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

👉Trên trình đơn "Xem trước trên web" ở đầu cửa sổ Cloud Shell, hãy chọn "Xem trước trên cổng 8080". Thao tác này sẽ mở ứng dụng của bạn trong một thẻ trình duyệt mới. Chuyển đến đường liên kết Bài tập trong thanh điều hướng. Tại thời điểm này, bạn sẽ thấy một trang trống. Đây là hành vi dự kiến vì chúng ta chưa thiết lập cầu nối liên lạc giữa tác nhân chỉ định và cổng thông tin để điền nội dung một cách linh động.

14-02-deployment-overview

👉Để kết hợp các thay đổi này và triển khai mã đã cập nhật, hãy tạo lại và đẩy hình ảnh tác nhân cổng thông tin:

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

👉Sau khi đẩy hình ảnh mới, hãy triển khai lại dịch vụ Cloud Run. Chạy tập lệnh sau để thực thi bản cập nhật Cloud Run:

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

👉Bây giờ, chúng ta sẽ triển khai một trình kích hoạt Eventarc để theo dõi mọi đối tượng mới được tạo (hoàn tất) trong bộ chứa bài tập. Điều kiện kích hoạt này sẽ tự động gọi điểm cuối /render_assignment trên dịch vụ cổng thông tin khi một tệp bài tập mới được tạo.

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"

Để xác minh rằng bạn đã tạo thành công điều kiện kích hoạt, hãy chuyển đến trang Điều kiện kích hoạt Eventarc trong Google Cloud Console. Bạn sẽ thấy portal-assignment-trigger được liệt kê trong bảng. Nhấp vào tên điều kiện kích hoạt để xem thông tin chi tiết. Điều kiện kích hoạt bài tập

Có thể mất đến 2-3 phút thì điều kiện kích hoạt mới mới hoạt động.

Để xem quá trình tạo lượt chỉ định động, hãy chạy lệnh sau để tìm URL của tác nhân lập kế hoạch (nếu bạn không có URL này):

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

Tìm URL của tác nhân cổng thông tin:

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

Trong tác nhân lập kế hoạch, hãy tạo một kế hoạch giảng dạy mới.

13-02-assignment

Sau vài phút (để quá trình tạo âm thanh, tạo bài tập và hiển thị HTML hoàn tất), hãy chuyển đến cổng thông tin dành cho học viên.

👉Nhấp vào đường liên kết "Bài tập" trong thanh điều hướng. Bạn sẽ thấy một bài tập mới tạo có HTML được tạo động. Mỗi khi một kế hoạch giảng dạy được tạo, kế hoạch đó phải là một bài tập động.

13-02-assignment

Chúc mừng bạn đã hoàn thành hệ thống nhiều tác nhân Aidemy! Bạn đã có được kinh nghiệm thực tế và thông tin chi tiết có giá trị về:

  • Lợi ích của hệ thống nhiều tác nhân, bao gồm cả tính mô-đun, khả năng mở rộng, chuyên môn hoá và đơn giản hoá việc bảo trì.
  • Tầm quan trọng của cấu trúc do sự kiện điều khiển để xây dựng các ứng dụng thích ứng và được ghép nối lỏng lẻo.
  • Sử dụng chiến lược LLM, so khớp mô hình phù hợp với tác vụ và tích hợp các mô hình đó với các công cụ để tạo ra tác động thực tế.
  • Các phương pháp phát triển gốc trên đám mây sử dụng các dịch vụ của Google Cloud để tạo ra các giải pháp có thể mở rộng và đáng tin cậy.
  • Tầm quan trọng của việc xem xét quyền riêng tư đối với dữ liệu và mô hình tự lưu trữ thay vì các giải pháp của nhà cung cấp.

Giờ đây, bạn đã có nền tảng vững chắc để xây dựng các ứng dụng tinh vi dựa trên AI trên Google Cloud!

15. Những thách thức và bước tiếp theo

Chúc mừng bạn đã xây dựng hệ thống nhiều tác nhân Aidemy! Bạn đã xây dựng một nền tảng vững chắc cho việc giáo dục bằng AI. Bây giờ, hãy cùng xem xét một số thách thức và các điểm cải tiến tiềm năng trong tương lai để mở rộng thêm chức năng của công cụ này và giải quyết các nhu cầu thực tế:

Học tập tương tác bằng tính năng Hỏi đáp trực tiếp:

  • Thách thức: Bạn có thể tận dụng Live API của Gemini 2 để tạo tính năng hỏi đáp theo thời gian thực cho học viên không? Hãy tưởng tượng một lớp học ảo, nơi học viên có thể đặt câu hỏi và nhận được câu trả lời tức thì nhờ công nghệ AI.

Tự động gửi và chấm bài tập:

  • Thử thách: Thiết kế và triển khai một hệ thống cho phép học viên nộp bài tập bằng phương thức kỹ thuật số và tự động chấm điểm bằng AI, với cơ chế phát hiện và ngăn chặn hành vi đạo văn. Thử thách này là cơ hội tuyệt vời để khám phá Tạo dữ liệu tăng cường truy xuất (RAG) nhằm nâng cao độ chính xác và độ tin cậy của quy trình chấm điểm và phát hiện đạo văn.

aidemy-climb

16. Dọn dẹp

Giờ đây, khi đã xây dựng và khám phá hệ thống nhiều tác nhân Aidemy, đã đến lúc dọn dẹp môi trường Google Cloud.

  1. Xoá dịch vụ Cloud Run
gcloud run services delete aidemy-planner --region=us-central1 --quiet
gcloud run services delete aidemy-portal --region=us-central1 --quiet
gcloud run services delete courses-agent --region=us-central1 --quiet
gcloud run services delete book-provider --region=us-central1 --quiet
gcloud run services delete assignment-agent --region=us-central1 --quiet
  1. Xoá trình kích hoạt Eventarc
gcloud eventarc triggers delete portal-assignment-trigger --location=us --quiet
gcloud eventarc triggers delete plan-topic-trigger --location=us-central1 --quiet
gcloud eventarc triggers delete portal-assignment-trigger --location=us-central1 --quiet
ASSIGNMENT_AGENT_TRIGGER=$(gcloud eventarc triggers list --project="$PROJECT_ID" --location=us-central1 --filter="name:assignment-agent" --format="value(name)")
COURSES_AGENT_TRIGGER=$(gcloud eventarc triggers list --project="$PROJECT_ID" --location=us-central1 --filter="name:courses-agent" --format="value(name)")
gcloud eventarc triggers delete $ASSIGNMENT_AGENT_TRIGGER --location=us-central1 --quiet
gcloud eventarc triggers delete $COURSES_AGENT_TRIGGER --location=us-central1 --quiet
  1. Xoá chủ đề Pub/Sub
gcloud pubsub topics delete plan --project="$PROJECT_ID" --quiet
  1. Xoá phiên bản Cloud SQL
gcloud sql instances delete aidemy --quiet
  1. Xoá kho lưu trữ Artifact Registry
gcloud artifacts repositories delete agent-repository --location=us-central1 --quiet
  1. Xoá khoá bí mật trong Trình quản lý bí mật
gcloud secrets delete db-user --quiet
gcloud secrets delete db-pass --quiet
gcloud secrets delete db-name --quiet
  1. Xoá phiên bản Compute Engine (nếu được tạo cho Deepseek)
gcloud compute instances delete ollama-instance --zone=us-central1-a --quiet
  1. Xoá quy tắc tường lửa cho thực thể Deepseek
gcloud compute firewall-rules delete allow-ollama-11434 --quiet
  1. Xoá bộ chứa trên Cloud Storage
export COURSE_BUCKET_NAME=$(gcloud storage buckets list --format="value(name)" | grep aidemy-recap)
export ASSIGNMENT_BUCKET=$(gcloud storage buckets list --format="value(name)" | grep aidemy-assignment)
gsutil rb gs://$COURSE_BUCKET_NAME
gsutil rb gs://$ASSIGNMENT_BUCKET

aidemy-broom