Как я ночами учил Go и написал AI-тренажер для собеседований: голосовой ввод, интервальное повторение и Docker в проде

Привет, Хабр! Около полугода я пытаюсь вкатиться в бэкенд на Go. Днем кручу гайки в автосервисе, а по ночам и по дороге на работу сижу с кодом и пытаюсь понять устройство рантайма.

В какой‑то момент тем стало слишком много. В голове всё перемешалось: заголовки слайсов, Swiss Tables, планировщик GMP, escape analysis. Зубрить статьи с телефона в дороге толку мало, а на реальные собесы идти страшно — я просто не умел говорить об этом вслух. Когда тебя в реальном времени спрашивают про память или горутины, в голове ступор, язык заплетается.

Делать очередной To‑Do лист для галочки не хотелось, поэтому я решил написать штуку чисто для себя: бота в Telegram, который гоняет меня по техническим вопросам, слушает именно голосовые ответы, строго их оценивает и заставляет снова и снова повторять то, в чем я плаваю. Проект я в итоге доделал и задеплоил на сервак. Ниже хочу рассказать, как устроен бэкенд и на каких граблях я споткнулся по ходу дела.

Стек и архитектура

Писать всё в один файл main.go я не стал, хотелось сразу привыкать к нормальной структуре. Разбил код по Clean Architecture: отдельно доменные модели, отдельно юзкейсы со сценариями, отдельно работа с базой и отдельно слой телеграма с хэндлерами.

Сам бэкенд написан на Go 1.23 с библиотекой telebot.v3. База данных — PostgreSQL 16 через pgx/v5 с пулом соединений, а миграции накатываю через golang-migrate. Состояния сессий и очереди для повторения держу в Redis 7. Для распознавания голоса прикрутил API Deepgram, а ответы оценивает DeepSeek через Groq Cloud API. Все это крутится в Docker Compose на обычном дешевом VPS под Ubuntu.

├── cmd/bot/ # Точка входа, инициализация зависимостей├── internal/│ ├── domain/ # Бизнес-сущности (Question, User, Attempt)│ ├── usecase/ # Сценарии (интервью, mock, квизы, прогресс)│ ├── repository/ # Реализация хранилищ (Postgres + Redis)│ ├── delivery/telegram # Хэндлеры команд, инлайн-кнопки и рендер HTML│ └── ai/ # Клиенты нейросетей и распознавания речи└── migrations/ # Версионированные SQL-миграции

Многие пишут ботов на Python, но на Go эта штука в проде ест всего 25–35 мегабайт оперативки, поднимается за долю секунды и спокойно живет на сервере за 200 рублей в месяц.

Голосовой ввод и почему Deepgram

Вся суть проекта была именно в голосе, потому что на собеседовании тебе никто не дает писать эссе текстом. Сначала я пытался поднять локальный Whisper, но на дешевом сервере без видеокарты он намертво укладывал процессор в полку на 100% при каждом аудиосообщении.

В итоге я перешел на облачный Deepgram с моделью Nova-2. Аудиосообщение из телеги (OGG/Opus) он разбирает меньше чем за секунду, и главное — он нормально понимает наш русский айтишный сленг. Слова вроде «горутины», «мьютекс», «срез» или «дженерики» он не коверкает в кашу. Бот забирает войс из телеги, стримит его в Deepgram и дальше уже работает с чистым текстом.

Оценка ответов и как утихомирить нейросеть

С нейросетями главная беда в том, что они вечно пытаются тебе поддакнуть. Если просто спросить ChatGPT или DeepSeek «как тебе мой ответ?», модель почти всегда напишет «молодец, отлично!», даже если ты сморозил полную ерунду.

Чтобы заставить её судить жестко, я собрал базу из 860 вопросов по 85 темам и к каждому вопросу прописал эталонные критерии (key_points). Модель работает строго по проверочному списку:

Ты строгий технический интервьюер по Go. Вопрос: %s Эталонный ответ: %s Обязательные ключевые критерии: %s Ответ кандидата: %s Твоя задача: 1. Проверь наличие каждого обязательного критерия в ответе кандидата. 2. Поставь объективную оценку от 1 до 10. 3. Укажи, какие именно критерии кандидат упустил. 4. Дай краткий, жесткий разбор без лести.

Теперь, если кандидат говорит «ну, слайс при переполнении удваивается», но забывает упомянуть порог в 256 элементов и новую формулу из Go 1.18+, модель не поставит высокий балл, а прямо ткнет носом в то, что именно было упущено.

Интервальное повторение на Redis и режим мок‑собеса

Зубрить вопросы подряд бесполезно, поэтому я прикрутил интервальное повторение на Redis. Для каждого юзера собирается очередь вопросов на сессию. Ответил уверенно на 8–10 баллов — вопрос улетает в конец очереди. Смазал ответ или набрал меньше 6 баллов — вопрос вернется тебе в лоб уже через пару шагов. Все попытки и баллы пишутся в Postgres, чтобы потом можно было посмотреть статистику.

Еще сделал режим симуляции реального собеседования под разные грейды: выбираешь от стажера до сеньора, бот собирает пул из 10 вопросов, может задавать уточняющие вопросы по ходу ответа, а в конце выдает вердикт: Hire или No Hire с подробным разбором ошибок.

С какими граблями я столкнулся

Первая боль — это HTML‑разметка в Telegram. Если модель возвращает код без бэктиков или где‑то теряется закрывающий тег, телеграм просто падает с ошибкой и не отправляет сообщение. Пришлось написать небольшой санитайзер, который на лету ловит код на Go по ключевым словам (package, func, :=) и сам аккуратно заворачивает его в

 с экранированием.

Вторая проблема — дабл‑клики по кнопкам. Если быстро нажать на инлайн‑кнопку дважды, телеграм шлет два колбэка подряд, и в Redis начиналась гонка с созданием двух параллельных сессий. Вылечил тем, что теперь моментально гашу кнопки через EditReplyMarkup до того, как бэкенд вообще пойдет делать тяжелые запросы в базу.

Ну и по безопасности: в Docker контейнеры не запускаются из‑под root, там настроен отдельный юзер appuser, а в конфиге docker-compose.prod.yml стоят флаги read_only: true, сброшены права cap_drop: ALL, а системные папки вынесены в tmpfs.

Итог

Для меня этот бот стал реальным шансом перестать бесконечно читать теорию и наконец‑то поработать руками с базой, кэшем, внешними API и деплоем на сервак.

Бот полностью бесплатный, я поднял его для всех: @go_SikkiMentor_bot. Исходники открыты на гитхабе: github.com/powood/Tg_GO_Mentor.

Если у кого‑то из опытных ребят найдется время глянуть код или архитектуру и накидать критики — буду очень благодарен!

Источник: habr.com

0 0 голоса
Рейтинг новости
1
0
Подписаться
Уведомить о
0 комментариев