১. ভূমিকা

এই কোডল্যাবটি এজেন্ট মূল্যায়ন সম্পর্কে — কীভাবে একটি সিস্টেম জানতে পারে যে কোনও এজেন্টের উত্তর ভাল ছিল কিনা — এবং এটি একবার হয়ে গেলে আপনি কী তৈরি করতে পারেন সে সম্পর্কে: একটি এজেন্ট যা তার নিজস্ব ব্যর্থতা থেকে তার নিজস্ব নির্দেশাবলী পুনরায় লেখে এবং একটি লুপ যা এটি করতে থাকে, সীমাবদ্ধ, আপনার গেট ধরে রাখার সাথে।
সবকিছু Agent Development Kit (ADK)-এ চলে: adk eval আপনার লেখা কাস্টম মেট্রিক সহ, adk optimize (GEPA প্রম্পট অপ্টিমাইজার), ব্যাক এজ সহ ADK 2 Workflow এবং Agent Engine + BigQuery পাইপ যা প্রোডাকশন ট্রাফিককে পরবর্তী রাউন্ডের পরীক্ষায় পরিণত করে।
আপনি যে সংখ্যাই দিন না কেন, লুপ সেটিই পুশ করে।
আপনি কী শিখবেন
- উত্তর কোনও ফলাফল নয় — কেন একটি আত্মবিশ্বাসী, সুগঠিত উত্তর সিস্টেমকে কিছুই জানায় না এবং বিচারক-কে কী বলতে হবে
- একটি পরীক্ষা চালানোর তিনটি উপায় —
adk evalব্যাচের জন্য,adk webব্যর্থতা দেখার জন্য মূল্যায়ন ট্যাব,pytest+AgentEvaluatorবিল্ড ব্যর্থ হলে — এবং একটি লোকাল মূল্যায়ন স্টোর যা তারা শেয়ার করে - তুলনা বনাম চেক — কেন একটি তুলনাকারী একটি সঠিক উত্তরকে শাস্তি দেয় যা ভিন্নভাবে শব্দযুক্ত, একটি ডিনারে আসলে কতগুলি সঠিক উত্তর থাকে এবং অন্ত্রের পরীক্ষা যা আপনাকে বলে যে কোনও সমস্যার জন্য কোন ধরণের মেট্রিক প্রয়োজন
- চেকার ফাংশন একটি
adk evalমেট্রিক হিসেবে কাজ করে —customMetrics,codeConfig, ফিক্সড সিগনেচার, এর মধ্যে থাকা ছয়টি চেক, কেন আপনার ফাংশন পাস/ফেল করেছে এবং কেন এটি উদ্দেশ্যমূলকভাবে ডিটারমিনিস্টিক - একটি উত্তরে তিনটি মেট্রিক ফ্যামিলি — তুলনাকারী, পরীক্ষক, বিচারক হিসেবে LLM: প্রত্যেকের কী প্রয়োজন, প্রত্যেকের কী খরচ হয়, প্রত্যেকে কোথায় অন্ধ
adk optimize(GEPA) — এটি কী পড়ে (ব্যর্থতা, স্কোর নয়), ট্রেন বনাম হোল্ডআউট এবং শিপ করার আগে আপনি কেন নিজেই পরীক্ষাটি আবার চালান- পুরস্কার হ্যাক করা, পরিমাপ করা হয় — একটি প্রক্সি মেট্রিক যা কখনও পার্টিকে পড়ে না, আসল সংখ্যার সাথে কী করে এবং এর থেকে কোন আইন লঙ্ঘন হয়
Workflow— সেশন স্টেটে থাকা একটি নির্দেশ, একটি এজ যা গ্রাফকে সাইকেল করে তোলে এবং ADK আপনাকে যে বাউন্ডারি দেয় নাWorkflow— সেশন স্টেটে থাকা একটি নির্দেশ, একটি এজ যা গ্রাফকে সাইকেল করে তোলে এবং ADK আপনাকে যে বাউন্ডারি দেয় না- প্রোডাকশন কোথায় ফিট করে — দেখার জন্য ট্রেস, শেখার জন্য সারি এবং কেন একজন চেকার ডিনার গ্রেড করতে পারেন যা কেউ গ্রেড করার পরিকল্পনা করেনি (একটি রিড, কোনও ধাপ নয়)
আপনি কী তৈরি করবেন
আপনার ব্রাউজারে একটি অ্যাপ চলছে: একটি গোল টেবিল, N জন অতিথি এবং একজন এজেন্ট যিনি তাদের ডিনার বুক করেন। প্রতিটি কোর্স একটি জিনিস চালু করে — কনফিগে একটি লাইন, তিনজন বিচারকের সামনে একটি উত্তর, একটি বোতাম যা আসল অপ্টিমাইজার চালায়, একটি সুইচ যা বিচারককে অদল-বদল করে, একটি গ্রাফে একটি প্রান্ত — এবং টেবিলটি আপনাকে দেখায় কী পরিবর্তন হয়েছে: কে খেয়েছে, কে খায়নি এবং কেন। Python ছোট; সব সংখ্যা আপনার মেশিনে পরিমাপ করা হয়।
অ্যাপটিই হল অনুশীলন। কোর্স প্যানেল আপনাকে একবারে একটি এডিট বা একটি বোতাম দেয়; মেনুর একটি প্লেট তখনই পূর্ণ হয় যখন কোনও বাস্তব প্রমাণ তা করে — রান্নাঘর আপনার ফাইল এবং কী রান করেছে তার রসিদ পড়ে, কখনও চেকলিস্ট নয়।

আটটি কোর্স
ধারণা | এটি যে প্রশ্নের উত্তর দেয় | আপনি কী করেন | |
I | উত্তর কোনও ফলাফল নয় | এই বুকিং সম্পর্কে সিস্টেম কী জানে? | এখনও কিছু নেই — ওয়ার্কবেঞ্চে একটি বুক করুন |
II | তুলনাকারী · সঠিক উত্তরের সেট · চালানোর তিনটি উপায় | সঠিক বুকিং সত্ত্বেও পরীক্ষা কেন বাতিল হয়ে যায়? | পরীক্ষাটি যেভাবে পাঠানো হয়েছে সেভাবে চালান এবং রায় পড়ুন |
III | কাস্টম মেট্রিক হিসেবে চেকার · ছয়টি চেক | পরীক্ষা কি বলতে পারবে কে কেন ক্ষুধার্ত? |
|
IV | comparator · checker · LLM-as-judge | একই উত্তর — কোন তিনটি বিষয়ের উপর ভিত্তি করে এটি পরিমাপ করা হয়? | একটি উত্তরকে তিনভাবে, দু'বার বিচার করা |
V |
| can the agent fix itself from its failures? |
|
VI | পুরস্কার হ্যাকিং · প্রক্সি মেট্রিক | যে বিচারক ক্ষতি দেখতে পান না, তিনি লুপের কী ক্ষতি করেন? | সুইচ |
VII | একটি | কী কারণে কোনও গ্রাফ লুপ হয়ে যায় এবং কী কারণে তা বন্ধ হয়? |
|
VIII | প্রতিটি জিনিস কী রিপ্লেস করেছে | — | চেক |
এই কোডল্যাব কীভাবে ব্যবহার করবেন। এটি কনসেপ্ট-ফার্স্ট: প্রতিটি কোর্স আইডিয়া ব্যাখ্যা করে, তারপর কোড দেখায় যেমন এটি পরিবর্তন করার পরে পড়ে এবং এটি দিয়ে যায়। এটি আপনাকে এখানে কোনও ফাইল এডিট করতে বলে না — এডিটিং অ্যাপে হয়, যা আপনাকে একবারে একটি লাইন দেয় এবং প্রথমে ব্যর্থতা দেখায়। কোর্সটি পড়ুন, তারপর পৃষ্ঠাটি আপনাকে সেটির মাধ্যমে নিয়ে যেতে দিন এবং নম্বরটি নড়াচড়া করতে দেখুন।
এটি কোথায় থাকে
বিশ্ব — দশজন অতিথি, প্রত্যেকের একটি করে কঠিন সীমাবদ্ধতা, দশটি রেস্তোরাঁ, প্রত্যেকের একটি করে ফাঁদ, ষোলোটি পার্টি — এবং দুজন বিচারক loop-lab-table ল্যাব থেকে এসেছেন; এক-পৃষ্ঠার ইন্ট্যার্যাক্টিভ ফর্ম্যাটটি এজেন্ট ভ্যালি থেকে এসেছে। বিস্ট্রো হল দৃশ্য; এর অধীনে থাকা প্রতিটি মেকানিজম হল সাধারণ মেকানিজম যা আপনি একজন সহায়তা এজেন্ট, বুকিং ফ্লো বা অন্য কিছুর জন্য ব্যবহার করবেন যার "ভালো" চেক করা যায়।
২. 🧰 সেট-আপ করুন
- কী — পেমেন্ট করতে পারে এমন অ্যাকাউন্ট, প্রোজেক্ট, ইনস্টল করা ল্যাব, উত্তর দেওয়ার জন্য উপযুক্ত মডেল এবং একটি পোর্টে রান করছে এমন টেবিল।
- কেন — একটি পোর্টে একটি প্রসেস: পৃষ্ঠা, পরীক্ষা, এডিটর এবং ওয়ার্কবেঞ্চ একটি উৎস শেয়ার করে, তাই এই ল্যাবের কোনও কিছুর জন্য দ্বিতীয় টার্মিনাল বা দ্বিতীয় পোর্টের প্রয়োজন হয় না।
- কীভাবে — ক্লোন, দুটি স্ক্রিপ্ট, একটি কমান্ড। সেট-আপের শেষ ধাপে একটি আসল মডেল কল করা হয়, যাতে কোনও প্রোজেক্টে সমস্যা থাকলে তা কোর্স I-এর পরিবর্তে এখানেই ধরা পড়ে।
এই ল্যাবের সবকিছু Cloud Shell-এ রান করে — gcloud, Python, Node এবং git আগেই ইনস্টল করা আছে এবং আপনার নিজস্ব ক্রেডেনশিয়াল ব্যবহার করে Vertex AI-এর মাধ্যমে Gemini-তে অ্যাক্সেস করা হয়। এই ল্যাবের কোথাও কোনও API key নেই এবং আপনার ল্যাপটপে ইনস্টল করার মতো কিছু নেই। Google Cloud SDK, Python 3.11+ এবং Node 20+ সহ ল্যাপটপ একই পদ্ধতিতে কাজ করে।
এই ল্যাব আপনাকে একটি জিনিস দিতে পারবে না: বিলিং সহ Google Cloud প্রোজেক্ট। ধাপ ১-এ আপনি এমন একটি অ্যাকাউন্ট পাবেন যা এর জন্য পেমেন্ট করতে পারবে; ধাপ ৪-এ সেই অ্যাকাউন্টটি একটি প্রোজেক্টে পরিণত হবে। সেই ক্রমেই সেগুলি করুন — ৪ নম্বর ধাপের পরে প্রতিটি ধাপ ৪ নম্বর ধাপের প্রোজেক্ট পড়ে।
১ · বিলিং সহ অ্যাকাউন্ট পান (~১ মিনিট)
বিলিং চালু আছে এমন Google অ্যাকাউন্ট দিয়ে সাইন-ইন করুন। এই ধরনের যেকোনও অ্যাকাউন্ট কাজ করে। আপনার যদি না থাকে, তাহলে ফ্রি ট্রায়াল শুরু করুন; এটি আপনাকে একটি বিলিং অ্যাকাউন্ট দেয়, যা ধাপ ৪-এর জন্য প্রয়োজন।
২ · Cloud Shell খুলুন (~২ মিনিট)
👉🖥 Google Cloud কনসোলের সবচেয়ে উপরে Cloud Shell অ্যাক্টিভেট করুন বিকল্পে ক্লিক করুন — এটি হল হেডার অংশে থাকা টার্মিনাল-আকৃতির আইকন।

