توسعهی ایجنتهای کدنویسی بدون یک چارچوب سنجش دقیق، شبیه برنامهنویسی بدون تست واحد است. وقتی یک پرامپت یا مدل زبانی را تغییر میدهید، چطور مطمئن میشوید که عملکرد کلی سیستم بهبود یافته و دچار افت کیفیت (Regression) نشده است؟ تکیه بر بررسی دستی کدها یا ادعای خود ایجنت مبنی بر حل مسئله، بهسرعت به خطاهای پنهان در محیط عملیاتی ختم میشود.
در این درس یک بستر ارزیابی (Eval Harness) کامل میسازید که مجموعهای از وظایف آزمایشی ثابت (Fixture Tasks) را روی ایجنت اجرا میکند، با اعتبارسنجهای قطعی نتیجه را میسنجد و معیارهای کلیدی مانند pass@1، pass@k، تاخیر زمانی و هزینهی توکنها را استخراج میکند تا منبع حقیقت پایداری برای سنجش تغییرات داشته باشید.
یک ایجنت کدنویسی دقیقاً به همان اندازهای خوب است که مجموعهی وظایف سنجش آن خوب باشد. در این درس، یک بستر ارزیابی (Evaluation Harness) میسازیم که پوشهای از وظایف ثابت (Fixture Tasks) را میگیرد، هرکدام را روی یک ایجنت کاندید اجرا میکند، قبولی یا رد شدن را با یک مدل اعتبارسنج (Verifier) قطعی میسنجد و نتایج را در قالب شاخصهای pass@1، pass@k، میانگین تاخیر (Latency) و میانگین هزینه تجمیع میکند. این بستر آزمایشی همان منبع حقیقت (Source of Truth) شماست که تفاوت بین یک افت کیفیت (Regression) و یک بازآفرینی کد (Refactor) را مشخص میکند.
اهداف یادگیری
- تعریف وظیفهی ثابت بهصورت سهگانهای (Triple) از هدف (Goal)، راهاندازی (Setup) و اعتبارسنج (Verifier).
- امتیازدهی به چندین اجرای نمونه برای هر وظیفه و محاسبهی pass@1 و pass@k.
- تجمیع تاخیر و هزینه در قالب معیارهای میانگین و صدک 95ام.
- اتصال اعتبارسنجهای قطعی (تفاوت فایل، کد خروجی، تطابق رگکس) به توابعی با قابلیت استفادهی مجدد.
- ارائهی یک گزارش ساختاریافتهی JSON که اسکریپتهای رهگیری افت کیفیت بتوانند آن را دریافت و پردازش کنند.
صورت مسئله
سه حالت شکست (Failure modes) گریبانگیر بنچمارکهای ایجنتی میشود که بدون بستر ارزیابی ساخته شدهاند:
اولی قبولی تاییدنشده است. ایجنت ادعا میکند باگ را برطرف کرده، انسان نگاهی گذرا به تفاوت فایلها (Diff) میاندازد، تستها سبز ثبت میشوند و سه هفته بعد، تستهای ارزیابی افت کیفیت همان باگ را دوباره رو میکنند. ایجنت استدلالی بهظاهر موجه ارائه داده بود، بدون اینکه در عمل چیزی را درست کرده باشد.
دومی افت کیفیت پنهان است. تغییری در قالب پرامپت (Prompt template) ایجنت را روی وظیفهای پرسرصدا 4% بهتر و روی وظیفهای بیسرصدا 14% بدتر میکند. بدون یک مجموعهی مرجع و معتبر (Goldset) و نمرهی مجزا برای هر وظیفه، این افت کیفیت راهی شاخهی اصلی پروژه (Main) میشود و فقط وقتی صدای مشتری درآمد خودش را نشان میدهد.
سومی انحراف معیارهای مقایسه در طول زمان است. ارزیابی روز دوشنبه با 100 وظیفه اجرا شده و روز جمعه با 95 تا، چون یک نفر نام پنج وظیفهی ثابت را عوض کرده است. در ظاهر، نرخ قبولی (Pass Rate) نشاندهندهی 5% بهبود است؛ اما در واقعیت اینطور نیست.
بستر آزمایشی همان برنامهای است که این ناکامیها را به دادههای واقعی و مستند تبدیل میکند. این بستر تکتک وظایف ثابت را، هر بار با ترتیبی تکرارپذیر، در برابر اعتبارسنجی اجرا میکند که بر اساس یک بررسی قطعی، فقط درست یا غلط برمیگرداند.
مفاهیم پایه
یک FixtureTask شامل یک فایل JSON کوچک بههمراه یک پوشهی اختیاری به نام expected/ است. فایل JSON مواردی چون id، بخش goal (همان پرامپت ارائهشده به ایجنت)، بلوک setup (فایلهایی که باید در دایرکتوری کاری موقت قرار گیرند) و بلوک verifier را تعریف میکند. بلوک اعتبارسنج نام تابعی را در رجیستری اعتبارسنجهای بستر مشخص کرده و آرگومانهایش را به آن میرساند.
سه نوع اعتبارسنج بخش اعظم وظایف کاربردی را پوشش میدهند:
اولین مورد file_equals است. پس از اجرای ایجنت، فایل مشخصی را با محتوای مورد انتظار مقایسه میکند. این اعتبارسنج مخصوص وظایف از نوع «این باگ را دقیقاً به همین شیوه برطرف کن» است.
دومین مورد regex_match است. محتوای فایل مشخصشده با یک عبارت منظم (Regex) سنجیده میشود. این حالت وظایف از نوع «تابع باید وجود داشته باشد و مقدار X را برگرداند» را پوشش میدهد که در آنها راهحلهای متعددی قابلقبول هستند.
سومین مورد shell_exit_zero است. بستر آزمایشی دستوری را در شل (از طریق ساندباکسِ درس 26) اجرا میکند و تنها در صورتی وظیفه را قبول در نظر میگیرد که کد خروجی دستور صفر باشد. این اعتبارسنج به درد وظایف از نوع «تستها باید پاس شوند» میخورد.
بستر آزمایشی هر وظیفه را k بار اجرا میکند. فرمول pass@k برابر است با 1 - (1 - p)^k که در آن p نرخ قبولی تجربی است؛ بستر برای بررسی واریانس، تعداد دفعات خام را هم گزارش میدهد. تاخیر، زمان واقعی اجرای هر نمونه است. هزینه هم همان مقداری است که خود ایجنت گزارش میدهد (تعداد توکن، دلار یا هر دو)؛ بستر آزمایشی این هزینه را روی نمونهها جمع میزند و اعداد تجمیعی و مجزای هر وظیفه را ارائه میکند.
معماری سیستم
کاندید یک موجودیت قابلفراخوانی است: Callable[[FixtureTask, str], SampleResult]. بستر آزمایشی دایرکتوری کاری موقت را با tempfile.mkdtemp() میسازد و مسیرش را در قالب یک رشته ساده تحویل میدهد. بستر کاری ندارد که کاندید پشت صحنه چطور کار میکند. این کاندید میتواند یک اعمالکنندهی قطعیِ تکه کدها (Patch) باشد (که برای خودآزمایی بستر مفید است)، یک ایجنت واقعیِ مبتنی بر مدل زبانی، یا یک فازر (Fuzzer). آنچه بین آنها برقرار است صرفاً قرارداد SampleResult است.
پیادهسازی از صفر
فایل main.py شامل این بخشهاست:
- دیتاکلاس
FixtureTask. - دیتاکلاس
SampleResult: شامل success_self_reported، latency_ms، cost_units و edits. - دیتاکلاسهای
TaskReportوEvalReportبا متدto_dict(). - کلاس
VerifierRegistryبرای نگاشت نام اعتبارسنج به تابع مربوطه. اعتبارسنجهای پیشفرض: file_equals، regex_match و shell_exit_zero. - کلاس
EvalHarnessکه یک دایرکتوری از وظایف را در برابر یک کاندید اجرا میکند و خروجی EvalReport میدهد. - پنج وظیفهی آزمایشی ثابت که در پوشهی
tasks/قرار دارند:- خطای مرتبهی یک (Off-by-one) در
fizzbuzz - نبود return در تابع
factorial - غلط املایی در پیام خطا
- بدنهی خالی یک تابع
- خطای مرتبهی یک در پیمایش لیست پیوندی
- خطای مرتبهی یک (Off-by-one) در
- یک کاندید مرجعِ قطعی (
apply_known_fixes) که بستر آزمایشی برای اثبات عملکرد و کسب pass@1 معادل 1.0 از آن استفاده میکند. - دمویی که گزارش نهایی EvalReport را به شکل JSON چاپ کرده و با کد صفر خارج میشود.
وظایف ثابت بهشکل فایلهای JSON درون tasks/ و فایلهای منبع متناظر درون tasks/<id>/buggy/ و tasks/<id>/expected/ سازماندهی شدهاند. بستر آزمایشی پوشهی buggy را در یک دایرکتوری کاری موقت کپی میکند، آن را تحویل کاندید میدهد و نتیجه را در برابر expected اعتبارسنجی میکند.
چرا pass@k به جای اکتفا به pass@1؟
ایجنتهای واقعیِ مبتنی بر مدلهای زبانی رفتاری تصادفی دارند. کسب pass@1 معادل 0.6 در نگاه اول شکست به نظر میرسد. اما pass@5 معادل 0.95 نشان میدهد که ایجنت در اکثر مواقع به پاسخ درست میرسد، فقط در تلاشهای اولیه انتخاب اشتباهی میکند. راهحل این مشکل استفاده از روشهای نمونهگیر (Sampler) و رتبهبندی (Rank) است، نه لزوماً آموزش بیشتر مدل. معیار pass@k این موضوع را شفاف میکند.
شاخص pass@k همیشه در کنار pass@1 گزارش میشود؛ زیرا pass@k میتواند روی یک نقص واقعی سرپوش بگذارد: اگر مدلی در هر بیست بار تلاش فقط یک بار جواب درست بدهد، با یک ایجنت کاربردی طرف نیستید. بستر آزمایشی هر دو زاویه را نشان میدهد.
هماهنگی با سایر بخشهای مسیر A
در درس 25 زنجیرهی گیتهای کنترلی (Gates) را ساختیم. درس 26 ساندباکس (Sandbox) را اضافه کرد. بستر آزمایشی برای هر اعتبارسنج shell_exit_zero از این ساندباکس استفاده میکند. در درس 28 هر بار اجرای بستر را درون یک ردگیری محاسبات (Trace) مربوط به OTel قرار میدهیم. در نهایت درس 29 دموی سرتاسری (End-to-end) را در برابر یکی از وظایف ثابت موجود اجرا میکند و pass@1 = 1.0 را برای کاندید مرجع اعتبارسنجی خواهد کرد.
اجرای پروژه
cd phases/19-capstone-projects/27-eval-harness-fixture-tasks
python3 code/main.py
python3 -m pytest code/tests/ -v
این دمو گزارش نهایی EvalReport را بهصورت JSON چاپ میکند که شامل pass@1، pass@5، میانگین تاخیر و تفکیک عملکرد در هر وظیفه است. کد خروجی برنامه صفر خواهد بود. تستها توابع اعتبارسنج، محاسبات ریاضی pass@k، بارگذاری وظایف ثابت و اجرای سرتاسری بستر ارزیابی در برابر کاندید مرجع را پوشش میدهند.
