
Une supervision LLM utile commence par une requête qui n'a pas abouti. Relevez son heure, son modèle, son statut et son identifiant, puis suivez les indices entre le routeur, l'agent sélectionné et le moteur d'inférence. Un contrôle de santé positif ou une faible latence moyenne n'explique pas une erreur individuelle.
Hivenet Router, présenté en anglais, dirige les requêtes vers les moteurs d'inférence que vous exploitez. Ce guide propose une démarche d'investigation pour un déploiement existant : agents indisponibles, capacité saturée, rejets liés aux quotas et réponses lentes. Il n'évalue pas la qualité ni l'exactitude des réponses générées.
Périmètre de validation : la documentation et les parties pertinentes du code ont été vérifiées le 11 novembre 2026, au commit Router f97fec83. L'exemple de recherche hors ligne dans un fichier d'audit a passé 26 vérifications sur Windows avec CPython 3.12.14 et des données fictives. Aucun Router, moteur d'inférence, Prometheus, Loki ou collecteur de traces n'a été exécuté pour ce guide. Les étapes opérationnelles et expressions PromQL restent à valider dans votre déploiement.
Conservez le statut initial, le corps d'erreur structuré et les en-têtes de réponse avant de réessayer. Notez aussi la durée observée par le client, l'activation éventuelle du streaming, le modèle ou l'alias demandé et la période concernée avec son décalage UTC. Évitez d'inclure des prompts, des en-têtes d'autorisation ou des identifiants secrets dans une note d'incident partagée.
Dans la version vérifiée, Router génère l'identifiant renvoyé dans l'en-tête de réponse X-Request-ID. Un UUID valide fourni par le client est conservé séparément dans client_request_id. Lisez l'en-tête de réponse plutôt que de supposer que l'identifiant envoyé est celui de Router. Un identifiant client répété peut relier plusieurs tentatives, mais chacune possède son propre identifiant Router. La référence des journaux d'audit, en anglais, décrit ces champs.
Utilisez un identifiant de trace correspondant seulement si le traçage était actif et les spans collectés. Un champ de trace vide ne signifie pas que la requête n'a jamais été exécutée. De même, l'absence d'agent sélectionné peut correspondre à un rejet avant l'envoi vers un moteur.
Vérifiez les points suivants dans l'ordre. Ce sont des observations en lecture seule : ne remettez pas les compteurs à zéro, ne modifiez pas les quotas et ne redémarrez pas les agents avant de conserver les éléments de l'incident.
GET /health confirme que le processus HTTP de Router répond. Le contrôle peut réussir alors qu'aucun moteur ne peut servir le modèle demandé.GET /admin/health résume les agents connectés, leur état et le canal global de requêtes. Il ne prouve pas que le modèle demandé dispose de capacité libre.GET /admin/routing-table expose l'état actuel des agents. Examinez le modèle, les métadonnées, la santé du moteur, les requêtes actives et la capacité déclarée, puis tenez compte de la politique de routage de la requête.Utilisez une connexion administrateur autorisée pour ces points d'accès et suivez la référence de l'API d'administration, en anglais. Un agent sain peut rester inéligible parce qu'il sert un autre modèle ou ne satisfait pas une condition de routage. Un état observé après le rétablissement ne prouve pas non plus ce qui était disponible pendant l'erreur ; comparez-le aux journaux et métriques historiques.
Le statut HTTP seul est trop général. Les champs code et source du corps d'erreur Router orientent l'investigation. La référence des erreurs, en anglais, fournit la liste complète.
unauthorized ou model_forbidden : vérifiez l'identifiant présenté et l'accès au modèle. Ajouter de la capacité d'inférence ne résout pas un problème d'autorisation.rate_limit_exceeded : examinez les quotas de la clé et la limite d'admission applicable. L'absence d'une déclaration stricte de quota par modèle peut aussi produire ce code ; il ne signale pas toujours un pic de trafic.no_agents_available : vérifiez l'éligibilité du modèle, la connexion des agents, la santé des moteurs et les exclusions de la politique.no_capacity : des agents éligibles existent, mais leurs emplacements déclarés sont occupés. Examinez la concurrence, la file d'attente de capacité et la durée d'occupation des emplacements.request_timeout : examinez la chronologie pour localiser le délai. L'expiration de l'échéance ne désigne pas à elle seule le composant lent.Conservez un code inconnu plutôt que de le faire entrer dans l'une de ces catégories. Les erreurs des moteurs, les contrôles d'admission et le réveil des modèles possèdent d'autres codes. Vérifiez source avant d'attribuer une réponse à Router lui-même.
Pour une petite investigation locale, ce script de la bibliothèque standard sélectionne un identifiant Router exact et n'affiche qu'une liste explicite de champs. Enregistrez-le dans find_request.py. Il lit un fichier JSONL UTF-8 local sans le modifier. Il ne télécharge pas de journaux et ne contacte pas Router.
"""Read a local audit JSONL file; print selected fields for one request ID."""
import argparse
import json
import sys
FIELDS = ("request_id", "client_request_id", "model", "status_code",
"error_code", "latency_ms", "agent_id", "trace_id")
def find_records(lines, request_id):
matches = []
malformed = 0
for line in lines:
if not line.strip():
continue
try:
record = json.loads(line)
except json.JSONDecodeError:
malformed += 1
continue
if not isinstance(record, dict):
malformed += 1
continue
if (record.get("log_type") == "audit"
and record.get("request_id") == request_id):
matches.append({key: record.get(key) for key in FIELDS})
return matches, malformed
def main():
parser = argparse.ArgumentParser()
parser.add_argument("path")
parser.add_argument("request_id")
args = parser.parse_args()
with open(args.path, encoding="utf-8") as stream:
records, malformed = find_records(stream, args.request_id)
print(json.dumps(records, indent=2, ensure_ascii=False))
if malformed:
print(f"Skipped {malformed} malformed records", file=sys.stderr)
return 2 if malformed else (0 if records else 1)
if __name__ == "__main__":
raise SystemExit(main())
Pour reproduire l'exercice, enregistrez ces deux enregistrements fictifs dans example-audit.jsonl. Ils illustrent un échec suivi d'une tentative réussie avec le même identifiant client. Les durées, identifiants et modèle sont inventés ; il ne s'agit ni de trafic Router capturé ni d'un benchmark.
{"log_type":"audit","request_id":"019a7250-0000-7000-8000-000000000001","client_request_id":"11111111-1111-4111-8111-111111111111","model":"demo-model","status_code":503,"error_code":"no_capacity","latency_ms":1200,"agent_id":"","trace_id":""}
{"log_type":"audit","request_id":"019a7250-0000-7000-8000-000000000002","client_request_id":"11111111-1111-4111-8111-111111111111","model":"demo-model","status_code":200,"error_code":"","latency_ms":800,"agent_id":"demo-agent","trace_id":""}
Lancez la recherche de la première tentative :
python find_request.py example-audit.jsonl 019a7250-0000-7000-8000-000000000001
Voici la sortie enregistrée du script hors ligne :
[
{
"request_id": "019a7250-0000-7000-8000-000000000001",
"client_request_id": "11111111-1111-4111-8111-111111111111",
"model": "demo-model",
"status_code": 503,
"error_code": "no_capacity",
"latency_ms": 1200,
"agent_id": "",
"trace_id": ""
}
]
L'exemple isole la tentative en 503 sans la fusionner avec la réponse 200 suivante. Un champ agent vide ne désigne pas un moteur défaillant. Le code no_capacity oriente vers la capacité, mais cet enregistrement seul n'explique pas pourquoi les emplacements sont restés occupés.
Le code de sortie 0 indique une correspondance dans un fichier valide, 1 l'absence de correspondance et 2 des enregistrements mal formés ignorés. Les champs absents restent à null et les doublons restent visibles. Pour de gros journaux, utilisez un extrait limité à l'incident : le script parcourt le fichier et conserve les lignes correspondantes. Pour un historique centralisé, utilisez Loki ou votre outil habituel avec le même identifiant. La sélection des champs limite les sorties accidentelles, mais ne remplace pas les contrôles d'accès et ne masque pas les données sensibles à l'intérieur d'un champ sélectionné.
Avant d'interpréter un graphique, vérifiez que la collecte fonctionne. Dans les exemples suivants, job="hivenet-router" désigne le label du job de collecte Prometheus ; adaptez-le si votre configuration utilise un autre nom. Évaluez chaque expression séparément.
up{job="hivenet-router"}
Une valeur nulle signifie que Prometheus n'a pas pu collecter les données de cette cible. Une série absente peut indiquer une autre cible ou d'autres labels. Aucun de ces cas ne mesure une charge sans erreur. Router expose /metrics sur un port distinct, 2112 par défaut, sans authentification intégrée. Limitez son accès au réseau privé prévu ou aux contrôles d'accès appropriés. Consultez la référence des métriques Router, en anglais.
Examinez ensuite les requêtes que Router n'a pas pu transmettre, par instance Router et par modèle :
sum by (instance, model) (
rate(hivenet_routing_requests_failed_total{job="hivenet-router"}[5m])
)
Il s'agit d'un signal d'échecs de routage par seconde, pas du pourcentage total d'erreurs de l'application. Les erreurs d'authentification, réponses des moteurs et flux interrompus demandent leurs propres éléments de preuve. La référence des fonctions Prometheus, en anglais, explique l'utilisation de rate sur une fenêtre temporelle pour les compteurs. Appliquez rate avant la somme pour traiter les remises à zéro par série.
Pour les requêtes qui attendent de la capacité déclarée chez un agent, examinez la file Router par modèle :
hivenet_queue_depth{job="hivenet-router"} > 0
Comparez-la à hivenet_agent_capacity_utilization et à hivenet_agent_engine_waiting_requests côté moteur. L'attente de capacité Router et la file interne du moteur sont deux étapes distinctes. Augmenter une limite déclarée sans examiner le moteur peut déplacer l'attente plutôt que la supprimer.
Les métriques dépendent du moteur et peuvent rester absentes jusqu'à leur transmission. Un ancien état matériel ou moteur peut aussi subsister après un problème de collecte. Vérifiez la fraîcheur des signaux de présence et les journaux de collecte avant d'interpréter une valeur inchangée comme une performance stable. Le guide des métriques moteur, en anglais, précise les possibilités selon le backend.
Utilisez la durée côté client pour l'attente de l'utilisateur, la latence d'audit pour le traitement achevé dans Router et les traces disponibles pour séparer les étapes. Le SRTT est un signal lissé entre Router et l'agent ou le moteur, pas le temps aller-retour du client ni la latence du premier token. Pour les définitions et la conception des benchmarks, consultez notre guide des métriques d'inférence LLM, en anglais.
En streaming, conservez la preuve que la réponse s'est terminée. Un statut HTTP 200 peut précéder un flux incomplet. Examinez l'événement terminal et les erreurs de l'application ; la version Router vérifiée expose aussi hivenet_stream_truncated_total. Un faible nombre d'erreurs HTTP ne prouve pas que les utilisateurs ont reçu des réponses complètes.
Après correction de la cause identifiée, reproduisez la même forme de requête dans un test contrôlé. Notez le nouvel identifiant, le résultat, l'état pertinent des files et la durée de bout en bout. Si la correction consiste à ajouter de la capacité, vérifiez que l'attente et les erreurs diminuent sans créer de pression mémoire ni ralentir la file du moteur.
Conservez une courte fiche d'incident : période affectée, modèles, codes observés, journaux justificatifs, modification et contrôle de rétablissement. Fixez les seuils d'alerte à partir du comportement habituel du service et des besoins des utilisateurs. Protégez les métadonnées d'audit même si l'enregistrement dédié exclut les corps des prompts et des réponses ; d'autres composants peuvent journaliser d'autres données. Hivenet Router fournit des éléments opérationnels, dont l'interprétation exige de rapprocher les bons signaux.
Choisissez une charge de travail d’IA, de calcul ou de stockage à tester sur Hivenet, ou échangez avec notre équipe sur sa mise en production.