การสร้างเอเจนต์แบบมีสถานะและปรับเปลี่ยนในแบบของคุณด้วย ADK

1. บทนำ

title

ปัญหา "ปลาทอง"

สมมติว่าคุณจ้างตัวแทนท่องเที่ยวเพื่อวางแผนทริปพักผ่อนในฝันที่โตเกียว ใช้ตัวแทนเซสชันเพื่อดู "ปัญหาปลาทอง" ในการทำงาน

คุณเดินเข้าไปในออฟฟิศของพวกเขาและพูดว่า

"สวัสดี ฉันอยากวางแผนทริป 2 วันไปโตเกียว ฉันสนใจสถานที่ทางประวัติศาสตร์และซูชิ"

ตัวแทนตอบกลับอย่างกระตือรือร้นว่า

"เยี่ยม ฉันวางแผนที่จะไปเยี่ยมชมพระราชวังอิมพีเรียลและทานซูชิที่ Sukiyabashi Jiro"

คุณยิ้มและพูดว่า

"ฟังดูดีมากเลย คุณช่วยส่งกำหนดการให้ฉันได้ไหม"

ตัวแทนมองคุณด้วยสีหน้าว่างเปล่าและถามว่า

"สวัสดี วันนี้จะให้เราช่วยคุณวางแผนการเดินทางได้อย่างไร

นี่คือ "ปัญหาปลาทอง" หากไม่มีหน่วยความจำ ทุกการโต้ตอบก็เหมือนกระดาษเปล่า ความสามารถนั้นมีอยู่แล้ว ตัวแทนรู้วิธีวางแผนการเดินทาง แต่ขาดความต่อเนื่อง เอเจนต์ AI ต้องจดจำได้จึงจะมีประโยชน์อย่างแท้จริง

ภารกิจของคุณในวันนี้

ในเวิร์กช็อปนี้ คุณจะได้แก้ปัญหาปลาทองด้วยการสร้างตัวแทนการท่องเที่ยวที่จดจำ เรียนรู้ และปรับตัวได้ คุณจะก้าวหน้าผ่านระดับความจำของเอเจนต์ 6 ระดับ ซึ่งจะสร้างระบบที่ทำงานได้ไม่เหมือนแชทบอท แต่เหมือนผู้ช่วยส่วนตัวที่ทุ่มเทมากกว่า

ระดับ

แนวคิด

"พลังวิเศษ"

ระดับ 1

เซสชันและสถานะ

สนทนาโดยไม่ลืม

ระดับ 2

สถานะของหลายเอเจนต์

การแชร์โน้ตระหว่างสมาชิกในทีม

ระดับ 3

ความคงทน

จดจำคุณได้แม้หลังจากรีบูตระบบ

ระดับ 4

การโทรกลับ

อัปเดตหน่วยความจำโดยอัตโนมัติอย่างสมบูรณ์

ระดับ 5

เครื่องมือที่กำหนดเอง

การอ่านและการเขียนโปรไฟล์ผู้ใช้ที่มีโครงสร้าง

ระดับ 6

หน่วยความจำมัลติโมดัล

"การมองเห็น" และการจดจำรูปภาพและวิดีโอ

สแต็กหน่วยความจำ ADK

ก่อนเขียนโค้ด มาทำความเข้าใจเครื่องมือที่เราจะใช้กันก่อน ชุดพัฒนาเอเจนต์ของ Google (ADK) มีวิธีจัดการหน่วยความจำที่เป็นระบบดังนี้

  1. เซสชัน: คอนเทนเนอร์สำหรับการสนทนา ซึ่งจะเก็บประวัติของสิ่งที่พูด
  2. สถานะ: "กระดาษทด" คีย์-ค่าที่แนบกับเซสชัน เอเจนต์ใช้สิ่งนี้เพื่อจัดเก็บข้อเท็จจริงที่เฉพาะเจาะจง (เช่น destination="Tokyo")
  3. MemoryService: ที่เก็บข้อมูลระยะยาว ซึ่งเป็นที่ที่เราเก็บข้อมูลไว้ตลอดไป เช่น ค่ากำหนดของผู้ใช้หรือเอกสารที่วิเคราะห์แล้ว

2. ตั้งค่า

เราต้องมี 2 สิ่งเพื่อขับเคลื่อนเอเจนต์ AI ได้แก่ โปรเจ็กต์ Google Cloud เพื่อเป็นรากฐาน

ส่วนที่ 1: เปิดใช้บัญชีสำหรับการเรียกเก็บเงิน

  • การอ้างสิทธิ์บัญชีการเรียกเก็บเงินพร้อมเครดิต 5 ดอลลาร์ คุณจะต้องใช้เครดิตนี้ในการติดตั้งใช้งาน โปรดตรวจสอบบัญชี gmail

