Keyboard shortcuts

Press ← or → to navigate between chapters

Press ? to show this help

Press Esc to hide this help

Cycle de vie du routage des fournisseurs

Le routage des fournisseurs commence après que ZeroClaw a sélectionné l’agent auquel revient un tour. Il couvre la sélection du profil du fournisseur et du modèle, les réessais et le basculement, la récupération du flux, ainsi que l’attribution indiquant quel backend a traité la requête. L’acheminement des canaux vers les agents relève d’un cycle de vie distinct ; consultez Cycle de vie de l’exécution des canaux.

Utilisez cette page lorsqu’une modification concerne model_routes, la sélection du modèle de session ou en cours de tour, le basculement entre fournisseurs, la classification des nouvelles tentatives, les périodes de refroidissement liées à la limitation du débit, la fin du flux, la relecture après un échec du flux ou l’attribution du fournisseur demandé par rapport au fournisseur effectivement utilisé.

Carte des propriétés

InquiétudePropriétaire actuelContrat
Profils des fournisseurs et graphe de secourszeroclaw-config schéma et validation du fournisseurUne valeur <family>.<alias> séparée par un point identifie le point de terminaison, les informations d’identification, le modèle principal facultatif, les capacités et les déclarations de repli ordonnées d’un profil.
Construction du fournisseurzeroclaw-providers fonctions de fabriqueInstanciez chaque profil avec ses propres paramètres, aplatissez les entrées de secours configurées dans l’ordre et concevez le routage en fonction de la fiabilité.
Sélection basée sur des indicesRouterModelProviderRésolvez hint:<name> en une cible de fournisseur configurée et un modèle de routage. La cible principale est épinglée au modèle actif/par défaut. Une cible non principale est épinglée lorsque son profil configure un modèle ; sinon, son entrée fiable reste non épinglée et reçoit le modèle de routage.
Sélection basée sur l’effortProfil d’exécution résolu ainsi que points d’entrée des tours agent/canalClasser de manière déterministe un tour activé, sélectionner l’une des deux indications de routage existantes, préserver les choix de modèle explicites et limiter les tentatives de fournisseur éligibles pour le tour.
Nouvelle tentative et basculementReliableModelProviderClasser les échecs, réessayer avec une temporisation plafonnée, respecter les délais de récupération liés à la limitation du débit et parcourir les entrées matérialisées.
Arrêt du flux du fournisseurFournisseurs concrets et zeroclaw-providers/src/stream_guard.rsTraduisez la sémantique d’achèvement de chaque protocole de fournisseur en StreamEvent::Final ou en une erreur de troncature.
Rejeu du flux et validation d’une sortie partiellezeroclaw-runtime/src/agent/turn/provider_call.rs and stream_consume.rsRéessayez un flux ayant échoué en mode non streaming uniquement avant que la sortie d’événements immuable ne soit validée. Ne rejouez jamais une réponse annulée ou visiblement partielle.
Attribution par appelProviderDispatchOuvrez des portées d’attribution autour de l’appel au fournisseur sélectionné pour chaque tentative.
Enregistrement de récupération réussieReliableModelProviderExposer un unique enregistrement local à la tâche, comparant le modèle demandé au modèle servi, après une récupération réussie. Dans les chemins de tour instrumentés par événements, le registre usage_by_provider enregistre les tentatives facturables via TurnEvent::Usage, avec l’identité du service qui a répondu et un indicateur accepted.
Notifications de récupération destinées aux utilisateursConsommateurs de l’environnement d’exécution et des canauxAffichez l’enregistrement de récupération réussie sur leur propre surface de sortie. Notez que les règles ne sont pas uniformes selon les consommateurs.

Construction et sélection

