เริ่มต้นใช้งานโปรโตคอล Agent2Agent (A2A): การโต้ตอบระหว่างผู้ช่วยซื้อและตัวแทนผู้ขายระยะไกลใน Cloud Run และ Agent Engine

1. 📖 บทนำ

b013ad6b246401eb.png

โปรโตคอล Agent2Agent (A2A) ออกแบบมาเพื่อกำหนดมาตรฐานการสื่อสารระหว่างเอเจนต์ AI โดยเฉพาะอย่างยิ่งสำหรับเอเจนต์ที่ใช้งานในระบบภายนอก ก่อนหน้านี้ โปรโตคอลดังกล่าวได้รับการสร้างขึ้นสำหรับเครื่องมือที่เรียกว่าModel Context Protocol (MCP) ซึ่งเป็นมาตรฐานใหม่ในการเชื่อมต่อ LLM กับข้อมูลและทรัพยากร A2A พยายามเสริม MCP โดยที่ A2A มุ่งเน้นไปที่ปัญหาที่แตกต่างกัน ในขณะที่ MCP มุ่งเน้นไปที่การลดความซับซ้อนในการเชื่อมต่อตัวแทนกับเครื่องมือและข้อมูล A2A มุ่งเน้นไปที่วิธีช่วยให้ตัวแทนทำงานร่วมกันในรูปแบบที่เป็นธรรมชาติ ซึ่งช่วยให้ตัวแทนสื่อสารในฐานะตัวแทน (หรือในฐานะผู้ใช้) แทนที่จะเป็นเครื่องมือได้ เช่น เปิดใช้การสื่อสารแบบไปมาเมื่อคุณต้องการสั่งซื้อสินค้า

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

83b1a03588b90b68.png

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

A2A ใช้หลักการไคลเอ็นต์-เซิร์ฟเวอร์ ขั้นตอน A2A ทั่วไปที่คุณจะเห็นในบทแนะนำนี้มีดังนี้

aa6c8bc5b5df73f1.jpeg

  1. ไคลเอ็นต์ A2A จะค้นหาการ์ดตัวแทนเซิร์ฟเวอร์ A2A ที่เข้าถึงได้ทั้งหมดก่อน แล้วใช้ข้อมูลดังกล่าวเพื่อสร้างไคลเอ็นต์การเชื่อมต่อ
  2. เมื่อจำเป็น ไคลเอ็นต์ A2A จะส่งข้อความไปยังเซิร์ฟเวอร์ A2A และเซิร์ฟเวอร์จะประเมินข้อความนี้เป็นงานที่ต้องดำเนินการให้เสร็จ หากกำหนดค่า URL ผู้รับการแจ้งเตือนแบบพุชในไคลเอ็นต์ A2A และเซิร์ฟเวอร์ A2A รองรับ เซิร์ฟเวอร์จะสามารถเผยแพร่สถานะความคืบหน้าของงานไปยังปลายทางการรับในไคลเอ็นต์ได้ด้วย
  3. หลังจากงานเสร็จสมบูรณ์แล้ว เซิร์ฟเวอร์ A2A จะส่งอาร์ติแฟกต์การตอบกลับไปยังไคลเอ็นต์ A2A

ใน Codelab นี้ คุณจะได้ใช้แนวทางแบบทีละขั้นตอนดังนี้

  1. เตรียมโปรเจ็กต์ Google Cloud
  2. ตั้งค่าไดเรกทอรีงานสำหรับสภาพแวดล้อมการเขียนโค้ด
  3. ติดตั้งใช้งานเอเจนต์เบอร์เกอร์ใน Cloud Run
  4. ติดตั้งใช้งานเอเจนต์พิซซ่าใน Cloud Run
  5. ติดตั้งใช้งานผู้ช่วยซื้อใน Agent Engine
  6. โต้ตอบกับผู้ช่วยจัดซื้อผ่านอินเทอร์เฟซในพื้นที่

ภาพรวมสถาปัตยกรรม

คุณจะติดตั้งใช้งานสถาปัตยกรรมบริการต่อไปนี้

9cfc4582f2d8b6f3.jpeg

คุณจะติดตั้งใช้งาน 2 บริการที่จะทําหน้าที่เป็นเซิร์ฟเวอร์ A2A, เอเจนต์ Burger ( ขับเคลื่อนโดยเฟรมเวิร์กเอเจนต์ CrewAI) และเอเจนต์ Pizza ( ขับเคลื่อนโดยเฟรมเวิร์กเอเจนต์ Langgraph) ผู้ใช้จะโต้ตอบกับผู้ช่วยจัดซื้อโดยตรงเท่านั้น ซึ่งจะทํางานโดยใช้เฟรมเวิร์ก Agent Development Kit (ADK) ที่จะทําหน้าที่เป็นไคลเอ็นต์ A2A

เอเจนต์แต่ละรายจะมีสภาพแวดล้อมและการติดตั้งใช้งานของตนเอง

ข้อกำหนดเบื้องต้น

  • คุ้นเคยกับการทำงานด้วย Python
  • ความเข้าใจเกี่ยวกับสถาปัตยกรรม Full-Stack พื้นฐานโดยใช้บริการ HTTP

สิ่งที่คุณจะได้เรียนรู้

  • โครงสร้างหลักของเซิร์ฟเวอร์ A2A
  • โครงสร้างหลักของไคลเอ็นต์ A2A
  • การติดตั้งใช้งานบริการตัวแทนใน Cloud Run
  • การติดตั้งใช้งานบริการ Agent ใน Agent Engine
  • วิธีที่ไคลเอ็นต์ A2A เชื่อมต่อกับเซิร์ฟเวอร์ A2A
  • โครงสร้างคำขอและการตอบกลับในการเชื่อมต่อที่ไม่ใช่การสตรีม

สิ่งที่คุณต้องมี

  • เว็บเบราว์เซอร์ Chrome
  • บัญชี Gmail
  • โปรเจ็กต์ระบบคลาวด์ที่เปิดใช้บัญชีสำหรับการเรียกเก็บเงิน

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

2. 🚀 เตรียมการตั้งค่าการพัฒนาเวิร์กช็อป

ขั้นตอนที่ 1: เลือกโปรเจ็กต์ที่ใช้งานอยู่ใน Cloud Console

ในคอนโซล Google Cloud ให้เลือกหรือสร้างโปรเจ็กต์ Google Cloud ในหน้าตัวเลือกโปรเจ็กต์ (ดูส่วนบนซ้ายของคอนโซล)

78c981437f90248.png

คลิกที่ไอคอนดังกล่าว แล้วคุณจะเห็นรายการโปรเจ็กต์ทั้งหมดของคุณดังตัวอย่างนี้

2f5247dd825b808c.png

ค่าที่ระบุโดยกรอบสีแดงคือรหัสโปรเจ็กต์ และค่านี้จะใช้ตลอดทั้งบทแนะนำ

ตรวจสอบว่าได้เปิดใช้การเรียกเก็บเงินสำหรับโปรเจ็กต์ Cloud แล้ว หากต้องการตรวจสอบ ให้คลิกไอคอนแฮมเบอร์เกอร์ ☰ ในแถบด้านซ้ายบน ซึ่งจะแสดงเมนูการนำทางและค้นหาเมนูการเรียกเก็บเงิน

db49b5267c00cc33.png