ส่วนที่ 2: สภาพแวดล้อมแบบเปิด

  1. 👉 คลิกลิงก์นี้เพื่อไปยัง Cloud Shell Editor โดยตรง
  2. 👉 หากระบบแจ้งให้ให้สิทธิ์ในวันนี้ ให้คลิกให้สิทธิ์เพื่อดำเนินการต่อ คลิกเพื่อให้สิทธิ์ Cloud Shell
  3. 👉 หากเทอร์มินัลไม่ปรากฏที่ด้านล่างของหน้าจอ ให้เปิดโดยทำดังนี้
    • คลิกดู
    • คลิก Terminalเปิดเทอร์มินัลใหม่ใน Cloud Shell Editor
  4. 👉💻 ในเทอร์มินัล ให้ตรวจสอบว่าคุณได้รับการตรวจสอบสิทธิ์แล้วและตั้งค่าโปรเจ็กต์เป็นรหัสโปรเจ็กต์โดยใช้คำสั่งต่อไปนี้
    gcloud auth list
    
  5. 👉💻 โคลนโปรเจ็กต์ Bootstrap จาก GitHub
    git clone https://github.com/cuppibla/memory_agent_starter
    
    
  6. 👉💻 เรียกใช้สคริปต์การตั้งค่าจากไดเรกทอรีโปรเจ็กต์
    cd ~/memory_agent_starter
    ./init.sh
    
    สคริปต์จะจัดการกระบวนการตั้งค่าที่เหลือโดยอัตโนมัติ
  7. 👉💻 ตั้งค่ารหัสโปรเจ็กต์ที่จำเป็น
    gcloud config set project $(cat ~/project_id.txt) --quiet
    

ส่วนที่ 3: การตั้งค่าสิทธิ์

  1. 👉💻 เปิดใช้ API ที่จำเป็นโดยใช้คำสั่งต่อไปนี้ การดำเนินการนี้อาจใช้เวลาสักครู่
    gcloud services enable \
        cloudresourcemanager.googleapis.com \
        servicenetworking.googleapis.com \
        run.googleapis.com \
        aiplatform.googleapis.com \
        compute.googleapis.com
    
  2. 👉💻 ให้สิทธิ์ที่จำเป็นโดยเรียกใช้คำสั่งต่อไปนี้ในเทอร์มินัล
    . ~/memory_agent_starter/set_env.sh
    

โปรดสังเกตว่าระบบได้สร้างไฟล์ .env ให้คุณแล้ว ซึ่งจะแสดงข้อมูลโปรเจ็กต์ของคุณ

3. พื้นฐาน - เซสชันและสถานะ

เก็บสถานะ

แนวคิด: บริบทคือสิ่งสำคัญที่สุด

รูปแบบพื้นฐานที่สุดของหน่วยความจำคือหน่วยความจำเซสชัน ซึ่งช่วยให้ตัวแทนทราบว่า "มัน" ในประโยค "ฉันต้องการซื้อมัน" หมายถึงรองเท้าที่คุณพูดถึงเมื่อ 10 วินาทีก่อน

ใน ADK เราจัดการเรื่องนี้ด้วยออบเจ็กต์ Session

  • แนวทางแบบไม่เก็บสถานะ: สร้างเซสชันใหม่สำหรับทุกข้อความ
  • แนวทางแบบมีสถานะ: สร้างเซสชันเดียวและนำกลับมาใช้ใหม่สำหรับการสนทนาทั้งหมด

ขั้นตอนที่ 1: ตรวจสอบเอเจนต์

👉💻 ในเทอร์มินัล Cloud Shell ให้เปิดไฟล์ใน Cloud Shell Editor โดยเรียกใช้คำสั่งต่อไปนี้

cloudshell edit ~/memory_agent_starter/01_session_agent/agent.py

เปิด ~/memory_agent_starter/01_session_agent/agent.py

👉 ค้นหาความคิดเห็น # TODO: Create a root agent ภายในฟังก์ชัน agent.py

แทนที่ทั้งบรรทัดนี้ด้วยโค้ดต่อไปนี้

root_agent = LlmAgent(
    name="multi_day_trip_agent",
    model="gemini-2.5-flash",
    description="Agent that progressively plans a multi-day trip, remembering previous days and adapting to user feedback.",
    instruction="""
    You are the "Adaptive Trip Planner" 🗺️ - an AI assistant that builds multi-day travel itineraries step-by-step.

    Your Defining Feature:
    You have short-term memory. You MUST refer back to our conversation to understand the trip's context, what has already been planned, and the user's preferences. If the user asks for a change, you must adapt the plan while keeping the unchanged parts consistent.

    Your Mission:
    1.  **Initiate**: Start by asking for the destination, trip duration, and interests.
    2.  **Plan Progressively**: Plan ONLY ONE DAY at a time. After presenting a plan, ask for confirmation.
    3.  **Handle Feedback**: If a user dislikes a suggestion (e.g., "I don't like museums"), acknowledge their feedback, and provide a *new, alternative* suggestion for that time slot that still fits the overall theme.
    4.  **Maintain Context**: For each new day, ensure the activities are unique and build logically on the previous days. Do not suggest the same things repeatedly.
    5.  **Final Output**: Return each day's itinerary in MARKDOWN format.
    """,
    tools=[google_search]
)

คำสั่งจะบอก LLM ให้จดจำ แต่โค้ดต้องมีความสามารถในการจดจำ

ขั้นตอนที่ 2: 2 สถานการณ์

เปิด ~/memory_agent_starter/01_session_agent/main.py

👉 ในเทอร์มินัล Cloud Shell ให้เปิดไฟล์ใน Cloud Shell Editor โดยเรียกใช้คำสั่งต่อไปนี้

