Почему Gemma 4 через llama.cpp падает с ошибками 400 и 500?
Вы настроили tool calling для Gemma 4 в llama.cpp, отправили запрос и получили в ответ молчаливый HTTP 500. Или, что еще хуже, 400 с туманным сообщением о неверном формате. Знакомая картина? Мне пришлось потратить целый день, чтобы докопаться до сути. Оказалось, проблема не в вашем коде, а в тонкостях обработки chat template самой моделью и тем, как llama.cpp ее упаковывает для отправки.
На момент 08 апреля 2026 года актуальная версия Gemma 4 (не Gemma 3 или 2) использует обновленный формат системных промптов и специфичные токены для вызова инструментов. Llama.cpp версии, вышедшей до марта 2026 года, этого не знала.
Мой путь от 500-й ошибки до работающего tool call
Я не стал сразу лезть в километры логов. Вместо этого я скормил всю историю запросов и ответов сервера, включая сырые заголовки и тело ошибки, ChatGPT-4.5 Turbo (самая новая версия на апрель 2026). ИИ сразу указал на несоответствие в структуре JSON, которое порождало ошибку валидации на стороне сервера llama.cpp. Но это была лишь верхушка айсберга.
1 Находим корень зла в исходниках llama.cpp
Ошибка 400 — это почти всегда проблема с входными данными. Используя подсказку от ChatGPT, я начал искать в коде llama.cpp (ветка master на апрель 2026) функцию, которая формирует контекст для моделей с поддержкой инструментов. Ключевым файлом оказался llama_chat_format.h.
// Пример проблемного кода (упрощенно)
// В старых версиях для Gemma 4 могло быть так:
// if (model == "gemma") {
// // устаревший шаблон для Gemma 2/3
// return apply_chat_template(messages, tools, "gemma");
// }
Проблема была в том, что логика определения модели и применения соответствующего chat template не была обновлена для Gemma 4. Вместо нового токена <|tool_call|> использовался старый или общий формат. Это приводило к тому, что модель получала промпт в неправильном формате и возвращала нечто, что llama.cpp не мог корректно распарсить в JSON для инструмента. Результат — 500 Internal Server Error.
2 Временный фикс: правим запрос в обход бага
Пока pull request с исправлением летит в репозиторий llama.cpp, нужно работать. Решение — вручную указать корректный chat template и формат инструментов в клиентском коде. Это похоже на трюки, которые мы применяли для Qwen 3.5, но с другими токенами.
# Пример правильного формирования запроса для Gemma 4 в апреле 2026
# Используем актуальные токены из документации Google
def build_gemma4_tool_call_messages(system_prompt, user_query, tools):
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_query}
]
# Ключевой момент: инструменты должны быть переданы в отдельном поле "tools"
# и сериализованы в специфичном для Gemma 4 JSON-формате
return {
"messages": messages,
"tools": tools, # Список описаний функций в формате OpenAI
"tool_choice": "auto",
"chat_template": "gemma-4" # Явное указание шаблона
}
Если вы используете последнюю версию llama.cpp с апреля 2026 года, проверьте, добавлена ли поддержка chat_template "gemma-4". Если нет, возможно, стоит использовать значение "gemma" и надеяться на обратную совместимость, но лучше смотреть в исходники.
3 Проверяем и валидируем ответ модели
Даже с правильным промптом модель может выдать ответ в странном формате. Нужно добавить строгую валидацию ответа перед тем, как пытаться его исполнить как вызов функции. Это спасет от 500-х ошибок.
import json
def parse_gemma4_tool_response(raw_response):
try:
# Ищем JSON-блок в ответе модели
# Gemma 4 может обернуть его в <|tool_call|>{...}<|tool_call_end|>
start = raw_response.find("{")
end = raw_response.rfind("}") + 1
if start == -1 or end == 0:
raise ValueError("No JSON found in response")
json_str = raw_response[start:end]
tool_call = json.loads(json_str)
# Дополнительная валидация структуры
if "name" not in tool_call or "arguments" not in tool_call:
raise ValueError("Invalid tool call structure")
return tool_call
except json.JSONDecodeError as e:
# Логируем ошибку и возвращаем None или пробрасываем исключение
print(f"Failed to parse tool call: {e}")
print(f"Raw response: {raw_response}")
return None
Где еще может таиться проблема?
- Несовместимость версий квантования. Модель Gemma 4 в формате GGUF, квантованная по старой схеме (например, Q4_0), может вести себя иначе, чем новая (Q4_K_M). Всегда берите последние квантования от авторитетных источников.
- Системный промпт. Gemma 4 стала более чувствительной к инструкциям в system prompt. Слишком длинный или сложный промпт может сломать генерацию. Проверьте, решается ли проблема с пустым системным промптом.
- Контекстное окно. Если диалог слишком длинный, модель может начать "глючить". Убедитесь, что не превышаете контекстное окно, указанное для вашей конкретной версии GGUF-файла.
Частые ошибки и как их избежать
| Ошибка | Причина | Быстрое решение |
|---|---|---|
| HTTP 400 Bad Request | Неверный JSON в запросе (часто поле "tools") или неизвестный chat_template. | Уберите поле "tools" из запроса, оставив только "messages". Если ошибка исчезла, проблема в сериализации инструментов. |
| HTTP 500 Internal Server Error | llama.cpp не смог распарсить ответ модели или произошла внутренняя ошибка при генерации. | Включите детальное логирование в llama.cpp (флаг --log-format json) и смотрите, что модель генерирует на самом деле. |
| Модель игнорирует инструменты | Неправильный chat template или потеря описаний инструментов в контексте. | Явно укажите "tool_choice": "required" в запросе и проверьте, что инструменты описаны в формате, совместимом с OpenAI. |
Что делать, если ничего не помогает?
Спуститесь на уровень ниже. Запустите llama.cpp с флагом --verbose-prompt и посмотрите, какой именно сырой промпт уходит в модель. Сравните его с тем, что ожидает Gemma 4 (документация Google часто отстает, но на апрель 2026 она актуальна). Затем смоделируйте этот промпт вручную через Python, используя библиотеку tokenizers от Hugging Face, чтобы убедиться, что проблема именно в формате, а не в чем-то другом.
Иногда проще откатиться на проверенную связку, например, Qwen 2.5 27B, у которой tool calling работает как часы. Или поэкспериментировать с FunctionGemma 270M для легких задач.
Мой главный совет: не бойтесь использовать ChatGPT или другую мощную облачную модель для отладки проблем с локальными моделями. Это не измена идеям локальности, а практичный способ сэкономить часы жизни. А дальше — глубоко в кроличью нору исходного кода, где и живут настоящие ответы.