TL;DR et verdict
Quand la limite de votre forfait Claude tombe en pleine tâche, vous pouvez faire tourner le même Claude Code sur un modèle de votre propre laptop via Ollama. Nous l'avons fait sur un M1 Pro avec 16 Go, avec qwen3.5:9b, en lui donnant deux vraies tâches dans un petit package Python.
- Tâche A, une option
--prefixdans un fichier : sans--bare, rien ne s'est passé pendant environ quinze minutes. Avec--bare, réussi en 93 s. - Tâche B, un renommage entre fichiers : le modèle local a annoncé les tests verts, et un import était cassé dans les deux runs (contexte 4k et 64k). Sur le forfait, Claude l'a fait en 38 s avec les imports fonctionnels.
- Ollama donne par défaut un contexte de 4k à une machine comme la nôtre.
OLLAMA_CONTEXT_LENGTH=65536l'augmente.
Verdict : l'alias vaut le coup pour les jobs sur un fichier que vous pouvez vérifier d'un coup d'œil. Tout ce qui traverse les fichiers attend le forfait, et vous lisez toujours le diff. Le pack vous donne le bloc d'alias et une fiche d'une page avec la règle.
Synthèse sourcée
La mise en place tient en trois variables d'environnement et un nom de modèle. Le guide d'Ollama pointe Claude Code vers http://localhost:11434 avec ANTHROPIC_AUTH_TOKEN=ollama s1. La doc de Claude Code dit que ANTHROPIC_BASE_URL remplace l'endpoint de l'API s17. Comme les variables sont sur la ligne de l'alias, un simple claude continue d'utiliser votre forfait.
Une clé d'API définie remplace votre abonnement. La doc de Claude Code dit qu'une ANTHROPIC_API_KEY définie est utilisée à la place de votre abonnement, même si vous êtes connecté s17. C'est pourquoi les variables doivent aller dans l'alias et non dans votre profil de shell.
Le modèle doit tenir en mémoire. qwen3.5:9b est indiqué à 6,6 Go à 7,6 Go avec un contexte de 256K et la prise en charge des outils s18. Sur notre machine, ollama ps affichait 7,6 Go au contexte par défaut et 7,7 Go à 65536.
La valeur par défaut du contexte est le premier piège. Ollama utilise un contexte de 4k sous 24 GiB de VRAM, et dit que les outils de code doivent être réglés à au moins 64000 s3. Vous l'augmentez avec OLLAMA_CONTEXT_LENGTH au démarrage du serveur, et ollama ps montre si c'est pris en compte s3. Le guide d'Ollama demande aussi une fenêtre de 64k ou plus s1.
--bare est le second. Un article a mesuré la première requête par défaut à 14,0k tokens et celle avec --bare à 0,75k s8. Nous n'avons pas compté les tokens. Ce que nous avons vu : la même tâche au même contexte 4k est passée de rien en 900 s à une réussite en 93 s. --bare saute aussi les hooks, les plugins, la mémoire et CLAUDE.md, donc le modèle local ne voit jamais les règles de votre projet. Dans notre Claude Code 2.1.294, l'aide dit que l'authentification passe strictement par ANTHROPIC_API_KEY ou apiKeyHelper, donc l'alias définit ANTHROPIC_API_KEY=ollama. Nous n'avons pas testé une clé vide sous --bare.
Un réglage d'en-tête est dans l'alias, mais nous ne l'avons pas mesuré. La doc d'Unsloth dit que l'en-tête d'attribution invalide le cache local et que CLAUDE_CODE_ATTRIBUTION_HEADER=0 règle ça s5, et une issue de Claude Code retrace les mêmes échecs de cache s9. --bare saute déjà l'attribution, donc nous ne pouvons pas dire ce que la variable en plus a changé sur nos runs.
Les petits modèles locaux échouent souvent sur le contrat des outils, pas sur le code. Un benchmark a trouvé des modèles qui appellent write_file au lieu de Write s7. Le tracker d'issues d'Ollama contient un rapport sur des appels d'outils tronqués ou perdus s10. Nous n'avons vu aucune erreur de nom d'outil. Notre échec était plus discret : un renommage qui laissait un import cassé.
Les premiers tours peuvent être lents. Un retour terrain sur qwen3.5:9b avec un contexte de 64k, sur un laptop de 32 Go, a vu une première requête de plus de 10 min et des tours suivants de 1 à 3 min s6. Nos durées sont dans le tableau ci-dessous.
Ce que l'endpoint ne sait pas faire. La page de compatibilité Anthropic d'Ollama liste le prompt caching et le choix d'outil forcé comme non pris en charge s2. La doc d'Anthropic dit qu'elle ne prend pas en charge le routage de Claude Code vers des modèles qui ne sont pas Claude, quelle que soit la passerelle s4. Une revue critique arrive au même point que nous : du local pour le travail de routine seulement s11.
Un alias simple vaut mieux qu'un routeur qui réutilise le token de votre abonnement. Un guide de mise en place signale que ces routeurs échouent avec une 401 s13.
Code
claude-alt.zsh
# claude-alt: one-file jobs I can check in a glance
# plan: anything across files, or that I can't check by reading the diff
# always: read the diff before committing
alias claude-alt='CLAUDE_CODE_ATTRIBUTION_HEADER=0 ANTHROPIC_AUTH_TOKEN=ollama ANTHROPIC_API_KEY=ollama ANTHROPIC_BASE_URL=http://localhost:11434 claude --bare --model qwen3.5:9b'
: le bloc d'alias avec la règle de bascule en trois commentaires au-dessus. Collez-le dans `~/.zshrc`, puis lancez `source ~/.zshrc`.
- switch-rule.md
# The switch rule
Keep this next to the claude-alt alias. Decide before your limit hits, not while you wait.
## Goes to claude-alt (local model)
- A job in one file that you can check at a glance
- Adding an option, explaining a function, testing one function
- Work where a wrong result is visible in one short diff
## Waits for the plan
- Anything that crosses files, such as a rename with callers in other modules
- Anything you cannot check by reading the diff
- Work that depends on your hooks or your project rules (the alias runs with --bare, which skips both)
- Long sessions
## Always
- Read the diff before you commit, whichever model wrote it
- After a rename, import the old names by hand. A green test run only covers what the tests cover
## Why this rule
Two tasks on a 16 GB M1 Pro with qwen3.5:9b:
- One file (add a --prefix option): passed in 93 s with --bare.
- Across files (rename a function, keep the old name as an alias): the model said the tests were green, and an import was broken in both runs.
- On the plan, Claude did the rename in 38 s with both the old and the new imports working.
Two tasks on one small package is a starting point. Widen the rule with your own runs.
: une fiche d'une page indiquant quels jobs vont en local, lesquels attendent le forfait, et la vérification à toujours faire.
- run-task.sh
#!/bin/zsh
# Usage: run-task.sh <name> <A|B> <plan|alt|alt-nobare> [timeout_s]
# Runs one task on a fresh copy of slugkit and records time, output, diff and the check.
set -u
NAME=$1; TASK=$2; MODE=$3; TO=${4:-1800}
SRC=${SLUGKIT:?set SLUGKIT to the slugkit repo}
OUT=${RUNS:?set RUNS to the output dir}/$NAME
rm -rf $OUT; mkdir -p $OUT; cp -R $SRC $OUT/repo; cd $OUT/repo
A='In slugkit/cli.py add a --prefix option: when given, the slug starts with the prefix and the separator (python3 -m slugkit --prefix blog Hello World prints blog-hello-world). Then run that command to check it.'
B='Rename the function smart_truncate to truncate_words everywhere in this repo (definition, imports, calls, tests, __all__). Keep smart_truncate as an alias of truncate_words in slugkit/truncate.py so old imports still work. Then run python3 -m unittest discover tests and make sure every test passes.'
[[ $TASK == A ]] && P=$A || P=$B
TOOLS=(--allowedTools "Read" "Edit" "Write" "Glob" "Grep" "Bash(python3:*)" "Bash(grep:*)" "Bash(ls:*)")
start=$(date +%s)
case $MODE in
plan) perl -e 'alarm shift; exec @ARGV' $TO claude -p "$P" --output-format json $TOOLS > $OUT/out.json 2> $OUT/err.log ;;
alt) CLAUDE_CODE_ATTRIBUTION_HEADER=0 ANTHROPIC_AUTH_TOKEN=ollama ANTHROPIC_API_KEY=ollama ANTHROPIC_BASE_URL=http://localhost:11434 \
perl -e 'alarm shift; exec @ARGV' $TO claude --bare --model qwen3.5:9b -p "$P" --output-format json $TOOLS > $OUT/out.json 2> $OUT/err.log ;;
alt-nobare) CLAUDE_CODE_ATTRIBUTION_HEADER=0 ANTHROPIC_AUTH_TOKEN=ollama ANTHROPIC_API_KEY="" ANTHROPIC_BASE_URL=http://localhost:11434 \
perl -e 'alarm shift; exec @ARGV' $TO claude --model qwen3.5:9b -p "$P" --output-format json $TOOLS > $OUT/out.json 2> $OUT/err.log ;;
esac
rc=$?; end=$(date +%s)
git diff --stat > $OUT/diff.stat; git diff > $OUT/diff.patch
if [[ $TASK == A ]]; then python3 -m slugkit --prefix blog Hello World > $OUT/check.txt 2>&1; else python3 -m unittest discover tests > $OUT/check.txt 2>&1; grep -rn smart_truncate slugkit tests >> $OUT/check.txt; fi
echo "{\"name\":\"$NAME\",\"task\":\"$TASK\",\"mode\":\"$MODE\",\"rc\":$rc,\"seconds\":$((end-start))}" > $OUT/meta.json
cat $OUT/meta.json; tail -3 $OUT/check.txt
: le script qui nous a servi à chronométrer la tâche A ou B sur le forfait ou sur l'alias, et à sauvegarder le diff et la vérification. Définissez d'abord `SLUGKIT` vers votre dépôt de test et `RUNS` vers un dossier de sortie.À faire lundi
- Lancez
ollama --versionetclaude --version. Nous avons utilisé Ollama 0.40.13 s19 et Claude Code 2.1.294. - Téléchargez un modèle qui tient dans votre mémoire. Pour 16 Go, nous avons pris
qwen3.5:9b, qui fait 7,6 Go. - Démarrez Ollama avec
OLLAMA_CONTEXT_LENGTH=65536 ollama serve, puis lancezollama pset lisez la colonne CONTEXT. - Collez le bloc de
claude-alt.zshdans~/.zshrcet lancezsource ~/.zshrc. Vérifiez qu'un simpleclaudeutilise toujours votre forfait. - Choisissez une tâche sur un fichier dans une copie jetable d'un dépôt et lancez-la avec
claude-alt. - Lisez le diff et lancez la vérification vous-même. Ne vous fiez pas au rapport du modèle.
- Essayez un renommage multi-fichiers dans la même copie. Importez ensuite les anciens noms à la main.
- Gardez
switch-rule.mdà côté de l'alias et modifiez-le avec ce que montrent vos propres runs.
Mesures
Machine : MacBook Pro M1 Pro, 16 Go, Claude Code 2.1.294, Ollama 0.40.13, qwen3.5:9b (7,6 Go). Le dépôt était un petit package Python de 4 modules avec 12 tests unittest, écrit pour ce test. La machine a été sous pression mémoire pendant toute la durée, donc les durées locales sont une borne haute.
| Tâche | Route | Durée | Résultat |
|---|---|---|---|
| A, ajout de --prefix (un fichier) | Forfait, Claude | 21 s | Réussi, blog-hello-world |
| A, ajout de --prefix (un fichier) | Alias, 4k, sans --bare | 900 s | Tué au plafond de 15 minutes, aucune modification, aucune réponse |
| A, ajout de --prefix (un fichier) | Alias, 4k, --bare | 93 s | Réussi, blog-hello-world |
| A, ajout de --prefix (un fichier) | Alias, 64k, --bare | 35 s | Réussi, blog-hello-world |
| A, ajout de --prefix (un fichier) | Alias, 64k, sans --bare | non terminé | Tué par le système faute de mémoire, aucun résultat |
| B, renommage entre fichiers | Forfait, Claude | 38 s | Réussi, 12 tests OK, ancien et nouvel import fonctionnent, tests renommés |
| B, renommage entre fichiers | Alias, 4k, --bare | 464 s | Cassé : "All 12 unit tests pass", mais from slugkit import truncate_words a levé ImportError |
| B, renommage entre fichiers | Alias, 64k, --bare | 142 s | Cassé : 12 tests OK et job annoncé terminé, mais l'alias est allé dans __init__.py, donc from slugkit.truncate import smart_truncate a levé ImportError |
La tâche B demandait de garder smart_truncate comme alias dans slugkit/truncate.py. Claude Code a aussi prévenu qu'il ne connaissait pas le modèle et supposait une fenêtre de 200k tokens. Nos sessions sont restées très en dessous, et nous n'en avons pas testé de longue.
Aller plus loin
Ce que nous avons laissé de côté, et où chercher si cela vous intéresse.
- Routes hébergées. Des fournisseurs comme DeepSeek, Z.AI ou OpenRouter utilisent le même alias avec une autre URL de base. Ils facturent au token et nous n'avions pas de clé, donc nous ne les avons pas testés.
- Routeurs à offre gratuite. Des routeurs qui mutualisent des offres gratuites promettent Claude Code sans payer. Nous ne les avons pas testés. Vérifiez leurs conditions et où vont vos prompts avant d'en essayer un.
- Modèles cloud d'Ollama. Il n'y a plus d'offre gratuite, ils sont donc payants à l'usage.
- Modèles locaux plus gros. Nous n'avons pas essayé les modèles de la gamme 26b à 35b, qui ne tiennent pas dans 16 Go avec un contexte de 64k. Avec 32 Go ou plus, un retour terrain a fait tourner un modèle 35b sur six tâches notées et a obtenu six sur six, pour environ 8 fois la durée des modèles cloud s12.
- Routage hybride. Du local pour les subagents et le forfait pour la boucle principale est une config que nous n'avons pas essayée.
- Autres petits modèles. gemma4:e4b est une solution de repli que certains guides utilisent. Nous ne l'avons pas lancé.
- Un vrai benchmark. Deux tâches sur un package montrent une tendance, pas un classement. Pour élargir la règle, lancez vos propres tâches avec
run-task.shet gardez un tableau comme celui ci-dessus.
FAQ
Est-ce qu'un simple claude change quand j'ajoute l'alias ?
Non. Les variables sont sur la ligne de l'alias, donc seul claude-alt parle à Ollama.
Pourquoi l'alias définit-il ANTHROPIC_API_KEY=ollama et non une clé vide ?
Sous --bare, Claude Code lit sa clé uniquement depuis ANTHROPIC_API_KEY ou apiKeyHelper. Notre alias avec ollama comme clé a fonctionné. Nous n'avons pas testé jusqu'au bout une clé vide sous --bare.
Est-ce pris en charge par Anthropic ? Non. La doc d'Anthropic dit qu'elle ne prend pas en charge le routage de Claude Code vers des modèles qui ne sont pas Claude, quelle que soit la passerelle s4.
Puis-je me fier à un run de tests vert du modèle local ? Seulement pour ce que les tests couvrent. Dans la tâche B, les tests sont passés alors qu'un import était cassé, parce que rien ne testait l'ancien chemin d'import. Lisez le diff et importez les anciens noms à la main.
Sera-t-il aussi rapide que le forfait ? Pas sur nos runs. La tâche B a pris 38 s sur le forfait et de 142 s à 464 s sur l'alias, et elle a quand même cassé.
AIDive