Авторская колонка

Тихие отказы в ИИ-пайплайнах: почему зелёный статус контейнера врёт и как мы срезали 90% расхода на LLM

Запасная ветка в блоке catch без отдельного счётчика и алерта маскирует полный отказ системы: новый агентный конвейер из трёх моделей не отработал в продакшне ни разу за несколько месяцев, пока CLI зависал на пустых OAuth-конфигах MCP-серверов. Постановка детерминированного предфильтра заголовков и триграммного дедупликатора перед вызовом LLM и генерацией медиа отсекла 76,5% информационного шума и сократила ежедневные расходы на API примерно на 90% (со 100+ до 22 вызовов за прогон). Использование DEFAULT 0 вместо DEFAULT NULL при миграции колонки рейтинга молча выкинуло из выдачи 678 качественных статей, а цикл сна перед первым запуском фоновой задачи обнулял обновление курсов валют при каждом деплое.

Тихие отказы в ИИ-пайплайнах: почему зелёный статус контейнера врёт и как мы срезали 90% расхода на LLM
Hamidun News.
Слушать статью
Паспорт материала и цифры
ПараметрДанные
Главный редактор
Экономия токенов на предфильтрациипадение доли холостых расходов с 88% до минимума, снижение вызовов LLM со 100+ до 22 за прогон (-90% бюджета)
Эффективность детерминированного скорингаотсев 76,5% нерелевантного входящего потока за 0 API-вызовов и освобождение 4,9 ГБ диска
Цена ошибки в миграции БД678 потерянных статей из-за `DEFAULT 0` вместо `DEFAULT NULL` и рост одобренной базы с 341 до 943 после бэкфилла
Скрытые ресурсы2 244 озвученных аудиофайла (6 ГБ, 69% покрытия базы), не попадавших в интерфейс из-за пропущенного поля в `SELECT`
  • Экономия токенов на предфильтрации: падение доли холостых расходов с 88% до минимума, снижение вызовов LLM со 100+ до 22 за прогон (-90% бюджета)
  • Эффективность детерминированного скоринга: отсев 76,5% нерелевантного входящего потока за 0 API-вызовов и освобождение 4,9 ГБ диска
  • Цена ошибки в миграции БД: 678 потерянных статей из-за `DEFAULT 0` вместо `DEFAULT NULL` и рост одобренной базы с 341 до 943 после бэкфилла
  • Скрытые ресурсы: 2 244 озвученных аудиофайла (6 ГБ, 69% покрытия базы), не попадавших в интерфейс из-за пропущенного поля в `SELECT`

Самый дорогой класс багов в автономных ИИ-системах — это не падение с красным трейсбеком, а тихий отказ (`silent failure`). Контейнер в `docker ps` светится статусом `Up`, крон завершается с кодом `0`, статьи выходят в ленту — а новый код, на который ты потратил неделю, в реальности не выполнился ни одного раза.

Именно это произошло в моём новостном ИИ-конвейере после переезда со связки `n8n + Airtable` на собственный серверный стек (40 часов миграции, `MIGRATION.md` на 600+ строк для сохранения контекста между сессиями). Я собрал цепочку из трёх агентов (`EditorAgent`, `WriterAgent`, `QAAgent`) и обернул её в аккуратный `try / catch` с переключением на старый `AIProcessor`.

Почему блок catch с запасной веткой маскирует пятиярусный отказ месяцами?

Поскольку запасная ветка исправно выпускала материалы, внешне система выглядела здоровой. Лишь спустя несколько месяцев я заметил в логах регулярную строку `Agent pipeline failed. Falling back to AIProcessor`. Когда мы начали снимать слои этого отказа (сессия потребовала 1 320 сообщений и 517 запусков субагентов), под капотом обнаружилась матрёшка из пяти причин:

1. Короткоживущий токен авторизации CLI протухал каждые несколько часов. 2. Настройка `timeout` принималась конструктором базового агента, но не пробрасывалась в дочерний вызов. 3. Тяжёлая модель на этапе кураторства не укладывалась в лимит времени на больших пачках (перевод на более быструю модель ускорил шаг в 3–4 раза). 4. Обновлённый бинарник CLI перестал принимать устаревший флаг `--max-tokens`. 5. Внутри контейнера CLI намертво зависал на строке `Fetched 4 servers`, пытаясь инициализировать облачные MCP-коннекторы (Notion, Figma, Linear, Sentry) с пустыми OAuth-конфигами.

Урок железный: любой fallback без отдельного счётчика и алерта — это не отказоустойчивость, а маскировка аварии. Точно так же статус `Up` у контейнера не ловит внутренний Python-скрипт в краш-лупе по `SyntaxError` на 98% CPU: настоящая проверка — стабильный `PID` старше 60 секунд и прогон `python3 -m py_compile`.

Как перестановка фаз пайплайна и триграммный дедуп экономят 90%

бюджета на модели?

Вторая ревизия вскрыла финансовую дыру: из 3 235 обработанных новостей в публикацию шло 340, но дорогие вызовы LLM, обложки и озвучка (TTS) выполнялись до финального отсева (88% бюджета сгорало впустую, а дублирующийся crontab дёргал сборщик 288 раз в сутки вместо 48). Мы поставили перед моделью бесплатный предфильтр на регулярках и весах источников (100+ паттернов на EN/RU/ZH, где для иероглифов без пробелов используется `includes()` вместо `\b`):

