সমস্যা সমাধানগাইড
একটি LLM উত্তরে json.JSONDecodeError: Python-এ JSON বের করুন
একটি মডেল উত্তরে json.loads ব্যর্থ হয়? কারণ খুঁজে পেতে exc.pos এবং finish_reason পড়ুন, তারপর raw_decode দিয়ে JSON বের করুন এবং একবার পুনরায় চেষ্টা করুন।

এই পৃষ্ঠায়
আপনার স্ক্রিপ্ট মডেলের কাছে JSON চায়, json. কল করে এবং json. নিয়ে মারা যায়। সাধারণত মডেল উত্তর দিয়েছিল: এটি JSON-কে ```json fence, put a line of chat in front of it, or ran into max_tokens halfway through. Look at exc.pos and finish_reason to tell which one it is, pull the object out with JSONDecoder. দিয়ে মুড়ে দিয়েছিল, এবং কিছুই সংরক্ষণ করা না গেলে একবার পুনরায় চেষ্টা করুন।
একটি উত্তর json.loads ভাঙার তিনটি উপায়#
Python-এর json ডকুমেন্টেশন (Python 3.15 ডকস, ২ নভেম্বর ২০২৬-এ যাচাই করা) বলে JSONDecodeError হলো ValueError-এর একটি সাবক্লাস যার অ্যাট্রিবিউট msg, doc, pos, lineno এবং colno। এখানে যেটি গুরুত্বপূর্ণ তা হলো pos: স্ট্রিংয়ে যে ইনডেক্সে পার্সিং ব্যর্থ হয়েছে।
ধরুন একটি স্ক্রিপ্ট গ্রাহকের ইমেইল থেকে ফিল্ড বের করে এবং {"name": . চায়। এই তিনটি স্ট্রিং হলো সেই উত্তরগুলো যা এটি ভাঙে। এগুলো একটি Python শেলে পেস্ট করুন:
import json
fenced = '```json\n{"name": "Anna", "age": 31}\n```'
chatty = 'Sure! Here is the JSON:\n{"name": "Anna", "age": 31}'
cut = '{"name": "Anna", "age": 31'
for reply in (fenced, chatty, cut):
try:
json.loads(reply)
except json.JSONDecodeError as exc:
print(exc.pos, len(reply))প্রথম দুটি pos 0-এ ব্যর্থ হয়: পার্সার একেবারে প্রথম অক্ষরটি, একটি ব্যাকটিক বা একটি S, দেখে এবং হাল ছেড়ে দেয়। তৃতীয়টি স্ট্রিংয়ের শেষে ব্যর্থ হয় (pos সমান len(reply)): এটি যা পড়েছিল সব ঠিক ছিল, তারপর টেক্সট শেষ হয়ে যায়। এই পার্থক্যই আপনার রোগনির্ণয়। পজিশন 0 মানে JSON সম্ভবত সেখানে আছে, শুধু মোড়ানো। শেষে পজিশন মানে উত্তরটি তাড়াতাড়ি থেমে গেছে।

কাটা উত্তর: পার্স করার আগে finish_reason পরীক্ষা করুন#
মাঝপথে থেমে যাওয়া উত্তর কোনো পার্সিং সমস্যা নয়, এটি দৈর্ঘ্যের সমস্যা। API আপনাকে জানায়: চয়েসে finish_reason আছে, এবং "length" মানে উত্তরটি max_tokens দ্বারা কাটা হয়েছে (সম্পূর্ণ গল্পটি AI উত্তর কাটা? finish_reason "length" এবং max_tokens ঠিক করুন-এ)। যেকোনো এক্সট্র্যাকশনের আগে এটি প্রথমে দেখুন।
আপনি যদি সেই ধাপটি এড়িয়ে যান তবে একটি ভয়ানক ফাঁদ আছে। একটি স্ক্যানার যা প্রথম পার্সযোগ্য { খোঁজে তা অর্ধ-সমাপ্ত বাইরের অবজেক্ট থেকে একটি সম্পূর্ণ ভেতরের অবজেক্ট তুলে নিতে পারে, এবং আপনার স্ক্রিপ্ট ভুল ডেটা নিয়ে এগিয়ে যায় কোনো ত্রুটি ছাড়াই। চেষ্টা করুন:
নিজে চেষ্টা করুনচেষ্টা করুন: এই কাটা উত্তরের জন্য একটি "প্রথম অবজেক্ট খুঁজুন" ফাংশন কী ফেরত দেয়?উত্তর দেখান
'{"user": {"name": "Anna"}, "age": 31' নিন। বাইরের অবজেক্ট কখনো বন্ধ হয় না, কিন্তু ভেতরের {"name": "Anna"} সম্পূর্ণ, তাই নিচের এক্সট্র্যাক্টর {'name': 'Anna'} ফেরত দেয়। কোনো এক্সসেপশন নেই, ভুল ফলাফল। এজন্যই finish_reason চেক এক্সট্র্যাকশনের আগে আসে।
সত্যিকারের কাটা উত্তরের সমাধান হলো আরও জায়গা: max_tokens বাড়ান। DeepSeek-V4-Flash প্রতি উত্তরে সর্বোচ্চ 32,768 আউটপুট টোকেন অনুমোদন করে, GLM-5.3-Flash সর্বোচ্চ 8,192, তাই একটি দীর্ঘ JSON তালিকার প্রথমটিতে বেশি জায়গা আছে। মডেলের বিস্তারিত CheapTokens মডেল পৃষ্ঠা-এ আছে।
টেক্সট থেকে JSON বের করুন#
ফেন্স এবং বাড়তি কথার জন্য, স্ট্যান্ডার্ড লাইব্রেরিতে ইতিমধ্যে টুলটি আছে। একই Python পৃষ্ঠা অনুসারে, JSONDecoder. একটি স্ট্রিং থেকে একটি JSON ডকুমেন্ট ডিকোড করে যা একটি দিয়ে শুরু হয় এবং অবজেক্টটি প্লাস যে ইনডেক্সে ডকুমেন্টটি শেষ হয়েছিল তা ফেরত দেয়। ডকস বলে এটি শেষে অতিরিক্ত ডেটা থাকা স্ট্রিংয়ে ব্যবহার করা যায়। এটি ডকুমেন্টটি প্রথম অক্ষরে শুরু হতে হবে, তাই প্রথমে অগ্রণী টেক্সট কেটে ফেলুন: একটি { খুঁজুন, সেখান থেকে ডিকোড করার চেষ্টা করুন, এবং ব্যর্থ হলে পরবর্তী {-এ যান।
import json
def extract_json(text):
"""Return the first JSON object found in text, or raise ValueError."""
decoder = json.JSONDecoder()
for i, ch in enumerate(text):
if ch == '{':
try:
obj, _end = decoder.raw_decode(text[i:])
return obj
except json.JSONDecodeError:
continue
raise ValueError('no JSON object in the reply')এটি ফেন্স পরিচালনা করে (অগ্রণী ```json is skipped because it is not a {), the sentence before the object, and text after the closing brace. Expecting a list? Look for '[' instead. Don't look for both: a stray [1] "see note [1]" এর মতো একটি বাক্যে বৈধ JSON এবং জিতবে।
এটি ইচ্ছাকৃতভাবে দুটি কাজ করে না। এটি কখনো টেক্সট সম্পাদনা করে না (ব্যাকটিক মুছে ফেলার কোনো রেজেক্স নেই, অনুপস্থিত ব্র্যাকেট অনুমান নেই), তাই আপনি একটি ভাঙা উত্তরকে বৈধ কিন্তু ভুল ডেটায় পরিণত করতে পারবেন না। এবং খুঁজে পেতে কিছু না থাকলে এটি জোরে ব্যর্থ হয়, যা নিচের পুনরায় চেষ্টার জন্য প্রয়োজন।

