1. ภาพรวม
ใน Codelab นี้ คุณจะได้สร้าง AI Creative Studio ซึ่งเป็นระบบแบบหลาย Agent ที่กระจายอยู่ซึ่งจะเปลี่ยนพรอมต์เดียวให้เป็นแคมเปญ Instagram ที่สมบูรณ์
พิมพ์ประโยคเดียว แล้วรับข้อมูลการวิจัยผู้ชม คำบรรยายแทนเสียง แนวคิดภาพ ข้อความที่ผ่านการตรวจสอบคุณภาพ และไทม์ไลน์โปรเจ็กต์ทั้งหมด ซึ่งทั้งหมดนี้สร้างขึ้นโดยทีมเอเจนต์ AI ที่ทำงานร่วมกัน
Agent ที่คุณจะสร้าง
Agent | บทบาท |
นักวางกลยุทธ์ของแบรนด์ | ค้นหาข้อมูลเชิงลึกของกลุ่มเป้าหมาย การวิเคราะห์คู่แข่ง และเทรนด์ปี 2025 บนเว็บ |
Copywriter | เขียนคำบรรยายภาพ Instagram พร้อมแฮชแท็กและ CTA โดยใช้ทักษะ ADK ที่โหลดหลักเกณฑ์ของแพลตฟอร์มและสูตรคำบรรยายภาพตามต้องการ |
Designer | สร้างแนวคิดแบบภาพและสร้างรูปภาพจริงผ่าน Gemini ซึ่งจัดเก็บไว้ใน GCS |
นักวิจารณ์ | สำเนาและภาพของรีวิว - ส่งคืน |
ผู้จัดการโปรเจ็กต์ | สร้างไทม์ไลน์ของโปรเจ็กต์และการแบ่งงาน โดยอาจซิงค์กับ Notion ผ่าน MCP ก็ได้ |
ผู้อำนวยการฝ่ายสร้างสรรค์ | ประสานงานผู้เชี่ยวชาญทั้ง 5 คนตามลำดับ - คุณป้อนพรอมต์เดียว แล้วโมเดลจะประสานงานส่วนที่เหลือ |
โดยจะติดตั้งใช้งานเอเจนต์ทั้ง 5 รายการเป็นไมโครเซอร์วิส Cloud Run ที่เป็นอิสระ โดยจะสื่อสารผ่านโปรโตคอล A2A ซึ่งเป็นมาตรฐานแบบเปิดที่ไม่ขึ้นอยู่กับภาษา ดังนั้น Agent ใดก็ตามจะเรียก Agent อื่นได้ไม่ว่าเฟรมเวิร์กจะเป็นอะไรก็ตาม ครีเอทีฟไดเรกเตอร์ทำงานใน Agent Runtime และเชื่อมต่อกับผู้เชี่ยวชาญแต่ละคนจากระยะไกล
สถาปัตยกรรม
สิ่งที่คุณจะได้เรียนรู้
- สร้างเอเจนต์ LLM ด้วย Google ADK -
Agent, คำสั่งของระบบ และเครื่องมือในตัว - จัดแพ็กเกจความรู้ของเอเจนต์ที่นำกลับมาใช้ใหม่เป็นไฟล์แบบแยกส่วนด้วย ADK Skills (
SkillToolset) - สร้างรูปภาพจริงโดยเชื่อมต่อ Agent ข้อความกับโมเดลรูปภาพผ่าน
FunctionTool - ผสานรวม API ภายนอกโดยไม่ต้องใช้โค้ดกาวที่กำหนดเองโดยใช้ Model Context Protocol (MCP)
- เปลี่ยน Agent ให้เป็นบริการที่เรียกใช้ผ่านเครือข่ายได้โดยใช้โปรโตคอล Agent-to-Agent (A2A) ผ่าน HTTPS
- ประสานงาน Agent ที่กระจายอยู่ด้วย
RemoteA2aAgentและAgentTool - แพ็กเกจและติดตั้งใช้งาน Agent อิสระเป็นไมโครเซอร์วิสของ Cloud Run
- โฮสต์ Orchestrator แบบมีสถานะใน Agent Runtime
- คงเวิร์กโฟลว์แบบหลาย Agent ที่มีขนาดยาวไว้ภายในขีดจํากัดบริบทโดยใช้การบีบอัดบริบท
- สร้างวงจรควบคุมคุณภาพ: ผลลัพธ์ของรีวิวจากนักวิจารณ์ → การแก้ไขอัตโนมัติเมื่อจำเป็น
สิ่งที่คุณต้องมี
- โปรเจ็กต์ Google Cloud ที่เปิดใช้การเรียกเก็บเงิน
- บทบาท IAM เจ้าของหรือผู้แก้ไข
- ความรู้พื้นฐานเกี่ยวกับ Python
2. ตั้งค่าสภาพแวดล้อม
สำหรับ Codelab นี้ เราจะใช้ Cloud Shell
Cloud Shell คืออะไร
Cloud Shell เป็นสภาพแวดล้อม Linux บนเบราว์เซอร์ฟรีที่มีการติดตั้งทุกอย่างไว้ล่วงหน้า ได้แก่ gcloud, git, Python, Docker และอื่นๆ คุณไม่จำเป็นต้องติดตั้งอะไรในเครื่อง
หากต้องการเปิด Cloud Shell ให้คลิกไอคอนเทอร์มินัลในแถบเครื่องมือด้านขวาบนของคอนโซล GCP

เมื่อเปิด Cloud Shell เป็นครั้งแรก ระบบจะแจ้งให้คุณยืนยันบัญชี ให้คลิกยืนยัน

จากนั้นคลิกให้สิทธิ์เพื่อให้ Cloud Shell เรียก Google Cloud API ได้

ตอนนี้ Cloud Shell พร้อมใช้งานแล้ว คุณจะเห็นข้อความต้อนรับในเทอร์มินัล 
ตรวจสอบสิทธิ์และกำหนดค่าโปรเจ็กต์
Cloud Shell ได้รับการตรวจสอบสิทธิ์ด้วยบัญชี Google ของคุณแล้ว ยืนยันบัญชีที่ใช้งานอยู่และค้นหารหัสโปรเจ็กต์
gcloud config list
นอกจากนี้ คุณยังดูรหัสโปรเจ็กต์ได้ในแดชบอร์ดคอนโซล GCP ที่แผงด้านซ้าย คัดลอกไว้ เนื่องจากคุณจะต้องใช้ในคำสั่งถัดไป

ตอนนี้ให้ตั้งค่าโปรเจ็กต์ดังนี้
export PROJECT_ID=$(gcloud config get-value project)
export REGION="us-central1" # Cloud Run deployment region
echo "Project: $PROJECT_ID"
ผลลัพธ์ที่คาดไว้
Project: my-project-123
เปิดใช้ API ที่จำเป็น
gcloud services enable \
aiplatform.googleapis.com \
apphub.googleapis.com \
run.googleapis.com \
cloudbuild.googleapis.com \
artifactregistry.googleapis.com \
generativelanguage.googleapis.com \
iam.googleapis.com \
cloudresourcemanager.googleapis.com \
storage.googleapis.com \
secretmanager.googleapis.com
โดยจะใช้เวลาประมาณ 2 นาที คุณจะเห็น Operation finished successfully เมื่อเสร็จแล้ว
ตั้งค่าข้อมูลรับรองเริ่มต้นของแอปพลิเคชัน (ADC)
Agent จะเรียกใช้แพลตฟอร์ม Agent ของ Gemini Enterprise โดยใช้ไลบรารี Google Auth ซึ่งต้องใช้ข้อมูลรับรองเริ่มต้นของแอปพลิเคชันแยกต่างหากจากการตรวจสอบสิทธิ์ CLI ของ gcloud
เรียกใช้คำสั่งนี้ 1 ครั้ง
gcloud auth application-default login
แท็บเบราว์เซอร์จะเปิดขึ้นเพื่อขอให้คุณยืนยัน คลิกอนุญาต คุณจะเห็นข้อมูลดังนี้
Credentials saved to file: ~/.config/gcloud/application_default_credentials.json
โคลนที่เก็บเริ่มต้น
Codelab นี้ใช้ที่เก็บเริ่มต้น ซึ่งเป็นโปรเจ็กต์โครงร่างที่มีโครงสร้างพื้นฐานทั้งหมด (Dockerfile, pyproject.toml, สคริปต์การติดตั้งใช้งาน) แต่มีตรรกะของ Agent ที่คุณต้องเขียน
git clone https://github.com/Saoussen-CH/mas-a2a-gcp.git ~/ai-creative-studio
cd ~/ai-creative-studio/workshop/starter
แต่ละ agent.py มีตัวยึดตำแหน่ง # TODO ที่คุณจะเขียนตรรกะของเอเจนต์ Dockerfile, pyproject.toml และสคริปต์การทำให้ใช้งานได้เสร็จสมบูรณ์แล้ว
กำหนดค่าตัวแปรสภาพแวดล้อม
คัดลอกตัวอย่างที่ให้ไว้และแทรกรหัสโปรเจ็กต์ในขั้นตอนเดียว
cp .env.example .env
sed -i "s|GOOGLE_CLOUD_PROJECT=your-project-id|GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project)|" .env
จากนั้นสร้างที่เก็บข้อมูล GCS ที่ดีไซเนอร์จะจัดเก็บรูปภาพที่สร้างขึ้น และอัปเดต .env ด้วยชื่อของที่เก็บข้อมูล
export PROJECT_ID=$(gcloud config get-value project)
export BUCKET_NAME="${PROJECT_ID}-campaign-images"
gcloud storage buckets create gs://${BUCKET_NAME} \
--location=us-central1 \
--project=${PROJECT_ID}
sed -i "s|GCS_IMAGES_BUCKET=your-project-id-campaign-images|GCS_IMAGES_BUCKET=${BUCKET_NAME}|" .env
จากนั้นตั้งค่าการรองรับ URL รูปภาพที่ลงนามแล้ว โดยครีเอทีฟไดเรกเตอร์จะสร้างลิงก์ HTTPS ที่คลิกได้สำหรับรูปภาพแต่ละรูปในสรุปแคมเปญสุดท้าย ซึ่งต้องใช้บัญชีบริการเพื่อลงนามใน URL เรียกใช้คำสั่งต่อไปนี้เพื่อกำหนดค่า
export PROJECT_NUMBER=$(gcloud projects describe $(gcloud config get-value project) --format="value(projectNumber)")
export SA_EMAIL="${PROJECT_NUMBER}-compute@developer.gserviceaccount.com"
export AGENT_RUNTIME_SA="service-${PROJECT_NUMBER}@gcp-sa-aiplatform-re.iam.gserviceaccount.com"
# Allow your user account to sign URLs locally (adk web)
gcloud iam service-accounts add-iam-policy-binding ${SA_EMAIL} \
--member="user:$(gcloud config get-value account)" \
--role="roles/iam.serviceAccountTokenCreator"
# Allow Agent Runtime to sign URLs when deployed
gcloud projects add-iam-policy-binding $(gcloud config get-value project) \
--member="serviceAccount:${AGENT_RUNTIME_SA}" \
--role="roles/iam.serviceAccountTokenCreator"
# Save SA email and project number to .env
grep -q "^SIGNING_SERVICE_ACCOUNT" .env \
&& sed -i "s|^SIGNING_SERVICE_ACCOUNT=.*|SIGNING_SERVICE_ACCOUNT=${SA_EMAIL}|" .env \
|| echo "SIGNING_SERVICE_ACCOUNT=${SA_EMAIL}" >> .env
grep -q "^GOOGLE_CLOUD_PROJECT_NUMBER" .env \
&& sed -i "s|^GOOGLE_CLOUD_PROJECT_NUMBER=.*|GOOGLE_CLOUD_PROJECT_NUMBER=${PROJECT_NUMBER}|" .env \
|| echo "GOOGLE_CLOUD_PROJECT_NUMBER=${PROJECT_NUMBER}" >> .env
เปิด .env ในเอดิเตอร์เพื่อตรวจสอบการตั้งค่าทั้งหมด
cloudshell edit .env
การดำเนินการนี้จะเปิด .env เป็นแท็บใน Cloud Shell Editor - คลิกปุ่มเปิดตัวแก้ไขในแถบเครื่องมือหากแผงตัวแก้ไขไม่ปรากฏ


