← All courses ← Course home

Lesson 02 of 04

Set the context and size the memory

There are three ways to set the context in Ollama. Pick one that also works for the endpoint your app uses. Then check that the memory fits.

Agent brief (llms.md)

Environment setup

Use the same local-lab folder from lesson 01 (uv, Python 3.11+, and uv add numpy gguf). If you skipped it, do the setup in lesson 01 first.

Install Ollama from ollama.com/download. Check it with ollama --version.

Install llama.cpp. On macOS: brew install llama.cpp. On Linux or Windows, download a release from github.com/ggml-org/llama.cpp/releases. Check it with llama-server --version.

Three ways to set it

# Per model
cat > Modelfile <<'MF'
FROM qwen2.5:0.5b
PARAMETER num_ctx 16384
MF
ollama create qwen2.5-16k -f Modelfile
uv run python ctx_guard.py qwen2.5-16k
# tokens in the full prompt: 9515
# tokens the app path read: 9515
# PASS

# Server-wide
OLLAMA_CONTEXT_LENGTH=16384 ollama serve
uv run python ctx_guard.py qwen2.5:0.5b
# PASS

Both fixes passed on the test box (Ollama 0.40.4). ollama ps then showed 16384 in the CONTEXT column.

Size the memory

A bigger context needs more memory for the KV cache. For a normal attention model the cache is about:

KV bytes = 2 (K and V) x layers x kv_heads x head_dim x context x 2 bytes (f16)

This script reads those numbers from the GGUF file:

"""Print the architecture stored in a GGUF file and estimate KV cache memory."""
import sys

from gguf import GGUFReader


def field(reader, key):
    f = reader.fields.get(key)
    if f is None:
        return None
    value = f.parts[f.data[0]]
    if f.types[0].name == "STRING":
        return bytes(value).decode()
    return value.tolist()[0]


def main():
    path = sys.argv[1]
    ctx = int(sys.argv[2]) if len(sys.argv) > 2 else 8192
    reader = GGUFReader(path)
    arch = field(reader, "general.architecture")
    layers = field(reader, f"{arch}.block_count")
    heads = field(reader, f"{arch}.attention.head_count")
    kv_heads = field(reader, f"{arch}.attention.head_count_kv") or heads
    head_dim = field(reader, f"{arch}.attention.key_length")
    if head_dim is None:
        head_dim = field(reader, f"{arch}.embedding_length") // heads
    print("architecture:", arch)
    print("name:", field(reader, "general.name"))
    print("trained context_length:", field(reader, f"{arch}.context_length"))
    print(f"layers={layers} kv_heads={kv_heads} head_dim={head_dim}")
    # K and V, every layer, f16 = 2 bytes. Hybrid and sliding-window models use less.
    kv_bytes = 2 * layers * kv_heads * head_dim * ctx * 2
    print(f"KV cache at {ctx} tokens (f16, full attention): about {kv_bytes / 2**20:.0f} MiB")


if __name__ == "__main__":
    main()
$ uv run python gguf_arch.py Qwen3-1.7B-Q8_0.gguf 8192
architecture: qwen3
name: Qwen3 1.7B Instruct
trained context_length: 40960
layers=28 kv_heads=8 head_dim=128
KV cache at 8192 tokens (f16, full attention): about 896 MiB

This is an estimate. Hybrid models (recurrent or sliding-window layers) use less, a quantized KV cache uses less, and the runtime adds compute buffers. In Ollama the memory grows with OLLAMA_NUM_PARALLEL times the context, as the Ollama FAQ says. Use the number to choose a context that leaves room, then confirm with ollama ps that the model is still 100% GPU and not split to CPU.

Rule: set the context where your app's endpoint will see it, then run the CI check again.

Key concepts

  • OLLAMA_CONTEXT_LENGTH and a Modelfile work for every endpoint.
  • Per-request num_ctx works on /api/chat only.
  • KV cache memory grows with layers, KV heads, head size, context, and parallel slots.
  • ollama ps shows context and CPU/GPU split.

Takeaways

  • Use a Modelfile or OLLAMA_CONTEXT_LENGTH if your app uses the OpenAI endpoint.
  • Estimate KV memory from the GGUF before you raise the context.
  • Run ctx_guard.py again after every change.