Игорь Градов
Игорь Градов
7 мин
ai

LLM-конвейер молча теряет записи: три бага, которые не видны в сводке

Конвейеры на базе больших языковых моделей (LLM, от англ. Large Language Model) часто теряют записи так, что итоговые проверки показывают успех, а пользователь не получает результат: разработчик обнаружил это в собственном боте для отбора вакансий и описал три конкретных места, где данные пропадали незаметно.

LLM-конвейер молча теряет записи: три бага, которые не видны в сводке
Почему это важно

Потеря записей в LLM-конвейере маскируется успешным статусом: сводка считает только дошедшие ответы, и сумма всегда сходится. Без отдельной проверки входов и выходов бот, агент или любой автоматический обработчик молча выбрасывает часть работы.

Материал основан на разборе, опубликованном разработчиком после аудита собственного продакшен-бота. Бот собирал посты с вакансиями, проверял их по профилю кандидата и показывал подходящие. В какой-то момент вместо десятков вакансий в неделю стало приходить две. Все автоматические проверки при этом проходили без ошибок.

Что выяснилось при разборе?

В одном из прогонов бот обработал 543 поста. Из них 530 получили статус «не показывать». 196 отказов пришлись на один фильтр по роли. Первая реакция: ослабить фильтр. Но разработчик сначала проследил путь записи от загрузки до показа и нашёл три разных бага.

Все три бага объединяет одно: LLM-конвейер на каждом шаге может потерять запись, а стандартная сводка этого не покажет, потому что считает только то, что до неё дошло.

Баг первый: пачка считалась готовой, хотя часть ответов не вернулась

Посты уходят в модель пачками. Для каждого поста модель должна вернуть разметку. Код проверяет ответ и выбирает маршрут: показать вакансию, отклонить или отправить на ручную проверку.

Вот упрощённый фрагмент, где прятался баг:

missing = expected_ids - seen_ids
if missing:
    errors.append(f"нет результата для: {sorted(missing)}")
if errors:
    print(f"[chunk {idx}] validation errors: {errors}")
    ok = True  # даже если есть ошибки
    break

Код замечал недостающие записи, печатал их в лог и всё равно выставлял ok = True. Пачка считалась обработанной, повтор не запускался. Потерянные записи не попадали ни в показ, ни в отказы, ни в очередь на ручную проверку.

Сводка по маршрутам тоже не спасала. Она считала только дошедшие результаты, а общее число брала из той же выборки. Сумма всегда сходилась.

Баг второй: валидатор выбрасывал вакансию из-за вспомогательного поля

Модель иногда возвращала значения не из заданного списка. Например, поле с вариантами yes, no и unknown получало значение pass. В других случаях модель присылала строку там, где ожидался объект.

В промпте (текстовая инструкция для модели) было прямо написано:

remote_from_russia и relocation принимают ТОЛЬКО yes/no/unknown — не pass/fail

Модель всё равно возвращала pass. Повторное указание запрета в промпте не помогало.

При разборе выяснилось: часть проблемных полей нужна только для отчёта и сортировки. Решение о маршруте принимается отдельно. Но неверное значение во вспомогательном поле приводило к отбраковке всей вакансии, хотя остальные данные позволяли её обработать.

Ошибка воспроизводилась стабильно. В одном прогоне из 115 записей без результата остались 4. В другом, из 764, неверное значение встретилось в 11 записях: 5 спас повторный запрос, 6 остались без результата.

Баг третий: подходящие вакансии оставались за пределами показа

Помимо потери записей и ложной отбраковки, разработчик обнаружил, что очередь вывода сама по себе ограничивала число показанных вакансий. Подходящие посты проходили все фильтры, но не доходили до пользователя, потому что оставались за пределами окна показа.

Что понадобится

  • Доступ к логам вашего LLM-конвейера (любой бот, агент или пакетный обработчик на базе языковой модели)
  • Список идентификаторов входных записей до обработки
  • Возможность сравнить входы и выходы поштучно, а не только по количеству
  • 30 минут на первый аудит, дальше проверки автоматизируются

Пошаговая инструкция: как найти скрытые потери в LLM-конвейере

  1. Зафиксируйте число входов ДО обработки. Сохраните список идентификаторов записей до отправки в модель. Никогда не берите «общее число» из итоговой сводки: она считает только дошедшие ответы.

  2. Введите контракт прогона. Проверяйте равенство после каждого прогона:

входы = обработанные + отклонённые + технические ошибки + оговорённые исключения

Каждая запись должна учитываться только в одной категории. Исключения задаются заранее, с названием и допустимым количеством. Если числа не сходятся или указана неизвестная причина исключения, прогон завершается с ошибкой.

  1. Сравнивайте не только количества, но и идентификаторы. Дубль может компенсировать пропуск: 100 входов и 100 выходов не гарантируют, что это одни и те же записи.

  2. Ограничьте допустимое число технических ошибок. Даже если все записи учтены, но модель на каждой упала по таймауту (ограничению времени ответа), задача не выполнена. Для критичных типов ошибок установите порог в ноль.

  3. Разделите обязательные и вспомогательные поля в валидаторе. Неверное значение во вспомогательном поле (нужном только для отчёта) не должно выбрасывать всю запись. Замените отбраковку на значение по умолчанию. Неверное значение в yes/no/unknown становится unknown, а не no: отсутствие уверенности не равно отказу.

  4. Не додумывайте за модель. Значение pass не переводится автоматически в yes. Сохраняйте исходный ответ модели рядом с заменой, чтобы видеть частоту ошибок.

  5. Проверьте очередь вывода. Убедитесь, что записи, прошедшие все фильтры, действительно доходят до пользователя, а не остаются за пределами окна показа.

