LMS и CRM на 205 000 строк кода силами ИИ-агентов: цена эксплуатации, три отметки лида и один символ \r
Две связанные боевые системы — образовательная платформа LMS (121 853 строки TS/TSX, 47 роутеров, 288 процедур) и CRM с единым реестром из 334 инструментов (83 251 строка) — были написаны без команды разработчиков с помощью ИИ-агентов поверх единого PostgreSQL. Написание кода заняло вечера, а главную цену выставила доводка до продакшна: 14 критических дефектов на этапе адверсарного ревью, ещё 5 при проверке собственных фиксов и невидимый символ возврата каретки \r, тихо отключивший интерактивную практику на 138 уроках из 146. Надёжная интеграция ИИ-агентов с живым бизнесом потребовала разделить работу с лидом на три независимых состояния под блокировкой FOR UPDATE, реконструировать историю диалога на сервере и сохранять свою запись встречи до вызова внешних API Zoom и Google Calendar.

Написать крупную систему силами ИИ-агентов без штата программистов сегодня стало дешёвой задачей. Дорого стоит всё, что начинается на следующее утро после демо: защита от подделки истории клиентом, блокировки в базе при двойном клике менеджеров и невидимые спецсимволы в секретах.
Обе мои системы — обучающая платформа LMS (121 853 строки TS/TSX, Next 16.2.6, React 19, Drizzle ORM, 58 миграций, 16 треков, 211 уроков и 55 симуляторов) и стоящая рядом CRM (83 251 строка, Prisma 7.6, 48 моделей БД, BullMQ и четыре подключённых ИИ-агента) — живут на одном инстансе PostgreSQL. Свой стек мы строили по двум причинам: в коробочных LMS нет примитива, где нейросеть сначала отыгрывает слабый промпт студента и лишь затем коучит по скрытой преамбуле, а CRM должна была без дублирования баз лечь поверх работающего в Telegram бота воронки. Кстати, перекраска 289 цветовых переменных при форке CRM заняла вечер, а вычищение унаследованных демо-данных (205 тестовых заявок, фолбэк «маржа 38%» и фальшивые бейджи «Задачи 7») растянулось на два месяца.
Как один невидимый символ возврата каретки \r сломал 138 уроков, дедупликацию и OAuth?
Самый коварный баг в LMS не оставлял ошибок в логах. Студенты видели нормально отрисованный блок :::practice, нажимали «Отправить» и получали «Практический блок не найден». Статистика по базе показала, что на 138 уроках из 146 было ровно ноль успешных прохождений. Контент создавался на Windows с переносами CRLF (\r\n): фронтенд перед рендером нормализовал текст, а серверный парсер резал по \n, оставляя невидимый \r на конце строки:
``typescript // Без нормализации хвостовой \r ломает якорь $ в JS, // так как точка (.) не захватывает \r, и поле task парсится пустым: md = md.replace(/\r\n?/g, "\n"); const match = line.match(/^(task|starter|goal|model|system)\s:\s?(.)$/i); ``
Тот же \r менял контрольный хеш текста при нарезке чанков, заставляя систему заново оплачивать генерацию эмбеддингов, а в третий раз приехал в конец секрета в .env, из-за чего Google OAuth возвращал invalid_client (поймано проверкой длины строки awk -F= '/^GOOGLE_/ {print $1, length($2)}' .env — 36 байт вместо 35).
Почему нельзя верить данным из браузера и как устроен реестр из 334 инструментов CRM?
Адверсарное ревью перед запуском вскрыло 14 тяжёлых дефектов, а проверка написанных по ним патчей — ещё 5. В первой версии практики браузер сам присылал на сервер текст задания и массив history, позволяя подменить реплику через DevTools, а 8 из 55 симуляторов проходились на 71–90 баллов простым копированием условия в ответ. Мы перевели сборку преамбулы и реконструкцию 6 последних реплик строго на сервер, а таймаут грейдера подняли с 30 до 60 секунд.
В CRM вся архитектура построена вокруг единого реестра из 334 инструментов (257 открыты в агентный фасад, а 77 системных закрыты флагом facadeExposed: false). Любой вызов проходит единый конвейер invoke(): идентичность acting_user извлекается только из сессии (разделяя ошибки missing и invalid, когда прислали ID сессии api-... или отрицательный ID группового чата), а аргументы перед записью в audit_log чистятся регуляркой SECRET_KEY_RE.
Зачем делить одну кнопку лида на три состояния и как ловить встречи-сироты в Zoom?
В живых продажах продавец пишет клиенту в личном Telegram, а параллельно того же человека догревает бот. Поэтому одну кнопку «Взять лида» пришлось разделить на три независимых состояния: «Беру в работу» (ответственный), «Связались» (факт первого касания) и «Перехвачен» (бот замолкает и уступает диалог человеку). Чтобы два менеджера не перетёрли карточку при двойном клике, CRM находит самую старую запись по telegram_id и берёт её под блокировку SELECT ... FOR UPDATE. А при создании встреч в Zoom и Google Calendar первой строкой всегда идёт INSERT в свою БД со статусом creating и полями zoom_state / gcal_state, чтобы при сбое внешнего API в облаке не оставались неучтённые встречи-сироты.
Эта механика интерактивных тренажёров и агентных реестров работает внутри Hamidun Academy (открыт 72-часовой триал) и в контуре Nomad AI / agent.nomadnet.ai, а архитектуру наших CRM и LMS мы разбираем в Vibe Club.
- 12 нояб. 2026 г.Google открывает предзаказ на очки с Gemini от $395 и блокирует скрытую съёмку
- 12 нояб. 2026 г.Google привезла Gemini Intelligence в Android Auto — пока только для Pixel
- 12 нояб. 2026 г.По иску, школьница три месяца говорила с Gemini о суициде, а мониторинг молчал неделями
- 12 нояб. 2026 г.Google и Adobe сближают продукты: Acrobat появится в Gmail, Drive и Gemini
Вопросы и ответы по материалу
Как один невидимый символ возврата каретки \r сломал 138 уроков, дедупликацию и OAuth?
Самый коварный баг в LMS не оставлял ошибок в логах. Студенты видели нормально отрисованный блок :::practice, нажимали «Отправить» и получали «Практический блок не найден». Статистика по базе показала, что на 138 уроках из 146 было ровно ноль успешных прохождений.
Почему нельзя верить данным из браузера и как устроен реестр из 334 инструментов CRM?
Адверсарное ревью перед запуском вскрыло 14 тяжёлых дефектов, а проверка написанных по ним патчей — ещё 5. В первой версии практики браузер сам присылал на сервер текст задания и массив history, позволяя подменить реплику через DevTools, а 8 из 55 симуляторов проходились на 71–90 баллов простым копированием условия в ответ. Мы перевели сборку преамбулы и реконструкцию 6 последних реплик строго на сервер, а таймаут грейдера подняли с 30 до 60 секунд.
Зачем делить одну кнопку лида на три состояния и как ловить встречи-сироты в Zoom?
В живых продажах продавец пишет клиенту в личном Telegram, а параллельно того же человека догревает бот. Поэтому одну кнопку «Взять лида» пришлось разделить на три независимых состояния: «Беру в работу» (ответственный), «Связались» (факт первого касания) и «Перехвачен» (бот замолкает и уступает диалог человеку). Чтобы два менеджера не перетёрли карточку при двойном клике, CRM находит самую старую запись по telegram_id и берёт её под блокировку SELECT ... FOR UPDATE.
От чтения про ИИ-агентов — к запуску своего агента за вечер
Пошаговая сборка автономных агентов и MCP-связок в Hamidun Academy (academy.hamidun.com) (72 часа бесплатно) или мгновенный запуск готовых мультишаговых агентов в облаке Nomad.
Главное из мира ИИ — раз в неделю
7 ключевых событий недели, отобранных вручную. Без шума, репостов и пресс-релизов.
Готово! Проверьте почту — мы отправили подтверждение.