Jev, l'IA de TypeSafe qui renvoie des décisions plutôt que du texte

Jev ne génère pas de texte : ce modèle d'IA renvoie un choix, un score ou un oui/non, avec sa probabilité. De nombreux schémas, dont certains à parcourir étape par étape, le comparent à ChatGPT, présentent ses trois types de questions et te montrent comment l'appeler en Python.

Jev est un modèle d'IA développé par TypeSafe AI.

Contrairement à ChatGPT, il n'écrit pas de texte. Quand il faut trancher une question comme « À quelle équipe confier ce ticket ? » ou « Est-ce urgent ? », il renvoie une réponse sous une forme que tu as définie à l'avance, accompagnée d'une probabilité (un nombre entre 0 et 1 : plus il est proche de 1, plus la réponse est probable).

Dans sa vidéo en anglais « JEV: How It Works and What You Can Build », Riley Brown explique comment fonctionne Jev et ce qu'il permet de construire.

Pour t'en servir, tu envoies les données à évaluer (le state, par exemple le texte d'un ticket) et tes questions via une API (l'interface par laquelle un programme fait appel aux fonctions d'un service externe).

La suite à donner à la réponse, c'est ton propre code qui en décide.

Schémas à l'appui, cette page explique ce qui distingue Jev de ChatGPT, puis passe en revue ses trois types de questions, la façon de l'appeler depuis Python et d'aiguiller le traitement selon l'indice de confiance, les tâches à ne pas lui confier et, pour finir, son fonctionnement interne.

Ce que tu envoies à Jev, ce qu'il renvoie et qui décide de la suite
ChoiceQuelle équipe ?ScoreQuel agacement ?NoulUrgent ?Évaluerselon le stateÉvaluerselon le stateÉvaluerselon le statechoice+ probabilitéscore+ probabiliténoul(proba. du oui)Le code envoieà l'équipeLe code triepar prioritéLe code passeà un humainJev évalue en parallèle
Une seule requête suffit : Jev évalue les trois questions sur le même state et renvoie une réponse distincte pour chacune.

Jev se charge de tout, sauf de la colonne de droite : celle-ci relève de ton propre code, écrit avec des instructions if.

Qu'est-ce que Jev ? Un modèle System One qui renvoie des décisions, pas du texte