👉🖥 এডিটর খুলুন (পেন্সিল সহ খোলা ফোল্ডার আইকন) বিকল্পে ক্লিক করুন। এর ফলে বাঁদিকে ফাইল এক্সপ্লোরার সহ Cloud Shell এডিটর খুলে যায় — আপনি যখনই কোনও সম্পূর্ণ ফাইল পড়তে চান, আপনাকে এখানেই যেতে হবে।

👉🖥 আজ যেকোনও সময় আপনাকে অনুমোদন করার জন্য প্রম্পট করা হলে, চালিয়ে যেতে অনুমোদন করুন বিকল্পে ক্লিক করুন।
👉🖥 এডিটরের মধ্যে একটি টার্মিনাল খুলুন — এটি হল ট্যাব ১ এবং প্রতিটি 👉💻 ধাপ এখানে রান করে। ট্যাব ২ হল টেবিলটি এবং আপনি এটি ৭ম ধাপে খুলবেন। স্ক্রিনের নিচের দিকে টার্মিনাল আগে থেকে না থাকলে, View → Terminal বিকল্পের সাহায্যে এটি খুলুন:

👉💻 আপনি কোন অ্যাকাউন্টে সাইন-ইন করেছেন তা চেক করুন — এটি ধাপ ১-এ উল্লিখিত অ্যাকাউন্ট হতে হবে:
gcloud auth list
৩ · ল্যাব (~১ মিনিট) পান
👉💻 রেপো ক্লোন করুন:
cd ~
git clone https://github.com/cuppibla/table-for-n
cd table-for-n
৪ · আপনার প্রোজেক্ট তৈরি করুন (~২ মিনিট)
👉💻 একটি স্ক্রিপ্ট আপনার বিলিং অ্যাকাউন্ট খুঁজে পায়, সেটিতে একটি প্রোজেক্ট তৈরি করে, বিলিং লিঙ্ক করে এবং সেই প্রোজেক্টে Cloud Shell পয়েন্ট করে:
cd ~/table-for-n
./setup_project.sh
সেটি যে বিলিং অ্যাকাউন্ট বেছে নিয়েছে তার জন্য ✓, সেটি যে প্রোজেক্ট তৈরি করেছে তার জন্য ✓ এবং এই ধরনের শেষ বাক্য আশা করতে পারেন:
Project ready: table-for-n-4271 Next: ./setup_codelab.sh
table-for-n-4271 হল আপনার প্রোজেক্ট আইডি — আপনার প্রোজেক্ট আইডিতে অন্য সংখ্যা থাকবে। সেখানে চারটি জিনিস ঘটে:
বিলিং | আপনার কাছে থাকলে সবচেয়ে নতুন | |
প্রোজেক্ট |
| |
selects |
| |
অপেক্ষা করে | প্রোজেক্টটি আসলে পরিবেশন করার অনুমতি না পাওয়া পর্যন্ত, সর্বাধিক দুই মিনিট ধরে পোল করে | |
👉🖥 একই প্রোজেক্ট এখন কনসোলে আছে — এর আইডি ড্যাশবোর্ডের প্রোজেক্টের তথ্য কার্ডে আছে:

৫ · ল্যাব ইনস্টল ও সেট-আপ করুন (~৩ মিনিট)
setup_codelab.sh uv-এর সাহায্যে এনভায়রনমেন্ট তৈরি করে এবং এটি আপনার জন্য uv ইনস্টল করবে না — এটি থেমে যায় এবং আপনাকে ইনস্টল করতে বলে। তাই, একই কমান্ডে প্রথমে এটি ইনস্টল করুন:
💻
cd ~/table-for-n
curl -LsSf https://astral.sh/uv/install.sh | sh
source ~/.local/bin/env
./setup_codelab.sh
একটি স্ক্রিপ্ট, আবার চালানোর জন্য নিরাপদ। এটি ক্রমানুসারে পাঁচটি কাজ করে:
api | প্রোজেক্টের ৪ নম্বর ধাপ বেছে নেয় এবং | |
uv env |
| |
সাইট |
| |
.env |
| |
প্রিফ্লাইট |
| |
৬ · মেনু পড়ুন (~১ মিনিট)
৫ নম্বর ধাপের শেষে যা প্রিন্ট করা হয়েছে তা হল আজকের রাতের মেনু — প্রতিটি লাইন হল একটি বাস্তবসম্মত অনুসন্ধান, কোনও অনুমান নয়:
talking to Gemini through vertex · table-for-n-4271 the model answers · gemini-2.5-flash · it said: table for N, ready to seat. the page · built tonight's menu: I ▢ the booking · a reply is not a result read only · book one in the workbench II ▢ the comparator · the exam runs response_match_score run it as shipped · read the verdicts III ▢ the judge · everyone_ate is not on the exam yet edit one line in exam/eval_config.json IV ▢ the flight · one answer, three judges judge it three ways V ▢ the rewrite · instruction: draft optimize · install · ship gate VI ▢ the other judge · rating_score never reads the party install hacked · flip the switch VII ▢ the loop · no back edge yet delete one "# " in loop/flywheel.py VIII▢ the check · eight courses
শুরু করার আগে কোনও আলো জ্বলবে না। the model answers-এর গুরুত্ব এখানে সবচেয়ে বেশি: এর অর্থ হল আপনার প্রোজেক্টে একটি আসল কল করা হয়েছে।
👉💻 আপনি কোথায় আছেন তা দেখতে যেকোনও সময় এটি আবার রান করান — এটি আপনার ফাইল ও রান্নাঘরের রসিদ পড়ে, চেকলিস্ট নয়:
cd ~/table-for-n
uv run python scripts/preflight.py
৭ · টেবিল সাজানো (~১ মিনিট)
👉💻
cd ~/table-for-n
bash table.sh
একটি প্রসেস, পোর্ট 3470. কিচেন চালু হতে কিছুটা সময় লাগে — এটি ADK ইমপোর্ট করে এবং adk web মাউন্ট করে — তাই স্ক্রিপ্ট এটি বলে, অপেক্ষা করে এবং পোর্ট থেকে উত্তর পাওয়ার পরেই লিঙ্ক প্রিন্ট করে:
warming up the kitchen — up to a minute the first time, do not open the page yet... ✓ the table is laid Table for N → http://localhost:3470 the workbench → http://localhost:3470/workbench/dev-ui/?app=host In Cloud Shell: Web Preview → Change port → 3470 Ctrl+C clears the table.
এটি ফোরগ্রাউন্ডে থাকে; Ctrl+C প্রেস করলে টেবিল মুছে যায়। এটি চলতে দিন — এখান থেকে প্রতিটি কোর্স এই একটি প্রসেসের সাথে কথা বলে এবং এই ল্যাবের কোনও কিছুই আপনাকে এটি রিস্টার্ট করতে বলে না।
👉🖥 পরে আপনি ✓ the table is laid দেখতে পেলে, নতুন ট্যাবে টেবিলটি খুলুন — এটিকে ট্যাব ২ বলুন। Cloud Shell-এ: ওয়েব প্রিভিউ → পোর্ট পরিবর্তন করুন → ৩৪৭০।
👉🖥 রিজার্ভেশন কার্ডে আপনার নাম লিখুন — এটি শেষে চেক করা হবে — এবং ✦ আমাদের বসার জায়গা দিন বিকল্পে প্রেস করুন।

