Кратко и вердикт
Когда лимит вашего плана Claude кончается посреди задачи, вы можете запустить тот же Claude Code на модели на собственном ноутбуке через Ollama. Мы сделали это на M1 Pro с 16 ГБ, с qwen3.5:9b, и дали ей две реальные задачи в небольшом пакете на Python.
- Задача A, опция
--prefixв одном файле: без--bareоколо пятнадцати минут ничего не происходило. С--bareона прошла за 93 s. - Задача B, переименование в нескольких файлах: локальная модель сообщила о зелёных тестах, а импорт был сломан в обоих запусках (контекст 4k и 64k). На плане Claude сделал это за 38 s, и импорты работали.
- Ollama даёт машине вроде нашей контекст 4k по умолчанию.
OLLAMA_CONTEXT_LENGTH=65536его увеличивает.
Вердикт: алиас стоит настроить для задач в одном файле, которые можно проверить с одного взгляда. Всё, что затрагивает несколько файлов, ждёт плана, а diff вы читаете всегда. В наборе есть блок алиаса и одностраничная карточка с правилом.
Синтез с источниками
Настройка это три переменные окружения и имя модели. Руководство Ollama направляет Claude Code на http://localhost:11434 с ANTHROPIC_AUTH_TOKEN=ollama s1. В документации Claude Code сказано, что ANTHROPIC_BASE_URL переопределяет эндпоинт API s17. Поскольку переменные стоят в строке алиаса, обычный claude продолжает работать на вашем плане.
Заданный API key заменяет вашу подписку. В документации Claude Code сказано, что заданный ANTHROPIC_API_KEY используется вместо подписки, даже если вы вошли в аккаунт s17. Поэтому переменные должны быть в алиасе, а не в профиле вашего shell.
Модель должна помещаться в память. qwen3.5:9b указана как 6,6 ГБ до 7,6 ГБ с контекстом 256K и поддержкой инструментов s18. На нашей машине ollama ps показал 7,6 ГБ при контексте по умолчанию и 7,7 ГБ при 65536.
Контекст по умолчанию это первая ловушка. Ollama использует контекст 4k при VRAM меньше 24 GiB и рекомендует для инструментов программирования значение не меньше 64000 s3. Вы увеличиваете его через OLLAMA_CONTEXT_LENGTH при запуске сервера, а ollama ps показывает, применилось ли значение s3. Руководство Ollama также просит окно 64k или больше s1.
--bare это вторая. В одном разборе первый запрос по умолчанию занимал 14,0k токенов, а с --bare 0,75k s8. Мы токены не считали. Мы видели вот что: та же задача при том же контексте 4k прошла путь от ничего за 900 s до успеха за 93 s. --bare также пропускает hooks, plugins, память и CLAUDE.md, так что локальная модель никогда не видит правил вашего проекта. В нашем Claude Code 2.1.294 справка говорит, что аутентификация строго через ANTHROPIC_API_KEY или apiKeyHelper, поэтому алиас задаёт ANTHROPIC_API_KEY=ollama. Пустой ключ под --bare мы не проверяли.
Настройка заголовка есть в алиасе, но мы её не измеряли. В документации Unsloth сказано, что заголовок атрибуции инвалидирует локальный кеш и что CLAUDE_CODE_ATTRIBUTION_HEADER=0 это исправляет s5, а в issue Claude Code прослеживаются те же промахи кеша s9. --bare уже пропускает атрибуцию, поэтому мы не можем сказать, что дала дополнительная переменная в наших запусках.
Маленькие локальные модели часто ошибаются в контракте инструментов, а не в коде. В одном бенчмарке модели вызывали write_file вместо Write s7. В трекере Ollama есть сообщение об обрезанных и пропущенных вызовах инструментов s10. Ошибки имени инструмента мы не видели. Наш сбой был тише: переименование, оставившее сломанный импорт.
Первые ходы могут быть медленными. В полевом отчёте о qwen3.5:9b с контекстом 64k на ноутбуке с 32 ГБ первый запрос занял больше 10 минут, а последующие ходы от 1 до 3 минут s6. Наши времена в таблице ниже.
Чего эндпоинт не умеет. На странице совместимости Ollama с Anthropic prompt caching и принудительный выбор инструмента значатся неподдерживаемыми s2. В документации Anthropic сказано, что маршрутизация Claude Code на модели, не являющиеся Claude, через любой шлюз не поддерживается s4. Один критический обзор приходит к тому же, что и мы: локально только для рутинной работы s11.
Простой алиас лучше роутера, который переиспользует токен вашей подписки. В одном руководстве по настройке сообщается, что такие роутеры падают с 401 s13.
Код
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'
: блок алиаса с правилом переключения в виде трёх комментариев над ним. Вставьте его в `~/.zshrc`, затем выполните `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.
: одностраничная карточка с правилом: какие задачи идут на локальную модель, какие ждут план, и проверка, которую вы выполняете всегда.
- 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
: скрипт, которым мы замеряли задачу A или B на плане или на алиасе и сохраняли diff и проверку. Сначала задайте `SLUGKIT` как ваш тестовый репозиторий и `RUNS` как папку для результатов.Сделайте в понедельник
- Выполните
ollama --versionиclaude --version. Мы использовали Ollama 0.40.13 s19 и Claude Code 2.1.294. - Скачайте модель, которая помещается в вашу память. Для 16 ГБ мы взяли
qwen3.5:9b, это 7,6 ГБ. - Запустите Ollama командой
OLLAMA_CONTEXT_LENGTH=65536 ollama serve, затем выполнитеollama psи посмотрите столбец CONTEXT. - Вставьте блок из
claude-alt.zshв~/.zshrcи выполнитеsource ~/.zshrc. Убедитесь, что обычныйclaudeпо-прежнему работает на вашем плане. - Выберите задачу в одном файле в одноразовой копии репозитория и выполните её через
claude-alt. - Прочитайте diff и выполните проверку сами. Не полагайтесь на отчёт самой модели.
- Попробуйте одно переименование в нескольких файлах в той же копии. Потом вручную импортируйте старые имена.
- Держите
switch-rule.mdрядом с алиасом и правьте его по результатам ваших собственных запусков.
Измерения
Машина: MacBook Pro M1 Pro, 16 ГБ, Claude Code 2.1.294, Ollama 0.40.13, qwen3.5:9b (7,6 ГБ). Репозиторий был небольшим пакетом на Python из 4 модулей с 12 тестами unittest, написанным для этого теста. Всё время машина была под давлением памяти, поэтому локальные времена это верхняя оценка.
| Задача | Маршрут | Время | Результат |
|---|---|---|---|
| A, добавить --prefix (один файл) | План, Claude | 21 s | Прошла, blog-hello-world |
| A, добавить --prefix (один файл) | Алиас, 4k, без --bare | 900 s | Остановлена по лимиту 15 минут, правок нет, ответа нет |
| A, добавить --prefix (один файл) | Алиас, 4k, --bare | 93 s | Прошла, blog-hello-world |
| A, добавить --prefix (один файл) | Алиас, 64k, --bare | 35 s | Прошла, blog-hello-world |
| A, добавить --prefix (один файл) | Алиас, 64k, без --bare | не завершена | Убита системой из-за нехватки памяти, результата нет |
| B, переименование в нескольких файлах | План, Claude | 38 s | Прошла, 12 тестов OK, старый и новый импорты работают, тесты переименованы |
| B, переименование в нескольких файлах | Алиас, 4k, --bare | 464 s | Сломано: "All 12 unit tests pass", но from slugkit import truncate_words вызвал ImportError |
| B, переименование в нескольких файлах | Алиас, 64k, --bare | 142 s | Сломано: 12 тестов OK и сообщено о завершении, но алиас попал в __init__.py, поэтому from slugkit.truncate import smart_truncate вызвал ImportError |
В задаче B просили оставить smart_truncate как алиас в slugkit/truncate.py. Claude Code также предупредил, что не знает модель, и предположил окно в 200k токенов. Наши сессии оставались намного ниже, а длинную мы не проверяли.
Что дальше
Что мы оставили за рамками и где искать, если это нужно.
- Хостинговые маршруты. Провайдеры вроде DeepSeek, Z.AI или OpenRouter используют тот же алиас с другим базовым URL. Они тарифицируют по токенам, а ключа у нас не было, поэтому мы их не запускали.
- Роутеры на бесплатных тарифах. Роутеры, объединяющие бесплатные тарифы, обещают Claude Code даром. Мы их не тестировали. Проверьте их условия и то, куда уходят ваши prompts, прежде чем пробовать.
- Облачные модели Ollama. Бесплатного тарифа больше нет, они оплачиваются по факту использования.
- Более крупные локальные модели. Мы не пробовали модели диапазона от 26b до 35b, которые не помещаются в 16 ГБ с контекстом 64k. Если у вас 32 ГБ или больше, в одном полевом отчёте модель 35b прошла шесть оцениваемых задач и выполнила все шесть, примерно в 8 раз медленнее облачных моделей s12.
- Гибридная маршрутизация. Локально для субагентов и план для основного цикла: такую схему мы не пробовали.
- Другие маленькие модели. gemma4:e4b это запасной вариант, который используют некоторые руководства. Мы её не запускали.
- Настоящий бенчмарк. Две задачи на одном пакете показывают закономерность, а не рейтинг. Чтобы расширить правило, запустите свои задачи через
run-task.shи ведите таблицу вроде приведённой выше.
FAQ
Меняется ли обычный claude, когда я добавляю алиас?
Нет. Переменные стоят в строке алиаса, поэтому с Ollama общается только claude-alt.
Почему алиас задаёт ANTHROPIC_API_KEY=ollama, а не пустой ключ?
Под --bare Claude Code берёт ключ только из ANTHROPIC_API_KEY или apiKeyHelper. Наш алиас с ollama в качестве ключа работал. Пустой ключ под --bare мы до конца не проверяли.
Поддерживает ли это Anthropic? Нет. В документации Anthropic сказано, что маршрутизация Claude Code на модели, не являющиеся Claude, через любой шлюз не поддерживается s4.
Можно ли доверять зелёному прогону тестов от локальной модели? Только в том, что покрывают тесты. В задаче B тесты прошли при сломанном импорте, потому что старый путь импорта никто не тестировал. Прочитайте diff и вручную импортируйте старые имена.
Будет ли это так же быстро, как план? Не в наших запусках. Задача B заняла 38 s на плане и от 142 s до 464 s на алиасе, и всё равно сломалась.
AIDive