المخرجات المنظّمة في 2026: كيف تحصل على JSON موثوق من نماذج الذكاء الاصطناعي (OpenAI وClaude وGemini)

التوليد المقيّد بمخطط يصلح صياغة JSON، لكنه لا يمنع الانقطاع أو الرفض أو القيم الخاطئة. كيف تعمل المخرجات المنظّمة لدى OpenAI وAnthropic وGoogle، وقواعد تصميم المخطط، وطبقة تحقق مُختبرة بلغة Python.

Lumis Editorial · 11 دقائق قراءة · October 7, 2026

Read this article in English

المخرجات المنظّمة في 2026: كيف تحصل على JSON موثوق من نماذج الذكاء الاصطناعي (OpenAI وClaude وGemini)

في اللحظة التي تدخل فيها إجابة نموذج الذكاء الاصطناعي إلى الكود (صف في قاعدة بيانات، أو فاتورة، أو سجل عميل، أو الخطوة التالية لوكيل)، لا يعود "JSON صحيح في الغالب" كافياً. قوس واحد ناقص، أو حقل اسمه Total بدل total، ويتوقف نظامك في الثالثة فجراً. في 2026 تقدّم كل الشركات الكبرى ميزة المخرجات المنظّمة (Structured Outputs): تعطي النموذج مخططاً بصيغة JSON Schema، فتُقيّد الواجهة البرمجية عملية التوليد حتى تلتزم الإجابة به. هذا يحل مشكلة الصياغة، لكنه لا يحل كل شيء، والثغرات المتبقية هي بالضبط حيث تعيش أخطاء الإنتاج. يشرح هذا الدليل كيف تعمل المخرجات المنظّمة لدى OpenAI وAnthropic وGoogle، وما الذي لا تضمنه بعد، وكيف تصمم مخططاً ناجحاً، مع طبقة تحقق مُختبرة بلغة Python تستطيع تعديلها.

ثلاثة مستويات لطلب "أعطني JSON"

  • الطلب في الموجّه فقط. تطلب بلطف ("ردّ بصيغة JSON بهذا الشكل"). النماذج الحديثة تلتزم في معظم الأحيان، لكن "معظم الأحيان" يعني أنك تحتاج إعادة محاولات، والأخطاء تظهر كنص زائد، أو علامات Markdown، أو حقول ناقصة.
  • وضع JSON. تضمن الواجهة أن المخرجات قابلة للقراءة كـJSON، لكن لا تضمن أنها تطابق البنية التي تريدها. وتنبّه OpenAI نفسها في إعلانها إلى أن وضع JSON لا يضمن الالتزام بمخطط محدد.
  • المخرجات المقيّدة بمخطط (المخرجات المنظّمة). ترسل مخطط JSON Schema، فتجبر الواجهة المخرجات على اتباعه. هذا هو المستوى المناسب لأي شيء سيقرؤه برنامج.

كيف يعمل التوليد المقيّد؟

النموذج اللغوي يكتب وحدة نصية (token) واحدة في كل مرة، ويختارها من احتمالات موزعة على كل مفرداته. مع المخرجات المنظّمة، يحوّل المزوّد مخططك أولاً إلى قواعد (grammar). ثم يحدد في كل خطوة الوحدات المسموح بها بعد ذلك، ويجعل احتمال كل ما عداها صفراً. فإذا كان المخطط يقول إن الشيء التالي يجب أن يكون المفتاح "total_aed" متبوعاً برقم، فلن يستطيع النموذج كتابة أي شيء آخر.

قدّمت OpenAI هذه الميزة في واجهتها البرمجية في أغسطس 2024. وفي تقييمها الخاص لاتباع المخططات المعقدة، حقق نموذج GPT-4o الجديد آنذاك مع المخرجات المنظّمة نسبة 100%، مقابل أقل من 40% لنموذج GPT-4 الأقدم (نسخة يونيو 2023). التدريب وحده أوصل النموذج الجديد إلى 93%، وأغلق التوليد المقيّد بقية الفجوة. وهذا هو الدرس الأساسي: النماذج الأفضل تقلل الأخطاء، لكن الفرض وحده يلغيها.

