SSD Nodes Learn 🎉 VPS من $5.50/شهر
الأدلة Matt Connorبقلم Matt Connor · آخر تحديث في 2026-08-10

ضبط طول السياق num_ctx في Ollama

يقتطع Ollama الموجّهات الطويلة بصمت. تعلّم ضبط num_ctx لكل طلب أو على مستوى الخادم، والتحقق من القيمة الفعلية، وحساب RAM اللازمة لذاكرة KV قبل رفعها.

ما الذي يفعله num_ctx، ولماذا اقتُطع موجّهك الطويل

طول السياق في Ollama هو عدد الرموز التي يمكن للنموذج المحمّل الاحتفاظ بها في الذاكرة في وقت واحد، وnum_ctx هو الخيار الذي يحدده. يختار Ollama قيمة افتراضية أقل بكثير من الحد الأقصى الذي يعلنه النموذج، لذلك يُقتطع الموجّه الطويل قبل أن يقرأه النموذج. ولا يخبرك أي شيء في الاستجابة بأن ذلك حدث.

يُدرج Llama 3.1 8B في مكتبة نماذج Ollama بنافذة سياق تبلغ 128k. لكن الخادم بالإعدادات الافتراضية لن يمنحك ذلك. تعرض وثائق Ollama نفسها قيماً افتراضية مختلفة في صفحات مختلفة: تذكر الأسئلة الشائعة 4096 رمزاً، ويذكر مرجع Modelfile أن num_ctx تكون قيمته الافتراضية 2048، بينما تقول صفحة طول السياق إن القيمة الافتراضية تُحدَّد وفق VRAM المتاحة، فتكون 4k عند أقل من 24 GiB، و32k من 24 إلى 48 GiB، و256k عند تجاوز ذلك. كانت كل قيمة منها صحيحة في إصدار معيّن. والدرس المفيد من هذا الاختلاف هو أن تقرأ القيمة من خادمك قيد التشغيل بدلاً من الوثوق بأي صفحة، بما فيها هذه الصفحة.

يحدث الاقتطاع بصمت لأن النموذج يظل يجيب، وتظل الإجابة تبدو جيدة. لكنه كتبها استناداً إلى نهاية مدخلاتك. ويبدو التلخيص الذي يفوّت النصف الأول من مستند كأنه ناتج عن نموذج ضعيف. لكنه يكون عادةً نتيجة نافذة سياق صغيرة.

تحقّق من طول سياق Ollama الذي طبّقه خادمك فعلياً

الفحص الذي يعمل مع أي إصدار هو prompt_eval_count، وهو عدد رموز المطالبة التي يذكر الخادم أنه عالجها. أرسل مطالبة تتجاوز سعة السياق، وسيتوقف هذا العدد عند الحد الأقصى.

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}'

تحتوي هذه المطالبة على نحو 18,000 كلمة، أي أكثر بكثير من 4096 رمزاً. يعود prompt_eval_count بقيمة قريبة من 4096 بدلاً من عدد الرموز الفعلي، لأن الخادم أسقط الجزء المتبقي. شغّلها مرة أخرى باستخدام "num_ctx":16384، وسيرتفع العدد. إذا أعاد إصدارك خطأً بدلاً من اقتطاع المطالبة، فهذه هي النتيجة نفسها لكن بإشارة أوضح.

ollama ps

يحتوي العمود CONTEXT، في الإصدارات التي تعرضه، على طول السياق الذي يعمل به النموذج المحمّل حالياً. ويعرض العمود PROCESSOR المجاور له موضع النموذج. يُعد 100% CPU وضعاً طبيعياً على VPS لا يحتوي على GPU. ويعني تقسيم مثل 30%/70% CPU/GPU على خادم مزوّد بـGPU أن الأوزان وذاكرة التخزين المؤقت لم تعد تتسع داخل VRAM، ويكون رفع num_ctx هو السبب المعتاد.

journalctl -u ollama --no-pager | grep -i n_ctx | tail -n 5

يطبع مشغّل الاستدلال حجم السياق في سطر يحتوي على n_ctx. قد تختلف الصياغة الدقيقة بين الإصدارات، لذلك تعامل مع غياب السطر على أنه إعادة تسمية، لا دليلاً على أي نتيجة.

