Claude Code без Anthropic: заводим агента на GLM-5.3 и Qwen через свой шлюз и собираем грабли. claude code.. claude code. DevOps.. claude code. DevOps. glm.. claude code. DevOps. glm. LiteLLM.. claude code. DevOps. glm. LiteLLM. llm.. claude code. DevOps. glm. LiteLLM. llm. qwen.. claude code. DevOps. glm. LiteLLM. llm. qwen. v100.. claude code. DevOps. glm. LiteLLM. llm. qwen. v100. vllm.. claude code. DevOps. glm. LiteLLM. llm. qwen. v100. vllm. искусственный интеллект.. claude code. DevOps. glm. LiteLLM. llm. qwen. v100. vllm. искусственный интеллект. Программирование.

Из Беларуси Claude Code официально не работает: Anthropic не обслуживает регион, а белорусской картой не оплатить ни подписку, ни API. Мы делаем ZuBrIQ — шлюз к моделям с оплатой в BYN — и за последние месяцы заставили Claude Code, Codex и OpenCode работать на открытых моделях — GLM-5.3 у провайдера и Qwen3.8-27B на собственных V100. Ниже — схема и пять граблей, на каждую из которых мы наступили сами.

Зачем вообще подменять модель под Claude Code

Claude Code — хороший агент, и он умеет ходить не только к Anthropic. Достаточно ANTHROPIC_BASE_URL и токена — и он шлёт запросы по протоколу Anthropic Messages куда угодно. Про это недавно был хороший обзор на Хабре — «Чем заменить Claude или Gemini после блокировки». Там подменяли модель на DeepSeek и MiniMax.

У нас задача шире. Нужны не один клиент и не одна модель, а десяток клиентов и несколько моделей за одним адресом и одним ключом. Плюс учёт расходов, резервные маршруты и модель, данные которой не покидают страну. Поэтому между агентами и моделями стоит шлюз на LiteLLM.

Схема

Claude Code ── /v1/messages ─────────┐
Codex ──────── /v1/responses ────────┤
OpenCode, Zed, Cursor, SDK ──────────┤
  /v1/chat/completions               │
                                     ▼
                         ┌───────────────────────┐
                         │   LiteLLM (2 пода)    │
                         │ ключи, бюджеты, логи  │
                         │ имя модели → маршруты │
                         └───────────┬───────────┘
              ┌──────────────────────┼───────────────────────┐
              ▼                      ▼                       ▼
     vLLM, 4×V100 16 ГБ      GLM-5.3 у провайдера    DeepSeek V4 Pro
     Qwen3.8-27B AWQ INT4    (основной маршрут)      (резерв)
     имя: zubr               имя: zubr-coder         под тем же именем

Главное правило — клиенты знают только стабильные имена: zubr, zubr-flash, zubr-coder. За каждым именем стоит список маршрутов с порядком и резервом. Сменились веса или упал провайдер — интеграции этого не замечают.

Грабля 1. Тихий уход в резерв на /v1/messages

Сначала мы завели GLM-5.3 как обычный OpenAI-совместимый бэкенд — провайдером openai/ с нужным api_base. Чат и SDK работали. А Claude Code и Codex получали ответы… от резервной модели.

Оказалось, LiteLLM для openai/ переводит /v1/messages (Claude Code) и /v1/responses (Codex) в OpenAI Responses API. Провайдер отвечает на /responses 404, и роутер молча уходит на следующий маршрут. В логах ошибки нет, ответ есть — просто не от той модели.

Выдаёт это только заголовок ответа:

x-litellm-attempted-fallbacks: 1

Лечится нативным провайдером LiteLLM для этого вендора: он переводит Messages и Responses в chat/completions, который у провайдера есть. Переменная LITELLM_USE_CHAT_COMPLETIONS_URL_FOR_ANTHROPIC_MESSAGES чинит только /v1/messages, Codex с ней всё равно уходит в резерв.

Вывод: порядок маршрутов LiteLLM соблюдает всегда. Если отвечает «не та» модель — смотрите на fallback, а не на order.

Грабля 2. Claude Code сам просит Haiku, Sonnet и Opus

Подменить ANTHROPIC_MODEL мало. Claude Code отдаёт служебные подзадачи — заголовки, сжатие контекста, субагентов — моделям классов Haiku, Sonnet и Opus по их именам. У ключа без доступа к Anthropic такие запросы падают с 403, причём посреди работы.

Рабочий ~/.claude/settings.json выглядит так:

{
  "env": {
    "ANTHROPIC_BASE_URL": "https://api.zubriq.ai",
    "ANTHROPIC_AUTH_TOKEN": "sk-…",
    "ANTHROPIC_MODEL": "zubr-coder[1m]",
    "ANTHROPIC_DEFAULT_OPUS_MODEL": "zubr-coder[1m]",
    "ANTHROPIC_DEFAULT_SONNET_MODEL": "zubr-flash[1m]",
    "ANTHROPIC_DEFAULT_HAIKU_MODEL": "zubr",
    "CLAUDE_CODE_MAX_CONTEXT_TOKENS": "262144",
    "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"
  }
}

