ব্লগে ফিরে যান

সমস্যা সমাধানগাইড

AI উত্তর কেটে গেছে? finish_reason "length" এবং max_tokens ঠিক করুন

আপনার API উত্তর বাক্যের মাঝখানে থামে বা ভাঙা JSON ফেরত দেয়? finish_reason চেক করুন, সীমার মধ্যে max_tokens বাড়ান এবং একটি continue ধাপ যোগ করুন।

CheapTokens Team দ্বারাপ্রকাশিত হয়েছে 9 মিনিট পড়ার সময়

একটি API রেসপন্সে finish_reason length দেখানো টার্মিনাল-স্টাইল কভার
এই পৃষ্ঠায়
  1. স্ক্রিনে একটি কাটা উত্তর কেমন দেখায়
  2. ধাপ ১: যে ফিল্ডটি আপনাকে জানায় সেটি পড়ুন
  3. ধাপ ২: max_tokens বাড়ান, কিন্তু সিলিং ছাড়িয়ে নয়
  4. ধাপ ৩: যে JSON অর্ধপথে থেমে যায়
  5. ধাপ ৪: মডেলকে চালিয়ে যেতে দিন
  6. একটি দীর্ঘ উত্তরের খরচ কত
  7. শিপ করার আগে: একটি সংক্ষিপ্ত চেকলিস্ট

আপনার স্ক্রিপ্ট চলেছে, মডেল উত্তর দিয়েছে, আর ফাংশনটি একটি লাইনের মাঝখানে শেষ হয়ে গেছে। অথবা JSON পার্সার একটি একেবারে স্বাভাবিক দেখতে উত্তরেই এরর দিচ্ছে। বেশিরভাগ ক্ষেত্রেই মডেল ব্যর্থ হয়নি: এটি আপনার সেট করা max_tokens সীমায় পৌঁছেছে (বা এমন একটি ডিফল্টে যা আপনি দেখেননি) এবং API এটিকে শব্দের মাঝখানে থামিয়ে দিয়েছে।

দ্রুত সমাধান: রেসপন্সে finish_reason পড়ুন। যদি এটি "length" হয় (OpenAI-সামঞ্জস্যপূর্ণ API) অথবা stop_reason হয় "max_tokens" (Anthropic Messages), তাহলে উত্তরটি কেটে গেছে। max_tokens মডেলের আউটপুট সীমা পর্যন্ত বাড়ান, অথবা মডেলকে চালিয়ে যেতে বলুন। এই পেজের বাকি অংশে কোডে দুটোই দেখানো হয়েছে যা আপনি পেস্ট করতে পারবেন।

কেন এমন হয় তার সহজ ভাষার ব্যাখ্যা চাইলে শুরু করুন কেন একটি AI উত্তর বাক্যের মাঝখানে থামে দিয়ে। এই পেজটি ডিবাগিং সংস্করণ।

স্ক্রিনে একটি কাটা উত্তর কেমন দেখায়#

লক্ষণগুলো আপনি কী চেয়েছেন তার উপর নির্ভর করে ভিন্ন হয়, কিন্তু কারণ একই। আপনি যা দেখেছেন তার সাথে এগুলো মিলিয়ে দেখুন:

জোড়া মেলান

লক্ষণকে তার সম্ভাব্য কারণের সাথে মিলান

  • একটি ফাংশন return resu দিয়ে শেষ হয়কোড উত্তরটি লাইনের মাঝখানে max_tokens-এ পৌঁছেছে
  • json.loads "Unterminated string" বা "Expecting ',' delimiter" দিয়ে ব্যর্থ হয়JSON মাঝপথে কেটে গেছে
  • একটি অনুবাদ টেক্সটের প্রথম অর্ধেকের পরে থেমে যায়আউটপুট সীমা অনুবাদের চেয়ে ছোট
  • উত্তরটি সম্পূর্ণ এবং ফিল্ডে stop লেখা আছেমডেল নিজে থেকেই শেষ করেছে; অন্যত্র খুঁজুন

শেষ সারিটি গুরুত্বপূর্ণ। যদি finish_reason হয় stop, তাহলে সীমা আপনার সমস্যা নয়, এবং max_tokens বাড়ালে কিছুই বদলাবে না।

ধাপ ১: যে ফিল্ডটি আপনাকে জানায় সেটি পড়ুন#

প্রতিটি রেসপন্স বলে দেয় কেন জেনারেশন শেষ হয়েছে। OpenAI-সামঞ্জস্যপূর্ণ রেসপন্সে এটি choices[0].finish_reason: মডেল শেষ করলে "stop", টোকেন ফুরালে "length"। Anthropic Messages API-তে ফিল্ডটি stop_reason, এবং কাটা পড়ার মান "max_tokens"। Anthropic-এর stop-reason গাইড (২৮ অক্টোবর ২০২৬-এ যাচাইকৃত) ঠিক নিচের দুটি সমাধানেরই সুপারিশ করে: max_tokens বাড়ান অথবা রেসপন্স চালিয়ে যান।

