SSD Nodes Learn 🎉 VPS $5.50/মাস থেকে
নির্দেশিকা Matt Connorদ্বারা Matt Connor · আপডেট করা হয়েছে 2026-08-21

স্থানীয় LLM-এ reasoning effort কীভাবে কাজ করে

reasoning effort শুধু scratchpad token-এর সংখ্যা বদলায়, weights বা quantisation নয়। নিজের CPU বা GPU-তে প্রতিটি level-এর আসল সময়, context খরচ ও মাপার পদ্ধতি জানুন।

স্থানীয় LLM-এ reasoning effort কী পরিবর্তন করে

Reasoning effort হলো এমন একটি সেটিং, যা মডেলকে উত্তর দেওয়ার আগে কতক্ষণ চিন্তা করতে হবে তা নির্ধারণ করে। এটি reasoning segment-এর দৈর্ঘ্য পরিবর্তন করে; অন্য কিছু নয়। প্রতিটি level-এ disk-এ থাকা weights অভিন্ন, quantisation অভিন্ন, এবং উত্তর একই forward pass থেকে তৈরি হয়। পরিবর্তন হয় শুধু মডেল প্রথমে নিজের scratchpad-এ কত token ব্যয় করে।

এই পার্থক্য গুরুত্বপূর্ণ, কারণ ওই token-গুলো কোথায় ব্যবহৃত হয় তা বিবেচ্য। hosted API-তে reasoning token-এর জন্য invoice আসে। আপনার মালিকানাধীন VPS-এ এগুলোর মূল্য দিতে হয় আপনার নিজের CPU বা GPU-র generation time এবং context window-এর ভেতরের স্থান দিয়ে। সর্বোচ্চ effort-এ রাখা একটি মডেল উত্তরটির প্রথম শব্দ দেখানোর আগে output-এর বেশির ভাগ reasoning-এ ব্যয় করতে পারে। self-hosted hardware-এ এর ফলে উত্তর পেতে দুই সেকেন্ডের বদলে দুই মিনিট লাগতে পারে।

স্তরটি কোথায় নির্ধারিত হয়: weights-এ নয়, chat template-এ

একটি thinking model-কে final answer দেওয়ার আগে সাধারণত <think> এবং </think> tag-এর মধ্যে reasoning segment তৈরি করার জন্য প্রশিক্ষণ দেওয়া হয়। effort level হলো একটি instruction, যা model-এর chat template prompt-এর মধ্যে লিখে দেয়। এই template হলো model-এর সঙ্গে সরবরাহ করা একটি Jinja file। এটি reasoning_effort-এর মতো একটি variable পড়ে এবং প্রতিটি value-এর জন্য আলাদা system-level line তৈরি করে। সেই line অনুযায়ী model-কে তার scratchpad ছোট বা বড় করতে প্রশিক্ষণ দেওয়া হয়েছে।

এর দুটি ফল আছে। level-এর নাম আপনার runtime-এর নয়, model-এর অন্তর্ভুক্ত। তাই একটি model-এর card-এ থাকা কোনো নাম অন্য model-এর ক্ষেত্রে অর্থহীন হতে পারে। আবার কোনো chain যদি model-এর chat template-এর পরিবর্তে একটি generic template ব্যবহার করে, তাহলে variable-টি কখনো render হয় না এবং setting-টি নীরবে কোনো কাজ করে না।

2026-08-20 তারিখে যাচাই করা Qwen3.8-27B model card-এ তিনটি effort level নথিভুক্ত আছে: low, medium এবং xhigh; default হলো xhighhigh নেই। Thinking চালু বা বন্ধ করা হয় enable_thinking দিয়ে; এটি default অবস্থায় চালু থাকে। Card-এ preserve_thinking-ও নথিভুক্ত আছে, যা default অবস্থায় চালু থাকে এবং আগের turn-এর reasoning conversation history-তে সংরক্ষণ করে। gpt-oss-এর ক্ষেত্রে low, medium এবং high ব্যবহার করা হয়। আরও অনেক model family শুধু একটি boolean গ্রহণ করে, অন্য কোনো level নয়। আপনি যে exact version download করেছেন, তার card দেখুন, কারণ এগুলো কোনো standard নয়। VPS-এ 27B model চালু করা আগে পড়ুন। এই page-এ model উত্তর দেওয়া শুরু করার পর কী সেট করতে হবে, তা ব্যাখ্যা করা হয়েছে।

