1. สิ่งที่คุณจะได้เรียนรู้
ยินดีต้อนรับ วันนี้เราจะมาเริ่มต้นการเดินทางที่น่าสนใจกัน มาเริ่มกันด้วยการนึกถึงแพลตฟอร์มกิจกรรมทางสังคมยอดนิยมอย่าง InstaVibe แม้ว่าฟีเจอร์นี้จะประสบความสำเร็จ แต่เราก็ทราบว่าสำหรับผู้ใช้บางราย การวางแผนกิจกรรมกลุ่มจริงๆ อาจเป็นเรื่องน่าเบื่อ ลองนึกภาพการพยายามหาว่าเพื่อนๆ สนใจอะไร จากนั้นก็ต้องมานั่งคัดกรองตัวเลือกกิจกรรมหรือสถานที่จัดงานไม่รู้จบ และสุดท้ายก็ต้องประสานงานทุกอย่าง เยอะมากเลย และนี่คือจุดที่เราสามารถนำ AI มาใช้ได้ และโดยเฉพาะอย่างยิ่ง ตัวแทนอัจฉริยะที่จะสร้างความแตกต่างอย่างแท้จริง
แนวคิดนี้คือการสร้างระบบที่เอเจนต์เหล่านี้สามารถจัดการงานหนักๆ ได้ เช่น “ฟัง” อย่างชาญฉลาดเพื่อทำความเข้าใจความชอบของผู้ใช้และเพื่อนๆ แล้วเสนอแนะกิจกรรมที่ยอดเยี่ยมและปรับแต่งมาให้โดยเฉพาะ เป้าหมายของเราคือการเปลี่ยนรูปแบบการวางแผนทางสังคมใน InstaVibe ให้เป็นสิ่งที่ราบรื่นและน่าพึงพอใจ หากต้องการเริ่มต้นสร้างผู้ช่วยอัจฉริยะเหล่านี้ เราต้องวางรากฐานที่แข็งแกร่งด้วยเครื่องมือที่เหมาะสม
แนวคิดที่คุณจะเห็นมีดังนี้

พื้นฐานเกี่ยวกับ ADK ของ Google: ทำความเข้าใจพื้นฐานของการสร้างเอเจนต์อัจฉริยะตัวแรกโดยใช้ Agent Development Kit (ADK) ของ Google ทำความเข้าใจองค์ประกอบที่สำคัญ วงจรของเอเจนต์ และวิธีใช้ประโยชน์จากเครื่องมือในตัวของเฟรมเวิร์กอย่างมีประสิทธิภาพ
การขยายความสามารถของ Agent ด้วย Model Context Protocol (MCP): เรียนรู้วิธีติดตั้งเครื่องมือและบริบทที่กำหนดเองให้กับ Agent เพื่อให้ Agent ทำงานเฉพาะทางและเข้าถึงข้อมูลที่เฉพาะเจาะจงได้ แนะนำแนวคิด Model Context Protocol (MCP) คุณจะได้เรียนรู้วิธีตั้งค่าเซิร์ฟเวอร์ MCP เพื่อให้บริบทนี้
การออกแบบการโต้ตอบและการประสานงานของ Agent: ทำความเข้าใจการประสานงานของ Agent ให้มากกว่าการใช้ Agent เพียงตัวเดียว ออกแบบรูปแบบการโต้ตอบตั้งแต่เวิร์กโฟลว์แบบลำดับอย่างง่ายไปจนถึงสถานการณ์ที่ซับซ้อนซึ่งเกี่ยวข้องกับการวนซ้ำ ตรรกะแบบมีเงื่อนไข และการประมวลผลแบบคู่ขนาน แนะนําแนวคิดของ Sub-Agent ภายในเฟรมเวิร์ก ADK เพื่อจัดการงานแบบแยกส่วน
การสร้างระบบแบบหลาย Agent ที่ทำงานร่วมกัน: ค้นพบวิธีออกแบบระบบที่ Agent หลายตัวทำงานร่วมกันเพื่อให้บรรลุเป้าหมายที่ซับซ้อน เรียนรู้และใช้โปรโตคอลการสื่อสารแบบ Agent-to-Agent (A2A) เพื่อสร้างวิธีมาตรฐานสำหรับ Agent ที่กระจายอยู่ (อาจทำงานบนเครื่องหรือบริการต่างๆ) ในการโต้ตอบอย่างน่าเชื่อถือ
การนำเอเจนต์ไปใช้จริงใน Google Cloud: เปลี่ยนแอปพลิเคชันเอเจนต์จากสภาพแวดล้อมการพัฒนาไปยังระบบคลาวด์ ดูแนวทางปฏิบัติแนะนำสำหรับการออกแบบและติดตั้งใช้งานระบบแบบหลายเอเจนต์ที่ปรับขนาดได้และมีประสิทธิภาพบน Google Cloud Platform (GCP) รับข้อมูลเชิงลึกเกี่ยวกับการใช้ประโยชน์จากบริการของ GCP เช่น Cloud Run และสำรวจความสามารถของ Google Agent Engine เวอร์ชันล่าสุดสำหรับการโฮสต์และจัดการเอเจนต์
2. สถาปัตยกรรม
การวางแผนโซเชียลมีเดียที่ทำงานด้วยระบบ AI ด้วย InstaVibe
การฟังทางโซเชียลคืออะไร
การฟังทางโซเชียลคือกระบวนการตรวจสอบการสนทนาดิจิทัลในแพลตฟอร์มต่างๆ เช่น โซเชียลมีเดีย ฟอรัม และเว็บไซต์ข่าว เพื่อทำความเข้าใจสิ่งที่ผู้คนพูดถึงหัวข้อ แบรนด์ หรืออุตสาหกรรม ซึ่งให้ข้อมูลเชิงลึกที่มีประโยชน์เกี่ยวกับความรู้สึกของสาธารณชน แนวโน้ม และความต้องการของผู้ใช้ ในเวิร์กช็อปนี้ เราจะใช้แนวคิดนี้ภายในระบบที่อิงตามเอเจนต์
คุณอยู่ทีม InstaVibe
สมมติว่าคุณทำงานที่ "InstaVibe" ซึ่งเป็นสตาร์ทอัพที่ประสบความสำเร็จและมีแพลตฟอร์มกิจกรรมทางสังคมยอดนิยมที่มุ่งเป้าไปที่กลุ่มคนหนุ่มสาว ทุกอย่างเป็นไปด้วยดี แต่ทีมของคุณก็เหมือนกับบริษัทเทคโนโลยีหลายๆ แห่งที่ต้องเผชิญกับแรงกดดันจากนักลงทุนให้คิดค้นนวัตกรรมโดยใช้ AI ภายในองค์กร คุณยังสังเกตเห็นกลุ่มผู้ใช้ที่ไม่ค่อยมีส่วนร่วมเท่ากับคนอื่นๆ ซึ่งอาจเป็นเพราะผู้ใช้กลุ่มนี้ไม่ค่อยอยากเริ่มกิจกรรมกลุ่มหรือพบว่ากระบวนการวางแผนเป็นเรื่องยาก สำหรับบริษัทของคุณ นี่หมายถึงการยึดติดกับแพลตฟอร์มที่ลดลงในกลุ่มผู้ใช้ที่สําคัญนี้
การวิจัยของทีมคุณชี้ให้เห็นว่าความช่วยเหลือที่ขับเคลื่อนด้วย AI จะช่วยปรับปรุงประสบการณ์ของผู้ใช้เหล่านี้ได้อย่างมาก แนวคิดนี้คือการปรับปรุงกระบวนการวางแผนการออกไปสังสรรค์ในโซเชียลด้วยการแนะนำกิจกรรมที่เกี่ยวข้องอย่างรวดเร็วตามความสนใจของผู้ใช้และเพื่อนๆ คำถามที่คุณและเพื่อนร่วมงานต้องเผชิญคือ ตัวแทน AI จะทำให้งานที่มักใช้เวลานานอย่างการค้นพบความสนใจ การวิจัยกิจกรรม และการประสานงานเบื้องต้นทำงานโดยอัตโนมัติได้อย่างไร
โซลูชันที่ใช้ตัวแทน (แนวคิดต้นแบบ)
คุณเสนอให้พัฒนาฟีเจอร์ต้นแบบที่ขับเคลื่อนโดยระบบแบบหลายเอเจนต์ โดยมีรายละเอียดแนวคิดดังนี้

- ตัวแทนการสร้างโปรไฟล์โซเชียล: ตัวแทนนี้ใช้เทคนิคการฟังทางโซเชียลเพื่อวิเคราะห์การเชื่อมต่อ การโต้ตอบของผู้ใช้ และเทรนด์สาธารณะที่อาจกว้างขึ้นซึ่งเกี่ยวข้องกับค่ากำหนดของผู้ใช้ โดยมีวัตถุประสงค์เพื่อระบุความสนใจร่วมกันและลักษณะกิจกรรมที่เหมาะสม (เช่น ความชอบในการสังสรรค์ที่เงียบกว่า งานอดิเรกที่เฉพาะเจาะจง)
- ตัวแทนวางแผนกิจกรรม: ตัวแทนนี้ใช้ข้อมูลเชิงลึกจากตัวแทนการสร้างโปรไฟล์โซเชียลเพื่อค้นหาแหล่งข้อมูลออนไลน์สำหรับกิจกรรม สถานที่ หรือไอเดียที่เฉพาะเจาะจงซึ่งสอดคล้องกับเกณฑ์ที่ระบุ (เช่น สถานที่ ความสนใจ)
- ตัวแทนการโต้ตอบกับแพลตฟอร์ม (ใช้ MCP): ตัวแทนนี้จะรับแผนที่สรุปแล้วจากตัวแทนการวางแผนกิจกรรม ฟังก์ชันหลักของเครื่องมือนี้คือการโต้ตอบกับแพลตฟอร์ม InstaVibe โดยตรงโดยใช้เครื่องมือ MCP (Model Context Protocol) ที่กำหนดไว้ล่วงหน้า เครื่องมือนี้จะช่วยให้ตัวแทนมีความสามารถเฉพาะในการร่างคำแนะนำเกี่ยวกับกิจกรรมและสร้างโพสต์ที่ระบุแผน
- Orchestrator Agent: Agent นี้ทำหน้าที่เป็นผู้ประสานงานส่วนกลาง โดยจะรับคำขอเริ่มต้นของผู้ใช้จากแพลตฟอร์ม InstaVibe ทำความเข้าใจเป้าหมายโดยรวม (เช่น "วางแผนจัดกิจกรรมให้ฉันและเพื่อนๆ หน่อย") แล้วมอบหมายงานที่เฉพาะเจาะจงให้กับเอเจนต์ผู้เชี่ยวชาญที่เหมาะสมตามลำดับอย่างมีเหตุผล โดยจะจัดการการไหลเวียนของข้อมูลระหว่างเอเจนต์และตรวจสอบว่าได้ส่งผลลัพธ์สุดท้ายกลับไปยังผู้ใช้
องค์ประกอบและเทคโนโลยีหลักของสถาปัตยกรรม

Google Cloud Platform (GCP):
- Vertex AI:
- โมเดล Gemini: ให้สิทธิ์เข้าถึงโมเดลภาษาขนาดใหญ่ (LLM) ที่ล้ำสมัยของ Google เช่น Gemini ซึ่งขับเคลื่อนความสามารถในการให้เหตุผลและการตัดสินใจของเอเจนต์
- Vertex AI Agent Engine: บริการที่มีการจัดการซึ่งใช้ในการติดตั้งใช้งาน โฮสต์ และปรับขนาด Agent ตัวประสานงานของเรา ซึ่งช่วยลดความซับซ้อนในการนำไปใช้งานจริงและซ่อนความซับซ้อนของโครงสร้างพื้นฐาน
- Cloud Run: แพลตฟอร์มแบบ Serverless สำหรับการทำให้แอปพลิเคชันที่สร้างโดยใช้คอนเทนเนอร์ใช้งานได้ เราใช้ข้อมูลนี้เพื่อทำสิ่งต่อไปนี้
- โฮสต์เว็บแอปพลิเคชัน InstaVibe หลัก
- ทำให้ใช้งานได้ Agent แต่ละตัวที่เปิดใช้ A2A (Planner, Social Profiling, Platform Interaction) เป็น Microservice อิสระ
- เรียกใช้เซิร์ฟเวอร์เครื่องมือ MCP เพื่อให้ตัวแทนเข้าถึง API ภายในของ InstaVibe ได้
- Spanner: ฐานข้อมูลเชิงสัมพันธ์ที่มีการจัดการครบวงจร มีการกระจายทั่วโลก และมีความสม่ำเสมออย่างมาก ในเวิร์กช็อปนี้ เราจะใช้ประโยชน์จากความสามารถของฐานข้อมูลกราฟโดยใช้ฟีเจอร์ DDL และการค้นหาของ GRAPH เพื่อทำสิ่งต่อไปนี้
- สร้างและจัดเก็บความสัมพันธ์ทางสังคมที่ซับซ้อน (ผู้ใช้ มิตรภาพ การเข้าร่วมกิจกรรม โพสต์)
- เปิดใช้การค้นหาความสัมพันธ์เหล่านี้อย่างมีประสิทธิภาพสำหรับเอเจนต์การสร้างโปรไฟล์ทางสังคม
- Artifact Registry: บริการที่มีการจัดการครบวงจรสำหรับจัดเก็บ จัดการ และรักษาความปลอดภัยของอิมเมจคอนเทนเนอร์
- Cloud Build: บริการที่ดำเนินการกับบิลด์ของคุณใน Google Cloud เราใช้เพื่อสร้างอิมเมจคอนเทนเนอร์ Docker จากซอร์สโค้ดของ Agent และแอปพลิเคชันโดยอัตโนมัติ
- Cloud Storage: ใช้โดยบริการต่างๆ เช่น Cloud Build สำหรับจัดเก็บอาร์ติแฟกต์ของบิลด์ และใช้โดย Agent Engine สำหรับความต้องการด้านการปฏิบัติงาน
- เฟรมเวิร์กและโปรโตคอลหลักของ Agent:
- ชุดเครื่องมือพัฒนาเอเจนต์ (ADK) ของ Google: เฟรมเวิร์กหลักสำหรับ
- การกำหนดตรรกะหลัก ลักษณะการทำงาน และชุดคำสั่งสำหรับเอเจนต์อัจฉริยะแต่ละตัว
- การจัดการวงจร สถานะ และหน่วยความจำของ Agent (สถานะเซสชันระยะสั้นและความรู้ระยะยาวที่อาจเกิดขึ้น)
- การผสานรวมเครื่องมือ (เช่น Google Search หรือเครื่องมือที่สร้างขึ้นเอง) ที่ Agent ใช้โต้ตอบกับโลกได้
- การจัดการเวิร์กโฟลว์แบบหลาย Agent รวมถึงการดำเนินการแบบลำดับ แบบวนซ้ำ และแบบขนานของ Agent ย่อย
- โปรโตคอลการสื่อสารแบบ Agent-to-Agent (A2A): มาตรฐานแบบเปิดที่ช่วยให้ทำสิ่งต่อไปนี้ได้
- การสื่อสารและการทำงานร่วมกันโดยตรงและเป็นมาตรฐานระหว่างเอเจนต์ AI ต่างๆ แม้ว่าจะทำงานเป็นบริการแยกต่างหากหรือในเครื่องที่ต่างกันก็ตาม
- เอเจนต์จะค้นพบความสามารถของกันและกัน (ผ่านการ์ดเอเจนต์) และมอบหมายงาน ซึ่งมีความสำคัญอย่างยิ่งต่อตัวแทน Orchestrator ในการโต้ตอบกับตัวแทน Planner, Social และ Platform ที่เชี่ยวชาญ
- ไลบรารี A2A Python (a2a-python): ไลบรารีที่ใช้เพื่อทำให้ Agent ADK ของเราพูดโปรโตคอล A2A โดยจะให้คอมโพเนนต์ฝั่งเซิร์ฟเวอร์ที่จำเป็นต่อการทำสิ่งต่อไปนี้
- แสดงเอเจนต์ของเราเป็นเซิร์ฟเวอร์ที่สอดคล้องกับ A2A
- จัดการการแสดง "การ์ดเอเจนต์" เพื่อการค้นพบโดยอัตโนมัติ
- รับและจัดการคำของานขาเข้าจากตัวแทนรายอื่นๆ (เช่น Orchestrator)
- Model Context Protocol (MCP): มาตรฐานแบบเปิดที่ช่วยให้ Agent ทำสิ่งต่อไปนี้ได้
- เชื่อมต่อและใช้เครื่องมือ แหล่งข้อมูล และระบบภายนอกในลักษณะที่เป็นมาตรฐาน
- Platform Interaction Agent ของเราใช้ไคลเอ็นต์ MCP เพื่อสื่อสารกับเซิร์ฟเวอร์ MCP ซึ่งจะแสดงเครื่องมือเพื่อโต้ตอบกับ API ที่มีอยู่ของแพลตฟอร์ม InstaVibe
- ชุดเครื่องมือพัฒนาเอเจนต์ (ADK) ของ Google: เฟรมเวิร์กหลักสำหรับ
- เครื่องมือแก้ไขข้อบกพร่อง:
- A2A Inspector: A2A Inspector เป็นเครื่องมือแก้ไขข้อบกพร่องบนเว็บที่ใช้ตลอดทั้งเวิร์กช็อปนี้เพื่อเชื่อมต่อ ตรวจสอบ และโต้ตอบกับเอเจนต์ที่เปิดใช้ A2A แม้จะไม่ใช่ส่วนหนึ่งของสถาปัตยกรรมการผลิตขั้นสุดท้าย แต่ก็เป็นส่วนสำคัญของเวิร์กโฟลว์การพัฒนาของเรา โดยมีฟีเจอร์ต่อไปนี้
- Agent Card Viewer: เพื่อดึงและตรวจสอบความสามารถสาธารณะของเอเจนต์
- อินเทอร์เฟซแชทสด: เพื่อส่งข้อความโดยตรงไปยังเอเจนต์ที่ทำให้ใช้งานได้เพื่อทดสอบทันที
- คอนโซลการแก้ไขข้อบกพร่อง: เพื่อดูข้อความ JSON-RPC ดิบที่แลกเปลี่ยนระหว่างเครื่องมือตรวจสอบกับตัวแทน
- A2A Inspector: A2A Inspector เป็นเครื่องมือแก้ไขข้อบกพร่องบนเว็บที่ใช้ตลอดทั้งเวิร์กช็อปนี้เพื่อเชื่อมต่อ ตรวจสอบ และโต้ตอบกับเอเจนต์ที่เปิดใช้ A2A แม้จะไม่ใช่ส่วนหนึ่งของสถาปัตยกรรมการผลิตขั้นสุดท้าย แต่ก็เป็นส่วนสำคัญของเวิร์กโฟลว์การพัฒนาของเรา โดยมีฟีเจอร์ต่อไปนี้
- โมเดลภาษา (LLM): "สมอง" ของระบบ:
- โมเดล Gemini ของ Google: เราใช้เวอร์ชันต่างๆ เช่น gemini-2.0-flash เราเลือกรุ่นเหล่านี้เนื่องจากเหตุผลต่อไปนี้
- การให้เหตุผลขั้นสูงและการทำตามคำสั่ง: ความสามารถในการทำความเข้าใจพรอมต์ที่ซับซ้อน ทำตามคำสั่งโดยละเอียด และให้เหตุผลเกี่ยวกับงานต่างๆ ทำให้โมเดลเหล่านี้เหมาะสำหรับการขับเคลื่อนการตัดสินใจของเอเจนต์
- การใช้เครื่องมือ (การเรียกฟังก์ชัน): โมเดล Gemini มีความสามารถในการพิจารณาเวลาและวิธีการใช้เครื่องมือที่ให้ไว้ผ่าน ADK ซึ่งช่วยให้เอเจนต์รวบรวมข้อมูลหรือดำเนินการได้
- ประสิทธิภาพ (โมเดล Flash): โมเดลเวอร์ชัน "Flash" มีความสมดุลที่ดีระหว่างประสิทธิภาพและความคุ้มค่า เหมาะสำหรับงานของเอเจนต์แบบอินเทอร์แอกทีฟหลายอย่างที่ต้องตอบกลับอย่างรวดเร็ว
- โมเดล Gemini ของ Google: เราใช้เวอร์ชันต่างๆ เช่น gemini-2.0-flash เราเลือกรุ่นเหล่านี้เนื่องจากเหตุผลต่อไปนี้
หากต้องการเครดิต Google Cloud
3. ก่อนเริ่มต้น
👉คลิกเปิดใช้งาน Cloud Shell ที่ด้านบนของคอนโซล Google Cloud (ไอคอนรูปเทอร์มินัลที่ด้านบนของแผง Cloud Shell) 
👉คลิกปุ่ม "เปิดเอดิเตอร์" (มีลักษณะเป็นโฟลเดอร์ที่เปิดอยู่พร้อมดินสอ) ซึ่งจะเปิดตัวแก้ไขโค้ด Cloud Shell ในหน้าต่าง คุณจะเห็น File Explorer ทางด้านซ้าย 
👉คลิกปุ่มลงชื่อเข้าใช้ Cloud Code ในแถบสถานะด้านล่างตามที่แสดง ให้สิทธิ์ปลั๊กอินตามวิธีการ หากเห็น Cloud Code - ไม่มีโปรเจ็กต์ในแถบสถานะ ให้เลือกข้อความดังกล่าว จากนั้นเลือก "เลือกโปรเจ็กต์ Google Cloud" ในเมนูแบบเลื่อนลง แล้วเลือกโปรเจ็กต์ Google Cloud ที่ต้องการจากรายการโปรเจ็กต์ที่คุณสร้าง 
👉 ค้นหารหัสโปรเจ็กต์ Google Cloud
- เปิด คอนโซล Google Cloud: https://console.cloud.google.com
- เลือกโปรเจ็กต์ที่ต้องการใช้สำหรับเวิร์กช็อปนี้จากเมนูแบบเลื่อนลงของโปรเจ็กต์ที่ด้านบนของหน้า
- รหัสโปรเจ็กต์จะแสดงในการ์ดข้อมูลโปรเจ็กต์ในแดชบอร์ด

