Ассистент «Кодиума» уехал на сервер. Первым живым сообщением было /start, а следом вместо имени вопрос: «Сколько стоит курс Python?». Бот ответил: «Приятно познакомиться, Сколько стоит курс Python?!», и выйти из заявки было нечем. Тесты этого не ловили: в них люди отвечают на вопрос бота, а живые пишут то, что им нужно.
Чему научишься в этом уроке:
• выкладывать новый режим бота так, чтобы ничего не стереть на сервере;
• устраивать живую проверку в Telegram и превращать находки в точные задания агенту;
• отличать поломку кода от сбоя хостинга;
• мерить скорость модели с того сервера, где работает бот.
Перед стартом
- Ассистент из уроков 8.4–8.5 работает локально, тест промпта пройден на двух моделях, всё закоммичено в git.
- Бот уже живёт на сервере в Амстердаме службой
bot-kodium(урок 2.5), заявки уходят в CRM на сервере в Москве. - Ключ CloseRouter и имена моделей в
.envна сервере ты кладёшь сам. Агент.envне трогает, показывает только имена переменных.
Задание 54. Выкладка
Задание из прогона. Вместо <адрес сервера> и <файл SSH-ключа> подставь свои (имя файла SSH-ключа из урока 2.5, не ключ CloseRouter). В прогоне в базе знаний нашлись выдуманные агентом почта и @-ник школы, здесь они заменены пометками.
Прочитал оба отчёта: на «курс ведь 3 месяца?» обе модели во всех повторах поправляют — это главное; «скидка 50%» — в базе скидок нет, честное «не знаю» верно. Одно: в базе знаний придуманные контакты (<выдуманная почта>, <выдуманный @-ник>) — бот отправит людей на несуществующие адреса. Убери их из базы; для связи — «напишите /start и оставьте заявку» или передача администратору.
Теперь выкладка на сервер <адрес сервера> (ключ ~/.ssh/<файл SSH-ключа>, root, приложение /opt/bot-kodium, служба bot-kodium):
1. Перед выкладкой: копия папки приложения на сервере (без .env), покажи путь.
2. Выложить код (без .env, *.db, логов, .venv, reports/, scripts/, tests/ и test_*.py), зависимости, перезапуск, служба active, в журнале службы за минуту — нет ошибок (покажи последние 20 строк, тексты сообщений и ключи не печатать).
3. .env на сервере не трогать — модели и ключ я положил: покажи только cut -d= -f1 .env.
4. Проверь, что прежний сценарий /start → заявка не сломан: тест на сервере не нужен, достаточно, что команды зарегистрированы (журнал старта).
Работай только в этой папке и на этом сервере. Отвечай по-русски.
Агент собрался выкладывать командой rsync --delete. Это как попросить сделать шкаф на даче точной копией шкафа в квартире: помощник выбросит с дачи всё, чего нет в квартире. У бота на сервере такие вещи есть: файлы данных, которые он создаёт сам.
Задание 54b. Только scp
rsync не разрешён и не нужен. И rsync --delete на боевой папке опасен: удалит на сервере всё, чего нет у тебя локально (файлы данных, которые создаёт сам бот). Выкладывай как в прошлые разы — scp конкретных файлов кода (bot.py, assistant.py, sanitizer.py, conversation.py, rate_limiter.py, requirements.txt, knowledge/), без удаления чего-либо на сервере. Дальше — пункты 2–4 прошлого сообщения. Отвечай по-русски.
Выложено, в отчёте «база знаний без несуществующих контактов», а поиск на сервере нашёл почту в строке про возврат. Отчёт проверяй поиском.
Задание 54c. Найти поиском
Ты написал «база знаний без несуществующих контактов», а в knowledge/kodium.md:120 осталось «написать заявление на email: <выдуманная почта>». Замени на «напишите боту — передадим администратору» (возврат и так идёт через handoff). Найди поиском все адреса почты и @-ники в базе, покажи вывод поиска до и после, закоммить, выложи только этот файл (scp) и перезапусти службу. Отвечай по-русски.
Живые люди пишут не по сценарию
Дальше в Telegram писали оператор и я. Каждая находка становилась коротким заданием: что увидел, где в коде, что сделать, какой тест.

