Agent2Agent (A2A) প্রোটোকল দিয়ে শুরু করা: ক্লাউড রান এবং এজেন্ট ইঞ্জিনে একটি ক্রয়কারী কনসিয়ার এবং রিমোট সেলার এজেন্ট ইন্টারঅ্যাকশন

১. 📖 ভূমিকা

983be32cd5f6b65d.png

এজেন্ট-টু-এজেন্ট (A2A) প্রোটোকলটি এআই এজেন্টদের মধ্যে যোগাযোগকে মানসম্মত করার জন্য ডিজাইন করা হয়েছে, বিশেষ করে সেইসব এজেন্টদের জন্য যেগুলো বাহ্যিক সিস্টেমে স্থাপন করা হয়। পূর্বে, টুলসের জন্য মডেল কনটেক্সট প্রোটোকল (MCP) নামে এই ধরনের প্রোটোকল প্রতিষ্ঠিত ছিল, যা ডেটা এবং রিসোর্সের সাথে এলএলএম (LLM) সংযোগ করার জন্য একটি উদীয়মান মান। A2A, MCP-কে পরিপূরক করার চেষ্টা করে, যেখানে A2A একটি ভিন্ন সমস্যার উপর দৃষ্টি নিবদ্ধ করে। MCP যেখানে এজেন্টদের টুলস এবং ডেটার সাথে সংযোগ করার জটিলতা কমানোর উপর মনোযোগ দেয়, সেখানে A2A এজেন্টদের তাদের স্বাভাবিক পদ্ধতিতে সহযোগিতা করতে সক্ষম করার উপর মনোযোগ দেয়। এটি এজেন্টদের টুলস হিসেবে নয়, বরং এজেন্ট (বা ব্যবহারকারী) হিসেবে যোগাযোগ করার সুযোগ দেয়; উদাহরণস্বরূপ, আপনি যখন কিছু অর্ডার করতে চান তখন পারস্পরিক যোগাযোগ সক্ষম করে।

A2A-কে MCP-এর পরিপূরক হিসেবে তৈরি করা হয়েছে। এর অফিসিয়াল ডকুমেন্টেশনে সুপারিশ করা হয়েছে যে, অ্যাপ্লিকেশনগুলো যেন টুলসের জন্য MCP এবং এজেন্টদের জন্য A2A ব্যবহার করে—যা AgentCard দ্বারা প্রতিনিধিত্ব করা হয় (আমরা এই বিষয়ে পরে আলোচনা করব)। এরপর ফ্রেমওয়ার্কগুলো তাদের ব্যবহারকারী, রিমোট এজেন্ট এবং অন্যান্য এজেন্টদের সাথে যোগাযোগের জন্য A2A ব্যবহার করতে পারে।

cb49a578e47636e1.png

এই ডেমোতে, আমরা A2A-এর পাইথন SDK ব্যবহার করে এর বাস্তবায়ন দিয়ে শুরু করব। আমরা এমন একটি ব্যবহারের ক্ষেত্র খতিয়ে দেখব যেখানে আমাদের একজন ব্যক্তিগত ক্রয় সহায়ক (purchasing concierge) আছেন, যিনি আমাদের অর্ডারটি সম্পন্ন করার জন্য বার্গার এবং পিৎজা বিক্রেতা এজেন্টদের সাথে যোগাযোগ করতে সাহায্য করতে পারেন।

A2A ক্লায়েন্ট-সার্ভার নীতি ব্যবহার করে। এই টিউটোরিয়ালে আপনি সাধারণত A2A-এর যে প্রবাহটি আশা করতে পারেন, তা নিচে দেওয়া হলো।

e7e3224d05b725f0.jpeg

  1. A2A ক্লায়েন্ট প্রথমে সমস্ত অ্যাক্সেসযোগ্য A2A সার্ভার এজেন্ট কার্ড অনুসন্ধান করবে এবং এর তথ্য ব্যবহার করে একটি সংযোগ ক্লায়েন্ট তৈরি করবে।
  2. প্রয়োজন অনুযায়ী, A2A ক্লায়েন্ট A2A সার্ভারে একটি বার্তা পাঠাবে, সার্ভার এটিকে সম্পন্ন করার জন্য একটি টাস্ক হিসেবে মূল্যায়ন করবে। যদি A2A ক্লায়েন্টে পুশ নোটিফিকেশন রিসিভার ইউআরএল কনফিগার করা থাকে এবং তা A2A সার্ভার দ্বারা সমর্থিত হয়, তবে সার্ভারটি ক্লায়েন্টের রিসিভিং এন্ডপয়েন্টে টাস্কের অগ্রগতির অবস্থা প্রকাশ করতেও সক্ষম হবে।
  3. কাজটি শেষ হওয়ার পর, A2A সার্ভারটি A2A ক্লায়েন্টের কাছে প্রতিক্রিয়া আর্টিফ্যাক্টটি পাঠাবে।

কোডল্যাবের মাধ্যমে, আপনি নিম্নলিখিত ধাপে ধাপে পদ্ধতিটি অনুসরণ করবেন:

  1. গুগল ক্লাউড প্রজেক্ট প্রস্তুত করুন
  2. কোডিং পরিবেশের জন্য কাজের ডিরেক্টরি তৈরি করুন।
  3. ক্লাউড রানে বার্গার এজেন্ট স্থাপন করুন
  4. ক্লাউড রানে পিজা এজেন্ট স্থাপন করুন
  5. এজেন্ট ইঞ্জিনে পারচেজিং কনসিয়ার্জ স্থাপন করুন
  6. স্থানীয় ইন্টারফেসের মাধ্যমে পারচেজিং কনসিয়ার্জের সাথে যোগাযোগ করুন।

স্থাপত্যের সংক্ষিপ্ত বিবরণ

