
Привет! Я Артем Житько, iOS разработчик в Т-Банке. Каждому iOS-разработчику знакома ситуация: задача уже готова, кажется, что самое сложное уже позади. Но затем наступает этап написания unit-тестов. Мы пробовали поручить это ChatLLM, но модель генерировала код, который не компилировался: придумывала несуществующие Mock-объекты, игнорировала архитектурные соглашения проекта и использовала не те тестовые утилиты.
Проблема оказалась не в модели, а в отсутствии контекста. Вместо очередного набора промптов мы создали систему правил на базе AGENTS.md и SKILL.md. Это превратило генерацию тестов из разового запроса в воспроизводимый инженерный процесс.
В статье расскажу, как мы организовали работу AI-агента, какие проблемы встретили и почему хорошие правила сегодня важнее выбора «самой умной» модели.
Почему обычные LLM не подходят
Когда появились первые модели, способные генерировать код, мы, как и многие, начали пробовать их для написания unit-тестов. Казалось, что задача идеально подходит для LLM: есть исходный код, нужно сгенерировать набор проверок — что может пойти не так?
На небольших примерах результат действительно выглядел впечатляюще. Но как только мы попробовали применять подход в реальном проекте, выяснилось, что между «генерирует тест» и «генерирует тест, который соответствует стандартам проекта» — огромная разница. Главная проблема заключалась в том, что модель не знает особенностей конкретного проекта. Она опирается на знания, полученные во время обучения, или на открытые примеры из интернета. Поэтому вместо привычных для команды классов и соглашений LLM начинает использовать свои. Например, в ответе можно встретить let mockUserService = MockUserService(), хотя в проекте уже давно существует let userService = UserServiceMock().
Еще пример: модель может сгенерировать тест с использованием Swift Testing, тогда как весь проект написан на XCTestCase. Формально код выглядит правильным, но использовать его без доработок невозможно. Со временем накопился целый список типичных проблем:
неправильные import'ы;
использование несуществующих fake-объектов;
генерация новых mock-классов вместо использования уже существующих;
вызов методов, которых нет в наших mock-классах;
слишком сложный код там, где в команде принят простой и читаемый стиль;
использование практик, которые запрещены в проекте.
В результате генерация тестов с помощью LLM не избавила от рутины, поменялся лишь ее характер. Вместо того чтобы писать тест с нуля, мы стали постоянно заниматься исправлением ответа модели. Получался примерно такой процесс:

В какой-то момент мы поняли, что проблема не исчезла. Модель экономила время на наборе кода, но большая часть усилий по-прежнему уходила на механические действия: привести тест к принятому в проекте виду, заменить несуществующие сущности, исправить стиль и добиться того, чтобы код вообще начал компилироваться. Стало очевидно, что для автоматизации недостаточно просто использовать LLM. Нужно дать ей контекст проекта: рассказать, какие инструменты уже существуют, какие правила приняты в команде, где искать похожие тесты и что вообще считается правильным кодом в нашем репозитории. Именно с этого момента мы перестали думать в терминах промптов и начали строить правила для AI-агента.
От промптов к агентам
Когда стало понятно, что модели не хватает контекста о проекте, первое решение напрашивалось само собой: добавить этот контекст в промпт. Именно так мы и начали работать. Каждый стартовый запрос к LLM содержал в начале подробную инструкцию о том, как в нашей команде принято писать unit-тесты, а затем исходный код тестируемого класса. Промпт выглядел примерно так:
Ты — опытный iOS разработчик. Твоя задача — покрыть код unit-тестами с помощью XCTest. Тебе необходимо писать unit-тесты для методов класса. Учти следующие пункты:
1. Используй given-when-then стиль в тестах. Пиши given, when, then со строчной буквы.
2. Называй тесты в стиле test_<метод>_<ситуация> или test_<метод>_<ситуация><ожидаемый результат>. Например: test_viewDidLoad_startsPreparingConfigs.
3. Используй sut для тестируемого класса.
4. Все зависимости должны быть заменены на mock-объекты, а параметры — на fake-объекты.
После этого шел еще один важный блок — эталонные примеры. Мы прикладывали существующий класс и соответствующие ему тесты, чтобы модель могла ориентироваться не только на текстовые правила, но и на реальные примеры из нашего проекта. На первых порах это работало достаточно хорошо. Чем подробнее был промпт, тем качественнее получались тесты.
Довольно быстро возникла другая проблема. Со временем желание автоматизировать написание unit-тестов росло вместе с количеством правил и ограничений. Одни касались именования тестов, другие — работы с mock-объектами, третьи описывали особенности архитектуры, четвертые — соглашения по оформлению кода. Постепенно в промпт начали попадать десятки пунктов, исключений и примеров. Кроме того, появились и более практические проблемы:
Одни и те же правила приходилось отправлять модели снова и снова.
Не каждая задача требовала всех инструкций. Например, правила генерации mock или работы с асинхронными тестами были совершенно бесполезны, если агент писал простой синхронный тест для небольшого класса, у которого mock-классы уже написаны. Но модель все равно получала весь этот контекст.
По мере роста промпта мы начали замечать снижение качества ответов. Модель чаще забывала отдельные требования, путалась между похожими правилами, а иногда начинала «галлюцинировать». Вместо ожидаемого улучшения мы получили обратный эффект: чем больше правил пытались запихнуть в один промпт, тем менее предсказуемым становился результат.
Тогда мы начали искать способ разделить знания о проекте на независимые части и подгружать только ту информацию, которая действительно нужна для текущей задачи. Именно в этот момент мы познакомились с двумя механизмами, которые сегодня лежат в основе большинства агентных систем, — AGENTS.md и SKILL.md.
Вместо одного огромного промпта мы разнесли знания по нескольким файлам. Общие правила работы агента переехали в AGENTS.md, а специализированные инструкции — в отдельный SKILL.md. Теперь агент не загружает весь накопленный контекст целиком, а обращается только к тем инструкциям, которые нужны для выполнения конкретной задачи. В результате изменился не только размер промпта, но и сам подход к работе с LLM. Мы перестали воспринимать модель как чат-бота, которому каждый раз нужно заново объяснять правила игры. Она стала агентом, который умеет находить нужные инструкции, понимать структуру проекта и применять знания именно там, где они действительно необходимы.
Почему появился AGENTS.md
Когда мы начали разбираться, как сегодня строятся AI-агенты, выяснилось, что похожие проблемы уже давно решают другие команды. Постоянно передавать один и тот же промпт оказалось не лучшей идеей, поэтому постепенно сформировался открытый стандарт — AGENTS.md.
Если README.md объясняет человеку, как работать с проектом, то AGENTS.md объясняет это искусственному интеллекту. Идея довольно простая: вместо того чтобы каждый раз заново рассказывать модели устройство проекта, все постоянные знания выносятся в отдельный файл, который агент может прочитать перед началом работы.
По сути, AGENTS.md — открытый стандарт описания контекста проекта для AI-инструментов. Он помогает агенту понять:
предметную область проекта;
структуру репозитория;
архитектурные принципы;
правила разработки, принятые в команде;
соглашения по оформлению кода;
доступные инструменты и инфраструктуру.
Благодаря этому агент начинает работать как разработчик, который уже некоторое время находится в команде и базово понимает ее правила. Для нашего проекта AGENTS.md стал своеобразной конституцией. В него мы вынесли все, что должно применяться независимо от конкретной задачи:
как агент должен загружать контекст из AGENTS.md и SKILL.md;
описание проекта, особенностей архитектуры и структура проекта;
как найти документацию.
AGENTS.md не заменяет промпт, а дополняет его. Благодаря этому отпадает необходимость каждый раз отправлять десятки одинаковых инструкций.
Зачем понадобился SKILL.md
Довольно быстро выяснилось, что AGENTS.md недостаточно. В нем удобно хранить общие правила проекта: структуру, архитектуру, соглашения и используемые инструменты. Но этих инструкций недостаточно, чтобы подробно описать, как выполнять каждую конкретную задачу. Даже в рамках одного проекта агент может выполнять совершенно разные задачи: писать unit-тесты, проводить code review, генерировать документацию, рефакторить код или искать причины падения приложения. Каждой из этих задач нужен свой набор специализированных инструкций.
Если описывать все в одном AGENTS.md, он очень быстро превратится в огромный файл на сотни строк, большая часть которого не имеет отношения к текущей задаче, так еще и перегружая контекст. Поэтому следующим шагом стало использование SKILL.md — механизма, который позволяет разбить знания по отдельным специализациям и подгружать только те инструкции, которые действительно нужны агенту в данный момент.
Если провести аналогию с разработчиком в команде:
AGENTS.md отвечает на вопрос: «В каком проекте мы работаем и какие общие правила?»
SKILL.md отвечает на вопрос: «Как правильно выполнить конкретную задачу?»
Для генерации unit-тестов мы создали отдельный skill, в котором описали все, что относится именно к написанию тестов. В него вошли правила:
использовать XCTest;
придерживаться стиля given-when-then;
использовать sut для тестируемого объекта;
не создавать новые mock, если они уже существуют в проекте, а если не существуют, то какую команду запустить для генерации mock-классов;
использовать fake-объекты как входные данные;
соблюдать соглашения по именованию тестов;
не тестировать приватные методы напрямую;
ориентироваться на существующие тесты в проекте.
После этого процесс работы изменился.

Например, когда агент получает задачу «Напиши unit-тесты для ProfileViewModel», то он сначала понимает общий контекст проекта из AGENTS.md:
это iOS-проект;
используется Swift;
тесты находятся в отдельном модуле;
есть определенная структура модулей;
существуют внутренние правила разработки.
После этого он подключает skill для unit-тестов и получает дополнительные инструкции:
как называются mock-классы, нужно ли их генерировать;
как называть тесты;
какие сценарии обязательно покрыть;
какого стиля придерживаться.
Главное изменение заключалось в том, что знания стали структурированными. Мы перестали каждый раз передавать модели огромный набор правил и начали формировать для нее рабочее окружение, похожее на то, которое есть у обычного разработчика. Именно этот подход позволил перейти от простого «генератора кода» к инструменту, который действительно учитывает особенности нашей разработки.
Что получилось
У нас поменялся процесс разработки.

Теперь разработчик занимается не механическим исправлением генерации, а проверкой результата.
После внедрения AI-агента первое, что хотелось сделать, — измерить эффект в метриках. Например, показать красивую таблицу.

Но сделать это и получить объективную картину не получилось, так как процесс написания unit-тестов всегда был трудноизмеряемым процессом. Время зависит от множества факторов:
опыта разработчика;
сложности класса;
количества зависимостей;
качества существующей тестовой инфраструктуры;
необходимости разобраться в бизнес-логике;
количества edge cases.
Подобрать задачи одинаковой сложности, сравнить их выполнение разработчиками с сопоставимым опытом и собрать статистически достоверную выборку — задача сложная. При этом даже после генерации теста разработчик может изменить его структуру, добавить дополнительные проверки или адаптировать под конкретный сценарий, что также влияет на результаты измерений. Поэтому мы не стали приводить искусственные цифры, которые могут создавать ложное ощущение точности. Вместо этого мы оценивали результат с практической точки зрения.
Этап |
До |
После |
Создание файла теста |
Вручную |
Автоматически |
Поиск похожих тестов |
Вручную |
Агент анализирует проект |
Поиск Mock и генерация Mock |
Вручную |
Агент переиспользует существующие и генерирует при необходимости |
Исправление компиляции |
Часто требуется |
Значительно меньше |
Финальная проверка |
Обязательно |
Обязательно |
Заключение
За время работы с LLM мы пришли к простому выводу: качество генерации кода зависит не только от возможностей модели. Намного важнее контекст, который получает агент, и правила, по которым он работает.
Первый подход с промптами позволил понять возможности технологии, но быстро уперся в ограничения: большой объем инструкций, нестабильность результатов и необходимость постоянно исправлять сгенерированный код.
Использование AGENTS.md и SKILL.md изменило подход. Вместо передачи знаний через огромные промпты мы начали формировать рабочую среду для AI-агента: с правилами проекта, архитектурными соглашениями и специализированными навыками.
При этом цель была не заменить разработчика, а убрать наиболее рутинную часть работы. Написание unit-тестов осталось инженерной задачей, но теперь значительная часть механических действий выполняется автоматически.
AI-агенты постепенно становятся еще одним обязательным инструментом разработки. И как любой другой инструмент, они дают максимальную пользу тогда, когда ими умеют пользоваться и они встроены в правильный процесс.
Комментарии (2)

ToxaBes
12.10.2026 09:36Почему обычные LLM не подходят
Уточните пожалуйста, какие модели вы понимаете под обычными и какую в итоге используете?
Temoxa
Коллеги а где примеры этих файлов агента и скилов?)