أربعة مواضع لتعيين num_ctx

في الطلب. أرسل "options": {"num_ctx": 16384} إلى /api/generate أو /api/chat. يتغلب هذا على أي إعداد آخر، وينطبق على استدعاء واحد فقط. إذا اختلفت القيمة عن القيمة التي يعمل بها النموذج المحمّل، يعيد الخادم تحميل النموذج أولاً. يمكنك رؤية ذلك في load_duration ضمن الاستجابة، إذ ترتفع القيمة من قرابة الصفر إلى ثوانٍ كاملة.

في الجلسة التفاعلية. داخل ollama run، اكتب /set parameter num_ctx 16384. يستمر هذا الإعداد طوال الجلسة.

في Modelfile. يضمّن هذا القيمة في نموذج مُسمّى، لذلك يحصل عليها كل عميل من دون أي تغيير من جانب العميل.

FROM llama3.1:8b
PARAMETER num_ctx 16384
ollama create llama3.1-16k -f ./Modelfile
ollama run llama3.1-16k

على الخادم. يعيّن OLLAMA_CONTEXT_LENGTH القيمة الافتراضية لكل طلب لا يتضمن num_ctx الخاص به. عند استخدام systemd، أضف ملف 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 من دون تنبيه واضح. عندما توجّه coding agent إلى خادم Ollama الخاص بك، تحقق مما يرسله العميل قبل إلقاء اللوم على الخادم.

لماذا لا يمكنك ضبط num_ctx على الحد الأقصى للنموذج

تجعل آلية الانتباه كل token ينظر إلى جميع الـtokens السابقة. ويُحتفَظ بالمفاتيح والقيم المحسوبة للـtokens السابقة حتى لا تُعاد حساباتها مع كل token جديد. ويُسمّى هذا التخزين KV cache (ذاكرة التخزين المؤقت للمفتاح/القيمة). يُخصَّص هذا التخزين لكامل num_ctx عند تحميل النموذج، وليس مع نمو المحادثة. لذلك يستهلك السياق الكبير هذه الذاكرة حتى مع prompt مكوّن من سطر واحد.

يشرح دليل 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 لرؤوس key/value. ويُحسب بُعد الرأس بقسمة embed على heads، لذلك تكون العملية هنا 4096 / 32 = 128. تنشر بعض النماذج هذه القيمة مباشرة باسم llama.attention.key_length. تحتفظ ذاكرة التخزين المؤقت الافتراضية بقيم f16، لذلك تكون قيمة bytes_per_value هي 2. وتنتج العملية 2 32 8 128 2 القيمة 131,072 بايت. وهذا يعادل 128 KiB من ذاكرة التخزين المؤقت لكل token واحد من السياق. اضرب هذه القيمة في طول السياق، وستصبح التكلفة واضحة.

ChartLlama 3.1 8B at f16: KV cache and total RAM by context length, in GiB
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 GB الذي أدرجته مكتبة Ollama لـ llama3.1:8b في August 2026، أي 4.6 GiB، ولا يتضمن مخازن الحساب المؤقتة أو عملية الخادم نفسها. تعامل مع هذه القيمة باعتبارها الحد الأدنى.

المهم هو شكل الزيادة. عند 8k، تستهلك ذاكرة التخزين المؤقت 1 GiB، وهي قيمة ضئيلة مقارنة بأوزان النموذج. وعند 128k، وهو طول السياق الكامل للنموذج، تستهلك 16 GiB، أي أكثر من ثلاثة أضعاف حجم الأوزان، ليصبح الإجمالي قريباً من 20.6 GiB. لذلك لا يستطيع VPS بسعة 4 GB تحميل هذا النموذج مع سياق مفيد. ويعمل VPS بسعة 8 GB بشكل مريح عند 8k. ويصل VPS بسعة 16 GB إلى 32k مع بقاء مساحة لبقية مكونات الخادم. تزداد كل هذه الحدود مع زيادة حجم الأوزان. لذلك، إذا كنت تقارن نموذجاً أكبر بهذا النموذج 8B، فإن الحسابات نفسها التي أُجريت لـوسم Qwen بحجم 27B على VPS يعمل بالـCPU فقط توضّح مقدار الذاكرة القليل الذي ستتركه الأوزان للسياق بين 8 و64 GB.

