Structured Outputs چیست؟ آموزش دریافت خروجی JSON از مدلهای هوش مصنوعی با API درواره
Structured Outputs قابلیتی برای دریافت خروجی JSON استاندارد و منطبق با Schema از مدلهای هوش مصنوعی است. در این راهنمای فنی، تفاوت آن با JSON Mode و Tool Calling را بررسی میکنیم و پیادهسازی آن با Python، JavaScript، Pydantic، Zod و API درواره را آموزش میدهیم.
مقدمه
وقتی با یک مدل هوش مصنوعی گفتوگو میکنیم، دریافت یک پاسخ متنی روان و طبیعی معمولاً همان چیزی است که میخواهیم. اما در یک نرمافزار واقعی، پاسخ زیبا و طبیعی همیشه کافی نیست.
فرض کنید میخواهید از مدل هوش مصنوعی برای استخراج اطلاعات یک فاکتور استفاده کنید. نرمافزار شما انتظار دارد نتیجهای شبیه این دریافت کند:
{
"invoice_number": "INV-2048",
"seller": "شرکت نمونه",
"total_amount": 12500000,
"currency": "IRR",
"payment_status": "unpaid"
}
اما یک مدل زبانی ممکن است چنین پاسخی تولید کند:
اطلاعات فاکتور با موفقیت استخراج شد:
شماره فاکتور: INV-2048
فروشنده: شرکت نمونه
مبلغ کل: ۱۲,۵۰۰,۰۰۰ ریال
وضعیت پرداخت: پرداختنشده
این پاسخ برای انسان قابلفهم است، اما نرمافزار نمیتواند بهسادگی آن را وارد دیتابیس کند، روی مبلغ محاسبات انجام دهد یا براساس وضعیت پرداخت تصمیم بگیرد.
حتی اگر در Prompt از مدل بخواهیم فقط JSON برگرداند، همچنان احتمال بروز مشکلاتی مانند موارد زیر وجود دارد:
- اضافهشدن توضیح قبل یا بعد از JSON
- حذف یکی از فیلدهای ضروری
- تغییر نام فیلدها
- بازگرداندن عدد بهشکل رشته
- تولید مقدار خارج از گزینههای مجاز
- قراردادن JSON داخل Markdown
- ایجاد JSON ناقص یا نامعتبر
- اضافهکردن فیلدهای پیشبینینشده
- استفاده از
nullدر محلی که برنامه انتظار رشته دارد
Structured Outputs برای حل همین مشکل ساخته شده است: تبدیل پاسخ احتمالی و آزاد مدل زبانی به یک قرارداد دادهای مشخص، قابلپردازش و قابلاعتماد.
Structured Outputs چیست؟
Structured Outputs یا «خروجی ساختاریافته» قابلیتی در API مدلهای هوش مصنوعی است که به توسعهدهنده اجازه میدهد ساختار دقیق پاسخ را با یک Schema تعریف کند.
این Schema مشخص میکند:
- پاسخ باید Object باشد یا Array
- چه فیلدهایی در پاسخ وجود داشته باشند
- نوع هر فیلد چیست
- کدام فیلدها الزامی هستند
- چه مقادیری برای یک فیلد مجاز است
- آیا فیلدهای اضافی پذیرفته میشوند
- ساختار Objectها و Arrayهای تودرتو چگونه است
برای مثال، میتوانیم از مدل بخواهیم اطلاعات یک تیکت پشتیبانی را دقیقاً با ساختار زیر برگرداند:
{
"type": "object",
"properties": {
"category": {
"type": "string",
"enum": ["billing", "technical", "account", "other"]
},
"priority": {
"type": "string",
"enum": ["low", "medium", "high", "urgent"]
},
"summary": {
"type": "string"
},
"requires_human": {
"type": "boolean"
}
},
"required": [
"category",
"priority",
"summary",
"requires_human"
],
"additionalProperties": false
}
در این قرارداد:
categoryباید یکی از چهار مقدار مشخصشده باشد.priorityفقط میتواند یکی از مقادیر مجاز باشد.summaryباید رشته باشد.requires_humanباید مقدار Boolean داشته باشد.- تمام فیلدها الزامی هستند.
- مدل اجازه ندارد فیلد دیگری اضافه کند.
براساس مستندات رسمی OpenAI، Structured Outputs برای منطبقکردن پاسخ مدل با JSON Schema طراحی شده است و میتواند مشکلاتی مانند حذف کلیدهای ضروری یا تولید مقدار نامعتبر برای enum را کاهش دهد. این قابلیت همچنین امکان تشخیص برنامهنویسیشده امتناع مدل از پاسخگویی را فراهم میکند. مستندات Structured Outputs در OpenAI
چرا خروجی متنی برای نرمافزار کافی نیست؟
مدلهای زبانی در اصل برای پیشبینی و تولید متن ساخته شدهاند. آنها بهطور طبیعی پاسخهایی مناسب انسان تولید میکنند، نه الزاماً دادههایی مناسب ماشین.
برای مثال، اگر از مدل بخواهیم احساس مشتری را تشخیص دهد، ممکن است در اجراهای مختلف پاسخهای زیر را دریافت کنیم:
مثبت
احساس مشتری مثبت است.
{
"sentiment": "positive"
}
نتیجه تحلیل: Positive
```json
{"sentiment":"positive"}
تمام این پاسخها از نظر معنایی یکساناند، اما از دید نرمافزار ساختار یکسانی ندارند.
اگر نتیجه قرار است فقط روی صفحه نمایش داده شود، این تفاوت ممکن است مهم نباشد. اما اگر پاسخ قرار است:
- در دیتابیس ذخیره شود؛
- به API دیگری ارسال شود؛
- بخشی از Workflow باشد؛
- در محاسبات استفاده شود؛
- وضعیت یک سفارش را تغییر دهد؛
- یک تیکت را به تیم مشخصی ارجاع دهد؛
- ورودی مرحله بعدی یک Agent باشد؛
- یا به یک ابزار و تابع ارسال شود؛
ساختار پاسخ باید قابل پیشبینی باشد.
Structured Outputs فاصله میان «متن تولیدشده توسط مدل» و «داده قابلاستفاده توسط نرمافزار» را کمتر میکند.
---
## JSON Schema چیست؟
JSON Schema استانداردی برای توصیف ساختار و محدودیتهای یک سند JSON است.
با JSON Schema میتوان تعیین کرد که یک داده JSON چه شکلی داشته باشد و برای هر قسمت آن چه قواعدی اعمال شود.
یک Schema ساده برای اطلاعات کاربر میتواند چنین باشد:
```json
{
"type": "object",
"properties": {
"name": {
"type": "string"
},
"age": {
"type": "integer"
},
"is_active": {
"type": "boolean"
}
},
"required": ["name", "age", "is_active"],
"additionalProperties": false
}
کلمات کلیدی مهم JSON Schema
type
نوع داده را مشخص میکند:
{
"type": "string"
}
انواع متداول عبارتاند از:
stringnumberintegerbooleanobjectarraynull
properties
فیلدهای مجاز یک Object را تعریف میکند:
{
"type": "object",
"properties": {
"title": {
"type": "string"
},
"price": {
"type": "number"
}
}
}
required
مشخص میکند کدام فیلدها باید حتماً در پاسخ حضور داشته باشند:
{
"required": ["title", "price"]
}
تعریف یک فیلد در properties بهتنهایی به این معنا نیست که آن فیلد الزامی است. برای الزامیکردن آن باید نام فیلد در required نیز قرار گیرد.
enum
مقادیر مجاز را محدود میکند:
{
"type": "string",
"enum": ["pending", "paid", "failed"]
}
این ویژگی برای وضعیت سفارش، اولویت تیکت، نوع سند، زبان، دستهبندی و موارد مشابه بسیار مفید است.
items
ساختار اعضای یک Array را تعیین میکند:
{
"type": "array",
"items": {
"type": "string"
}
}
یا برای Array شامل Object:
{
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"quantity": {
"type": "integer"
}
},
"required": ["name", "quantity"],
"additionalProperties": false
}
}
additionalProperties
مشخص میکند آیا فیلدهایی خارج از properties مجاز هستند یا خیر:
{
"additionalProperties": false
}
در استاندارد JSON Schema، فیلدهای اضافی بهصورت پیشفرض مجازند. قراردادن additionalProperties: false از حضور کلیدهای تعریفنشده جلوگیری میکند. راهنمای رسمی JSON Schema
description
هدف فیلد را برای مدل و توسعهدهنده توضیح میدهد:
{
"type": "number",
"description": "مبلغ نهایی فاکتور به ریال و بدون جداکننده"
}
توضیح دقیق میتواند کیفیت استخراج اطلاعات را بهتر کند، مخصوصاً زمانی که نام فیلد بهتنهایی مبهم است.
تفاوت Prompt معمولی، JSON Mode و Structured Outputs
این سه روش یکسان نیستند.
۱. درخواست JSON در Prompt
سادهترین روش این است که در متن Prompt بنویسیم:
پاسخ را فقط بهصورت JSON برگردان.
این روش هیچ تضمین فنی ایجاد نمیکند. مدل ممکن است:
- متن توضیحی اضافه کند؛
- JSON را داخل Markdown قرار دهد؛
- یکی از فیلدها را حذف کند؛
- نام فیلد را تغییر دهد؛
- یا خروجی نامعتبر تولید کند.
این روش برای نمونهسازی اولیه قابلاستفاده است، اما برای سیستمهای Production کافی نیست.
۲. JSON Mode
در JSON Mode از API میخواهیم پاسخ مدل یک JSON معتبر باشد.
در رابطهای OpenAI-compatible معمولاً ساختار کلی آن چنین است:
{
"response_format": {
"type": "json_object"
}
}
JSON Mode معمولاً تضمین میکند خروجی از نظر Syntax یک JSON معتبر باشد، اما الزاماً تضمین نمیکند JSON دقیقاً با ساختار مورد انتظار شما منطبق باشد.
برای مثال، شما انتظار دارید:
{
"name": "علی",
"age": 32
}
اما مدل ممکن است این JSON معتبر را برگرداند:
{
"full_name": "علی",
"age": "32",
"description": "کاربر جدید"
}
خروجی JSON معتبر است، اما:
nameبهfull_nameتغییر کرده است.ageبهجای عدد، رشته است.- یک فیلد اضافی ایجاد شده است.
۳. Structured Outputs
در Structured Outputs علاوه بر JSON معتبر، Schema مورد انتظار نیز تعریف میشود:
{
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "user_profile",
"strict": true,
"schema": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"age": {
"type": "integer"
}
},
"required": ["name", "age"],
"additionalProperties": false
}
}
}
}
در این حالت هدف این است که خروجی هم JSON معتبر باشد و هم با Schema تعیینشده تطابق داشته باشد.
مقایسه سه روش
| روش | JSON معتبر | تطابق با Schema | مناسب Production |
|---|---|---|---|
| درخواست JSON در Prompt | تضمینشده نیست | خیر | خیر |
| JSON Mode | بله | خیر | برای ساختارهای ساده |
| Structured Outputs | بله | بله، در مدل پشتیبانیشده | بله |
| Tool Calling با حالت Strict | آرگومان ساختاریافته | مطابق Schema ابزار | بله، برای اجرای عملیات |
تفاوت Structured Outputs و Tool Calling
Structured Outputs و Tool Calling هر دو از Schema استفاده میکنند، اما هدف متفاوتی دارند.
Structured Outputs
از Structured Outputs زمانی استفاده کنید که هدف، دریافت یک پاسخ ساختاریافته از مدل است.
مثالها:
- استخراج اطلاعات فاکتور
- تحلیل رزومه
- دستهبندی تیکت
- ساخت اطلاعات محصول
- تولید خروجی برای رابط کاربری
- تحلیل احساس مشتری
- تبدیل متن به رکورد دیتابیس
Tool Calling
از Tool Calling زمانی استفاده کنید که مدل باید تشخیص دهد یک تابع یا ابزار فراخوانی شود.
مثالها:
- جستوجوی سفارش
- دریافت موجودی محصول
- ثبت تیکت
- ارسال درخواست بازپرداخت
- رزرو جلسه
- استعلام آبوهوا
- اجرای Query روی سیستم داخلی
فرض کنید ابزار زیر را در اختیار مدل قرار میدهیم:
{
"type": "function",
"function": {
"name": "get_order_status",
"description": "دریافت وضعیت سفارش براساس شناسه سفارش",
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string"
}
},
"required": ["order_id"],
"additionalProperties": false
}
}
}
مدل خودش سفارش را جستوجو نمیکند. فقط یک درخواست ساختاریافته برای اجرای تابع تولید میکند:
{
"order_id": "ORD-4521"
}
سپس برنامه شما تابع واقعی را اجرا میکند.
قاعده ساده این است:
- اگر به «داده ساختاریافته» نیاز دارید، از Structured Outputs استفاده کنید.
- اگر مدل باید «عملی را در یک سیستم انجام دهد»، از Tool Calling استفاده کنید.
- اگر هم تصمیمگیری و هم پاسخ ساختاریافته لازم است، میتوان هر دو را در معماری Workflow ترکیب کرد.
پیشنیاز اتصال به API درواره
درواره یک API سازگار با OpenAI ارائه میدهد. بنابراین در بسیاری از ابزارها و SDKهای OpenAI-compatible کافی است base_url را به آدرس زیر تغییر دهید:
https://api.darvareh.ir/v1
همچنین باید کلید API درواره را در اختیار داشته باشید.
پشتیبانی از Structured Outputs به قابلیت مدل و ارائهدهنده بالادستی وابسته است. همه مدلها الزاماًjson_schemaیا حالتstrictرا پشتیبانی نمیکنند. پیش از استفاده در محیط Production، قابلیت مدل انتخابی را بررسی و خروجی آن را آزمایش کنید.
در مثالها بهجای نام یک مدل خاص از مقدار زیر استفاده میکنیم:
MODEL_ID
آن را با شناسه یکی از مدلهای سازگار در فهرست مدلهای درواره جایگزین کنید.
آموزش Structured Outputs با Python
نصب SDK
ابتدا SDK پایتون OpenAI را نصب یا بهروزرسانی کنید:
pip install -U openai
بهتر است کلید API را در متغیر محیطی نگه دارید:
export DARVAREH_API_KEY="YOUR_DARVAREH_API_KEY"
در Windows PowerShell:
$env:DARVAREH_API_KEY="YOUR_DARVAREH_API_KEY"
نمونه پایه
import os
import json
from openai import OpenAI
client = OpenAI(
api_key=os.environ["DARVAREH_API_KEY"],
base_url="https://api.darvareh.ir/v1",
)
response = client.chat.completions.create(
model="MODEL_ID",
messages=[
{
"role": "system",
"content": (
"You extract structured customer-support data. "
"Do not invent information that is not present."
),
},
{
"role": "user",
"content": (
"سه روز است پرداخت کردهام اما کیف پول حسابم شارژ نشده. "
"لطفاً سریع بررسی کنید."
),
},
],
response_format={
"type": "json_schema",
"json_schema": {
"name": "support_ticket_analysis",
"strict": True,
"schema": {
"type": "object",
"properties": {
"category": {
"type": "string",
"enum": [
"billing",
"technical",
"account",
"other",
],
},
"priority": {
"type": "string",
"enum": [
"low",
"medium",
"high",
"urgent",
],
},
"summary": {
"type": "string",
},
"requires_human": {
"type": "boolean",
},
},
"required": [
"category",
"priority",
"summary",
"requires_human",
],
"additionalProperties": False,
},
},
},
)
content = response.choices[0].message.content
data = json.loads(content)
print(data)
print(data["category"])
print(data["requires_human"])
خروجی احتمالی:
{
"category": "billing",
"priority": "high",
"summary": "پرداخت انجام شده اما کیف پول پس از سه روز شارژ نشده است.",
"requires_human": true
}
چرا باز هم json.loads لازم است؟
در Chat Completions، محتوای پاسخ معمولاً بهشکل رشته JSON در message.content قرار میگیرد. برای تبدیل آن به Dictionary پایتون باید از json.loads استفاده کنید:
data = json.loads(response.choices[0].message.content)
منطبقبودن خروجی با Schema به این معنی نیست که نوع خروجی SDK همیشه خودکار به Object پایتون تبدیل میشود. این رفتار به endpoint و قابلیتهای نسخه SDK بستگی دارد.
اعتبارسنجی خروجی با Pydantic
حتی با Structured Outputs بهتر است در مرز ورود داده به برنامه، اعتبارسنجی سمت سرور داشته باشید.
دلایل آن عبارتاند از:
- ممکن است مدل انتخابی Structured Outputs را کامل پشتیبانی نکند.
- ممکن است ارائهدهنده پارامتر را نادیده بگیرد یا تغییر دهد.
- ممکن است درخواست ناقص شود.
- ممکن است پاسخ Safety Refusal باشد.
- ممکن است در آینده Schema یا مدل تغییر کند.
- هیچ داده خارجی نباید بدون Validation وارد بخش حساس برنامه شود.
نصب Pydantic
pip install -U pydantic
تعریف مدل داده
from typing import Literal
from pydantic import BaseModel, ConfigDict
class SupportTicketAnalysis(BaseModel):
model_config = ConfigDict(extra="forbid")
category: Literal["billing", "technical", "account", "other"]
priority: Literal["low", "medium", "high", "urgent"]
summary: str
requires_human: bool
گزینه extra="forbid" باعث میشود فیلدهای اضافی پذیرفته نشوند.
اعتبارسنجی پاسخ
import os
from openai import OpenAI
from pydantic import ValidationError
client = OpenAI(
api_key=os.environ["DARVAREH_API_KEY"],
base_url="https://api.darvareh.ir/v1",
)
response = client.chat.completions.create(
model="MODEL_ID",
messages=[
{
"role": "system",
"content": "درخواست پشتیبانی را دقیق و بدون حدس تحلیل کن.",
},
{
"role": "user",
"content": "API من از صبح خطای 429 میدهد.",
},
],
response_format={
"type": "json_schema",
"json_schema": {
"name": "support_ticket_analysis",
"strict": True,
"schema": SupportTicketAnalysis.model_json_schema(),
},
},
)
raw_content = response.choices[0].message.content
try:
ticket = SupportTicketAnalysis.model_validate_json(raw_content)
print(ticket.category)
print(ticket.priority)
print(ticket.summary)
except ValidationError as error:
print("Invalid model output:", error)
Pydantic میتواند Schema را از Type Hintهای پایتون تولید و پاسخ را نیز با همان مدل اعتبارسنجی کند. این کار از ایجاد دو تعریف جداگانه و ناسازگار جلوگیری میکند.
نکته درباره Schema تولیدشده
تمام ویژگیهای JSON Schema الزاماً توسط تمام مدلها و ارائهدهندگان پشتیبانی نمیشوند. بنابراین Schema تولیدشده توسط Pydantic را بررسی کنید و در صورت نیاز آن را سادهتر کنید.
برای محیطهای چندارائهدهندهای بهتر است از زیرمجموعهای رایج و ساده استفاده کنید:
typepropertiesrequiredadditionalPropertiesenumitemsdescription- ساختارهای ساده تودرتو
آموزش Structured Outputs با JavaScript و TypeScript
نصب SDK و Zod
npm install openai zod
متغیر محیطی:
export DARVAREH_API_KEY="YOUR_DARVAREH_API_KEY"
نمونه مستقیم با JSON Schema
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.DARVAREH_API_KEY,
baseURL: "https://api.darvareh.ir/v1",
});
const completion = await client.chat.completions.create({
model: "MODEL_ID",
messages: [
{
role: "system",
content:
"You extract product information. Return only fields supported by the input.",
},
{
role: "user",
content:
"هدفون بیسیم مدل X، رنگ مشکی، دارای حذف نویز فعال و ۳۰ ساعت شارژدهی، قیمت ۴۵۰۰۰۰۰ تومان",
},
],
response_format: {
type: "json_schema",
json_schema: {
name: "product_information",
strict: true,
schema: {
type: "object",
properties: {
name: {
type: "string",
},
color: {
type: ["string", "null"],
},
price_toman: {
type: ["number", "null"],
},
features: {
type: "array",
items: {
type: "string",
},
},
},
required: ["name", "color", "price_toman", "features"],
additionalProperties: false,
},
},
},
});
const rawContent = completion.choices[0].message.content;
const product = JSON.parse(rawContent);
console.log(product);
خروجی احتمالی:
{
"name": "هدفون بیسیم مدل X",
"color": "مشکی",
"price_toman": 4500000,
"features": [
"حذف نویز فعال",
"۳۰ ساعت شارژدهی"
]
}
اعتبارسنجی خروجی با Zod
Zod یک کتابخانه TypeScript-first برای تعریف و اعتبارسنجی Schema است. داده ورودی با parse یا safeParse بررسی میشود و در صورت اعتبار، نوع TypeScript نیز از Schema قابل استنتاج است. مستندات Zod
import OpenAI from "openai";
import { z } from "zod";
const ProductSchema = z
.object({
name: z.string(),
color: z.string().nullable(),
price_toman: z.number().nullable(),
features: z.array(z.string()),
})
.strict();
type Product = z.infer<typeof ProductSchema>;
const client = new OpenAI({
apiKey: process.env.DARVAREH_API_KEY,
baseURL: "https://api.darvareh.ir/v1",
});
const jsonSchema = z.toJSONSchema(ProductSchema);
const completion = await client.chat.completions.create({
model: "MODEL_ID",
messages: [
{
role: "system",
content:
"Extract product information. Do not guess missing values; use null.",
},
{
role: "user",
content:
"مانیتور ۲۷ اینچی مدل A با پنل IPS و نرخ نوسازی ۱۴۴ هرتز",
},
],
response_format: {
type: "json_schema",
json_schema: {
name: "product_information",
strict: true,
schema: jsonSchema,
},
},
});
const rawContent = completion.choices[0].message.content;
if (!rawContent) {
throw new Error("The model returned an empty response.");
}
const untrustedData: unknown = JSON.parse(rawContent);
const result = ProductSchema.safeParse(untrustedData);
if (!result.success) {
console.error(result.error.flatten());
throw new Error("The model output did not match ProductSchema.");
}
const product: Product = result.data;
console.log(product.name);
console.log(product.features);
Zod 4 تبدیل مستقیم Schema به JSON Schema را با z.toJSONSchema() ارائه میکند. بعضی نوعهای JavaScript و Zod مانند Date، Map، Set، BigInt و Transformهای خاص معادل مستقیم و قابلحملی در JSON Schema ندارند؛ در چنین مواردی بهتر است از نمایشهای ساده JSON مانند رشته ISO برای تاریخ استفاده شود. تبدیل JSON Schema در Zod
پروژه عملی اول: استخراج اطلاعات فاکتور
یکی از بهترین کاربردهای Structured Outputs، تبدیل متن فاکتور یا نتیجه OCR به داده ساختاریافته است.
Schema فاکتور
{
"type": "object",
"properties": {
"invoice_number": {
"type": ["string", "null"],
"description": "شماره فاکتور؛ اگر وجود ندارد null"
},
"issue_date": {
"type": ["string", "null"],
"description": "تاریخ دقیقاً مطابق سند"
},
"seller": {
"type": ["string", "null"]
},
"buyer": {
"type": ["string", "null"]
},
"currency": {
"type": "string",
"enum": ["IRR", "TOMAN", "USD", "EUR", "UNKNOWN"]
},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"quantity": {
"type": ["number", "null"]
},
"unit_price": {
"type": ["number", "null"]
},
"total_price": {
"type": ["number", "null"]
}
},
"required": [
"description",
"quantity",
"unit_price",
"total_price"
],
"additionalProperties": false
}
},
"subtotal": {
"type": ["number", "null"]
},
"tax": {
"type": ["number", "null"]
},
"total": {
"type": ["number", "null"]
},
"warnings": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"invoice_number",
"issue_date",
"seller",
"buyer",
"currency",
"items",
"subtotal",
"tax",
"total",
"warnings"
],
"additionalProperties": false
}
Prompt مناسب استخراج
اطلاعات را فقط از متن سند استخراج کن.
قواعد:
1. هیچ مقدار گمشدهای را حدس نزن.
2. برای مقدار اسکالر ناموجود از null استفاده کن.
3. اعداد را بدون جداکننده هزارگان برگردان.
4. ریال و تومان را با یکدیگر تبدیل نکن.
5. اگر واحد پول نامشخص است، UNKNOWN برگردان.
6. اگر جمع اقلام با مبلغ کل سازگار نیست، یک هشدار اضافه کن.
7. متن فارسی را به زبان دیگری ترجمه نکن.
Structured Outputs ساختار را کنترل میکند، اما صحت معنایی داده همچنان به مدل، کیفیت سند و Prompt وابسته است.
برای مثال، Schema میتواند تضمین کند total عدد یا null باشد، اما نمیتواند بهتنهایی تضمین کند عدد استخراجشده واقعاً مبلغ کل صحیح فاکتور است.
به همین دلیل باید کنترلهای قطعی برنامهنویسی نیز اجرا شوند:
calculated_total = sum(
item["total_price"] or 0
for item in invoice["items"]
)
if invoice["subtotal"] is not None:
difference = abs(calculated_total - invoice["subtotal"])
if difference > 1:
invoice["warnings"].append(
"Sum of items does not match subtotal."
)
پروژه عملی دوم: دستهبندی تیکت پشتیبانی
فرض کنید متن زیر از کاربر دریافت شده است:
بعد از پرداخت، پول از حسابم کم شد ولی کیف پول شارژ نشده و شماره پیگیری هم دارم.
Schema:
{
"type": "object",
"properties": {
"department": {
"type": "string",
"enum": [
"finance",
"technical",
"sales",
"account",
"general"
]
},
"priority": {
"type": "string",
"enum": [
"low",
"normal",
"high",
"critical"
]
},
"sentiment": {
"type": "string",
"enum": [
"positive",
"neutral",
"negative"
]
},
"summary": {
"type": "string"
},
"suggested_tags": {
"type": "array",
"items": {
"type": "string"
}
},
"auto_reply_allowed": {
"type": "boolean"
}
},
"required": [
"department",
"priority",
"sentiment",
"summary",
"suggested_tags",
"auto_reply_allowed"
],
"additionalProperties": false
}
خروجی:
{
"department": "finance",
"priority": "high",
"sentiment": "negative",
"summary": "کسر مبلغ بدون شارژشدن کیف پول پس از پرداخت",
"suggested_tags": [
"payment",
"wallet",
"missing-credit"
],
"auto_reply_allowed": false
}
برنامه میتواند براساس این داده:
- تیکت را به واحد مالی ارسال کند؛
- اولویت را بالا قرار دهد؛
- پاسخ خودکار را متوقف کند؛
- شماره پیگیری را درخواست کند؛
- و رویداد را در سیستم مانیتورینگ ثبت کند.
اما تصمیمهای حساس مانند بازپرداخت یا افزایش اعتبار نباید صرفاً براساس خروجی مدل انجام شوند.
پروژه عملی سوم: تحلیل رزومه
Structured Outputs برای تبدیل رزومههای غیرساختاریافته به اطلاعات قابلجستوجو نیز مفید است.
{
"type": "object",
"properties": {
"full_name": {
"type": ["string", "null"]
},
"email": {
"type": ["string", "null"]
},
"phone": {
"type": ["string", "null"]
},
"skills": {
"type": "array",
"items": {
"type": "string"
}
},
"years_of_experience": {
"type": ["number", "null"]
},
"languages": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"level": {
"type": "string",
"enum": [
"unknown",
"basic",
"intermediate",
"advanced",
"native"
]
}
},
"required": ["name", "level"],
"additionalProperties": false
}
},
"education": {
"type": "array",
"items": {
"type": "object",
"properties": {
"degree": {
"type": ["string", "null"]
},
"field": {
"type": ["string", "null"]
},
"institution": {
"type": ["string", "null"]
}
},
"required": [
"degree",
"field",
"institution"
],
"additionalProperties": false
}
}
},
"required": [
"full_name",
"email",
"phone",
"skills",
"years_of_experience",
"languages",
"education"
],
"additionalProperties": false
}
نکته مهم این است که مدل نباید برای اطلاعات ناموجود حدس بزند. برای نمونه، اگر سال شروع و پایان مشاغل در رزومه مشخص نیست، بهتر است years_of_experience برابر null باشد.
همچنین نباید از خروجی مدل برای تصمیمگیری خودکار و نهایی درباره استخدام استفاده کرد. مدل میتواند در استخراج و سازماندهی داده کمک کند، اما تصمیم انسانی و بررسی سوگیری همچنان ضروری است.
طراحی فیلدهای اختیاری
یکی از چالشهای Structured Outputs، نمایش اطلاعات اختیاری است.
اگر یک فیلد ممکن است در ورودی وجود نداشته باشد، دو راه رایج داریم.
حذف فیلد از required
{
"type": "object",
"properties": {
"phone": {
"type": "string"
}
}
}
الزامیبودن فیلد با امکان null
{
"type": "object",
"properties": {
"phone": {
"type": ["string", "null"]
}
},
"required": ["phone"],
"additionalProperties": false
}
برای خروجی مدلهای هوش مصنوعی، روش دوم معمولاً قابلپیشبینیتر است. در این حالت کلید همیشه وجود دارد، اما مقدار آن ممکن است null باشد.
{
"phone": null
}
این روش پردازش سمت برنامه را سادهتر میکند، زیرا لازم نیست در هر مرحله حضور یا عدم حضور کلید بررسی شود.
مدیریت Refusal، پاسخ ناقص و خطا
Structured Outputs به این معنا نیست که هر درخواست حتماً به یک Object معتبر منتهی میشود.
ممکن است:
- مدل بهدلایل ایمنی از پاسخ خودداری کند؛
- پاسخ به سقف Token برسد؛
- درخواست Timeout شود؛
- ارائهدهنده خطا برگرداند؛
- Schema پشتیبانی نشود؛
- پاسخ خالی باشد؛
- مدل یا endpoint انتخابی پارامتر را نپذیرد.
الگوی مناسب مدیریت خطا در Python
import json
from openai import (
APIConnectionError,
APIStatusError,
APITimeoutError,
RateLimitError,
)
from pydantic import ValidationError
def parse_structured_response(response, model_class):
if not response.choices:
raise ValueError("No choices returned by the model.")
choice = response.choices[0]
message = choice.message
refusal = getattr(message, "refusal", None)
if refusal:
return {
"status": "refused",
"reason": refusal,
"data": None,
}
if choice.finish_reason == "length":
return {
"status": "incomplete",
"reason": "Maximum output length reached.",
"data": None,
}
if not message.content:
return {
"status": "empty",
"reason": "The model returned no content.",
"data": None,
}
try:
validated = model_class.model_validate_json(
message.content
)
return {
"status": "success",
"reason": None,
"data": validated,
}
except (ValidationError, json.JSONDecodeError) as error:
return {
"status": "invalid",
"reason": str(error),
"data": None,
}
try:
response = client.chat.completions.create(
# request parameters
)
result = parse_structured_response(
response,
SupportTicketAnalysis,
)
except RateLimitError:
result = {
"status": "retryable_error",
"reason": "Rate limit exceeded.",
"data": None,
}
except APITimeoutError:
result = {
"status": "retryable_error",
"reason": "Request timed out.",
"data": None,
}
except APIConnectionError:
result = {
"status": "retryable_error",
"reason": "Could not connect to the API.",
"data": None,
}
except APIStatusError as error:
result = {
"status": "api_error",
"reason": f"HTTP {error.status_code}",
"data": None,
}
Retry هوشمند
Retry باید فقط برای خطاهای موقت انجام شود:
- Timeout
- خطای اتصال
429 Too Many Requests- برخی خطاهای
5xx
برای خطاهای زیر Retry بدون تغییر درخواست معمولاً فایدهای ندارد:
- API Key نامعتبر
- Schema نامعتبر
- مدل ناسازگار
- پارامتر پشتیبانینشده
- خطای Validation تکرارشونده
- درخواست غیرمجاز
از Exponential Backoff استفاده کنید:
import random
import time
def retry_delay(attempt):
base = min(2 ** attempt, 30)
return base + random.uniform(0, 1)
آیا با Structured Outputs دیگر Validation لازم نیست؟
خیر.
Structured Outputs احتمال دریافت ساختار نادرست را بسیار کمتر میکند، اما Validation سمت برنامه همچنان باید حفظ شود.
مدل هوش مصنوعی و API یک ورودی خارجی محسوب میشوند. همانطور که داده فرم کاربر یا پاسخ یک سرویس خارجی را بدون بررسی وارد دیتابیس نمیکنیم، خروجی مدل نیز باید اعتبارسنجی شود.
Validation میتواند در چند سطح انجام شود:
۱. اعتبار ساختاری
آیا پاسخ JSON معتبر و مطابق Schema است؟
ابزارهای مناسب:
- Pydantic در Python
- Zod در TypeScript
- Ajv برای JSON Schema
- JSON Schema Validatorهای زبانهای دیگر
۲. اعتبار معنایی
آیا مقادیر از نظر کسبوکار منطقیاند؟
مثال:
if invoice.total is not None and invoice.total < 0:
raise ValueError("Invoice total cannot be negative.")
۳. اعتبار رابطهای
آیا فیلدها با یکدیگر سازگارند؟
if order.payment_status == "paid" and order.paid_at is None:
raise ValueError("paid_at is required for paid orders.")
۴. اعتبار خارجی
آیا داده با سیستم واقعی مطابقت دارد؟
مثلاً مدل ممکن است یک customer_id معتبر از نظر ساختاری تولید کند، اما برنامه باید بررسی کند چنین مشتریای واقعاً در دیتابیس وجود دارد.
Structured Outputs مانع Hallucination نمیشود
یکی از مهمترین نکات این است که «ساختار معتبر» با «محتوای صحیح» یکسان نیست.
این خروجی ممکن است کاملاً با Schema منطبق باشد:
{
"invoice_number": "INV-9999",
"total": 25000000,
"currency": "IRR"
}
اما اگر شماره فاکتور یا مبلغ در سند اصلی وجود نداشته باشد، مدل اطلاعات را ساخته است.
Structured Outputs تضمین میکند ظرف داده شکل صحیحی دارد؛ الزاماً تضمین نمیکند محتوای داخل ظرف واقعی است.
برای کاهش Hallucination:
- صریحاً بگویید اطلاعات ناموجود را حدس نزند.
- برای فیلدهای ناموجود امکان
nullتعریف کنید. - متن یا سند مرجع را در Context قرار دهید.
- از مدل بخواهید شواهد هر مقدار را نیز برگرداند.
- برای اطلاعات حساس، بررسی انسانی داشته باشید.
- قواعد قطعی کسبوکار را در کد اجرا کنید.
- خروجی را با دیتابیس و منابع معتبر تطبیق دهید.
اضافهکردن شواهد به Schema
{
"type": "object",
"properties": {
"value": {
"type": ["string", "null"]
},
"evidence": {
"type": ["string", "null"],
"description": "عبارت کوتاه موجود در سند که مقدار از آن استخراج شده است"
},
"confidence": {
"type": "string",
"enum": ["low", "medium", "high"]
}
},
"required": ["value", "evidence", "confidence"],
"additionalProperties": false
}
البته confidence تولیدشده توسط خود مدل احتمال آماری کالیبرهشده نیست. از آن فقط بهعنوان سیگنال کمکی استفاده کنید.
چرا نباید JSON را با Regex استخراج کنیم؟
در پروژههای اولیه گاهی چنین کدی دیده میشود:
import re
match = re.search(r"\{.*\}", model_output, re.DOTALL)
این روش شکننده است، زیرا JSON ممکن است شامل موارد زیر باشد:
- Objectهای تودرتو
- Array
- آکولاد داخل رشته
- چند Object جداگانه
- Markdown
- Escape Character
- پاسخ ناقص
Regex ابزار مناسبی برای Parseکردن ساختار کامل JSON نیست.
ترتیب مناسب این است:
- از Structured Outputs استفاده کنید.
- محتوای پاسخ را با JSON Parser استاندارد Parse کنید.
- نتیجه را با Pydantic، Zod یا JSON Schema Validator اعتبارسنجی کنید.
- در صورت شکست، خطا را مدیریت یا درخواست کنترلشدهای را Retry کنید.
اصول طراحی Schema مناسب برای مدلهای هوش مصنوعی
Schema را تا حد ممکن ساده نگه دارید
Schema بسیار پیچیده احتمال ناسازگاری میان مدلها و ارائهدهندگان را افزایش میدهد.
بهجای ساختارهای بیشازحد انتزاعی، از Objectها و Arrayهای واضح استفاده کنید.
نام فیلدها واضح باشد
نام ضعیف:
{
"v": 1200,
"s": "p"
}
نام بهتر:
{
"total_amount": 1200,
"payment_status": "paid"
}
از description استفاده کنید
{
"type": "number",
"description": "مبلغ نهایی به ریال؛ بدون علامت واحد پول و جداکننده هزارگان"
}
گزینههای محدود را با enum تعریف کنید
نامناسب:
{
"priority": {
"type": "string"
}
}
مناسب:
{
"priority": {
"type": "string",
"enum": ["low", "medium", "high", "critical"]
}
}
مقدار ناموجود را مشخص کنید
در Prompt و Schema روشن کنید که مدل برای اطلاعات ناموجود چه کاری انجام دهد:
null- رشته خالی
- Array خالی
- مقدار
unknown
در بیشتر موارد:
- برای مقدار اسکالر ناموجود از
nullاستفاده کنید. - برای فهرست بدون عضو از
[]استفاده کنید. - برای وضعیت دستهبندینشده از
unknownاستفاده کنید.
فیلدهای اضافی را ببندید
{
"additionalProperties": false
}
واحد اندازهگیری را در نام یا توضیح مشخص کنید
مبهم:
{
"price": 250000
}
شفافتر:
{
"price_irr": 250000
}
یا:
{
"price": 250000,
"currency": "IRR"
}
تاریخ را استاندارد کنید
برای پردازش نرمافزاری بهتر است تاریخ میلادی را در قالب ISO 8601 دریافت کنید:
{
"created_at": "2026-07-11T10:30:00Z"
}
اگر سند دارای تاریخ شمسی است و تبدیل دقیق اهمیت دارد، بهتر است هم مقدار خام و هم مقدار نرمالشده را نگه دارید:
{
"raw_date": "۱۴۰۵/۰۴/۲۰",
"normalized_date": "2026-07-11"
}
تبدیل تاریخ را در صورت امکان با کتابخانه قطعی برنامهنویسی انجام دهید، نه صرفاً با مدل.
Versioning برای Schema
Schema بخشی از قرارداد API داخلی شماست و باید نسخهبندی شود.
برای مثال:
{
"schema_version": "1.0",
"data": {
"category": "billing",
"priority": "high"
}
}
اگر بعداً فیلدهای جدیدی اضافه شوند، مصرفکنندگان قدیمی ممکن است دچار مشکل شوند. راهکارهای مناسب:
- نگهداری نسخه Schema
- ثبت Migration
- تست Consumerها
- جلوگیری از تغییر ناگهانی نام و نوع فیلدها
- استفاده از Feature Flag
- اجرای موازی نسخه جدید پیش از جایگزینی کامل
Schema را مانند یک Interface نرمافزاری مدیریت کنید، نه بخشی موقت از Prompt.
تست Structured Outputs
یک نمونه موفق برای Production کافی نیست.
مجموعه تست شما باید ورودیهای مختلفی داشته باشد:
- متن کامل و واضح
- متن ناقص
- متن بسیار طولانی
- متن فارسی و انگلیسی ترکیبی
- اعداد فارسی و لاتین
- اطلاعات متناقض
- سند بدون یکی از فیلدهای اصلی
- درخواست نامرتبط
- متن دارای Prompt Injection
- متن دارای کاراکترهای ویژه
- پاسخ احتمالی Safety Refusal
- ورودی خالی
- چند سند در یک پیام
تست قرارداد در Python
from pydantic import ValidationError
def test_output_contract(raw_output: str):
try:
result = SupportTicketAnalysis.model_validate_json(
raw_output
)
except ValidationError as error:
return False, str(error)
return True, result
معیارهایی که باید ثبت شوند
- نرخ خروجی معتبر
- نرخ Refusal
- نرخ پاسخ ناقص
- نرخ Retry
- زمان پاسخ
- تعداد Token ورودی و خروجی
- هزینه هر استخراج
- دقت معنایی روی Dataset ارزیابی
- خطا به تفکیک مدل
- خطا به تفکیک نسخه Schema
داشتن JSON معتبر بهتنهایی معیار موفقیت نیست. برای مثال، ممکن است ۱۰۰٪ پاسخها Schema معتبر داشته باشند اما دقت استخراج مبلغ فقط ۸۰٪ باشد.
امنیت Structured Outputs
خروجی ساختاریافته همچنان داده غیرقابلاعتماد است.
نباید خروجی مدل را مستقیماً در عملیات حساس استفاده کنید:
# ناامن
database.execute(model_output["sql"])
یا:
// ناامن
eval(modelOutput.code);
یا:
# خطرناک
transfer_money(
account=model_output["account"],
amount=model_output["amount"],
)
برای استفاده امن:
- خروجی را Validate کنید.
- مقادیر را با Allowlist محدود کنید.
- مجوز کاربر را مستقل از مدل بررسی کنید.
- Queryهای دیتابیس را Parameterized اجرا کنید.
- ابزارها را با حداقل دسترسی طراحی کنید.
- برای عملیات مالی یا غیرقابلبازگشت تأیید انسانی بگیرید.
- Log و Audit Trail نگه دارید.
- اطلاعات محرمانه غیرضروری را وارد Prompt نکنید.
- محتوای اسناد را داده غیرقابلاعتماد در نظر بگیرید.
- دستور موجود در سند را از دستور سیستم جدا کنید.
Structured Outputs از تغییر شکل پاسخ جلوگیری میکند، اما جلوی Prompt Injection یا تصمیم نادرست مدل را بهتنهایی نمیگیرد.
Structured Outputs در معماری Multi-Provider
هنگامی که از چند مدل یا ارائهدهنده استفاده میکنید، باید تفاوت قابلیتها را در نظر بگیرید.
برخی مدلها ممکن است:
json_schemaرا پشتیبانی کنند؛- فقط
json_objectداشته باشند؛ - Structured Outputs را با پارامتر اختصاصی ارائه دهند؛
- فقط Tool Calling ساختاریافته داشته باشند؛
- تنها زیرمجموعهای از JSON Schema را بپذیرند؛
- یا هیچکدام را بهصورت Native پشتیبانی نکنند.
برای نمونه، رابطهای OpenAI-compatible معمولاً از response_format استفاده میکنند، درحالیکه API بومی Claude برای JSON Output از ساختار اختصاصی output_config.format و برای ابزارها از حالت Strict استفاده میکند. مستندات Structured Outputs در Claude
بنابراین در یک سیستم چندمدلی بهتر است Capability Matrix داشته باشید:
| قابلیت | مدل A | مدل B | مدل C |
|---|---|---|---|
| JSON Mode | بله | بله | بله |
| JSON Schema | بله | خیر | بله |
| Strict Tool Calling | بله | بله | خیر |
| Streaming ساختاریافته | بله | محدود | خیر |
| Schema تودرتو | بله | بله | محدود |
Router باید فقط درخواست Structured Outputs را به مدلی ارسال کند که قابلیت لازم را دارد.
Fallback نیز نباید صرفاً براساس در دسترسبودن مدل انجام شود. مدل جایگزین باید از قرارداد خروجی مورد نیاز پشتیبانی کند.
راهبرد جایگزین برای مدلهای فاقد Structured Outputs
اگر مدل انتخابی json_schema را پشتیبانی نمیکند، میتوان از یک مسیر چندلایه استفاده کرد:
سطح اول: Structured Outputs
بهترین گزینه برای مدلهای دارای پشتیبانی Native.
سطح دوم: JSON Mode بههمراه Validation
{
"response_format": {
"type": "json_object"
}
}
سپس خروجی را با Pydantic یا Zod بررسی کنید.
سطح سوم: Prompt دقیق بههمراه Validation و Retry
در Prompt:
فقط یک JSON معتبر و بدون Markdown برگردان.
تمام فیلدهای مشخصشده باید حضور داشته باشند.
هیچ فیلد اضافی تولید نکن.
برای مقدار ناموجود از null استفاده کن.
سپس:
JSON.parseیاjson.loads- Validation
- در صورت خطا، Retry کنترلشده
- در صورت شکست مجدد، ارجاع به مسیر انسانی یا مدل جایگزین
سطح چهارم: مدل تعمیرکننده خروجی
میتوان خروجی نامعتبر را برای تعمیر به یک مدل دیگر فرستاد، اما این راهکار:
- هزینه را افزایش میدهد؛
- Latency را بیشتر میکند؛
- ممکن است معنای داده را تغییر دهد؛
- و نباید جایگزین طراحی درست شود.
در صورت استفاده، متن اصلی و خروجی خراب را نگه دارید و نتیجه تعمیرشده را دوباره Validate کنید.
Streaming و خروجی ساختاریافته
Streaming برای پاسخ متنی ساده است، زیرا میتوان هر Token را بلافاصله نمایش داد. اما JSON تا زمانی که کامل نشده باشد ممکن است قابل Parse نباشد.
مثلاً Chunkهای زیر بهترتیب میرسند:
{"name":
"محصول نمونه",
"price": 120
000}
هیچیک از Chunkهای میانی بهتنهایی JSON کامل نیستند.
راهکارهای رایج:
- Chunkها را Buffer و پس از پایان Parse کنید.
- از Parser افزایشی مخصوص JSON استفاده کنید.
- برای عملیات Backend حساس Streaming را غیرفعال کنید.
- پیشرفت عملیات را جدا از داده نهایی نمایش دهید.
- فقط پس از دریافت و Validation کامل، داده را در دیتابیس ذخیره کنید.
هرگز یک Object ناقص Streaming را بهعنوان نتیجه نهایی اجرا نکنید.
تأثیر Schema بر هزینه و سرعت
Schema نیز بخشی از ورودی درخواست است و میتواند Token مصرف کند. اگر Schema بسیار بزرگ باشد:
- تعداد Token ورودی افزایش مییابد؛
- هزینه بیشتر میشود؛
- Latency ممکن است افزایش یابد؛
- احتمال ناسازگاری بیشتر میشود؛
- نگهداری Schema دشوارتر میشود.
برای بهینهسازی:
- فقط فیلدهای لازم را نگه دارید.
- توضیحات را دقیق اما کوتاه بنویسید.
- Schemaهای بسیار بزرگ را به چند مرحله تقسیم کنید.
- از Enumهای ضروری استفاده کنید.
- داده خام غیرضروری را به مدل ارسال نکنید.
- نرخ موفقیت و هزینه هر مدل را اندازهگیری کنید.
- برای وظایف ساده از مدلهای سریعتر و ارزانتر استفاده کنید.
- در صورت پشتیبانی ارائهدهنده، از Prompt Caching برای بخشهای تکراری بهره ببرید.
بهینهترین مدل لزوماً قویترین یا گرانترین مدل نیست. مدل مناسب مدلی است که قرارداد داده را با دقت کافی، هزینه قابلقبول و Latency مناسب اجرا کند.
الگوی پیشنهادی Production
یک جریان امن برای استفاده از Structured Outputs میتواند به این شکل باشد:
ورودی کاربر یا سند
↓
پاکسازی و محدودکردن ورودی
↓
انتخاب مدل دارای قابلیت JSON Schema
↓
ارسال Prompt و Schema
↓
بررسی وضعیت HTTP و finish_reason
↓
تشخیص Refusal یا پاسخ ناقص
↓
Parseکردن JSON
↓
Validation با Pydantic یا Zod
↓
اعتبارسنجی قواعد کسبوکار
↓
بررسی مجوز و دادههای مرجع
↓
ثبت نتیجه یا درخواست تأیید انسانی
↓
ذخیره Log، هزینه، Latency و نسخه Schema
این معماری خروجی مدل را مستقیماً به عملیات حساس متصل نمیکند و چند لایه کنترل میان آنها قرار میدهد.
بهترین کاربردهای Structured Outputs
Structured Outputs برای وظایفی مناسب است که خروجی آنها باید وارد یک سیستم نرمافزاری شود:
- استخراج اطلاعات فاکتور و رسید
- استخراج بندهای قرارداد
- پردازش رزومه
- تحلیل فرمها
- دستهبندی تیکت پشتیبانی
- تحلیل احساس مشتری
- تشخیص Intent
- تولید Metadata محتوا
- ساخت اطلاعات محصول
- استخراج موجودیتها
- تبدیل متن به رکورد CRM
- تحلیل نتایج نظرسنجی
- تولید داده برای نمودار
- ایجاد تنظیمات رابط کاربری
- ساخت برنامه چندمرحلهای برای Agent
- تولید ورودی برای Workflow
- ارزیابی محتوای متنی براساس Rubric
- تبدیل گزارش به داده قابلجستوجو
برای پاسخهای خلاقانه، مقالهنویسی، مکالمه طبیعی و تولید داستان، معمولاً خروجی متنی آزاد مناسبتر است.
اشتباهات رایج
اتکا به جمله «فقط JSON برگردان»
Prompt بهتنهایی قرارداد فنی محسوب نمیشود.
استفاده از JSON Mode بهجای Schema
JSON معتبر الزاماً ساختار مورد انتظار را ندارد.
حذف Validation سمت سرور
حتی داده Schema-valid باید با قواعد کسبوکار بررسی شود.
پیچیدهکردن بیشازحد Schema
ساختار سادهتر معمولاً قابلحملتر و قابلاعتمادتر است.
اجباریکردن مقدار ناموجود
اگر Schema هیچ راهی برای نمایش اطلاعات ناموجود نداشته باشد، مدل ممکن است مجبور به حدسزدن شود.
اعتماد به confidence مدل
Confidence متنی مدل معیار آماری تضمینشدهای نیست.
انجام عملیات حساس بدون تأیید
Structured Outputs مجوز اجرای انتقال مالی، حذف اطلاعات یا ارسال پیام نیست.
فرض پشتیبانی همه مدلها
قابلیت Structured Outputs باید برای مدل و endpoint انتخابی بررسی شود.
یکیدانستن ساختار صحیح با حقیقت
یک پاسخ میتواند Schema-valid اما از نظر محتوایی اشتباه باشد.
تغییر Schema بدون Versioning
مصرفکنندگان قدیمی ممکن است با تغییر ناگهانی قرارداد شکسته شوند.
چکلیست استفاده در محیط Production
پیش از انتشار، این موارد را بررسی کنید:
- مدل انتخابی از JSON Schema پشتیبانی میکند.
- Schema ساده و دارای نامگذاری واضح است.
- تمام فیلدهای ضروری در
requiredقرار دارند. - فیلدهای اختیاری امکان
nullدارند. additionalPropertiesدر محل مناسب برابرfalseاست.- گزینههای محدود با
enumتعریف شدهاند. - واحد پول، زمان و اندازه مشخص شده است.
- Prompt مدل را از حدسزدن منع میکند.
- خروجی با Pydantic یا Zod اعتبارسنجی میشود.
- قواعد کسبوکار جداگانه بررسی میشوند.
- Refusal و پاسخ ناقص مدیریت میشوند.
- Retry فقط برای خطاهای موقت انجام میشود.
- Timeout تعریف شده است.
- عملیات حساس تأیید مستقل دارند.
- ورودیهای Prompt Injection آزمایش شدهاند.
- نسخه Schema ثبت میشود.
- هزینه، Latency و نرخ خطا مانیتور میشوند.
- Fallback فقط به مدل سازگار انجام میشود.
- Dataset ارزیابی واقعی وجود دارد.
پرسشهای متداول
Structured Outputs چیست؟
Structured Outputs قابلیتی است که به توسعهدهنده امکان میدهد ساختار پاسخ مدل هوش مصنوعی را با یک Schema تعریف کند. هدف این است که پاسخ بهجای متن آزاد، دادهای قابلپردازش و منطبق با قرارداد نرمافزار باشد.
تفاوت Structured Outputs و JSON Mode چیست؟
JSON Mode معمولاً فقط معتبربودن JSON را تضمین میکند، اما Structured Outputs علاوه بر JSON معتبر، پاسخ را به یک JSON Schema مشخص محدود میکند. در نتیجه نوع فیلدها، کلیدهای ضروری و مقادیر مجاز قابلکنترلتر هستند.
آیا Structured Outputs از Hallucination جلوگیری میکند؟
خیر. این قابلیت ساختار پاسخ را کنترل میکند، نه حقیقت محتوای آن را. مدل ممکن است دادهای از نظر ساختاری معتبر اما از نظر معنایی اشتباه تولید کند.
آیا بعد از Structured Outputs به Pydantic یا Zod نیاز داریم؟
بله. Validation سمت برنامه یک لایه دفاعی ضروری است. علاوه بر ساختار، باید قواعد کسبوکار، مجوزها و ارتباط داده با سیستم واقعی نیز بررسی شوند.
Structured Outputs چه تفاوتی با Tool Calling دارد؟
Structured Outputs برای دریافت پاسخ ساختاریافته است. Tool Calling زمانی استفاده میشود که مدل باید یک تابع یا ابزار را با آرگومانهای مشخص فراخوانی کند.
آیا همه مدلها از json_schema پشتیبانی میکنند؟
خیر. پشتیبانی به مدل، ارائهدهنده و endpoint بستگی دارد. برخی مدلها فقط JSON Mode یا Tool Calling را ارائه میکنند.
برای اطلاعات ناموجود چه کاری انجام دهیم؟
بهتر است فیلد همیشه در پاسخ وجود داشته باشد اما نوع آن امکان null داشته باشد:
{
"type": ["string", "null"]
}
در Prompt نیز صریحاً از مدل بخواهید اطلاعات ناموجود را حدس نزند.
آیا میتوان خروجی را مستقیماً وارد دیتابیس کرد؟
خیر. ابتدا باید JSON Parse، سپس با Schema اعتبارسنجی و بعد با قواعد کسبوکار بررسی شود. Queryهای دیتابیس نیز باید Parameterized باشند.
آیا Structured Outputs برای زبان فارسی کار میکند؟
بله. ساختار JSON مستقل از زبان محتوای فیلدهاست. نام فیلدها میتوانند انگلیسی و مقادیر فارسی باشند. برای سازگاری بهتر نرمافزاری، معمولاً نام کلیدها به انگلیسی و محتوای آنها به زبان مورد نیاز نگهداری میشود.
آیا میتوان از Structured Outputs همراه Streaming استفاده کرد؟
در صورت پشتیبانی مدل و API بله، اما Chunkهای میانی معمولاً JSON کامل نیستند. باید آنها را Buffer کنید و فقط پس از پایان پاسخ، JSON نهایی را Parse و Validate کنید.
جمعبندی
Structured Outputs یکی از مهمترین قابلیتها برای تبدیل مدلهای زبانی از ابزار تولید متن به اجزای قابلاعتمادتر یک سیستم نرمافزاری است.
با این قابلیت میتوان ساختار پاسخ را از قبل مشخص کرد و خروجیهایی مناسب برای دیتابیس، CRM، پردازش اسناد، سیستم پشتیبانی، Workflowها و Agentهای هوش مصنوعی دریافت کرد.
بااینحال، Structured Outputs بهتنهایی تمام مشکلات را حل نمیکند. یک پیادهسازی Production همچنان به این موارد نیاز دارد:
- Schema ساده و دقیق
- Prompt مناسب
- مدل سازگار
- Validation سمت سرور
- کنترل قواعد کسبوکار
- مدیریت Refusal و پاسخ ناقص
- Retry کنترلشده
- ملاحظات امنیتی
- مانیتورینگ هزینه و خطا
- ارزیابی صحت معنایی
مهمترین اصل این است:
Structured Outputs شکل پاسخ را قابلاعتمادتر میکند؛ صحت محتوا و ایمنی عملیات همچنان مسئولیت برنامه شماست.
استفاده از Structured Outputs با API درواره
درواره دسترسی به مدلهای مختلف هوش مصنوعی را از طریق یک API سازگار با OpenAI فراهم میکند. برای اتصال بسیاری از SDKها و ابزارهای OpenAI-compatible کافی است آدرس پایه را روی مقدار زیر قرار دهید:
https://api.darvareh.ir/v1
سپس میتوانید براساس قابلیت مدل انتخابی از JSON Mode، Structured Outputs یا Tool Calling برای ساخت اپلیکیشنهای هوش مصنوعی استفاده کنید.
پیش از استقرار نهایی، پشتیبانی مدل موردنظر از response_format، json_schema و حالت strict را بررسی کرده و خروجی را روی دادههای واقعی پروژه خود آزمایش کنید.
مقالات مرتبط
- Function Calling و Tool Calling چیست؟ آموزش اتصال هوش مصنوعی به ابزارها و APIها
- OpenAI-Compatible API چیست و چگونه کار میکند؟
- API هوش مصنوعی چیست؟ راهنمای کامل اتصال AI به نرمافزار
- Prompt Engineering چیست؟ راهنمای مهندسی پرامپت برای توسعهدهندگان
- Token چیست و چگونه هزینه API هوش مصنوعی محاسبه میشود؟
- AI Agent چیست؟ آموزش ساخت عامل هوش مصنوعی
- چگونه با API درواره یک چتبات هوش مصنوعی بسازیم؟
- RAG چیست؟ آموزش ساخت سیستم بازیابی و تولید تقویتشده
- Prompt Caching چیست؟ راهنمای کاهش هزینه و زمان پاسخ API