cloudshell edit ~/memory_agent_starter/01_session_agent/main.py

เปิด ~/memory_agent_starter/01_session_agent/main.py แล้วค้นหาความคิดเห็น # TODO: Create a runner with in memorysession service ภายในฟังก์ชัน main.py

แทนที่ทั้งบรรทัดนี้ด้วยโค้ดต่อไปนี้

    runner = Runner(
        agent=agent,
        session_service=session_service,
        app_name=agent.name
    )

👉 ค้นหาความคิดเห็น # TODO: create a different session to test ภายในฟังก์ชัน main.py

แทนที่ทั้งบรรทัดนี้ด้วยโค้ดต่อไปนี้

    tokyo_session_2 = await session_service.create_session(
        app_name=multi_day_agent.name,
        user_id=user_id
    )

ทดสอบ

เรามีฟังก์ชัน 2 อย่างที่แสดงให้เห็นความแตกต่างระหว่างความจำของ "ปลาทอง" กับ "ช้าง"

สถานการณ์ที่ 1: มีสถานะ (เซสชันที่แชร์)

async def run_trip_same_session_scenario(session_service, user_id):
    # 1. Create ONE session
    trip_session = await session_service.create_session(...)

    # 2. Turn 1
    await run_agent_query(..., trip_session, ...)

    # 3. Turn 2 - REUSING the same session!
    # The agent can "see" Turn 1 because it's in the session history.
    await run_agent_query(..., trip_session, ...)

สถานการณ์ที่ 2: ไม่มีการเก็บสถานะ (เซสชันใหม่ทุกครั้ง)

async def run_trip_different_session_scenario(session_service, user_id):
    # Turn 1
    tokyo_session = await session_service.create_session(...)
    await run_agent_query(..., tokyo_session, ...)

    # Turn 2 - Creating a FREASH session
    # The agent has NO IDEA what happened in Turn 1.
    tokyo_session_2 = await session_service.create_session(...)
    await run_agent_query(..., tokyo_session_2, ...)

ขั้นตอนที่ 3: เรียกใช้เอเจนต์

มาดูความแตกต่างในการใช้งานจริงกัน เรียกใช้สคริปต์

👉💻 ในบรรทัดคำสั่ง ให้เรียกใช้บรรทัดคำสั่งด้านล่าง

cd ~/memory_agent_starter
uv run python ~/memory_agent_starter/01_session_agent/main.py

ดูสถานการณ์ที่ 1: ตัวแทนจดจำค่ากำหนดของคุณจากข้อความแรกและปรับแผนในข้อความที่ 2

สังเกตสถานการณ์ที่ 2: ในเทิร์นที่ 2 ("คุณจำได้ไหมว่าฉันชอบอะไรเกี่ยวกับอาหาร") ตัวแทนล้มเหลวโดยสิ้นเชิงเนื่องจากเป็นเซสชันใหม่ ซึ่งหมายความว่า "ฉันไม่รู้ว่าคุณกำลังพูดถึงอะไร"

ประเด็นสำคัญ

กฎข้อที่ 1 ของฟีเจอร์ความจำ: ใช้ session.id ซ้ำเสมอเพื่อรักษาบริบทของการสนทนา ออบเจ็กต์ Session คือบัฟเฟอร์หน่วยความจำระยะสั้นของเอเจนต์

4. ทีม - สถานะตัวแทนหลายราย

อาหาร

แนวคิด: "เกมโทรศัพท์"

เมื่อตัวแทนหลายคนทำงานร่วมกัน พวกเขาจะเหมือนเพื่อนร่วมงานที่ส่งโฟลเดอร์ไฟล์ไปมา หากตัวแทนเขียนหมายเหตุในโฟลเดอร์ ตัวแทนคนถัดไปควรจะอ่านหมายเหตุนั้นได้

ใน ADK "โฟลเดอร์" นี้คือสถานะ

  • State คือพจนานุกรม ({"key": "value"}) ที่อยู่ในเซสชัน
  • Agent ทุกคนในเซสชันสามารถอ่านหรือเขียนข้อมูลในเซสชันได้

ขั้นตอนที่ 1: ตรวจสอบเวิร์กโฟลว์

👉💻 ในเทอร์มินัล Cloud Shell ให้เปิดไฟล์ใน Cloud Shell Editor โดยเรียกใช้คำสั่งต่อไปนี้

cloudshell edit ~/memory_agent_starter/02_multi_agent/agent.py

👉ค้นหาความคิดเห็น # TODO: foodie agent ในไฟล์ ~/memory_agent_starter/02_multi_agent/agent.py

แทนที่ทั้งบรรทัดนี้ด้วยโค้ดต่อไปนี้

foodie_agent = LlmAgent(
    name="foodie_agent",
    model="gemini-2.5-flash",
    tools=[google_search],
    instruction="""You are an expert food critic. Your goal is to find the best restaurant based on a user's request.

    When you recommend a place, you must output *only* the name of the establishment and nothing else.
    For example, if the best sushi is at 'Jin Sho', you should output only: Jin Sho
    """,
    output_key="destination"  # ADK will save the agent's final response to state['destination']
)

👉 ค้นหาความคิดเห็น # TODO: transportation agent ภายในฟังก์ชัน agent.py

แทนที่ทั้งบรรทัดนี้ด้วยโค้ดต่อไปนี้

