المستوى: محترف
لو production بتاعك بيكسر في 11% من responses عشان Llama رجّع JSON بـ trailing comma أو enum غلط، انت مش محتاج تغيّر النموذج ولا تعمل 3 retry loops متتالية. Constrained Decoding بيضمنلك JSON يطابق schema بنسبة 100%، بـ overhead 4-12ms على كل token، ومن غير ما تفقد جودة الـ output الدلالية. دي مش حيلة prompt، دي تقييد على مستوى الـ logits نفسها.
Structured Outputs بـ Outlines: ضمان JSON صحيح من Llama 70B بدون retry
المشكلة باختصار
عندك pipeline بيستخرج بيانات منظمة من نصوص عربية: فواتير، CVs، تذاكر دعم. الـ LLM لازم يرجّع JSON يطابق schema محدد. الواقع المُر: حتى Llama 3.1 70B مع system prompt مفصّل و 3-shot examples بيرد JSON صحيح في 87 إلى 93% بس من الحالات. الباقي بيكسر الـ pipeline، أو بيتطلب retry يضاعف الـ latency والتكلفة.
الحل الشائع: retry loop مع validation. ده بيخفّي العَرَض، بس بيخسّرك 250-800ms متوسط لكل request فاشل، وبيستهلك tokens مرتين. الطريقة دي بتفشل لما الفشل بيكون systematic — يعني الـ model مش قادر يحترم schema معين أصلاً، فالـ retry بيرجع نفس الغلط.
المفهوم للمبتدئ: السكة الحديد مقابل الطريق المفتوح
تخيّل مدرّس بيقول للطالب: "اكتب رقم بين 1 و 10". الطالب الموثوق بيكتب "7". الطالب اللي لسه بيتعلم ممكن يكتب "احد عشر" أو "7.5" أو حتى "سبعة". الـ prompting لوحده ده. الكلام نصيحة، مفيش حاجز فعلي يمنع الطالب من الانحراف.
Constrained Decoding سكة حديد. الطالب مفيش قدامه أصلاً إلا أرقام صحيحة من 1 لـ 10. لو حاول يكتب أي حاجة تانية، الكلمة مش متاحة في القاموس اللحظي قدامه. ده مش filter بعد الكتابة، ده تقييد على مستوى المخرجات الممكنة قبل ما تتولد. النتيجة: مفيش طريقة يطلع منها output غلط.
الشرح العلمي: Logit Masking عبر Finite State Machine
الـ LLM في كل خطوة بيحسب probability distribution على كل tokens في الـ vocabulary (typical 32K إلى 128K token). الـ greedy أو الـ sampling بياخد token بناءً على الاحتمالات. Outlines (Willard & Louf, 2023) بيشتغل بطريقة مختلفة جذرياً:
- بياخد الـ JSON schema (Pydantic class أو JSON Schema مباشرة) ويحوّله لـ regular expression.
- بيبني Finite State Machine من الـ regex. كل state بيمثّل موقع داخل الـ JSON المنتظَر.
- عند توليد كل token، الـ FSM بيحدد الـ tokens المسموحة في الـ vocabulary اللي تحافظ على schema صالح. باقي الـ tokens بيتعمل لها logit = -infinity قبل الـ softmax.
- الـ sampling بيختار من الـ tokens المسموحة فقط. النتيجة الرياضية: كل sequence منتهية صالحة بنسبة 100%.
الـ FSM compilation بيتعمل مرة واحدة لكل schema (بـ caching). الـ overhead الفعلي وقت الـ inference هو lookup للـ state الحالي ومسح للـ vocabulary. على A100 80GB ده بيكلّف 4 إلى 8ms لكل token مع schema متوسطة (20-40 field).