ساخت عامل‌های هوش مصنوعی پایدار با ADK و CloudSQL

۱. مقدمه

در این جلسه عملی، شما فراتر از چت‌بات‌های ساده و بدون وضعیت، یک Smart Cafe Concierge ایجاد خواهید کرد - یک عامل هوش مصنوعی که توسط Gemini پشتیبانی می‌شود و به عنوان یک باریستای دوستانه عمل می‌کند. این عامل سفارش‌های قهوه ردیابی شده در حالت جلسه را دریافت می‌کند، ترجیحات غذایی بلندمدت را در حالت کاربر-محور به خاطر می‌سپارد و همه چیز را در یک پایگاه داده Cloud SQL PostgreSQL ذخیره می‌کند. در پایان، عامل شما حتی پس از راه‌اندازی مجدد برنامه و شروع یک مکالمه کاملاً جدید، به خاطر می‌سپارد که شما به لاکتوز حساسیت دارید.

Here is the system architecture that we will build

a98bbd65ddedd29c.jpeg

پیش‌نیازها

  • A Google Cloud account with a trial billing account
  • Basic familiarity with Python
  • No prior experience with ADK, AI agents, or Cloud SQL required

آنچه یاد خواهید گرفت

  • Create an AI agent using Google's Agent Development Kit (ADK) with custom tools
  • Define tools that read and write session state via ToolContext
  • Distinguish between session-scoped state and user-scoped state ( user: prefix)
  • Provision a Cloud SQL PostgreSQL instance and connect to it from Cloud Shell
  • Migrate from local storage (which is default when you use adk web command) to DatabaseSessionService for persistent storage with dedicated database
  • Verify that agent memory persists across application restarts and across separate conversation sessions

آنچه نیاز دارید

  • A working computer and a reliable internet connection.
  • A browser, such as Chrome , to access Google Cloud Console
  • A curious mind and an eagerness to learn.

2. Set Up Your Environment

This step prepares your Cloud Shell environment and configures your Google Cloud project

Open Cloud Shell

Open Cloud Shell in your browser. Cloud Shell provides a pre-configured environment with all the tools you need for this codelab. Click Authorize when prompted to

Your interface should look similar to this

86307fac5da2f077.png

This will be our main interface, IDE on top, terminal on the bottom

Set up your working directory

Create your working directory. All code you write in this codelab lives here — separate from the reference repo:

# Create your working directory
mkdir -p ~/build-agent-adk-cloudsql

# Change cloudshell workspace and working directory into previously created dir
cloudshell workspace ~/build-agent-adk-cloudsql && cd ~/build-agent-adk-cloudsql

To spawn your terminal find View -> Terminal

ccc3214812750f1c.png

Set up your Google Cloud project and initial environment variables

Download the project setup script into your working directory:

curl -sL https://raw.githubusercontent.com/alphinside/cloud-trial-project-setup/main/setup_verify_trial_project.sh -o setup_verify_trial_project.sh

اسکریپت را اجرا کنید. این اسکریپت حساب کاربری شما را در نسخه آزمایشی تأیید می‌کند، یک پروژه جدید ایجاد می‌کند (یا یک پروژه موجود را تأیید می‌کند)، شناسه پروژه شما را در یک فایل .env در دایرکتوری فعلی ذخیره می‌کند و پروژه فعال را در ترمینال تنظیم می‌کند.

bash setup_verify_trial_project.sh && source .env

When running this, you will be prompted with suggested project ID name, you can press Enter to continue

54b615cd15f2a535.png

After waiting for a while, if you see this output in your console, then you are ready to go to next step e576b4c13d595156.png

The executed script do the following steps:

  1. Verify you have an active trial billing account
  2. Check for an existing project in .env (if any)
  3. Create a new project or reuse the existing one
  4. Link the trial billing account to your project
  5. Save the project ID to .env
  6. Set the project as the active gcloud project

Verify the project is set correctly by checking the yellow text next to your working directory in the Cloud Shell terminal prompt. It should display your project ID.

9e11ee21cd23405f.png

Enable required APIs

Enable the Google Cloud APIs needed for this codelab:

gcloud services enable \
  aiplatform.googleapis.com \
  sqladmin.googleapis.com \
  compute.googleapis.com
  • Vertex AI API ( aiplatform.googleapis.com ) — your agent uses Gemini models through Vertex AI.
  • Cloud SQL Admin API ( sqladmin.googleapis.com ) — you provision and manage a PostgreSQL instance for persistent storage.
  • Compute Engine API ( compute.googleapis.com ) — required for creating Cloud SQL instances.

Config Gemini and Cloud products region

Before continuing, let's also set up the necessary location/region configuration for the product that we interact with. Add the following configuration to our .env file

# This is for our Gemini endpoint
echo "GOOGLE_CLOUD_LOCATION=global" >> .env

# This is for our other Cloud products
echo "REGION=us-central1" >> .env

source .env

Let's continue to the next step

3. Set Up Cloud SQL

این مرحله یک نمونه Cloud SQL PostgreSQL را فراهم می‌کند و عامل شما را از حافظه داخلی به فضای ذخیره‌سازی تحت پشتیبانی پایگاه داده تغییر می‌دهد. ایجاد نمونه چند دقیقه طول می‌کشد، بنابراین ابتدا آن را شروع می‌کنید و ما می‌توانیم در حالی که منتظر پایان آن هستیم، بحث خود را در مورد موضوع بعدی ادامه دهیم.

Start the instance creation

Add the database password to your .env file and reload it, we will use cafe-agent-pwd-2025 as the password.

echo "DB_PASSWORD=cafe-agent-pwd-2025" >> .env
source .env

Run this command to create a Cloud SQL PostgreSQL instance. It takes a few minutes to complete — leave it running and continue to the next section .

gcloud sql instances create cafe-concierge-db \
  --database-version=POSTGRES_17 \
  --edition=ENTERPRISE \
  --region=${REGION} \
  --availability-type=ZONAL \
  --project=${GOOGLE_CLOUD_PROJECT} \
  --tier=db-f1-micro \
  --root-password=${DB_PASSWORD} \
  --quiet &

Several notes about the above command:

  • db-f1-micro is the smallest (and cheapest) Cloud SQL tier — sufficient for this codelab.
  • --root-password sets the password for the default postgres user.
  • The suffix & in the command runs the command in the background so you can keep working.

این فرآیند در پس‌زمینه اجرا خواهد شد، با این حال خروجی کنسول گاهی اوقات در ترمینال فعلی نمایش داده می‌شود. بیایید یک تب ترمینال جدید در Cloud Shell باز کنیم (روی نماد + کلیک کنیم) تا بتوانیم بیشتر متمرکز شویم.

b01e3fbd89f17332.png

Navigate to your working directory again and activate the project using the previous setup script.

cd ~/build-agent-adk-cloudsql
bash setup_verify_trial_project.sh && source .env

Then, let's continue to the next section

4. Build the Cafe Concierge Agent

This step creates the project structure for your ADK agent and defines a basic Cafe Concierge with a menu tool.

Initialize the Python project

This codelab uses uv , a fast Python package manager that handles virtual environments and dependencies in one tool. It's pre-installed in Cloud Shell.

Initialize a Python project and add the ADK as a dependency:

uv init
uv add google-adk==1.25.0 asyncpg

uv init creates a pyproject.toml and a virtual environment. uv add installs the dependency and records it in pyproject.toml .

ساختار پروژه عامل را مقداردهی اولیه کنید

ADK expects a specific folder layout: a directory named after your agent containing __init__.py , agent.py , and also .env inside the agent directory

ADK has built-in command to help you establish this quickly, run the following command

uv run adk create cafe_concierge \
    --model gemini-2.5-flash \
    --project ${GOOGLE_CLOUD_PROJECT} \
    --region ${GOOGLE_CLOUD_LOCATION}

This command will create an agent structure with gemini-2.5-flash as the brain. Your directory should now look like this:

build-agent-adk-cloudsql/
├── cafe_concierge/
│   ├── __init__.py
│   ├── agent.py
│   └── .env
├── pyproject.toml
├── .env      
├── .venv/
└── ...

Write the agent

Open cafe_concierge/agent.py in the Cloud Shell Editor