Как это выглядит на практике

Разработчик расширял профиль отбора: поменял правила в коде, затем промпт. Потребовалось заново разметить 188 записей. Разметку получили 168. Ещё 20 не дошли до маршрута. Новая проверка остановила прогон с сообщением:

FAIL coverage: прогон не выполнил агрегатный контракт
reason 'missing_record' on 20 case(s), contract allows at most 0

Раньше у этих двадцати записей уже были маршруты. После переразметки результатов не стало. Без контракта прогона это выглядело бы как успешное обновление. После исправления промпта повторная обработка дала результат для всех 20.

Причём диагностика уточнила картину: модель не вернула только 2 записи. Остальные 18 она вернула, но их отклонил валидатор из-за вспомогательных полей.

Частые ошибки
  • Сводка считает сама себя. Если общее число записей берётся из итоговой выборки, пропуски невидимы. Берите число входов до обработки.
  • Лог пишет ошибку, а статус остаётся «успех». Классический баг: ok = True после вывода ошибки в консоль. Проверьте, что ошибка действительно влияет на статус прогона.
  • Любое неверное значение приводит к полной отбраковке. Отделите поля, от которых зависит маршрут, от полей для отчёта. Иначе галлюцинация (когда модель уверенно выдумывает ответ, которого не было в инструкции) в одном вспомогательном поле убивает корректную запись.
  • Повтор запрета в промпте вместо обработки в коде. Модель может игнорировать ограничения формата, сколько бы раз вы их ни написали. Обрабатывайте невалидные значения на стороне кода.

Что делать с этим прямо сейчас?

Автору Дзена или копирайтеру, если вы используете LLM для пакетной обработки текстов (рерайт, классификация, генерация описаний), проверьте: совпадает ли число входных файлов с числом готовых результатов? Сравнивайте поштучно, а не итогами.

Маркетологу, если бот или агент на базе языковой модели обрабатывает обращения, лиды или анкеты, внедрите контракт прогона. Без него вы не узнаете, что часть заявок молча выпала из обработки.

Предпринимателю в РФ и СНГ, описанные баги не зависят от конкретной модели. Они воспроизводятся с YandexGPT, GigaChat и любой другой языковой моделью, которую вы подключаете к своему LLM-конвейеру. Чек-лист контрактов из этой инструкции работает для любого провайдера.

Мнение редакции dzen.guru

Этот разбор ценен не технической сложностью, а тем, как буднично выглядит потеря данных в LLM-конвейере. Код логирует ошибку, ставит «ок», идёт дальше. Сводка зелёная. Пользователь молчит, потому что не знает, что должен был получить больше. По моему опыту, большинство ботов и агентов на базе языковых моделей сегодня работают без контракта прогона. Это значит, что потери есть, но их никто не измеряет. Честная оговорка: описанный подход требует, чтобы у каждой записи был уникальный идентификатор и вы контролировали код обработки. Если вы используете готовый no-code-сервис без доступа к логам, начните хотя бы с ручного сравнения: сколько записей отправили и сколько получили на выходе.

Генератор промптов dzen.guru

Попробуйте наш генератор промптов, чтобы точнее формулировать инструкции для языковых моделей и снизить число ошибок формата в ответах

Попробовать генератор

Контракт прогона из семи шагов занимает полчаса на внедрение, а ловит баги, которые месяцами прячутся за зелёным статусом. Начните с первого пункта: зафиксируйте число входов до обработки и сравните с выходами поштучно, а не по итоговой сумме.

Поделиться:TelegramVK
Игорь Градов
Игорь Градов

Основатель dzen.guru. Эксперт по монетизации и продвижению на Дзен. Автор курса «Старт на Дзен 2026».

Комментарии

Читайте также

Claude Code через Telegram: разрешения ИИ-агенту теперь даются кнопкой с телефона
ai

Claude Code через Telegram: разрешения ИИ-агенту теперь даются кнопкой с телефона

Разработчик собрал agent-hub, приложение для управления Claude Code, Codex и Qwen через темы Telegram-группы, чтобы ИИ-агент не простаивал вдали от хозяина:…

5 мин
ИИ-агент Claude на VPS банит сам себя в 7 прогонах из 60, отрезая доступ владельцу
ai

ИИ-агент Claude на VPS банит сам себя в 7 прогонах из 60, отрезая доступ владельцу

Автор открытого навыка vps-forge провёл 120 прогонов Claude Opus 5.5 на виртуальных серверах и показал, что без подсказок агент банит сам себя в 7 прогонах из…

4 мин
Разработчик показал, как локально запустить нейросеть на MacBook с 24 ГБ без облака
ai

Разработчик показал, как локально запустить нейросеть на MacBook с 24 ГБ без облака

Разработчик за полгода собрал голосового помощника для macOS, который сам открывает папки, ищет файлы и читает почту, и дообучил для него большую модель на…

6 мин