VPS-এ বেশি reasoning-এর খরচ কেন বেশি

Output tokens। Reasoning tokens-ও উত্তর তৈরির tokens-এর মতো একই decode loop-এর মধ্য দিয়ে তৈরি হয়। এগুলো আপনার হার্ডওয়্যার যে tokens per second গতিতে সামলাতে পারে, সেই একই গতিতে তৈরি হয়। ধরুন, একটি task-এ 200 tokens-এর উত্তর এবং 4,000 tokens-এর reasoning তৈরি হয়। আপনি মোট 4,200 tokens তৈরি করেছেন, কিন্তু পাঠক এর মধ্যে 200 tokens দেখেছেন। আপনার decode rate memory bandwidth এবং আপনি যে quantisation বেছে নিয়েছেন—এই দুটির ওপর নির্ভর করে। তাই নিয়ন্ত্রণ করার একমাত্র উপায় হলো token count।

Wall clock। একজন ব্যক্তি উত্তর-এর প্রথম token-এর জন্য অপেক্ষা করেন। তার আগে যা দেখা যায়, তা হলো ফাঁকা screen বা ছোট হয়ে থাকা spinner। Reasoning আগে তৈরি হয়। তাই অপেক্ষার সময় মোটামুটি reasoning token count-কে আপনার decode rate দিয়ে ভাগ করার ফলের সঙ্গে prompt processing-এর সময় যোগ করলে যত হয়, তত। Reasoning-এর দৈর্ঘ্য দ্বিগুণ করলে এই অপেক্ষার সময়ও দ্বিগুণ হয়।

Context। অন্য যেকোনো token-এর মতো reasoning tokens-ও context window দখল করে। preserve_thinking চালু থাকলে turn one-এর scratchpad turn five-এও prompt-এর মধ্যে থাকে। ফলে window দুই দিক থেকে পূর্ণ হওয়ার সঙ্গে সঙ্গে প্রতিটি turn-এ prompt processing ধীর হয়। এটি ধরে রাখার জন্য num_ctx বাড়ালে KV cache memory লাগে। GPU ছাড়া VPS-এ এই memory system RAM থেকে নিতে হয়, আর আপনার কাছে অতিরিক্ত RAM নাও থাকতে পারে।

কখন স্তর বাড়াবেন এবং কখন কম রাখবেন

যে কাজে কোনো ভুল মধ্যবর্তী ধাপ পুরো ফলাফলকে ভুল করে দিতে পারে, সেখানে স্তর বাড়ান। এর মধ্যে বহু-ধাপের arithmetic ও unit conversion, একাধিক file জুড়ে edit-এর পরিকল্পনা, compile হওয়া বাধ্যতামূলক এমন code এবং এমন constraint problem রয়েছে যেখানে একটিমাত্র উত্তরকে একসঙ্গে কয়েকটি শর্ত পূরণ করতে হয়। এসব ক্ষেত্রে scratchpad সত্যিই কাজ করে। একটু দীর্ঘ scratchpad ব্যবহার করলে এমন ভুল ধরা পড়তে পারে, যা না হলে model সেটিই চূড়ান্ত উত্তর হিসেবে দিয়ে দিত।

যখন উত্তরটি input-এর মধ্যেই রয়েছে এবং কাজটি শুধু সেটি স্থানান্তর করা, তখন স্তর কম রাখুন। Extraction, classification, tagging, translation, rewriting, summarising এবং formatting এই শ্রেণিতে পড়ে। Reasoning অংশটি সাধারণত শুধু task-টি পুনরাবৃত্তি করে। এতে model-এর সঠিক প্রথম অনুমান থেকে সরে গিয়ে নিজের উত্তর নিয়ে অযথা তর্ক করার সুযোগ তৈরি হয়।

