Четыре бага за один вечер
Утром запустил боту два новых инструмента - сбор заявок и отправку CV. Всё работало. К обеду начал тестировать по-настоящему и поймал четыре бага которые складывались друг в друга как матрёшка.

Баг 1: потерянные сессии
Посетитель просит CV. Бот спрашивает email. Посетитель пишет адрес. Бот отвечает как будто видит его впервые.
Причина - два воркера uvicorn. Каждый хранит сессии в своей памяти. Первый запрос попадает на воркер 1, второй на воркер 2. Воркер 2 не знает ни про какую сессию и создаёт новую.
Для CPU-bound нагрузки несколько воркеров имеют смысл. Чатбот - чистый I/O: ждёт ответа от Claude API, ждёт SMTP. Один async-воркер спокойно держит двадцать одновременных подключений. В моём случае два воркера не удваивали производительность - они удваивали количество багов.
Фикс - одна цифра в Dockerfile. --workers 2 стало --workers 1.
Баг 2: email меняет язык
Посетитель пишет по-русски. Бот отвечает по-русски. Посетитель вводит ilikosha@icloud.com. Бот отвечает на английском.
Детектор языка считает латинские и кириллические символы в сообщении. Email-адрес - сплошная латиница. Ноль кириллицы. Детектор решает что это английский и тегирует сообщение [Reply in English].
Фикс - перед подсчётом вырезать из текста email-адреса, URL и @хэндлы. Это не естественный язык, их не надо считать.
Баг 3: галлюцинация email
Посетитель: «пришли резюме». Бот, вместо того чтобы спросить email, берёт info@imarch.dev из системного промпта и вызывает инструмент. Письмо уходит мне же на почту.
Haiku 3 - маленькая модель. Если tool definition говорит что поле visitor_email обязательное, а email в диалоге нет, модель не остановится. Она возьмёт первый подходящий адрес из контекста. А в системном промпте их три.
Три уровня защиты:
Одного уровня мало. Haiku находит способ обойти каждый по отдельности. Три вместе работают. Это принцип эшелонированной защиты - тот же подход, что и в безопасности сетей: ни один барьер не идеален, но вместе они закрывают слабые места друг друга.
Баг 4: письма в спаме
Заявки от бота приходят нормально. CV-письма падают в спам. На нескольких ящиках.
Разница - заявки идут на мой же домен (Cloudflare Email Routing), а CV уходят на внешние ящики (iCloud, Gmail). Внешние серверы строже.
Само письмо выглядело как спам. noreply@ в отправителе. HTML-шаблон с большой кнопкой. Минимум текста. Безликая тема «Ильяс Мустафин - CV». Ни имени получателя, ни возможности ответить.
Фикс - сделать письмо похожим на обычное письмо от человека:
Заодно добавил Resend (Amazon SES) в SPF-запись домена - авторизовал сервер-отправитель. Без SPF и DKIM даже идеально написанное письмо с нового домена будет попадать в спам - почтовые серверы доверяют не содержимому, а подписям.
Итого
Четыре бага. Ни один не был сложным сам по себе. Но они накладывались: потерянная сессия ломала предохранитель, сломанный предохранитель пропускал галлюцинацию, галлюцинированное письмо летело в спам. Классический каскадный отказ - когда каждый следующий баг маскирует предыдущий и найти корень проблемы в одном тестовом прогоне невозможно.
Самый полезный фикс из четырёх - admin-эндпоинт для очистки кэша. Раньше нужен был SSH на сервер. Теперь один curl с токеном. Мелочь, но когда меняешь промпт пять раз за вечер - экономит нервы.
Главный урок вечера: AI-чатбот - это не “подключил API и забыл”. Это система с состоянием, языковой логикой, моделью которая галлюцинирует, и внешними сервисами которые фильтруют результат. Каждый слой может сломаться, и ломается он тихо - ты узнаёшь только когда пользователь говорит “бот странно отвечает”.
Бот работает. Попробуйте попросить CV или оставить заявку - кнопка чата в правом нижнем углу. Или напишите напрямую если что-то ещё ломается.
Читайте также