L’environnement d’exécution démarre avec une référence active vers le fournisseur et un modèle provenant de l’agent sélectionné, d’une surcharge de session ou d’un model_switch effectué pendant le tour. La construction du fournisseur combine ensuite deux wrappers :

  1. La fabrique construit un ReliableModelProvider pour le profil de fournisseur actif. Un modèle principal effectif provient d’une surcharge de construction explicite ou du model configuré dans le profil. Lorsqu’il en existe un, celui-ci et les fallback_models du profil deviennent des entrées épinglées. En l’absence d’un tel modèle, le profil fournit une entrée non épinglée et ses fallback_models ne sont pas matérialisés. Les profils fallback référencés récursivement sont néanmoins parcourus. Chaque profil référencé conserve ses propres identifiants, point de terminaison, en-têtes, modèle et remplacements de capacités.
  2. Lorsque model_routes est configuré, la fabrique construit un fournisseur fiable distinct pour la route principale et pour chaque cible de route unique, puis les encapsule dans RouterModelProvider.
  3. Un hint:<name> reconnu sélectionne sa cible configurée avant que l’appel ne soit soumis à la politique de fiabilité de cette cible. Une valeur de modèle standard utilise la route par défaut. Un hint inconnu consigne un avertissement, reste dans le domaine de fiabilité par défaut et conserve la valeur littérale hint:<name> comme modèle demandé. Une entrée par défaut épinglée continue de servir sa valeur épinglée ; une entrée par défaut non épinglée transmet la valeur littérale, que le fournisseur peut rejeter avant la poursuite du repli normal ou de la gestion des erreurs.

Il existe actuellement deux contraintes de construction :

  • L’épinglage des routes est conditionnel. La cible principale est épinglée sur le modèle actif/par défaut transmis lors de la construction du fournisseur, y compris lorsqu’un indice reconnu renvoie vers le profil principal actif ; la valeur model_routes[].model de cet indice ne remplace pas l’épinglage principal. Une cible non principale avec un modèle de profil configuré est épinglée sur ce modèle ; son modèle de route ne remplace donc pas non plus le modèle du profil. Une cible non principale sans modèle configuré est valide et reste non épinglée ; le modèle de route est transmis à ce fournisseur, et les fallback_models de ce profil ne sont pas matérialisés, même si les profils de secours référencés sont tout de même parcourus. Maintenez chaque modèle de route aligné sur l’épinglage de la cible lorsqu’il existe, et tenez compte du comportement non épinglé lorsque le profil cible omet model.
  • Les cibles de route sont dédupliquées par model_provider. Si une route fournit api_key, les premières informations d’identification de route correspondantes sont prioritaires lors de la construction de la cible partagée. Privilégiez les informations d’identification du profil du fournisseur lorsque plusieurs indications partagent une même cible.

Cet ordre est important : le routage choisit un domaine de fiabilité ; il ne contourne pas la fiabilité. Un service de routage externe tel qu’OpenRouter peut toujours effectuer une sélection côté serveur derrière un seul profil ZeroClaw, mais il est facultatif et ne remplace pas les contrats natifs de routage et de basculement de ZeroClaw.

Lorsqu’un profil d’exécution résolu comporte effort_routing, le point d’entrée du tour exécute l’estimateur de complexité déterministe avant l’exécution par le fournisseur. Les tours simples et standard sélectionnent l’indication locale configurée. Un tour complexe ne sélectionne l’indication cloud que sous la politique d’escalade explicite auto. Aucun appel de modèle n’est effectué pour la classification, et la télémétrie de décision exclut le texte des prompts. Les sélections explicites de l’opérateur, via la CLI, la session ou le canal, désactivent cette étape automatique à leur portée applicable ; /model auto sur le canal la rétablit. Un model_switch émis par le modèle modifie le fournisseur/modèle actif sans changer le caractère automatique ou détenu par l’opérateur de la route. Le routage automatique de l’effort reste donc actif après un model_switch émis par le modèle, sauf si une substitution de l’opérateur l’a déjà désactivé.

La frontière du fournisseur applique la décision autant qu’elle sélectionne un modèle. Les chemins structurés de l’agent et des canaux installent une liste d’autorisation locale à la tâche, que chaque boucle de candidats Reliable vérifie, y compris les chemins sans streaming, avec streaming et de récupération de flux. Les points d’entrée directs de la CLI et du daemon construisent leur routeur de tours uniquement à partir des cibles de routes autorisées. Une décision locale ne peut donc pas passer vers le fournisseur cloud configuré via un repli de disponibilité. Une décision cloud peut utiliser les références de fournisseurs locaux et cloud configurées.