Задание 54d. Отмена и проверка имени
Живая проверка оператора: после /start (сценарий заявки) человек написал вопрос «Сколько стоит курс Python?» — бот принял его за имя: «Приятно познакомиться, Сколько стоит курс Python?!». Выйти из сценария нечем — команды отмены нет. Живые люди будут делать так постоянно.
Сделай (локально, НЕ выкладывай — оператор сейчас тестирует бота):
1. Команда /cancel и кнопка «Отмена» на каждом шаге сценария: выход из заявки, состояние очищено, сообщение «Заявка отменена. Можете задать вопрос».
2. Проверка имени: если в ответ на «Как вас зовут?» пришло сообщение с «?», или длиннее 4 слов, или 40 символов — не принимать как имя: «Похоже, это вопрос. Ответить на него?» с кнопками «Ответить на вопрос» (выход из сценария + вопрос уходит ассистенту) и «Продолжить заявку».
3. Тесты на оба пункта; старые тесты не трогать. Полный pytest -q — последняя строка. Закоммить. Отвечай по-русски.
После правки стало 80 passed, 4 старых падения.
CRM упала: это не код
11.10 около 20:00 по Москве заявка не дошла: «CRM недоступна». Сервер CRM почти не отвечал, соединение по SSH рвалось, перезагрузка зависла. Это был сбой хранилища у хостинга. Данные уцелели (84 заявки, ночная копия на Яндекс Диске). Такое ловят мониторинг, резервная копия и запасной путь бота, а не тесты.
Но сбой показал три слабых места бота: уведомление владельцу содержало имя и телефон (через Telegram, а бот за рубежом); бот ждал CRM 3 × 10 секунд и опоздал с ответом на кнопку (Telegram ждёт около 15 секунд, ошибка «query is too old»); пересказ модели для администратора мог унести имя.
Задание 54e. Без личных данных и с очередью
Отмена и проверка имени — принято (80 passed, 4 старых). Живая проверка нашла ещё три вещи, сделай локально (не выкладывать):
1. Сервер CRM лёг (сбой диска у хостинга). Бот трижды ждал по 10 с, потом вызвал callback.answer() — Telegram ответил «query is too old» (на кнопку надо ответить за ~15 с), обработчик упал. Отвечай на нажатие кнопки сразу, до долгих операций.
2. Уведомление владельцу «CRM недоступна» сейчас содержит имя и телефон клиента — это персональные данные через Telegram, а бот за рубежом; по нашим правилам так нельзя. Уведомление — без ПД: «CRM недоступна, заявка №N сохранена, отправлю повторно». Сама заявка ждёт в локальной очереди и отправляется в CRM повторно, когда та оживёт (фоновая задача раз в минуту, без потерь и без дублей — проверь, что CRM не создаст две заявки при повторе). Тесты: CRM недоступна → уведомление без телефона/имени; CRM ожила → заявка ушла один раз.
3. Предупреждение «YADISK_TOKEN не указан» вводит в заблуждение: токен есть, выключена выгрузка (YADISK_EXPORT=0). Текст — по настоящей причине.
4. handoff администратору отправляет result['context'] — это пересказ модели, туда может попасть имя или вопрос целиком. Прогоняй его через маску и ограничь длину; лучше — только вид вопроса (жалоба/возврат/нет в базе).
Старые тесты не удалять и не ослаблять. Полный pytest -q — последняя строка. Закоммить. Отвечай по-русски.
Стало 88 passed. Выкладка этого и прошлого исправлений — задание 54f.
CRM снова отвечает. Выложи на сервер <адрес сервера> (ключ ~/.ssh/<файл SSH-ключа>, root, /opt/bot-kodium, служба bot-kodium) изменения шагов «отмена + проверка имени» и «без ПД + очередь + кнопка» — как в прошлый раз: копия папки на сервере, scp только изменённых файлов кода (посмотри git diff --name-only <хеш коммита прошлой выкладки>..HEAD, без тестов и отчётов), ничего на сервере не удалять, .env не трогать (покажи cut -d= -f1). Если появились новые таблицы — убедись, что они создаются при старте на старой базе. Перезапуск, служба active, последние 20 строк журнала без ошибок (тексты сообщений и ключи не печатать). Отвечай по-русски.

