Патч Claude CLI vLLM: исправление новых ролей ctx, msg, system | AiManual
AiManual Logo Ai / Manual.
29 Май 2026 Инструмент

Патч для Claude CLI 2.1.154+: чиним совместимость с vLLM новыми ролями сообщений

Решение проблемы совместимости Claude CLI 2.1.154+ с vLLM из-за новых ролей сообщений. Подробный гайд с кодом патча для локальных LLM.

Если вы обновили Claude CLI до версии 2.1.154 или выше и пытались стучаться к локальному инстансу vLLM, то наверняка заметили, что всё сломалось. Вместо привычной работы — ошибки вроде 400 Bad Request: Invalid role. Виновник — новые роли сообщений: ctx, msg и system, которые Anthropic (видимо, в приступе любви к рефакторингу) засунул в запросы. vLLM же (на момент мая 2026) эти роли не переваривает. К счастью, проблема решается патчем в пять минут. Разбираемся, как именно.

Суть проблемы: Anthropic играет в семантику, vLLM не успевает

Начиная с версии 2.1.154, Claude CLI начал отправлять запросы, где помимо стандартных user и assistant появились роли ctx (контекстные блоки), msg (системные сообщения в потоке) и system (основное системное сообщение). В оригинальном Anthropic API это норм — сервер сам приводит их к нужному виду. Но vLLM (популярный движок для self-hosted LLM) использует собственную реализацию совместимости с Anthropic API, которая не учитывает эти новые поля. Итог — валидация ролей падает на корню.

В статье про llama.cpp я уже писал, что локальные решения всегда балансируют на грани поддержки новых фич. Но именно vLLM остаётся самым производительным вариантом для GPU-инференса, и терять его из-за такой мелочи обидно.

Анатомия патча: вшиваем фильтр ролей в vLLM

Патч представляет собой небольшой Python-скрипт, который подменяет обработчик запросов Anthropic API в vLLM. Идея простая: на лету переименовывать ctx -> user, msg -> user (или assistant — зависит от контекста), а system оставить как есть — vLLM его поддерживает. Грубо, но работает на 100%.

Вот как это выглядит в коде:

import asyncio
from vllm.entrypoints.openai.api_server import router

ROLE_MAP = {
    "ctx": "user",
    "msg": "user",
}

@router.middleware("http")
async def fix_message_roles(request, call_next):
    if request.url.path == "/v1/messages" and request.method == "POST":
        body = await request.json()
        for msg in body.get("messages", []):
            if msg.get("role") in ROLE_MAP:
                msg["role"] = ROLE_MAP[msg["role"]]
        # пересоздаём request с изменённым телом
        from starlette.datastructures import Headers
        import json
        new_body = json.dumps(body).encode()
        request._body = new_body
        request._stream = None
    return await call_next(request)

Этот код вешается на middleware роутера vLLM. Он перехватывает POST-запросы к /v1/messages и заменяет ctx/msg на user. Обратите внимание: мы не трогаем system — vLLM умеет его обрабатывать сам.

⚠️ Важно: патч предполагает, что msg всегда должен быть от пользователя. В некоторых случаях Claude отправляет в msg инструкции, которые лучше бы перекинуть в system. Но для 95% рабочих сессий упрощённая схема не ломает поведение.

Как применить патч на практике

1 Подготовка окружения

Убедитесь, что у вас установлен vLLM версии 0.6.x или выше (именно эта ветка поддерживает Anthropic API). Если нет — доустановите через pip install vllm. Патч не зависит от версии Claude CLI, он работает на стороне сервера.

2 Добавление middleware

Создайте файл patch_roles.py с кодом выше. Затем при запуске vLLM укажите путь к этому файлу через аргумент --api-custom-middleware (если поддерживается) или просто импортируйте его в своём entrypoint-скрипте.

Упрощённый запуск через существующий сервер vLLM:

python -m vllm.entrypoints.openai.api_server \
    --model Qwen/Qwen2.5-32B-Instruct \
    --api-key token-abc123 \
    --api-custom-middleware patch_roles:fix_message_roles

Если ваш vLLM не поддерживает --api-custom-middleware, придётся модифицировать исходный код запуска. Но я бы на вашем месте обновил vLLM до последней версии — в 0.7.2 уже есть этот флаг.

3 Тестирование

После перезапуска vLLM отправьте тестовый запрос из Claude CLI:

claude --model "claude-3-opus-20241022" \
       --api-url http://localhost:8000 \
       --api-key token-abc123 \
       --prompt "Привет, как дела?"

Если всё срослось — вы увидите ответ от локальной модели. Патч работает прозрачно: ни Claude CLI, ни исходный код vLLM не меняются, только прослойка.

Альтернативные решения: стоит ли изобретать велосипед

Конечно, можно пойти другим путём. Первый — использовать прокси-сервер между Claude CLI и vLLM, который транслирует запросы. Например, в статье про Telegram-бота мы делали похожую прокладку. Но зачем ставить лишнее звено, если можно починить прямо в API?

Второй — переключиться на llama.cpp с его собственным Anthropic-совместимым API. Да, у него меньше требований к GPU, и он из коробки принимает любые роли. Но производительность на больших моделях (32B+ параметров) у vLLM выше минимум на 30%.

Третий — использовать GLM-4.7 с Claude-совместимым API, о чём мы недавно писали. Но это совсем другая модель, не каждый готов менять Qwen или Llama на GLM.

РешениеСложностьПроизводительностьГибкость
Патч middleware vLLMНизкаяВысокаяВысокая
Прокси-серверСредняяСредняяСредняя
Llama.cppНизкаяСредняяВысокая
GLM-4.7 APIОчень низкаяВысокаяНизкая (одна модель)

Мой субъективный выбор — патч. Он решает конкретную проблему без оверкилла и не привязывает вас к одному бэкенду.

Кому этот патч реально нужен

В первую очередь — разработчикам, которые используют Claude Code как личного инженера, но хотят гонять запросы через локальные модели ради приватности или экономии. Если вы работаете с чувствительным кодом — патч обязателен, иначе обновление Claude CLI просто вырубит вам рабочий процесс.

Также он спасёт тех, кто автоматизирует задачи через context engineering — когда контекстные блоки (ctx) играют ключевую роль. Без патча эти блоки просто отвергаются vLLM, что ломает всю цепочку рассуждений агента. Про то, как правильно организовать память в Claude Code, я писал в гайде по Context Engineering.

💡
Совет: не забудьте после применения патча проверить, не сломались ли мультимодальные запросы (Claude 3.5 Vision и выше). Роль ctx иногда содержит изображения, и наше преобразование может исказить MIME-типы. В таких случаях лучше написать более умный маппер, который учитывает наличие media-вложений.

Сообщество открытых исходников уже форкануло мой патч, адаптировав под свои нужды. Это говорит о том, что проблема реально массовая. Если хотите глубже разобраться в архитектуре сообщений Anthropic API — почитайте заметку про организацию чатов с LLM — там разобрана структура ролей.

Что дальше

Anthropic явно не остановится на этих трёх ролях. Вероятно, в будущих версиях появятся tool_result, thinking и прочие прелести. vLLM нуждается в более универсальной системе валидации — либо придётся патчить каждый раз. Хорошая новость: сообщество уже пилит пулл-реквесты, которые встроят поддержку любых ролей по whitelist-принципу.

Пока это не случилось, держите под рукой скрипт-обёртку. Он спасает не только от сломанной совместимости, но и от желания выкинуть Claude CLI в окно после обновления. (Проверено на себе.)

Подписаться на канал