👉เปิดเทอร์มินัลใน Cloud IDE 
👉💻 ในเทอร์มินัล ให้ตรวจสอบว่าคุณได้รับการตรวจสอบสิทธิ์แล้วและตั้งค่าโปรเจ็กต์เป็นรหัสโปรเจ็กต์โดยใช้คำสั่งต่อไปนี้
gcloud auth list
👉💻 โคลนinstavibe-bootstrapโปรเจ็กต์จาก GitHub:
git clone -b adk-1.2.1-a2a-0.2.7 https://github.com/weimeilin79/instavibe-bootstrap.git
chmod +x ~/instavibe-bootstrap/init.sh
chmod +x ~/instavibe-bootstrap/set_env.sh
ทำความเข้าใจโครงสร้างโปรเจ็กต์
ก่อนเริ่มสร้าง เรามาทำความเข้าใจเลย์เอาต์ของโปรเจ็กต์ instavibe-bootstrap ที่คุณเพิ่งโคลนกันสักครู่ ซึ่งจะช่วยให้คุณทราบว่าควรไปที่ใดเพื่อค้นหาและแก้ไขไฟล์ตลอดทั้งเวิร์กช็อป
instavibe-bootstrap/
├── agents/
│ ├── orchestrate/
│ ├── planner/
│ ├── platform_mcp_client/
│ └── social/
├── instavibe/
│ ├── static/
│ └── templates/
├── tools/
│ └── instavibe/
├── utils/
├── init.sh
└── set_env.sh
รายละเอียดของไดเรกทอรีหลักมีดังนี้
agents/: นี่คือหัวใจสำคัญของระบบ AI ของเรา แต่ละไดเรกทอรีย่อย (planner/, social/ ฯลฯ) จะมีซอร์สโค้ดสำหรับเอเจนต์อัจฉริยะที่เฉพาะเจาะจงagent.py: ภายในโฟลเดอร์ของตัวแทนแต่ละราย ไฟล์นี้คือไฟล์หลักที่มีตรรกะของตัวแทนa2a_server.py: ไฟล์นี้จะรวม Agent ADK ไว้กับเซิร์ฟเวอร์ Agent-to-Agent (A2A)Dockerfile: กำหนดวิธีสร้างอิมเมจคอนเทนเนอร์สำหรับการทำให้ Agent ใช้งานได้ใน Cloud Run หรือ Agent Engine
instavibe/: ไดเรกทอรีนี้มีซอร์สโค้ดทั้งหมดสำหรับเว็บแอปพลิเคชัน InstaVibetools/: ไดเรกทอรีนี้มีไว้สำหรับการสร้างเครื่องมือภายนอกที่ตัวแทนของเราใช้ได้instavibe/มีเซิร์ฟเวอร์ Model Context Protocol (MCP)
โครงสร้างแบบโมดูลนี้จะแยกเว็บแอปพลิเคชันออกจากคอมโพเนนต์ AI ต่างๆ ทำให้ทั้งระบบจัดการ ทดสอบ และติดตั้งใช้งานได้ง่ายขึ้น
👉💻 เรียกใช้สคริปต์การเริ่มต้น
สคริปต์นี้จะแจ้งให้คุณป้อนรหัสโปรเจ็กต์ Google Cloud
ป้อนรหัสโปรเจ็กต์ Google Cloud ที่คุณพบจากขั้นตอนสุดท้ายเมื่อสคริปต์ init.sh แจ้งให้ป้อน
cd ~/instavibe-bootstrap
./init.sh
👉💻 ตั้งค่ารหัสโปรเจ็กต์ที่จำเป็น
gcloud config set project $(cat ~/project_id.txt) --quiet
👉💻 เรียกใช้คำสั่งต่อไปนี้เพื่อเปิดใช้ Google Cloud APIs ที่จำเป็น
gcloud services enable run.googleapis.com \
cloudfunctions.googleapis.com \
cloudbuild.googleapis.com \
artifactregistry.googleapis.com \
spanner.googleapis.com \
apikeys.googleapis.com \
iam.googleapis.com \
compute.googleapis.com \
aiplatform.googleapis.com \
cloudresourcemanager.googleapis.com \
maps-backend.googleapis.com
👉💻 ตั้งค่าตัวแปรสภาพแวดล้อมที่จำเป็นทั้งหมด
export PROJECT_ID=$(gcloud config get project)
export PROJECT_NUMBER=$(gcloud projects describe ${PROJECT_ID} --format="value(projectNumber)")
export SERVICE_ACCOUNT_NAME=$(gcloud compute project-info describe --format="value(defaultServiceAccount)")
export SPANNER_INSTANCE_ID="instavibe-graph-instance"
export SPANNER_DATABASE_ID="graphdb"
export GOOGLE_CLOUD_PROJECT=$(gcloud config get project)
export GOOGLE_GENAI_USE_VERTEXAI=TRUE
export GOOGLE_CLOUD_LOCATION="us-central1"
การตั้งค่าสิทธิ์
👉💻 ให้สิทธิ์ ในเทอร์มินัล ให้เรียกใช้คำสั่งต่อไปนี้
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/spanner.admin"
# Spanner Database User
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/spanner.databaseUser"
# Artifact Registry Admin
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/artifactregistry.admin"
# Cloud Build Editor
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/cloudbuild.builds.editor"
# Cloud Run Admin
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/run.admin"
# IAM Service Account User
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/iam.serviceAccountUser"
# Vertex AI User
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/aiplatform.user"
# Logging Writer (to allow writing logs)
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/logging.logWriter"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/logging.viewer"
👉 ตรวจสอบผลลัพธ์ในคอนโซล IAM
👉💻 เรียกใช้คำสั่งต่อไปนี้ในเทอร์มินัลเพื่อสร้างที่เก็บ Artifact Registry อิมเมจ Docker ทั้งหมดสำหรับเอเจนต์ เซิร์ฟเวอร์ MCP และแอปพลิเคชัน InstaVibe จะจัดเก็บไว้ที่นี่ก่อนที่จะนำไปใช้งานใน Cloud Run หรือ Agent Engine
export REPO_NAME="introveally-repo"
gcloud artifacts repositories create $REPO_NAME \
--repository-format=docker \
--location=us-central1 \
--description="Docker repository for InstaVibe workshop"
ตั้งค่าแพลตฟอร์มแผนที่สำหรับคีย์ API
หากต้องการใช้บริการ Google Maps ในแอปพลิเคชัน InstaVibe คุณต้องสร้างคีย์ API และจำกัดคีย์อย่างเหมาะสม
👉 ในแท็บใหม่ ให้ไปที่ API และบริการ > ข้อมูลเข้าสู่ระบบ ในหน้า "ข้อมูลเข้าสู่ระบบ" ให้คลิกปุ่ม + สร้างข้อมูลเข้าสู่ระบบที่ด้านบน เลือกคีย์ API จากเมนูแบบเลื่อนลง 
👉 กล่องโต้ตอบจะปรากฏขึ้นพร้อมแสดงคีย์ API ที่สร้างขึ้นใหม่ คุณจะต้องใช้ข้อมูลนี้ในภายหลังเพื่อกำหนดค่าแอปพลิเคชัน
👉 คลิกปิดในกล่องโต้ตอบ "สร้างคีย์ API แล้ว"
👉 คุณจะเห็นคีย์ API ใหม่ปรากฏในรายการ (เช่น "คีย์ API 1") คลิกจุด 3 จุดทางด้านขวา แล้วเลือกแก้ไขคีย์ API เพื่อเปิดหน้า "จำกัดและเปลี่ยนชื่อคีย์ API" 
👉 ในช่องชื่อที่ด้านบน ให้เปลี่ยนชื่อเริ่มต้นเป็น คีย์ API ของ Maps Platform (🚨🚨สำคัญ🚨🚨 โปรดใช้ชื่อนี้)
Maps Platform API Key
👉 ในส่วน "ข้อจำกัดของแอปพลิเคชัน" ให้ตรวจสอบว่าได้เลือกไม่มี
👉 ในส่วน "การจำกัด API" ให้เลือกปุ่มตัวเลือกจำกัดคีย์
👉 คลิกเมนูแบบเลื่อนลงเลือก API ในช่องค้นหาที่ปรากฏขึ้น ให้พิมพ์ Maps JavaScript API แล้วเลือกจากรายการ 
👉 คลิกตกลง
👉 คลิกปุ่มบันทึกที่ด้านล่างของหน้า

ตอนนี้คุณได้สร้างคีย์ API ชื่อ "คีย์ API ของ Maps Platform" เรียบร้อยแล้ว โดยจำกัดให้ใช้ได้เฉพาะ "Maps JavaScript API" และตรวจสอบว่าได้เปิดใช้ API สำหรับโปรเจ็กต์แล้ว
4. ตั้งค่าฐานข้อมูลกราฟ
ก่อนที่จะสร้างเอเจนต์อัจฉริยะได้ เราต้องมีวิธีจัดเก็บและทำความเข้าใจการเชื่อมต่อที่ซับซ้อนภายในโซเชียลเน็ตเวิร์ก InstaVibe ซึ่งเป็นจุดที่ฐานข้อมูลกราฟเข้ามามีบทบาท ฐานข้อมูลกราฟได้รับการออกแบบมาโดยเฉพาะเพื่อแสดงและค้นหาข้อมูลในรูปแบบของโหนด (เช่น ผู้คน เหตุการณ์ หรือโพสต์) และความสัมพันธ์ (ขอบ) ที่เชื่อมโยงโหนดเหล่านั้น (เช่น มิตรภาพ การเข้าร่วมกิจกรรม หรือการกล่าวถึง) ซึ่งแตกต่างจากฐานข้อมูลเชิงสัมพันธ์แบบเดิมที่จัดเก็บข้อมูลในตารางของแถวและคอลัมน์ โครงสร้างนี้มีประสิทธิภาพอย่างยิ่งสำหรับแอปพลิเคชันโซเชียลมีเดีย เนื่องจากจำลองวิธีที่โซเชียลเน็ตเวิร์กในโลกแห่งความเป็นจริงมีโครงสร้าง ทำให้สำรวจวิธีที่เอนทิตีต่างๆ เชื่อมต่อกันได้อย่างง่ายดาย
เรากำลังใช้ฐานข้อมูลกราฟนี้โดยใช้ Google Cloud Spanner แม้ว่า Spanner จะเป็นที่รู้จักกันในฐานะฐานข้อมูลเชิงสัมพันธ์แบบกระจายทั่วโลกที่มีความสอดคล้องกันอย่างมาก แต่ก็ยังช่วยให้เรากำหนดและค้นหาโครงสร้างกราฟได้โดยตรงบนตารางเชิงสัมพันธ์
ซึ่งทำให้เราได้รับประโยชน์ร่วมกันจากความสามารถในการปรับขนาด ความสอดคล้องของธุรกรรม และอินเทอร์เฟซ SQL ที่คุ้นเคยของ Spanner รวมถึงความสามารถในการแสดงออกของการค้นหากราฟสำหรับการวิเคราะห์พลวัตทางสังคมที่ซับซ้อนซึ่งมีความสำคัญต่อฟีเจอร์ที่ทำงานด้วยระบบ AI
👉💻 ในเทอร์มินัลของ Cloud Shell IDE จัดสรรโครงสร้างพื้นฐานที่จำเป็นใน Google Cloud เราจะเริ่มต้นด้วยการสร้างอินสแตนซ์ Spanner ซึ่งทำหน้าที่เป็นคอนเทนเนอร์เฉพาะสำหรับฐานข้อมูล เมื่ออินสแตนซ์พร้อมแล้ว เราจะสร้างฐานข้อมูล Spanner จริงภายในอินสแตนซ์ ซึ่งจะจัดเก็บตารางทั้งหมดและข้อมูลกราฟสำหรับ InstaVibe
. ~/instavibe-bootstrap/set_env.sh
gcloud spanner instances create $SPANNER_INSTANCE_ID \
--config=regional-us-central1 \
--description="GraphDB Instance InstaVibe" \
--processing-units=100 \
--edition=ENTERPRISE
gcloud spanner databases create $SPANNER_DATABASE_ID \
--instance=$SPANNER_INSTANCE_ID \
--database-dialect=GOOGLE_STANDARD_SQL
👉💻 ให้สิทธิ์การอ่าน/เขียน Spanner แก่บัญชีบริการเริ่มต้น
echo "Granting Spanner read/write access to ${SERVICE_ACCOUNT_NAME} for database ${SPANNER_DATABASE_ID}..."
gcloud spanner databases add-iam-policy-binding ${SPANNER_DATABASE_ID} \
--instance=${SPANNER_INSTANCE_ID} \
--member="serviceAccount:${SERVICE_ACCOUNT_NAME}" \
--role="roles/spanner.databaseUser" \
--project=${PROJECT_ID}
👉💻 ตอนนี้ เราจะตั้งค่าสภาพแวดล้อมเสมือนของ Python ติดตั้งแพ็กเกจ Python ที่จำเป็น จากนั้นตั้งค่าสคีมาของฐานข้อมูลกราฟภายใน Spanner และโหลดด้วยข้อมูลเริ่มต้น แล้วเรียกใช้สคริปต์ setup.py
. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap
python -m venv env
source env/bin/activate
pip install -r requirements.txt
cd instavibe
python setup.py
ในแท็บเบราว์เซอร์ใหม่ ให้ไปที่ คอนโซล Google Cloud แล้วไปที่ Spanner คุณควรเห็นรายการอินสแตนซ์ Spanner คลิก instavibe-graph-instance
👉 ในหน้าภาพรวมของอินสแตนซ์ คุณจะเห็นรายการฐานข้อมูลภายในอินสแตนซ์นั้น คลิก graphdb
👉 ในแผงการนำทางด้านซ้ายของฐานข้อมูล ให้คลิก Spanner Studio 
ในตัวแก้ไขคำค้นหา (แท็บการค้นหาที่ไม่มีชื่อ) ให้วางคำค้นหา Graph SQL ต่อไปนี้ การค้นหานี้จะค้นหาโหนด Person ทั้งหมดและความสัมพันธ์ Friendship โดยตรงกับโหนด Person อื่นๆ แล้วคลิกRUNเพื่อดูผลลัพธ์
Graph SocialGraph
MATCH result_paths = ((p:Person)-[f:Friendship]-(friend:Person))
RETURN SAFE_TO_JSON(result_paths) AS result_paths

👉 ในเครื่องมือแก้ไขคำค้นหาเดียวกัน ให้แทนที่ DDL ก่อนหน้าเพื่อค้นหาผู้ที่เข้าร่วมกิจกรรมเดียวกัน ซึ่งหมายถึงการเชื่อมต่อทางอ้อมผ่านกิจกรรมที่แชร์
Graph SocialGraph
MATCH result_paths = (p1:Person)-[:Attended]->(e:Event)<-[:Attended]-(p2:Person)
WHERE p1.person_id < p2.person_id
RETURN SAFE_TO_JSON(result_paths) AS result_paths

👉 คำค้นหานี้จะสำรวจการเชื่อมต่ออีกประเภทหนึ่ง ซึ่งก็คือการที่ผู้คนกล่าวถึงในโพสต์ที่เพื่อนของบุคคลหนึ่งๆ เขียนขึ้น ให้เรียกใช้คำค้นหาต่อไปนี้ในเครื่องมือแก้ไขคำค้นหา
Graph SocialGraph
MATCH result_paths = (user:Person {name: "Alice"})-[:Friendship]-(friend:Person)-[:Wrote]->(post:Post)-[:Mentioned]->(mentioned_person:Person)
WHERE user <> mentioned_person AND friend <> mentioned_person -- Avoid self-mentions or friend mentioning themselves in their own post if not intended
RETURN SAFE_TO_JSON(result_paths) AS result_paths

คําค้นหาเหล่านี้เป็นเพียงตัวอย่างความสามารถของการใช้ Spanner เป็นฐานข้อมูลกราฟสําหรับแอปพลิเคชัน InstaVibe ของเรา การสร้างโมเดลข้อมูลโซเชียลเป็นกราฟที่เชื่อมต่อกันจะช่วยให้เราวิเคราะห์ความสัมพันธ์และกิจกรรมต่างๆ ได้อย่างซับซ้อน ซึ่งจะเป็นพื้นฐานสำคัญสำหรับเอเจนต์ AI ในการทำความเข้าใจบริบทของผู้ใช้ ค้นพบความสนใจ และท้ายที่สุดคือให้ความช่วยเหลือด้านการวางแผนโซเชียลอย่างชาญฉลาด
เมื่อมีโครงสร้างข้อมูลพื้นฐานที่สร้างและทดสอบแล้ว ตอนนี้เรามาดูแอปพลิเคชัน InstaVibe ที่มีอยู่ซึ่งตัวแทนจะใช้กัน
5. สถานะปัจจุบันของ InstaVibe
หากต้องการทราบว่า AI Agent ของเราจะทำงานในส่วนใด เราต้องทำให้ใช้งานได้และเรียกใช้เว็บแอปพลิเคชัน InstaVibe ที่มีอยู่ก่อน แอปพลิเคชันนี้มีอินเทอร์เฟซผู้ใช้และฟังก์ชันพื้นฐานที่เชื่อมต่อกับฐานข้อมูลกราฟ Spanner ที่เราตั้งค่าไว้แล้ว

แอปพลิเคชัน InstaVibe ใช้ Google Maps เพื่อแสดงตำแหน่งกิจกรรมในรูปแบบภาพบนหน้ารายละเอียดกิจกรรม หากต้องการเปิดใช้ฟังก์ชันนี้ แอปพลิเคชันต้องใช้คีย์ API ที่เราสร้างไว้ก่อนหน้านี้ สคริปต์ต่อไปนี้จะดึงสตริงคีย์จริงโดยใช้ชื่อที่แสดงที่เรากำหนด ("คีย์ API ของ Maps Platform")

👉💻 กลับไปที่ IDE ของ Cloud Shell เรียกใช้สคริปต์ด้านล่าง หลังจากนั้น ให้ตรวจสอบเอาต์พุตอย่างละเอียดเพื่อให้แน่ใจว่า GOOGLE_MAPS_API_KEY ที่แสดงตรงกับคีย์ที่คุณสร้างและคัดลอกจากคอนโซล Google Cloud ก่อนหน้านี้
. ~/instavibe-bootstrap/set_env.sh
export KEY_DISPLAY_NAME="Maps Platform API Key"
GOOGLE_MAPS_KEY_ID=$(gcloud services api-keys list \
--project="${PROJECT_ID}" \
--filter="displayName='${KEY_DISPLAY_NAME}'" \
--format="value(uid)" \
--limit=1)
GOOGLE_MAPS_API_KEY=$(gcloud services api-keys get-key-string "${GOOGLE_MAPS_KEY_ID}" \
--project="${PROJECT_ID}" \
--format="value(keyString)")
echo "${GOOGLE_MAPS_API_KEY}" > ~/mapkey.txt
echo "Retrieved GOOGLE_MAPS_API_KEY: ${GOOGLE_MAPS_API_KEY}"

👉💻 ตอนนี้มาสร้างอิมเมจคอนเทนเนอร์สำหรับเว็บแอปพลิเคชัน InstaVibe และพุชไปยังที่เก็บ Artifact Registry กัน
. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap/instavibe/
export IMAGE_TAG="latest"
export APP_FOLDER_NAME="instavibe"
export IMAGE_NAME="instavibe-webapp"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="instavibe"
gcloud builds submit . \
--tag=${IMAGE_PATH} \
--project=${PROJECT_ID}
👉💻 ทำให้รูปภาพเว็บแอป InstaVibe ที่สร้างใหม่ใช้งานได้ใน Cloud Run
. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap/instavibe/
export IMAGE_TAG="latest"
export APP_FOLDER_NAME="instavibe"
export IMAGE_NAME="instavibe-webapp"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="instavibe"
gcloud run deploy ${SERVICE_NAME} \
--image=${IMAGE_PATH} \
--platform=managed \
--region=${REGION} \
--allow-unauthenticated \
--set-env-vars="SPANNER_INSTANCE_ID=${SPANNER_INSTANCE_ID}" \
--set-env-vars="SPANNER_DATABASE_ID=${SPANNER_DATABASE_ID}" \
--set-env-vars="APP_HOST=0.0.0.0" \
--set-env-vars="APP_PORT=8080" \
--set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
--set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
--set-env-vars="GOOGLE_MAPS_API_KEY=${GOOGLE_MAPS_API_KEY}" \
--project=${PROJECT_ID} \
--min-instances=1
เมื่อการติดตั้งใช้งานเสร็จสมบูรณ์แล้ว บันทึกของ Cloud Run ควรแสดง URL สาธารณะสำหรับแอปพลิเคชัน InstaVibe ที่ทำงานอยู่