cloudshell edit cafe_concierge/agent.py

and overwrite the file with the following code

# cafe_concierge/agent.py
from google.adk.agents import LlmAgent
from google.adk.tools import ToolContext

CAFE_MENU = {
    "espresso": {
        "price": 3.50,
        "description": "Rich and bold single shot",
        "tags": ["vegan", "dairy-free", "gluten-free"],
    },
    "latte": {
        "price": 5.00,
        "description": "Espresso with steamed milk",
        "tags": ["gluten-free"],
    },
    "oat milk latte": {
        "price": 5.50,
        "description": "Espresso with steamed oat milk",
        "tags": ["vegan", "dairy-free", "gluten-free"],
    },
    "cappuccino": {
        "price": 4.50,
        "description": "Espresso with equal parts steamed milk and foam",
        "tags": ["gluten-free"],
    },
    "cold brew": {
        "price": 4.00,
        "description": "Slow-steeped for 12 hours, served over ice",
        "tags": ["vegan", "dairy-free", "gluten-free"],
    },
    "matcha latte": {
        "price": 5.50,
        "description": "Ceremonial grade matcha with steamed milk",
        "tags": ["gluten-free"],
    },
    "croissant": {
        "price": 3.00,
        "description": "Buttery, flaky French pastry",
        "tags": [],
    },
    "banana bread": {
        "price": 3.50,
        "description": "Homemade with walnuts",
        "tags": ["vegan"],
    },
}


def get_menu() -> dict:
    """Returns the full cafe menu with prices, descriptions, and dietary tags.

    Use this tool when the customer asks what's available, wants to see
    the menu, or asks about specific items.
    """
    return CAFE_MENU


root_agent = LlmAgent(
    name="cafe_concierge",
    model="gemini-2.5-flash",
    instruction="""You are a friendly and knowledgeable barista at "The Cloud Cafe".

Your job:
- Help customers browse the menu and answer questions about items.
- Take coffee and food orders.
- Remember and respect dietary preferences.

Be conversational, warm, and concise. If a customer mentions a dietary
restriction, acknowledge it and suggest suitable options from the menu.
""",
    tools=[get_menu],
)

This defines a basic agent with one tool: get_menu() . The agent can answer questions about the menu but can't track orders or remember preferences yet.

Verify the agent runs

Start the ADK dev UI from your working directory:

cd ~/build-agent-adk-cloudsql
uv run adk web

