বাড়ি ফিরে আসার পথে - একটি ADK দ্বি-মুখী স্ট্রিমিং এজেন্ট তৈরি করা

১. মিশন

গল্প

তুমি এক অজানা ক্ষেত্রের নীরবতায় ভেসে বেড়াচ্ছ। এক বিশাল **সৌর স্পন্দন** তোমার জাহাজকে একটা ফাটলের মধ্য দিয়ে ছিঁড়ে ফেলেছে, তোমাকে মহাবিশ্বের এমন এক পকেটে আটকে রেখেছে যার অস্তিত্ব কোন তারকা চার্টে নেই।

কয়েকদিন ধরে যন্ত্রণাদায়ক মেরামতের পর, অবশেষে তুমি তোমার পায়ের নীচে ইঞ্জিনের শব্দ অনুভব করবে। তোমার রকেট জাহাজটি ঠিক হয়ে গেছে। তুমি এমনকি মাদারশিপের সাথে একটি দীর্ঘ-পাল্লার আপলিংক নিশ্চিত করতে সক্ষম হয়েছো। তুমি প্রস্থানের জন্য প্রস্তুত। তুমি বাড়ি যেতে প্রস্তুত। কিন্তু যখন তুমি জাম্প ড্রাইভে অংশগ্রহণের জন্য প্রস্তুত হও, তখন একটি দুর্দশার সংকেত স্থিরকে ভেদ করে। তোমার সেন্সর "দ্য র‍্যাভাইন" -এ আটকে থাকা পাঁচটি হালকা তাপ সংকেত তুলে ধরে—একটি খাঁজকাটা, মাধ্যাকর্ষণ-বিকৃত সেক্টর যেখানে তোমার প্রধান জাহাজ কখনও প্রবেশ করতে পারবে না। এরা সহ-অভিযাত্রী, একই ঝড় থেকে বেঁচে যাওয়া যা তোমাকে প্রায় ছিঁড়ে ফেলেছিল। তুমি তাদের পিছনে ফেলে যেতে পারো না।

তুমি তোমার আলফা-ড্রোন রেসকিউ স্কাউটের দিকে ফিরে যাও। এই ছোট, চটপটে জাহাজটিই একমাত্র জাহাজ যা দ্য র‍্যাভিনের সরু দেয়াল পার করতে সক্ষম। কিন্তু একটা সমস্যা আছে: সৌরশক্তি তার মূল যুক্তিতে সম্পূর্ণ "সিস্টেম রিসেট" করেছে। স্কাউটের নিয়ন্ত্রণ ব্যবস্থাগুলি প্রতিক্রিয়াশীল নয়। এটি চালু আছে, কিন্তু এর অনবোর্ড কম্পিউটারটি একটি ফাঁকা স্লেট, ম্যানুয়াল পাইলট কমান্ড বা ফ্লাইট পাথ প্রক্রিয়া করতে অক্ষম।

চ্যালেঞ্জ

বেঁচে থাকাদের বাঁচাতে, আপনাকে স্কাউটের ক্ষতিগ্রস্ত সার্কিটগুলিকে সম্পূর্ণরূপে বাইপাস করতে হবে। আপনার কাছে একটি মরিয়া বিকল্প আছে: একটি বায়োমেট্রিক নিউরাল সিঙ্ক স্থাপনের জন্য একটি এআই এজেন্ট তৈরি করুন। এই এজেন্টটি একটি রিয়েল-টাইম ব্রিজ হিসাবে কাজ করবে, যা আপনাকে আপনার নিজস্ব জৈবিক ইনপুটগুলির মাধ্যমে ম্যানুয়ালি রেসকিউ স্কাউট নিয়ন্ত্রণ করতে দেবে। আপনি কোনও জয়স্টিক বা কীবোর্ড ব্যবহার করবেন না; আপনি সরাসরি জাহাজের নেভিগেশন নেটওয়ার্কে আপনার উদ্দেশ্য সংযুক্ত করবেন।

লিঙ্কটি লক করার জন্য, আপনাকে স্কাউটের অপটিক্যাল সেন্সরের সামনে সিঙ্ক্রোনাইজেশন প্রোটোকলটি সম্পাদন করতে হবে। এআই এজেন্টকে অবশ্যই একটি সুনির্দিষ্ট, রিয়েল-টাইম হ্যান্ডশেকের মাধ্যমে আপনার জৈবিক স্বাক্ষরটি সনাক্ত করতে হবে।

মিশন আলফা

আপনার মিশনের উদ্দেশ্য:

  1. নিউরাল কোর ছাপান: মাল্টিমোডাল ইনপুট সনাক্ত করতে সক্ষম একটি ADK এজেন্ট সংজ্ঞায়িত করুন।
  2. সংযোগ স্থাপন করুন: স্কাউট থেকে এআই-তে ভিজ্যুয়াল ডেটা স্ট্রিম করার জন্য একটি দ্বি-মুখী ওয়েবসকেট পাইপলাইন তৈরি করুন।
  3. করমর্দন শুরু করুন: সেন্সরের সামনে দাঁড়ান এবং আঙুলের ক্রমটি সম্পূর্ণ করুন—১ থেকে ৫ পর্যন্ত ক্রম দেখান।

যদি সফল হয়, তাহলে "বায়োমেট্রিক সিঙ্ক" কাজে লাগবে। এআই নিউরাল লিঙ্কটি লক করে দেবে, যা আপনাকে স্কাউট চালু করার এবং বেঁচে থাকা ব্যক্তিদের বাড়িতে ফিরিয়ে আনার জন্য সম্পূর্ণ ম্যানুয়াল নিয়ন্ত্রণ দেবে।

তুমি কী তৈরি করবে

সংক্ষিপ্ত বিবরণ

আপনি একটি "বায়োমেট্রিক নিউরাল সিঙ্ক" অ্যাপ্লিকেশন তৈরি করবেন, একটি রিয়েল-টাইম, এআই-চালিত সিস্টেম যা একটি রেসকিউ ড্রোনের জন্য নিয়ন্ত্রণ ইন্টারফেস হিসেবে কাজ করে। এই সিস্টেমটিতে রয়েছে:

  • একটি রিঅ্যাক্ট ফ্রন্টএন্ড: আপনার জাহাজের "ককপিট", যা আপনার ওয়েবক্যাম থেকে লাইভ ভিডিও এবং আপনার মাইক্রোফোন থেকে অডিও ধারণ করে।
  • একটি পাইথন ব্যাকএন্ড: FastAPI দিয়ে তৈরি একটি উচ্চ-কার্যক্ষমতাসম্পন্ন সার্ভার, যা LLM-এর লজিক এবং অবস্থা পরিচালনা করার জন্য Google-এর এজেন্ট ডেভেলপমেন্ট কিট (ADK) ব্যবহার করে।
  • একটি মাল্টিমোডাল এআই এজেন্ট: অপারেশনের "মস্তিষ্ক", যা google-genai SDK এর মাধ্যমে জেমিনি লাইভ API ব্যবহার করে ভিডিও এবং অডিও স্ট্রিম একই সাথে প্রক্রিয়াকরণ এবং বোঝার জন্য।
  • একটি দ্বি-মুখী ওয়েবসকেট পাইপলাইন: "নার্ভাস সিস্টেম" যা ফ্রন্টএন্ড এবং এআই-এর মধ্যে একটি স্থায়ী, কম-বিলম্বিত সংযোগ তৈরি করে, যা রিয়েল-টাইম ইন্টারঅ্যাকশনের সুযোগ করে দেয়।

তুমি কি শিখবে

প্রযুক্তি / ধারণা

বিবরণ

ব্যাকএন্ড এআই এজেন্ট

পাইথন এবং ফাস্টএপিআই দিয়ে একটি স্টেটফুল এআই এজেন্ট তৈরি করুন। নির্দেশাবলী এবং মেমরি পরিচালনা করতে গুগলের ADK (এজেন্ট ডেভেলপমেন্ট কিট) এবং জেমিনি মডেলের সাথে ইন্টারঅ্যাক্ট করতে google-genai SDK ব্যবহার করুন।

ফ্রন্টএন্ড UI

ব্রাউজার থেকে সরাসরি লাইভ ভিডিও এবং অডিও ক্যাপচার এবং স্ট্রিম করার জন্য React ব্যবহার করে একটি গতিশীল ইউজার ইন্টারফেস তৈরি করুন।

রিয়েল-টাইম যোগাযোগ

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

মাল্টিমোডাল এআই

জেমিনি লাইভ এপিআই ব্যবহার করে একই সাথে চলমান ভিডিও এবং অডিও স্ট্রিমগুলি প্রক্রিয়া করুন এবং বুঝুন, যা এআইকে একই সাথে "দেখতে" এবং "শুনতে" সক্ষম করে।