আপনি নিম্নলিখিত পরিষেবা স্থাপত্যটি স্থাপন করবেন

a485135c8f7b97bd.jpeg

আপনি দুটি সার্ভিস ডেপ্লয় করবেন যা A2A সার্ভার হিসেবে কাজ করবে: বার্গার এজেন্ট (CrewAI এজেন্ট ফ্রেমওয়ার্ক দ্বারা সমর্থিত) এবং পিজ্জা এজেন্ট (Langgraph এজেন্ট ফ্রেমওয়ার্ক দ্বারা সমর্থিত)। ব্যবহারকারী শুধুমাত্র পারচেজিং কনসিয়ার্জের সাথে সরাসরি যোগাযোগ করবে, যা এজেন্ট ডেভেলপমেন্ট কিট (ADK) ফ্রেমওয়ার্ক ব্যবহার করে চালানো হবে এবং এটি A2A ক্লায়েন্ট হিসেবে কাজ করবে।

এই এজেন্টগুলোর প্রত্যেকের নিজস্ব পরিবেশ এবং মোতায়েন ব্যবস্থা থাকবে।

পূর্বশর্ত

  • পাইথন নিয়ে কাজ করতে স্বাচ্ছন্দ্যবোধ করি।
  • HTTP পরিষেবা ব্যবহার করে মৌলিক ফুল-স্ট্যাক আর্কিটেকচার সম্পর্কে ধারণা

আপনি যা শিখবেন

  • A2A সার্ভারের মূল কাঠামো
  • A2A ক্লায়েন্টের মূল কাঠামো
  • ক্লাউড রানে এজেন্ট পরিষেবা স্থাপন করা হচ্ছে
  • এজেন্ট ইঞ্জিনে এজেন্ট পরিষেবা স্থাপন করা হচ্ছে
  • A2A ক্লায়েন্ট কীভাবে A2A সার্ভারের সাথে সংযোগ স্থাপন করে
  • নন-স্ট্রিমিং সংযোগে অনুরোধ এবং প্রতিক্রিয়া কাঠামো

আপনার যা যা লাগবে

  • ক্রোম ওয়েব ব্রাউজার
  • একটি জিমেইল অ্যাকাউন্ট
  • বিলিং অ্যাকাউন্ট সক্রিয় করা একটি ক্লাউড প্রজেক্ট

সকল স্তরের (শিক্ষানবিশ সহ) ডেভেলপারদের জন্য ডিজাইন করা এই কোডল্যাবটির নমুনা অ্যাপ্লিকেশনে পাইথন ব্যবহার করা হয়েছে। তবে, এখানে উপস্থাপিত ধারণাগুলো বোঝার জন্য পাইথন জ্ঞানের প্রয়োজন নেই।

২. 🚀 ওয়ার্কশপ উন্নয়ন সেটআপ প্রস্তুত করা

ধাপ ১: ক্লাউড কনসোলে সক্রিয় প্রজেক্ট নির্বাচন করুন

গুগল ক্লাউড কনসোলের প্রজেক্ট সিলেক্টর পেজে, একটি গুগল ক্লাউড প্রজেক্ট নির্বাচন করুন বা তৈরি করুন (আপনার কনসোলের উপরের বাম অংশ দেখুন)।

7758dd17c20c3094.png

এটিতে ক্লিক করলে, আপনি এই উদাহরণের মতো আপনার সমস্ত প্রকল্পের একটি তালিকা দেখতে পাবেন।

8c93fa37e7a7b856.png

লাল বাক্স দ্বারা নির্দেশিত মানটি হলো প্রজেক্ট আইডি এবং এই মানটি পুরো টিউটোরিয়াল জুড়ে ব্যবহার করা হবে।

আপনার ক্লাউড প্রজেক্টের জন্য বিলিং চালু আছে কিনা তা নিশ্চিত করুন। এটি পরীক্ষা করতে, আপনার উপরের বাম দিকের বারে থাকা বার্গার আইকনে ☰ ক্লিক করুন, যেটি নেভিগেশন মেনু দেখাবে এবং বিলিং মেনুটি খুঁজুন।

db07810b26fc61d6.png

যদি দেখেন "Google Cloud Platform Trial Billing Account" লিঙ্ক করা আছে , তাহলে আপনার প্রজেক্টটি এই টিউটোরিয়ালের জন্য ব্যবহারের জন্য প্রস্তুত। যদি তা না হয়, এই টিউটোরিয়ালের শুরুতে ফিরে যান এবং বিলিং অ্যাকাউন্টটি রিডিম করুন।

4f592f31aef0c40.png

ধাপ ২: ক্লাউড শেল-এর সাথে পরিচিত হন

টিউটোরিয়ালের বেশিরভাগ অংশেই আপনি ক্লাউড শেল ব্যবহার করবেন। গুগল ক্লাউড কনসোলের উপরে থাকা ‘Activate Cloud Shell’-এ ক্লিক করুন। যদি এটি আপনাকে অনুমোদন করতে বলে, তাহলে ‘Authorize’-এ ক্লিক করুন।

26f20e837ff06119.png

79b06cc89a99f840.png

ক্লাউড শেলে সংযুক্ত হওয়ার পর, আমাদের যাচাই করে দেখতে হবে যে শেলটি (বা টার্মিনালটি) আমাদের অ্যাকাউন্ট দিয়ে আগে থেকেই প্রমাণীকৃত আছে কি না।

gcloud auth list

আপনার ব্যক্তিগত জিমেইল যদি নিচের উদাহরণের মতো আউটপুট দেখায়, তাহলে সবকিছু ঠিক আছে।

Credentialed Accounts

ACTIVE: *
ACCOUNT: alvinprayuda@gmail.com

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

