ब्लॉग पर वापस जाएं

API और SDKगाइड

LLM API से मान्य JSON प्राप्त करें: response_format और पुनः प्रयास

एक कॉपी करने योग्य Python अनुरोध जो पार्स करने योग्य JSON लौटाता है, एक json.loads रैपर जो खाली या कटे हुए उत्तरों को संभालता है, और JSON क्यों टूटता है इसकी एक चेकलिस्ट।

CheapTokens Team द्वाराप्रकाशित 9 मिनट का पाठ

एक टर्मिनल जो response_format json_object के साथ चैट पूर्णता अनुरोध दिखा रहा है
इस पृष्ठ पर
  1. काम: एक ईमेल अंदर, एक पंक्ति बाहर
  2. उदाहरण फ़्लैग से अधिक क्यों मायने रखता है
  3. सुरक्षित रूप से पार्स करें: खाली, कटा हुआ, या बस टूटा हुआ
  4. सिंटैक्स ही नहीं, आकार भी जाँचें
  5. ईमेल के एक बैच की लागत क्या है
  6. त्वरित जाँच

चैट मॉडल से JSON निकालने के लिए तीन काम करें: response_format={"type": "json_object"} पास करें, प्रॉम्प्ट में "json" शब्द और संरचना का एक उदाहरण डालें, और पूरे ऑब्जेक्ट के लिए max_tokens को काफी ऊँचा सेट करें। फिर जवाब पर कभी आँख बंद करके भरोसा न करें: जाँचें कि यह खाली नहीं है, finish_reason जाँचें, और तभी json.loads कॉल करें।

यह संक्षिप्त संस्करण है। इस गाइड का बाकी हिस्सा एक बहुत ही सामान्य काम के लिए एक पूरी स्क्रिप्ट है: ईमेल से फ़ील्ड निकालना। उदाहरण बेस URL https://api.cheaptokens.dev/v1 और मॉडल आईडी deepseek-ai/DeepSeek-V4-Flash-0731 का उपयोग करते हैं (docs)।

काम: एक ईमेल अंदर, एक पंक्ति बाहर#

मान लें कि आपकी स्क्रिप्ट सपोर्ट ईमेल पढ़ती है और हर एक को एक रिकॉर्ड में बदलना है: प्रेषक, तारीख, राशि, और प्रेषक क्या चाहता है। यह वाला लें:

नमस्ते, मैं Lumen Ltd. से Anna Petrova हूँ। 14 अक्टूबर को मैंने 1,250 EUR के लिए इनवॉइस #3381 का भुगतान किया, लेकिन रसीद अभी भी इसे अवैतनिक दिखाती है। कृपया शुक्रवार से पहले इसे ठीक करें।

आपको यह वापस चाहिए, और कुछ नहीं:

json
{"sender": "Anna Petrova", "date": "2026-10-14", "amount": 1250, "currency": "EUR", "action": "fix receipt status"}

एक साधारण "फ़ील्ड निकालें" प्रॉम्प्ट आपको एक मैत्रीपूर्ण वाक्य, एक कोड फ़ेंस, या दोनों दे सकता है। आपका json.loads पहले बैकटिक पर अटक जाता है। यही समस्या है जिसे response_format और एक अच्छा प्रॉम्प्ट मिलकर हल करते हैं।

Python और curl में एक ही अनुरोध

Python

python
import json
import os

from openai import OpenAI

client = OpenAI(
    base_url="https://api.cheaptokens.dev/v1",
    api_key=os.environ["CHEAPTOKENS_API_KEY"],
)
MODEL = "deepseek-ai/DeepSeek-V4-Flash-0731"

SYSTEM = """Extract fields from the email and answer with json only.
Example of the json format:
{"sender": "Jane Roe", "date": "2026-01-31", "amount": 99.5, "currency": "USD", "action": "short phrase"}
Use null for a field that is not in the email."""

resp = client.chat.completions.create(
    model=MODEL,
    messages=[
        {"role": "system", "content": SYSTEM},
        {"role": "user", "content": email_text},
    ],
    response_format={"type": "json_object"},
    max_tokens=1000,
    temperature=0,
)
print(resp.choices[0].message.content)

curl

bash
curl https://api.cheaptokens.dev/v1/chat/completions \
  -H "Authorization: Bearer $CHEAPTOKENS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek-ai/DeepSeek-V4-Flash-0731",
    "messages": [
      {"role": "system", "content": "Extract fields from the email and answer with json only. Example: {\"sender\": \"Jane Roe\", \"amount\": 99.5}"},
      {"role": "user", "content": "Hi, this is Anna Petrova from Lumen Ltd. I paid invoice #3381 for 1,250 EUR..."}
    ],
    "response_format": {"type": "json_object"},
    "max_tokens": 1000
  }'