TypeSafe appelle ce type de modèle un modèle System One (un modèle d'IA qui renvoie rapidement des décisions typées, directement exploitables par un logiciel).

La place de Jev parmi les modèles d'IA
Modèles d'IA qui lisent le langage naturel
Modèles qui génèrent du texte (LLM)
  • Chatbots comme ChatGPT et modèles de raisonnement
  • Renvoient : du texte, du code, des explications
  • La forme de la réponse n'est connue qu'à la lecture
Modèles qui renvoient des décisions (System One)
  • Renvoient : options, niveaux ou oui/non prédéfinis, avec une probabilité
  • N'écrivent ni texte, ni code, ni explications
Jev (TypeSafe AI)
  • Nom du modèle dans l'API : jev-latest
  • Texte uniquement en entrée (ni image, ni audio, ni vidéo)
Les deux familles de modèles lisent le langage naturel ; ce qui les distingue, c'est ce qu'elles renvoient : du texte ou une décision. Jev, dans le cadre le plus imbriqué, en bas, est le premier modèle System One.

jev-latest est un alias (un nom qui renvoie à une version précise) qui désigne toujours la version la plus récente : dès qu'une nouvelle version sort, l'alias bascule vers elle.

En quoi Jev se distingue-t-il d'un LLM comme ChatGPT ?

Comparons-les sur une même tâche : aiguiller les tickets de support entrants vers trois équipes.

Commençons par un LLM (grand modèle de langage : un modèle d'IA qui génère du texte mot après mot), par exemple ChatGPT. Si tu lui demandes quelle équipe doit traiter un ticket, il te répond par du texte.

Il suffit que la formulation passe de « support technique » à « l'équipe tech » pour qu'un test comme if answer == "technical" ne corresponde plus.

Et même si tu utilises une fonctionnalité qui force la sortie au format JSON, ton code doit quand même valider son contenu.

Comme Jev reçoit la liste des candidats à l'avance, le choice qu'il renvoie est forcément l'un d'eux.

Quand TypeSafe annonce « 0 % d'hallucination » (le fait de produire un contenu faux mais plausible), l'affirmation porte sur le format : tu n'obtiendras jamais de réponse en dehors des candidats.

Cela ne garantit pas que le candidat choisi soit le bon.

Voici les deux modèles comparés sur cinq points. La dernière ligne porte sur le prix, calculé d'après le nombre de tokens (les unités en lesquelles un modèle d'IA découpe le texte pour le traiter).

CritèreLLM comme ChatGPTJev
Ce qu'il renvoieDu texte généréRéponse prédéfinie + probabilité
Écrit du texte ou du codeOuiNon
Points fortsTexte, code, raisonnement longClasser, noter, répondre oui/non
Avec plus de questionsPlus lent : tout est rédigéEn parallèle : attente quasi stable
Ce qui est facturéTokens d'entrée et de sortieTokens d'entrée seulement

Choice, Score ou Noul : quel type de question choisir ?

Quand tu interroges Jev, la première chose à décider n'est pas la formulation de la question, mais la forme de la réponse.

Il existe trois types de questions : Choice (choisir un candidat parmi plusieurs, sans ordre entre eux), Score (situer quelque chose sur une échelle de niveaux ordonnés) et Noul (répondre par oui ou par non).

Avec Choice et Score, la réponse s'accompagne aussi d'un indice de confiance (le champ confidence) : un nombre entre 0 et 1 qui mesure à quel point la probabilité se concentre sur un seul candidat ou un seul niveau. Plus il est proche de 1, plus le modèle a tranché nettement en faveur d'une réponse.

Noul est le seul type qui ne renvoie pas d'indice de confiance.

Si tu hésites, demande-toi ce que ton code fera de la réponse : Choice pour aiguiller, Score pour comparer à un seuil, Noul pour une simple instruction if.

Pour Choice, criteria (les critères sur lesquels repose le jugement) est un dictionnaire qui associe à chaque nom de candidat sa description.

Sers-toi de ces descriptions pour préciser où s'arrête un candidat et où commence le suivant, par exemple « les problèmes de paiement vont à billing, les bugs d'intégration à technical ».

Pour Score, criteria est une liste qui décrit les niveaux du plus bas au plus haut, numérotés à partir de 0. Le score renvoyé est la somme des numéros de niveau, pondérés chacun par sa probabilité.

Probabilité de chaque niveau et score obtenu (question Score)
00.200.400.600.8010Niveau 00.57Niveau 10.43Niveau 2
Le score vaut 0 × 0,0 + 1 × 0,57 + 2 × 0,43 = 1,43 : il tombe entre les niveaux 1 et 2, plus près du niveau 1.

Il ne reste plus à ton code qu'à comparer ce score à un seuil, par exemple « traiter en priorité tout ce qui atteint 1,5 ou plus ».

Un Noul de 0,5 ne veut pas dire « moyen »

Une valeur Noul, c'est la probabilité que la réponse soit oui. Si la question « Cette personne maîtrise-t-elle Python ? » renvoie 0,5, cela ne veut pas dire que son niveau est intermédiaire, mais qu'il y a une chance sur deux qu'elle maîtrise le langage.

Pour mesurer un degré ou un niveau, utilise Score.

Utiliser Jev : aiguiller les tickets de support vers la bonne équipe avec le SDK Python

Reprenons l'exemple du guide de démarrage officiel : aiguiller un ticket vers l'une des trois équipes.

Avec le SDK (une bibliothèque qui fournit tout le nécessaire pour appeler un service), tu n'as ni à construire les requêtes, ni à gérer toi-même les nouvelles tentatives en cas d'échec.

Le SDK demande Python 3.10 ou une version plus récente, et TypeSafeClient() lit la clé dans la variable d'environnement TYPESAFE_API_KEY.

Pour savoir comment définir une variable d'environnement, consulte Les variables d'environnement sous Linux ; pour bien gérer tes clés, reporte-toi à Clés API et webhooks.

Ce qu'un appel envoie et ce qu'il renvoie
statetexte du ticketquestionschacune nomméemodeldéfaut : jev-latestJev litet jugeanswersune par questionmodelversion utiliséeusagetokens utilisés
Tu envoies les trois éléments de gauche et tu récupères les trois de droite. Le nom donné à chaque question devient la clé qui permet d'extraire sa réponse de answers.

Un code tiré de la documentation officielle

Le code et les réponses présentés ici sont tirés des exemples de la documentation officielle, et non de mes propres exécutions. Le texte du ticket et les instructions en sont repris tels quels, en anglais.

Appeler Jev avec le SDK Python

pip install typesafe-sdk
export TYPESAFE_API_KEY="ta-cle-api"   # shell macOS / Linux
from typesafe_sdk import Choice, Noul, Score, TypeSafeClient

client = TypeSafeClient()  # lit TYPESAFE_API_KEY et utilise jev-latest

# Les données à évaluer (state) : un ticket de support entrant
# (l'intégration Stripe échoue depuis 3 jours et le client a besoin d'aide au plus vite)
ticket = "Hi, I've been trying to connect my Stripe account for 3 days and the integration keeps failing. I'm losing sales. Please help ASAP."

response = client.system_one(
    state=ticket,
    questions={
        # Quelle équipe : dictionnaire nom du candidat -> description
        "department": Choice(
            instructions="Which team should handle this",
            criteria={
                "billing": "Payment or subscription issues",
                "technical": "Bugs or integration problems",
                "sales": "Pricing or account questions",
            },
        ),
        # Quel degré d'agacement : niveaux du plus bas au plus haut
        "frustration": Score(
            instructions="How frustrated the customer appears",
            criteria=[
                "Calm, just stating facts",
                "Frustrated but civil",
                "Very angry, strong language",
            ],
        ),
        # Est-ce urgent : oui ou non
        "is_urgent": Noul(
            instructions="The message conveys urgency or time-sensitivity",
        ),
    },
)

print(response.answers["department"].choice)  # technical
print(response.answers["frustration"].score)  # 1.0
print(response.answers["is_urgent"].noul)     # 1.0

questions est un dictionnaire qui associe un nom à chaque question ; tu retrouves ensuite chaque réponse dans response.answers, sous ce même nom. Les valeurs indiquées en commentaire sont celles de la réponse donnée dans le guide de démarrage officiel.

L'équipe retenue est technical, l'agacement se situe au niveau 1 (« Frustrated but civil », agacé mais poli) et l'urgence ressort comme un oui quasi certain.

Est-ce cohérent avec les mentions « for 3 days » et « ASAP » du ticket ?

Appeler Jev directement en HTTP

Tu peux aussi te passer du SDK et appeler Jev directement en HTTP (pour les bases sur les API, voir Qu'est-ce qu'une API ?).

L'API n'expose qu'un seul endpoint (l'URL qui reçoit les requêtes), et tu transmets ta clé dans l'en-tête Authorization.