অন্যথায়, আপনার ব্রাউজার রিফ্রেশ করার চেষ্টা করুন এবং অনুরোধ করা হলে ' Authorize'- এ ক্লিক করুন (সংযোগ সমস্যার কারণে এটি বাধাগ্রস্ত হতে পারে)।

এরপর, আমাদের এটাও যাচাই করতে হবে যে শেলটি আপনার সঠিক প্রজেক্ট আইডিতে আগে থেকেই কনফিগার করা আছে কিনা। যদি আপনি টার্মিনালে $ আইকনের আগে ( ) বন্ধনীর ভেতরে কোনো মান দেখতে পান (নিচের স্ক্রিনশটে, মানটি হলো "a2a-agent-engine" ), তাহলে এই মানটি আপনার সক্রিয় শেল সেশনের জন্য কনফিগার করা প্রজেক্টটি নির্দেশ করে।

8365518c832055f.png

প্রদর্শিত মানটি যদি ইতিমধ্যেই সঠিক হয়, তাহলে আপনি পরবর্তী কমান্ডটি এড়িয়ে যেতে পারেন। তবে যদি এটি সঠিক না হয় বা অনুপস্থিত থাকে, তাহলে নিম্নলিখিত কমান্ডটি চালান।

gcloud config set project <YOUR_PROJECT_ID>

এরপর, গিটহাব থেকে এই কোডল্যাবের টেমপ্লেট ওয়ার্কিং ডিরেক্টরিটি ক্লোন করুন এবং নিম্নলিখিত কমান্ডটি চালান। এটি purchasing-concierge-a2a ডিরেক্টরিতে ওয়ার্কিং ডিরেক্টরিটি তৈরি করবে।

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

ধাপ ৩: ক্লাউড শেল এডিটর সম্পর্কে জানুন এবং অ্যাপ্লিকেশন ওয়ার্কিং ডিরেক্টরি সেটআপ করুন।

এখন, আমরা কোডিং করার জন্য আমাদের কোড এডিটর সেট আপ করতে পারি। এর জন্য আমরা ক্লাউড শেল এডিটর ব্যবহার করব।

ওপেন এডিটর বাটনে ক্লিক করুন, এটি একটি ক্লাউড শেল এডিটর খুলবে। 168eacea651b086c.png

এরপর, ক্লাউড শেল এডিটরের উপরের অংশে যান এবং File->Open Folder-এ ক্লিক করুন, আপনার ইউজারনেম ডিরেক্টরি খুঁজুন এবং purchasing-concierge-a2a ডিরেক্টরিটি খুঁজে বের করে OK বোতামে ক্লিক করুন। এটি নির্বাচিত ডিরেক্টরিটিকে প্রধান ওয়ার্কিং ডিরেক্টরি হিসেবে সেট করবে। এই উদাহরণে, ইউজারনেম হলো alvinprayuda , তাই ডিরেক্টরি পাথটি নিচে দেখানো হলো।

c87d2b76896d0c59.png

d5d829a1c43d7451.png

এখন, আপনার ক্লাউড শেল এডিটরটি দেখতে এইরকম হওয়া উচিত।

9b4793fa38e35af2.png

এখন, এডিটরের জন্য টার্মিনালটি খুলুন। আপনি মেনু বারে থাকা Terminal -> New Terminal- এ ক্লিক করে এটি করতে পারেন, অথবা Ctrl + Shift + C ব্যবহার করতে পারেন, এটি ব্রাউজারের নিচের অংশে একটি টার্মিনাল উইন্ডো খুলবে।

8635b60ae2f45bbc.jpeg

আপনার বর্তমান সক্রিয় টার্মিনালটি purchasing-concierge-a2a ওয়ার্কিং ডিরেক্টরির ভিতরে থাকা উচিত। আমরা এই কোডল্যাবে পাইথন ৩.১২ ব্যবহার করব এবং পাইথন ভার্সন ও ভার্চুয়াল এনভায়রনমেন্ট তৈরি ও পরিচালনার প্রয়োজনীয়তা সহজ করার জন্য uv পাইথন প্রজেক্ট ম্যানেজার ব্যবহার করব। এই uv প্যাকেজটি ক্লাউড শেলে আগে থেকেই ইনস্টল করা আছে।

.venv ডিরেক্টরিতে ভার্চুয়াল এনভায়রনমেন্টের জন্য প্রয়োজনীয় ডিপেন্ডেন্সিগুলো ইনস্টল করতে এই কমান্ডটি চালান।

uv sync --frozen

এই টিউটোরিয়ালের জন্য ঘোষিত ডিপেন্ডেন্সিগুলো, যেগুলো হলো a2a-sdk, google-adk, and gradio , দেখতে pyproject.toml ফাইলটি দেখুন।

এখন, নিচে দেখানো কমান্ডের মাধ্যমে আমাদের প্রয়োজনীয় এপিআইগুলো সক্রিয় করতে হবে। এতে কিছুটা সময় লাগতে পারে।

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

কমান্ডটি সফলভাবে কার্যকর হলে, আপনি নিচে দেখানো বার্তার মতো একটি বার্তা দেখতে পাবেন:

Operation "operations/..." finished successfully.

৩. 🚀 A2A সার্ভার রিমোট সেলার এজেন্টদের ক্লাউড রানে স্থাপন করা

এই ধাপে, আমরা লাল বাক্স দ্বারা চিহ্নিত এই দুটি রিমোট সেলার এজেন্ট স্থাপন করব। বার্গার এজেন্টটি CrewAI এজেন্ট ফ্রেমওয়ার্ক দ্বারা এবং পিজ্জা এজেন্টটি Langgraph এজেন্ট দ্বারা চালিত হবে।

87dbae9eff7781f9.png