คุณยังค้นหา URL นี้ได้โดยไปที่ส่วน Cloud Run ในคอนโซล Google Cloud แล้วเลือกบริการ instavibe 

เปิด URL นั้นในเว็บเบราว์เซอร์ตอนนี้เลยเพื่อสำรวจแพลตฟอร์ม InstaVibe ขั้นพื้นฐาน ดูโพสต์ กิจกรรม และการเชื่อมต่อผู้ใช้ที่ขับเคลื่อนโดยฐานข้อมูลกราฟที่เราตั้งค่าไว้
ตอนนี้เรามีแอปพลิเคชันเป้าหมายที่ทำงานอยู่แล้ว มาเริ่มสร้าง Agent อัจฉริยะตัวแรกเพื่อเพิ่มความสามารถกัน
6. Basic Agent,Event Planner พร้อม ADK
เฟรมเวิร์ก ADK
ข้อมูลเบื้องต้นเกี่ยวกับเฟรมเวิร์ก ADK ของ Google เมื่อตั้งค่าพื้นฐาน (แอปและฐานข้อมูล InstaVibe) แล้ว เราจะเริ่มสร้าง Agent อัจฉริยะตัวแรกได้โดยใช้ Agent Development Kit (ADK) ของ Google
Agent Development Kit (ADK) เป็นเฟรมเวิร์กที่ยืดหยุ่นและเป็นโมดูลาร์ซึ่งออกแบบมาเพื่อพัฒนาและติดตั้งใช้งาน AI Agent โดยเฉพาะ หลักการออกแบบคือการทําให้การพัฒนา Agent รู้สึกเหมือนการพัฒนาซอฟต์แวร์แบบดั้งเดิม โดยมีเป้าหมายเพื่อทําให้นักพัฒนาซอฟต์แวร์สร้าง ทำให้ใช้งานได้ และจัดการเป็นกลุ่มสถาปัตยกรรมที่เป็น Agent ที่จัดการได้ทุกอย่างตั้งแต่การทำงานแบบง่ายๆ แบบเดี่ยวไปจนถึงเวิร์กโฟลว์ที่ซับซ้อนแบบหลาย Agent ได้ง่ายขึ้นอย่างมาก
หัวใจสำคัญของ ADK คือแนวคิดของ Agent ซึ่งสรุปคำสั่ง การกำหนดค่า (เช่น โมเดลภาษาที่เลือก เช่น Gemini) และชุด Tools ที่ใช้เพื่อดำเนินการหรือรวบรวมข้อมูล

Agent ตัวแรกของเราจะเป็น "ผู้จัดกิจกรรม" โดยมีจุดประสงค์หลักคือการรับคำขอของผู้ใช้สำหรับการออกไปสังสรรค์ (ระบุสถานที่ วันที่ และความสนใจ) และสร้างคำแนะนำที่สร้างสรรค์และปรับแต่งมาโดยเฉพาะ เพื่อให้มั่นใจว่าคำแนะนำมีความเกี่ยวข้องและอิงตามข้อมูลปัจจุบัน (เช่น กิจกรรมที่เฉพาะเจาะจงซึ่งเกิดขึ้นในช่วงสุดสัปดาห์นั้น) เราจะใช้ประโยชน์จากเครื่องมือในตัวของ ADK อย่างหนึ่ง นั่นคือ Google Search ซึ่งจะช่วยให้เอเจนต์อ้างอิงคำตอบจากผลการค้นหาบนเว็บแบบเรียลไทม์ โดยดึงรายละเอียดล่าสุดเกี่ยวกับสถานที่จัดงาน กิจกรรม และกิจกรรมที่ตรงกับเกณฑ์ของผู้ใช้
👉📝 กลับไปที่ Cloud Shell IDE ใน ~/instavibe-bootstrap/agents/planner/agent.py ให้เพิ่มพรอมต์และวิธีการต่อไปนี้เพื่อสร้าง Agent
from google.adk.agents import Agent
from google.adk.tools import google_search
root_agent = Agent(
name="planner_agent",
model="gemini-2.0-flash",
description="Agent tasked with generating creative and fun dating plan suggestions",
instruction="""
You are a specialized AI assistant tasked with generating creative and fun plan suggestions.
Request:
For the upcoming weekend, specifically from **[START_DATE_YYYY-MM-DD]** to **[END_DATE_YYYY-MM-DD]**, in the location specified as **[TARGET_LOCATION_NAME_OR_CITY_STATE]** (if latitude/longitude are provided, use these: Lat: **[TARGET_LATITUDE]**, Lon: **[TARGET_LONGITUDE]**), please generate a distinct dating plan suggestions.
Constraints and Guidelines for Suggestions:
1. Creativity & Fun: Plans should be engaging, memorable, and offer a good experience for a date.
2. Budget: All generated plans should aim for a moderate budget (conceptually "$$"), meaning they should be affordable yet offer good value, without being overly cheap or extravagant. This budget level should be *reflected in the choice of activities and venues*, but **do not** explicitly state "Budget: $$" in the `plan_description`.
3. Interest Alignment:
Consider the following user interests: **[COMMA_SEPARATED_LIST_OF_INTERESTS, e.g., outdoors, arts & culture, foodie, nightlife, unique local events, live music, active/sports]**. Tailor suggestions specifically to these where possible. The plan should *embody* these interests.
Fallback: If specific events or venues perfectly matching all listed user interests cannot be found for the specified weekend, you should create a creative and fun generic dating plan that is still appealing, suitable for the location, and adheres to the moderate budget. This plan should still sound exciting and fun, even if it's more general.
4. Current & Specific: Prioritize finding specific, current events, festivals, pop-ups, or unique local venues operating or happening during the specified weekend dates. If exact current events cannot be found, suggest appealing evergreen options or implement the fallback generic plan.
5. Location Details: For each place or event mentioned within a plan, you MUST provide its name, precise latitude, precise longitude, and a brief, helpful description.
6. Maximum Activities: The plan must contain a maximum of 3 distinct activities.
RETURN PLAN in MARKDOWN FORMAT
""",
tools=[google_search]
)
และนี่ก็คือ Agent ตัวแรกที่เรากำหนด ข้อดีอย่างหนึ่งของ ADK คือลักษณะที่ใช้งานง่ายและเครื่องมือที่มีประโยชน์ เครื่องมือที่มีประโยชน์อย่างยิ่งคือ ADK Dev UI ซึ่งช่วยให้คุณทดสอบ Agent แบบอินเทอร์แอกทีฟและดูคำตอบได้แบบเรียลไทม์
👉💻 มาเริ่มกันเลย คำสั่งต่อไปนี้จะเปิด UI ของ ADK DEV
. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd ~/instavibe-bootstrap/agents
sed -i "s|^\(O\?GOOGLE_CLOUD_PROJECT\)=.*|GOOGLE_CLOUD_PROJECT=${PROJECT_ID}|" ~/instavibe-bootstrap/agents/planner/.env
adk web
หลังจากเรียกใช้คำสั่งแล้ว คุณควรเห็นเอาต์พุตในเทอร์มินัลที่ระบุว่าเว็บเซิร์ฟเวอร์ ADK เริ่มทำงานแล้ว ซึ่งมีลักษณะคล้ายกับเอาต์พุตนี้
+-----------------------------------------------------------------------------+
| ADK Web Server started |
| |
| For local testing, access at http://localhost:8000. |
+-----------------------------------------------------------------------------+
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
👉 จากนั้น หากต้องการเข้าถึง UI สำหรับนักพัฒนาแอป ADK จากเบราว์เซอร์ ให้ทำดังนี้
จากไอคอนตัวอย่างเว็บ (มักจะมีลักษณะเป็นรูปดวงตาหรือสี่เหลี่ยมที่มีลูกศร) ในแถบเครื่องมือ Cloud Shell (มักจะอยู่ด้านขวาบน) ให้เลือกเปลี่ยนพอร์ต ในหน้าต่างป๊อปอัป ให้ตั้งค่าพอร์ตเป็น 8000 แล้วคลิก "เปลี่ยนและแสดงตัวอย่าง" จากนั้น Cloud Shell จะเปิดแท็บเบราว์เซอร์หรือหน้าต่างใหม่ที่แสดง UI ของ ADK Dev

เมื่อเปิด ADK Dev UI ในเบราว์เซอร์แล้ว ให้เลือก planner เป็นเอเจนต์ที่คุณต้องการโต้ตอบในเมนูแบบเลื่อนลงด้านขวาบนของ UI ตอนนี้ในกล่องโต้ตอบแชททางด้านขวา ให้ลองมอบหมายงานให้ตัวแทน เช่น สนทนากับตัวแทนดังนี้
Search and plan something in Seattle for me this weekend
This weekend and I enjoy food and anime
แนะนำวันที่ (วันที่ที่คุณต้องการ)
July 12 2025
คุณควรเห็นตัวแทนประมวลผลคำขอและเสนอแผนตามผลการค้นหาของ Google Search

การโต้ตอบกับตัวแทนเป็นเรื่องหนึ่ง แต่เราจะรู้ได้อย่างไรว่าตัวแทนทำงานได้ตามที่คาดไว้เสมอ โดยเฉพาะอย่างยิ่งเมื่อเราทำการเปลี่ยนแปลง
วิธีการทดสอบซอฟต์แวร์แบบดั้งเดิมมักไม่เพียงพอสำหรับเอเจนต์ AI เนื่องจากลักษณะการสร้างและลักษณะที่ไม่แน่นอน กลยุทธ์การประเมินที่มั่นคงเป็นสิ่งสำคัญในการเชื่อมช่องว่างจากเดโมที่ยอดเยี่ยมไปสู่เอเจนต์เวอร์ชันที่ใช้งานจริงที่เชื่อถือได้ การประเมินเอเจนต์มักเกี่ยวข้องกับการประเมินกระบวนการตัดสินใจและความสามารถในการใช้เครื่องมืออย่างถูกต้องหรือทำตามคำสั่งในสถานการณ์ต่างๆ ซึ่งแตกต่างจากการตรวจสอบเอาต์พุตสุดท้ายของโมเดล Generative AI เพียงอย่างเดียว ADK มีฟีเจอร์ที่จะช่วยในเรื่องนี้

👉 ใน UI ของ ADK Dev ให้คลิกแท็บ "Eval" ในการนำทางด้านซ้าย คุณควรเห็นไฟล์ทดสอบที่โหลดไว้ล่วงหน้าชื่อ plan_eval ไฟล์นี้มีอินพุตและเกณฑ์ที่กำหนดไว้ล่วงหน้าสำหรับการทดสอบเอเจนต์วางแผน
👉 เลือกสถานการณ์ เช่น "บอสตัน" แล้วคลิกปุ่มเรียกใช้การประเมิน ในหน้าต่างป๊อปอัปที่ปรากฏขึ้น ให้ลดคะแนนการจับคู่เป็น 0.3 แล้วคลิกเริ่ม

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

👉 ทีนี้มาดูกันว่าเกิดอะไรขึ้นเมื่อใช้เกณฑ์ที่เข้มงวดขึ้น เลือกสถานการณ์ "nyc" แล้วคลิกเรียกใช้การประเมินอีกครั้ง คราวนี้ให้ปล่อยคะแนนการจับคู่ไว้ที่ค่าเริ่มต้น (คะแนนการจับคู่การตอบกลับ: 0.7) แล้วคลิกเริ่ม คุณจะเห็นว่าผลลัพธ์คือ "ไม่ผ่าน" ซึ่งเป็นเรื่องปกติเนื่องจากผลงานครีเอทีฟโฆษณาของเอเจนต์ไม่ตรงกับคำตอบ "ดีที่สุด" ที่กำหนดไว้ล่วงหน้าอย่างสมบูรณ์

👉 หากต้องการทราบสาเหตุที่การดำเนินการไม่สำเร็จ ให้คลิกไอคอนไม่สำเร็จในแถว "nyc" ตอนนี้ UI จะแสดงการเปรียบเทียบข้อมูลคู่กันระหว่างการตอบกลับจริงจากตัวแทนกับการตอบกลับที่คาดไว้จากกรณีทดสอบ มุมมองนี้มีความสำคัญอย่างยิ่งต่อการแก้ไขข้อบกพร่อง เนื่องจากช่วยให้คุณเห็นได้อย่างชัดเจนว่าเอาต์พุตของ Agent แตกต่างกันตรงจุดใด และปรับแต่งคำสั่งตามนั้น
เมื่อสำรวจ UI และการประเมินเสร็จแล้ว ให้กลับไปที่เทอร์มินัล Cloud Shell Editor แล้วกด Ctrl+C เพื่อหยุด UI สำหรับนักพัฒนา ADK
แม้ว่าเอาต์พุตข้อความแบบอิสระจะเป็นจุดเริ่มต้นที่ดี แต่สำหรับแอปพลิเคชันอย่าง InstaVibe ที่จะใช้คำแนะนำของเอเจนต์ได้อย่างง่ายดาย Structured Data (เช่น JSON) จะใช้งานได้จริงมากกว่า มาแก้ไข Agent ให้แสดงแผนในรูปแบบ JSON ที่สอดคล้องกัน
👉📝 ใน ~/instavibe-bootstrap/agents/planner/agent.py ให้ค้นหาบรรทัดที่ปัจจุบันระบุ RETURN PLAN in MARKDOWN FORMAT ภายในสตริงคำสั่งของเอเจนต์ แทนที่บรรทัดดังกล่าวด้วยโครงสร้าง JSON แบบละเอียดต่อไปนี้
Return your response *exclusively* as a single JSON object. This object should contain a top-level key, "fun_plans", which holds a plan objects. Each plan object in the list must strictly adhere to the following structure:
--json--
{
"plan_description": "A summary of the overall plan, consisting of **exactly three sentences**. Craft these sentences in a friendly, enthusiastic, and conversational tone, as if you're suggesting this awesome idea to a close friend. Make it sound exciting and personal, highlighting the positive aspects and appeal of the plan without explicitly mentioning budget or listing interest categories.",
"locations_and_activities": [
{
"name": "Name of the specific place or event",
"latitude": 0.000000, // Replace with actual latitude
"longitude": 0.000000, // Replace with actual longitude
"description": "A brief description of this place/event, why it's suitable for the date, and any specific details for the weekend (e.g., opening hours, event time)."
}
// Add more location/activity objects here if the plan involves multiple stops/parts
]
}
ตอนนี้คุณได้อัปเดตวิธีการของเอเจนต์เพื่อขอเอาต์พุต JSON โดยเฉพาะแล้ว มาตรวจสอบการเปลี่ยนแปลงกัน
👉💻 เปิดอีกครั้ง ADK Dev UI โดยใช้คำสั่งเดียวกับก่อนหน้า
. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd ~/instavibe-bootstrap/agents
adk web
รีเฟรชแท็บหากเปิดอยู่แล้ว หรือทำตามขั้นตอนเดียวกับก่อนหน้านี้เพื่อเปิด UI ของ ADK Dev ในเบราว์เซอร์ (ผ่านตัวอย่างเว็บของ Cloud Shell ในพอร์ต 8000) เมื่อโหลด UI แล้ว ให้ตรวจสอบว่าได้เลือกเอเจนต์วางแผนแล้ว
👉 คราวนี้เรามาลองส่งคำขออื่นกัน ในกล่องโต้ตอบแชท ให้ป้อนข้อมูลต่อไปนี้
Plan an event Boston this weekend with art and coffee
ตรวจสอบคำตอบของตัวแทนอย่างละเอียด ตอนนี้คุณควรเห็นการตอบกลับที่จัดรูปแบบเป็นออบเจ็กต์ JSON อย่างเคร่งครัด ซึ่งตรงกับโครงสร้างที่เรากำหนดไว้ในวิธีการ (มี fun_plans, plan_description, locations_and_activities ฯลฯ) แทนที่จะเป็นการตอบกลับด้วยข้อความแบบสนทนาอย่างเดียว ซึ่งเป็นการยืนยันว่าตอนนี้เอเจนต์สามารถสร้างเอาต์พุตที่มีโครงสร้างซึ่งเหมาะสำหรับการใช้งานแบบเป็นโปรแกรมโดยแอปพลิเคชัน InstaVibe ของเราได้แล้ว

