AIDive

Пакет к видео

Claude Code на Ollama: алиас claude-alt и правило переключения

6 мин чтения

Кратко и вердикт

Когда лимит вашего плана 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'

claude-alt.zsh on GitHub

: блок алиаса с правилом переключения в виде трёх комментариев над ним. Вставьте его в `~/.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.

switch-rule.md on GitHub

: одностраничная карточка с правилом: какие задачи идут на локальную модель, какие ждут план, и проверка, которую вы выполняете всегда. -

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

run-task.sh on GitHub

: скрипт, которым мы замеряли задачу 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 на алиасе, и всё равно сломалась.

Файлы