curl -X POST https://api.typesafe.ai/v1/systemone \
  -H "Authorization: Bearer $TYPESAFE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "state": "Hi, I have been trying to connect my Stripe account for 3 days ...",
    "model": "jev-latest",
    "questions": {
      "department": {
        "type": "choice",
        "instructions": "Which team should handle this",
        "criteria": {
          "billing": "Payment or subscription issues",
          "technical": "Bugs or integration problems",
          "sales": "Pricing or account questions"
        }
      }
    }
  }'

En HTTP, model est obligatoire, et tu indiques le type de chaque question dans son champ type.

Lire la réponse

Voici un exemple de JSON renvoyé, limité à la réponse department.

{
  "answers": {
    "department": {
      "type": "choice",
      "choice": "technical",
      "confidence": 0.78,
      "probabilities": {
        "technical": 0.85,
        "sales": 0.0,
        "billing": 0.15
      }
    }
  },
  "usage": {
    "input_tokens": 392,
    "output_tokens": 65
  }
}
Comment s'imbrique le JSON de réponse
Réponse complète
answers : une réponse par nom de question
department (une réponse Choice)
  • choice : le candidat choisi → technical
  • probabilities : une par candidat (somme = 1)
  • confidence : leur degré de concentration → 0,78
usage
  • input_tokens / output_tokens : tokens utilisés