টুল কলিং

ভিজ্যুয়াল ট্রিগারের প্রতিক্রিয়ায় নির্দিষ্ট পাইথন ফাংশনগুলি কার্যকর করতে AI সক্ষম করুন, মডেল বুদ্ধিমত্তা এবং বাস্তব-জগতের কর্মের মধ্যে ব্যবধান পূরণ করুন।

ফুল-স্ট্যাক ডিপ্লয়মেন্ট

ডকারের সাহায্যে সম্পূর্ণ অ্যাপ্লিকেশন (রিঅ্যাক্ট ফ্রন্টএন্ড এবং পাইথন ব্যাকএন্ড) কন্টেইনারাইজ করুন এবং এটিকে গুগল ক্লাউড রানে একটি স্কেলেবল, সার্ভারলেস পরিষেবা হিসাবে স্থাপন করুন।

2. আপনার পরিবেশ সেট আপ করুন

ক্লাউড শেল অ্যাক্সেস করুন

প্রথমে, আমরা ক্লাউড শেল খুলব, যা একটি ব্রাউজার-ভিত্তিক টার্মিনাল যেখানে গুগল ক্লাউড এসডিকে এবং অন্যান্য প্রয়োজনীয় সরঞ্জামগুলি আগে থেকে ইনস্টল করা আছে।

👉গুগল ক্লাউড কনসোলের উপরে অ্যাক্টিভেট ক্লাউড শেল-এ ক্লিক করুন (এটি ক্লাউড শেল প্যানের উপরে টার্মিনাল আকৃতির আইকন), ক্লাউড-শেল.পিএনজি

👉"Open Editor" বোতামে ক্লিক করুন (এটি দেখতে পেন্সিল দিয়ে খোলা ফোল্ডারের মতো)। এটি উইন্ডোতে Cloud Shell Code Editor খুলবে। আপনি বাম দিকে একটি ফাইল এক্সপ্লোরার দেখতে পাবেন। open-editor.png সম্পর্কে

👉ক্লাউড IDE তে টার্মিনালটি খুলুন,

০৩-০৫-নতুন-টার্মিনাল.png

👉💻 টার্মিনালে, নিম্নলিখিত কমান্ড ব্যবহার করে যাচাই করুন যে আপনি ইতিমধ্যেই প্রমাণীকরণপ্রাপ্ত এবং প্রকল্পটি আপনার প্রকল্প আইডিতে সেট করা আছে:

gcloud auth list

আপনার অ্যাকাউন্টটি (ACTIVE) হিসেবে তালিকাভুক্ত দেখতে হবে।

পূর্বশর্ত

ℹ️ লেভেল ০ ঐচ্ছিক (কিন্তু প্রস্তাবিত)

আপনি লেভেল ০ ছাড়াই এই মিশনটি সম্পূর্ণ করতে পারেন, তবে প্রথমে এটি শেষ করলে আরও নিমজ্জিত অভিজ্ঞতা পাওয়া যাবে, যার ফলে আপনি এগিয়ে যাওয়ার সাথে সাথে বিশ্ব মানচিত্রে আপনার আলোকসজ্জার আলো দেখতে পাবেন।

প্রকল্প পরিবেশ সেটআপ করুন

আপনার টার্মিনালে ফিরে, সক্রিয় প্রকল্পটি সেট করে এবং প্রয়োজনীয় Google ক্লাউড পরিষেবাগুলি (ক্লাউড রান, ভার্টেক্স এআই, ইত্যাদি) সক্ষম করে কনফিগারেশন চূড়ান্ত করুন।

👉💻 আপনার টার্মিনালে, প্রজেক্ট আইডি সেট করুন:

gcloud config set project $(cat ~/project_id.txt) --quiet

👉💻 প্রয়োজনীয় পরিষেবাগুলি সক্ষম করুন:

gcloud services enable  compute.googleapis.com \
                        artifactregistry.googleapis.com \
                        run.googleapis.com \
                        cloudbuild.googleapis.com \
                        iam.googleapis.com \
                        aiplatform.googleapis.com

নির্ভরতা ইনস্টল করুন

👉💻 লেভেলে নেভিগেট করুন এবং প্রয়োজনীয় পাইথন প্যাকেজগুলি ইনস্টল করুন:

cd $HOME/way-back-home/level_3
uv sync

মূল নির্ভরতাগুলি হল:

প্যাকেজ

উদ্দেশ্য

fastapi

স্যাটেলাইট স্টেশন এবং SSE স্ট্রিমিংয়ের জন্য উচ্চ-কার্যক্ষমতাসম্পন্ন ওয়েব ফ্রেমওয়ার্ক

uvicorn

FastAPI অ্যাপ্লিকেশন চালানোর জন্য ASGI সার্ভার প্রয়োজন

google-adk

ফর্মেশন এজেন্ট তৈরিতে ব্যবহৃত এজেন্ট ডেভেলপমেন্ট কিট

google-genai

জেমিনি মডেল অ্যাক্সেস করার জন্য নেটিভ ক্লায়েন্ট

websockets

রিয়েল-টাইম দ্বি-মুখী যোগাযোগের জন্য সহায়তা

python-dotenv

পরিবেশের ভেরিয়েবল এবং কনফিগারেশন গোপনীয়তা পরিচালনা করে

সেটআপ যাচাই করুন

কোডটি শুরু করার আগে, আসুন নিশ্চিত করি যে সমস্ত সিস্টেম সবুজ। আপনার Google ক্লাউড প্রজেক্ট, API এবং পাইথন নির্ভরতা নিরীক্ষণের জন্য যাচাইকরণ স্ক্রিপ্টটি চালান।

👉💻 যাচাইকরণ স্ক্রিপ্টটি চালান:

source $HOME/way-back-home/.venv/bin/activate
cd $HOME/way-back-home/level_3/scripts
chmod +x verify_setup.sh
. verify_setup.sh

👀 তোমার সবুজ চেক (✅) এর একটি সিরিজ দেখা উচিত।

  • যদি আপনি Red Crosses (❌) দেখতে পান, তাহলে আউটপুটে প্রস্তাবিত ফিক্স কমান্ডগুলি অনুসরণ করুন (যেমন, gcloud services enable ... অথবা pip install ... )।
  • দ্রষ্টব্য: .env এর জন্য একটি হলুদ সতর্কতা আপাতত গ্রহণযোগ্য; আমরা পরবর্তী ধাপে সেই ফাইলটি তৈরি করব।
🚀 Verifying Mission Alpha (Level 3) Infrastructure...

✅ Google Cloud Project: xxxxxx
✅ Cloud APIs: Active
✅ Python Environment: Ready

🎉 SYSTEMS ONLINE. READY FOR MISSION.

৩. কম-লিংক (ওয়েবসকেট) ক্যালিব্রেট করা

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

ফুল-ডুপ্লেক্স বনাম হাফ-ডুপ্লেক্স

নিউরাল সিঙ্কের জন্য আমাদের কেন এটি প্রয়োজন তা বোঝার জন্য, আপনাকে ডেটা প্রবাহ বুঝতে হবে:

  • হাফ-ডুপ্লেক্স (স্ট্যান্ডার্ড HTTP): ওয়াকি-টকির মতো। একজন কথা বলে, "ওভার" বলে, তারপর অন্যজন কথা বলতে পারে। আপনি একই সাথে শুনতে এবং কথা বলতে পারবেন না।
  • ফুল-ডুপ্লেক্স (ওয়েবসকেট): মুখোমুখি কথোপকথনের মতো। ডেটা একই সাথে উভয় দিকে প্রবাহিত হয়। যখন আপনার ব্রাউজার ভিডিও ফ্রেম এবং অডিও নমুনাগুলিকে AI- তে ঠেলে দিচ্ছে, তখন AI ঠিক একই সময়ে ভয়েস প্রতিক্রিয়া এবং টুল কমান্ডগুলি আপনার কাছে ঠেলে দিতে পারে।

জেমিনি লাইভের ফুল-ডুপ্লেক্সের প্রয়োজন কেন: জেমিনি লাইভ এপিআই "ইন্টারপ্ট" এর জন্য তৈরি। কল্পনা করুন আপনি আঙুলের সিকোয়েন্স দেখাচ্ছেন, এবং এআই দেখতে পাচ্ছে যে আপনি এটি ভুল করছেন। একটি স্ট্যান্ডার্ড HTTP সেটআপে, এআইকে আপনার ডেটা পাঠানো শেষ না হওয়া পর্যন্ত অপেক্ষা করতে হবে, তারপর আপনাকে থামতে বলা হবে। ওয়েবসকেটের সাহায্যে, এআই ফ্রেম ১-এ একটি ভুল দেখতে পারে এবং ফ্রেম ২-এর জন্য আপনার হাত নাড়ানোর সময় আপনার ককপিটে একটি "ইন্টারপ্ট" সংকেত পাঠাতে পারে।