transportation_agent = LlmAgent(
    name="transportation_agent",
    model="gemini-2.5-flash",
    tools=[google_search],
    instruction="""You are a navigation assistant. Given a destination, provide clear directions.
    The user wants to go to: {destination}.

    Analyze the user's full original query to find their starting point.
    Then, provide clear directions from that starting point to {destination}.
    """,
)

👉 ค้นหาความคิดเห็น # TODO: root_agent ภายในฟังก์ชัน agent.py

แทนที่ทั้งบรรทัดนี้ด้วยโค้ดต่อไปนี้

root_agent = SequentialAgent(
    name="find_and_navigate_agent",
    sub_agents=[foodie_agent, transportation_agent],
    description="A workflow that first finds a location and then provides directions to it."
)

ตอนนี้เรามีเอเจนต์ 2 คนที่ทำงานตามลำดับดังนี้

  1. ตัวแทนนักชิม: ค้นหาร้านอาหาร
  2. ตัวแทนการเดินทาง: บอกเส้นทางไปยังร้านอาหารนั้น

การส่งต่อแบบแมจิก: สังเกตว่า foodie_agent ส่งต่อให้ transportation_agent อย่างไร

foodie_agent = LlmAgent(
    # ...
    # CRITICAL: This tells ADK to save the agent's output to state['destination']
    output_key="destination"
)

transportation_agent = LlmAgent(
    # ...
    # CRITICAL: This injects state['destination'] into the prompt
    instruction="""
    The user wants to go to: {destination}.
    Provide clear directions...
    """,
)
  1. output_key="destination": ระบบจะบันทึกคำตอบของ Foodie Agent อย่างมีประสิทธิภาพ
  2. {destination}: ตัวแทนการขนส่งจะอ่านคำตอบนั้นโดยอัตโนมัติ

(ไม่ต้องดำเนินการใดๆ) ขั้นตอนที่ 2: Orchestrator

เปิด 02_multi_agent/main.py

เราใช้ SequentialAgent เพื่อเรียกใช้ตามลำดับ

# 1. Create a single session for the sequential agent
session = await session_service.create_session(...)

# 2. Run the query
# The SequentialAgent manages the state flow:
# Query -> Foodie -> state['destination'] -> Transportation -> Final Answer
await run_agent_query(root_agent, query, ...)

ผู้ใช้ส่งพรอมต์เดียว

"Find best sushi in Palo Alto and then tell me how to get there."

ตัวแทนจะทำงานร่วมกันเพื่อตอบคำถาม

ขั้นตอนที่ 3: เรียกใช้ทีม

👉💻 ในเทอร์มินัล Cloud Shell ให้เรียกใช้เวิร์กโฟลว์แบบหลายเอเจนต์

cd ~/memory_agent_starter
uv run python ~/memory_agent_starter/02_multi_agent/main.py

จะเกิดอะไรขึ้น

  1. Foodie Agent: ค้นหา "Jin Sho" (หรือร้านอาหารที่คล้ายกัน)
  2. ADK: บันทึก "จินโช" ไปยัง state['destination']
  3. ตัวแทนการขนส่ง: ได้รับ "Jin Sho" ในคำสั่ง
  4. ผลลัพธ์: "หากต้องการไปร้านจินโชจากสถานี Caltrain ให้เดินลงไปตามถนน University..."

ประเด็นสำคัญ

กฎข้อที่ 2 ของหน่วยความจำ: ใช้สถานะเพื่อส่งข้อมูลที่มีโครงสร้างระหว่างเอเจนต์ ใช้ output_key เพื่อเขียนและ {placeholders} เพื่ออ่าน

5. การรีบูต - ความคงทน

เก็บสถานะ

แนวคิด: "ปัญหาการรีบูต"

ตอนนี้ความจำของเราอยู่ที่ InMemory หากคุณหยุดสคริปต์และเริ่มอีกครั้ง เอเจนต์จะลืมทุกอย่าง ซึ่งก็เหมือนกับคอมพิวเตอร์ที่ล้างฮาร์ดไดรฟ์ทุกครั้งที่คุณปิดเครื่อง

เราจึงต้องใช้การคงอยู่เพื่อแก้ไขปัญหานี้ เราจะเปลี่ยน InMemorySessionService เป็น DatabaseSessionService

ขั้นตอนที่ 1: การเปลี่ยนฐานข้อมูล

👉💻 ในเทอร์มินัล Cloud Shell ให้เปิดไฟล์ใน Cloud Shell Editor โดยเรียกใช้คำสั่งต่อไปนี้

cloudshell edit ~/memory_agent_starter/03_persistent_agent/main.py

👉 ค้นหาความคิดเห็น # TODO: Configuration for Persistent Sessions ในไฟล์ ~/memory_agent_starter/03_persistent_agent/main.py

แทนที่ทั้งบรรทัดนี้ด้วยโค้ดต่อไปนี้

SESSIONS_DIR = Path(os.path.expanduser("~")) / ".adk_codelab" / "sessions"
os.makedirs(SESSIONS_DIR, exist_ok=True)
SESSION_DB_FILE = SESSIONS_DIR / "trip_planner.db"
SESSION_URL = f"sqlite:///{SESSION_DB_FILE}"