หลังจากยืนยันเอาต์พุต JSON แล้ว ให้กลับไปที่เทอร์มินัล Cloud Shell แล้วกด Ctrl+C เพื่อหยุด ADK Dev UI
คอมโพเนนต์ ADK
แม้ว่า UI สำหรับนักพัฒนา ADK จะเหมาะสำหรับการทดสอบแบบอินเทอร์แอกทีฟ แต่เรามักจะต้องเรียกใช้เอเจนต์แบบเป็นโปรแกรม ซึ่งอาจเป็นส่วนหนึ่งของแอปพลิเคชันหรือบริการแบ็กเอนด์ที่ใหญ่ขึ้น หากต้องการทำความเข้าใจวิธีการทำงานนี้ ลองมาดูแนวคิดหลักบางอย่างของ ADK ที่เกี่ยวข้องกับการจัดการรันไทม์และบริบทกัน
การสนทนาหลายรอบที่มีความหมายกำหนดให้ Agent ต้องเข้าใจบริบท โดยการจดจำสิ่งที่พูดและทำไปแล้วเพื่อรักษาความต่อเนื่อง ADK มีวิธีที่เป็นระบบในการจัดการบริบทนี้ผ่านเซสชัน สถานะ และหน่วยความจำ ดังนี้
- เซสชัน: เมื่อผู้ใช้เริ่มโต้ตอบกับตัวแทน ระบบจะสร้างเซสชัน ให้คิดว่านี่คือคอนเทนเนอร์สำหรับชุดข้อความแชทเดียวที่เฉพาะเจาะจง โดยมีรหัสที่ไม่ซ้ำกัน ประวัติการโต้ตอบ (เหตุการณ์) ข้อมูลการทำงานปัจจุบัน (สถานะ) และข้อมูลเมตา เช่น เวลาอัปเดตล่าสุด
- สถานะ: นี่คือหน่วยความจำในการทำงานระยะสั้นของตัวแทนภายในเซสชันเดียว เป็นพจนานุกรมที่เปลี่ยนแปลงได้ซึ่งเอเจนต์สามารถจัดเก็บข้อมูลชั่วคราวที่จำเป็นต่อการทำงานปัจจุบันให้เสร็จสมบูรณ์ (เช่น ค่ากำหนดของผู้ใช้ที่รวบรวมมาจนถึงตอนนี้ ผลลัพธ์ระดับกลางจากการเรียกใช้เครื่องมือ)
- หน่วยความจำ: แสดงถึงศักยภาพของเอเจนต์ในการเรียกคืนข้อมูลระยะยาวในเซสชันต่างๆ หรือการเข้าถึงฐานความรู้ภายนอก ในขณะที่ Session และ State จัดการการสนทนาในทันที Memory (มักจะจัดการโดย MemoryService) จะช่วยให้ Agent ดึงข้อมูลจากการโต้ตอบที่ผ่านมาหรือแหล่งข้อมูลที่มีโครงสร้างได้ ซึ่งจะทำให้ Agent มีบริบทความรู้ที่กว้างขึ้น (หมายเหตุ: ไคลเอ็นต์อย่างง่ายของเราใช้บริการในหน่วยความจำเพื่อให้ง่ายต่อการใช้งาน ซึ่งหมายความว่าหน่วยความจำ/สถานะจะคงอยู่ขณะที่สคริปต์ทำงานเท่านั้น)
- เหตุการณ์: ระบบจะบันทึกการโต้ตอบทุกครั้งภายในเซสชัน (ข้อความของผู้ใช้ การตอบกลับของตัวแทน คำขอใช้เครื่องมือ ผลลัพธ์ของเครื่องมือ การเปลี่ยนสถานะ ข้อผิดพลาด) เป็นเหตุการณ์ที่แก้ไขไม่ได้ ซึ่งจะสร้างบันทึกตามลำดับเวลา ซึ่งโดยพื้นฐานแล้วคือข้อความถอดเสียงและประวัติการดำเนินการของการสนทนา
แล้วระบบจะจัดการข้อมูลเหล่านี้อย่างไรเมื่อ Agent ทำงาน นั่นคือหน้าที่ของผู้ส่งต่อ
- Runner: Runner คือกลไกการดำเนินการหลักที่ ADK จัดเตรียมไว้ให้ คุณกำหนด Agent และเครื่องมือที่ Agent ใช้ ส่วน Runner จะประสานงานกระบวนการตอบสนองคำขอของผู้ใช้ โดยจะจัดการเซสชัน จัดการโฟลว์ของเหตุการณ์ อัปเดตสถานะ เรียกใช้โมเดลภาษาพื้นฐาน ประสานงานการเรียกใช้เครื่องมือ และอาจโต้ตอบกับ MemoryService คิดเสียว่าเป็นการนำทางเพื่อให้มั่นใจว่าส่วนต่างๆ ทำงานร่วมกันได้อย่างถูกต้อง
เราสามารถใช้ Runner เพื่อเรียกใช้เอเจนต์เป็นแอปพลิเคชัน Python แบบสแตนด์อโลน ซึ่งไม่ขึ้นอยู่กับ UI ของนักพัฒนาแอปเลย
มาสร้างสคริปต์ไคลเอ็นต์อย่างง่ายเพื่อเรียกใช้เอเจนต์วางแผนของเราแบบเป็นโปรแกรมกัน
👉📝 ในไฟล์ ~/instavibe-bootstrap/agents/planner/planner_client.py ให้เพิ่มโค้ด Python ต่อไปนี้ภายใต้การนำเข้าที่มีอยู่ ใน planner_client.py ให้เพิ่มข้อมูลต่อไปนี้ในส่วนการนำเข้า
async def async_main():
session_service = InMemorySessionService()
session = await session_service.create_session(
state={}, app_name='planner_app', user_id='user_dc'
)
query = "Plan Something for me in San Francisco this weekend on wine and fashion "
print(f"User Query: '{query}'")
content = types.Content(role='user', parts=[types.Part(text=query)])
root_agent = agent.root_agent
runner = Runner(
app_name='planner_app',
agent=root_agent,
session_service=session_service,
)
print("Running agent...")
events_async = runner.run_async(
session_id=session.id, user_id=session.user_id, new_message=content
)
async for event in events_async:
print(f"Event received: {event}")
if __name__ == '__main__':
try:
asyncio.run(async_main())
except Exception as e:
print(f"An error occurred: {e}")
โค้ดนี้จะตั้งค่าบริการในหน่วยความจำสำหรับการจัดการเซสชันและอาร์ติแฟกต์ (ทำให้ตัวอย่างนี้เรียบง่าย) สร้างเซสชัน กำหนดคำค้นหาของผู้ใช้ กำหนดค่า Runner ด้วยเอเจนต์ของเรา แล้วเรียกใช้เอเจนต์แบบไม่พร้อมกัน โดยพิมพ์เหตุการณ์แต่ละรายการที่สร้างขึ้นระหว่างการดำเนินการ
👉💻 ตอนนี้ ให้เรียกใช้สคริปต์ไคลเอ็นต์นี้จากเทอร์มินัล
. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd ~/instavibe-bootstrap/agents
python -m planner.planner_client
👀 สังเกตเอาต์พุต คุณจะเห็นโครงสร้างโดยละเอียดของออบเจ็กต์เหตุการณ์แต่ละรายการที่สร้างขึ้นในระหว่างขั้นตอนการดำเนินการของเอเจนต์ แทนที่จะเห็นเพียงแผน JSON สุดท้าย ซึ่งรวมถึงเหตุการณ์ข้อความแรกของผู้ใช้ เหตุการณ์ที่อาจเกี่ยวข้องกับการเรียกใช้เครื่องมือ (เช่น Google Search) และสุดท้ายคือเหตุการณ์การตอบกลับของโมเดลที่มีแผน JSON สตรีมเหตุการณ์แบบละเอียดนี้มีประโยชน์อย่างยิ่งในการแก้ไขข้อบกพร่องและทําความเข้าใจการประมวลผลแบบทีละขั้นตอนที่เกิดขึ้นภายใน ADK Runtime
Running agent...
Event received: content=Content(parts=[Part(video_metadata=None, thought=None, code_execution_result=None, executable_code=None, file_data=None, function_call=None, function_response=None, inline_data=None, text='```json\n{\n "fun_plans": [\n {\n "plan_description": "Embark on a stylish adventure through Hayes Valley,
...(turncated)
, offering a variety of fashion styles to browse and enjoy."\n }\n ]\n }\n ]\n}\n```')], role='model') grounding_metadata=GroundingMetadata(grounding_chunks=[GroundingChunk(retrieved_context=None, web=GroundingChunkWeb(domain='islands.com', title='islands.com', uri='http
...(turncated)
QyTpPV7jS6wUt-Ix7GuP2mC9J4eY_8Km6Vv44liF9cb2VSs='))], grounding_supports=[GroundingSupport(confide
...(turncated)
>\n', sdk_blob=None), web_search_queries=['..e']) partial=None turn_complete=None error_code=None error_message=None interrupted=None custom_metadata=None invocation_id='e-04d97b8b-9021-47a5-ab41-17b5cbb4bf03' author='location_search_agent' actions=EventActions(skip_summarization=None, state_delta={}, artifact_delta={}, transfer_to_agent=None, escalate=None, requested_auth_configs={}) long_running_tool_ids=None branch=None id='CInHdkKw' timestamp=1746978846.232674
หากสคริปต์ทำงานอย่างต่อเนื่องหรือค้าง คุณอาจต้องหยุดด้วยตนเองโดยกด Ctrl+C
7. Platform Interaction Agent - โต้ตอบกับเซิร์ฟเวอร์ MCP
แม้ว่า ADK จะช่วยจัดโครงสร้างเอเจนต์ แต่เอเจนต์มักจะต้องโต้ตอบกับระบบหรือ API ภายนอกเพื่อดำเนินการในโลกแห่งความเป็นจริง
Model Context Protocol (MCP)
Model Context Protocol (MCP) เป็นมาตรฐานแบบเปิดที่ออกแบบมาเพื่อกำหนดมาตรฐานวิธีที่แอปพลิเคชัน AI เช่น Agent เชื่อมต่อกับแหล่งข้อมูล เครื่องมือ และระบบภายนอก โดยมีเป้าหมายเพื่อแก้ปัญหาการผสานรวมที่กำหนดเองสำหรับทุกแอปพลิเคชัน AI และการผสมผสานแหล่งข้อมูลด้วยการจัดเตรียมอินเทอร์เฟซแบบสากล MCP ใช้สถาปัตยกรรมไคลเอ็นต์-เซิร์ฟเวอร์ที่ไคลเอ็นต์ MCP ซึ่งอยู่ในแอปพลิเคชัน AI (โฮสต์) จะจัดการการเชื่อมต่อกับเซิร์ฟเวอร์ MCP เซิร์ฟเวอร์เหล่านี้เป็นโปรแกรมภายนอกที่แสดงฟังก์ชันการทำงานที่เฉพาะเจาะจง เช่น การเข้าถึงข้อมูลผลิตภัณฑ์ในพื้นที่ การโต้ตอบกับบริการระยะไกลผ่าน API หรือการระบุพรอมต์ที่กำหนดไว้ล่วงหน้า ซึ่งช่วยให้โมเดล AI เข้าถึงข้อมูลปัจจุบันและทำงานต่างๆ ได้นอกเหนือจากการฝึกครั้งแรก โครงสร้างนี้ช่วยให้โมเดล AI ค้นพบและโต้ตอบกับความสามารถภายนอกในลักษณะที่เป็นมาตรฐาน ทำให้การผสานรวมง่ายขึ้นและปรับขนาดได้มากขึ้น
สร้างและติดตั้งใช้งานเซิร์ฟเวอร์ InstaVibe MCP

ในที่สุดเอเจนต์ของเราจะต้องโต้ตอบกับแพลตฟอร์ม InstaVibe เอง โดยเฉพาะอย่างยิ่งเพื่อสร้างโพสต์และลงทะเบียนกิจกรรมโดยใช้ API ที่มีอยู่ของแพลตฟอร์ม แอปพลิเคชัน InstaVibe แสดงฟังก์ชันการทำงานเหล่านี้ผ่านปลายทาง HTTP มาตรฐานอยู่แล้ว
Enpoint | URL | เมธอด HTTP | คำอธิบาย |
สร้างโพสต์ | api/posts | POST | ปลายทาง API เพื่อเพิ่มโพสต์ใหม่ คาดหวังเนื้อหา JSON: |
สร้างกิจกรรม | api/events | POST | ปลายทาง API เพื่อเพิ่มกิจกรรมใหม่และผู้เข้าร่วม (สคีมาแบบง่าย) |
หากต้องการให้ความสามารถเหล่านี้พร้อมใช้งานสำหรับตัวแทนผ่าน MCP เราต้องสร้างฟังก์ชัน Python อย่างง่ายก่อน ซึ่งทำหน้าที่เป็น Wrapper รอบการเรียก API เหล่านี้ ฟังก์ชันเหล่านี้จะจัดการตรรกะคำขอ HTTP
👉 ก่อนอื่น เรามาใช้ฟังก์ชัน Wrapper สำหรับสร้างโพสต์กัน เปิดไฟล์ ~/instavibe-bootstrap/tools/instavibe/instavibe.py แล้วแทนที่ความคิดเห็น #REPLACE ME CREATE POST ด้วยโค้ด Python ต่อไปนี้
def create_post(author_name: str, text: str, sentiment: str, base_url: str = BASE_URL):
"""
Sends a POST request to the /posts endpoint to create a new post.
Args:
author_name (str): The name of the post's author.
text (str): The content of the post.
sentiment (str): The sentiment associated with the post (e.g., 'positive', 'negative', 'neutral').
base_url (str, optional): The base URL of the API. Defaults to BASE_URL.
Returns:
dict: The JSON response from the API if the request is successful.
Returns None if an error occurs.
Raises:
requests.exceptions.RequestException: If there's an issue with the network request (e.g., connection error, timeout).
"""
url = f"{base_url}/posts"
headers = {"Content-Type": "application/json"}
payload = {
"author_name": author_name,
"text": text,
"sentiment": sentiment
}
try:
response = requests.post(url, headers=headers, json=payload)
response.raise_for_status() # Raise an exception for bad status codes (4xx or 5xx)
print(f"Successfully created post. Status Code: {response.status_code}")
return response.json()
except requests.exceptions.RequestException as e:
print(f"Error creating post: {e}")
# Optionally re-raise the exception if the caller needs to handle it
# raise e
return None
except json.JSONDecodeError:
print(f"Error decoding JSON response from {url}. Response text: {response.text}")
return None
👉📝 จากนั้นเราจะสร้างฟังก์ชัน Wrapper สำหรับ API การสร้างเหตุการณ์ ใน~/instavibe-bootstrap/tools/instavibe/instavibe.pyไฟล์เดียวกัน ให้แทนที่ความคิดเห็น #REPLACE ME CREATE EVENTS ด้วยโค้ดนี้
def create_event(event_name: str, description: str, event_date: str, locations: list, attendee_names: list[str], base_url: str = BASE_URL):
"""
Sends a POST request to the /events endpoint to create a new event registration.
Args:
event_name (str): The name of the event.
description (str): The detailed description of the event.
event_date (str): The date and time of the event (ISO 8601 format recommended, e.g., "2025-06-10T09:00:00Z").
locations (list): A list of location dictionaries. Each dictionary should contain:
'name' (str), 'description' (str, optional),
'latitude' (float), 'longitude' (float),
'address' (str, optional).
attendee_names (list[str]): A list of names of the people attending the event.
base_url (str, optional): The base URL of the API. Defaults to BASE_URL.
Returns:
dict: The JSON response from the API if the request is successful.
Returns None if an error occurs.
Raises:
requests.exceptions.RequestException: If there's an issue with the network request (e.g., connection error, timeout).
"""
url = f"{base_url}/events"
headers = {"Content-Type": "application/json"}
payload = {
"event_name": event_name,
"description": description,
"event_date": event_date,
"locations": locations,
"attendee_names": attendee_names,
}
try:
response = requests.post(url, headers=headers, json=payload)
response.raise_for_status() # Raise an exception for bad status codes (4xx or 5xx)
print(f"Successfully created event registration. Status Code: {response.status_code}")
return response.json()
except requests.exceptions.RequestException as e:
print(f"Error creating event registration: {e}")
# Optionally re-raise the exception if the caller needs to handle it
# raise e
return None
except json.JSONDecodeError:
print(f"Error decoding JSON response from {url}. Response text: {response.text}")
return None
ดังที่เห็น ฟังก์ชันเหล่านี้เป็น Wrapper ที่ตรงไปตรงมารอบๆ API ของ InstaVibe ที่มีอยู่ รูปแบบนี้มีประโยชน์ในกรณีที่คุณมี API สำหรับบริการอยู่แล้ว คุณสามารถแสดงฟังก์ชันการทำงานเป็นเครื่องมือสำหรับตัวแทนได้อย่างง่ายดายโดยการสร้าง Wrapper ดังกล่าว
การติดตั้งใช้งานเซิร์ฟเวอร์ MCP
ตอนนี้เรามีฟังก์ชัน Python ที่ดำเนินการ (เรียกใช้ InstaVibe API) แล้ว เราจึงต้องสร้างคอมโพเนนต์เซิร์ฟเวอร์ MCP เซิร์ฟเวอร์นี้จะแสดงฟังก์ชันเหล่านี้เป็น "เครื่องมือ" ตามมาตรฐาน MCP ซึ่งจะช่วยให้ไคลเอ็นต์ MCP (เช่น ตัวแทนของเรา) ค้นพบและเรียกใช้ฟังก์ชันเหล่านี้ได้
โดยทั่วไปแล้วเซิร์ฟเวอร์ MCP จะใช้ฟังก์ชันการทำงานหลัก 2 อย่าง ได้แก่
- list_tools: มีหน้าที่อนุญาตให้ไคลเอ็นต์ค้นพบเครื่องมือที่พร้อมใช้งานบนเซิร์ฟเวอร์ โดยให้ข้อมูลเมตา เช่น ชื่อ คำอธิบาย และพารามิเตอร์ที่จำเป็น ซึ่งมักกำหนดโดยใช้ JSON Schema
- call_tool: จัดการการดำเนินการของเครื่องมือที่ไคลเอ็นต์ขอโดยเฉพาะ รับชื่อและอาร์กิวเมนต์ของเครื่องมือ และดำเนินการที่เกี่ยวข้อง เช่น ในกรณีของเราคือการโต้ตอบกับ API
เซิร์ฟเวอร์ MCP ใช้เพื่อให้สิทธิ์เข้าถึงข้อมูลและการดำเนินการในโลกจริงแก่โมเดล AI ซึ่งช่วยให้สามารถทำงานต่างๆ เช่น การส่งอีเมล การสร้างงานในระบบการจัดการโปรเจ็กต์ การค้นหาฐานข้อมูล หรือการโต้ตอบกับซอฟต์แวร์และบริการบนเว็บต่างๆ แม้ว่าการติดตั้งใช้งานครั้งแรกมักจะมุ่งเน้นที่เซิร์ฟเวอร์ในพื้นที่ที่สื่อสารผ่านอินพุต/เอาต์พุต (stdio) มาตรฐานเพื่อความเรียบง่าย โดยเฉพาะอย่างยิ่งในสภาพแวดล้อมการพัฒนาหรือ "สตูดิโอ" แต่การเปลี่ยนไปใช้เซิร์ฟเวอร์ระยะไกลที่ใช้โปรโตคอลอย่าง HTTP กับ Server-Sent Events (SSE) นั้นสมเหตุสมผลกว่าสำหรับการนำไปใช้ในวงกว้างและกรณีการใช้งานระดับองค์กร
แม้ว่าสถาปัตยกรรมระยะไกลจะมีเลเยอร์การสื่อสารผ่านเครือข่ายเพิ่มเติม แต่ก็มีข้อดีที่สำคัญ ได้แก่ ช่วยให้ไคลเอ็นต์ AI หลายรายแชร์สิทธิ์เข้าถึงเซิร์ฟเวอร์เดียวได้ รวมทั้งรวมศูนย์การจัดการและการอัปเดตเครื่องมือ เพิ่มความปลอดภัยด้วยการเก็บข้อมูลที่ละเอียดอ่อนและคีย์ API ไว้ที่ฝั่งเซิร์ฟเวอร์แทนที่จะกระจายไปยังเครื่องไคลเอ็นต์จำนวนมากที่อาจเกิดขึ้น และแยกโมเดล AI ออกจากการผสานรวมระบบภายนอกที่เฉพาะเจาะจง ซึ่งทำให้ทั้งระบบนิเวศมีความสามารถในการปรับขนาด ปลอดภัย และดูแลรักษาได้มากกว่าการกำหนดให้แต่ละอินสแตนซ์ AI จัดการการผสานรวมโดยตรงของตนเอง

เราจะใช้เซิร์ฟเวอร์ MCP โดยใช้ HTTP และเหตุการณ์ที่เซิร์ฟเวอร์ส่ง (SSE) เพื่อการสื่อสาร ซึ่งเหมาะสําหรับการเรียกใช้เครื่องมือที่อาจใช้เวลานานและสถานการณ์ขององค์กร
👉📝 ก่อนอื่น ให้ใช้ปลายทาง list_tools เปิดไฟล์ ~/instavibe-bootstrap/tools/instavibe/mcp_server.py แล้วแทนที่ความคิดเห็น #REPLACE ME - LIST TOOLS ด้วยโค้ดต่อไปนี้ :
@app.list_tools()
async def list_tools() -> list[mcp_types.Tool]:
"""MCP handler to list available tools."""
# Convert the ADK tool's definition to MCP format
mcp_tool_schema_event = adk_to_mcp_tool_type(event_tool)
mcp_tool_schema_post = adk_to_mcp_tool_type(post_tool)
print(f"MCP Server: Received list_tools request. \n MCP Server: Advertising tool: {mcp_tool_schema_event.name} and {mcp_tool_schema_post}")
return [mcp_tool_schema_event,mcp_tool_schema_post]
ฟังก์ชันนี้จะกำหนดเครื่องมือ (create_event, create_post) และแจ้งให้ไคลเอ็นต์ที่เชื่อมต่อทราบ
👉📝 จากนั้น ให้ติดตั้งใช้งานปลายทาง call_tool ซึ่งจัดการคำขอการดำเนินการจริงจากไคลเอ็นต์ ในไฟล์ ~/instavibe-bootstrap/tools/instavibe/mcp_server.py เดียวกัน ให้แทนที่#REPLACE ME - CALL TOOLSความคิดเห็นด้วยโค้ดนี้
@app.call_tool()
async def call_tool(
name: str, arguments: dict
) -> list[mcp_types.TextContent | mcp_types.ImageContent | mcp_types.EmbeddedResource]:
"""MCP handler to execute a tool call."""
print(f"MCP Server: Received call_tool request for '{name}' with args: {arguments}")
# Look up the tool by name in our dictionary
tool_to_call = available_tools.get(name)
if tool_to_call:
try:
adk_response = await tool_to_call.run_async(
args=arguments,
tool_context=None, # No ADK context available here
)
print(f"MCP Server: ADK tool '{name}' executed successfully.")
response_text = json.dumps(adk_response, indent=2)
return [mcp_types.TextContent(type="text", text=response_text)]
except Exception as e:
print(f"MCP Server: Error executing ADK tool '{name}': {e}")
# Creating a proper MCP error response might be more robust
error_text = json.dumps({"error": f"Failed to execute tool '{name}': {str(e)}"})
return [mcp_types.TextContent(type="text", text=error_text)]
else:
# Handle calls to unknown tools
print(f"MCP Server: Tool '{name}' not found.")
error_text = json.dumps({"error": f"Tool '{name}' not implemented."})
return [mcp_types.TextContent(type="text", text=error_text)]
ฟังก์ชันนี้จะรับชื่อเครื่องมือและอาร์กิวเมนต์ ค้นหาฟังก์ชัน Wrapper ของ Python ที่เกี่ยวข้องซึ่งเรากำหนดไว้ก่อนหน้านี้ ดำเนินการ และแสดงผลลัพธ์
👉💻 เมื่อกำหนดตรรกะของเซิร์ฟเวอร์ MCP แล้ว ตอนนี้เราต้องจัดแพ็กเกจเป็นคอนเทนเนอร์ ในเทอร์มินัล ให้เรียกใช้สคริปต์ต่อไปนี้เพื่อสร้างอิมเมจ Docker โดยใช้ Cloud Build
. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap/tools/instavibe
export IMAGE_TAG="latest"
export MCP_IMAGE_NAME="mcp-tool-server"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${MCP_IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="mcp-tool-server"
export INSTAVIBE_BASE_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep instavibe)/api
gcloud builds submit . \
--tag=${IMAGE_PATH} \
--project=${PROJECT_ID}
👉💻 และทําให้อิมเมจใช้งานได้เป็นบริการใน Google Cloud Run
. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap/tools/instavibe
export IMAGE_TAG="latest"
export MCP_IMAGE_NAME="mcp-tool-server"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${MCP_IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="mcp-tool-server"
export INSTAVIBE_BASE_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep instavibe)/api
gcloud run deploy ${SERVICE_NAME} \
--image=${IMAGE_PATH} \
--platform=managed \
--region=${REGION} \
--allow-unauthenticated \
--set-env-vars="INSTAVIBE_BASE_URL=${INSTAVIBE_BASE_URL}" \
--set-env-vars="APP_HOST=0.0.0.0" \
--set-env-vars="APP_PORT=8080" \
--set-env-vars="GOOGLE_GENAI_USE_VERTEXAI=TRUE" \
--set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
--set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
--project=${PROJECT_ID} \
--min-instances=1
👉💻 หลังจากติดตั้งใช้งานสำเร็จแล้ว เซิร์ฟเวอร์ MCP จะทํางานและเข้าถึงได้ผ่าน URL สาธารณะ เราต้องบันทึก URL นี้เพื่อให้ตัวแทนของเรา (ทำหน้าที่เป็นไคลเอ็นต์ MCP) ทราบว่าจะเชื่อมต่อที่ใด
export MCP_SERVER_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep mcp-tool-server)/sse
นอกจากนี้ คุณควรจะเห็นบริการ mcp-tool-server แสดงเป็น "กำลังทำงาน" ในส่วน Cloud Run ของคอนโซล Google Cloud ด้วย

เมื่อติดตั้งใช้งานเซิร์ฟเวอร์ MCP และบันทึก URL แล้ว ตอนนี้เราก็สามารถติดตั้งใช้งาน Agent ที่จะทำหน้าที่เป็นไคลเอ็นต์ MCP และใช้เครื่องมือที่เซิร์ฟเวอร์นี้เปิดเผย
8. ตัวแทนการโต้ตอบกับแพลตฟอร์ม (ใช้ MCP)
ไคลเอ็นต์ MCP ไคลเอ็นต์ MCP เป็นคอมโพเนนต์ที่อยู่ในแอปพลิเคชันหรือเอเจนต์ AI ซึ่งทำหน้าที่เป็นอินเทอร์เฟซระหว่างโมเดล AI กับเซิร์ฟเวอร์ MCP อย่างน้อย 1 เครื่อง ในการติดตั้งใช้งานของเรา ไคลเอ็นต์นี้จะผสานรวมโดยตรงภายในเอเจนต์ของเรา ฟังก์ชันหลักของไคลเอ็นต์นี้คือการสื่อสารกับเซิร์ฟเวอร์ MCP เพื่อค้นหาเครื่องมือที่พร้อมใช้งานผ่านฟังก์ชัน list_tools และขอให้ดำเนินการเครื่องมือที่เฉพาะเจาะจงโดยใช้ฟังก์ชัน call_tool โดยส่งอาร์กิวเมนต์ที่จำเป็นซึ่งโมเดล AI หรือ Agent ที่ประสานงานการเรียกให้

ตอนนี้เราจะสร้างเอเจนต์ที่ทำหน้าที่เป็นไคลเอ็นต์ MCP Agent นี้ซึ่งทำงานภายในเฟรมเวิร์ก ADK จะมีหน้าที่สื่อสารกับ mcp-tool-server ที่เราเพิ่งติดตั้งใช้งาน
👉 ก่อนอื่น เราต้องแก้ไขคำจำกัดความของเอเจนต์เพื่อดึงข้อมูลเครื่องมือจากเซิร์ฟเวอร์ MCP ที่ทำงานอยู่แบบไดนามิก ใน agents/platform_mcp_client/agent.py ให้แทนที่ #REPLACE ME - FETCH TOOLS ด้วยข้อความต่อไปนี้
"""Gets tools from the File System MCP Server."""
tools = MCPToolset(
connection_params=SseServerParams(url=MCP_SERVER_URL, headers={})
)
โค้ดนี้ใช้วิธีการ MCPToolset.from_server เพื่อเชื่อมต่อกับ MCP_SERVER_URL (ซึ่งเราตั้งค่าเป็นตัวแปรสภาพแวดล้อมไว้ก่อนหน้านี้) และดึงรายการเครื่องมือที่พร้อมใช้งาน
จากนั้นเราต้องบอกคำจำกัดความของเอเจนต์ ADK ให้ใช้เครื่องมือที่ดึงข้อมูลแบบไดนามิกเหล่านี้จริงๆ
👉 ใน agents/platform_mcp_client/agent.py ให้แทนที่ #REPLACE ME - SET TOOLs ด้วยข้อมูลต่อไปนี้
tools=[tools],
👉💻 ตอนนี้มาทดสอบเอเจนต์นี้ในเครื่องโดยใช้ ADK Dev UI เพื่อดูว่าเอเจนต์เชื่อมต่อกับเซิร์ฟเวอร์ MCP ได้อย่างถูกต้องหรือไม่ และใช้เครื่องมือเพื่อโต้ตอบกับแอปพลิเคชัน InstaVibe ที่กำลังทำงานอยู่ได้หรือไม่
. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
export MCP_SERVER_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep mcp-tool-server)/sse
cd ~/instavibe-bootstrap/agents
sed -i "s|^\(O\?GOOGLE_CLOUD_PROJECT\)=.*|GOOGLE_CLOUD_PROJECT=${PROJECT_ID}|" ~/instavibe-bootstrap/agents/platform_mcp_client/.env
sed -i "s|^\(O\?MCP_SERVER_URL\)=.*|MCP_SERVER_URL=${MCP_SERVER_URL}|" ~/instavibe-bootstrap/agents/platform_mcp_client/.env
adk web
เปิด UI สำหรับนักพัฒนาซอฟต์แวร์ ADK ในเบราว์เซอร์อีกครั้ง (โดยใช้ตัวอย่างเว็บของ Cloud Shell บนพอร์ต 8000) คราวนี้ ให้เลือกplatform_mcp_clientเอเจนต์ในเมนูแบบเลื่อนลงที่ด้านขวาบน
มาทดสอบเครื่องมือ create_post กัน ในกล่องโต้ตอบแชท ให้ป้อนคำขอต่อไปนี้
Create a post saying "Y'all I just got the cutest lil void baby 😭✨ Naming him Abyss bc he's deep, mysterious, and lowkey chaotic 🔥🖤 #VoidCat #NewRoomie" I'm Julia

เอเจนต์ควรประมวลผลสิ่งนี้ ระบุความจำเป็นในการใช้เครื่องมือ create_post สื่อสารกับเซิร์ฟเวอร์ MCP ซึ่งจะเรียกใช้ InstaVibe API
👉 ขั้นตอนการยืนยัน: หลังจากที่ตัวแทนยืนยันการดำเนินการแล้ว ให้เปิดแท็บที่แอปพลิเคชัน InstaVibe ทำงานอยู่ (หรือรีเฟรช) คุณควรเห็นโพสต์ใหม่จาก "Julia" ปรากฏในฟีดหลัก

👉💻 เรียกใช้สคริปต์นี้ในเทอร์มินัลแยกต่างหากเพื่อรับลิงก์ Instavibe หากจำเป็น
gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep instavibe
👉📝 ตอนนี้มาทดสอบเครื่องมือ create_event กัน ป้อนคำขอแบบหลายบรรทัดต่อไปนี้ลงในกล่องโต้ตอบแชท
Hey, can you set up an event for Hannah and George and me, and I'm Julia? Let's call it 'Mexico City Culinary & Art Day'.
here are more info
{"event_name": "Mexico City Culinary & Art Day",
"description": "A vibrant day in Mexico City for Hannah and George, starting with lunch at one of the city's best taco spots in the hip Condesa neighborhood, followed by an inspiring afternoon exploring the Museo Soumaya's stunning art collection.",
"event_date": "2025-10-17T12:00:00-06:00",
"locations": [
{
"name": "El Tizoncito",
"description": "Considered one of the original creators of tacos al pastor, El Tizoncito offers a legendary taco experience in the heart of Condesa. Their flavorful meats, house salsas, and casual vibe make it a must-visit for foodies.",
"latitude": 19.412179,
"longitude": -99.171308,
"address": "Av. Tamaulipas 122, Hipódromo, Cuauhtémoc, 06100 Ciudad de México, CDMX, Mexico"
},
{
"name": "Museo Soumaya",
"description": "An architectural icon in Mexico City, Museo Soumaya houses over 66,000 works of art, including pieces by Rodin, Dalí, and Rivera. The striking silver structure is a cultural landmark and a visual feast inside and out.",
"latitude": 19.440056,
"longitude": -99.204281,
"address": "Plaza Carso, Blvd. Miguel de Cervantes Saavedra 303, Granada, Miguel Hidalgo, 11529 Ciudad de México, CDMX, Mexico"
}
],
"attendee_names": ["Hannah", "George", Julia],
}
อีกครั้งที่เอเจนต์ควรใช้เครื่องมือที่เหมาะสมผ่านเซิร์ฟเวอร์ MCP ในแท็บเหตุการณ์ คุณสามารถคลิกเหตุการณ์แต่ละรายการเพื่อดูการติดตามการดำเนินการแบบทีละขั้นตอนโดยละเอียด

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

ซึ่งแสดงให้เห็นว่า MCP ช่วยให้เอเจนต์ของเราใช้ประโยชน์จากเครื่องมือภายนอก (ในกรณีนี้คือ API ของ InstaVibe) ในลักษณะที่เป็นมาตรฐานได้อย่างไร
เมื่อยืนยันทั้ง 2 การดำเนินการแล้ว ให้กลับไปที่เทอร์มินัล Cloud Shell แล้วกด Ctrl+C เพื่อหยุด UI สำหรับนักพัฒนาซอฟต์แวร์ของ ADK
9. Agent เวิร์กโฟลว์และ Multi-Agent ใน ADK
ปัจจุบันเอเจนต์ของเราสามารถวางแผนการออกนอกบ้านและโต้ตอบกับแพลตฟอร์มได้ อย่างไรก็ตาม การวางแผนที่ปรับตามโปรไฟล์ของผู้ใช้จริงๆ ต้องอาศัยความเข้าใจวงสังคมของผู้ใช้ สำหรับผู้ใช้ที่ยุ่งและอาจไม่ได้ติดตามกิจกรรมของเพื่อนอย่างใกล้ชิด การรวบรวมบริบทนี้ด้วยตนเองจึงเป็นเรื่องยาก เพื่อแก้ปัญหานี้ เราจะสร้างเอเจนต์การสร้างโปรไฟล์โซเชียลที่ใช้ประโยชน์จากฐานข้อมูลกราฟ Spanner เพื่อวิเคราะห์กิจกรรมและความสนใจของเพื่อน ซึ่งจะช่วยให้เราให้คำแนะนำที่ปรับแต่งได้มากขึ้น

ก่อนอื่น เราต้องมีเครื่องมือเพื่อให้เอเจนต์นี้เข้าถึงข้อมูลกราฟได้
👉📝 เพิ่มฟังก์ชัน Python ต่อไปนี้ที่ส่วนท้ายของไฟล์ ~/instavibe-bootstrap/agents/social/instavibe.py
def get_person_attended_events(person_id: str)-> list[dict]:
"""
Fetches events attended by a specific person using Graph Query.
Args:
person_id (str): The ID of the person whose posts to fetch.
Returns: list[dict] or None.
"""
if not db_instance: return None
graph_sql = """
Graph SocialGraph
MATCH (p:Person)-[att:Attended]->(e:Event)
WHERE p.person_id = @person_id
RETURN e.event_id, e.name, e.event_date, att.attendance_time
ORDER BY e.event_date DESC
"""
params = {"person_id": person_id}
param_types_map = {"person_id": param_types.STRING}
fields = ["event_id", "name", "event_date", "attendance_time"]
results = run_graph_query( graph_sql, params=params, param_types=param_types_map, expected_fields=fields)
if results is None: return None
for event in results:
if isinstance(event.get('event_date'), datetime):
event['event_date'] = event['event_date'].isoformat()
if isinstance(event.get('attendance_time'), datetime):
event['attendance_time'] = event['attendance_time'].isoformat()
return results
def get_person_id_by_name( name: str) -> str:
"""
Fetches the person_id for a given name using SQL.
Args:
name (str): The name of the person to search for.
Returns:
str or None: The person_id if found, otherwise None.
Returns the ID of the *first* match if names are duplicated.
"""
if not db_instance: return None
sql = """
SELECT person_id
FROM Person
WHERE name = @name
LIMIT 1 -- Return only the first match in case of duplicate names
"""
params = {"name": name}
param_types_map = {"name": param_types.STRING}
fields = ["person_id"]
# Use the standard SQL query helper
results = run_sql_query( sql, params=params, param_types=param_types_map, expected_fields=fields)
if results: # Check if the list is not empty
return results[0].get('person_id') # Return the ID from the first dictionary
else:
return None # Name not found
def get_person_posts( person_id: str)-> list[dict]:
"""
Fetches posts written by a specific person using Graph Query.
Args:
person_id (str): The ID of the person whose posts to fetch.
Returns:
list[dict] or None: List of post dictionaries with ISO date strings,
or None if an error occurs.
"""
if not db_instance: return None
# Graph Query: Find the specific Person node, follow 'Wrote' edge to Post nodes
graph_sql = """
Graph SocialGraph
MATCH (author:Person)-[w:Wrote]->(post:Post)
WHERE author.person_id = @person_id
RETURN post.post_id, post.author_id, post.text, post.sentiment, post.post_timestamp, author.name AS author_name
ORDER BY post.post_timestamp DESC
"""
# Parameters now include person_id and limit
params = {
"person_id": person_id
}
param_types_map = {
"person_id": param_types.STRING
}
# Fields returned remain the same
fields = ["post_id", "author_id", "text", "sentiment", "post_timestamp", "author_name"]
results = run_graph_query(graph_sql, params=params, param_types=param_types_map, expected_fields=fields)
if results is None:
return None
# Convert datetime objects to ISO format strings
for post in results:
if isinstance(post.get('post_timestamp'), datetime):
post['post_timestamp'] = post['post_timestamp'].isoformat()
return results
def get_person_friends( person_id: str)-> list[dict]:
"""
Fetches friends for a specific person using Graph Query.
Args:
person_id (str): The ID of the person whose posts to fetch.
Returns: list[dict] or None.
"""
if not db_instance: return None
graph_sql = """
Graph SocialGraph
MATCH (p:Person {person_id: @person_id})-[f:Friendship]-(friend:Person)
RETURN DISTINCT friend.person_id, friend.name
ORDER BY friend.name
"""
params = {"person_id": person_id}
param_types_map = {"person_id": param_types.STRING}
fields = ["person_id", "name"]
results = run_graph_query( graph_sql, params=params, param_types=param_types_map, expected_fields=fields)
return results
ตอนนี้เรามาพูดถึงวิธีจัดโครงสร้าง Agent กัน การวิเคราะห์โปรไฟล์ของเพื่อนหลายคนแล้วสรุปสิ่งที่ค้นพบมีหลายขั้นตอน นี่เป็นสถานการณ์ที่เหมาะอย่างยิ่งสำหรับการใช้ความสามารถแบบหลาย Agent ของ ADK โดยเฉพาะ Workflow Agent
ใน ADK ของ Google นั้น Workflow Agent จะไม่ทำงานด้วยตัวเอง แต่จะประสานงานกับ Agent อื่นๆ ที่เรียกว่าSub-Agent ซึ่งช่วยให้สามารถออกแบบแบบแยกส่วนได้ โดยแบ่งปัญหาที่ซับซ้อนออกเป็นคอมโพเนนต์เฉพาะทาง ADK มีประเภทเวิร์กโฟลว์ในตัว เช่น
- ตามลำดับ (ทีละขั้นตอน)
- Parallel (การดำเนินการพร้อมกัน)
- และ Loop (การดำเนินการซ้ำ)

สำหรับงานการสร้างโปรไฟล์โซเชียล การออกแบบของเราใช้ Loop Agent เพื่อสร้างเวิร์กโฟลว์แบบวนซ้ำ โดยมีจุดประสงค์เพื่อประมวลผลบุคคลทีละคน กล่าวคือ profile_agent รวบรวมข้อมูล summary_agent อัปเดตการวิเคราะห์ และ check_agent พิจารณาว่าเราควรวนซ้ำอีกหรือไม่
มากำหนดเอเจนต์ย่อยที่จำเป็นสำหรับเวิร์กโฟลว์นี้กัน
👉📝 ใน ~/instavibe-bootstrap/agents/social/agent.py ให้แทนที่ #REPLACE FOR profile_agent ด้วยข้อความต่อไปนี้
profile_agent = LlmAgent(
name="profile_agent",
model="gemini-2.5-flash",
description=(
"Agent to answer questions about the this person social profile. Provide the person's profile using their name, make sure to fetch the id before getting other data."
),
instruction=(
"You are a helpful agent to answer questions about the this person social profile. You'll be given a list of names, provide the person's profile using their name, make sure to fetch the id before getting other data. Get one person at a time, start with the first one on the list, and skip if already provided. return this person's result"
),
tools=[get_person_posts,get_person_friends,get_person_id_by_name,get_person_attended_events],
)
จากนั้นเอเจนต์จะนำข้อมูลโปรไฟล์ที่รวบรวมไว้ (สะสมจากการวนซ้ำ) มาสร้างข้อมูลสรุปสุดท้าย โดยจะระบุจุดร่วมหากมีการวิเคราะห์ผู้ใช้หลายคน
👉📝 ใน ~/instavibe-bootstrap/agents/social/agent.py เดียวกัน ให้แทนที่ #REPLACE FOR summary_agent ด้วยค่าต่อไปนี้
summary_agent = LlmAgent(
name="summary_agent",
model="gemini-2.5-flash",
description=(
"Generate a comprehensive social summary as a single, cohesive paragraph. This summary should cover the activities, posts, friend networks, and event participation of one or more individuals. If multiple profiles are analyzed, the paragraph must also identify and integrate any common ground found between them."
),
instruction=(
"""
Your primary task is to synthesize social profile information into a single, comprehensive paragraph.
**Input Scope & Default Behavior:**
* If specific individuals are named by the user, focus your analysis on them.
* **If no individuals are specified, or if the request is general, assume the user wants an analysis of *all relevant profiles available in the current dataset/context*.**
**For each profile (whether specified or determined by default), you must analyze:**
1. **Post Analysis:**
* Systematically review their posts (e.g., content, topics, frequency, engagement).
* Identify recurring themes, primary interests, and expressed sentiments.
2. **Friendship Relationship Analysis:**
* Examine their connections/friends list.
* Identify key relationships, mutual friends (especially if comparing multiple profiles), and the general structure of their social network.
3. **Event Participation Analysis:**
* Investigate their past (and if available, upcoming) event participation.
* Note the types of events, frequency of attendance, and any notable roles (e.g., organizer, speaker).
**Output Generation (Single Paragraph):**
* **Your entire output must be a single, cohesive summary paragraph.**
* **If analyzing a single profile:** This paragraph will detail their activities, interests, and social connections based on the post, friend, and event analysis.
* **If analyzing multiple profiles:** This paragraph will synthesize the key findings regarding posts, friends, and events for each individual. Crucially, it must then seamlessly integrate or conclude with an identification and description of the common ground found between them (e.g., shared interests from posts, overlapping event attendance, mutual friends). The aim is a unified narrative within this single paragraph.
**Key Considerations:**
* Base your summary strictly on the available data.
* If data for a specific category (posts, friends, events) is missing or sparse for a profile, you may briefly acknowledge this within the narrative if relevant.
"""
),
output_key="summary"
)
เราต้องมีวิธีพิจารณาว่าควรหยุดลูปเมื่อใด (เช่น เมื่อสรุปโปรไฟล์ที่ขอทั้งหมดแล้ว)
👉📝 ใน ~/instavibe-bootstrap/agents/social/agent.py เดียวกัน ให้แทนที่ #REPLACE FOR check_agent ด้วยค่าต่อไปนี้
check_agent = LlmAgent(
name="check_agent",
model="gemini-2.5-flash",
description=(
"Check if everyone's social profile are summarized and has been generated. Output 'completed' or 'pending'."
),
output_key="summary_status"
)
เราเพิ่มการตรวจสอบแบบเป็นโปรแกรมอย่างง่าย (CheckCondition) ที่ดู summary_status ที่จัดเก็บไว้ใน State อย่างชัดเจน ซึ่ง check_agent จะส่งคืน และบอก Loop Agent ว่าจะดำเนินการต่อ (escalate=False) หรือหยุด (escalate=True)
👉📝 ใน ~/instavibe-bootstrap/agents/social/agent.py เดียวกัน ให้แทนที่ #REPLACE FOR CheckCondition ที่อยู่ด้านบนของไฟล์ด้วยข้อความต่อไปนี้
class CheckCondition(BaseAgent):
async def _run_async_impl(self, ctx: InvocationContext) -> AsyncGenerator[Event, None]:
#log.info(f"Checking status: {ctx.session.state.get("summary_status", "fail")}")
log.info(f"Summary: {ctx.session.state.get("summary")}")
status = ctx.session.state.get("summary_status", "fail").strip()
is_done = (status == "completed")
yield Event(author=self.name, actions=EventActions(escalate=is_done))
สถานะและการเรียกกลับสำหรับผลลัพธ์ของ Loop
ใน ADK ของ Google State เป็นแนวคิดที่สำคัญซึ่งแสดงถึงหน่วยความจำหรือข้อมูลการทำงานของเอเจนต์ในระหว่างการดำเนินการ ซึ่งเป็นบริบทแบบถาวรที่เก็บข้อมูลที่ Agent ต้องรักษาไว้ในขั้นตอนต่างๆ การเรียกใช้เครื่องมือ หรือการโต้ตอบ สถานะนี้สามารถจัดเก็บผลลัพธ์ชั่วคราว ข้อมูลผู้ใช้ พารามิเตอร์สำหรับการดำเนินการในภายหลัง หรือข้อมูลอื่นๆ ที่เอเจนต์ต้องจดจำขณะทำงาน
ในสถานการณ์ของเรา เมื่อ Loop Agent ทำซ้ำ summary_agent และ check_agent จะจัดเก็บเอาต์พุต (summary และ summary_status) ไว้ในสถานะของ Agent ซึ่งจะช่วยให้ข้อมูลคงอยู่ได้ตลอดการทำซ้ำ อย่างไรก็ตาม ตัวแทน Loop เองจะไม่ส่งคืนสรุปสุดท้ายจากสถานะโดยอัตโนมัติเมื่อดำเนินการเสร็จสิ้น

Callbackใน ADK ช่วยให้เราแทรกลอจิกที่กำหนดเองเพื่อดำเนินการในจุดที่เฉพาะเจาะจงในวงจรของ Agent หรือเพื่อตอบสนองต่อเหตุการณ์บางอย่าง เช่น การเรียกเครื่องมือเสร็จสมบูรณ์ หรือก่อนที่ Agent จะดำเนินการเสร็จ ซึ่งเป็นวิธีปรับแต่งลักษณะการทำงานของเอเจนต์และประมวลผลผลลัพธ์แบบไดนามิก
เราจะใช้ after_agent_callback ที่ทำงานเมื่อลูปสิ้นสุด (เนื่องจาก CheckCondition มีการส่งต่อ) การเรียกกลับนี้ modify_output_after_agent จะดึงข้อมูลสรุปสุดท้ายจากสถานะและจัดรูปแบบเป็นข้อความเอาต์พุตสุดท้ายของตัวแทน

👉📝 ใน ~/instavibe-bootstrap/agents/social/agent.py เดียวกัน ให้แทนที่ #REPLACE FOR modify_output_after_agent ด้วย follow:
def modify_output_after_agent(callback_context: CallbackContext) -> Optional[types.Content]:
agent_name = callback_context.agent_name
invocation_id = callback_context.invocation_id
current_state = callback_context.state.to_dict()
current_user_content = callback_context.user_content
print(f"[Callback] Exiting agent: {agent_name} (Inv: {invocation_id})")
print(f"[Callback] Current summary_status: {current_state.get("summary_status")}")
print(f"[Callback] Current Content: {current_user_content}")
status = current_state.get("summary_status").strip()
is_done = (status == "completed")
# Retrieve the final summary from the state
final_summary = current_state.get("summary")
print(f"[Callback] final_summary: {final_summary}")
if final_summary and is_done and isinstance(final_summary, str):
log.info(f"[Callback] Found final summary, constructing output Content.")
# Construct the final output Content object to be sent back
return types.Content(role="model", parts=[types.Part(text=final_summary.strip())])
else:
log.warning("[Callback] No final summary found in state or it's not a string.")
# Optionally return a default message or None if no summary was generated
return None
การกำหนดตัวแทนลูปรูท
สุดท้าย เราจะกำหนด LoopAgent หลัก โดยจะจัดลำดับการทำงานของเอเจนต์ย่อยภายในแต่ละการวนซ้ำ (profile_agent -> summary_agent -> check_agent -> CheckCondition) โดยจะทำซ้ำลำดับนี้สูงสุด max_iterations ครั้งหรือจนกว่า CheckCondition จะส่งสัญญาณว่าเสร็จสมบูรณ์ after_agent_callback ช่วยให้มั่นใจได้ว่าจะมีการแสดงผลสรุปสุดท้าย
👉📝 ใน ~/instavibe-bootstrap/agents/social/agent.py เดียวกัน ให้แทนที่ #REPLACE FOR root_agent ด้วย follow:
root_agent = LoopAgent(
name="InteractivePipeline",
sub_agents=[
profile_agent,
summary_agent,
check_agent,
CheckCondition(name="Checker")
],
description="Find everyone's social profile on events, post and friends",
max_iterations=10,
after_agent_callback=modify_output_after_agent
)
มาทดสอบเวิร์กโฟลว์แบบหลาย Agent นี้โดยใช้ UI สำหรับนักพัฒนาซอฟต์แวร์ ADK กัน
👉💻 เปิดใช้เว็บเซิร์ฟเวอร์ ADK
. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd ~/instavibe-bootstrap/agents
sed -i "s|^\(O\?GOOGLE_CLOUD_PROJECT\)=.*|GOOGLE_CLOUD_PROJECT=${PROJECT_ID}|" ~/instavibe-bootstrap/agents/social/.env
adk web
เปิด UI สำหรับนักพัฒนา ADK (พอร์ต 8000 ผ่านตัวอย่างเว็บ) ในเมนูแบบเลื่อนลงของตัวแทน (ด้านขวาบน) ให้เลือกตัวแทนโซเชียล
👉 ตอนนี้ให้งานโปรไฟล์ผู้ใช้หลายคนแก่โมเดล ในกล่องโต้ตอบแชท ให้ป้อนข้อมูลต่อไปนี้
Tell me about Mike and Bob
หลังจากที่ตัวแทนตอบกลับ (ซึ่งอาจใช้เวลานานกว่าปกติเล็กน้อยเนื่องจากการวนซ้ำและการเรียก LLM หลายครั้ง) อย่าดูแค่เอาต์พุตแชทสุดท้าย ไปที่แท็บเหตุการณ์ในแผงด้านซ้ายของ UI สำหรับนักพัฒนา ADK
👉 ขั้นตอนการยืนยัน: ในแท็บเหตุการณ์ คุณจะเห็นการติดตามการดำเนินการแบบทีละขั้นตอนโดยละเอียด 
หลังจากสังเกตวิธีที่ Agent เรียกใช้ Agent ย่อย แต่ละตัว ซึ่งคุณคาดหวังให้โฟลว์ไปจาก profile_agent -> summary_agent -> check_agent, Checker ภายในแต่ละการทำซ้ำ แต่ในทางปฏิบัติ เราเห็น "การเพิ่มประสิทธิภาพด้วยตนเอง" ที่มีประสิทธิภาพของเอเจนต์
เนื่องจากโมเดลพื้นฐานจะเห็นคำขอทั้งหมด (เช่น "สร้างโปรไฟล์ของไมค์และบ็อบ") จึงมักเลือกเส้นทางที่มีประสิทธิภาพที่สุด โดยรวบรวมข้อมูลที่จำเป็นทั้งหมดในรอบเดียวที่รวมไว้ แทนที่จะทำซ้ำหลายครั้ง คุณจะเห็นอินพุต เอาต์พุต และสถานะของแต่ละขั้นตอน รวมถึงการเรียกใช้เครื่องมือที่ดำเนินการโดย profile_agent

และข้อมูลอัปเดตสถานะจาก check_agent และ CheckCondition 
ร่องรอยภาพนี้มีประโยชน์อย่างยิ่งในการทำความเข้าใจและแก้ไขข้อบกพร่องเกี่ยวกับวิธีการทำงานของเวิร์กโฟลว์แบบหลายเอเจนต์จนกว่าระบบจะสร้างสรุปสุดท้ายและส่งคืนโดย Callback
เมื่อสำรวจการตอบกลับของแชทและการติดตามเหตุการณ์แล้ว ให้กลับไปที่เทอร์มินัล Cloud Shell แล้วกด Ctrl+C เพื่อหยุด UI สำหรับนักพัฒนา ADK
10. การสื่อสารระหว่างตัวแทน (A2A)
ที่ผ่านมา เราได้สร้าง Agent เฉพาะทาง แต่ Agent เหล่านี้ทำงานแยกกันหรือภายในเวิร์กโฟลว์ที่กำหนดไว้ล่วงหน้าในเครื่องเดียวกัน หากต้องการสร้างระบบแบบหลายเอเจนต์ที่กระจายและทำงานร่วมกันอย่างแท้จริง เราต้องมีวิธีให้เอเจนต์ซึ่งอาจทำงานเป็นบริการแยกกันค้นพบกันและสื่อสารกันได้อย่างมีประสิทธิภาพ โปรโตคอล Agent-to-Agent (A2A) จึงเข้ามามีบทบาทในจุดนี้
โปรโตคอล A2A เป็นมาตรฐานแบบเปิดที่ออกแบบมาโดยเฉพาะสำหรับการสื่อสารที่ทำงานร่วมกันได้ระหว่าง AI Agent แม้ว่า MCP จะมุ่งเน้นการโต้ตอบระหว่างเอเจนต์กับเครื่องมือ แต่ A2A จะมุ่งเน้นการโต้ตอบระหว่างเอเจนต์กับเอเจนต์ ซึ่งจะช่วยให้ตัวแทนทำสิ่งต่อไปนี้ได้
- ค้นพบ: ค้นหาเอเจนต์อื่นๆ และดูความสามารถของเอเจนต์เหล่านั้นผ่านการ์ดเอเจนต์มาตรฐาน
- สื่อสาร: แลกเปลี่ยนข้อความและข้อมูลอย่างปลอดภัย
- ทำงานร่วมกัน: มอบหมายงานและประสานงานเพื่อบรรลุเป้าหมายที่ซับซ้อน
โปรโตคอล A2A ช่วยให้การสื่อสารนี้เป็นไปได้ผ่านกลไกต่างๆ เช่น "การ์ดเอเจนต์" ซึ่งเอเจนต์สามารถใช้เพื่อโฆษณาความสามารถและข้อมูลการเชื่อมต่อของตนได้

A2A ใช้มาตรฐานเว็บที่คุ้นเคย (HTTP, SSE, JSON-RPC) และมักใช้รูปแบบไคลเอ็นต์-เซิร์ฟเวอร์ที่เอเจนต์หนึ่ง (ไคลเอ็นต์) ส่งงานไปยังอีก Agent ระยะไกล (Agent/เซิร์ฟเวอร์ระยะไกล) การกำหนดมาตรฐานนี้เป็นกุญแจสำคัญในการสร้างระบบแบบแยกส่วนที่ปรับขนาดได้ ซึ่งเอเจนต์ที่พัฒนาแยกกันจะทำงานร่วมกันได้
การเปิดใช้ A2A สำหรับตัวแทน InstaVibe
หากต้องการให้ตัวแทน Planner, Platform Interaction และ Social ที่มีอยู่เข้าถึงได้สำหรับตัวแทนอื่นๆ ผ่าน A2A เราต้องห่อหุ้มแต่ละตัวด้วยคอมโพเนนต์เซิร์ฟเวอร์ A2A เซิร์ฟเวอร์นี้จะทำสิ่งต่อไปนี้
- แสดงการ์ด Agent: แสดงคำอธิบายมาตรฐานของความสามารถของ Agent ผ่านปลายทาง HTTP
- ฟังงาน(ข้อความคำขอ): ยอมรับคำของานขาเข้าจากตัวแทนรายอื่น (ไคลเอ็นต์ A2A) ตามโปรโตคอล A2A
- จัดการการดำเนินการงาน(ข้อความคำขอ): ส่งต่องานที่ได้รับไปยังตรรกะของ Agent ประเภท ADK ที่อยู่เบื้องหลังเพื่อประมวลผล
Planner Agent (เปิดใช้ A2A)

มาเริ่มด้วยการเพิ่มเลเยอร์เซิร์ฟเวอร์ A2A ลงใน Planner Agent กัน
กำหนดตรรกะการเริ่มต้นเซิร์ฟเวอร์ A2A โค้ดนี้กำหนด AgentCard (คำอธิบายสาธารณะของ Agent) กำหนดค่า A2AServer และเริ่มทำงานโดยลิงก์กับ PlatformAgentExecutor
👉📝 เพิ่มโค้ดต่อไปนี้ที่ส่วนท้ายของ ~/instavibe-bootstrap/agents/planner/a2a_server.py
class PlannerAgent:
"""An agent to help user planning a event with its desire location."""
SUPPORTED_CONTENT_TYPES = ["text", "text/plain"]
def __init__(self):
self._agent = self._build_agent()
self.runner = Runner(
app_name=self._agent.name,
agent=self._agent,
artifact_service=InMemoryArtifactService(),
session_service=InMemorySessionService(),
memory_service=InMemoryMemoryService(),
)
capabilities = AgentCapabilities(streaming=True)
skill = AgentSkill(
id="event_planner",
name="Event planner",
description="""
This agent generates multiple fun plan suggestions tailored to your specified location, dates, and interests,
all designed for a moderate budget. It delivers detailed itineraries,
including precise venue information (name, latitude, longitude, and description), in a structured JSON format.
""",
tags=["instavibe"],
examples=["What about Bostona MA this weekend?"],
)
self.agent_card = AgentCard(
name="Event Planner Agent",
description="""
This agent generates multiple fun plan suggestions tailored to your specified location, dates, and interests,
all designed for a moderate budget. It delivers detailed itineraries,
including precise venue information (name, latitude, longitude, and description), in a structured JSON format.
""",
url=f"{PUBLIC_URL}",
version="1.0.0",
defaultInputModes=PlannerAgent.SUPPORTED_CONTENT_TYPES,
defaultOutputModes=PlannerAgent.SUPPORTED_CONTENT_TYPES,
capabilities=capabilities,
skills=[skill],
)
def get_processing_message(self) -> str:
return "Processing the planning request..."
def _build_agent(self) -> LlmAgent:
"""Builds the LLM agent for the night out planning agent."""
return agent.root_agent
if __name__ == '__main__':
try:
plannerAgent = PlannerAgent()
request_handler = DefaultRequestHandler(
agent_executor=PlannerAgentExecutor(plannerAgent.runner,plannerAgent.agent_card),
task_store=InMemoryTaskStore(),
)
server = A2AStarletteApplication(
agent_card=plannerAgent.agent_card,
http_handler=request_handler,
)
logger.info(f"Attempting to start server with Agent Card: {plannerAgent.agent_card.name}")
logger.info(f"Server object created: {server}")
uvicorn.run(server.build(), host='0.0.0.0', port=port)
except Exception as e:
logger.error(f"An error occurred during server startup: {e}")
exit(1)
👉💻 มาทดสอบอย่างรวดเร็วว่าเซิร์ฟเวอร์ A2A เริ่มทำงานอย่างถูกต้องในเครื่องและแสดงการ์ดตัวแทนหรือไม่ เรียกใช้คำสั่งต่อไปนี้ในเทอร์มินัลแรก
. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd ~/instavibe-bootstrap/agents/
python -m planner.a2a_server
👉 ตอนนี้ให้เปิดหน้าต่างเทอร์มินัลอีกหน้าต่าง (คลิกเครื่องหมาย + ในแผงเทอร์มินัล) 
👉💻 ใช้ curl เพื่อขอ Agent Card จากเซิร์ฟเวอร์ที่ทำงานในเครื่อง
curl http://localhost:10003/.well-known/agent.json | jq
คุณควรเห็นการแสดง JSON ของ AgentCard ที่เรากำหนด ซึ่งยืนยันว่าเซิร์ฟเวอร์กำลังทำงานและโฆษณา Agent ของ Planner

กลับไปที่เทอร์มินัลแรก (ที่เซิร์ฟเวอร์ทำงานอยู่) แล้วกด Ctrl+C เพื่อหยุด
👉💻 เมื่อเพิ่มตรรกะของเซิร์ฟเวอร์ A2A แล้ว ตอนนี้เราก็สร้างอิมเมจคอนเทนเนอร์ได้แล้ว
สร้างและติดตั้งใช้งาน Planner Agent
. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap/agents
# Set variables specific to the PLANNER agent
export IMAGE_TAG="latest"
export AGENT_NAME="planner"
export IMAGE_NAME="planner-agent"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="planner-agent"
export PUBLIC_URL="https://planner-agent-${PROJECT_NUMBER}.${REGION}.run.app"
echo "Building ${AGENT_NAME} agent..."
gcloud builds submit . \
--config=cloudbuild-build.yaml \
--project=${PROJECT_ID} \
--region=${REGION} \
--substitutions=_AGENT_NAME=${AGENT_NAME},_IMAGE_PATH=${IMAGE_PATH}
echo "Image built and pushed to: ${IMAGE_PATH}"
👉💻 และติดตั้งใช้งาน Planner Agent ใน Cloud Run
. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap/agents
# Set variables specific to the PLANNER agent
export IMAGE_TAG="latest"
export AGENT_NAME="planner"
export IMAGE_NAME="planner-agent"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="planner-agent"
export PUBLIC_URL="https://planner-agent-${PROJECT_NUMBER}.${REGION}.run.app"
gcloud run deploy ${SERVICE_NAME} \
--image=${IMAGE_PATH} \
--platform=managed \
--region=${REGION} \
--set-env-vars="A2A_HOST=0.0.0.0" \
--set-env-vars="A2A_PORT=8080" \
--set-env-vars="GOOGLE_GENAI_USE_VERTEXAI=TRUE" \
--set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
--set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
--set-env-vars="PUBLIC_URL=${PUBLIC_URL}" \
--allow-unauthenticated \
--project=${PROJECT_ID} \
--min-instances=1
มาตรวจสอบว่าบริการที่ติดตั้งใช้งานทำงานและแสดงการ์ดเอเจนต์อย่างถูกต้องจากระบบคลาวด์โดยใช้ A2A Inspector กัน
👉 จากไอคอนตัวอย่างเว็บในแถบเครื่องมือ Cloud Shell ให้เลือกเปลี่ยนพอร์ต ตั้งค่าพอร์ตเป็น 8081 แล้วคลิก "เปลี่ยนและแสดงตัวอย่าง" แท็บเบราว์เซอร์ใหม่จะเปิดขึ้นพร้อมอินเทอร์เฟซของเครื่องมือตรวจสอบ A2A

👉💻 ในเทอร์มินัล ให้รับ URL ของเอเจนต์วางแผนที่ติดตั้งใช้งานแล้วโดยทำดังนี้
export PLANNER_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner-agent)
echo ${PLANNER_AGENT_URL}
👉💻 คัดลอก URL เอาต์พุต
👉 ใน UI ของเครื่องมือตรวจสอบ A2A ให้วาง URL ลงในช่อง URL ของเอเจนต์ แล้วคลิก "เชื่อมต่อ"
👀 รายละเอียดบัตรและ JSON ของตัวแทนควรปรากฏในแท็บการ์ดตัวแทน ซึ่งเป็นการยืนยันว่าเชื่อมต่อสำเร็จ

