
Над системами аналитики мы обычно работаем командой: менеджер, фронтенд и бэкенд. Но в этот раз фронты были загружены, и проект для своих HR реализовывали вдвоем со Светланой Гущиной — бэкенд‑разработчиком Morizo из заказной разработки и лидом задачи: она решала, как строить, и писала код вместе с агентом. Я участвовал в созвонах, обсуждении метрик, сверке цифр и держал поп‑корн.
Опыт у Светы разноплановый, но значительная его часть связана с Битриксом. Эта задача относилась к другой области. Мы дорабатывали HR‑аналитику в корпоративном портале на Laravel Nova. Портал построили до нас, и мы пришли в готовую систему. Света увидела Nova впервые. Для нее это был и первый опыт работы с агентом в таком формате: ИИ она использовала и раньше, но теперь Kimi Code стоял на ее компьютере и работал напрямую с кодом проекта.
Сначала хотели привлечь фронтендера
Портал внутренний, собран в основном для HR. В нем хранятся данные сотрудников: контакты, сведения о трудоустройстве, проектах и скилах сотрудников, количество детей, адреса для подарков. Это сфера частной жизни человека.
Когда появилась задача на дашборды, мы открыли документацию Nova и быстро поняли, что без Vue‑компонентов не обойтись. Первая мысль была о том, кого из фронтендеров подключить. Вторая: зачем, если есть Kimi Code?
Целей у нас было три:
проверить, как агент пишет код;
понять, поможет ли он в нашем случае;
получить результат, который не стыдно оставить в проде.
Новые инструменты Света любит, но каждый раз ждет подвоха: вдруг станет только хуже.
Токены считать не пришлось. Kimi работал по подписке, и счетчик мы ни разу не открывали.
«Нужен новый дашборд»
Технического задания не было. Было пожелание HR: «Нужен новый дашборд, текущие непонятны». Какие данные нужны и сколько их, предстояло выяснить.
Мы созванивались и обсуждали, что хотим увидеть, зачем и какие данные вообще сможем получить. Придумали один дашборд, решили, как его делать, и взялись за работу. В итоге получилось два блока аналитики с фильтрами и модальными окнами.
На задачу ушло 34 часа. Это общее время команды, а не время работы Kimi Code: в него вошли созвоны, планирование, согласования, вопросы к HR, разработка и тестирование.
У Kimi Code не было доступа к базе
На портале хранятся персональные данные, поэтому безопасность продумали в первую очередь. Закон о персональных данных нам знаком, и наша работа — его соблюдать. Внешний агент рядом с таким порталом уже сам по себе риск: данные могут утечь или попасть не в те руки.
Поэтому всю работу вели на локальном компьютере, в режиме планирования. «Доступа к базе у Kimi не было. Работали в отдельной ветке Git. Коммитила, пушила и вливала в master я, вручную. Я видела код в момент подтверждения каждого изменения и еще раз целиком в конце: какие методы применил агент и как. Без моего ведома он не мог повлиять на работу портала», — объясняет Света.
Из‑за этих ограничений Kimi не мог сам запустить тестирование и посмотреть результат. Это делала Света.
Дело было в дате
До нас на портале уже висели дашборды на встроенных средствах Nova: круговые диаграммы, количество сотрудников, проценты.

