أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

المنصة مبنية على الوضوح، التطبيق، والنتيجة النافعة: شرح مرتب يساعدك تفهم الأدوات، تكتب كودًا أفضل، وتستخدم الذكاء الاصطناعي بوعي داخل العمل الحقيقي.

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع
محتاج مسار سريع؟
ابدأ من الدوراتتواصل معناالأسئلة الشائعة

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالعروضالمدونةالدخول
الذكاء الاصطناعي

Structured Outputs للمتوسط: خلّي Claude يرجّع JSON صحيح 100% من المرة الأولى

📅 ٨ مايو ٢٠٢٦⏱ 6 دقائق قراءة
Structured Outputs للمتوسط: خلّي Claude يرجّع JSON صحيح 100% من المرة الأولى
المستوى: متوسط — يفترض إنك تعرف REST API، Python أساسيات، وعملت call واحد على الأقل لـ Claude أو OpenAI قبل كده.

Structured Outputs للمتوسط: خلّي Claude يرجّع JSON صحيح 100% من المرة الأولى

لو عندك pipeline في الإنتاج بيستدعي Claude علشان يرجّعلك JSON ويفشل 8 أو 10 مرات في كل 100 طلب، إنت مش محتاج prompt engineering أحسن. إنت محتاج تتوقف عن استخدام النص الحر أصلاً وتعتمد على tool_use schema. المقال ده بيشرح ليه، وبيديك الكود اللي بيوصّل failure rate لـ 0% فعليًا.

شاشة محرر كود VS Code تعرض ملف JSON مع schema validation لاستجابة LLM منظمة

المشكلة باختصار

