اگر امروز از هوش مصنوعی برای بازنویسی توابع پیچیده استفاده میکنید، احتمالاً سطح بازبینی کد خود را دوبرابر کردهاید بدون آنکه متوجه شوید چه رفتارهایی را از دست دادهاید. در ۱۳ اوت ۲۰۲۶، یک توسعهدهنده با تکیه بر بازنویسیهای مدلهای زبانی، ریسک ایجاد خطاهای معنایی پنهانی را میپذیرد که در تستهای معمول دیده نمیشوند. این چالش دقیقاً با آنچه در تحلیل ما دربارهی خطاهای پنهان و «بلفهای» برنامهنویسی هوش مصنوعی بررسی کردیم، همسو است؛ جایی که کد در ظاهر درست به نظر میرسد اما در عمل شکست میخورد. وقتی یک بازسازی کوچک (Refactor) روی یک مسیر حساس و پرتردد (Hot Path) اعمال میشود، نگرانی اصلی نیست که کد جدید چقدر «تمیز» است، بلکه این است که آیا رفتار تابع تغییر کرده است یا خیر.
بسیاری از تیمهای مهندسی برای اطمینان از تغییر نکردن رفتار کد در زمان بازسازی، به مجموعهای از تستهای موجود و بازبینی انسانی تفاوتها (Diff Reviews) تکیه میکنند. اما این تستها معمولاً فقط «مسیر خوشبینانه» یا همان حالتهای عادی را پوشش میدهند. بازبینهای انسانی نیز بهجای بررسی سیستماتیک ورودیها، صرفاً تفاوتهای متنی را میخوانند و بهراحتی اثر تغییرات کوچک بر ورودیهای غیرمعمول را نادیده میگیرند، زیرا آنها در حال خواندن یک Diff هستند، نه کاوش سیستماتیک در ورودیها. این شکاف باعث میشود توسعهدهندگان وسوسه شوند تا اجازه دهند یک مدل زبانی بزرگ (LLM) — مثل کتابخانهداری که میلیاردها صفحه را خوانده و حالا با همان لحن کتابها جواب میدهد — کد را بهسادگی «بهتر» بازنویسی کند.
به نقل از راهنمای منتشرشده در dev.to، این رویکرد اساساً معیوب است؛ زیرا تفاوتهای متنی فقط نشان میدهند چه چیزی تغییر کرده است، نه اینکه چه چیزی دیگر کار نمیکند. اگر مدل تابع را بازنویسی کند، ریسک معنایی در تمام ورودیهایی پخش میشود که مدل هنگام بازنویسی در نظر داشته است. برای تأیید واقعی یک بازسازی، شما به مجموعهای از ورودیها نیاز دارید که احتمالاً تفاوتهای رفتاری را آشکار کنند؛ این در واقع یک مسئله «تولید تست» است، نه یک مسئله «تولید کد».
مکانیسم اوراکل تفاضلی
همانطور که در تحلیلهای قبلی ما دربارهی امنیت مدلهای بازمتن اشاره کردیم، اعتماد کورکورانه به خروجیهای سنتز شده بدون لایهی اعتبارسنجی، خطرناک است. برای جلوگیری از ادغامهای کورکورانه، میتوان از سازوکارهای ممیزی رفتاری برای کنترل خروجیهای مدلهای برنامهنویسی بهره برد تا لایهای از امنیت افزوده شود. در اینجا راهکار پیشنهادی، گردشکار «اوراکل تفاضلی» (Differential Oracle) است. بهجای درخواست تغییر کد از هوش مصنوعی، از آن بخواهید لیست ورودیهای کاندید شما را گسترش دهد. سپس هر دو نسخه قدیمی (Legacy) و جدید (Refactored) یک تابع خالص را بهصورت موازی با این ورودیها اجرا کنید. این روش برای توابعی که به اندازه کافی خالص هستند تا با مقادیر ساده فراخوانی شوند، ایدهآل است، مانند:
- نرمالسازهای رشته (String Normalizers)
- کمککنندههای تجزیه تاریخ (Date Parse Helpers)
- سازندههای URL
- انتقالهای کوچک در ماشینهای وضعیت
اگر هر دو نسخه خروجی یکسانی تولید کنند، بازسازی سازگار است. در غیر این صورت، ورودی برای بازبینی انسانی علامتگذاری میشود. در این ساختار، کد قدیمی تنها منبع حقیقت (Ground Truth) باقی میماند و نقش هوش مصنوعی به «کنجکاوی» درباره ورودیهایی که شما ممکن است فراموش کرده باشید، تقلیل مییابد. این روش ثابت نمیکند بازسازی کاملاً درست است، اما ثابت میکند رفتار قدیمی در مجموعه تستها حفظ شده است و تغییرات خاموشی را که تیمها بیشترین ترس را از آنها دارند (در ورودیهای غیرمعمول)، شکار میکند.
پیادهسازی یک هارنس حداقلی
برای اجرای این متد، نویسنده یک هارنس (Harness) ساده پایتونی را پیشنهاد میکند. این ابزار بیشتر توصیفگر شکل گردشکار است تا یک کتابخانه نهایی و تستشده، بنابراین توسعهدهندگان باید بخشهای مربوط به سریالسازی (Serialization) و مدیریت استثناها را با کد خود تطبیق دهند. ساختار این هارنس شامل موارد زیر است:
- InputSpec: یک دیتاکلاس شامل نام، یک پارسر برای تبدیل متن به مقادیر تایپشده و مجموعهای از ورودیهای اولیه (Seed Inputs).
- نرمالسازی: یک ابزار کمکی برای اطمینان از اینکه نوسانات کوچک عددی در اعداد اعشاری (با استفاده از رشتههای با دقت ثابت مانند
f'{value:.15g}') باعث پر شدن گزارش با مثبتهای کاذب (False Positives) نشود. این هارنس ازreprبهعنوان یک سریالساز ارزان برای اکثر مقادیر استفاده میکند. - منطق مقایسه: حلقهای که استثناها را در هر دو نسخه میگیرد. عدم تطابق زمانی ثبت میشود که:
- یک نسخه خطا دهد در حالی که نسخه دیگر مقداری را برگرداند.
- هر دو خطا دهند، اما کلاس شکست (نام نوع استثنا) تغییر کرده باشد.
- هر دو مقادیری را برگردانند که پس از نرمالسازی متفاوت باشند.
پرامپتنویسی برای حجم و غرابت
هنگام استفاده از مدلی مانند MonkeyCode (که دسترسی رایگان به مدل و گزینه سرور رایگان ارائه میدهد)، پرامپت باید صراحتاً مدل را از نوشتن کد منع کند. هدف، تولید لیستی حجیم و «نامرتب» از موارد لبهای (Edge Cases) است که در آن خروجی مدل نیازی به معتبر بودن مطلق ندارد. برای یک نرمالساز نام خانوادگی، پرامپت باید این باشد: «شما در حال تولید ورودیهای تست برای تابعی هستید که نام خانوادگی را برای آدرس صورتحساب نرمال میکند. کد ننویسید. هر ورودی را در یک خط برگردانید.»
در این پرامپت باید بهطور مشخص موارد زیر درخواست شود:
- کاراکترهای لاتین با اکسان و نویسههای غیرلاتین.
- رشتههای حاوی آپاستروف، خط تیره، تب و فضاهای خالی در ابتدا و انتها.
- طولهای بسیار زیاد (بیش از ۲۰۰ کاراکتر).
- رشتههای متشکل از علائم نگارشی یا رشتههای عددی.
- ورودیهایی که شبیه به JSON، HTML یا SQL هستند تا احتمال تزریق (Injection) یا خطاهای تجزیه تست شود.
توسعهدهندگان باید نتایج را جمعآوری، فضاهای خالی را حذف، تکراریها را پاک کرده و خطوطی که شبیه به متن (Prose) هستند را دور بریزند. هدف، حجم و «عجیب بودن» ورودیهاست، نه جامعیت کامل. هر خطی که پارسر نتواند بپذیرد، بهسادگی نادیده گرفته میشود تا یک مرز امن بین مدل و کد ایجاد شود.
مدیریت نتایج
عدم تطابقها باید بهعنوان «سرنخ» دیده شوند، نه گزارش باگ. تفاوت در خروجی ممکن است یک رگرسیون واقعی باشد یا یک تسهیل عمدی در قرارداد ورودی. برای مثال، اگر کد قدیمی استثنا میداد و کد جدید مقداری را برمیگرداند، این میتواند یک بهبود عمدی یا یک شکست تصادفی باشد.
کلید موفقیت در کوچک نگه داشتن لیست عدم تطابقهاست — شروع با چند ده بذر اولیه و چند صد گونه تولید شده — تا بازبینی انسانی همچنان ممکن باشد. گزارشی با صدها تفاوت نویزی، بسیار کمفایدهتر از ۲۰ جفت ورودی/خروجی است که بهراحتی قابل تأیید باشند.
محدودیتها و ملاحظات
البته این گردشکار یک راهکار جادویی نیست. این متد فقط ثابت میکند رفتار قدیمی حفظ شده است؛ نمیتواند باگهای موجود در پیادهسازی اصلی را پیدا کند و در برابر هر تغییر عمدی در رفتار مقاومت میکند، مگر اینکه اوراکل بهروزرسانی شود. همچنین، این روش نیازمند توابع «خالص» است. هر تابعی که به تصادف، زمان سیستم (Wall-clock time)، فایلسیستم یا شبکه وابسته باشد، نیازمند پوششهایی (Wrappers) است که ممکن است تفاوتها را پنهان کنند.
مدلهای رایگان همچنین غیرقطعی (Non-deterministic) هستند و ممکن است متون نامرتبط تولید کنند، مثالهای پرامپت را تکرار کنند یا دادههای آموزشی را تقلید کنند. بنابراین، فیلتر کردن سختگیرانه و حذف تکراریها الزامی است. نویسنده هشدار میدهد که سرورهای رایگان جایگزین یک محیط اجرای کنترلشده (Controlled Runner) نیستند و اطلاعات حساس یا ورودیهای محرمانه هرگز نباید بدون بررسی اینکه چه چیزی قابل ارسال است، به نقاط انتهایی مدلهای عمومی فرستاده شوند.
این تغییر رویکرد، هوش مصنوعی را از یک «همنویس» به یک «تستکننده استرس» تبدیل میکند. با بهرهگیری از توانایی مدل در توهم (Hallucination) — یعنی وقتی مدل با اطمینان چیزی میگوید که اصلاً وجود ندارد، شبیه دوستی که خاطرهای را اشتباه تعریف میکند — برای تولید ورودیهای عجیب، توسعهدهندگان میتوانند بدهیهای فنی را بدون اضطرابِ شکستهای خاموش پاک کنند. این رویکرد در واقع نوعی پروتکل بازپخش برای تضمین پایداری است که بهجای تکیه بر بنچمارکهای ایستا، بر تکرار رفتار در دنیای واقعی متمرکز است.
چه زمانی این روش مناسب نیست؟
اگر بازسازی شما بهطور عمدی قراردادهای عمومی (Public Contract) را تغییر میدهد یا دارای اثرات جانبی (Side Effects) شدید است، این روش را رها کنید. در این موارد، ابزارهای تست مبتنی بر ویژگی مانند Hypothesis یا Fuzzerهای گرامری پوشش بهتری نسبت به حدسهای مدل ارائه میدهند. مدلهای زبانی زمانی بیشترین کاربرد را دارند که حدسهای مبتنی بر دامنه (مانند نامها، تاریخها یا پارامترهای API) ارائه دهند؛ جایی که گرامرهای خالص برای یافتن موارد لبهای دنیای واقعی، نیاز به مدلسازی دستی گستردهای دارند.
گام بعدی شما
- توابع کوچکی را شناسایی کنید که بیشترین تغییرات را در ماههای اخیر داشتهاند و کاندیدهای مناسبی برای تست تفاضلی هستند.
- یک پرامپت سختگیرانه برای تولید ورودیهای «عجیب» بنویسید و مدل را از تولید کد منع کنید.
- یک اسکریپت ساده برای اجرای موازی نسخه قدیمی و جدید کد روی این ورودیها طراحی کنید.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید.




گفتگو