Claude Context domyślnie kieruje cię do OpenAI za embeddingi i Zilliz Cloud jako magazyn wektorów. Dla firm z wymaganiami zgodności albo po prostu z awersją do wysyłania kodu na zewnątrz obie decyzje da się cofnąć. Tu opisuję pełną konfigurację offline: Ollama lokalnie, Milvus w Dockerze, żadnych kluczy API.
Wracamy do głównego artykułu: Claude Code: jak przestać palić tokeny.
Dlaczego offline
Kod, który indeksujesz Claude Contextem, idzie do modelu embeddingów. Domyślnie to OpenAI (text-embedding-3-small albo -large), który:
- Wysyła fragmenty twojego kodu poza sieć firmową
- Wymaga klucza API, który musi być rotowany
- Kosztuje (niedużo, ale notuje zużycie)
- Podlega polityce retencji OpenAI (30 dni logów domyślnie)
W wielu kontraktach (ochrona zdrowia, finanse, obronność, sektor publiczny) to wyklucza takie użycie. Konfiguracja offline rozwiązuje wszystkie cztery problemy naraz.
Architektura stosu offline
Trzy komponenty, wszystkie lokalne. Ruch sieciowy zerowy (po jednorazowym pobraniu obrazu Dockera i modelu Ollama).
Krok 1: Ollama i model embeddingów
Instalacja
# macOS:
brew install ollama
brew services start ollama
# Linux:
curl -fsSL https://ollama.com/install.sh | sh
systemctl --user enable --now ollama
# Weryfikacja:
curl http://localhost:11434/api/tags
# powinno zwrócić {"models": []}
Wybór modelu
Ollama udostępnia kilka modeli embeddingów. Dla Claude Context polecane:
| Model | Wymiar | Rozmiar | Jakość | Komenda pull |
|---|---|---|---|---|
nomic-embed-text | 768 | 274 MB | dobra | ollama pull nomic-embed-text |
mxbai-embed-large | 1024 | 670 MB | bardzo dobra | ollama pull mxbai-embed-large |
bge-m3 | 1024 | 1,2 GB | flagowa | ollama pull bge-m3 |
Domyślnie wybieram nomic-embed-text: najlepszy kompromis między jakością a rozmiarem, mieści się w <300 MB. mxbai-embed-large bierzesz, gdy masz dużą bazę kodu (>10k plików) i zależy ci na semantyce.
ollama pull nomic-embed-text
# Weryfikacja że działa:
curl http://localhost:11434/api/embed \
-d '{"model": "nomic-embed-text", "input": "test embedding"}'
# powinno zwrócić {"embeddings": [[...768 liczb...]]}
Krok 2: Milvus w Dockerze
Milvus to ten sam silnik co w Zilliz Cloud, tylko lokalnie. Najprostsza konfiguracja to tryb standalone przez docker-compose.
Pobranie i uruchomienie
mkdir -p ~/milvus && cd ~/milvus
curl -L https://github.com/milvus-io/milvus/releases/download/v2.4.15/milvus-standalone-docker-compose.yml -o docker-compose.yml
docker compose up -d
Standalone startuje trzy kontenery: milvus-standalone, milvus-etcd, milvus-minio. Razem ~2 GB pamięci w spoczynku, ~500 MB dysku per 100k embeddingów.
Weryfikacja
# Health check:
curl http://localhost:9091/healthz
# powinno zwrócić 200 OK
# Port 19530: główne API gRPC
docker compose ps
# wszystkie trzy powinny być (healthy)
Utrwalenie danych
Domyślny docker-compose.yml zapisuje dane w wolumenie. Jeśli skasujesz kontenery, dane zostają. Jeśli chcesz reset:
docker compose down -v # -v usuwa też volumes
Krok 3: Claude Context z lokalnym backendem
Instalacja
claude mcp add claude-context \
-e EMBEDDING_PROVIDER=ollama \
-e EMBEDDING_MODEL=nomic-embed-text \
-e OLLAMA_HOST=http://localhost:11434 \
-e MILVUS_ADDRESS=localhost:19530 \
-e MILVUS_TOKEN= \
-e HYBRID_MODE=true \
-- npx @zilliz/claude-context-mcp@latest
Najważniejsze zmienne:
EMBEDDING_PROVIDER=ollamawyłącza domyślne OpenAIEMBEDDING_MODEL=nomic-embed-textmusi wskazywać model, który ściągnąłollama pullOLLAMA_HOSTto domyślniehttp://localhost:11434, ale lepiej ustawić jawnieMILVUS_ADDRESS=localhost:19530to lokalny port MilvusaMILVUS_TOKEN=zostaje pusty (lokalny Milvus nie wymaga tokena)HYBRID_MODE=truewłącza BM25 + wyszukiwanie wektorowe zamiast samego wektorowego
Globalna konfiguracja (alternatywa)
Zmienne można trzymać w ~/.context/.env zamiast w linii komend:
EMBEDDING_PROVIDER=ollama
EMBEDDING_MODEL=nomic-embed-text
OLLAMA_HOST=http://localhost:11434
MILVUS_ADDRESS=localhost:19530
MILVUS_TOKEN=
HYBRID_MODE=true
CUSTOM_IGNORE_PATTERNS=node_modules,dist,build,.planning,archives,.next,target
SPLITTER_TYPE=ast
CUSTOM_IGNORE_PATTERNS jest ważne, bo domyślnie Claude Context indeksuje wszystko. Wyklucz katalogi, które nie mają wartości semantycznej (artefakty builda, cache, archiwa).
Krok 4: pierwsze indeksowanie
W sesji Claude Code:
Zaindeksuj tę bazę kodu używając claude-context.
Claude wywoła index_codebase przez MCP. W zależności od wielkości repo to od kilku minut do pół godziny. Postęp można śledzić:
# W drugim terminalu:
docker compose -f ~/milvus/docker-compose.yml logs -f milvus-standalone | grep -i "inserted"
Dla porównania: 5000 plików TypeScript indeksuje się u mnie ~8 minut na M2, głównie w oczekiwaniu na Ollamę (wąskim gardłem jest embedding, nie Milvus).
Po zakończeniu weryfikacja:
Jaki jest status indeksowania?
Albo bezpośredni test:
Znajdź funkcje, które obsługują uwierzytelnianie użytkownika.
Jeśli semantyka działa, zwróci trafne fragmenty kodu bez czytania plików. To ten moment, kiedy widzisz oszczędność tokenów w akcji.
Rozwiązywanie problemów
DEADLINE_EXCEEDED na gRPC
Najczęściej w trybie Zilliz Cloud Free tier (mamy opcję offline, więc nie dotyczy). Ale jeśli trafisz na to lokalnie, Milvus nie wstał do końca. Sprawdź:
docker compose logs milvus-standalone | tail -50
Typowa przyczyna: kontener jeszcze się inicjalizuje (pierwszy start trwa do 2 minut). Odczekaj.
Osierocone pliki merkle
Drzewa Merkle służą do przyrostowego reindeksowania. Jeśli skasujesz kolekcję w Milvusie ręcznie, zostają puste pliki śledzące. Rozwiązanie:
rm -rf ~/.context/merkle/<nazwa-projektu>/
# w sesji Claude:
# "Zaindeksuj tę bazę kodu od zera"
Przekroczony czas odpowiedzi Ollamy podczas indeksowania
Duże pliki (>100 KB) bywają za ciężkie dla Ollamy w jednym żądaniu. W ~/.context/.env:
SPLITTER_MAX_CHUNK_SIZE=2000
SPLITTER_CHUNK_OVERLAP=200
To wymusza mniejsze fragmenty (2000 znaków z zakładką 200). Ollama radzi sobie z tym od ręki.
Niekompatybilny Node.js
Claude Context wymaga Node.js 20.x lub 22.x. Nie 24.0.0+, bo pakiet @zilliz/claude-context-mcp nie kompiluje się na 24. Jeśli masz nvm:
nvm install 22
nvm use 22
claude mcp add ... # świeża rejestracja
Benchmark jakości: offline a OpenAI
Claude Context oficjalnie benchmarkował konfigurację z OpenAI. Z Ollamą nomic-embed-text otrzymasz:
- F1 wyszukiwania niższe o ~5-10% (ale wciąż w użytecznym zakresie)
- Embeddingi szybsze lokalnie (brak dodatkowego opóźnienia sieciowego)
- Dłuższy zimny start (pierwszy embedding ~2 s na Apple Silicon, potem ~50-100 ms)
Dla większości zastosowań różnica jakości jest niezauważalna. Jeśli widzisz, że Claude często nie znajduje funkcji, które powinien, przesiądź się na mxbai-embed-large. To wyraźnie lepszy model, kosztem 670 MB dysku.
Monitorowanie i konserwacja
Miejsce na dysku
Lokalny Milvus zapisuje dane w ~/milvus/volumes/ (domyślnie). Dla średniego projektu:
- 1000 plików ≈ 50 MB
- 10 000 plików ≈ 500 MB
- 100 000 plików ≈ 5 GB
Sprawdzenie:
du -sh ~/milvus/volumes/
Czyszczenie starych kolekcji
Każdy projekt ma osobną kolekcję. Jeśli skończyłeś pracę z jakimś repo:
docker exec milvus-standalone attu-cli --help
# lub wejście do Python API:
python -c "from pymilvus import connections, utility; connections.connect('default', host='localhost', port='19530'); print(utility.list_collections())"
Aktualizacje Ollamy i modeli
ollama pull nomic-embed-text # zaktualizuje jeśli jest nowsza wersja
brew upgrade ollama # update samej Ollamy
Po aktualizacji modelu trzeba reindeksować, bo stare embeddingi nie są zgodne z nową wersją (inna skala, często inny wymiar).
Kiedy ta konfiguracja się nie opłaca
Offline ma koszt stały:
- ~2 GB RAM dla Milvusa
- ~300 MB-1 GB dla Ollamy (zależnie od modelu)
- Docker Desktop / Docker Engine
Jeśli pracujesz na małym projekcie (<500 plików), hobbystycznie, bez wymagań zgodności, to prostsza konfiguracja z OpenAI + Zilliz Cloud Free tier działa od razu, w 2 minuty, i kosztuje cię ~$0 miesięcznie. Offline to decyzja dla kontekstu firmowego albo mocnego przywiązania do prywatności.
Wracając do głównego artykułu: Claude Code: jak przestać palić tokeny. Dla terminala zamiast bazy kodu: RTK: pełna konfiguracja.