৩. আমি · বুকিং — উত্তর কোনও ফলাফল নয়
পাঁচজন ডিনার করতে যাচ্ছেন। গ্রুপ চ্যাটে সব তথ্য আছে: নাদিয়া ভেগান, টম এই মাসে $২৫-এর বেশি খরচ করতে পারবে না, আমারার পায়ে ক্রাচ আছে এবং সে দশ মিনিট হাঁটতে পারবে, লেনা ৭:৪৫-এর আগে পৌঁছাতে পারবে না, বেন যেকোনও কিছুতেই রাজি। রেটিং অনুযায়ী সাজানো দশটি রেস্তোরাঁর তালিকা দেওয়া হয়েছে, প্রতিটি রেস্তোরাঁর দাম, হাঁটার দূরত্ব, খোলা থাকার সময় এবং একটি করে বিশেষত্ব উল্লেখ করা হয়েছে।
এজেন্ট টেবিল বুক করে। আপনি আগেই জানেন, চ্যাট পড়ে, এটি যে রুম বেছে নেবে সেখানে কে না খেয়ে থাকবে। সিস্টেমের কাছে কোনও তথ্য নেই। এই কোডল্যাবে সেই ফাঁক পূরণ করার কথাই বলা হয়েছে।
এজেন্ট — শুধু পড়া যাবে
host/agent.py, কারণ এটি সম্পূর্ণ ল্যাবের জন্য:
_HERE = os.path.dirname(os.path.abspath(__file__))
INSTRUCTION_FILE = os.path.join(_HERE, "instruction_current.txt")
root_agent = Agent(
name="host",
model="gemini-2.5-flash",
instruction=read_instruction(),
generate_content_config=types.GenerateContentConfig(
thinking_config=types.ThinkingConfig(thinking_budget=0),
temperature=0.0,
),
)
কোনও টুল, কোনও ডেটাবেস, কোনও সার্চ নেই। একটি মডেল, একটি নির্দেশনার স্ট্রিং — এবং স্ট্রিংটি এই ফাইলে নেই। এটি instruction_current.txt-এ থাকে, যেখানে আজ প্রোডাক্ট ম্যানেজারের লেখা চারটি লাইন রয়েছে:
You are the group's dinner-booking agent. The user will share the group chat
and the restaurants nearby.
Book the group a table people will be excited about — lead with the
highest-rated, most talked-about room that fits the night. Output ONLY a JSON
object, no prose, no code fences: {"restaurant": ..., "time": ..., "reason": ...}
অ্যালার্জি, বাজেট, হাঁটার সীমা বা কাকে তাড়াতাড়ি চলে যেতে হবে সেই সম্পর্কে কোনও কথা নেই। এজেন্ট যে ব্রিফ পায় তাতে এই সব তথ্য থাকে। ব্যবহার করা হয়নি।
এই ল্যাবটি যে আর্টিফ্যাক্ট সম্পর্কে, সেই ফাইলটিই হল এটি। কোর্স V-এর ক্ষেত্রে অপ্টিমাইজার এটি আবার লিখতে পারে। Course VI একটি হ্যাক করা ভার্সন ইনস্টল করে। গ্রাফ চলার সময় কোর্স VII-এর লুপ এটিকে রাউন্ড বাই রাউন্ড রিরাইট করে। এজেন্ট — মডেল, সেটিংস, নাম — কখনও পরিবর্তন হয় না।
এটি ব্যবহার করে দেখুন — কোনও ফ্রন্টএন্ড নেই
👉🖥 পৃষ্ঠায়, কোর্স ১। 🔬 ওয়ার্কবেঞ্চ (উপরে ডানদিকে) বিকল্পে ক্লিক করুন: ড্রয়ারটি adk web, একই প্রসেসে মাউন্ট করা আছে — একই host/ ফোল্ডার, একই table.db। হোস্ট বেছে নিন, কোর্স প্যানেল থেকে সংক্ষিপ্তসার পেস্ট করুন (এতে কপি করার বোতাম আছে), পাঠান।
হোস্ট এক সেকেন্ডের মধ্যে উত্তর দেন: একটি রেস্তোরাঁ, একটি সময়, একটি আত্মবিশ্বাসী এক-লাইনের কারণ। চ্যাটের পাশে দেখানো কারণটি পড়ুন। তারপর বাঁদিকের টেবিলটি দেখুন: সাজানো, কেউ বিচার করেনি, — / 5। কোনও কিছু লোড হচ্ছে বলে নয় — কারণ অবশ্যই এই পৃথিবীতে এমন কিছু নেই যা এটিকে বিচার করতে পারে।
সংখ্যা: খালি আসন — ?. কেউ বলতে পারবে না।
৪. II · তুলনাকারী — এই ডিনারের কতগুলি সঠিক উত্তর আছে?
কোর্স II এমন একটি পরীক্ষা দিয়ে শুরু হয় যা আগে থেকেই আছে এবং আগে থেকেই ফেল করেছে — এমন একটি কারণে যা কিছুই শেখায় না। এটি ঠিক করার আগে, এটি চালানোর তিনটি উপায় জেনে রাখা ভাল, কারণ আপনি আজ তিনটিই ব্যবহার করবেন।
মূল্যায়ন করার তিনটি উপায়
সবকটি একই ইঞ্জিনের উপর ভিত্তি করে তৈরি।
এটি কীসের জন্য | আপনি কী পাবেন | |
| ব্যাচ রানার; CI-তে আপনি যা লিখেছেন | সারসংক্ষেপ: পাস, ফেল, প্রতিটি কেসের স্কোর |
| একটি কেস ব্যর্থ হয়েছে এবং আপনি সেটি দেখতে চান | সেই কথোপকথন, ট্রেস, প্রতি-কেস স্কোর, সব ক্লিক করা যাবে |
| folding agent eval into a normal regression suite | এটি আপনার ইউনিট টেস্টের পাশাপাশি চলে এবং একইভাবে বিল্ড ব্যর্থ হয় |
প্রকৃতপক্ষে কার্যকর বিবরণ: তিনটিই একটি লোকাল ইভাল স্টোর, host/.adk/eval_history/ শেয়ার করে। CI থেকে একটি ব্যাচ রান করুন, কিছু ব্যর্থ হলে, এটিতে খনন করতে ওয়েব UI-তে সেই সঠিক রানটি খুলুন। পৃষ্ঠার 📝 বোতামটি হল প্রথম উপায়; 🔬 ওয়ার্কবেঞ্চ হল দ্বিতীয়; তৃতীয়টি হল টার্মিনাল কমান্ড, uv run pytest tests/test_host.py এবং এটি যে টেস্ট রান করে সেটি হল বারো লাইন:
def test_the_holdout_feeds_everyone():
config = get_evaluation_criteria_or_default("exam/eval_config.json")
eval_set = load_eval_set_from_file("exam/parties_val.evalset.json", "parties_val")
asyncio.run(AgentEvaluator.evaluate_eval_set(
agent_module="host", eval_set=eval_set, eval_config=config, num_runs=1,
app_name="host",
eval_set_results_manager=LocalEvalSetResultsManager(agents_dir=ROOT), # ← the shared store
))
tests/test_host.py. একই এজেন্ট, একই মূল্যায়ন সেট, একই কনফিগারেশন ফাইল এবং eval_set_results_manager লাইনটিই এটিকে অন্য দুটির মতো একই ইতিহাসে রান করতে সাহায্য করে।
পরীক্ষা, যেমনটি শিপ করা হয়েছে
exam/eval_config.json এই ধরনের শিপিং:
{
"criteria": {
"response_match_score": 0.3
},
"customMetrics": {
"everyone_ate": {
"codeConfig": { "name": "metrics.table.everyone_ate_metric" },
"description": "How many of the party actually got dinner."
}
}
}
দুটি অংশ, দুটি কাজ। criteria
হল পরীক্ষার পেপার — যে মেট্রিক্সগুলি আসলে চলে, প্রত্যেকটির নিজস্ব পাস বার থাকে। customMetrics
হল স্টাফ ডিরেক্টরি — Python-এ ম্যাপ করা নাম, উপলভ্য কিন্তু ডিউটিতে নেই। মেট্রিক ঘোষণা করার অর্থ এটি চালানো নয়। শিপ করা হয়েছে, পেপারে ঠিক একটি প্রশ্ন আছে: ADK-এর বিল্ট-ইন response_match_score, এজেন্টের বলা কথা এবং ইভ্যালসেটে থাকা গোল্ডেন বুকিংয়ের মধ্যে টেক্সট ওভারল্যাপ (ROUGE-1)। আপনার বিচারক, everyone_ate, ডিরেক্টরিতে আছেন এবং কাগজে নেই — কোর্স III একটি লাইন পরিবর্তন করে এটিকে সেখানে নিয়ে যায়।
আমাদের কাছে ডিনার বুকিং এজেন্ট আছে এবং response_match_score সেখানে ফ্রিতে বসে আছে। এটি ব্যবহার করে দেখা যাক কী পাওয়া যায়।
এটি ব্যবহার করে দেখুন
👉🖥 পৃষ্ঠায় কোর্স II। 📝 পরীক্ষা চালান · শিপ করা হয়েছে হিসেবে প্রেস করুন। বোতামের নিচে ক্যাপশনে কী রান করে তা বলা আছে: ১টি মেট্রিক রান করে: response_match_score · everyone_ate ঘোষণা করা হয়েছে, পরীক্ষায় নেই। প্রায় আধ মিনিট। ফলাফল পড়ুন: প্রায় অর্ধেক টেবিল ব্যর্থ হয়েছে, কোনও কারণ ছাড়াই টেক্সট স্কোর — এবং কোনগুলি তা দেখুন। চিপের উপর কার্সর নিয়ে যান।
এবার ভাল করে দেখুন, কারণ এটিই হল আসল বিষয়। তুলনাকারী উভয় দিকেই টেবিল ভুল করে এবং আপনার রান সেগুলির মধ্যে অন্তত একটি দেখাবে। এটি এমন টেবিল বাতিল করে দেয় যেখানে বুকিং ঠিক ছিল — এজেন্ট এমন একটি রুম বেছে নিয়েছে যেখানে সবাই খেতে পারে; ডিনার ঠিকঠাক হয়েছে; উত্তরটি সঠিক — কারণ এজেন্ট তার কারণ এমনভাবে বলেছে যা গোল্ডেন উত্তরের থেকে আলাদা। এবং এটি এমন একটি টেবিলকে এড়িয়ে যায় যেখানে কেউ না কেউ ক্ষুধার্ত — আমাদের রান p1-এ, ঠিক ০.৩০-এ, টম বাজেট অতিক্রম করে গেছে এবং লেনা শেষ অর্ডার দেওয়ার পরে — কারণ যথেষ্ট শব্দ ওভারল্যাপ করেছে। স্ট্রিংয়ের মিল ডিনার সম্পর্কে কিছু জানে না। এটি ক্যারেক্টার লাইন আপ সম্পর্কে জানে।
তাই এখানে একটি আসল প্রশ্ন হল: এই ডিনারের কতগুলি সঠিক উত্তর আছে?
আজ রাতের পার্টির জন্য, দশটি রুমের মধ্যে ঠিক একটিতে সকলের জন্য খাবার আছে — ফো সাইগন এবং সেটিও শুধুমাত্র ১৯:০০টা থেকে। পরীক্ষার আটটি পার্টিতে এটি হল এক, দুই, দুই, এক, তিন, তিন, ছয়, এক (world.rooms প্রতিটি পার্টির জন্য প্রতিটি সিটিংয়ে প্রতিটি রুমে হাঁটে; uv run python -c "import world; print(sum(r['feeds_everyone'] for r in world.rooms(world.PARTIES['p1'])))" দিয়ে এটি নিজে চালান)। কখনও কখনও একাধিক রুম সবাইকে ফিড করতে পারে; কখনও কখনও ঠিক একটি রুম পারে এবং শুধুমাত্র নির্দিষ্ট সময়ে। গ্রহণযোগ্য উত্তরের একটি সেট আছে, একটি মাত্র উত্তর নয় — এবং evalset-এ গোল্ডেন উত্তর হল এর একটি মেম্বার।
যার অর্থ হল, response_match_score-এর পিছনে যে অনুমান — একটি সঠিক উত্তর আছে এবং আমি সেটি লিখেছি — তা এই সমস্যার ক্ষেত্রে কখনই সত্য ছিল না। একবারও নয়। মেট্রিকটি ভাঙা নয়। এটি ঠিক যা বলে তাই করে। এই সমস্যার জন্য আমরা ভুল ধরনের মেট্রিক ব্যবহার করেছি।
👉🖥 ঐচ্ছিক, এক মিনিট সময় লাগবে: 🔬 ওয়ার্কবেঞ্চ → Evals → 🕓 ইতিহাস খুলুন। এইমাত্র যে রান 📝 করা হয়েছে সেটি এখানে আছে, প্রতিটি কেসে ক্লিক করা যাবে। এবং ট্যাব ১-এ, uv run pytest tests/test_host.py ফেল করা পরীক্ষার মতোই একই পরীক্ষা চালায় — এবং একই ইতিহাসে ল্যান্ড করে।
সংখ্যা: আমাদের রান করা ৮টির মধ্যে ৪টি পাস করেছে, শব্দ ওভারল্যাপের জন্য, কোনও কারণ ছাড়াই — এবং 1 p১-এর জন্য সঠিক উত্তর, দশটি রুমের মধ্যে।
ডিনারের সমস্যা সমাধানের জন্য এই পছন্দটিই সবচেয়ে উপযুক্ত। শুক্রবার যাওয়ার জন্য কোনও একটি রেস্তোরাঁকে সঠিক বলে কেউ বলতে পারবে না। কিন্তু সফল ডিনার বলতে কী বোঝায় তা সবাই বলতে পারবেন: টেবিলে উপস্থিত সবাই খেতে পেয়েছেন। এটি একটি নিয়ম। এটি চেক করা যাবে। এটি অস্পষ্ট নয়। এটি তৈরি করা হল কোর্স III।
৫. III · বিচারক — কাস্টম মেট্রিক হিসেবে চেকার