Le schéma et les exemples destinés aux opérateurs se trouvent dans Configuration du fournisseur et Routage. Conservez la syntaxe des champs à cet endroit plutôt que de la dupliquer dans les documents d’architecture.

Ordre des tentatives sans streaming

Pour un alias de production, la fabrique aplatit le graphe configuré en profondeur d’abord. L’ordre effectif est :

  1. Le modèle principal effectif du profil, ou une entrée non épinglée lorsqu’aucun modèle principal effectif n’existe.
  2. Les fallback_models de ce profil, dans l’ordre, uniquement lorsqu’un modèle principal effectif existe.
  3. Chaque profil fallback, dans l’ordre, y compris l’entrée principale ou non épinglée de ce profil, les modèles de secours éligibles et les profils de secours imbriqués.

Pour chaque entrée matérialisée, ReliableModelProvider tente la requête jusqu’à provider_retries + 1 fois. Une erreur réessayable reste normalement sur l’entrée et applique un délai d’attente borné. Une limitation de débit réessayable place ce profil de fournisseur en période de refroidissement en mémoire et passe à l’entrée suivante lorsqu’il en existe une autre. La plupart des erreurs non réessayables passent immédiatement à l’entrée suivante ; les erreurs de fenêtre de contexte font l’objet d’un traitement spécifique à la méthode et peuvent entraîner un retour anticipé pour permettre la récupération à l’exécution. Une réponse réussie met fin au parcours ; si chaque entrée échoue, l’enveloppe renvoie une erreur agrégée contenant les échecs des tentatives.

L’ordre de matérialisation et l’ordre d’exécution effectif peuvent différer après une limitation de débit. Les entrées primary et fallback_models d’un profil partagent une même clé de délai de récupération, de sorte qu’un 429 sur le modèle primary peut entraîner l’omission des autres modèles du même profil tant que le délai de récupération est actif.

Le pool global reliability.api_keys ne constitue pas actuellement un mécanisme de basculement fonctionnel. Le wrapper sélectionne et journalise une clé alternative après une limitation de débit réessayable, mais le trait ModelProvider ne peut pas appliquer cette clé au fournisseur construit ; la nouvelle tentative utilise donc toujours l’identifiant d’origine. Le ticket #9190 suit la correction. Utilisez des profils de secours distincts ou un service de routage externe lorsqu’un basculement au niveau des identifiants est nécessaire.

Les complétions vides font l’objet du même traitement de nouvelle tentative limité, au lieu de devenir immédiatement un message d’assistant vide.

Les déclarations de secours non valides sont traitées à deux niveaux distincts. Les références orphelines, les cycles, les arêtes au-delà de la profondeur autorisée, les identifiants de modèle vides et les modèles principaux dupliqués sont signalés et élagués, comme décrit dans Provider configuration. Un profil de secours qui se résout, mais ne peut pas fournir les informations d’identification requises ou ne peut pas être construit, entraîne l’échec de l’initialisation du fournisseur au lieu de modifier silencieusement la route.

Frontière entre la diffusion en continu et la relecture

Le streaming présente délibérément un contrat de nouvelle tentative plus limité que les appels sans streaming :

  1. ReliableModelProvider choisit la première entrée dans l’ordre qui prend en charge les capacités de flux demandées et n’est pas en période de refroidissement.
  2. Il ouvre ce flux une seule fois. Il ne change pas d’entrée une fois le flux démarré.
  3. Le parseur concret du fournisseur traduit la sémantique d’achèvement de son protocole en Final ou en erreur. La plupart des parseurs SSE protégés exigent le signal d’achèvement configuré. Anthropic considère actuellement aussi qu’un EOF après un message_delta.stop_reason non vide signale la fin, même si message_stop n’a pas été observé ; la PR #9447 propose d’exiger message_stop, mais cette modification n’a pas encore été fusionnée.
  4. L’environnement d’exécution consomme et nettoie les événements du flux. Si le flux échoue avant que la sortie immuable des événements soit visible, l’environnement d’exécution réessaie l’appel complet via le chemin sans flux, qui repasse par l’intégralité du parcours de fiabilité.
  5. Si du texte, du raisonnement ou des événements d’outils préexécutés ont déjà atteint un réceptacle d’événements immuable, l’interruption devient StreamInterruptedAfterOutput. L’environnement d’exécution ne rejoue pas la requête. Seul le texte déjà transmis au consommateur est conservé comme texte partiel de l’assistant.
  6. Une annulation ne déclenche jamais de nouvelle tentative automatique auprès du fournisseur. Une annulation avant la transmission de texte interrompt le tour. Une annulation après la transmission de texte conserve ce texte partiel de l’assistant ; une sortie contenant uniquement le raisonnement ou issue d’un outil préexécuté ne devient pas, à elle seule, un texte partiel de l’assistant conservé lors de l’annulation.

