AI-приложения
Тестирование AI-приложений: что ломается и как это тестировать.
LLM-приложение ломается не так, как обычное веб-приложение. Модель каждый раз отвечает по-разному, поэтому проверять сам ответ нельзя — проверяется всё вокруг него: что ответ приходит, стримится, останавливается, заметно сообщает о сбое, соблюдает лимит и никогда не возвращает данные, которые человеку видеть нельзя.
Обновлено 24 августа 2026
Чем тестирование AI-приложения отличается?
Интерфейс тот же. Контракт — нет. Классическая автоматизация тестирования опирается на проверку, которая срабатывает дважды: нажал это — увидел то. Модель ломает это допущение с первого же запуска, поэтому команды часто решают, что AI-функции вообще нельзя покрыть регрессионными тестами, и выпускают их непротестированными.
Можно. Весь фокус в том, чтобы перенести проверку с текста на детерминированные части: ответ пришёл в рамках бюджета, распарсился по запрошенной схеме, сослался на документ, который этому пользователю можно читать, отказ отобразился как отказ, повтор не списал деньги дважды. Всё это обычная автоматизация. А вот оценка того, хорош ли ответ, — задача evaluation: ей нужны ваши собственные эталонные данные, и место ей в отдельном harness рядом с промптами, а не в регрессионном наборе.
Что на самом деле ломается в LLM-приложении
Это поведение, а не мнение о качестве ответа, — именно поэтому его можно тестировать. Каждый пункт доходит до пользователей как баг продукта, а не как плохой ответ.
| Сбой | Как он доходит до пользователя | Тест |
|---|---|---|
| Ответ стримится, а потом обрывается на середине | Обрыв соединения посреди стрима оставляет на экране недописанный ответ без ошибки и без повтора. Пользователи принимают его за ответ. | Оборвать ответ посреди стрима и проверить, что UI показывает состояние ошибки, а не обрезанный ответ. |
| Модель медленная, а UI нечего на это ответить | p95 держится на сорока секундах, а интерфейс не показывает ни прогресса, ни отмены, ни таймаута. Люди нажимают ещё раз — и теперь оплачиваются два запроса. | Держать ответ открытым дольше таймаута и проверить, что есть видимое состояние, способ отмены и нет дублирующего запроса при повторном клике. |
| Провайдер возвращает 429 или 503 | Лимиты и сбои провайдера — обычное дело, а не исключение. Приложения, которые считают их невозможными, показывают сырую ошибку или бесконечный спиннер. | Вернуть 429 и 503 от провайдера и проверить, что приложение деградирует до сообщения, с которым пользователь может что-то сделать. |
| Пустые, отклонённые и отфильтрованные ответы | Модель отказывается или срабатывает фильтр безопасности, а интерфейс рисует пустой пузырь, потому что умеет только счастливый путь. | Вызвать отказ и пустой ответ; проверить, что ни то ни другое не даёт пустого сообщения или сломанной вёрстки. |
| Разговор теряет контекст | История обрезается под размер окна, и ассистент молча забывает то, что ему сказали три сообщения назад. Выглядит так, будто продукт просто глупый. | Довести разговор до предела контекста и проверить, что приложение сообщает, что отбросило, а не отбрасывает молча. |
| Вызовы инструментов срабатывают больше одного раза | Агент повторяет шаг, чей инструмент уже отработал. Что бы этот инструмент ни делал — письмо, списание, запись, — это происходит дважды. | Сделать так, чтобы вызов инструмента завершился по таймауту уже после успеха, и проверить, что повтор не дублирует побочный эффект. |
| Поиск возвращает документ другого клиента | Векторное хранилище фильтруется в промпте, а не в запросе, поэтому поиск, проигнорировавший инструкцию, возвращает данные, которые пользователю читать нельзя. | Запросить от имени одного клиента документ, принадлежащий другому, и проверить, что его нет в найденном наборе, а не просто не упомянуто в ответе. |
| Расходы растут без потолка | Нет лимита на пользователя, поэтому один цикл или один скриптовый аккаунт превращается в счёт, который никто не замечает до инвойса. | Многократно вызывать один и тот же эндпоинт и проверить, что лимит существует — на пользователя, на ключ, в минуту. |
Безопасность
Тестирование безопасности LLM: поверхность, которую стоит проверить
AI-функция обычно приходит подключённой к двум вещам, которых нет у обычной функции: к платному эндпоинту и к компоненту, который выполняет инструкции из всего, что читает. Обе расширяют поверхность атаки так, как стандартный аудит безопасности приложений не предусматривал.
Ниже — в порядке того, как часто это действительно находят, а это не тот порядок, в котором об этом пишут.
- 01
Ключи провайдера в клиентском бандле
Ключ провайдера модели, отправленный во фронтенд-JavaScript или доступный через прокси-маршрут без авторизации перед ним.
Любой с инструментами разработчика может потратить вашу квоту. Это самое дешёвое в проверке из этого списка и самое дорогое, если пропустить.
- 02
Prompt injection через контент
Инструкции, спрятанные в том, что читает модель, — в загруженном документе, спарсенной странице, тикете поддержки, тексте письма, — которые модель затем выполняет.
Атакующий даже не касается вашего поля ввода. Всё, что поглощает ваша система, — это ввод, задумывали вы его таким или нет.
- 03
Инъекция, которая доходит до инструмента
Внедрённые инструкции, заставляющие агента вызвать инструмент: отправить сообщение, записать данные, сделать запрос на внутренний адрес.
Здесь инъекция перестаёт быть конфузом и становится инцидентом. Радиус поражения — всё, что агенту разрешено делать.
- 04
Раскрытие системного промпта и секретов
Модель вынуждают повторить собственные инструкции, определения инструментов или фрагменты переданной ей конфигурации.
Системный промпт часто содержит бизнес-правила, внутренние эндпоинты, а иногда и учётные данные, которые туда никто не собирался класть.
- 05
Поиск по данным другого клиента
Поиск, ограниченный вежливой просьбой к модели, а не фильтром в запросе.
Промпт — это не контроль доступа. Если фильтр живёт в инструкциях, одна проигнорированная инструкция — это утечка данных.
- 06
Вывод, отрисованный как разметка или код
Ответ модели, вставленный на страницу как HTML или переданный в shell, запрос или eval.
Классическая инъекция с новым источником. Модель теперь — недоверенный ввод, и на него атакующий может влиять.
- 07
Неограниченные расходы как отказ в обслуживании
Эндпоинт, вызывающий платную модель без лимита запросов, без потолка на аккаунт и без ограничения размера входных данных.
Цель атаки — не положить вас. Цель — накручивать счёт, пока вы не положите себя сами.
- 08
Что хранят логи
Промпты и ответы, записанные в логи и аналитику, — включая всё, что пользователь туда вставил.
Люди вставляют в чат продакшен-учётки и данные клиентов. Срок хранения ваших логов теперь вопрос защиты данных.
Что QA Spider автоматизирует, а что — нет
Сформулировано достаточно узко, чтобы вы могли нас на этом поймать.
Это мы автоматизируем
- Проходит ваш продукт в настоящем браузере, поэтому описанные выше случаи стриминга, таймаутов, повторов, пустых состояний и ошибок — это обычные UI-тесты: они запускаются по расписанию и при каждом деплое.
- Проверяет API под интерфейсом, включая маршруты, через которые ваш фронтенд обращается к модели.
- Заводит упавшее как тикет в Jira, Azure DevOps, ClickUp, GitHub или Linear — с шагами воспроизведения и скриншотом.
- Проводит поверхностную проверку безопасности публичного сайта: транспорт и заголовки безопасности, флаги cookie, смешанный контент, открытость форм, защита DNS и email от подделки.
Этого мы не делаем
- Сейчас мы не поставляем автоматический набор тестов на prompt injection или jailbreak. Если кто-то говорит, что их краулер делает это из коробки, попросите показать проверки.
- Мы не оцениваем, правилен ли ответ. Это задача evaluation, ей нужны ваши эталонные данные, и это другая дисциплина, чем регрессионное тестирование.
Что спрашивают о тестировании AI-приложений
- Чем тестирование AI-приложения отличается от тестирования обычного веб-приложения?
- Интерфейс тот же, вывод — нет. Обычное приложение дважды возвращает один и тот же ответ, поэтому его можно проверить. Модель каждый раз отвечает по-разному, поэтому проверки переходят на форму и поведение вокруг ответа: что он приходит, стримится, останавливается, заметно сообщает о сбое и стоит столько, сколько должен.
- Как написать проверку, если вывод каждый раз разный?
- Проверяйте контракт, а не текст. Что ответ пришёл в рамках бюджета, что он распарсился по запрошенной схеме, что он сослался на документ, который пользователю можно видеть, что отказ отображается как отказ. Оценка формулировок — это eval, и запускается она отдельно от регрессионного набора.
- Что такое prompt injection и как его тестировать?
- Это инструкции, спрятанные в контенте, который читает ваша система, — в документе, веб-странице, тикете, — и которые модель затем выполняет. Тестируют так: подкладывают инструкции в каждый канал, который доходит до модели, и проверяют, что эффект не наступает — никаких вызовов инструментов, никаких возвращённых данных, никакого повтора системного промпта.
- Какая проблема безопасности в AI-продуктах встречается чаще всего?
- Самые приземлённые. Ключ провайдера, доступный из браузера, эндпоинт модели без лимита запросов и поиск, ограниченный инструкцией в промпте, а не фильтром в запросе. Все три найти дешевле, чем хитрый jailbreak, и встречаются они чаще.
- Может ли QA Spider протестировать мой AI-продукт?
- Поведение вокруг модели — да: стриминг, таймауты, повторы, отказы, состояния ошибок, лимиты запросов и API под интерфейсом, в настоящем браузере при каждом деплое. Автоматических наборов тестов на prompt injection мы сегодня не поставляем, и лучше скажем об этом сразу, чем вы узнаете позже.
- Вы проверяете, правильно ли отвечает модель?
- Нет. Для правильности нужны ваши эталонные данные, и место ей в evaluation harness, которым владеете вы и который версионируется вместе с промптами. Регрессионное тестирование отвечает на другой вопрос: работает ли после сегодняшнего деплоя то, что работало вчера.
Направьте его на свой AI-продукт.
Поведение вокруг модели — стриминг, таймауты, повторы, отказы, лимиты запросов и API под интерфейсом — проверяется в настоящем браузере при каждом деплое.