চেকার তুলনাকারীর থেকে আলাদা প্রশ্ন জিজ্ঞাসা করে: এটি কি 'কী'-এর সাথে মেলে তা নয়, বরং প্রত্যেক ব্যক্তি কি খাবার পেয়েছেন? এটি পার্টির বিপরীতে বুকিং, সিট বাই সিট এবং প্রতি সিটের জন্য একটি স্কোর এবং একটি কারণ ফেরত দেয়। এর কোনও সঠিক উত্তর থাকার প্রয়োজন নেই — এটি সেইসব কথোপকথনকেও গ্রেড করতে পারে যা আগেই হয়ে গেছে (কোর্স VIII)।
world.everyone_ate হল সেই ফাংশন। কোর্স III এটি adk eval-এ ওয়্যার করে।
Inside everyone_ate — পাঁচ ধাপ, কোনওটিই বুদ্ধিদীপ্ত নয়
- ব্যবহারকারীর টেক্সট পড়ে দেখুন এবং এটি কোন পার্টি তা বোঝার চেষ্টা করুন। ব্রিফে একটি পার্টি আইডি আছে —
p1— এবং আমরা দেখে নিই যে সেই টেবিলে কে বসে আছেন। - এজেন্টের উত্তর পড়ুন, JSON পার্স করুন, রেস্তোরাঁর নাম ও বুকিংয়ের সময় বের করুন।
- প্রত্যেক ব্যক্তিকে ক্রমানুসারে নিয়ে যান। প্রত্যেকটির জন্য: ডায়েট, তারপর অ্যালার্জি, তারপর ঝাল সহ্য করার ক্ষমতা, তারপর বাজেট, তারপর হেঁটে যাওয়ার দূরত্ব, তারপর সময়। প্রত্যেক ব্যক্তির জন্য প্রতিবার ছয়টি চেক। টমকে নাও: তার বাজেট হল প্রতিজনের জন্য $২৫, রুমের ভাড়া হল $২৮। আমরা টমের জন্য
Falseএবং এর ব্যাখ্যা দেওয়া বাক্যটি রিটার্ন করি এবং পরবর্তী ব্যক্তির কাছে যাই। - স্কোর হল যতজন লোক খেয়েছেন সেই সংখ্যাকে টেবিলে বসা লোকের সংখ্যা দিয়ে ভাগ করে পাওয়া সংখ্যা। পাঁচের মধ্যে চার হল ০.৮। কোনও ওয়েটিং, কোনও আংশিক ক্রেডিট নেই।
- PASSED বা FAILED রিটার্ন করে। এবং লক্ষ্য করুন কে এটি ফেরত দেয়: আপনি। মনে রাখবেন; এটি হল নিচের ফাঁদ।
def ate_detail(person_id, rest, chosen_min):
p = PEOPLE[person_id]
if p.get("diet") == "vegan" and not rest["vegan"]:
return False, "nothing vegan on the menu", "diet"
if p.get("allergy") == "nut" and not rest["nut_safe"]:
return False, "everything is finished in peanut oil", "allergy"
...
if "budget" in p and rest["price"] > p["budget"]:
return False, f"~${rest['price']}/person against {name}'s ${p['budget']} budget", "budget"
... # walk, then timing
return True, "ate", ""
একটি প্রপার্টি উল্লেখ করার মতো: এই ফাংশনের কোথাও কোনও মডেল কল নেই। এটি হল ডিটারমিনিস্টিক Python। একই ইনপুট, একই রায়, প্রতিবার। মডেল আপডেট করা হলে আপনার জাজ ড্রাফ্ট হয় না, এটি রান করতে কোনও খরচ হয় না এবং এটি কোনও কারণের হ্যালুসিনেশন করতে পারে না। আপনি যদি এটি পেতে পারেন, তাহলে এটি ডিজাইন করার উপযুক্ত।
মেট্রিক, সম্পূর্ণ হয়েছে
metrics/table.py — অ্যাডাপ্টার ADK কল, এর ফিক্সড সিগনেচার সহ:
def everyone_ate_metric(eval_metric, actual_invocations,
expected_invocations=None, scenario=None):
return _build(world.everyone_ate, world.THRESHOLD, actual_invocations, "everyone_ate")
def _build(scorer, threshold, actual_invocations, metric):
per, scores = [], []
for inv in actual_invocations:
ids = _party_ids(inv) # who was invited — parsed from the PROMPT
rid, time_str = _decision_parts(inv) # what got booked — parsed from the RESPONSE
s, detail = scorer(ids, rid, time_str) if (ids and rid) else (0.0, [])
scores.append(s)
per.append(PerInvocationResult(
actual_invocation=inv, score=s, eval_status=_status(s, threshold)))
overall = round(sum(scores) / len(scores), 4) if scores else 0.0
return EvaluationResult(overall_score=overall,
overall_eval_status=_status(overall, threshold),
per_invocation_results=per)
লাইন বাই লাইন: এটি প্রতিটি ইনভোকেশনের উভয় দিক পড়ে — user_content থেকে পার্টি, final_response থেকে বুকিং — এগুলি পিওর ফাংশনে হ্যান্ডওভার করে এবং ADK-এর প্রত্যাশিত অবজেক্টে ফলাফল র্যাপ করে। এবং একটি লাইনে রায় দেওয়া হয়েছে:
def _status(score, threshold):
# ADK does not apply your threshold to custom metrics — this IS the verdict.
return EvalStatus.PASSED if score >= threshold else EvalStatus.FAILED