ตอนนี้ระบบจะบันทึกเซสชันและเหตุการณ์ทั้งหมดลงในไฟล์ SQLite

ขั้นตอนที่ 2: การดึงข้อมูลข้ามเซสชัน

ความต่อเนื่องไม่เพียงช่วยให้สนทนาต่อได้ แต่ยังช่วยเรียนรู้จากแชทที่ผ่านมาได้ด้วย

ในไฟล์เดียวกัน~/memory_agent_starter/03_persistent_agent/main.py ให้ดูกรณีทดสอบที่ 3: การดึงข้อมูลข้ามเซสชัน

👉 ค้นหาความคิดเห็น # TODO: retrieve the previous session manually

แทนที่ทั้งบรรทัดนี้ด้วยโค้ดต่อไปนี้

    old_session = await session_service.get_session(
        app_name=root_agent.name, user_id="user_01", session_id=session_id
    )

👉 ค้นหาความคิดเห็น # TODO: Extract content from the OLD session ภายในฟังก์ชัน main.py

แทนที่ทั้งบรรทัดนี้ด้วยโค้ดต่อไปนี้

                    previous_context += f"- {role}: {text}\n"

👉 ค้นหาความคิดเห็น # TODO: Manually inject the context to the query ภายในฟังก์ชัน main.py

แทนที่ทั้งบรรทัดนี้ด้วยโค้ดต่อไปนี้

    query_3 = f"""
    {previous_context}

    I'm planning a new trip to Osaka this time. 
    Based on my previous preferences (above), what should I eat?
    """

ซึ่งจำลองผู้ใช้ที่กลับมาในอีกหลายเดือนต่อมา คุณจะดึงประวัติเก่าๆ นั้นได้ก็ต่อเมื่อมีฐานข้อมูลเท่านั้น

ขั้นตอนที่ 3: การอยู่รอดหลังการรีบูต

👉💻 เรียกใช้สคริปต์ในเทอร์มินัลโดยใช้คำสั่งต่อไปนี้

cd ~/memory_agent_starter
uv run python ~/memory_agent_starter/03_persistent_agent/main.py

ซึ่งจะสร้างไฟล์ ~/memory_agent_starter/trip_planner.db ลองทำดังนี้ เรียกใช้สคริปต์2 ครั้ง

  • ในการเรียกใช้ครั้งที่ 2 ให้มองหา "เซสชันที่มีอยู่ซึ่งกลับมาทำงานต่อ"
  • เอเจนต์จะจดจำบริบทจากการเรียกใช้ครั้งแรกได้เนื่องจากโหลดจากไฟล์ฐานข้อมูล

ประเด็นสำคัญ

กฎข้อที่ 3 ของหน่วยความจำ: ใช้ DatabaseSessionService สำหรับเวอร์ชันที่ใช้งานจริง ซึ่งจะช่วยให้การสนทนาของผู้ใช้ยังคงอยู่แม้จะมีการรีสตาร์ทเซิร์ฟเวอร์ และช่วยให้วิเคราะห์ประวัติในระยะยาวได้

6. The Spy - Callbacks

เก็บสถานะ

บางครั้งคุณต้องอัปเดตหน่วยความจำโดยอัตโนมัติตามสิ่งที่เอเจนต์ทำ ไม่ใช่แค่สิ่งที่เอเจนต์พูด คุณต้องการ "สายลับ" ที่คอยสังเกต Agent และจดบันทึก

ใน ADK สปายนี้คือ Callback adk_callback

  • after_tool_callback: ฟังก์ชันที่ทำงานทุกครั้งที่เอเจนต์ทำงาน
  • ToolContext: วิธีเขียนไปยังสถานะจากภายในฟังก์ชันนั้น

ขั้นตอนที่ 1: ตรรกะ

👉💻 ในเทอร์มินัล Cloud Shell ให้เปิดไฟล์ใน Cloud Shell Editor โดยเรียกใช้คำสั่งต่อไปนี้

cloudshell edit ~/memory_agent_starter/04_stateful_agent/agent.py

👉 ในไฟล์ ~/memory_agent_starter/04_stateful_agent/agent.py ให้ค้นหาความคิดเห็น # TODO: Implement call back logic

แทนที่ทั้งบรรทัดนี้ด้วยโค้ดต่อไปนี้

def save_activity_type_callback(
    tool,
    args: Dict[str, Any],
    tool_context: ToolContext,
    tool_response: Dict[str, Any],
) -> Optional[Dict[str, Any]]:
    """
    Callback to save the TYPE of activity just planned into the session state.
    """
    # 1. Get the actual agent name.
    if tool.name == "transfer_to_agent":
         agent_name = args.get("agent_name")
    else:
         agent_name = tool.name

    activity_type = "unknown"

    # 2. Determine the type based on which agent was actually used
    if agent_name == "museum_expert":
        activity_type = "CULTURAL"
    elif agent_name == "restaurant_expert":
        activity_type = "FOOD"
    elif agent_name == "outdoor_expert":
        activity_type = "OUTDOOR"

    print(f"\n🔔 [CALLBACK] The planner transferred to '{agent_name}'.")

    # 3. Update the state directly
    tool_context.state["last_activity_type"] = activity_type
    print(f"💾 [STATE UPDATE] 'last_activity_type' is now set to: {activity_type}\n")

    return tool_response