หากเห็นข้อความว่าลิงก์"บัญชีสำหรับการเรียกเก็บเงินของ Google Cloud Platform เวอร์ชันทดลองใช้งาน" แล้ว แสดงว่าโปรเจ็กต์พร้อมใช้งานสำหรับบทแนะนำนี้ หากไม่ ให้กลับไปที่จุดเริ่มต้นของบทแนะนำนี้และแลกรับบัญชีการเรียกเก็บเงิน

e44b767990aa6aab.png

ขั้นตอนที่ 2: ทำความคุ้นเคยกับ Cloud Shell

คุณจะใช้ Cloud Shell ในบทแนะนำส่วนใหญ่ คลิก "เปิดใช้งาน Cloud Shell" ที่ด้านบนของคอนโซล Google Cloud หากระบบแจ้งให้คุณให้สิทธิ์ ให้คลิกให้สิทธิ์

1829c3759227c19b.png

b8fe7df5c3c2b919.png

เมื่อเชื่อมต่อกับ Cloud Shell แล้ว เราจะต้องตรวจสอบว่า Shell ( หรือเทอร์มินัล) ได้รับการตรวจสอบสิทธิ์ด้วยบัญชีของเราแล้วหรือไม่

gcloud auth list

หากเห็น Gmail ส่วนตัวของคุณตามตัวอย่างเอาต์พุตด้านล่าง แสดงว่าทุกอย่างเรียบร้อยดี

Credentialed Accounts

ACTIVE: *
ACCOUNT: alvinprayuda@gmail.com

To set the active account, run:
    $ gcloud config set account `ACCOUNT`

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

จากนั้นเรายังต้องตรวจสอบด้วยว่ามีการกำหนดค่า Shell ให้กับ PROJECT ID ที่ถูกต้องที่คุณมีอยู่แล้วหรือไม่ หากคุณเห็นว่ามีค่าอยู่ภายใน ( ) ก่อนไอคอน $ ในเทอร์มินัล ( ในภาพหน้าจอด้านล่าง ค่าคือ "a2a-agent-engine") ค่านี้จะแสดงโปรเจ็กต์ที่กำหนดค่าไว้สำหรับเซสชัน Shell ที่ใช้งานอยู่

fadd80f0da3b906.png

หากค่าที่แสดงถูกต้องอยู่แล้ว คุณก็ข้ามคำสั่งถัดไปได้ แต่หากไม่ถูกต้องหรือไม่มี ให้เรียกใช้คำสั่งต่อไปนี้

gcloud config set project <YOUR_PROJECT_ID>

จากนั้นโคลนไดเรกทอรีการทำงานของเทมเพลตสำหรับโค้ดแล็บนี้จาก Github โดยเรียกใช้คำสั่งต่อไปนี้ ซึ่งจะสร้างไดเรกทอรีการทำงานในไดเรกทอรี purchasing-concierge-a2a

git clone https://github.com/alphinside/purchasing-concierge-intro-a2a-codelab-starter.git purchasing-concierge-a2a

ขั้นตอนที่ 3: ทำความคุ้นเคยกับ Cloud Shell Editor และตั้งค่าไดเรกทอรีการทำงานของแอปพลิเคชัน

ตอนนี้เราสามารถตั้งค่าโปรแกรมแก้ไขโค้ดเพื่อเขียนโค้ดได้แล้ว เราจะใช้ Cloud Shell Editor สำหรับการดำเนินการนี้

คลิกปุ่มเปิดตัวแก้ไข ซึ่งจะเปิด Cloud Shell Editor b16d56e4979ec951.png

หลังจากนั้น ให้ไปที่ส่วนบนสุดของ Cloud Shell Editor แล้วคลิกFile->Open Folder ค้นหาไดเรกทอรีชื่อผู้ใช้ แล้วค้นหาไดเรกทอรี purchasing-concierge-a2a จากนั้นคลิกปุ่ม OK ซึ่งจะทำให้ไดเรกทอรีที่เลือกเป็นไดเรกทอรีการทำงานหลัก ในตัวอย่างนี้ ชื่อผู้ใช้คือ alvinprayuda ดังนั้นเส้นทางไดเรกทอรีจึงแสดงอยู่ด้านล่าง

2c53696f81d805cc.png

253b472fa1bd752e.png

ตอนนี้ Cloud Shell Editor ควรมีลักษณะดังนี้

aedd0725db87717e.png

ตอนนี้ให้เปิดเทอร์มินัลสำหรับเครื่องมือแก้ไข โดยคลิกเทอร์มินัล -> เทอร์มินัลใหม่ในแถบเมนู หรือใช้ Ctrl + Shift + C ซึ่งจะเปิดหน้าต่างเทอร์มินัลที่ส่วนล่างของเบราว์เซอร์

f8457daf0bed059e.jpeg

เทอร์มินัลที่ใช้งานอยู่ปัจจุบันควรอยู่ในไดเรกทอรีการทำงาน purchasing-concierge-a2a เราจะใช้ Python 3.12 ในโค้ดแล็บนี้ และจะใช้ตัวจัดการโปรเจ็กต์ Python ของ uv เพื่อลดความจำเป็นในการสร้างและจัดการเวอร์ชัน Python และสภาพแวดล้อมเสมือน แพ็กเกจ uv ได้รับการติดตั้งล่วงหน้าใน Cloud Shell แล้ว

เรียกใช้คำสั่งนี้เพื่อติดตั้งการอ้างอิงที่จำเป็นลงในสภาพแวดล้อมเสมือนในไดเรกทอรี .venv

uv sync --frozen

ดูการอ้างอิงที่ประกาศไว้สำหรับบทแนะนำนี้ได้ใน pyproject.toml ซึ่งก็คือ a2a-sdk, google-adk, and gradio

ตอนนี้เราจะต้องเปิดใช้ API ที่จำเป็นผ่านคำสั่งที่แสดงด้านล่าง อาจใช้เวลาสักครู่

gcloud services enable aiplatform.googleapis.com \
                       run.googleapis.com \
                       cloudbuild.googleapis.com \
                       cloudresourcemanager.googleapis.com

เมื่อเรียกใช้คำสั่งสำเร็จ คุณควรเห็นข้อความที่คล้ายกับข้อความที่แสดงด้านล่าง

Operation "operations/..." finished successfully.

3. 🚀 การติดตั้งใช้งาน Agent ผู้ขายระยะไกลของเซิร์ฟเวอร์ A2A ใน Cloud Run

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

e91777eecfbae4f7.png

4. 🚀 การติดตั้งใช้งานเอเจนต์ผู้ขายเบอร์เกอร์ - เซิร์ฟเวอร์ A2A

ซอร์สโค้ดของ Burger Agent อยู่ในไดเรกทอรี remote_seller_agents/burger_agent

ไฟล์ทั้งหมดที่อยู่ในไดเรกทอรี remote_seller_agents/burger_agent เพียงพอที่จะนำ Agent ไปใช้งานใน Cloud Run เพื่อให้เข้าถึงได้ในรูปแบบบริการ เรียกใช้คำสั่งต่อไปนี้เพื่อติดตั้งใช้งาน

gcloud run deploy burger-agent \
    --source remote_seller_agents/burger_agent \
    --port=8080 \
    --allow-unauthenticated \
    --min 1 \
    --region us-central1 \
    --update-env-vars GOOGLE_CLOUD_LOCATION=us-central1 \
    --update-env-vars GOOGLE_CLOUD_PROJECT={your-project-id}