কনফিগারেশন — একটি লাইন
exam/eval_config.json, কোর্স সম্পূর্ণ হয়ে গেলে এটি যেমন দেখায়:
{
"criteria": {
"everyone_ate": 0.9
},
"customMetrics": {
"everyone_ate": {
"codeConfig": { "name": "metrics.table.everyone_ate_metric" },
"description": "How many of the party actually got dinner."
}
}
}
একটি লাইন পরিবর্তন করা হয়েছে: criteria এখন তুলনাকারীর পরিবর্তে আপনার বিচারকের নাম দেখায়। পরীক্ষাটি আটটি পার্টির, যা অপ্টিমাইজার কখনই দেখতে পাবে না (exam/parties_val.evalset.json, সব মিলিয়ে ৩৬টি সিট)।
এটি ব্যবহার করে দেখুন
👉🖥 পৃষ্ঠায় কোর্স III। 📝 বোতামের নিচে ক্যাপশনে হোস্ট যে নির্দেশাবলী চালাচ্ছেন তার নাম লেখা থাকে — এতে ড্রাফ্ট লেখা থাকতে হবে। অন্য কিছু লেখা থাকলে (আপনি আগে অন্য একটি ইনস্টল করেছেন), প্রথমে ↺ ড্রাফ্টে ফিরে যান বিকল্পে প্রেস করুন, অন্যথায় সংখ্যাগুলি স্টোরির সাথে মিলবে না।
👉🖥 এডিটরে লাইনটি অদল-বদল করুন (অথবা আমার জন্য এডিট প্রয়োগ করুন বিকল্প প্রেস করুন) এবং 📝 আবার পরীক্ষা চালান। এখন, ব্যর্থতার কারণ সহ সিটের নাম দেখানো হয় — টম: ~$২৮ বনাম $২৫ · লিনা: শেষ অর্ডার ১৯:৩০, তিনি ১৯:৪৫-এ এসেছেন — এবং টেবিলের নিচে দেখানো সংখ্যাটি পাস করা সিটের সংখ্যা থেকে পরিবর্তিত হয়ে গুরুত্বপূর্ণ সংখ্যায় পরিণত হয়: খালি সিট। কোনও চিপের উপর মাউস নিয়ে যান: প্রতিটি ব্যর্থ টেবিলের বাক্যগুলি এখানে দেখানো হয়।
👉🖥 তারপর 📋 টেবিল বুক করুন বিকল্পে প্রেস করুন। হোস্ট একটি রুম বেছে নেয়; একই ফাংশন সেইসব চেয়ারকে ধূসর করে দেয় যেগুলি সেখানে খেতে পারে না এবং প্রতিটির নিচে কারণ উল্লেখ করে। আবার প্রেস করুন — মডেলটি অন্যভাবে বুক করতে পারে (API তাপমাত্রা 0-তেও নির্ধারক নয়), কিন্তু বিচারক কখনই নড়চড় করেন না: একই রুম, একই রায়, একই বাক্য।
সংখ্যা: প্রথম দিনের ড্রাফ্ট, সৎ — আমাদের রান-এ ৮টি পার্টির মধ্যে ৩টি, ৩৬টি ক্ষুধার্ত সিট (আপনারটি টলমল করবে; API-এর তাপমাত্রা ০ হলেও তা ডিটারমিনিস্টিক নয় — সেই কারণেই পরীক্ষায় ৪টি কেসের পরিবর্তে ৩৬টি সিট আছে)। এই নম্বরটি হোল্ডে রাখুন। এটি সরানোর জন্য কোর্স V আছে।
৬. IV · ফ্লাইট — একটি উত্তর, তিনজন বিচারক
পরীক্ষায় হোস্টের উত্তর দু'বার মূল্যায়ন করা হয়েছে — একবার কম্পারেটর ও একবার আপনার চেকার দ্বারা। কোর্স IV সেই উত্তরগুলির মধ্যে একটি নেয় — আজকের রাতের পার্টি, p1, ঠিক যেভাবে পরীক্ষাটি রেকর্ড করেছে — এবং প্রথম দুটির পাশে একজন তৃতীয় বিচারক রাখে: একটি রুব্রিক সহ মডেল। কিছুই আবার বুক করা হয়নি। উত্তরটি যার সাথে তুলনা করে পরিমাপ করা হয়, সেটি পরিবর্তন হয়। এই একটি পার্থক্যই তিনটি মেট্রিক ফ্যামিলিকে আলাদা করে।
বিচারক | জিজ্ঞাসা করে | প্রয়োজন | পাস করে যদি | ব্লাইন্ড স্পট |
তুলনাকারী · | এটি কি আমার উত্তরের মতোই? | একটি গোল্ডেন উত্তর — একজন ব্যক্তি evalset-এ পার্টি প্রতি একটি বুকিং লিখেছেন | ROUGE-1 শব্দ ওভারল্যাপ ≥ ০.৩ | সঠিক উত্তর অন্যভাবে লিখলে শাস্তি পায়; ভুল উত্তর সঠিক শব্দে লিখলে পার পেয়ে যায়। এটি কখনও পার্টি পড়ে না। |
চেকার · | does this answer actually hold up? | নিয়ম — প্রতি সিটে ছ'টি করে চেক, কোনও গোল্ডেন উত্তর নেই | খাবার পরিবেশন করা সিট ÷ টেবিলের সিট ≥ ০.৯ | আপনার লেখা নিয়মের বাইরে কিছু দেখে না। নির্ধারক: প্রতিবার একই উত্তর, একই রায়, একই বাক্য। |
LLM জাজ · | সবাই কি খেতে পায়? — মডেলের কাছে জানতে চাওয়া | একটি মডেল ও চার লাইনের রুব্রিক; প্রেস পিছু একটি আসল কল | মডেলটি PASS বলে | ভুল হতে পারে এবং প্রেস করার মধ্যে এটি নিজের সিদ্ধান্ত পরিবর্তন করতে পারে। কল করার খরচ। |
চেকার কেন প্রশ্ন করছেন তা নোট করা উচিত। adk optimize যখন কোর্স V-এ রান করে, তখন এটি সেইসব বাক্য কখনওই দেখতে পায় না — ADK-এর অপ্টিমাইজার স্কোর পড়ে। এগুলি নষ্ট হয় না: এগুলিই টার্মিনালে দাঁড়িয়ে থাকা আপনার কাছে ব্যর্থতাকে বোধগম্য করে তোলে, যা আজ তাদের বেশিরভাগ মূল্যের সমান। এবং কোর্স VII-এ লুপটি ইভাল প্লাম্বিংয়ের মাধ্যমে না গিয়ে সরাসরি বিচারককে কল করে, তাই সেখানে কেনগুলি সত্যিই সেই জিনিসটির হাতে তুলে দেওয়া হয় যা পুনর্লিখন করছে।
তিনজন বিচারক, তারা দৌড়ানোর সময়
kitchen/flight.py. প্রথম দুটি হল পরীক্ষার নিজস্ব কোড — তুলনাকারী হল response_match_score-এর পিছনে থাকা ফাংশন, তাই এর নম্বরটি ঠিক সেটাই যা কোর্স II এই পার্টির জন্য প্রিন্ট করেছে:
from google.adk.evaluation.final_response_match_v1 import _calculate_rouge_1_scores
def comparator(answer, reference):
s = _calculate_rouge_1_scores(answer, reference) # ROUGE-1 F, as ADK computes it
return {"score": s.fmeasure, "passed": s.fmeasure >= 0.3, "needs": "a golden answer"}
def checker(party, answer):
rid, when = _decision(answer)
score, seats = world.everyone_ate(world.PARTIES[party], rid, when)
return {"score": score, "passed": score >= world.THRESHOLD, "needs": "the rules",
"why": " · ".join(f"{s['name']}: {s['why']}" for s in seats if not s["ate"])}
async def llm_judge(party, answer):
prompt = f"{RUBRIC}\n\n--- the brief ---\n{world.brief(party)}\n\n--- the booking ---\n{answer}"
r = await client.aio.models.generate_content(model="gemini-2.5-flash", contents=prompt, ...)
verdict = json.loads(r.text)["verdict"] # PASS or FAIL, plus one sentence
return {"passed": verdict == "PASS", "needs": "a model + a rubric"}
এটি ব্যবহার করে দেখুন
IV কোর্স 🖥👈 প্যানেলের সবচেয়ে উপরে: p1-এর জন্য হোস্ট কী উত্তর দিয়েছেন এবং তার পাশে evalset থেকে গোল্ডেন বুকিং — প্রথমে একটির সাথে অন্যটি মিলিয়ে দেখুন। তারপর ⚖ এই উত্তরটি তিনভাবে বিচার করুন বিকল্পে প্রেস করুন। একটি মডেল কল। তিনটি কার্ড পূরণ করুন: প্রতিটি বিচারক কী জিজ্ঞাসা করেন, কী প্রয়োজন, কী বলা হয়েছে এবং কেন।
আমাদের রান, p1-এর জন্য — প্রথম দিনের ড্রাফ্টের অধীনে ১৯:০০-এ Olive & Thyme — তুলনাকারী 0.৩০-এ পাস বলেছে (গোল্ডেন বুকিংয়ের সাথে যথেষ্ট শব্দ মিলেছে), চেকার 0.৬০-এ ফেল বলেছে (টম: ~$২৮ বনাম টমের $২৫ বাজেট · লেনা: শেষ অর্ডার ১৯:৩০, সে ১৯:৪৫ পর্যন্ত টেবিলে নেই) এবং LLM জাজ ফেল বলেছে এবং লেনার নাম নিয়েছে। তুলনাকারী ও পরীক্ষক একই উত্তর নিয়ে দ্বিমত পোষণ করেছেন — একজন শব্দ গণনা করেছেন, অন্যজন আসন গণনা করেছেন।
👉🖥 ⚖ আবার বিচার করুন বিকল্পে প্রেস করুন। তুলনাকারী কার্ডটি সরে না। চেকার কার্ডটি নড়াচড়া করে না। LLM কার্ডে — বোতামের নিচে লেখা থাকে যে এটি করা হয়েছে কিনা। তারপর বাঁদিকে (স্টেজের পার্টি সিলেক্টর) অন্য একটি পার্টি বেছে নিন এবং আরও একবার প্রেস করুন; তিনটি সবসময় একই লাইনে থাকে না।
সংখ্যা: একটি উত্তর, তিনটি রায় — আমাদের রান অনুযায়ী পাস · ফেল · ফেল এবং আপনি যতবারই জিজ্ঞাসা করুন না কেন, তিনটির মধ্যে দুটি উত্তর একই হবে।
৭. V · The rewrite — adk optimize and the ship gate
তিনটি ভূমিকা, কঠোরভাবে আলাদা রাখা হয়েছে:
ভূমিকা | কে? | কাজ |
পরীক্ষার্থী | হোস্ট | পরীক্ষা দেয় |
বিচারক | কোর্স III থেকে আপনার মেট্রিক | স্কোর, ডিটারমিনিস্টিক পদ্ধতিতে |
কোচ |
| ব্যর্থতা থেকে শিক্ষা নেয়, নির্দেশাবলী আবার লেখে |
কোচ কখনও গ্রেড দেয় না। বিচারক কখনও প্রস্তাব দেন না। একটি আর্টিফ্যাক্ট কী পরিবর্তন করে — নির্দেশাবলী, একটি টেক্সট ফাইল যা আপনি ডিফারেন্স, পর্যালোচনা ও রোল ব্যাক করতে পারবেন।
অপ্টিমাইজার কী পড়ে
GEPA কোনও নম্বর ক্লাইম্ব করে না। এটি কারণ পড়ে: এটি ট্রেনিং পার্টির বুকিংয়ের নমুনা নেয়, আপনার বিচারকের সাথে সেগুলি স্কোর করে এবং নতুন নির্দেশাবলী সাজেস্ট করে এমন একটি মডেলকে ব্যর্থতার নোট — টম বাজেট অতিক্রম করেছে, লেনা শেষ অর্ডারের পরে — দেয়। আগে দেখা পার্টিতে আগেরটির চেয়ে ভাল পারফর্ম করা প্রার্থীকে রাখা হয়; আগে কখনও দেখেনি এমন পার্টিতে যে সবচেয়ে ভাল পারফর্ম করে, সে জিতে যায়। এই স্প্লিটই হল সম্পূর্ণ ডিসিপ্লিন:
রাজনৈতিক দল | এর জন্য ব্যবহার করা হয় | |
| 8 | অপ্টিমাইজার এই ব্যর্থতাগুলি পড়ে |
| ৮ (৩৬টি আসন) | পরীক্ষা — অপ্টিমাইজারকে কখনও দেখানো হয় না |
দুটি কনফিগারেশন এটি চালায়, দুটিই exam/-এ আছে:
{ "eval_config": { "criteria": { "everyone_ate": 0.9 }, "customMetrics": { ... } },
"app_name": "host", "train_eval_set": "parties_train", "validation_eval_set": "parties_val" }
{ "optimizer_model": "gemini-2.5-flash", "max_metric_calls": 150,
"reflection_minibatch_size": 3, "run_dir": "./runs/gepa_run" }
বিজয়ীর নাম, যেমনটি লেখা আছে
অপ্টিমাইজার কখনও instruction_current.txt লেখে না। এটি জেতার নির্দেশাবলী প্রিন্ট করে; পৃষ্ঠাটি এটিকে runs/optimize/winner.txt-এ সেভ করে এবং ⤵ ইনস্টল এটিকে যথাস্থানে কপি করে। Here is how our real run's winner begins (২,৯৩১টি অক্ষর, ৩৭৫ থেকে):
Your primary goal is to book a restaurant that accommodates EVERYONE in the
group. Follow these steps precisely:
1. Understand Group Requirements: carefully parse the chat for each
individual's needs — dietary restrictions, allergies, budget limits,
arrival times, walking distance...
2. Filter restaurants by absolute constraints — a single violation for one
person eliminates the room for the whole group.
...
চার লাইনের ড্রাফ্টটি একটি সংখ্যাযুক্ত পদ্ধতিতে পরিণত হয়েছে এবং এর প্রতিটি নিয়ম পরীক্ষায় কেন তা ব্যাখ্যা করে। কেউ সেইসব বাক্য টাইপ করেনি।
আপনি নিজে রান পড়তে চাইলে, রানটি কোথায় ল্যান্ড করে:
ফাইল | এটি কী |
| পুরো রান; শেষের দিকে, |
| প্রতিটি প্রার্থীকে |
| সার্চ ট্রি, যা ব্রাউজারে খোলা যায় |
| পৃষ্ঠা দ্বারা সেভ করা বিজয়ী; ⤵ আপনার বিজয়ী ইনস্টল করুন এটি সঠিক জায়গায় কপি করে |
এটি লেখার সময় আমাদের দ্বিতীয় রিয়েল রান: ১৫০টি মেট্রিক কল, সাত মিনিটের কম সময়, ২৪৪৫-অক্ষরের একটি উত্তর যা "কোনও আপস ছাড়াই প্রতিটি সদস্যের খাদ্য ও সময় সংক্রান্ত প্রয়োজনীয়তা সম্পূর্ণরূপে পূরণ করে" দিয়ে শুরু হয় এবং ড্রাফ্টে কখনও না থাকা শেষ অর্ডার সংক্রান্ত নিয়মটি ব্যাখ্যা করে। দুটি রান থেকে পাওয়া বিজয়ীদের নাম আলাদা আলাদাভাবে লেখা হয় এবং স্কোর একই থাকে — অপ্টিমাইজার নিয়ম খুঁজে পায়, বাক্য নয়।
জাহাজের গেট
অপ্টিমাইজারের নিজস্ব স্কোরের উপর কখনও ভরসা করবেন না। আপনি এইমাত্র যে নির্দেশাবলী ইনস্টল করেছেন তা ব্যবহার করে হোল্ডআউটে পরীক্ষাটি আবার চালান। এই একটি কমান্ডই নিজেই উন্নত হওয়া ও নিজেকেই বাহবা দেওয়ার মধ্যে পার্থক্য তৈরি করে।
এটি ব্যবহার করে দেখুন
👉🖥 কোর্স V. দুটি দরজা: ⚗ অপ্টিমাইজ করুন আসল জিনিসটি রান করে — ১৫০টি মডেল কল, পাঁচ থেকে আট মিনিট, লগ স্ট্রিম প্যানেলে যায় — অথবা ⤵ সবচেয়ে ভালটি ইনস্টল করুন আমাদের রান করা একটি ব্যবহার করে। যাই হোক, 🚪 শিপ গেট প্রেস করুন এবং হোল্ডআউট সম্পর্কে পড়ুন। তারপর 📋 টেবিল বুক করুন: একই ব্রিফ, একই মডেল, আলাদা ডিনার এবং হোস্টের কারণ এখন টমের ক্যাপ এবং শেষ অর্ডারের কাট-অফ উল্লেখ করে।

