שינוי אורך הקשר ב-Ollama בעזרת num_ctx
ה-prompt שלכם נחתך ב-Ollama? ברירת המחדל של num_ctx נמוכה משמעותית מהיכולת של המודל. למדו כיצד להגדיר את הפרמטר ברמת הבקשה או השרת וכיצד לחשב נכון את צריכת ה-VRAM.
מה עושה num_ctx, ומדוע ה-prompt הארוך שלכם נחתך
אורך ההקשר (context length) ב-Ollama הוא מספר ה-tokens שמודל טעון יכול להחזיק בזיכרון בו-זמנית, והאופציה num_ctx היא זו שקובעת אותו. Ollama בוחרת ערך ברירת מחדל שנמוך משמעותית מהמקסימום שהמודל מצהיר עליו, לכן prompt ארוך נחתך עוד לפני שהמודל מספיק לקרוא אותו. שום דבר בתגובה לא יתריע בפניכם שזה קרה.
המודל Llama 3.1 8B מופיע בספריית המודלים של Ollama עם חלון הקשר של 128k. שרת בתצורה סטנדרטית לא יספק לכם את הערך הזה. התיעוד של Ollama עצמה מציג ברירות מחדל שונות בדפים שונים: ה-FAQ מציין 4096 tokens, ההפניה ל-Modelfile מציינת ש-num_ctx מוגדר כברירת מחדל ל-2048, ודף אורך ההקשר מציין שברירת המחדל נבחרת לפי ה-VRAM (זיכרון וידאו) הזמין: 4k מתחת ל-24 GiB, 32k בין 24 ל-48 GiB, ו-256k מעל ערך זה. כל אחד מהנתונים הללו היה נכון לגרסת בנייה מסוימת. חוסר ההסכמה הזה הוא הלקח החשוב כאן: קראו את הערך מהשרת הפעיל שלכם במקום להסתמך על דף כלשהו, כולל דף זה.
קיטום (truncation) מתבצע בשקט כיוון שהמודל עדיין עונה, והתשובה עדיין נראית תקינה. היא נכתבה על בסיס סוף הקלט שלכם. סיכום שמחמיץ את החצי הראשון של מסמך נראה כמו מודל חלש. בדרך כלל, מדובר פשוט בחלון הקשר קטן מדי.
בדיקת אורך ההקשר (context length) ששרת ה-Ollama שלכם החיל בפועל
הבדיקה שעובדת בכל גרסת build היא prompt_eval_count, המונה את מספר ה-tokens של ה-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 tokens. הערך prompt_eval_count יחזור קרוב ל-4096 במקום קרוב למספר ה-tokens האמיתי, כיוון שהשרת השמיט את השאר. הריצו זאת שוב עם "num_ctx":16384 והמספר יעלה. אם ה-build שלכם מחזיר שגיאה במקום לקטום את הטקסט, זו אותה תוצאה עם חיווי ברור יותר.
ollama psהעמודה CONTEXT, בגרסאות שמדפיסות אותה, מכילה את אורך ההקשר שבו המודל הטעון רץ כרגע. העמודה PROCESSOR לצדה מציגה היכן המודל ממוקם. הערך 100% CPU הוא תקין בשרת VPS ללא GPU. פיצול כמו 30%/70% CPU/GPU בשרת עם GPU מעיד על כך שהמשקולות (weights) יחד עם ה-cache אינם נכנסים יותר ב-VRAM, וערך num_ctx גבוה הוא הסיבה הנפוצה לכך.
journalctl -u ollama --no-pager | grep -i n_ctx | tail -n 5מריץ ההסקה (inference runner) מדפיס את גודל ההקשר שלו בשורה המכילה את n_ctx. הניסוח המדויק משתנה בין גרסאות, לכן התייחסו לשורה חסרה כאל שינוי שם ולא כאל הוכחה למשהו אחר.
ארבעה מקומות להגדרת num_ctx
בבקשה עצמה. שלחו את "options": {"num_ctx": 16384} ל־/api/generate או ל־/api/chat. הגדרה זו גוברת על כל הגדרה אחרת וחלה על הקריאה הספציפית הזו. אם הערך שונה מזה שבו המודל הטעון פועל, השרת יטען מחדש את המודל, דבר שניתן לראות ב־load_duration בתגובה: הערך יקפוץ מאפס לשניות שלמות. המתנה דומה מתרחשת כאשר המודל היה במצב המתנה זמן רב מדי ופונה מהזיכרון, לכן ברגע שהחלטתם על גודל הקשר (context size), כדאי להשאיר את המודל בזיכרון באמצעות 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, הוסיפו קובץ drop-in במקום לערוך את קובץ ה-unit ישירות.
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 גורם לכל טוקן "להסתכל" על כל הטוקנים שקדמו לו. המפתחות (keys) והערכים (values) המחושבים עבור טוקנים מוקדמים נשמרים כדי שלא יהיה צורך לחשב אותם מחדש עבור כל טוקן חדש; מאגר זה נקרא KV cache (מטמון מפתחות/ערכים). הוא מוקצה עבור מלוא ה-num_ctx בעת טעינת המודל, ולא גדל ככל שהשיחה מתארכת. לכן, הקשר (context) גדול צורך את הזיכרון שלו גם עבור הנחיה (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 ראשי מפתח/ערך. ממד הראש הוא embed חלקי heads, כלומר 4096 / 32 = 128 במקרה זה, וחלק מהמודלים מפרסמים זאת ישירות כ-llama.attention.key_length. המטמון המוגדר כברירת מחדל מחזיק ערכי f16, לכן bytes_per_value הוא 2, והחישוב 2 32 8 128 2 מניב 131,072 בתים. מדובר ב-128 KiB של מטמון עבור כל טוקן בודד של הקשר. הכפילו זאת באורך ההקשר, והעלות מפסיקה להיות מופשטת.
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 באוגוסט 2026, שהם 4.6 GiB, והיא אינה כוללת את מאגרי החישוב (compute buffers) ואת תהליך השרת עצמו. התייחסו לנתון זה כאל רף תחתון.
הצורה היא העיקר. ב-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 (resident set size) מוצג בקילובייטים. אם השימוש ב-swap ב-free -m מתחיל לעלות, הקטינו את ה-context. כאשר ה-KV cache נמצא ב-swap, יצירת הטקסט תיתקע למשך שניות עבור כל token, כיוון שכל token חדש דורש קריאה של כל ה-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) צונח בחדות. עוצמת הירידה תלויה בחומרה שלכם, לכן מדדו tokens לשנייה על השרת שלכם עבור כל הגדרת context, במקום להסתמך על נתונים ממכונה של מישהו אחר.
זמן ה-Prefill גדל מהר יותר מה-prompt
ה-Prefill הוא העבודה שמתבצעת על הקלט שלכם לפני שמופיע ה-token הראשון של הפלט. כל token ב-prompt מבצע פעולת attention על כל ה-tokens שלפניו, לכן סך העבודה גדל לפי ריבוע אורך הקלט. הכפלת ה-prompt גורמת לעיכוב של יותר מפי שניים בקבלת ה-token הראשון.
התגובה כוללת את המדידה, כך שאין צורך להסתמך על הערכות.
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 ארוך, ולאחר מכן חלקו את מספר ה־tokens במספר השניות בכל אחד מהמקרים. ב־VPS שפועל על CPU בלבד, שלב ה־prefill הוא בדרך כלל החלק האיטי ביותר בבקשה עם הקשר ארוך, ונתון tokens per second שנמדד באמצעות prompt קצר לא ינבא את הביצועים במקרה זה. כאשר שלב ה־prefill נמשך מעבר ל־timeout שמוגדר לפניו, זו בדרך כלל הסיבה לכך ש־prompt ארוך מחזיר חריגה של תום הזמן שהוקצה להקשר במקום תשובה. לכן, בררו איזו שכבה ויתרה על הבקשה לפני שאתם מקצרים את ההקשר.
הבעיה מורגשת ביותר במצבי Concurrency. כל בקשה שמטופלת זקוקה ל-cache משלה, לכן הזיכרון בתרשים לעיל הוא לכל בקשה ולא לכל שרת, ובקשה אחת ארוכה עלולה לתפוס את המשאבים בזמן שבקשות קצרות ממתינות בתור. הגדירו את OLLAMA_NUM_PARALLEL באופן מכוון, וקראו את כמה משתמשים בו-זמנית יכול שרת LLM עצמאי לשרת לפני שתעלו את שני המספרים יחד.
החזרת הקשר (context) באמצעות מטמון קטן יותר
bytes_per_value בנוסחה הוא הגדרה שניתן לשלוט בה. ה-FAQ של Ollama מתעד את OLLAMA_KV_CACHE_TYPE, כאשר f16 הוא ערך ברירת המחדל ב-2 בתים, בתוספת q8_0 ב-1 בתים ו-q4_0 מתחת לכך. מעבר ל-q8_0 מקטין את המטמון בחצי, כך ששורה של 32k תופסת 2 GiB במקום 4 GiB. קוונטיזציה (quantisation) של המשקולות מפנה זיכרון מהצד השני של אותו תקציב, ו-תגית ה-GLM שמתאימה בפועל ל-VPS מחושבת שלב אחר שלב באמצעות קוונטיזציה, אם זהו הוויתור שאתם מעדיפים לבצע. ה-FAQ מתעד גם את OLLAMA_FLASH_ATTENTION=1, שחלק מהגרסאות דורשות לפני שמטמון שעבר קוונטיזציה נכנס לתוקף.
[Service]
Environment="OLLAMA_FLASH_ATTENTION=1"
Environment="OLLAMA_KV_CACHE_TYPE=q8_0"בצעו אימות במקום להניח הנחות: הפעילו מחדש את השירות, טענו את המודל באותו num_ctx כמו קודם, והשוו את ה-RSS. התמיכה תלויה במודל וב-backend, לכן הגדרה שאינה משנה דבר מעידה על כך שהשילוב שלכם אינו נתמך. התיעוד מפרט אפשרויות אלו ללא הבטחה לתוצאה איכותית, לכן בדקו את q4_0 מול ה-prompts שלכם לפני שתסתמכו עליו. אם כפתורי כוונון אלו הם הסיבה לכך שאתם כאן, Ollama ו-llama.cpp חושפים אותם בצורה שונה.
מתכון לבחירת num_ctx
- קראו את הקונטקסט המקסימלי של המודל, את מספר השכבות שלו ואת מספר ה-key/value heads מתוך
/api/show. - חשבו את מספר הבייטים לכל טוקן לפי הנוסחה, והכפילו בקונטקסט הרצוי לכם.
- הוסיפו את גודל המשקולות, השוו מול ה-RAM הפנוי, והשאירו לפחות 1 GiB פנוי עבור שאר המערכת.
- הגדירו את הערך, טענו את המודל, ואז ודאו מה הוחל בפועל באמצעות
ollama psו-prompt_eval_count. - הריצו את עומס העבודה האמיתי שלכם תוך ניטור
free -m, וצמצמו את הקונטקסט בחצי אם מתחילה פעילות ב-swap.
רוב המשימות דורשות פחות קונטקסט ממה שאנשים נוטים להקצות להן. סיכום של דוח ארוך נכנס ב-16k. ממשק שליפה (retrieval) שמדביק חמישה מקטעי מסמכים לעיתים רחוקות עובר את ה-8k. סוכן תכנות שקורא קבצים שלמים הוא המקרה שבאמת זקוק ל-64k ומעלה, וזהו גם המקרה שבו עליכם להתאים את המכונה לקונטקסט ולא להפך. אם השרת עצמו עדיין חדש, התחילו מ-התקנה תקינה של Ollama על גבי VPS וכוונו את הקונטקסט לאחר שהמודלים נטענים בצורה נקייה.
FAQ
מהו אורך ההקשר (context length) המוגדר כברירת מחדל ב-Ollama?
הדבר תלוי בגרסת הבנייה ובחומרה, לכן יש לבדוק זאת במקום להניח הנחות. ה-FAQ של Ollama מציין 4096 אסימונים (tokens), תיעוד ה-Modelfile מציין ערך ברירת מחדל של num_ctx השווה ל-2048, ודף אורך ההקשר מציין ברירת מחדל הנבחרת לפי ה-VRAM הזמין: 4k מתחת ל-24 GiB, 32k בין 24 ל-48 GiB, ו-256k מעל לערך זה. שרת VPS מבוסס CPU בלבד יקבל את הערך הנמוך. הפקודה ollama ps מציגה את אורך ההקשר המיושם בגרסאות הכוללות עמודה זו, וערך ה-prompt_eval_count בתגובת ה-API מוכיח זאת בכל גרסה.
מדוע Ollama מתעלם מתחילת ה-prompt הארוך שלי?
מכיוון שה-prompt היה ארוך מחלון ההקשר, השרת קיצץ אותו לפני שהמודל הספיק לעבד אותו, ולא הוחזרה שגיאה. שלחו שוב את אותו ה-prompt עם ערך num_ctx גדול יותר ועקבו אחר העלייה ב-prompt_eval_count בתגובה. אם מספר זה אינו משתנה, ייתכן שרכיב כלשהו ביניכם לבין השרת מגדיר את ה-num_ctx בעצמו; תופעה זו נפוצה בממשקי צ'אט ובספריות סוכנים (agent frameworks).
כמה זיכרון RAM נוסף דורש ערך num_ctx גדול יותר?
יש להכפיל את אורך ההקשר בעלות המטמון (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 גבוה צורך זיכרון זה גם כאשר ה-prompts שלכם נשארים קצרים.
האם חלון הקשר גדול יותר הופך את Ollama לאיטי יותר?
כן, בשתי דרכים. עבודת ה-prefill גדלה ביחס ריבועי לאורך ה-prompt, לכן קלט ארוך מעכב את האסימון הראשון מעבר למה שמרמז אורכו. המטמון הגדול גם מתחרה על הזיכרון: בשרת עם 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 משלה עדיין תגבר על הגדרה זו, כך שמדובר בברירת מחדל ולא בתקרה קשיחה.