Продакшн-агент принимает сообщения, генерация закрывается со статусом stop, в amoCRM тишина. Нода отправки падает с non-empty string, клиент не получает ответа, заказчик злится. Именно так выглядит Reasoning Lock — баг, который не бросается в глаза, потому что всё «успешно».
Ниже — механика бага, пошаговая диагностика по слоям и конкретные правки, которые убирают пустые ответы.
Что происходит внутри модели
Reasoning-модели (DeepSeek V4 Pro, Qwen3, Kimi K2, GPT-5-nano и другие) перед финальным ответом генерируют внутренние «мысли» — reasoning_content. Это отдельный поток токенов, невидимый в обычном выводе.
Проблема возникает, когда лимит max_tokens (или его аналог max_completion_tokens) выставлен слишком низко — или не выставлен вообще, и шлюз применяет внутренний дефолт. Модель тратит весь бюджет на reasoning, до генерации видимого content токены заканчиваются, и поле text возвращается пустым. finish_reason при этом честно пишет stop или length — в зависимости от провайдера. Коннектор передаёт пустоту дальше как есть.
В разобранном кейсе: 95 completion-токенов всего, из них 88 ушли в reasoning, на content осталось 7 — и модель вернула "".
Диагностика: смотрим по слоям
Баг прячется между тремя слоями. Проверять нужно именно в таком порядке — снизу вверх.
Слой 1 — нода отправки. Если в логах n8n видите Invalid or missing message text parameter или аналогичный non-empty string — это симптом, не причина. Нода честно валидирует то, что получила.
Слой 2 — выход агента. Открываете OUTPUT ноды агента в execution. Смотрите на три поля:
{
"text": "",
"finish_reason": "stop",
"completionTokens": 95,
"promptTokens": 6677
}Пустой text при stop и маленьком completionTokens — почти гарантированный Reasoning Lock. Запомните число completion-токенов.
Слой 3 — лог провайдера. Идёте в OpenRouter (или другой шлюз) и открываете карточку конкретной генерации. Там будет разбивка: сколько токенов ушло в reasoning, сколько в content. Если reasoning_tokens ≈ completionTokens, а content_tokens близко к нулю — диагноз подтверждён.
Три способа починить
1. Поднять max_tokens до разумного значения
Самое прямое решение. В вызове модели явно задайте лимит, достаточный для reasoning + ответа. Для агентов с промптом 6–7k токенов минимальный рабочий порог — 2048, комфортный — 4096–8192.
В n8n это параметр в ноде LangChain или HTTP-запросе к OpenRouter:
{
"model": "deepseek/deepseek-v4-pro-20260423",
"max_tokens": 4096,
"messages": [...]
}Если используете legacy-параметр max_tokens вместо max_completion_tokens — у некоторых шлюзов они считаются по-разному. Для reasoning-моделей предпочтительнее max_completion_tokens.
2. Ограничить reasoning effort
Большинство провайдеров поддерживают параметр reasoning_effort (или аналог). Значение low или minimal сокращает бюджет на внутренние размышления и оставляет больше токенов на видимый ответ.
{
"model": "deepseek/deepseek-v4-pro-20260423",
"max_tokens": 4096,
"reasoning_effort": "low",
"messages": [...]
}Для диалоговых агентов, где нужна скорость и короткие реплики, low обычно достаточно. Для аналитических задач — medium.
3. Fallback на reasoning_content
Если пустой content всё же прилетел — не роняйте воркфлоу, а попробуйте достать ответ из reasoning_content. Некоторые модели (Kimi K2, Qwen3) при исчерпании токенов кладут весь текст именно туда.
В n8n это можно сделать через Function-ноду перед отправкой:
const text = $input.item.json.text;
const reasoning = $input.item.json.reasoning_content;
if (text && text.trim() !== '') {
return { text };
}
if (reasoning && reasoning.trim() !== '') {
// Опционально: обрезать до первого абзаца
return { text: reasoning.split('\n\n')[0] };
}
// Если оба пустые — явная ошибка для алертинга
throw new Error('LLM returned empty content and empty reasoning_content');Почему промпт стал частью проблемы
В разобранном кейсе системный промпт разросся до 6677 токенов — инструкции по оплате, VIP-сценарии, уточнения формулировок после каждого спорного диалога. Плотность директив («КАТЕГОРИЧЕСКИ ЗАПРЕЩЕНО», «СТРОГО этот текст») выросла до максимума.
Большой и жёсткий промпт создаёт двойное давление: модель тратит больше токенов на reasoning, чтобы «разобраться» в противоречивых инструкциях, и одновременно пытается точно выполнить все запреты. При низком max_tokens это прямой путь к Reasoning Lock.
Практическое правило: если промпт превысил 4k токенов, max_tokens для completion должен быть не меньше 2048. При 6k+ промпте — минимум 4096.
Где ещё ломается
- Резервная модель с другим поведением. Если основная модель упала и включился fallback (например, с DeepSeek на Qwen3 Flash) — у резервной может быть другой дефолт по reasoning. Параметры нужно задавать явно для каждой модели в цепочке.
- Шлюз с внутренним лимитом. Некоторые OpenAI-совместимые шлюзы применяют свой
max_output_tokens, который перекрывает вашmax_tokens. Проверяйте документацию провайдера и смотрите в логах фактический лимит, а не тот, что вы передали. - Нет алертинга на пустой ответ. Без явной проверки
text !== ''перед отправкой воркфлоу молча падает, клиент не получает ничего, а вы узнаёте об этом от заказчика через два дня.
Что попробовать дальше
Добавить в воркфлоу явную проверку длины ответа перед нодой отправки и счётчик пустых генераций в отдельный лог или метрику. Семь дней без пустых ответов — нормальный KPI для такого агента, но без мониторинга вы не узнаете, когда счётчик снова пойдёт вверх.
Если промпт продолжает расти — рассмотрите разбивку на несколько специализированных агентов вместо одного с мегапромптом. Меньше контекста на вход = меньше reasoning на «разбор» инструкций = стабильнее вывод.
FAQ
Почему finish_reason возвращает «stop», если ответ пустой?
stop означает, что модель завершила генерацию штатно — не по обрыву, не по ошибке. Если весь бюджет ушёл в reasoning до генерации видимого текста, модель считает задачу выполненной и закрывает запрос с stop. Это поведение на стороне провайдера, не баг коннектора.
Какое значение max_tokens ставить для диалогового агента?
Зависит от длины промпта. Ориентир: промпт до 4k токенов — max_tokens: 2048; промпт 4–8k — max_tokens: 4096; если агент пишет длинные структурированные ответы — 8192. Начните с 4096 и смотрите на фактический completionTokens в логах.
Помогает ли смена модели на не-reasoning?
Да, если задача не требует сложных рассуждений. Классические instruction-tuned модели без встроенного reasoning не тратят токены на внутренние мысли. Но для агентов с многошаговой логикой reasoning-модели обычно дают лучший результат — лучше настроить параметры, чем менять модель.
Как поймать баг до того, как его заметит клиент?
Добавьте Function-ноду сразу после агента с проверкой if (!text || text.trim() === '') throw new Error('empty_llm_response'). Это переведёт молчание в явную ошибку execution, которую видно в логах n8n и можно алертить.
Влияет ли размер системного промпта на частоту бага?
Напрямую. Чем больше промпт и чем больше в нём противоречивых директив, тем больше токенов модель тратит на reasoning перед ответом. При фиксированном max_tokens это сокращает бюджет на видимый текст. Рост промпта с 3k до 6k токенов при том же лимите completion — типичный триггер Reasoning Lock.
