1. บทนำ
โปรโตคอลตัวแทนถึงตัวแทน (A2A) ออกแบบมาเพื่อกำหนดมาตรฐานการสื่อสารระหว่างเอเจนต์ AI โดยเฉพาะอย่างยิ่งสำหรับเอเจนต์ที่ใช้งานในระบบภายนอก ก่อนหน้านี้ โปรโตคอลดังกล่าวได้รับการสร้างขึ้นสำหรับเครื่องมือที่เรียกว่าModel Context Protocol (MCP) ซึ่งเป็นมาตรฐานใหม่ในการเชื่อมต่อ LLM กับข้อมูลและทรัพยากร A2A พยายามเสริม MCP โดยที่ A2A มุ่งเน้นไปที่ปัญหาที่แตกต่างกัน ในขณะที่ MCP มุ่งเน้นไปที่การลดความซับซ้อนในการเชื่อมต่อตัวแทนกับเครื่องมือและข้อมูล A2A มุ่งเน้นไปที่วิธีช่วยให้ตัวแทนทำงานร่วมกันในรูปแบบที่เป็นธรรมชาติ ซึ่งช่วยให้ตัวแทนสื่อสารในฐานะตัวแทน (หรือในฐานะผู้ใช้) แทนที่จะเป็นเครื่องมือได้ เช่น เปิดใช้การสื่อสารแบบไปมาเมื่อคุณต้องการสั่งซื้อสินค้า
A2A ได้รับการออกแบบมาเพื่อเสริม MCP ในเอกสารประกอบอย่างเป็นทางการ เราขอแนะนำให้แอปพลิเคชันใช้ MCP สำหรับเครื่องมือและ A2A สำหรับเอเจนต์ ซึ่งแสดงโดย AgentCard ( เราจะพูดถึงเรื่องนี้ในภายหลัง) จากนั้นเฟรมเวิร์กจะใช้ A2A เพื่อสื่อสารกับผู้ใช้ เอเจนต์ระยะไกล และเอเจนต์อื่นๆ ได้
ในการสาธิตนี้ เราจะเริ่มต้นด้วยการติดตั้งใช้งาน A2A โดยใช้ Python SDK เราจะพิจารณากรณีการใช้งานเมื่อมีผู้ช่วยส่วนตัวด้านการซื้อที่ช่วยเราสื่อสารกับตัวแทนผู้ขายเบอร์เกอร์และพิซซ่าเพื่อจัดการคำสั่งซื้อของเรา
A2A ใช้หลักการไคลเอ็นต์-เซิร์ฟเวอร์ นี่คือโฟลว์ A2A ทั่วไปที่เราคาดหวังในการสาธิตนี้
- ไคลเอ็นต์ A2A จะค้นหาการ์ดตัวแทนเซิร์ฟเวอร์ A2A ที่เข้าถึงได้ทั้งหมดก่อน แล้วใช้ข้อมูลดังกล่าวเพื่อสร้างไคลเอ็นต์การเชื่อมต่อ
- เมื่อจำเป็น ไคลเอ็นต์ A2A จะส่งข้อความไปยังเซิร์ฟเวอร์ A2A และเซิร์ฟเวอร์จะประเมินข้อความนี้เป็นงานที่ต้องดำเนินการให้เสร็จ หากกำหนดค่า URL ผู้รับการแจ้งเตือนแบบพุชในไคลเอ็นต์ A2A และเซิร์ฟเวอร์ A2A รองรับ เซิร์ฟเวอร์จะสามารถเผยแพร่สถานะความคืบหน้าของงานไปยังปลายทางการรับในไคลเอ็นต์ได้ด้วย
- หลังจากงานเสร็จสมบูรณ์ เซิร์ฟเวอร์ A2A จะส่งอาร์ติแฟกต์การตอบกลับไปยังไคลเอ็นต์ A2A
ใน Codelab นี้ คุณจะได้ใช้แนวทางแบบทีละขั้นตอนดังนี้
- เตรียมโปรเจ็กต์ Google Cloud และเปิดใช้ API ที่จำเป็นทั้งหมดในโปรเจ็กต์
- ตั้งค่าพื้นที่ทำงานสำหรับสภาพแวดล้อมการเขียนโค้ด
- เตรียมตัวแปรสภาพแวดล้อมสำหรับบริการเอเจนต์เบอร์เกอร์และพิซซ่า แล้วลองใช้ในเครื่อง
- ติดตั้งใช้งานเอเจนต์เบอร์เกอร์และพิซซ่าใน Cloud Run
- ตรวจสอบรายละเอียดเกี่ยวกับวิธีสร้างเซิร์ฟเวอร์ A2A
- เตรียมตัวแปรสภาพแวดล้อมสำหรับการซื้อแบบช่วยแนะนำและทดลองใช้ในเครื่อง
- ติดตั้งใช้งานผู้ช่วยซื้อใน Agent Engine
- เชื่อมต่อกับเครื่องมือตัวแทนผ่านอินเทอร์เฟซในเครื่อง
- ตรวจสอบรายละเอียดเกี่ยวกับวิธีที่ไคลเอ็นต์ A2A สร้างขึ้นและการสร้างแบบจำลองข้อมูล
- ตรวจสอบเพย์โหลดและการโต้ตอบระหว่างไคลเอ็นต์และเซิร์ฟเวอร์ A2A
ภาพรวมสถาปัตยกรรม
เราจะติดตั้งใช้งานสถาปัตยกรรมบริการต่อไปนี้
เราจะติดตั้งใช้งาน 2 บริการที่จะทำหน้าที่เป็นเซิร์ฟเวอร์ A2A, ตัวแทน Burger ( ขับเคลื่อนโดยเฟรมเวิร์กตัวแทน CrewAI) และตัวแทน Pizza ( ขับเคลื่อนโดยเฟรมเวิร์กตัวแทน Langgraph) ผู้ใช้จะโต้ตอบกับผู้ช่วยจัดซื้อโดยตรงเท่านั้น ซึ่งจะทำงานโดยใช้เฟรมเวิร์กชุดพัฒนาตัวแทน (ADK) ที่จะทำหน้าที่เป็นไคลเอ็นต์ A2A
เอเจนต์แต่ละรายจะมีสภาพแวดล้อมและการติดตั้งใช้งานของตนเอง
ข้อกำหนดเบื้องต้น
- คุ้นเคยกับการทำงานด้วย Python
- ความเข้าใจเกี่ยวกับสถาปัตยกรรม Full-Stack พื้นฐานโดยใช้บริการ HTTP
สิ่งที่คุณจะได้เรียนรู้
- โครงสร้างหลักของเซิร์ฟเวอร์ A2A
- โครงสร้างหลักของไคลเอ็นต์ A2A
- การติดตั้งใช้งานบริการตัวแทนใน Cloud Run
- การติดตั้งใช้งานบริการ Agent ใน Agent Engine
- วิธีที่ไคลเอ็นต์ A2A เชื่อมต่อกับเซิร์ฟเวอร์ A2A
- โครงสร้างคำขอและการตอบกลับในการเชื่อมต่อแบบไม่ใช่สตรีมมิง
สิ่งที่คุณต้องมี
- เว็บเบราว์เซอร์ Chrome
- บัญชี Gmail
- โปรเจ็กต์ Cloud ที่เปิดใช้การเรียกเก็บเงิน
Codelab นี้ออกแบบมาสำหรับนักพัฒนาซอฟต์แวร์ทุกระดับ (รวมถึงผู้เริ่มต้น) โดยใช้ Python ในแอปพลิเคชันตัวอย่าง อย่างไรก็ตาม คุณไม่จำเป็นต้องมีความรู้เกี่ยวกับ Python เพื่อทำความเข้าใจแนวคิดที่นำเสนอ
2. ก่อนเริ่มต้น
เลือกโปรเจ็กต์ที่ใช้งานอยู่ใน Cloud Console
Codelab นี้ถือว่าคุณมีโปรเจ็กต์ Google Cloud ที่เปิดใช้การเรียกเก็บเงินอยู่แล้ว หากยังไม่มี ให้ทำตามวิธีการด้านล่างเพื่อเริ่มต้นใช้งาน
- ใน Google Cloud Console ให้เลือกหรือสร้างโปรเจ็กต์ Google Cloud ในหน้าตัวเลือกโปรเจ็กต์
- ตรวจสอบว่าได้เปิดใช้การเรียกเก็บเงินสำหรับโปรเจ็กต์ Cloud แล้ว ดูวิธีตรวจสอบว่าได้เปิดใช้การเรียกเก็บเงินในโปรเจ็กต์แล้วหรือไม่
ตั้งค่าโปรเจ็กต์ Cloud ในเทอร์มินัล Cloud Shell
- คุณจะใช้ Cloud Shell ซึ่งเป็นสภาพแวดล้อมบรรทัดคำสั่งที่ทำงานใน Google Cloud และโหลด bq ไว้ล่วงหน้า คลิกเปิดใช้งาน Cloud Shell ที่ด้านบนของคอนโซล Google Cloud หากระบบแจ้งให้คุณให้สิทธิ์ ให้คลิกให้สิทธิ์
- เมื่อเชื่อมต่อกับ Cloud Shell แล้ว ให้ตรวจสอบว่าคุณได้รับการตรวจสอบสิทธิ์แล้วและตั้งค่าโปรเจ็กต์เป็นรหัสโปรเจ็กต์ของคุณโดยใช้คำสั่งต่อไปนี้
gcloud auth list
- เรียกใช้คำสั่งต่อไปนี้ใน Cloud Shell เพื่อยืนยันว่าคำสั่ง gcloud รู้จักโปรเจ็กต์ของคุณ
gcloud config list project
- หากไม่ได้ตั้งค่าโปรเจ็กต์ ให้ใช้คำสั่งต่อไปนี้เพื่อตั้งค่า
gcloud config set project <YOUR_PROJECT_ID>
หรือคุณจะดู PROJECT_ID
id ในคอนโซลก็ได้
คลิก แล้วคุณจะเห็นโปรเจ็กต์ทั้งหมดและรหัสโปรเจ็กต์ทางด้านขวา
- เปิดใช้ API ที่จำเป็นผ่านคำสั่งที่แสดงด้านล่าง การดำเนินการนี้อาจใช้เวลาสักครู่ โปรดอดทนรอ
gcloud services enable aiplatform.googleapis.com \
run.googleapis.com \
cloudbuild.googleapis.com \
cloudresourcemanager.googleapis.com
เมื่อเรียกใช้คำสั่งสำเร็จ คุณควรเห็นข้อความที่คล้ายกับข้อความที่แสดงด้านล่าง
Operation "operations/..." finished successfully.
คุณสามารถใช้คอนโซลแทนคำสั่ง gcloud ได้โดยค้นหาแต่ละผลิตภัณฑ์หรือใช้ลิงก์นี้
หากพลาด API ใดไป คุณก็เปิดใช้ได้เสมอในระหว่างการติดตั้งใช้งาน
โปรดดูคำสั่งและการใช้งาน gcloud ในเอกสารประกอบ
ไปที่ Cloud Shell Editor และตั้งค่าไดเรกทอรีการทำงานของแอปพลิเคชัน
ตอนนี้เราสามารถตั้งค่าโปรแกรมแก้ไขโค้ดเพื่อเขียนโค้ดได้แล้ว เราจะใช้ Cloud Shell Editor สำหรับการดำเนินการนี้
- คลิกปุ่ม "เปิดเครื่องมือแก้ไข" ซึ่งจะเปิด Cloud Shell Editor และเราจะเขียนโค้ดที่นี่ได้
- ตรวจสอบว่าได้ตั้งค่าโปรเจ็กต์ Cloud Code ไว้ที่มุมล่างซ้าย (แถบสถานะ) ของโปรแกรมแก้ไข Cloud Shell ตามที่ไฮไลต์ไว้ในรูปภาพด้านล่าง และตั้งค่าเป็นโปรเจ็กต์ Google Cloud ที่ใช้งานอยู่ซึ่งคุณเปิดใช้การเรียกเก็บเงินไว้ ให้สิทธิ์หากได้รับข้อความแจ้ง หากคุณทำตามคำสั่งก่อนหน้านี้แล้ว ปุ่มอาจชี้ไปยังโปรเจ็กต์ที่เปิดใช้งานโดยตรงแทนปุ่มลงชื่อเข้าใช้
- จากนั้นโคลนไดเรกทอรีการทำงานของเทมเพลตสำหรับโค้ดแล็บนี้จาก Github โดยเรียกใช้คำสั่งต่อไปนี้ ซึ่งจะสร้างไดเรกทอรีการทำงานในไดเรกทอรี purchasing-concierge-a2a
git clone https://github.com/alphinside/purchasing-concierge-intro-a2a-codelab-starter.git purchasing-concierge-a2a
- หลังจากนั้น ให้ไปที่ส่วนบนสุดของ Cloud Shell Editor แล้วคลิกFile->Open Folder ค้นหาไดเรกทอรีชื่อผู้ใช้ แล้วค้นหาไดเรกทอรี purchasing-concierge-a2a จากนั้นคลิกปุ่ม OK การดำเนินการนี้จะทำให้ไดเรกทอรีที่เลือกเป็นไดเรกทอรีการทำงานหลัก ในตัวอย่างนี้ ชื่อผู้ใช้คือ alvinprayuda ดังนั้นเส้นทางไดเรกทอรีจึงแสดงอยู่ด้านล่าง
ตอนนี้ Cloud Shell Editor ควรมีลักษณะดังนี้
การตั้งค่าสภาพแวดล้อม
ขั้นตอนถัดไปคือการเตรียมสภาพแวดล้อมในการพัฒนา เทอร์มินัลที่ใช้งานอยู่ปัจจุบันควรอยู่ในไดเรกทอรีการทำงาน purchasing-concierge-a2a เราจะใช้ Python 3.12 ใน Codelab นี้ และจะใช้ตัวจัดการโปรเจ็กต์ Python ของ uv เพื่อลดความซับซ้อนในการสร้างและจัดการเวอร์ชัน Python และสภาพแวดล้อมเสมือน
- หากยังไม่ได้เปิดเทอร์มินัล ให้เปิดโดยคลิกเทอร์มินัล -> เทอร์มินัลใหม่ หรือใช้ Ctrl + Shift + C ซึ่งจะเปิดหน้าต่างเทอร์มินัลที่ส่วนล่างของเบราว์เซอร์
- ตอนนี้เรามาเริ่มต้นสภาพแวดล้อมเสมือนของผู้ช่วยการซื้อโดยใช้
uv
(ติดตั้งไว้ล่วงหน้าแล้วในเทอร์มินัลระบบคลาวด์) เรียกใช้คำสั่งนี้
uv sync --frozen
การดำเนินการนี้จะสร้างไดเรกทอรี .venv และติดตั้งทรัพยากร Dependency การดูpyproject.toml อย่างรวดเร็วจะให้ข้อมูลเกี่ยวกับทรัพยากร Dependency ที่แสดงดังนี้
dependencies = [ "a2a-sdk>=0.2.16", "google-adk>=1.8.0", "gradio>=5.38.2", ]
- หากต้องการทดสอบสภาพแวดล้อมเสมือน ให้สร้างไฟล์ใหม่ชื่อ main.py แล้วคัดลอกโค้ดต่อไปนี้
def main():
print("Hello from purchasing-concierge-a2a!")
if __name__ == "__main__":
main()
- จากนั้นเรียกใช้คำสั่งต่อไปนี้
uv run main.py
คุณจะได้รับเอาต์พุตดังที่แสดงด้านล่าง
Using CPython 3.12 Creating virtual environment at: .venv Hello from purchasing-concierge-a2a!
ซึ่งแสดงว่าระบบกำลังตั้งค่าโปรเจ็กต์ Python อย่างถูกต้อง
ตอนนี้เราสามารถไปยังขั้นตอนถัดไปได้แล้ว นั่นคือการกำหนดค่าและติดตั้งใช้งานตัวแทนผู้ขายระยะไกล
3. การติดตั้งใช้งานตัวแทนผู้ขายระยะไกล - เซิร์ฟเวอร์ A2A ไปยัง Cloud Run
ในขั้นตอนนี้ เราจะติดตั้งใช้งานเอเจนต์ผู้ขายระยะไกล 2 รายนี้ซึ่งทำเครื่องหมายด้วยกรอบสีแดง เอเจนต์เบอร์เกอร์จะขับเคลื่อนด้วยเฟรมเวิร์กเอเจนต์ CrewAI และเอเจนต์พิซซ่าจะขับเคลื่อนด้วยเอเจนต์ Langgraph โดยทั้ง 2 เอเจนต์จะขับเคลื่อนด้วยโมเดล Gemini Flash 2.0
การติดตั้งใช้งาน Remote Burger Agent
ซอร์สโค้ดของ Burger Agent อยู่ในไดเรกทอรี remote_seller_agents/burger_agent คุณตรวจสอบการเริ่มต้นตัวแทนได้ในสคริปต์ agent.py นี่คือข้อมูลโค้ดของเอเจนต์ที่เริ่มต้น
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
...
ไฟล์ทั้งหมดที่อยู่ในไดเรกทอรี 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 ซึ่งจะเกิดขึ้นในกรณีที่คุณไม่เคยทำให้ Cloud Run ใช้งานได้จากแหล่งที่มาก่อน หลังจากติดตั้งใช้งานสำเร็จแล้ว ระบบจะแสดงบันทึกดังนี้
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
เส้นทางของบริการเอเจนต์เบอร์เกอร์ที่ติดตั้งใช้งานเหล่านั้นผ่านเบราว์เซอร์กัน นี่คือ URL สำหรับเข้าถึงการ์ด Agent เซิร์ฟเวอร์ A2A ที่ติดตั้งใช้งาน
หากติดตั้งใช้งานสำเร็จ คุณจะเห็นการตอบกลับดังที่แสดงด้านล่างในเบราว์เซอร์เมื่อเข้าถึง https://burger-agent-xxxxxxxxx.us-central1.run.app/.well-known/agent.json
นี่คือข้อมูลการ์ดตัวแทนร้านเบอร์เกอร์ที่ควรเข้าถึงได้เพื่อวัตถุประสงค์ในการค้นพบ เราจะพูดถึงเรื่องนี้ในภายหลัง โปรดสังเกตว่าค่า url
ยังคงตั้งค่าเป็น http://0.0.0.0:8080/
ที่นี่ url
ค่านี้ควรเป็นข้อมูลหลักสำหรับไคลเอ็นต์ A2A ในการส่งข้อความจากภายนอก แต่มีการกำหนดค่าไม่ถูกต้อง สำหรับการสาธิตนี้ เราจะต้องอัปเดตค่านี้เป็น URL ของบริการเอเจนต์เบอร์เกอร์โดยการเพิ่มตัวแปรสภาพแวดล้อมเพิ่มเติม HOST_OVERRIDE
การอัปเดตค่า URL ของ Burger Agent ในการ์ดตัวแทนผ่านตัวแปรสภาพแวดล้อม
หากต้องการเพิ่ม HOST_OVERRIDE
ในบริการของตัวแทน Burger ให้ทำตามขั้นตอนต่อไปนี้
- ค้นหา Cloud Run ในแถบค้นหาที่ด้านบนของคอนโซลระบบคลาวด์
- คลิกบริการ Cloud Run burger-agent ที่ติดตั้งใช้งานก่อนหน้านี้
- คัดลอก URL ของเบอร์เกอร์เซอร์วิส แล้วคลิกแก้ไขและติดตั้งใช้งานการแก้ไขใหม่
- จากนั้นคลิกส่วนตัวแปรและข้อมูลลับ
- หลังจากนั้น ให้คลิกเพิ่มตัวแปร แล้วตั้งค่า
HOST_OVERRIDE
เป็น URL ของบริการ ( URL ที่มีรูปแบบhttps://burger-agent-xxxxxxxxx.us-central1.run.app
)
- สุดท้าย ให้คลิกปุ่มติดตั้งใช้งานเพื่อติดตั้งใช้งานบริการอีกครั้ง
ตอนนี้เมื่อคุณเข้าถึงการ์ดเอเจนต์ Burger-Agent อีกครั้งในเบราว์เซอร์ https://burger-agent-xxxxxxxxx.us-central1.run.app/.well-known/agent.json
ค่า url
จะได้รับการกำหนดค่าอย่างถูกต้องแล้ว
การติดตั้งใช้งาน Remote Pizza Agent
ในทำนองเดียวกัน ซอร์สโค้ดของเอเจนต์พิซซ่าจะอยู่ในไดเรกทอรี remote_seller_agents/pizza_agent คุณตรวจสอบการเริ่มต้นตัวแทนได้ในสคริปต์ agent.py นี่คือข้อมูลโค้ดของเอเจนต์ที่เริ่มต้น
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,
)
...
เช่นเดียวกับขั้นตอนการติดตั้งใช้งานเอเจนต์เบอร์เกอร์ก่อนหน้านี้ ไฟล์ทั้งหมดที่อยู่ในไดเรกทอรี 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
ลงในบริการตัวแทนพิซซ่า ให้ทำตามขั้นตอนต่อไปนี้
- ค้นหา Cloud Run ในแถบค้นหาที่ด้านบนของคอนโซลระบบคลาวด์
- คลิกบริการ Cloud Run pizza-agent ที่ติดตั้งใช้งานก่อนหน้านี้
- คลิกแก้ไขและทำให้การแก้ไขใหม่ใช้งานได้
- คัดลอก URL ของบริการพิซซ่า แล้วคลิกส่วนตัวแปรและความลับ
- หลังจากนั้น ให้คลิกเพิ่มตัวแปร แล้วตั้งค่า
HOST_OVERRIDE
เป็น URL ของบริการ ( URL ที่มีรูปแบบhttps://pizza-agent-xxxxxxxxx.us-central1.run.app
)
- สุดท้าย ให้คลิกปุ่มติดตั้งใช้งานเพื่อติดตั้งใช้งานบริการอีกครั้ง
ตอนนี้เมื่อคุณเข้าถึงการ์ดเอเจนต์ pizza-agent อีกครั้งในเบราว์เซอร์ https://pizza-agent-xxxxxxxxx.us-central1.run.app/.well-known/agent.json
ระบบจะกำหนดค่าค่า url
อย่างถูกต้องแล้ว
ตอนนี้เราได้ทำให้บริการเบอร์เกอร์และพิซซ่าใช้งานได้ใน Cloud Run เรียบร้อยแล้ว ตอนนี้เรามาพูดถึงองค์ประกอบหลักของเซิร์ฟเวอร์ A2A กัน
4. คอมโพเนนต์หลักของเซิร์ฟเวอร์ 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],
)
...
เราจะเห็นฟิลด์หลายรายการ เช่น
AgentCapabilities
: การประกาศฟังก์ชันเพิ่มเติมที่ไม่บังคับซึ่งบริการตัวแทนรองรับ เช่น ความสามารถในการสตรีมและ/หรือการรองรับการแจ้งเตือนแบบพุชAgentSkill
: เครื่องมือหรือฟังก์ชันที่ตัวแทนรองรับInput/OutputModes
: รูปแบบประเภทอินพุต/เอาต์พุตที่รองรับUrl
: ที่อยู่สำหรับติดต่อตัวแทน
ในการกำหนดค่านี้ เราจะสร้าง URL ของโฮสต์ตัวแทนแบบไดนามิกเพื่อให้สลับระหว่างการทดสอบในเครื่องกับการติดตั้งใช้งานระบบคลาวด์ได้ง่ายขึ้น จึงเป็นเหตุผลที่เราต้องเพิ่มตัวแปร HOST_OVERRIDE
ในขั้นตอนก่อนหน้า
คิวงานและตัวดำเนินการ Agent
เซิร์ฟเวอร์ A2A อาจจัดการคำขอจากเอเจนต์หรือผู้ใช้ที่แตกต่างกัน และสามารถแยกแต่ละงานได้อย่างสมบูรณ์ คุณสามารถตรวจสอบรูปภาพด้านล่างเพื่อให้เห็นภาพบริบทของสิ่งเหล่านี้ได้ชัดเจนขึ้น
ดังนั้น เซิร์ฟเวอร์ 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 อย่างด้านล่างนี้
- การเผยแพร่การ์ดตัวแทนในเส้นทาง
/.well-known/agent.json
- จัดการคำขอ JSON-RPC ด้วยการจัดคิวงานในหน่วยความจำ
คุณสามารถตรวจสอบจุดแรกเข้าในการเริ่มต้นฟังก์ชันการทำงานเหล่านี้ได้ในสคริปต์ __main__.py
( ใน remote_seller_agents/burger_agent
หรือ remote_seller_agents/pizza_agent
)
5. การติดตั้งใช้งาน Purchasing Concierge - A2A Client to 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,
],
)
...
เราจะติดตั้งใช้งานเอเจนต์นี้ในเครื่องมือเอเจนต์ Vertex AI Agent Engine คือชุดบริการที่ช่วยให้นักพัฒนาซอฟต์แวร์สามารถติดตั้งใช้งาน จัดการ และปรับขนาดเอเจนต์ AI ในการใช้งานจริง โดยจะจัดการโครงสร้างพื้นฐานเพื่อปรับขนาดเอเจนต์ในเวอร์ชันที่ใช้งานจริงเพื่อให้เรามุ่งเน้นที่การสร้างแอปพลิเคชันได้ อ่านข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ได้ในเอกสารนี้ หากก่อนหน้านี้เราต้องเตรียมไฟล์ที่จำเป็นในการติดตั้งใช้งานบริการตัวแทน (เช่น สคริปต์เซิร์ฟเวอร์ main และ Dockerfile) ในกรณีนี้ เราสามารถติดตั้งใช้งานตัวแทนได้โดยตรงจากสคริปต์ Python โดยไม่ต้องพัฒนาบริการแบ็กเอนด์ของเราเองด้วยการใช้ ADK และ Agent Engine ร่วมกัน ทำตามขั้นตอนต่อไปนี้เพื่อติดตั้งใช้งาน
- ก่อนอื่นเราต้องสร้างที่เก็บข้อมูลการทดลองใช้ใน Cloud Storage
gcloud storage buckets create gs://purchasing-concierge-{your-project-id} --location=us-central1
- ตอนนี้เราต้องเตรียมตัวแปร .env ก่อน โดยคัดลอก .env.example ไปยังไฟล์ .env
cp .env.example .env
- ตอนนี้ให้เปิดไฟล์ .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 เพื่อเปิดในแท็บใหม่
คุณควรเห็นบริการตัวแทนผู้ขายระยะไกลที่เราเคยให้บริการก่อนหน้านี้ดังที่แสดงด้านล่าง
ตอนนี้หากต้องการดู URL สาธารณะของบริการเหล่านั้น ให้คลิกบริการใดบริการหนึ่ง แล้วระบบจะเปลี่ยนเส้นทางคุณไปยังหน้ารายละเอียดบริการ คุณดู URL ได้ที่ด้านบนข้างข้อมูลภูมิภาค
ตัวแปรสภาพแวดล้อมสุดท้ายควรมีลักษณะคล้ายกับตัวแปรนี้
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}
- ตอนนี้เราพร้อมที่จะติดตั้งใช้งานเอเจนต์ผู้ช่วยส่วนตัวด้านการซื้อแล้ว ในเดโมนี้ เราจะติดตั้งใช้งานโดยใช้สคริปต์
deploy_to_agent_engine.py
ซึ่งเนื้อหาจะแสดงอยู่ด้านล่าง
"""
Copyright 2025 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
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
และระบุตัวแปรสภาพแวดล้อมที่จำเป็น
เราสามารถติดตั้งใช้งานได้โดยการเรียกใช้สคริปต์ต่อไปนี้
uv run deploy_to_agent_engine.py
หลังจากติดตั้งใช้งานสำเร็จแล้ว ระบบจะแสดงบันทึกดังนี้ โปรดทราบว่า xxxx คือรหัสโปรเจ็กต์ และ yyyy คือรหัสทรัพยากรของ Agent Engine
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
และเมื่อตรวจสอบในแดชบอร์ดเครื่องมือเอเจนต์ (ค้นหา "เครื่องมือเอเจนต์" ในแถบค้นหา) ระบบจะแสดงการติดตั้งใช้งานก่อนหน้า
การทดสอบเอเจนต์ที่ติดตั้งใช้งานใน Agent Engine
คุณโต้ตอบกับเครื่องมือ Agent ได้ผ่านcurl
คำสั่งและ SDK เช่น เรียกใช้คำสั่งต่อไปนี้เพื่อลองโต้ตอบกับเอเจนต์ที่ติดตั้งใช้งาน
คุณลองส่งคำค้นหานี้เพื่อตรวจสอบว่ามีการติดตั้งใช้งานเอเจนต์สำเร็จหรือไม่
curl \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
https://us-central1-aiplatform.googleapis.com/v1/projects/{YOUR_PROJECT_ID}/locations/us-central1/reasoningEngines/{YOUR_AGENT_ENGINE_RESOURCE_ID}:streamQuery?alt=sse -d '{
"class_method": "stream_query",
"input": {
"user_id": "user_123",
"message": "List available burger menu please",
}
}'
หากสำเร็จ ระบบจะแสดงเหตุการณ์การตอบกลับหลายรายการที่สตรีมในคอนโซลของคุณดังนี้
{ "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 คืออะไร
6. คอมโพเนนต์หลักของไคลเอ็นต์ A2A
รูปภาพที่แสดงด้านบนคือขั้นตอนปกติของการโต้ตอบแบบ A2A
- ไคลเอ็นต์จะพยายามค้นหาการ์ดตัวแทนที่เผยแพร่ใน URL ของตัวแทนระยะไกลที่ระบุในเส้นทาง
/.well-known/agent.json
- จากนั้นเมื่อจำเป็น ระบบจะส่งข้อความไปยังตัวแทนดังกล่าวพร้อมกับข้อความและพารามิเตอร์ข้อมูลเมตาที่จำเป็น ( เช่น รหัสเซสชัน บริบทในอดีต ฯลฯ) เซิร์ฟเวอร์จะรับรู้ข้อความนี้เป็นงานที่ต้องดำเนินการให้เสร็จสมบูรณ์
- เซิร์ฟเวอร์ A2A จะประมวลผลคำขอ หากเซิร์ฟเวอร์รองรับการแจ้งเตือนแบบพุช ก็จะสามารถเผยแพร่การแจ้งเตือนบางอย่างตลอดการประมวลผลงานได้ด้วย ( ฟังก์ชันการทำงานนี้อยู่นอกขอบเขตของ Codelab นี้)
- หลังจากเสร็จสิ้นแล้ว เซิร์ฟเวอร์ 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 ไคลเอ็นต์จะมองว่านี่คือข้อความที่ส่งไปยังเซิร์ฟเวอร์ ซึ่งขึ้นอยู่กับเซิร์ฟเวอร์ว่าจะกำหนดข้อความขาเข้าจากไคลเอ็นต์เป็นงานใด และการทำงานให้เสร็จสมบูรณ์ต้องมีการโต้ตอบจากไคลเอ็นต์หรือไม่ คุณสามารถอ่านรายละเอียดเพิ่มเติมเกี่ยวกับวงจรของงานได้ในเอกสารนี้ แนวคิดระดับสูงของเรื่องนี้สามารถแสดงภาพได้ดังนี้
การแลกเปลี่ยนข้อความ -> งานนี้จะใช้รูปแบบเพย์โหลดบนมาตรฐาน 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 ได้อย่างยืดหยุ่นเพื่อจัดการมาตรฐานคำจำกัดความของงานเหล่านี้ ดูรายละเอียดของวิธีการเหล่านี้ได้ในเอกสารนี้
7. การทดสอบการผสานรวมและการตรวจสอบเพย์โหลด
ตอนนี้เรามาตรวจสอบผู้ช่วยการซื้อของเราด้วยการโต้ตอบกับตัวแทนระยะไกลโดยใช้ UI ของเว็บกัน
ก่อนอื่น เราต้องอัปเดต 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
หลังจากนั้น ให้เรียกใช้คำสั่งต่อไปนี้เพื่อติดตั้งใช้งานแอป Gradio
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 เว็บ
ลองสนทนาแบบนี้
- ขอดูเมนูเบอร์เกอร์และพิซซ่า
- ฉันต้องการสั่งพิซซ่าไก่บาร์บีคิว 1 ถาดและเบอร์เกอร์เคจันรสเผ็ด 1 ชิ้น
และสนทนาต่อจนกว่าคุณจะสั่งซื้อเสร็จ ตรวจสอบว่าการโต้ตอบเป็นอย่างไร รวมถึงการเรียกใช้เครื่องมือและการตอบกลับคืออะไร รูปภาพต่อไปนี้เป็นตัวอย่างผลลัพธ์ของการโต้ตอบ
เราเห็นว่าการสื่อสารกับตัวแทน 2 คนที่แตกต่างกันจะทำให้เกิดพฤติกรรมที่แตกต่างกัน 2 แบบ และ A2A สามารถจัดการเรื่องนี้ได้ดี ตัวแทนผู้ขายพิซซ่ารับคำขอจากตัวแทนการซื้อของเราโดยตรง ในขณะที่ตัวแทนผู้ขายเบอร์เกอร์ต้องการการยืนยันจากเราก่อนที่จะดำเนินการตามคำขอของเรา และหลังจากที่เรายืนยันแล้ว ตัวแทนผู้ขายพิซซ่าสามารถส่งต่อการยืนยันไปยังตัวแทนผู้ขายเบอร์เกอร์ได้
ตอนนี้เราได้เรียนรู้แนวคิดพื้นฐานของ A2A แล้ว และมาดูวิธีนำไปใช้เป็นสถาปัตยกรรมไคลเอ็นต์และเซิร์ฟเวอร์กัน
8. ความท้าทาย
ตอนนี้คุณพร้อมที่จะเตรียมไฟล์ที่จำเป็นและนำแอป Gradio ไปใช้งานใน Cloud Run ด้วยตัวเองแล้วใช่ไหม ได้เวลาเริ่มชาเลนจ์แล้ว
9. ล้างข้อมูล
โปรดทำตามขั้นตอนต่อไปนี้เพื่อเลี่ยงไม่ให้เกิดการเรียกเก็บเงินกับบัญชี Google Cloud สำหรับทรัพยากรที่ใช้ในโค้ดแล็บนี้
- ใน Google Cloud Console ให้ไปที่หน้าจัดการทรัพยากร
- ในรายการโปรเจ็กต์ ให้เลือกโปรเจ็กต์ที่ต้องการลบ แล้วคลิกลบ
- ในกล่องโต้ตอบ ให้พิมพ์รหัสโปรเจ็กต์ แล้วคลิกปิดเพื่อลบโปรเจ็กต์
- หรือจะไปที่ Cloud Run ในคอนโซล เลือกบริการที่คุณเพิ่งทําให้ใช้งานได้ แล้วลบก็ได้