Documentación

Preguntas y respuestas

Una pregunta de Laya es una decisión tipada, y el tipo decide tanto lo que preguntas como lo que recibes. Hay tres, y un mismo estado puede llevarlas todas en una sola pasada hacia adelante:

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

Una sola pasada hacia adelante responde a las tres. Ese es el sentido de la interfaz tipada: una pregunta noul no es un choice con dos opciones que resultan ser “sí” y “no” — es una cabeza distinta con una forma de salida distinta, y el tipo le dice a Laya cuál usar.

Los tres tipos

choice — elegir una de un conjunto

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

criteria es un mapeo ordenado de etiqueta a descripción. El orden es posicional en la pregunta renderizada, así que dos preguntas con las mismas etiquetas en distinto orden son preguntas distintas. Una descripción es opcional; {"billing": None} renderiza solo la etiqueta. Las etiquetas se devuelven exactamente como las escribiste, así que una etiqueta no textual vuelve como sí misma en choice y como la clave en probabilities.

Vale la pena escribir la descripción. No es decoración: el texto renderizado de la pregunta es lo que lee el modelo, así que un {"a": None, "b": None} escueto no le da nada para distinguir las opciones.

{"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 — sí o no

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

noul es el nombre del proyecto para una decisión binaria, y su respuesta es la probabilidad de verdadero, no un booleano con umbral:

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

El umbral lo eliges tú, porque depende de lo que te cueste un falso positivo. No hay ningún campo bool que se pueda confundir con una decisión.

Puedes reetiquetar las dos opciones cuando la decisión no sea naturalmente un sí/no — labels toma exactamente las claves false y true, y la polaridad no cambia: noul sigue siendo P(true).

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

Las etiquetas no son una forma de arreglar una pregunta que el modelo responde mal. noul sigue sus propias etiquetas de opción, con más fuerza en el checkpoint inglés, así que un par de etiquetas que se lea como una decisión (“approve” / “reject”) puede arrastrar la respuesta hacia la etiqueta en lugar de hacia el estado. Valida cualquier reetiquetado con tus propios datos antes de confiar en él.

score — un nivel ordenado

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

criteria es una lista ordenada, y debe estar ordenada de forma ascendente — la posición es la escala.

{"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 es un valor esperado, no el nivel más probable. Arriba, score es 2.90 mientras que el único nivel más probable es serious (3) con 0.788. Ambos son útiles y responden a preguntas distintas: el valor esperado minimiza el error cuadrático sobre la escala, y el argmax minimiza la discrepancia con el modelo. Si quieres la etiqueta, toma el argmax de probabilities o lee la contraparte de answer_confidence en la leyenda — no redondees score y asumas que es la etiqueta. legend existe para que nunca tengas que adivinar qué significa cada índice.

Leer la confianza

Cada respuesta lleva dos números de confianza, y miden cosas distintas.

campo qué es ¿aplicar gating sobre él?
answer_confidence max(p) — la probabilidad de la respuesta que se informa sí, tras el ajuste
confidence 1 - H(p) / log(k) — cuán concentrada está toda la distribución no
probabilities la distribución completa (choice, score) —

answer_confidence es la cantidad que ajusta el escalado por temperatura y sobre la que se calcula cada cifra de calibración del repositorio, que es lo que la convierte en la cantidad sobre la que aplicar gating. No está calibrada tal como se distribuye: la propiedad que se le suele atribuir — que de las respuestas devueltas con confianza c, aproximadamente c son correctas — solo se sostiene una vez que las temperaturas se han ajustado y validado sobre datos reservados para tu checkpoint con tu número de opciones. Los checkpoints publicados son demasiado confiados y cuánto depende del número de opciones, así que un umbral sin ajustar puede seleccionar por debajo de la propia precisión del modelo (#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 es la entropía normalizada: alta cuando la distribución está concentrada, baja cuando está repartida, sin importar si la respuesta principal es correcta. Es una señal útil y no está en la misma escala, así que las dos no deben compararse contra un solo número:

# 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

Para noul son iguales por construcción — sobre dos opciones, max(p, 1-p) es max(p) — así que una respuesta noul no puede decirte cuál de las dos has estado leyendo. Ambas claves están presentes en todos los tipos para que la elección sea explícita en lugar de implícita.

Un umbral es una política, no una propiedad del modelo. Ambos checkpoints se distribuyen demasiado confiados, y cuánto depende del número de opciones, así que un número medido en una pregunta de 3 opciones no se traslada a una de 20. Mídelo con tus propios datos; la sección de Calibración de BENCHMARKS.md tiene el bucle de ajuste y los valores ajustados.

action y act_probability

action.act_probability es la puntuación de una cabeza aparte para “¿debería un agente actuar sobre esto en absoluto?”, distinta de la confianza de la propia respuesta. Se informa para todos los tipos de pregunta. Nada en la biblioteca le aplica un umbral por ti.

Presets

Tres conjuntos de preguntas ya listos, para que los casos comunes no necesiten criterios escritos a mano:

from laya import triage_questions, guard_questions, moderation_questions

agent.system_one(ticket, triage_questions())

Úsalos como punto de partida más que como contrato — lee las preguntas que producen con render_options y comprueba que las etiquetas encajan en tu dominio antes de ponerlos en producción.

Volver a leer las opciones

Como el orden de las opciones es posicional y el texto de las opciones es lo que lee el modelo, vale la pena poder ver exactamente lo que se envió:

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']

Las mismas etiquetas en distinto orden se renderizan en ese orden, que es por lo que el orden forma parte de la identidad de la pregunta:

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']

Fíjate en las claves. render_options toma la forma interna de claves cortas {"t": ..., "crit": ...}, no la forma {"type": ..., "criteria": ...} que escribes en una pregunta — pasar la forma pública lanza KeyError: 't'.

La conversión es un mapeo corto y estable que puedes integrar, lo que evita recurrir a un ayudante privado — Agent._to_internal es interno y puede cambiar:

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))

Eso refleja lo que hace la biblioteca para una pregunta choice escrita como lista de etiquetas; un diccionario criteria y una lista score pasan sin cambios.

Límites que conviene conocer antes de diseñar sobre esto

  • La confianza no es una garantía de corrección con muchas opciones. En una pregunta de 20 opciones las distribuciones de las respuestas correctas e incorrectas se solapan mucho, y un umbral puede acabar seleccionando por debajo de la propia precisión del modelo. Consulta #394.
  • La negación no se maneja de forma fiable en preguntas de opción forzada. Una pregunta de cancelación puede devolver la etiqueta de cancelación para un estado que dice no cancelar, con alta confianza, en ambos checkpoints. Consulta #377.
  • noul puede seguir sus etiquetas en lugar del estado, así que valida cualquier reetiquetado.
  • Más opciones no es gratis. Pasadas unas 20, el modelo se degrada rápido; usa el ayudante de preselección para reducir un espacio grande de etiquetas antes de preguntar, o divídelo en una pregunta gruesa y otra fina.