৪. 🚀 বার্গার সেলার এজেন্ট - A2A সার্ভার স্থাপন করা হচ্ছে

বার্গার এজেন্ট সোর্স কোডটি remote_seller_agents/burger_agent ডিরেক্টরির অধীনে রয়েছে।

remote_seller_agents/burger_agent ডিরেক্টরির অধীনে থাকা সমস্ত ফাইলই আমাদের এজেন্টকে ক্লাউড রান-এ স্থাপন করার জন্য যথেষ্ট, যাতে এটি একটি পরিষেবা হিসাবে অ্যাক্সেসযোগ্য হতে পারে। এটি স্থাপন করতে নিম্নলিখিত কমান্ডটি চালান।

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 রুটে যান। ডেপ্লয় করা A2A সার্ভার এজেন্ট কার্ড অ্যাক্সেস করার জন্য এটিই হলো URL।

সফলভাবে স্থাপন করা হলে, এজেন্ট কার্ড অ্যাক্সেস করার সময় আপনি আপনার ব্রাউজারে নীচে দেখানো প্রতিক্রিয়ার মতো কিছু দেখতে পাবেন।

3d353be0e82ff46c.png

এটি বার্গার এজেন্ট কার্ডের তথ্য যা অনুসন্ধানের উদ্দেশ্যে সহজলভ্য হওয়া উচিত।

লক্ষ্য করুন যে এখানে url ভ্যালুটি এখনও http://0.0.0.0:8080/ এ সেট করা আছে। এই url ভ্যালুটিই A2A ক্লায়েন্টের বহির্বিশ্ব থেকে বার্তা পাঠানোর জন্য মূল তথ্য হওয়া উচিত, কিন্তু এটি সঠিকভাবে কনফিগার করা হয়নি।

আমাদের বার্গার এজেন্ট সার্ভিসের URL-এ HOST_OVERRIDE একটি অতিরিক্ত এনভায়রনমেন্ট ভ্যারিয়েবল যোগ করে এই ভ্যালুটি আপডেট করতে হবে।

এনভায়রনমেন্ট ভেরিয়েবলের মাধ্যমে এজেন্ট কার্ডে বার্গার এজেন্ট URL-এর মান আপডেট করা

বার্গার এজেন্ট সার্ভিসে HOST_OVERRIDE যোগ করতে, নিম্নলিখিত ধাপগুলি অনুসরণ করুন।

  1. আপনার ক্লাউড কনসোলের উপরের সার্চ বারে ক্লাউড রান (Cloud Run) অনুসন্ধান করুন।

f56ec00607eafb6f.png

  1. পূর্বে স্থাপন করা বার্গার-এজেন্ট ক্লাউড রান পরিষেবাতে ক্লিক করুন

9bf379c89e7fe625.png

  1. বার্গার-সার্ভিস ইউআরএলটি কপি করুন, তারপর এডিট-এ ক্লিক করুন এবং নতুন রিভিশনটি ডেপ্লয় করুন।

75a4a1a3b7fa3cd6.png

  1. তারপর, ভেরিয়েবল ও সিক্রেটস বিভাগে ক্লিক করুন

