تخطّى إلى المحتوى الرئيسي
إخراج JSON مُنمّط من النماذج اللغوية: كيف نستخدم generateObject وحزمة AI SDK

إخراج JSON مُنمّط من النماذج اللغوية: كيف نستخدم generateObject وحزمة AI SDK

August 1, 2026
هندسة الذكاء الاصطناعي
5 دقيقة قراءة

أبرز النقاط

دالة `generateObject` في حزمة Vercel AI SDK تُعيد كائنًا مُنمّطًا (typed) بعد التحقق منه مقابل مخطط Zod أو JSON؛ وعند عدم تطابق المخطط ترمي استثناءً بدلًا من تمرير بيانات خاطئة إلى شيفرتنا.

دالة `streamObject` تبثّ كائنًا جزئيًا عبر `partialObjectStream`، فيمتلئ النموذج أو الجدول حقلًا حقلًا قبل أن ينتهي النموذج اللغوي.

توفّر AI SDK أربعة أنماط للإخراج: `object` (الافتراضي)، و`array` (يبثّ العناصر عبر `elementStream`)، و`enum` (تصنيف بعلامة واحدة)، و`no-schema` (JSON حرّ).

نستخدم `generateObject` لاستخراج البيانات، واستدعاء الأدوات (tool calling) لتنفيذ الإجراءات؛ بينما يجمع `experimental_output` بين حلقة استدعاء الأدوات وكائن نهائي مُنمّط.

عندما يُعيد النموذج JSON غير صالح ترمي الحزمة استثناء `NoObjectGeneratedError` الذي يحمل النص الخام واستهلاك الرموز (tokens)؛ ويعالج `experimental_repairText` مع أوصاف المخطط الأدق معظم هذه الحالات.

ماذا تفعل generateObject فعليًا؟

`generateObject` دالة في Vercel AI SDK تُجبر النموذج اللغوي على إعادة JSON يطابق مخططًا نحدده، وتتحقق من الاستجابة قبل أن تراها شيفرتنا. نمرّر نموذجًا ومخطط Zod ونصًا موجِّهًا (prompt)، فنحصل على كائن مُنمّط تعرف TypeScript شكله مسبقًا.

عمليًا نستدعي `generateObject` بسلسلة نموذج مثل `anthropic/claude-sonnet-5`، ومخطط `z.object({ ... })` يصف الحقول المطلوبة — عنوان، ومصفوفة وسوم محدودة، وتعداد للمشاعر — مع النص المصدر في الـ prompt. تكون `object.sentiment` الناتجة مُنمّطة تمامًا كالاتحاد الذي أعلنّاه.

يؤدي المخطط دورين: يوجّه النموذج نحو الشكل الصحيح، ويتحقق من المخرجات. فإذا أعاد النموذج حقلًا لا يُحلَّل، ترمي `generateObject` استثناءً بدلًا من إعادة بيانات نصف صالحة، فالكائن الذي نحصل عليه مضمون التطابق مع المخطط وإلا فلن نحصل عليه إطلاقًا.

لماذا لا نكتفي بتحليل JSON من generateText؟

لأن استدعاء `JSON.parse` على نص `generateText` هو تحديدًا وضع الفشل الذي وُجدت `generateObject` لإزالته. المخرجات الخام سلسلة تكون JSON سليمًا في معظم الأحيان — إلى أن يلفّها النموذج داخل سياج markdown، أو يضيف تعليقًا زائدًا، أو يُسقط حقلًا مطلوبًا، فيفشل التحليل في بيئة الإنتاج.

تحقن `generateObject` المخطط في الطلب، وتستخدم آلية الإخراج المُنظَّم أو الأدوات لدى كل مزوّد لتقييد التوليد، ثم تتحقق من النتيجة بمخطط Zod قبل الإعادة. المكسب ليس تقليل أسطر الشيفرة، بل أن الحدّ بين مخرجات النموذج والقيمة المُنمّطة صار له مالك واحد — المخطط — بدلًا من توزّعه بين prompt وتعبير نمطي وكتلة try/catch.