এখানে একই চেক তিনটি রূপে। OpenAI SDK-এর জন্য বেস URL https://api.cheaptokens.dev/v1 এবং Anthropic SDK-এর জন্য https://api.cheaptokens.dev (/v1 ছাড়া)। max_tokens=200 ইচ্ছাকৃতভাবে ছোট, যাতে আপনি নিজেই কাটাটি দেখতে পারেন।

একটি চেক, তিনটি উপায়

Python (OpenAI SDK)

python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.cheaptokens.dev/v1",
    api_key=os.environ["CHEAPTOKENS_API_KEY"],
)

resp = client.chat.completions.create(
    model="deepseek-ai/DeepSeek-V4-Flash-0731",
    max_tokens=200,
    messages=[{"role": "user", "content": "Write a Python class for a todo list with tests."}],
)

choice = resp.choices[0]
if choice.finish_reason == "length":
    print("Cut off at the token limit")
print(choice.message.content)
print(resp.usage.completion_tokens)

Python (Anthropic SDK)

python
import os
import anthropic

client = anthropic.Anthropic(
    base_url="https://api.cheaptokens.dev",
    api_key=os.environ["CHEAPTOKENS_API_KEY"],
)

msg = client.messages.create(
    model="deepseek-ai/DeepSeek-V4-Flash-0731",
    max_tokens=200,
    messages=[{"role": "user", "content": "Write a Python class for a todo list with tests."}],
)

if msg.stop_reason == "max_tokens":
    print("Cut off at the token limit")
print(msg.content[0].text)

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",
    "max_tokens": 200,
    "messages": [{"role": "user", "content": "Write a Python class for a todo list with tests."}]
  }'

ফিরে আসা JSON-এ "finish_reason": "length" খুঁজুন।

যখন এটি ডায়াগনসিস হিসেবে কাজ করে, আপনি এই জোড়াটি দেখবেন: কম সীমা length দেয়, প্রশস্ত সীমা stop দেয়।

কম ও বেশি max_tokens সহ একই রিকোয়েস্ট: finish_reason length থেকে stop-এ বদলায়
finish_reason-এর যে দুটি মান আপনি সবচেয়ে বেশি দেখবেন

ধাপ ২: max_tokens বাড়ান, কিন্তু সিলিং ছাড়িয়ে নয়#

স্পষ্ট সমাধান হলো বড় সংখ্যা। ফাঁদটি হলো: প্রতিটি মডেলের উত্তরের উপর নিজস্ব ক্যাপ আছে। এর উপরে মান সেট করবেন না: সীমাটি প্রতি মডেল ভিন্ন, তাই আগে নিচের টেবিলটি দেখুন। এগুলো CheapTokens-এ প্রতি-উত্তরের সীমা (তথ্য ২৮ অক্টোবর ২০২৬-এ যাচাইকৃত):

Model Max output per answer Context
DeepSeek-V4-Flash 32,768 tokens 380K
GLM-5.3-Flash 8,192 tokens 390K
MiniMax-M2.7 8,192 tokens 180K

৯,০০০-টোকেনের অনুবাদের জন্য বড় প্রতি-উত্তর সীমার একটি মডেল দরকার — DeepSeek-V4-Flash প্রতি উত্তরে ৩২,৭৬৮ টোকেন পর্যন্ত নেয়। দীর্ঘ কোড, দীর্ঘ JSON বা একটি সম্পূর্ণ ডকুমেন্টের জন্য deepseek-ai/DeepSeek-V4-Flash-0731-এর সবচেয়ে বেশি জায়গা আছে: প্রতি উত্তরে ৩২,৭৬৮ টোকেন পর্যন্ত, দিনের যেকোনো সময়ে একই দাম। সঠিক মডেল আইডি নিন CheapTokens ডকস এবং মডেল পেজ থেকে, কারণ প্রোভাইডাররা সেগুলো ভিন্নভাবে নাম দেয়।

একটি ব্যবহারিক নিয়ম: max_tokens সেট করুন আপনি যে দীর্ঘতম উত্তর গ্রহণ করবেন তার সমান, সর্বোচ্চে নয়। মডেল আসলে যত টোকেন লেখে তার জন্য আপনি অর্থ দেন, তাই উত্তর ছোট হলে উচ্চ সিলিংয়ে কোনো খরচ নেই।

ধাপ ৩: যে JSON অর্ধপথে থেমে যায়#

কাটা JSON উত্তর সবচেয়ে বিরক্তিকর কেস, কারণ পার্সার শেষে পৌঁছানো পর্যন্ত টেক্সট ঠিকই দেখায়। ধরুন আপনি এক কলে ৪০টি ইনভয়েস থেকে ফিল্ড বের করেন এবং একটি অ্যারে চান। মডেল ৩১টি অবজেক্ট লেখে, সীমায় পৌঁছায়, আর অ্যারেটি কখনো বন্ধ হয় না।