আরও কঠোর প্রম্পট দিয়ে একবার পুনরায় চেষ্টা করুন#
যদি উত্তরটি কাটা হয় বা কোনো অবজেক্ট না থাকে, আবার জিজ্ঞাসা করুন। একবার, অসীম লুপে নয়। কাটা উত্তরের ক্ষেত্রে, দ্বিতীয় প্রচেষ্টাকে আরও জায়গা দিন; অন্যথায় আপনি কী চান তা স্পষ্টভাবে বলুন। এটি CheapTokens বেস URL (https:/) এবং ডকস থেকে সম্পূর্ণ মডেল আইডির বিরুদ্ধে OpenAI Python SDK ব্যবহার করে; কীটি একটি এনভায়রনমেন্ট ভেরিয়েবলে থাকে।
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'
STRICT = '\n\nReply with one JSON object only. No text before or after it, no code fence.'
def ask_json(prompt, max_tokens=800):
content = prompt
for attempt in (1, 2):
resp = client.chat.completions.create(
model=MODEL,
messages=[{'role': 'user', 'content': content}],
max_tokens=max_tokens,
)
choice = resp.choices[0]
if choice.finish_reason == 'length':
max_tokens *= 2 # cut off: more room next time
else:
try:
return extract_json(choice.message.content or '')
except ValueError:
pass # no object: try the stricter prompt
content = prompt + STRICT
raise ValueError('no valid JSON after one retry')ক্রমটাই মূল বিষয়: প্রথমে finish_reason, দ্বিতীয়ত এক্সট্র্যাকশন, এবং কেবল তারপর পুনরায় চেষ্টা। দ্বিতীয় প্রচেষ্টার পরে ত্রুটি বাড়ান যাতে একটি ব্যাচ জব সেই আইটেমটি লগ করে এগিয়ে যেতে পারে।
একটি পুনরায় চেষ্টার খরচ অনুমান করা সহজ। ধরুন 1,200 ইনপুট টোকেন এবং 300 আউটপুট টোকেনের একটি অনুরোধ (একটি অনুমান, আপনার নিজের সংখ্যা ব্যবহার করুন): DeepSeek-V4-Flash-এ $0.000048 বা GLM-5.3-Flash-এ $0.000036। যদি 1,000টি অনুরোধের মধ্যে 30টির দ্বিতীয় প্রচেষ্টা প্রয়োজন হয়, অতিরিক্ত বিল হলো 30 × সেই অনুরোধ, DeepSeek-V4-Flash-এ $0.0014। দাম লাইভ, তারা বর্তমান মূল্য তালিকা থেকে আসে।
ক্রমে সাজান
মেরামতের ধাপগুলো ক্রমানুসারে রাখুন
আইটেমগুলো ড্র্যাগ করুন বা তীর ব্যবহার করুন
- finish_reason পরীক্ষা করুন: এটি length বললে এখানে থামুন
- প্রথম { খুঁজুন এবং তার আগের টেক্সট কেটে ফেলুন
- raw_decode দিয়ে ডিকোড করুন এবং পরেরটি উপেক্ষা করুন
- কিছু পাওয়া না গেলে, আরও কঠোর প্রম্পট দিয়ে একবার পুনরায় চেষ্টা করুন
কোন লক্ষণ, কোন সমাধান#
পরবর্তী অদ্ভুত ত্রুটি দেখা না দেওয়া পর্যন্ত উপরের ধাপগুলো আপনার মাথায় ফিট করে। এটি একটি লুকআপের মতোই: স্ক্রিনে যা দেখেন তা খুঁজুন এবং সমাধান পড়ুন।
আপনার কেস বেছে নিন
আপনি যা দেখেন তা বেছে নিন
উত্তর দেখতে একটি বিকল্প বেছে নিন
json.loads pos 0-এ ব্যর্থ হয়
উত্তরটি একটি ফেন্স বা একটি বাক্য দিয়ে শুরু হয়। এটি extract_json এর মধ্য দিয়ে চালান; পুনরায় চেষ্টার প্রয়োজন নেই।
pos স্ট্রিংয়ের শেষে
উত্তরটি তাড়াতাড়ি থেমে গেছে। finish_reason পরীক্ষা করুন: যদি এটি length হয়, max_tokens বাড়ান এবং আবার জিজ্ঞাসা করুন।
ত্রুটি মাঝখানে কোথাও
মডেল নিজেই ভাঙা JSON লিখেছে (একটি অনুপস্থিত কমা, একটি ট্রেইলিং কমা, একটি আনএস্কেপড কোট)। এটি প্যাচ করবেন না: আরও কঠোর প্রম্পট দিয়ে একবার পুনরায় চেষ্টা করুন এবং আবার ব্যর্থ হলে উত্তরটি লগ করুন।
json.loads কাজ করে কিন্তু একটি কী অনুপস্থিত
পার্সিং ঠিক আছে, বিষয়বস্তু নয়। প্রম্পটে সঠিক কী-গুলো তালিকাভুক্ত করুন এবং পার্স করার পরে কোডে সেগুলো পরীক্ষা করুন।
API কি বৈধ JSON বাধ্যতামূলক করতে পারে?#
কখনও কখনও। OpenAI-এর স্ট্রাকচার্ড আউটপুট গাইড দুটি মোড নথিভুক্ত করে: JSON মোড (type: json_), যা বৈধ JSON ফেরত দেয় কিন্তু একটি স্কিমা অনুসরণ করে না, এবং Structured Outputs (type: json_, strict: true), যা করে; OpenAI সম্ভব হলে দ্বিতীয়টির সুপারিশ করে (সূত্র: developers.openai.com, ২ নভেম্বর ২০২৬-এ যাচাই করা)। এগুলো OpenAI-এর নিজস্ব API অপশন। অন্য এন্ডপয়েন্টের সাথে response_format-এর উপর নির্ভর করার আগে, CheapTokens ডকস দেখুন; উপরের এক্সট্র্যাকশন এবং পুনরায় চেষ্টা সাধারণ চ্যাট কমপ্লিশনগুলির সাথে কাজ করে এবং কোনো বিশেষ প্যারামিটারের প্রয়োজন নেই। অনুরোধ-সাইড অপশন সম্পর্কে আরও একটি LLM API থেকে বৈধ JSON পান: response_format এবং পুনরায় চেষ্টা-এ।
পার্সার শিপ করার আগে, চারটি জিনিস পরীক্ষা করুন: টেক্সট পার্স করার আগে finish_reason পড়া হয়, কেবল একটি পুনরায় চেষ্টা আছে (এবং দ্বিতীয় ব্যর্থতা আইটেম আইডি সহ লগ করা হয়), কোড পার্স করার পরে প্রত্যাশিত কী-গুলো বিদ্যমান কিনা তা পরীক্ষা করে, এবং max_tokens আপনার প্রত্যাশিত দীর্ঘতম JSON-এর জন্য জায়গা রাখে।
পরের বার json.loads নিক্ষেপ করলে, প্রথমে exc.pos এবং finish_reason প্রিন্ট করুন। সেই দুটি সংখ্যা সাধারণত উত্তরটির একটি লাইন পড়ার আগেই আপনাকে বলে দেয় কী ঘটেছে।