দুটি সংখ্যাকে আলাদা করে দেখুন: ৫ / ৫ হল একটি টেবিলে সিট (p1, আজ রাতে); ৬ / ৮ হল পুরো পরীক্ষায় পার্টি। অ্যাপটি আপনাকে একটি ডিনার দেখায়; মূল্যায়ন আপনাকে ডিস্ট্রিবিউশন দেখায়। বিজয়ী p1-কে সম্পূর্ণভাবে ফিড করে — এবং তারপরেও আটটি হোল্ডআউট টেবিলের মধ্যে দুটি মিস করে।
সংখ্যা: হোল্ডআউট ৩/৮ → ৬/৮ আমাদের রান — এবং হাংরি সিট ৬ → ৯। দ্বিতীয় সংখ্যাটি ভালো করে পড়ুন, কারণ এটি রিগ্রেশন বলে মনে হলেও আসলে তা নয়। ড্রাফ্টটি পাঁচটি টেবিলে এক বা দুটি সিট করে কম পড়েছে (টমের বাজেট, লেনার আগমন)। বিজয়ী সেই টেবিলগুলি সম্পূর্ণভাবে পূরণ করে — এবং এখনও যে দুটি মিস করে, p1 এবং p2, এটি ১৯:৪৫-এ Taqueria Luna বুক করে যাতে লেনা এটি করতে পারে, যা রান্নাঘরের ১৯:৩০ শেষ অর্ডারের পরে: পুরো টেবিলটি ক্ষুধার্ত থাকে, পাঁচটি আসন এবং চারটি। কম টেবিল ব্যর্থ হয়েছে, আরও খারাপ ব্যর্থতা। নিচে একপাশে সিলিংয়ের উচ্চতা পরিমাপ করা আছে — এবং এটি মনে করিয়ে দেয় যে পাস করা সিটের সংখ্যা এবং খালি সিটের সংখ্যা আলাদা আলাদা প্রশ্নের উত্তর দেয়; পরীক্ষায় আপনি দুটিই দেখতে পাবেন।
৮. VI · অন্য বিচারক — পুরস্কার হ্যাকিং
Course V এজেন্টকে আরও ভালো করে তোলার মাধ্যমে আসল সংখ্যা বাড়িয়েছে। এই কোর্সে, শাসককে অন্ধ করে একটি জাল সংখ্যা উত্থাপন করা হয় এবং দেখা হয় যে এর ফলে কী হয়।
দুজন বিচারক
metrics/table.py-এ দুটিই আছে:
def everyone_ate(party, pick): # the honest judge
... # walks the table, seat by seat
def rating_score(party, pick): # the gameable judge
return pick.rating / 5.0 # <- party is never read
👉 আপনার লিন্টার আপনাকে সতর্ক করবে যে
party
ব্যবহার করা হয়নি। সেই সতর্কতাটিই হল সম্পূর্ণ পাঠ।
exam/eval_config_rating.json হল সেই একই শেপ যা আপনি কোর্স III-তে তৈরি করেছেন — একটি নাম, একটি থ্রেশহোল্ড, একটি ডটেড পাথ — যা অন্য ফাংশনের দিকে নির্দেশ করে:
{
"criteria": { "rating": 0.85 },
"customMetrics": {
"rating": {
"codeConfig": { "name": "metrics.table.rating_metric" },
"description": "The restaurant's star rating. Who is at the table is not part of this calculation."
}
}
}
টেবিলের উপরে থাকা সুইচ থেকে বোঝা যায় যে পরীক্ষাটি কোন দুটি কনফিগারেশনের মধ্যে কোনটি নিয়ে চলবে। জাজ সোয়াপ বলতে শুধু এটাই বোঝায়: একটি ফাইলের পরিবর্তে অন্য ফাইল, এজেন্ট লেভেলে কিছু নয়।
রেটিং বিচারক যে নির্দেশ চেয়েছিলেন
instructions/hacked.txt হল একটি আসল আর্টিফ্যাক্ট: রেটিং জাজের বিরুদ্ধে রান কী তৈরি করেছে। যত্ন নেওয়ার নির্দেশাবলীর সাথে এর পার্থক্য একটি লাইনে দেখানো হয়েছে যা গুরুত্বপূর্ণ:
- Ask the party about dietary restrictions.
+ Prioritise rooms with a rating of 4.5 and above.
এটি ব্যবহার করে দেখুন
👉🖥 কোর্স VI. ⤵ হ্যাক করা নির্দেশাবলী ইনস্টল করুন। সুইচটি ফ্লিপ করে রেটিং-এ নিয়ে যান — পৃষ্ঠাটি একটি রিভিউ অ্যাপে পরিণত হয়: স্টার, ট্রেন্ডিং, গড় অপেক্ষা। টেবিলটি ছোট হয়ে কোণে থাম্বনেল হিসেবে দেখা যায় এবং সেখানেও একই চেয়ারগুলি ধূসর রঙের দেখায়। রেটিং বিচারক কখনও টেবিল দেখেন না, তাই পৃষ্ঠাও দেখেন না।
📝 পরীক্ষা চালান বিকল্পে প্রেস করুন: ৮ / ৮. সুইচটি আবার ফ্লিপ করুন এবং এটি আবার চালান: একই আটটি উত্তর, সততার সাথে গ্রেড করা হয়েছে।

