- BrainTools - https://www.braintools.ru -

Claude Code без Anthropic: заводим агента на GLM-5.3 и Qwen через свой шлюз и собираем грабли

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

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

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

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

Схема

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, и роутер молча уходит на следующий маршрут. В логах ошибки [4] нет, ответ есть — просто не от той модели.

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

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 [1], а 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 мы сделали подмену. Если у модели в каталоге нет зрения [5], картинка заменяется текстовым описанием от быстрой модели со зрением — 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 съедает больше, чем можно, и соседу на тех же картах памяти [6] не остаётся. Мы задаём --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 [1]: там же готовые настройки для Claude Code [7] и других клиентов. В следующих статьях цикла — подробно про vLLM на V100 и маскирование персональных данных до отправки в модель. Делитесь в комментариях, какие клиенты и модели вы связываете и на какие грабли наступали.

Автор: ZubikIT

Источник [8]


Сайт-источник BrainTools: https://www.braintools.ru

Путь до страницы источника: https://www.braintools.ru/article/38153

URLs in this post:

[1] ZuBrIQ: https://zubriq.ai

[2] «Чем заменить Claude или Gemini после блокировки»: https://habr.com/ru/articles/1104258/

[3] LiteLLM: https://github.com/BerriAI/litellm

[4] ошибки: http://www.braintools.ru/article/4192

[5] зрения: http://www.braintools.ru/article/6238

[6] памяти: http://www.braintools.ru/article/4140

[7] Claude Code: https://docs.zubriq.ai/code/clients/claude-code/

[8] Источник: https://habr.com/ru/articles/1104826/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1104826

www.BrainTools.ru

Rambler's Top100