Первое, что делает разработчик на новом проекте, — читает код. Первое, что предлагают делать с агентом, — просить его этот код объяснить. Звучит логично, работает плохо, и я хочу разобраться, почему.
Это первый заход серии, где один и тот же проект пройдёт за лето десять инженерных задач вместе с агентом. Начинаем с самой безобидной: просто понять, что там внутри.
Стенд
Все статьи серии работают с одним проектом и стартуют от одного зафиксированного коммита.
- Внутренний трекер задач, Symfony 8.0 на PHP 8.4
- 1180 PHP-файлов, около 96 000 строк
- PostgreSQL 16, Doctrine ORM 3, Messenger, Redis
- Twig, Turbo, Stimulus
- 784 теста, покрытие 61 процент
- PHPStan на шестом уровне с базовой линией на 2100 записей
- Модули Core, Tasks, Docs
- Полный прогон CI — 8 минут
Проект я знаю хорошо. Это принципиально: чтобы оценить ответы агента, нужен кто-то, кто знает правильные.
Это наблюдение за двумя заходами в одной рабочей среде, а не воспроизводимый benchmark. Я не зафиксировал в заметках модель, её версию, лимит контекста и commit SHA стенда. Поэтому числа ниже описывают этот случай, а не качество всех агентов. В следующих заходах эти параметры должны записываться до первого запроса.
Symfony 8.0 и PHP 8.4 — версии зафиксированного стенда. Поддержка Symfony 8.0 завершится в июле 2026 года; повторять опыт на новом проекте надо на поддерживаемой ветке, не обновляя исходный стенд задним числом.
Что не работает
Заход первый, наивный: «изучи проект и опиши архитектуру».
Агент потратил четыре минуты, прочитал около семидесяти файлов и выдал полторы страницы текста. Текст выглядел прекрасно. Модули перечислены верно, слои названы правильно, стек определён точно.
Дальше я начал проверять детали и настроение испортилось.
Он написал, что аутентификация построена на JWT. В проекте сессии. Взялось это, судя по всему, из наличия пакета для подписи токенов — он там есть, но используется для ссылок в письмах.
Он написал, что для очередей используется RabbitMQ. Используется Redis. В конфигурации есть закомментированная строка с другим транспортом, оставшаяся с прошлого года.
Он описал модуль Chat как работающий. В src/Module/Chat/ лежат три файла-заготовки и ни одного маршрута.
Три ошибки из пятнадцати проверяемых утверждений: 20 процентов в этой выборке. Это не доля вымысла во всём документе, но для текста, который выглядит как справка, и такой выборки достаточно, чтобы ему не доверять без проверки.
Почему так получается
Не потому что модель плохая. Потому что задача поставлена так, что провоцирует именно этот результат.
«Опиши архитектуру» — это просьба выдать связный текст. Связность подталкивает заполнять пробелы. Здесь агент достроил картину по нерелевантным следам: пакету для подписи токенов и старой закомментированной строке транспорта. Этого было недостаточно для выводов про JWT и RabbitMQ.
Второе: агент читал файлы выборочно, по своему усмотрению. Сводка инструментов показала около семидесяти открытых файлов из 1180, но их список я не сохранил. Это ещё одно ограничение эксперимента.
Третье: у ответа не было формы, в которой ошибку видно. Полторы страницы прозы проверять неудобно, и это само по себе часть проблемы.
Что работает
Заход второй: не просить описание, просить ответы на конкретные вопросы со ссылками на код.
Ответь на вопросы ниже. На каждый ответ дай ссылку file:line, где это видно.
Если ответа в коде нет — напиши "не найдено", не предполагай.
1. Как аутентифицируется пользователь? Где настроен firewall?
2. Какой транспорт у Messenger в проде? Где это задано?
3. Какие модули реально имеют маршруты? Перечисли по routes.yaml.
4. Где проверяется принадлежность к организации?
5. Какие сущности используют мягкое удаление?
...Требование ссылки меняет всё. Утверждение без file:line теперь выглядит подозрительно, а с ним — проверяется за пять секунд в зафиксированном commit. Номера строк поплывут после правок, поэтому в постоянной документации я оставляю путь и имя класса или метода, а строку использую как подсказку для текущей ревизии.
Разрешение ответить «не найдено» снимает давление на связность. В первом заходе агент не мог сказать «не знаю», потому что его просили описать, а не ответить.
Результат второго захода: 15 вопросов, 13 точных ответов со ссылками, 2 честных «не найдено». Ни одного выдуманного факта. На проверку ушло 12 минут.
Что вообще стоит спрашивать
Список вопросов важнее формулировки запроса. За несколько проектов у меня сложился набор, который даёт максимум понимания за минимум чтения.
Границы. Где точки входа: контроллеры, консольные команды, обработчики сообщений, подписчики событий. Это скелет — всё остальное вызывается отсюда.
Данные. Какие таблицы самые большие, где составные индексы, какие сущности связаны с какими. Схема рассказывает о продукте честнее, чем код.
Правила доступа. Где проверяются права, есть ли места, где не проверяются.
Асинхронное. Что уходит в очередь, что по расписанию. Это то, что не видно при чтении контроллеров, и то, что ломается тише всего.
Странности. Что в этом проекте сделано не так, как принято. Вот этот вопрос даёт больше всего пользы и хуже всего формулируется — агент отвечает на него плохо, но сами попытки подсвечивают нестандартные места.
Карта, которая остаётся
Разовое исследование быстро теряет ценность: в новой сессии контекста уже нет, а код продолжает меняться. Результат должен превратиться в файл в репозитории и обновляться в том же review, где меняется описанная архитектура.
У меня это AGENTS.md — не сгенерированный, а собранный руками из проверенных ответов. Для экспериментального снимка рядом нужен commit SHA; в живом файле долговечнее ссылки вида «путь + класс или метод», чем голые номера строк:
## Архитектура
Модульный монолит. Активные модули: Core, Tasks, Docs.
Chat, Planning, Helpdesk — заготовки без маршрутов, не трогать.
Слои внутри модуля: Domain / Application / Infrastructure.
Core — единственный модуль, от которого можно зависеть.
## Ключевые решения
- Аутентификация: сессии, firewall в config/packages/security.yaml
- Очереди: Redis, транспорты heavy / default / notifications
- Идентификаторы: UUID v7, трейт HasUuidIdentity
- Мультиарендность: фильтр Doctrine + явное условие в репозиториях
## Чего здесь нет
- REST API наружу. Есть только внутренние маршруты для Turbo.
- Событий Doctrine. Журнал активности пишется явным вызовом.Раздел «чего здесь нет» я добавил после первого захода. Он напрямую бьёт по механизму, который породил выдумку про JWT: агент достраивает типичное, а этот раздел говорит, чего в проекте нет вопреки типичному.
Замер
| Заход | Время агента | Проверка | Ошибок |
|---|---|---|---|
| «Опиши архитектуру» | 4 мин | 25 мин | 3 из 15 |
| 15 вопросов со ссылками | 6 мин | 12 мин | 0 из 15 |
Второй заход дольше по работе агента и вдвое короче по моей. Разница именно в проверке: утверждение со ссылкой проверяется взглядом, утверждение без ссылки — поиском по проекту.
И главное число, которого нет в таблице: после первого захода у меня остался красивый текст, после второго — файл в репозитории, который работает на всех следующих задачах.
Что дальше
В следующем заходе — баг. Там появится вещь, которой в исследовании не было: способ проверить результат машинно, а не глазами. Посмотрим, насколько это меняет поведение агента.