AnythingLLM Max Tokens: почему обрывается длинный ответ
Обрыв ответа на полуслове не обязательно связан с сетью. Проверьте настройки Generic OpenAI, причину завершения на стороне API и результат сравнения, в котором меняется только один параметр.
Содержание

Если длинный ответ AnythingLLM обрывается на полуслове, сначала проверьте, не ограничил ли его параметр Max Tokens. Увеличение окна контекста и тайм-аута прокси решает другие задачи. Для проверки нужны фактически отправленный лимит вывода, поле finish_reason от API и повторный запрос с изменением только одного условия; исходные настройки стоит сохранить.
Выбирайте действие по признакам завершения
То, что текст перестал появляться, — симптом, а не диагноз. По спецификации OpenAI Chat Completions, length означает достижение заданного в запросе лимита токенов, а stop — естественное завершение или встречу с заданной стоп-последовательностью. Значение tool_calls означает переход к вызову инструмента, а не готовый итоговый ответ. Для совместимого API нужно учитывать и поведение конкретного сервера.
| Что удалось увидеть | Что проверить первым | Следующее действие |
|---|---|---|
API вернул finish_reason=length, фактический лимит вывода известен | Ограничение генерации | Не менять входные данные и проверить другой допустимый лимит вывода |
| Сервер явно сообщает о слишком большом контексте; та же задача работает с меньшей историей или выборкой документов | Переполнение контекста | Сократить входные данные и проверить реальное окно модели, а не просто увеличить вывод |
| Есть тайм-аут, сброс соединения, отмена или поток закончился без видимой причины завершения | Соединение, отмена запроса или совместимость потока | Сопоставить время событий и найти участок обрыва; отсутствие поля само по себе не доказывает сетевой сбой |
Пришёл stop, но отсутствует раздел или пункт | Требования в запросе, стоп-последовательности, полнота текста | Проверить пропуски: нормальное завершение не равно выполненной задаче |
Лимит вывода ограничивает объём генерации в одном запросе. Окно контекста определяет объём контекста, с которым работает модель. В запрос могут входить история, системные инструкции и найденные фрагменты документов: адаптер Generic OpenAI собирает эти составляющие. Поэтому короткий вопрос не обязательно означает короткий запрос, а большое число в настройках окна не расширяет возможности серверной модели.
В issue #6703 один пользователь рассказал, что из-за ограничения вывода сначала искал причину в тайм-аутах обратного прокси и WebSocket. Это конкретный пользовательский опыт, объясняющий порядок проверок, но не доказательство той же причины в вашей установке.
Сохраните настройки и проверьте, какая модель вызывается
Сначала запишите исходное состояние. Нужны версия AnythingLLM, Desktop или самостоятельное размещение, глобальный провайдер, провайдер и модель в рабочем пространстве и чате, использование агента, окно контекста и лимит вывода. На снимках настроек закройте ключи. Для эксперимента не удаляйте исходное рабочее пространство, переписку или проиндексированные документы.
Откройте настройки LLM-провайдера и убедитесь, что тест действительно идёт через Generic OpenAI. Официальная инструкция требует совместимости и функций API, и формата ответа модели. Если запрос направляется через другого провайдера, изменение Generic OpenAI не настроит этот другой путь.
В официальном коде интерфейса, проверенном 2026-10-16, есть следующие поля:
| Поле | Что проверить |
|---|---|
Base URL, API Key, Selected Model | Ранее работавший адрес, соответствующий ключ и точный идентификатор модели; не угадывайте его по отображаемому названию |
Model context window | Указанную ёмкость контекста, а не бюджет ответа |
Max Tokens | Лимит генерации — отдельно от окна контекста |
Сохраните изменения, снова откройте настройки и убедитесь, что значения остались. При наличии разрешённого доступа к журналу исходящих запросов проверьте фактический model и поле ограничения вывода. Снимок интерфейса показывает только отображённые значения, но не то, что получил сервер. Обычный чат и агента проверяйте отдельно: в списке изменений Desktop v1.16.2 есть исправление игнорирования max tokens при потоковых вызовах агентов через Generic OpenAI.
Если вы уже используете BetterToken, на этом шаге сверьте адрес, ключ и модель с инструкцией Chat Completions; это справка по настройке подключения, а не гарантия работы всех функций AnythingLLM или снятия ограничений вывода.
Почему 0 не снимает лимиты и какой параметр отправляется
0 не означает бесконечный ответ. В списке изменений Desktop v1.17.0 указано: для Generic OpenAI значение 0 позволяет не отправлять max_tokens в запросе. Тогда действуют значения по умолчанию и ограничения сервера. Для диагностики удобнее явный положительный бюджет, который поддерживает выбранная модель.
В коде адаптера, проверенном 2026-10-16, значение вывода берётся из GENERIC_OPEN_AI_MAX_TOKENS, а окно — из GENERIC_OPEN_AI_MODEL_TOKEN_LIMIT. По умолчанию поле вывода называется max_tokens; переменная GENERIC_OPEN_AI_MODEL_MAX_TOKEN_KEY позволяет указать другое имя. Это описание прочитанного кода, а не обещание одинаковых настроек во всех установленных версиях.
Не переименовывайте параметры во всех совместимых API наугад. В актуальном справочнике OpenAI указан max_completion_tokens: в этот бюджет входят видимый текст и токены рассуждения. Поэтому небольшой объём видимого ответа ещё не доказывает, что лимит не исчерпан. Проверяйте поддерживаемое имя у своего сервера и модели и меняйте настройку только там, где её поддерживает ваша версия.
В Desktop используйте интерфейс, а не правку .env по случайному примеру: официальное руководство ограничивает такие инструкции самостоятельным размещением и Docker. В v1.17.0 изменился и способ сохранения настроек рабочего пространства. Проверяйте сохранённое значение повторным открытием, а не наличием одинаковой кнопки во всех версиях.
Где искать finish_reason и фактический расход
Нужен прежде всего ответ модельного API на запрос AnythingLLM. В непотоковом Chat Completions проверьте choices[0].finish_reason и возвращённый usage. В потоке найдите фрагмент с причиной завершения и проверьте, получена ли полная последовательность окончания, которую использует сервер. Пустое значение в первом фрагменте не является итоговым статусом. Определения полей доступны в официальном справочнике.
В веб-интерфейсе откройте Network в инструментах разработчика браузера, включите сохранение журнала и воспроизведите проблему один раз. Запишите время, статус и ответ соответствующего запроса чата. Но это участок браузер — AnythingLLM, а не обязательно исходный ответ модельного API.
В обработчике Generic OpenAI, проверенном 2026-10-16, непустая причина завершения от сервера превращается в собственное сообщение AnythingLLM с close: true, без передачи причины в исходном виде. Если сервер не сообщил расход, код также может оценивать его по количеству фрагментов текста. Поэтому завершение в интерфейсе и показанное число токенов не доказывают ни полноту ответа, ни точное достижение лимита.
Получите исходные причину и расход из доступных вам журналов модельного сервиса или сведений о запросе. Если доступа нет, передайте администратору время и идентификатор запроса. В Desktop v1.16.2 описан переход к журналам и хранилищу через метку версии вверху окна; наличие журнала ещё не означает, что конкретная версия записывает исходное поле завершения.
Для потока итоговый расход может не прийти: OpenAI предупреждает, что при обрыве или отмене последний фрагмент usage может отсутствовать. Записывайте «не получено», а не ноль, и не подменяйте данные биллинга оценкой приложения.
Сравните короткий и длинный ответы на прежних настройках
Сначала зафиксируйте путь запроса, затем меняйте требуемую длину ответа. Используйте тестовое рабочее пространство без подключённых документов базы знаний. Выберите одного провайдера и одну модель, сохраните одинаковые системные инструкции, температуру, бюджет вывода и остальные настройки. Для каждого теста открывайте пустой чат, чтобы второй запрос не унаследовал первый. Начните с обычного чата, без агента и инструментов.
Короткий тест A:
Перечислите 3 рекомендации по ведению проектной документации. На каждую отведите 2–3 предложения. После завершения напишите
[END]на отдельной строке.
Длинный тест B:
Перечислите 20 рекомендаций по ведению проектной документации. На каждую отведите 2–3 предложения. После завершения напишите
[END]на отдельной строке.
Отправьте запрос и дождитесь завершения генерации: не нажимайте остановку и не обновляйте страницу. При тайм-ауте или обрыве запишите время, а не запускайте серию повторов. Скопируйте весь показанный текст в локальные A.txt и B.txt. Отдельно сохраните время запроса, модель, фактический бюджет, причину завершения, происхождение usage, наличие последнего пункта и [END]. Числа здесь задают тест, а не лимиты модели или гарантированную длину ответа.
Если A завершился, а B нет, вы установили лишь зависимость проблемы от сценария с длинным выводом, но ещё не доказали причину в Max Tokens. Когда B вернул length, оставьте его запрос и все остальные настройки неизменными, задайте больший явный бюджет в допустимых сервером пределах и выполните тест C в пустом чате. Сохраните результат в C.txt. Полные 20 пунктов, завершающая метка и отсутствие length подтверждают, что изменение бюджета помогло в этом случае.
Если B уже выполняется полностью, не увеличивайте лимит без необходимости. Сравните одну и ту же реальную задачу в исходной переписке и пустом чате с одинаковым бюджетом. Исследуйте контекст, когда сбой появляется вместе с историей или найденными фрагментами. Верните временные настройки, которые больше не нужны, и сохраните исходные записи. Вызовы платного API при тестировании могут оплачиваться на обычных условиях.
Исправляйте подтверждённую причину, а не все настройки сразу
Ограничение вывода: увеличьте бюджет и проверьте исходящий запрос
Меняйте только бюджет на реально используемом пути вызова и не выходите за ограничения модели. Если в интерфейсе число выросло, а запрос остался прежним, проверьте выбор провайдера, сохранение, версию и различия обычного чата и агента. Увеличение окна не исправит настройку вывода, которая не дошла до сервера.
Большой контекст: сократите вход, оставив задачу и бюджет прежними
Сначала повторите ту же длинную задачу в пустом чате. Затем отдельно уменьшите объём найденных фрагментов или ненужной истории. При явной ошибке контекста укажите реальную ёмкость модели: большое число в поле не расширяет её. Если пустой чат помогает, но явной ошибки нет, контекст пока остаётся гипотезой, которую нужно проверить.
Сбой соединения или потока: найдите участок обрыва
Сопоставьте записи браузера, AnythingLLM и модельного API за один период. Ищите отмену, тайм-аут или закрытие соединения. Если API завершил ответ, а браузер получил только часть, проверьте промежуточную передачу и отображение. Если сбой произошёл уже у API, начните с его ошибки. Меняйте тайм-аут только того участка, на который указывают журналы.
Администратор самостоятельной установки или Docker может отдельно сравнить потоковый и непотоковый режимы. В официальной конфигурации есть GENERIC_OPENAI_STREAMING_DISABLED="true", действующий только на Generic OpenAI; после изменения нужен перезапуск сервиса или контейнера. Повторите тот же длинный запрос при остальных прежних настройках, затем верните исходное значение и перезапустите сервис. Успех без потока сужает поиск до связанных с потоком участков, но не доказывает вину прокси. В Desktop эту правку .env не выполняйте.
Как понять, что исправление помогло
Более длинный ответ сам по себе ещё не успех. Проверьте все разделы или номера, завершённость последнего предложения и метку окончания; по исходной причине завершения и журналам соединения убедитесь, что обрыв не повторился. Ни stop, ни [END] не заменяют проверку текста. После упрощённого теста повторите реальную задачу, которая раньше не выполнялась.
Если проблема осталась, передайте администратору версию и способ развёртывания, обычный чат или агент, идентификаторы провайдера и модели, настройки без секретов, минимальный запрос, различия A/B/C, время и полученную причину завершения. Удалите API-ключи, заголовки авторизации и содержимое частных документов; для отсутствующих полей укажите «не получено». Такой отчёт позволяет исследовать конкретный запрос, а не снова начинать с предположений о тайм-ауте прокси.