STRIKE / BLOG

Décisions IA typées : oui/non, choix, score avec probabilités

La sortie structurée d'un LLM garantit le format, pas la fiabilité. Découvrez les décisions typées (oui/non, choix, score) avec une probabilité par option, exemples Python et JavaScript à l'appui.

Décisions IA typées : oui/non, choix, score avec probabilités

Demander à un LLM de « répondre en JSON » est devenu un réflexe. Les outils de sortie structurée ont beaucoup progressé : mode JSON, décodage contraint par un schéma, validation avec Pydantic ou Zod. Pour extraire des informations d'un texte (noms, adresses, lignes de facture), c'est exactement ce qu'il faut.

Mais dans bien des cas, ce qu'on attend du modèle n'est pas une extraction. C'est une décision : classer un message, valider ou refuser une demande, noter un contenu de 1 à 5. Et là, un JSON bien formé ne répond qu'à moitié à la question. Il garantit que le format est valide. Il ne dit rien du degré de confiance qu'on peut accorder à la réponse.

Ce que la sortie structurée ne dit pas

Prenons cette réponse :

{ "categorie": "facturation", "urgent": true, "mecontentement": 2 }

Le format est impeccable. En revanche, on ignore :

  • le niveau de certitude : "urgent": true s'affiche de la même manière, que le modèle soit quasi certain ou qu'il ait hésité ;
  • l'option arrivée deuxième : si « technique » talonnait « facturation », c'est précisément le ticket qu'un humain devrait relire ;
  • la répartition sur l'échelle : 2 n'est qu'un jeton, on ne sait pas comment la croyance du modèle se répartissait entre 1, 2 et 3.

On peut bien sûr ajouter un champ "confiance": 0.9 au schéma. Mais ce nombre est généré comme le reste du texte : le modèle l'écrit, il ne le calcule pas à partir de ses probabilités internes.

Le principe des décisions typées

Avec une API de décisions typées, la logique s'inverse. Au lieu de décrire un schéma de sortie, vous posez des questions typées, et chaque réponse est accompagnée d'une distribution de probabilités. Strike propose trois types de questions.

noul : oui ou non

"urgent": {
  "type": "noul",
  "instructions": "Le client attend-il une réponse dans les prochaines heures ?",
  "criteria": {
    "true": "Échéance explicite ou service bloqué",
    "false": "Aucune contrainte de temps exprimée"
  }
}

La réponse est une valeur entre 0 (non) et 1 (oui) : { "type": "noul", "noul": 0.9 }.

choice : une option parmi plusieurs

"equipe": {
  "type": "choice",
  "instructions": "Quelle équipe doit traiter ce ticket ?",
  "criteria": {
    "facturation": "Paiements, remboursements, factures",
    "technique": "Bugs, pannes, intégrations",
    "compte": "Connexion, mots de passe, droits d'accès",
    "autre": null
  }
}

La réponse indique l'option retenue et la probabilité de chaque option :

{
  "type": "choice",
  "choice": "compte",
  "probabilities": { "compte": 0.6, "technique": 0.35, "facturation": 0.03, "autre": 0.02 },
  "confidence": 0.4
}

C'est de la classification zero-shot : vous décrivez vos catégories en langage naturel, sans entraînement préalable.

score : une note sur une échelle

"mecontentement": {
  "type": "score",
  "instructions": "Quel est le niveau de mécontentement du client ?",
  "criteria": ["Calme", "Agacé", "Très en colère"]
}

La réponse contient un score pondéré par les probabilités (il peut tomber entre deux niveaux), la légende et la probabilité de chaque niveau :

{
  "type": "score",
  "score": 1.1,
  "legend": { "0": "Calme", "1": "Agacé", "2": "Très en colère" },
  "probabilities": { "0": 0.1, "1": 0.7, "2": 0.2 },
  "confidence": 0.5
}

(Toutes les valeurs de cet article sont données à titre d'illustration.)

En pratique : Python

import os
import requests

reponse = requests.post(
    "https://api.strike-ai.eu/v1/systemone",
    headers={"Authorization": f"Bearer {os.environ['STRIKE_API_KEY']}"},
    json={
        "model": "strike-1",
        "state": {
            "sujet": "Impossible de me connecter",
            "message": "J'ai réinitialisé mon mot de passe deux fois, toujours refusé. Démo client dans une heure !",
        },
        "questions": {
            "equipe": {
                "type": "choice",
                "instructions": "Quelle équipe doit traiter ce ticket ?",
                "criteria": {
                    "facturation": "Paiements, remboursements, factures",
                    "technique": "Bugs, pannes, intégrations",
                    "compte": "Connexion, mots de passe, droits d'accès",
                    "autre": None,
                },
            },
            "urgent": {"type": "noul", "instructions": "Le client attend-il une réponse rapide ?"},
        },
    },
    timeout=30,
)
reponse.raise_for_status()
r = reponse.json()["answers"]

equipe = r["equipe"]["choice"]
proba = r["equipe"]["probabilities"][equipe]

# La règle est la vôtre : à ajuster sur vos propres données.
if proba >= 0.8 and equipe != "autre":
    file = equipe
else:
    file = "tri_manuel"
priorite = "haute" if r["urgent"]["noul"] >= 0.7 else "normale"

En pratique : JavaScript

const res = await fetch("https://api.strike-ai.eu/v1/systemone", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.STRIKE_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "strike-1",
    state: "Ma facture de septembre est en double, merci de corriger.",
    questions: {
      remboursement: {
        type: "noul",
        instructions: "Le client demande-t-il un remboursement ou une correction de facture ?",
      },
    },
  }),
});
if (!res.ok) throw new Error(`Erreur Strike ${res.status}`);
const { answers } = await res.json();
console.log(answers.remboursement.noul);

Aperçu : l'API Strike fonctionne actuellement en mode simulé. Les probabilités renvoyées aujourd'hui sont des valeurs factices, mais les formats de requête et de réponse sont ceux sur lesquels vous pouvez construire.

Une probabilité, ça se vérifie

Une probabilité n'est utile que si elle correspond à peu près à la fréquence réelle des bonnes réponses. Quand le modèle annonce 0,8, il devrait avoir raison environ 8 fois sur 10. C'est ce qu'on appelle le calibrage, et il se mesure, il ne se décrète pas. Avant de fixer un seuil en production :

  1. rassemblez quelques centaines d'exemples réels dont vous connaissez la bonne réponse ;
  2. comparez les probabilités renvoyées aux résultats observés (diagramme de fiabilité, score de Brier) ;
  3. fixez vos seuils à partir de ces mesures, puis revérifiez-les dès que vous changez de modèle, de formulation ou de langue.

Strike est en préversion. Nous ne revendiquons donc aucun calibrage pour strike-1 à ce stade.

Sortie structurée ou décision typée ?

| Besoin | Outil adapté | |---|---| | Extraire des champs, générer du contenu dans un schéma | Sortie structurée / mode JSON | | Répondre oui ou non, classer, noter | Décisions typées | | Savoir à quel point la réponse est sûre | Décisions typées |

Les deux approches se complètent très bien : la sortie structurée pour extraire les informations d'un document, les décisions typées pour décider de la suite.

Pour aller plus loin

← Tous les articles · Accueil

Hébergé dans l’UE (France). Les prompts ne sont pas conservés sauf si vous le demandez. strike-1 · v0.1 · Terms · Privacy · Legal notice