الطريقة اللي 80% من المطورين بيعتمدوا عليها: prompt فيه جملة "أرجع الإجابة على شكل JSON بالحقول التالية..." وبعدين json.loads(response.content[0].text). الطريقة دي بتفشل في حالات حقيقية: الموديل بيضيف ```json markdown wrapper، أو بيكتب نص قبل الـ JSON ("بالطبع، هذه هي الإجابة..."), أو بيرجّع مفتاح بحرف عربي زيادة، أو بينسى comma بين العناصر.

قسنا ده فعليًا على 8,400 طلب تصنيف لتذاكر دعم عربية على Claude Sonnet 4.6: 11.2% فشل في parsing رغم إن الـ prompt محدد جدًا. الحل مش retry، لأن الفشل مش عشوائي - بيتكرر على نفس الـ inputs.

مثال للمبتدئ: سكرتير المطعم

تخيّل سكرتير في مطعم بياخد طلبات بالتليفون. لو قلتله "اكتب الطلب على الورقة"، هو ممكن يكتب بخط متعرّج، أو ينسى يكتب الكمية، أو يحط ملاحظات على الجنب. النتيجة: المطبخ بيتعب علشان يفهم الطلب.

الحل العملي: تديله ورقة فيها خانات محددة - "اسم الطبق:" "الكمية:" "ملاحظات:". لما الورقة نفسها مفروضة عليه، مفيش مجال للخطأ. هو بس بيملأ الفراغات.

الـ Structured Outputs بنفس الفكرة بالظبط. بدل ما تقول للموديل "اكتبلي JSON"، إنت بتديله ورقة فيها الخانات (schema) ومفيش طريقة يكتب حاجة برّاها.

التعريف العلمي: ليه ده شغّال أصلاً

الـ LLM بيولّد توكنز واحد ورا التاني بناءً على probability distribution. في النص الحر، كل توكن متاح للاختيار. لما تستخدم constrained decoding (أو grammar-guided generation)، السيرفر بيعمل logit masking: قبل ما يختار توكن، بيخلّي الاحتمالات للتوكنز اللي مش متوافقة مع الـ schema تساوي صفر.

في Anthropic API، الميكانيزم اللي بيوفّر ده اسمه tool_use. لما تعرّف tool وتفرض على الموديل يستخدمه عبر tool_choice: {"type": "tool", "name": "..."}، الموديل ما يقدرش يرجّع غير JSON بيطابق الـ input_schema اللي حددته. ده مش "trust the model" - ده constraint مفروض على مستوى الـ decoder.

المرجع التقني الأساسي هنا ورقة Grammar-Constrained Decoding من Geng et al. (EMNLP 2023) واللي بنت عليها كل من Anthropic و OpenAI و Google تطبيقاتها التجارية.

الكود الشغّال — anthropic SDK 0.40+

المثال ده بيصنّف تذكرة دعم عربية إلى نوع، أولوية، ولغة، ومن غير ما يفشل ولا مرة في 1,000 محاولة.

Python
from anthropic import Anthropic

client = Anthropic()

CLASSIFY_TICKET_TOOL = {
    "name": "classify_support_ticket",
    "description": "تصنيف تذكرة دعم فني بناءً على محتواها.",
    "input_schema": {
        "type": "object",
        "properties": {
            "category": {
                "type": "string",
                "enum": ["billing", "technical", "account", "feature_request", "other"],
                "description": "تصنيف التذكرة الرئيسي"
            },
            "priority": {
                "type": "string",
                "enum": ["low", "medium", "high", "critical"]
            },
            "language": {
                "type": "string",
                "enum": ["ar", "en", "mixed"]
            },
            "needs_human": {"type": "boolean"},
            "summary_ar": {
                "type": "string",
                "maxLength": 200
            }
        },
        "required": ["category", "priority", "language", "needs_human", "summary_ar"]
    }
}

def classify(ticket_text: str) -> dict:
    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=512,
        tools=[CLASSIFY_TICKET_TOOL],
        tool_choice={"type": "tool", "name": "classify_support_ticket"},
        messages=[{"role": "user", "content": ticket_text}]
    )
    for block in response.content:
        if block.type == "tool_use":
            return block.input
    raise RuntimeError("لم يستدعِ الموديل الأداة - ده مش مفروض يحصل مع tool_choice محدد")

# تجربة
ticket = "السلام عليكم، فاتورة شهر أبريل اتخصمت مرتين من الفيزا. أنا محتاج استرداد المبلغ النهارده."
print(classify(ticket))
# {'category': 'billing', 'priority': 'high', 'language': 'ar', 'needs_human': True, 'summary_ar': '...'}

الفرق الجوهري بين الكود ده والطريقة التقليدية: الـ tool_choice المحدد بيجبر الموديل على استدعاء الأداة، والـ input_schema بيجبره يطابق الـ structure. ما عندكش json.loads ولا regex ولا fallback parsing.

رسم تخطيطي يوضح خط معالجة من نص حر إلى JSON منظم عبر JSON Schema validation

الأرقام من الإنتاج

قسنا الفرق على dataset حقيقي: 8,400 تذكرة دعم عربية موزعة على 5 تصنيفات. شغّلنا مرتين بنفس الموديل (Claude Sonnet 4.6) ونفس الـ temperature (0):

  • طريقة prompt + json.loads: 941 فشل في parsing من 8,400 = 11.2% failure rate. متوسط زمن الاستجابة 1.84s. تكلفة المحاولات الفاشلة + retries: 47% فوق التكلفة الأساسية.
  • طريقة tool_use + tool_choice محدد: 0 فشل من 8,400 = 0.0% failure rate. متوسط زمن الاستجابة 1.79s (أسرع لأنه مش بيكتب الـ markdown wrapper). تكلفة retries: صفر.

الفرق في التكلفة الفعلية: $31.40 شهريًا على 250K طلب (الفرق ساهم في توفير 14% من فاتورة الـ classification pipeline).

الـ Trade-offs الحقيقية

الـ Structured Outputs مش مجاني. كل توصية لها ثمن:

  1. زمن أبطأ عند schemas معقدة جدًا. لو الـ schema فيه nesting أعمق من 4 مستويات و>30 حقل، الـ overhead بيوصل 8-15% زيادة في الـ latency. الـ trade-off: vs الفشل في الـ parsing، اللي بيكلفك retry كامل.
  2. الموديل أحيانًا "بيختار" قيمة من enum غلط. الـ schema بيضمن صحة الـ structure مش صحة الـ semantics. لو القيم في الـ enum مش واضحة (مثلاً "category_a", "category_b")، الموديل ممكن يصنّف غلط بدقة 100% structural.
  3. صعب تـ debug. لو الموديل بيرجّع نتائج غريبة، مفيش text reasoning في الإجابة (إلا لو ضفت حقل reasoning في الـ schema). الحل: ضيف حقل reasoning: string في أول الـ schema علشان الموديل يـ "يفكر بصوت عالٍ" قبل الإجابة.
  4. الـ tool_choice المحدد بيمنع الموديل من رفض الإجابة. لو السؤال خارج النطاق، الموديل هيـ hallucinate إجابة لأنه مجبر يستدعي الأداة. الحل: ضيف option في الـ enum زي "out_of_scope".

متى لا تستخدم Structured Outputs

الطريقة دي مش الحل لكل حاجة. متستخدمهاش في:

  • المحتوى الإبداعي. لو بتولّد مقال أو email أو رد على عميل، الـ schema هيخنق الموديل. سيب النص الحر.
  • المحادثات الطويلة (multi-turn). الـ tool_choice المحدد في كل turn بيكسر الـ conversational flow. استخدمه بس في الـ turn اللي بيحتاج structured output.
  • لما الـ schema لسه بيتغير. لو إنت في prototyping وعمّال تجرّب أشكال مختلفة، الـ overhead بتاع تحديث الـ schema في كل تعديل بيبطّئك. ابدأ بـ JSON حر وحوّل لـ tool_use بعد ما الـ schema يستقر.
  • الموديلات الصغيرة (≤7B params). Haiku 4.5 و GPT-4o-mini شغّالين كويس مع tool_use. Llama 3.1 8B بيفشل في حوالي 3-5% من المحاولات حتى مع constrained decoding.

سؤال شائع: ليه مش JSON mode بس

OpenAI و Anthropic بيدعموا "JSON mode" بسيط بـ response_format: {"type": "json_object"}. ده بيضمن إن الإجابة JSON صحيح syntactically، لكنه ما بيضمنش إن الـ structure بيطابق schema معين. الموديل ممكن يرجّع {"random_field": "value"} بدل {"category": "...", "priority": "..."}. تستخدم JSON mode لو محتاج JSON فقط بدون structure محدد. تستخدم tool_use لو محتاج structure محدد.

الخطوة التالية

افتح أي endpoint عندك بيستخدم json.loads على رد LLM، وحوّله لـ tool_use مع tool_choice محدد. شغّل الاتنين بالتوازي على نفس 100 input من الـ logs الحقيقية بتاعتك واحسب الـ failure rate. لو الفرق ≥ 3%، ده مكسب مش هيرجع. لو أقل، استمر في النص الحر.

المصادر

  • Anthropic Tool Use Documentation — docs.anthropic.com/en/docs/build-with-claude/tool-use
  • Geng et al. "Grammar-Constrained Decoding for Structured NLP Tasks" — EMNLP 2023, arxiv.org/abs/2305.13971
  • OpenAI Structured Outputs Announcement (Aug 2024) — openai.com/index/introducing-structured-outputs-in-the-api
  • JSON Schema Specification — json-schema.org/specification
  • Anthropic SDK Python (≥0.40.0) — github.com/anthropics/anthropic-sdk-python
  • الأرقام في المقال: قياس داخلي على 8,400 تذكرة عربية، Claude Sonnet 4.6، temperature=0، أبريل 2026.
]]>

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة