تصور کنید کدی را در محیط عملیاتی مستقر میکنید که در نگاه اول بینقص است، اما در مواجهه با اولین ورودی غیرمنتظره، کل سیستم را متوقف میکند. این دقیقاً همان تلهای است که مدلهای رایگان هوش مصنوعی برای برنامهنویسان میپاشند؛ کدهایی که «محتمل» هستند اما لزوماً «صحیح» نیستند. یک مدل رایگان هوش مصنوعی میتواند در عرض چند ثانیه یک تجزیهکننده (Parser) زبان C++ بنویسد که منطقی به نظر برسد، اما شباهت به درستی، به معنای درست بودن نیست.
طبق یک نمایش عملی در ۲۶ اوت ۲۰۲۶، حتی تمیزترین وصلههای (Patches) کد که توسط هوش مصنوعی نوشته شدهاند، اغلب نقصهای بحرانی را پنهان میکنند که از بررسیهای دستی استاندارد میگریزند. راهکار این مشکل، ایجاد یک «نردبان اعتبارسنجی» است که کد را پیش از رسیدن به مخزن اصلی، از سه دروازه سختگیرانه عبور میدهد. این رویکرد شباهت زیادی به حلقه اعتبارسنجی سهمرحلهای دارد که پیشتر برای اصلاح خطاهای پارسر C++ به کار گرفته شد و توانست باگها را در تکرارهای متوالی حذف کند. این نردبان شامل تستهای واحد، تستهای تفاضلی، ابزارهای شناسایی خطا (Sanitizers) و یک جدول تصمیمگیری صریح است تا تعیین کند چه زمانی یک وصله برای ادغام ایمن است.
بسیاری از توسعهدهندگان برای تأیید پیشنهادات هوش مصنوعی تنها به چند تست واحد ساده تکیه میکنند. این رویکرد توهمی خطرناک از مهارت مدل ایجاد میکند؛ زیرا تستهای واحد فقط انتظاراتی را بررسی میکنند که برنامهنویس از قبل به آنها فکر کرده است. همانطور که در بحثهای گذشته ما دربارهی امنیت مدلهای بازمتن اشاره کردیم، خطر اصلی در نقاطی است که انسان فراموش کرده تصور کند؛ مواردی مثل رشتههای خالی، فاصلههای ابتدایی یا سرریز اعداد صحیح (Integer Overflow). این شکاف دقیقاً همان جایی است که باگهای تولید شده توسط عاملهای هوشمند (Agents) در آن جای میگیرند.
برای حذف حدس و گمان، این فرآیند مکانیکی روی پلتفرم MonkeyCode — سرویسی که محیطهای رایگان برای کامپایل و تست فراهم میکند تا سختافزار محلی اشغال نشود — آزمایش شد. هدف، پیادهسازی تابعی بود که دقیقاً مشابه atoi در کتابخانه استاندارد C عمل کند.
وصلهای که «بیش از حد تمیز» بود
وظیفه ساده بود: ساخت تابعی برای تبدیل رشته به عدد که رفتاری مشابه atoi کتابخانه استاندارد C داشته باشد. مدل رایگان در اولین تلاش، پیادهسازی را در یک مرحله ارائه داد. کد علامت منفی ابتدایی را مدیریت میکرد، روی ارقام پیمایش میکرد و نتیجه را با استفاده از فرمول result = result * 10 + (*s - '0') میساخت. در نگاه اول، منطق کد خوانا و ساختار آن آشنا بود. حتی برای ورودیهای «42» و «7-» مقدار درست را برمیگرداند.
پله اول: کفِ تستهای واحد
دروازه اول، تستهای واحد (Unit Tests) — شبیه به چکلیستهای سادهای که فقط موارد بدیهی را بررسی میکنند — است. در مورد مورد آزمایش شده، پیادهسازی my_atoi مدل برای ورودیهایی مانند «42»، «7-»، «0» و «12345» پاس شد. هر چهار ادعا (Assertion) تأیید شدند. اگر بررسی در اینجا متوقف میشد، وصله ادغام میشد، اما این یک اشتباه است.
تستهای واحد کفِ کیفیت هستند، نه سقف آن. در یک تجزیهکننده (Parser)، مواردی که معمولاً فراموش میشوند عبارتند از: فاصلههای خالی (Whitespace)، علامت مثبت صریح، رشتههای خالی و سرریز اعداد صحیح. یک مجموعه تست کمی سختگیرانهتر بلافاصله دو نقص را افشا کرد:
- فاصلههای ابتدایی: پیادهسازی مدل هرگز فاصلههای ابتدایی را نادیده نمیگرفت، بنابراین ورودی « 42» مقدار 0 را تولید میکرد.
- علامت مثبت صریح: علامت مثبت نادیده گرفته میشد، بنابراین ورودی «7+» مقدار 0 را تولید میکرد.
- رشتههای خالی: رفتار برای یک رشته خالی باید در برابر قرارداد (Contract) تابع تأیید میشد.
اینها لبههای عجیب (Edge Cases) نیستند، بلکه الزامات اصلی قرارداد استاندارد C هستند. مدل رایگان صرفاً قرارداد کامل را پیادهسازی نکرده بود. در حالی که تستهای واحد این شکافهای آشکار را گرفتند، اما نتوانستند باگ سرریز را شناسایی کنند، زیرا سرریز تنها با ورودیهای بسیار بزرگی ظاهر میشود که هیچ انسانی به صورت دستی نمینویسد.
پله دوم: تست تفاضلی
از آنجایی که انسانها نمیتوانند تمام ورودیهای ممکن را به صورت دستی بنویسند، دروازه دوم از تست تفاضلی (Differential Testing) استفاده میکند. این روش شامل دادن یک ورودی یکسان به پیادهسازی شما و یک مرجع مورد اعتماد، و سپس مقایسه خروجیها است. برای atoi مرجع، تابع کتابخانه استاندارد با همان نام است.
- سازوکار: سیستم با استفاده از یک بذر (Seed) ثابت (مثلاً ۲۰۲۶۰۸۲۶) رشتههای تصادفی تولید میکند تا نتایج تکرارپذیر باشند.
- محدوده ورودی: رشتههایی با طولهای متغیر (از 0 تا 12 کاراکتر) با توزیع کاراکترهای بین ASCII 32 تا 126 تولید میشوند.
- مقیاس: این مقایسه مکانیکی است و اجازه میدهد توسعهدهنده ۱۰۰,۰۰۰ یا میلیونها ورودی را بدون تلاش دستی بررسی کند.
- شناسایی شکست: هرگونه عدم تطابق بین نتیجه واقعی (
actual) مدل و نتیجه مورد انتظار (expected) کتابخانه، علامتگذاری میشود.
این مقایسه مکانیکی سریعاً نقصی را یافت که تستهای واحد ندیده بودند: سرریز اعداد صحیح علامتدار. وقتی رشتهای طولانی از ارقام که باعث سرریز یک int میشد وارد میشد، کتابخانه استاندارد نتیجه را به یک مقدار تعریفشده در پیادهسازی محدود (Clamp) میکرد. اما کد هوش مصنوعی از طریق سرریز اعداد صحیح علامتدار دچار چرخش (Wrap around) میشد که در C++ یک «رفتار تعریفنشده» (Undefined Behavior یا UB) است. تست تفاضلی یک UB نامرئی را به یک عدم تطابق آشکار تبدیل کرد.
پله سوم: شناسایهها و جداول تصمیم
در نهایت، ابزارهایی مثل UndefinedBehaviorSanitizer (UBSan) برای تبیین علت شکست تست تفاضلی استفاده میشوند. با کامپایل کد با فلگهای -fsanitize=undefined و -fno-sanitize-recover=all توسعهدهنده میتواند دقیقاً خطی را که خطا در آن رخ میدهد، شناسایی کند. در این مورد، شناسایه (Sanitizer) سرریز اعداد صحیح علامتدار را در خط result = result * 10 + (*s - '0') گزارش کرد. علت ریشهای تأیید شد: وصله فاقد بررسی سرریز بود.
برای حذف سلیقه و ذهنی بودن از فرآیند ادغام، این چارچوب از یک جدول تصمیمگیری سختگیرانه استفاده میکند:
| تست واحد | تست تفاضلی | شناسایهها | تصمیم |
|---|---|---|---|
| پاس | پاس | پاس | ادغام و نظارت |
| پاس | شکست | - | رد و درخواست اصلاح |
| پاس | پاس | شکست | رد و ابتدا اصلاح UB |
| شکست | - | - | رد و درخواست اصلاح |
| شکست | شکست | - | رد و درخواست بازنویسی کامل |
این رویکرد محافظهکارانه تضمین میکند که هرگونه شکست در هر یک از دروازهها، وصله را به همراه تست شکستخورده به عنوان بازخورد به مدل بازگرداند. در عمل، معمولاً دو یا سه تکرار (Iteration) منجر به تولید وصلهای میشود که هر سه دروازه را پاس کند.
محیط اجرا و محدودیتها
تمام این زنجیره روی گزینه سرور رایگان MonkeyCode اجرا شد. مدل رایگان وصله را تولید کرد و سرور رایگان عملیات کامپایل، تست تفاضلی و اجرای شناسایه را انجام داد. این گردش کار مشابه یک ترمینال محلی است: کلون کردن مخزن، نوشتن فایلهای تست، اجرای دستورات و خواندن خروجی. سرور به یک محیط اعتبارسنجی تکرارپذیر تبدیل میشود که در آن یک وصله از طریق لاگها و نتایج مقایسهشده در تکرارهای مختلف، اجازه ادغام میگیرد.
البته این نردبان یک اثبات کامل از صحت (Proof of Correctness) نیست و محدودیتهای واقعی دارد:
- وابستگی به مرجع: تست تفاضلی نیازمند یک مرجع مورد اعتماد است. اگر در حال ساخت یک الگوریتم کاملاً جدید هستید، ممکن است چنین مرجعی نداشته باشید.
- دامنه شناسایهها: شناسایهها فقط رفتارهای تعریفنشده خاصی را که برای شناسایی آنها طراحی شدهاند میگیرند؛ آنها خطاهای منطقی را که از نظر فنی «تعریفشده» هستند، نمیگیرند.
- تصادفی بودن: تولید ورودی تصادفی ممکن است برخی شاخههای نادر کد را نادیده بگیرد. اگرچه بذر ثابت باعث تکرارپذیری میشود، اما به این معنی است که هر بار همان مجموعه داده اجرا میشود.
- محافظهکاری: جدول تصمیم ممکن است وصلههایی را که در واقع درست هستند اما در یک تست ضعیف شکست میخورند، رد کند. هنگام برخورد با کدهای تولید شده توسط مدل، این یک ویژگی است، نه یک باگ.
چه زمانی نردبان را نادیده بگیریم؟
از این نردبان برای اسکریپتهای یکبار مصرف، مهاجرتهای موقت یا نمونههای اولیهای (Prototypes) که هفته آینده حذف میشوند، استفاده نکنید. هزینه راهاندازی این سیستم واقعی است و بازده آن تنها زمانی ظاهر میشود که یک وصله قرار باشد برای ماهها در کدبیس باقی بماند. اگر وصله بدیهی است و حالت شکست آن بیضرر است، یک بررسی دستی سریع کافی است. اما برای هر چیزی که با ورودی کاربر، تجزیه (Parsing) یا مرزهای عددی سر و کار دارد، این نردبان ارزش تلاش را دارد.
این متدولوژی نقش توسعهدهنده را از «بازبین کد» به «معمار تست» تغییر میدهد. به جای خیره شدن به خطوط کد برای یافتن باگ، شما ماشینی میسازید که باگ را بهطور خودکار رد میکند. این کار هوش مصنوعی را از منبعی برای بدهی فنی (Technical Debt) احتمالی به ابزاری برای تکرار سریع و قابل تأیید تبدیل میکند.
برای یک توسعهدهنده حرفهای، این بدان معناست که هزینه استفاده از هوش مصنوعی دیگر ریسک کرش کردن سیستم در محیط عملیاتی نیست، بلکه هزینه اولیه راهاندازی محیط اعتبارسنجی است. نتیجه، کدبیسی است که در آن وصلههای تولید شده توسط هوش مصنوعی به جای «ظاهر محتمل»، از طریق «اثبات تجربی» راه خود را به شاخه اصلی (Master Branch) باز میکنند. برای پیادهسازی این روش از امروز، با شناسایی یک تابع مرجع مورد اعتماد برای وظیفه خود و نوشتن یک مولد ورودی تصادفی ساده برای مقایسه خروجی هوش مصنوعی با آن مرجع شروع کنید.
گام بعدی شما
- برای هر تابع حساس که توسط هوش مصنوعی تولید میکنید، یک تابع مرجع (Reference) در کتابخانه استاندارد یا کدهای قدیمیتر پیدا کنید.
- یک مولد ورودی تصادفی ساده بنویسید تا خروجی مدل را با مرجع مقایسه کنید.
- از فلگهای
-fsanitizeدر کامپایلر GCC یا Clang برای شناسایی رفتارهای تعریفنشده استفاده کنید.
اما هزینه سختافزاری اجرای این تستها در مقیاس بزرگ میتواند چالشبرانگیز باشد — به تحلیل ما درباره بهینهسازی هزینه استنتاج در مدلهای کوچک مراجعه کنید.




گفتگو