หากระบบแจ้งว่ากำลังจะสร้างที่เก็บคอนเทนเนอร์สำหรับการติดตั้งใช้งานจากแหล่งที่มา ให้ตอบว่า Y หลังจากติดตั้งใช้งานสำเร็จแล้ว ระบบจะแสดงบันทึกดังนี้

Service [burger-agent] revision [burger-agent-xxxxx-xxx] has been deployed and is serving 100 percent of traffic.
Service URL: https://burger-agent-xxxxxxxxx.us-central1.run.app

ส่วน xxxx ในที่นี้จะเป็นตัวระบุที่ไม่ซ้ำกันเมื่อเราเปิดตัวบริการ

เปิดแท็บเบราว์เซอร์ใหม่ แล้วไปที่เส้นทาง https://burger-agent-xxxxxxxxx.us-central1.run.app/.well-known/agent.json ของบริการตัวแทน Burger ที่ติดตั้งใช้งานเหล่านั้นผ่านเบราว์เซอร์ นี่คือ URL สำหรับเข้าถึงการ์ด Agent เซิร์ฟเวอร์ A2A ที่ติดตั้งใช้งาน

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

72fdf3f52b5e8313.png

นี่คือข้อมูลการ์ดตัวแทนร้านเบอร์เกอร์ที่ควรเข้าถึงได้เพื่อวัตถุประสงค์ในการค้นพบ

โปรดทราบว่าค่า url ยังคงตั้งค่าเป็น http://0.0.0.0:8080/ ที่นี่ url ค่านี้ควรเป็นข้อมูลหลักสำหรับไคลเอ็นต์ A2A ในการส่งข้อความจากภายนอก แต่มีการกำหนดค่าไม่ถูกต้อง

เราต้องอัปเดตค่านี้เป็น URL ของบริการตัวแทนเบอร์เกอร์โดยการเพิ่มตัวแปรสภาพแวดล้อมเพิ่มเติม HOST_OVERRIDE

การอัปเดตค่า URL ของตัวแทน Burger ในการ์ดตัวแทนผ่านตัวแปรสภาพแวดล้อม

หากต้องการเพิ่ม HOST_OVERRIDE ในบริการของตัวแทนเบอร์เกอร์ ให้ทำตามขั้นตอนต่อไปนี้

  1. ค้นหา Cloud Run ในแถบค้นหาที่ด้านบนของคอนโซลระบบคลาวด์

1adde569bb345b48.png

  1. คลิกบริการ Cloud Run burger-agent ที่ติดตั้งใช้งานก่อนหน้านี้

9091c12526fb7f41.png

  1. คัดลอก URL ของเบอร์เกอร์เซอร์วิส แล้วคลิกแก้ไขและติดตั้งใช้งานรีวิชันใหม่

2701da8b124793b9.png

  1. จากนั้นคลิกส่วนตัวแปรและข้อมูลลับ

