context_length_exceeded и превышение длины контекста: что делать
Ошибка означает, что суммарная длина запроса — системный промпт, история диалога и место, зарезервированное под ответ, — больше, чем модель способна обработать за один вызов. Границу ставит сама модель у вендора: OpenAI API возвращает 400 с кодом context_length_exceeded, Anthropic — 400 invalid_request_error с текстом про превышение токенов. У go2llm отдельного кода на этот случай в таблице ошибок нет: шлюз не хранит собственный лимит длины контекста поверх лимита модели, вызов просто доходит до вендора и получает отказ оттуда.
Страница составлена 2026-09-25, обновлена 2026-09-25.
Как выглядит ошибка
OpenAI Chat Completions на превышении отвечает так:
{
"error": {
"message": "This model's maximum context length is ... tokens. However, your messages resulted in ... tokens. Please reduce the length of the messages.",
"type": "invalid_request_error",
"param": "messages",
"code": "context_length_exceeded"
}
}
Anthropic Messages API формулирует то же по-другому, без отдельного кода:
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "prompt is too long: ... tokens > ... maximum"
}
}
Оба сообщения называют два числа: предел модели и то, сколько токенов получилось у вас. Конкретные цифры зависят от модели и здесь не приводятся намеренно — точный потолок контекста у каждой модели свой, задаёт его вендор, и в каталоге go2llm он не публикуется.
На go2llm живым запросом эта конкретная форма ошибки не зафиксирована: воспроизвести её означало бы либо собрать запрос на сотни тысяч токенов, либо подобрать модель с маленьким окном, а такой прогон в проверенную фактуру не входит. Собственного кода на этот случай в таблице ошибок go2llm нет: invalid_request, unsupported_content_type, model_protocol_mismatch и другие коды под 400 покрывают то, что проверяет сам шлюз до отправки запроса вендору, а превышение контекста возникает уже после этих проверок и приходит с обобщённым, шлюзовым error.message — слова вендора, если они вообще доезжают, лежат отдельно, в error.upstream_message, а не в error.message. Практический вывод: смотрите HTTP-статус (400) и, если оно есть, error.upstream_message на слова вроде «context», «length» или «token», а не ветвитесь по error.code == "context_length_exceeded" — такого кода на этом пути не будет.
Причины
1. Диалог накопил длинную историю
Самая частая причина. Приложение хранит весь чат и при каждом новом сообщении отправляет его целиком. Каждое отдельное сообщение выглядит разумным, а сумма через полсотни реплик уже упирается в потолок модели. Агентские фреймворки раздувают историю ещё быстрее: инструментальные вызовы и их результаты добавляются в историю на каждом шаге, и разговор в десять реплик пользователя может превратиться в контекст на порядок больше.
2. В один запрос целиком вставлен большой документ
Извлечённый текст PDF, лог или дамп базы вставлены в сообщение без обрезки. Для файлов это тот же по духу случай, что и вложения не текстом — разбор рядом: 400 unsupported_content_type про бинарные вложения, здесь же про текст, который сам по себе слишком длинный.
3. max_tokens запрошен на пределе оставшегося места
У части моделей max_tokens — это не потолок сверху, а часть общего бюджета контекста: вход плюс запрошенный максимум ответа должны уместиться в одно окно. Промпт, который сам по себе укладывается с запасом, может провалиться из-за того, что max_tokens выставлен неоправданно большим значением по умолчанию.
4. Инструмент или библиотека сам раздувает промпт
RAG-пайплайны и агентские SDK любят подкладывать в системный промпт найденные документы, схему инструментов и служебные инструкции. Разработчик пишет короткий пользовательский вопрос, а фактический запрос к модели оказывается в разы длиннее.
5. Выбрана модель с маленьким окном для задачи, где нужно большое
Не все модели в одном семействе имеют одинаковый потолок контекста — это решает вендор для конкретной версии модели. Задача, рассчитанная на большое окно, падает при переключении на более лёгкую или более старую версию той же линейки.
Как починить по шагам
Шаг 1. Посчитайте входные токены заранее, а не по факту ошибки. У go2llm для этого есть отдельный бесплатный endpoint — резерв и списание он не делает:
curl https://go2llm.tech/v1/messages/count_tokens \
-H "x-api-key: $GO2LLM_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{"model":"claude-sonnet-5","messages":[{"role":"user","content":"..."}]}'
Ответ: {"input_tokens": N, "go2llm": {"exact": false, "method": "byte_estimate", "typical_error_pct": X}}. Важная оговорка: это оценка по байтам тем же оценщиком, которым шлюз считает резерв, а не вендорский токенайзер — «точный подсчёт токенов» это не даёт, но порядок величины и факт приближения к потолку показывает надёжно.
Шаг 2. Если оценка близка к пределу — обрежьте историю на своей стороне до отправки:
def trim_history(messages, keep_last=20, system=None):
"""Оставляет последние N сообщений; system-промпт не трогает."""
tail = messages[-keep_last:]
if system:
return [{"role": "system", "content": system}, *tail]
return tail
Для длинных диалогов лучше не просто обрезать, а суммаризировать старые сообщения отдельным дешёвым вызовом — это одновременно чинит ошибку и снижает счёт, потому что входные токены оплачиваются независимо от результата.
Шаг 3. Если раздувает не история, а один документ — извлеките из него только нужную часть до отправки, тем же способом, что и при разборе PDF в 400 unsupported_content_type: текст, а не файл целиком, и с ограничением по символам.
Шаг 4. Проверьте max_tokens. Уменьшение до реально нужного значения освобождает место под вход в тех моделях, где лимит общий:
import os
from anthropic import Anthropic
client = Anthropic(base_url="https://go2llm.tech", api_key=os.environ["GO2LLM_API_KEY"])
msg = client.messages.create(
model="claude-sonnet-5",
max_tokens=512, # не 64000 "на всякий случай"
messages=[{"role": "user", "content": "Сделай выжимку в трёх пунктах"}],
)
Чем это отличается от лимита тела 413 у go2llm
У go2llm есть собственный лимит — 20 MiB на тело запроса, статус 413, код request_too_large. Он считается в байтах и не связан с потолком контекста модели: для чистого текста без вложений 20 MiB это очень много токенов, поэтому на практике текстовый запрос почти всегда упрётся в границу модели по токенам заметно раньше, чем в границу шлюза по байтам. Если вы видите 413 — проблема в размере тела; если вендор отклонил запрос с упоминанием токенов и предела модели — проблема в длине контекста, и увеличивать здесь нечего, лимит не настраивается на стороне go2llm, потому что задаёт его не шлюз. Подробный разбор 413 — 413 request_too_large.
Как избежать в будущем
Считайте токены на входе в конвейер, а не полагайтесь на ошибку как на единственный сигнал: вызов count_tokens бесплатный и не тратит лимит 20 RPM на ключ смыслово иначе, чем обычный запрос, но не резервирует и не списывает деньги. Держите жёсткий предел на длину истории диалога и суммаризируйте старые сообщения вместо их накопления. Для задач с заведомо большим контекстом проверяйте документацию конкретной модели у вендора перед выбором — потолок у моделей одного каталога отличается, и go2llm его не расширяет и не публикует отдельно от вендора.
Связанные ошибки
- 413 request_too_large — лимит тела в байтах, отдельная граница от контекста модели.
- 400 unsupported_content_type — файлы и вложения в текстовом запросе.
- 402 insufficient_balance — большой запрос упирается в баланс, а не в контекст.
- Справочник всех кодов go2llm и docs/errors.
FAQ
Может ли go2llm сам обрезать слишком длинный запрос? Нет. Шлюз передаёт запрос модели как есть и не редактирует его содержимое. Обрезка истории или документа — задача на стороне клиента.
Есть ли у go2llm единый лимит контекста для всех моделей? Нет, и такого кода в таблице ошибок шлюза нет вовсе. Потолок задаёт вендор для конкретной модели, и он не одинаков даже внутри одного семейства.
count_tokens посчитает токены точно так же, как посчитает их модель? Нет. Это оценка по байтам, "exact": false, "method": "byte_estimate" — тем же способом, которым шлюз считает резерв под баланс. Вендорский токенайзер go2llm не запускает. Для проверки «не упрёмся ли мы в потолок» приближения достаточно, для миллиметровой точности — нет.
Списываются ли деньги за запрос, отклонённый из-за длины контекста? Если отказ пришёл до инференса — с большой вероятностью нет, но это поведение конкретного вендора, а не отдельно объявленное правило go2llm. POST /v1/messages/count_tokens в любом случае бесплатен и не резервирует ничего — это единственная проверка, за которую точно не платите.
Получить ключ и сделать первый запрос за 3 минуты → go2llm.tech. Подключение и список endpoint — в документации, цены моделей — на странице тарифов.