Documentation

Questions et réponses

Une question Laya est une décision typée, et le type décide à la fois de ce que tu demandes et de ce que tu récupères. Il y en a trois, et un seul état peut les porter toutes en une seule passe avant :

import laya

agent = laya.load("convaiinnovations/laya")

questions = {
    "dept": {"type": "choice", "instructions": "Which team should handle this?",
             "criteria": {"billing": "money and invoices",
                          "technical": "bugs and outages",
                          "sales": "pricing and contracts"}},
    "urgent": {"type": "noul", "instructions": "Is this urgent?"},
    "severity": {"type": "score", "instructions": "How severe is this?",
                 "criteria": ["trivial", "minor", "moderate", "serious", "critical"]},
}

result = agent.system_one({"text": "I was charged twice and nobody has replied for a week. "
                                   "Please refund me."}, questions)
result["answers"]["dept"]["choice"]        # 'billing'
result["answers"]["urgent"]["noul"]        # 0.8727
result["answers"]["severity"]["score"]     # 2.9046

Une seule passe avant répond aux trois. C’est tout l’intérêt de l’interface typée : une question noul n’est pas un choice à deux options qui se trouvent être « oui » et « non » — c’est une tête différente avec une forme de sortie différente, et le type dit à Laya laquelle utiliser.

Les trois types

choice — choisir un élément d’un ensemble

{"type": "choice",
 "instructions": "Which team should handle this?",
 "criteria": {"billing": "money and invoices", "technical": "bugs and outages"}}

criteria est un mapping ordonné de libellé vers description. L’ordre est positionnel dans la question rendue, donc deux questions avec les mêmes libellés dans un ordre différent sont des questions différentes. Une description est optionnelle ; {"billing": None} rend le libellé tout seul. Les libellés sont renvoyés exactement comme tu les as écrits, donc un libellé non-chaîne revient tel quel dans choice et comme clé dans probabilities.

La description vaut la peine d’être écrite. Ce n’est pas de la décoration : le texte de question rendu est ce que lit le modèle, donc un {"a": None, "b": None} nu ne lui donne rien pour distinguer les options.

{"type": "choice", "choice": "billing",
 "probabilities": {"billing": 0.9881, "technical": 0.0057, "sales": 0.0062},
 "confidence": 0.9339, "answer_confidence": 0.9881,
 "action": {"act_probability": 1.0}}

noul — oui ou non

{"type": "noul", "instructions": "Is this urgent?"}

noul est le nom du projet pour une décision à deux voies, et sa réponse est la probabilité de vrai, pas un booléen seuillé :

{"type": "noul", "noul": 0.8727, "confidence": 0.8727, "answer_confidence": 0.8727,
 "action": {"act_probability": 1.0}}

Le seuil est à toi de choisir, parce qu’il dépend de ce que te coûte un faux positif. Il n’y a pas de champ bool à confondre avec une décision.

Tu peux relibeller les deux options quand la décision n’est pas naturellement un oui/non — labels prend exactement les clés false et true, et la polarité est inchangée : noul est toujours P(true).

{"type": "noul", "instructions": "Does this need a human?",
 "labels": {"false": "automatic", "true": "escalate"}}

Les libellés ne sont pas un moyen de corriger une question que le modèle se trompe. noul suit ses propres libellés d’option, le plus fortement sur le checkpoint anglais, donc une paire de libellés qui se lit comme une décision (« approve » / « reject ») peut tirer la réponse vers le libellé plutôt que vers l’état. Valide tout relibellage sur tes propres données avant de t’y fier.

score — un niveau ordonné

{"type": "score", "instructions": "How severe is this?",
 "criteria": ["trivial", "minor", "moderate", "serious", "critical"]}

criteria est une liste ordonnée, et elle doit être ordonnée par ordre croissant — la position est l’échelle.

{"type": "score", "score": 2.9046,
 "legend": {"0": "trivial", "1": "minor", "2": "moderate", "3": "serious", "4": "critical"},
 "probabilities": {"0": 0.0134, "1": 0.05, "2": 0.0518, "3": 0.7881, "4": 0.0967},
 "confidence": 0.5187, "answer_confidence": 0.7881,
 "action": {"act_probability": 1.0}}

score est une valeur attendue, pas le niveau le plus probable. Ci-dessus, score vaut 2.90 alors que le niveau le plus probable unique est serious (3) à 0.788. Les deux sont utiles et répondent à des questions différentes : la valeur attendue minimise l’erreur quadratique sur l’échelle, l’argmax minimise le désaccord avec le modèle. Si tu veux le libellé, prends l’argmax de probabilities ou lis la contrepartie de answer_confidence dans la légende — n’arrondis pas score en supposant que c’est le libellé. legend existe pour que tu n’aies jamais à deviner quel index signifie quoi.

Lire la confiance

Chaque réponse porte deux nombres de confiance, et ils mesurent des choses différentes.

champ ce que c’est gater dessus ?
answer_confidence max(p) — la probabilité de la réponse rapportée oui, après ajustement
confidence 1 - H(p) / log(k) — à quel point toute la distribution est concentrée non
probabilities la distribution complète (choice, score) —