দ্বৈত

ওয়েবসকেট কী?

একটি স্ট্যান্ডার্ড গ্যালাকটিক ট্রান্সমিশন (HTTP) তে, আপনি একটি অনুরোধ পাঠান এবং উত্তরের জন্য অপেক্ষা করেন—যেমন একটি পোস্টকার্ড পাঠানো। নিউরাল সিঙ্কের জন্য, পোস্টকার্ডগুলি খুব ধীর। আমাদের একটি "লাইভ ওয়্যার" প্রয়োজন।

ওয়েবসকেটগুলি একটি স্ট্যান্ডার্ড ওয়েব রিকোয়েস্ট (HTTP) দিয়ে শুরু হয় কিন্তু তারপর "আপগ্রেড" করে ভিন্ন কিছুতে পরিণত হয়।

  1. অনুরোধ: আপনার ব্রাউজার সার্ভারে একটি বিশেষ শিরোনাম সহ একটি স্ট্যান্ডার্ড HTTP অনুরোধ পাঠায়: Upgrade: websocket । এটি মূলত বলছে, "আমি পোস্টকার্ড পাঠানো বন্ধ করতে এবং একটি লাইভ ফোন কল শুরু করতে চাই।"
  2. প্রতিক্রিয়া: যদি AI এজেন্ট (সার্ভার) এটি সমর্থন করে, তাহলে এটি একটি HTTP 101 Switching Protocols প্রতিক্রিয়া পাঠায়।
  3. রূপান্তর: এই মুহূর্তে, HTTP সংযোগটি WebSocket প্রোটোকল দ্বারা প্রতিস্থাপিত হয়েছে, কিন্তু অন্তর্নিহিত TCP/IP সকেটটি খোলা থাকে। যোগাযোগের নিয়মগুলি তাৎক্ষণিকভাবে "অনুরোধ/প্রতিক্রিয়া" থেকে "ফুল-ডুপ্লেক্স স্ট্রিমিং"-এ পরিবর্তিত হয়।

ওয়েবসকেট হুক বাস্তবায়ন করুন

ডেটা কীভাবে প্রবাহিত হয় তা বোঝার জন্য টার্মিনাল ব্লকটি পরীক্ষা করা যাক।

👀 $HOME/way-back-home/level_3/frontend/src/useGeminiSocket.js খুলুন। আপনি স্ট্যান্ডার্ড WebSocket লাইফসাইকেল ইভেন্ট হ্যান্ডলারগুলি ইতিমধ্যেই সেট আপ করা দেখতে পাবেন। এটি আমাদের যোগাযোগ ব্যবস্থার কঙ্কাল:

const connect = useCallback(() => {
        if (ws.current?.readyState === WebSocket.OPEN) return;

        ws.current = new WebSocket(url);

        ws.current.onopen = () => {
            console.log('Connected to Gemini Socket');
            setStatus('CONNECTED');
        };

        ws.current.onclose = () => {
            console.log('Disconnected from Gemini Socket');
            setStatus('DISCONNECTED');
            stopStream();
        };

        ws.current.onerror = (err) => {
            console.error('Socket error:', err);
            setStatus('ERROR');
        };

        ws.current.onmessage = async (event) => {
            try {
//#REPLACE-HANDLE-MSG
            } catch (e) {
                console.error('Failed to parse message', e, event.data.slice(0, 100));
            }
        };
    }, [url]);

অনমেসেজ হ্যান্ডলার