ماذا يحدث عندما لا تتسع ذاكرة KV cache

في VPS يعمل بوحدة CPU فقط، يزداد استهلاك الذاكرة تدريجياً. راقبه أثناء تحميل النموذج وأثناء تنفيذ طلب طويل.

free -m
ps -eo rss,comm --sort=-rss | head -n 5

تُعرض RSS ‏(حجم مجموعة الذاكرة المقيمة) بالكيلوبايت. إذا بدأ swap المستخدم في free -m بالازدياد، فخفّض قيمة context. تؤدي إقامة KV cache في swap إلى توقف التوليد لثوانٍ لكل token، لأن كل token جديد يقرأ ذاكرة التخزين المؤقت كاملة.

إذا نفدت ذاكرة الجهاز بالكامل، تختار النواة أكبر عملية وتنهيها.

sudo dmesg | grep -i "killed process"

يعني السطر الذي يقرأ Out of memory: Killed process 1234 (ollama) أن قيمة context التي طلبتها لا تتسع في الذاكرة. غالباً ما يرفض Ollama الطلب قبل الوصول إلى هذه المرحلة، ويفشل الطلب برسالة تذكر الذاكرة التي احتاجها مقابل الذاكرة الحرة.

يكون الفشل أقل وضوحاً على جهاز مزود بـGPU. تُنقل بعض الطبقات إلى RAM النظام، ويعرض ollama ps توزيع الحمل بين CPU وGPU، وينخفض معدل المعالجة بشدة. يعتمد مقدار الانخفاض على العتاد لديك، لذلك قِس عدد tokens في الثانية على جهازك عند كل إعداد لـcontext بدلاً من الاعتماد على رقم من جهاز شخص آخر.

يزداد زمن المعالجة الأولية أسرع من زيادة طول الطلب

المعالجة الأولية هي العمل الذي يُنجز على مدخلاتك قبل ظهور أول رمز من المخرجات. ينتبه كل رمز في الطلب إلى كل رمز يسبقه، لذلك يزداد إجمالي العمل مع مربع طول المدخلات. تؤدي مضاعفة طول الطلب إلى زيادة زمن انتظار الرمز الأول بأكثر من الضعف.

تتضمن الاستجابة القياس، لذلك لا يتعين عليك قبول ذلك دون تحقق.

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)}'

شغّل ذلك باستخدام طلب قصير، ثم شغّله مرة أخرى باستخدام طلب طويل، واقسم عدد الرموز على عدد الثواني في كل حالة. في VPS يعمل على CPU فقط، تكون المعالجة الأولية عادةً أبطأ جزء من الطلب ذي السياق الطويل، ولن تتنبأ قيمة الرموز في الثانية المأخوذة من طلب قصير بأدائه في هذه الحالة.

يظهر أثر ذلك بأكبر قدر عند التزامن. تحتاج كل استجابة قيد المعالجة إلى cache خاص بها، لذلك تكون الذاكرة الموضحة في المخطط أعلاه لكل طلب، لا لكل خادم، ويمكن لطلب طويل واحد أن يشغل الخادم بينما تنتظر الطلبات القصيرة خلفه. اضبط OLLAMA_NUM_PARALLEL عمداً، واقرأ عدد المستخدمين المتزامنين الذين يستطيع نموذج LLM مستضاف ذاتياً خدمته قبل زيادة الرقمين معاً.

استعادة الذاكرة المتاحة بسعة تخزين مؤقت أصغر

bytes_per_value في الصيغة إعداد تتحكم فيه. توثّق الأسئلة الشائعة في Ollama الخيار OLLAMA_KV_CACHE_TYPE، مع f16 كقيمة افتراضية بحجم 2 بايت، إضافة إلى q8_0 بحجم 1 بايت، والقيم الأقل من ذلك في q4_0. يؤدي الانتقال إلى q8_0 إلى خفض حجم الذاكرة المؤقتة إلى النصف، لذلك يستهلك الصف بحجم 32k مقدار 2 GiB بدلاً من 4 GiB. كما توثّق الأسئلة الشائعة الخيار 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

  1. اقرأ الحد الأقصى للسياق في النموذج، وعدد طبقاته، وعدد رؤوس المفتاح/القيمة من /api/show.
  2. احسب عدد البايتات لكل رمز باستخدام الصيغة، ثم اضربه في حجم السياق المطلوب.
  3. أضف حجم الأوزان، وقارنه بذاكرة RAM الحرة، واترك 1 GiB على الأقل لبقية الخادم.
  4. اضبط القيمة، وحمّل النموذج، ثم تأكد من القيمة المطبقة باستخدام ollama ps وprompt_eval_count.
  5. شغّل عبء العمل الفعلي مع مراقبة free -m، وخفّض حجم السياق إلى النصف إذا بدأ استخدام swap بالارتفاع.

