GitHub Actions মার্জ কিউ-তে (merge queue) আট মাস কাটানো আপনাকে এমন কিছু শেখাবে যা ফিচার তুলনা ম্যাট্রিক্স (feature comparison matrices) কখনোই শেখাতে পারবে না। একটি ফ্রেমওয়ার্ক হয়তো পঞ্চাশটি মেট্রিক্স, চমৎকার ড্যাশবোর্ড এবং নামী গবেষণা ল্যাব থেকে প্রাপ্ত সাইটেশন দিতে পারে। কিন্তু যদি এটি আপনার ডেপ্লয়মেন্ট আটকে দেয় কারণ একটি "vibe check" স্কোর একই কোডের বিপরীতে ০.৭২ থেকে কমে ০.৬৮ হয়ে গেছে, তবে এটি উপকারের চেয়ে অপকারই বেশি করে। এটি আপনার শিপিং ভেলোসিটির (shipping velocity) জন্য একটি সক্রিয় হুমকি হয়ে দাঁড়ায়।
এটাই সেই ফিল্টার যা বেশিরভাগ LLM ইভ্যালুয়েশন রাউন্ডআপ মিস করে। তারা সক্ষমতা (capabilities) গণনা করে। কিন্তু মার্জ কিউ-তে একমাত্র গুরুত্বপূর্ণ প্রশ্নটি তারা খুব কমই করে: এই চেকটি প্রতিবার চলার সময় ঠিক একইভাবে পাস এবং ফেইল হয় কি না?
আমি এই কঠিন কাজটি করার মাধ্যমেই এটি শিখেছি। আমি ছয়টি ওপেন-সোর্স LLM ইভ্যালুয়েশন ফ্রেমওয়ার্ককে একটি রিয়েল CI পাইপলাইনের সাথে যুক্ত করেছি। সেগুলো আট মাস ধরে লাইভ প্রোডাকশন পুল রিকোয়েস্টের (pull requests) বিপরীতে চলেছে। দুটি ফ্রেমওয়ার্ক গেটকিপার হিসেবে টিকে থাকার যোগ্যতা অর্জন করেছে। বাকিগুলোকে অ্যাডভাইজরি ড্যাশবোর্ড হিসেবে নামিয়ে আনা হয়েছে, নাইটলি জবে সরিয়ে দেওয়া হয়েছে অথবা পুরোপুরি সরিয়ে ফেলা হয়েছে। শিক্ষাটি ছিল কঠোর এবং ব্যয়বহুল: আপনি যখন মেইন ব্রাঞ্চ পাহারা দিচ্ছেন, তখন প্রোবাবিলিস্টিক কোয়ালিটির (probabilistic quality) চেয়ে ডিটারমিনিস্টিক স্ট্রাকচার (deterministic structure) অনেক বেশি কার্যকর।
মার্জ গেটের আসল কাজ
একটি CI গেট কোনো গবেষণার পরিবেশ নয়। এটি একটি বাউন্সার। এর পুরো উদ্দেশ্য হলো একটি নির্দিষ্ট পরিবর্তন দেখে 'হ্যাঁ' বা 'না' উত্তর দেওয়া। হ্যাঁ, এই PR মেইন ব্রাঞ্চে যোগ হতে পারে। না, এটি পারে না। সেই উত্তরটি কয়েক সেকেন্ডের মধ্যে আসতে হবে, খরচ হতে হবে সামান্য, এবং এটি কখনোই পূর্ববর্তী সিদ্ধান্তের বিপরীতে পরিবর্তিত হওয়া চলবে না। আপনি যদি কোনো শান্ত মঙ্গলবার এবং একটি ব্যস্ত শুক্রবার একই কমিটের বিপরীতে একই পাইপলাইন পুনরায় চালান, তবে ফলাফল অবশ্যই অভিন্ন হতে হবে।
এখানেই বেশিরভাগ LLM ইভ্যালুয়েশন ফ্রেমওয়ার্ক হোঁচট খায়। এগুলো ডেটা সায়েন্টিস্টদের দ্বারা ডেটা সায়েন্টিস্টদের জন্যই তৈরি করা হয়েছে। এগুলো ইনসাইট, এক্সপ্লোরেশন এবং সূক্ষ্ম স্কোরিংয়ের জন্য অপ্টিমাইজ করা। কিন্তু একটি মার্জ কিউ অপ্টিমাইজ করা হয় বাইনারি সিদ্ধান্ত, গতি এবং জিরো ফ্ল্যাকিনেস (zero flakiness)-এর জন্য। এই দুটি লক্ষ্যের মধ্যে খুব সামান্যই মিল রয়েছে।
কেন LLM-as-Judge মার্জ কিউকে অচল করে দেয়
আমার পরীক্ষায় ব্যর্থ হওয়া টুলগুলোর একটি সাধারণ ডিজাইনের ত্রুটি ছিল: তারা প্রাথমিক গেট মেকানিজম হিসেবে LLM-as-judge কলের ওপর অতিরিক্ত নির্ভরশীল ছিল।
একটি LLM-as-judge প্রম্পট মডেলকে একটি আউটপুটকে এক থেকে দশের স্কেলে স্কোর করতে বলে, অথবা দুটি উত্তরের মধ্যে কোনটি ভালো তা বেছে নিতে বলে, অথবা তথ্যের সঠিকতা যাচাই করতে বলে। কোয়ালিটি ট্রেন্ড বোঝার জন্য এই পদ্ধতিটি শক্তিশালী। কিন্তু একটি ব্লকিং CI চেকের জন্য এটি বিষের মতো। একই ইনপুট বিভিন্ন দিনে ভিন্ন ভিন্ন স্কোর দিতে পারে কারণ টেম্পারেচার (temperature), মডেল ভার্সনিং এবং প্রম্পট ফরম্যাটিং—সবই নয়েজ (noise) তৈরি করে। যখন সেই স্কোরটি একটি কঠোর থ্রেশহোল্ড এবং একটি নির্দিষ্ট এক্সিট কোডের সাথে যুক্ত থাকে, তখন আপনার কিউটি অস্পষ্ট বা কাল্পনিক সমস্যার কারণে আটকে যায়।
এই ব্যর্থতাগুলো দ্রুত ছড়িয়ে পড়ে। একটি নন-ডিটারমিনিস্টিক চেক কিউতে ব্যাকআপ তৈরি করে। ইঞ্জিনিয়াররা যতক্ষণ না ফলাফল তাদের অনুকূলে আসছে ততক্ষণ বারবার চেষ্টা করতে শেখে, যা টিমকে রেড বিল্ড (red builds) বা ত্রুটিপূর্ণ বিল্ড উপেক্ষা করতে শেখায়। টোকেন খরচও বেড়ে যায় কারণ প্রতিটি রিট্রাই আরও বেশি API ক্রেডিট খরচ করে। সবচেয়ে খারাপ বিষয় হলো, সিগন্যালটি অর্থহীন হয়ে পড়ে। একটি রেড বিল্ডের অর্থ হওয়া উচিত "আপনি একটি বাগ (bug) তৈরি করেছেন।" কিন্তু যদি এর অর্থ হয় "জাজ মডেলটি আজ একটু বেশি খুঁতখুঁতে মেজাজে আছে," তবে বিশ্বাস হারিয়ে যায়।
টিকে থাকা টুলগুলো কী ভিন্নভাবে কাজ করে
Promptfoo এবং DeepEval টিকে গেছে কারণ তারা ডিটারমিনিস্টিক চেকগুলোকে প্রধান গুরুত্ব দেয় এবং LLM জাজ স্কোরগুলোকে গৌণ, নন-ব্লকিং সিগন্যাল হিসেবে বিবেচনা করে। তারা বোঝে যে একটি গেটের জন্য একটি এক্সিট কোড প্রয়োজন, কোনো মতামত প্রদানকারী ফ্লোটিং-পয়েন্ট নম্বর নয়।
Promptfoo, যা MIT লাইসেন্সের অধীনে মুক্তিপ্রাপ্ত, কমান্ড লাইনের জন্য তৈরি করা হয়েছে। এটি regex ম্যাচ, JSON স্কিমা ভ্যালিডেশন, contains চেক এবং সঠিক স্ট্রিং কম্পারিজন-এর মতো অ্যাসারশন (assertions) চালায়। এগুলো খুব জাঁকজমকপূর্ণ কিছু নয়; এগুলো মূলত উন্নত মানের grep এবং jq কমান্ডের মতো। ঠিক এই কারণেই এগুলো CI-তে কাজ করে। একটি regex হয় ম্যাচ করবে অথবা করবে না। একটি JSON স্কিমা হয় ভ্যালিডেট করবে অথবা এরর দেবে। Promptfoo স্ট্যান্ডার্ড ইউনিক্স এক্সিট কোড প্রদান করে, তাই GitHub Actions সহজেই বুঝতে পারে কখন মার্জ থামিয়ে দিতে হবে। এটি ল্যাঙ্গুয়েজ-অ্যাগনস্টিক (language-agnostic) কারণ এটি একটি CLI টুল হিসেবে কাজ করে। আউটপুট ভ্যালিডেট করার জন্য আপনাকে একটি Node.js সার্ভিস রিপোর ভেতরে পাইথন ইকোসিস্টেম ইনস্টল করার প্রয়োজন নেই।
DeepEval, যা Apache 2.0 লাইসেন্সের অধীনে, পাইথন টিমগুলোর জন্য সেরা পছন্দ। এটি pytest-এর মতো ইন্টিগ্রেট করা যায়। আপনি পরিচিত সিনট্যাক্সে টেস্ট লিখতে পারেন এবং একটি ফেইলর স্বাভাবিকভাবেই পুরো স্যুটকে আটকে দেয়। DeepEval অনেক ধরণের মেট্রিক্সের বিশাল ক্যাটালগ অফার করে, তবে গুরুত্বপূর্ণ বিষয় হলো আপনাকে সেগুলো সাবধানে ব্যবহার করতে হবে। গেটের জন্য ডিটারমিনিস্টিক বা হিউরিস্টিক মেট্রিক্সের ওপর নির্ভর করুন। আপনি যদি G-Eval বা অন্যান্য জাজ-ভিত্তিক স্কোরার ব্যবহার করেন, তবে সেগুলোকে হার্ড অ্যাসার্ট (hard asserts) হিসেবে না ব্যবহার করে নন-ব্লকিং রিপোর্ট জেনারেটর হিসেবে ব্যবহার করুন। এভাবে ব্যবহার করলে, DeepEval আপনাকে একটি রিসার্চ নোটবুকের অস্থিরতা ছাড়াই একটি টেস্টিং ফ্রেমওয়ার্কের সুবিধা প্রদান করে।
বাকি চারটি ফ্রেমওয়ার্কের অবস্থান
যে চারটি ফ্রেমওয়ার্ক গেট হিসেবে টিকে থাকতে পারেনি, তাদেরও মূল্য আছে। সেগুলো কেবল আপনার টুলচেইনের অন্য কোনো স্থানে ব্যবহারের জন্য উপযুক্ত।
Future AGI (Apache 2.0) পঞ্চাশটিরও বেশি মেট্রিক্স প্রদান করে এবং কাস্টম SDK তৈরি করা দলগুলোকে লক্ষ্য করে। মেট্রিক্সগুলো অত্যন্ত পুঙ্খানুপুঙ্খ। সমস্যা হলো, টুলটি আশা করে যে আপনি CI কিউতে (queue) এটি চালানোর জন্য নিজস্ব হারনেস (harness) লিখবেন। গবেষণার ক্ষেত্রে এটি একটি যুক্তিসঙ্গত বিনিময়। কিন্তু একটি মার্জ কিউতে (merge queue), কাস্টম ওয়্যারিং-এর প্রতিটি স্তর অস্থিরতার একটি নতুন উৎস হয়ে দাঁড়ায়। এটি একটি সক্ষম মূল্যায়ন ইঞ্জিন, কিন্তু এটি কোনো প্রস্তুত গেটকিপার (gatekeeper) নয়।
RAGAS (Apache 2.0) রিট্রিভাল-অগমেন্টেড জেনারেশন (retrieval-augmented generation) এর মান পরিমাপে পারদর্শী। এর faithfulness এবং answer relevance মেট্রিক্সগুলো একটি নলেজ বেস সময়ের সাথে সাথে কেমন পারফর্ম করছে তা বোঝার জন্য সত্যিই কার্যকর। দুর্ভাগ্যবশত, এই মেট্রিক্সগুলো ব্যাপকভাবে LLM জাজদের (judges) ওপর নির্ভরশীল। স্ল্যাকে (Slack) ট্রেন্ড পোস্ট করার জন্য একটি নাইটলি কোয়ালিটি জবের ক্ষেত্রে এগুলো চমৎকার। কিন্তু পুল রিকোয়েস্টের (pull request) ক্ষেত্রে এগুলো খুব একটা কার্যকর নয়। RAGAS-কে আপনার শিডিউলড অ্যানালাইসিস পাইপলাইনে রাখুন, মার্জ ব্লকার হিসেবে নয়।
Arize Phoenix-এ Elastic License 2.0 ব্যবহৃত হয় এবং এটি সম্পূর্ণ ভিন্ন একটি ক্ষেত্রে কাজ করে। এটি ডিস্ট্রিবিউটেড ট্রেসিং-এর সাথে মূল্যায়নের সংযোগ ঘটায়, যা একটি মডেল কেন নির্দিষ্টভাবে আচরণ করছে তা পর্যবেক্ষণ করার সুযোগ দেয়। যখন আপনি কোনো প্রোডাকশন ইনসিডেন্ট ডিবাগ করছেন বা কোনো হ্যালুসিনেশনকে (hallucination) একটি ভুল রিট্রিভাল চাঙ্কের (retrieval chunk) সাথে যুক্ত করার চেষ্টা করছেন, তখন এটি আপনার প্রয়োজন। একজন জুনিয়র ডেভেলপারের ফিচার ব্রাঞ্চ শিপ করা যাবে কি না, তা সিদ্ধান্ত নেওয়ার জন্য আপনি কোনো ট্রেসিং টুল ব্যবহার করতে চাইবেন না। এর আর্কিটেকচার ইনসাইট বা অন্তর্দৃষ্টির জন্য তৈরি করা হয়েছে, বাইনারি গেটের জন্য নয়।
MLflow Evaluate (Apache 2.0) এক্সপেরিমেন্ট ট্র্যাকিং থেকে এর ঐতিহ্য উত্তরাধিকারসূত্রে পেয়েছে। এটি বেশ ভারী। একটি হালকা (lean) CI ইমেজে এটি যুক্ত করলে স্টার্টআপ টাইম এবং ডিপেন্ডেন্সি বেড়ে যায়, যা প্রতিটি জবকে ধীর করে দেয়। যদি আপনাকে অবশ্যই একটি পাইপলাইনের ভেতরে এটি ব্যবহার করতে হয়, তবে স্ট্রাকচারাল চেকের জন্য এর হিউরিস্টিক মেট্রিক্সগুলো ব্যবহার করুন। এমনকি তখনও, আপনি ফ্রেমওয়ার্কের মৌলিক ডিজাইনের বিরুদ্ধে লড়াই করছেন। MLflow চায় রানগুলো লগ করতে এবং কয়েক সপ্তাহ জুড়ে এক্সপেরিমেন্টগুলো তুলনা করতে। অন্যদিকে, একটি মার্জ কিউ এক মিনিটের কম সময়ে একটি সিদ্ধান্ত চায়।
গেটিং-এর জন্য ব্যবহারিক নিয়মাবলী
এই পরীক্ষা থেকে আপনি অন্য কিছু না পেলেও, এই তিনটি নিয়ম মনে রাখুন।
প্রথমত, ভাইব (vibe) নয়, বরং স্ট্রাকচার (structure) যাচাই করুন। আপনি নিশ্চিত করতে পারেন যে আউটপুটটি একটি বৈধ JSON। আপনি নিশ্চিত করতে পারেন যে এতে প্রয়োজনীয় কী (keys) আছে। আপনি নিশ্চিত করতে পারেন যে একটি ক্লাসিফিকেশন লেবেল অনুমোদিত কোনো enum-এর অন্তর্ভুক্ত। এই পরীক্ষাগুলো দ্রুত, সাশ্রয়ী এবং ডিটারমিনিস্টিক (deterministic)। একটি সারাংশ "বন্ধুত্বপূর্ণ" কি না বা একটি পুনর্লিখন "সৃজনশীল" কি না, তা আপনি নির্ভরযোগ্যভাবে নিশ্চিত করতে পারবেন না। এই গুণাবলীগুলো মানুষের রিভিউ বা পর্যায়ক্রমিক ব্যাচ মূল্যায়নের জন্য, স্বয়ংক্রিয় গেটের জন্য নয়।
দ্বিতীয়ত, যদি অপরিবর্তিত ইনপুটের ওপর কোনো স্কোর পরিবর্তিত হয়, তবে অবিলম্বে সেটিকে গুরুত্ব কমিয়ে দিন। ঠিক একই আর্টিফ্যাক্টের (artifact) বিপরীতে আপনার ইভ্যালুয়েশন স্যুটটি দুবার চালান। যদি কোনো মেট্রিক্স 'পাস' থেকে 'ফেল'-এ পরিবর্তিত হয়, তবে সেটি মার্জ ব্লক করার অধিকার হারিয়ে ফেলে। এটিকে একটি অ্যাডভাইজরি ড্যাশবোর্ডে নিয়ে যান যেখানে ভ্যারিয়েন্স (variance) প্রত্যাশিত এবং সহনশীল।
তৃতীয়ত, এক্সিট কোডকে (exit code) সম্মান করুন। একটি লাল ব্যানারসহ সুন্দর HTML রিপোর্ট মার্জ থামায় না। একটি নন-জিরো (nonzero) এক্সিট কোড থামায়। আপনার ইভ্যালুয়েশন টুলকে অবশ্যই আপনার CI প্ল্যাটফর্মের নিজস্ব ভাষায় কথা বলতে হবে। স্ট্যান্ডার্ড আউট (Standard out) মানুষের জন্য। এক্সিট কোড মেশিনের জন্য।
মূল শিক্ষা
LLM-চালিত অ্যাপ্লিকেশনগুলো কীভাবে পরীক্ষা করতে হবে তা বোঝার ক্ষেত্রে আমরা এখনও প্রাথমিক পর্যায়ে আছি। প্রলোভন হলো মূল্যায়নকে মানুষের গ্রেডিং রুব্রিকের মতো দেখা: সূক্ষ্ম, প্রাসঙ্গিক এবং কিছুটা ব্যক্তিনিষ্ঠ। এটি একটি রিসার্চ পেপারে কাজ করে। কিন্তু একটি মার্জ কিউতে এটি ব্যর্থ হয়।
আট মাসের প্রোডাকশন ট্র্যাফিকের পর, আমার পাইপলাইন এখন বিভিন্ন সার্ভিসের স্ট্রাকচারাল এবং স্কিমা অ্যাসারশনের (schema assertions) জন্য Promptfoo এবং পাইথন-সাইড বিহেভিয়ারাল চেকের জন্য DeepEval ব্যবহার করে, যা সরাসরি পাস-ফেল কন্ডিশনের সাথে মিলে যায়। বাকি সবকিছু নাইটলি ড্যাশবোর্ডে রিপোর্ট করা হয়। কিউটি এখন স্থিতিশীল। সিগন্যালটি পরিষ্কার। টিম এখন আবার একটি 'রেড বিল্ড'-এর ওপর আস্থা রাখতে পারে।
আপনার গেটে আরও বেশি মেট্রিক্সের প্রয়োজন নেই। আপনার প্রয়োজন এমন কিছু মেট্রিক যা প্রতিবার সত্য প্রকাশ করে।
মূল টেস্টিং এবং লেখার ওপর ভিত্তি করে যা Dev.to-এ শেয়ার করা হয়েছে। নির্ভরযোগ্য AI সিস্টেম তৈরির বিষয়ে আরও আলোচনার জন্য, Telegram-এ GyaanSetu কমিউনিটিতে যোগ দিন।