Les sinks de mise à jour des brouillons sont mutables. Un mécanisme de secours avant le commit peut remplacer un brouillon sans dupliquer une sortie immuable, tandis que les sinks d’événements définissent la limite de non-rejeu.

Un flux qui se termine sans texte final ni appel d’outil est une réponse sémantiquement vide, et non une réponse valide. Lorsque le runtime marque ce résultat comme sans risque de rejeu et que provider_retries est différent de zéro, Reliable autorise un appel de récupération sans diffusion en continu (non-streaming) unique vers le couple fournisseur/modèle exact ayant produit le flux vide. Cette autorisation n’est consommée qu’une seule fois ; si la récupération échoue, on passe aux candidats configurés restants, chacun avec son budget de nouvelles tentatives normal. Avec zéro nouvelle tentative, l’entrée du flux en échec reste ignorée, sauf si elle est le seul candidat, auquel cas une tentative de récupération sans diffusion en continu unique de la même entrée est autorisée (récupération, et non rejeu). Cette autorisation pour candidat unique est également consommée une seule fois ; lorsque les deux autorisations s’appliquent à la même entrée, elles fusionnent en une seule tentative. Le raisonnement déjà affiché reste visible une fois, mais ne compte pas comme réponse finale. Cette exception n’autorise pas de rejeu après une annulation, une sortie visible interrompue, ni un travail d’outil exécuté par le fournisseur.

Cette division maintient la récupération du transport dans l’environnement d’exécution, le framing propre au fournisseur dans l’adaptateur et la stratégie de nouvelle tentative/de repli dans le wrapper de fiabilité. Une implémentation de fournisseur ne doit pas inventer une seconde stratégie de rejeu au niveau du tour.

Attribution et lacunes connues

ProviderDispatch ouvre le contexte d’attribution autour de chaque appel au fournisseur. ReliableModelProvider enregistre un repli demandé par rapport au repli servi uniquement après la réussite d’un appel sans diffusion en continu ou l’achèvement sans erreur d’un flux de repli. Le code d’exécution et du canal peut exploiter cet enregistrement local à la tâche pour informer un utilisateur qu’une reprise a eu lieu.

L’enregistrement n’est qu’un indice de récupération famille/modèle. Les entrées de production utilisent la famille de fournisseur comme display_name, si bien que l’enregistrement peut perdre l’alias de profil pointé. Un repli entre alias de même famille et de même modèle peut donc être indiscernable de la route demandée. Les réponses d’exécution ajoutent une notice de repli modèle/fournisseur lorsque l’enregistrement diffère. La livraison par canal conserve le pied de page inter-familles et utilise channels.model_fallback_notice pour le repli ordinaire vers un modèle différent de la même famille. Les modes globaux sont off par défaut, redacted sans identifiants de routage, et detailed avec les identifiants fournisseur/modèle demandés et servis. Voir Notices de repli de modèle pour la configuration et les compromis de confidentialité selon l’audience.

L’orchestrateur de canaux résout cette politique à partir de la Config active au moment de l’envoi sortant, plutôt que de la copier dans un handle de canal de longue durée. Les chemins existants de publication et de rechargement de la configuration appliquent les modifications à un démon en cours d’exécution. L’avis est ajouté au contenu sortant après que la réponse de l’assistant est entrée dans l’historique ; il ne devient donc pas une partie de l’historique des prompts et ne modifie pas l’enregistrement de la tentative de service. Les avis de garde-fou conservent leur comportement distinct.