```javascript function scoreArticle(title, sourceName) { let score = 0; const t = (title || '').toLowerCase(); const s = (sourceName || '').toLowerCase(); if (SOURCE_TIERS.tier1.some(x => s.includes(x))) score += 4; else if (SOURCE_TIERS.tier2.some(x => s.includes(x))) score += 3; let kw = 0; for (const rx of AI_KEYWORDS) { if (rx.test(title)) kw++; } score += Math.min(kw, 4); if (AI_COMPANIES.some(c => t.includes(c))) score += 2; if (title && title.length > 20) score += 1; if (/\d/.test(title)) score += 1; return score; } ```

Этот фильтр отсекает 76,5% входящего шума за ноль токенов. Поиск дублей мы перевели с LLM на гибрид коэффициента Жаккара по триграммам и расстояния Левенштейна (снизив порог Жаккара для русской морфологии с 0,65 до 0,40 со сверкой сущностей). Расход на API упал на 90% (со 100+ до 22 вызовов за прогон), а чистка сиротских медиа освободила 4,9 ГБ (1 764 картинки и 1 398 аудио).

Почему DEFAULT 0 в миграции базы и sleep перед первым прогоном прячут данные?

Третий тихий отказ стоил месяца поисков: при добавлении колонки `ALTER TABLE ... ADD COLUMN hot_score INTEGER DEFAULT 0` все 678 старых статей получили оценку `0`, и SQL перестал отличать «ещё не считали» от «получили ноль». Переход на `DEFAULT NULL` и бэкфилл вернули в выдачу 581 статью, утроив одобренную базу с 341 до 943. Рядом нашлись забытый `LIMIT 5` в выборке оркестратора и пропущенное поле `audio_path` в `SELECT`, прятавшее 2 244 готовых аудиофайла на 6 ГБ (69% базы). Наконец, фоновая задача курсов валют начиналась с `await asyncio.sleep(86400)` до первого запуска и при ежедневных деплоях не сработала ни разу (вылечено `FIRST_RUN_DELAY_S = 90` и `last_success_at`), а проверка статики через `curl` по коду `200` на SPA-конфиге nginx получала `index.html` вместо пропавших PDF.

Эти инварианты наблюдаемости встроены в платформу [Nomad AI](https://nomadnet.ai) и облачный рантайм [agent.nomadnet.ai](https://agent.nomadnet.ai). Практикум по отладке автономных пайплайнов доступен в [Hamidun Academy](https://academy.hamidun.com) (72-часовой триал), а аудит-скрипты лежат в [Vibe Club](https://hamidun.com/club).

Вопросы и ответы по материалу

Почему блок catch с запасной веткой маскирует пятиярусный отказ месяцами?

Поскольку запасная ветка исправно выпускала материалы, внешне система выглядела здоровой. Лишь спустя несколько месяцев я заметил в логах регулярную строку `Agent pipeline failed. Falling back to AIProcessor`. Когда мы начали снимать слои этого отказа (сессия потребовала 1 320 сообщений и 517 запусков субагентов), под капотом обнаружилась матрёшка из пяти причин: 1. Короткоживущий токен авторизации CLI протухал каждые несколько часов. 2. Настройка `timeout` принималась конструктором базового агента, но не пробрасывалась в дочерний вызов. 3. Тяжёлая модель на этапе кураторст

Как перестановка фаз пайплайна и триграммный дедуп экономят 90% бюджета на модели?

Вторая ревизия вскрыла финансовую дыру: из 3 235 обработанных новостей в публикацию шло 340, но дорогие вызовы LLM, обложки и озвучка (TTS) выполнялись до финального отсева (88% бюджета сгорало впустую, а дублирующийся crontab дёргал сборщик 288 раз в сутки вместо 48). Мы поставили перед моделью бесплатный предфильтр на регулярках и весах источников (100+ паттернов на EN/RU/ZH, где для иероглифов без пробелов используется `includes()` вместо `\b`): ```javascript function scoreArticle(title, sourceName) { let score = 0; const t = (title || '').toLowerCase(); const s = (sourc

Почему DEFAULT 0 в миграции базы и sleep перед первым прогоном прячут данные?

Третий тихий отказ стоил месяца поисков: при добавлении колонки `ALTER TABLE ... ADD COLUMN hot_score INTEGER DEFAULT 0` все 678 старых статей получили оценку `0`, и SQL перестал отличать «ещё не считали» от «получили ноль». Переход на `DEFAULT NULL` и бэкфилл вернули в выдачу 581 статью, утроив одобренную базу с 341 до 943. Рядом нашлись забытый `LIMIT 5` в выборке оркестратора и пропущенное поле `audio_path` в `SELECT`, прятавшее 2 244 готовых аудиофайла на 6 ГБ (69% базы). Наконец, фоновая задача курсов валют начиналась с `await asyncio.sleep(86400)` до первого запуска и

ЖХ
Жемал Хамидун — CPO Alpina Digital, продакт-менеджер AlpinaGPT. Делает AI-продукты с 2019 года. Hamidun News — pet project, который вырос в production-агрегатор с 6000+ читателей.
✦ ИИ-агенты на практике · Академия & Nomad

От чтения про ИИ-агентов — к запуску своего агента за вечер

Пошаговая сборка автономных агентов и MCP-связок в Hamidun Academy (academy.hamidun.com) (72 часа бесплатно) или мгновенный запуск готовых мультишаговых агентов в облаке Nomad.

Попробовать Академию (72ч бесплатно) →Запустить Nomad Agent
Что вы думаете?
Загружаем комментарии…