👉 คลิกแท็บแชทใน A2A Inspector ซึ่งเป็นที่ที่คุณสามารถโต้ตอบกับเอเจนต์ที่ใช้งานได้โดยตรง ส่งข้อความเพื่อทดสอบความสามารถในการวางแผน เช่น
Plan something for me in Boston MA this weekend, and I enjoy classical music
👀 หากต้องการตรวจสอบการสื่อสารดิบ ให้คลิกบับเบิลข้อความของคุณ แล้วคลิกบับเบิลคำตอบของตัวแทนในหน้าต่างแชท เมื่อคลิกแต่ละรายการ ระบบจะแสดงข้อความ JSON-RPC 2.0 แบบเต็มที่ส่งหรือรับ ซึ่งมีประโยชน์อย่างยิ่งสำหรับการแก้ไขข้อบกพร่อง
โปรดเก็บแท็บเครื่องมือตรวจสอบ A2A ไว้ใกล้มือ อย่าปิด เราจะใช้ฟีเจอร์นี้อีกครั้งในอีกสักครู่เพื่อทดสอบเอเจนต์อีก 2 คน

Agent การโต้ตอบกับแพลตฟอร์ม (เปิดใช้ A2A)

จากนั้นเราจะทำซ้ำกระบวนการสำหรับตัวแทนการโต้ตอบกับแพลตฟอร์ม (ตัวแทนที่ใช้ MCP)
👉📝 กำหนดการตั้งค่าเซิร์ฟเวอร์ A2A รวมถึง AgentCard ที่ไม่ซ้ำกันที่ส่วนท้ายของ ~/instavibe-bootstrap/agents/platform_mcp_client/a2a_server.py:
class PlatformAgent:
"""An agent that post event and post to instavibe."""
SUPPORTED_CONTENT_TYPES = ["text", "text/plain"]
def __init__(self):
self._agent = self._build_agent()
self.runner = Runner(
app_name=self._agent.name,
agent=self._agent,
artifact_service=InMemoryArtifactService(),
session_service=InMemorySessionService(),
memory_service=InMemoryMemoryService(),
)
capabilities = AgentCapabilities(streaming=True)
skill = AgentSkill(
id="instavibe_posting",
name="Post social post and events on instavibe",
description="""
This "Instavibe" agent helps you create posts (identifying author, text, and sentiment – inferred if unspecified) and register
for events (gathering name, date, attendee). It efficiently collects required information and utilizes dedicated tools
to perform these actions on your behalf, ensuring a smooth sharing experience.
""",
tags=["instavibe"],
examples=["Create a post for me, the post is about my cute cat and make it positive, and I'm Alice"],
)
self.agent_card = AgentCard(
name="Instavibe Posting Agent",
description="""
This "Instavibe" agent helps you create posts (identifying author, text, and sentiment – inferred if unspecified) and register
for events (gathering name, date, attendee). It efficiently collects required information and utilizes dedicated tools
to perform these actions on your behalf, ensuring a smooth sharing experience.
""",
url=f"{PUBLIC_URL}",
version="1.0.0",
defaultInputModes=PlatformAgent.SUPPORTED_CONTENT_TYPES,
defaultOutputModes=PlatformAgent.SUPPORTED_CONTENT_TYPES,
capabilities=capabilities,
skills=[skill],
)
def get_processing_message(self) -> str:
return "Processing the social post and event request..."
def _build_agent(self) -> LlmAgent:
"""Builds the LLM agent for the Processing the social post and event request."""
return agent.root_agent
if __name__ == '__main__':
try:
platformAgent = PlatformAgent()
request_handler = DefaultRequestHandler(
agent_executor=PlatformAgentExecutor(platformAgent.runner,platformAgent.agent_card),
task_store=InMemoryTaskStore(),
)
server = A2AStarletteApplication(
agent_card=platformAgent.agent_card,
http_handler=request_handler,
)
uvicorn.run(server.build(), host='0.0.0.0', port=port)
except Exception as e:
logger.error(f"An error occurred during server startup: {e}")
exit(1)
Social Agent (เปิดใช้ A2A)

