افزایش طول کانتکست در Ollama با تنظیم پارامتر num_ctx
اگر پرامپتهای طولانی شما در Ollama قطع میشود، باید num_ctx را افزایش دهید. در این راهنما نحوه تنظیم این پارامتر در Modelfile یا درخواست API و محاسبه میزان RAM مورد نیاز را میخوانید.
پارامتر num_ctx چه کاری انجام میدهد و چرا پرامپت طولانی شما قطع میشود
طول کانتکست در Ollama تعداد توکنهایی است که یک مدل بارگذاریشده میتواند بهطور همزمان در حافظه نگه دارد و num_ctx گزینهای است که این مقدار را تعیین میکند. Ollama بهصورت پیشفرض مقداری را انتخاب میکند که بسیار کمتر از حداکثر ظرفیت اعلامشده توسط مدل است؛ بنابراین، پرامپتهای طولانی پیش از آنکه مدل بتواند آنها را بخواند، قطع میشوند. هیچ بخشی از پاسخ مدل، شما را از وقوع این اتفاق مطلع نمیکند.
مدل Llama 3.1 8B در کتابخانه مدلهای Ollama با پنجره کانتکست 128k فهرست شده است. یک سرور معمولی چنین ظرفیتی را در اختیار شما قرار نمیدهد. مستندات خود Ollama در صفحات مختلف، مقادیر پیشفرض متفاوتی را ذکر کردهاند: بخش FAQ عدد 4096 توکن را بیان میکند، مرجع Modelfile میگوید num_ctx بهطور پیشفرض روی 2048 تنظیم شده است و صفحه طول کانتکست میگوید مقدار پیشفرض بر اساس VRAM (حافظه ویدیویی) موجود انتخاب میشود: 4k برای کمتر از 24 GiB، 32k برای بازه 24 تا 48 GiB و 256k برای مقادیر بالاتر از آن. هر یک از این موارد در نسخههای خاصی از نرمافزار صادق بودهاند. این تضاد، درس مفیدی به ما میدهد: بهجای اعتماد به هر صفحهای، از جمله همین صفحه، مقدار را مستقیماً از سرور در حال اجرای خود بخوانید.
قطع شدن متن (Truncation) بیسروصدا انجام میشود، زیرا مدل همچنان پاسخ میدهد و پاسخ نیز از نظر متنی منسجم به نظر میرسد. این پاسخ از انتهای ورودی شما نوشته شده است. خلاصهای که نیمه اول یک سند را نادیده میگیرد، ممکن است نشاندهنده ضعف مدل به نظر برسد، اما معمولاً دلیل آن کوچک بودن پنجره کانتکست است.
بررسی طول کانتکست Ollama که عملاً روی سرور شما اعمال شده است
بررسی که روی هر build جواب میدهد prompt_eval_count است، یعنی تعداد توکنهای prompt که سرور گزارش میدهد پردازش کرده است. بیش از ظرفیت کانتکست، داده ارسال کنید؛ در این صورت آن عدد روی همان حد مجاز متوقف میشود.
sudo apt update && sudo apt install -y jq
LONG=$(python3 -c "print('the quick brown fox jumps over the lazy dog. ' * 2000)")
jq -n --arg p "$LONG" '{model:"llama3.1:8b", prompt:$p, stream:false, options:{num_ctx:4096}}' |
curl -s http://localhost:11434/api/generate -d @- |
jq '{prompt_eval_count, prompt_eval_duration}'آن prompt حدود 18,000 کلمه است، بنابراین بسیار بیشتر از 4096 توکن است. مقدار prompt_eval_count به جای نزدیک شدن به تعداد واقعی توکنها، به عدد 4096 نزدیک میشود، زیرا سرور بقیه را حذف کرده است. آن را دوباره با "num_ctx":16384 اجرا کنید تا مقدار افزایش یابد. اگر build شما به جای truncating (کوتاه کردن)، خطا برگرداند، این همان نتیجه را با سیگنالی واضحتر نشان میدهد.
ollama psستون CONTEXT، در buildهایی که آن را چاپ میکنند، طول کانتکستی را نشان میدهد که مدل بارگذاریشده در حال حاضر با آن اجرا میشود. ستون PROCESSOR در کنار آن نشان میدهد که مدل در کجا قرار گرفته است. مقدار 100% CPU در یک VPS بدون GPU طبیعی است. تقسیمبندیای مانند 30%/70% CPU/GPU در یک سیستم دارای GPU به این معنی است که وزنها به همراه cache دیگر در VRAM جا نمیشوند و افزایش num_ctx دلیل معمول آن است.
journalctl -u ollama --no-pager | grep -i n_ctx | tail -n 5اجراکننده inference (استنتاج)، اندازه کانتکست خود را در خطی شامل n_ctx چاپ میکند. عبارت دقیق آن بین releaseها تغییر میکند، بنابراین اگر خطی را پیدا نکردید، آن را به عنوان تغییر نام در نظر بگیرید، نه به عنوان مدرکی برای اثبات چیزی.
چهار مکان برای تنظیم num_ctx
در درخواست. مقدار "options": {"num_ctx": 16384} را به /api/generate یا /api/chat ارسال کنید. این تنظیم بر تمام تنظیمات دیگر اولویت دارد و فقط برای همان فراخوانی اعمال میشود. اگر این مقدار با مقداری که مدل بارگذاریشده با آن در حال اجراست تفاوت داشته باشد، سرور ابتدا مدل را دوباره بارگذاری میکند که میتوانید آن را در load_duration در پاسخ مشاهده کنید: زمان از نزدیک به صفر به چندین ثانیه افزایش مییابد. همین زمان انتظار زمانی که مدل برای مدتی طولانی بیکار بوده و از حافظه خارج شده است نیز دیده میشود، بنابراین هنگامی که اندازه context مورد نظر خود را تعیین کردید، ارزش آن را دارد که مدل را با keep_alive در حافظه نگه دارید.
در نشست تعاملی. داخل ollama run، عبارت /set parameter num_ctx 16384 را تایپ کنید. این تنظیم برای همان نشست باقی میماند.
در Modelfile. این کار مقدار را در یک مدل نامگذاریشده تثبیت میکند، بنابراین هر کلاینتی بدون نیاز به تغییر در سمت خود، از آن استفاده میکند.
FROM llama3.1:8b
PARAMETER num_ctx 16384ollama create llama3.1-16k -f ./Modelfile
ollama run llama3.1-16kروی سرور. OLLAMA_CONTEXT_LENGTH مقدار پیشفرض را برای هر درخواستی که num_ctx اختصاصی خود را ندارد، تعیین میکند. در systemd، بهجای ویرایش فایل unit، از یک فایل drop-in استفاده کنید.
sudo systemctl edit ollama.service[Service]
Environment="OLLAMA_CONTEXT_LENGTH=16384"sudo systemctl daemon-reload
sudo systemctl restart ollama
ollama psاولویتبندی زمانی اهمیت پیدا میکند که در حال عیبیابی کلاینت شخص دیگری هستید. درخواستی که حاوی num_ctx باشد، بر پیشفرض سرور غلبه میکند؛ بنابراین یک رابط کاربری چت یا عاملی (agent) که مقدار کوچکی را بهصورت خودکار ارسال میکند، تغییرات شما در systemd را بیاثر میکند. هنگامی که یک عامل برنامهنویسی را به سرور Ollama خود متصل میکنید، پیش از مقصر دانستن سرور، بررسی کنید که کلاینت چه مقداری ارسال میکند.
چرا نمیتوانید num_ctx را صرفاً روی حداکثر مقدار مدل تنظیم کنید
مکانیسم توجه (Attention) باعث میشود هر توکن، تمام توکنهای پیش از خود را بررسی کند. کلیدها و مقادیر محاسبهشده برای توکنهای قبلی ذخیره میشوند تا برای هر توکن جدید دوباره محاسبه نشوند؛ این فضای ذخیرهسازی، KV cache (کش کلید/مقدار) نام دارد. این فضا هنگام بارگذاری مدل برای کل num_ctx تخصیص مییابد، نه اینکه با رشد گفتگو بزرگ شود؛ بنابراین یک context بزرگ، حتی برای یک پرامپت تکخطی نیز تمام حافظه اختصاصیافته را اشغال میکند.
آموزش هزینه استنتاج در DigitalOcean محاسبات ریاضی آن را در یک خط بیان میکند:
kv_bytes_per_token = 2 * layers * kv_heads * head_dim * bytes_per_valueعدد 2 در فرمول، کلیدها و مقادیر را بهصورت جداگانه محاسبه میکند. سایر اعداد را از مشخصات مدل خود استخراج کنید.
curl -s http://localhost:11434/api/show -d '{"model":"llama3.1:8b"}' |
jq '.model_info | {ctx: ."llama.context_length", layers: ."llama.block_count", heads: ."llama.attention.head_count", kv_heads: ."llama.attention.head_count_kv", embed: ."llama.embedding_length"}'مدل Llama 3.1 8B دارای 32 لایه و 8 هد کلید/مقدار است. ابعاد هد برابر است با embed تقسیم بر heads، که در اینجا 4096 / 32 = 128 میشود؛ برخی مدلها این مقدار را مستقیماً به عنوان llama.attention.key_length منتشر میکنند. کش پیشفرض مقادیر f16 را نگه میدارد، بنابراین bytes_per_value برابر با 2 است و حاصل 2 32 8 128 2 برابر با 131,072 بایت میشود. این یعنی 128 KiB حافظه کش برای هر توکن از context. این مقدار را در طول context ضرب کنید تا هزینه از حالت انتزاعی خارج شود.
The data behind this chart
[
{
"label": "4k",
"kv_cache_gib": 0.5,
"total_ram_gib": 5.1
},
{
"label": "8k",
"kv_cache_gib": 1,
"total_ram_gib": 5.6
},
{
"label": "16k",
"kv_cache_gib": 2,
"total_ram_gib": 6.6
},
{
"label": "32k",
"kv_cache_gib": 4,
"total_ram_gib": 8.6
},
{
"label": "64k",
"kv_cache_gib": 8,
"total_ram_gib": 12.6
},
{
"label": "128k",
"kv_cache_gib": 16,
"total_ram_gib": 20.6
}
]آن 6 ردیف، حاصل محاسبات ریاضی فرمول بالاست، نه اندازهگیریهای واقعی. ستون مجموع، حجم دانلود 4.9 گیگابایتی که کتابخانه Ollama برای llama3.1:8b در اوت 2026 لیست کرده (یعنی 4.6 GiB) را اضافه میکند و بافرهای محاسباتی و خودِ پردازش سرور را در نظر نمیگیرد. این اعداد را به عنوان کفِ مصرف حافظه در نظر بگیرید.
نکته اصلی، شکلِ رشد مصرف حافظه است. در 8k، هزینه کش برابر با 1 GiB است که در مقایسه با وزنهای مدل ناچیز است. در حداکثر ظرفیت 128k مدل، این هزینه به 16 GiB میرسد که بیش از سه برابر وزنهای مدل است و مجموع آن به نزدیکی 20.6 GiB میرسد. بنابراین، یک VPS با 4 گیگابایت رم نمیتواند این مدل را با هیچ context مفیدی بارگذاری کند. یک VPS با 8 گیگابایت رم برای 8k مناسب است. یک VPS با 16 گیگابایت رم به 32k میرسد و همچنان برای سایر پردازشهای سرور فضا باقی میماند. هر یک از این آستانهها با افزایش وزن مدل تغییر میکنند؛ بنابراین اگر در حال مقایسه یک مدل بزرگتر با این مدل 8B هستید، محاسبات مشابهی که برای تگ Qwen 27B روی یک VPS فقط با CPU انجام شده، نشان میدهد که چقدر فضای کمی برای context بین 8 تا 64 گیگابایت رم باقی میماند.
وقتی KV cache در حافظه جا نمیشود چه اتفاقی میافتد
در یک VPS که فقط از CPU استفاده میکند، حجم پردازش بهسادگی افزایش مییابد. هنگام بارگذاری مدل و اجرای یک درخواست طولانی، مصرف منابع را زیر نظر بگیرید.
free -m
ps -eo rss,comm --sort=-rss | head -n 5مقدار RSS (resident set size) بر حسب کیلوبایت نمایش داده میشود. اگر میزان استفاده از swap در free -m شروع به افزایش کرد، مقدار context را کاهش دهید. KV cacheای که در swap قرار میگیرد، باعث میشود تولید هر توکن چندین ثانیه متوقف شود، زیرا برای هر توکن جدید، کل cache باید خوانده شود.
اگر حافظهٔ سرور بهطور کامل تمام شود، هستهٔ سیستمعامل (kernel) بزرگترین پردازش را انتخاب کرده و آن را متوقف (kill) میکند.
sudo dmesg | grep -i "killed process"خطی که عبارت Out of memory: Killed process 1234 (ollama) را نشان میدهد، به این معناست که context درخواستی شما در حافظه جا نشده است. Ollama اغلب پیش از رسیدن به این مرحله درخواست را رد میکند و در نتیجه، درخواست با پیامی که میزان حافظهٔ مورد نیاز را در مقایسه با حافظهٔ آزاد نشان میدهد، با خطا مواجه میشود.
در سرورهای دارای GPU، این خطا بیسروصداتر رخ میدهد. لایهها به حافظهٔ RAM سیستم منتقل میشوند، ollama ps تقسیم بار بین CPU و GPU را نشان میدهد و نرخ خروجی (throughput) بهشدت افت میکند. میزان این افت به سختافزار شما بستگی دارد؛ بنابراین بهجای اعتماد به اعداد و ارقام ماشینهای دیگر، تعداد توکن در ثانیه را روی سیستم خودتان اندازهگیری کنید و این کار را برای هر تنظیمات context تکرار کنید.
زمان پیشپردازش (Prefill) سریعتر از طول پرامپت افزایش مییابد
پیشپردازش (Prefill) کاری است که روی ورودی شما پیش از ظاهر شدن اولین توکن خروجی انجام میشود. هر توکن در پرامپت به تمام توکنهای پیش از خود توجه (attend) میکند، بنابراین حجم کل کار با توان دوم طول ورودی افزایش مییابد. دو برابر کردن پرامپت، زمان انتظار برای اولین توکن را بیش از دو برابر میکند.
پاسخ شامل اندازهگیری مربوطه است، بنابراین نیازی نیست به آن اعتماد کنید.
jq -n --arg p "$LONG" '{model:"llama3.1:8b", prompt:$p, stream:false, options:{num_ctx:16384}}' |
curl -s http://localhost:11434/api/generate -d @- |
jq '{tokens: .prompt_eval_count, prefill_seconds: (.prompt_eval_duration/1000000000)}'آن را یکبار با prompt کوتاه و بار دیگر با prompt طولانی اجرا کنید، سپس در هر حالت تعداد tokenها را بر تعداد ثانیهها تقسیم کنید. در یک VPS فقط-CPU، prefill معمولاً کندترین بخش یک درخواست با context طولانی است و نرخ token بر ثانیهای که از یک prompt کوتاه به دست میآید، سرعت آن را پیشبینی نمیکند. وقتی زمان prefill از timeout تعیینشده در یکی از لایههای جلویی بیشتر شود، prompt طولانی معمولاً بهجای پاسخ با خطای عبارت «مهلت اجرای context تمام شد» برمیگردد. بنابراین پیش از کوتاهکردن context، مشخص کنید کدام لایه اجرای درخواست را متوقف کرده است.
همروندی (Concurrency) جایی است که این موضوع بیشترین آسیب را میزند. هر درخواستی که در حال پردازش است به کش (cache) اختصاصی خود نیاز دارد، بنابراین حافظه در نمودار بالا به ازای هر درخواست است، نه به ازای هر سرور؛ و یک درخواست طولانی میتواند منابع سیستم را اشغال کند در حالی که درخواستهای کوتاه در صف منتظر میمانند. مقدار OLLAMA_NUM_PARALLEL را با دقت تنظیم کنید و پیش از افزایش همزمان هر دو عدد، تعداد کاربرانی که یک LLM خودمیزبان میتواند همزمان سرویسدهی کند را مطالعه کنید.
بازپسگیری حافظه با استفاده از کش کوچکتر
مقدار bytes_per_value در فرمول، تنظیمی است که شما کنترل میکنید. بخش FAQ در Ollama مستندات مربوط به OLLAMA_KV_CACHE_TYPE را ارائه میدهد که در آن f16 به عنوان مقدار پیشفرض 2 بایت، به همراه q8_0 با 1 بایت و q4_0 برای مقادیر کمتر از آن در نظر گرفته شده است. تغییر به q8_0 حجم کش را نصف میکند، بنابراین ردیف 32k به جای 4 گیگابایت، تنها 2 گیگابایت فضا اشغال میکند. کوانتیزه کردن وزنها (Quantising the weights) حافظه را از سمت دیگر همان بودجه آزاد میکند و تگ GLM که واقعاً روی یک VPS جا میشود، با بررسی کوانتیزاسیون به کوانتیزاسیونِ دیگر، در صورتی که این همان مبادلهای باشد که ترجیح میدهید، قابل دستیابی است. همان بخش FAQ به OLLAMA_FLASH_ATTENTION=1 اشاره دارد که برخی بیلدها پیش از اعمال کش کوانتیزه شده به آن نیاز دارند.
[Service]
Environment="OLLAMA_FLASH_ATTENTION=1"
Environment="OLLAMA_KV_CACHE_TYPE=q8_0"به جای فرض کردن، تأیید کنید: سرویس را ریاستارت کنید، مدل را با همان num_ctx قبلی بارگذاری کرده و RSS را مقایسه کنید. پشتیبانی به مدل و بکاند بستگی دارد، بنابراین اگر تنظیمی تغییری ایجاد نمیکند، به این معناست که ترکیب فعلی شما تحت پوشش نیست. مستندات این گزینهها را بدون تضمین نتیجهای با کیفیت فهرست کردهاند، بنابراین پیش از تکیه بر آنها، q4_0 را با پرامپتهای خود تست کنید. اگر این تنظیمات دلیل حضور شما در اینجا هستند، Ollama و llama.cpp آنها را به شیوههای متفاوتی ارائه میدهند.
دستورالعملی برای انتخاب num_ctx
- حداکثر context مدل، تعداد لایهها و تعداد headهای key/value آن را از
/api/showبخوانید. - مقدار بایت به ازای هر توکن را با فرمول محاسبه کرده و سپس در context مورد نظر خود ضرب کنید.
- حجم وزنها (weight size) را اضافه کنید، آن را با RAM آزاد مقایسه کرده و حداقل 1 GiB را برای سایر فعالیتهای سیستم کنار بگذارید.
- مقدار را تنظیم کنید، مدل را بارگذاری نمایید و سپس مقادیر اعمالشده را با
ollama psوprompt_eval_countتأیید کنید. - بار کاری واقعی خود را اجرا کنید و در حین کار
free -mرا زیر نظر بگیرید؛ اگر swap شروع به فعالیت کرد، مقدار context را نصف کنید.
بیشتر کارها به context کمتری نسبت به آنچه کاربران اختصاص میدهند نیاز دارند. خلاصهسازی یک گزارش طولانی در 16k جا میشود. یک رابط بازیابی اطلاعات که پنج بخش از اسناد را جایگذاری میکند، بهندرت از 8k فراتر میرود. یک عامل برنامهنویسی که کل فایلها را میخواند، موردی است که واقعاً به 64k یا بیشتر نیاز دارد و در این حالت باید سختافزار را بر اساس context انتخاب کنید، نه برعکس. اگر سرور شما هنوز جدید است، از نصب Ollama روی یک VPS شروع کنید و پس از بارگذاری صحیح مدلها، context را تنظیم کنید.
FAQ
طول کانتکست پیشفرض در Ollama چقدر است؟
این مقدار به نسخهٔ build و سختافزار بستگی دارد، بنابراین بهجای فرضکردن، آن را بررسی کنید. بخش FAQ در مستندات Ollama به 4096 توکن اشاره دارد، مرجع Modelfile مقدار پیشفرض num_ctx را 2048 ذکر میکند، و صفحهٔ مربوط به طول کانتکست، مقداری را بر اساس VRAM موجود تعیین میکند: 4k برای کمتر از 24 GiB، 32k برای 24 تا 48 GiB، و 256k برای مقادیر بالاتر. یک VPS که فقط از CPU استفاده میکند در محدودهٔ پایین قرار میگیرد. دستور ollama ps در نسخههایی که این ستون را دارند، کانتکست اعمالشده را چاپ میکند و prompt_eval_count در پاسخ API، آن را در تمامی نسخهها تأیید میکند.
چرا Ollama ابتدای پرامپت طولانی من را نادیده میگیرد؟
به این دلیل که پرامپت از پنجرهٔ کانتکست طولانیتر بوده است؛ بنابراین سرور پیش از آنکه مدل آن را ببیند، بخش اضافی را حذف کرده و هیچ خطایی نیز بازنگردانده است. همان پرامپت را دوباره با یک num_ctx بزرگتر ارسال کنید و رشد prompt_eval_count را در پاسخ مشاهده کنید. اگر این عدد تغییر نکرد، چیزی بین شما و سرور در حال تعیین مقدار num_ctx است که این مسئله در رابطهای کاربری چت و چارچوبهای agent رایج است.
یک num_ctx بزرگتر به چه مقدار RAM اضافی نیاز دارد؟
طول کانتکست را در هزینهٔ حافظهٔ پنهان (cache) به ازای هر توکن که 2 * layers * kv_heads * head_dim * bytes_per_value است، ضرب کنید. برای مدل Llama 3.1 8B با دقت f16، این مقدار 128 KiB به ازای هر توکن است؛ بنابراین 32k توکن هزینهای معادل 4 GiB و 128k کامل هزینهای معادل 16 GiB علاوه بر وزنهای مدل دارد. حافظهٔ پنهان هنگام بارگذاری مدل تخصیص مییابد، بنابراین یک num_ctx بزرگ، حتی زمانی که پرامپتهای شما کوتاه هستند، آن مقدار حافظه را اشغال میکند.
آیا پنجرهٔ کانتکست بزرگتر باعث کندی Ollama میشود؟
بله، از دو طریق. عملیات پیشپردازش (prefill) با توان دوم طول پرامپت افزایش مییابد، بنابراین یک ورودی طولانی، اولین توکن را بیش از آنچه طول آن نشان میدهد، به تأخیر میاندازد. حافظهٔ پنهان بزرگتر نیز برای دستیابی به حافظه رقابت میکند: در سیستمهای دارای GPU، لایهها را به RAM سیستم میراند و در سیستمهای دارای CPU، دستگاه را به سمت استفاده از swap سوق میدهد. یک num_ctx بزرگ که هرگز آن را پر نمیکنید، همچنان هزینهٔ حافظه را تحمیل میکند، هرچند هزینهٔ زمانی prefill را ندارد.
آیا میتوانم num_ctx را برای یک مدل بهصورت دائمی تنظیم کنم؟
بله. یک Modelfile حاوی FROM llama3.1:8b و PARAMETER num_ctx 16384 بنویسید و سپس ollama create llama3.1-16k -f ./Modelfile را اجرا کنید. هر کلاینتی که llama3.1-16k را درخواست کند، بدون نیاز به ارسال هیچ گزینهای، آن کانتکست را دریافت خواهد کرد. درخواستی که num_ctx اختصاصی خود را داشته باشد همچنان اولویت دارد، بنابراین این کار یک مقدار پیشفرض تعیین میکند، نه یک سقف محدودکننده.