Честная оговорка про 152-ФЗ. Очередь из задания 54e лежит на сервере бота в Амстердаме: пока CRM недоступна, имя и телефон записаны за рубежом. Это противоречит правилу из урока 8.1: первая запись данных граждан РФ делается в базе в России. В учебном прогоне мы это оставили. Для настоящей школы два варианта: очередь на российском сервере (например, отдельный приёмник заявок на сервере CRM) или при недоступной CRM заявку не принимать и ответить: «Не получилось сохранить заявку, попробуйте через 10 минут или оставьте телефон администратору». Это не юридическая консультация: решение владелец школы принимает с юристом.
Новая живая проверка: после /start человек, не нажимая кнопок согласия, спросил цену и дважды получил «отправьте команду /start». А в тексте согласия нашлись Яндекс Диск, обработка за рубежом и отзыв «письмом на почту» без адреса, хотя с П4 заявки хранятся в CRM в Москве.
Задание 54g. Вопрос на любом шаге и согласие по факту
Живая проверка: человек отправил /start, увидел текст согласия и, не нажимая кнопок, написал «Сколько стоит курс Python?». Дважды получил «Для начала работы с ботом отправьте команду /start» — хотя /start он уже отправил. Это общий перехватчик StateFilter("*") (bot.py:551): в шаге согласия текст не обрабатывается.
1. В любом шаге сценария (согласие, имя, телефон, курс) текст, похожий на вопрос, — тот же выбор, что для имени: «Похоже, это вопрос. Ответить на него?» [Ответить на вопрос] [Продолжить заявку]. Остальной текст в шаге согласия — «Нажмите „Согласен“ или „Отмена“». Перехватчик «отправьте /start» — только если человек вообще ни в каком сценарии и ассистент выключен.
2. Текст согласия устарел: в нём написано, что данные хранятся на Яндекс Диске и обрабатываются ботом за рубежом, и «отозвать — письмом на электронную почту Оператора» без адреса. Сейчас заявка уходит в CRM на сервере в России (Москва), бот за рубежом данные только передаёт и хранит временно, пока CRM недоступна. Перепиши согласие по факту: что собираем, где хранится (Россия), что бот за рубежом лишь передаёт, как отозвать (написать боту «отзываю согласие» → передача администратору). Без выдуманных адресов. Это не юридическая консультация — оставь пометку в коде, что текст должен проверить владелец школы.
Тесты на оба пункта, старые не трогать. Полный pytest -q — последняя строка. Закоммить и выложи как в прошлый раз (копия, scp изменённых файлов, .env не трогать, служба active, журнал без ошибок). Отвечай по-русски.
Согласие переписано по факту, отзыв теперь сообщением боту; в коде пометка, что текст проверяет владелец школы (это не юридическая консультация).