สุดท้ายนี้ มาเปิดใช้ A2A สำหรับตัวแทนการสร้างโปรไฟล์โซเชียลกัน
👉📝 กำหนดการตั้งค่าเซิร์ฟเวอร์ A2A และ AgentCard ที่ส่วนท้ายของ ~/instavibe-bootstrap/agents/social/a2a_server.py:
class SocialAgent:
"""An agent that handles social profile analysis."""
SUPPORTED_CONTENT_TYPES = ["text", "text/plain"]
def __init__(self):
self._agent = self._build_agent()
self.runner = Runner(
app_name=self._agent.name,
agent=self._agent,
artifact_service=InMemoryArtifactService(),
session_service=InMemorySessionService(),
memory_service=InMemoryMemoryService(),
)
capabilities = AgentCapabilities(streaming=True)
skill = AgentSkill(
id="social_profile_analysis",
name="Analyze Instavibe social profile",
description="""
Using a provided list of names, this agent synthesizes Instavibe social profile information by analyzing posts, friends, and events.
It delivers a comprehensive single-paragraph summary for individuals, and for groups, identifies commonalities in their social activities
and connections based on profile data.
""",
tags=["instavibe"],
examples=["Can you tell me about Bob and Alice?"],
)
self.agent_card = AgentCard(
name="Social Profile Agent",
description="""
Using a provided list of names, this agent synthesizes Instavibe social profile information by analyzing posts, friends, and events.
It delivers a comprehensive single-paragraph summary for individuals, and for groups, identifies commonalities in their social activities
and connections based on profile data.
""",
url=f"{PUBLIC_URL}",
version="1.0.0",
defaultInputModes=self.SUPPORTED_CONTENT_TYPES,
defaultOutputModes=self.SUPPORTED_CONTENT_TYPES,
capabilities=capabilities,
skills=[skill],
)
def get_processing_message(self) -> str:
return "Processing the social profile analysis request..."
def _build_agent(self) -> LoopAgent:
"""Builds the LLM agent for the social profile analysis agent."""
return agent.root_agent
if __name__ == '__main__':
try:
socialAgent = SocialAgent()
request_handler = DefaultRequestHandler(
agent_executor=SocialAgentExecutor(socialAgent.runner,socialAgent.agent_card),
task_store=InMemoryTaskStore(),
)
server = A2AStarletteApplication(
agent_card=socialAgent.agent_card,
http_handler=request_handler,
)
uvicorn.run(server.build(), host='0.0.0.0', port=port)
except Exception as e:
logger.error(f"An error occurred during server startup: {e}")
exit(1)
สร้างและทำให้ใช้งานได้เอเจนต์การโต้ตอบบนแพลตฟอร์มและเอเจนต์โซเชียล
เอเจนต์เหล่านี้ต้องมีสิทธิ์เข้าถึง Spanner ดังนั้นโปรดตรวจสอบว่ามีการส่งตัวแปรสภาพแวดล้อม SPANNER_INSTANCE_ID, SPANNER_DATABASE_ID และ MCP_SERVER_URL อย่างถูกต้องในระหว่างการติดตั้งใช้งาน
👉💻 สร้างและทำให้ใช้งานได้กับ Cloud Run ด้วย Cloud Build
. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap/agents
export MCP_SERVER_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep mcp-tool-server)/sse
gcloud builds submit . \
--config=cloudbuild.yaml \
--project="${PROJECT_ID}" \
--region="${REGION}" \
--substitutions=\
_PROJECT_ID="${PROJECT_ID}",\
_PROJECT_NUMBER="${PROJECT_NUMBER}",\
_REGION="${REGION}",\
_REPO_NAME="${REPO_NAME}",\
_SPANNER_INSTANCE_ID="${SPANNER_INSTANCE_ID}",\
_SPANNER_DATABASE_ID="${SPANNER_DATABASE_ID}",\
_MCP_SERVER_URL="${MCP_SERVER_URL}"
👉💻 ในเทอร์มินัล ให้รับ URL ของ Agent แพลตฟอร์มที่ทำให้ใช้งานได้แล้ว
export PLATFORM_MPC_CLIENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep platform-mcp-client)
echo $PLATFORM_MPC_CLIENT_URL
👉💻 คัดลอก URL เอาต์พุต
👉 ใน UI ของเครื่องมือตรวจสอบ A2A ให้วาง URL ลงในช่อง URL ของเอเจนต์ แล้วคลิก "เชื่อมต่อ"
👀 รายละเอียดบัตรและ JSON ของตัวแทนควรปรากฏในแท็บการ์ดตัวแทน ซึ่งเป็นการยืนยันว่าเชื่อมต่อสำเร็จ