JSON Output के लिए DeepSeek API संदर्भ (27 अक्टूबर 2026 को जाँचा गया; पृष्ठ पर कोई प्रकाशन तिथि नहीं है) उन नियमों को बताता है जिनका यह अनुरोध पालन करता है: response_format को json_object पर सेट करें, सिस्टम या उपयोगकर्ता प्रॉम्प्ट में कहीं "json" शब्द रखें, और लक्ष्य संरचना का एक उदाहरण दें। उदाहरण के बिना मॉडल को आपके फ़ील्ड नामों का अनुमान लगाना पड़ता है, और यह अगले ईमेल पर अलग अनुमान लगाएगा।

उदाहरण फ़्लैग से अधिक क्यों मायने रखता है#

response_format API से JSON ऑब्जेक्ट लौटाने के लिए कहता है। यह मॉडल को यह नहीं बताता कि कौन सा ऑब्जेक्ट। इसे ऐसे समझें जैसे किसी सहकर्मी से कहना "मुझे एक स्प्रेडशीट भेजें" बिना यह बताए कि कौन से कॉलम। आपको एक स्प्रेडशीट मिलती है, बस आपकी नहीं।

प्रॉम्प्ट में उदाहरण कॉलम हेडर पंक्ति है। दो आदतें इसे बेहतर बनाती हैं:

  • वास्तविक आकार दिखाएँ, प्रकारों सहित: 1250 एक संख्या है, "2026-10-14" एक निश्चित तारीख प्रारूप में एक स्ट्रिंग है।
  • बताएँ कि जब कोई फ़ील्ड गायब हो तो क्या करें (ऊपर null)। अन्यथा मॉडल अंतराल भरने के लिए एक मान गढ़ लेता है।

response_format को एक मजबूत संकेत मानें, गारंटी नहीं। मॉडल कभी-कभी अभी भी एक खाली स्ट्रिंग लौटाते हैं, या ऑब्जेक्ट को गद्य में लपेट देते हैं। इसीलिए अगला भाग मौजूद है।

सुरक्षित रूप से पार्स करें: खाली, कटा हुआ, या बस टूटा हुआ#

व्यवहार में तीन विफलताएँ दिखती हैं, और प्रत्येक एक अलग निशान छोड़ती है:

  • खाली सामग्री। DeepSeek दस्तावेज़ कहते हैं कि JSON मोड कभी-कभी खाली सामग्री लौटा सकता है और प्रॉम्प्ट को फिर से लिखने का सुझाव देता है। आपकी ओर से यह message.content के "" या None होने जैसा दिखता है, और json.loads("") Expecting value: line 1 column 1 (char 0) उठाता है।
  • कटा हुआ। max_tokens ऑब्जेक्ट के बीच में खत्म हो गया। finish_reason length है, और टेक्स्ट स्ट्रिंग के बीच में समाप्त होता है। इसके बारे में अधिक क्यों एक AI उत्तर वाक्य के बीच में रुक जाता है में।
  • JSON नहीं। ऑब्जेक्ट से पहले एक वाक्य, या उसके चारों ओर एक फ़ेंस।
खाली उत्तर पर json.loads JSONDecodeError उठाता है
Python में एक खाली JSON-मोड उत्तर कैसा दिखता है

पहली और तीसरी का समाधान एक मजबूत प्रॉम्प्ट के साथ पुनः प्रयास है। दूसरी के लिए, समान सीमा के साथ पुनः प्रयास कुछ नहीं बदलता, इसलिए रैपर उठाता है और आपको max_tokens बढ़ाने के लिए कहता है:

python
def ask_json(email_text: str, retries: int = 1) -> dict:
    user = email_text
    for attempt in range(retries + 1):
        resp = client.chat.completions.create(
            model=MODEL,
            messages=[
                {"role": "system", "content": SYSTEM},
                {"role": "user", "content": user},
            ],
            response_format={"type": "json_object"},
            max_tokens=1000,
            temperature=0,
        )
        choice = resp.choices[0]
        if choice.finish_reason == "length":
            raise ValueError("JSON cut off by max_tokens: raise the limit")
        text = (choice.message.content or "").strip()
        if text:
            try:
                return json.loads(text)
            except json.JSONDecodeError:
                pass
        # empty or not JSON: ask again, more firmly
        user = email_text + "\n\nReply with one json object only, in the format of the example."
    raise ValueError("no valid JSON after retry")

एक स्क्रिप्ट के लिए एक पुनः प्रयास पर्याप्त है। यदि यह दो बार विफल होता है, तो ईमेल आईडी लॉग करें और आगे बढ़ें; तीसरा समान अनुरोध शायद ही मदद करता है।

जोड़े मिलाएँ