Dans les chemins de tour instrumentés par un event sink, la comptabilité par tentative est tenue dans le registre usage_by_provider, et non dans cet enregistrement. Chaque tentative facturable de ces chemins, acceptée ou rejetée, émet TurnEvent::Usage avec son provider_ref/model de service et un indicateur accepted. La trame done de la passerelle dérive cost_usd comme la somme du registre. Les requêtes ponctuelles autonomes sans event sink, y compris la réflexion de compétence (skill reflection), peuvent faire l’objet d’un suivi des coûts sans entrer dans ce registre. Ceci résout le problème #9470 (attribution incorrecte de l’usage et des coûts pour les tentatives rejetées et notifications de repli obsolètes après une reprise de flux). Ne déduisez pas la précision du coût par tentative de la notification de repli finale ou de la seule identité du fournisseur demandé ; consultez le registre.

Le repli en cas de refus de contenu et de garde-fou est également un contrat distinct, proposé, de la fiabilité du transport. Tracker #9293 coordonne ce travail entre les surfaces provider, configuration, channel, gateway et web. Le travail sur l’identité de service et le registre par tentative dans PR #8966 comble l’écart d’attribution des coûts de Reliable ; le repli en cas de refus de contenu reste suivi séparément.

Liste de contrôle des modifications

Pour les modifications du routage des fournisseurs, répondez à ces questions avant la validation du réviseur :

  • Le changement affecte-t-il la distribution des agents, la sélection des indices, le mécanisme de secours lié à la fiabilité ou un routeur externe ? Nommez exactement un responsable pour chaque décision.
  • Si une indication cible un profil de fournisseur, sa gestion du modèle est-elle conforme à la construction de la cible ? Comparez la cible principale avec la valeur épinglée active/par défaut. Pour une cible non principale avec un modèle configuré, comparez le modèle de routage à cette valeur épinglée. Si le profil omet model, confirmez que le modèle de routage doit être transmis tel quel et que les fallback_models du profil ne seront pas matérialisés.
  • Chaque profil de repli conserve-t-il son propre point de terminaison, ses identifiants, son modèle, ses en-têtes et ses remplacements de capacités ?
  • Qu’est-ce qui peut être réessayé, qu’est-ce qui avance immédiatement et quelle erreur est renvoyée après épuisement des tentatives ?
  • Une requête peut-elle être rejouée après toute sortie déjà observée par un utilisateur ou un consommateur immuable ?
  • Quel signal exact chaque parseur de fournisseur accepte-t-il comme marqueur d’achèvement ? Une fin de fichier avant ce signal est-elle traitée comme une troncature et entraîne-t-elle un échec ?
  • Les identités demandées et effectivement servies du fournisseur et du modèle sont-elles conservées séparément ?
  • L’utilisation, le coût, les journaux et les avis destinés aux utilisateurs sont-ils dérivés de la même tentative de traitement, ou la limitation fait-elle l’objet d’un suivi explicite ?
  • Les tests en streaming et sans streaming couvrent-ils la même limite d’échec, là où le comportement est censé être le même ?

Pointeurs source

  • Trait du fournisseur et événements de flux : crates/zeroclaw-api/src/model_provider.rs
  • Sélection de la route : crates/zeroclaw-providers/src/router.rs
  • Épinglage du modèle du profil : crates/zeroclaw-providers/src/model_pin.rs
  • Réessai, délai d’attente, solution de secours et notifications de solution de secours : crates/zeroclaw-providers/src/reliable.rs
  • Construction des fournisseurs et matérialisation du graphe de repli : crates/zeroclaw-providers/src/lib.rs, crates/zeroclaw-providers/src/factory.rs
  • Garde de fin de flux du fournisseur : crates/zeroclaw-providers/src/stream_guard.rs
  • Relecture du flux d’exécution et gestion des sorties partielles : crates/zeroclaw-runtime/src/agent/turn/provider_call.rs, crates/zeroclaw-runtime/src/agent/turn/stream_consume.rs
  • Guides de l’opérateur : Configuration du fournisseur, Routage, Streaming