👉 คลิกแท็บแชทใน A2A Inspector ส่วนนี้ให้คุณโต้ตอบกับเอเจนต์ที่ใช้งานได้โดยตรง ส่งข้อความเพื่อทดสอบความสามารถของเอเจนต์ในการสร้างโพสต์
Create a post for me, the post says 'Paws, purrs, and ocean views 🐾☕🌊. Spent my morning at the Morning Seaside Cat Café, where every sip comes with a side of snuggles and sea breeze.' and make it positive, and I'm Oscar.
👀 หากต้องการตรวจสอบการสื่อสารดิบ ให้คลิกบับเบิลข้อความของคุณ แล้วคลิกบับเบิลคำตอบของตัวแทนในหน้าต่างแชท เมื่อคลิกแต่ละรายการ ระบบจะแสดงข้อความ JSON-RPC 2.0 แบบเต็มที่ส่งหรือรับ ซึ่งมีประโยชน์อย่างยิ่งสำหรับการแก้ไขข้อบกพร่อง
👉💻 ในเทอร์มินัล ให้รับ URL ของ Agent โซเชียลที่ทำให้ใช้งานได้
export SOCIAL_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep social-agent)
echo $SOCIAL_AGENT_URL
👉💻 คัดลอก URL เอาต์พุต
👉 ใน UI ของเครื่องมือตรวจสอบ A2A ให้วาง URL ลงในช่อง URL ของเอเจนต์ แล้วคลิก "เชื่อมต่อ"
👀 รายละเอียดบัตรและ JSON ของตัวแทนควรปรากฏในแท็บการ์ดตัวแทน ซึ่งเป็นการยืนยันว่าเชื่อมต่อสำเร็จ

👉 คลิกแท็บแชทใน A2A Inspector คุณสามารถโต้ตอบกับเอเจนต์ที่ติดตั้งใช้งานได้โดยตรงในส่วนนี้ ส่งข้อความเพื่อวิเคราะห์โปรไฟล์ผู้ใช้จากฐานข้อมูล
Can you tell me about both Ian and Kevin's profile, what are their common interests?
👀 หากต้องการตรวจสอบการสื่อสารดิบ ให้คลิกบับเบิลข้อความของคุณ แล้วคลิกบับเบิลคำตอบของตัวแทนในหน้าต่างแชท เมื่อคลิกแต่ละรายการ ระบบจะแสดงข้อความ JSON-RPC 2.0 แบบเต็มที่ส่งหรือรับ ซึ่งมีประโยชน์อย่างยิ่งสำหรับการแก้ไขข้อบกพร่อง
👉 เยี่ยม เราตรวจสอบตัวแทนทั้งหมดเสร็จแล้ว คุณปิดแท็บ A2A Inspector ได้แล้ว
11. Orchestrator Agent (ไคลเอ็นต์ A2A)
ตอนนี้เรามีเอเจนต์เฉพาะทาง 3 ราย (Planner, Platform, Social) ที่ทำงานเป็นบริการอิสระที่เปิดใช้ A2A ใน Cloud Run ส่วนสุดท้ายคือ Orchestrator Agent เอเจนต์นี้จะทำหน้าที่เป็นผู้ประสานงานส่วนกลางหรือไคลเอ็นต์ A2A โดยจะรับคำขอของผู้ใช้ ค้นหา Agent ระยะไกล ที่จำเป็นต่อการดำเนินการตามคำขอ (อาจเป็นลำดับ) แล้วใช้โปรโตคอล A2A เพื่อมอบหมายงานให้กับ Agent ระยะไกล เหล่านั้น สำหรับเวิร์กช็อปนี้ เราจะเรียกใช้ Agent Orchestrator ในเครื่องโดยใช้ ADK Dev UI

ก่อนอื่น มาปรับปรุงตรรกะของ Orchestrator เพื่อจัดการการลงทะเบียนของเอเจนต์ระยะไกลที่ค้นพบกัน จัดเก็บรายละเอียดการเชื่อมต่อจากการ์ดตัวแทนที่ดึงข้อมูลมาในระหว่างการเริ่มต้น
👉📝 ใน ~/instavibe-bootstrap/agents/orchestrate/agent.py ให้แทนที่ #REPLACE ME REG AGENT CARD ด้วย
async with httpx.AsyncClient(timeout=30) as client:
for i, address in enumerate(REMOTE_AGENT_ADDRESSES):
log.info(f"--- STEP 3.{i}: Attempting connection to: {address} ---")
try:
card_resolver = A2ACardResolver(client, address)
card = await card_resolver.get_agent_card()
remote_connection = RemoteAgentConnections(agent_card=card, agent_url=address)
self.remote_agent_connections[card.name] = remote_connection
self.cards[card.name] = card
log.info(f"--- STEP 5.{i}: Successfully stored connection for {card.name} ---")
except Exception as e:
log.error(f"--- CRITICAL FAILURE at STEP 4.{i} for address: {address} ---")
log.error(f"--- The hidden exception type is: {type(e).__name__} ---")
log.error(f"--- Full exception details and traceback: ---", exc_info=True)
จากนั้นกำหนดเครื่องมือสำหรับตัวแทน Orchestrator ภายใน ADK
send_message(ฟังก์ชัน A2A สำหรับมอบหมายงาน)
👉📝 แทนที่ #REPLACE ME CREATE AGENT ใน ~/instavibe-bootstrap/agents/orchestrate/agent.py ด้วย
def create_agent(self) -> Agent:
"""Synchronously creates the ADK Agent object."""
return Agent(
model="gemini-2.5-flash",
name="orchestrate_agent",
instruction=self.root_instruction,
before_agent_callback=self.before_agent_callback,
description=("Orchestrates tasks for child agents."),
tools=[self.send_message],
)
ตรรกะหลักของ Orchestrator อยู่ในคำสั่ง ซึ่งจะบอกวิธีใช้ A2A
👉📝 แทนที่ #REPLACE ME INSTRUCTIONS ใน ~/instavibe-bootstrap/agents/orchestrate/agent.py ด้วยวิธีการสร้างคำสั่งนี้
def root_instruction(self, context: ReadonlyContext) -> str:
current_agent = self.check_active_agent(context)
return f"""
You are an expert AI Orchestrator. Your primary responsibility is to intelligently interpret user requests, break them down into a logical plan of discrete actions, and delegate each action to the most appropriate specialized remote agent using the send_message function. You do not perform the tasks yourself but manage their assignment, sequence, and critically, their outcomes.
**Core Directives & Decision Making:**
* **Understand User Intent & Complexity:**
* Carefully analyze the user's request to determine the core task(s) they want to achieve. Pay close attention to keywords and the overall goal.
* Identify if the request requires a single agent or a sequence of actions from multiple agents. For example, "Analyze John Doe's profile and then create a positive post about his recent event attendance" would require two agents in sequence.
* **Task Planning & Sequencing (for Multi-Step Requests):**
* Before delegating, outline the clear sequence of agent tasks.
* Identify dependencies. If Task B requires output from Task A, execute them sequentially. If tasks are independent (like creating a post and then creating an event), execute them one after the other as separate delegations.
* Agent Reusability: An agent's completion of one task does not make it unavailable. If a user's plan involves multiple, distinct actions that fall under the same agent's expertise (e.g., create a post, then create an event), you must call that same agent again for the subsequent task.
* **Task Delegation & Management (using `send_message`):**
* **Delegation:** Use `send_message` to assign actionable tasks to the selected remote agent. Your `send_message` call MUST include:
* The `remote_agent_name` you've selected.
* The `user_request` or all necessary parameters extracted from the user's input, formatted in a way the target agent will understand.
* **Contextual Awareness for Remote Agents:** If a remote agent repeatedly requests user confirmation or seems to lack context, assume it lacks access to the full conversation history. In such cases, enrich your `send_message` with all necessary contextual information relevant to that specific agent from the conversation history.
* **Sequential Task Execution:**
* After a preceding task completes (indicated by the agent's response or a success signal), gather any necessary output from it.
* Then, use `send_message` for the next agent in the sequence, providing it with the user's original relevant intent and any necessary data obtained from the previous agent's task.
* **Active Agent Prioritization:** If an active agent is already engaged and the user's request is related to its current task, route subsequent related requests directly to that agent by providing updated context via `send_message`.
**Critical Success Verification:**
* You **MUST** wait for the tool_output after every send_message call before taking any further action.
* Your decision to proceed to the next task in a sequence **MUST** be based entirely on a confirmation of success from the tool_output of the previous task.
* If a tool call fails, returns an error, or the tool_output is ambiguous, you MUST STOP the sequence. Your next action is to report the exact failure or ambiguity to the user.
* DO NOT assume a task was successful. Do not invent success messages like "The event has been created." Only state that a task is complete if the tool's response explicitly says so.
**Communication with User:**
* **Transparent Communication:** Always present the complete and detailed response from the remote agent to the user. Do not summarize or filter unless explicitly instructed.
* When you delegate a task (or the first task in a sequence), clearly inform the user which remote agent is handling it.
* For multi-step requests, you can optionally inform the user of the planned sequence (e.g., "Okay, first I'll ask the 'Social Profile Agent' to analyze the profile, and then I'll have the 'Instavibe Posting Agent' create the post.").
* If waiting for a task in a sequence to complete, you can inform the user (e.g., "The 'Social Profile Agent' is currently processing. I'll proceed with the post once that's done.").
* **User Confirmation Relay:** If a remote agent asks for confirmation, and the user has not already provided it, just make up something.
* If the user's request is ambiguous, if necessary information is missing for any agent in the sequence, or if you are unsure about the plan, just make up something.
**Important Reminders:**
* **Autonomous Agent Engagement:** Never seek user permission before engaging with remote agents. If multiple agents are required to fulfill a request, connect with them directly without requesting user preference or confirmation.
* **Focused Information Sharing:** Provide remote agents with only relevant contextual information. Avoid extraneous details that are not directly pertinent to their task.
* **No Redundant Confirmations:** Do not ask remote agents for confirmation of information or actions they have already processed or committed to.
* **Tool Reliance:** Strictly rely on your available tools, primarily `send_message`, to address user requests. Do not generate responses based on assumptions. If information is insufficient, request clarification from the user.
* **Prioritize Recent Interaction:** Focus primarily on the most recent parts of the conversation when processing requests, while maintaining awareness of the overall goal for multi-step tasks.
* Always prioritize selecting the correct agent(s) based on their documented purpose.
* Ensure all information required by the chosen remote agent is included in the `send_message` call, including outputs from previous agents if it's a sequential task.
Agents:
{self.agents}
Current agent: {current_agent['active_agent']}`
"""
การทดสอบ Orchestrator และระบบ A2A แบบเต็ม
ตอนนี้มาทดสอบทั้งระบบกัน เราจะเรียกใช้ Orchestrator ในเครื่องโดยใช้ UI สำหรับนักพัฒนาซอฟต์แวร์ ADK และจะสื่อสารกับ Planner, Platform และ Social Agent ที่ทำงานจากระยะไกลใน Cloud Run
👉💻 ก่อนอื่น ตรวจสอบว่าตัวแปรสภาพแวดล้อม REMOTE_AGENT_ADDRESSES มี URL ที่คั่นด้วยคอมมาของเอเจนต์ที่เปิดใช้ A2A ที่คุณติดตั้งใช้งาน จากนั้นตั้งค่าตัวแปรสภาพแวดล้อมที่จำเป็นสำหรับเอเจนต์ Orchestrator และเปิดใช้ UI สำหรับนักพัฒนา ADK โดยทำดังนี้
. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
export PLATFORM_MPC_CLIENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep platform-mcp-client)
export PLANNER_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner-agent)
export SOCIAL_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep social-agent)
export REMOTE_AGENT_ADDRESSES=${PLANNER_AGENT_URL},${PLATFORM_MPC_CLIENT_URL},${SOCIAL_AGENT_URL}
cd ~/instavibe-bootstrap/agents
sed -i "s|^\(O\?REMOTE_AGENT_ADDRESSES\)=.*|REMOTE_AGENT_ADDRESSES=${REMOTE_AGENT_ADDRESSES}|" ~/instavibe-bootstrap/agents/orchestrate/.env
adk web
👉 เปิด UI สำหรับนักพัฒนาแอป ADK (เปลี่ยนพอร์ตกลับเป็น 8000 ผ่านตัวอย่างเว็บ)

👉 ในเมนูแบบเลื่อนลงของ Agent ให้เลือก Agent Orchestrate
👉 ตอนนี้ให้งานที่ซับซ้อนซึ่งต้องประสานงานกับเอเจนต์ระยะไกลหลายคน ลองใช้ตัวอย่างแรกนี้ ซึ่งควรมีทั้งตัวแทนโซเชียลและตัวแทนวางแผน
You are an expert event planner for a user named Diana.
Your task is to design a fun and personalized event.
Here are the details for the plan:
- Friends to invite: Ian, Nora
- Desired date: "2025-10-15"
- Location idea or general preference: "Chicago"
Your process should be:
1. Analyze the provided friend names. If you have access to a tool to get their InstaVibe profiles or summarized interests, please use it.
2. Based on their potential interests (or general good taste if profiles are unavailable), create a tailored plan for the outing, check if you have access to any event planner tools.
3. Ensure the plan includes the original `planned_date`.
The user wants a comprehensive plan that includes:
- The list of invited friends.
- A catchy and descriptive name for the event.
- The exact planned date for the event.
- A summary of what the group will do.
- Specific recommended spots (e.g., restaurants, bars, activity venues) with their names, (if possible, approximate latitude/longitude for mapping, and address), and a brief description of why it fits the plan.
- A short, exciting message that {Diana} can send to {Ian, Nora} to get them excited about the event.

สังเกตการโต้ตอบในหน้าต่างแชทของ UI สำหรับนักพัฒนาซอฟต์แวร์ ADK โปรดใส่ใจกับคำตอบของ Orchestrator อย่างใกล้ชิด โดยควรระบุว่า Agent ระยะไกลใดที่มอบหมายงานให้ (เช่น "โอเค ฉันจะถาม Social Profile Agent เกี่ยวกับ Ian และ Nora ก่อน...")
นอกจากนี้ ให้ตรวจสอบแท็บเหตุการณ์ใน UI เพื่อดูการเรียกเครื่องมือ (send_message) ที่ทําไปยัง URL ของเอเจนต์ระยะไกล