ยืนยันว่าได้ตั้งค่าโปรเจ็กต์อย่างถูกต้อง
grep GOOGLE_CLOUD_PROJECT .env
ติดตั้งการอ้างอิง
เราใช้ uv ซึ่งเป็นตัวจัดการแพ็กเกจ Python ที่รวดเร็วและทันสมัยซึ่งจัดการสภาพแวดล้อมเสมือนและติดตั้งในเครื่องมือเดียว โดยเร็วกว่า pip ประมาณ 10-100 เท่า และเป็นวิธีที่แนะนำในการจัดการโปรเจ็กต์ Python
Cloud Shell มี uv ติดตั้งไว้แล้ว เอเจนต์ทั้งหมดใช้การอ้างอิงหลักเดียวกัน ดังนั้นติดตั้งเพียงครั้งเดียวก็ใช้ได้กับเอเจนต์ทุกตัวในโค้ดแล็บนี้
uv sync
uv sync คำสั่งจะอ่าน pyproject.toml และสร้างไดเรกทอรี .venv/ ที่มีทรัพยากร Dependency ทั้งหมด นอกจากนี้ ผู้เชี่ยวชาญแต่ละคนยังมี pyproject.toml ของตัวเองซึ่งใช้เฉพาะสำหรับการสร้าง Docker เท่านั้น โดยการติดตั้งที่แชร์ด้านบนจะครอบคลุมทุกอย่างที่คุณต้องการสำหรับการทดสอบในเครื่อง
3. ทำความเข้าใจ Google ADK
ก่อนเขียนโค้ด มาทำความเข้าใจชุดพัฒนาเอเจนต์ (ADK) ซึ่งเป็นเฟรมเวิร์กที่คุณจะใช้สร้างเอเจนต์ทุกตัวใน Codelab นี้กัน
ADK คืออะไร
Agent Development Kit (ADK) เป็นเฟรมเวิร์กที่ยืดหยุ่นและแยกเป็นโมดูลสำหรับการพัฒนาและติดตั้งใช้งาน AI Agent แม้ว่าจะได้รับการเพิ่มประสิทธิภาพสำหรับ Gemini และระบบนิเวศของ Google แต่ ADK ก็ไม่ขึ้นอยู่กับโมเดล ไม่ขึ้นอยู่กับการติดตั้งใช้งาน และสร้างขึ้นเพื่อให้ใช้งานร่วมกับเฟรมเวิร์กอื่นๆ ได้ ADK ได้รับการออกแบบมาเพื่อให้การพัฒนา Agent รู้สึกเหมือนการพัฒนาซอฟต์แวร์มากขึ้น เพื่อให้ผู้พัฒนาสร้าง ติดตั้งใช้งาน และจัดการสถาปัตยกรรม Agent ได้ง่ายขึ้น ซึ่งมีตั้งแต่การทำงานง่ายๆ ไปจนถึงเวิร์กโฟลว์ที่ซับซ้อน
ADK จะจัดการส่วนที่ซับซ้อน เช่น การเรียกใช้เครื่องมือ การสนทนาไปมา การจัดการบริบท การสตรีม เพื่อให้คุณมุ่งเน้นที่ตรรกะของ Agent ได้
องค์ประกอบที่ใช้สร้างสรรค์ของ Agent ประเภท ADK
Agent ทุกตัวประกอบด้วยองค์ประกอบพื้นฐาน 4 อย่าง ได้แก่
บล็อก | บทบาท |
โมเดล | LLM ที่ให้เหตุผลเกี่ยวกับเป้าหมาย กำหนดแผน และสร้างคำตอบ |
เครื่องมือ | ฟังก์ชันที่ดึงข้อมูลหรือดำเนินการโดยการเรียกใช้ API หรือบริการ |
การจัดการเป็นกลุ่ม | คงหน่วยความจำและสถานะไว้ตลอดการสนทนา กำหนดเส้นทางการเรียกใช้เครื่องมือ และส่งผลลัพธ์กลับไปยังโมเดล |
รันไทม์ | เรียกใช้ระบบเมื่อมีการเรียกใช้ ไม่ว่าจะในเครื่องผ่าน |
คำจำกัดความของ Agent
เอเจนต์ทั้ง 5 รายในโค้ดแล็บนี้ได้รับการกำหนดในลักษณะเดียวกัน ดังนี้
from google.adk.agents import Agent
from google.adk.tools.google_search_tool import google_search
root_agent = Agent(
name="brand_strategist", # unique identifier
model=os.getenv("GEMINI_MODEL", "gemini-2.5-flash"), # the LLM powering this agent
instruction=SYSTEM_INSTRUCTION, # the agent's persona, constraints, and output format
description="Brand strategist for market research, trend analysis, and competitive insights",
tools=[google_search], # functions the LLM can call
)
ช่อง | วัตถุประสงค์ |
| รหัสที่ไม่ซ้ำกัน - ใช้โดยตัวจัดสรรเพื่อกำหนดเส้นทางการเรียกใช้ |
| โมเดล Gemini ที่สนับสนุน Agent นี้ |
| พรอมต์ของระบบ - กำหนดบทบาท ข้อจำกัด และรูปแบบเอาต์พุตของเอเจนต์ |
| ข้อมูลสรุปบรรทัดเดียว - ตัวจัดสรรจะอ่านข้อมูลนี้เพื่อตัดสินใจว่าจะเรียกผู้เชี่ยวชาญคนใด |
| ฟังก์ชันที่ LLM เรียกใช้ได้ (ฟังก์ชันในตัว เช่น |
วิธีที่ ADK เรียกใช้ Agent
User message
│
▼
Agent (LLM) ← reads instruction + conversation history
│
├─► needs more info? → calls a tool → gets result → continues reasoning
│
└─► done reasoning → returns final text response
LLM จะตัดสินใจด้วยตนเองว่าจะเรียกใช้เครื่องมือใด เครื่องมือใด และมีอาร์กิวเมนต์ใดบ้าง คุณเขียนวิธีการ ส่วน ADK จะจัดการที่เหลือ
4. สร้างและทดสอบเอเจนต์นักวางแผนกลยุทธ์แบรนด์
มาเริ่มกันที่เอเจนต์ตัวแรกกันเลย นั่นก็คือนักวางแผนกลยุทธ์แบรนด์ ซึ่งเป็นเอเจนต์ที่ใช้สำหรับการค้นหาเท่านั้น เพื่อค้นหาข้อมูลเชิงลึกของกลุ่มเป้าหมาย การวิเคราะห์คู่แข่ง และหัวข้อที่กำลังมาแรงโดยใช้ Google Search
เปิดไฟล์ Agent โครงร่างใน Cloud Shell Editor โดยทำดังนี้
cloudshell edit agents/brand_strategist/agent.py
คุณจะเห็นส่วน # TODO 2 ส่วนให้กรอกข้อมูล
สิ่งที่ต้องทำ 1 - เขียนคำสั่งของระบบ
ก่อนอื่น คุณจะต้องเขียนคำสั่งของระบบสำหรับเอเจนต์ คำสั่งของระบบคือสตริงที่กำหนดบทบาท ข้อจำกัด และรูปแบบเอาต์พุตของเอเจนต์
SYSTEM_INSTRUCTION = f"""You are a Brand Strategist specializing in market research and trend analysis.
IMPORTANT: Today's date is {datetime.date.today().strftime("%B %d, %Y")}.
When conducting research, focus on current trends from {datetime.date.today().year}.
Use search queries like "[topic] trends {datetime.date.today().year}" for recent insights.
IMPORTANT: Your role is RESEARCH ONLY. You do NOT create campaign content, captions, or designs.
After providing research insights, your work is complete.
Your expertise:
- Identifying target audience insights and behaviors
- Analyzing competitor strategies
- Researching current social media trends
- Understanding platform algorithms and best practices
You have access to:
- google_search: Search the web for competitors, trends, and market insights
When given a campaign brief:
1. Use google_search to research the target audience's current interests
2. Search for and analyze 2-3 competitor brands
3. Identify 3-5 trending topics related to the product category
4. Provide high-level strategic insights - NOT specific campaign content
DO NOT create captions, copy, designs, or any campaign content.
Format your output as:
**Audience Insights:**
[Key behaviors and preferences based on research]
**Competitive Analysis:**
[What 2-3 competitors are doing - strengths and weaknesses]
**Trending Topics:**
[3-5 relevant trends to consider]
**Key Strategic Insights:**
[High-level themes and positioning opportunities]
"""
สิ่งที่ต้องทำ 2 - สร้าง root_agent
จากนั้นแทนที่ root_agent ที่ยังไม่สมบูรณ์ด้วย
root_agent = Agent(
name="brand_strategist",
model=os.getenv("GEMINI_MODEL", "gemini-2.5-flash"),
instruction=SYSTEM_INSTRUCTION,
description="Brand strategist for market research, trend analysis, and competitive insights",
tools=[google_search],
)
ทดสอบในเครื่องด้วยเว็บ UI ของ ADK
ตอนนี้มาทดสอบเอเจนต์โดยใช้ ADK Web UI ซึ่งเป็นอินเทอร์เฟซแชทในตัวสำหรับทดสอบเอเจนต์ก่อนที่จะนำไปใช้งานในระบบคลาวด์
uv run adk web agents --allow_origins='*'
คุณจะเห็นข้อมูลดังนี้
INFO: Started server process
INFO: Uvicorn running on http://localhost:8000
ตอนนี้เซิร์ฟเวอร์กำลังทำงานภายใน Cloud Shell
หากต้องการเปิดในเบราว์เซอร์ ให้ใช้ตัวอย่างเว็บโดยทำดังนี้
- ดูแถบเครื่องมือ Cloud Shell ที่ด้านบนของหน้า
- คลิกไอคอนตัวอย่างเว็บ (มีลักษณะคล้ายกล่องที่มีลูกศรขึ้น ที่ด้านขวาบนของแถบเครื่องมือ Cloud Shell)
- คลิก "เปลี่ยนพอร์ต" แล้วป้อน
8000จากนั้นคลิก "เปลี่ยนและแสดงตัวอย่าง"
แท็บเบราว์เซอร์ใหม่จะเปิดขึ้นพร้อมกับเว็บ UI ของ ADK คลิกเมนูแบบเลื่อนลง "เลือกตัวแทน" ที่ด้านซ้ายบน คุณจะเห็นรายชื่อตัวแทนทั้งหมด
เลือก brand_strategist เพื่อเริ่มทดสอบ
ลองใช้พรอมต์ทดสอบต่อไปนี้
ในช่องแชทของเว็บ UI ของ ADK ให้ลองทำดังนี้
Research the eco-friendly water bottle market for health-conscious millennialsWhat are the top Instagram trends in the wellness space in 2025?
คุณควรเห็นเอเจนต์เรียกใช้ Google Search และแสดงผลการวิจัยที่มีโครงสร้างพร้อมส่วนข้อมูลเชิงลึกของกลุ่มเป้าหมาย การวิเคราะห์คู่แข่ง และหัวข้อที่กำลังมาแรง
5. สร้างทักษะนักเขียนคำโฆษณา - ADK
บทบาท: เปลี่ยนการวิจัยแบรนด์ให้เป็นคำบรรยายใต้ภาพของ Instagram นักเขียนคำโฆษณาสร้างคำบรรยายแทนเสียง 3 รูปแบบที่ครอบคลุมโทนต่างๆ (สร้างแรงบันดาลใจ ให้ความรู้ ชุมชน) โดยแต่ละรูปแบบมีแฮชแท็กและ CTA
แนวคิด: ทักษะ ADK
แนวทางที่เรียบง่ายคือการฝังความรู้เกี่ยวกับแพลตฟอร์มทั้งหมด เช่น ขีดจำกัดอักขระ ระดับแฮชแท็ก สูตรคำบรรยายแทนเสียง ตัวอย่างเสียงของแบรนด์ ไว้ในพรอมต์ของระบบโดยตรง วิธีนี้ใช้ได้ แต่จะทำให้คำขอทุกรายการมีเนื้อหาที่เอเจนต์ต้องการเพียงบางครั้ง
ทักษะ ADK (SkillToolset เปิดตัวใน ADK 1.25.0) ช่วยให้คุณแพ็กความรู้ดังกล่าวลงในไฟล์แบบแยกส่วนที่มีการโหลด 3 ระดับ ดังนี้
- L1 - frontmatter (
name+descriptionในSKILL.md): พร้อมใช้งานเสมอ ใช้สำหรับการค้นพบทักษะ - L2 - instructions (เนื้อหาของ
SKILL.md): โหลดเมื่อ Agent เรียกใช้ทักษะ - L3 - ทรัพยากร (ไฟล์
references/และassets/): โหลดเมื่อ Agent อ่านอย่างชัดเจนเท่านั้น
คำสั่งของระบบจะลดลงเหลือเพียงคำสั่งสั้นๆ เกี่ยวกับบทบาทและ "โหลดทักษะก่อนเขียน" รายละเอียดแพลตฟอร์มจะเข้าสู่หน้าต่างบริบทเมื่อเอเจนต์ต้องการใช้จริงๆ เท่านั้น
ทักษะของนักเขียนคำโฆษณาจะอยู่ใน agents/copywriter/skills/instagram-copywriting/
skills/
instagram-copywriting/
SKILL.md ← L1 frontmatter (discovery) + L2 instructions (loaded on trigger)
references/
platform-guide.md ← L3: character limits, hashtag tiers, algorithm signals
caption-formulas.md ← L3: hook formulas, CTA patterns, full caption structures
assets/
brand-voice-examples.md ← L3: annotated real-world caption examples
เปิดไฟล์ในโปรแกรมแก้ไข Cloud Shell โดยตรง
cloudshell edit agents/copywriter/agent.py
TODO 1 - นำเข้า load_skill_from_dir และ skill_toolset
ค้นหาความคิดเห็น # TODO 1: Import load_skill_from_dir and skill_toolset แล้วเพิ่มการนำเข้า 2 รายการต่อไปนี้
from google.adk.skills import load_skill_from_dir
from google.adk.tools import skill_toolset
TODO 2 - โหลดทักษะและสร้าง SkillToolset
ค้นหาความคิดเห็น 2 รายการด้านล่างการนำเข้า
# TODO 2: Load the instagram-copywriting skill from the skills/ directory
# TODO 2: Create a SkillToolset with the loaded skill
แทนที่ด้วย
_instagram_skill = load_skill_from_dir(
pathlib.Path(__file__).parent / "skills" / "instagram-copywriting"
)
_copywriting_skills = skill_toolset.SkillToolset(skills=[_instagram_skill])
load_skill_from_dir อ่าน SKILL.md รวมถึงไฟล์ใดก็ตามใน references/ และ assets/ SkillToolset จะห่อหุ้มไว้ในรูปแบบที่เอเจนต์ ADK ยอมรับ ซึ่งก็คือชุดเครื่องมือ ไม่ใช่ทักษะดิบ
TODO 3 - ลงทะเบียนชุดเครื่องมือกับตัวแทน
ค้นหา tools=[], # TODO 3: Add the SkillToolset here แล้วแทนที่ด้วย
tools=[_copywriting_skills],
เปิดไฟล์ทักษะเพื่อดูโครงสร้างของไฟล์
cloudshell edit agents/copywriter/skills/instagram-copywriting/SKILL.md
เปิด UI เว็บของ ADK ไว้ ใช้เมนูแบบเลื่อนลงของเอเจนต์เพื่อเปลี่ยนไปใช้ copywriter โดยไม่ต้องรีสตาร์ทเซิร์ฟเวอร์
หากไม่ได้ทำงาน ให้เริ่มอีกครั้งโดยทำดังนี้
uv run adk web agents --allow_origins='*'
ลองเลย: เปลี่ยนเมนูแบบเลื่อนลงเป็น copywriter แล้วส่ง
You are writing captions for EcoFlow Smart Water Bottle targeting health-conscious millennials aged 25-35.
Audience insight: they prioritize sustainability, track health metrics, and share lifestyle content.
Competitor insight: Hydro Flask dominates with lifestyle branding; S'well leads on premium aesthetics.
Write 3 Instagram captions - one inspirational, one educational, one community-focused. Include 5 hashtags each and a CTA.
6. สร้างดีไซเนอร์ - การสร้างรูปภาพแบบมัลติโมดัล
เปิด UI เว็บของ ADK ไว้ ใช้เมนูแบบเลื่อนลงของตัวแทนเพื่อเปลี่ยนตัวแทนโดยไม่ต้องรีสตาร์ทเซิร์ฟเวอร์
บทบาท: สร้างแนวคิดแบบภาพสำหรับคำบรรยายแต่ละรายการและสร้างรูปภาพจริงโดยใช้การสร้างรูปภาพดั้งเดิมของ Gemini นักออกแบบจะแสดงผลแนวคิดแบบภาพ 1 รายการต่อคำบรรยาย 1 รายการ โดยมีพรอมต์ สไตล์ จานสี อารมณ์ และรูปแบบ Instagram แบบละเอียด จากนั้นจะเรียกใช้เครื่องมือ generate_image ทันทีเพื่อสร้างรูปภาพจริงและอัปโหลดไปยัง GCS
แนวคิด: เชื่อมต่อ Agent ข้อความกับโมเดลรูปภาพผ่านเครื่องมือ
ดีไซเนอร์ทำงานบน gemini-3-flash-preview (โมเดลข้อความที่ตั้งค่าผ่าน GEMINI_MODEL ใน .env) แต่การสร้างรูปภาพต้องใช้โมเดลเฉพาะ (gemini-3.1-flash-image) โมเดลรูปภาพดังกล่าวไม่รองรับการเรียกใช้ฟังก์ชัน จึงใช้เป็นเอเจนต์ ADK โดยตรงไม่ได้ แต่จะห่อหุ้มไว้ในฟังก์ชัน Python ธรรมดาและลงทะเบียนเป็น FunctionTool แทน
นี่คือรูปแบบสำหรับโมเดลหรือ API ที่ LLM เรียกใช้โดยตรงไม่ได้ ให้ห่อหุ้มไว้ในเครื่องมือ อนุญาตให้เอเจนต์ประสานงานเมื่อใดที่จะเรียกใช้ และรับผลลัพธ์ที่มีโครงสร้างกลับมา
Designer agent (text model)
│
│ decides visual concept, writes image prompt
▼
generate_image tool
│
│ calls gemini-3.1-flash-image
│ uploads result to GCS
▼
{"status": "success", "gcs_uri": "gs://..."}
│
│ returned to agent, included in response
▼
Critic (receives gcs_uri, passes to Vertex AI for multimodal review)
เปิดไฟล์ในโปรแกรมแก้ไข Cloud Shell โดยตรง
cloudshell edit agents/designer/image_gen_tool.py
โดยมีลายเซ็นของฟังก์ชัน การตั้งค่าสภาพแวดล้อม และการแทรกสัดส่วนภาพ ทำตาม TODO ทั้ง 3 รายการตามลำดับ
TODO 1 - เรียกใช้โมเดลรูปภาพของ Gemini
ค้นหาความคิดเห็น # TODO 1 แล้วแทนที่ด้วยข้อความต่อไปนี้
client = genai.Client(vertexai=True, project=project_id, location=location)
response = client.models.generate_content(
model=image_model,
contents=prompt_with_aspect,
config=types.GenerateContentConfig(
response_modalities=["IMAGE", "TEXT"],
http_options=types.HttpOptions(
retry_options=types.HttpRetryOptions(
attempts=5, exp_base=2, initial_delay=30,
http_status_codes=[429, 500, 503, 504],
),
timeout=180_000,
),
),
)
TODO 2 - Extract image bytes from the response
ค้นหาความคิดเห็น # TODO 2 แล้วแทนที่ด้วยข้อความต่อไปนี้
image_bytes = None
mime_type = "image/png"
for part in response.candidates[0].content.parts:
if part.inline_data is not None:
image_bytes = part.inline_data.data
mime_type = part.inline_data.mime_type or "image/png"
break
if not image_bytes:
return {"status": "error", "error": "Gemini returned no image data"}
สิ่งที่ต้องทำ 3 - อัปโหลดไปยัง GCS และส่งคืน URI
ค้นหาความคิดเห็น # TODO 3 แล้วแทนที่ด้วยข้อความต่อไปนี้
ext = "jpg" if "jpeg" in mime_type else "png"
from google.cloud import storage
gcs_client = storage.Client(project=project_id)
bucket = gcs_client.bucket(bucket_name)
blob_name = f"campaign-images/{concept_name}-{uuid.uuid4().hex[:8]}.{ext}"
blob = bucket.blob(blob_name)
blob.upload_from_file(io.BytesIO(image_bytes), content_type=mime_type)
gcs_uri = f"gs://{bucket_name}/{blob_name}"
ลองเลย: เปลี่ยนเมนูแบบเลื่อนลงเป็น designer แล้วส่ง
Create a visual concept and generate the image for an EcoFlow Smart Water Bottle Instagram post targeting health-conscious millennials.
Style: clean, modern, lifestyle-focused. Include a detailed prompt with color palette, mood, and format (1080x1080 or 1080x1350).
7. สร้างเอาต์พุตที่มีโครงสร้างของ Critic
บทบาท: ตรวจสอบคุณภาพของข้อความและภาพก่อนส่งให้ผู้จัดการโปรเจ็กต์ คะแนนของ Critic จะแสดงทั้งในผลงานและผลตอบแทน APPROVED หรือ NEEDS_REVISION พร้อมคำแนะนำที่เฉพาะเจาะจง เมื่อมีgcs_uriในอินพุต ระบบจะเรียกใช้เครื่องมือ review_image เพื่อตรวจสอบภาพแต่ละภาพที่สร้างขึ้นด้วยสายตาก่อนให้คะแนน
แนวคิด: เมื่อใดควรใช้โมเดล Pydantic สำหรับเอาต์พุตของ Gemini
กฎนี้เกี่ยวข้องกับผู้ที่ใช้เอาต์พุต
- โค้ด Python ใช้ → ใช้
response_schema+ Pydantic โค้ดไม่สามารถจัดการความคลุมเครือได้ คุณจึงต้องมีโครงสร้างที่รับประกันเพื่อดึงข้อมูลฟิลด์ได้อย่างน่าเชื่อถือ - LLM จะใช้ → รูปแบบข้อความ + คำสั่งของระบบก็เพียงพอ LLM เข้าใจกฎการจัดรูปแบบและยอมรับความแตกต่างได้
ใน review_image โค้ด Python ต้องมี score, approval_status, what_works, issues และ suggestions เป็นค่าที่พิมพ์ การส่ง response_schema=_GeminiReview จะจำกัด Gemini ที่ระดับ API ให้แสดงผล JSON ที่ถูกต้อง ส่วน model_validate_json() จะแยกวิเคราะห์เป็นออบเจ็กต์ที่มีการพิมพ์ซึ่งโค้ดของคุณใช้ได้อย่างน่าเชื่อถือ
class _GeminiReview(BaseModel):
score: int = Field(ge=1, le=10)
approval_status: Literal["APPROVED", "NEEDS_REVISION"]
what_works: str
issues: str
suggestions: str
เปิดไฟล์ในโปรแกรมแก้ไข Cloud Shell โดยตรง
cloudshell edit agents/critic/image_review_tool.py
โดยมีโมเดล Pydantic และพรอมต์ให้ ทำตาม 3 รายการที่ต้องทำตามลำดับ
TODO 1 - สร้างชิ้นส่วนรูปภาพจาก URI ของ GCS
ค้นหาความคิดเห็น # TODO 1 แล้วแทนที่ด้วยข้อความต่อไปนี้
image_part = types.Part.from_uri(file_uri=gcs_uri, mime_type=mime_type)
TODO 2 - เรียกใช้ Gemini ด้วยสคีมาการตอบกลับที่มีโครงสร้าง
ค้นหาความคิดเห็น # TODO 2 แล้วแทนที่ด้วยข้อความต่อไปนี้
response = client.models.generate_content(
model=model,
contents=[image_part, prompt],
config=types.GenerateContentConfig(
response_schema=_GeminiReview,
response_mime_type="application/json",
),
)
TODO 3 - แยกวิเคราะห์การตอบกลับและแสดงผลลัพธ์
ค้นหาความคิดเห็น # TODO 3 แล้วแทนที่ด้วยข้อความต่อไปนี้
review = _GeminiReview.model_validate_json(response.text)
return ImageReviewResult(status="success", concept_name=concept_name, **review.model_dump())
ลองเลย: เปลี่ยนเมนูแบบเลื่อนลงเป็น critic แล้วส่ง
Review this Instagram caption for an eco-friendly water bottle brand targeting millennials:
"Hydrate smarter, live greener. 💧 Our EcoFlow bottle tracks your intake, keeps your drink cold for 24h, and never touches single-use plastic. Because what you drink from matters as much as what you drink. #EcoFlow #HydrationGoals #SustainableLiving #ZeroWaste #HealthyHabits - Shop link in bio."
Score it and indicate APPROVED or NEEDS_REVISION with specific feedback.
ตรวจสอบว่าคำตอบมี **POSTS REVIEW:**, Status: APPROVED (หรือ NEEDS_REVISION) และ **OVERALL ASSESSMENT:** หากมีส่วนดังกล่าว Critic ก็พร้อมที่จะเชื่อมต่อกับ Orchestrator
เมื่อทดสอบเอเจนต์ทั้ง 3 รายการเสร็จแล้ว ให้กด Ctrl+C เพื่อหยุดเซิร์ฟเวอร์
8. สร้างเอเจนต์ผู้จัดการโปรเจ็กต์ด้วย MCP
ผู้จัดการโปรเจ็กต์แนะนำแนวคิดใหม่ นั่นคือ MCP (Model Context Protocol)
เปิดไฟล์โดยทำดังนี้
cloudshell edit agents/project_manager/agent.py
ไฟล์นี้มีความซับซ้อนมากขึ้น โดยมีcreate_project_manager_agent()ฟังก์ชันที่มี 2 สาขา ได้แก่ สาขาที่ไม่มี Notion (ไทม์ไลน์แบบข้อความเท่านั้น) และสาขาที่มีชุดเครื่องมือ MCP ของ Notion คุณจะต้องกรอกข้อมูลทั้ง 2 สาขา
ปัญหาที่ MCP แก้ไข
เอเจนต์ของคุณต้องเรียกใช้บริการภายนอก เช่น สร้างหน้าใน Notion คุณสามารถเขียนโค้ด Python ที่เรียกใช้ Notion REST API ได้โดยตรง แต่
- นักพัฒนาซอฟต์แวร์ทุกคนเขียน Wrapper ที่แตกต่างกัน
- คุณต้องดูแลโค้ดการผสานรวมที่กำหนดเอง
- LLM จะไม่ทราบว่ามี API อยู่ เว้นแต่คุณจะอธิบายทุกปลายทางด้วยตนเอง
MCP แก้ปัญหานี้ด้วยการกำหนดวิธีมาตรฐานสำหรับบริการภายนอกในการแสดงความสามารถของตนเป็นเครื่องมือที่ LLM สามารถค้นหาและเรียกใช้ได้โดยอัตโนมัติ
MCP คืออะไร
MCP (Model Context Protocol) เป็นมาตรฐานแบบเปิด (เผยแพร่โดย Anthropic) สำหรับการเชื่อมต่อ Agent AI กับเครื่องมือและแหล่งข้อมูลภายนอก โดยจะทำงานเหมือนอะแดปเตอร์แบบใช้ได้ทั่วโลก
เซิร์ฟเวอร์ MCP คือโปรแกรมขนาดเล็กที่มีหน้าที่ดังนี้
- เรียกใช้ API ภายนอก (Notion, GitHub, ฐานข้อมูล, ระบบไฟล์...)
- แสดง API นั้นเป็นรายการเครื่องมือที่มีการพิมพ์และจัดทำเอกสาร
- สื่อสารกับเอเจนต์ผ่านโปรโตคอลอย่างง่าย (stdio หรือ HTTP)
เอเจนต์จะเชื่อมต่อกับเซิร์ฟเวอร์ MCP ค้นหาเครื่องมือที่พร้อมใช้งานโดยอัตโนมัติ และเรียกใช้เครื่องมือเหล่านั้นได้เหมือนกับเครื่องมืออื่นๆ โดย LLM จะเห็น API-post-page(...) เป็นฟังก์ชันที่เรียกใช้ได้
A2A กับ MCP แตกต่างกันอย่างไร
นี่เป็นจุดที่มักเกิดความสับสน ความแตกต่างที่สำคัญมีดังนี้
A2A | MCP | |
สิ่งที่เชื่อมต่อ | Agent ↔ Agent | ตัวแทน ↔ เครื่องมือ/บริการภายนอก |
อีกด้านหนึ่งคือ | LLM Agent อื่น | API Wrapper (ไม่มี LLM) |
ตัวอย่าง | ครีเอทีฟไดเรกเตอร์โทรหานักวางกลยุทธ์ของแบรนด์ | Project Manager เรียกใช้ Notion API |
โปรโตคอล | JSON-RPC ผ่าน HTTPS | สตรีม stdio หรือ HTTP |
กำหนดโดย | Anthropic |
ลองคิดแบบนี้
- A2A = วิธีที่เอเจนต์พูดคุยกับเอเจนต์อื่นๆ
- MCP = วิธีที่ตัวแทนพูดคุยกับเครื่องมือและบริการ
ในโปรเจ็กต์นี้จะใช้ทั้ง 2 อย่างร่วมกัน
Creative Director
│
│ (A2A) Brand Strategist ─── (google_search tool built into ADK)
│ (A2A) Copywriter
│ (A2A) Designer
│ (A2A) Critic
│ (A2A) Project Manager
│
│ (MCP) notion-mcp-server ──► Notion REST API
วิธีการทำงานของ MCP ในโปรเจ็กต์นี้
เมื่อเอเจนต์ทำงาน ADK จะเปิดใช้ notion-mcp-server เป็นกระบวนการย่อย กระบวนการดังกล่าวจะแสดงเครื่องมือเหล่านี้ต่อ LLM โดยตรง
เครื่องมือ | การทำงาน |
| ดึงข้อมูลสคีมา (ชื่อพร็อพเพอร์ตี้ ประเภท ค่าที่ถูกต้อง) |
| ค้นหาหน้าเว็บที่มีอยู่ |
| สร้างหน้าใหม่ |
| อัปเดตหน้าเว็บที่มีอยู่ |
LLM จะเรียกฟังก์ชันเหล่านี้เหมือนกับฟังก์ชันอื่นๆ โดยไม่ทราบว่าฟังก์ชันเหล่านี้ผ่าน MCP ไปยัง Notion REST API เบื้องหลัง
ทำไมต้อง stdio ทำไมจึงไม่ใช้แค่ HTTP
เซิร์ฟเวอร์ MCP ทำงานเป็นกระบวนการย่อยของเอเจนต์ โดยสื่อสารผ่าน stdin/stdout ซึ่งหมายความว่า
- ไม่ต้องใช้พอร์ตเครือข่ายเพิ่มเติม
- วงจรการใช้งานได้รับการจัดการโดยตัวแทน (เริ่มเมื่อต้องการ หยุดเมื่อออก)
- ทุกอย่างจะจัดส่งในอิมเมจ Docker เดียว ไม่ต้องติดตั้งใช้งานบริการแยกต่างหาก
(ไม่บังคับ) เปิดใช้การผสานรวม Notion
คุณข้ามส่วนนี้ทั้งหมดได้ เอเจนต์ผู้จัดการโปรเจ็กต์จะสร้างไทม์ไลน์แคมเปญแบบข้อความที่สมบูรณ์เสมอ ไม่ว่าจะใช้ Notion หรือไม่ก็ตาม หากข้ามการตั้งค่านี้ เอเจนต์จะกลับไปใช้โหมดในหน่วยความจำและแสดงไทม์ไลน์เป็นข้อความธรรมดาในแชท ทุกอย่างจะยังคงทำงานได้ตามปกติ เพียงแต่คุณจะไม่เห็นงานปรากฏในฐานข้อมูล Notion ไปที่ส่วนสิ่งที่ต้องทำ 1 โดยตรงหากต้องการข้าม
หากมีบัญชี Notion และต้องการดูการผสานรวม MCP ในการทำงาน ให้ตั้งค่าด้านล่างตอนนี้ TODO ที่ตามมาจะอ้างอิงรหัสฐานข้อมูล Notion ซึ่งคุณจะดูได้ที่นี่
ขั้นตอนที่ 1 - สร้างฐานข้อมูล Notion จากเทมเพลต
เราใช้เทมเพลต Notion Projects & Tasks อย่างเป็นทางการเป็นฐานข้อมูล เราเลือกเทมเพลตนี้โดยตั้งใจเพื่อแสดงให้เห็นถึงการตั้งค่าที่ซับซ้อนในโลกแห่งความเป็นจริง ซึ่งมีพร็อพเพอร์ตี้หลายประเภท (สถานะ ช่วงวันที่ ความสัมพันธ์ ตัวเลือก) ที่มีชื่อที่ไม่ชัดเจน นี่เป็นการทดสอบที่ยอดเยี่ยมสำหรับการค้นหาสคีมาแบบไดนามิกของ MCP โดยเอเจนต์ต้องค้นหาชื่อพร็อพเพอร์ตี้ที่แน่นอนในรันไทม์แทนที่จะฮาร์ดโค้ด
คลิกลิงก์ด้านล่างเพื่อเพิ่มเทมเพลตลงในพื้นที่ทำงาน Notion
→ เพิ่มเทมเพลต "โปรเจ็กต์และงาน" ลงใน Notion

เมื่อเพิ่มแล้ว คุณจะมีฐานข้อมูลที่ลิงก์ 2 รายการ ได้แก่ โปรเจ็กต์และงาน เทมเพลตมาพร้อมกับรายการตัวอย่าง ลบรายการทั้งหมดก่อนดำเนินการต่อ เพื่อให้ตัวแทนเริ่มต้นด้วยพื้นที่ทํางานที่สะอาด (เลือกทั้งหมด → ลบ)
ขั้นตอนที่ 2 - สร้างการผสานรวม Notion
สร้างการผสานรวม
- ไปที่ notion.so/my-integrations
- คลิก New Integration → ตั้งชื่อว่า
AI Creative Studio - เชื่อมโยงกับพื้นที่ทำงาน
- คลิกกำหนดการตั้งค่า → ตรวจสอบว่าได้เลือกความสามารถอ่านเนื้อหา อัปเดตเนื้อหา และแทรกเนื้อหาทั้งหมดแล้ว

- คัดลอกโทเค็นการผสานรวมภายใน (
ntn_...) แล้ววางลงในไฟล์.envดังนี้
NOTION_TOKEN=ntn_your-token-here
เชื่อมต่อการผสานรวมกับฐานข้อมูล
- เปิดหน้าเทมเพลตที่คุณเพิ่งทำซ้ำ แล้วคลิกฐานข้อมูลโปรเจ็กต์
- คลิกเมนู
...(ด้านขวาบน) → การเชื่อมต่อ → เพิ่มการเชื่อมต่อ → เลือกAI Creative Studio


- ทำเช่นเดียวกันกับฐานข้อมูลงาน
รับรหัสฐานข้อมูล
- คลิกลิงก์ฐานข้อมูลโปรเจ็กต์เพื่อเปิด โดยจะเปิดในหน้าของตัวเองพร้อม URL เช่น
https://www.notion.so/9887b6a94f7f83f68f8581e038d1aaa4?v=2c37b6a94f7f838685f1086e312c7278

รหัสฐานข้อมูลคือ UUID แรกใน URL ซึ่งอยู่ก่อน ?v=
https://www.notion.so/{DATABASE_ID}?v=...
^^^^^^^^^^^^^^^^
9887b6a94f7f83f68f8581e038d1aaa4 ← this is your DATABASE_ID
- ทำเช่นเดียวกันกับลิงก์ฐานข้อมูลงานเพื่อรับรหัสฐานข้อมูล
- เพิ่มค่าทั้ง 3 ค่าลงใน
.env
NOTION_TOKEN=ntn_your-token-here
NOTION_PROJECT_DATABASE_ID=9887b6a94f7f83f68f8581e038d1aaa4 # <-- your Projects DB ID
NOTION_TASKS_DATABASE_ID=your-tasks-db-id # <-- your Tasks DB ID
ขั้นตอนที่ 3 - ติดตั้งเซิร์ฟเวอร์ Notion MCP
Project Manager เชื่อมต่อกับ Notion ผ่าน@notionhq/notion-mcp-serverแพ็กเกจ Node.js อย่างเป็นทางการ ติดตั้งแพ็กเกจนี้ทั่วโลกโดยทำดังนี้
npm install -g @notionhq/notion-mcp-server@1.9.1
ยืนยันการติดตั้งโดยทำดังนี้
npm list -g @notionhq/notion-mcp-server
ผลลัพธ์ที่คาดไว้
└── @notionhq/notion-mcp-server@1.9.1
notion-mcp-server: command not found
? ตรวจสอบว่าได้ติดตั้ง Node.js (node --version) และ npm global bin อยู่ใน PATH (export PATH=$PATH:$(npm bin -g))
ขั้นตอนที่ 4 - ยืนยัน .env
เปิด .env และยืนยันว่าได้ตั้งค่า Notion ทั้ง 3 ค่าแล้ว (คุณเพิ่มค่าเหล่านี้ในขั้นตอนที่ 2)
cloudshell edit .env
NOTION_TOKEN=ntn_... # integration token
NOTION_PROJECT_DATABASE_ID=... # Projects database ID
NOTION_TASKS_DATABASE_ID=... # Tasks database ID
เอเจนต์ Project Manager จะตรวจหาตัวแปรเหล่านี้โดยอัตโนมัติเมื่อเริ่มต้น และเปิดใช้ชุดเครื่องมือ Notion MCP
วิธีการทำงานของการค้นพบสคีมา
ผู้จัดการโปรเจ็กต์ใช้การค้นหาสคีมาแบบไดนามิก ซึ่งจะไม่ฮาร์ดโค้ดชื่อพร็อพเพอร์ตี้ Notion
Step 1: Call API-retrieve-a-database to discover exact property names
Step 2: Read the "properties" object in the response
Step 3: Use ONLY discovered property names (case-sensitive) in API calls
Step 4: For select/status fields, use only values from the options array
ซึ่งหมายความว่าเอเจนต์จะปรับให้เข้ากับโครงสร้างฐานข้อมูล Notion โดยอัตโนมัติ ไม่ว่าคุณจะเปลี่ยนชื่อพร็อพเพอร์ตี้เป็นภาษาฝรั่งเศส ภาษาอาหรับ หรือภาษาอื่นๆ เอเจนต์ก็จะยังคงทำงานได้
สิ่งที่ต้องทำ 1 - เขียนคำสั่งของระบบ
ตัวเริ่มต้นจะคำนวณ notion_section ซึ่งเป็นสตริงว่างเมื่อไม่ได้กำหนดค่า Notion หรือบล็อกที่มีรหัสฐานข้อมูลพร้อมคำแนะนำเครื่องมือแบบเต็มเมื่อกำหนดค่าแล้ว วิธีนี้จะช่วยให้คำสั่ง Notion ไม่ปรากฏในพรอมต์ของเอเจนต์ที่ไม่มี Notion และ LLM จะไม่เห็นกฎสำหรับเครื่องมือที่ไม่มี
งานของคุณคือการแทนที่ตัวยึดตำแหน่ง return ด้วยคำสั่งระบบจริงที่ใช้ {notion_section}
return f"""You are a Project Manager specializing in creative campaign execution.
Today's date is {datetime.date.today().strftime("%B %d, %Y")}.
Use this as the starting point for all timelines.
Your goal: create a complete project plan for the campaign.
{notion_section}
**Project Timeline:**
Phase 1: Strategy & Research | [date] → [date] | [key activities]
Phase 2: Content Creation | [date] → [date] | [key activities]
Phase 3: Review & Revision | [date] → [date] | [key activities]
Phase 4: Launch & Monitoring | [date] → [date] | [key activities]
**Task List:**
| Task | Owner | Deadline | Status |
[list each task with realistic deadlines from today; set Owner to TBD]
**Budget Breakdown:**
[by category with approximate allocations]
**Milestones:**
[3-5 key checkpoints with dates]
**Notion Status:**
[What happened - e.g. "Project created (ID: xxx), 8 tasks linked" or "Notion not configured - text timeline only"]
"""
TODO 2 - Agent ที่ไม่มี Notion
ใน create_project_manager_agent() ในสาขา if not notion_token ให้แทนที่เอเจนต์ที่ไม่สมบูรณ์ด้วย
return Agent(
name="project_manager",
model=os.getenv("GEMINI_MODEL", "gemini-2.5-flash"),
generate_content_config=GENERATE_CONTENT_CONFIG,
instruction=get_system_instruction(),
description="Project manager that creates campaign timelines and task breakdowns",
)
TODO 3 - Agent ที่มี MCP ของ Notion
หมายเหตุ: ไฟล์เริ่มต้นมีhandle_notion_errorการเรียกกลับที่เขียนไว้ล่วงหน้าอยู่แล้วเหนือ create_project_manager_agent() โดยจะสกัดกั้นข้อผิดพลาดของ Notion API (400/404) และแทนที่เพย์โหลดข้อผิดพลาดดิบด้วยข้อความที่ชัดเจนและนำไปใช้ได้ เพื่อให้ LLM สามารถแก้ไขตัวเองได้ คุณเพียงแค่ต้องต่อสายผ่าน after_tool_callback
ก่อนอื่น ให้อ่านรหัสฐานข้อมูลทั้ง 2 รายการที่ด้านบนของ create_project_manager_agent()
notion_token = os.getenv("NOTION_TOKEN")
notion_project_db_id = os.getenv("NOTION_PROJECT_DATABASE_ID")
notion_tasks_db_id = os.getenv("NOTION_TASKS_DATABASE_ID")
จากนั้นในสาขา else ให้สร้างชุดเครื่องมือ MCP และเอเจนต์
from google.adk.tools.mcp_tool import McpToolset, StdioConnectionParams
from mcp import StdioServerParameters
server_params = StdioServerParameters(
command="notion-mcp-server",
env={
"NOTION_TOKEN": notion_token,
"PATH": os.environ.get("PATH", ""),
}
)
notion_toolset = McpToolset(
connection_params=StdioConnectionParams(
server_params=server_params,
timeout=30.0
)
)
return Agent(
name="project_manager",
model=os.getenv("GEMINI_MODEL", "gemini-2.5-flash"),
generate_content_config=GENERATE_CONTENT_CONFIG,
after_tool_callback=handle_notion_error,
instruction=get_system_instruction(
project_database_id=notion_project_db_id,
tasks_database_id=notion_tasks_db_id,
),
description="Project manager with Notion integration for task tracking",
tools=[notion_toolset],
)
แนวทางปฏิบัติแนะนำ: อย่าทำให้การผสานรวมที่ไม่บังคับล้มเหลวโดยเด็ดขาด ไทม์ไลน์ข้อความคือสิ่งที่ต้องส่งมอบหลักเสมอ ส่วน Notion เป็นเพียงส่วนเสริม
ทดสอบ Project Manager ในเครื่องด้วย ADK Web
uv run adk web agents --allow_origins='*'
เปิดตัวอย่างเว็บในพอร์ต 8000 ใช้เมนูแบบเลื่อนลงของตัวแทนเพื่อเลือก project_manager จากนั้นลองทำดังนี้
Create a project plan for a GreenBrew organic coffee brand Instagram campaign.
Budget: $2,500. Launch in 3 weeks. Target audience: eco-conscious millennials aged 22-30.
Include phases, tasks with deadlines from today, and milestones.
คุณจะเห็นไทม์ไลน์ข้อความที่มีโครงสร้างพร้อมเฟส รายการงาน และเหตุการณ์สำคัญ หากตั้งค่าข้อมูลเข้าสู่ระบบของ Notion ใน .env Agent จะสร้างรายการในพื้นที่ทำงาน Notion ด้วย
9. ทำความเข้าใจโปรโตคอล A2A
เราจะใช้โปรโตคอล Agent-to-Agent (A2A) เพื่อเชื่อมต่อเอเจนต์ต่างๆ ในระบบของเรา มาดูวิธีการทำงานกัน
ปัญหาที่ A2A แก้ไข
ลองนึกภาพว่าคุณมีเอเจนต์นักวางกลยุทธ์แบรนด์ที่สร้างด้วย ADK และเอเจนต์นักเขียนคำโฆษณาที่สร้างด้วย LangGraph จะโทรหาอีกฝ่ายได้อย่างไร โดยจะใช้ภาษาภายในที่แตกต่างกัน คุณจะต้องเขียนโค้ดกาวที่กำหนดเองทุกครั้ง
A2A แก้ปัญหานี้ด้วยการกำหนดภาษาที่เป็นสากลซึ่งเอเจนต์ทุกรายสามารถใช้ได้ ไม่ว่าจะเป็นเฟรมเวิร์กใดก็ตาม ซึ่งเป็น HTTP ของโลกเอเจนต์ ซึ่งเป็นมาตรฐานที่ทุกคนเห็นพ้องต้องกันเพื่อให้ทุกคนคุยกับทุกคนได้
A2A คืออะไร
Agent-to-Agent (A2A) เป็นมาตรฐานแบบเปิดสำหรับการสื่อสารของเอเจนต์ที่เผยแพร่โดย Google โดยจะกำหนดสิ่งต่อไปนี้
- ตัวแทนอธิบายตัวเองอย่างไร - การ์ดตัวแทนที่
/.well-known/agent.json - วิธีที่เอเจนต์อื่นเรียกใช้ - JSON-RPC ผ่าน HTTPS
- วิธีแสดงผลลัพธ์ - การสตรีมหรือการตอบกลับครั้งเดียว
สิ่งที่ทำให้ A2A มีความยืดหยุ่น
- ไม่ขึ้นอยู่กับภาษา - Agent Python สามารถพูดคุยกับ Agent TypeScript ได้
- ไม่ขึ้นอยู่กับเฟรมเวิร์ก - Agent ของ ADK สามารถพูดคุยกับ Agent ของ LangGraph หรือ CrewAI ได้
- ไม่ขึ้นอยู่กับโครงสร้างพื้นฐาน - Agent ในเครื่องสามารถพูดคุยกับ Agent ในระบบคลาวด์ได้
วิธีการทำงาน - ทีละขั้นตอน
Creative Director Brand Strategist
│ │
│ 1. GET /.well-known/agent.json │
│ ────────────────────────────────►│
│ ◄──── agent card (name, url, │
│ skills, capabilities) ───│
│ │
│ 2. POST / │
│ {"method": "tasks/send", │
│ "params": {"message": ...}} │
│ ────────────────────────────────►│
│ │ LLM does
│ │ the work...
│ 3. streaming response chunks │
│ ◄───────────────────────────────│
│ ◄───────────────────────────────│
│ ◄───────────────────────────────│
ขั้นตอนที่ 1 - การค้นหา: Orchestrator จะดึงข้อมูลการ์ดตัวแทน 1 ครั้งเพื่อดูชื่อ URL และความสามารถของตัวแทน
ขั้นตอนที่ 2 - การเรียกใช้: ตัวจัดสรรจะส่งงานผ่าน JSON-RPC POST เนื้อหาประกอบด้วยข้อความ (พรอมต์สำหรับผู้เชี่ยวชาญ)
ขั้นตอนที่ 3 - การตอบกลับ: ผู้เชี่ยวชาญจะสตรีมการตอบกลับเป็นกลุ่ม เช่นเดียวกับการเรียกใช้ LLM ปกติ
การ์ดตัวแทน
Agent แต่ละตัวจะเผยแพร่คำอธิบายของตัวเองที่ /.well-known/agent.json ซึ่งคล้ายกับนามบัตรที่บอกให้โลกรู้ว่า Agent ทำอะไรได้บ้างและจะติดต่อได้ที่ไหน
{
"name": "brand_strategist",
"description": "Market research and competitive analysis",
"url": "https://brand-strategist-xyz.run.app",
"capabilities": { "streaming": true },
"skills": [
{
"id": "market_research",
"description": "Research target audiences, competitors, and trends"
}
]
}
Orchestrator จะอ่านการ์ดนี้เพื่อสร้างออบเจ็กต์ RemoteA2aAgent โดยไม่จำเป็นต้องมีความรู้แบบฮาร์ดโค้ดเกี่ยวกับส่วนประกอบภายในของผู้เชี่ยวชาญ
การเปิดเผย Agent ผ่าน A2A ใน ADK
to_a2a() จะห่อหุ้ม Agent ADK ใดๆ ในแอป FastAPI ที่เป็นไปตามข้อกำหนดของ A2A โดยมีบรรทัดเดียวดังนี้
from google.adk.a2a.utils.agent_to_a2a import to_a2a
# root_agent = your normal ADK Agent(...)
a2a_app = to_a2a(root_agent, host=PUBLIC_HOST, port=PUBLIC_PORT, protocol=PROTOCOL)
uvicorn.run(a2a_app, host=HOST, port=PORT)
ซึ่งจะสร้างสิ่งต่อไปนี้โดยอัตโนมัติ
/.well-known/agent.json- การ์ดตัวแทน/- ปลายทาง JSON-RPC (คำขอของงาน A2A ทั้งหมดจะไปที่เส้นทางราก)
10. แสดง Agent เป็นบริการ A2A
หากต้องการแสดงเอเจนต์เป็นบริการ A2A คุณสามารถใช้ฟังก์ชันยูทิลิตี to_a2a() จาก ADK ได้
วิธีการทำงานของ to_a2a()
from google.adk.a2a.utils.agent_to_a2a import to_a2a
a2a_app = to_a2a(root_agent, host=PUBLIC_HOST, port=PUBLIC_PORT, protocol=PROTOCOL)
uvicorn.run(a2a_app, host=HOST, port=PORT)
to_a2a() จะห่อหุ้มตัวแทน ADK ในแอปพลิเคชัน FastAPI ที่เปิดเผยสิ่งต่อไปนี้โดยอัตโนมัติ
/.well-known/agent.json- การ์ดตัวแทน (ชื่อ คำอธิบาย ความสามารถ)/a2a/{agent_name}- ปลายทาง JSON-RPC สำหรับรับงาน
โค้ดโครงร่างของเอเจนต์แต่ละรายมีบล็อก __main__ อยู่แล้ว ซึ่งจะห่อหุ้มเอเจนต์ในเซิร์ฟเวอร์ A2A โดยใช้ to_a2a() คุณไม่จำเป็นต้องเขียนโค้ดนี้ เนื่องจากเราได้จัดเตรียมไว้ให้แล้ว
ทำความเข้าใจการกำหนดค่า URL แบบคู่
เมื่อคุณเรียกใช้ python agent.py บล็อก __main__ จะใช้การกำหนดค่า URL 2 รายการแยกกัน ดังนี้
# Where the server actually listens (network interface):
HOST = "0.0.0.0"
PORT = 8082 # Brand Strategist (others use 8083–8086 locally)
# What gets advertised in the agent card (the address other agents use to reach it):
PUBLIC_HOST = os.getenv("PUBLIC_HOST", "localhost")
PUBLIC_PORT = int(os.getenv("PUBLIC_PORT", str(PORT)))
PROTOCOL = os.getenv("PROTOCOL", "http")
a2a_app = to_a2a(root_agent, host=PUBLIC_HOST, port=PUBLIC_PORT, protocol=PROTOCOL)
uvicorn.run(a2a_app, host=HOST, port=PORT)
สภาพแวดล้อม |
|
|
ในพื้นที่ |
|
|
Cloud Run |
|
|
ทั้ง 2 รายการจะชี้ไปยังเครื่องเดียวกันในเครื่อง ใน Cloud Run คอนเทนเนอร์จะรับฟังภายในที่ 8080 แต่การ์ดตัวแทนต้องโฆษณา HTTPS URL สาธารณะ มิเช่นนั้นครีเอทีฟไดเรกเตอร์จะติดต่อผู้เชี่ยวชาญจากภายนอกคอนเทนเนอร์ไม่ได้
เริ่มเซิร์ฟเวอร์ A2A เฉพาะทางทั้ง 5 เซิร์ฟเวอร์
มาเรียกใช้ผู้เชี่ยวชาญทั้ง 5 คนเป็นเซิร์ฟเวอร์ A2A พร้อมกัน จากนั้นทดสอบครีเอทีฟไดเรกเตอร์ในเครื่องโดยชี้ไปที่เซิร์ฟเวอร์เหล่านั้น
เปิดเทอร์มินัล Cloud Shell แยกกัน 5 เทอร์มินัล (คลิกไอคอน + ในแถบแท็บเทอร์มินัล) แล้วเรียกใช้ Agent 1 ตัวต่อเทอร์มินัล
uv run จะเปิดใช้งาน .venv โดยอัตโนมัติ ไม่จำเป็นต้องใช้ source ด้วยตนเองในแต่ละเทอร์มินัล
เทอร์มินัล 1 - นักวางแผนแบรนด์ (พอร์ต 8082):
cd ~/ai-creative-studio/workshop/starter
PORT=8082 uv run agents/brand_strategist/agent.py
เทอร์มินัล 2 - Copywriter (พอร์ต 8083):
cd ~/ai-creative-studio/workshop/starter
PORT=8083 uv run agents/copywriter/agent.py
Terminal 3 - Designer (พอร์ต 8084):
cd ~/ai-creative-studio/workshop/starter
PORT=8084 uv run agents/designer/agent.py
Terminal 4 - Critic (พอร์ต 8085):
cd ~/ai-creative-studio/workshop/starter
PORT=8085 uv run agents/critic/agent.py
Terminal 5 - Project Manager (port 8086):
cd ~/ai-creative-studio/workshop/starter
PORT=8086 uv run agents/project_manager/agent.py
ตั้งค่า URL ของ localhost ใน .env
ใน Terminal 6 ให้อัปเดต .env ด้วย URL ของเอเจนต์ในพื้นที่เพื่อให้ครีเอทีฟไดเรกเตอร์ค้นหาได้
cd ~/ai-creative-studio/workshop/starter
sed -i \
-e 's|STRATEGIST_AGENT_URL=.*|STRATEGIST_AGENT_URL=http://localhost:8082|' \
-e 's|COPYWRITER_AGENT_URL=.*|COPYWRITER_AGENT_URL=http://localhost:8083|' \
-e 's|DESIGNER_AGENT_URL=.*|DESIGNER_AGENT_URL=http://localhost:8084|' \
-e 's|CRITIC_AGENT_URL=.*|CRITIC_AGENT_URL=http://localhost:8085|' \
-e 's|PM_AGENT_URL=.*|PM_AGENT_URL=http://localhost:8086|' \
.env
ตรวจสอบเอเจนต์ด้วย A2A Inspector
A2A Inspector เป็นเครื่องมือสำหรับนักพัฒนาแอปแบบโอเพนซอร์สที่ใช้โปรโตคอล A2A โดยเฉพาะ ซึ่งช่วยให้คุณเชื่อมต่อกับเอเจนต์ A2A ที่ทำงานอยู่ อ่านการ์ดเอเจนต์ และส่งงานได้โดยตรงโดยไม่ต้องเขียนโค้ดไคลเอ็นต์
สิ่งที่แสดงให้คุณเห็น
- การ์ดเอเจนต์ - ข้อมูลเมตาที่มีโครงสร้างที่เอเจนต์โฆษณา ได้แก่ ชื่อ คำอธิบาย โหมดอินพุต/เอาต์พุตที่รองรับ และ URL ของปลายทาง นี่คือสิ่งที่ครีเอทีฟไดเรกเตอร์อ่านเมื่อพบผู้เชี่ยวชาญ
- อินเทอร์เฟซแชท - ส่งข้อความใดก็ได้ไปยังเอเจนต์ผ่าน A2A และดูการตอบกลับแบบดิบ คุณทดสอบพรอมต์แยกกันได้ก่อนที่จะเชื่อมต่อเอเจนต์เข้าด้วยกัน
- การตรวจสอบโปรโตคอล - เครื่องมือตรวจสอบจะตรวจสอบว่าการ์ดตัวแทนเป็นไปตามข้อกำหนด A2A หรือไม่ โดยจะแสดงฟิลด์ที่ขาดหายไปหรือการตอบกลับที่ผิดรูปแบบตั้งแต่เนิ่นๆ
เหตุผลที่เรื่องนี้สำคัญ: เมื่อคุณติดตั้งใช้งานใน Cloud Run ในภายหลัง ครีเอทีฟไดเร็กเตอร์จะค้นพบผู้เชี่ยวชาญแต่ละรายโดยการดึงการ์ดเอเจนต์จาก /.well-known/agent.json หากการ์ดนั้นไม่ถูกต้อง เช่น URL ไม่ถูกต้อง หรือไม่มีความสามารถที่จำเป็น ตัวจัดระเบียบจะล้มเหลวโดยไม่มีการแจ้งเตือน เครื่องมือตรวจสอบจะช่วยให้คุณตรวจพบปัญหาเหล่านี้ได้ในเครื่องก่อนที่จะติดตั้งใช้งานในระบบคลาวด์

การ์ดตัวแทนจะแสดงตัวตนและความสามารถของผู้เชี่ยวชาญตรงตามที่ตัวแทนคนอื่นๆ เห็น

ติดตั้งและเริ่มใช้เครื่องมือตรวจสอบ
cd ~/ai-creative-studio/workshop
./setup_inspector.sh
.env การอัปเดตเป็นคำสั่งแบบครั้งเดียว ใช้ Terminal 6 เพื่อเริ่มเครื่องมือตรวจสอบต่อไปนี้
cd ~/a2a-inspector
bash scripts/run.sh
หากต้องการเปิด UI ของเครื่องมือตรวจสอบ ให้ใช้ตัวอย่างเว็บ → เปลี่ยนพอร์ต → พิมพ์ 5001
ติดต่อ Brand Strategist
ป้อน http://localhost:8082 ในช่อง URL ของเครื่องมือตรวจสอบ แล้วคลิกเชื่อมต่อ เครื่องมือตรวจสอบจะดึงการ์ดตัวแทนและแสดงข้อมูลเมตาของผู้เชี่ยวชาญ

สิ่งที่การ์ดตัวแทนบอกคุณ
การ์ดเอเจนต์เป็นมากกว่าข้อมูลเมตา แต่เป็นสัญญาความสามารถเต็มรูปแบบที่เอเจนต์โฆษณาต่อเครือข่าย ติดต่อผู้จัดการโปรเจ็กต์ (http://localhost:8086) เพื่อดูตัวอย่างที่สมบูรณ์ที่สุด
{
"name": "project_manager",
"description": "Project manager with Notion integration for task tracking",
"protocolVersion": "0.3.0",
"defaultInputModes": ["text/plain"],
"defaultOutputModes": ["text/plain"],
"skills": [
{
"id": "project_manager",
"name": "model",
"tags": ["llm"],
"description": "... full system instruction including today's date and Notion database IDs ..."
},
{
"id": "project_manager-API-post-page",
"name": "API-post-page",
"tags": ["llm", "tools"],
"description": "Notion | Create a page"
},
{
"id": "project_manager-API-retrieve-a-database",
"name": "API-retrieve-a-database",
"tags": ["llm", "tools"],
"description": "Notion | Retrieve a database"
}
]
}
โดยมี 3 สิ่งที่โดดเด่นดังนี้
1. เครื่องมือ MCP จะกลายเป็นทักษะ A2A - เครื่องมือ Notion ทุกอย่างที่ผู้จัดการโปรเจ็กต์มีสิทธิ์เข้าถึง (API-post-page, API-retrieve-a-database ฯลฯ) จะแสดงเป็นทักษะแยกต่างหากในการ์ดเอเจนต์ Agent อื่นๆ ในเครือข่ายจะค้นพบเครื่องมือที่ Agent นี้ใช้ได้โดยไม่ต้องอ่านโค้ด
2. ฝังคำสั่งของระบบ - ทักษะแรกของ description มีคำสั่งของระบบทั้งหมด รวมถึงวันที่ปัจจุบันและรหัสฐานข้อมูล Notion นี่คือวิธีที่ครีเอทีฟไดเรกเตอร์ทราบว่าจะส่งอะไรเมื่อเรียกโปรเจ็กต์เมเนเจอร์
3. URL คือปลายทางแบบสด - ฟิลด์ url คือสิ่งที่ RemoteA2aAgent ใช้เมื่อครีเอทีฟไดเรกเตอร์โทรหานักการตลาดผู้เชี่ยวชาญรายนี้ หาก URL ในการ์ดไม่ถูกต้อง ตัวจัดสรรจะเข้าถึงตัวแทนไม่ได้
ด้วยเหตุนี้เองที่เครื่องมือตรวจสอบจึงเป็นเครื่องมือแก้ไขข้อบกพร่องที่มีประสิทธิภาพ เพียงแค่ดูการ์ดเอเจนต์ คุณก็จะทราบว่าเอเจนต์กำลังทำงานอยู่หรือไม่ มีเครื่องมือใดบ้าง และปลายทางถูกต้องหรือไม่
ส่งข้อความทดสอบ
เมื่อเชื่อมต่อแล้ว ให้พิมพ์พรอมต์ในแผงแชทแล้วส่ง ผู้ตรวจสอบจะส่งเป็นงาน A2A และสตรีมคำตอบกลับมาในลักษณะเดียวกับที่ครีเอทีฟไดเรกเตอร์จะเรียก Agent นี้ในขั้นตอนการผลิต

ชี้เครื่องมือตรวจสอบไปยังพอร์ตในเครื่อง (8082-8086) เพื่อทดสอบผู้เชี่ยวชาญแต่ละคนแยกกัน
11. สร้าง Orchestrator ของครีเอทีฟไดเรกเตอร์
ครีเอทีฟไดเรกเตอร์เป็นผู้ประสานงานหลัก โดยจะอ่าน URL ของผู้เชี่ยวชาญจากตัวแปรสภาพแวดล้อม ห่อหุ้มแต่ละ URL เป็น RemoteA2aAgent และแสดงเป็น AgentTool ที่ LLM เรียกใช้ได้
ตรวจสอบว่าเอเจนต์ผู้เชี่ยวชาญ 5 คนยังทำงานอยู่ (เทอร์มินัล 1-5 จากขั้นตอนที่ 10)
ในเทอร์มินัล 6 (เทอร์มินัลของ A2A Inspector) ให้หยุด Inspector ด้วย Ctrl+C
เปิดไฟล์โดยทำดังนี้
cd ~/ai-creative-studio/workshop/starter
cloudshell edit agents/creative_director/agent.py
ไฟล์นี้มีรายการที่ต้องทำ 3 รายการ ทำตามขั้นตอนเหล่านี้ตามลำดับ
TODO 1 - Review the already written system instruction
คำสั่งของระบบจะอยู่ใน prompt.py ในไดเรกทอรีเดียวกัน ซึ่งจะนำเข้าโดยอัตโนมัติ
from .prompt import SYSTEM_INSTRUCTION_TEMPLATE
เปิด prompt.py เพื่ออ่านก่อนดำเนินการต่อ
cloudshell edit agents/creative_director/prompt.py
การทำความเข้าใจค่านี้เป็นสิ่งสำคัญเนื่องจากค่านี้ควบคุมลักษณะการทำงานของการจัดสรรทั้งหมด
เหตุผลที่พรอมต์ผู้ประสานงานควบคุมทุกสิ่ง
เปิด prompt.py ควบคู่ไปกับส่วนนี้ เนื่องจากตัวอย่างด้านล่างอ้างอิงถึงส่วนที่เฉพาะเจาะจงของส่วนนี้
พรอมต์ใน prompt.py ไม่ใช่แค่เอกสารประกอบ แต่เป็นระนาบควบคุมของทั้งระบบ พรอมต์ของ Orchestrator ที่มีโครงสร้างไม่ดีจะทำให้เกิดสิ่งต่อไปนี้ Agent ที่เรียกใช้ตามลำดับ เนื้อหาที่ Orchestrator สร้างขึ้นแทนผู้เชี่ยวชาญ เวิร์กโฟลว์ที่ทำงานต่อหลังจากเกิดข้อผิดพลาด และบริบทที่ถูกละเลยอย่างเงียบๆ ระหว่าง Agent องค์ประกอบทั้ง 9 นี้ช่วยป้องกันความล้มเหลวที่พบบ่อยที่สุดได้
องค์ประกอบ 0 - วางแผนก่อน แล้วจึงดำเนินการ
นี่คือองค์ประกอบที่สำคัญที่สุด ก่อนที่จะเรียกผู้เชี่ยวชาญใดๆ ระบบจะสั่งให้ตัวจัดสรรแสดงแผนที่มีหมายเลขดังนี้
I'll create your campaign by coordinating the specialist agents in sequence:
1. Brand Strategist - develop positioning and audience insights
2. Copywriter - write captions using those insights
3. Visual Designer - create image prompts aligned with the copy
4. Critic - review and score the full package
5. Project Manager - build the timeline and task breakdown
หากไม่มีขั้นตอนนี้ LLM จะข้ามไปยังการเรียกใช้เครื่องมือโดยตรงและไม่สามารถติดตามได้ว่าอยู่ในเวิร์กโฟลว์ใด โดยเฉพาะหลังจากได้รับคำตอบยาวๆ จากผู้เชี่ยวชาญ การร่างแผนก่อนจะช่วยให้ตัวจัดสรรรู้ว่าตอนนี้อยู่ในขั้นตอนใด ขั้นตอนถัดไปคืออะไร และการดำเนินการที่สมบูรณ์มีลักษณะอย่างไร การข้ามขั้นตอนนี้จะทำให้ตัวจัดสรรหยุดชะงักกลางเวิร์กโฟลว์หรือทำขั้นตอนซ้ำ
องค์ประกอบที่ 1 - คำจำกัดความของบทบาทที่ชัดเจน
❌ "You are a helpful creative assistant."
✅ "You orchestrate specialists. You do NOT write captions, designs, or timelines yourself."
หากไม่มีการห้ามอย่างชัดเจน LLM จะข้ามการเรียกผู้เชี่ยวชาญและสร้างเนื้อหาโดยตรงในบางครั้ง ซึ่งจะเร็วกว่าและ "รู้" วิธีทำ คำสั่งต้องทำให้สิ่งนี้ผิด
องค์ประกอบที่ 2 - ไวยากรณ์การเรียกใช้เครื่องมือที่มีรูปแบบไม่ถูกต้อง
การแสดงเฉพาะไวยากรณ์ที่ถูกต้องไม่เพียงพอ LLM สามารถสร้างการเรียกที่ดูสมเหตุสมผลแต่ล้มเหลวโดยไม่มีการแจ้งเตือน พรอมต์จะแสดงทั้งรูปแบบที่ถูกต้องและรูปแบบที่ไม่ควรใช้ไว้อย่างชัดเจน
✅ copywriter(request="...") ← correct
❌ print(copywriter(...)) ← breaks silently
❌ default_api.copywriter(...) ← breaks silently
❌ copywriter.run(...) ← breaks silently
❌ agents.copywriter(...) ← breaks silently
การแสดงรูปแบบที่ไม่ถูกต้องอย่างชัดเจนช่วยลดการเรียกใช้เครื่องมือที่ผิดรูปแบบได้ประมาณ 95% ในเวอร์ชันที่ใช้งานจริง
องค์ประกอบที่ 3 - การดำเนินการตามลำดับที่อธิบายทีละขั้นตอน
a) Call the tool
b) Wait for tool_output
c) Verify the output is not an error
d) Confirm to the user: "✓ Brand Strategist complete"
e) Then move to the next agent
หากไม่มีขั้นตอน (ข) และ (ค) บางครั้ง LLM จะเรียกเอเจนต์ 2 รายพร้อมกัน หรือถือว่าสำเร็จและดำเนินการต่อก่อนที่จะได้รับคำตอบ
องค์ประกอบที่ 4 - คำสั่งข้อผิดพลาด: หยุด รายงาน ไม่ดำเนินการต่อ
ในเวอร์ชันแรกๆ ออร์เคสเตรเตอร์จะได้รับข้อผิดพลาดจากผู้เชี่ยวชาญคนหนึ่ง สร้างเอาต์พุตที่สมเหตุสมผลสำหรับข้อผิดพลาดนั้น และดำเนินการต่อกับเอเจนต์รายถัดไป ผู้ใช้จึงได้รับแคมเปญที่ดูสมบูรณ์ซึ่งสร้างขึ้นบนพื้นฐานที่เกิดจากการหลอน การแก้ไขคือการระบุอย่างชัดเจนว่า หยุดทันที รายงานข้อผิดพลาดที่แน่นอน ห้ามดำเนินการต่อ
องค์ประกอบที่ 5 - กฎการส่งบริบท
ตัวแทนระยะไกลไม่มีประวัติการสนทนา เมื่อผู้ประสานงานเรียกใช้ Copywriter ผ่าน A2A นั้น Copywriter จะเห็นเฉพาะข้อความในคำขอเดียวเท่านั้น โดยจะไม่ทราบว่า Brand Strategist พูดอะไร Orchestrator ต้องรวมเอาเอาต์พุตก่อนหน้าไว้ในการเรียกใช้ครั้งต่อๆ ไปอย่างชัดเจน
copywriter(request="Create 3 posts for EcoFlow water bottle targeting millennials.
Use these insights from the Brand Strategist: [paste full strategist output here].
Create engaging captions with hashtags.")
คำสั่งระบุไว้อย่างชัดเจนว่า "เอเจนต์ระยะไกลไม่มีหน่วยความจำที่ใช้ร่วมกัน คุณต้องส่งเอาต์พุตก่อนหน้าอย่างชัดเจน" หากไม่มีข้อมูลนี้ Agent แต่ละรายจะทำงานโดยไม่ทราบข้อมูล
องค์ประกอบที่ 6 - การแยกประเภทคำขอ: แบบง่ายเทียบกับแบบซับซ้อน
คำขอไม่ได้จำเป็นต้องใช้ตัวแทนทั้ง 5 คน พรอมต์จะสั่งให้ตัวจัดระเบียบจัดประเภทคำขอก่อนวางแผน ดังนี้
SIMPLE → one agent needed
"Research the eco-friendly water bottle market" → brand_strategist only
"Write 3 Instagram captions" → copywriter only
COMPLEX → all agents sequentially
"Create a complete campaign with timeline" → all 5 agents
หากไม่มีการจัดประเภทนี้ ตัวจัดระเบียบจะเรียกใช้เอเจนต์ทั้ง 5 รายการสำหรับทุกคำขอ ซึ่งรวมถึง "ขอไอเดียโพสต์มา 3 รายการ" ซึ่งจะเพิ่มเวลาในการตอบสนองและค่าใช้จ่ายที่ไม่จำเป็น
องค์ประกอบที่ 7 - กฎการสื่อสาร: แสดงเอาต์พุตทั้งหมด ไม่มีการกรอง
พรอมต์ระบุอย่างชัดเจนว่าผู้ประสานงานต้องไม่สรุปหรือแก้ไขสิ่งที่ผู้เชี่ยวชาญส่งกลับมา
- DO NOT summarize unless the output exceeds 2000 words
- DO NOT filter or edit agent responses
- Show the user exactly what each specialist produced
- NEVER say results are ready unless you received them in tool_output
หากไม่มีการดำเนินการนี้ ตัวจัดสรรจะเขียนเอาต์พุตของผู้เชี่ยวชาญใหม่ด้วยคำพูดของตัวเอง ซึ่งจะทำให้รายละเอียดหายไป เกิดข้อผิดพลาด และทำให้วัตถุประสงค์ของการมีผู้เชี่ยวชาญไม่เป็นผล
องค์ประกอบที่ 8 - เวิร์กโฟลว์เสร็จสมบูรณ์: อย่าหยุดก่อนเวลา
โหมดความล้มเหลวที่สำคัญแต่สังเกตได้ยาก: ออร์เคสเตรเตอร์ประกาศแผน 5 ขั้นตอน ดำเนินการ 3 ขั้นตอนให้เสร็จ แล้วนำเสนอผลลัพธ์ราวกับว่าดำเนินการเสร็จแล้ว พรอมต์จะป้องกันปัญหานี้ด้วยรายการตรวจสอบที่ชัดเจนซึ่งต้องผ่านก่อนที่ออร์เคสเตรเตอร์จะดำเนินการเสร็จ
✓ Did I announce a plan with N agents?
✓ Have I called ALL N agents from my plan?
✓ Did each agent respond successfully?
✓ Am I presenting complete results from ALL agents?
If any answer is NO → continue executing the remaining agents.
ซึ่งจะหยุดไม่ให้ Orchestrator ถือว่าการเรียกใช้บางส่วนเสร็จสมบูรณ์แล้ว
วงจรการควบคุมคุณภาพ
เวิร์กโฟลว์การแก้ไขเป็นส่วนที่ซับซ้อนที่สุดของ prompt.py เปิดส่วน ## REVISION WORKFLOW แล้วทำตาม
วิธีการทำงาน
หลังจากที่นักวิจารณ์ตอบแล้ว ครีเอทีฟไดเรกเตอร์จะไม่ส่งต่อให้โปรเจ็กต์เมเนเจอร์โดยไม่พิจารณา แต่จะอ่านผลงานของนักวิจารณ์และดำเนินการต่อดังนี้
Critic output
│
├── "All Approved: YES"
│ └──► proceed to Project Manager
│
└── "Status: NEEDS_REVISION"
│
├── posts fail → call copywriter again with feedback
├── visuals fail → call designer again with feedback
└── both fail → call copywriter, then designer
│
└──► revised output → Project Manager
(1 revision max per deliverable)
ซึ่งขับเคลื่อนด้วย LLM ไม่ใช่โค้ด
Codelab ที่กล่าวถึงก่อนหน้านี้ระบุว่าตัวจัดสรรจะ "แยกวิเคราะห์" การตอบกลับของ Critic ไม่มีโค้ด Python ที่ทำการแยกวิเคราะห์นี้ ไม่มีการจับคู่สตริงหรือนิพจน์ทั่วไป ครีเอทีฟไดเรกเตอร์คือ LLM ที่อ่านคำสั่งของตัวเอง โดยคำสั่งดังกล่าวระบุว่า
Look for "Status: NEEDS_REVISION" in the critic's response.
Posts need revision → call copywriter
Visuals need revision → call designer
LLM จะอ่านสตริงเหล่านั้นในเอาต์พุตของ Critic และทำตามกิ่งก้าน ด้วยเหตุนี้ รูปแบบของ Critic จึงเป็นสิ่งที่ต่อรองไม่ได้ หาก Critic เขียนว่า "ต้องปรับปรุง" แทนที่จะเป็น NEEDS_REVISION LLM จะไม่พบข้อความที่ตรงกันในคำสั่งและข้ามขั้นตอนการแก้ไขไปโดยไม่แจ้งให้ทราบ
วิธีส่งต่อบริบทในการเรียกใช้การแก้ไข
การเรียกให้แก้ไขเป็นไปตามกฎการส่งบริบทเดียวกันจากองค์ประกอบที่ 5 โดยผู้ประสานงานต้องรวมทุกอย่างอย่างชัดเจนเนื่องจากผู้เขียนคำโฆษณาไม่มีข้อมูลเวอร์ชันแรก
"I need you to revise the Instagram posts based on critic feedback.
ORIGINAL BRIEF:
[the original user request]
YOUR FIRST VERSION:
[the posts the copywriter created]
CRITIC FEEDBACK (Score: 6/10 - NEEDS_REVISION):
[the critic's specific suggestions]
Please revise the posts addressing this feedback while maintaining
the strengths the critic identified."
หากไม่มีส่วน "เวอร์ชันแรกของคุณ" ครีเอเตอร์คำโฆษณาจะเขียนใหม่ทั้งหมดแทนที่จะปรับปรุงสิ่งที่สร้างไว้แล้ว
ขีดจำกัดการแก้ไข 1 ครั้งและเหตุผลที่ควรทราบ
หลังจากรอบการแก้ไข 1 รอบ ผู้ประสานงานจะส่งต่อให้ผู้จัดการโปรเจ็กต์ไม่ว่าคะแนนจะเป็นเท่าใด โดยผู้สอนจะติดตามเรื่องนี้ในใจ
After calling copywriter for revision once:
→ mark "copywriter_revised = true" in context
→ even if the critic still suggests changes, proceed to PM
หากไม่มีขีดจำกัดนี้ ลูปอาจทำงานไปเรื่อยๆ โดยไม่มีที่สิ้นสุด เช่น นักวิจารณ์แจ้งปัญหา → นักเขียนคำโฆษณาทบทวน → นักวิจารณ์แจ้งอีกครั้ง → นักเขียนคำโฆษณาทบทวนอีกครั้ง แต่ละรอบจะใช้โทเค็นและเวลา การแก้ไขเพียงครั้งเดียวก็เพียงพอที่จะปรับปรุงคุณภาพโดยไม่มีความเสี่ยงที่จะเกิดวงจรที่ควบคุมไม่ได้
สิ่งที่ส่งต่อไปยังผู้จัดการโครงการ
Project Manager จะได้รับเวอร์ชันสุดท้ายที่ได้รับอนุมัติเสมอ ไม่ใช่เวอร์ชันต้นฉบับ หากมีการแก้ไข ผู้ประสานงานจะส่งสำเนาและภาพที่แก้ไขแล้ว หากทุกอย่างได้รับอนุมัติในการส่งครั้งแรก ผู้ประสานงานจะส่งสำเนาและภาพเหล่านั้นโดยตรง PM จะไม่เห็นฉบับร่างที่ถูกปฏิเสธ
TODO 2 - Register each specialist as a RemoteA2aAgent + AgentTool
ค้นหาความคิดเห็น # TODO 2: For each specialist URL... แล้วแทนที่ด้วยข้อความต่อไปนี้
if strategist_url:
available_agents_list.append(
"- **brand_strategist**: Market research, competitor analysis, trend identification"
)
strategist_agent = RemoteA2aAgent(
name="brand_strategist",
description="Researches markets, competitors, and trends using Google Search",
agent_card=f"{strategist_url}/.well-known/agent.json",
)
agent_tools.append(AgentTool(agent=strategist_agent))
if copywriter_url:
available_agents_list.append(
"- **copywriter**: Instagram captions, hashtags, and CTAs"
)
copywriter_agent = RemoteA2aAgent(
name="copywriter",
description="Creates Instagram captions with hashtags and CTAs",
agent_card=f"{copywriter_url}/.well-known/agent.json",
)
agent_tools.append(AgentTool(agent=copywriter_agent))
if designer_url:
available_agents_list.append(
"- **designer**: Visual concepts and real images generated via Gemini (GCS URIs returned)"
)
designer_agent = RemoteA2aAgent(
name="designer",
description="Creates visual concepts and generates real images via Gemini, stored in GCS",
agent_card=f"{designer_url}/.well-known/agent.json",
)
agent_tools.append(AgentTool(agent=designer_agent))
if critic_url:
available_agents_list.append(
"- **critic**: Quality review with APPROVED/NEEDS_REVISION scoring"
)
critic_agent = RemoteA2aAgent(
name="critic",
description="Reviews campaign materials and returns structured quality feedback",
agent_card=f"{critic_url}/.well-known/agent.json",
)
agent_tools.append(AgentTool(agent=critic_agent))
if pm_url:
available_agents_list.append(
"- **project_manager**: Project timelines, task breakdowns, Notion integration"
)
pm_agent = RemoteA2aAgent(
name="project_manager",
description="Creates project timelines and task breakdowns, optionally in Notion",
agent_card=f"{pm_url}/.well-known/agent.json",
)
agent_tools.append(AgentTool(agent=pm_agent))
TODO 3 - Wrap in an App with context compaction
เหตุใดจึงจำเป็นต้องมีการบีบอัด
ข้อความทุกข้อความในการสนทนา ไม่ว่าจะเป็นพรอมต์ของผู้ใช้ การเรียกใช้เครื่องมือทุกครั้ง หรือการตอบกลับของเครื่องมือทุกครั้ง จะได้รับการผนวกเข้ากับหน้าต่างบริบทที่ LLM อ่านในรอบถัดไป ในเวิร์กโฟลว์ที่มีเอเจนต์ 5 คน การสะสมนี้จะเกิดขึ้นอย่างรวดเร็ว
Turn 1: user prompt ~200 tokens
Turn 2: orchestrator plan ~300 tokens
Turn 3: brand_strategist tool_call ~150 tokens
Turn 4: brand_strategist tool_output ~1,500 tokens ← full research report
Turn 5: copywriter tool_call ~300 tokens ← must include strategist output
Turn 6: copywriter tool_output ~2,000 tokens ← 3 captions
Turn 7: designer tool_call ~500 tokens
Turn 8: designer tool_output ~1,500 tokens
...
เมื่อถึงเอเจนต์ 4 (นักวิจารณ์) หน้าต่างบริบทจะมีเอาต์พุตทั้งหมดของเอเจนต์ 3 คนก่อนหน้า ซึ่งมักจะมีโทเค็น 8,000-12,000 โทเค็นเฉพาะในคำตอบของเครื่องมือเท่านั้น แม้ว่าหน้าต่างบริบทขนาดใหญ่ของ Gemini 2.5 Pro จะมีขนาดใหญ่ แต่คุณภาพการให้เหตุผลของตัวจัดสรรจะลดลงเนื่องจากต้องพิจารณาประวัติที่เพิ่มขึ้นเรื่อยๆ หากไม่มีการบีบอัด เวิร์กโฟลว์ที่ยาวจะถึงขีดจำกัดที่ใช้งานได้จริงเมื่อถึงเอเจนต์ 4
การบดอัดดิน
แทนที่จะเก็บเหตุการณ์ทั้งหมดไว้ ADK จะเรียกใช้ LLM เป็นระยะๆ เพื่อสรุปเหตุการณ์เก่าๆ ให้เป็นตัวแทนแบบย่อ ระบบจะเก็บเฉพาะสรุปของเหตุการณ์ที่ผ่านมาและเอาต์พุตทั้งหมดของเอเจนต์ล่าสุดไว้ในบริบท
Without compaction:
[full strategist output] + [full copywriter output] + [full designer output] + → Critic
With compaction (interval=3, overlap=1):
[summary of strategist + copywriter] + [full designer output] + → Critic
โดยสรุปจะยังคงข้อเท็จจริงที่สำคัญ (ข้อมูลเชิงลึกที่สำคัญ คำบรรยายที่ได้รับอนุมัติ แนวคิดภาพ) ในขณะที่ทิ้งการจัดรูปแบบที่ยาวเหยียด บริบทที่ซ้ำกันซึ่งส่งไปยังตัวแทนแต่ละราย และการให้เหตุผลขั้นกลาง นักวิจารณ์ยังคงมีทุกอย่างที่จำเป็นต่อการประเมิน เพียงแต่จะอ่านข้อมูลสรุปแทนที่จะอ่านรายงานฉบับเต็ม 3 ฉบับ
รหัส
ค้นหาความคิดเห็น # TODO 3: Wrap the agent in an App... แล้วแทนที่ตัวยึดตำแหน่ง App(...) ด้วยข้อมูลต่อไปนี้
from google.adk.apps import App
from google.adk.apps.app import EventsCompactionConfig
from google.adk.apps.llm_event_summarizer import LlmEventSummarizer
from google.adk.models import Gemini
compaction_config = EventsCompactionConfig(
summarizer=LlmEventSummarizer(llm=Gemini(model_id=os.getenv("GEMINI_MODEL", "gemini-2.5-flash"))),
compaction_interval=3, # Summarize after every 3 agent completions
overlap_size=1, # Keep the most recent agent's output in full
)
app = App(
name="creative_director",
root_agent=agent,
events_compaction_config=compaction_config,
plugins=[LoggingPlugin()],
)
return agent, app
compaction_interval=3 - การบีบอัดจะเริ่มทำงานหลังจากที่เอเจนต์ทำงานเสร็จทุกๆ 3 ราย สำหรับไปป์ไลน์ที่มีเอเจนต์ 5 ราย การบีบอัดจะเริ่มทำงาน 1 ครั้ง (หลังจากเอเจนต์ 1-3) จากนั้น Critic และ PM จะเห็นสรุปของเอเจนต์ 1-3 พร้อมเอาต์พุตของเอเจนต์ก่อนหน้าแบบเต็ม
overlap_size=1 - ระบบจะเก็บเอาต์พุตแบบเต็มของเอเจนต์ล่าสุดไว้ตามเดิมเสมอและจะไม่สรุปข้อมูล ซึ่งเป็นสิ่งสำคัญเนื่องจาก Critic ต้องการเอาต์พุตแบบเต็มของ Designer รวมถึงค่า gcs_uri เพื่อโหลดและตรวจสอบรูปภาพจริง การสรุปจะทำให้ URI เหล่านั้นหายไป
การทำงานเมื่อเรียกใช้แคมเปญแบบเต็ม
Agent 1 (Strategist) → full context
Agent 2 (Copywriter) → full context
Agent 3 (Designer) → full context
↓ compaction fires: summarizes agents 1-2, keeps 3 in full
Agent 4 (Critic) → sees [summary of 1-2] + [full output of 3]
Agent 5 (PM) → sees [summary of 1-3] + [full output of 4]
ทำความเข้าใจRemoteA2aAgentและAgentTool
RemoteA2aAgent("brand_strategist", agent_card=url)
│
│ wraps the remote service so ADK can call it
▼
AgentTool(agent=strategist_agent)
│
│ exposes it as a callable tool to the LLM
▼
Agent(tools=[...])
│
│ LLM calls tool("brand_strategist", message=...) when needed
▼
brand-strategist-xxxx.run.app ← actual HTTP A2A call happens here
LLM จะตัดสินใจเวลาที่จะเรียกใช้เครื่องมือแต่ละอย่างตามคำสั่งของระบบและคำขอของผู้ใช้ Orchestrator จะไม่เรียกใช้เอเจนต์โดยตรงในโค้ด แต่จะขับเคลื่อนด้วยการให้เหตุผลของ LLM
ทดสอบ Creative Director ในเครื่อง
uv run adk web agents --allow_origins='*'
เปิดตัวอย่างเว็บในพอร์ต 8000 ใช้เมนูแบบเลื่อนลงของตัวแทนเพื่อเลือก creative_director จากนั้นลองทำดังนี้
Research the eco-friendly water bottle market for health-conscious millennials
คุณจะเห็นว่าครีเอทีฟไดเรกเตอร์จะส่งเรื่องนี้ไปยังนักวางแผนกลยุทธ์แบรนด์เท่านั้น และคุณจะได้รับการตอบกลับจากนักวางแผนกลยุทธ์แบรนด์
หากต้องการดูแคมเปญทั้งหมด ให้ลองทำดังนี้
Create a complete Instagram campaign for SolarPack portable solar charger targeting
outdoor enthusiasts and digital nomads aged 22-35.
Budget $2,000, launch in 2 weeks.
คุณจะเห็นครีเอทีฟไดเรกเตอร์ประสานงานกับผู้เชี่ยวชาญทั้ง 5 คนตามลำดับ โดยผลงานของตัวแทนแต่ละคนจะส่งต่อไปยังตัวแทนคนถัดไป

หยุดครีเอทีฟไดเรกเตอร์ (Ctrl+C) ก่อนดำเนินการต่อ เนื่องจากเครื่องมือตรวจสอบ A2A ใช้พอร์ต 8000 ด้วย
หยุดเซิร์ฟเวอร์ผู้เชี่ยวชาญ 5 เครื่อง (Ctrl+C ในแต่ละเทอร์มินัล) เมื่อทดสอบในเครื่องเสร็จแล้ว
12. ติดตั้งใช้งานและทดสอบเอเจนต์ผู้เชี่ยวชาญ
ตอนนี้เราพร้อมที่จะทำให้ Agent ใช้งานได้ใน Google Cloud แล้ว Cloud Run เป็นบริการที่ยอดเยี่ยมในการทำให้ Agent ใช้งานได้ โดยเป็นแบบ Serverless รองรับการปรับขนาดได้ และใช้งานง่าย Agent ผู้เชี่ยวชาญแต่ละรายจะได้รับการทำให้ใช้งานได้เป็นบริการ Cloud Run ที่เป็นอิสระ
การกำหนดค่าการทำให้ใช้งานได้
Dockerfile ของผู้เชี่ยวชาญแต่ละคนจะเป็นไปตามรูปแบบต่อไปนี้
FROM python:3.12-slim
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends gcc curl
# Fast dependency install with uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
COPY pyproject.toml .
RUN uv sync --no-install-project --no-dev
COPY . .
RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
USER appuser
ENV PYTHONUNBUFFERED=1 PORT=8080 HOST=0.0.0.0
EXPOSE 8080
CMD ["uv", "run", "python", "agent.py"]
ส่งผู้เชี่ยวชาญทั้ง 5 คนตามลำดับ
cd ~/ai-creative-studio/workshop/starter
source .env
uv run deploy/deploy_all_specialists.py
สคริปต์นี้จะติดตั้งใช้งาน Agent ทั้ง 5 รายการทีละรายการ (รวมประมาณ 10-12 นาที) การติดตั้งใช้งานตามลำดับจะช่วยหลีกเลี่ยงโควต้าการสำรวจของ Cloud Build (60 คำขอ/นาที) เมื่อเสร็จแล้ว ระบบจะเขียน URL ของ Cloud Run ของแต่ละเอเจนต์กลับไปที่ .env
หลังจากติดตั้งใช้งานดีไซเนอร์แล้ว สคริปต์จะให้สิทธิ์บัญชีบริการ Cloud Run ของดีไซเนอร์ roles/storage.objectCreator ใน Bucket ของ GCS โดยอัตโนมัติเพื่อให้สามารถอัปโหลดรูปภาพที่สร้างขึ้นได้
หากคุณกำหนดค่าข้อมูลเข้าสู่ระบบ Notion ใน .env สคริปต์จะจัดเก็บข้อมูลดังกล่าวอย่างปลอดภัยใน Secret Manager (เป็น notion-token, notion-project-db-id, notion-tasks-db-id) และแทรกข้อมูลดังกล่าวลงในบริการ Project Manager ผ่าน --set-secrets แทนที่จะใช้ตัวแปรสภาพแวดล้อมแบบธรรมดา ซึ่งหมายความว่าโทเค็นจะไม่ปรากฏในแท็บสภาพแวดล้อมของ Cloud Run หรือในประวัติคำสั่ง gcloud
ยืนยันการติดตั้งใช้งาน
เมื่อการติดตั้งใช้งานเสร็จสมบูรณ์ สคริปต์จะเขียน URL ของ Cloud Run กลับไปที่ .env โดยอัตโนมัติ ซึ่งจะแทนที่ URL ของ localhost จากขั้นตอนก่อนหน้า
source .env
echo "Deployed URLs:"
echo " Brand Strategist: $STRATEGIST_AGENT_URL"
echo " Copywriter: $COPYWRITER_AGENT_URL"
echo " Designer: $DESIGNER_AGENT_URL"
echo " Critic: $CRITIC_AGENT_URL"
echo " Project Manager: $PM_AGENT_URL"
ครีเอทีฟไดเรกเตอร์จะใช้ URL ของ Cloud Run เหล่านี้โดยอัตโนมัติเมื่อนำไปใช้งานกับ Agent Runtime ในขั้นตอนถัดไป
ยืนยันบัตรตัวแทน
เอเจนต์แต่ละตัวที่ติดตั้งใช้งานจะแสดงการ์ดเอเจนต์ที่ /.well-known/agent.json ดึงข้อมูลเพื่อยืนยันว่าทุกอย่างพร้อมใช้งาน
source .env
for agent_url in $STRATEGIST_AGENT_URL $COPYWRITER_AGENT_URL $DESIGNER_AGENT_URL $CRITIC_AGENT_URL $PM_AGENT_URL; do
echo "=== Agent Card: $agent_url ==="
curl -s "${agent_url}/.well-known/agent.json" | python3 -m json.tool | grep -E '"name"|"url"|"description"'
echo ""
done
ผลลัพธ์ที่คาดหวังสำหรับแต่ละเอเจนต์
"name": "brand_strategist",
"url": "https://brand-strategist-xxxx.run.app",
"description": "Brand strategist for market research and competitive insights"
ทดสอบด้วยเครื่องมือตรวจสอบ A2A (Cloud Run)
มีการติดตั้ง A2A Inspector จากขั้นตอนที่ 10 แล้ว เริ่มใช้งาน
cd ~/a2a-inspector
bash scripts/run.sh
เปิดตัวอย่างเว็บ → เปลี่ยนพอร์ต → 5001 ป้อน URL ของ Cloud Run ในช่องการเชื่อมต่อ
https://brand-strategist-xxxx.us-central1.run.app
คลิกเชื่อมต่อ ไม่จำเป็นต้องใช้โทเค็นการให้สิทธิ์เนื่องจากมีการติดตั้งใช้งานบริการด้วย --allow-unauthenticated
ผู้ตรวจสอบจะเชื่อมต่อ ตรวจสอบบัตรตัวแทน และให้คุณแชทแบบอินเทอร์แอกทีฟผ่าน A2A
ตรวจสอบ Agent ที่ติดตั้งใช้งานใน Cloud Run
หลังจากทําการติดตั้งใช้งานใน Cloud Run แล้ว ให้ชี้ตัวตรวจสอบไปยัง URL HTTPS สาธารณะเพื่อยืนยันว่าการติดตั้งใช้งานระบบคลาวด์ทํางานได้

เวิร์กโฟลว์จะเหมือนกันทุกประการ เพียงวาง URL ของ Cloud Run เชื่อมต่อ และส่งข้อความทดสอบ หากการ์ดตัวแทนโหลดขึ้นมาและแชทตอบกลับ แสดงว่าระบบได้ติดตั้งใช้งานผู้เชี่ยวชาญอย่างถูกต้องและติดต่อได้
13. ติดตั้งใช้งาน Creative Director กับ Agent Runtime
ระบบจะติดตั้งใช้งาน Orchestrator ใน Agent Runtime ซึ่งมีสถานะเซสชันที่มีการจัดการ การปรับขนาดอัตโนมัติ และการติดตามในตัว
เหตุใดจึงต้องใช้ Agent Runtime สำหรับ Orchestrator
เราได้ติดตั้งใช้งานผู้เชี่ยวชาญทั้ง 5 คนใน Cloud Run ซึ่งเป็นบริการที่ไม่เก็บสถานะและมีน้ำหนักเบา โดยแต่ละคนจะรับผิดชอบงาน 1 อย่าง ครีเอทีฟไดเรกเตอร์มีข้อกำหนดที่แตกต่างกันดังนี้
ข้อกำหนด | ทำไมจึงมีความสำคัญ |
สถานะเซสชัน | เวิร์กโฟลว์แบบหลายขั้นตอนใช้เวลา 45 วินาทีขึ้นไป Agent Runtime จะรักษาสถานะการสนทนาระหว่างการเรียกใช้เครื่องมือของ Orchestrator เพื่อไม่ให้ข้อมูลสูญหายกลางไปป์ไลน์ |
โหลดตัวแปร | บางครั้งอาจมีแคมเปญ 1 รายการต่อชั่วโมง แต่บางครั้งก็อาจมีหลายรายการพร้อมกัน Agent Runtime จะปรับขนาดเป็น 0 เมื่อไม่มีการใช้งานและจะปรับขนาดโดยอัตโนมัติ คุณจึงไม่ต้องจ่ายค่าความจุที่ไม่ได้ใช้งาน |
ความสามารถในการสังเกต | Cloud Logging, Cloud Monitoring และ Cloud Trace มีมาให้ในตัว คุณจะเห็นการเรียกใช้ A2A ทุกครั้ง โทเค็นทุกรายการที่ใช้ และเวลาในการตอบสนองที่เพิ่มขึ้นทุกครั้งโดยไม่ต้องเพิ่มการตรวจสอบ |
เวิร์กโฟลว์ที่ทำงานเป็นเวลานาน | Cloud Run มีการหมดเวลาคำขอที่ 3600 วินาที รันไทม์ของ Agent ออกแบบมาสำหรับเวิร์กโฟลว์ที่อาจใช้เวลาหลายนาที โดยมีการลองใหม่ที่จัดการแล้วและการคงสถานะ |
Cloud Run เป็นแพลตฟอร์มที่เหมาะสำหรับผู้เชี่ยวชาญด้านการไม่เก็บสถานะ ส่วน Agent Runtime เป็นแพลตฟอร์มที่เหมาะสำหรับ Orchestrator ที่เก็บสถานะ
ติดตั้งใช้งาน Orchestrator
cd ~/ai-creative-studio/workshop/starter
source .env
uv run deploy/deploy_orchestrator.py --action deploy
ซึ่งจะใช้เวลาประมาณ 5–10 นาที เมื่อเสร็จแล้ว ระบบจะบันทึก AGENT_ENGINE_ID และ AGENT_ENGINE_RESOURCE_NAME ไว้ใน .env
source .env
echo "Agent Engine ID: $AGENT_ENGINE_ID"
echo "Resource: $AGENT_ENGINE_RESOURCE_NAME"
วิธีการทํางานของการติดตั้งใช้งาน
client.agent_engines.create() จะแพ็กเกจออบเจ็กต์ App อัปโหลดพร้อมกับทรัพยากร Dependency และนำไปใช้งานในโครงสร้างพื้นฐานที่มีการจัดการ พารามิเตอร์แต่ละรายการมีหน้าที่ดังนี้
import vertexai
from vertexai import Client, agent_engines
vertexai.init(project=PROJECT_ID, location=LOCATION, staging_bucket=STAGING_BUCKET)
# Wrap the App in an AdkApp adapter - enables tracing in Cloud Trace
adk_app = agent_engines.AdkApp(app=root_app, enable_tracing=True)
# Initialize client and deploy
client = Client(project=PROJECT_ID, location=LOCATION)
agent_engine_resource = client.agent_engines.create(
agent=adk_app,
config={
"staging_bucket": STAGING_BUCKET, # GCS bucket for packaging artifacts
"display_name": "Creative Director",
# Python packages installed in the managed runtime - pin for reproducibility
"requirements": [
"google-cloud-aiplatform[agent_engines]>=1.132.0,<2.0.0",
"google-adk[a2a]==1.31.1",
"google-genai>=1.70.0",
"google-cloud-storage>=2.10.0",
"python-dotenv>=1.0.0",
"pydantic>=2.0.0",
"cloudpickle>=3.0.0",
],
# Specialist URLs passed as env vars - the orchestrator reads these at runtime
"env_vars": {
"COPYWRITER_AGENT_URL": COPYWRITER_URL,
"DESIGNER_AGENT_URL": DESIGNER_URL,
"STRATEGIST_AGENT_URL": STRATEGIST_URL,
"CRITIC_AGENT_URL": CRITIC_URL,
"PM_AGENT_URL": PM_URL,
},
},
)
resource_name = agent_engine_resource.api_resource.name
agent_engine_id = resource_name.split("/")[-1]
สิ่งที่เกิดขึ้นเบื้องหลัง
1. Agent Engine packages your App + requirements into a container
2. Uploads it to the staging bucket in your project
3. Deploys to managed compute (you never see or manage the VM)
4. Returns a resource name: projects/.../locations/.../reasoningEngines/<id>
5. That ID is saved to .env as AGENT_ENGINE_ID
หลังจากการติดตั้งใช้งานแล้ว ตัวจัดสรรจะเชื่อมต่อกับผู้เชี่ยวชาญ Cloud Run ทั้ง 5 คนผ่าน URL ในตัวแปรสภาพแวดล้อม
- ระบบจะส่งค่าเหล่านี้ผ่าน
.envก่อนที่สคริปต์การทำให้ใช้งานได้จะทำงาน
14. เรียกใช้แคมเปญแบบครบวงจร
ระบบทั้งหมดได้รับการติดตั้งใช้งานแล้ว เรียกใช้แคมเปญที่สมบูรณ์จากสนามทดสอบ Agent Runtime
เปิด Playground ของ Agent Runtime
- ไปที่ https://console.cloud.google.com/agent-platform/runtimes นอกจากนี้ คุณยังไปที่ Agent Runtime ได้จากแพลตฟอร์มเอเจนต์ > เอเจนต์ > การติดตั้งใช้งาน
- เลือก Agent Runtime ที่คุณติดตั้งใช้งาน (
creative-director) - คลิก Playground ในแถบด้านข้างทางซ้าย
- คลิกเซสชันใหม่เพื่อเปิดการสนทนาใหม่
เรียกใช้แคมเปญเต็มรูปแบบ
วางบรีฟนี้ลงในแชทแล้วส่ง
Create a complete Instagram campaign for:
- Product: EcoFlow Smart Water Bottle (tracks hydration, keeps drinks cold 24h)
- Target Audience: Health-conscious millennials, 25-35 years old
- Platform: Instagram
- Goal: Brand awareness + drive website traffic
- Brand Voice: Motivational, clean, science-backed
- Budget: $3,000
- Timeline: Launch in 2 weeks
ครีเอทีฟไดเรกเตอร์จะดำเนินการกับเอเจนต์ทั้ง 5 รายตามลำดับ ดังนี้
- นักวางกลยุทธ์ของแบรนด์ → การวิจัยตลาด การวิเคราะห์คู่แข่ง ข้อมูลเชิงลึกของกลุ่มเป้าหมาย
- นักเขียนคำโฆษณา → โพสต์ Instagram 3 รายการพร้อมคำบรรยาย แฮชแท็ก และ CTA
- ดีไซเนอร์ → แนวคิดภาพ + รูปภาพจริงที่สร้างผ่าน Gemini (URI ของ GCS) สำหรับแต่ละโพสต์
- นักวิจารณ์ → รีวิวคุณภาพที่มีคะแนนเป็น APPROVED / NEEDS_REVISION
- (แก้ไขหากจำเป็น) → เรียกนักเขียนคำโฆษณาหรือนักออกแบบอีกครั้งพร้อมความคิดเห็น
- ผู้จัดการโปรเจ็กต์ → ลำดับเวลา 2 สัปดาห์ การแบ่งงาน การจัดสรรงบประมาณ

ทดสอบการกำหนดเส้นทางแบบตัวแทนรายเดียว
ส่งคำขอที่สั้นลงนี้ในเซสชันใหม่
Research the luxury skincare market - top brands and trends in 2025
โปรดสังเกตว่าครีเอทีฟไดเร็กเตอร์จะกำหนดเส้นทางคำขอนี้ไปยังนักวางแผนกลยุทธ์แบรนด์เท่านั้น โดยจะไม่มีการเรียกเอเจนต์รายอื่น นี่คือตรรกะการจัดประเภทคำขอจากคำสั่งของระบบที่ทำงานอย่างถูกต้อง
ตรวจสอบการติดตามการดำเนินการ
ขณะที่ยังอยู่ในคอนโซล ให้ทำดังนี้
- คลิกร่องรอยในแถบด้านข้างซ้าย (ข้าง Playground)
- ในส่วนมุมมองการติดตาม ให้เลือกการติดตามสำหรับเซสชันที่คุณเพิ่งเรียกใช้
- ขยายแผนผังการติดตามเพื่อดูการเรียกใช้เอเจนต์แต่ละรายการ อินพุต/เอาต์พุต เวลาในการตอบสนอง และการใช้โทเค็น
การโทรแบบ A2A แต่ละครั้งไปยังผู้เชี่ยวชาญจะปรากฏเป็น Span แยกต่างหาก คุณจะเห็นบริบทที่ครีเอทีฟไดเร็กเตอร์ส่งไปยังตัวแทนแต่ละรายและสิ่งที่ได้รับกลับมาอย่างชัดเจน
ไม่บังคับ: เรียกใช้จากเทอร์มินัล
นอกจากนี้ คุณยังเรียกใช้แคมเปญแบบเป็นโปรแกรมได้โดยใช้run_campaign.pyสคริปต์ที่รวมอยู่ใน Starter อยู่แล้ว
cd ~/ai-creative-studio/workshop/starter
uv run run_campaign.py
15. ล้าง
ล้างข้อมูลทรัพยากร Google Cloud เพื่อหลีกเลี่ยงการเรียกเก็บเงินอย่างต่อเนื่อง
เรียกใช้สคริปต์การลบ - สคริปต์จะอ่าน .env และลบทุกอย่างที่สร้างขึ้นระหว่าง Codelab นี้
bash deploy/teardown_gcp.sh
สคริปต์จะแสดงให้คุณเห็นอย่างชัดเจนว่าจะลบอะไรบ้าง และจะแจ้งให้ยืนยันก่อนดำเนินการ
ทรัพยากร | ข้อมูลที่ระบบจะลบ |
บริการ Cloud Run | นักวางกลยุทธ์ของแบรนด์ นักเขียนคำโฆษณา นักออกแบบ นักวิจารณ์ ผู้จัดการโครงการ |
Agent Runtime | เครื่องมือให้เหตุผลของครีเอทีฟไดเรกเตอร์ + เซสชันทั้งหมด |
Artifact Registry |
|
ที่เก็บข้อมูล GCS |
|
Secret Manager |
|
ตรวจสอบว่าระบบนำทุกอย่างออกแล้ว
gcloud run services list --region=us-central1
gcloud storage buckets list --project=$GCP_PROJECT_ID
ผลลัพธ์ที่คาดหวัง: รายการว่างเปล่าหรือมีเฉพาะทรัพยากรของคุณเองที่มีอยู่ก่อนแล้ว
16. สรุป
ยินดีด้วย คุณได้สร้างและติดตั้งใช้งานระบบ AI แบบหลาย Agent ระดับการใช้งานจริงบน Google Cloud แล้ว
สิ่งที่คุณสร้าง
Agent | ความสามารถ | การทำให้ใช้งานได้ |
นักวางกลยุทธ์ของแบรนด์ | การวิจัยตลาดผ่าน Google Search | Cloud Run |
นักเขียนคำโฆษณา | การสร้างคำบรรยายแทนเสียงของ Instagram | Cloud Run |
นักออกแบบ | การสร้างรูปภาพผ่านการอัปโหลด Gemini + GCS | Cloud Run |
นักวิจารณ์ | รีวิวคุณภาพพร้อมคะแนน | Cloud Run |
ผู้จัดการโครงการ | ไทม์ไลน์ + Notion MCP | Cloud Run |
ผู้อำนวยการฝ่ายสร้างสรรค์ | การจัดการเป็นกลุ่มทั้งหมดผ่าน A2A | Agent Runtime |
รูปแบบสำคัญที่คุณได้เรียนรู้
- ADK
Agent- กำหนด Agent LLM ด้วยคำสั่ง + เครื่องมือที่ไม่บังคับ adk web- เรียกใช้และทดสอบ Agent ของ ADK ในเครื่องด้วย UI แชทในตัวSkillToolset- แพ็กเกจความรู้ที่นำกลับมาใช้ใหม่ได้เป็นไฟล์แบบแยกส่วนซึ่งโหลดตามต้องการFunctionTool- ห่อหุ้มฟังก์ชัน Python (หรือโมเดลภายนอก) เป็นเครื่องมือเอเจนต์ที่เรียกใช้ได้to_a2a()- แสดง Agent ADK เป็นบริการ HTTPS ที่เป็นไปตามข้อกำหนดของ A2ARemoteA2aAgent+AgentTool- จัดการเอเจนต์ระยะไกลเป็นเครื่องมือที่เรียกใช้ได้McpToolset- เชื่อมต่อกับบริการภายนอกผ่านเซิร์ฟเวอร์ MCP stdioEventsCompactionConfig- จัดการขีดจำกัดโทเค็นในเวิร์กโฟลว์แบบหลายเอเจนต์ที่ยาว- เอาต์พุตของรีวิววิจารณ์ที่มีโครงสร้าง - การควบคุมคุณภาพที่เครื่องอ่านได้พร้อมการแก้ไขอัตโนมัติ
- Cloud Run - ทำให้ Agent ที่เป็นคอนเทนเนอร์ใช้งานได้ในวงกว้าง
- Agent Runtime - จัดการ Orchestrator ด้วยเซสชันและการติดตามที่มีการจัดการ
ขั้นตอนถัดไป
- เพิ่มการแก้ไขรูปภาพแบบการสนทนาไปมาลงในดีไซเนอร์โดยใช้ความสามารถในการแก้ไขของ
gemini-3.1-flash-image - เพิ่มการตรวจสอบสิทธิ์ IAM ลงในบริการ Cloud Run (นำ
--allow-unauthenticatedออก) - แทนที่ผู้เชี่ยวชาญด้วยเอเจนต์ LangGraph หรือ CrewAI - A2A ไม่ขึ้นอยู่กับเฟรมเวิร์ก
- เพิ่มความคิดเห็นของผู้ใช้เป็นเครื่องมือเพื่อให้ผู้เข้าร่วมให้คะแนนและทำซ้ำเอาต์พุตได้
- สำรวจการติดตามรันไทม์ของเอเจนต์ใน Cloud Console