Open the URL shown in the terminal (typically http://localhost:8000 ) using Cloud Shell's Web Preview feature. Select cafe_concierge from the agent dropdown in the top-left corner.

Type the following text on the chat bar and verify the agent responds with menu items and prices.

What's on the menu?

376ee6b189657e7a.png

Stop the dev UI with Ctrl+C before proceeding.

5. Add Stateful Order Management

عامل می‌تواند منو را نشان دهد، اما نمی‌تواند سفارش‌ها را دریافت کند یا ترجیحات را به خاطر بسپارد. این مرحله چهار ابزار اضافه می‌کند که از سیستم وضعیت ADK برای ردیابی سفارش‌ها در یک مکالمه و ذخیره ترجیحات غذایی در مکالمات استفاده می‌کنند.

Understand session events and state

هر مکالمه ADK درون یک شیء Session قرار دارد. یک Session دو چیز متمایز را ردیابی می‌کند: رویدادها و وضعیت . درک این تفاوت، کلید ساخت عامل‌هایی است که چیزهای درست را به روش درست به خاطر می‌سپارند.

رویدادها ، گزارش زمانی هر اتفاقی هستند که در یک مکالمه رخ می‌دهد. هر پیام کاربر، هر پاسخ عامل، هر فراخوانی ابزار و مقدار بازگشتی آن - هر یک به عنوان یک Event ثبت شده و به لیست رویدادهای جلسه اضافه می‌شود. رویدادها تغییرناپذیر هستند: پس از ثبت، هرگز تغییر نمی‌کنند. رویدادها را به عنوان متن کامل یک مکالمه در نظر بگیرید.

وضعیت (State) یک دفترچه یادداشت کلید-مقدار است که عامل در طول مکالمه آن را می‌خواند و می‌نویسد. برخلاف رویدادها، وضعیت (state) قابل تغییر است - مقادیر با پیشرفت مکالمه تغییر می‌کنند. وضعیت جایی است که عامل داده‌های ساختاریافته‌ای را که برای اقدام روی آنها نیاز دارد، ذخیره می‌کند: سفارش فعلی، تنظیمات برگزیده مشتری، یک جمع کل در حال اجرا. وضعیت را به عنوان یادداشت‌های چسبانی در نظر بگیرید که عامل در کنار متن یادداشت نگه می‌دارد.

Here's how they relate:

cd9871699451867d.png

ابزارها از طریق ToolContext وضعیت را می‌خوانند و می‌نویسند - شیء‌ای که ADK به طور خودکار به هر تابع ابزاری که آن را به عنوان پارامتر اعلام می‌کند، تزریق می‌کند. شما خودتان آن را ایجاد نمی‌کنید. از طریق tool_context.state ، یک ابزار می‌تواند دفترچه یادداشت وضعیت جلسه را بخواند و بنویسد. ADK امضای تابع را بررسی می‌کند: پارامترهایی با نوع ToolContext تزریق می‌شوند، تمام پارامترهای دیگر توسط LLM بر اساس مکالمه پر می‌شوند.

وقتی ابزاری در tool_context.state می‌نویسد، ADK آن تغییر را به عنوان یک state_delta درون رویداد ثبت می‌کند. سپس SessionService دلتا را به وضعیت فعلی جلسه اعمال می‌کند. این بدان معناست که تغییرات وضعیت همیشه تا رویدادی که باعث آنها شده است قابل ردیابی هستند. این موضوع در مورد سایر انواع context مانند callback_context نیز صادق است.

Understand state prefixes

State keys use prefixes to control their scope:

پیشوند

دامنه

Survives restart? (with DB)

(none)

Current session only

بله

user:

تمام جلسات این کاربر

بله

app:

All sessions, all users

بله

temp:

فقط فراخوانی فعلی

خیر

در این آزمایشگاه کد، شما از دو مورد از این پیشوندها استفاده می‌کنید: کلیدهای بدون پیشوند برای داده‌های مربوط به جلسه (ترتیب فعلی - فقط مربوط به این مکالمه) و کلیدهای user: برای داده‌های مربوط به کاربر (ترجیحات غذایی - مربوط به تمام مکالمات این کاربر).

ابزارهای stateful را اضافه کنید

Open cafe_concierge/agent.py in the Cloud Shell Editor.

cloudshell edit cafe_concierge/agent.py

Then, add the following four functions above the root_agent definition:

# cafe_concierge/agent.py (add below get_menu, above root_agent)

def place_order(tool_context: ToolContext, items: list[str]) -> dict:
    """Places an order for the specified menu items.

    Use this tool when the customer confirms they want to order something.

    Args:
        tool_context: Provided automatically by ADK.
        items: A list of menu item names the customer wants to order.
    """
    valid_items = []
    invalid_items = []
    total = 0.0

    for item in items:
        item_lower = item.lower()
        if item_lower in CAFE_MENU:
            valid_items.append(item_lower)
            total += CAFE_MENU[item_lower]["price"]
        else:
            invalid_items.append(item)

    if not valid_items:
        return {"error": f"None of these items are on our menu: {invalid_items}"}

    order = {"items": valid_items, "total": round(total, 2)}
    tool_context.state["current_order"] = order

    result = {"order": order}
    if invalid_items:
        result["warning"] = f"These items are not on our menu: {invalid_items}"
    return result


def get_order_summary(tool_context: ToolContext) -> dict:
    """Returns the current order summary for this session.

    Use this tool when the customer asks about their current order,
    wants to review what they ordered, or asks for the total.

    Args:
        tool_context: Provided automatically by ADK.
    """
    order = tool_context.state.get("current_order")
    if order:
        return {"order": order}
    return {"message": "No order has been placed yet in this session."}


def set_dietary_preference(tool_context: ToolContext, preference: str) -> dict:
    """Saves a dietary preference that persists across all conversations.

    Use this tool when the customer mentions a dietary restriction or
    preference (e.g., "I'm vegan", "I'm lactose intolerant",
    "I have a nut allergy").

    Args:
        tool_context: Provided automatically by ADK.
        preference: The dietary preference to save (e.g., "vegan",
            "lactose intolerant", "nut allergy").
    """
    existing = tool_context.state.get("user:dietary_preferences", [])
    if not isinstance(existing, list):
        existing = []

    preference_lower = preference.lower().strip()
    if preference_lower not in existing:
        existing.append(preference_lower)

    tool_context.state["user:dietary_preferences"] = existing
    return {
        "saved": preference_lower,
        "all_preferences": existing,
    }


def get_dietary_preferences(tool_context: ToolContext) -> dict:
    """Retrieves the customer's saved dietary preferences.

    Use this tool when you need to check the customer's dietary
    restrictions before making recommendations.

    Args:
        tool_context: Provided automatically by ADK.
    """
    preferences = tool_context.state.get("user:dietary_preferences", [])
    if preferences:
        return {"preferences": preferences}
    return {"message": "No dietary preferences saved yet."}

دو نکته قابل توجه:

  1. place_order and get_order_summary use unprefixed keys ( current_order ). This state is tied to the current session — a new conversation starts with an empty order.
  2. set_dietary_preference and get_dietary_preferences use the user: prefix ( user:dietary_preferences ). This state is shared across all sessions for the same user.

به‌روزرسانی عامل با ابزارها و دستورالعمل‌های جدید

Replace the existing root_agent definition at the bottom of the file with:

# cafe_concierge/agent.py (replace the existing root_agent)

root_agent = LlmAgent(
    name="cafe_concierge",
    model="gemini-2.5-flash",
    instruction="""You are a friendly and knowledgeable barista at "The Cloud Cafe".

Your job:
- Help customers browse the menu and answer questions about items.
- Take coffee and food orders.
- Remember and respect dietary preferences.

The customer's saved dietary preferences are: {user:dietary_preferences?}

IMPORTANT RULES:
- When a customer mentions a dietary restriction, ALWAYS save it using the
  set_dietary_preference tool before doing anything else.
- Before recommending items, check the customer's dietary preferences. If they
  have preferences saved, only recommend items compatible with those
  restrictions. Check the menu item tags to determine compatibility.
- When placing an order, confirm the items and total with the customer.

Be conversational, warm, and concise.
""",
    tools=[
        get_menu,
        place_order,
        get_order_summary,
        set_dietary_preference,
        get_dietary_preferences,
    ],
)

The instruction uses the state injection template {user:dietary_preferences?} to inject this customer's saved preferences directly into the prompt.

فایل کامل را تأیید کنید

cafe_concierge/agent.py شما اکنون باید شامل موارد زیر باشد:

  • فرهنگ لغت CAFE_MENU
  • Five tool functions: get_menu , place_order , get_order_summary , set_dietary_preference , get_dietary_preferences
  • تعریف root_agent با هر پنج ابزار

۶. تست عامل با رابط کاربری ADK Dev

این مرحله، عامل را اجرا می‌کند و تمام ویژگی‌های حالت‌مند (stateful) را اعمال می‌کند: مرتب‌سازی، ردیابی ترجیحات و حافظه بین جلسات (در همان فرآیند). همچنین پنل‌های رویدادها و وضعیت را بررسی خواهید کرد تا ببینید ADK چگونه مکالمه را به صورت داخلی ردیابی می‌کند.

رابط کاربری توسعه‌دهنده را شروع کنید

cd ~/build-agent-adk-cloudsql
uv run adk web

پیش‌نمایش وب را روی پورت ۸۰۰۰ باز کنید و از منوی کشویی cafe_concierge را انتخاب کنید.

مکالمه ۱: ثبت سفارش و تنظیم تنظیمات برگزیده

این دستورالعمل‌ها را به ترتیب امتحان کنید:

What's on the menu?
I'm lactose intolerant
What would you recommend?
I'll have an oat milk latte and a banana bread
What's my order?

رویدادهای جلسه را بررسی کنید

تمام رویداد ضبط شده و در رابط کاربری وب نمایش داده می‌شود، خواهید دید که در چت باکس نه تنها اعلان و پاسخ شما، بلکه tool_call و tool_response نیز نمایش داده می‌شود.

9051b46978c8017b.png

شما باید لیستی از رویدادها را به ترتیب ببینید. هر رویداد یک نویسنده (که آن را تولید کرده است) و یک نوع (چه نوع تعاملی را نشان می‌دهد) دارد:

نویسنده

نوع

آنچه نشان دهنده آن است

user

message

پیامی که در چت تایپ کرده‌اید

cafe_concierge

message

پاسخ پیامکی نماینده

cafe_concierge

tool_call

عامل تصمیم گرفت ابزاری را فراخوانی کند (نام تابع + آرگومان‌ها را نشان می‌دهد)

cafe_concierge

tool_response

مقدار برگشتی از فراخوانی یک ابزار

روی یکی از رویدادهای tool_call کلیک کنید - برای مثال، فراخوانی set_dietary_preference . باید این را ببینید:

  • نام تابع : set_dietary_preference
  • آرگومان‌ها : {"preference": "lactose intolerant"}

حالا روی رویداد tool_response مربوطه که درست زیر آن قرار دارد کلیک کنید. باید مقدار برگشتی را ببینید:

  • پاسخ : {"saved": "lactose intolerant", "all_preferences": ["lactose intolerant"]}

b528f4efd6a9f337.png

به دنبال فیلد state_delta در رویداد tool_response بگردید. این فیلد دقیقاً نشان می‌دهد که چه وضعیتی در نتیجه‌ی فراخوانی این ابزار تغییر کرده است:

state_delta: {"user:dietary_preferences": ["lactose intolerant"]}

هر تغییر وضعیت تا یک رویداد خاص قابل ردیابی است. اینگونه است که ADK تضمین می‌کند که دفترچه یادداشت وضعیت با تاریخچه مکالمات همگام می‌ماند.

بررسی وضعیت جلسه

روی برگه وضعیت (State) کلیک کنید. برخلاف گزارش رویدادها (event log) (که تاریخچه کامل را نشان می‌دهد)، برگه وضعیت (state tab) تصویری از آنچه عامل در حال حاضر می‌داند را نشان می‌دهد - مقدار فعلی هر کلید وضعیت (state key).

5e06fb54f3f0d8d6.png

شما باید دو ورودی را ببینید:

  • current_order{"items": ["oat milk latte", "banana bread"], "total": 9.0}
  • user:dietary_preferences["lactose intolerant"]

به تفاوت نام کلیدها توجه کنید:

  • current_order هیچ پیشوندی ندارد — محدود به جلسه است. فقط در این مکالمه وجود دارد و با پایان جلسه ناپدید می‌شود.
  • user:dietary_preferences پیشوند user: را دارد — این تنظیمات به کاربر اختصاص داده شده است. این تنظیمات در هر جلسه برای این کاربر به اشتراک گذاشته می‌شود.

این تمایز در کد قابل مشاهده نیست (هر دو tool_context.state استفاده می‌کنند)، اما میزان دسترسی داده‌ها را کنترل می‌کند. در آزمایش بعدی این موضوع را مشاهده خواهید کرد.

مکالمه ۲: بررسی وضعیت کاربر در جلسات متقابل

برای شروع یک مکالمه جدید، روی دکمه «جلسه جدید» در رابط کاربری توسعه‌دهندگان کلیک کنید. این کار یک جلسه جدید برای همان کاربر ایجاد می‌کند.

57408cfae5f041ac.png

این دستور را امتحان کنید:

What do you recommend for me?

تب State را در جلسه جدید بررسی کنید. کلید user:dietary_preferences باقی می‌ماند، اما current_order حذف شده است - آن وضعیت به جلسه قبلی گره خورده بود.

764eb3885251307d.png

7. Observe Local Storage Limitation

عامل تنظیمات برگزیده را در طول جلسات به خاطر می‌سپارد - اما فقط تا زمانی که فضای ذخیره‌سازی محلی وجود داشته باشد. این مرحله محدودیت اساسی فضای ذخیره‌سازی محلی را نشان می‌دهد.

Start the agent again

شما رابط کاربری توسعه (dev UI) را در پایان مرحله قبل متوقف کردید. حالا بیایید حافظه محلی را حذف کرده و دوباره آن را راه‌اندازی کنیم تا محیط بدون سرور (serverless) که بدون وضعیت (stateless) است را شبیه‌سازی کنیم:

cd ~/build-agent-adk-cloudsql
rm -f cafe_concierge/.adk/session.db
uv run adk web

حالا، پیش‌نمایش وب را روی پورت ۸۰۰۰ باز کنید و cafe_concierge را انتخاب کنید.

Test preference recall

نوع:

Do you remember my dietary preferences?

نماینده هیچ چیزی به خاطر نمی‌آورد. ترجیحات غذایی، سابقه سفارش - همه از بین رفته‌اند.

82a5e05434cafe83.png

وقتی حافظه محلی را حذف کردیم، همه چیز پاک شد، که معمولاً وقتی از یک محیط بدون سرور استفاده می‌کنیم اتفاق می‌افتد. session.db تمام وضعیت‌ها را در حافظه پردازش ذخیره می‌کند. حذف آن، همه چیز را پاک می‌کند.

راه حل: DatabaseSessionService را مشخص کنید، که در این آموزش تمام داده‌های جلسه را در یک پایگاه داده PostgreSQL در Cloud SQL ذخیره می‌کند. کد و ابزارهای عامل دقیقاً یکسان باقی می‌مانند - فقط backend ذخیره‌سازی تغییر می‌کند.

Stop the dev UI with Ctrl+C before proceeding.

8. Revisit Database Setup

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

gcloud sql instances describe cafe-concierge-db --format="value(state)"

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

RUNNABLE

Create the database

یک پایگاه داده اختصاصی برای داده‌های جلسه عامل ایجاد کنید:

gcloud sql databases create agent_db --instance=cafe-concierge-db

Start the Cloud SQL Auth Proxy

پروکسی احراز هویت ابری SQL، بدون نیاز به قرار دادن آدرس‌های IP در لیست سفید، یک اتصال امن و احراز هویت شده از Cloud Shell به نمونه Cloud SQL شما فراهم می‌کند. این پروکسی از قبل روی پوسته ابری نصب شده است.

cloud-sql-proxy ${GOOGLE_CLOUD_PROJECT}:${REGION}:cafe-concierge-db --port 5432 &

پسوند & در دستور باعث می‌شود پروکسی در پس‌زمینه اجرا شود. باید خروجی را مشاهده کنید که تأیید می‌کند پروکسی آماده است، مانند تصویر زیر.

[your-project-id:your-region:cafe-concierge-db] Listening on 127.0.0.1:5432
The proxy has started successfully and is ready for new connections!

Verify the connection

بررسی کنید که آیا می‌توانید از طریق پروکسی به پایگاه داده متصل شوید:

psql "host=127.0.0.1 port=5432 dbname=agent_db user=postgres password=$DB_PASSWORD" -c "SELECT 'Connection ok' AS status;"

You should see:

      status
---------------------
 Connection ok
(1 row)

9. Verify Persistent Memory Across Sessions

این مرحله ثابت می‌کند که حافظه عامل شما پس از ریست شدن، در صورتی که مطمئن شویم cafe_concierge/.adk/session_db (پایگاه داده محلی) حذف شده و در جلسات مکالمه اعمال می‌شود، همچنان قابل استفاده است.

Start the agent

مطمئن شوید که Cloud SQL Auth Proxy هنوز در حال اجرا است (با jobs بررسی کنید). اگر اینطور نیست، آن را مجدداً راه اندازی کنید:

if ss -tlnp | grep -q ':5432 '; then
  echo "Cloud SQL Auth Proxy is already running."
else
  cloud-sql-proxy ${GOOGLE_CLOUD_PROJECT}:${REGION}:cafe-concierge-db --port 5432 &
fi

و سپس، بیایید رابط کاربری توسعه ADK را با مشخص کردن پایگاه داده به عنوان سرویس جلسه شروع کنیم.

uv run adk web --session_service_uri postgresql+asyncpg://postgres:${DB_PASSWORD}@127.0.0.1:5432/agent_db

پیش‌نمایش وب را روی پورت ۸۰۰۰ باز کنید و cafe_concierge را انتخاب کنید.

Test 1: Place an order and set preferences

In the first session, run through these prompts:

Show me the menu
I'm vegan
What can I eat?
I'll have a cold brew and banana bread

Test 2: Survive a restart

رابط کاربری توسعه‌دهندگان را با Ctrl+C متوقف کنید و مطمئن شوید که session.db محلی حذف شده است.

rm -f cafe_concierge/.adk/session.db

Then, re-run the dev UI server

uv run adk web --session_service_uri postgresql+asyncpg://postgres:${DB_PASSWORD}@127.0.0.1:5432/agent_db

پیش‌نمایش وب را روی پورت ۸۰۰۰ باز کنید، cafe_concierge را انتخاب کنید و یک جلسه جدید را شروع کنید. سپس بپرسید

What are my dietary preferences?

عامل با تنظیمات ذخیره شده شما پاسخ می‌دهد - vegan . داده‌ها پس از راه‌اندازی مجدد باقی ماندند زیرا اکنون در PostgreSQL ذخیره شده‌اند، نه در حافظه محلی. اگر جلسه جدیدی ایجاد کنیم user: به هر جلسه جدید برای این کاربر منتقل می‌شود.

9c139bf89becb748.png

Inspect the database directly

یک تب ترمینال جدید در Cloud Shell باز کنید و برای مشاهده داده‌های ذخیره شده، از پایگاه داده کوئری بگیرید:

psql "host=127.0.0.1 port=5432 dbname=agent_db user=postgres password=$DB_PASSWORD" -c "\dt"

شما باید جداولی را که ADK به طور خودکار برای ذخیره جلسات، رویدادها و حالت ایجاد کرده است، مانند این مثال، ببینید.

                List of relations
 Schema |         Name          | Type  |  Owner   
--------+-----------------------+-------+----------
 public | adk_internal_metadata | table | postgres
 public | app_states            | table | postgres
 public | events                | table | postgres
 public | sessions              | table | postgres
 public | user_states           | table | postgres
(5 rows)

Summary of state behavior

State key

پیشوند

دامنه

Shared across sessions?

current_order

(none)

جلسه

خیر

user:dietary_preferences

user:

کاربر

بله

10. Congratulations / Clean Up

تبریک! شما با موفقیت یک عامل هوش مصنوعی پایدار و دارای وضعیت با استفاده از ADK و Cloud SQL ساختید.

What you've learned

  • نحوه ایجاد یک عامل ADK با ابزارهای سفارشی که وضعیت جلسه را می‌خوانند و می‌نویسند
  • تفاوت بین وضعیت محدود به جلسه (بدون پیشوند) و وضعیت محدود به کاربر ( user: پیشوند)
  • چرا adk پیش‌فرض local session.db فقط برای توسعه مناسب است - تمام داده‌ها هنگام حذف از بین می‌روند (و حذف آنها آسان است، بدون پشتیبان)، برای استقرار بدون سرور که بدون وضعیت است مناسب نیست.
  • نحوه تهیه یک نمونه Cloud SQL PostgreSQL و اتصال به آن با Cloud SQL Auth Proxy
  • نحوه اتصال به DatabaseSessionService با PostgreSQL در CloudSQL با حداقل تغییر کد - همان ابزارها، همان عامل، backend متفاوت
  • چگونه وضعیتِ تحتِ محدوده‌ی کاربر در جلسات مکالمه‌ی جداگانه باقی می‌ماند

تمیز کردن

برای جلوگیری از تحمیل هزینه به حساب Google Cloud خود، منابع ایجاد شده در این codelab را پاک کنید.

ساده‌ترین راه برای پاکسازی، حذف پروژه است. این کار تمام منابع مرتبط با پروژه را حذف می‌کند.

gcloud projects delete ${GOOGLE_CLOUD_PROJECT}

گزینه ۲: حذف منابع تکی

اگر می‌خواهید پروژه را نگه دارید اما فقط منابع ایجاد شده در این codelab را حذف کنید:

gcloud sql instances delete cafe-concierge-db --quiet