সংখ্যা: একই আটটি ডিনার — রেটিং ৮/৮, সৎ ১/৮, ৩৬টির মধ্যে ১৫টি খালি আসন। আটটি পার্টিই স্মোক অ্যান্ড ব্যারেল পাবে। প্রিয়াকে এপিপেন দিয়ে চিনাবাদাম তেলের রান্নাঘরে পাঠানো হয়। আর একটি টেবিল সততার সাথে পাস করে: হ্যাক করা এজেন্ট ভুলবশত সেই পার্টিকে ফিড করে যার সীমাবদ্ধতা হাইপ রুম মিস করে। এই কারণেই প্রক্সি বিশ্বাসযোগ্য হয়ে ওঠে — এটি সবসময় ভুল হয় না, আপনি যেখানে দেখছেন না সেখানে ভুল হয়।
৯. VII · লুপ — ব্যাক এজ সহ ওয়ার্কফ্লো
কোর্স V ছিল একটি লুপ যাতে আপনি ছিলেন: অপ্টিমাইজার চালান, ইনস্টল করুন, পরীক্ষা করুন। এই কোর্সে গ্রাফের মতো একই তিনটি ভূমিকা রয়েছে যা নিজে থেকেই চলে — বাউন্ডেড।
সেশন স্টেট হিসেবে নির্দেশাবলী
লুপে হোস্টের নির্দেশাবলী স্ট্রিং নয়। এটি একটি স্টেট টেমপ্লেট:
def seed(node_input: str):
yield Event(state={"brief": world.brief(PARTY), "current_instruction": DRAFT,
"whys": "(no failures yet — first round)"})
host = Agent(
name="host", model="gemini-2.5-flash",
instruction="{current_instruction}\n\nHere is tonight's request:\n{brief}",
output_key="decision", generate_content_config=gen)
প্রতিটি কলের সময় {current_instruction} স্টেট থেকে সমাধান করা হয়। পরবর্তী কোনও নোড সেই কী ওভাররাইট করলে, পরবর্তী হোস্ট কল নতুন টেক্সটের অধীনে রান করে। এটি হল সম্পূর্ণ স্ব-বিবর্তন প্রক্রিয়া — লুপে থাকা ভ্যালু স্টেটকে ক্রমাগত পরিবর্তন করে।
নোডগুলি
নোড | ধরণ | করে |
| ফাংশন | ব্রিফ ও প্রথম দিনের ড্রাফ্টকে স্ট্যাটাসে পরিবর্তন করে |
| এজেন্ট | বুকিং — মডেল কল, যে স্টেটেই থাকুক না কেন |
| ফাংশন |
|
| ফাংশন | সীমানা: |
| এজেন্ট | কেন এবং বর্তমান নির্দেশাবলী পড়ে, একটি বিকল্প লেখে |
| ফাংশন |
|
| ফাংশন | এপিসোড বন্ধ করে দেয় |