👉 ในไฟล์เดียวกัน ให้ค้นหาความคิดเห็น # TODO: add callback to root agent ภายในฟังก์ชัน 04_stateful_agent/agent.py

แทนที่ทั้งบรรทัดนี้ด้วยโค้ดต่อไปนี้

    after_tool_callback=save_activity_type_callback,

คำสั่งแบบไดนามิก: ตอนนี้คำสั่งของเอเจนต์เป็นฟังก์ชัน ไม่ใช่สตริง โดยจะเปลี่ยนไปตามสถานะ

def get_planner_instruction(context):
    last_activity = context.state.get("last_activity_type", "None")
    
    return f"""
    The last activity was: {last_activity}
    
    If last_activity is 'CULTURAL' -> `museum_expert` is BANNED.
    """

ขั้นตอนที่ 3: ทดสอบ Spy

👉💻 ในเทอร์มินัล ให้เรียกใช้สคริปต์โดยคัดลอกและวางคำสั่งด้านล่าง

cd ~/memory_agent_starter
uv run python ~/memory_agent_starter/04_stateful_agent/main.py

เมื่อเรียกใช้เอเจนต์นี้ คุณจะเห็นลูป

  1. เทิร์นที่ 1: คุณขอพิพิธภัณฑ์ ชุดสปาย last_activity="CULTURAL"
  2. เทิร์นที่ 2: คุณขอพิพิธภัณฑ์อื่น
  3. การอัปเดตคำสั่งของตัวแทน: "ห้ามใช้คำว่า CULTURAL"
  4. เอเจนต์กล่าวว่า "ฉันไม่สามารถทำพิพิธภัณฑ์อื่นได้ ไปสวนสาธารณะไหม"

ดูบันทึกคอนโซลสำหรับ [CALLBACK] และ [STATE UPDATE] คุณจะเห็นการเปลี่ยนแปลงหน่วยความจำแบบเรียลไทม์ขณะที่เอเจนต์ทำงาน

ประเด็นสำคัญ

กฎข้อที่ 4 ของ Memory: ใช้การเรียกกลับเพื่อทำให้การจัดการสถานะเป็นแบบอัตโนมัติ Agent จะสร้างบริบทของตัวเองได้เพียงแค่ทำงาน

7. ตู้เก็บเอกสาร - เครื่องมือที่กำหนดเอง

แนวคิด: "ความทรงจำที่มีโครงสร้าง"

เก็บสถานะ

ที่ผ่านมา "ความจำ" เป็นบันทึกการแชทหรือคู่คีย์-ค่าอย่างง่าย แต่จะเกิดอะไรขึ้นหากคุณต้องจดจำโปรไฟล์ผู้ใช้ที่ซับซ้อน เช่น diet: vegan, budget: high, pets: [cat, dog]

เราจึงถือว่าหน่วยความจำเป็นเครื่องมือ เอเจนต์จะตัดสินใจอย่างชัดเจนว่าจะเปิดตู้เก็บเอกสาร (อ่าน) เมื่อใด และจะยื่นรายงาน (เขียน) เมื่อใด แผนภาพเครื่องมือที่กำหนดเอง

ขั้นตอนที่ 1: เครื่องมือ

👉💻 ในเทอร์มินัล Cloud Shell ให้เปิดไฟล์ใน Cloud Shell Editor โดยเรียกใช้คำสั่งต่อไปนี้

cloudshell edit ~/memory_agent_starter/05_profile_agent/tools.py

👉 ในไฟล์นี้: ~/memory_agent_starter/05_profile_agent/tools.py

เราต้องใช้เครื่องมือ 2 อย่างนี้

  1. save_user_preferences: เขียนไปยังฐานข้อมูล
  2. recall_user_preferences: อ่านจากฐานข้อมูล

ค้นหาความคิดเห็น # TODO: implement save_user_preferences tools ภายในฟังก์ชัน ~/memory_agent_starter/05_profile_agent/tools.py

แทนที่ทั้งบรรทัดนี้ด้วยโค้ดต่อไปนี้

def save_user_preferences(tool_context: ToolContext, new_preferences: Dict[str, Any]) -> str:
    user_id = tool_context.session.user_id
    with sqlite3.connect(USER_DB_FILE) as conn:
        for key, value in new_preferences.items():
            conn.execute("INSERT INTO user_preferences (user_id, pref_key, pref_value) VALUES (?, ?, ?) ON CONFLICT(user_id, pref_key) DO UPDATE SET pref_value = excluded.pref_value;",
                         (user_id, key, json.dumps(value)))
    return f"Preferences updated: {list(new_preferences.keys())}"

👉 ค้นหาความคิดเห็น # TODO: implement recall_user_preferences tools ภายในฟังก์ชัน 05/tools.py

แทนที่ทั้งบรรทัดนี้ด้วยโค้ดต่อไปนี้

def recall_user_preferences(tool_context: ToolContext) -> Dict[str, Any]:
    user_id = tool_context.session.user_id
    preferences = {}
    with sqlite3.connect(USER_DB_FILE) as conn:
        rows = conn.execute("SELECT pref_key, pref_value FROM user_preferences WHERE user_id = ?", (user_id,)).fetchall()
        if not rows: return {"message": "No preferences found."}
        for key, value_str in rows: preferences[key] = json.loads(value_str)
    return preferences

