اگر هنوز برای اطمینان از صحت خروجی مدلهای زبانی، عبارتهای خاصی را در متن جستوجو میکنید، در واقع در حال ساخت نرمافزاری هستید که هر لحظه ممکن است فرو بپاشد. این روشِ متکی به «تطابق متنی»، عامل اصلی ایجاد تستهای ناپایدار (Flaky Tests) در سیستمهای هوش مصنوعی است. استدلال مرکزی یک راهنمای فنی که در ۱۵ اوت ۲۰۲۶ در وبسایت dev.to منتشر شد، این بود: «ادعای رفتار مدل را متوقف کنید و در عوض، پارسرهای خروجی خود را بهصورت قطعی (Deterministic) تست کنید.»
این تغییر استراتژی، یک نقطه ضعف رایج در مهندسی هوش مصنوعی را هدف قرار میدهد: خلط مفاهیم «ارزیابی» (Evaluation) و «تست واحد» (Unit Testing). در حالی که ارزیابیها احتمال آماریِ بازگشت یک JSON معتبر را میسنجند، تستهای واحد باید تضمین کنند که کد شما هر رشته متنی احتمالی را که مدل ممکن است تولید کند، بهدرستی مدیریت میکند. این رویکرد با استراتژیهای تست واحد برای قالبهای پرامپت همسو است که از افت کیفیت خاموش در مدلها جلوگیری میکند.
تفکیک ارزیابی از تست واحد
این راهنما برای شفافسازی، دو پرسش را در کنار هم قرار میدهد. پرسش اول این است: «آیا مدل برای این پرامپت، یک JSON معتبر برمیگرداند؟» این پرسشی درباره رفتار یک شخص ثالث روی توزیعی از ورودیهاست. پاسخ به این سوال نیازمند نمونههای زیاد، یک تابع امتیازدهی (Scoring Function) و یک ادعای آماری است. بنابراین، جای این پرسش در یک اجرای ارزیابی با استفاده از مجموعهدادههای طلایی (Golden Datasets) است، نه در یک مجموعه تست واحد.
پرسش دوم این است: «با داشتن این رشته متنی دقیق، آیا پارسر من مقدار درست یا خطای صحیح را تولید میکند؟» این یک تابع خالص روی ورودی ثابت است. این تست در یک میلیثانیه اجرا میشود و هرگز ناپایدار نیست. تمام باگهای پارسرهایی که تا به حال به محیط عملیاتی رسیدهاند، در این لایه نهفتهاند و این تنها بخشی است که واقعاً به مجموعه تست واحد تعلق دارد. در واقع، جداسازی اعتبارسنجها از فرآیند استنتاج میتواند بهطور چشمگیری هزینهها و تأخیر در تستهای LLM را کاهش دهد.
همانطور که در تحلیل قبلی ما دربارهی کوانتش مدلهای زبانی و افت کیفیت در مدلهای بالای ۶.۷ میلیارد پارامتر اشاره کردیم، واضح است که با تغییر مدلها — چه از طریق کوانتش و چه از طریق بهروزرسانیهای نسخهای — شکل خروجیهای آنها تغییر میکند. اگر مدیریت JSON شما در دلِ فراخوانیهای HTTP باشد، نمیتوانید این شکستها را ایزوله کنید. شما نمیتوانید پارسر را بدون یک لایه انتقال (Transport) روی یک نمونه ثابت (Fixture) اجرا کنید، و این منجر به چرخهای میشود که در آن باگهای پارسر را به اشتباه به گردن مدل میاندازید.
استراتژی مجموعهی نمونهها (Fixture Corpus)
برای ساخت یک پارسر قابلاعتماد، به یک مجموعه نمونه (Fixture Corpus) نیاز دارید؛ یعنی مجموعهای از رشتههای متنی واقعی که از پاسخهای مدل ضبط شدهاند. این مجموعه، تحویلدهنده اصلی (Primary Deliverable) است و هر چیز دیگری صرفاً داربست است. این نمونهها باید به جای رشتههای متنی داخلی (Inline Literals)، به صورت فایلهایی در یک پوشه به نام fixtures نگهداری شوند. این کار برای جلوگیری از این است که توسعهدهندگان متنها را تا زمان پاس شدن تست ویرایش کنند، زیرا این کار هدف اصلی تست را معکوس میکند.
طبق گزارش dev.to، یک مجموعه جامع باید شامل موارد زیر باشد:
- مورد ایدهآل (The Clean Case): دقیقاً همان شیء درخواستی و هیچ چیز دیگر.
- خروجی محصور (Fenced Output): JSONهایی که در بلوکهای کد (triple-backticks) قرار دارند، چه با تگ زبان
jsonو چه بدون آن. این مورد حتی زمانی که در پرامپت صراحتاً ممنوع شده باشد، بسیار رایج است. - مقدمه و موخره (Preamble and Postscript): پاسخهایی که شامل جملات پرکننده محاورهای هستند، مانند «حتماً! این هم JSON درخواستی شما:» قبل از شیء، یا یک توضیح کمکی بعد از آن.
- پاسخهای ناقص (Truncated Responses): JSONهایی که تا نقطهای معتبر هستند که در آن
finish_reasonمقدارlengthرا برگردانده است. پارسر باید این حالت را از خروجیهای بدشکل (Malformed) تشخیص دهد، زیرا راه حل در اینجا افزایش حد توکنهاست، نه اصلاح پرامپت. - خطاهای تایپی (Typographical Errors): استفاده از گیومههای منحنی (Smart Quotes) به جای گیومههای مستقیم، که معمولاً در خروجیهای غیرانگلیسی ظاهر میشوند.
- عدم تطابق طرحواره (Schema Mismatches): ارسال عدد به جای رشته، وجود مقدار null در جایی که مقدار مورد انتظار بود، وجود یک فیلد اضافی، مقداری برای Enum که در لیست شما نیست، یا یک شیء تودرتو که به صورت تخت (Flattened) درآمده است.
- توقفهای سیستمی (System Stops): پاسخهای مربوط به امتناع مدل (Refusals) یا توقفهای
content_filter. هر دوی اینها پاسخهایی از نظر ساختاری معتبر هستند اما هیچ دادهای ندارند. - حالات تهی (Null/Empty States): رشتههای خالی، یا پاسخی که در آن محتوا null است زیرا مدل به جای متن، فراخوانیهای ابزار (Tool Calls) را برگردانده است.
پیادهسازی امضاهای جامع (Total Signatures)
برای موفقیت این تستها، پارسر باید «امضای جامع» داشته باشد؛ به این معنی که هر ورودی باید به یک مقدار نگاشت شود، حتی ورودیهای بد. پارسرهایی که به دلایل مختلف Exception پرتاب میکنند، هر تست را مجبور میکنند تا روی متن پیام خطا تطبیق دهد که این روشی شکننده است.
این راهنما پیشنهاد میکند از یک تایپ اتحادی (Union Type) برای نتایج استفاده کنید. برای مثال، در یک طرحواره Zod برای فاکتور (شامل invoiceNumber و totalCents و currency)، پارسر باید یک ParseResult برگرداند. این اتحادیه شامل یک حالت ok: true برای موفقیت و یک حالت ok: false به همراه یک reason مشخص (مانند empty ،no_json ،truncated ،invalid_json یا schema) و یک رشته detail است. این رویکرد مشابه پیادهسازی در پلتفرم SpaceAI360 است که با ترکیب Zod و لایههای پاکسازی از توقف APIها جلوگیری میکند.
این رویکرد، یک کرش (Crash) را به یک متریک تبدیل میکند. حالا اتحادیه دلیل شکست به رابط واقعی تبدیل میشود که هر مقدار آن به یک پاسخ عملیاتی متفاوت نگاشت میگردد:
- truncated: افزایش بودجه توکنها.
- no_json: نیاز به اصلاح پرامپت یا فرمت پاسخ.
- schema: مدل در حال پاسخ به سوالی است که کمی با سوال اصلی متفاوت است.
- empty: معمولاً نشاندهنده امتناع مدل یا یک فراخوانی ابزار مدیریتنشده است.
ارسال یک رشته به عنوان دلیل شکست در قالب یک برچسب متریک (Metric Label)، داشبوردی ایجاد میکند که دقیقاً به شما میگوید چه اصلاحاتی باید انجام دهید.
تستهای جدولمحور و تضمین پایداری
با استفاده از فریمورکهایی مثل Vitest، توسعهدهندگان میتوانند تستهای جدولمحور (Table-driven tests) را روی مجموعه نمونهها اجرا کنند. این کار اجازه میدهد موارد پذیرفتهشده و ردشده بهسرعت تایید شوند. برای مثال، یک تست میتواند روی لیستی از نمونهها (مانند clean و fenced-json و with-preamble) پیمایش کند تا تایید کند که پذیرفته شدهاند، و لیست دیگری (مانند smart-quotes یا total-as-string) را بررسی کند تا تایید کند که با دلیل صحیح رد شدهاند.
یک تست حیاتی، «بررسی جامعیت» (Totality Check) است که تضمین میکند پارسر تحت هیچ شرایطی و فارغ از ورودی، Exception پرتاب نمیکند. این ارزانترین راه برای تقریب زدن ایمنی است. از آنجا که مجموعه ورودیهای پیشبینینشده نامعلوم است، اولویت اول برای پایداری در محیط عملیاتی این است که سیستم بهجای کرش کردن کل مسیر درخواست، بهصورت متین (Gracefully) شکست بخورد. هر بار که یک نمونه جدید به مجموعه اضافه میشود، این سوئیت تست قویتر میشود.
رشد بر اساس دادههای عملیاتی
یک مجموعه نمونه استاتیک کافی نیست زیرا توسعهدهندگان نمیتوانند هر مورد خاص و عجیب (Edge Case) را تصور کنند. خط لوله پیشنهادی این است که شکستهای محیط عملیاتی مستقیماً به مجموعه تستها تزریق شوند. هر نتیجه ok: false در محیط Production باید محتوای خام را به همراه دلیل شکست و مدل ID در حافظه ذخیره کند.
برای اجرای ایمن این فرآیند:
- محتوای خام هر شکست را (پس از حذف دادههای حساس پرامپت) لاگ کنید.
- طول لاگها را محدود کنید تا پاسخهای بسیار حجیم باعث پر شدن حافظه نشوند.
- هفتهای یکبار، یک نمونه از هر دلیل شکست متمایز را به پوشه
test/fixtures/منتقل کنید. - نام فایلها را بر اساس «دلیل شکست» بگذارید، نه بر اساس «اتفاق».
این کار هر غافلگیری در محیط عملیاتی را به یک تست رگرسیون دائمی تبدیل میکند. اگر پارسر باید آن را مدیریت میکرد، پارسر را اصلاح کنید؛ اگر شکل خروجی واقعاً غیرقابل مدیریت است، آن نمونه مستند میکند که فراخواننده چه خطایی را میبیند.
ثبت مدل ID و تاریخ در کنار هر نمونه ضروری است. هنگام تعویض مدل، شکل خروجیها میتواند تغییر کند و دانستن اینکه کدام نمونهها قبل از تعویض مدل ضبط شدهاند، به شما میگوید کدام یک را باید مجدداً ضبط کنید به جای اینکه به آنها اعتماد کنید.
این انضباط در استریمینگ (Streaming) نیز کاربرد دارد، جایی که نمونه به جای یک رشته، توالی از رویدادها است. بحرانیترین موارد برای تست، مقادیر JSONی هستند که بین دو تکه (Chunk) تقسیم شدهاند یا استریمهایی که در میانه یک شیء بهطور ناگهانی قطع میشوند. ضبط این موارد یک چالش است، اما تستهای پارسر حاصل از آنها دقیقاً شبیه به رویکرد جدولمحور توصیف شده در بالا است.
برای کسانی که طرحوارههای پیچیده را مدیریت میکنند، این راهنما هشدار میدهد که هرگز نباید روی محتوای دقیق فیلدهای متنی (Prose) حساس بود. اگر طرحواره شامل یک رشته «خلاصه» است، شما فقط باید محدودیتها را تست کنید — مانند غیر تهی بودن، ماندن زیر یک سقف طول مشخص، یا نداشتن عبارت «Sure» در ابتدا — و نه خودِ متن را. تطابق دقیق متنی با هر بهروزرسانی مدل شکست میخورد، حتی اگر خروجی مدل کاملاً درست باشد.
این متدولوژی تمرکز توسعهدهنده را از تلاش برای کنترل ماهیت پیشبینیناپذیر LLMها به ساخت یک دژ مستحکم در لایه استخراج دادهها تغییر میدهد. با تبدیل پارسر به یک تابع خالص روی ورودیهای ثابت، ناپایداریِ ادغام هوش مصنوعی بهطور موثری خنثی میشود.
گام بعدی شما
- تمام تستهای
assertکه روی متن خروجی LLM اجرا میشوند را شناسایی و به تستهای پارسر منتقل کنید. - یک پوشه
fixturesبسازید و پاسخهای عجیب و غریب مدل را در فایلهای مجزا ذخیره کنید. - خروجی پارسر خود را به یک Union Type تبدیل کنید تا هر شکست را با یک برچسب (Label) مشخص کنید.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید.




گفتگو