Accueil / Articles

Modèles de décision en mode texte versus appels à des LLM : latence et stabilité aux frontières

Une expérience de 240 appels comparant les probabilités natives de Jev avec celles auto-déclarées par un LLM, ainsi que la manière de transformer ce signal en un routeur de modèles LangChain.

Éditeur : REACTAPP.TOP12 min de lecture2657 mots

Sources: **Does Every AI Decision Need an LLM? Testing Jev with LangChain**

Le code des agents modernes est rempli de petites décisions du type oui ou non : ce ticket est-il urgent, cette requête d’appel d’outil correspond-elle à la demande, vaut-il la peine de récupérer ce fragment ? L’habitude courante est de répondre à chacune d’elles en effectuant un nouvel appel à un LLM génératif et de parser la réponse structurée, ce qui fonctionne mais coûte plusieurs secondes à chaque étape et génère des chiffres dont la signification est incertaine. Cette démonstration exécute la même série de décisions limitées à l’aide d’un modèle de décision spécialement conçu ainsi qu’à l’aide d’un LLM général, au total 240 fois, puis relie les probabilités obtenues à un routeur LangChain. À la fin, vous saurez comment effectuer de manière équitable une telle comparaison, quelles sont les différences en termes de latence et de stabilité, et comment conserver la politique de routage dans du code pur plutôt que dans un prompt.

Les décisions limitées ne sont pas des tâches de génération

Regardez les décisions présentes dans une application typique d’LLM : beaucoup d’entre elles ne concernent pas vraiment la génération de texte :

  • Faut-il traiter ce ticket de support comme urgent ?
  • L’outil proposé correspond-il réellement à ce que l’utilisateur a demandé ?
  • Un document remplit-il une condition spécifique ?
  • Le passage récupéré est-il suffisamment pertinent pour être inclus dans le contexte ?

L’implémentation habituelle transmet l’état actuel à un LLM, demande une réponse structurée et convertit cette réponse en un branchement dans le code. En principe, il n’y a rien de mal à cela. La question reste de savoir si chacun de ces points de branchement mérite un modèle autoregressif complet.

Jev est le premier modèle de TypeSafe AI au sein de ce que l’entreprise qualifie de gamme « System One », et il adopte une approche inverse. Il ne génère jamais de texte libre ; on lui fournit un certain état ainsi qu’un ensemble de questions restreintes, et il répond par des décisions codées et des probabilités. Le fournisseur le présente comme un élément de construction pour des logiciels, et non comme un substitut à un modèle de chat ou de raisonnement. LangChain a publié une intégration utilisant Jev au sein d’outils d’agent pour des tâches telles que le choix d’un modèle et la vérification des appels aux outils. Ces deux produits étaient nouveaux au moment de la rédaction de cet article, il convient donc de vérifier les API actuelles avant de s’en servir. Si vous êtes principalement intéressé par l’aspect coûts lié à ce modèle, l’article sur le tarif d’un routeur de décisions codées utilisé avant les appels aux LLM de Frontier aborde ce sujet ; ici, l’attention se porte sur le comportement et la latence.

Conception de la comparaison

Les deux systèmes ont reçu exactement une question à répondre, reformulée ainsi : la requête nécessite-t-elle un raisonnement en plusieurs étapes réel, ou s’agit-il de quelque chose de plus simple, comme une recherche directe, l’extraction d’une valeur, un calcul de base, une réécriture ou une petite modification locale ?

