Dimensionner pour la production
Le nombre d'instances fahmek dépend des appels à fahmek par seconde au
pic, pas du nombre d'utilisateurs. La détection coûte peu (environ 0,1 ms
par message de chat) ; chaque appel HTTP coûte plus que le travail des
expressions régulières qu'il porte. Le kit d'intégration fait un appel par
message de la conversation à chaque tour : les longues conversations
multiplient la charge. Avant la mise en service, relever la limite de débit
par défaut (100 inspections par seconde et par instance), lancer au moins
deux instances et confirmer avec scripts/load_test.py sur le matériel du
client.
La pile de démonstration (une machine, docker-compose.demo.yml) n'est pas
une installation de production : un seul processus LiteLLM, une seule
instance fahmek, pas de TLS, et une trace qui peut être activée.
1. Estimer la charge#
tours par seconde = utilisateurs en conversation au pic × messages envoyés par minute ÷ 60
appels fahmek par sec. = tours par seconde × appels par tourLes appels par tour dépendent de la façon dont LiteLLM appelle fahmek :
| Intégration | Appels par tour | Comptés par la limite de débit |
|---|---|---|
| Interface web (Generic Guardrail API), réponse sans streaming | 2 (masquage + restitution) | 1 par tour |
| Interface web, réponse en streaming | 1 + un par tour de streaming (LiteLLM décide combien) | 1 par tour |
| Kit d'intégration | un /v1/inspect par message de la conversation, plus un /v1/restore par message masqué | un par message |
Avec le kit, une conversation de 10 messages a coûté 14 appels par tour dans le test de charge (10 inspections, 4 restitutions), et les appels se font l'un après l'autre : fahmek ajoute environ 5 ms par message au tour (médiane mesurée 46 ms pour 10 messages à faible charge).
2. Capacité mesurée#
Mesures du 2026-10-16 avec scripts/load_test.py, fahmek 0.1.0, politique
bank-pilot, dans un bac à sable de 4 vCPU où le générateur de charge
partageait les processeurs avec les services. Ce sont des ordres de grandeur
prudents, pas les chiffres du client.
| Configuration | Tient | Latence ajoutée par fahmek (p99) |
|---|---|---|
| Détection seule, un cœur, en mémoire | 9 000 messages de chat/s ; 1 000 messages de 4 000 caractères/s | 0,2 ms ; 1,7 ms |
| Une instance, 2 workers, kit, tours d'un message | 200 tours/s (370 appels/s) | 20 ms |
| Une instance, 2 workers, kit, conversations de 10 messages | 35 tours/s (495 appels/s) | 135 ms |
| Une instance, 4 workers, kit, conversations de 10 messages | 45 tours/s (640 appels/s) | 105 ms |
| Une instance, 2 workers, interface web, 10 messages, sans streaming | 200 tours/s (400 appels/s) | 24 ms |
| Une instance, 2 workers, interface web, 10 messages, 5 tours de streaming | 50 tours/s (300 appels/s) | 33 ms |
| Passerelle de démonstration entière (1 processus LiteLLM, kit, auto-gad, modèle de test), 4 messages | 15 tours/s | LiteLLM à 99 % CPU, LanguageTool 130 %, fahmek 15 % |
Règle pratique tirée de ces mesures : environ 150 appels fahmek par
seconde et par worker, un worker par cœur. Au-delà de la saturation, les
tours attendent (la latence monte à plusieurs secondes), puis quelques-uns
sont refusés parce que leur budget d'inspection de 15 ms expire : ils
reviennent en 408 ou BLOCKED, jamais laissés passer.
3. Exemple : 10 000 utilisateurs en même temps#
Hypothèses : 10 000 utilisateurs en conversation en même temps, chacun envoie un message toutes les 30 secondes, soit 333 tours par seconde. On compte 150 appels/s par worker, avec 50 % de marge.
| Intégration | Appels fahmek/s | Workers | Exemple de répartition |
|---|---|---|---|
| Interface web, sans streaming | 667 | 7 | 2 instances × 4 workers |
| Interface web, streaming en 5 tours environ | 2 000 | 20 | 5 instances × 4 workers |
| Kit, conversations de 10 messages (14 appels par tour) | 4 700 | 47 | 12 instances × 4 workers |
fahmek est rarement la limite. Au même débit :
- LiteLLM a demandé un processus occupé à plein pour 15 tours/s dans la
démonstration (avec le kit et auto-gad dans le même processus) : prévoir
des dizaines de processus ou d'instances LiteLLM, selon la documentation
de LiteLLM, et mesurer avec
--mode gateway. - Les quotas du fournisseur de modèle (requêtes et jetons par minute) et sa latence (des secondes par réponse) limitent en général en premier.
- auto-gad : LanguageTool a utilisé 1,3 cœur à 15 tours/s. À 333 tours/s,
cela fait environ 30 cœurs. Soit couper auto-gad en production, soit
lancer plusieurs paires auto-gad + LanguageTool derrière un répartiteur de
charge. En surcharge, auto-gad renvoie le texte inchangé (
busy) et ne bloque jamais. - Redis garde quelques kilo-octets par tour pendant la durée de vie de
la politique (120 s pour
bank-pilot) : moins de 100 Mo et quelques milliers d'opérations par seconde ici, très loin de la capacité d'une instance Redis.
4. Réglages#
Les mettre dans le .env à côté du fichier compose ; docker-compose.services.yml
et docker-compose.demo.yml les transmettent à fahmek. Sur Kubernetes, ce sont
les valeurs du chart Helm fahmek.workers, fahmek.inspectRps,
fahmek.inspectBurst et fahmek.replicas ou fahmek.autoscaling
(deploy/helm/fahmek).
| Réglage | Par défaut | Production |
|---|---|---|
FAHMEK_WORKERS | 2 | un par cœur (1 à 32) |
FAHMEK_INSPECT_RPS | 50 par worker | environ deux fois les inspections que reçoit chaque worker au pic (kit : 3 333/s ÷ 48 workers = 70 → 150 ; interface web : 333/s ÷ 8 workers = 42 → 100) |
FAHMEK_INSPECT_BURST | 100 par worker | deux fois FAHMEK_INSPECT_RPS |
| Instances fahmek | 1 | au moins 2, derrière un répartiteur de charge, même image, licence, FAHMEK_API_KEY, politiques et FAHMEK_REDIS_URL |
| Redis | conteneur de démonstration | une adresse partagée par toutes les instances, save "" et appendonly no, redémarrage automatique |
FAHMEK_TRACE | 0 | 0 |
| unreachable fallback de LiteLLM (interface web) | — | fail_closed avec bank-pilot ou bank-ma-strict |
| store prompts in spend logs de LiteLLM | désactivé | désactivé |
La limite de débit est par worker et par instance : l'admission totale vaut
FAHMEK_INSPECT_RPS × workers × instances. Elle protège fahmek d'un appelant
qui s'emballe ; ce n'est pas un réglage de capacité. Au-delà, la réponse est
429 (BLOCKED par l'interface web), jamais un passage sans inspection.
Autres points :
- Pas d'affinité de session. Les valeurs masquées sont dans Redis : n'importe quelle instance restitue ce qu'une autre a masqué. Une répartition tour à tour suffit.
- Contrôles de santé. Router sur
/healthz(ouvert)./readyz(avecX-Fahmek-Key) vérifie aussi que Redis répond sans persistance ; il a besoin deCONFIG GET, que certains services Redis gérés désactivent. - Une seule adresse Redis. fahmek prend une seule
FAHMEK_REDIS_URL; Redis Sentinel et Cluster ne sont pas pris en charge. Un redémarrage ne perd que les correspondances en cours : ces réponses gardent leurs étiquettes«…». Avec une politiquefail_open: false, les requêtes sont refusées tant que Redis est arrêté. - En cas de panne. Avec
bank-pilot, un fahmek indisponible arrête le chat (requêtes refusées, rien ne fuit). Dimensionner pour le pic plus une marge, et garder au moins deux instances pour pouvoir en redémarrer une. - Transport. fahmek parle HTTP simple et la clé partagée passe dans un en-tête : garder LiteLLM, fahmek et Redis sur un réseau privé, avec le TLS terminé par le proxy ou le service mesh du client quand les liens traversent plusieurs machines.
- Métriques. Collecter
/metricssur chaque instance (compteurs et histogrammes, aucune donnée personnelle).
5. Lancer le test de charge#
scripts/load_test.py envoie des conversations fictives à débit fixe et
affiche, pour chaque débit, les tours réussis par seconde, les appels fahmek
par seconde, la part de tours réussis et les latences p50/p95/p99. La sortie
ne contient que des comptes et des durées.
Le lancer contre une instance de test réglée comme la production (même
FAHMEK_WORKERS, limite de débit, Redis), pas contre la production pendant
les heures de travail. Sur une machine qui a l'archive hors-ligne, l'image
fahmek fournit Python ; rien à installer :
export FAHMEK_API_KEY=... # la même clé que l'instance
docker run --rm --network host -e FAHMEK_API_KEY \
-v "$PWD/scripts:/scripts:ro" --entrypoint python fahmek:0.1.0 \
/scripts/load_test.py --mode ui --url http://localhost:8100 \
--history 10 --stream-rounds 5 --rates 100,200,400 --duration 60 --max-p99-ms 100| Option | Sens |
|---|---|
--mode kit / ui / gateway | comment LiteLLM appelle fahmek (section 1) ; gateway envoie des chats entiers à LiteLLM (définir LITELLM_KEY) |
--rates | tours par seconde à essayer, l'un après l'autre |
--duration | secondes par débit (60 ou plus pour décider) |
--history | messages par conversation : la longueur habituelle chez le client |
--stream-rounds | interface web : appels de réponse par réponse (1 = sans streaming) |
--max-p99-ms, --min-ok | code de sortie 1 quand un débit manque ces objectifs |
--json FICHIER | enregistre aussi les résultats |
N'utiliser --mode gateway qu'avec le modèle de test de la démonstration ou
un modèle de test : avec un vrai modèle, chaque tour est une requête payante,
et la latence et les quotas du modèle dominent.
Lire le tableau :
- La capacité est le débit le plus haut où
ok/ségale la cible,ok sharevaut 1.0 et le p99 respecte l'objectif. Prévoir le pic à deux tiers de ce débit au plus. rate_limited: releverFAHMEK_INSPECT_RPS/FAHMEK_INSPECT_BURST.budget_exceeded, ou des latences en secondes : les processeurs sont saturés ; ajouter des workers ou des instances.license: l'instance n'a pas de licence valide (corriger avant le test).
6. Liste de mise en service#
- Pic de tours par seconde estimé (section 1), avec la vraie longueur des conversations du client.
- Test de charge réussi à 1,5 × ce pic, sur le matériel de production.
- Au moins deux instances fahmek ;
FAHMEK_WORKERS,FAHMEK_INSPECT_RPSetFAHMEK_INSPECT_BURSTréglés. - Redis sans persistance ;
/readyzrépond prêt sur chaque instance. -
FAHMEK_TRACE=0; stockage des prompts de LiteLLM désactivé. - Politique
bank-pilot(ou plus stricte) et, par l'interface web,unreachable_fallback: fail_closed. - Expiration de la licence surveillée (
GET /v1/license,days_left). - LiteLLM, les quotas du fournisseur et (s'il est gardé) auto-gad dimensionnés pour le même pic.
Cette page est le guide livré avec fahmek, tenu à jour à chaque version.