Guide the user through running DSPy with local models via Ollama. No API keys, no cloud costs, full privacy.
What is Ollama
Ollama is a local LLM runner (166k+ GitHub stars) that wraps llama.cpp. It downloads, manages, and serves models locally with a simple CLI. DSPy connects to it through LiteLLM's ollama_chat/ provider.
Setup
Install Ollama
# macOS
brew install ollama
# Linux
curl -fsSL https://ollama.com/install.sh | sh
Windows: download from ollama.com
### Start the server and pull a model
```bash
# Start the Ollama server (runs in background)
ollama serve
# Pull a model (one-time download)
ollama pull llama3.1
# Quick test
ollama run llama3.1 "What is DSPy?"
Connect DSPy to Ollama
import dspy
lm = dspy.LM(
"ollama_chat/llama3.1",
api_base="http://localhost:11434",
api_key="", # required but ignored
temperature=0.7,
num_ctx=8192, # IMPORTANT: set context window explicitly
)
dspy.configure(lm=lm)
# Test it
classify = dspy.Predict("text -> sentiment")
result = classify(text="DSPy makes AI development easier")
print(result.sentiment)
Note: dspy.OllamaLocal is deprecated. Use dspy.LM("ollama_chat/...") instead.
Model selection guide
| Model | Sizes | Context | Good for | Notes |
|---|---|---|---|---|
| Llama 3.1 | 8B, 70B | 128K | General purpose, instruction following | Best all-rounder |
| Llama 3.2 | 1B, 3B | 128K | Edge, mobile, lightweight tasks | Very fast, less capable |
| Qwen 2.5 | 0.5B–72B | 128K | Multilingual, coding, math | Strong on benchmarks |
| Qwen 3 | 0.6B–32B | 128K | Reasoning, multilingual | Latest, thinking mode |
| Mistral | 7B | 32K | Fast general purpose | Good speed/quality tradeoff |
| Phi-4 | 14B | 16K | Reasoning, STEM, code | Small but capable |
| Gemma 2 | 2B, 9B, 27B | 8K | Lightweight, fast | Google, good quality/size ratio |
| DeepSeek-R1 | 1.5B–70B | 128K | Complex reasoning | Distilled reasoning chains |
| CodeLlama | 7B, 13B, 34B | 16K | Code generation | Fine-tuned for code |
Quick recommendations
Prototyping (fast iteration, good quality):
→ llama3.1:8b or qwen2.5:7b
Best quality on consumer hardware (16GB+ RAM):
→ llama3.1:8b or phi4:14b
Complex reasoning:
→ deepseek-r1:14b or qwen3:14b
Coding tasks:
→ qwen2.5-coder:7b or codellama:13b
Minimal resources (8GB RAM):
→ llama3.2:3b or gemma2:2b or qwen2.5:3b
Context window gotcha (critical)
Ollama defaults to 4096 tokens regardless of the model's actual capacity. This is the #1 source of issues when running DSPy with Ollama. DSPy prompts with few-shot demos can easily exceed 4096 tokens.
Always set num_ctx explicitly:
# BAD — defaults to 4096 tokens, will silently truncate
lm = dspy.LM("ollama_chat/llama3.1", api_base="http://localhost:11434", api_key="")
# GOOD — set context window to match model capability
lm = dspy.LM(
"ollama_chat/llama3.1",
api_base="http://localhost:11434",
api_key="",
num_ctx=8192, # 8K is a safe default for most tasks
)
Larger context = more VRAM. If you get OOM errors, reduce num_ctx:
| num_ctx | VRAM overhead (approx) | When to use |
|---|---|---|
| 4096 | Baseline | Simple classification, short prompts |
| 8192 | +2-4 GB | Most DSPy tasks, few-shot demos |
| 16384 | +4-8 GB | RAG with long contexts |
| 32768 | +8-16 GB | Long document processing |
Performance tuning
GPU acceleration
Ollama automatically uses GPU if available. Check with:
ollama ps # shows which models are loaded and GPU usage
Control GPU usage with environment variables:
# Use all GPU layers (default if GPU detected)
export OLLAMA_NUM_GPU=999
# CPU only (useful for testing or shared machines)
export OLLAMA_NUM_GPU=0
# Partial offload (when model doesn't fully fit in VRAM)
export OLLAMA_NUM_GPU=20
Concurrent requests
# Allow multiple parallel requests (default: 1)
export OLLAMA_NUM_PARALLEL=4
# Keep multiple models loaded (for multi-model pipelines)
export OLLAMA_MAX_LOADED_MODELS=2
Apple Silicon optimization
Ollama runs natively on Apple Silicon using Metal. Performance tips:
- M1/M2 (8GB): 8B models work well with
num_ctx=4096 - M1/M2 Pro (16GB): 8B models with
num_ctx=8192, or 14B withnum_ctx=4096 - M1/M2 Max (32GB+): 70B quantized models with
num_ctx=4096 - M3/M4 Max (64GB+): 70B models with
num_ctx=8192
Per-module model assignment
Use a big model for hard tasks and a small model for simple ones:
import dspy
big = dspy.LM("ollama_chat/llama3.1:8b", api_base="http://localhost:11434",
api_key="", num_ctx=8192)
small = dspy.LM("ollama_chat/llama3.2:3b", api_base="http://localhost:11434",
api_key="", num_ctx=4096)
dspy.configure(lm=small) # default: cheap model
class Pipeline(dspy.Module):
def __init__(self):
self.classify = dspy.Predict("text -> category")
self.analyze = dspy.ChainOfThought("text, category -> analysis")
def forward(self, text):
cat = self.classify(text=text)
return self.analyze(text=text, category=cat.category)
pipeline = Pipeline()
pipeline.classify.set_lm(small) # simple task → small model
pipeline.analyze.set_lm(big) # complex task → big model
Running DSPy optimization with Ollama
Optimization works with local models but is significantly slower than cloud APIs. Tips:
import dspy
lm = dspy.LM("ollama_chat/llama3.1:8b", api_base="http://localhost:11434",
api_key="", num_ctx=8192)
dspy.configure(lm=lm)
# Tip 1: Start with BootstrapFewShot (fastest optimizer)
optimizer = dspy.BootstrapFewShot(metric=metric, max_bootstrapped_demos=4)
optimized = optimizer.compile(program, trainset=trainset)
# Tip 2: For MIPROv2, use auto="light" (fewest trials)
optimizer = dspy.MIPROv2(metric=metric, auto="light")
optimized = optimizer.compile(program, trainset=trainset)
# Tip 3: Use a bigger model as teacher, smaller as student
teacher_lm = dspy.LM("ollama_chat/llama3.1:70b", api_base="http://localhost:11434",
api_key="", num_ctx=8192)
optimizer = dspy.BootstrapFewShot(metric=metric, max_bootstrapped_demos=4)
with dspy.context(lm=teacher_lm):
optimized = optimizer.compile(program, trainset=trainset)
# Deploy optimized program with the smaller model
Expect hours, not minutes for optimization with local models. A MIPROv2 auto="medium" run that takes 5 minutes with GPT-4o-mini might take 2-4 hours with a local 8B model.
Ollama vs vLLM
| Ollama | vLLM | |
|---|---|---|
| Setup | brew install ollama |
pip install vllm (NVIDIA only) |
| Platform | macOS, Linux, Windows | Linux (NVIDIA GPU required) |
| Apple Silicon | Yes (Metal) | No |
| Throughput | Single-user | High concurrency (10+ users) |
| Multi-GPU | No | Yes (tensor parallelism) |
| Best for | Development, prototyping | Production serving |
Recommended workflow: Develop with Ollama locally, deploy with vLLM in production. The DSPy code is identical — only the LM config line changes:
# Development (Ollama)
lm = dspy.LM("ollama_chat/llama3.1:8b", api_base="http://localhost:11434", api_key="")
# Production (vLLM)
lm = dspy.LM("openai/meta-llama/Llama-3.1-8B-Instruct", api_base="http://gpu-server:8000/v1", api_key="none")
Gotchas
- Context window defaults to 4096 — always set
num_ctxexplicitly. DSPy optimized prompts with few-shot demos easily exceed 4096 tokens. api_key=""is required — even though Ollama doesn't use it, LiteLLM requires the parameter.- First request is slow — Ollama loads the model into memory on the first call. Subsequent calls are fast.
- OOM errors — reduce
num_ctxor switch to a smaller model. Check VRAM withollama ps. dspy.OllamaLocalis deprecated — usedspy.LM("ollama_chat/...")instead.
Cross-references
- LM configuration basics (providers, parameters, caching) —
/dspy-lm - Production serving with vLLM —
/dspy-vllm - Reducing costs (model routing, caching) —
/ai-cutting-costs - Switching models without breaking things —
/ai-switching-models - For worked examples, see examples.md