Агент закончил сборку ассистента строкой 68 passed, 4 failed. Четыре падения, а в отчёте «готово». В П6 после такого выяснялось, что агент удалил или перевернул тесты. В этот раз я сначала проверил, были ли эти падения раньше. Были: на первом коммите шага 49 падали даже шесть тестов. Агент ничего не сломал и ничего не спрятал, а старые падения честно назвал старыми.
Чему научишься в этом уроке:
• давать агенту задание на ассистента внутри готового бота;
• понимать, зачем маска телефонов, история в памяти, лимиты и запасная модель;
• разводить запись и передачу администратору так, чтобы модель не видела персональных данных;
• проверять через git, какие падения тестов были до агента.
Перед стартом
- Есть git и первый коммит из задания 49 (урок 8.3), есть база знаний и выбранные модели.
- Запиши последнюю строку
pytest -qдо работы агента. С ней будешь сравнивать. - В
.envестьOWNER_CHAT_ID— куда бот пишет администратору. Его ты кладёшь сам. - Локально бота запускай не с боевым токеном. Telegram отдаёт сообщения бота только одной запущенной копии: пока служба
bot-kodiumработает на сервере, копия у тебя с тем же токеном получит ошибку Conflict (как в уроке 2.5). Два пути: заведи у @BotFather отдельного тестового бота и положи его токен в локальный.env; или на время проверки останови службу на сервере (systemctl stop bot-kodium, после проверкиsystemctl start bot-kodium).
Задание 51
Задание из прогона. В первом абзаце моё решение по замеру: тогда основной стала gpt-5.4-mini. Позже, после замера с сервера, основной стала gemini (урок 8.3), ты ставь свои модели по своему замеру.
Вывод принимаю: MODEL_MAIN=openai/gpt-5.4-mini, MODEL_FALLBACK=google/gemini-3.6-flash (в .env поменяю сам). Две поправки к замеру — я проверил сырой ответ сам: стоимость лежит в usage.cost (у тебя null), а у gemini есть рассуждения — usage.completion_tokens_details.reasoning_tokens ~300 из ~400 токенов ответа (у тебя 0). Кэша сейчас действительно нет. Поправь чтение этих полей в zamer_modeley_v2.py, лишние скрипты (test_api.py, check_usage.py, первая версия замера и её результаты) удали, закоммить.
Дальше — сам ассистент локально (на сервер не выкладывать):
- assistant.py: системный промпт (роль, база знаний из knowledge/kodium.md, правила «только по базе», «чего нет — не знаю», формат JSON answer/action), ввод пользователя — только в сообщении user, не в system; дата и имя в промпт не попадают; основная модель → при ошибке запасная; в журнал — модель, action, usage, время, без текстов.
- маска телефонов и почт до отправки модели; история в памяти 30–60 мин; лимиты длины и частоты.
- в bot.py: свободный текст вне сценария заявки → ассистент; action=lead → запуск готового сценария заявки; action=handoff → сообщение администратору (OWNER_CHAT_ID) без текста пользователя и без ПД, пользователю — «передал администратору».
- тесты кодом (модель подменяется): маска, разбор JSON (в т.ч. в обёртке ```json), запасная модель, lead запускает сценарий, handoff не содержит ПД, лимиты, история истекает.
Старые тесты не удалять и не ослаблять. Полный pytest -q — последняя строка в ответе. Коммить по ходу. Отвечай по-русски.
Что делает каждая часть
Маска до модели. Представь, что письмо уходит за границу через чужую почту: перед отправкой ты закрываешь маркером номер телефона. Код делает то же: телефоны и почты в тексте заменяются на [телефон] и [почта] до того, как текст уйдёт модели. Имена не маскируем: правило «слово с заглавной = имя» испортило бы «Python». Маска снижает риск, но не обнуляет: «восемь девятьсот шестнадцать…» словами она не узнает. Поэтому главная мера другая: модель не участвует в записи вовсе. Отдельный случай: родитель пишет о здоровье ребёнка. Это особая категория данных, такое сообщение лучше модели не отправлять, а сразу передать администратору (в прогоне не делали, можно добавить).
Запись готовой формой. Модель только понимает, что человек хочет записаться, и ставит в бланке action: lead. Дальше код запускает форму из П1: согласие, имя, телефон, курс, и заявка уходит в Кабинет заявок в Москве. Имя и телефон до модели не доходят.
Передача администратору. При action: handoff администратор получает сообщение «есть вопрос, требующий человека» без текста и без телефона, а человек видит «передал администратору».
История в памяти. Чтобы ассистент понимал «а сколько он длится?» после вопроса про Python, бот помнит последние сообщения разговора. Помнит в оперативной памяти и 30–60 минут, на диск сервера в Амстердаме не пишет.
Лимиты. Длина сообщения и число вопросов в минуту. Это защита от того, кто захочет раскрутить счёт школы, вместе с лимитом расхода на ключе в кабинете CloseRouter.
Запасная модель. Основная не ответила или ответила ошибкой, и код повторяет вопрос запасной модели другого поставщика. Тот же приём, что в мониторинге П3 и выводах недели П5.
Журнал без текстов. В журнал пишется модель, действие, расход и время ответа. Текст вопроса и телефон туда не пишутся.
![Путь сообщения в боте: «Сколько стоит Python? мой тел. 8 916 …» → блок «лимиты: длина и частота» → блок «маска»: текст стал «Сколько стоит Python? мой тел. [телефон]» → блок «история в памяти 30–60 мин» → основная модель, при ошибке стрелка к запасной → бланк answer, action, context → три выхода: ответ человеку; action lead → форма записи П1 → Кабинет заявок в Москве; action handoff → администратору сообщение без текста и телефона. Внизу журнал: модель, действие, расход, время, без текста.](https://victor-komlev.ru/wp-content/uploads/2026/10/t1-8-4-01-put-voprosa.png)
Как проверить отчёт агента
Агент написал: 68 тестов прошли, 4 упали, и эти 4 были до него. Проверка занимает пять минут: запусти тесты на первом коммите и сравни. git умеет временно переключить папку на старый коммит и потом вернуть как было:
git log --oneline
git switch --detach <хеш первого коммита>
pytest -q
git switch -
Хеш первого коммита — самая нижняя строка git log --oneline. Перед переключением всё должно быть закоммичено (git status без изменений). Папка .venv и файл .env в git не входят, поэтому при переключении остаются на месте, и тесты идут в том же окружении. git switch - возвращает папку к последнему коммиту. В прогоне на первом коммите упали 6 тестов, а после работы агента 4. Значит, агент ничего не сломал. Ещё три быстрые проверки:
- Число тестов не уменьшилось: сравни с записанной строкой «до».
- Нет обходов:
grep -rn "skip\|DEBUG" --include="test_*.py" .не показывает новых пропусков тестов. - Вопрос человека не попадает в инструкцию: попроси агента показать, как собирается запрос к модели, и найди, что текст пользователя идёт отдельным сообщением
user, а даты и имени вsystemнет.
Обвязка сработала
В П6 без хука, который следит за тестами, агент трижды удалял или ослаблял тесты. В П7 стоял комплект проверок и хук tests_guard (как его поставить, в уроке 1.11), а в папке был git. Хук не сработал ни разу: агент тестов не трогал. Комплект помогает начать правильно, но строку «до», прогон на первом коммите и grep делаешь ты.
Чего эти тесты не видят, станет ясно в живом боте. В них сообщение Telegram подменено заглушкой, которой можно всё. В уроке 8.6 из-за этого ассистент в живом боте молчал, хотя тесты были зелёные.
Сломай и почини
- Поставь в
.envсвоей локальной копии несуществующее имя вMODEL_MAIN, запусти бота у себя (с тестовым токеном, см. «Перед стартом») и задай вопрос. Ответить должна запасная модель, а в журнале видно её имя. Верни имя обратно. - Напиши боту у себя: «мой телефон 8 916 000-00-00, запишите на Python». Бот должен открыть форму записи. Потом поищи номер в файле журнала ассистента (имя файла спроси у агента):
grep -c "916" <файл журнала>. Ответ 0, телефона в журнале нет.
Если не получилось
- Агент пишет «готово», а тесты падают → запусти тесты на первом коммите (
git switch --detach, см. выше) и сравни число падений до и после. - Число тестов уменьшилось →
git diffпо файлам тестов, верни удалённое, задание агенту: «старые тесты не удалять и не ослаблять». - В тексте для модели виден телефон → маска стоит после истории или журнала; попроси маскировать сразу на входе, до всего остального.
- Модель «записывает» человека словами («Записал вас!») → в бланке нет
action: leadили код его не читает; попроси тест «lead запускает сценарий». - Администратору приходит текст вопроса с телефоном → попроси тест «handoff не содержит ПД» и сообщение без текста пользователя.
- Нужно откатить неудачную правку →
git revert <хеш неудачного коммита>: отменяет коммит новым коммитом, история остаётся.git reset --hardкомплект блокирует, и правильно (урок 8.3).
Контрольный вопрос. После работы агента pytest показывает 4 упавших теста. Ты запустил тесты на первом коммите, там упало 6. Какой вывод верный?
Подсказка: Сравни, сколько падало до работы агента и после.
На курсе задания проверяются автоматически. Решить первое задание бесплатно → · Подобрать курс
Контрольный вопрос. Родитель пишет «хочу записать сына на Python». Что должно произойти в собранном ассистенте?
Подсказка: Кто в схеме «Кодиума» собирает имя и телефон?
На курсе задания проверяются автоматически. Решить первое задание бесплатно → · Подобрать курс
Контрольный вопрос. Ты поставил в MODEL_MAIN несуществующее имя модели и задал вопрос. Бот ответил, в журнале имя второй модели. О чём это говорит?
Подсказка: Что код делает, когда основная модель ответила ошибкой?
На курсе задания проверяются автоматически. Решить первое задание бесплатно → · Подобрать курс
Задание. МИССИЯ «Ассистент в своём боте». 1. Запиши последнюю строку pytest -q до работы агента. 2. Дай агенту задание 51 со своими моделями из замера урока 8.3. 3. Проверь отчёт: число тестов не уменьшилось; падения сравни с прогоном тестов на первом коммите (git switch —detach); grep по test_*.py на skip и DEBUG. 4. Сломай и почини: несуществующая основная модель — отвечает запасная; сообщение с телефоном — открывается форма записи, телефона в журнале нет. 5. Запусти бота у себя с тестовым ботом от @BotFather (или остановив службу на сервере) и задай три вопроса: с ответом в базе, без ответа в базе, «хочу записаться». Принято, если: приложены последние строки pytest -q до и после работы агента, число падений на первом коммите, вывод grep -c по номеру телефона в журнале (0), строка журнала с именем запасной модели и снимок переписки с тремя вопросами, где на вопрос без ответа в базе бот говорит «не знаю» или передаёт администратору, а на запись открывает форму. Ключей и настоящих телефонов в приложенном нет.
Подсказка: Строку pytest «до» запиши до того, как отдашь задание: потом её уже не восстановить.
✅ Готово, если: программа запускается без ошибок и выводит то, что просят в задании.
На курсе задания проверяются автоматически. Решить первое задание бесплатно → · Подобрать курс
Читайте также: AI-ассистент приёмной в Telegram-боте: почему модель молчит на сервере
Что дальше
Ассистент работает у тебя на компьютере, и тесты кодом зелёные. Но тесты кодом подменяют модель. В следующем уроке проверяем, что отвечает настоящая модель: девять видов вопросов, чужие команды и поддакивание, на обеих моделях.
Итог:
• ассистент стал режимом бота П1: база, бланк, запись формой, передача администратору;
• телефоны и почты закрываются до модели, история живёт в памяти 30–60 минут;
• при ошибке основной модели отвечает запасная, и ты это проверил;
• отчёт агента ты сверил с прогоном тестов на первом коммите;
• в П7 с комплектом и git агент тестов не удалял.