कौन सा लक्षण किस कारण की ओर इशारा करता है?

  • finish_reason "length" हैmax_tokens ऑब्जेक्ट के लिए बहुत छोटा है
  • content एक खाली स्ट्रिंग हैJSON मोड ने कुछ नहीं लौटाया; प्रॉम्प्ट को फिर से लिखें और पुनः प्रयास करें
  • JSONDecodeError एक ऐसे वर्ण पर जो बैकटिक हैमॉडल ने JSON को एक कोड फ़ेंस में लपेट दिया
  • फ़ील्ड नाम ईमेल के बीच भिन्न हैंप्रॉम्प्ट में संरचना का कोई उदाहरण नहीं है

सिंटैक्स ही नहीं, आकार भी जाँचें#

json.loads केवल यह साबित करता है कि टेक्स्ट मान्य JSON है। यह साबित नहीं करता कि इसमें आपके मांगे गए फ़ील्ड हैं। पार्स करने के तुरंत बाद एक सस्ती जाँच जोड़ें:

python
REQUIRED = {"sender", "date", "amount", "currency", "action"}

data = ask_json(email_text)
missing = REQUIRED - data.keys()
if missing:
    raise ValueError(f"missing fields: {sorted(missing)}")

यहाँ से dict सीधे डेटाबेस पंक्ति या स्प्रेडशीट में जाता है। यदि आपको मॉडल से एक निश्चित रिकॉर्ड भरने के बजाय अपने स्वयं के फ़ंक्शन कॉल कराने हैं, तो वह एक अलग उपकरण है: Python में function calling देखें।

ईमेल से जाँचे गए रिकॉर्ड तक का पथ: उदाहरण के साथ प्रॉम्प्ट, json_object, पार्स, फ़ील्ड जाँच
पूरा पथ पाँच चरणों में

ईमेल के एक बैच की लागत क्या है#

एक छोटा निष्कर्षण सस्ते प्रकार का अनुरोध है: कुछ सौ टोकन अंदर, कुछ दर्जन बाहर। मान लें कि प्रति दिन 1,000 ईमेल लगभग 400 इनपुट टोकन (प्रॉम्प्ट, उदाहरण और ईमेल) और लगभग 80 आउटपुट टोकन (JSON) के साथ। DeepSeek-V4-Flash पर यह प्रति दिन $0.014 है, और तालिका अन्य मॉडलों पर समान लोड दिखाती है:

मॉडल प्रति अनुरोध 1,000 अनुरोध OpenRouter, 1,000 अनुरोध
GLM-5.3-Flash $0.000011 $0.0112 $0.0226
DeepSeek-V4-Flash $0.000014 $0.014 $0.0315
MiniMax-M2.7 $0.000032 $0.032 $0.216

प्रति अनुरोध 400 इनपुट और 80 आउटपुट टोकन। प्रति 1M टोकन की कीमतें वर्तमान हैं और हमारे टैरिफ के साथ बदलती रहती हैं; OpenRouter कॉलम समान कार्यभार के लिए संदर्भ कीमतें दिखाता है।

कुल मिलाकर, इनपुट और आउटपुट को एक साथ गिनने पर, DeepSeek-V4-Flash इस लोड के लिए OpenRouter के संदर्भ मूल्य से सस्ता निकलता है। यह प्रति उत्तर 32,768 आउटपुट टोकन तक की अनुमति भी देता है, इसलिए लाइन आइटम की एक लंबी सूची उस तरह कट नहीं जाएगी जैसे छोटी सीमा के साथ होती, और कीमत किसी भी समय समान है। GLM-5.3-Flash (zai-org/GLM-5.3-Flash) उसी API पते पर एक और विकल्प है: 8,192 आउटपुट टोकन तक, जो प्रति ईमेल एक रिकॉर्ड के लिए पर्याप्त है। वर्तमान कीमतें मूल्य निर्धारण पृष्ठ पर हैं; DeepSeek मॉडल पृष्ठ में यहाँ उपयोग किए गए मॉडल का विवरण है।

त्वरित जाँच#

त्वरित जाँच

आपका JSON एक स्ट्रिंग के बीच में कट गया है। आप पहले क्या देखते हैं?

उत्तर दिखाएँ

उत्तर: finish_reason और max_tokens

एक कटा हुआ ऑब्जेक्ट finish_reason के length पर सेट होने के साथ समाप्त होता है। सीमा पार हो गई, इसलिए max_tokens बढ़ाएँ; तापमान या पता बदलने से मदद नहीं मिलती।

यदि आपने अभी तक कोई अनुरोध नहीं भेजा है, तो अपने पहले API अनुरोध से शुरू करें, फिर वापस आएँ और ऊपर दिया गया कोड डालें।