Ключ выпускается в личном кабинете zubriq.ai, а zubrcode connect claude записывает этот файл сам. Три детали, каждая стоила нам вечера:

  • ANTHROPIC_BASE_URL — без /v1. Claude Code сам дописывает /v1/messages, и с /v1 получается /v1/v1/messages.

  • Суффикс [1m] говорит Claude Code, что у модели окно 1M, и отрезается до отправки: на шлюз уходит чистое zubr-coder. Без него агент считает окно по своим правилам и сжимает контекст слишком рано.

  • Для модели без [1m] окно задаёт CLAUDE_CODE_MAX_CONTEXT_TOKENS. На модели с [1m] эта переменная не влияет.

Грабля 3. Нулевые расходы

С нативным провайдером запросы пошли куда надо, но расход по ключам стал равен нулю. Для этого провайдера LiteLLM не берёт цену из model_info. Spend ноль — бюджеты ключей не срабатывают, отчёт по марже врёт.

Решение — продублировать цены прямо в litellm_params записи модели:

litellm_params:
  model: <provider>/glm-5.3
  input_cost_per_token: 0.0000012
  output_cost_per_token: 0.000004
  cache_read_input_token_cost: 0.00000022

Цифры условные — важно место, где они лежат. И ещё: правка модели через PATCH /model/{id}/update доезжает до второго пода не сразу, а через минуту-две. Не спешите считать, что не сработало.

Грабля 4. Модель, которая не видит картинок

GLM-5.3 силён в коде, но изображения не принимает. Причём ведёт себя по-разному в зависимости от входа:

Вход

Что происходит с картинкой

chat/completions провайдера

400: allowed values: ['text']

Anthropic-совместимый эндпоинт провайдера

молча выкидывается, модель «угадывает» по тексту

резерв DeepSeek V4 Pro

отвечает, что изображения нет

Самое опасное — второй вариант: ошибки нет, ответ уверенный и неверный. Пользователь кидает скриншот с ошибкой, а модель рассуждает о том, чего не видела.

В нашем агенте ZubrCode мы сделали подмену. Если у модели в каталоге нет зрения, картинка заменяется текстовым описанием от быстрой модели со зрением — GLM-5.3 Flash. Тонкость: агент переигрывает всю историю на каждом ходе. Без кеша одно и то же фото описывалось бы заново каждый раз. Поэтому описание кешируется по sha256 картинки на диске.

Отдельная мелочь в каталоге: supports_vision: null из /model/info у нас трактовался как явное false ((bool) null). Из-за этого зрение пропало и у модели, которая видит. Правильно — «не знаю, спроси выше по цепочке». И учтите: если под одним именем несколько маршрутов, флаги берутся с первого по порядку.

Грабля 5. Своя модель на V100

Для данных, которые не должны уходить за границу, у нас Qwen3.8-27B на собственных 4×V100 по 16 ГБ. Карты 2017 года, без bf16 и без FP8. Поэтому квант AWQ INT4 с вычислениями в FP16, vLLM с тензорным параллелизмом на 4 карты и форк vLLM с поддержкой SM70.

Что всплыло:

  • KV-кеш нужно задавать явно. Форк считает бюджет KV как total × gpu_memory_utilization − активации и не вычитает веса модели: загрузчик для SM70 отдаёт их размер почти нулём. Итог — KV съедает больше, чем можно, и соседу на тех же картах памяти не остаётся. Мы задаём --kv-cache-memory-bytes явно, а --gpu-memory-utilization оставили только как проверку свободной памяти при старте.

  • Два инстанса нельзя стартовать одновременно. Летом рядом с основной моделью жила отдельная VL-модель. Пик инициализации двух процессов был больше 16 ГБ — один воркер ловил OOM. Дальше хуже: основной инстанс зависал на NCCL watchdog. systemd показывал active, а порт был закрыт. Лечили так: второй юнит ждал health первого в ExecStartPre.

  • Dense против MoE. До сентября под этим именем была MoE 35B-A3B: около 3 млрд активных параметров, первый токен за ~0,6 с. Плотная 27B умнее и видит картинки сама, без отдельной VL-модели. Но первый токен теперь ~1,3 с. Для агента, который делает десятки вызовов, это заметно. Мы оставили её как «Haiku-слот»: фоновые подзадачи и всё, что нельзя отдавать наружу.

Текущие параметры: контекст 262 144 токена, max-num-seqs 24, KV ~4,6 ГБ на карту, tool calling через парсер qwen3_xml.

Что в итоге

  • Claude Code, Codex и OpenCode работают через один шлюз на GLM-5.3 и Qwen, без патчей клиентов — только конфиг.

  • Модель в ключе стабильна по имени, а маршруты и веса под ней меняются без участия пользователей.

  • Самые неприятные ошибки здесь молчаливые: уход в резерв, нулевой spend, выброшенная картинка. Мониторьте заголовки fallback и сверяйте расход, а не только коды ответов.

Шлюз из статьи — часть нашей платформы ZuBrIQ: там же готовые настройки для Claude Code и других клиентов. В следующих статьях цикла — подробно про vLLM на V100 и маскирование персональных данных до отправки в модель. Делитесь в комментариях, какие клиенты и модели вы связываете и на какие грабли наступали.

Автор: ZubikIT

Источник