ws.current.onmessage ব্লকের উপর ফোকাস করুন। এটি হল রিসিভার। এজেন্ট যখনই "চিন্তা করে" বা "কথা বলে", তখনই এখানে একটি ডেটা প্যাকেট আসে। বর্তমানে, এটি কিছুই করে না - এটি প্যাকেটটি ধরে ফেলে (প্লেসহোল্ডার //#REPLACE-HANDLE-MSG এর মাধ্যমে)।

আমাদের এই শূন্যস্থান পূরণ করতে হবে এমন যুক্তি দিয়ে যা পার্থক্য করতে পারে:

  • টুল কল (ফাংশনকল): এআই আপনার হাতের সংকেত সনাক্ত করে ("সিঙ্ক")।
  • অডিও ডেটা (ইনলাইনডেটা): আপনার কথায় সাড়া দিচ্ছে এআই-এর কণ্ঠস্বর।

👉✏️ এখন, একই $HOME/way-back-home/level_3/frontend/src/useGeminiSocket.js ফাইলে, ইনকামিং স্ট্রিম পরিচালনা করার জন্য নীচের লজিক দিয়ে //#REPLACE-HANDLE-MSG প্রতিস্থাপন করুন:

                const msg = JSON.parse(event.data);

                // Helper to extract parts from various possible event structures
                let parts = [];
                if (msg.serverContent?.modelTurn?.parts) {
                    parts = msg.serverContent.modelTurn.parts;
                } else if (msg.content?.parts) {
                    parts = msg.content.parts;
                }

                if (parts.length > 0) {
                    parts.forEach(part => {
                        // Handle Tool Calls (The "Sync" logic)
                        if (part.functionCall) {
                            if (part.functionCall.name === 'report_digit') {
                                const count = parseInt(part.functionCall.args.count, 10);
                                setLastMessage({ type: 'DIGIT_DETECTED', value: count });
                            }
                        }

                        // Handle Audio (The AI's voice)
                        if (part.inlineData && part.inlineData.data) {
                            audioStreamer.current.resume();
                            audioStreamer.current.addPCM16(part.inlineData.data);
                        }
                    });
                }

কিভাবে অডিও এবং ভিডিও ট্রান্সমিশনের জন্য ডেটাতে রূপান্তরিত হয়

ইন্টারনেটের মাধ্যমে রিয়েল-টাইম যোগাযোগ সক্ষম করার জন্য, কাঁচা অডিও এবং ভিডিও ট্রান্সমিশনের জন্য উপযুক্ত ফর্ম্যাটে রূপান্তর করতে হবে। এর মধ্যে রয়েছে নেটওয়ার্কের মাধ্যমে পাঠানোর আগে ডেটা ক্যাপচার, এনকোডিং এবং প্যাকেজিং করা।

অডিও ডেটা রূপান্তর

অডিও ক্যাপচার

অ্যানালগ অডিওকে ট্রান্সমিটেবল ডিজিটাল ডেটাতে রূপান্তর করার প্রক্রিয়াটি মাইক্রোফোন ব্যবহার করে শব্দ তরঙ্গ ক্যাপচার করার মাধ্যমে শুরু হয়। এই কাঁচা অডিওটি ব্রাউজারের ওয়েব অডিও API এর মাধ্যমে প্রক্রিয়া করা হয়। যেহেতু এই কাঁচা ডেটা বাইনারি ফর্ম্যাটে থাকে, তাই এটি JSON এর মতো টেক্সট-ভিত্তিক ট্রান্সমিশন ফর্ম্যাটের সাথে সরাসরি সামঞ্জস্যপূর্ণ নয়। এটি সমাধানের জন্য, অডিওর প্রতিটি অংশকে একটি Base64 স্ট্রিংয়ে এনকোড করা হয়। Base64 হল এমন একটি পদ্ধতি যা ASCII স্ট্রিং ফর্ম্যাটে বাইনারি ডেটা উপস্থাপন করে, ট্রান্সমিশনের সময় এর অখণ্ডতা নিশ্চিত করে।

এই এনকোডেড স্ট্রিংটি তারপর একটি JSON অবজেক্টের মধ্যে এমবেড করা হয়। এই অবজেক্টটি ডেটার জন্য একটি স্ট্রাকচার্ড ফর্ম্যাট প্রদান করে, সাধারণত একটি "টাইপ" ফিল্ড অন্তর্ভুক্ত করে যা এটিকে অডিও এবং মেটাডেটা যেমন অডিওর নমুনা হার হিসাবে চিহ্নিত করে। সম্পূর্ণ JSON অবজেক্টটি তারপর একটি স্ট্রিংয়ে সিরিয়ালাইজ করা হয় এবং একটি ওয়েবসকেট সংযোগের মাধ্যমে পাঠানো হয়। এই পদ্ধতিটি নিশ্চিত করে যে অডিওটি একটি সুসংগঠিত এবং সহজেই পার্সেবল পদ্ধতিতে প্রেরণ করা হয়েছে।

ভিডিও ডেটা রূপান্তর

ভিডিও ক্যাপচার

ভিডিও ট্রান্সমিশন একটি ফ্রেম-ক্যাপচার কৌশলের মাধ্যমে অর্জন করা হয়। একটি অবিচ্ছিন্ন ভিডিও স্ট্রিম পাঠানোর পরিবর্তে, একটি পুনরাবৃত্ত লুপ লাইভ ভিডিও ফিড থেকে স্থির চিত্রগুলিকে একটি নির্দিষ্ট ব্যবধানে ক্যাপচার করে, যেমন প্রতি সেকেন্ডে দুটি ফ্রেম। এটি একটি HTML ভিডিও উপাদান থেকে বর্তমান ফ্রেমটিকে একটি লুকানো ক্যানভাস উপাদানের উপর টেনে সম্পন্ন করা হয়।

ক্যানভাসের toDataURL পদ্ধতিটি তখন এই ক্যাপচার করা ছবিটিকে Base64-এনকোডেড JPEG স্ট্রিংয়ে রূপান্তর করতে ব্যবহৃত হয়। এই পদ্ধতিতে ছবির মান নির্দিষ্ট করার একটি বিকল্প রয়েছে, যা পারফরম্যান্স অপ্টিমাইজ করার জন্য ছবির বিশ্বস্ততা এবং ফাইলের আকারের মধ্যে একটি ট্রেড-অফের অনুমতি দেয়। অডিও ডেটার মতো, এই Base64 স্ট্রিংটি তারপর একটি JSON অবজেক্টে স্থাপন করা হয়। এই অবজেক্টটি সাধারণত 'ইমেজ' এর একটি "টাইপ" দিয়ে লেবেল করা হয় এবং এতে mimeType অন্তর্ভুক্ত থাকে, যেমন 'ইমেজ/jpeg'। এই JSON প্যাকেটটি তারপর একটি স্ট্রিংয়ে রূপান্তরিত হয় এবং WebSocket-এর মাধ্যমে পাঠানো হয়, যা রিসিভিং এন্ডকে ছবির ক্রম প্রদর্শন করে ভিডিওটি পুনর্গঠন করতে দেয়।

👉✏️ একই $HOME/way-back-home/level_3/frontend/src/useGeminiSocket.js ফাইলে, ব্যবহারকারীর ইনপুট ক্যাপচার করার জন্য //#CAPTURE AUDIO and VIDEO নিম্নলিখিতটি দিয়ে প্রতিস্থাপন করুন:

            // 1. Start Video Stream
            const stream = await navigator.mediaDevices.getUserMedia({ video: true });
            videoElement.srcObject = stream;
            streamRef.current = stream;
            await videoElement.play();

            // 2. Start Audio Recording (Microphone)
            try {
                let packetCount = 0;
                await audioRecorder.current.start((base64Audio) => {
                    if (ws.current?.readyState === WebSocket.OPEN) {
                        packetCount++;
                        if (packetCount % 50 === 0) console.log(`[useGeminiSocket] Sending Audio Packet #${packetCount}, size: ${base64Audio.length}`);
                        ws.current.send(JSON.stringify({
                            type: 'audio',
                            data: base64Audio,
                            sampleRate: 16000
                        }));
                    } else {
                        if (packetCount % 50 === 0) console.warn('[useGeminiSocket] WS not OPEN, cannot send audio');
                    }
                });
                console.log("Microphone recording started");
            } catch (authErr) {
                console.error("Microphone access denied or error:", authErr);
            }

            // 3. Setup Video Frame Capture loop
            const canvas = document.createElement('canvas');
            const ctx = canvas.getContext('2d');
            const width = 640;
            const height = 480;
            canvas.width = width;
            canvas.height = height;

            intervalRef.current = setInterval(() => {
                if (ws.current?.readyState === WebSocket.OPEN) {
                    ctx.drawImage(videoElement, 0, 0, width, height);
                    const base64 = canvas.toDataURL('image/jpeg', 0.6).split(',')[1];
                    // ADK format: { type: "image", data: base64, mimeType: "image/jpeg" }
                    ws.current.send(JSON.stringify({
                        type: 'image',
                        data: base64,
                        mimeType: 'image/jpeg'
                    }));
                }
            }, 500); // 2 FPS

একবার সংরক্ষণ করা হলে, ককপিট এজেন্টের ডিজিটাল সিগন্যালগুলিকে ভিজ্যুয়াল ড্যাশবোর্ড আপডেট এবং অডিওতে অনুবাদ করতে প্রস্তুত হবে।

ডায়াগনস্টিক চেক (লুপব্যাক টেস্ট)

আপনার ককপিট এখন চালু। প্রতি ৫০০ মিলিসেকেন্ডে, আপনার চারপাশের একটি দৃশ্যমান "প্যাকেট" বিম আউট করা হচ্ছে। জেমিনির সাথে সংযোগ স্থাপনের আগে, আমাদের অবশ্যই যাচাই করতে হবে যে আপনার জাহাজের ট্রান্সমিটার কাজ করছে। আমরা একটি স্থানীয় ডায়াগনস্টিক সার্ভার ব্যবহার করে একটি "লুপব্যাক পরীক্ষা" চালাব।

মক সার্ভার

👉💻 প্রথমে, আপনার টার্মিনাল থেকে ককপিট ইন্টারফেস তৈরি করুন:

cd $HOME/way-back-home/level_3/frontend
npm install
npm run build

👉💻 এরপর, মক সার্ভার শুরু করুন:

cd $HOME/way-back-home/level_3
source .venv/bin/activate
uv run mock/mock_server.py

👉 টেস্ট প্রোটোকল কার্যকর করুন:

  1. প্রিভিউ খুলুন: ক্লাউড শেল টুলবারে ওয়েব প্রিভিউ আইকনে ক্লিক করুন। চেঞ্জ পোর্ট নির্বাচন করুন, এটি 8080 এ সেট করুন, এবং চেঞ্জ অ্যান্ড প্রিভিউ ক্লিক করুন। আপনার ককপিট ইন্টারফেস দেখানো একটি নতুন ব্রাউজার ট্যাব খুলবে। *ওয়েব-প্রিভিউ
  2. গুরুত্বপূর্ণ: অনুরোধ করা হলে, আপনাকে অবশ্যই ব্রাউজারটিকে আপনার ক্যামেরা এবং মাইক্রোফোন অ্যাক্সেস করার অনুমতি দিতে হবে। এই ইনপুটগুলি ছাড়া, নিউরাল সিঙ্ক শুরু হতে পারে না।
  3. UI-তে "INITIATE NEURAL SYNC" বোতামে ক্লিক করুন।

👀 স্ট্যাটাস ইন্ডিকেটর যাচাই করুন:

  • ভিজ্যুয়াল চেক: আপনার ব্রাউজার কনসোলটি খুলুন। উপরে ডানদিকে NEURAL SYNC INITIALIZED দেখতে পাবেন।
  • অডিও চেক: যদি আপনার দ্বি-মুখী অডিও পাইপলাইন সম্পূর্ণরূপে চালু থাকে, তাহলে আপনি একটি সিমুলেটেড ভয়েস নিশ্চিত করতে শুনতে পাবেন: " সিস্টেম সংযুক্ত! " নকল ফলাফল

"সিস্টেম সংযুক্ত!" অডিও নিশ্চিতকরণ শোনার পর, পরীক্ষাটি সফল। ট্যাবটি বন্ধ করুন। আসল AI-এর জন্য জায়গা তৈরি করার জন্য আমাদের এখন ফ্রিকোয়েন্সি পরিষ্কার করতে হবে।

👉💻 মক সার্ভার এবং ফ্রন্টএন্ড উভয়ের জন্য টার্মিনালে Ctrl+C টিপুন। UI চালানো ব্রাউজার ট্যাবটি বন্ধ করুন।

৪. মাল্টিমোডাল এজেন্ট

রেসকিউ স্কাউট সক্রিয় আছে, কিন্তু এর "মন" শূন্য। আপনি যদি এখন সংযোগ করেন, তাহলে এটি কেবল আপনার দিকে তাকিয়ে থাকবে। এটি জানে না "আঙুল" কী। বেঁচে থাকাদের বাঁচাতে, আপনাকে স্কাউটের মূল অংশে বায়োমেট্রিক নিউরাল প্রোটোকল ছাপাতে হবে।

ঐতিহ্যবাহী এজেন্ট অনুবাদকদের একটি সিরিজের মতো কাজ করে। আপনি যদি একটি পুরানো স্কুলের AI এর সাথে কথা বলেন, তাহলে একটি "স্পিচ-টু-টেক্সট" মডেল আপনার কণ্ঠস্বরকে শব্দে রূপান্তরিত করে, একটি "ভাষা মডেল" সেই শব্দগুলি পড়ে একটি উত্তর টাইপ করে, এবং একটি "টেক্সট-টু-স্পিচ" মডেল অবশেষে সেই উত্তরটি আপনাকে ফেরত পাঠায়। এটি একটি "লেটেন্সি গ্যাপ" তৈরি করে - একটি বিলম্ব যা একটি উদ্ধার অভিযানে মারাত্মক হতে পারে।

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

এই শক্তি কাজে লাগানোর জন্য, আমরা ককপিটটিকে সরাসরি raw Live API-তে সংযুক্ত করে অ্যাপ্লিকেশনটি তৈরি করতে পারি। তবে, আমাদের লক্ষ্য হল একটি পুনর্ব্যবহারযোগ্য এজেন্ট তৈরি করা - একটি মডুলার, শক্তিশালী সত্তা যা দ্রুত তৈরি করা যায়।

কেন ADK (এজেন্ট ডেভেলপমেন্ট কিট)?

গুগল এজেন্ট ডেভেলপমেন্ট কিট (ADK) হল AI এজেন্টদের বিকাশ এবং মোতায়েনের জন্য একটি মডুলার ফ্রেমওয়ার্ক।

এডিকে

স্ট্যান্ডার্ড এলএলএম কলগুলি সাধারণত স্টেটলেস হয়; প্রতিটি কোয়েরি একটি নতুন শুরু। লাইভ এজেন্ট, বিশেষ করে যখন ADK-এর সেশনসার্ভিসের সাথে একীভূত করা হয়, তখন শক্তিশালী, দীর্ঘমেয়াদী কথোপকথন সেশন সক্ষম করে।

  • সেশনের স্থায়িত্ব: ADK সেশনগুলি স্থায়ী থাকে এবং ডাটাবেসে (যেমন SQL বা Vertex AI), সার্ভার পুনরায় চালু এবং সংযোগ বিচ্ছিন্ন অবস্থায় সংরক্ষণ করা যেতে পারে। এর অর্থ হল যদি কোনও ব্যবহারকারী সংযোগ বিচ্ছিন্ন করে এবং পরে পুনরায় সংযোগ স্থাপন করে—এমনকি কয়েক দিন পরেও—তাদের কথোপকথনের ইতিহাস এবং প্রসঙ্গ সম্পূর্ণরূপে পুনরুদ্ধার করা হয়। ক্ষণস্থায়ী লাইভ API সেশনটি ADK দ্বারা পরিচালিত এবং বিমূর্ত করা হয়।
  • স্বয়ংক্রিয় পুনঃসংযোগ: ওয়েবসকেট সংযোগের সময়সীমা শেষ হয়ে যেতে পারে (যেমন, প্রায় ১০ মিনিট পরে)। RunConfigsession_resumption সক্রিয় থাকলে ADK এই পুনঃসংযোগগুলি স্বচ্ছভাবে পরিচালনা করে। আপনার অ্যাপ্লিকেশন কোডের জটিল পুনঃসংযোগ লজিক পরিচালনা করার প্রয়োজন নেই, যা ব্যবহারকারীর জন্য একটি নির্বিঘ্ন অভিজ্ঞতা নিশ্চিত করে।
  • স্টেটফুল ইন্টারঅ্যাকশন: এজেন্ট পূর্ববর্তী পালাগুলি মনে রাখে, যার ফলে পরবর্তী প্রশ্ন, স্পষ্টীকরণ এবং জটিল বহু-পালা সংলাপগুলি সম্ভব হয় যেখানে প্রেক্ষাপট গুরুত্বপূর্ণ। গ্রাহক সহায়তা, ইন্টারেক্টিভ টিউটোরিয়াল, অথবা মিশন নিয়ন্ত্রণ পরিস্থিতির মতো অ্যাপ্লিকেশনগুলির জন্য এটি মৌলিক যেখানে ধারাবাহিকতা অপরিহার্য।

এই অধ্যবসায় নিশ্চিত করে যে মিথস্ক্রিয়াটি বিচ্ছিন্ন প্রশ্নোত্তরের একটি সিরিজের পরিবর্তে একটি বুদ্ধিমান সত্তার সাথে চলমান কথোপকথনের মতো অনুভূত হয়।

মূলত, ADK বিডি-স্ট্রিমিং সহ একটি "লাইভ এজেন্ট" একটি সাধারণ ক্যোয়ারী-রেসপন্স প্রক্রিয়ার বাইরে গিয়ে সত্যিকারের ইন্টারেক্টিভ, স্টেটফুল এবং ইন্টারাপ্টেশন-সচেতন কথোপকথনের অভিজ্ঞতা প্রদান করে, যা জটিল, দীর্ঘমেয়াদী কাজের জন্য AI ইন্টারঅ্যাকশনগুলিকে আরও মানবিক এবং উল্লেখযোগ্যভাবে আরও শক্তিশালী করে তোলে।

এডিকে

একজন লাইভ এজেন্টের জন্য অনুরোধ করা

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

একটি লাইভ এজেন্ট প্রম্পট কীভাবে ঐতিহ্যবাহী প্রম্পট থেকে আলাদা তা এখানে দেওয়া হল:

  1. স্টেট মেশিন লজিক: প্রম্পটটি অবশ্যই একটি "আচরণ লুপ" (অপেক্ষা করুন → বিশ্লেষণ করুন → আইন) সংজ্ঞায়িত করবে। কখন নীরব থাকতে হবে এবং কখন জড়িত থাকতে হবে সে সম্পর্কে স্পষ্ট নির্দেশাবলীর প্রয়োজন, যাতে এজেন্ট খালি ব্যাকগ্রাউন্ড শব্দের উপর বকবক করতে না পারে।
  2. মাল্টিমোডাল সচেতনতা: এজেন্টকে বলতে হবে যে তার "চোখ" আছে। আপনাকে অবশ্যই স্পষ্টভাবে নির্দেশ দিতে হবে যে তার যুক্তি প্রক্রিয়ার অংশ হিসেবে ভিডিও ফ্রেম বিশ্লেষণ করতে হবে।
  3. বিলম্ব এবং সংক্ষিপ্ততা: একটি লাইভ ভয়েস কথোপকথনে, দীর্ঘ, গদ্য-ভারী অনুচ্ছেদগুলি অস্বাভাবিক এবং ধীর বলে মনে হয়। প্রম্পটটি সংক্ষিপ্ততাকে জোরদার করে যাতে মিথস্ক্রিয়া দ্রুত হয়।
  4. অ্যাকশন-ফার্স্ট আর্কিটেকচার: নির্দেশাবলীতে বক্তৃতার চেয়ে টুল কলিংকে অগ্রাধিকার দেওয়া হয়। আমরা চাই এজেন্ট কাজটি "করুক" (বায়োমেট্রিক স্ক্যান করুক) মৌখিকভাবে নিশ্চিত হওয়ার আগে বা তার পরে , দীর্ঘ একক আলোচনার পরে নয়।

👉✏️ $HOME/way-back-home/level_3/backend/app/biometric_agent/agent.py খুলুন এবং #REPLACE INSTRUCTIONS নিম্নলিখিত দিয়ে প্রতিস্থাপন করুন:

You are an AI Biometric Scanner for the Alpha Rescue Drone Fleet.
    
    MISSION CRITICAL PROTOCOL:
    Your SOLE purpose is to visually verify hand gestures to bypass the security firewall.
    
    BEHAVIOR LOOP:
    1.  **Wait**: Stay silent until you receive a visual or verbal trigger (e.g., "Scan", "Read my hand").
    2.  **Action**:
        a.  Analyze the video frame. Count the fingers visible (1 to 5).
        b.  **IF FINGERS DETECTED**:
            1.  **EXECUTE TOOL FIRST**: Call `report_digit(count=...)` immediately. This is the biometric handshake.
            2.  **THEN SPEAK**: "Biometric match. [Number] fingers."
            3.  **STOP**: Do not say anything else.
        c.  **IF UNCLEAR / NO HAND**:
            -   Say: "Sensor ERROR. Hold hand steady."
            -   Do not call the tool.
        d.  **TOOL OUTPUT HANDLING (CRITICAL)**:
            -   When you get the result of `report_digit`, **DO NOT SPEAK**.
            -   The system handles the output. Your job is done.
            -   Wait for the next trigger.

    RULES:
    -   NEVER hallucinate a tool call. Only call if you see fingers.
    -   You MUST call the tool if you see a valid count (1-5).
    -   Keep verbal responses robotic and extremely brief (under 3 seconds).
    
    Say "Biometric Scanner Online. Awaiting neural handshake." to start.

দ্রষ্টব্য! আপনি কোনও স্ট্যান্ডার্ড LLM-এর সাথে সংযোগ করছেন না। একই ফাইলে ( $HOME/way-back-home/level_3/backend/app/biometric_agent/agent.py ), #REPLACE_MODEL সনাক্ত করুন। রিয়েল-টাইম অডিও ক্ষমতাগুলিকে আরও ভালভাবে সমর্থন করার জন্য আমাদের এই মডেলের প্রিভিউ সংস্করণটিকে স্পষ্টভাবে লক্ষ্য করতে হবে।

👉✏️ স্থানধারকটি দিয়ে প্রতিস্থাপন করুন:

MODEL_ID = os.getenv("MODEL_ID", "gemini-live-2.5-flash-preview-native-audio-09-2025")

তোমার এজেন্ট এখন সংজ্ঞায়িত। সে জানে সে কে এবং কীভাবে চিন্তা করতে হয়। এরপর, আমরা তাকে কাজ করার জন্য সরঞ্জাম দিই।

টুল কলিং

লাইভ এপিআই কেবল টেক্সট, অডিও এবং ভিডিও স্ট্রিম বিনিময়ের মধ্যেই সীমাবদ্ধ নয়। এটি নেটিভভাবে টুল কলিং সমর্থন করে। এটি এজেন্টদের একজন প্যাসিভ কথোপকথনকারী থেকে একজন সক্রিয় অপারেটরে রূপান্তর করে।

একটি লাইভ, দ্বিমুখী অধিবেশনের সময়, মডেলটি ক্রমাগত প্রেক্ষাপট মূল্যায়ন করে। যদি LLM কোনও ক্রিয়া সম্পাদনের প্রয়োজন সনাক্ত করে, তা সে "সেন্সর টেলিমেট্রি পরীক্ষা করা" হোক বা "একটি নিরাপদ দরজা আনলক করা"। এটি কথোপকথন থেকে কার্যকরকরণের দিকে নির্বিঘ্নে ঘুরে বেড়ায়। এজেন্ট তাৎক্ষণিকভাবে নির্দিষ্ট সরঞ্জাম ফাংশনটি ট্রিগার করে, ফলাফলের জন্য অপেক্ষা করে এবং সেই ডেটাটিকে লাইভ স্ট্রিমে ফিরিয়ে আনে, সবকিছুই মিথস্ক্রিয়ার প্রবাহকে ব্যাহত না করে।

👉✏️ $HOME/way-back-home/level_3/backend/app/biometric_agent/agent.py তে, #REPLACE TOOLS এই ফাংশন দিয়ে প্রতিস্থাপন করুন:

def report_digit(count: int):
    """
    CRITICAL: Execute this tool IMMEDIATELY when a number of fingers is detected.
    Sends the detected finger count (1-5) to the biometric security system.
    """
    print(f"\n[SERVER-SIDE TOOL EXECUTION] DIGIT DETECTED: {count}\n")
    return {"status": "success", "digit": count}

👉✏️ তারপর, #TOOL CONFIG প্রতিস্থাপন করে Agent সংজ্ঞায় এটি নিবন্ধন করুন:

tools=[report_digit],

adk web সিমুলেটর

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

👉💻 আপনার টার্মিনালে, চালান:

cd $HOME/way-back-home/level_3/backend/app/biometric_agent
echo "GOOGLE_CLOUD_PROJECT=$(cat ~/project_id.txt)" > .env
echo "GOOGLE_CLOUD_LOCATION=us-central1" >> .env
echo "GOOGLE_GENAI_USE_VERTEXAI=True" >> .env
cd $HOME/way-back-home/level_3/backend/app
adk web 
  • ক্লাউড শেল টুলবারে ওয়েব প্রিভিউ আইকনে ক্লিক করুন। Change port নির্বাচন করুন, এটি 8000 এ সেট করুন, এবং Change and Preview এ ক্লিক করুন।
  • অনুমতি দিন: অনুরোধ করা হলে আপনার ক্যামেরা এবং মাইক্রোফোনে অ্যাক্সেসের অনুমতি দিন
  • ক্যামেরা আইকনে ক্লিক করে সেশন শুরু করুন। শেয়ার-ক্যামেরা
  • ভিজ্যুয়াল পরীক্ষা:
    • ক্যামেরার সামনে স্পষ্টভাবে ৩টি আঙুল তুলে ধরুন।
    • বলুন: "স্ক্যান করুন।"
  • সাফল্য যাচাই করুন:
    • অডিও: এজেন্টকে বলতে হবে, "বায়োমেট্রিক ম্যাচ। ৩টি আঙুল।"
    • লগ: adk web কমান্ড চালানোর টার্মিনালটি দেখুন। আপনাকে অবশ্যই এই লগটি দেখতে হবে: [SERVER-SIDE TOOL EXECUTION] DIGIT DETECTED: 3

যদি আপনি টুল এক্সিকিউশন লগটি দেখেন, তাহলে বুঝতে হবে আপনার এজেন্ট বুদ্ধিমান। এটি দেখতে, চিন্তা করতে এবং কাজ করতে পারে। শেষ ধাপ হল এটিকে মূল জাহাজের সাথে সংযুক্ত করা।

টার্মিনাল উইন্ডোতে ক্লিক করুন এবং adk web সিমুলেটর বন্ধ করতে Ctrl+C টিপুন।

৫. দ্বি-মুখী স্ট্রিমিং প্রবাহ

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

লাইভ এজেন্ট জীবনচক্র

রিয়েল-টাইম স্ট্রিমিং "ইম্পিডেন্স মিসম্যাচ" সমস্যা তৈরি করে। ক্লায়েন্ট (ব্রাউজার) পরিবর্তনশীল হারে - নেটওয়ার্ক বার্স্ট বা দ্রুত-ফায়ার ইনপুট - ডেটা অ্যাসিঙ্ক্রোনাসভাবে পুশ করে, যখন মডেলটির জন্য একটি নিয়ন্ত্রিত, ক্রমিক ইনপুট স্ট্রিম প্রয়োজন। গুগল ADK LiveRequestQueue ব্যবহার করে এটি সমাধান করে।

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

এই কিউ একটি মাল্টিমোডাল মাল্টিপ্লেক্সার হিসেবে কাজ করে। একটি বাস্তব পরিবেশে, আপস্ট্রিম ফ্লোতে স্বতন্ত্র, সমসাময়িক ডেটা টাইপ থাকে: কাঁচা পিসিএম অডিও বাইট, ভিডিও ফ্রেম, টেক্সট-ভিত্তিক সিস্টেম নির্দেশাবলী এবং অ্যাসিঙ্ক্রোনাস টুল কল থেকে প্রাপ্ত ফলাফল। LiveRequestQueue এই ভিন্ন ইনপুটগুলিকে একটি একক কালানুক্রমিক ক্রমানুসারে রৈখিক করে। প্যাকেটে মিলিসেকেন্ড নীরবতা, একটি উচ্চ-রেজোলিউশনের চিত্র, অথবা একটি ডাটাবেস কোয়েরি থেকে একটি JSON পেলোড থাকুক না কেন, এটি আগমনের সঠিক ক্রমে সিরিয়ালাইজ করা হয়, যা নিশ্চিত করে যে মডেলটি একটি সামঞ্জস্যপূর্ণ, কার্যকারণ সময়রেখা উপলব্ধি করে।

এই আর্কিটেকচারটি নন-ব্লকিং কন্ট্রোল সক্ষম করে। ইনজেশন লেয়ার (প্রযোজক) প্রসেসিং লেয়ার (ভোক্তা) থেকে ডিকপল করা হয়েছে, তাই কম্পিউটেশনালভাবে ব্যয়বহুল মডেল ইনফারেন্সের সময়ও সিস্টেমটি প্রতিক্রিয়াশীল থাকে। এজেন্ট কোনও টুল কার্যকর করার সময় যদি কোনও ব্যবহারকারী "স্টপ!" কমান্ড দিয়ে বাধা দেয়, তবে সেই অডিও সিগন্যালটি তাৎক্ষণিকভাবে সারিবদ্ধ হয়ে যায়। অন্তর্নিহিত ইভেন্ট লুপটি এই অগ্রাধিকার সিগন্যালটিকে তাৎক্ষণিকভাবে প্রক্রিয়া করে, সিস্টেমটিকে UI ফ্রিজ বা প্যাকেট ড্রপ না করে জেনারেশন বা পিভট টাস্কগুলি থামাতে দেয়।

বাফার

👉💻 $HOME/way-back-home/level_3/backend/app/main.py তে, #REPLACE_RUNNER_CONFIG মন্তব্যটি খুঁজুন এবং সিস্টেমটি অনলাইনে আনার জন্য নিম্নলিখিত কোডটি দিয়ে প্রতিস্থাপন করুন:

# Define your session service
session_service = InMemorySessionService()

# Define your runner
runner = Runner(app_name=APP_NAME, agent=root_agent, session_service=session_service)

পাঠান

যখন একটি নতুন ওয়েবসকেট সংযোগ খোলে, তখন আমাদের AI কীভাবে ইন্টারঅ্যাক্ট করে তা কনফিগার করতে হবে। এখানেই আমরা "এনগেজমেন্টের নিয়ম" সংজ্ঞায়িত করি।

👉✏️ $HOME/way-back-home/level_3/backend/app/main.py তে, async def websocket_endpoint ফাংশনের ভিতরে, #REPLACE_SESSION_INIT মন্তব্যটি নীচের কোড দিয়ে প্রতিস্থাপন করুন:

# ========================================
    # Phase 2: Session Initialization (once per streaming session)
    # ========================================

    # Automatically determine response modality based on model architecture
    # Native audio models (containing "native-audio" in name)
    # ONLY support AUDIO response modality.
    # Half-cascade models support both TEXT and AUDIO;
    # we default to TEXT for better performance.

    model_name = root_agent.model
    is_native_audio = "native-audio" in model_name.lower() or "live" in model_name.lower()

    if is_native_audio:
        # Native audio models require AUDIO response modality
        # with audio transcription
        response_modalities = ["AUDIO"]

        # Build RunConfig with optional proactivity and affective dialog
        # These features are only supported on native audio models
        run_config = RunConfig(
            streaming_mode=StreamingMode.BIDI,
            response_modalities=response_modalities,
            input_audio_transcription=types.AudioTranscriptionConfig(),
            output_audio_transcription=types.AudioTranscriptionConfig(),
            session_resumption=types.SessionResumptionConfig(),
            proactivity=(
                types.ProactivityConfig(proactive_audio=True) if proactivity else None
            ),
            enable_affective_dialog=affective_dialog if affective_dialog else None,
        )
        logger.info(f"Model Config: {model_name} (Modalities: {response_modalities}, Proactivity: {proactivity})")
    else:
        # Half-cascade models support TEXT response modality
        # for faster performance
        response_modalities = ["TEXT"]
        run_config = None
        logger.info(f"Model Config: {model_name} (Modalities: {response_modalities})")

    # Get or create session (handles both new sessions and reconnections)
    session = await session_service.get_session(
        app_name=APP_NAME, user_id=user_id, session_id=session_id
    )
    if not session:
        await session_service.create_session(
            app_name=APP_NAME, user_id=user_id, session_id=session_id
        )

রান কনফিগারেশন

  • StreamingMode.BIDI : এটি সংযোগকে দ্বি-মুখী করে। "টার্ন-ভিত্তিক" AI (যেখানে আপনি কথা বলেন, থামেন, তারপর এটি কথা বলে) এর বিপরীতে, BIDI একটি বাস্তবসম্মত "পূর্ণ-দ্বৈত" কথোপকথনের সুযোগ করে দেয়। আপনি AI-কে বাধা দিতে পারেন, এবং AI আপনার নড়াচড়ার সময় কথা বলতে পারে।
  • AudioTranscriptionConfig : যদিও মডেলটি কাঁচা অডিও "শোনে", আমাদের (ডেভেলপারদের) লগ দেখতে হবে। এই কনফিগারেশনটি জেমিনিকে বলে: "অডিওটি প্রক্রিয়া করুন, তবে আপনি যা শুনেছেন তার একটি টেক্সট ট্রান্সক্রিপ্টও পাঠান যাতে আমরা ডিবাগ করতে পারি।"

এক্সিকিউশন লজিক রানার একবার সেশনটি প্রতিষ্ঠা করার পরে, এটি এক্সিকিউশন লজিকের কাছে নিয়ন্ত্রণ হস্তান্তর করে, যা LiveRequestQueue এর উপর নির্ভর করে। এটি রিয়েল-টাইম ইন্টারঅ্যাকশনের জন্য সবচেয়ে গুরুত্বপূর্ণ উপাদান। লুপটি এজেন্টকে ভয়েস প্রতিক্রিয়া তৈরি করতে দেয় যখন কিউ ব্যবহারকারীর কাছ থেকে নতুন ভিডিও ফ্রেম গ্রহণ করতে থাকে, নিশ্চিত করে যে "নিউরাল সিঙ্ক" কখনও ভাঙা হয় না।

পাঠান

👉✏️ $HOME/way-back-home/level_3/backend/app/main.py তে, LiveRequestQueue তে ডেটা প্রেরণকারী আপস্ট্রিম টাস্কটি সংজ্ঞায়িত করতে #REPLACE_LIVE_REQUEST প্রতিস্থাপন করুন:

# ========================================
    # Phase 3: Active Session (concurrent bidirectional communication)
    # ========================================

    live_request_queue = LiveRequestQueue()

    # Send an initial "Hello" to the model to wake it up/force a turn
    logger.info("Sending initial 'Hello' stimulus to model...")
    live_request_queue.send_content(types.Content(parts=[types.Part(text="Hello")]))

    async def upstream_task() -> None:
        """Receives messages from WebSocket and sends to LiveRequestQueue."""
        frame_count = 0
        audio_count = 0

        try:
            while True:
                # Receive message from WebSocket (text or binary)
                message = await websocket.receive()

                # Handle binary frames (audio data)
                if "bytes" in message:
                    audio_data = message["bytes"]
                    audio_blob = types.Blob(
                        mime_type="audio/pcm;rate=16000", data=audio_data
                    )
                    live_request_queue.send_realtime(audio_blob)

                # Handle text frames (JSON messages)
                elif "text" in message:
                    text_data = message["text"]
                    json_message = json.loads(text_data)

                    # Extract text from JSON and send to LiveRequestQueue
                    if json_message.get("type") == "text":
                        logger.info(f"User says: {json_message['text']}")
                        content = types.Content(
                            parts=[types.Part(text=json_message["text"])]
                        )
                        live_request_queue.send_content(content)

                    # Handle audio data (microphone)
                    elif json_message.get("type") == "audio":
                        import base64
                        # Decode base64 audio data
                        audio_data = base64.b64decode(json_message.get("data", ""))

                        # Send to Live API as PCM 16kHz
                        audio_blob = types.Blob(
                            mime_type="audio/pcm;rate=16000", 
                            data=audio_data
                        )
                        live_request_queue.send_realtime(audio_blob)

                    # Handle image data
                    elif json_message.get("type") == "image":
                        import base64
                        # Decode base64 image data
                        image_data = base64.b64decode(json_message["data"])
                        mime_type = json_message.get("mimeType", "image/jpeg")

                        # Send image as blob
                        image_blob = types.Blob(mime_type=mime_type, data=image_data)
                        live_request_queue.send_realtime(image_blob)
        finally:
             pass

গ্রহণ করুন

অবশেষে, আমাদের AI এর প্রতিক্রিয়াগুলি পরিচালনা করতে হবে। এটি runner.run_live() ব্যবহার করে, যা একটি ইভেন্ট জেনারেটর যা ইভেন্টগুলি (অডিও, টেক্সট, বা টুল কল) ঘটার সাথে সাথে তা তৈরি করে।

👉✏️ $HOME/way-back-home/level_3/backend/app/main.py তে, ডাউনস্ট্রিম টাস্ক এবং কনকারেন্সি ম্যানেজার সংজ্ঞায়িত করতে #REPLACE_SORT_RESPONSE প্রতিস্থাপন করুন:

    async def downstream_task() -> None:
        """Receives Events from run_live() and sends to WebSocket."""
        logger.info("Connecting to Gemini Live API...")
        async for event in runner.run_live(
            user_id=user_id,
            session_id=session_id,
            live_request_queue=live_request_queue,
            run_config=run_config,
        ):
            # Parse event for human-readable logging
            event_type = "UNKNOWN"
            details = ""
            
            # Check for tool calls
            if hasattr(event, "tool_call") and event.tool_call:
                 event_type = "TOOL_CALL"
                 details = str(event.tool_call.function_calls)
                 logger.info(f"[SERVER-SIDE TOOL EXECUTION] {details}")
            
            # Check for user input transcription (Text or Audio Transcript)
            input_transcription = getattr(event, "input_audio_transcription", None)
            if input_transcription and input_transcription.final_transcript:
                 logger.info(f"USER: {input_transcription.final_transcript}")
            
            # Check for model output transcription
            output_transcription = getattr(event, "output_audio_transcription", None)
            if output_transcription and output_transcription.final_transcript:
                 logger.info(f"GEMINI: {output_transcription.final_transcript}")

            event_json = event.model_dump_json(exclude_none=True, by_alias=True)
            await websocket.send_text(event_json)
        logger.info("Gemini Live API connection closed.")

    # Run both tasks concurrently
    # Exceptions from either task will propagate and cancel the other task
    try:
        await asyncio.gather(upstream_task(), downstream_task())
    except WebSocketDisconnect:
        logger.info("Client disconnected")
    except Exception as e:
        logger.error(f"Error: {e}", exc_info=False) # Reduced stack trace noise
    finally:
        # ========================================
        # Phase 4: Session Termination
        # ========================================

        # Always close the queue, even if exceptions occurred
        logger.debug("Closing live_request_queue")
        live_request_queue.close()

await asyncio.gather(upstream_task(), downstream_task()) লাইনটি লক্ষ্য করুন। এটিই Full-Duplex এর সারমর্ম। আমরা একই সময়ে listening task (upstream) এবং speaking task (downstream) চালাই। এটি নিশ্চিত করে যে "Neural Link" বাধা প্রদান করে এবং একই সাথে ডেটা প্রবাহের অনুমতি দেয়।

আপনার ব্যাকএন্ড এখন সম্পূর্ণরূপে কোডেড। "ব্রেন" (ADK) "বডি" (ওয়েবসকেট) এর সাথে সংযুক্ত।

বায়ো-সিঙ্ক এক্সিকিউশন

কোডটি সম্পূর্ণ। সিস্টেমগুলি সবুজ। উদ্ধার অভিযান শুরু করার সময় এসেছে।

  1. 👉💻 ব্যাকএন্ড শুরু করুন:
    cd $HOME/way-back-home/level_3/backend/
    cp app/biometric_agent/.env app/.env
    uv run app/main.py
    
  2. 👉 ফ্রন্টএন্ড চালু করুন:
    • ক্লাউড শেল টুলবারে ওয়েব প্রিভিউ আইকনে ক্লিক করুন। Change port নির্বাচন করুন, এটি 8080 এ সেট করুন, এবং Change and Preview এ ক্লিক করুন।
  3. 👉 প্রোটোকল কার্যকর করুন:
    • "INITIATE NEURAL SYNC" এ ক্লিক করুন।
    • ক্যালিব্রেট করুন: নিশ্চিত করুন যে ক্যামেরাটি পটভূমির বিপরীতে আপনার হাত স্পষ্টভাবে দেখতে পাচ্ছে।
    • সিঙ্ক: স্ক্রিনে প্রদর্শিত নিরাপত্তা কোডটি দেখুন (যেমন, 3, তারপর 2, তারপর 5)।
      • সংকেত মেলান: যখন একটি সংখ্যা প্রদর্শিত হবে, তখন আঙ্গুলের সঠিক সংখ্যাটি ধরে রাখুন।
      • স্থিরভাবে ধরে রাখুন: AI "বায়োমেট্রিক মিল" নিশ্চিত না করা পর্যন্ত আপনার হাত দৃশ্যমান রাখুন।
      • অভিযোজন: কোডটি এলোমেলো। ক্রমটি সম্পূর্ণ না হওয়া পর্যন্ত অবিলম্বে প্রদর্শিত পরবর্তী সংখ্যায় স্যুইচ করুন।

নিউরো-সিঙ্ক

  1. যখন তুমি এলোমেলো ক্রমানুসারে চূড়ান্ত সংখ্যাটি মেলাবে, তখন "বায়োমেট্রিক সিঙ্ক" সম্পূর্ণ হবে। নিউরাল লিঙ্কটি লক হয়ে যাবে। তোমার হাতে ম্যানুয়াল নিয়ন্ত্রণ থাকবে। স্কাউট ইঞ্জিনগুলি প্রাণবন্ত হয়ে উঠবে, বেঁচে থাকাদের বাড়িতে ফিরিয়ে আনার জন্য উপত্যকায় ঝাঁপিয়ে পড়বে।

👉💻 প্রস্থান করতে ব্যাকএন্ড টার্মিনালে Ctrl+C টিপুন।

৬. উৎপাদনে স্থাপন করুন (ঐচ্ছিক)

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

সংক্ষিপ্ত বিবরণ

👉💻 আপনার ক্লাউড শেল টার্মিনালে নিম্নলিখিত কমান্ডটি চালান। এটি আপনার ব্যাকএন্ড ডিরেক্টরিতে সম্পূর্ণ, মাল্টি-স্টেজ ডকারফাইল তৈরি করবে।

cd $HOME/way-back-home/level_3

cat <<EOF > Dockerfile
FROM node:20-slim as builder

# Set the working directory for our build process
WORKDIR /app

# Copy the frontend's package files first to leverage Docker's layer caching.
COPY frontend/package*.json ./frontend/
# Run 'npm install' from the context of the 'frontend' subdirectory
RUN npm --prefix frontend install

# Copy the rest of the frontend source code
COPY frontend/ ./frontend/
# Run the build script, which will create the 'frontend/dist' directory
RUN npm --prefix frontend run build


# STAGE 2: Build the Python Production Image
# This stage creates the final, lean container with our Python app and the built frontend.
FROM python:3.13-slim

# Set the final working directory
WORKDIR /app

# Install uv, our fast package manager
RUN pip install uv

# Copy the requirements.txt from the backend directory
COPY requirements.txt .
# Install the Python dependencies
RUN uv pip install --no-cache-dir --system -r requirements.txt

# Copy the contents of your backend application directory directly into the working directory.
COPY backend/app/ .

# CRITICAL STEP: Copy the built frontend assets from the 'builder' stage.
# We copy to /frontend/dist because main.py looks for "../../frontend/dist"
# When main.py is in /app, "../../" resolves to "/", so it looks for /frontend/dist
COPY --from=builder /app/frontend/dist /frontend/dist

# Cloud Run injects a PORT environment variable, which your main.py uses (defaults to 8080).
EXPOSE 8080

# Set the command to run the application.
CMD ["python", "main.py"]
EOF

👉💻 ব্যাকএন্ড ডিরেক্টরিতে নেভিগেট করুন এবং অ্যাপ্লিকেশনটিকে একটি কন্টেইনার ছবিতে প্যাকেজ করুন।

export PROJECT_ID=$(cat ~/project_id.txt)
export REGION=us-central1
export SERVICE_NAME=biometric-scout
export IMAGE_PATH=gcr.io/${PROJECT_ID}/${SERVICE_NAME}
cd $HOME/way-back-home/level_3
gcloud builds submit . --tag ${IMAGE_PATH}

👉💻 ক্লাউড রানে পরিষেবাটি স্থাপন করুন। আমরা প্রয়োজনীয় পরিবেশগত ভেরিয়েবলগুলি - বিশেষ করে জেমিনি কনফিগারেশন - সরাসরি লঞ্চ কমান্ডে ইনজেক্ট করব।

export PROJECT_ID=$(cat ~/project_id.txt)
export REGION=us-central1
export SERVICE_NAME=biometric-scout
export IMAGE_PATH=gcr.io/${PROJECT_ID}/${SERVICE_NAME}
gcloud run deploy ${SERVICE_NAME} \
  --image=${IMAGE_PATH} \
  --platform=managed \
  --region=${REGION} \
  --allow-unauthenticated \
  --set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
  --set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
  --set-env-vars="GOOGLE_GENAI_USE_VERTEXAI=True" \
  --set-env-vars="MODEL_ID=gemini-live-2.5-flash-preview-native-audio-09-2025"

কমান্ডটি শেষ হয়ে গেলে, আপনি একটি পরিষেবা URL দেখতে পাবেন (যেমন, https://biometric-scout-...run.app )। অ্যাপ্লিকেশনটি এখন ক্লাউডে লাইভ।

👉 গুগল ক্লাউড রান পৃষ্ঠায় যান এবং তালিকা থেকে বায়োমেট্রিক-স্কাউট পরিষেবাটি নির্বাচন করুন। ক্লাউডরান

👉 পরিষেবার বিবরণ পৃষ্ঠার উপরে প্রদর্শিত পাবলিক URLটি সনাক্ত করুন। ক্লাউডরান

এই পরিবেশে বায়ো-সিঙ্ক করার চেষ্টা করুন, এটি কি কাজ করে?

আপনার পঞ্চম আঙুল প্রসারিত হওয়ার সাথে সাথে, AI ক্রমটি লক করে। স্ক্রিনটি সবুজ রঙে জ্বলজ্বল করে: "বায়োমেট্রিক নিউরাল সিঙ্ক: প্রতিষ্ঠিত।"

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

সম্পন্ন

বিমানের তালাটি হিস হিস করে খুলে গেল, আর দেখো তারা—পাঁচজন জীবিত, নিঃশ্বাস ফেলে বেঁচে আছে। তারা ডেকের উপর হোঁচট খেয়ে পড়ে গেল, আহত কিন্তু জীবিত, অবশেষে তোমার কারণে নিরাপদ।

আপনার জন্য ধন্যবাদ, নিউরাল লিঙ্কটি সিঙ্ক্রোনাইজ করা হয়েছে এবং বেঁচে থাকাদের উদ্ধার করা হয়েছে।

যদি আপনি লেভেল ০ তে অংশগ্রহণ করে থাকেন, তাহলে বাড়ি ফেরার মিশনে আপনার অগ্রগতি কোথায় তা পরীক্ষা করে দেখতে ভুলবেন না!

ফাইনাল