تنظیم Reasoning Effort در مدلهای LLM محلی
تنظیم Reasoning Effort تعیین میکند مدل پیش از پاسخدهی چقدر فکر کند. در این مقاله بررسی میکنیم چگونه این پارامتر بر زمان تولید توکن و اشغال حافظه در سختافزار شخصی تأثیر میگذارد.
تغییرات در میزان تلاش برای استدلال (Reasoning Effort) در یک LLM محلی
میزان تلاش برای استدلال (Reasoning Effort)، تنظیمی است که به مدل میگوید پیش از پاسخدهی، چه مدت فکر کند. این تنظیم تنها طول بخش استدلال را تغییر میدهد و هیچ چیز دیگری را تحت تأثیر قرار نمیدهد. وزنهای موجود روی دیسک در تمام سطوح یکسان هستند، کوانتیزاسیون (quantisation) تغییری نمیکند و پاسخ از همان forward pass استخراج میشود. آنچه تغییر میکند، تعداد توکنهایی است که مدل ابتدا در فضای یادداشت (scratchpad) خود صرف میکند.
این تمایز به دلیل محل قرارگیری توکنها اهمیت دارد. در یک API میزبانیشده، توکنهای استدلال در صورتحساب لحاظ میشوند. در یک VPS که مالک آن هستید، هزینه این توکنها با زمان تولید (generation time) روی CPU یا GPU خودتان و همچنین اشغال فضای داخل context window پرداخت میشود. مدلی که در بالاترین سطح تلاش قرار دارد، میتواند پیش از ظاهر شدن اولین کلمه از پاسخ، بخش عمده خروجی خود را صرف استدلال کند؛ این موضوع در سختافزار self-hosted، تفاوت بین یک پاسخ دو ثانیهای و یک پاسخ دو دقیقهای است.
محل قرارگیری سطح: قالب چت، نه وزنها
یک مدل تفکر (thinking model) آموزش دیده است تا پیش از ارائه پاسخ نهایی، یک بخش استدلالی تولید کند که معمولاً با تگهای <think> و </think> محصور میشود. سطح تلاش (effort level)، دستوری است که قالب چت مدل در پرامپت مینویسد. آن قالب، یک فایل Jinja است که همراه با مدل عرضه میشود. این فایل متغیری مانند reasoning_effort را میخواند و برای هر مقدار، یک خط در سطح سیستم (system-level) متفاوت رندر میکند؛ مدل نیز آموزش دیده است تا در پاسخ به آن خط، فضای پیشنویس (scratchpad) خود را کوتاه یا بلند کند.
دو نتیجه از این موضوع حاصل میشود. نامهای سطح متعلق به مدل هستند و نه به runtime شما؛ بنابراین نامی که در کارت یک مدل آمده ممکن است برای مدل دیگر هیچ معنایی نداشته باشد. همچنین اگر هر بخشی در زنجیره، قالب چت مدل را با یک قالب عمومی جایگزین کند، متغیر هرگز رندر نمیشود و تنظیمات بدون هیچ خطایی، بیاثر میمانند.
در تاریخ 2026-08-20 بررسی شد که کارت مدل Qwen3.8-27B سه سطح تلاش را مستند کرده است: low، medium و xhigh، که xhigh به عنوان پیشفرض در نظر گرفته شده است. هیچ سطح high وجود ندارد. خودِ قابلیت تفکر با enable_thinking فعال یا غیرفعال میشود که بهصورت پیشفرض روشن است؛ همچنین این کارت preserve_thinking را مستند کرده که آن هم بهصورت پیشفرض روشن است و استدلالهای نوبتهای قبلی را در تاریخچه گفتگو حفظ میکند. gpt-oss بهجای آنها از low، medium و high استفاده میکند. بسیاری از خانوادههای دیگر مدلها فقط یک مقدار بولی (boolean) را میپذیرند و بس. کارت مربوط به نسخهای که دریافت کردهاید را مطالعه کنید، زیرا این نامها استاندارد نیستند. راهاندازی یک مدل 27B روی VPS اولویت دارد. این صفحه درباره تنظیماتی است که پس از پاسخدهی مدل باید اعمال کنید.
چرا تلاش پردازشی بالا در یک VPS هزینه بیشتری دارد
توکنهای خروجی. توکنهای استدلال (Reasoning)، توکنهای تولیدشده هستند. این توکنها همان چرخه رمزگشایی (decode loop) پاسخ نهایی را طی میکنند و با همان سرعتی که سختافزار شما مدیریت میکند، پردازش میشوند. فرض کنید یک وظیفه، 200 توکن پاسخ و 4,000 توکن استدلال تولید میکند. شما در مجموع 4,200 توکن تولید کردهاید، اما کاربر تنها 200 توکن آن را مشاهده میکند. نرخ رمزگشایی شما توسط پهنای باند حافظه و کوانتیزاسیونی که انتخاب کردهاید تعیین میشود، بنابراین تنها اهرم باقیمانده، تعداد توکنها است.
زمان واقعی (Wall clock). کاربر منتظر اولین توکن پاسخ میماند، زیرا هر چیزی پیش از آن، یک صفحه خالی یا یک آیکون بارگذاری است. استدلال ابتدا صادر میشود، بنابراین زمان انتظار تقریباً برابر است با تعداد توکنهای استدلال تقسیم بر نرخ رمزگشایی شما، بهعلاوه زمان پردازش پرامپت. اگر طول استدلال را دو برابر کنید، آن زمان انتظار نیز دو برابر میشود.
زمینه (Context). توکنهای استدلال مانند هر توکن دیگری، پنجره زمینه (context window) را اشغال میکنند. با فعال بودن preserve_thinking، فضای چرکنویس از نوبت اول همچنان در پرامپت نوبت پنجم باقی میماند؛ بنابراین با پر شدن پنجره از هر دو طرف، پردازش پرامپت در هر نوبت کندتر میشود. افزایش num_ctx برای نگهداری آن، حافظه KV cache مصرف میکند که در یک VPS بدون GPU، از رم سیستم شما کسر میشود و ممکن است فضای کافی برای آن نداشته باشید.
چه زمانی سطح استدلال را افزایش دهیم و چه زمانی آن را پایین نگه داریم
برای کارهایی که در آنها یک گام میانی اشتباه، نتیجه نهایی را مخدوش میکند، سطح استدلال را افزایش دهید: محاسبات چندمرحلهای و تبدیل واحد، برنامهریزی برای ویرایش در چندین فایل، کدی که باید کامپایل شود، و مسائل دارای محدودیت که در آنها یک پاسخ باید همزمان چندین شرط را برآورده کند. در این موارد، فضای پیشنویس (scratchpad) در حال انجام کار واقعی است و استفاده از فضای طولانیتر، روشی کمهزینه برای شناسایی خطایی است که مدل در غیر این صورت به آن متعهد میشد.
زمانی که پاسخ از قبل در ورودی موجود است و وظیفه شما صرفاً انتقال آن است، سطح استدلال را پایین نگه دارید. استخراج، دستهبندی، برچسبگذاری، ترجمه، بازنویسی، خلاصهسازی و فرمتدهی همگی در این دسته قرار میگیرند. بخش استدلال در این موارد عمدتاً وظیفه را بازگو میکند و به مدل فرصت میدهد تا با استدلالهای بیهوده، خود را از یک غریزه اولیه درست دور کند.
برای هر نوع تعامل نیز سطح استدلال را پایین نگه دارید. در یک محیط چت یا ویرایشگر، شما در چرخه تعامل حضور دارید؛ بنابراین یک پاسخ سریع که میتوانید آن را اصلاح کنید، بهتر از پاسخ کندی است که باید برای آن منتظر بمانید. این همان مبادله واقعی پشت هدایت یک عامل کدنویسی به سمت یک مدل محلی است: یک عامل، فراخوانیهای کوچک متعددی انجام میدهد و هزینه استدلال برای تکتک آنها محاسبه میشود.
نحوه تنظیم سطح در llama.cpp
ابزار llama.cpp متغیر را مستقیماً در قالب (template) مینویسد؛ این همان زمان اجرا (runtime) است که میتوانید مطمئن باشید سطح مورد نظر اعمال شده است. فایل -m را به سمت GGUF که از قبل در اختیار دارید، نشانه بگیرید.
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 از قالب چت خودِ مدل استفاده میکند و در نسخههای فعلی بهصورت پیشفرض فعال است. --reasoning-effort مقادیر default، minimal، low، medium، high، xhigh یا max را میپذیرد؛ که در آن default به معنای عدم تغییر در پیشفرضِ خودِ قالب است. این لیست مربوط به واژگان (vocabulary) خودِ llama.cpp است و نه مدل؛ بنابراین فقط نامی را وارد کنید که کارت مدل لیست کرده است: سطحی که در قالب تعریف نشده باشد، میتواند در زمان درخواست منجر به خطای قالب شود. --reasoning-format deepseek بخش استدلال (reasoning) را از message.content خارج کرده و به message.reasoning_content منتقل میکند؛ این همان چیزی است که باعث میشود تفکیک در بخش بعدی قابل اندازهگیری باشد.
برای غیرفعال کردن تفکر (thinking) بهجای کوتاهتر کردن آن، متغیر قالب را شخصاً تنظیم کنید:
llama-server -m ./qwen3.8-27b-Q4_K_M.gguf --jinja \
--chat-template-kwargs '{"enable_thinking": false}'--reasoning-budget مکانیزم متفاوتی است. این گزینه بخش استدلال را بر اساس توکن محدود میکند، بهطوری که 0 آن را بلافاصله پایان میدهد و -1 آن را بدون محدودیت باقی میگذارد؛ این روش بهجای درخواست از مدل برای برنامهریزی کوتاهتر، عمل میکند. هر دو فلگ در سطح کل سرور اعمال میشوند. ابزار llama-server گزینه reasoning_effort را بهعنوان یک فیلد در هر درخواست نمیپذیرد، بنابراین ارائه دو سطح تلاش بهصورت همزمان، مستلزم اجرای دو پردازش روی دو پورت متفاوت است.
ابزار vLLM همین متغیر را برای هر درخواست، درون بدنه سازگار با OpenAI در دسترس قرار میدهد:
{"model": "Qwen/Qwen3.8-27B",
"messages": [{"role": "user", "content": "Summarise this changelog in two lines."}],
"chat_template_kwargs": {"reasoning_effort": "medium"}}نحوه تنظیم سطح در Ollama
نرمافزار Ollama دارای فیلد اختصاصی خود به نام think در /api/chat و /api/generate است. این فیلد مقادیر true، false، یا یکی از مقادیر low، medium، high و max را میپذیرد؛ که در این میان max بالاترین سطح ارائهشده توسط مدل را درخواست میکند. قابلیت 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}استدلال در message.thinking و پاسخ در message.content بازگردانده میشود که از قبل برای شما تفکیک شدهاند. در یک نشست تعاملی ollama run، دستورات /set think و /set nothink این قابلیت را بدون نیاز به راهاندازی مجدد، تغییر میدهند.
اکنون به عدم تطابق توجه کنید. واژگان Ollama شامل low، medium، high و max است. قالب Qwen3.8 مقادیر low، medium و xhigh را تعریف میکند. باید نگاشتی بین این دو وجود داشته باشد، و از آنجا که مدل Ollama قالبی را درون تگ خود بستهبندی میکند (بهجای استفاده از فایل Jinja موجود در مخزن اصلی)، اینکه آیا سطح انتخابی شما به مدل میرسد یا خیر، به همان قالب بستهبندیشده وابسته است. فرض نکنید که تنظیمات اعمال شدهاند. اندازهگیری این موضوع حدود 1 دقیقه زمان میبرد.
چگونه اندازهگیری کنیم که آیا سطح (level) واقعاً اعمال شده است
یک پرامپت مشابه را در بیش از یک سطح با temperature برابر با 0 ارسال کنید، سپس تعداد توکنها را مقایسه کنید. در اینجا jq بدنه را میسازد تا نیازی نباشد کوتیشنها را بهصورت دستی 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))
}'
doneeval_count شامل تمام توکنهای تولیدشده، از جمله استدلال (reasoning) است، بنابراین اختلاف بین دو سطح تقریباً تماماً مربوط به استدلال است. thinking_chars این تفکیک را مستقیماً به شما میدهد. دو مورد باید صادق باشد: اعداد بین سطوح تغییر کنند و پاسخ در سطح پایینتر همچنان صحیح باقی بماند. اگر eval_count در هر سه اجرا در محدوده نویز (noise) باقی بماند، سطح نادیده گرفته شده است و راهحل، استفاده از یک runtime است که آن را عبور دهد، نه تغییر نام سطح.
زمان کل تنها نیمی از ماجراست، بنابراین فاصله تا اولین توکن پاسخ را با استریم کردن و توقف در اولین قطعه غیرخالی content اندازهگیری کنید. این کار به 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، همان اعداد در داخل پاسخ بازگردانده میشوند و نیازی به محاسبات shell نیست:
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
}'این کار را روی سیستم خودتان انجام دهید. مقایسه تلاشهای منتشرشده روی سختافزاری انجام شده که متعلق به شما نیست، و نرخ decode شما همان عاملی است که تعداد توکن را به ثانیه تبدیل میکند. اندازهگیری توکن بر ثانیه روی سرور شخصی آن عامل را به شما میدهد: توکنهای استدلال تقسیم بر نرخ decode شما، همان زمانی است که به انتظار شما اضافه شده است.
چه چیزی اشتباه پیش میرود
پاسخ ناقص است، یا content خالی است در حالی که thinking پر است. محدودیت تولید توسط بخش استدلال (reasoning) مصرف شده است. پارامتر num_predict در Ollama کل خروجی، شامل استدلال را محدود میکند و چون استدلال در ابتدا قرار دارد، سقف 512 توکن در تلاش (effort) بالا میتواند پاسخ را پیش از شروع بخش اصلی قطع کند. Ollama در چنین پاسخی وضعیت "done_reason": "length" را گزارش میدهد. سقف را افزایش دهید یا میزان تلاش را کاهش دهید. نحوه شمارش توکنها توسط num_predict این تعامل را بهطور دقیق پوشش میدهد.
تغییر سطح هیچ تأثیری ندارد. تعداد توکنها در تمام سطوح یکسان است. یا runtime متغیر را ارسال نمیکند، یا قالب (template) آن را نمیخواند. قالبی که runtime شما در حال حاضر استفاده میکند را بررسی کنید، نه قالبی که در مخزن اصلی وجود دارد. ابزار llama.cpp با استفاده از --jinja و --chat-template-kwargs متغیر را بهصورت دستی وارد میکند، بنابراین یک کنترل مناسب است: اگر سطح در آنجا کار میکند و در جای دیگر خیر، مدل سالم است و runtime دیگر آن را نادیده میگیرد.
نام یک سطح رد میشود. خطای قالب در زمان درخواست، یا شکست در اولین پیام با وجود سرور سالم، معمولاً به این معنی است که سطحی را ارسال کردهاید که قالب تعریف نکرده است؛ برای مثال ارسال high به مدلی که در کارت آن فقط سطوح low، medium و xhigh لیست شدهاند.
چتهای چندمرحلهای در هر مرحله کندتر میشوند. استدلالهای قدیمی در تاریخچه باقی میمانند. اگر مدل پشتیبانی میکند، preserve_thinking را روی false تنظیم کنید، یا فیلد thinking را از پیامهایی که بازمیگردانید حذف کنید. در غیر این صورت، پردازش پرامپت در هر مرحله افزایش مییابد در حالی که طول پاسخها ثابت میماند.
کیفیت در تلاش پایین برای وظیفهای که فکر میکردید ساده است، افت میکند. برخی استخراجها، در واقع استخراج نیستند. اگر ورودی نیاز به تبدیل واحد یا اعمال یک قاعده به ترتیب خاص داشته باشد، این یک وظیفه استدلالی با خروجی کوتاه است. برای آن فراخوانی خاص، سطح را افزایش دهید و نه برای کل سرور.
اجرای همزمان دو سطح
نرمافزار llama.cpp سطح را در زمان راهاندازی تعیین میکند، بنابراین سیستمی که همزمان به یک ویرایشگر و یک پردازش دستهای شبانه سرویس میدهد، به دو پردازش روی دو پورت مجزا نیاز دارد که هر کدام --reasoning-effort خاص خود را داشته باشند. دو پردازش به معنای وجود دو نسخه از وزنها در حافظه است، مگر اینکه زمان اجرای این دو کار را از هم جدا کنید. در یک VPS، ارزانترین روش معمولاً اجرای یک سرور سبک برای کارهایی است که کاربر منتظر پاسخ آنهاست، به همراه یک اجرای سنگین زمانبندیشده برای کارهایی که کسی بهصورت زنده آنها را مشاهده نمیکند. مطلب وقتی چندین کاربر از یک مدل محلی استفاده میکنند چه اتفاقی میافتد در اینجا نیز صدق میکند: توکنهای استدلال (reasoning tokens) بخشی از عملیات رمزگشایی (decode) هستند، بنابراین افزایش سطح تلاش، ظرفیت همزمانی مؤثر شما را تقریباً به همان نسبتی که تعداد توکنها را افزایش میدهد، کاهش میدهد.
FAQ
بهطور پیشفرض از چه سطح تلاش استدلالی (reasoning effort) استفاده کنم؟
از پایینترین سطحی که مدل ارائه میدهد شروع کنید و تنها برای وظایفی که مشاهده کردهاید در آن شکست میخورد، آن را افزایش دهید. چندین مدل استدلالی با سطح پیشفرض بالا عرضه میشوند و Qwen3.8-27B از اوت 2026 بهطور پیشفرض روی xhigh، یعنی بالاترین سطح خود، تنظیم شده است. این مقدار پیشفرض برای کسب نتایج بهتر در جداول بنچمارک انتخاب شده است، در حالی که جداول بنچمارک هزینهای بابت زمان صرفشده دریافت نمیکنند. روی سختافزار خودتان، شما هزینه را با ثانیهها میپردازید؛ بنابراین سطوح بالاتر را به گزینهای تبدیل کنید که برای هر وظیفه بهصورت انتخابی فعال میکنید، نه تنظیمی که هر درخواست بهطور خودکار از آن ارثبری کند.
آیا توکنهای استدلالی (reasoning tokens) در پنجره کانتکست (context window) محاسبه میشوند؟
بله. اینها توکنهای معمولی در خروجی هستند و در کنار سایر محتواها در پنجره کانتکست قرار میگیرند. اینکه آیا این توکنها در نوبت بعدی باقی میمانند یا خیر، به runtime و مدل بستگی دارد. مستندات مدل Qwen3.8 به preserve_thinking اشاره دارد که بهطور پیشفرض فعال است و استدلالهای قبلی را در تاریخچه حفظ میکند؛ بنابراین یک گفتگوی طولانی، تمام چرکنویسهای تولیدشده را با خود حمل میکند. آن را روی false تنظیم کنید یا فیلد thinking را از پیامهایی که دوباره ارسال میکنید حذف نمایید تا پردازش پرامپت بیش از حد رشد نکند.
چرا تغییر سطح استدلال تأثیری بر تعداد توکنهای من ندارد؟
این تنظیم به قالب چت (chat template) نمیرسد. سطح استدلال یک متغیر قالب است، بنابراین تنها در صورتی کار میکند که runtime آن را ارسال کند و قالب بستهبندیشده آن را بخواند. برخی از runtimeها به جای استفاده از فایل Jinja موجود در مخزن اصلی، قالب اختصاصی خود را همراه با مدل عرضه میکنند و در نتیجه، متغیر بدون نمایش هیچ خطایی نادیده گرفته میشود. برای بررسی این موضوع، همان پرامپت را در پایینترین و بالاترین سطح با مقدار 0 برای temperature ارسال کنید و eval_count را مقایسه نمایید. اگر تعداد توکنها با در نظر گرفتن نویز یکسان باشد، سطح استدلال نادیده گرفته شده است.
آیا کاهش تلاش استدلالی باعث کاهش دقت مدل میشود؟
این موضوع به نوع وظیفه بستگی دارد و بهتر است بهجای فرض کردن، آن را اندازهگیری کنید. در مواردی که پاسخ از قبل در ورودی موجود است، مانند استخراج یا بازنویسی متن، یک چرکنویس کوتاهتر معمولاً تغییری ایجاد نمیکند. در مواردی که یک گام میانی باید پیش از گام نهایی درست باشد، مانند محاسبات چندمرحلهای یا کدی که باید کامپایل شود، دقت با چرکنویس کوتاهتر کاهش مییابد. مجموعهای از 20 پرامپت از حجم کاری واقعی خود بسازید، آنها را در دو سطح با مقدار 0 برای temperature اجرا کنید و پاسخهای اشتباه را بشمارید. این عدد مختص حجم کاری شماست و هیچ جدول منتشرشدهای نمیتواند آن را به شما ارائه دهد.