Le cadre extérieur représente le JSON complet. Ce que Jev a répondu se trouve dans le cadre qui porte le nom de la question, à l'intérieur de answers.

usage indique le nombre de tokens qui sert de base à la facturation.

Avec le SDK, il suffit d'écrire response.answers["department"].choice pour extraire de ce JSON le candidat choisi.

Les quatre valeurs de la réponse department
00.200.400.600.8010.85technical0.15billing0sales0.78confidence
Ces valeurs sont tirées de l'exemple de réponse du guide de démarrage officiel. Les trois barres de gauche donnent la probabilité de chaque candidat (leur somme vaut 1). La barre confidence, tout à droite, n'est pas une probabilité : elle résume à quel point ces probabilités sont concentrées.

Le candidat en tête, technical, atteint 0,85, alors que la confidence vaut 0,78. Celle-ci est de 0 quand les probabilités sont réparties uniformément, et de 1 quand elles se concentrent toutes sur un seul candidat.

Pour trois candidats, la démo officielle en donne une approximation, « (3 × probabilité maximale − 1) ÷ 2 », et on retrouve bien (3 × 0,85 − 1) ÷ 2 ≈ 0,78.

Utiliser l'indice de confiance pour automatiser, confirmer ou transmettre à un humain

La documentation officielle recommande de découper l'indice de confiance en trois plages à l'aide de seuils (les valeurs à partir desquelles le traitement bifurque) : agir automatiquement quand il est élevé, demander confirmation à l'utilisateur quand il est moyen, et passer la main à un humain quand il est faible.

Les bons seuils varient d'une action à l'autre. L'exemple officiel prend le cas d'un service bancaire vocal, où l'on gère son compte à la voix.

Pour un même indice de confiance de 0,7, le solde est lu directement, alors qu'un virement n'est validé qu'après confirmation de l'utilisateur. Plus une action peut causer de dégâts, plus le seuil doit être élevé.

Vois 0,6 et 0,85 comme des points de départ, à ajuster d'après les résultats obtenus sur tes propres données.

action = response.answers["intent"]  # Choice: check_balance / approve_transfer / other

if action.confidence < 0.6:
    # Quelle que soit l'action, ne rien exécuter si le modèle hésite
    route_to_support_agent(account_id)

elif action.choice == "check_balance":
    # En cas d'erreur, il suffit de redemander le solde
    show_balance(account_id)

elif action.choice == "approve_transfer":
    if action.confidence > 0.85:
        approve_transfer(account_id)  # de l'argent est en jeu : seuil plus élevé
    else:
        ask_user_to_confirm("Voulez-vous valider ce virement ?")

else:
    # classé en other
    route_to_support_agent(account_id)

Le premier if fixe un plancher pour l'indice de confiance, puis les branches elif appliquent un seuil propre à chaque action : le contrôle se fait ainsi à deux niveaux.

Comme if et elif évaluent les conditions dans l'ordre, de haut en bas, on voit d'un coup d'œil quelle ligne protège contre quel type de dégât.

