Come eseguire un test di carico di un'API LLM con Gatling
Quando si creano applicazioni con large language models (LLM), è essenziale assicurarsi che possano gestire livelli di domanda variabili. È qui che entra in gioco il load testing. Il load testing simula il traffico reale per valutare le prestazioni della tua API in condizioni diverse. Questo approccio aiuta a identificare potenziali colli di bottiglia e aree di miglioramento, garantendo che l’applicazione rimanga affidabile e reattiva.
In un recente Unstructured Data Meetup a Berlino, Samir Akarioh, developer advocate presso Gatling, ha parlato di come eseguire il load test di un’API LLM usando Gatling. Gatling è un framework open source per il performance testing utilizzato per eseguire load test di applicazioni web javascript. I suoi spunti hanno fatto luce sull’importanza del load testing delle API LLM e sui diversi metodi utilizzati. In questo blog, riepilogheremo i suoi punti principali e discuteremo come eseguire il load test di applicazioni basate su large language model, in particolare app RAG (retrieval augmented generation) alimentate da vector databases come Milvus per migliorare prestazioni, carico e tempi di risposta.
Guarda il replay del talk di Samir su YouTube.
Che cos’è il Load Testing?
Il load testing è un tipo di performance testing che valuta come si comporta un sistema in specifiche condizioni di carico, di solito cercando di sottoporre il sistema a uno stress test con grandi quantità di dati. L’obiettivo principale è valutare se il sistema può gestire il traffico utente o il volume di dati previsto in condizioni normali e di picco. Durante il load testing, il comportamento del sistema viene monitorato per identificare colli di bottiglia, degrado delle prestazioni o guasti che potrebbero influire sull’esperienza utente.
Per le API LLM, il load testing è particolarmente importante a causa della natura complessa di questi sistemi e delle elevate esigenze computazionali dell’elaborazione del linguaggio naturale. Non condurre un load testing adeguato può portare a interruzioni del servizio, tempi di risposta lenti o risultati imprecisi, danneggiando potenzialmente la fiducia degli utenti e l’affidabilità complessiva delle applicazioni basate sull’AI.
In questo meetup sui dati non strutturati, Samir ha passato rapidamente in rassegna tre tipi di load testing: capacity test, stress test, e soak test. Rallentiamo un po’ e diamo uno sguardo approfondito a ciascuno.
Capacity Test
Un capacity test determina il carico massimo che la tua API può gestire rispettando i requisiti di prestazione. L’obiettivo è identificare lo "sweet spot" in cui il sistema opera al suo carico massimo senza degradazione dei tempi di risposta o del throughput. Per un’API LLM, il capacity testing individua il numero massimo di richieste al secondo che può gestire continuando a fornire risposte accurate e tempestive.
Ad esempio, un capacity test potrebbe comportare l’aumento graduale del numero di utenti concorrenti che inviano prompt all’API finché i tempi di risposta iniziano ad aumentare o l’accuratezza inizia a diminuire. Questa informazione è preziosissima per la pianificazione della capacità e può orientare le decisioni su quando scalare l’infrastruttura o ottimizzare l’API.
Il capacity testing ci aiuta a pianificare il traffico previsto e a comprendere i limiti prima di iniziare a vedere cali di prestazioni. È essenziale per garantire che il sistema possa gestire la crescita prevista e i periodi di picco di utilizzo senza compromettere l’esperienza utente.
Stress Test
Mentre un test di capacità identifica il carico ottimale, uno stress test spinge il sistema oltre i suoi limiti per scoprirne il punto di rottura. Lo scopo è valutare come si comporta la tua API in condizioni estreme, come un improvviso picco di richieste o un volume di dati inaspettatamente elevato.
Lo stress testing può simulare situazioni reali come un post virale sui social media che improvvisamente genera un traffico massiccio verso un’applicazione basata sull’AI. Durante questi test, è importante monitorare non solo i tempi di risposta e il throughput, ma anche i tassi di errore, l’utilizzo delle risorse (CPU, memoria, rete) e la qualità delle risposte dell’API.
Lo stress testing è essenziale per capire come l’API LLM potrebbe fallire e cosa succede quando ciò accade: se si blocca, rallenta o si riprende. Queste informazioni sono fondamentali per migliorare la resilienza del sistema e garantire che possa gestire con eleganza picchi di utilizzo imprevisti. Può anche aiutare a progettare migliori strategie di failover e bilanciamento del carico.
Soak Test
Il soak testing, o endurance testing, valuta come si comporta la tua API per un periodo prolungato. Identifica problemi come perdite di memoria, degrado delle prestazioni o saturazione delle connessioni al database che potrebbero non essere evidenti in test più brevi. Per un’API LLM, un soak test esegue un flusso costante di richieste per diverse ore o giorni per osservare come il sistema mantiene le prestazioni.
La durata ideale di un soak test può variare a seconda del sistema e dei suoi modelli di utilizzo previsti. Per alcune API LLM, un test di 24 ore potrebbe essere sufficiente, mentre altre potrebbero trarre beneficio da test della durata di una settimana per scoprire problemi sottili che si manifestano solo su periodi prolungati.
I soak test sono particolarmente efficaci nel rivelare un degrado graduale delle prestazioni. Ad esempio, potrebbero mostrare che i tempi di risposta aumentano lentamente nel tempo, o che la qualità del testo generato diminuisce sottilmente dopo l’elaborazione di un gran numero di richieste. Questi insight possono essere cruciali per implementare misure di manutenzione proattiva e ottimizzare le prestazioni a lungo termine.
Questo test garantisce che l’API possa gestire una domanda sostenuta senza deteriorarsi, il che è cruciale per le applicazioni che devono funzionare continuamente o gestire traffico costante a lungo termine.
Best practice per il load testing delle API LLM
Quando conduci test di carico sulle API LLM, considera le seguenti best practice:
Usa dati e scenari realistici che imitino i modelli di utilizzo effettivi.
Aumenta gradualmente il carico per identificare accuratamente le soglie di prestazione.
Monitora un’ampia gamma di metriche, inclusi tempi di risposta, tassi di errore e utilizzo delle risorse.
Esegui test da diverse località geografiche per tenere conto della latenza di rete.
Includi un mix di diversi tipi di richieste che la tua API gestisce tipicamente.
Strumenti per il load testing Diverse strumenti possono essere utilizzati per il load testing delle API LLM, tra cui:
Apache JMeter: Uno strumento open-source che può essere utilizzato per vari tipi di test di carico.
Locust: Uno strumento basato su Python particolarmente adatto al load testing distribuito.
Gatling: Uno strumento basato su Scala eccellente per il load testing ad alto volume. La prossima sezione passerà al load testing con Gatling.
Load testing di un’API LLM con Gatling
Gatling è uno strumento di load testing per applicazioni web progettato per DevOps e Continuous Integration. Ora che hai compreso la teoria del load testing, vediamo come possiamo condurre praticamente un test di carico sull’API Chat Completions di OpenAI usando Gatling. Segui questa guida per configurare il tuo progetto Gatling.
1. Configurazione della classe di simulazione
Per prima cosa, inizia importando le librerie necessarie e configurando la classe di simulazione:
import static io.gatling.javaapi.core.CoreDsl.*;
import static io.gatling.javaapi.http.HttpDsl.*;
import io.gatling.javaapi.core.*;
import io.gatling.javaapi.http.*;
Queste importazioni includono il DSL (Domain-Specific Language) principale e il DSL HTTP per creare scenari e gestire richieste HTTP in Gatling. Dopo aver importato le librerie, definisci la classe di simulazione.
public class SSELLM extends Simulation {
String api_key = System.getenv("api_key");
Nel codice sopra, la classe SSELLM estende Simulation , una classe base obbligatoria per tutte le simulazioni Gatling. La variabile api_key recupera la chiave API dalle tue variabili d’ambiente, assicurando che le informazioni sensibili non siano inserite direttamente nel codice.
2. Configurazione del protocollo HTTP
Dopo aver passato la chiave API, il passaggio successivo è specificare l’URL di base del servizio del tuo LLM.
HttpProtocolBuilder httpProtocol =
http.baseUrl("https://api.openai.com/v1/chat")
.sseUnmatchedInboundMessageBufferSize(100);
Il baseUrl specifica l’endpoint di base dell’API, e sseUnmatchedInboundMessageBufferSize(100) configura la dimensione del buffer per gestire i messaggi server-sent events (SSE) che non corrispondono ad alcuna risposta prevista.
3. Definizione dello scenario
Il cuore di qualsiasi test Gatling è lo scenario che simula il comportamento dell’utente. Definiamo uno scenario per il nostro test.
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()
);
Nello scenario sopra, simuliamo un utente che invia una richiesta all’API OpenAI Chat e attende una risposta. Il test stabilisce una connessione SSE (Server-Sent Events) all’API. Viene quindi inviata una richiesta POST all’endpoint /completions con un messaggio che chiede al modello di definire un database vettoriale. Il test continua a elaborare i messaggi SSE in arrivo con asLongAs("#{stop.isUndefined()}") , che mantiene la connessione aperta finché non viene soddisfatta una condizione specifica.
Man mano che i messaggi vengono ricevuti, sse.processUnmatchedMessages(...) controlla se la risposta contiene il segnale "[DONE]" , indicando che l’interazione è completa. Una volta rilevato questo segnale, la sessione si interrompe e la connessione viene chiusa con sse("close").close().
4. Inserimento di utenti virtuali
Dopo aver creato lo scenario, il passaggio finale consiste nello specificare il numero di utenti da simulare e configurare il protocollo.
{
setUp(
prompt.injectOpen(atOnceUsers(3))
).protocols(httpProtocol);
}
}
Il codice sopra inserisce simultaneamente tre utenti virtuali nello scenario. Questo semplice test di carico verifica le prestazioni dell’API nella gestione di tre richieste concorrenti.
Usa il seguente comando per eseguire il codice:
.mvnw.cmd gatling:test
Una volta eseguito il codice, Gatling fornirà un percorso file nel terminale. Questo è il percorso del report del test. Ecco una parte di esempio del report.
Figura 1: Report dell’intervallo dei tempi di risposta di Gatling sul test dell’API di completamento chat di OpenAI
Come previsto per l’API di completamento chat di OpenAI, il test dimostra che l’API OpenAI ha gestito le richieste concorrenti in modo efficiente, con tutte le risposte ricevute in tempi adeguati. Ma ricorda, abbiamo inviato solo tre richieste concorrenti e utilizzato un prompt molto breve.
In uno scenario reale, il tuo LLM potrebbe ricevere migliaia di richieste contemporaneamente e prompt più lunghi. I prompt più lunghi si verificano soprattutto quando l’utente fornisce più contesto all’LLM. Considera di aggiungere il numero di utenti virtuali e la lunghezza dei prompt durante un caso reale.
Gatling non è limitato alle API degli LLM. Puoi usarlo anche per testare sotto carico le API che alimentano applicazioni di Retrieval Augmented Generation (RAG). Questo approccio fornirà una valutazione complessiva delle prestazioni dell’applicazione. Creiamo uno scenario in cui possiamo testare sotto carico un’applicazione basata su RAG.
Ma prima di farlo, potresti chiederti: che cos’è RAG? Comprendiamo prima il concetto di RAG.
Comprendere la Retrieval Augmented Generation (RAG)
RAG, o retrieval augmented generation, è una tecnica che combina le capacità generative di un large language model (LLM) con un meccanismo di recupero per reperire informazioni pertinenti da un database vettoriale come Milvus e Zilliz Cloud (il Milvus gestito). Sfruttando dati esterni come contesti, l’LLM ha meno probabilità di allucinare e genera risposte più accurate e contestualmente pertinenti. Inoltre, RAG ti consente anche di recuperare dati privati o proprietari per il tuo LLM come contesto per risposte più personalizzate, senza preoccuparti di problemi di sicurezza dei dati.
In una tipica configurazione RAG, quando viene ricevuta una query utente, il sistema RAG recupera documenti o frammenti pertinenti da una base di conoscenza alimentata da un database vettoriale. Questi documenti vengono poi utilizzati per fornire contesto all’LLM, in modo che l’LLM generi una risposta più informata e completa.
Figura 2: Come funziona RAG
Creare uno scenario che testa un’applicazione RAG
Ora che abbiamo compreso RAG, impariamo come testare sotto carico un’applicazione RAG usando Gatling.
Immagina una situazione in cui un’applicazione di assistenza clienti è alimentata da un LLM. Gli utenti potrebbero interrogare il sistema con domande che richiedono risposte dettagliate e consapevoli del contesto. L’applicazione sfrutta RAG per estrarre documenti pertinenti da un database vettoriale come Milvus per migliorare la qualità di queste risposte. Questi documenti forniscono all’LLM il contesto necessario, consentendogli di produrre risposte più accurate e informate.
Progetteremmo uno scenario con i processi seguenti per il nostro test di carico.
Richieste utente simulate: Gli utenti virtuali inviano query complesse che richiedono contesto aggiuntivo. Ad esempio, un utente potrebbe chiedere: "Come posso risolvere un problema di connessione con il mio dispositivo?" Questa domanda da sola non fornisce informazioni sufficienti per ottenere una risposta di alta qualità da un LLM, quindi il sistema deve recuperare documenti pertinenti per la risoluzione dei problemi dal database vettoriale Milvus .
Recupero contestuale: Il sistema recupera i documenti più pertinenti da Milvus in base alla query dell’utente. Questo passaggio è cruciale poiché determina la qualità del contesto fornito all’LLM, influenzando direttamente l’accuratezza della risposta generata. In una pipeline RAG, Milvus indicizza una vasta quantità di documentazione ed esegue una ricerca di similarità vettoriale per trovare rapidamente le informazioni più pertinenti.
Generazione della risposta dell’LLM: Una volta recuperati i documenti pertinenti, questi vengono forniti come contesto all’LLM. L’LLM utilizza quindi queste informazioni per rispondere alla query dell’utente. Questo processo genera la risposta e potenzialmente cita fonti specifiche dai documenti recuperati.
Test di carico con utenti concorrenti: Quindi inietti un numero maggiore di utenti virtuali per simulare condizioni di picco di utilizzo.
Monitoraggio e analisi: Quando Gatling genera il report, monitori eventuali colli di bottiglia che l'API alla base della tua applicazione potrebbe incontrare. È importante notare che i risultati dei test sono influenzati sia dalle prestazioni dei modelli linguistici di grandi dimensioni sia dall'efficienza del meccanismo di recupero sotto carico. Questo ti offre una valutazione completa di come sta funzionando l'intero sistema.
Eseguire il test di carico della tua app basata su RAG utilizzando lo scenario sopra descritto garantirà l'identificazione di eventuali problemi di scalabilità.
Conclusione
Samir ha fatto un buon lavoro nel fornire informazioni preziose sul test di carico di un'API LLM utilizzando Gatling. Ha spiegato i diversi tipi di test di carico e come possiamo eseguire il test di carico di un'API LLM. Abbiamo anche ampliato l'articolo per esplorare come eseguire ulteriormente il test di carico su altre applicazioni basate su LLM, come le applicazioni di assistenza clienti basate su RAG. Con queste conoscenze, puoi sviluppare test API per le tue API, assicurando che i tuoi prodotti possano scalare senza problemi. Quando conduci test di carico su API LLM, considera le seguenti best practice:
Best practice per il test di carico delle API LLM
Utilizza dati e casi di test realistici che imitino i modelli di utilizzo effettivi.
Aumenta gradualmente il carico per identificare accuratamente le soglie di prestazioni.
Monitora un'ampia gamma di metriche, inclusi tempi di risposta, tassi di errore e utilizzo delle risorse.
Esegui test da diverse località geografiche per tenere conto della latenza di rete.
Includi un mix di diversi tipi di richieste che la tua API gestisce tipicamente.
Ulteriori risorse su RAG, GenAI e Vector Search
Il panorama dell'ecosistema GenAI: oltre gli LLM e i database vettoriali
Configurazione RAG locale con Llama 3, Ollama, Milvus e LangChain
Costruisci un RAG multimodale con Gemini, BGE-M3, Milvus e LangChain
Modelli AI con le migliori prestazioni per le tue app GenAI | Zilliz
Come scegliere il modello di embedding giusto per i tuoi dati
Continua a leggere

Zilliz Cloud Now Available in Azure North Europe: Bringing AI-Powered Vector Search Closer to European Customers
The addition of the Azure North Europe (Ireland) region further expands our global footprint to better serve our European customers.

Creating Collections in Zilliz Cloud Just Got Way Easier
We've enhanced the entire collection creation experience to bring advanced capabilities directly into the interface, making it faster and easier to build production-ready schemas without switching tools.

Why Not All VectorDBs Are Agent-Ready
Explore why choosing the right vector database is critical for scaling AI agents, and why traditional solutions fall short in production.