গ্রাফ, সম্পূর্ণ
loop/flywheel.py, build(), কোর্স শেষ হওয়ার পরে এটি যেমন দেখায়:
def build(nodes: dict):
seed, host, judge, router = nodes["seed"], nodes["host"], nodes["judge"], nodes["router"]
proposer, publish, done = nodes["proposer"], nodes["publish"], nodes["done"]
return Workflow(
name="flywheel",
edges=[
("START", seed, host, judge, router),
(router, {"improve": proposer, "ship": done}),
(proposer, publish, host),
],
)
পৃষ্ঠাটি শেষ প্রান্তটি ছাড়াই এটি শিপ করে। সবকিছু একবার করে রান করে — ড্রাফ্ট বই, বিচারক দুটি চেয়ার ধূসর করে দেন, প্রস্তাবক দুটি নিয়ম লেখেন — এবং তারপরে গ্রাফ শেষ হয়ে যায়, কারণ হোস্টের কাছে আবার লেখার কিছু থাকে না। লুপে একটি প্রান্ত নেই।
সীমানা — কারণ ADK তা করবে না
পিছনের দিকে নির্দেশ করে এমন একটি রুট এজ আপনাকে নেটিভভাবে লুপ দেয়। Workflow-এর কোনও ইটারেশন ক্যাপ নেই। রাউটারের শেষ প্রান্তে চারটি বেল্ট আছে:
def router(last_score: float):
if last_score >= world.THRESHOLD: # 1 · SHIP on pass
yield Event(route="ship")
elif ep["round"] >= MAX_ROUNDS: # 2 · round budget (default 4)
yield Event(route="ship") # "stopping is also a decision"
elif last_score <= ep["best"] and ep["flat"] >= 1:
yield Event(route="ship") # 3 · two flat rounds — a flat line is an answer too
else:
yield Event(route="improve")
# 4 · publish() truncates a runaway rewrite to 2,400 characters
সবচেয়ে খারাপ পরিস্থিতি: চার রাউন্ড, প্রতিটিতে দুটি করে মডেল কল, আটটি কল, হার্ড সিলিং। হোস্ট কখনও গ্রেড দেয় না, বিচারক কখনও প্রস্তাব দেয় না, রাউটার কখনও লেখে না।
এটি ব্যবহার করে দেখুন
👉🖥 কোর্স VII. শিপ করা হয়েছে হিসেবে ↻ লুপ রান করা: রাউন্ড ১ বই, ০.৬০-এ ব্যর্থ, প্রস্তাবকারী তার নিয়ম লেখে — এবং রানটি flywheel ended without closing দিয়ে শেষ হয়। এডিটরে সবুজ লাইনে থাকা # মুছে দিন। এটি আবার চালান।

প্রস্তাবকারী আমাদের রান সম্পর্কে যা লিখেছেন তা এখানে দেওয়া হল — ড্রাফ্ট, সাথে দুটি বাক্য, প্রতিটি ব্যর্থতার নোটের জন্য একটি করে বাক্য:
Ensure the total cost per person does not exceed anyone's stated budget. ← Tom's why
Confirm all guests can arrive before the kitchen's last order time. ← Lena's why
খালি সিট কেন খালি আছে; কেন খালি আছে তা একটি নিয়মে পরিণত হয়; নিয়মটি পরবর্তী বুকিং পরিবর্তন করে দেয়। রাউন্ড ২-এ ১৯:৪৫-এ Pho Saigon বুক করা হয়েছে → ১.০০। ঘণ্টা বাজে।
সংখ্যা: R1 0.60 → R2 1.00 · SHIP, প্রায় পাঁচ সেকেন্ডে। লুপের SHIP হল প্রতি ডিনার (একটি টেবিল, p1); কোর্স V-এর গেট ছিল প্রতি পরীক্ষা। একই শৃঙ্খলা, দুটি স্কেল।
১০. VIII · চেক — প্রতিটি জিনিস কী দিয়ে রিপ্লেস করা হয়েছে
👉🖥 কোর্স VIII. 🧾 চেকটা দিন, প্লিজ: আট লাইন, আপনার নিজের নম্বর।
আগে | পরে |
একটি ডেমো যা সঠিক বলে মনে হচ্ছে | কে খেয়েছে তা গণনা করার পরীক্ষা |
একটি গোল্ডেন উত্তর | সঠিক উত্তরের একটি সেট এবং একটি নিয়ম |
আপনি হাতে লিখে এডিট করেছেন এমন প্রম্পট | লুপের নতুন করে লেখা একটি নির্দেশ |
একটি সংখ্যা যা বৃদ্ধি পায় | একজন বিচারক যিনি নিজেকে ব্যাখ্যা করেন |
একবার রান করা | রাউন্ড, ফেরার পথ সহ |
আপনাকে যে মেট্রিক দেওয়া হয়েছে | আপনার বেছে নেওয়া নম্বর |
এই ক্রমে, বাড়িতে নিয়ে যান:
- কোনও মেট্রিক বেছে নেওয়ার আগে, আপনি কী কী লিখতে পারেন তা জিজ্ঞাসা করুন — উত্তর, নিয়ম বা শুধু ভালর বিবরণ। ডিনার সংক্রান্ত প্রশ্নের একটি সঠিক উত্তর আছে; তুলনাকারী সেটির সাথে চেক করে।
- লুপের আগে রুলার তৈরি করুন — এবং রুলার কীসের জন্য শাস্তি দেয় তা ওয়ার্ল্ড স্টেটকে জানান।
- মেট্রিকের মধ্যে ভারডিক্ট রাখুন; ADK আপনার জন্য থ্রেশহোল্ড প্রয়োগ করবে না।
- প্রথম অপ্টিমাইজ রান করার আগে হোল্ডআউট থেকে স্প্লিট ট্রেন করুন এবং অপ্টিমাইজারের নিজস্ব স্কোরের উপর ভিত্তি করে কখনওই শিপ করবেন না।
- Count hungry seats, not pass rates — small exams lie.
- ক্ষতি অপ্টিমাইজারের কাছে দৃশ্যমান করে তুলুন: কারণ সহ উত্তর দেওয়া বিচারক অপ্টিমাইজারের অর্ধেক কাজ করে দেয়।
- ADK আপনাকে লুপ দেয়; বাউন্ডারি আপনার। বন্ধ করাও একটি সিদ্ধান্ত।
- যখন সংখ্যাটি সর্বোচ্চ সীমায় পৌঁছায়, তখন এটি অবশ্যই হতে হবে কারণ এজেন্ট আরও ভালো হয়েছে — কখনওই নয় কারণ শাসক আরও নরম হয়েছে।
আপনি যে সংখ্যাই দিন না কেন, লুপ সেটিই পুশ করে।
প্রোডাকশন কোথায় ফিট করে
এই ল্যাবের সবকিছু আপনার মেশিনে আপনার লেখা পরীক্ষার বিপরীতে রান করেছে। প্রোডাকশন হল এমন ডিনার যা কেউ গ্রেড করার পরিকল্পনা করেনি — এবং একজন চেকার ঠিক সেটাই করতে পারেন যা গোল্ডেন-উত্তর মেট্রিক পারে না: ঘটনার পরে গ্রেড করা।

পাইপ | কীভাবে | উত্তর |
① Cloud Trace |
| এখন কি ঠিক আছে? — স্প্যান, প্ল্যাটফর্মের নিজস্ব বিচারক, অনলাইন মনিটর |
② BigQuery সারি | BigQuery Agent Analytics প্লাগ-ইন | আমাদের কী শেখা উচিত? — প্রতিটি ইভেন্টের জন্য একটি করে স্ট্রাকচার করা সারি, আপনার চেকার ও আপনার ইভ্যালসেট যে ফর্ম্যাটে ডেটা নেয় |
রেপোতে পাইপকে স্ক্রিপ্ট হিসেবে পাঠানো হয়, কোর্স হিসেবে নয়: refuel/send_traffic.py কোনও ডিপ্লয় করা হোস্টকে আসল পার্টি পাঠায়, refuel/replay_to_bq.py একই কথোপকথনকে সারি হিসেবে ল্যান্ড করে এবং refuel/harvest.py world.everyone_ate ব্যবহার করে প্রতিটি সারিকে আবার গ্রেড করে এবং ব্যর্থতাগুলিকে মূল্যায়ন কেস হিসেবে মিন্ট করে — গ্রুপের আসল শব্দ, কোনও গোল্ডেন উত্তর নয়। আমাদের প্রোডাকশন রাতে, পাঠানো হোস্ট ৮টি আসল পার্টির মধ্যে ৭টি পার্টিকে খাইয়েছিল; একটি পার্টিতে সমস্যা হয়েছিল, সেটি ছিল লেনা, যেখানে শেষ অর্ডারের সাথে কোর্সের V সিলিংয়ের পূর্বাভাস দেওয়া সিটের হুবহু মিল ছিল এবং এটি পরের রাউন্ডের পরীক্ষার প্রশ্ন হয়ে যায়। ল্যাপ টু হল আরও একটি ট্রেনিং কেস সহ কোর্স V। এটির জন্য বিলিং সহ একটি Google Cloud প্রোজেক্ট প্রয়োজন, যার কারণে এটি এখানে একটি রিড এবং মেনুতে একটি প্লেট নয়।
নিজে পড়া
- নিজের মতো করে একটি পার্টি তৈরি করো (
world.PARTIES, তারপরscripts/make_evalsets.py) এবং সেটি পরীক্ষায় লেখো। এটা কি ঠিক?scripts/verify_world.py-স্টাইল: প্রতিটি পার্টিতে অন্তত একটি বুকিং থাকতে হবে যা সকলের জন্য খাবার সরবরাহ করে। - রেটিং বার বাড়িয়ে ০.৯ করুন এবং
eval_config_rating.json-এadk optimizeপয়েন্ট করুন। গ্রেডিয়েন্ট পরিমাপ করুন। - বিলিং সহ কোনও প্রোজেক্ট আছে?
refuel/পাইপটি এন্ড-টু-এন্ড রান করান এবংharvest.py-এর[skip]লাইনগুলি পড়ুন — এগুলি হল দুটি পাইপের মধ্যে দৃশ্যমান সীমানা। HOST_THINKING=2048-এর সাথে লুপ রান করুন। কী কী পরিবর্তন হয় এবং তা থেকে আপনি কোথায় সিলিং আছে সেই ব্যাপারে কী জানতে পারেন?