Figer la version du modèle une fois les seuils réglés

jev-latest pointe vers chaque nouvelle version dès sa sortie : une même entrée peut alors se mettre à donner d'autres réponses et d'autres indices de confiance.

Une fois tes seuils réglés, indique dans model un nom de modèle qui comporte un numéro de version, et ne passe à une nouvelle version qu'après l'avoir testée.

Quand utiliser Jev (ou pas) : points faibles et précision hors anglais

Jev n'écrit pas de texte : il ne remplace donc pas un LLM.

Son point fort, c'est de décider vite et à moindre coût où envoyer une demande, avant qu'un LLM ou un humain n'intervienne.

Associer Jev à un LLM : décider d'abord du chemin

Dans l'exemple officiel, Jev évalue en même temps l'intention d'un message (avec Choice) et sa difficulté de traitement (avec Score), puis choisit où l'envoyer.

Jev choisit le chemin en amont, d'autres rédigent la réponse
Messagedu clientJev : intentionet difficultéSuivi decommandeQuestion surun produitconfidencesous 0,5Le code répondvia la baseUn LLM spécialisérédigeUn humains'en charge
On ne fait appel au LLM, coûteux, que pour les messages qui en ont vraiment besoin. Jev ne remplace pas le LLM : c'est une étape de décision qui se place en amont.

Points faibles et solutions de contournement

TypeSafe publie la liste des tâches qui posent problème à Jev. Les trois que tu risques le plus de croiser au quotidien sont le comptage, la comparaison de dates et le calcul.

Dans les trois cas, la parade est la même : ne laisse que le jugement à Jev et fais les calculs dans le code.

Contourner les trois points faibles en séparant jugement et calcul
Demander un totald'un coupFaire comparerles datesFaire calculerles montantsJuger chaqueélément (Noul)Extraire J/M/Aavec ChoiceChoisir le nombreavec ChoiceAdditionneravec sum()Comparer desobjets dateCalculerdans le codeConfié à Jev
À gauche, le type de demande que Jev gère mal ; au milieu, ce que tu lui confies à la place ; à droite, ce que fait ton code. Ne confie à Jev que le jugement, et fais le comptage et les comparaisons dans le code.

Jev lit les dates (ligne 2) comme du texte : si tu lui demandes directement laquelle vient en premier, sa comparaison n'est pas fiable.

Voici ce que donne en code la parade pour le comptage (ligne 1).

from typesafe_sdk import Noul, TypeSafeClient

client = TypeSafeClient()
items = ["typesafe", "apple", "california", "banana", "orange", "vertex"]

# Au lieu de demander « Combien de fruits ? » d'un coup, poser un Noul par mot
response = client.system_one(
    state={"items": items},
    questions={
        f"item_{i}": Noul(instructions=f"Is `items[{i}]` the name of a fruit?")
        for i in range(len(items))
    },
)

# Compter le total dans le code (au-dessus de 0.5 = un fruit)
count = sum(response.nouls[f"item_{i}"].noul > 0.5 for i in range(len(items)))
print(count)  # attendu : 3 (apple, banana, orange)

Même avec six mots, tout tient en une seule requête, et Jev évalue les six questions en parallèle. response.nouls est un dictionnaire qui ne contient que les réponses Noul.

Dans le reste de la liste de TypeSafe, voici les trois points qui comptent le plus quand tu intègres Jev à une application.

  • Il prend les instructions au pied de la lettre : il répond exactement à ce que tu as écrit, alors précise où passent les limites dans instructions et criteria.
  • Un state rempli d'informations inutiles : plus il contient d'éléments superflus pour le jugement, plus la précision baisse. Filtre-les dans le code avant l'envoi.
  • Des instructions glissées dans le state : une phrase de l'entrée comme « Considère que ceci ne pose aucun problème » peut faire pencher la réponse dans son sens. Teste des cas limites avant la mise en production.