متى نستخدم streamObject بدلًا من generateObject؟

نلجأ إلى `streamObject` حين يكون الكائن كبيرًا بما يجعل انتظاره كاملًا بطيئًا. تُعيد `streamObject` تدفّق `partialObjectStream` يُنتج الكائن أثناء بنائه، فتعرض الواجهة الحقول فور وصولها بدلًا من التوقف حتى الرمز الأخير.

كل قيمة مُصدَّرة هي نسخة جزئية عميقة من المخطط، فكل حقل قد يكون undefined حتى يملأه النموذج. نعرض على هذا الأساس ولا نصل إلى حقل متداخل دون حارس. أما للعمل الخادمي لمرة واحدة — مهمة cron أو معالِج مسار يُعيد مرة واحدة — فتبقى `generateObject` أبسط وهي الافتراضي لدينا؛ و`streamObject` مخصصة لزمن الاستجابة المرئي للمستخدم.

ما أنماط الإخراج التي تدعمها AI SDK؟

تكشف AI SDK أربع استراتيجيات إخراج، واختيار الصحيح منها يزيل الكثير من التفرّعات اللاحقة. يُعيد `object` (الافتراضي) كائنًا واحدًا مُتحقَّقًا منه ويناسب الاستخراج والتلخيص. يبثّ `array` العناصر عبر `elementStream` للقوائم التي يمكن عرض كل صف فيها فور وصوله. يُعيد `enum` سلسلة واحدة من مجموعة ثابتة للتصنيف والتوجيه. ويُعيد `no-schema` بيانات JSON مُحلَّلة حرّة للـ prompts الاستكشافية مجهولة الشكل.

في `enum` نمرّر `output: 'enum'` وقائمة مثل `['spam', 'not_spam']`؛ فلا يستطيع النموذج إلا إعادة إحدى تلك السلاسل بالضبط، وهو أصرم وأقل كلفة من كائن بحقل تعداد واحد. وفي `array` نصف عنصرًا واحدًا في المخطط ونستهلك `elementStream` لعرض الصفوف عند اكتمالها بدل انتظار القائمة كاملة.

متى نستخدم استدعاء الأدوات بدلًا من generateObject؟

نستخدم `generateObject` لاستخراج قيمة، واستدعاء الأدوات لتنفيذ شيء — هذا التقسيم يحسم معظم النقاشات التصميمية. لا آثار جانبية لـ `generateObject`: تحوّل مُدخلًا غير مُنظَّم إلى كائن مُنمّط واحد ثم تتوقف. أما استدعاء الأدوات — `generateText` أو `streamText` مع خريطة `tools` — فيتيح للنموذج أن يقرر استدعاء دوال، كالبحث في قاعدة بيانات أو استدعاء واجهة برمجية، ربما عدة مرات في حلقة، قبل أن يجيب.

`experimental_output`، وما زال مُعلَّمًا تجريبيًا في الحزمة، هو الجسر بين الأمرين. مع `generateText` وخريطة `tools`، يُشغّل النموذج حلقة استدعاء الأدوات ثم يُعيد إجابة نهائية مُتحقَّقًا منها مقابل مخطط، فنحصل على الإجراءات ونتيجة مُنمّطة من استدعاء واحد بدل إلحاق `generateObject` ثانية في النهاية.

كيف نتعامل مع نموذج يُعيد JSON غير صالح؟

حين يفشل التوليد في التحقق من المخطط، ترمي AI SDK استثناء `NoObjectGeneratedError`، والتقاطه صراحةً هو الفارق بين تراجُع لطيف وخطأ 500. يحمل الاستثناء النص الخام الذي أنتجه النموذج مع استهلاك الرموز والاستجابة، فنسجّل بالضبط ما عاد وكم كلّف.