تحتاج معظم المهام إلى سياق أصغر مما يحدده المستخدمون. يلائم تلخيص تقرير طويل سياقاً بحجم 16k. ونادراً ما تتجاوز واجهة الاسترجاع التي تلصق خمس فقرات من المستندات 8k. أما وكيل البرمجة الذي يقرأ الملفات كاملة، فهو الحالة التي تحتاج فعلاً إلى 64k أو أكثر. وهي أيضاً الحالة التي ينبغي فيها تحديد مواصفات الجهاز وفقاً لحجم السياق، لا العكس. إذا كان الخادم نفسه جديداً، فابدأ من تثبيت Ollama يعمل على VPS، واضبط السياق بعد تحميل النماذج بنجاح.

FAQ

ما طول السياق الافتراضي في Ollama؟

يعتمد ذلك على الإصدار والعتاد، لذا تحقّق بدلاً من الافتراض. توثّق الأسئلة الشائعة في Ollama قيمة 4096 رمزاً، بينما توثّق مرجعية Modelfile قيمة num_ctx افتراضية مقدارها 2048، وتوثّق صفحة طول السياق قيمة افتراضية تُحدَّد وفق VRAM المتاحة: 4k لأقل من 24 GiB، و32k من 24 إلى 48 GiB، و256k لأكثر من 48 GiB. يعمل VPS الذي يستخدم CPU فقط عند الطرف الأصغر. يعرض ollama ps السياق المطبّق في الإصدارات التي تتضمن هذا العمود، ويثبت prompt_eval_count في استجابة API ذلك في جميع الإصدارات.

لماذا يتجاهل Ollama بداية المطالبة الطويلة؟

لأن المطالبة كانت أطول من نافذة السياق، فاقتطعها الخادم قبل أن يراها النموذج، ولم يُرجع أي خطأ. أرسل المطالبة نفسها مرة أخرى باستخدام num_ctx أكبر، وراقب ازدياد prompt_eval_count في الاستجابة. إذا لم تتغير هذه القيمة، فهناك مكوّن بينك وبين الخادم يضبط num_ctx بنفسه، وهذا شائع مع واجهات الدردشة وأطر عمل الوكلاء.

ما مقدار RAM الإضافية التي يحتاجها num_ctx الأكبر؟

اضرب طول السياق في تكلفة الذاكرة المؤقتة لكل رمز، وهي 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 أبطأ؟

نعم، بطريقتين. يزداد عمل المعالجة التمهيدية مع مربع طول المطالبة، لذلك تؤخر المدخلة الطويلة ظهور الرمز الأول أكثر مما يوحي به طولها. كما تنافس الذاكرة المؤقتة الأكبر على الذاكرة؛ فعلى جهاز GPU تدفع بعض الطبقات إلى RAM النظام، وعلى جهاز CPU تدفع الجهاز نحو استخدام swap. يستهلك num_ctx كبير لا تملؤه أبداً الذاكرة رغم أنه لا يضيف وقت المعالجة التمهيدية.

هل يمكنني ضبط num_ctx بشكل دائم لنموذج واحد؟

نعم. أنشئ Modelfile يحتوي على FROM llama3.1:8b وPARAMETER num_ctx 16384، ثم شغّل ollama create llama3.1-16k -f ./Modelfile. يحصل كل عميل يطلب llama3.1-16k على هذا السياق دون إرسال أي خيارات. يظل الطلب الذي يتضمن num_ctx خاصاً به هو المطبّق، لذا يحدد هذا قيمة افتراضية وليس حداً أقصى.