31ea00e12134d74d.png

  1. หลังจากนั้น ให้คลิกเพิ่มตัวแปร แล้วตั้งค่า HOST_OVERRIDE เป็น URL ของบริการ ( URL ที่มีรูปแบบ https://burger-agent-xxxxxxxxx.us-central1.run.app)

52b382da7cf33cd5.png

  1. สุดท้าย ให้คลิกปุ่มติดตั้งใช้งานเพื่อติดตั้งใช้งานบริการอีกครั้ง

11464f4a51ffe54.png

เมื่อเข้าถึงการ์ดเอเจนต์ Burger-Agent อีกครั้งในเบราว์เซอร์ https://burger-agent-xxxxxxxxx.us-central1.run.app/.well-known/agent.json ค่า url จะได้รับการกำหนดค่าอย่างถูกต้องแล้ว

2ed7ebcb530f070a.png

5. 🚀 การติดตั้งใช้งานเอเจนต์ผู้ขายพิซซ่า - เซิร์ฟเวอร์ A2A

ในทำนองเดียวกัน ซอร์สโค้ดของตัวแทนจำหน่ายพิซซ่าจะอยู่ในไดเรกทอรี remote_seller_agents/pizza_agent

เช่นเดียวกับขั้นตอนการติดตั้งใช้งานเอเจนต์เบอร์เกอร์ก่อนหน้านี้ ไฟล์ทั้งหมดที่อยู่ในไดเรกทอรี remote_seller_agents/pizza_agent ก็เพียงพอที่จะติดตั้งใช้งานเอเจนต์ใน Cloud Run เพื่อให้เข้าถึงได้ในรูปแบบบริการ เรียกใช้คำสั่งต่อไปนี้เพื่อติดตั้งใช้งาน

gcloud run deploy pizza-agent \
    --source remote_seller_agents/pizza_agent \
    --port=8080 \
    --allow-unauthenticated \
    --min 1 \
    --region us-central1 \
    --update-env-vars GOOGLE_CLOUD_LOCATION=us-central1 \
    --update-env-vars GOOGLE_CLOUD_PROJECT={your-project-id}

หลังจากติดตั้งใช้งานสำเร็จแล้ว ระบบจะแสดงบันทึกดังนี้

Service [pizza-agent] revision [pizza-agent-xxxxx-xxx] has been deployed and is serving 100 percent of traffic.
Service URL: https://pizza-agent-xxxxxxxxx.us-central1.run.app

ส่วน xxxx ในที่นี้จะเป็นตัวระบุที่ไม่ซ้ำกันเมื่อเราเปิดตัวบริการ

ในกรณีเดียวกันนี้ เมื่อคุณพยายามไปที่เส้นทาง https://pizza-agent-xxxxxxxxx.us-central1.run.app/.well-known/agent.json ของบริการตัวแทนพิซซ่าที่ติดตั้งใช้งานเหล่านั้นผ่านเบราว์เซอร์เพื่อเข้าถึงการ์ดตัวแทนเซิร์ฟเวอร์ A2A ค่า url ของตัวแทนพิซซ่าในการ์ดตัวแทนยังไม่ได้กำหนดค่าอย่างถูกต้อง นอกจากนี้ เรายังต้องเพิ่ม HOST_OVERRIDE ลงในตัวแปรสภาพแวดล้อมด้วย

การอัปเดตค่า URL ของตัวแทนพิซซ่าในการ์ดตัวแทนผ่านตัวแปรสภาพแวดล้อม

หากต้องการเพิ่ม HOST_OVERRIDE ลงในบริการตัวแทนพิซซ่า ให้ทำตามขั้นตอนต่อไปนี้

  1. ค้นหา Cloud Run ในแถบค้นหาที่ด้านบนของคอนโซลระบบคลาวด์

1adde569bb345b48.png

  1. คลิกบริการ Cloud Run pizza-agent ที่ติดตั้งใช้งานก่อนหน้านี้

5743b0aa0555741f.png

  1. คลิกแก้ไขและทำให้การแก้ไขใหม่ใช้งานได้

d60ba267410183be.png

  1. คัดลอก URL ของบริการพิซซ่า แล้วคลิกส่วนตัวแปรและข้อมูลลับ

618e9da2f94ed415.png

  1. หลังจากนั้น ให้คลิกเพิ่มตัวแปร แล้วตั้งค่า HOST_OVERRIDE เป็น URL ของบริการ ( URL ที่มีรูปแบบ https://pizza-agent-xxxxxxxxx.us-central1.run.app)

214a6eb98f877e65.png

  1. สุดท้าย ให้คลิกปุ่มติดตั้งใช้งานเพื่อติดตั้งใช้งานบริการอีกครั้ง

11464f4a51ffe54.png

ตอนนี้เมื่อคุณเข้าถึงการ์ดเอเจนต์ pizza-agent อีกครั้งในเบราว์เซอร์ https://pizza-agent-xxxxxxxxx.us-central1.run.app/.well-known/agent.json ระบบจะกำหนดค่า url อย่างถูกต้องแล้ว

c37b26ec80c821b6.png

ตอนนี้เราได้ทำให้บริการเบอร์เกอร์และพิซซ่าใช้งานได้ใน Cloud Run เรียบร้อยแล้ว

6. 🚀 การติดตั้งใช้งาน Concierge การซื้อ - เครื่องมือไคลเอ็นต์ A2A ไปยังเครื่องมือ Agent

ในขั้นตอนนี้ เราจะติดตั้งใช้งานเอเจนต์ผู้ช่วยซื้อ ตัวแทนนี้คือตัวแทนที่เราจะโต้ตอบด้วย

c4a8e7a3d18b1ef.png

ซอร์สโค้ดของเอเจนต์ผู้ช่วยซื้อของเราอยู่ในไดเรกทอรี purchasing_concierge คุณตรวจสอบการเริ่มต้นตัวแทนได้ในสคริปต์ purchasing_concierge/purchasing_agent.py

ทำตามขั้นตอนต่อไปนี้เพื่อติดตั้งใช้งาน

  1. ก่อนอื่นเราต้องสร้างที่เก็บข้อมูลการทดลองใช้ใน Cloud Storage
gcloud storage buckets create gs://purchasing-concierge-{your-project-id} --location=us-central1
  1. ตอนนี้เราต้องเตรียมตัวแปร .env ก่อน โดยคัดลอก .env.example ไปยังไฟล์ .env
cp .env.example .env
  1. ตอนนี้ให้เปิดไฟล์ .env แล้วคุณจะเห็นเนื้อหาต่อไปนี้
GOOGLE_GENAI_USE_VERTEXAI=TRUE
GOOGLE_CLOUD_PROJECT={your-project-id}
GOOGLE_CLOUD_LOCATION=us-central1
STAGING_BUCKET=gs://purchasing-concierge-{your-project-id}
PIZZA_SELLER_AGENT_URL={your-pizza-agent-url}
BURGER_SELLER_AGENT_URL={your-burger-agent-url}
AGENT_ENGINE_RESOURCE_NAME={your-agent-engine-resource-name}

เอเจนต์นี้จะสื่อสารกับเอเจนต์เบอร์เกอร์และพิซซ่า ดังนั้นเราจึงต้องระบุข้อมูลเข้าสู่ระบบที่เหมาะสมสำหรับทั้ง 2 เอเจนต์ เราจะต้องอัปเดต PIZZA_SELLER_AGENT_URL และ BURGER_SELLER_AGENT_URL ด้วย URL ของ Cloud Run จากขั้นตอนก่อนหน้า

หากคุณลืมเรื่องนี้ โปรดไปที่คอนโซล Cloud Run พิมพ์ "Cloud Run" ในแถบค้นหาที่ด้านบนของคอนโซล แล้วคลิกขวาที่ไอคอน Cloud Run เพื่อเปิดในแท็บใหม่

1adde569bb345b48.png

คุณควรเห็นบริการตัวแทนผู้ขายระยะไกลที่เราเคยให้บริการก่อนหน้านี้ดังที่แสดงด้านล่าง

179e55cc095723a8.png

ตอนนี้หากต้องการดู URL สาธารณะของบริการเหล่านั้น ให้คลิกบริการใดบริการหนึ่ง แล้วระบบจะเปลี่ยนเส้นทางคุณไปยังหน้ารายละเอียดบริการ คุณดู URL ได้ที่ด้านบนข้างข้อมูลภูมิภาค

64c01403a92b1107.png

ตัวแปรสภาพแวดล้อมสุดท้ายควรมีลักษณะคล้ายกับตัวแปรนี้

GOOGLE_GENAI_USE_VERTEXAI=TRUE
GOOGLE_CLOUD_PROJECT={your-project-id}
GOOGLE_CLOUD_LOCATION=us-central1
STAGING_BUCKET=gs://purchasing-concierge-{your-project-id}
PIZZA_SELLER_AGENT_URL=https://pizza-agent-xxxxx.us-central1.run.app
BURGER_SELLER_AGENT_URL=https://burger-agent-xxxxx.us-central1.run.app
AGENT_ENGINE_RESOURCE_NAME={your-agent-engine-resource-name}
  1. ตอนนี้เราพร้อมที่จะติดตั้งใช้งานเอเจนต์ผู้ช่วยด้านการซื้อแล้ว เราจะติดตั้งใช้งานในเครื่องมือของเอเจนต์ และโค้ดการติดตั้งใช้งานจะอยู่ในสคริปต์ deploy_to_agent_engine.py

เราสามารถติดตั้งใช้งานได้โดยการเรียกใช้สคริปต์ต่อไปนี้

uv run deploy_to_agent_engine.py

หลังจากติดตั้งใช้งานสำเร็จแล้ว ระบบจะแสดงบันทึกดังนี้ โดยจะแสดงชื่อทรัพยากร Agent Engine เป็น "projects/xxxx/locations/us-central1/reasoningEngines/yyyy"

AgentEngine created. Resource name: projects/xxxx/locations/us-central1/reasoningEngines/yyyy
To use this AgentEngine in another session:
agent_engine = vertexai.agent_engines.get('projects/xxxx/locations/us-central1/reasoningEngines/yyyy)
Deployed remote app resource: projects/xxxx/locations/us-central1/reasoningEngines/xxxx

และเมื่อตรวจสอบในแดชบอร์ดเครื่องมือเอเจนต์ (ค้นหา "เครื่องมือเอเจนต์" ในแถบค้นหา) ระบบจะแสดงการติดตั้งใช้งานก่อนหน้า

e80f1c00ec9fbb38.png

นอกจากนี้ คุณยังตรวจสอบได้ว่าชื่อทรัพยากรของ Agent Engine แสดงอยู่ที่นั่น จากนั้นเราจะใช้ชื่อทรัพยากรนี้เพื่อทดสอบได้

หลังจากนั้น อัปเดต AGENT_ENGINE_RESOURCE_NAME ในไฟล์ .env ด้วยค่านี้ ตรวจสอบว่าคุณระบุชื่อทรัพยากรของเครื่องมือ Agent ถูกต้อง ไฟล์ .env ควรมีลักษณะดังนี้

GOOGLE_GENAI_USE_VERTEXAI=TRUE
GOOGLE_CLOUD_PROJECT={your-project-id}
GOOGLE_CLOUD_LOCATION=us-central1
STAGING_BUCKET=gs://purchasing-concierge-{your-project-id}
PIZZA_SELLER_AGENT_URL=https://pizza-agent-xxxxx.us-central1.run.app
BURGER_SELLER_AGENT_URL=https://burger-agent-xxxxx.us-central1.run.app
AGENT_ENGINE_RESOURCE_NAME=projects/xxxx/locations/us-central1/reasoningEngines/yyyy

การทดสอบเอเจนต์ที่ติดตั้งใช้งานใน Agent Engine

คุณโต้ตอบกับเครื่องมือ Agent ได้ผ่านcurlคำสั่งและ SDK เช่น เรียกใช้คำสั่งต่อไปนี้เพื่อลองโต้ตอบกับเอเจนต์ที่ติดตั้งใช้งาน

คุณลองส่งคำค้นหานี้เพื่อตรวจสอบว่าติดตั้งใช้งานเอเจนต์สำเร็จหรือไม่ เรียกใช้สคริปต์ test_agent_engine.sh ต่อไปนี้

bash test_agent_engine.sh

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

หากสำเร็จ ระบบจะแสดงเหตุการณ์การตอบกลับหลายรายการที่สตรีมในคอนโซลของคุณดังนี้

{
  "content": {
    "parts": [
      {
        "text": "Here is our burger menu:\n- Classic Cheeseburger: IDR 85K\n- Double Cheeseburger: IDR 110K\n- Spicy Chicken Burger: IDR 80K\n- Spicy Cajun Burger: IDR 85K"
      }
    ],
    "role": "model"
  },
  "usage_metadata": {
    "candidates_token_count": 51,
    "candidates_tokens_details": [
      {
        "modality": "TEXT",
        "token_count": 51
      }
    ],
    "prompt_token_count": 907,
    "prompt_tokens_details": [
      {
        "modality": "TEXT",
        "token_count": 907
      }
    ],
    "total_token_count": 958,
    "traffic_type": "ON_DEMAND"
  },
  "invocation_id": "e-14679918-af68-45f1-b942-cf014368a733",
  "author": "purchasing_agent",
  "actions": {
    "state_delta": {},
    "artifact_delta": {},
    "requested_auth_configs": {}
  },
  "id": "dbe7fc43-b82a-4f3e-82aa-dd97afa8f15b",
  "timestamp": 1754287348.941454
}

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

7. 🚀 การทดสอบการผสานรวมและการตรวจสอบเพย์โหลด

ตอนนี้เรามาตรวจสอบผู้ช่วยการซื้อของเราด้วยการโต้ตอบกับตัวแทนระยะไกลโดยใช้ UI ของเว็บกัน เรียกใช้คำสั่งต่อไปนี้เพื่อทำให้แอป Gradio ใช้งานได้ การเรียกใช้แอปนี้กำหนดให้คุณต้องกรอกข้อมูลในไฟล์ .env อย่างถูกต้องแล้ว

uv run purchasing_concierge_ui.py

หากสำเร็จ ระบบจะแสดงเอาต์พุตต่อไปนี้

* Running on local URL:  http://0.0.0.0:8080
* To create a public link, set `share=True` in `launch()`.

จากนั้น Ctrl + คลิก URL http://0.0.0.0:8080 ในเทอร์มินัลหรือคลิกปุ่มแสดงตัวอย่างเว็บเพื่อเปิด UI ของเว็บ

b38b428d9e4582bc.png

ลองสนทนาแบบนี้

  • ขอดูเมนูเบอร์เกอร์และพิซซ่า
  • ฉันต้องการสั่งพิซซ่าไก่บาร์บีคิว 1 ถาดและเบอร์เกอร์เคจันรสเผ็ด 1 ชิ้น

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

ff5f752965816b2b.png

6f65155c7a289964.png

b390f4b15f1c5a8c.png

ff44c54b50c36e1a.png

เราเห็นว่าการสื่อสารกับตัวแทน 2 คนที่แตกต่างกันจะทำให้เกิดพฤติกรรมที่แตกต่างกัน 2 แบบ และ A2A สามารถจัดการเรื่องนี้ได้ดี ตัวแทนผู้ขายพิซซ่าตอบรับคำขอจากตัวแทนการซื้อของเราโดยตรง ในขณะที่ตัวแทนผู้ขายเบอร์เกอร์ต้องการการยืนยันจากเราก่อนที่จะดำเนินการตามคำขอของเรา และหลังจากที่เรายืนยันแล้ว ตัวแทนผู้ขายพิซซ่าสามารถส่งต่อการยืนยันไปยังตัวแทนผู้ขายเบอร์เกอร์ได้

ตอนนี้เราได้เรียนรู้แนวคิดพื้นฐานของ A2A แล้ว และมาดูวิธีนำไปใช้เป็นสถาปัตยกรรมไคลเอ็นต์และเซิร์ฟเวอร์กัน

8. 💡 [คำอธิบายโค้ด] แนวคิดและการติดตั้งใช้งานเซิร์ฟเวอร์ A2A

การเริ่มต้นตัวแทนผู้ขายระยะไกลสามารถตรวจสอบได้ในสคริปต์ remote_seller_agents/*/agent.py ต่อไปนี้คือข้อมูลโค้ดของตัวแทนผู้ขาย

Burger Agent

from crewai import Agent, Crew, LLM, Task, Process
from crewai.tools import tool

...

       model = LLM(
            model="vertex_ai/gemini-2.5-flash-lite",  # Use base model name without provider prefix
        )
        burger_agent = Agent(
            role="Burger Seller Agent",
            goal=(
                "Help user to understand what is available on burger menu and price also handle order creation."
            ),
            backstory=("You are an expert and helpful burger seller agent."),
            verbose=False,
            allow_delegation=False,
            tools=[create_burger_order],
            llm=model,
        )

        agent_task = Task(
            description=self.TaskInstruction,
            agent=burger_agent,
            expected_output="Response to the user in friendly and helpful manner",
        )

        crew = Crew(
            tasks=[agent_task],
            agents=[burger_agent],
            verbose=False,
            process=Process.sequential,
        )

        inputs = {"user_prompt": query, "session_id": sessionId}
        response = crew.kickoff(inputs)
        return response

...

ตัวแทนพิซซ่า

from langchain_google_vertexai import ChatVertexAI
from langgraph.prebuilt import create_react_agent

...

self.model = ChatVertexAI(
    model="gemini-2.5-flash-lite",
    location=os.getenv("GOOGLE_CLOUD_LOCATION"),
    project=os.getenv("GOOGLE_CLOUD_PROJECT"),
)
self.tools = [create_pizza_order]
self.graph = create_react_agent(
    self.model,
    tools=self.tools,
    checkpointer=memory,
    prompt=self.SYSTEM_INSTRUCTION,
)

...

ดังที่เห็นได้ว่าเอเจนต์ 2 ตัวนี้สร้างขึ้นด้วยเฟรมเวิร์กที่แตกต่างกันโดยสิ้นเชิง ( CrewAI และ Langgraph) เมื่อเทียบกับเอเจนต์ไคลเอ็นต์ ( ADK) แต่ A2A ไม่ได้มีปัญหาใดๆ เราไม่จำเป็นต้องให้เอเจนต์ทั้ง 2 แชร์โค้ดภายในเพื่อสื่อสารกัน ไม่ว่าจะเป็นเฟรมเวิร์กใด ภาษาใด หรือตำแหน่งที่ใช้งาน

คอมโพเนนต์หลักของเซิร์ฟเวอร์ A2A

ตอนนี้เรามาพูดถึงแนวคิดและคอมโพเนนต์หลักของเซิร์ฟเวอร์ A2A กัน

การ์ดตัวแทน

เซิร์ฟเวอร์ A2A แต่ละเครื่องต้องมีการ์ดตัวแทนที่เข้าถึงได้ในทรัพยากร /.well-known/agent.json ซึ่งจะช่วยสนับสนุนระยะการค้นพบบนไคลเอ็นต์ A2A ซึ่งควรให้ข้อมูลและบริบทที่สมบูรณ์เกี่ยวกับวิธีเข้าถึงเอเจนต์และทราบความสามารถทั้งหมดของเอเจนต์ ซึ่งคล้ายกับเอกสารประกอบ API ที่มีเอกสารประกอบที่ดีโดยใช้ Swagger หรือ Postman

นี่คือเนื้อหาของการ์ดเอเจนต์ของเอเจนต์เบอร์เกอร์ที่เราใช้งาน

{
  "capabilities": {
    "streaming": true
  },
  "defaultInputModes": [
    "text",
    "text/plain"
  ],
  "defaultOutputModes": [
    "text",
    "text/plain"
  ],
  "description": "Helps with creating burger orders",
  "name": "burger_seller_agent",
  "protocolVersion": "0.2.6",
  "skills": [
    {
      "description": "Helps with creating burger orders",
      "examples": [
        "I want to order 2 classic cheeseburgers"
      ],
      "id": "create_burger_order",
      "name": "Burger Order Creation Tool",
      "tags": [
        "burger order creation"
      ]
    }
  ],
  "url": "https://burger-agent-109790610330.us-central1.run.app",
  "version": "1.0.0"
}

การ์ดเอเจนต์เหล่านี้จะไฮไลต์คอมโพเนนต์สำคัญหลายอย่าง เช่น ทักษะของเอเจนต์ ความสามารถในการสตรีม รูปแบบที่รองรับ เวอร์ชันโปรโตคอล และอื่นๆ

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

ในโค้ดของเรา การติดตั้งใช้งานการ์ดตัวแทนจะสร้างขึ้นโดยใช้ A2A Python SDK โปรดดูข้อมูลการติดตั้งใช้งานในข้อมูลโค้ด remote_seller_agents/burger_agent/main.py ด้านล่าง

...

        capabilities = AgentCapabilities(streaming=True)
        skill = AgentSkill(
            id="create_burger_order",
            name="Burger Order Creation Tool",
            description="Helps with creating burger orders",
            tags=["burger order creation"],
            examples=["I want to order 2 classic cheeseburgers"],
        )
        agent_host_url = (
            os.getenv("HOST_OVERRIDE")
            if os.getenv("HOST_OVERRIDE")
            else f"http://{host}:{port}/"
        )
        agent_card = AgentCard(
            name="burger_seller_agent",
            description="Helps with creating burger orders",
            url=agent_host_url,
            version="1.0.0",
            defaultInputModes=BurgerSellerAgent.SUPPORTED_CONTENT_TYPES,
            defaultOutputModes=BurgerSellerAgent.SUPPORTED_CONTENT_TYPES,
            capabilities=capabilities,
            skills=[skill],
        )

...

เราจะเห็นฟิลด์หลายรายการ เช่น

  1. AgentCapabilities : การประกาศฟังก์ชันเพิ่มเติมที่ไม่บังคับซึ่งบริการตัวแทนรองรับ เช่น ความสามารถในการสตรีมและ/หรือการรองรับการแจ้งเตือนแบบพุช
  2. AgentSkill : เครื่องมือหรือฟังก์ชันที่ตัวแทนรองรับ
  3. Input/OutputModes : รูปแบบประเภทอินพุต/เอาต์พุตที่รองรับ
  4. Url : ที่อยู่สำหรับติดต่อตัวแทน

ในการกำหนดค่านี้ เราจะสร้าง URL ของโฮสต์ตัวแทนแบบไดนามิกเพื่อให้สลับระหว่างการทดสอบในเครื่องกับการติดตั้งใช้งานระบบคลาวด์ได้ง่ายขึ้น จึงเป็นเหตุผลที่เราต้องเพิ่มตัวแปร HOST_OVERRIDE ในขั้นตอนก่อนหน้า

คิวงานและ Agent Executor

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

b9eb6b4025db4642.jpeg

ดังนั้น เซิร์ฟเวอร์ A2A แต่ละเครื่องควรติดตามงานที่เข้ามาและจัดเก็บข้อมูลที่เหมาะสมเกี่ยวกับงานนั้นได้ A2A SDK มีโมดูลเพื่อรับมือกับความท้าทายนี้ในเซิร์ฟเวอร์ A2A ก่อนอื่น เราสามารถสร้างตรรกะเกี่ยวกับวิธีจัดการคำขอขาเข้า การรับค่าคลาส AgentExecutor แบบนามธรรมช่วยให้เราควบคุมวิธีจัดการการดำเนินการและการยกเลิกงานได้ คุณตรวจสอบการติดตั้งใช้งานตัวอย่างนี้ได้ที่ remote_seller_agents/burger_agent/agent_executor.py module ( เส้นทางที่คล้ายกันสำหรับกรณีผู้ขายพิซซ่า)

...

class BurgerSellerAgentExecutor(AgentExecutor):
    """Burger Seller AgentExecutor."""

    def __init__(self):
        self.agent = BurgerSellerAgent()

    async def execute(
        self,
        context: RequestContext,
        event_queue: EventQueue,
    ) -> None:
        query = context.get_user_input()
        try:
            result = self.agent.invoke(query, context.context_id)
            print(f"Final Result ===> {result}")

            parts = [Part(root=TextPart(text=str(result)))]
            await event_queue.enqueue_event(
                completed_task(
                    context.task_id,
                    context.context_id,
                    [new_artifact(parts, f"burger_{context.task_id}")],
                    [context.message],
                )
            )
        except Exception as e:
            print("Error invoking agent: %s", e)
            raise ServerError(error=ValueError(f"Error invoking agent: {e}")) from e

    async def cancel(
        self, request: RequestContext, event_queue: EventQueue
    ) -> Task | None:
        raise ServerError(error=UnsupportedOperationError())

...

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

หลังจากสร้าง Executor แล้ว เราจะใช้ DefaultRequestHandler, InMemoryTaskStore และ A2AStarletteApplication ในตัวเพื่อเปิดใช้งานเซิร์ฟเวอร์ HTTP ได้โดยตรง คุณตรวจสอบการติดตั้งใช้งานนี้ได้ใน remote_seller_agents/burger_agent/__main__.py

...

        request_handler = DefaultRequestHandler(
            agent_executor=BurgerSellerAgentExecutor(),
            task_store=InMemoryTaskStore(),
        )
        server = A2AStarletteApplication(
            agent_card=agent_card, http_handler=request_handler
        )

        uvicorn.run(server.build(), host=host, port=port)

...

โมดูลนี้จะช่วยให้คุณสามารถใช้/.well-known/agent.jsonเส้นทางเพื่อเข้าถึงการ์ดตัวแทน รวมถึงปลายทาง POST เพื่อรองรับโปรโตคอล A2A

สรุป

กล่าวโดยย่อคือ ขณะนี้เซิร์ฟเวอร์ A2A ที่เราติดตั้งใช้งานโดยใช้ Python SDK รองรับฟังก์ชันการทำงาน 2 อย่างด้านล่าง

  1. การเผยแพร่การ์ดตัวแทนในเส้นทาง /.well-known/agent.json
  2. จัดการคำขอ JSON-RPC ด้วยการจัดคิวงานในหน่วยความจำ

คุณสามารถตรวจสอบจุดแรกเข้าในการเริ่มต้นฟังก์ชันการทำงานเหล่านี้ได้ในสคริปต์ __main__.py ( ใน remote_seller_agents/burger_agent หรือ remote_seller_agents/pizza_agent)

9. 💡 [คำอธิบายโค้ด] การติดตั้งใช้งาน Agent Engine

ต่อไปนี้คือข้อมูลโค้ดของตัวแทนผู้ช่วยการซื้อใน purchasing_concierge/purchasing_agent.py:

from google.adk import Agent

...

def create_agent(self) -> Agent:
        return Agent(
            model="gemini-2.5-flash-lite",
            name="purchasing_agent",
            instruction=self.root_instruction,
            before_model_callback=self.before_model_callback,
            before_agent_callback=self.before_agent_callback,
            description=(
                "This purchasing agent orchestrates the decomposition of the user purchase request into"
                " tasks that can be performed by the seller agents."
            ),
            tools=[
                self.send_task,
            ],
        )

...

ตัวแทนนี้สร้างขึ้นโดยใช้ ADK และติดตั้งใช้งานใน Agent Engine

Vertex AI Agent Engine คือชุดบริการที่ช่วยให้นักพัฒนาซอฟต์แวร์สามารถติดตั้งใช้งาน จัดการ และปรับขนาดเอเจนต์ AI ในการใช้งานจริง โดยจะจัดการโครงสร้างพื้นฐานเพื่อปรับขนาดเอเจนต์ในเวอร์ชันที่ใช้งานจริงเพื่อให้เรามุ่งเน้นที่การสร้างแอปพลิเคชันได้ อ่านข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ได้ในเอกสารนี้ หากก่อนหน้านี้เราต้องเตรียมไฟล์ที่จำเป็นในการติดตั้งใช้งานบริการตัวแทน (เช่น สคริปต์เซิร์ฟเวอร์ main และ Dockerfile) ในกรณีนี้ เราสามารถติดตั้งใช้งานตัวแทนได้โดยตรงจากสคริปต์ Python โดยไม่ต้องพัฒนาบริการแบ็กเอนด์ของเราเองด้วยการใช้ ADK และ Agent Engine ร่วมกัน

ในบทแนะนำนี้ เราจะติดตั้งใช้งานโดยใช้สคริปต์ deploy_to_agent_engine.py ซึ่งเนื้อหาจะแสดงอยู่ด้านล่าง

import vertexai
from vertexai.preview import reasoning_engines
from vertexai import agent_engines
from dotenv import load_dotenv
import os
from purchasing_concierge.agent import root_agent

load_dotenv()

PROJECT_ID = os.getenv("GOOGLE_CLOUD_PROJECT")
LOCATION = os.getenv("GOOGLE_CLOUD_LOCATION")
STAGING_BUCKET = os.getenv("STAGING_BUCKET")

vertexai.init(
    project=PROJECT_ID,
    location=LOCATION,
    staging_bucket=STAGING_BUCKET,
)

adk_app = reasoning_engines.AdkApp(
    agent=root_agent,
)

remote_app = agent_engines.create(
    agent_engine=adk_app,
    display_name="purchasing-concierge",
    requirements=[
        "google-cloud-aiplatform[adk,agent_engines]",
        "a2a-sdk==0.2.16",
    ],
    extra_packages=[
        "./purchasing_concierge",
    ],
    env_vars={
        "GOOGLE_GENAI_USE_VERTEXAI": os.environ["GOOGLE_GENAI_USE_VERTEXAI"],
        "PIZZA_SELLER_AGENT_URL": os.environ["PIZZA_SELLER_AGENT_URL"],
        "BURGER_SELLER_AGENT_URL": os.environ["BURGER_SELLER_AGENT_URL"],
    },
)

print(f"Deployed remote app resource: {remote_app.resource_name}")

นี่คือขั้นตอนที่จำเป็นในการติดตั้งใช้งานตัวแทน ADK กับเครื่องมือตัวแทน ก่อนอื่น เราต้องสร้างออบเจ็กต์ AdkApp จาก ADK root_agent จากนั้นเราจะเรียกใช้เมธอด agent_engines.create ได้โดยการระบุออบเจ็กต์ adk_app ระบุข้อกำหนดในฟิลด์ requirements ระบุเส้นทางไดเรกทอรีของเอเจนต์ใน extra_packages ( คุณยังระบุไดเรกทอรีและไฟล์อื่นๆ ได้หากจำเป็น) และระบุตัวแปรสภาพแวดล้อมที่จำเป็น

10. 💡 [คำอธิบายโค้ด] แนวคิดและการติดตั้งใช้งานไคลเอ็นต์ A2A

aa6c8bc5b5df73f1.jpeg

รูปภาพที่แสดงด้านบนคือขั้นตอนปกติของการโต้ตอบแบบ A2A

  1. ไคลเอ็นต์จะพยายามค้นหาการ์ดตัวแทนที่เผยแพร่ใน URL ของตัวแทนระยะไกลที่ระบุในเส้นทาง /.well-known/agent.json
  2. จากนั้นเมื่อจำเป็น ระบบจะส่งข้อความไปยังตัวแทนดังกล่าวพร้อมกับข้อความและพารามิเตอร์ข้อมูลเมตาที่จำเป็น ( เช่น รหัสเซสชัน บริบทในอดีต ฯลฯ) เซิร์ฟเวอร์จะรับรู้ข้อความนี้เป็นงานที่ต้องดำเนินการให้เสร็จสมบูรณ์
  3. เซิร์ฟเวอร์ A2A จะประมวลผลคำขอ หากเซิร์ฟเวอร์รองรับการแจ้งเตือนแบบพุช ก็จะสามารถเผยแพร่การแจ้งเตือนบางอย่างตลอดการประมวลผลงานได้ด้วย ( ฟังก์ชันการทำงานนี้อยู่นอกขอบเขตของ Codelab นี้)
  4. หลังจากเสร็จสิ้นแล้ว เซิร์ฟเวอร์ A2A จะส่งอาร์ติแฟกต์การตอบกลับกลับไปยังไคลเอ็นต์

ออบเจ็กต์หลักบางส่วนสำหรับการโต้ตอบข้างต้นคือรายการต่อไปนี้ (อ่านรายละเอียดเพิ่มเติมได้ที่นี่)

  • ข้อความ: การสื่อสารระหว่างไคลเอ็นต์กับตัวแทนระยะไกล
  • งาน: หน่วยงานพื้นฐานที่ A2A จัดการ ซึ่งระบุด้วยรหัสที่ไม่ซ้ำ
  • อาร์ติแฟกต์: เอาต์พุต (เช่น เอกสาร รูปภาพ Structured Data) ที่เอเจนต์สร้างขึ้นเป็นผลมาจากงาน ซึ่งประกอบด้วยส่วนต่างๆ
  • Part: หน่วยเนื้อหาที่เล็กที่สุดภายในข้อความหรืออาร์ติแฟกต์ โดยส่วนดังกล่าวอาจเป็นข้อความ รูปภาพ วิดีโอ ไฟล์ ฯลฯ

การค้นพบการ์ด

เมื่อเริ่มเปิดตัวบริการไคลเอ็นต์ A2A กระบวนการทั่วไปคือการพยายามรับข้อมูลบัตรตัวแทนและจัดเก็บไว้เพื่อให้เข้าถึงได้ง่ายเมื่อจำเป็น ในโค้ดแล็บนี้ เราจะติดตั้งใช้งานใน before_agent_callback คุณจะเห็นการติดตั้งใช้งานใน purchasing_concierge/purchasing_agent.py ดูข้อมูลโค้ดด้านล่าง

...

async def before_agent_callback(self, callback_context: CallbackContext):
        if not self.a2a_client_init_status:
            httpx_client = httpx.AsyncClient(timeout=httpx.Timeout(timeout=30))
            for address in self.remote_agent_addresses:
                card_resolver = A2ACardResolver(
                    base_url=address, httpx_client=httpx_client
                )
                try:
                    card = await card_resolver.get_agent_card()
                    remote_connection = RemoteAgentConnections(
                        agent_card=card, agent_url=card.url
                    )
                    self.remote_agent_connections[card.name] = remote_connection
                    self.cards[card.name] = card
                except httpx.ConnectError:
                    print(f"ERROR: Failed to get agent card from : {address}")
            agent_info = []
            for ra in self.list_remote_agents():
                agent_info.append(json.dumps(ra))
            self.agents = "\n".join(agent_info)

...

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

เครื่องมือพรอมต์และส่งงาน

นี่คือพรอมต์และเครื่องมือที่เรามอบให้ตัวแทน ADK ที่นี่

...

def root_instruction(self, context: ReadonlyContext) -> str:
    current_agent = self.check_active_agent(context)
    return f"""You are an expert purchasing delegator that can delegate the user product inquiry and purchase request to the
appropriate seller remote agents.

Execution:
- For actionable tasks, you can use `send_task` to assign tasks to remote agents to perform.
- When the remote agent is repeatedly asking for user confirmation, assume that the remote agent doesn't have access to user's conversation context. 
So improve the task description to include all the necessary information related to that agent
- Never ask user permission when you want to connect with remote agents. If you need to make connection with multiple remote agents, directly
connect with them without asking user permission or asking user preference
- Always show the detailed response information from the seller agent and propagate it properly to the user. 
- If the remote seller is asking for confirmation, rely the confirmation question to the user if the user haven't do so. 
- If the user already confirmed the related order in the past conversation history, you can confirm on behalf of the user
- Do not give irrelevant context to remote seller agent. For example, ordered pizza item is not relevant for the burger seller agent
- Never ask order confirmation to the remote seller agent 

Please rely on tools to address the request, and don't make up the response. If you are not sure, please ask the user for more details.
Focus on the most recent parts of the conversation primarily.

If there is an active agent, send the request to that agent with the update task tool.

Agents:
{self.agents}

Current active seller agent: {current_agent["active_agent"]}
"""

...

async def send_task(self, agent_name: str, task: str, tool_context: ToolContext):
        """Sends a task to remote seller agent

        This will send a message to the remote agent named agent_name.

        Args:
            agent_name: The name of the agent to send the task to.
            task: The comprehensive conversation context summary
                and goal to be achieved regarding user inquiry and purchase request.
            tool_context: The tool context this method runs in.

        Yields:
            A dictionary of JSON data.
        """
        if agent_name not in self.remote_agent_connections:
            raise ValueError(f"Agent {agent_name} not found")
        state = tool_context.state
        state["active_agent"] = agent_name
        client = self.remote_agent_connections[agent_name]
        if not client:
            raise ValueError(f"Client not available for {agent_name}")
        session_id = state["session_id"]
        task: Task
        message_id = ""
        metadata = {}
        if "input_message_metadata" in state:
            metadata.update(**state["input_message_metadata"])
            if "message_id" in state["input_message_metadata"]:
                message_id = state["input_message_metadata"]["message_id"]
        if not message_id:
            message_id = str(uuid.uuid4())

        payload = {
            "message": {
                "role": "user",
                "parts": [
                    {"type": "text", "text": task}
                ],  # Use the 'task' argument here
                "messageId": message_id,
                "contextId": session_id,
            },
        }

        message_request = SendMessageRequest(
            id=message_id, params=MessageSendParams.model_validate(payload)
        )
        send_response: SendMessageResponse = await client.send_message(
            message_request=message_request
        )
        print(
            "send_response",
            send_response.model_dump_json(exclude_none=True, indent=2),
        )

        if not isinstance(send_response.root, SendMessageSuccessResponse):
            print("received non-success response. Aborting get task ")
            return None

        if not isinstance(send_response.root.result, Task):
            print("received non-task response. Aborting get task ")
            return None

        return send_response.root.result

...

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

โปรโตคอลการสื่อสาร

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

65b8878a4854fd93.jpeg

9ddfae690d40cbbf.jpeg

การแลกเปลี่ยนข้อความ -> งานนี้จะใช้รูปแบบเพย์โหลดบนมาตรฐาน JSON-RPC ดังที่แสดงในตัวอย่างโปรโตคอล message/send ด้านล่าง

{
  # identifier for this request
  "id": "abc123",
  # version of JSON-RPC protocol
  "jsonrpc": "2.0",
  # method name
  "method": "message/send",
  # parameters/arguments of the method
  "params": {
    "message": "hi, what can you help me with?"
  }  
}

มีหลายวิธีที่ใช้ได้ เช่น เพื่อรองรับการสื่อสารประเภทต่างๆ (เช่น การซิงค์ การสตรีม แบบไม่พร้อมกัน) หรือเพื่อกำหนดค่าการแจ้งเตือนสำหรับสถานะของงาน คุณกำหนดค่าเซิร์ฟเวอร์ A2A ได้อย่างยืดหยุ่นเพื่อจัดการมาตรฐานคำจำกัดความของงานเหล่านี้ ดูรายละเอียดของวิธีการเหล่านี้ได้ในเอกสารนี้

11. 🎯 ความท้าทาย

ตอนนี้คุณพร้อมที่จะเตรียมไฟล์ที่จำเป็นและนำแอป Gradio ไปใช้งานใน Cloud Run ด้วยตัวเองแล้วใช่ไหม ได้เวลาเริ่มชาเลนจ์แล้ว

12. 🧹 ล้างข้อมูล

โปรดทำตามขั้นตอนต่อไปนี้เพื่อเลี่ยงไม่ให้เกิดการเรียกเก็บเงินกับบัญชี Google Cloud สำหรับทรัพยากรที่ใช้ในโค้ดแล็บนี้

  1. ใน Google Cloud Console ให้ไปที่หน้าจัดการทรัพยากร
  2. ในรายการโปรเจ็กต์ ให้เลือกโปรเจ็กต์ที่ต้องการลบ แล้วคลิกลบ
  3. ในกล่องโต้ตอบ ให้พิมพ์รหัสโปรเจ็กต์ แล้วคลิกปิดเพื่อลบโปรเจ็กต์
  4. หรือคุณจะไปที่ Cloud Run และ Agent Engine ในคอนโซล เลือกบริการที่คุณเพิ่งติดตั้งใช้งาน แล้วลบก็ได้