أين وصلت الشركات (أكتوبر 2026)؟

  • OpenAI: فعّل strict: true، إما في تعريف الدالة (لاستدعاءات الأدوات)، أو في response_format من نوع json_schema (للإجابة نفسها). وتدعم جزءاً فقط من مواصفات JSON Schema.
  • Anthropic ‏(Claude): ميزتان يمكن استخدام كل منهما وحدها أو معاً. مخرجات JSON تستقبل المخطط في output_config.format، والاستخدام الصارم للأدوات يضيف strict: true إلى تعريف الأداة حتى تُتحقق أسماء الأدوات ومدخلاتها. أُطلقت الميزة بصفة تجريبية في نوفمبر 2025، ولم تعد تحتاج ترويسات تجريبية (beta headers). وتستطيع مكتبات Anthropic أيضاً استقبال نموذج Pydantic ‏(Python) أو Zod ‏(TypeScript) مباشرة.
  • Google ‏(Gemini): اضبط response_mime_type على application/json، ومرّر المخطط في response_json_schema. وفي نوفمبر 2025 أضافت Google دعم JSON Schema في كل نماذجها المدعومة حالياً، بما في ذلك anyOf و$ref وminimum/maximum وadditionalProperties، وأصبح ترتيب المفاتيح في المخرجات يتبع ترتيبها في مخططك في Gemini 2.5 وما بعده.

كل مزوّد يدعم جزءاً مختلفاً قليلاً من مواصفات JSON Schema، والقوائم تتغير. راجع التوثيق الحالي لمعرفة الكلمات المفتاحية المدعومة بالضبط قبل أن تعتمد على أي منها. (وللجانب المتعلق باستدعاء الأدوات، راجع دليلنا عن استدعاء الأدوات بشكل موثوق لوكلاء الذكاء الاصطناعي.)

ما الذي لا تضمنه المخرجات المنظّمة بعد؟

اقرأ التفاصيل الدقيقة في توثيق OpenAI وAnthropic، وستجد الثغرات الخمس نفسها:

  • الانقطاع. إذا وصلت الإجابة إلى حد الوحدات النصية (max_tokens) قبل أن تكتمل، ينقطع JSON في المنتصف. وتقول الشركتان إن المخرجات قد لا تطابق المخطط حينها. افحص دائماً سبب التوقف قبل القراءة.
  • الرفض. إذا رفض النموذج طلباً لأسباب تتعلق بالأمان، فقد لا تتبع الإجابة مخططك. ترجع OpenAI حقلاً منفصلاً اسمه refusal، وترجع Anthropic القيمة stop_reason: "refusal"، وتُحتسب عليك تكلفة الوحدات النصية رغم ذلك.
  • قيم خاطئة في الشكل الصحيح. تقول OpenAI بوضوح إن المخرجات قد تحتوي أخطاء في القيم. المخطط يستطيع أن يجبر total_aed على أن يكون رقماً، لكنه لا يستطيع أن يجبره على أن يكون الرقم الصحيح، أو موجباً، أو أن يكون التاريخ حقيقياً.
  • مفاجآت صغيرة في الصيغة. يذكر توثيق Anthropic أن حالة الأحرف في قيم القوائم المحددة (enum) غير مضمونة، فقارنها دون اعتبار لحالة الأحرف. كما أن Anthropic لا تفرض قيود الأرقام والأطوال مثل minimum أو maxLength في المخطط المرسل، بل تنقلها مكتباتها إلى أوصاف الحقول وتتحقق منها بعد وصول الإجابة.
  • البطء والحدود. أول طلب بمخطط جديد يكون أبطأ أثناء تجهيز القواعد (تقول OpenAI إن المخططات المعتادة تحتاج أقل من 10 ثوانٍ، والمعقدة حتى دقيقة، ثم تحفظ الشركتان النتيجة مؤقتاً). وقد تُرفض المخططات المعقدة جداً: توثّق Anthropic حدوداً مثل 20 أداة صارمة و24 معاملاً اختيارياً في الطلب الواحد. والوضع الصارم لدى OpenAI لا يعمل مع استدعاء عدة دوال بالتوازي.