টোকেন সীমায় কাটা একটি অ্যারের উপর json.loads এরর
পার্সারে একটি কাটা JSON অ্যারে কেমন দেখায়

]} যোগ করে স্ট্রিংটি মেরামত করার চেষ্টা করবেন না। আপনি অনুপস্থিত রেকর্ডসহ বৈধ JSON পাবেন, যা এররের চেয়েও খারাপ। আগে finish_reason চেক করুন; যদি এটি length হয়, তাহলে হয় সীমা বাড়িয়ে কলটি আবার চালান, অথবা ইনপুটটি ছোট ব্যাচে ভাগ করুন। ফরম্যাট নিজেই বাধ্যতামূলক করার জন্য দেখুন একটি LLM API থেকে বৈধ JSON।

ধাপ ৪: মডেলকে চালিয়ে যেতে দিন#

কখনো কখনো উত্তর সত্যিই যেকোনো সীমার চেয়ে দীর্ঘ: একটি বইয়ের অধ্যায়, একটি বড় রিফ্যাক্টর। তখন আপনার কাছে যা আছে তা ফেরত পাঠিয়ে মডেলকে চালিয়ে যেতে বলুন। নিচের লুপটি এখন পর্যন্ত পাওয়া টেক্সট রাখে, বাকিটা চায়, এবং কয়েক রাউন্ড পরে হাল ছেড়ে দেয় যাতে কোনো বাগ অনন্তকাল ঘুরতে না পারে।

python
def complete(base_messages, model, max_tokens=4096, rounds=4):
    text = ""
    for _ in range(rounds):
        messages = list(base_messages)
        if text:
            messages += [
                {"role": "assistant", "content": text},
                {"role": "user", "content": "Continue exactly where you stopped. Do not repeat anything."},
            ]
        r = client.chat.completions.create(model=model, max_tokens=max_tokens, messages=messages)
        choice = r.choices[0]
        text += choice.message.content or ""
        if choice.finish_reason != "length":
            return text, True   # finished on its own
    return text, False          # still cut after all rounds

দুটি অভ্যাস এটিকে সৎ রাখে। True/False ফ্ল্যাগটি ফেরত দিন এবং False-এ ব্যবহারকারীকে একটি সতর্কতা দেখান, অর্ধেক ডকুমেন্ট সম্পূর্ণ হিসেবে উপস্থাপন করবেন না। আর মনে রাখুন প্রতিটি রাউন্ড আগের টেক্সট ইনপুট হিসেবে আবার পাঠায়, তাই চালিয়ে যাওয়া উত্তর একটি বড় উত্তরের চেয়ে বেশি খরচ করে। স্ট্রাকচার্ড ডেটার জন্য, চালিয়ে যাওয়ার বদলে বড় সীমা বা ছোট ব্যাচ পছন্দ করুন।

একটি দীর্ঘ উত্তরের খরচ কত#

ধরুন একটি দীর্ঘ উত্তরে ইনপুট ২,০০০ টোকেন এবং আউটপুট ৮,০০০ টোকেন। DeepSeek-V4-Flash-এ আজকের দামে সেই রিকোয়েস্টের খরচ $0.00083, এবং ইনপুট ও আউটপুট একসাথে গুনলে মোটে এটি OpenRouter-এর চেয়ে সস্তা। সংখ্যাগুলো আপনার নিজের লোডে বদলে নিন:

খরচ ক্যালকুলেটর

৫০টি দীর্ঘ উত্তরের এক দিন

প্রতি অনুরোধে ইনপুট টোকেন: ২,০০০ · প্রতি অনুরোধে আউটপুট টোকেন: ৮,০০০ · প্রতিদিন অনুরোধ: ৫০

মডেলপ্রতিদিনপ্রতি মাসে
GLM-5.3-Flash$0.018$0.54
DeepSeek-V4-Flash$0.0415$1.25
MiniMax-M2.7$0.065$1.95

মূল কথা: এই দৃশ্যে আউটপুট দিকটাই প্রভাবশালী, তাই max_tokens-এ হেডরুম একটি রিরানের বিরুদ্ধে সস্তা বীমা। কাটা উত্তর যা আপনাকে আবার জেনারেট করতে হয় তার জন্য দুবার অর্থ দিতে হয়। বিলের দুই দিক নিয়ে পটভূমি আছে ইনপুট বনাম আউটপুট টোকেন-এ, এবং দামসহ সম্পূর্ণ মডেল তালিকা আছে প্রাইসিং পেজ-এ।

শিপ করার আগে: একটি সংক্ষিপ্ত চেকলিস্ট#

চেকলিস্ট

আপনার কোডের জন্য ট্রাঙ্কেশন চেকলিস্ট