fahmek
Dans ce guide

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 tour

Les appels par tour dépendent de la façon dont LiteLLM appelle fahmek :

IntégrationAppels par tourComptés par la limite de débit
Interface web (Generic Guardrail API), réponse sans streaming2 (masquage + restitution)1 par tour
Interface web, réponse en streaming1 + un par tour de streaming (LiteLLM décide combien)1 par tour
Kit d'intégrationun /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.

ConfigurationTientLatence ajoutée par fahmek (p99)
Détection seule, un cœur, en mémoire9 000 messages de chat/s ; 1 000 messages de 4 000 caractères/s0,2 ms ; 1,7 ms
Une instance, 2 workers, kit, tours d'un message200 tours/s (370 appels/s)20 ms
Une instance, 2 workers, kit, conversations de 10 messages35 tours/s (495 appels/s)135 ms
Une instance, 4 workers, kit, conversations de 10 messages45 tours/s (640 appels/s)105 ms
Une instance, 2 workers, interface web, 10 messages, sans streaming200 tours/s (400 appels/s)24 ms
Une instance, 2 workers, interface web, 10 messages, 5 tours de streaming50 tours/s (300 appels/s)33 ms
Passerelle de démonstration entière (1 processus LiteLLM, kit, auto-gad, modèle de test), 4 messages15 tours/sLiteLLM à 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égrationAppels fahmek/sWorkersExemple de répartition
Interface web, sans streaming66772 instances × 4 workers
Interface web, streaming en 5 tours environ2 000205 instances × 4 workers
Kit, conversations de 10 messages (14 appels par tour)4 7004712 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églagePar défautProduction
FAHMEK_WORKERS2un par cœur (1 à 32)
FAHMEK_INSPECT_RPS50 par workerenviron 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_BURST100 par workerdeux fois FAHMEK_INSPECT_RPS
Instances fahmek1au moins 2, derrière un répartiteur de charge, même image, licence, FAHMEK_API_KEY, politiques et FAHMEK_REDIS_URL
Redisconteneur de démonstrationune adresse partagée par toutes les instances, save "" et appendonly no, redémarrage automatique
FAHMEK_TRACE00
unreachable fallback de LiteLLM (interface web)—fail_closed avec bank-pilot ou bank-ma-strict
store prompts in spend logs de LiteLLMdé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 (avec X-Fahmek-Key) vérifie aussi que Redis répond sans persistance ; il a besoin de CONFIG 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 politique fail_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 /metrics sur 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
OptionSens
--mode kit / ui / gatewaycomment LiteLLM appelle fahmek (section 1) ; gateway envoie des chats entiers à LiteLLM (définir LITELLM_KEY)
--ratestours par seconde à essayer, l'un après l'autre
--durationsecondes par débit (60 ou plus pour décider)
--historymessages par conversation : la longueur habituelle chez le client
--stream-roundsinterface web : appels de réponse par réponse (1 = sans streaming)
--max-p99-ms, --min-okcode de sortie 1 quand un débit manque ces objectifs
--json FICHIERenregistre 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 share vaut 1.0 et le p99 respecte l'objectif. Prévoir le pic à deux tiers de ce débit au plus.
  • rate_limited : relever FAHMEK_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_RPS et FAHMEK_INSPECT_BURST réglés.
  • Redis sans persistance ; /readyz ré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.