Interactive কাজের ক্ষেত্রেও স্তর কম রাখুন। Chat box বা editor-এ আপনি প্রক্রিয়ার মধ্যেই আছেন। তাই ধীর উত্তর অপেক্ষা করার চেয়ে দ্রুত উত্তর নিয়ে প্রয়োজনে সংশোধন করা বেশি কার্যকর। একটি local model-এ coding agent নির্দেশ করার পেছনের প্রকৃত trade-off এটিই: একটি agent অনেক ছোট call করে, এবং প্রতিটি call-এর জন্য reasoning tax দিতে হয়।

llama.cpp-তে level নির্ধারণের পদ্ধতি

llama.cpp variable-টি সরাসরি template-এ লেখে। তাই level পৌঁছেছে কি না নিশ্চিতভাবে যাচাই করার জন্য এটিই runtime স্তর। আপনার কাছে থাকা GGUF-টির দিকে -m নির্দেশ করুন।

llama-server -m ./qwen3.8-27b-Q4_K_M.gguf \
  --jinja \
  --reasoning-effort medium \
  --reasoning-format deepseek \
  -c 32768 \
  --host 127.0.0.1 --port 8080

--jinja মডেলের নিজস্ব chat template ব্যবহার করে এবং বর্তমান build-গুলোতে এটি ডিফল্টভাবে সক্রিয় থাকে। --reasoning-effort-এ default, minimal, low, medium, high, xhigh অথবা max গ্রহণ করা হয়। এখানে default-এর অর্থ হলো template-এর নিজস্ব default অপরিবর্তিত রাখা। এই তালিকা llama.cpp-এর vocabulary, মডেলের নয়। তাই model card-এ থাকা নামই ব্যবহার করুন। Template-এ সংজ্ঞায়িত নয় এমন level দিলে request চলার সময় template error হতে পারে। --reasoning-format deepseek reasoning-কে message.content থেকে সরিয়ে message.reasoning_content-এ রাখে। এর ফলে পরের section-এ দুই অংশের পরিমাণ আলাদাভাবে মাপা যায়।

Reasoning সংক্ষিপ্ত না করে বন্ধ করতে template variable নিজে সেট করুন:

llama-server -m ./qwen3.8-27b-Q4_K_M.gguf --jinja \
  --chat-template-kwargs '{"enable_thinking": false}'

--reasoning-budget একটি ভিন্ন mechanism। এটি reasoning segment-এর token সংখ্যা সীমাবদ্ধ করে। 0 দিলে segment সঙ্গে সঙ্গে শেষ হয়, আর -1 দিলে কোনো সীমা থাকে না। এটি মডেলকে ছোট reasoning plan করতে বলার সমান নয়। উভয় flag পুরো server-এর জন্য প্রযোজ্য। llama-server per-request field হিসেবে reasoning_effort গ্রহণ করে না। তাই একই সময়ে দুইটি effort level পরিবেশন করতে হলে দুইটি port-এ দুইটি process চালাতে হবে।

vLLM OpenAI-compatible body-এর মধ্যে প্রতি request-এ একই variable প্রকাশ করে:

{"model": "Qwen/Qwen3.8-27B",
 "messages": [{"role": "user", "content": "Summarise this changelog in two lines."}],
 "chat_template_kwargs": {"reasoning_effort": "medium"}}

Ollama-তে level কীভাবে সেট করবেন

Ollama-তে think নামে নিজস্ব একটি field আছে, যা /api/chat এবং /api/generate-এ ব্যবহৃত হয়। এতে true, false অথবা low, medium, highmax-এর যেকোনো একটি গ্রহণ করা হয়। এর মধ্যে max মডেল যে সর্বোচ্চ level সমর্থন করে, সেটি ব্যবহার করতে বলে। যে মডেলগুলো এটি সমর্থন করে, সেগুলোতে Thinking ডিফল্টভাবে চালু থাকে।

ollama run qwen3.8:27b --think=low "Draft a one line commit message for a README typo fix"
{"model": "qwen3.8:27b",
 "messages": [{"role": "user", "content": "Which HTTP status code means the request body was too large?"}],
 "think": "low",
 "stream": false}

Reasoning message.thinking-এ এবং উত্তর message.content-এ ফেরত আসে; এগুলো আপনার জন্য আগে থেকেই আলাদা করা থাকে। একটি interactive ollama run session-এর মধ্যে /set think এবং /set nothink ব্যবহার করে restart ছাড়াই এটি চালু বা বন্ধ করা যায়।