Сначала мы тоже не понимали, что они показывают, и проверили код, чтобы выяснить, откуда берутся проценты. Но сотрудники HR задавали тот же вопрос: «Что это за процент? От чего он считается?» Мы объясняли, что диаграмма строится из имеющихся цифр. В ответ звучало: «Данные не совпадают с реальными цифрами».
Диаграммы и числа поменять несложно. Хуже другое: выбрать дату было нельзя. Показатели считались с начала года на сегодня, а HR требовался другой срез.
Про собственные компоненты мы прочитали еще до старта. Но Kimi об этом не сообщать не стали, решили посмотреть, что он предложит сам.
Он пошел по самому короткому пути. Нужны фильтры? Берем стандартные, которые Nova использует для других сущностей. Света сообщила, что фильтры не работают. Тогда агент искал обходные пути и в итоге пришел туда же, куда Света пришла по документации: для нашей задачи нужны собственные Vue‑компоненты.
К выбору даты добавились департаменты, потом период. Так на одной странице появились два блока. Первый показывает данные на выбранную дату, второй за период.
Сделали первый блок, и запросы возросли
Первый блок отвечает на вопрос: что у нас с кадрами на выбранную дату? В нем численность, прием и увольнения с начала года, испытательный срок, формы трудоустройства.
Вывести цифры оказалось проще, чем договориться о том, что они значат. Дольше всего спорили об испытательном сроке (ИС): что именно HR хочет под ним понимать. В показатель «Прошли испытательный срок» попадают сотрудники, которых приняли в текущем году, которые продолжают работать и завершили испытательный срок к выбранной дате. Для уволенных на испытательном сроке правила уже другие. Также важно было, чтобы в выборку попадали только те, кто пришел не ранее первого января, то есть в актуальном году. И таких деталей оказалось много.
Первый блок сдали, и он понравился. Сразу последовала просьба создать второй. В нем блоке появились среднесписочная численность и коэффициент текучести. Фильтры у блоков независимы: в одном выбираются дата и департаменты, в другом период и свой набор департаментов. Изменения в одном блоке на другом не отражаются.

«Сейчас сделаю», — и все упало
Для фильтра по департаментам пришлось учесть структуру компании. Подразделения хранятся в виде дерева, и мы долго решали, какой уровень показывать, как выбирать отделы и что удобнее пользователю. Остановились на втором уровне, вложенные отделы учитываются автоматически.
Технически это обычное дерево, записи связаны через parent_id. Для рекурсивной выборки Kimi взял метод withRecursiveExpression(). После его подключения все упало.
Света сообщила агенту об ошибке. Агент ответил: «Как так». Но в нашем Query Builder такого метода нет. Версию Laravel Kimi видел, но недоступный метод все равно предложил. Обход дерева в итоге реализовали на PHP.
«Нам понравилось упорство агента: есть задача, значит, он будет пробовать, пока не получится. Но у упорства есть обратная сторона. Если важно, как написан код, за агентом приходится следить: работать в режиме планирования, читать ход рассуждений, проверять итог, давать референсы по ходу дела. Ничто не остановит агента без ограничений, и при необходимости он напишет костыль. В этом он от нас с вами отличается не сильно», — уверена Света.
«Я молодец, все сделал за один запрос»
Зато расчет среднесписочной численности порадовал.
Условие: определить, сколько сотрудников работало в каждый день периода, и вычислить среднее. Можно было идти по дням и собирать данные отдельно. Kimi предложил один SQL‑запрос и подал его с интонацией «Я все сделал за один запрос. Смотрите, как здорово».
Для каждого сотрудника база считает, сколько дней выбранного периода он проработал, то есть пересечение срока его работы с интервалом. Дни складываются и делятся на длину периода. SQL‑выражение расчета:

COALESCE(fired_at, date_to) позволяет учитывать работающих сотрудников до конца выбранного периода, а «+1» включает граничные дни интервала. На слайде приведена расчетная часть запроса, без SELECT, FROM и условий отбора.
Нам важно, что он думал не только о результате, но и о нагрузке: отдельный запрос на каждый день приложению ни к чему.
«А кто эти четыре человека?»
Когда оба блока заработали, мы начали изучать цифры, и вопросы появились сами: «Кто эти люди? Почему здесь четыре? Кто попал в выборку?» Мы быстро добавили модальное окно: по клику на карточку открывается список сотрудников с должностями и отделами.