4fcaa1176de7038d.png

  1. এরপরে, 'Add variable'-এ ক্লিক করুন এবং HOST_OVERRIDE মান হিসেবে সার্ভিস URL-টি (যেটি https://burger-agent-xxxxxxxxx.us-central1.run.app প্যাটার্নের) সেট করুন।

c160f7d90d219129.png

  1. অবশেষে, আপনার সার্ভিসটি পুনরায় ডিপ্লয় করতে ডিপ্লয় বাটনে ক্লিক করুন।

763bbc02ceac0e28.png

আপনি যখন ব্রাউজারে https://burger-agent-xxxxxxxxx.us-central1.run.app/.well-known/agent.json এই ঠিকানায় বার্গার-এজেন্ট এজেন্ট কার্ডটি আবার অ্যাক্সেস করবেন, তখন url ভ্যালুটি ইতিমধ্যেই সঠিকভাবে কনফিগার করা থাকবে।

9acf6c51329d6e91.png

৫. 🚀 পিজ্জা সেলার এজেন্ট - A2A সার্ভার স্থাপন করা হচ্ছে

একইভাবে, পিজ্জা এজেন্ট সোর্স কোডটি remote_seller_agents/pizza_agent ডিরেক্টরির অধীনে রয়েছে।

পূর্ববর্তী বার্গার-এজেন্ট ডেপ্লয়মেন্ট ধাপের মতোই, remote_seller_agents/pizza_agent ডিরেক্টরির অধীনে থাকা সমস্ত ফাইলই আমাদের এজেন্টকে ক্লাউড রানে ডেপ্লয় করার জন্য যথেষ্ট, যাতে এটি একটি পরিষেবা হিসাবে অ্যাক্সেসযোগ্য হতে পারে। এটি ডেপ্লয় করতে নিম্নলিখিত কমান্ডটি চালান।

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 অংশটি একটি অনন্য শনাক্তকারী হিসেবে কাজ করবে।

বার্গার এজেন্টের ক্ষেত্রেও একই ব্যাপার, যখন আপনি ব্রাউজারের মাধ্যমে A2A সার্ভার এজেন্ট কার্ড অ্যাক্সেস করার জন্য ওই ডেপ্লয় করা পিজ্জা এজেন্ট সার্ভিসগুলোর https://pizza-agent-xxxxxxxxx.us-central1.run.app/.well-known/agent.json রুটে যাওয়ার চেষ্টা করেন, তখন এর এজেন্ট কার্ডে থাকা পিজ্জা এজেন্ট url ভ্যালুটি তখনও সঠিকভাবে কনফিগার করা থাকে না। এছাড়াও, আমাদের এর এনভায়রনমেন্ট ভেরিয়েবলে HOST_OVERRIDE যোগ করতে হবে।

এনভায়রনমেন্ট ভেরিয়েবলের মাধ্যমে এজেন্ট কার্ডে পিজা এজেন্ট URL-এর মান আপডেট করা

পিজ্জা এজেন্ট সার্ভিসে HOST_OVERRIDE যোগ করতে, নিম্নলিখিত ধাপগুলি অনুসরণ করুন।

  1. আপনার ক্লাউড কনসোলের উপরের সার্চ বারে ক্লাউড রান (Cloud Run) অনুসন্ধান করুন।

f56ec00607eafb6f.png

  1. পূর্বে ডেপ্লয় করা পিজ্জা-এজেন্ট ক্লাউড রান সার্ভিসে ক্লিক করুন

d9840497ae8afa2c.png

  1. সম্পাদনায় ক্লিক করুন এবং নতুন সংস্করণটি স্থাপন করুন

e45d52012bef34c6.png

  1. পিজ্জা-পরিষেবার URL-টি কপি করুন, তারপর Variable & Secrets সেকশনে ক্লিক করুন।

19265c4edc2be7ae.png

  1. এরপরে, 'Add variable'-এ ক্লিক করুন এবং HOST_OVERRIDE মান হিসেবে সার্ভিস URL-টি (যেটি https://pizza-agent-xxxxxxxxx.us-central1.run.app প্যাটার্নের) সেট করুন।

7a6bd93cb6f11b64.png

  1. অবশেষে, আপনার সার্ভিসটি পুনরায় ডিপ্লয় করতে ডিপ্লয় বাটনে ক্লিক করুন।

763bbc02ceac0e28.png

এখন, আপনি যখন ব্রাউজারে https://pizza-agent-xxxxxxxxx.us-central1.run.app/.well-known/agent.json এই পিজ্জা-এজেন্ট এজেন্ট কার্ডটি আবার অ্যাক্সেস করবেন, তখন url ভ্যালুটি ইতিমধ্যেই সঠিকভাবে কনফিগার করা থাকবে।

f682caa1d89c6b5d.png

এই পর্যায়ে, আমরা ইতিমধ্যেই ক্লাউড রান-এ বার্গার এবং পিজ্জা উভয় পরিষেবা সফলভাবে চালু করেছি।

৬. 🚀 পারচেজিং কনসিয়ার্জ স্থাপন করা - A2A ক্লায়েন্ট থেকে এজেন্ট ইঞ্জিন

এই ধাপে, আমরা পারচেজিং কনসিয়ার্জ এজেন্টকে নিযুক্ত করব। এই এজেন্টের সাথেই আমরা যোগাযোগ করব।

d62d062dd6959e8.png

আমাদের পারচেজিং কনসিয়ার্জ এজেন্টের সোর্স কোডটি purchasing_concierge ডিরেক্টরির অধীনে রয়েছে। এজেন্ট ইনিশিয়ালাইজেশনটি purchasing_concierge/purchasing_agent.py স্ক্রিপ্টে দেখা যাবে।

এটি স্থাপন করতে এই ধাপগুলো অনুসরণ করুন:

  1. প্রথমে, আমাদের ক্লাউড স্টোরেজে স্টেজিং স্টোরেজ তৈরি করতে হবে।
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}

এই এজেন্টটি বার্গার এবং পিজ্জা এজেন্টের সাথে যোগাযোগ করবে, তাই আমাদের উভয়ের জন্য সঠিক ক্রেডেনশিয়াল প্রদান করতে হবে। পূর্ববর্তী ধাপগুলোর ক্লাউড রান URL দিয়ে আমাদের PIZZA_SELLER_AGENT_URL এবং BURGER_SELLER_AGENT_URL আপডেট করতে হবে।

আপনি যদি এই বিষয়টি ভুলে যান, তাহলে ক্লাউড রান কনসোলে যান। আপনার কনসোলের উপরের সার্চ বারে 'Cloud Run' টাইপ করুন এবং ক্লাউড রান আইকনে রাইট ক্লিক করে এটিকে একটি নতুন ট্যাবে খুলুন।

f56ec00607eafb6f.png

নিচে দেখানো আমাদের পূর্বে চালু করা রিমোট সেলার এজেন্ট পরিষেবাগুলো আপনার দেখা উচিত।

561e3d125ae54e35.png

এখন সেই সার্ভিসগুলোর পাবলিক ইউআরএল দেখতে, সার্ভিসগুলোর যেকোনো একটিতে ক্লিক করুন এবং আপনাকে সার্ভিস ডিটেইলস পেজে নিয়ে যাওয়া হবে। আপনি উপরের অংশে রিজিয়ন ইনফরমেশনের ঠিক পাশেই ইউআরএলটি দেখতে পাবেন।

382c2094967718ae.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

সফলভাবে ডেপ্লয়মেন্টের পর এটি এইরকম একটি লগ দেখাবে। এটি আপনাকে এজেন্ট ইঞ্জিন রিসোর্সের নাম হিসেবে "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

এবং যখন আমরা এজেন্ট ইঞ্জিন ড্যাশবোর্ডে এটি পরিদর্শন করি, (সার্চ বারে 'এজেন্ট ইঞ্জিন' লিখে সার্চ করুন) তখন এটি আমাদের পূর্ববর্তী ডেপ্লয়মেন্ট দেখাবে।

765cdbdbbc3a94bc.png

আপনি আরও পরীক্ষা করে দেখতে পারেন যে এজেন্ট ইঞ্জিন রিসোর্স নামটি সেখানে প্রদর্শিত হচ্ছে কিনা। তারপর, আমরা এটি পরীক্ষা করার জন্য এই রিসোর্স নামটি ব্যবহার করতে পারি।

এরপরে, .env ফাইলে থাকা AGENT_ENGINE_RESOURCE_NAME কে এই মানটি দিয়ে আপডেট করুন । সঠিক এজেন্ট ইঞ্জিন রিসোর্স নামটি দিয়েছেন কিনা তা নিশ্চিত করুন। আপনার .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

এজেন্ট ইঞ্জিনে ডেপ্লয় করা এজেন্ট পরীক্ষা করা হচ্ছে

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 ক্লায়েন্টদের সাধারণ কার্যপ্রবাহ নিয়ে আলোচনা করা যাক।

৭. 🚀 ইন্টিগ্রেশন টেস্টিং এবং পেলোড ইন্সপেকশন

এখন চলুন একটি ওয়েব 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()`.

এরপর, টার্মিনালে http://0.0.0.0:8080 ইউআরএলটিতে Ctrl + ক্লিক করুন অথবা ওয়েব UI খোলার জন্য ওয়েব প্রিভিউ বোতামে ক্লিক করুন।

bf615f875b1d870.png

এইভাবে কথোপকথন করার চেষ্টা করুন:

  • আমাকে বার্গার এবং পিজ্জার মেনু দেখান
  • আমি ১টি বারবিকিউ চিকেন পিজ্জা এবং ১টি স্পাইসি কাজুন বার্গার অর্ডার করতে চাই।

এবং অর্ডারটি শেষ না হওয়া পর্যন্ত কথোপকথন চালিয়ে যান। মিথস্ক্রিয়াটি কীভাবে এগোচ্ছে এবং টুলটির কল ও রেসপন্স কী, তা খতিয়ে দেখুন। নিচের ছবিটি মিথস্ক্রিয়ার ফলাফলের একটি উদাহরণ।

e3ada3143680efff.png

f661b0721ec6fbb0.png

789782458635578e.png

5d66b087aed1743e.png

আমরা দেখতে পাচ্ছি যে দুটি ভিন্ন এজেন্টের সাথে যোগাযোগ করলে দুটি ভিন্ন আচরণ দেখা যায় এবং A2A এটি ভালোভাবে সামলাতে পারে। পিৎজা বিক্রেতা এজেন্ট সরাসরি আমাদের ক্রয় এজেন্টের অনুরোধ গ্রহণ করে, অন্যদিকে বার্গার এজেন্টের আমাদের অনুরোধ নিয়ে অগ্রসর হওয়ার আগে আমাদের নিশ্চিতকরণের প্রয়োজন হয় এবং আমাদের নিশ্চিতকরণের পর এজেন্ট সেই নিশ্চিতকরণটি বার্গার এজেন্টের কাছে পৌঁছে দিতে পারে।

এখন, আমরা A2A-এর মৌলিক ধারণাগুলো শেষ করেছি এবং দেখেছি কিভাবে এটিকে ক্লায়েন্ট ও সার্ভার আর্কিটেকচার হিসেবে বাস্তবায়ন করা হয়।

৮. 💡 [কোডের ব্যাখ্যা] A2A সার্ভারের ধারণা এবং বাস্তবায়ন

remote_seller_agents/*/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

...

পিজ্জা এজেন্ট

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,
)

...

যেমনটা আপনারা দেখতে পাচ্ছেন, ক্লায়েন্ট এজেন্টের (ADK) তুলনায় এই দুটি এজেন্ট সম্পূর্ণ ভিন্ন ফ্রেমওয়ার্ক (CrewAI এবং Langgraph) দিয়ে তৈরি করা হয়েছে। A2A-এর ক্ষেত্রে এটি কোনো সমস্যা নয়, কারণ একে অপরের সাথে যোগাযোগের জন্য তাদের অভ্যন্তরীণ কোড শেয়ার করার প্রয়োজন হয় না; এক্ষেত্রে কোন ফ্রেমওয়ার্ক, কোন ভাষা ব্যবহার করা হচ্ছে বা কোথায় সেগুলোকে ডেপ্লয় করা হয়েছে, তা কোনো বিষয় নয়।

A2A সার্ভারের মূল উপাদানসমূহ

এখন এ২এ সার্ভারের মূল ধারণা ও উপাদানগুলো নিয়ে আলোচনা করা যাক।

এজেন্ট কার্ড

প্রতিটি A2A সার্ভারে অবশ্যই একটি এজেন্ট কার্ড থাকতে হবে, যা /.well-known/agent.json রিসোর্সে অ্যাক্সেসযোগ্য। এর উদ্দেশ্য হলো A2A ক্লায়েন্টের ডিসকভারি পর্যায়কে সমর্থন করা, যা এজেন্টকে কীভাবে অ্যাক্সেস করতে হবে এবং এর সমস্ত ক্ষমতা সম্পর্কে জানতে সম্পূর্ণ তথ্য ও প্রেক্ষাপট প্রদান করবে। এটি অনেকটা Swagger বা Postman ব্যবহার করে তৈরি করা সুসংগঠিত API ডকুমেন্টেশনের মতোই।

এটি আমাদের স্থাপন করা বার্গার এজেন্ট কার্ডের বিষয়বস্তু।

{
  "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 পাইথন এসডিকে ব্যবহার করে এজেন্ট কার্ডের বাস্তবায়ন করা হয়েছে, এর বাস্তবায়নের জন্য নিচের 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 : এজেন্টের সাথে যোগাযোগের ঠিকানা

এই কনফিগারেশনে আমরা একটি ডাইনামিক এজেন্ট হোস্ট ইউআরএল তৈরির ব্যবস্থা রেখেছি, যাতে লোকাল টেস্টিং এবং ক্লাউড ডেপ্লয়মেন্টের মধ্যে পরিবর্তন করা সহজ হয়, আর একারণেই পূর্ববর্তী ধাপে আমাদের HOST_OVERRIDE ভ্যারিয়েবলটি যোগ করতে হয়েছিল।

টাস্ক কিউ এবং এজেন্ট এক্সিকিউটর

একটি A2A সার্ভার বিভিন্ন এজেন্ট বা ব্যবহারকারীর কাছ থেকে আসা অনুরোধগুলো সামলাতে পারে এবং প্রতিটি কাজকে নিখুঁতভাবে আলাদা করতে সক্ষম হতে পারে। এগুলোর প্রেক্ষাপট আরও ভালোভাবে বোঝার জন্য, আপনি নিচের ছবিটি দেখতে পারেন।

10c75db36741da90.jpeg

সুতরাং, প্রতিটি A2A সার্ভারের আগত টাস্কগুলো ট্র্যাক করতে এবং সে সম্পর্কে সঠিক তথ্য সংরক্ষণ করতে সক্ষম হওয়া উচিত। A2A সার্ভারে এই চ্যালেঞ্জ মোকাবেলার জন্য A2A SDK মডিউল সরবরাহ করে। প্রথমে, আমরা আগত অনুরোধটি কীভাবে পরিচালনা করতে চাই, তার জন্য লজিক ইনস্ট্যানশিয়েট করতে পারি। AgentExecutor অ্যাবস্ট্রাক্ট ক্লাসটি ইনহেরিট করার মাধ্যমে আমরা টাস্ক এক্সিকিউশন এবং ক্যান্সেলেশন কীভাবে পরিচালনা করতে চাই, তা নিয়ন্ত্রণ করতে পারি। এই উদাহরণ ইমপ্লিমেন্টেশনটি remote_seller_agents/burger_agent/agent_executor.py মডিউলে দেখা যেতে পারে (পিজ্জা বিক্রেতার ক্ষেত্রেও একই পথ প্রযোজ্য)।

...

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())

...

উপরের কোডে, আমরা একটি মৌলিক প্রক্রিয়াকরণ পদ্ধতি প্রয়োগ করেছি যেখানে অনুরোধ আসার সাথে সাথে এজেন্ট সরাসরি চালু হবে এবং চালু হওয়ার পর সম্পন্ন কাজের ইভেন্ট পাঠাবে। তবে, আমরা এখানে বাতিলকরণ পদ্ধতিটি প্রয়োগ করিনি, কারণ এটিকে একটি স্বল্পস্থায়ী প্রক্রিয়া হিসেবে বিবেচনা করা হয়েছিল।

এক্সিকিউটরটি বিল্ড করার পর, আমরা সরাসরি বিল্ট-ইন 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 রুটের বাস্তবায়ন এবং A2A প্রোটোকল সমর্থন করার জন্য POST এন্ডপয়েন্ট প্রদান করবে।

সারসংক্ষেপ

সংক্ষেপে, এখন পর্যন্ত আমরা পাইথন এসডিকে ব্যবহার করে যে এ২এ সার্ভারটি স্থাপন করেছি, সেটি নিচের দুটি কার্যকারিতা সমর্থন করতে পারে:

  1. /.well-known/agent.json রুটে এজেন্ট কার্ড প্রকাশ করা হচ্ছে
  2. ইন-মেমরি টাস্ক কিউইং ব্যবহার করে JSON-RPC অনুরোধ পরিচালনা করুন

এই কার্যকারিতাগুলি চালু করার প্রবেশপথটি __main__.py স্ক্রিপ্টে ( remote_seller_agents/burger_agent অথবা remote_seller_agents/pizza_agent ) পরীক্ষা করা যেতে পারে।

৯. 💡 [কোডের ব্যাখ্যা] এজেন্ট ইঞ্জিন ডেপ্লয়মেন্ট

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 ব্যবহার করে তৈরি করা হয়েছে এবং এজেন্ট ইঞ্জিনে স্থাপন করা হয়েছে।

ভার্টেক্স এআই এজেন্ট ইঞ্জিন হলো এমন কিছু পরিষেবার সমষ্টি যা ডেভেলপারদের প্রোডাকশনে এআই এজেন্ট ডেপ্লয়, পরিচালনা এবং স্কেল করতে সক্ষম করে। এটি প্রোডাকশনে এজেন্ট স্কেল করার জন্য প্রয়োজনীয় পরিকাঠামো পরিচালনা করে, যাতে আমরা অ্যাপ্লিকেশন তৈরির উপর মনোযোগ দিতে পারি। আপনি এই ডকুমেন্টটিতে এ সম্পর্কে আরও পড়তে পারেন। যদি আগে আমাদের এজেন্ট সার্ভিস ডেপ্লয় করার জন্য প্রয়োজনীয় ফাইল (যেমন মূল সার্ভার স্ক্রিপ্ট এবং ডকারফাইল) প্রস্তুত করতে হতো, তবে এক্ষেত্রে আমরা ADK এবং এজেন্ট ইঞ্জিনের সমন্বয় ব্যবহার করে নিজস্ব ব্যাকএন্ড সার্ভিস তৈরি করার প্রয়োজন ছাড়াই সরাসরি পাইথন স্ক্রিপ্ট থেকে আমাদের এজেন্ট ডেপ্লয় করতে পারি।

এই টিউটোরিয়ালে আমরা 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 এজেন্ট স্থাপন করার জন্য এই ধাপগুলো অনুসরণ করতে হবে। প্রথমে, আমাদের ADK root_agent থেকে একটি AdkApp অবজেক্ট তৈরি করতে হবে। এরপর, adk_app অবজেক্টটি প্রদান করে, requirements ফিল্ডে প্রয়োজনীয়তাগুলো উল্লেখ করে, extra_packages এ এজেন্ট ডিরেক্টরির পাথ নির্দিষ্ট করে (প্রয়োজনে এখানে অন্যান্য ডিরেক্টরি ও ফাইলও প্রদান করা যেতে পারে) এবং প্রয়োজনীয় এনভায়রনমেন্ট ভেরিয়েবলগুলো দিয়ে agent_engines.create মেথডটি রান করতে পারি।

১০. 💡 [কোডের ব্যাখ্যা] A2A ক্লায়েন্টের ধারণা এবং বাস্তবায়ন

e7e3224d05b725f0.jpeg

উপরে দেখানো চিত্রটি হলো A2A মিথস্ক্রিয়ার সাধারণ প্রবাহ:

  1. ক্লায়েন্ট /.well-known/agent.json রুটে প্রদত্ত রিমোট এজেন্ট URL-এ যেকোনো প্রকাশিত এজেন্ট কার্ড খুঁজে বের করার চেষ্টা করবে।
  2. তারপর, প্রয়োজনে এটি সেই এজেন্টের কাছে বার্তা এবং প্রয়োজনীয় মেটাডেটা প্যারামিটার (যেমন সেশন আইডি, ঐতিহাসিক প্রেক্ষাপট, ইত্যাদি) সহ একটি বার্তা পাঠাবে। সার্ভার এই বার্তাটিকে সম্পন্ন করার জন্য একটি কাজ (Task) হিসেবে গণ্য করবে।
  3. A2A সার্ভার অনুরোধটি প্রসেস করে, যদি সার্ভারটি পুশ নোটিফিকেশন সমর্থন করে, তবে এটি টাস্ক প্রসেসিং চলাকালীন কিছু নোটিফিকেশনও প্রকাশ করতে সক্ষম হবে (এই কার্যকারিতাটি এই কোডল্যাবের আওতার বাইরে)।
  4. কাজ শেষ হলে, A2A সার্ভারটি রেসপন্স আর্টিফ্যাক্টটি ক্লায়েন্টের কাছে ফেরত পাঠাবে।

উপরোক্ত মিথস্ক্রিয়াগুলোর কয়েকটি মূল বস্তু হলো এই আইটেমগুলো (আরও বিস্তারিত এখানে পড়া যাবে):

  • বার্তা: একজন ক্লায়েন্ট এবং একজন রিমোট এজেন্টের মধ্যে যোগাযোগের একটি পর্যায়।
  • টাস্ক : A2A দ্বারা পরিচালিত কাজের মৌলিক একক, যা একটি অনন্য আইডি দ্বারা চিহ্নিত।
  • আর্টিফ্যাক্ট: কোনো কাজের ফলস্বরূপ এজেন্ট কর্তৃক উৎপাদিত একটি আউটপুট (যেমন, একটি ডকুমেন্ট, ছবি, স্ট্রাকচার্ড ডেটা), যা বিভিন্ন অংশ দ্বারা গঠিত।
  • পার্ট: কোনো মেসেজ বা আর্টিফ্যাক্টের অন্তর্ভুক্ত বিষয়বস্তুর ক্ষুদ্রতম একক। পার্ট হতে পারে টেক্সট, ছবি, ভিডিও, ফাইল ইত্যাদি।

কার্ড আবিষ্কার

যখন 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 ক্লায়েন্টের দৃষ্টিকোণ থেকে, তারা এটিকে সার্ভারে পাঠানো একটি মেসেজ হিসেবে দেখে। ক্লায়েন্টের কাছ থেকে আসা মেসেজকে কোন টাস্ক হিসেবে সংজ্ঞায়িত করা হবে এবং টাস্কটি সম্পন্ন করার জন্য ক্লায়েন্টের ইন্টারঅ্যাকশনের প্রয়োজন আছে কিনা, তা সার্ভারের উপর নির্ভর করে। আপনি এই ডকুমেন্টেশনে টাস্ক লাইফসাইকেল সম্পর্কে আরও বিস্তারিত পড়তে পারেন। এর উচ্চতর ধারণাটি নিচে চিত্রিত করা হলো:

4f2a4c908e0fc75.jpeg

1810d38d264cb6a.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 সার্ভারকে নমনীয়ভাবে কনফিগার করা যেতে পারে। এই পদ্ধতিগুলোর বিস্তারিত বিবরণ এই ডকুমেন্টে পড়া যাবে।

১১. 🎯 চ্যালেঞ্জ

এখন, আপনি কি প্রয়োজনীয় ফাইল প্রস্তুত করে নিজে থেকে ক্লাউডে Gradio অ্যাপটি স্থাপন করতে পারবেন? চ্যালেঞ্জটি গ্রহণ করার সময় এসেছে!

১২. 🧹 পরিষ্কার করা

এই কোডল্যাবে ব্যবহৃত রিসোর্সগুলির জন্য আপনার গুগল ক্লাউড অ্যাকাউন্টে চার্জ হওয়া এড়াতে, এই ধাপগুলি অনুসরণ করুন:

  1. গুগল ক্লাউড কনসোলে, 'ম্যানেজ রিসোর্সেস' পৃষ্ঠায় যান।
  2. প্রজেক্ট তালিকা থেকে, আপনি যে প্রজেক্টটি মুছতে চান সেটি নির্বাচন করুন এবং তারপর ডিলিট বোতামে ক্লিক করুন।
  3. ডায়ালগ বক্সে প্রজেক্ট আইডি টাইপ করুন এবং তারপর প্রজেক্টটি মুছে ফেলার জন্য 'শাট ডাউন'-এ ক্লিক করুন।
  4. বিকল্পভাবে, আপনি কনসোলে Cloud Run এবং Agent Engine- এ গিয়ে, এইমাত্র ডেপ্লয় করা সার্ভিসটি নির্বাচন করে ডিলিট করে দিতে পারেন।