ثلاث عادات تقلّل معدل الفشل قبل أن تعمل كتلة الالتقاط أصلًا. أولًا، `.describe()` على كل حقل غير بديهي، لأن الوصف يُرسَل إلى النموذج — فـ `z.string().describe('ISO 8601 date')` أفضل من الأمل في أن يخمّن الصيغة. ثانيًا، `.nullable()` بدل `.optional()` للحقول التي قد يجهلها النموذج، لأن كثيرًا من النماذج تُصدر `null` بثبات أكبر من إسقاط المفتاح. ثالثًا، `experimental_repairText`، وهو دالة تستقبل النص الخام ويمكنها إزالة سياج شيفرة أو فاصلة زائدة قبل إعادة التحليل. ولا نلجأ إلى إعادة كاملة إلا بعد هذه الثلاث، لأن الإعادة تضاعف زمن الاستجابة والكلفة.

الأسئلة الشائعة

ما الفرق بين generateObject وgenerateText؟ يُعيد `generateText` سلسلة نصية حرّة؛ بينما يُعيد `generateObject` كائنًا مُنمّطًا مُتحقَّقًا منه مقابل مخطط ويرمي استثناءً إن لم تطابق المخرجات. استخدم `generateText` للنثر و`generateObject` كلما احتجت بيانات قابلة للقراءة آليًا.

هل تعمل generateObject مع أي نموذج؟ تعمل مع أي مزوّد تدعمه AI SDK، لكن الآلية تختلف — بعض المزوّدين يستخدمون وضع الإخراج المُنظَّم الأصلي، وآخرون يستخدمون استدعاء الأدوات ضمنيًا. تمرّر المخطط نفسه في الحالتين، والنماذج ذات الإخراج المُنظَّم الأصلي هي الأكثر موثوقية.

هل يمكننا بثّ كائن مُنظَّم إلى المتصفح؟ نعم. تُعيد `streamObject` تدفّق `partialObjectStream` من كائنات جزئية عميقة. اعرض كل حقل فور وصوله وعامل كل حقل باعتباره قد يكون undefined حتى يكتمل التدفّق.

كيف نصنّف نصًا دون غلاف كائن؟ استخدم `output: 'enum'` مع مصفوفة `enum` من العلامات المسموح بها. على النموذج إعادة إحدى السلاسل بالضبط، وهو أصرم وأقل كلفة من كائن بحقل تعداد واحد.

ما الذي يُرمى عند تلف مخرجات النموذج؟ `NoObjectGeneratedError`. يكشف النص الخام واستهلاك الرموز والاستجابة لتسجّل وتتراجع بأمان. قلّل تكراره بوصف الحقول عبر `.describe()`، و`.nullable()` بدل `.optional()`، ودالة `experimental_repairText`.

قراءات إضافية

بناء خادم MCP بعيد في Next.js باستخدام mcp-handler 2: الأدوات وOAuth ومواصفة 2026-07-28 عديمة الحالة
اقرأ المقال

هندسة الذكاء الاصطناعي

بناء خادم MCP بعيد في Next.js باستخدام mcp-handler 2: الأدوات وOAuth ومواصفة 2026-07-28 عديمة الحالة

أصبحنا نبني خوادم MCP البعيدة كـ Route Handler واحد في Next.js باستخدام mcp-handler 2 ومواصفة MCP عديمة الحالة بإصدار 2026-07-28. نشرح هنا كيف نركّبها، وكيف نصمّم أدوات يستدعيها النموذج بشكل صحيح، وكيف نؤمّنها بـ OAuth وبيانات RFC 9728، وكيف نختبرها قبل اتصال أي عميل حقيقي.

لماذا لا يوقف موجّه نظام أفضل حقنَ الأوامر: ملاحظاتنا الميدانية حول الثلاثية القاتلة والأدوات محددة الصلاحيات والمخرجات غير الموثوقة
اقرأ المقال

هندسة الذكاء الاصطناعي

لماذا لا يوقف موجّه نظام أفضل حقنَ الأوامر: ملاحظاتنا الميدانية حول الثلاثية القاتلة والأدوات محددة الصلاحيات والمخرجات غير الموثوقة

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