คำสั่งจะบังคับให้เวิร์กโฟลว์ทำดังนี้

instruction="""
1. RECALL FIRST: First action MUST be `recall_user_preferences`.
3. LEARN: If a user states a new preference, use `save_user_preferences`.
"""

ขั้นตอนที่ 2: การดำเนินการ

👉💻 ในเทอร์มินัล Cloud Shell ให้เปิดไฟล์ใน Cloud Shell Editor โดยเรียกใช้คำสั่งต่อไปนี้

cloudshell edit ~/memory_agent_starter/05_profile_agent/main.py

เปิด ~/memory_agent_starter/05_profile_agent/main.py

Agent จะเป็นผู้ควบคุม ซึ่งแตกต่างจากโมดูลก่อนหน้าที่ ADK จัดการสถานะโดยอัตโนมัติ

  • โดยจะเลือกโทรหา recall_user_preferences ก่อน
  • โดยจะเลือกโทรหา save_user_preferences เมื่อคุณพูดว่า "ฉันเป็นมังสวิรัติ"

ขั้นตอนที่ 3: สร้างโปรไฟล์

👉💻 เรียกใช้สคริปต์

cd ~/memory_agent_starter
uv run python ~/memory_agent_starter/05_profile_agent/main.py

ลองใช้โฟลว์การสนทนานี้

  1. "สวัสดี วางแผนมื้อค่ำหน่อย" -> Agent ตรวจสอบ DB แต่ไม่พบอะไร ขอค่ากำหนด
  2. "ฉันเป็นวีแกน" -> Agent บันทึก "มังสวิรัติ" ลงใน DB
  3. รีสตาร์ทสคริปต์
  4. "สวัสดี วางแผนมื้อค่ำหน่อย" -> ตัวแทนตรวจสอบฐานข้อมูล เห็นคำว่า "มังสวิรัติ" และแนะนำร้านอาหารมังสวิรัติทันที

ประเด็นสำคัญ

กฎข้อที่ 5 ของหน่วยความจำ: สำหรับข้อมูลที่มีโครงสร้างซับซ้อน ให้เครื่องมืออ่าน/เขียนแก่เอเจนต์ ให้ LLM จัดการพื้นที่เก็บข้อมูลระยะยาวของตัวเอง

8. สมอง - ความทรงจำแบบมัลติโมดัล

เก็บสถานะ

แนวคิด: "ประสบการณ์ของมนุษย์"

มนุษย์จดจำภาพได้ดีกว่าข้อความ เราจดจำกลิ่นอายของรูปภาพ เสียงของเสียงพูด ความรู้สึกของวิดีโอ

Vertex AI Memory Bank ช่วยให้เอเจนต์ของคุณจัดการหน่วยความจำมัลติโมดอลได้ โดยสามารถรับรูปภาพ วิดีโอ และเสียง "ทำความเข้าใจ" และดึงข้อมูลเหล่านั้นในภายหลังได้

ขั้นตอนที่ 1: การกำหนดค่า

👉💻 ในเทอร์มินัล Cloud Shell ให้เปิดไฟล์ใน Cloud Shell Editor โดยเรียกใช้คำสั่งต่อไปนี้

cloudshell edit ~/memory_agent_starter/06_multimodal_agent/main.py

👉 เปิด 06_multimodal_agent/main.py ค้นหาความคิดเห็น # TODO: Configure Memory Bank Topic

แทนที่ทั้งบรรทัดนี้ด้วยโค้ดต่อไปนี้

travel_topics = [
    MemoryTopic(
        managed_memory_topic=ManagedMemoryTopic(
            managed_topic_enum=ManagedTopicEnum.USER_PREFERENCES
        )
    ),
    MemoryTopic(
        managed_memory_topic=ManagedMemoryTopic(
            managed_topic_enum=ManagedTopicEnum.USER_PERSONAL_INFO
        )
    ),
    MemoryTopic(
        custom_memory_topic=CustomMemoryTopic(
            label="travel_experiences",
            description="""Memorable travel experiences including:
                - Places visited and impressions
                - Favorite restaurants, cafes, and food experiences
                - Preferred accommodation types and locations
                - Activities enjoyed (museums, hiking, beaches, etc.)
                - Travel companions and social preferences
                - Photos and videos from trips with location context""",
        )
    ),
    MemoryTopic(
        custom_memory_topic=CustomMemoryTopic(
            label="travel_preferences",
            description="""Travel style and preferences:
                - Budget preferences (luxury, mid-range, budget)
                - Transportation preferences (flying, trains, driving)
                - Trip duration preferences
                - Season and weather preferences
                - Cultural interests and language abilities
                - Dietary restrictions and food preferences""",
        )
    ),
    MemoryTopic(
        custom_memory_topic=CustomMemoryTopic(
            label="travel_logistics",
            description="""Practical travel information:
                - Passport and visa information
                - Frequent flyer numbers and hotel loyalty programs
                - Emergency contacts
                - Medical considerations and insurance
                - Packing preferences and essentials
                - Time zone preferences and jet lag strategies""",
        )
    ),
]

ค้นหาความคิดเห็น # TODO: Configure Memory Bank Customization