Окно объясняет показатель и заодно вылавливает ошибки в данных: например, если о сотруднике не хватает каких‑либо сведений.
Нашлась и наша собственная нестыковка. Карточка показывала число, а список сотрудников ему не соответствовал: подсчет и детализация применяли условия по‑разному. Света указала на ошибку Kimi, и выборки привели к единой логике.
Это правило не новое, и к ИИ оно отношения не имеет: если одни данные выводятся в нескольких местах, правила их получения должны совпадать. Иначе в одном месте будет четыре человека, а в другом непонятно кто. Единая точка получения данных стала нашим главным правилом.
Laravel Pint строгий. Очень строгий
Были и другие трудности. В интерфейсе Nova не заработала часть классов Tailwind. Мы написали inline‑стили и с их помощью довели фон и прокрутку модального окна.
Отдельная история связана с Laravel Pint. Сборка падала из‑за порядка импортов и выравнивания PHPDoc. Kimi даже записал для себя, что у нас очень строгие проверки.
Была и совсем простая ошибка: переменную использовали до объявления. Агент исправил ее сразу.
После этих взлетов и падений у нас появился список проверок перед пушем:
Laravel Pint: стиль кода.
Тесты: расчеты и фильтры.
Браузер: как все выглядит и работает в интерфейсе.
Сверка данных: показатели совпадают с согласованными правилами.
Авторизация: маршруты защищены.
ИИ ошибается. Код, который выглядит убедительно, еще ничего не доказывает. Смотреть надо, что он делает на самом деле.
«Запиши себе, как делать новые дашборды»
По ходу работы Света попросила Kimi сохранить контекст и правила проекта в AGENTS.md: что это за портал, какой стек, как устроено окружение, какие проверки обязательны. Туда же записали правило подключения базового класса дашборда:
use Laravel\Nova\Dashboards\Main as Dashboard
Kimi прочитал документацию и подключал класс по‑своему. Света исправляла, но агент принимал исправление за ошибку и возвращал прежний вариант. Тогда она четко прописала: «Запиши в AGENTS, что здесь подключаем так. Все, и точка». После этого проблема исчезла.
На изучение дашбордов и попытки со стандартными фильтрами ушло время. Чтобы второй раз не проходить тот же путь, Света сказала агенту записать, как делать новые дашборды в будущем. Так в.agents/skills/ появился навык nova‑dashboards. В нем устройство Vue‑карточки и запроса к Laravel, регистрация дашборда, учет вложенности отделов, независимые фильтры и проверка результата.
Так в.agents/skills/ появился навык nova‑dashboards: как устроить Vue‑карточку и запрос к Laravel, зарегистрировать дашборд, учесть вложенность отделов, сделать независимые фильтры и проверить результат.

Документация — любимая функция
Документация — любимая функция
Закончив разработку, Света сформулировал запрос на получение краткой технической документацию о том, что было сделано. В корпоративном wiki появились параметры запросов и фильтров и правила расчета показателей. Забылось что‑то — открыл и посмотрел.
Техническая документация

Разработчику часто проще придумать решение и написать код, чем затем описать его. Документация — тяжелая часть работы, и когда ее все же пишут, она получается краткой, скупой и не всегда понятной читателю.
Теперь после задач мы пишем документацию с помощью ИИ. Объем зависит от аудитории: можно попросить краткую версию или расширенную.
Лучше разделять документацию на две части. Первая — для менеджеров: больше объяснений словами, что делает функциональность и как ею пользоваться. Вторая — для разработчиков: меньше общих слов, больше таблиц, методов и технических деталей.
Что осталось на потом
После задачи Kimi предложил улучшения. Часть совпала с нашими планами, например выгрузка списка сотрудников в Excel. Ее отложили: сначала проверим, нужна ли она. Напрашивается и сохранение выбранных фильтров в ссылке.
Из технического: вынести Vue‑компоненты из inline JavaScript в отдельную сборку, разобраться с ограничениями, которые возникнут при этом, и добавить фронтенд‑тесты. В текущую задачу это не вошло.
Что мы вынесли из этого опыта
Первое. С Kimi Code работать оказалось несложно. Контекст в ходе задачи приходилось начинать заново, но суть работы агент не терял, и с продолжением проблем не возникало.
Второе. Архитектором может быть и не специалист по фронтенду, если он в теме: продумывает решение, предлагает варианты, обсуждает и проверяет результат. Рутинный код уходит к Kimi.
Третье. Документация. Да, снова. Понятное описание сделанного сразу после разработки — приятный бонус.
И последнее. ИИ ошибается, и ждать идеального результата с первого раза бесполезно. Ошибаются не только Kimi Code, но и все остальные. При этом с ним интересно работать и разговаривать.
Потом были другие задачи: подготовка ТЗ, разработка по нему, интеграции с внешними источниками. Но это отдельные истории.