এখন অমিলটি লক্ষ্য করুন। Ollama-র vocabulary হলো low, medium, high এবং max। Qwen3.8-এর template-এ low, medium এবং xhigh নির্ধারিত আছে। একটিকে অন্যটির সঙ্গে map করতেই হবে। Ollama model মূল repository-র Jinja file ব্যবহার না করে নিজের tag-এর মধ্যে packaged template বহন করে। তাই আপনার নির্ধারিত level model-এ পৌঁছাবে কি না, তা ওই packaged template-এর ওপর নির্ভর করে। এটি কাজ করেছে বলে ধরে নেবেন না। পরীক্ষা করতে প্রায় এক মিনিট লাগে।

বাস্তবে level কার্যকর হয়েছে কি না কীভাবে মাপবেন

temperature-কে 0 রেখে একই prompt একাধিক level-এ পাঠান। এরপর token count তুলনা করুন। এখানে jq body তৈরি করে, তাই quote হাতে escape করতে হবে না।

for level in low medium max; do
  body=$(jq -n --arg lvl "$level" '{
    model: "qwen3.8:27b",
    messages: [{role: "user", content: "A pump fills a 4500 litre tank in 25 minutes. A second pump is 40 percent slower. How long do both together take? Answer in minutes."}],
    think: $lvl,
    stream: false,
    options: {temperature: 0, num_ctx: 8192}
  }')
  echo "== $level"
  curl -s http://localhost:11434/api/chat -d "$body" | jq '{
    thinking_chars: (.message.thinking // "" | length),
    answer_chars: (.message.content | length),
    eval_count: .eval_count,
    seconds: (.total_duration / 1e9),
    tok_per_sec: (.eval_count / (.eval_duration / 1e9))
  }'
done

eval_count-এর মধ্যে reasoning-সহ উৎপন্ন প্রতিটি token অন্তর্ভুক্ত থাকে। তাই দুটি level-এর মধ্যকার পার্থক্য প্রায় সম্পূর্ণ reasoning-এর কারণে হয়। thinking_chars সরাসরি এই বিভাজন দেখায়। দুটি বিষয় সত্য হওয়া উচিত: level পরিবর্তনের সঙ্গে সংখ্যাগুলো বদলাবে, এবং lower level-এও উত্তর সঠিক থাকবে। তিনটি run-এই eval_count যদি noise-এর সীমার মধ্যে থাকে, তাহলে level উপেক্ষা করা হচ্ছে। সেক্ষেত্রে level-এর নাম বদলানোর বদলে সেটি pass করে এমন runtime ব্যবহার করতে হবে।

Total time কেবল সমস্যার অর্ধেক। তাই streaming ব্যবহার করে প্রথম non-empty content chunk আসা পর্যন্ত থামুন এবং প্রথম answer token পর্যন্ত সময়ের ব্যবধান মাপুন। এর জন্য jq এবং bc প্রয়োজন।

start=$(date +%s.%N)
curl -sN http://localhost:11434/api/chat -d '{
  "model": "qwen3.8:27b",
  "messages": [{"role": "user", "content": "Explain what a reverse proxy does, in three sentences."}],
  "think": "low",
  "stream": true
}' |
while IFS= read -r line; do
  if [ -n "$(printf '%s' "$line" | jq -r '.message.content // ""')" ]; then
    echo "first answer token after $(echo "$(date +%s.%N) - $start" | bc)s"
    break
  fi
done

এটি low-এ চালান, তারপর max-এ আবার চালান। এই পার্থক্যই আপনি যে অতিরিক্ত অপেক্ষার সময় কিনছেন। llama.cpp-এ একই সংখ্যাগুলো response-এর ভেতরেই ফেরত আসে। shell arithmetic প্রয়োজন হয় না:

curl -s http://localhost:8080/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{"model": "local", "temperature": 0,
       "messages": [{"role": "user", "content": "A pump fills a 4500 litre tank in 25 minutes. A second pump is 40 percent slower. How long do both together take?"}]}' | jq '{
  reasoning_chars: (.choices[0].message.reasoning_content // "" | length),
  answer_chars: (.choices[0].message.content | length),
  predicted_n: .timings.predicted_n,
  tok_per_sec: .timings.predicted_per_second
}'

এটি নিজের box-এ করুন। প্রকাশিত effort comparison এমন hardware-এ মাপা হয়েছিল যা আপনার নয়। আপনার decode rate-ই token count-কে seconds-এ রূপান্তর করে। নিজের server-এ প্রতি সেকেন্ডে token মাপা নিবন্ধে এই মানটি পাওয়া যাবে: reasoning token-কে আপনার decode rate দিয়ে ভাগ করলে আপনি যে অতিরিক্ত অপেক্ষার সময় যোগ করেছেন, তা পাওয়া যায়।

কী সমস্যা হয়

উত্তরটি কেটে যায়, অথবা content খালি থাকে কিন্তু thinking পূর্ণ থাকে। Reasoning-এর কারণে generation limit শেষ হয়ে গেছে। Ollama-এর num_predict পুরো generation-এর সীমা নির্ধারণ করে, যার মধ্যে reasoning-ও অন্তর্ভুক্ত। Reasoning আগে চলে, তাই high effort-এ 512 tokens-এর cap উত্তর শুরু হওয়ার আগেই generation শেষ করে দিতে পারে। Ollama ওই response-এ "done_reason": "length" report করে। cap বাড়ান অথবা effort কমান। num_predict কীভাবে tokens গণনা করে-এ এই পারস্পরিক প্রভাব বিস্তারিত ব্যাখ্যা করা হয়েছে।

Level পরিবর্তন করলেও কিছু হয় না। প্রতিটি level-এ token count একই থাকে। হয় runtime variable পাঠাচ্ছে না, নয়তো template এটি পড়ছে না। মূল repository-র template নয়, runtime যে template বাস্তবে ব্যবহার করছে সেটি পরীক্ষা করুন। --jinja এবং --chat-template-kwargs-সহ llama.cpp variable-টি হাতে লিখে বসায়, তাই এটি একটি ভালো control। সেখানে level কাজ করলে কিন্তু অন্য কোথাও কাজ না করলে model ঠিক আছে এবং অন্য runtime variable-টি বাদ দিচ্ছে।

একটি level name প্রত্যাখ্যাত হয়। Request-এর সময় template error, অথবা অন্যথায় সুস্থ server-এ প্রথম message পাঠানোর সময় failure—সাধারণত এর অর্থ হলো আপনি এমন একটি level পাঠিয়েছেন যা template-এ সংজ্ঞায়িত নেই। যেমন, এমন model-এ high পাঠানো যার card-এ শুধু low, medium এবং xhigh তালিকাভুক্ত আছে।

Multi-turn chat-এর প্রতিটি turn ধীর হয়ে যায়। পুরোনো reasoning history-তে রাখা হচ্ছে। Model এটি সমর্থন করলে preserve_thinking-কে false সেট করুন, অথবা ফেরত পাঠানো messages থেকে thinking field বাদ দিন। অন্যথায় প্রতিটি turn-এ prompt processing বাড়তে থাকবে, কিন্তু উত্তরের দৈর্ঘ্য একই থাকবে।

আপনার কাছে সহজ মনে হওয়া task-এ low effort ব্যবহার করলে quality কমে যায়। কিছু extraction প্রকৃতপক্ষে extraction নয়। Input-এ unit conversion বা ধারাবাহিকভাবে কোনো rule প্রয়োগের প্রয়োজন হলে সেটি সংক্ষিপ্ত output-সহ একটি reasoning task। পুরো server-এর জন্য নয়, শুধু ওই call-এর জন্য level বাড়ান।

একসঙ্গে দুটি স্তর চালানো

llama.cpp চালু হওয়ার সময় level নির্ধারণ করে। তাই একই server-এ editor এবং nightly batch job দুটিই চালাতে হলে দুটি port-এ দুটি process চালাতে হবে, এবং প্রতিটির নিজস্ব --reasoning-effort থাকতে হবে। দুটি process চালালে memory-তে weights-এর দুটি copy-ও থাকে, যদি না job-গুলোকে সময়ের ব্যবধানে চালান। একটি VPS-এ সাধারণত কম খরচের ব্যবস্থা হলো, যেসব কাজের ফলাফলের জন্য কেউ অপেক্ষা করছে সেগুলোর জন্য কম effort-এর server রাখা, এবং কেউ পর্যবেক্ষণ করছে না এমন কাজের জন্য scheduled higher effort run চালানো। একটি local model একাধিক user ব্যবহার করলে কী ঘটে-এ বর্ণিত বিষয় এখানেও প্রযোজ্য: reasoning token হলো decode কাজ। তাই effort বাড়ালে token count যে অনুপাতে বাড়ে, কার্যকর concurrency-ও প্রায় একই অনুপাতে কমে।

FAQ

ডিফল্টভাবে কোন reasoning effort level ব্যবহার করা উচিত?

মডেল যে সর্বনিম্ন level দেয়, সেটি দিয়ে শুরু করুন। যেসব কাজে এটি ব্যর্থ হয়েছে, শুধু সেসব কাজে level বাড়ান। কয়েকটি thinking model উচ্চ default নিয়ে release হয়। August 2026 অনুযায়ী Qwen3.8-27B-এর default হলো xhigh, যা এর সর্বোচ্চ level। Benchmark table-এ ভালো ফল দেখানোর জন্য এই default নির্ধারিত হয়। Benchmark table সময়ের জন্য কোনো খরচ নেয় না। কিন্তু নিজের hardware-এ আপনাকে সেকেন্ডের খরচ দিতে হয়। তাই উচ্চ level-কে প্রতিটি request-এর default না করে, task অনুযায়ী আলাদাভাবে চালু করুন।

reasoning token কি context window-এর সীমায় গণনা হয়?

হ্যাঁ। এগুলো output-এর সাধারণ token এবং অন্যান্য সব কিছুর সঙ্গে context window-এ থাকে। পরের turn-এ এগুলো থেকে যায় কি না, তা runtime ও model-এর ওপর নির্ভর করে। Qwen3.8-এর card-এ preserve_thinking নথিভুক্ত আছে, যা defaultভাবে চালু থাকে। এটি আগের reasoning history-তে রেখে দেয়। ফলে দীর্ঘ conversation-এ model তৈরি করা প্রতিটি scratchpad থেকে যায়। এটিকে false করুন, অথবা replay করা messages থেকে thinking field বাদ দিন। তাহলে prompt processing-এর আকার আর বাড়বে না।

thinking level পরিবর্তন করলেও token count-এ কোনো পার্থক্য হয় না কেন?

Setting-টি chat template-এ পৌঁছাচ্ছে না। Level-টি একটি template variable। তাই runtime এটি পাঠালে এবং packaged template এটি পড়লে তবেই কাজ করে। কিছু runtime মূল repository-এর Jinja file ব্যবহার না করে model-এর সঙ্গে নিজস্ব template release করে। তখন variable-টি কোথাও কোনো error না দেখিয়েই বাদ পড়ে। এটি যাচাই করতে সর্বনিম্ন এবং সর্বোচ্চ level-এ একই prompt পাঠান, temperature 0 রেখে eval_count তুলনা করুন। Count-গুলো noise-এর সীমার মধ্যে একই হলে level-টি উপেক্ষা করা হচ্ছে।

reasoning effort কমালে কি model কম নির্ভুল হয়?

এটি task-এর ওপর নির্ভর করে। অনুমান না করে এটি মেপে দেখা উচিত। Input-এ উত্তর আগে থেকেই থাকলে, যেমন extraction বা rewriting-এর ক্ষেত্রে, ছোট scratchpad সাধারণত কোনো পরিবর্তন করে না। কিন্তু final answer দেওয়ার আগে কোনো intermediate step সঠিক হওয়া দরকার হলে, যেমন multi-step arithmetic বা compile করতে হবে এমন code-এর ক্ষেত্রে, ছোট scratchpad-এ accuracy কমে যায়। আপনার বাস্তব workload থেকে 20টি prompt নিয়ে একটি set তৈরি করুন। দুটি level-এ সেগুলো চালান, temperature 0 রাখুন, এবং ভুল answer গুনুন। এই সংখ্যা আপনার workload-এর জন্য নির্দিষ্ট। কোনো published table আপনাকে এই সংখ্যা দিতে পারে না।