درخواست ساده در پرامپت برای دریافت پاسخ در قالب JSON حتی در قویترین مدلهای زبانی نیز بین 5 تا 15 درصد مواقع با شکست مواجه میشود؛ از جا افتادن آکولاژ گرفته تا تولید فیلدهای ساختگی یا خروجیهای ناقص. در پایپلاینهای عملیاتی و حساس نرمافزاری، چنین خطاهایی کل فرایند پردازش را مختل میکنند و روشهای اعتبارسنجی پس از تولید نیز هزینهی توکن و تأخیر پاسخها را به شدت افزایش میدهند.
راهکار پایدار برای این مسئله، استفاده از «دیکود مقید» (Constrained Decoding) و خروجیهای ساختاریافته است؛ قابلیتی که در آن مدل به صورت ریاضی از تولید توکنهای مغایر با اسکیما منع میشود. در این درس یاد میگیرید که چگونه با کمک JSON Schema 2020-12، حالت Strict در OpenAI و ابزارهایی مانند Pydantic و Zod، خروجیهایی با تضمین 100 درصدی ساختار بسازید و سناریوهای امتناع مدل (Refusal) را به صورت استاندارد مدیریت کنید.
اگر از ایران به API ارائهدهندگانی مثل OpenAI یا Gemini دسترسی دارید، ممکن است به دلیل محدودیت دسترسی بر پایهی IP با چالشهایی مواجه شوید؛ همچنین به دلیل محدودیت کارتهای بانکی ایرانی در پرداخت مستقیم، استفاده از گیتویهای واسط سازگار با OpenAI گزینهی رایجی است. خوشبختانه سازوکار خروجی ساختاریافته روی کدهای سازگار با API و حتی مدلهای بازمتن محلی نیز به خوبی پیادهسازی میشود.
«خواهش مؤدبانه از مدل برای برگرداندن JSON» حتی در پیشرفتهترین مدلها (Frontier models) هم در 5 تا 15 درصد مواقع شکست میخورد. خروجیهای ساختاریافته (Structured outputs) این شکاف را با دیکود مقید (Constrained decoding) پر میکنند: مدل عملاً نمیتواند توکنی تولید کند که اسکیما را نقض کند. حالت Strict در OpenAI، فراخوانی ابزار مقید به اسکیما در Anthropic، پارامتر
responseSchemaدر Gemini، خروجیoutput_typeدر Pydantic AI و متد.parseدر Zod، همگی پنج جلوهی بیرونی از یک ایدهی واحدند. در این درس، اعتبارسنج اسکیما و قرارداد حالت Strict را میسازیم؛ ابزارهایی که در تمام پایپلاینهای استخراج داده در محیط پروداکشن به کارتان خواهند آمد.
اهداف یادگیری
- نوشتن یک JSON Schema بر پایهی پیشنویس 2020-12 برای استخراج داده با قیدهای مناسب (
enum، حداقل و حداکثر، فیلدهای اجباری،pattern). - درک اینکه چرا حالت Strict و دیکود مقید، تضمینهایی کاملاً متفاوت از روش «اعتبارسنجی پس از تولید» میدهند.
- تفکیک سه حالت شکست (Failure modes): خطای تجزیه (Parse error)، نقض اسکیما (Schema violation) و امتناع مدل (Model refusal).
- پیادهسازی پایپلاین استخراج داده به همراه ترمیم تایپشده و مدیریت ساختاریافتهی موارد امتناع.
صورت مسئله
ایجنتی که ایمیل سفارش خرید را میخواند، باید متن آزاد را به ساختار {customer, line_items, total_usd} تبدیل کند. برای این کار سه رویکرد وجود دارد:
رویکرد اول: درخواست JSON در پرامپت. «پاسخ را در قالب JSON با فیلدهای customer، line_items و total_usd برگردان.» این روش روی پیشرفتهترین مدلها در 85 تا 95 درصد مواقع جواب میدهد، اما به 6 شکل شکست میخورد: جا افتادن آکولاژ، وجود کامای اضافه در انتها (Trailing comma)، نوعدادههای اشتباه، فیلدهای ساختگی ناشی از توهم مدل، هرس شدن (Truncation) خروجی به خاطر محدودیت سقف توکنها، و نشت متنهای اضافی مثل «Here is your JSON:».
رویکرد دوم: اعتبارسنجی پس از تولید. مدل خروجی را آزادانه تولید میکند؛ سپس آن را پارس کرده و با اسکیما اعتبارسنجی میکنید و اگر خطا داشت، دوباره تلاش میکنید (Retry). این روش قابل اتکاست ولی گران تمام میشود؛ بابت هر تلاش مجدد باید هزینه بدهید، و هر بار باگ هرس رخ دهد، یک نوبت گفتوگو (Turn) اضافه به پایپلاین تحمیل میکند.
رویکرد سوم: دیکود مقید. ارائهدهنده، اسکیما را مستقیماً هنگام رمزگشایی (Decode) اعمال میکند. توکنهای نامعتبر از همان ابتدا از توزیع نمونهبرداری حذف (Mask) میشوند. پارس شدن و انطباق قطعی خروجی با اسکیما کاملاً تضمین میشود. در این حالت، همهی شکلهای شکست فقط به یک حالت محدود میشوند: امتناع یا Refusal (مدل به این نتیجه میرسد که ورودی با اسکیما همخوانی ندارد).
تمام ارائهدهندگان پیشرو در سال 2026 شکلی از رویکرد سوم را پیاده کردهاند:
- OpenAI. گزینهی
response_format: {type: "json_schema", strict: true}به همراه فیلدrefusalدر پاسخ، برای مواقعی که مدل از جوابدادن سر باز میزند. - Anthropic. اعمال اسکیما روی ورودیهای فراخوانی ابزار (
tool_use)؛ اینجا مقداری به نامstop_reason: "refusal"وجود ندارد، اما پایان نوبت (end_turn) بدون فراخوانی ابزار، نشانهی امتناع است. - Gemini. پارامتر
responseSchemaدر سطح درخواست؛ جمینای در سال 2026 برای تایپهای منتخب، قیدهای گرامری در سطح توکن ارائه میدهد. - Pydantic AI. آرگومان
output_type=InvoiceModelیکRunResultساختاریافته با تایپ دقیقِInvoiceModelبرمیگرداند. - Zod (در اکوسیستم TypeScript). پارسر زمان اجرا (Runtime) که خروجی ارائهدهنده را در برابر اسکیمای Zod اعتبارسنجی میکند؛ این ابزار با متد
beta.chat.completions.parseدر OpenAI جفت میشود.
نقطهی اشتراک همهی اینها یک چیز است: اسکیما را یک بار تعریف کنید و آن را سرتاسر پایپلاین اعمال کنید.
مفاهیم کلیدی
استاندارد JSON Schema 2020-12 — زبان مشترک
همهی ارائهدهندگان، استاندارد JSON Schema 2020-12 را قبول دارند. پرکاربردترین ساختارها عبارتند از:
type: یکی از مقادیرobject،array،string،number،integer،booleanیاnull.properties: نگاشت نام فیلد به زیراسکیمای مربوطه.required: فهرستی از فیلدهایی که باید حتماً در خروجی باشند.enum: مجموعهای بسته از مقادیر مجاز.minimum/maximum(برای اعداد)،minLength/maxLength/pattern(برای رشتهها).items: زیراسکیمایی که روی تکتک عناصر یک آرایه اعمال میشود.additionalProperties: مقدارfalseاجازهی اضافهکردن فیلدهای ناشناخته را نمیدهد (پیشفرض آن در حالتهای مختلف فرق دارد).
حالت Strict در OpenAI سه شرط دیگر هم اضافه میکند: تکتک فیلدها باید در لیست required بیایند، همهجا additionalProperties: false باشد، و هیچ $ref حلنشدهای وجود نداشته باشد. در صورت نقض این شروط، API در همان لحظهی درخواست خطای 400 میدهد.
اتصال پایتون با Pydantic
نسخهی 2 پایدنتیک (Pydantic) با متد model_json_schema() از روی مدلهای ساختاریافته به شکل Dataclass، اسکیمای JSON استاندارد تولید میکند. کتابخانهی Pydantic AI این سازوکار را در قالب بستهای تمیز درآورده تا کافی باشد اینطور بنویسید:
class Invoice(BaseModel):
customer: str
line_items: list[LineItem]
total_usd: Decimal
سپس فریمورک ایجنت، این اسکیما را به تناسب ارائهدهنده به حالت Strict در OpenAI، فیلد input_schema در Anthropic یا responseSchema در Gemini تبدیل میکند. خروجی مدل به صورت یک نمونهی تایپشده از کلاس Invoice برمیگردد. در صورت بروز خطای اعتبارسنجی نیز استثنای ValidationError با مسیرهای خطای مشخص بالا میآید.
اتصال TypeScript با Zod
کتابخانهی Zod با سینتکس z.object({customer: z.string(), ...}) معادل همین سازوکار در تایپاسکریپت است. پکیج Node رسمی OpenAI تابعی به نام zodResponseFormat(Invoice) ارائه میدهد که این ساختار را به اسکیمای استاندارد JSON Schema برای ارسال به API تبدیل میکند.
امتناعها (Refusals)
حالت Strict نمیتواند مدل را به پاسخ دادن مجبور کند. اگر ورودی در اسکیما نگنجد (مثلاً ایمیل دریافتی به جای فاکتور، یک شعر باشد)، مدل فیلدی به نام refusal همراه با دلیلِ امتناع برمیگرداند. کد شما باید با این سناریو به عنوان یک نتیجهی معتبر و ردهاول برخورد کند، نه به چشم یک خطای سیستمی. فیلد امتناع برای بررسی ایمنی هم کاربرد دارد: اگر از مدل بخواهید شمارهی کارت اعتباری را از ایمیلی با محتوای محافظتشده استخراج کند، پاسخی از نوع امتناع همراه با دلیل امنیتی پس میدهد.
دیکود مقید در مدلهای بازمتن
پیادهسازیهای مدلهای بازمتن معمولاً از سه تکنیک استفاده میکنند:
- رمزگشایی مبتنی بر گرامر (در پکیجهای
outlines،guidanceوlm-format-enforcer): ساخت یک ماشین متناهی قطعی (FSM) از روی اسکیما؛ در هر گام، لاجیتهای (Logits) توکنهایی که این ماشین را نقض کنند ماسک میشوند. - ماسک کردن لاجیت با پارسر JSON: اجرای همگام یک پارسر استریمینگ JSON در کنار مدل؛ در هر گام، مجموعهی توکنهای مجاز بعدی محاسبه میشود.
- دیکودینگ حدسی (Speculative Decoding) به همراه مدل اعتبارسنج (Verifier): یک مدل پیشنویس (Draft Model) کمهزینه توکنها را پیشنهاد میدهد و مدل اعتبارسنج انطباق با اسکیما را تضمین میکند.
پلتفرمهای تجاری در پشتصحنه یکی از همین سه روش را انتخاب میکنند. در سال 2026، سرعت این فناوری برای خروجیهای ساختاریافتهی کوتاه حتی از تولید متن عادی هم بیشتر است و برای خروجیهای طولانی تقریباً برابری میکند.
سه حالت شکست
- خطای تجزیه (Parse error): خروجی، ساختار JSON معتبری ندارد. در حالت Strict این سناریو غیرممکن است، اما روی ارائهدهندگان دیگر پیش میآید.
- نقض اسکیما (Schema violation): خروجی بهدرستی پارس میشود ولی قواعد اسکیما را نقض میکند. در حالت Strict رخ نمیدهد، اما بیرون از آن شایع است.
- امتناع (Refusal): مدل از پاسخدادن سر باز میزند. باید به عنوان یک نتیجهی ساختاریافته و استاندارد مدیریت شود.
استراتژی تلاش مجدد (Retry)
وقتی بیرون از حالت Strict کار میکنید (مثل فراخوانی ابزار در Anthropic، مدلهای بدون حالت Strict در OpenAI یا نسخههای قدیمیتر Gemini)، الگوی جبران خطا به این شکل خواهد بود:
generate -> parse -> validate -> if fail, inject error and retry, max 3x
یک بار تلاش مجدد معمولاً مشکل را رفع میکند. سه بار تلاش، ناهماهنگیهای مدلهای ضعیفتر را میپوشاند. بیشتر از سه بار تلاش نشانهی نقص در خود اسکیماست؛ یعنی مدل برای بعضی ورودیها اصلاً قادر به تأمین شروط اسکیما نیست و باید پرامپت یا خود اسکیما بازنگری شوند.
پشتیبانی در مدلهای کوچک
دیکود مقید روی مدلهای کوچک هم عملکرد فوقالعادهای دارد. یک مدل بازمتن با 3 میلیارد پارامتر مجهز به قیدهای گرامری، در تسکهای ساختاریافته بهتر از یک مدل 70 میلیارد پارامتری عمل میکند که فقط با پرامپت خام کار میکند. دلیل اصلی ارزش خروجیهای ساختاریافته در پروداکشن دقیقاً همین است: اتکاپذیری سیستم را از ابعاد و حجم مدل مستقل میکند.
به کار ببندید
فایل code/main.py یک اعتبارسنج کمینهی استاندارد JSON Schema 2020-12 با استفاده از کتابخانهی استاندارد پایتون پیاده کرده است که مواردی مثل نوع دادهها، فیلدهای اجباری، مقادیر مجاز (enum)، شروط کمینه/بیشینه، الگوها، آرایهها و مشخصهی additionalProperties را بررسی میکند. این اسکریپت با اسکیمای Invoice، خروجیهای ساختگی یک LLM را ارزیابی میکند تا مسیرهای خطای تجزیه، نقض اسکیما و امتناع را عملاً نشان دهد. در محیط پروداکشن، خروجی ساختگی را با پاسخ واقعی هر ارائهدهندهای جایگزین کنید.
به این بخشها دقت کنید:
- اعتبارسنج، فهرستی تایپشده از جنس
[ValidationError]همراه با مسیر خطا و پیام برمیگرداند. این دقیقاً همان ساختاری است که باید به پرامپت تلاش مجدد تحویل دهید. - branch مربوط به امتناع اصلاً تلاش مجدد انجام نمیدهد؛ بلکه لاگ میاندازد و یک امتناع تایپشده برمیگرداند. فاز 14 · 09 از این امتناعها به عنوان سیگنال ایمنی استفاده میکند.
- بررسی
additionalProperties: falseروی ورودی تستی مخرب فعال میشود؛ این تست نشان میدهد چرا حالت Strict راه ورود فیلدهای ساختگی بر اثر توهم مدل را کاملاً میبندد.
تحویل پروژه
خروجی این درس فایل outputs/skill-structured-output-designer.md است. این مهارت با گرفتن یک هدف استخراج از متن آزاد (مثل فاکتورها، تیکتهای پشتیبانی، رزومهها و موارد مشابه)، یک اسکیمای JSON Schema 2020-12 سازگار با حالت Strict و یک مدل Pydantic متناظر با آن را میسازد؛ طوری که مدیریت تایپشدهی امتناع و تلاش مجدد هم درون آن قالببندی شده باشد.
تمرینها
-
فایل
code/main.pyرا اجرا کنید. یک مورد تست چهارم اضافه کنید که مقدارtotal_usdدر آن عددی منفی باشد. مطمئن شوید اعتبارسنج آن را به خاطر قیدminimumرد میکند. -
اعتبارسنج را طوری توسعه دهید که از شرط
oneOfهمراه با فیلد تفکیککننده (Discriminator) پشتیبانی کند؛ کاربرد رایج: وقتی هر قلم درline_itemکالا (Product) یا خدمت (Service) باشد که با فیلدkindتفکیک میشوند. حالت Strict در اینجا قواعد حساسی دارد؛ راهنمای structured outputs در مستندات OpenAI را ببینید. -
همان اسکیمای Invoice را در قالب یک
BaseModelدر Pydantic بنویسید و خروجی متدmodel_json_schema()را با اسکیمای دستنویس خود مقایسه کنید. آن یک فیلدی را که Pydantic بهطور پیشفرض تنظیم میکند ولی در نسخهی دستی وجود ندارد پیدا کنید. -
نرخ امتناع را اندازه بگیرید. 10 ورودی آماده کنید که اطلاعاتی برای استخراج نداشته باشند (مثل متن ترانه، اثبات ریاضی یا ایمیل خالی) و آنها را در حالت Strict به یک مدل واقعی بفرستید. تعداد امتناعها را در مقایسه با خروجیهای ساختگی بشمارید. این آمار، دادهی واقعی (Ground truth) شما برای تست تلاشهای مجدد آگاه به امتناع خواهد بود.
-
راهنمای structured outputs در OpenAI را از ابتدا تا انتها بخوانید. آن ساختاری را که در JSON Schema معمولی مجاز است ولی در حالت Strict صریحاً ممنوع شده پیدا کنید. سپس اسکیمایی بسازید که از این ساختار به شکلی غیرضروری استفاده کرده باشد و آن را طوری بازنویسی کنید که با حالت Strict سازگار شود.
اصطلاحات کلیدی
| اصطلاح | اصطلاح عامیانه | معنای دقیق فنی |
|---|---|---|
| JSON Schema 2020-12 | «مشخصات اسکیما» | لهجهای از اسکیمای پیشنویس IETF که تمام ارائهدهندگان مدرن از آن پشتیبانی میکنند |
| Strict mode | «اسکیمای تضمینشده» | فلگی در OpenAI که اسکیما را از طریق دیکود مقید الزامی میکند |
| دیکود مقید (Constrained decoding) | «ماسک کردن لاجیت» | اعمال محدودیت در زمان رمزگشایی که جلوی توکنهای بعدی نامعتبر را میگیرد |
| امتناع (Refusal) | «پاسخ ندادن مدل» | نتیجهی تایپشده برای شرایطی که متن ورودی با اسکیما جور درنمیآید |
| خطای تجزیه (Parse error) | «JSON نامعتبر» | متن خروجی به صورت JSON پارس نمیشود؛ در حالت Strict ناممکن است |
| نقض اسکیما (Schema violation) | «ساختار اشتباه» | خروجی پارس میشود اما تایپها، فیلدهای اجباری، مقادیر enum یا بازهها نقض شدهاند |
additionalProperties: false |
«فیلد اضافه ممنوع» | فیلدهای ناشناخته را ممنوع میکند؛ در حالت Strict پلتفرم OpenAI الزامی است |
| Pydantic BaseModel | «خروجی تایپشده» | کلاس پایتون که JSON Schema را تولید و خروجی را با آن اعتبارسنجی میکند |
| Zod schema | «تایپ خروجی در TypeScript» | اسکیمای زمان اجرا در TS برای اعتبارسنجی خروجی ارائهدهنده |
| اعمال گرامر (Grammar enforcement) | «دیکود مقید در مدلهای بازمتن» | ماسک کردن لاجیتها بر پایهی FSM، مانند کتابخانههای outlines یا guidance |
برای مطالعهی بیشتر
- OpenAI — خروجیهای ساختاریافته (Structured outputs) — حالت Strict، موارد امتناع و الزامات اسکیما
- OpenAI — معرفی خروجیهای ساختاریافته در API — پست انتشار در اوت 2024 در توضیح سازوکار تضمین در رمزگشایی
- Pydantic AI — خروجی (Output) — نگاشتهای تایپشدهی
output_typeو سریالسازی روی ارائهدهندههای مختلف - JSON Schema — یادداشتهای انتشار نسخهی 2020-12 — مستندات مرجع اسکیما
- مایکروسافت — خروجیهای ساختاریافته در Azure OpenAI — نکات پیادهسازی سازمانی و هشدارهای حالت Strict
