
Автотесты писали, чтобы экономить время. Но в какой-то момент поняли, что тратим на их обслуживание больше, чем экономим.
Тест упал в CI — открываешь Allure, идёшь на стенд через VPN, авторизуешься, лезешь в DOM, ищешь, какой локатор отвалился. Полчаса-час на один тест. Прошёл релиз, и ещё пара дней уходит на то, чтобы починить локаторы после того, как фронтенд поменял вёрстку. А когда с утра видишь 200 красных тестов при нетронутом коде, начинается расследование.
В этот момент хочется отдать всё это AI-ассистенту и заняться делом. Мы так и сделали, но панацеи не вышло. ИИ снимает часть рутины и экономит часы, но на сложных кейсах он ошибается, ходит по кругу и с уверенным видом выдаёт несуществующие локаторы за настоящие.
Всем привет! На связи Егор Лаптев — QA Fullstack Java в SENSE на проекте крупного российского банка.
В статье рассказываю, где ассистент помогает, а где создаёт больше проблем. Показываю, как он устроен внутри и разбираю шесть рабочих сценариев с кодом. Отдельно выделил ограничения и стоимость нововведений.
В примерах стек Selenide, Cucumber, REST Assured, Allure, Kafka и Moon в Kubernetes, но сами принципы переносятся на любую связку UI- и API-тестов.
С чем мы жили каждый день
В нашем проекте более 2000 автотестов: UI-сценарии на Selenide и Cucumber, API-тесты на REST Assured, интеграционные проверки с Kafka, инфраструктурный мониторинг. Всё это вшито в процессы команды: каждый прогон CI — это сотни сценариев, а каждый релиз — актуализация десятков тестов.
Боль 1: Упавший тест — это 30–60 минут рутины. Открыть Allure. Найти ошибку. Зайти на стенд через VPN. Авторизоваться. Найти страницу. Проверить DOM. Понять, что локатор сломался. Исправить. Запустить. Упало по-другому. Снова исправить. Снова запустить. Прошло. Следующий тест.
Боль 2: Релиз — это несколько дней актуализации. Фронтенд изменил data-test-id на 15 элементах. Нужно найти все 15, проверить DOM на стенде, обновить локаторы, запустить тесты. На каждый элемент — 10–15 минут. 15 элементов — 3–4 часа. А если ещё и вёрстка перестроилась — XPath тоже нужно переписывать.
Боль 3: Массовые падения — и непонятно почему. Утром пришёл — 200 тестов красных. Код не менялся. Что случилось? Moon лежит? DNS упал? Стенд перезапустили? Данные протухли? Нужно последовательно проверять каждую гипотезу — 20–30 минут на диагноз.
Боль 4: Написание нового теста — 2–3 часа на один сценарий. Прочитать тикет. Понять бизнес-логику. Написать feature-файл. Найти или создать Page Object. Написать шаги. Запустить. Упало — поправить локатор. Запустить. Упало — добавить wait. Запустить. Прошло.
Боль 5: Отладка iframe и перекрытых элементов — 1–2 часа на один кейс. Элемент «как бы есть» в DOM, но Selenide его не находит. Почему? iframe? Перекрытие? Не загрузился? Нет прав? Каждая гипотеза — 15–20 минут проверки на стенде.
ИИ берёт на себя рутину в этих пяти сценариях. Дальше разбираю подробнее каждый.

Архитектура: Supervisor + Workers + Skills
Ассистент работает по модели Supervisor-Worker с системой Skills:
-
Supervisor — оркестратор. Читает контекст, определяет тип задачи, делегирует работу конкретному воркеру
-
Workers — исполнители. Имеют доступ к файловой системе, терминалу, Allure-отчётам, Bitbucket, Confluence, Jira, TestOps
-
Skills — набор инструкций, которые определяют как делать задачи. Не общие знания, а конкретные протоколы под наш проект
Ключевое ограничение: Workers не видят историю чата. Supervisor обязан передавать весь контекст в каждом вызове. Пропущенная деталь — воркер работает без нужной информации.
Критическое правило инъекции: Supervisor обязан передавать содержимое релевантных Skills в каждом вызове воркеру. Не передал — воркер работает вслепую.
Практические сценарии
Процесс 1. Починка упавших UI-тестов
Задача: Тест упал с NoSuchElementException. Нужно понять причину и исправить.
Что делает ИИ:
-
Определяет тип ошибки по стектрейсу
-
Находит локатор в Page Object по аннотации @ElementName
-
Проверяет реальный DOM через playwright-cli
-
Сравнивает ожидаемый и реальный DOM, исправляет локатор
-
Запускает тест и проверяет результат
Пример кода — Page Object с аннотациями:
@PageName("Карточка организации")
public class CompanyCardPage {
@ElementName("Пользователи и роли")
public SelenideElement usersAndRolesTile = $(byAttribute("data-test-id", "nibAdminViewButton"));
@ElementName("Модальное окно Пользователи и роли")
public SelenideElement usersAndRolesModal = $(byAttribute("data-test-id", "nibAdminModal"));
}
Пример кода — Step, ищущий элемент по имени:
@Когда("кликаем на блок-кнопку {string}")
public void кликаемНаБлокКнопку(String buttonName) {
SelenideElement element = pageObjectFinder
.getSelenideElementByName(buttonName, pageContext.getCurrentPage());
element.shouldBe(Condition.visible).click();
}
Пример кода — проверка реального DOM через playwright-cli:
# Открываем браузер
playwright-cli open --headed about:blank
# Обход SSL + авторизация через Keycloak SSO
playwright-cli run-code --filename /tmp/pw_ssl_bypass.js
async () => {
const browser = page.context().browser();
const ctx = await browser.newContext({ignoreHTTPSErrors: true});
const p = await ctx.newPage();
await p.goto('https://app-int.moscow.internal.corp/...');
await p.waitForSelector('#username', {timeout: 10000});
await p.fill('#username', 'LOGIN');
await p.fill('#password', 'PASSWORD');
await p.click('#kc-login');
await p.waitForLoadState('networkidle', {timeout: 15000});
return p.url();
}
// Извлекаем все data-test-id из DOM
async () => {
const result = await targetPage.evaluate(() => {
const elements = [];
document.querySelectorAll('[data-test-id]').forEach(el => {
elements.push({
tag: el.tagName,
dataTestId: el.getAttribute('data-test-id'),
text: el.textContent.substring(0, 200).trim()
});
});
return JSON.stringify(elements, null, 2);
});
return result;
}
Приоритет локаторов при исправлении:
|
Приоритет |
Тип локатора |
Почему |
|---|---|---|
|
1 |
@data-test-id |
Стабильный, не зависит от вёрстки |
|
2 |
XPath с contains() |
По тексту или стабильному атрибуту |
|
3 |
CSS-классы через contains() |
CSS-модули генерируют хеш-суффиксы |
Типичные проблемы и решения:
|
Проблема |
Решение |
|---|---|
|
/span не находит |
Заменить на //span (не прямой потомок) |
|
CSS-класс с хеш-суффиксом |
contains(@class, ‘base-name’) |
|
Элемент не кликается |
click(ClickOptions.usingJavaScript()) |
|
Модалка: несколько совпадений |
Привязать к @data-test-id конкретной модалки |
Запуск и проверка:
./gradlew uiParallel -Dcucumber.filter.tags="@TICKET-3146 and not @Ignore"
Отладка сложных UI-флоу. Обычный сценарий: тест падает, инженер открывает стенд, видит, что элемент присутствует в DOM, но не кликается — и начинает разбираться. ИИ систематизирует этот процесс:
iframe (подсистемы внутри основного приложения). Фронтенд рендерит часть интерфейса в iframe — например, виджет из Подсистемы А внутри основного портала. Selenide по умолчанию ищет элементы в основном контексте, а элемент — в iframe. ИИI через playwright-cli переключает контекст, проверяет DOM внутри iframe и предлагает switchTo().frame() перед взаимодействием.
Перекрытые элементы. Элемент виден в DOM, но перекрыт другим — модалкой, тултипом, sticky-хедером. ИИ пробует разные стратегии клика:
-
click() — стандартный клик → падает, ElementClickInterceptedException
-
click(ClickOptions.usingJavaScript()) — JS-клик → может уйти не туда
-
scrollIntoView(true).click() — скролл + клик → если элемент за viewport
-
hover().click() — hover для убирания тултипа → иногда помогает
Если ни одна стратегия не работает — ИИ эскалирует на инженера. Проблема может быть не в локаторе, а в том, что модалка не закрылась из-за бага в приложении.
Race conditions. Тест падает через раз: иногда элемент успевает появиться, иногда нет. ИИ анализирует тайминги в Allure-отчёте — сколько шаг выполнялся, когда появился timeout — и предлагает wait-стратегии: shouldBe(visible) вместо shouldHave(text(…)), явные WaitFor с нужным условием, увеличение таймаута для конкретного шага.
Ограничение: ИИ не понимает ПОЧЕМУ элемент не появился. Он видит следствие — NoSuchElementException, ElementClickInterceptedException, timeout — но не причину. Причина может быть в бизнес-логике (роль не даёт доступ), в баге приложения (сервер вернул 500, данные не загрузились), в инфраструктуре (Moon-под завис, браузер не отвечает). AI видит только код и DOM, а не бизнес-контекст. Отладка — это партнёрство: ИИ работает с DOM, инженер принимает решение.
Процесс 2. Написание новых тестов
Задача: Написать новый автотест по описанию или тикету.
Что делает ИИ:
-
Читает тикет из Jira / описание задачи
-
Пишет feature-файл (Gherkin на русском)
-
Создаёт или дополняет Page Object
-
Пишет Step Definition
-
Запускает тест и проверяет
Пример кода — Feature-файл:
@CompanyCardGeneralInfo
Функционал: Вкладка Общая информация карточки организации
Предыстория: Пользователь входит на портал
* переход на страницу "Страница входа" по ссылке "loginPage"
* Пользователь с ролью "Role_DepartmentHead" авторизуется через логин
* загрузка страницы "Поиск организаций"
Сценарий: Отображение плиток при ролевой модели Подсистемы А
* заполнено поле "Поиск компаний" со значением "тестовая компания"
* нажата кнопка "Найти"
* отображаются результаты поиска, содержащие компанию с названием "Тестовая организация 1"
Тогда тег "Подсистема А" должен быть активен
И отображается блок кнопка "Пользователи и роли"
И элемент "Руководители" скрыт
Пример кода — Page Object:
@PageName("Карточка организации")
public class CompanyCardPage {
@ElementName("ИНН")
public SelenideElement inn = $(byXpath(
"//div[contains(@class, 'main-info-header_row')]" +
"//span[contains(text(), 'ИНН:')]/following-sibling::span"
));
@ElementName("Пользователи и роли")
public SelenideElement usersAndRolesTile = $(
byAttribute("data-test-id", "nibAdminViewButton")
);
}
Никаких @FindBy — только $, $x, $$, $$x. Ленивая инициализация Selenide.
Пример кода — Step Definition с PicoContainer DI:
public class CompanyCardStep {
private final PageObjectFinder pageObjectFinder;
private final PageContext pageContext;
public CompanyCardStep(PageObjectFinder pageObjectFinder, PageContext pageContext) {
this.pageObjectFinder = pageObjectFinder;
this.pageContext = pageContext;
}
@Когда("в хедере карточки компании отображаются нередактируемые поля:")
public void вХедереОтображаютсяПоля(DataTable dataTable) {
List<Map<String, String>> data = dataTable.asMaps(String.class, String.class);
for (Map<String, String> row : data) {
SelenideElement element = pageObjectFinder
.getSelenideElementByName(row.get("Поле"), pageContext.getCurrentPage());
element.shouldBe(Condition.visible);
}
}
}
Процесс 3. Анализ Allure-отчётов и умный реран
Задача: После прогона часть тестов упала по флаковым причинам. Нужно определить, какие стоит перезапустить, и сделать это автоматически.
Что делает ИИ:
-
Парсит JSON-результаты Allure из build/allure-results/
-
Разделяет упавшие тесты на параллельные (независимые) и процессные (с тегом @Sequential)
-
Генерирует rerun-parallel.txt и rerun-sequential.txt
-
Запускает только упавшие тесты через Gradle-таски
Пример кода — Gradle-таска рерана:
tasks.register('rerunParallel', Test) {
filter { includeTestsMatching("ru.company.runner.UITestRunner") }
ignoreFailures = true
onlyIf {
def rerunFile = file("${layout.buildDirectory.get()}/rerun-parallel.txt")
rerunFile.exists() && !rerunFile.text.trim().isEmpty()
}
doFirst {
def rerunPaths = file("${layout.buildDirectory.get()}/rerun-parallel.txt").text.trim()
systemProperty "cucumber.features", rerunPaths.replaceAll('\n', ',')
}
}
Ключевой нюанс: cucumber.features полностью переопределяет @SelectClasspathResource в Suite-раннере. Без onlyIf при пустом файле запустятся ВСЕ тесты.
Процесс 4. Диагностика Moon-кластера
Задача: UI-тесты не запускаются. Нужно понять, жив ли Moon-кластер (Aerokube) в Kubernetes.
Что делает ИИ:
-
Проверяет доступность Moon через /wd/hub/status
-
Пытается создать тестовую сессию Chrome
-
Пробует навигацию на целевой URL
-
Если навигация не работает — проверяет DNS внутри K8s
Пример кода — диагностика через curl:
# Проверка доступности
curl -sk http://moon.test-cluster.internal.corp/wd/hub/status
# Создание тестовой сессии Chrome
curl -sk -X POST http://moon.test-cluster.internal.corp/wd/hub/session
-H 'Content-Type: application/json'
-d '{"capabilities":{"alwaysMatch":{' +
'"browserName":"chrome","browserVersion":"126.0.6478.126-6",' +
'"acceptInsecureCerts":true}}}'
# Навигация на целевой URL
curl -sk --max-time 30 -X POST
"http://moon.test-cluster.internal.corp/wd/hub/session/{SESSION_ID}/url"
-d '{"url":"https://app-int.moscow.internal.corp/"}'
Процесс 5. Kafka в автотестах
Задача: Проверить, что сервис отправил сообщение в Kafka-топик после выполнения бизнес-операции.
Что делает ИИ:
-
Подключается к Kafka-топику с SASL/SCRAM-аутентификацией
-
Читает сообщения с нужным offset
-
Проверяет содержимое сообщения
Пример кода — Cucumber-шаги для Kafka:
@Дано("подключение к Kafka топику {string}")
public void subscribeToTopic(String topic) {
KafkaConsumerClient consumer = getConsumer();
consumer.subscribe(topic);
}
@Когда("прочитаны сообщения из Kafka топика в течение {int} секунд")
public void pollMessages(int timeoutSeconds) {
List<ConsumerRecord<String, String>> messages = consumer.pollMessages(timeoutSeconds);
ScenarioContext.getInstance().setData("kafkaMessages", messages);
}
@Тогда("сообщение из Kafka топика содержит текст {string}")
public void verifyMessageContainsText(String expectedText) {
List<ConsumerRecord<String, String>> messages =
ScenarioContext.getInstance().getData("kafkaMessages");
boolean found = messages.stream()
.anyMatch(record -> record.value().contains(expectedText));
assertTrue(found, "Сообщение с текстом '" + expectedText + "' не найдено");
}
Два режима: subscribe (consumer group) и assign (seekToBeginning, без влияния на offset).
Процесс 6. Выявление инфраструктурных проблем
Задача: Тесты падают массово, но код правильный. Нужно понять, что сломалось в инфраструктуре.
Что делает ИИI:
-
Проверяет доступность Moon-кластера
-
Проверяет создание сессии и навигацию
-
Если навигация не работает — проверяет DNS
-
Если DNS не резолвится — проверяет CoreDNS в K8s
-
Формирует диагноз и передаёт инфраструктурной команде
Типичные инфраструктурные проблемы:
-
Moon недоступен — все тесты падают с Connection Refused
-
DNS не резолвится — браузер открывается, но навигация зависает, ERR_NAME_NOT_RESOLVED
-
Egress заблокирован — сетевые политики K8s блокируют исходящий трафик, страница без стилей
-
Сессии зависают — поды не освобождаются, новые тесты встают в очередь и падают по таймауту
-
БД недоступна — API-тесты падают с 500, в логах ConnectionPoolTimeoutException
Пример кода — цепочка диагностики:
# Шаг 1: Moon жив?
curl -sk http://moon.test-cluster.internal.corp/wd/hub/status
# → 200 OK
# Шаг 2: Можем создать сессию?
curl -sk -X POST http://moon.test-cluster.internal.corp/wd/hub/session ...
# → Сессия создана
# Шаг 3: Навигация работает?
curl -sk --max-time 30 -X POST ".../url" -d '{"url":"https://app-int.moscow.internal.corp/"}'
# → Timeout!
# Шаг 4: DNS резолвится?
nslookup app-int.moscow.internal.corp
# → NXDOMAIN
# Шаг 5: CoreDNS
kubectl get pods -n kube-system -l k8s-app=kube-dns
# → CoreDNS pod CrashLoopBackOff
Весь диагноз за 3 минуты вместо 20–30 минут ручного разбора. ИИ не может починить CoreDNS — только поставить диагноз.
Система Skills: как мы управляем знаниями
Skills — это это конкретные протоколы под конкретные задачи:
|
Skill |
Когда применять |
|---|---|
|
quickfix |
Упал UI-тест — починить |
|
new-test |
Написать новый тест |
|
dom-tree-analysis |
Исследовать DOM через playwright-cli |
|
cucumber-rerun |
Реализовать умный реран |
|
moon-cluster-analysis |
Диагностика Moon |
|
kafka-consumer |
Работа с Kafka в тестах |
|
infra-diagnostics |
Диагностика инфраструктурных проблем |
|
no-analysis-scripts |
НЕ создавать мусорные файлы в проекте |
Правило маршрутизации: читать минимум Skills, достаточный для задачи. Не читать всё подряд.
Ограничения и проблемы
Галлюцинации

Проблема: ИИ генерирует несуществующие Cucumber-шаги и data-test-id.
Пример 1 — несуществующие шаги. ИИ «придумывает» шаг, которого нет в коде. Feature-файл выглядит логично, синтаксис Gherkin правильный — но при запуске UndefinedStepException. Какой именно шаг не существует — выясняется только после компиляции.
Пример 2 — придуманные data-test-id. ИИ анализирует паттерны именования в проекте и генерирует правдоподобный идентификатор. Выглядит как настоящий, но при запуске — NoSuchElementException.
Пример 3 — NBSP в тексте. ИИ видит в DOM <span>Руководители</span> и предлагает:
$(byXpath("//span[text()='Руководители']"))
Но реальный текст содержит NBSP: “Руководителиxa0”. В 9 из 10 случаев работает, на конкретном стенде — нет.
Пример 4 — CSS-хеш-суффиксы. ИИ видит <div class=”modal_header__a3b2c1″> и предлагает $(byClassName(“modal_header__a3b2c1”)). Завтра фронтенд пересоберётся, хеш изменится — тест падает. Правильный вариант:
$(byXpath("//div[contains(@class, 'modal_header')]"))
Последствие: UndefinedStepException или NoSuchElementException при запуске. Инженер тратит время на поиск несуществующего шага в кодовой базе.
Решение:
-
Перед добавлением нового Cucumber-шага ИИ обязан найти все существующие шаги через поиск по репозиторию. Если подходящий шаг существует — использовать его, а не создавать новый.
-
Перед предложением локатора ИИ обязан проверить реальный DOM через playwright-cli. Придуманные data-test-id запрещены.
Зацикливание
Проблема: ИИ зацикливается на исправлении, меняя локатор туда-сюда без результата.
Пример — перекрытый элемент:
Тест падает (NoSuchElementException)
→ AI меняет локатор: //span[text()='X']
→ Тест падает (элемент перекрыт)
→ AI меняет локатор: //span[contains(text(),'X')]
→ Тест падает (та же причина)
→ AI возвращает оригинальный локатор
→ Цикл повторяется
AI пробует стратегии клика:
-
click() → падает, элемент перекрыт
-
click(ClickOptions.usingJavaScript()) → падает, клик уходит не туда
-
scrollIntoView(true).click() → падает, элемент за пределами viewport
-
hover().click() → падает, hover не помогает
-
Меняет локатор на другой → падает, потому что проблема не в локаторе
5 итераций без результата.
Другой сценарий: тест падает из-за таймаута Moon. ИИ думает, что проблема в локаторе, и начинает его менять. //span[text()=’X’] → //span[contains(text(),’X’)] → //div//span[text()=’X’] → добавляет shouldBe(visible) вместо shouldHave(text(…)) — не помогает. Проблема в том, что Moon-под завис и браузер не отвечает. ИИ видит только код, а не метрики кластера.
Последствие: 3–5 итераций без результата, потерянное время на запуск тестов.
Решение: лимит 3 итерации, после — эскалация на инженера с описанием всех попыток.
Невозможность определить причину

Проблема: NoSuchElementException имеет 5+ разных причин, ИИ не может отличить их без анализа DOM.
Пример: тест падает на шаге «отображается блок кнопка Пользователи и роли». ИИ видит NoSuchElementException и начинает менять локатор. Но проблема в том, что пользователь авторизовался под ролью, у которой нет доступа к Подсистеме А — и плитка просто не рендерится. ИИ не знает бизнес-логику, он видит только «элемент не найден».
Возможные причины NoSuchElementException:
-
Элемент убрали из интерфейса → переписать тест
-
Элемент переехал в другой контейнер → обновить локатор
-
Элемент не загрузился из-за таймаута → добавить wait
-
Элемент скрыт из-за прав пользователя → проверить роль
-
Элемент рендерится в iframe → переключить контекст
Последствие: ИИ тратит 3–5 минут на анализ DOM, инженер с опытом видит паттерн за 30 секунд.
Решение: если после анализа DOM причина неясна — ИИ честно пишет «не могу определить причину, требуется инженер» вместо угадывания.
Частичные решения
Проблема: ИИ может починить 8 из 10 упавших тестов, а 2 останутся — и для них нужен инженер. Эти 2 — обычно самые сложные: race condition, неочевидная зависимость между шагами, баг в самом приложении.
ИИ может написать 90% нового теста, но последние 10% — только человек. Сложная бизнес-логика: почему при роли Role1 видна плитка «Пользователи и роли», а при роли Role2 — нет. ИИ видит DOM, но не знает почему.
Хитрые wait-стратегии — тоже проблема. Нужно дождаться не просто появления элемента, а завершения асинхронной операции. ИИ ставит shouldBe(visible) — элемент появился, но данные ещё пустые. Тест проходит иногда, иногда нет. Флак.
Потеря контекста
Проблема: Workers не видят историю чата. Если Supervisor не передал критическую деталь — воркер работает без контекста.
Пример: инженер сказал «не меняй локатор для модалки, он правильный, проблема в таймауте». Supervisor не передал это воркеру — воркер поменял локатор. По протоколу: элемент не найден → проверить DOM → обновить локатор. Воркер действовал по протоколу, но без контекста — сломал то, что не нужно было трогать.
Чем длиннее сессия — тем больше контекста теряется. Supervisor пытается сжимать историю, но иногда выкидывает критические детали.
Последствие: воркер выполняет правильный протокол, но без нужных инструкций — ломает то, что не нужно было трогать.
Решение: протокол инъекции — Supervisor обязан передавать содержимое релевантных Skills в каждом вызове воркеру. Пропущенный пункт — воркер работает без нужных инструкций.
Сколько времени экономит ИИ-ассистент

Ассистент не заменяет инженера, но оптимизирует рутину: диагностика, локаторы, реран, инфраструктурные проверки. Ниже сводная таблица, которая поможет наглядно оценить экономию времени и понять ограничения:
|
Процесс / Задача |
Вручную (Было) |
С ИИ-ассистентом (Стало) |
Ограничения / Нужен человек |
|---|---|---|---|
|
Диагностика UI-теста |
30–60 минут |
5–10 минут |
В 20% случаев (сложная бизнес-логика) |
|
Написание нового теста |
2–3 часа |
30–40 минут |
ИИ пишет ~90% кода, финал за человеком |
|
Актуализация при релизе |
Несколько дней |
Максимум 1 день |
Требуется контроль контекста |
|
Инфраструктурный анализ |
20–30 минут |
3–5 минут |
ИИ только диагностирует, но не чинит K8s |
|
Реран упавших тестов |
Ручной разбор Allure |
Автоматический анализ + перезапуск |
Ложные срабатывания ~5% |
|
Отладка сложных флоу (iframe, перекрытия, race conditions) |
1–2 часа |
15–30 минут |
ИИ не понимает бизнес-контекст, только DOM |
|
Ускорение производительности инженера |
30–40% времени на рутину |
Рутина автоматизирована, фокус на архитектуре |
ИИ не принимает архитектурные решения |
|
Актуализация DOM-карты при изменении фронтенда |
Полный аудит вручную 4–8 часов |
Автоматический обход через playwright-cli за 30–60 минут |
Требуется верификация человеком |
|
Поиск корневой причины массовых падений |
1–3 часа |
5–15 минут |
ИИ ставит диагноз, но не чинит инфраструктуру |
Сколько это стоит: считаем ресурсы

Возьмём команду из 8 человек. Средний запрос к ассистенту около 2 000 токенов (вход плюс выход). За рабочий день набегает примерно 200 запросов, это около 400 000 токенов. За месяц (22 рабочих дня) — порядка 8,8 млн токенов.
При цене ~$5 за миллион токенов это выходит около $44 в месяц. Немного. Но это уже оптимизированный режим — экономию потребления мы выстраивали семь месяцев. Без оптимизации каждый запрос использует полный контекст и требует около 15 000 токенов. Тот же месяц превращается в ~66 млн токенов, а счёт — примерно в $330 в месяц. Разница почти в 7 раз.
Когда ассистентом пользуется весь отдел тестирования, цифры растут:
|
Параметр |
1 команда |
10 команд |
50 команд |
|
Токенов/мес |
~8,8 млн |
~88 млн |
~440 млн |
|
Стоимость/мес |
~$44 |
~$440 |
~$2 200 |
|
Стоимость/год |
~$528 |
~$5 280 |
~$26 400 |
|
Без оптимизации/год |
~$3 960 |
~$39 600 |
~$198 000 |
И это ещё оптимистичная картина. В неё не заложены пиковые нагрузки при релизах (потребление ×3–5 к базовому), стоимость GPU-инфраструктуры под LLM, затраты на адаптацию под каждый проект и лицензии на IDE-плагины.
Почему одних денег может не хватить

Даже с бюджетом упереться можно в физику инфраструктуры:
— GPU в дефиците. LLM требуют A100/H100, очередь на такие карты — месяцы. Закупишь под пик — переплата, под среднее — не хватит в часы релизов.
— Пропускная способность ограничена. При 200+ одновременных сессиях сервис начинает троттлить: время ответа растёт с 5 секунд до 30+, и автотесты сыплются по таймаутам.
— Контекстное окно — бутылочное горлышко. У каждого проекта свой контекст, а 2 000+ тестов — это огромная кодовая база. Втиснуть её в окно целиком нельзя, резать — терять качество, а больше контекста означает дороже запрос.
— Оптимизация не бесплатна. Те самые семь месяцев и 80% потребления придётся повторять для каждого нового проекта. Без этого — потребление и стоимость в 5 раз выше.
— Скрытые расходы. Дообучение, промпт-инженерия, поддержка Skills, мониторинг качества ответов — это не разовые затраты, а постоянный FTE.
Вывод
ИИ-ассистент в тестировании — это инфраструктурный проект со своей стоимостью владения. Для одной команды это $528–3 960 в год, в зависимости от того, оптимизировали вы потребление или нет, — терпимо в обоих случаях. Для 50 команд вилка уже $26K–198K, и это предмет для разговора.
Экономия времени реальна. Но вопрос в том, кто за неё платит и сколько. На масштабе банка ИИ в тестировании — это больше про перераспределение затрат: вместо часов инженеров — часы GPU-кластеров.
P.S. А вы считали полную стоимость владения ИИ на своём проекте — не только токены, но и GPU-инфраструктуру, адаптацию и поддержку? Или пока платите только за токены?
Автор: Egor1301


