Documentation

Hooks de prédiction

Les hooks te permettent d’observer ou de façonner chaque décision de Laya, sans le forker.

Ils sont la couture d’extension pour ce dont chaque déploiement réel a besoin : journal d’audit, masquage des PII avant l’inférence, mise en cache, métriques, gating par confiance, surcharges de routage, et transmission d’une décision à un service externe. Ils sont opt-in : sans hook configuré, le comportement de Agent, Router et ONNXAgent est inchangé.

Ils ne servent pas qu’aux appels directs. Chaque runnable LangChain et LangGraph prend les mêmes cinq arguments par appel, donc un hook peut être attaché à un nœud d’un graphe plutôt qu’à tout l’agent.

Ce dossier est la référence complète. Commence ici, puis plonge dans la page dont tu as besoin :

page ce qu’elle contient
Référence de l’API chaque classe, champ, paramètre et valeur par défaut
Cycle de vie exactement quand chaque hook tourne, avec des organigrammes
Erreurs hooks_raise, on_error, enchaînement des exceptions, matrice de défaillance
Motifs et anti-motifs quoi faire, quoi éviter, et pourquoi
Exemples des recettes à copier-coller pour chaque cas d’usage
Traçage run_id, corrélation des spans, OpenTelemetry

Démarrage rapide

import laya

def log(ctx):
    print(ctx.model, ctx.results[0]["answers"], ctx.elapsed_ms)

agent = laya.load("convaiinnovations/laya", on_predict_end=log)
agent.system_one("I was charged twice.", {"urgent": {"type": "noul", "instructions": "Urgent?"}})

Un objet peut implémenter n’importe quel sous-ensemble des événements du cycle de vie :

class Audit:
    def on_predict_start(self, ctx):
        print("start", ctx.run_id)

    def on_predict_end(self, ctx):
        print("end", ctx.run_id, ctx.usage, ctx.elapsed_ms)

    def on_error(self, ctx):
        print("failed", ctx.run_id, ctx.error)

laya.load("convaiinnovations/laya", hooks=[Audit()])

Les hooks peuvent aussi être ajoutés plus tard ou limités à un bloc :

agent.add_hook(tracer)               # attach at runtime
with agent.hooks_installed(debug):   # installed for the block, removed on exit
    agent.system_one(state, questions)

Voir l’enregistrement à l’exécution. Pour un hook qui doit s’appliquer partout sans être passé à chaque appel, enregistre-le une fois avec les valeurs par défaut pour tout le processus :

from laya import hooks

hooks.set_default_hooks(hooks=[Tracer()])

Le modèle mental

Il y a trois idées.

  1. Un hook est un appelable ou un objet. Une fonction simple est pratique pour un événement ; un objet est pratique pour plusieurs. Les deux se passent à hooks= / on_predict_start= / on_predict_end=.

  2. Tous les hooks d’un appel partagent un même PredictContext mutable. Il porte les états, les questions, les résultats, la décision de routage, le nom du modèle, l’usage, le timing et toute erreur. Un appel peut porter plusieurs états à la fois (predict_batch), donc un hook qui veut couvrir chaque décision doit itérer ctx.states et ctx.results ; ctx.usage et ctx.elapsed_ms sont des totaux pour l’appel. Comme le contexte est mutable, un hook peut façonner l’appel, pas seulement l’observer : masquer l’état, réécrire les questions, remplacer le résultat, ou sauter l’inférence avec une réponse en cache.

  3. Il y a deux portées. Les hooks Agent enveloppent une passe avant ; les hooks Router enveloppent le routage plus l’inférence et peuvent aussi voir le cycle de vie du modèle (on_route, on_load, on_evict). Cela reflète le découpage « hooks de run » vs « hooks d’agent » d’autres frameworks d’agents.

                             Router.predict(state, questions)
   ┌──────────────────────────────────────────────────────────────────────────┐
   │  route()                                                                 │
   │    ├─ detect language / workflow                                         │
   │    └─ on_route          ctx.decision  (a hook may replace it)            │
   │                                                                          │
   │  load(decision.model)                                                    │
   │    ├─ build checkpoint on first use ──► on_load    ctx.model, ctx.agent  │
   │    └─ evict LRU checkpoint ───────────► on_evict   ctx.model             │
   │                                                                          │
   │  on_predict_start       ctx.states, ctx.questions, ctx.decision          │
   │    │                                                                     │
   │    ├── ctx.skip(results)? ──► skip the forward pass                      │
   │    │                                                                     │
   │    └── Agent.system_one(...)  ──►  Agent-level hooks run here            │
   │           on_predict_start  ─►  forward  ─►  on_predict_end              │
   │                                                                          │
   │  result["routing"] = decision                                            │
   │  on_predict_end         ctx.results, ctx.usage, ctx.elapsed_ms           │
   └──────────────────────────────────────────────────────────────────────────┘
                 any failure on the way ──► on_error, then on_predict_end

Portée en un coup d’œil

Agent / ONNXAgent Router
on_predict_start oui oui
on_predict_end oui oui
on_error oui oui
on_route non oui
on_load non oui
on_evict non oui

laya.serve et le serveur MCP appellent Router.predict, donc les hooks Router se déclenchent automatiquement pour eux. Les hooks Agent se déclenchent chaque fois que le Router exécute un agent attaché ou construit.

Compatibilité

  • Sans hook configuré, aucun changement de comportement. Le chemin non configuré est couvert par des tests de régression.
  • Tous les paramètres de hook sont des arguments nommés avec des valeurs par défaut, donc les appels existants continuent de fonctionner.
  • laya/hooks.py est du Python pur : import laya ne tire pas torch à cause de lui.
  • Les hooks sont synchrones par défaut. Un événement async def peut être enveloppé dans AsyncHook, ou passé comme un appelable async simple, et il s’exécute jusqu’au bout pour toi.
  • hooks_timeout borne un hook lent pour qu’il ne puisse pas bloquer une requête servie.
  • Garde les hooks rapides et non bloquants ; voir erreurs et motifs pour les conséquences sur laya.serve.

Voir aussi