كيف تصمم مخططاً ناجحاً؟

  • اجعله بسيطاً وصغيراً. كل حقل اختياري وكل نوع متعدد يزيد تعقيد القواعد. استخرج ما تحتاجه الآن، لا كل ما قد يفيد يوماً ما.
  • استخدم قوائم محددة للتصنيفات. قائمة ثابتة ("office"، "travel"، "software") أفضل من نص حر تحتاج إلى تحويله لاحقاً.
  • اسمح بـ"غير معروف". إذا كان الحقل قد يغيب عن المصدر، فاجعله قابلاً لقيمة null. وإلا اضطر النموذج إلى اختراع قيمة ليرضي المخطط، وهذا أسوأ من قيمة فارغة صادقة.
  • صِف كل حقل. الأوصاف جزء من الموجّه. عبارة مثل "المجموع شاملاً ضريبة القيمة المضافة، بالدرهم، كرقم دون رمز العملة" تزيل التخمين.
  • لا تضع تفكيراً طويلاً داخل المخطط. تحذّر Anthropic من أن طلب شرح خطوة بخطوة داخل أحد الحقول قد يؤدي إلى رفض الطلب، فاطلب حقلاً لشرح قصير بدلاً من ذلك.
  • حافظ على ثبات المخطط. تغيير بنيته يلغي القواعد المحفوظة مؤقتاً (ويلغي في Claude ذاكرة الموجّه المؤقتة أيضاً)، فتعامل معه كواجهة برمجية لها إصدارات.

مثال مُختبر: طبقة التحقق التي ما زلت تحتاجها

السكربت التالي لا يحتاج مفتاح API. يلعب دور الكود الذي يستقبل خمس إجابات خام من خطوة استخراج بيانات الفواتير، من النوع الذي يراه كل فريق في سجلات الإنتاج. يفحص سبب التوقف، ويستخرج JSON ويقرؤه، ويتحقق من الأنواع، ويوحّد حالة الأحرف في التصنيف، ويطبق قواعد العمل التي لا يستطيع المخطط التعبير عنها. وتحصل كل إجابة على واحد من ثلاثة أحكام: OK ‏(مقبولة)، أو RETRY ‏(أعد المحاولة، مع سبب ترسله إلى النموذج)، أو ESCALATE ‏(حوّلها إلى شخص).

"""Validate AI model output before your code trusts it (no API key needed).
Constrained decoding fixes the JSON syntax; this layer catches everything else:
truncation, refusals, wrong casing, and values that are valid JSON but wrong.
"""
import json, re
from datetime import date

SCHEMA = {  # what we asked the model to extract from an invoice
    "vendor": str, "invoice_date": str, "total_aed": (int, float),
    "category": str, "vat_included": bool,
}
CATEGORIES = {"office", "travel", "software"}

def right_type(value, expected):
    if expected is bool:
        return isinstance(value, bool)
    return isinstance(value, expected) and not isinstance(value, bool)  # True isn't a number here

def check(stop_reason, text):
    if stop_reason == "refusal":
        return "ESCALATE", "model refused; send to a person"
    if stop_reason == "max_tokens":
        return "RETRY", "output was cut off; raise max_tokens and retry"
    match = re.search(r"\{.*\}", text, re.S)  # tolerate ```json fences or chatter
    try:
        data = json.loads(match.group(0) if match else text)
    except json.JSONDecodeError as e:
        return "RETRY", f"invalid JSON ({e.msg})"
    errors = [f"missing '{k}'" for k in SCHEMA if k not in data]
    errors += [f"'{k}' has the wrong type" for k, t in SCHEMA.items()
               if k in data and not right_type(data[k], t)]
    if errors:
        return "RETRY", "; ".join(errors)
    data["category"] = data["category"].strip().lower()  # casing isn't guaranteed
    if data["category"] not in CATEGORIES:
        errors.append(f"unknown category '{data['category']}'")
    try:
        if date.fromisoformat(data["invoice_date"]) > date(2026, 10, 7):
            errors.append("invoice date is in the future")
    except ValueError:
        errors.append("invoice_date is not a real date")
    if not 0 < data["total_aed"] < 100_000:
        errors.append(f"total {data['total_aed']} AED is outside the allowed range")
    if errors:
        return "RETRY", "; ".join(errors)
    return "OK", data

# Simulated raw responses, the kind every team sees in production logs.
responses = [
    ("end_turn", '{"vendor": "Gulf Office Supplies", "invoice_date": "2026-09-28", '
                 '"total_aed": 1260.5, "category": "Office", "vat_included": true}'),
    ("end_turn", 'Sure! Here is the data:\n```json\n{"vendor": "SkyTravel", '
                 '"invoice_date": "2026-09-30", "total_aed": 2400, "category": "travel", '
                 '"vat_included": "yes"}\n```'),
    ("max_tokens", '{"vendor": "CloudSoft FZ-LLC", "invoice_date": "2026-10-01", "tot'),
    ("end_turn", '{"vendor": "CloudSoft FZ-LLC", "invoice_date": "2026-11-31", '
                 '"total_aed": -399, "category": "software", "vat_included": false}'),
    ("refusal", ""),
]
for i, (stop, text) in enumerate(responses, 1):
    verdict, detail = check(stop, text)
    print(f"{i}. {verdict:8} {detail}")