«Instance is frozen»: подделка в тестах
Кнопка «Ответить на вопрос» писала «Отвечаю на ваш вопрос…» и молчала, в журнале службы: ValidationError: Instance is frozen. Сообщение Telegram в aiogram (библиотеке бота) похоже на заверенный документ: исправить в нём текст нельзя, а агент пытался. Тесты были зелёные, потому что вместо настоящего сообщения в них стоял MagicMock, манекен, которому можно приписать что угодно. Тест на манекене проверяет манекен.
Задание 54h. Тест на настоящем сообщении
Живая проверка: после «Ответить на вопрос» бот пишет «Отвечаю на ваш вопрос…» и молчит. В журнале сервера: ValidationError «Instance is frozen» на строке `fake_message.text = question` — в aiogram 3 объект Message неизменяемый, подменить ему текст нельзя. Твои тесты прошли, потому что в них сообщение — MagicMock, которому можно присвоить что угодно: подделка не похожа на настоящий объект.
1. Почини: передавай текст вопроса в обработку ассистента параметром (общая функция answer_question(bot, chat_id, user_id, text)), без подмены полей Message.
2. Тест, который ловит именно эту ошибку: настоящий aiogram Message (Message.model_validate или конструктор с минимальными полями), а не MagicMock, — и проверка, что обработчик кнопки «Ответить на вопрос» не падает. Проверь, что тест падает на старом коде (запусти его до исправления) и проходит после.
3. Полный pytest -q, коммит, выкладка как раньше (копия, scp, .env не трогать, active, журнал без ошибок). Отвечай по-русски.
На этом шаге агент ушёл в сторону («интеграция с CRM») и поломку не тронул; правки я сделал руками по тому же заданию. Главная проверка из задания: новый тест падает на старом коде и проходит на новом, иначе он ничего не ловит.
Что нашла живая проверка дальше
- Модель не отвечает именно с сервера. Эту историю ты знаешь из урока 8.3: с сервера gpt-5.4-mini упиралась в таймаут, основной стала gemini.
- Ответы смешивались. Два сообщения подряд обрабатывались одновременно, история путалась: «Тип: запрос на возврат и вопрос по веб-разработке». Лечение: вопросы одного человека встают в очередь, как к одному окошку.
- Ложное обещание. При записи модель писала «мы передали ваш контакт администратору», хотя заявки ещё не было. Правило в промпте: при
action=leadтолько «Открываю форму записи». - Дубль заявки. Заявка, по которой бот получил таймаут, в CRM всё-таки записалась (№84, имя равно тексту вопроса). С очередью повтора это дало бы дубль. Нужен ключ заявки, как номер квитанции: бот даёт номер, CRM второй раз его не принимает. В прогоне не сделано, в миссии это задание по желанию.
Итог прогона: тестовая заявка №85 дошла в CRM; уведомление владельцу и журнал бота без имени и телефона; «Отмена» работает; на «курс ведь 3 месяца?» ответ «Нет, 6 месяцев»; на стих про кота отказ в роли.
Сторож тестов tests_guard из урока 1.11 за весь П7 не сработал ни разу: агент тестов не удалял. Но всё перечисленное выше нашла живая переписка, а не тесты.
По желанию: MCP на чтение базы CRM
Через MCP (урок 1.9) Claude Code можно подключить к базе CRM строго на чтение и спрашивать словами: сколько заявок от ассистента за неделю, нет ли двух заявок с одним телефоном за минуту. В прогоне этого не делали, MCP-сервер для SQLite не проверяли. Если пробуешь: только чтение, личные данные из ответов никуда не копируешь.
Сломай и почини
- Отправь боту два вопроса подряд, не дожидаясь ответа. Ответы приходят по очереди и не смешиваются.
- В тихий час и на минуту впиши в
.envна сервере несуществующую основную модель и перезапусти службу: это боевой бот. Отвечает запасная. Верни как было. - Нажми /start и вместо имени напиши вопрос. Бот спрашивает «Похоже, это вопрос?».
Если не получилось
- Агент предлагает
rsync --delete→ только scp перечисленных файлов, на сервере ничего не удалять, перед выкладкой копия папки. - «Ответить на вопрос» молчит, в журнале «Instance is frozen» → задание 54h.
- «Отправьте /start» тому, кто уже нажал /start → задание 54g.
- Ответ ждут полминуты, потом «произошла ошибка» → проба
proba.pyиз урока 8.3 с сервера, основной ставь быструю. - «query is too old» в журнале → отвечать на нажатие кнопки сразу, до долгих операций.
- Заявки нет в CRM → проверь, жив ли сервер CRM (
ssh,systemctl status); не входит по SSH — вопрос к хостингу.
Контрольный вопрос. Тесты агента зелёные, а в живом боте кнопка «Ответить на вопрос» падает с ошибкой «Instance is frozen». Почему тесты этого не поймали?
Подсказка: Чем объект в тесте отличался от сообщения, которое приходит от Telegram?
На курсе задания проверяются автоматически. Решить первое задание бесплатно → · Подобрать курс
Контрольный вопрос. С ноутбука основная модель отвечает за 9 секунд, с сервера бота в Амстердаме упирается в таймаут 60 секунд. Какую модель ставить основной?
Подсказка: Откуда на самом деле уходят запросы к модели, когда пишет живой человек?
На курсе задания проверяются автоматически. Решить первое задание бесплатно → · Подобрать курс
Контрольный вопрос. Бот не дождался ответа CRM и поставил заявку в очередь на повторную отправку. А CRM на самом деле заявку записала. Что защитит от двух одинаковых заявок?
Подсказка: Как кассир понимает, что квитанцию уже пробивали?
На курсе задания проверяются автоматически. Решить первое задание бесплатно → · Подобрать курс
Задание. МИССИЯ «Ассистент у живых людей». 1. Выложи ассистента по заданиям 54 и 54b: копия папки на сервере, scp конкретных файлов, .env не трогать, служба active, журнал за минуту без ошибок. 2. Найди поиском в базе знаний все почты и @-ники, оставь только настоящие контакты школы или ни одного. 3. Живая проверка в Telegram, минимум 10 сообщений: вопрос по базе; вопрос, которого нет в базе; «курс ведь …?» с неверным числом; просьба не по теме; вопрос вместо имени после /start; запись через ассистента до заявки в CRM. 4. Каждую найденную поломку оформи заданием по образцу урока (что увидел, где, что сделать, какой тест) и проверь снова. 5. По желанию: ключ заявки. Это правка в двух проектах: бот даёт заявке номер и при повторе шлёт тот же, а CRM в Москве не принимает второй раз тот же номер (CRM выкладывается, как в П4). Проверка повторяет настоящий сбой (CRM записала, бот не дождался): отправь в CRM одну и ту же заявку с тем же номером дважды (команду даст агент), в базе одна строка. 6. Напиши в двух предложениях, где у тебя ждёт заявка, пока CRM недоступна, и как это согласуется с правилом из урока 8.1 (первая запись в России). 7. Сломай и почини: два вопроса подряд, несуществующая основная модель, вопрос вместо имени. Принято, если: приложены снимки переписки по пункту 3 (имя, телефон и аватар закрыты, вместо id пользователя слова «id пользователя»), вывод поиска почт и @-ников до и после, последние 20 строк журнала службы без ошибок, два предложения по пункту 6; если делал пункт 5, вывод запроса к базе CRM с -readonly, где после двух отправок с одним номером одна строка; и последняя строка pytest -q. Ключей, телефонов и имён в приложенном нет.
Подсказка: Начни живую проверку с вопроса вместо имени: это первое, что сделает настоящий человек.
✅ Готово, если: программа запускается без ошибок и выводит то, что просят в задании.
На курсе задания проверяются автоматически. Решить первое задание бесплатно → · Подобрать курс
Читайте также: AI-ассистент приёмной в Telegram-боте: почему модель молчит на сервере
Частые вопросы
Почему AI-ассистент в боте не отвечает, хотя на компьютере работал?
Скорость модели зависит от того, откуда идёт запрос. 12.10.2026 основная модель с сервера бота не ответила за 60 секунд, а с ноутбука автора — за 9,4; замеряйте с того сервера, где работает бот, и в разные часы.
Можно ли использовать модель Claude для ассистента через прокси?
Проверяйте замером. В нашем прогоне 11.10.2026 Claude через прокси держал роль ассистента в 0 из 10 ответов; выбрали gemini-3.6-flash основной и gpt-5.4-mini запасной (пример на дату).
Сколько стоит один ответ AI-ассистента?
≈ 0,0001–0,00025 $ за ответ по нашему замеру 11.10.2026 (gpt-5.4-mini и gemini-3.6-flash, пример на дату).
Что ломается при живой проверке, чего не видно в тестах?
У нас 12.10.2026: вопрос посреди записи бот принял за имя («Приятно познакомиться, Сколько стоит курс Python?!»), два сообщения подряд перемешали историю, а текст согласия устарел. Тесты были зелёные — подмены в них «разрешали всё».
Что дальше
Ассистент отвечает живым людям. Следующий урок про деньги: цена ответа и продажа школе.
Итог:
• выкладка без rsync —delete: копия папки, scp конкретных файлов, .env не трогать;
• отчёт проверяется поиском и журналом, а не итоговой фразой;
• живая переписка находит то, чего нет в тестах: вопрос вместо имени, подделку в тестах, смешанные ответы, ложное обещание;
• скорость модели меряется с сервера бота в разные часы;
• повторная отправка заявки требует ключа, иначе будет дубль;
• очередь с личными данными за рубежом спорна по 152-ФЗ: для настоящей школы очередь в России или отказ с просьбой повторить.