👉 ตอนนี้ลองดูตัวอย่างที่ 2 ซึ่งควรมีตัวแทนการผสานรวมแพลตฟอร์มโดยตรง
Hey, can you register an event on Instavibe for Laura and Charlie? Let's call it 'Vienna Concert & Castles Day'.
here are more info
"event_name": "Vienna Concert & Castles Day",
"description": "A refined and unforgettable day in Vienna with Laura and Charlie. The day begins with a guided tour of the magnificent Schönbrunn Palace, showcasing imperial architecture and history. In the evening, enjoy a classical music concert in one of Vienna's most iconic concert halls.",
"event_date": "2025-10-14T10:00:00+02:00",
"locations": [
{
"name": "Schönbrunn Palace",
"description": "A UNESCO World Heritage Site and former imperial summer residence, Schönbrunn Palace offers opulent rooms, beautiful baroque gardens, and a glimpse into the life of the Habsburg monarchy. Visitors can stroll the grounds or take a guided historical tour.",
"latitude": 48.184516,
"longitude": 16.312222,
"address": "Schönbrunner Schloßstraße 47, 1130 Wien, Austria"
},
{
"name": "Musikverein Vienna",
"description": "Home to the world-renowned Vienna Philharmonic, the Musikverein is one of the finest concert halls in the world. Its 'Golden Hall' is famous for its acoustics and ornate design. Attendees can enjoy a powerful classical concert in an unforgettable setting.",
"latitude": 48.200132,
"longitude": 16.373777,
"address": "Musikvereinsplatz 1, 1010 Wien, Austria"
}
],
"attendee_names": ["Laura", "Charlie", "Oscar"] And I am Oscar
โปรดตรวจสอบแชทและแท็บกิจกรรมอีกครั้ง Orchestrator ควรระบุความจำเป็นในการสร้างกิจกรรมและมอบหมายงาน (พร้อมรายละเอียดทั้งหมดที่ระบุ) ให้กับ "Platform Integration Agent" นอกจากนี้ คุณยังคลิกปุ่มติดตามเพื่อดูการติดตามเพื่อวิเคราะห์เวลาในการตอบกลับของคำค้นหาและการดำเนินการที่ดำเนินการได้ด้วย 
จากนั้นคุณจะยืนยันได้ว่าเหตุการณ์ปรากฏในเว็บแอปพลิเคชัน InstaVibe 
ซึ่งแสดงให้เห็นถึงการติดตั้งใช้งานระบบแบบหลาย Agent โดยใช้ ADK และโปรโตคอล A2A ที่ประสบความสําเร็จ โดยที่ตัวจัดสรรส่วนกลางจะมอบหมายงานให้กับ Agent ระยะไกลที่เชี่ยวชาญ
อย่าลืมหยุด UI สำหรับนักพัฒนาซอฟต์แวร์ ADK (Ctrl+C ในเทอร์มินัล) เมื่อทดสอบเสร็จแล้ว
12. Agent Engine และการโทรระยะไกลจาก InstaVibe
ที่ผ่านมา เราได้เรียกใช้ Agent เฉพาะทางใน Cloud Run และทดสอบ Orchestrator ในเครื่องโดยใช้ UI สำหรับนักพัฒนาซอฟต์แวร์ของ ADK สำหรับสถานการณ์การใช้งานจริง เราต้องมีสภาพแวดล้อมที่แข็งแกร่ง รองรับการปรับขนาดได้ และมีการจัดการเพื่อโฮสต์ Agent และนี่คือจุดที่ Google Vertex AI Agent Engine เข้ามามีบทบาท
Agent Engine เป็นบริการที่มีการจัดการครบวงจรใน Vertex AI ซึ่งออกแบบมาโดยเฉพาะสำหรับการติดตั้งใช้งานและการปรับขนาดเอเจนต์ AI ซึ่งตัดการจัดการโครงสร้างพื้นฐาน ความปลอดภัย และค่าใช้จ่ายในการดำเนินงานออก เพื่อให้นักพัฒนาซอฟต์แวร์ (โดยเฉพาะผู้ที่ไม่คุ้นเคยกับสภาพแวดล้อมระบบคลาวด์ที่ซับซ้อน) สามารถมุ่งเน้นที่ตรรกะและความสามารถของเอเจนต์แทนการจัดการเซิร์ฟเวอร์ โดยมีรันไทม์เฉพาะที่ได้รับการเพิ่มประสิทธิภาพสำหรับภาระงานที่เป็น Agent
ตอนนี้เราจะติดตั้งใช้งานเอเจนต์ Orchestrator ใน Agent Engine (หมายเหตุ: กลไกการติดตั้งใช้งานที่แสดงด้านล่างใช้สคริปต์ที่กำหนดเอง (agent_engine_app.py) ซึ่งมีอยู่ในเอกสารประกอบของเวิร์กช็อป เนื่องจากเครื่องมือการติดตั้งใช้งาน ADK ไปยัง Agent Engine โดยตรงอย่างเป็นทางการอาจยังอยู่ระหว่างการพัฒนา สคริปต์นี้จะจัดการการสร้างแพ็กเกจและการทำให้ใช้งานได้ของ Agent Orchestrator ซึ่งกำหนดค่าด้วยที่อยู่ Agent ระยะไกลที่จำเป็น)
เรียกใช้คำสั่งต่อไปนี้เพื่อทำให้ใช้งานได้เอเจนต์ Orchestrator ไปยัง Agent Engine ตรวจสอบว่าตัวแปรสภาพแวดล้อม REMOTE_AGENT_ADDRESSES (ซึ่งมี URL ของเอเจนต์ Planner, Platform และ Social ใน Cloud Run) ยังคงตั้งค่าอย่างถูกต้องจากส่วนก่อนหน้า
👉💻 เราจะติดตั้งใช้งานเอเจนต์ Orchestrate กับ Agent Engine (หมายเหตุ: นี่คือการติดตั้งใช้งานของฉันเอง ADK มี CLI ที่ช่วยในการติดตั้งใช้งาน ฉันจะอัปเดตส่วนนี้หลังจากที่ใช้ BYO-SA แล้ว)
cd ~/instavibe-bootstrap/agents/
. ~/instavibe-bootstrap/set_env.sh
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:service-$PROJECT_NUMBER@gcp-sa-aiplatform-re.iam.gserviceaccount.com" \
--role="roles/viewer"
source ~/instavibe-bootstrap/env/bin/activate
export PLATFORM_MPC_CLIENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep platform-mcp-client)
export PLANNER_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner-agent)
export SOCIAL_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep social-agent)
export REMOTE_AGENT_ADDRESSES=${PLANNER_AGENT_URL},${PLATFORM_MPC_CLIENT_URL},${SOCIAL_AGENT_URL}
sed -i "s|^\(O\?REMOTE_AGENT_ADDRESSES\)=.*|REMOTE_AGENT_ADDRESSES=${REMOTE_AGENT_ADDRESSES}|" ~/instavibe-bootstrap/agents/orchestrate/.env
adk deploy agent_engine \
--display_name "orchestrate-agent" \
--project $GOOGLE_CLOUD_PROJECT \
--region $GOOGLE_CLOUD_LOCATION \
--staging_bucket gs://$GOOGLE_CLOUD_PROJECT-agent-engine \
--trace_to_cloud \
--requirements_file orchestrate/requirements.txt \
orchestrate
เมื่อโฮสต์ Orchestrator ในแพลตฟอร์ม Agent Engine ที่มีการจัดการแล้ว เว็บแอปพลิเคชัน InstaVibe ของเราจึงต้องสื่อสารกับ Orchestrator เว็บแอปจะโทรไปยังปลายทาง Agent Engine จากระยะไกลแทนการโต้ตอบผ่าน ADK Dev UI

ก่อนอื่น เราต้องแก้ไขโค้ดของแอปพลิเคชัน InstaVibe เพื่อเริ่มต้นไคลเอ็นต์ Agent Engine โดยใช้รหัสที่ไม่ซ้ำกันของเอเจนต์ Orchestrator ที่เราติดตั้งใช้งาน คุณต้องใช้รหัสนี้เพื่อกำหนดเป้าหมายไปยังอินสแตนซ์ของ Agent ที่ถูกต้องบนแพลตฟอร์ม
👉📝 เปิด ~/instavibe-bootstrap/instavibe/introvertally.py แล้วแทนที่ #REPLACE ME initiate agent_engine ด้วยโค้ดต่อไปนี้ ซึ่งจะดึงรหัส Agent Engine จากตัวแปรสภาพแวดล้อม (ซึ่งเราจะตั้งค่าในอีกไม่นาน) และรับออบเจ็กต์ไคลเอ็นต์
ORCHESTRATE_AGENT_ID = os.environ.get('ORCHESTRATE_AGENT_ID')
agent_engine = agent_engines.get(ORCHESTRATE_AGENT_ID)
โฟลว์ของผู้ใช้ที่เราวางแผนไว้ใน InstaVibe มีการโต้ตอบกับเอเจนต์ 2 ครั้ง ครั้งแรกคือการสร้างแพ็กเกจที่แนะนำ และครั้งที่ 2 คือการขอให้ผู้ใช้ยืนยันก่อนที่เอเจนต์จะโพสต์กิจกรรมไปยังแพลตฟอร์มจริง
เนื่องจากตอนนี้เว็บแอปพลิเคชัน InstaVibe (ทำงานใน Cloud Run) และ Agent Orchestrator (ทำงานใน Agent Engine) เป็นบริการแยกกัน เว็บแอปจึงต้องทำการเรียกใช้ระยะไกลไปยังปลายทาง Agent Engine เพื่อโต้ตอบกับ Agent
👉📝 มาอัปเดตโค้ดที่ทำการเรียกครั้งแรกเพื่อสร้างคำแนะนำแผนกัน ในไฟล์ introvertally.py เดียวกัน ให้แทนที่ #REPLACE ME Query remote agent get plan ด้วยข้อมูลโค้ดต่อไปนี้ ซึ่งใช้ไคลเอ็นต์ agent_engine เพื่อส่งคำขอของผู้ใช้
agent_engine.stream_query(
user_id=user_id,
message=prompt_message,
)
👉📝 จากนั้นให้อัปเดตรหัสที่จัดการการยืนยันของผู้ใช้ (เช่น เมื่อผู้ใช้คลิก "ยืนยันแพ็กเกจ") การดำเนินการนี้จะส่งข้อความติดตามผลไปยังการสนทนาเดียวกันใน Agent Engine โดยสั่งให้ Orchestrator ดำเนินการโพสต์กิจกรรมต่อไป (ซึ่งจะมอบหมายให้ตัวแทนการผสานรวมแพลตฟอร์ม) แทนที่ #REPLACE ME Query remote agent for confirmation เพื่อยืนยันใน introvertally.py ด้วย
agent_engine.stream_query(
user_id=agent_session_user_id,
message=prompt_message,
)
เส้นทางของเว็บแอปพลิเคชันต้องมีสิทธิ์เข้าถึงฟังก์ชันเหล่านี้ ตรวจสอบว่าได้นำเข้าฟังก์ชันที่จำเป็นจาก introvertally.py ในไฟล์เส้นทาง Flask แล้ว
👉📝 ใน cd ~/instavibe-bootstrap/instavibe/ally_routes.py เราจะชี้ไปยังการแทนที่อินสแตนซ์ # REPLACE ME TO ADD IMPORT ด้วยรายการต่อไปนี้ก่อน
from introvertally import call_agent_for_plan, post_plan_event
👉📝 เพิ่มฟีเจอร์ต้นแบบลงใน InstaVibe ใน ~/instavibe-bootstrap/instavibe/templates/base.html ให้แทนที่ <!–REPLACE_ME_LINK_TO_INTROVERT_ALLY–> ด้วยข้อความต่อไปนี้
<li class="nav-item">
<a class="nav-link" href="{{ url_for('ally.introvert_ally_page') }}">Introvert Ally</a>
</li>
ก่อนที่จะนำแอป InstaVibe กลับมาใช้งานได้ เราต้องมี Resource ID ของเอเจนต์ Orchestrator ที่เราติดตั้งใช้งานใน Agent Engine
ปัจจุบันการดึงข้อมูลนี้แบบเป็นโปรแกรมผ่าน gcloud อาจถูกจำกัด ดังนั้นเราจะใช้สคริปต์ Python ตัวช่วย (temp-endpoint.py ที่ให้ไว้ในเวิร์กช็อป) เพื่อดึงข้อมูลรหัสและจัดเก็บไว้ในตัวแปรสภาพแวดล้อม
👉💻 เรียกใช้คำสั่งต่อไปนี้เพื่อรันสคริปต์ สคริปต์จะบันทึกรหัสปลายทางของ Agent Engine และให้สิทธิ์ที่จำเป็นแก่บัญชีบริการเริ่มต้นของ Agent Engine (หมายเหตุ: สคริปต์ได้รับการกำหนดค่าให้ใช้บัญชีบริการเริ่มต้นเนื่องจากปัจจุบันผู้ใช้ยังแก้ไขไม่ได้)
. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap/instavibe/
source ~/instavibe-bootstrap/env/bin/activate
python temp-endpoint.py
export ORCHESTRATE_AGENT_ID=$(cat temp_endpoint.txt)
echo "ORCHESTRATE_AGENT_ID set to: ${ORCHESTRATE_AGENT_ID}"

สุดท้าย เราต้องนำเว็บแอปพลิเคชัน InstaVibe ไปใช้งานอีกครั้งด้วยโค้ดที่อัปเดตแล้วและตัวแปรสภาพแวดล้อม ORCHESTRATE_AGENT_ID ใหม่เพื่อให้ทราบวิธีเชื่อมต่อกับเอเจนต์ที่ทำงานใน Agent Engine
👉💻 คำสั่งต่อไปนี้จะสร้างอิมเมจแอปพลิเคชัน InstaVibe ใหม่และทําให้เวอร์ชันใหม่ใช้งานได้ใน Cloud Run
. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap/instavibe/
export IMAGE_TAG="latest"
export APP_FOLDER_NAME="instavibe"
export IMAGE_NAME="instavibe-webapp"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="instavibe"
echo "Building ${APP_FOLDER_NAME} webapp image..."
gcloud builds submit . \
--tag=${IMAGE_PATH} \
--project=${PROJECT_ID}
echo "Deploying ${SERVICE_NAME} to Cloud Run..."
gcloud run deploy ${SERVICE_NAME} \
--image=${IMAGE_PATH} \
--platform=managed \
--region=${REGION} \
--allow-unauthenticated \
--set-env-vars="SPANNER_INSTANCE_ID=${SPANNER_INSTANCE_ID}" \
--set-env-vars="SPANNER_DATABASE_ID=${SPANNER_DATABASE_ID}" \
--set-env-vars="APP_HOST=0.0.0.0" \
--set-env-vars="APP_PORT=8080" \
--set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
--set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
--set-env-vars="GOOGLE_MAPS_API_KEY=${GOOGLE_MAPS_API_KEY}" \
--set-env-vars="ORCHESTRATE_AGENT_ID=${ORCHESTRATE_AGENT_ID}" \
--project=${PROJECT_ID} \
--min-instances=1 \
--cpu=2 \
--memory=2Gi
เมื่อการติดตั้งใช้งานขั้นสุดท้ายเสร็จสมบูรณ์แล้ว ให้ไปที่ URL ของแอปพลิเคชัน InstaVibe ในแท็บเบราว์เซอร์อื่น
การทดสอบประสบการณ์การใช้งาน InstaVibe ที่ทำงานด้วยระบบ AI อย่างเต็มรูปแบบ
ฟีเจอร์ "InstaVibe Ally" พร้อมให้บริการแล้ว โดยขับเคลื่อนด้วยระบบแบบหลาย Agent ที่ประสานงานผ่าน Vertex AI Agent Engine และสื่อสารผ่าน A2A

คลิก "InstaVibe Ally" แล้วขอให้วางแผนกิจกรรม

สังเกตบันทึกกิจกรรมทางด้านขวาขณะที่ตัวแทนดำเนินการ (อาจใช้เวลา 90-120 วินาที) เมื่อแผนปรากฏขึ้น ให้ตรวจสอบและคลิก "ยืนยันแผนนี้" เพื่อดำเนินการโพสต์ต่อ

ตอนนี้ Orchestrator จะสั่งให้ตัวแทนแพลตฟอร์มสร้างโพสต์และกิจกรรมภายใน InstaVibe 
โปรดตรวจสอบหน้าแรกของ InstaVibe เพื่อดูโพสต์และกิจกรรมใหม่ 
หน้ากิจกรรมจะแสดงรายละเอียดที่ตัวแทนสร้างขึ้น

การวิเคราะห์ประสิทธิภาพด้วย Cloud Trace
คุณอาจสังเกตเห็นว่ากระบวนการนี้ใช้เวลาสักครู่ Vertex AI Agent Engine ผสานรวมกับ Cloud Trace ซึ่งช่วยให้เราวิเคราะห์เวลาในการตอบสนองของระบบหลาย Agent ได้
ไปที่การติดตามในคอนโซล Google Cloud แล้วเลือก agent_run[orchestrate_agent] ใน Span คุณจะเห็น Span 2-3 ช่วง ให้คลิก Span นั้น

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

ยินดีด้วย คุณสร้าง ติดตั้งใช้งาน และทดสอบระบบ AI แบบหลาย Agent ที่ซับซ้อนโดยใช้ ADK, A2A, MCP และบริการของ Google Cloud ได้สำเร็จแล้ว คุณได้จัดการการจัดการเป็นกลุ่มของ Agent การใช้เครื่องมือ การจัดการสถานะ และการทำให้ใช้งานได้บนระบบคลาวด์ ซึ่งเป็นการสร้างฟีเจอร์ที่ทำงานได้จริงซึ่งทำงานด้วยระบบ AI สำหรับ InstaVibe เก่งมาก คุณเข้าร่วมเวิร์กช็อปจนจบแล้ว
13. ล้าง
คุณควรลบทรัพยากรที่เราสร้างขึ้นในเวิร์กช็อปนี้เพื่อหลีกเลี่ยงการเรียกเก็บเงินอย่างต่อเนื่องในบัญชี Google Cloud คำสั่งต่อไปนี้จะช่วยคุณนำอินสแตนซ์ Spanner, บริการ Cloud Run, ที่เก็บ Artifact Registry, คีย์ API, Vertex AI Agent Engine และสิทธิ์ IAM ที่เกี่ยวข้องออก
สำคัญ
- ตรวจสอบว่าคุณเรียกใช้คำสั่งเหล่านี้ในโปรเจ็กต์ Google Cloud เดียวกันกับที่ใช้สำหรับเวิร์กช็อป
- หากปิดเทอร์มินัล Cloud Shell แล้ว ระบบอาจไม่ได้ตั้งค่าตัวแปรสภาพแวดล้อมบางอย่าง เช่น $PROJECT_ID, $SPANNER_INSTANCE_ID เป็นต้น คุณจะต้องส่งออกอีกครั้งเหมือนกับที่ทำในระหว่างการตั้งค่าเวิร์กช็อป หรือแทนที่ตัวแปรในคำสั่งด้านล่างด้วยค่าจริง
- คำสั่งเหล่านี้จะลบทรัพยากรของคุณอย่างถาวร โปรดตรวจสอบอีกครั้งก่อนเรียกใช้หากคุณมีข้อมูลสำคัญอื่นๆ ในโปรเจ็กต์นี้
👉💻 เรียกใช้สคริปต์ต่อไปนี้เพื่อล้างข้อมูล
รีเซ็ตตัวแปรสภาพแวดล้อม
. ~/instavibe-bootstrap/set_env.sh
ลบ Agent Engine:
cd ~/instavibe-bootstrap/utils
source ~/instavibe-bootstrap/env/bin/activate
export ORCHESTRATE_AGENT_ID=$(cat ~/instavibe-bootstrap/instavibe/temp_endpoint.txt)
echo "ORCHESTRATE_AGENT_ID set to: ${ORCHESTRATE_AGENT_ID}"
python remote_delete.py
deactivate
echo "Vertex AI Agent Engine deletion initiated."
ลบบริการ Cloud Run
# InstaVibe Web Application
gcloud run services delete instavibe --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet
# MCP Tool Server
gcloud run services delete mcp-tool-server --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet
# Planner Agent (A2A Server)
gcloud run services delete planner-agent --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet
# Platform MCP Client Agent (A2A Server)
gcloud run services delete platform-mcp-client --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet
# Social Agent (A2A Server)
gcloud run services delete social-agent --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet
echo "Cloud Run services deletion initiated."
หยุดและนำคอนเทนเนอร์ Docker ของ A2A Inspector ออก
docker rm --force a2a-inspector
ลบอินสแตนซ์ Spanner
echo "Deleting Spanner instance: ${SPANNER_INSTANCE_ID}..."
gcloud spanner instances delete ${SPANNER_INSTANCE_ID} --project=${PROJECT_ID} --quiet
echo "Spanner instance deletion initiated."
ลบที่เก็บ Artifact Registry
echo "Deleting Artifact Registry repository: ${REPO_NAME}..."
gcloud artifacts repositories delete ${REPO_NAME} --location=${REGION} --project=${PROJECT_ID} --quiet
echo "Artifact Registry repository deletion initiated."
นำบทบาทออกจากบัญชีบริการ
echo "Removing roles from service account: $SERVICE_ACCOUNT_NAME in project $PROJECT_ID"
# Remove Project-level roles for default service account
gcloud projects remove-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/spanner.admin"
gcloud projects remove-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/spanner.databaseUser"
gcloud projects remove-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/artifactregistry.admin"
gcloud projects remove-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/cloudbuild.builds.editor"
gcloud projects remove-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/run.admin"
gcloud projects remove-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/iam.serviceAccountUser"
gcloud projects remove-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/aiplatform.user"
gcloud projects remove-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/logging.logWriter"
gcloud projects remove-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/logging.viewer"
echo "All specified roles have been removed."
ลบไฟล์เวิร์กช็อปในเครื่อง
echo "Removing local workshop directory ~/instavibe-bootstrap..."
rm -rf ~/instavibe-bootstrap
rm -rf ~/a2a-inspector
rm -f ~/mapkey.txt
rm -f ~/project_id.txt
echo "Local directory removed."