Les exemples proposés visaient délibérément des cas ambigus. Les extrêmes évidents ne fournissent que peu d’informations ; le comportement intéressant d’un routeur se situe près de la frontière où « répondre simplement » devient « réfléchir sérieusement ». Huit requêtes de développeurs ont été utilisées :

  1. Recherche dans un dictionnaire : pourquoi la recherche d’une clé dans un dict Python est généralement en O(1).
  2. Liste contre ensemble : laquelle de ces deux structures convient à une collection d’ID d’utilisateurs uniques.

  • Conception de l’API REST : analyse de l’endpoint POST /users/{id}/delete et question de savoir s’une version REST plus propre existe.
  • Redis vs PostgreSQL : décision de savoir s’il convient de remplacer le cache Redis existant par PostgreSQL.
  • Python asynchrone : évaluation de l’efficacité d’un scraper web basé sur des fonctions asynchrones.
  • File d’attente vs pub/sub : choix entre ces deux systèmes pour les messages de notification des commandes.
  • Indexation SQL : examen d’une requête afin de déterminer si un index pourrait l’accélérer.
  • Microservices vs monolithe : décision de savoir si un service Python doit être divisé en microservices ou conservé intégré.
  • Chaque question a été posée dix fois dans chacune des trois configurations :

    • Jev, obtenu via un Noul qui renvoie la probabilité que la demande nécessite une réflexion approfondie.
  • GPT-5.6 Sol avec une probabilité, sollicité via LangChain pour un résultat structuré et devant retourner une valeur booléenne ainsi qu’une probabilité indiquée par le modèle lui-même.
  • GPT-5.6 Sol, uniquement binaire : le même modèle ne demande que True ou False, sans aucune mention de niveau de confiance.
  • Le budget d’appels est calculé comme suit.

    8 prompts
    × 10 repetitions
    × 3 conditions
    = 240 calls
    

    Chaque appel enregistre sa décision, sa probabilité le cas échéant, le nom du modèle et la latence aller-retour. Il est important de préciser ce que l’expérience ne cherche pas à faire : il n’existe aucune étiquette de vérité indiquant combien de raisonnement une demande donnée a réellement besoin, donc la correction n’est pas possible. Au lieu de cela, l’étude vise trois questions plus ciblées :

    1. Le modèle de décision et le modèle génératif aboutissent-ils à des jugements de routage similaires ?
  • Quelle est la cohérence de chacun lorsque la même requête est répétée ?
  • Quel est le coût en latence d’une décision sémantique bornée lorsqu’un modèle spécialisé la prend par rapport à un modèle génératif ?
  • Les résultats les plus importants proviennent des prompts à la frontière, et non de ceux qui sont clairement complexes.

    Trois implémentations d’une même décision

    Tous les trois ces systèmes ont été écrits en Python à l’aide de LangChain.

    Jev avec un seul Noul

    Noul modélise une proposition binaire, mais au lieu de se décider immédiatement pour Vrai ou Faux, il retourne une probabilité. Jev reçoit la requête du développeur comme état ainsi qu’un Noul contenant la question. En résumé, la définition de la question est la suivante :

    Noul(
        instructions="""
        Does this request require substantial multi-step reasoning
        rather than a direct lookup, extraction, simple calculation,
        rewriting task, or small localized edit?
        """
    )
    

    La réponse n’est qu’un nombre, par exemple :

    0.86
    

    Cette valeur représente la probabilité que l’énoncé soit vrai :

    P(requires substantial reasoning) = 0.86
    

    Pour comparer Jev avec l’installation LLM binaire, une probabilité de 0.5 ou plus est considérée comme vraie.

    GPT-5.6 Sol indique sa propre probabilité

    La base de génération utilisée était ChatOpenAI via LangChain, avec un modèle Pydantic qui contrôle la forme du résultat. Les bornes Field(ge=0.0, le=1.0) garantissent que la valeur se situe au moins dans cette plage ; elles ne disent rien sur le fait qu’elle ait un sens.

    class ReasoningProbability(BaseModel):
        requires_reasoning: bool
        probability: float = Field(ge=0.0, le=1.0)
    

    Ici, le modèle devait fournir à la fois un verdict et une estimation de la probabilité qu’une réponse soit affirmative. Cette distinction mérite attention. La sortie de Jev est ce pour quoi le modèle a été conçu. Le chiffre fourni par l’LLM est du texte qu’il génère, car le schéma exige un nombre flottant, et il n’y a aucune garantie qu’il reflète une probabilité interne réelle. Considérer ces deux valeurs comme du même type de quantité relève d’une hypothèse, et non d’un fait acquis.

    GPT-5.6 Sol contraint à une logique booléenne

    La troisième configuration a éliminé toute référence à la probabilité et a demandé au même modèle une seule valeur :

    class ReasoningBinary(BaseModel):
        requires_reasoning: bool
    

    Cela laisse trois signaux distincts : une probabilité native et deux variantes génératives.

    Jev
        → native probabilistic decision
    
    GPT-5.6 Sol
        → generated probability + decisionGPT-5.6 Sol
        → forced binary decision
    

    Ce que les 240 appels ont montré

    Trois schémas sont apparus :

    1. Jev a atteint la décision définie bien plus rapidement.
    2. Pour les demandes nécessitant clairement une réflexion, les systèmes étaient pour la plupart d’accord ; ils divergeaient près de la limite.
    3. Au sujet d’un prompt à frontière floue, la probabilité auto-déclarée du LLM a fortement fluctué entre des appels identiques.

    Latence : dizaines de millisecondes contre secondes

    Mesuré en temps moyen aller-retour, Jev a répondu en 128,8 ms par décision. GPT-5.6 Sol a mis 2 828,1 ms pour fournir une probabilité et 4 072,1 ms dans le mode binaire uniquement. Cela représente environ 22 fois plus de lenteur pour la version avec probabilité et 32 fois plus de lenteur pour la version booléenne.

    Considérez ces multiples comme un instantané plutôt que comme une référence de performance. La latence d’une API hébergée varie en fonction de la charge du fournisseur, de la version du modèle, de la région et du chemin réseau. L’ordre de grandeur est l’élément utile : lorsqu’il s’agit d’un jugement restreint, l’utilisation d’un modèle génératif général implique beaucoup plus de ressources que ce dont la question a besoin.

    Le résultat exclusivement binaire constitue une curiosité qui mérite d’être notée. Demander moins de champs de sortie n’a pas rendu l’appel plus rapide ; au contraire, il a été encore plus lent. Une seule exécution ne peut expliquer pourquoi, mais cela rappelle que la latence globale des LLM est dominée par d’autres facteurs que le nombre de tokens de sortie visibles, tels que la file d’attente, le préremplissage et toute réflexion cachée effectuée par le modèle avant de répondre.

    Conformité aux extrêmes, divergence aux limites

    Au sujet des prompts clairement complexes, avec le remplacement par Redis et la répartition en microservices, les deux systèmes ont été d’accord et ont évalué fortement la nécessité de raisonnement. L’écart est apparu pour les requêtes semblant plus simples. Pour les recherches dans un dictionnaire, les questions relatives à la comparaison entre listes et ensembles ainsi que celles de conception REST, GPT-5.6 Sol a indiqué des probabilités proches de zéro, tandis que Jev les a placées nettement plus près du milieu de la fourchette.

    L’analyse REST est l’exemple le plus marquant :

    La demande consiste en une critique de POST /users/{id}/delete ainsi qu’en une alternative plus conforme aux principes RESTful.

    La probabilité moyenne de Jev était de 0,551, légèrement supérieure au seuil de 0,5. GPT-5.6 Sol affichait en moyenne 0,071. Il ne s’agissait pas de bruit se situant à la frontière du seuil : lors des dix essais, Jev a classé la demande comme nécessitant beaucoup de raisonnement, tandis que Sol l’a classée ainsi dans les dix essais effectués avec chacun des deux modèles.

    Cela ne signifie pas pour autant que Jev ait raison. Sans une vérité de référence étiquetée, on ne peut pas déterminer quel jugement est le meilleur. Ce qui est clair, c’est que les deux signaux ne sont pas interchangeables, et qu’ils diffèrent précisément dans la zone où un routeur est le plus sensible à la manière dont ses limites sont définies. Si vous remplacez une source de décision par une autre, attendez-vous à ce que le routage change précisément pour les requêtes dont vous êtes le moins sûr.

    Deux nombres entre zéro et un qui se comportent différemment

    La répétition de chaque demande dix fois a permis de mettre en évidence la stabilité. La plupart des demandes ont produit des résultats assez cohérents de la part des deux systèmes. L’exception a été la demande de choisir entre une file d’attente et un système pub/sub pour les notifications de commande.

    Au cours de dix appels identiques, la probabilité auto-déclarée par GPT-5.6 Sol a varié d’environ 0,28 à 0,90, tandis que les valeurs de Jev sont restées très concentrées. La configuration du LLM binaire a répondu True lors de neuf des dix essais, ce qui montre que les verdicts étaient assez stables, même si le niveau de confiance indiqué ne l’était pas.

    L’explication est structurelle. Les deux systèmes émettent une valeur que l’on peut exprimer dans l’intervalle [0, 1], mais les processus qui la génèrent diffèrent. Pour Jev, la probabilité correspond à la sortie native du modèle pour une décision bornée. Pour le LLM, il s’agit d’une séquence de tokens produite parce que la demande initiale sollicitait une estimation, et l’échantillonnage peut aboutir à des valeurs très différentes d’une exécution à l’autre. Dans cette tâche, la probabilité auto-déclarée par un LLM ne se comportait pas de manière fiable comme la sortie d’un modèle conçu pour des décisions probabilistes. Si vous prévoyez d’utiliser un seuil basé sur une telle valeur, mesurez sa variance lors de plusieurs appels avant de lui faire confiance.

    Transformer la probabilité en routeur LangChain

    Cette comparaison a motivé un test pratique : utiliser la probabilité de Jev pour diriger des requêtes réelles entre deux modèles OpenAI.

    • GPT-5.6 Luna gère les requêtes pour lesquelles on estime qu’aucun raisonnement approfondi n’est nécessaire.
  • GPT-5.6 Sol s’occupe de ceux pour lesquels cela est jugé nécessaire.
  • Jev ne choisit jamais directement un modèle. Il répond à la même question Noul de l’expérience, afin de déterminer si la demande nécessite un raisonnement multi-étapes approfondi, et renvoie une probabilité telle que 0.54, à interpréter comme P(requiert un raisonnement approfondi) = 0.54.

    Le code d’application vérifie ensuite cette valeur par rapport à un seuil défini en Python classique :

    REASONING_THRESHOLD = 0.65
    

    La branche elle-même est tout aussi simple :

    if reasoning_probability >= REASONING_THRESHOLD:
        model = powerful_model
    else:
        model = fast_model
    

    L’essentiel réside dans la division du travail : Jev fournit les probabilités ; l’application décide de ce qu’il convient en faire. Avec un seuil de 0,65, une note de 0,54 est dirigée vers Luna et une note de 0,86 vers Sol. La politique est explicite, déterministe et pouvant être examinée via un diff de code, tandis que le jugement sémantique reste du ressort du modèle.

    Le flux complet se présente comme suit :

    User request
         ↓
        Jev
         ↓
    P(requires substantial reasoning)
         ↓
    Python threshold = 0.65
       ↙                  ↘
    Luna                  Sol
    

    Remarque concernant ce seuil : 0,65 est indicatif et non ajusté. Il a été choisi uniquement afin que, dans une petite démonstration, les requêtes évidemment simples soient classées d’un côté, tandis que les questions relatives à la conception et à l’architecture, qui obtenaient de plus hauts scores, le soient de l’autre. En production, on le déterminerait empiriquement à partir d’un ensemble plus important étiqueté, en évaluant deux types d’erreurs distinctes : le coût lié à l’utilisation d’un modèle coûteux pour une requête simple, et l’envoi d’une requête réellement difficile vers un modèle moins performant. Ces coûts sont rarement symétriques, ce qui explique pourquoi le meilleur seuil n’est pas systématiquement 0,5.

    Comportement du routeur

    Six requêtes ont été traitées par le routeur, allant d’un calcul simple à un problème d’architecture multi-régions. La plupart des résultats correspondaient à l’intuition. Les tâches de calcul et de réécriture ont obtenu des scores très bas et ont été orientées vers Luna ; les questions d’architecture complexes, en revanche, ont obtenu de hauts scores et ont été envoyées vers Sol.

    Les cas intermédiaires étaient les suivants :

    • l’explication basée sur la recherche dans le dictionnaire a obtenu un score de 0,46
    • l’analyse de l’API REST a obtenu un score de 0,54
    • la question relative aux files d’attente par rapport aux systèmes pub/sub a obtenu un score de 0,86

    Puisque Jev renvoie un score continu plutôt qu’une étiquette fixe « facile » ou « difficile », le degré d’agressivité dans l’affectation des tâches devient une décision distincte et ajustable. À un score de 0,65, l’analyse REST a été confiée à Luna et la question sur les files d’attente par rapport aux systèmes pub/sub à Sol. Augmenter le seuil permettrait d’envoyer moins de requêtes au modèle plus puissant, ce qui réduirait les coûts ; le diminuer diminuerait le risque de ne pas traiter correctement une requête difficile. Le jugement du modèle ne change pas lorsque l’on ajuste ce seuil, ce qui signifie que l’on peut affiner la politique, même de manière dynamique, sans avoir à reformuler quoi que ce soit.

    Limites de cette expérience

    Gardez les affirmations en proportion :

    • Huit exemples ne peuvent pas caractériser l’un ou l’autre modèle de manière générale, et la question de classification est elle-même subjective.
    • L’exactitude et la calibration n’ont pas été mesurées. Cette étude compare le comportement, la cohérence et le temps de réponse, et non la justesse.
    • On ne doit pas supposer que les probabilités de Jev et les valeurs auto-déclarées par le LLM aient une signification statistique commune.
    • Les valeurs de temps de réponse dépendent du fournisseur et des conditions au moment de l’exécution.

    Une étude plus approfondie utiliserait un ensemble de données plus important et marqué, afin d’évaluer le routeur du début à la fin : précision du routage, calibration, qualité des réponses obtenues, coût, ainsi que sensibilité aux différents seuils.

    Lorsqu’un modèle de décision distinct est utile

    Pour une petite application, il est souvent tout à fait suffisant de laisser un LLM gérer le routage ; une appel supplémentaire a rarement d’importance. La situation change lorsque l’agent doit effectuer de nombreuses étapes de routage, de récupération d’informations, de validation et de vérification d’outils. Beaucoup de ces étapes reposent sur des jugements définis, et si chacune devient à son tour un appel de génération prenant plusieurs secondes, le cycle de l’agent ralentit et devient coûteux pour des raisons sans rapport avec le travail réel.

    Une séparation claire des responsabilités se présente comme suit :

    • Un modèle de décision gère les jugements sémantiques définis et retourne une probabilité.
    • Le code de l’application définit les seuils, la politique et les branches logiques.
    • L’LLM s’occupe de la réflexion et de l’écriture sans contraintes.

    Un LLM performant pourrait gérer chacun d’eux, mais cela n’oblige pas toutes les parties du système à utiliser le même type de modèle, surtout lorsque la latence est importante. L’argument en faveur d’un dimensionnement des modèles adapté à la tâche est présenté dans right-sizing LLMs with routing, retrieval and evaluation.

    Points clés

    • Lors de cette expérience, le modèle de décision a nécessité en moyenne environ 129 ms par jugement défini, contre plusieurs secondes pour les configurations génératives.
    • Avec des entrées proches de l’ambiguïté, les deux systèmes étaient en désaccord de manière constante et non aléatoire ; remplacer un signal par l’autre modifiera donc le routage là où il est le plus sensible.
    • La probabilité auto-déclarée par un LLM peut varier considérablement entre des appels identiques (environ de 0,28 à 0,90 pour une même requête) ; vérifiez sa variance avant de définir des seuils.
  • Conservez le seuil dans le code, choisissez-le à partir de données étiquetées ainsi que du coût asymétrique de chaque erreur, et considérez un modèle de décision spécialisé comme un complément à l’LLM, et non comme une imitation plus rapide de celui-ci.