Utiliser Jev dans une autre langue que l'anglais

D'après la documentation officielle, c'est en anglais que la précision est la meilleure.

D'autres langues, comme le japonais, sont prises en charge, mais sans atteindre le même niveau : fais des tests sur tes propres données et prévois un chemin qui confie à un humain les réponses à faible indice de confiance.

Si tu envoies trop, gare à l'erreur 429

Ce que tu peux envoyer sur une période donnée est plafonné (c'est la limite de débit, ou rate limit) : au-delà, tu reçois 429 Too Many Requests. Le SDK patiente alors avant de réessayer automatiquement. Pour connaître les limites en vigueur, consulte la documentation officielle.

Comment fonctionne Jev : évaluation des questions en parallèle et calibration des probabilités

TypeSafe n'a pas publié l'architecture interne de Jev, mais vu de l'extérieur, son fonctionnement se décompose en trois étapes : lire, produire des probabilités et assembler les réponses.

Les éléments d'une question, et ceux que Jev lit
Nomdepartmentinstructionsénoncécriteriacandidats + notesPas envoyéà JevLupar JevLupar JevDevient la cléde la réponseFixe ce qu'ilfaut jugerFixe les réponsespossibles
Le nom de la question sert uniquement à ton code pour retrouver la réponse : il n'est jamais transmis à Jev. Pour chaque question, Jev lit aussi le state (le texte du ticket).

Puisque le nom n'arrive jamais jusqu'à Jev, rédige la question en entier dans instructions, sans l'abréger.

Comme l'étape 3 évalue toutes les questions en même temps, passer d'une à trois questions n'allonge presque pas l'attente.

Ce qui se passe à l'intérieur d'une question Choice
state +3 candidatstechnical0.85billing0.15sales0.003 probabilités(somme = 1)choicetechnicalconfidence0.78Probabilité par candidat
Il s'agit de l'exemple department du guide de démarrage officiel. De la même façon, Score produit une probabilité par niveau, et Noul la probabilité du oui.

Le candidat le plus probable devient le choice, et le degré de concentration des probabilités sur un seul candidat donne la confidence.

RLCD : entraîner Jev pour que ses probabilités reflètent son taux de bonnes réponses

Jev est entraîné avec une méthode appelée RLCD (apprentissage par renforcement pour des décisions calibrées).

L'objectif : faire en sorte que les probabilités qu'il produit correspondent à la fréquence à laquelle il a réellement raison. C'est ce qu'on appelle la calibration.

Probabilités d'un modèle calibré et nombre de bonnes réponses
100 réponsesà 0,9100 réponsesà 0,6100 réponsesà 0,2Environ 90justesEnviron 60justesEnviron 20justes1 sur 10est fausse4 sur 10sont fausses8 sur 10sont fausses
Ces chiffres sont donnés à titre d'exemple pour illustrer la calibration. Plus la probabilité est basse, plus les réponses fausses sont nombreuses : tu peux donc traiter les réponses différemment selon leur probabilité.

Puisque la probabilité reflète la fréquence des bonnes réponses, tu peux placer la limite en t'appuyant sur des chiffres concrets, par exemple « laisser Jev gérer seul à 0,9, faire vérifier par un humain à 0,6 ».

En résumé

Jev est un modèle System One : tu lui fournis un state et des questions, et il renvoie des décisions typées (Choice, Score ou Noul), accompagnées de leur probabilité. Comme les questions sont évaluées en parallèle, en poser plusieurs à la fois n'allonge presque pas l'attente.

Pour l'intégrer à une application, retiens deux points.

D'abord, découpe l'indice de confiance renvoyé en plages à l'aide de seuils, et laisse ton propre code décider de ce qu'il faut automatiser, faire confirmer ou transmettre à un humain.

Ensuite, garde le comptage, les comparaisons de dates et les calculs dans ton code.