แทนที่ทั้งบรรทัดนี้ด้วยโค้ดต่อไปนี้

memory_bank_config = {
    "customization_configs": [
        {
            "memory_topics": travel_topics,
        }
    ],
    "similarity_search_config": {
        "embedding_model": f"projects/{PROJECT_ID}/locations/{LOCATION}/publishers/google/models/gemini-embedding-001"
    },
    "generation_config": {
        "model": f"projects/{PROJECT_ID}/locations/{LOCATION}/publishers/google/models/gemini-2.5-flash"
    },
}

ขั้นตอนที่ 2: การนำเข้าข้อมูลทั่วโลก

ใน test_trip_planner เราจะส่ง

  1. ข้อความ ("สวัสดี")
  2. รูปภาพ (จุดสังเกต)
  3. วิดีโอ (ทะเลเมดิเตอร์เรเนียน)
  4. คลิปเสียง (ข้อความเสียงเกี่ยวกับกาเอตา)

ค้นหาความคิดเห็น # TODO create session service and memory service ภายในฟังก์ชัน 6_multimodal_agent/main.py

แทนที่ทั้งบรรทัดนี้ด้วยโค้ดต่อไปนี้

session_service = VertexAiSessionService(
    project=PROJECT_ID, location=LOCATION, agent_engine_id=agent_engine_id
)
memory_service = VertexAiMemoryBankService(
    project=PROJECT_ID, location=LOCATION, agent_engine_id=agent_engine_id
)

👉 ในไฟล์เดียวกัน 06_multimodal_agent/main.py ให้ค้นหาความคิดเห็น # TODO: create memory from session

แทนที่ทั้งบรรทัดนี้ด้วยโค้ดต่อไปนี้

    await memory_service.add_session_to_memory(final_session_state)

นี่คือเส้นมหัศจรรย์ โดยจะส่งสื่อสมบูรณ์ทั้งหมดไปยัง Vertex AI ซึ่งจะประมวลผลและจัดทำดัชนี

ขั้นตอนที่ 3: การดึงข้อมูล

👉💻 ในเทอร์มินัล Cloud Shell ให้เปิดไฟล์ใน Cloud Shell Editor โดยเรียกใช้คำสั่งต่อไปนี้

cloudshell edit ~/memory_agent_starter/06_multimodal_agent/agent.py

ตัวแทนมี PreloadMemoryTool

tools=[PreloadMemoryTool(), budget_tool]

เมื่อเซสชันใหม่เริ่มต้นขึ้น เครื่องมือนี้จะค้นหาประสบการณ์ที่ผ่านมาที่เกี่ยวข้องในธนาคารความทรงจำโดยอัตโนมัติและแทรกลงในบริบท

ขั้นตอนที่ 4: เรียกใช้ Brain

👉💻 เรียกใช้สคริปต์ในเทอร์มินัล Cloud Shell (หมายเหตุ: ต้องมีโปรเจ็กต์ Google Cloud ที่เปิดใช้ Vertex AI)

cd ~/memory_agent_starter
uv run python ~/memory_agent_starter/06_multimodal_agent/main.py

ดูขั้นตอนการยืนยันสุดท้าย

"จากรูปภาพ วิดีโอ และเสียงที่ฉันแชร์กับคุณก่อนหน้านี้..."

ตัวแทนจะตอบกลับว่า

"คุณควรไปเที่ยวกาเอตา คุณเคยเปิดวิดีโอทะเลเมดิเตอร์เรเนียนให้ฉันดู และเปิดคลิปเสียงที่คุณบอกว่าชอบกาเอตา"

ซึ่งเชื่อมโยงจุดต่างๆ ในสื่อประเภทต่างๆ จากอดีต

ประเด็นสำคัญ

กฎข้อที่ 6 ของหน่วยความจำ: ใช้คลังหน่วยความจำของ Vertex AI เพื่อประสบการณ์การใช้งานหน่วยความจำขั้นสุด โดยจะรวมข้อความ รูปภาพ และวิดีโอไว้ในสมองเดียวที่ค้นหาได้

9. บทสรุป

คุณได้เดินทางจากปลาทองที่ขี้หลงขี้ลืมไปจนถึงช้างที่ใช้หลายรูปแบบ

คุณสร้าง

ความสามารถ

ตัวแทนเซสชัน

หน่วยความจำในการสนทนาระยะสั้น

Multi-Agent

ความทรงจำของทีมที่แชร์

Persistent Agent

ประวัติระยะยาว

Stateful Agent

หน่วยความจำแบบไดนามิกที่อัปเดตตัวเอง

Profile Agent

หน่วยความจำของ Structured Data

เอเจนต์มัลติโมดัล

หน่วยความจำประสาทสัมผัสคล้ายมนุษย์

ความไว้วางใจสร้างขึ้นจากความทรงจำ การใช้รูปแบบเหล่านี้จะช่วยให้คุณสร้างเอเจนต์ที่คำนึงถึงเวลาและประวัติของผู้ใช้ ซึ่งจะนำไปสู่การโต้ตอบที่มีประสิทธิภาพและลึกซึ้งยิ่งขึ้น

เริ่มสร้างเอเจนต์ที่ปรับเปลี่ยนในแบบของคุณได้เลยวันนี้