answer_confidence est la quantité que l’échelonnage de température ajuste et sur laquelle chaque chiffre de calibration du dépôt est calculé, ce qui en fait celle sur laquelle gater. Elle n’est pas calibrée telle quelle : la propriété qu’on lui attribue d’habitude — que sur les réponses renvoyées à la confiance c, environ c d’entre elles sont correctes — ne tient qu’une fois les températures ajustées et validées sur des données mises de côté pour ton checkpoint à ton nombre d’options. Les checkpoints livrés sont trop confiants et l’écart dépend du nombre d’options, donc un seuil non ajusté peut sélectionner en dessous de l’exactitude du modèle lui-même (#394).

# THRESHOLD is a number you measured on your own held-out data, not one the model ships.
# Fit and validate the temperatures first — the fine-tuning notebook has the loop:
#   notebooks/laya_finetune_typed_decisions_2xT4_kaggle.ipynb
ans = result["answers"]["dept"]
if ans["answer_confidence"] >= THRESHOLD:
    ...

confidence est l’entropie normalisée : élevée quand la distribution est pointue, basse quand elle est étalée, que la réponse du haut soit correcte ou non. C’est un signal utile et il n’est pas sur la même échelle, donc les deux ne doivent pas être gatés contre un seul nombre :

# the same three answers, and the two numbers are not the same
dept      confidence 0.9339   answer_confidence 0.9881
urgent    confidence 0.8727   answer_confidence 0.8727
severity  confidence 0.5187   answer_confidence 0.7881

Pour noul, ils sont égaux par construction — sur deux options, max(p, 1-p) vaut max(p) — donc une réponse noul ne peut pas te dire lequel des deux tu as lu. Les deux clés sont présentes sur chaque type, pour que le choix soit explicite plutôt qu’implicite.

Un seuil est une politique, pas une propriété du modèle. Les deux checkpoints sont livrés trop confiants, et à quel point dépend du nombre d’options, donc un nombre mesuré sur une question à 3 options ne se transfère pas à une question à 20 options. Mesure-le sur tes propres données ; la section Calibration de BENCHMARKS.md a la boucle d’ajustement et les valeurs ajustées.

action et act_probability

action.act_probability est le score d’une tête séparée pour « un agent devrait-il agir là-dessus du tout », distinct de la confiance de la réponse elle-même. Il est rapporté pour chaque type de question. Rien dans la bibliothèque ne le met en seuil pour toi.

Presets

Trois ensembles de questions tout faits, pour que les cas courants n’aient pas besoin de critères écrits à la main :

from laya import triage_questions, guard_questions, moderation_questions

agent.system_one(ticket, triage_questions())

Utilise-les comme point de départ plutôt que comme contrat — lis les questions qu’ils produisent avec render_options et vérifie que les libellés conviennent à ton domaine avant de les livrer.

Relire les options

Comme l’ordre des options est positionnel et que le texte des options est ce que lit le modèle, il vaut la peine de pouvoir voir exactement ce qui a été envoyé :

from laya import render_options

render_options({"t": "choice", "crit": {"billing": None, "sales": "pricing"}})
# ['billing', 'sales: pricing']

render_options({"t": "score", "crit": ["low", "high"]})
# ['level 0: low', 'level 1: high']

Les mêmes libellés dans un ordre différent se rendent dans cet ordre, c’est pourquoi l’ordre fait partie de l’identité de la question :

render_options({"t": "choice", "crit": {"x": "first", "y": "second"}})
# ['x: first', 'y: second']
render_options({"t": "choice", "crit": {"y": "second", "x": "first"}})
# ['y: second', 'x: first']

Note les clés. render_options prend la forme interne à clés courtes {"t": ..., "crit": ...}, pas la forme {"type": ..., "criteria": ...} que tu écris dans une question — passer la forme publique lève KeyError: 't'.

La conversion est un mapping court et stable que tu peux inliner, ce qui évite d’aller chercher dans une aide privée — Agent._to_internal est interne et peut changer :

def as_internal(q):
    """The short-key shape `render_options` reads, from a question as you wrote it."""
    crit = q.get("criteria")
    if q["type"] == "choice" and isinstance(crit, list):
        crit = {c: None for c in crit}
    return {"t": q["type"], "ins": q["instructions"], "crit": crit}

render_options(as_internal(question))

Cela reflète ce que fait la bibliothèque pour une question choice écrite comme liste de libellés ; un dict criteria et une liste score passent tels quels.

Limites à connaître avant de concevoir autour de ça

  • La confiance n’est pas une garantie de justesse à nombre d’options élevé. Sur une question à 20 options, les distributions des réponses justes et fausses se recouvrent fortement, et un seuil peut finir par sélectionner en dessous de l’exactitude du modèle lui-même. Voir #394.
  • La négation n’est pas gérée de façon fiable dans les questions à choix forcé. Une question d’annulation peut renvoyer le libellé d’annulation pour un état qui dit de ne pas annuler, avec une confiance élevée, sur les deux checkpoints. Voir #377.
  • noul peut suivre ses libellés plutôt que l’état, donc valide tout relibellage.
  • Plus d’options n’est pas gratuit. Au-delà d’environ 20, le modèle se dégrade vite ; utilise l’aide de présélection pour réduire un grand espace de libellés avant de demander, ou découpe-le en une question grossière et une fine.