در مسیر توسعهی سیستمهای مبتنی بر هوش مصنوعی، ساخت متریکهای مجزا و فرمولهای کالیبراسیون بهتنهایی کافی نیست؛ چالش اصلی زمانی آغاز میشود که بخواهید این قطعات پراکنده را به یک پایپلاین یکپارچه و خودکار متصل کنید. بدون یک سامانهی اجرای ارزیابی (Eval Runner) استاندارد، مقایسهی مدلهای مختلف نیازمند اجرای دستی اسکریپتها و جمعآوری پراکندهی نتایج خواهد بود که خطای انسانی را به شدت بالا میبرد.
در این درس یاد میگیرید که چگونه تمام اجزای طراحیشده در درسهای پیشین — از مشخصات تسکها و لایهی متریکها گرفته تا تحلیل کالیبراسیون و تجمیع لیدربورد — را با یک رابط تطبیقدهنده (Adapter) تمیز به هم گره بزنید. این رانر وظایف را بهصورت موازی پردازش کرده و در یک دور پیمایش، هم امتیازات و هم دادههای قابلیت اطمینان مدل را استخراج میکند.
برای اجرای کدهای این درس به کارت گرافیک گرانقیمت یا اتصال مداوم به API خارجی نیازی ندارید؛ اجرای آزمایشی روی CPU با استفاده از مدلهای ساختگی (Mock) و ماژول استاندارد concurrent.futures در پایتون انجام میشود و معماری حاصل، آمادهی اتصال به مدلهای محلی یا سرویسهای ابری است.
پنج درس لولهکشی کردیم و حالا در این درس همه را به هم وصل میکنیم. رانر (runner) مشخصات وظیفه را از درس 70 میخواند، مدل را از طریق یک تطبیقدهنده (adapter) فراخوانی میکند، با منطق درسهای 71 و 72 امتیاز میدهد، گزارش کالیبراسیون درس 73 را ضمیمه میکند و در نهایت جدول ردهبندی درس 74 را تحویل میدهد. اجرای نمایشی در پایان بهطور خودکار بسته میشود.
اهداف یادگیری
- تعریف اینترفیس
ModelAdapterبهگونهای که هر مدلی (ساختگی، محلی، API) با پیادهسازی چند متد محدود با آن سازگار شود. - اجرای ارزیابی روی یک فایل فیکسچر JSONL با اجرای موازی وظایف در استخری از ورکرها.
- ترکیب لایهی متریکها (
exact_match،F1،BLEU-4،ROUGE-Lوcode_exec) با لایهی کالیبراسیون تنها در یک دور پیمایش (single pass). - تولید رکوردهای
EvalRunبه تفکیک هر مدل و ارسال مستقیم آنها به تجمیعکنندهی جدول ردهبندی. - ارائهی خروجی در هر دو قالب گزارش JSON و جدول Markdown؛ خروج خودکار با کد صفر در صورت اجرای موفق، و کد غیرصفر در صورت بروز خطای اعتبارسنجی یا محیط اجرا.
پایپلاین
رانر نقطهی یکپارچهسازی کل سیستم است. هر یک از درسهای 70 تا 74 ماژولی ساختهاند که رانر آنها را کنار هم مینشاند. رانر منطق هیچکدام از این ماژولها را دوباره تکرار نمیکند؛ صرفاً آنها را ایمپورت میکند.
اینترفیس تطبیقدهنده
تطبیقدهنده مرز اتصال میان رانر و هر مدلی است. این اینترفیس را عمداً بسیار جمعوجور طراحی کردهایم.
class ModelAdapter:
model_id: str
def generate(self, prompt: str, task: TaskSpec) -> Generation: ...
کلاس دادهی Generation شامل فیلدهای زیر است:
text: خروجی متنی آزاد مدلconfidence: عددی اعشاری در بازهی[0, 1]که نشاندهندهی احتمال خوداظهاریِ مدل برای پاسخ استtoken_nll: مجموع درستنمایی لگاریتمی منفیِ توکنهای تولیدشده (اختیاری)token_count: تعداد توکنهای تولیدشده (اختیاری)
تطبیقدهندههای ساختگی (mock) در رانر سه رفتار مختلف ارائه میدهند: RuleBasedAdapter (قطعیتگرا و تقریباً بینقص)، NoisyAdapter (بیشازحد مطمئن، اما اغلب اشتباه) و BiasedAdapter (عالی در یک دستهبندی و فاجعه در دستهبندی دیگر). دموی برنامه هر سه را روی فیکسچر درس 70 اجرا میکند.
اجرای موازی
رانر با استفاده از concurrent.futures.ThreadPoolExecutor وظایف مربوط به هر مدل را بهشکل موازی اجرا میکند. تعداد ورکرها بهطور پیشفرض برابر با مقدار کمتر میان عدد 8 و تعداد کل وظایف است. استفاده از نخها (threads) کاملاً کفایت میکند، چرا که گلوگاه اصلی در فراخوانی مدلهای واقعی، I/O شبکه است. مسیر ارزیابی اجرای کد (code_exec)، زیرفرایند (subprocess) اختصاصی خود را داخل تسک میسازد و مجری (executor) فقط منتظر پایان آن میماند.
رانر برای آزمونهای قطعی، تابع run_eval(adapters, tasks, parallel=False) را ارائه میدهد تا تستها بتوانند ترتیب اجرا را ثابت نگه دارند.
حلقهی امتیازدهی تکپیمایشی
برای هر وظیفه مراحل زیر طی میشود:
- پرامپت آماده و رندر میشود (پیشوند چندمثاله بههمراه متن اصلی پرامپت).
- تطبیقدهنده فراخوانی شده و مدتزمان فراخوانی اندازه گرفته میشود.
- خروجی مدل بر اساس قوانین همان وظیفه پسپردازش میشود.
- خروجی به لایهی متریک فرستاده میشود.
- رکورد
EvalRunشامل امتیاز و متادیتای متریک ساخته میشود. - جفت
(confidence, correct)به بافر کالیبراسیون اضافه میشود.
سیگنال درستی (correct) برای متریکهای مشابه تطابق دقیق (exact_match، accuracy و code_exec) شرط score >= 1.0 و برای متریکهای درجهبندیشده شرط score >= 0.5 است. این آستانه در _correct_from_score قرار دارد و رانر امکان بازنویسی (override) عمومی آن را نمیدهد.
تجمیع
پس از ثبت نتیجهی تمام وظایف، رانر aggregate و pairwise_diffs از درس 74 و CalibrationReport.from_predictions از درس 73 را فراخوانی میکند. خروجی نهایی یک پوشش (envelope) واحدِ JSON است:
{
"leaderboard": [...],
"pairwise": [...],
"calibration": {
"model_id_a": {"ece": 0.04, "brier": 0.10, "populated_bins": 8, ...},
...
},
"summary": {
"tasks": 10,
"models": 3,
"wall_seconds": 1.2
}
}
رانر همچنین یک جدول Markdown در خروجی استاندارد چاپ میکند تا کاربر بتواند نتیجه را مستقیماً در بررسی Pull Request بچسباند.
اجرای نمایشی خودپایاندهنده
دموی برنامه سه تطبیقدهندهی ساختگی را روی 10 وظیفهی فیکسچر درس 70 اجرا میکند. زمان اجرای واقعی (wall time) باید زیر 10 ثانیه بماند. اگر اجرا تمیز و بینقص باشد، کد خروجی صفر خواهد بود.
معیارهای یک اجرای تمیز عبارتند از:
- اعتبارسنجی همهی وظایف بر اساس درس 70.
- امتیازدهی تمام وظایف بر اساس درسهای 71 و 72.
- تجمیع بدون خطای گزارش کالیبراسیون با منطق درس 73.
- قرار گرفتن تطبیقدهندهی مبتنی بر قاعده بالاتر از تطبیقدهندهی تصادفی در رتبهبندی جدول.
اگر هرکدام از این موارد نقض شود، رانر با کد غیرصفر خارج میشود و یک خطای ساختاریافته در پوشش JSON برمیگرداند.
این درس چه کارهایی انجام نمیدهد؟
در این درس به یک مدل واقعی متصل نمیشویم. جریان کاری مدیریت کلید API یا محدودیت نرخ درخواست را پیادهسازی نمیکنیم. خبری از استریمینگ یا تولید جزئی پاسخ نیست؛ تطبیقدهنده در هر فراخوانی دقیقاً یک خروجی برمیگرداند. همچنین مکانیزم تلاش مجدد یا کش کردن را پیاده نمیکنیم. این دغدغهها به لایهی تطبیقدهنده مربوط میشوند؛ رانر به نوع متریک و ارائهدهندهی مدل کاملاً بیاعتنا و مستقل است.
راهنمای خواندن کد
فایل main.py محل یکپارچهسازی نهایی است. این فایل با کمک یک تابع کمکی کوچک به نام _load_sibling ماژولهای پنج درس قبلی را از مسیر نسبی ایمپورت میکند. کلاسهای دادهی Generation، EvalReport و ModelAdapter داخل خود فایل تعریف شدهاند. تطبیقدهندههای ساختگی نیز در انتهای فایل قرار دارند.
کد main.py را از بالا به پایین بخوانید: ابتدا نگاهی گذرا به ایمپورتها بیندازید، بعد سراغ run_eval و سپس _score_one بروید و در آخر تطبیقدهندهها را ببینید. دموی انتهای فایل نقطهی ورود برنامه است.
تستهای موجود در code/tests/test_runner.py اینترفیس تطبیقدهنده، حلقهی تکپیمایشی، یکسانی اجرای موازی و ترتیبی، بافر کالیبراسیون و ساختار پوشش JSON را تثبیت و بررسی میکنند.
گامهای بعدی
این رانر تازه کفِ کار است. یک سامانهی ارزیابی در محیط عملیاتی (پروداکشن) این موارد را اضافه میکند: کش نتایج با کلید ترکیبی (task_id, model_id, model_version)، دفتر ثبت هزینهها برای رهگیری دلاری و توکنیِ هر اجرا، لایهی تلاش مجدد با پسنشینی هنگام برخورد به محدودیت نرخ درخواست، سیاست نمونهبرداری برای وظایف pass-at-k، و فرمت خروجی استریمینگ برای مجموعهآزمونهای طولانی. هر یک از این موارد یک دغدغهی مستقل است که بدون تغییر لایههای متریک یا تجمیع، دور رانر را میپوشاند. هدف از این قرارداد دقیقاً رسیدن به همین تفکیک وظایف است.
وقتی تطبیقدهندههای ساختگی بهدرستی کار کردند، یک تطبیقدهنده برای ارائهدهندهای واقعی بنویسید. سرویسی با پلن رایگان انتخاب کنید، 30 خط کد اتصال بنویسید و روشن شدن جدول ردهبندی را تماشا کنید. سپس ارائهدهندهی دوم را اضافه کنید و کار را به این بستر آزمایشی بسپارید.