النتيجة، عند التشغيل بتاريخ 2026-10-07:

1. OK       {'vendor': 'Gulf Office Supplies', 'invoice_date': '2026-09-28', 'total_aed': 1260.5, 'category': 'office', 'vat_included': True}
2. RETRY    'vat_included' has the wrong type
3. RETRY    output was cut off; raise max_tokens and retry
4. RETRY    invoice_date is not a real date; total -399 AED is outside the allowed range
5. ESCALATE model refused; send to a person

ماذا تُظهر كل حالة:

  • 1. مقبولة، بعد تحويل "Office" إلى "office". اختلاف حالة الأحرف شائع، ولا يضر إذا توقعته.
  • 2. نص زائد وعلامات Markdown ونوع خاطئ. هكذا يبدو JSON عند الاكتفاء بالطلب في الموجّه. أداة القراءة تتجاهل الكلام الزائد، لكن "yes" ليست قيمة منطقية (boolean). ومع تفعيل المخرجات المنظّمة الصارمة يُفترض ألا تحدث هذه الحالة، ولهذا بالضبط تفعّلها.
  • 3. مقطوعة. سبب التوقف يقول إن الوحدات النصية نفدت، فلا يحاول الكود قراءتها أصلاً. لا يوجد مخطط يمنع هذا، الفحص وحده يكشفه.
  • 4. JSON مثالي وبيانات خاطئة. لا يوجد يوم 31 نوفمبر، والمجموع سالب. هذه الإجابة ستمر من أي واجهة مقيّدة بمخطط. قواعد العمل مكانها كودك.
  • 5. رفض. له مسار خاص ويُحوَّل إلى شخص، بدل أن يتعطل البرنامج عند قراءة نص فارغ.

حين تحتاج إعادة المحاولة، أرسل الخطأ المحدد إلى النموذج ("invoice_date is not a real date") بدل تكرار الطلب نفسه. واجعل الحد الأقصى محاولة أو محاولتين، ثم حوّل الحالة إلى شخص. ولقياس عدد مرات حدوث كل مسار على مستنداتك الحقيقية، راجع دليلنا عن تقييم وكلاء الذكاء الاصطناعي.

قائمة تحقق قبل الإطلاق

  • المخرجات المنظّمة الصارمة مفعّلة في كل مكان يقرأ فيه الكود النتيجة.
  • الكود يفحص سبب التوقف (انقطاع، رفض) قبل القراءة.
  • كل حقل قد لا يوجد في المصدر يقبل قيمة null.
  • مقارنة قيم القوائم المحددة لا تعتمد على حالة الأحرف.
  • قواعد العمل (النطاقات، والتواريخ الحقيقية، والمجاميع المتطابقة) يتحقق منها الكود.
  • إعادة المحاولة ترسل الخطأ المحدد، ولها حد أقصى ومسار بشري بديل.
  • المخطط له إصدارات، والتأخير في الطلب الأول مقبول لاستخدامك.

الخلاصة

حوّلت المخرجات المنظّمة عبارة "ردّ من فضلك بصيغة JSON" من أمنية إلى ضمان من الواجهة البرمجية بخصوص الشكل، ويجب أن يستخدمها كل تكامل جاد. لكن الشكل ليس الحقيقة. احتفظ بطبقة تحقق خفيفة تفحص كيف انتهت الإجابة، وهل القيم منطقية، وماذا تفعل حين لا تكون كذلك. إنها بضع عشرات من أسطر الكود، وهي الفرق بين عرض تجريبي ونظام تستطيع تركه يعمل طوال الليل. (وكتابة التعليمات المحيطة بالمخطط مهمة أيضاً، راجع دليلنا عن كتابة موجّه النظام لوكلاء الذكاء الاصطناعي.)

المصادر: OpenAI، "Introducing Structured Outputs in the API" ‏(أغسطس 2024)؛ Anthropic، توثيق المخرجات المنظّمة في Claude؛ Google، "Improving Structured Outputs in the Gemini API" ‏(نوفمبر 2025). راجعناها في أكتوبر 2026، والميزات المدعومة في المخططات تتغير، فتأكد من التفاصيل في التوثيق الحالي لكل مزوّد.

مقالات ذات صلة