Как провести нагрузочное тестирование API LLM с помощью Gatling
При создании приложений с большими языковыми моделями (LLM) важно убедиться, что они способны справляться с разными уровнями спроса. Именно здесь в игру вступает нагрузочное тестирование. Нагрузочное тестирование имитирует реальный трафик, чтобы оценить производительность вашего API в различных условиях. Такой подход помогает выявить потенциальные узкие места и области для улучшения, гарантируя, что приложение остается надежным и отзывчивым.
На недавней берлинской встрече Unstructured Data Meetup Самир Акарио, developer advocate в Gatling, рассказал о том, как проводить нагрузочное тестирование LLM API с помощью Gatling. Gatling — это open source фреймворк для тестирования производительности, который используется для нагрузочного тестирования javascript веб-приложений. Его идеи пролили свет на важность нагрузочного тестирования LLM API и различные используемые методы. В этом блоге мы кратко изложим его ключевые тезисы и обсудим, как проводить нагрузочное тестирование приложений на больших языковых моделях, в частности приложений RAG (retrieval augmented generation), работающих на базе векторных баз данных, таких как Milvus, чтобы улучшить производительность, нагрузку и время отклика.
Посмотрите запись выступления Самира на YouTube.
Что такое нагрузочное тестирование?
Нагрузочное тестирование — это тип тестирования производительности, который оценивает, как система ведет себя при определенных условиях нагрузки, обычно с попытками провести стресс-тестирование системы с огромными объемами данных. Основная цель — оценить, способна ли система обрабатывать ожидаемый пользовательский трафик или объем данных в нормальных и пиковых условиях. Во время нагрузочного тестирования поведение системы отслеживается, чтобы выявить узкие места, снижение производительности или сбои, которые могут повлиять на пользовательский опыт.
Для LLM API нагрузочное тестирование особенно важно из-за сложной природы этих систем и высоких вычислительных требований обработки естественного языка. Отсутствие надлежащего нагрузочного тестирования может привести к перебоям в работе сервиса, медленному времени отклика или неточным результатам, что потенциально подрывает доверие пользователей и общую надежность приложений на базе ИИ.
На этой встрече по неструктурированным данным Самир быстро прошелся по трем типам нагрузочного тестирования: тест емкости, стресс-тест и длительный тест. Давайте немного замедлимся и подробно рассмотрим каждый из них.
Тест емкости
Тест емкости определяет максимальную нагрузку, которую ваш API может выдержать при соблюдении требований к производительности. Цель — определить "золотую середину", в которой система работает при пиковой нагрузке без ухудшения времени отклика или пропускной способности. Для LLM API тестирование емкости находит максимальное число запросов в секунду, которое он может обработать, продолжая предоставлять точные и своевременные ответы.
Например, тест емкости может включать постепенное увеличение числа одновременных пользователей, отправляющих промпты в API, до тех пор, пока время отклика не начнет увеличиваться или точность не начнет снижаться. Эта информация бесценна для планирования емкости и может помочь в принятии решений о том, когда масштабировать инфраструктуру или оптимизировать API.
Тестирование емкости помогает нам планировать ожидаемый трафик и понимать ограничения до того, как мы начнем наблюдать падение производительности. Оно необходимо для того, чтобы гарантировать, что система сможет справиться с ожидаемым ростом и периодами пиковой нагрузки без ущерба для пользовательского опыта.
Стресс-тест
В то время как тест ёмкости определяет оптимальную нагрузку, стресс-тест выводит систему за пределы её возможностей, чтобы обнаружить точку отказа. Цель — оценить, как ваш API ведёт себя в экстремальных условиях, таких как внезапный всплеск запросов или неожиданно большой объём данных.
Стресс-тестирование может имитировать реальные ситуации, например вирусный пост в социальных сетях, который внезапно привлекает огромный трафик к приложению на базе ИИ. Во время таких тестов важно отслеживать не только время отклика и пропускную способность, но и частоту ошибок, использование ресурсов (CPU, память, сеть) и качество ответов API.
Стресс-тестирование необходимо для понимания того, как LLM API может дать сбой и что происходит в таком случае — падает ли он, замедляется или восстанавливается. Эта информация жизненно важна для повышения устойчивости системы и обеспечения её способности корректно обрабатывать неожиданные пики использования. Она также может помочь в разработке более эффективных стратегий аварийного переключения и балансировки нагрузки.
Длительный тест
Длительное тестирование, или тестирование на выносливость, оценивает, как ваш API работает в течение продолжительного периода. Оно выявляет такие проблемы, как утечки памяти, деградация производительности или насыщение подключений к базе данных, которые могут быть незаметны в более коротких тестах. Для LLM API длительный тест запускает стабильный поток запросов в течение нескольких часов или дней, чтобы наблюдать, как система поддерживает производительность.
Идеальная продолжительность длительного теста может различаться в зависимости от системы и ожидаемых сценариев её использования. Для некоторых LLM API 24-часового теста может быть достаточно, тогда как другим могут быть полезны недельные тесты, позволяющие выявить скрытые проблемы, проявляющиеся только в течение длительных периодов.
Длительные тесты особенно хорошо выявляют постепенную деградацию производительности. Например, они могут показать, что время отклика медленно увеличивается со временем или что качество генерируемого текста незаметно снижается после обработки большого количества запросов. Эти выводы могут быть критически важны для внедрения проактивных мер обслуживания и оптимизации долгосрочной производительности.
Этот тест гарантирует, что API способен выдерживать устойчивый спрос без ухудшения работы, что критически важно для приложений, которые должны работать непрерывно или обрабатывать долгосрочный стабильный трафик.
Лучшие практики нагрузочного тестирования LLM API
При проведении нагрузочных тестов LLM API учитывайте следующие лучшие практики:
Используйте реалистичные данные и сценарии, имитирующие реальные шаблоны использования.
Постепенно увеличивайте нагрузку, чтобы точно определить пороги производительности.
Отслеживайте широкий набор метрик, включая время отклика, частоту ошибок и использование ресурсов.
Тестируйте из разных географических регионов, чтобы учитывать сетевую задержку.
Включайте смесь разных типов запросов, которые обычно обрабатывает ваш API.
Инструменты для нагрузочного тестирования Для нагрузочного тестирования LLM API можно использовать несколько инструментов, включая:
Apache JMeter: инструмент с открытым исходным кодом, который можно использовать для различных типов нагрузочных тестов.
Locust: инструмент на базе Python, особенно хорошо подходящий для распределённого нагрузочного тестирования.
Gatling: инструмент на базе Scala, отлично подходящий для нагрузочного тестирования с большим объёмом трафика. В следующем разделе мы перейдём к нагрузочному тестированию с помощью Gatling.
Нагрузочное тестирование LLM API с помощью Gatling
Gatling — это инструмент нагрузочного тестирования веб-приложений, разработанный для DevOps и Continuous Integration. Поскольку вы уже поняли теорию нагрузочного тестирования, давайте посмотрим, как на практике провести нагрузочный тест OpenAI Chat Completions API с помощью Gatling. Следуйте этому руководству, чтобы настроить свой проект Gatling.
1. Настройка класса симуляции
Сначала начните с импорта необходимых библиотек и настройки класса симуляции:
import static io.gatling.javaapi.core.CoreDsl.*;
import static io.gatling.javaapi.http.HttpDsl.*;
import io.gatling.javaapi.core.*;
import io.gatling.javaapi.http.*;
Эти импорты подключают core DSL (Domain-Specific Language) и HTTP DSL для создания сценариев и обработки HTTP-запросов в Gatling. После импорта библиотек определите класс симуляции.
public class SSELLM extends Simulation {
String api_key = System.getenv("api_key");
В приведенном выше коде класс SSELLM расширяет Simulation , обязательный базовый класс для всех симуляций Gatling. Переменная api_key получает API-ключ из ваших переменных окружения, гарантируя, что конфиденциальная информация не будет жестко закодирована.
2. Настройка HTTP-протокола
После передачи API-ключа следующим шагом является указание базового URL сервиса вашего LLM.
HttpProtocolBuilder httpProtocol =
http.baseUrl("https://api.openai.com/v1/chat")
.sseUnmatchedInboundMessageBufferSize(100);
baseUrl указывает базовую конечную точку API, а sseUnmatchedInboundMessageBufferSize(100) настраивает размер буфера для обработки сообщений server-sent events (SSE), которые не соответствуют каким-либо ожидаемым ответам.
3. Определение сценария
Сердце любого теста Gatling — это сценарий, имитирующий поведение пользователя. Давайте определим сценарий для нашего теста.
ScenarioBuilder prompt = scenario("Scenario").exec(
sse("Connect to LLM and get Answer")
.post("/completions")
.header("Authorization", "Bearer " + api_key)
.body(StringBody("{"model": "gpt-3.5-turbo","stream":true,"messages":[{"role":"user","content":"What is a vector database "}]}"))
.asJson(),
asLongAs("#{stop.isUndefined()}").on(
sse.processUnmatchedMessages((messages, session) -> {
return messages.stream()
.anyMatch(message -> message.message().contains("{"data":"[DONE]"}")) ? session.set("stop", true) : session;
})
),
sse("close").close()
);
В приведенном выше сценарии мы имитируем отправку пользователем запроса к OpenAI Chat API и ожидание ответа. Тест устанавливает SSE-соединение (Server-Sent Events) с API. Затем POST-запрос отправляется на конечную точку /completions с сообщением, предлагающим модели дать определение векторной базы данных. Тест продолжает обрабатывать входящие SSE-сообщения с помощью asLongAs("#{stop.isUndefined()}") , что удерживает соединение открытым до тех пор, пока не будет выполнено определенное условие.
По мере получения сообщений sse.processUnmatchedMessages(...) проверяет, содержит ли ответ сигнал "[DONE]" , указывающий, что взаимодействие завершено. Как только этот сигнал обнаружен, сессия останавливается, а соединение закрывается с помощью sse("close").close().
4. Добавление виртуальных пользователей
После создания сценария последний шаг — указать количество пользователей для имитации и настроить протокол.
{
setUp(
prompt.injectOpen(atOnceUsers(3))
).protocols(httpProtocol);
}
}
Приведенный выше код одновременно добавляет в сценарий трех виртуальных пользователей. Этот простой нагрузочный тест проверяет производительность API при обработке трех одновременных запросов.
Используйте следующую команду для запуска кода:
.mvnw.cmd gatling:test
После запуска кода Gatling выведет путь к файлу в терминале. Это путь к отчету о тесте. Вот пример части отчета.
Рисунок 1: Отчет Gatling о диапазоне времени ответа при тестировании OpenAI chat completion API
Как и ожидалось для OpenAI chat completion API, тест показывает, что OpenAI API эффективно обработал одновременные запросы, причем все ответы были получены в хорошие временные интервалы. Но помните, мы отправили только три одновременных запроса и использовали очень короткий prompt.
В реальном сценарии ваша LLM может получать тысячи запросов одновременно и более длинные промпты. Более длинные промпты чаще всего появляются, когда пользователь предоставляет LLM больше контекста. Рассмотрите возможность добавления количества виртуальных пользователей и длины промптов в реальном сценарии.
Gatling не ограничивается API LLM. Вы также можете использовать его для нагрузочного тестирования API, которые обеспечивают работу приложений Retrieval Augmented Generation (RAG). Такой подход даст общую оценку производительности приложения. Давайте создадим сценарий, в котором мы сможем провести нагрузочное тестирование приложения на базе RAG.
Но прежде чем мы это сделаем, вы можете спросить себя: что такое RAG? Сначала давайте разберёмся с концепцией RAG.
Понимание Retrieval Augmented Generation (RAG)
RAG, или retrieval augmented generation, — это метод, который объединяет генеративные возможности большой языковой модели (LLM) с механизмом извлечения для получения релевантной информации из векторной базы данных, такой как Milvus и Zilliz Cloud (управляемый Milvus). Используя внешние данные в качестве контекста, LLM с меньшей вероятностью будет галлюцинировать и будет генерировать более точные и контекстуально релевантные ответы. Кроме того, RAG также позволяет извлекать частные или проприетарные данные для вашей LLM в качестве контекста для более персонализированных ответов без опасений по поводу проблем безопасности данных.
В типичной настройке RAG, когда поступает пользовательский запрос, система RAG извлекает релевантные документы или фрагменты из базы знаний, работающей на базе векторной базы данных. Затем эти документы используются для предоставления контекста LLM, чтобы LLM генерировала более информированный и исчерпывающий ответ.
Рисунок 2: Как работает RAG
Создание сценария для тестирования RAG-приложения
Теперь, когда мы разобрались с RAG, давайте узнаем, как провести нагрузочное тестирование RAG-приложения с помощью Gatling.
Представьте ситуацию, в которой приложение поддержки клиентов работает на базе LLM. Пользователи могут отправлять системе вопросы, требующие подробных ответов с учётом контекста. Приложение использует RAG для извлечения релевантных документов из векторной базы данных, такой как Milvus, чтобы повысить качество этих ответов. Эти документы предоставляют LLM необходимый контекст, позволяя ей формировать более точные и информированные ответы.
Для нашего нагрузочного теста мы разработаем сценарий со следующими процессами.
Симулированные пользовательские запросы: Виртуальные пользователи отправляют сложные запросы, требующие дополнительного контекста. Например, пользователь может спросить: «Как устранить проблему с подключением моего устройства?» Одного этого вопроса недостаточно, чтобы получить качественный ответ от LLM, поэтому системе необходимо извлечь релевантные документы по устранению неполадок из векторной базы данных Milvus .
Контекстное извлечение: Система извлекает наиболее релевантные документы из Milvus на основе пользовательского запроса. Этот шаг крайне важен, поскольку он определяет качество контекста, предоставляемого LLM, напрямую влияя на точность сгенерированного ответа. В конвейере RAG Milvus индексирует огромный объём документации и выполняет поиск по векторному сходству, чтобы быстро найти наиболее релевантную информацию.
Генерация ответа LLM: После извлечения релевантных документов они предоставляются LLM в качестве контекста. Затем LLM использует эту информацию, чтобы ответить на запрос пользователя. Этот процесс генерирует ответ и потенциально цитирует конкретные источники из извлечённых документов.
Нагрузочное тестирование с одновременными пользователями: Затем вы добавляете большее количество виртуальных пользователей, чтобы смоделировать условия пиковой нагрузки.
Мониторинг и анализ: Когда Gatling генерирует отчет, вы отслеживаете любые узкие места, с которыми может столкнуться API, обеспечивающий работу вашего приложения. Важно отметить, что на результаты теста влияют как производительность больших языковых моделей, так и эффективность механизма извлечения под нагрузкой. Это дает вам комплексную оценку того, как работает вся система.
Нагрузочное тестирование вашего приложения на базе RAG с использованием приведенного выше сценария позволит вам выявить любые проблемы масштабируемости.
Заключение
Самир хорошо справился с задачей предоставления ценных сведений о нагрузочном тестировании API LLM с использованием Gatling. Он объяснил различные типы нагрузочного тестирования и то, как мы можем выполнять нагрузочное тестирование API LLM. Мы также расширили статью, чтобы дополнительно рассмотреть, как выполнять нагрузочное тестирование других приложений на базе LLM, таких как приложения клиентской поддержки на основе RAG. Обладая этими знаниями, вы можете разрабатывать API-тесты для своих API, гарантируя, что ваши продукты смогут масштабироваться без проблем. При проведении нагрузочных тестов API LLM учитывайте следующие рекомендации:
Рекомендации по нагрузочному тестированию API LLM
Используйте реалистичные данные и тестовые случаи, имитирующие реальные шаблоны использования.
Постепенно увеличивайте нагрузку, чтобы точно определить пороговые значения производительности.
Отслеживайте широкий спектр метрик, включая время отклика, частоту ошибок и использование ресурсов.
Проводите тестирование из разных географических регионов, чтобы учитывать сетевую задержку.
Включайте сочетание различных типов запросов, которые обычно обрабатывает ваш API.
Дополнительные ресурсы о RAG, GenAI и векторном поиске
Читать далее

Introducing Zilliz CLI and Agent Skills for Zilliz Cloud
Manage your vector database from your terminal or AI coding agent. Zilliz CLI and Agent Skills work with Claude Code, Cursor, Codex, and Copilot.

Zilliz Cloud Just Landed in Claude Code
The Zilliz Cloud Plugin brings the full power of Zilliz Cloud directly into your Claude Code terminal as natural-language conversations.

1 Table = 1000 Words? Foundation Models for Tabular Data
TableGPT2 automates tabular data insights, overcoming schema variability, while Milvus accelerates vector search for efficient, scalable decision-making.


