Noul, Choice, Score: los nuevos mosqueteros
Tres nuevas formas de llevar decisiones no deterministas al código sin renunciar al control.
Buena parte del software se ha construido alrededor de una idea bastante simple: si conocemos las reglas, podemos escribirlas.
if, else, switch, comparaciones, tablas de decisión. El programa recibe un estado y, si las condiciones están bien definidas, produce siempre el mismo resultado.
El problema aparece cuando las reglas empiezan a describir cosas que nosotros entendemos fácilmente, pero que son difíciles de convertir en condiciones explícitas.
¿Este mensaje es urgente?
¿Este cliente parece estar molesto?
¿Este contenido pertenece a tecnología o negocios?
¿Este ticket debería ir al equipo de facturación o al de soporte?
Podemos intentar escribir reglas para cada caso. El problema es que, a medida que aumenta la ambigüedad, el código empieza a convertirse en una colección cada vez más frágil de excepciones.
Ahí apareció la inteligencia artificial generativa.
Le damos contexto a un modelo y le pedimos que interprete el estado. El modelo puede entender matices que serían difíciles de expresar con reglas tradicionales y devolver una respuesta que, en muchos casos, resulta sorprendentemente buena.
Pero aparece otro problema.
El modelo responde con texto.
Y el programa necesita un tipo.
Del modelo al programa
Cuando usamos un LLM para tomar una decisión dentro de un programa, normalmente terminamos haciendo plomería alrededor del modelo.
Le enviamos un prompt, recibimos texto, intentamos interpretar ese texto, validamos que tenga la estructura esperada y finalmente convertimos el resultado en algo que el programa pueda utilizar.
Conceptualmente:
programa
↓
prompt
↓
LLM
↓
texto
↓
parser
↓
validación
↓
programa
Pedir JSON reduce el trabajo de interpretación, pero no elimina el contrato que el programa necesita. Todavía hay que validar estructura, valores permitidos y casos límite.
El problema no es solo que el modelo pueda equivocarse. Es que entiende y responde en un formato abierto, mientras que el programa necesita operar sobre datos con restricciones explícitas.
El modelo genera una interpretación. El programa necesita una decisión con tipo.
Y esa diferencia importa mucho cuando la respuesta de una IA deja de ser algo que una persona va a leer y se convierte en una condición que un programa debe ejecutar.
¿Y si la decisión tuviera un tipo?
Imaginemos que, en lugar de pedirle a un modelo que opine sobre un ticket, la pregunta ya incluyera las respuestas que el programa está dispuesto a aceptar.
La IA seguiría teniendo que interpretar el contexto. Esa parte no es determinista.
Pero el resultado ya no podría ser cualquier cosa.
No tendría que decirnos "parece bastante urgente" para que después nosotros intentemos averiguar qué significa.
La pregunta ya define el espacio de decisión.
Eso es lo que propone Jev.
En el artículo anterior comparé Jev con un if cuyo criterio no tenemos que expresar por completo. Su propuesta, descrita como System One Models, consiste en modelar decisiones para que el programa las consuma dentro de un dominio de salida definido.
No se trata solo de obtener texto más estructurado. Se trata de definir antes qué decisiones puede recibir el programa y reservar al código el control sobre qué hacer con ellas.
La diferencia parece pequeña.
No lo es.
Porque significa que la inteligencia deja de entrar al programa como texto y empieza a entrar como una decisión que ya tiene forma de dato.
programa
↓
pregunta tipada
↓
Jev
↓
decisión tipada
↓
programa
No estamos eliminando la incertidumbre.
Estamos poniendo límites a dónde puede existir.
Tres formas de preguntar
Jev plantea tres primitivas que me parecen especialmente interesantes porque obligan a pensar la IA de una manera diferente: Noul, Choice y Score.
No son simplemente tres tipos de respuesta.
Son tres formas de formular una decisión.
Tres mosqueteros, cada uno con su especialidad.
Para verlas trabajar, voy a usar el mismo ticket de soporte en las tres.
Noul: ¿se cumple?
Noul sirve para expresar una proposición.
¿El mensaje solicita un reembolso?
La respuesta es un valor entre 0 y 1 que representa el grado de respaldo que el modelo asigna a esa proposición. No debe confundirse automáticamente con una probabilidad estadística perfectamente calibrada.
Puede devolver 0.52 o 0.97.
El programa no tiene que traducir "probablemente": recibe un valor sobre el que puede definir una política.
Por ejemplo:
si reembolso >= 0.97
procesar el reembolso
si no
enviar a revisión
Un 0.52 no es un sí tímido.
Es otra rama.
Eso puede parecer trivial, pero es exactamente lo que normalmente terminamos haciendo cuando usamos un LLM: pedirle que responda en lenguaje natural y luego tratar de convertir ese lenguaje en un booleano.
Con Noul, la evaluación queda expresada como un valor sobre el que el código puede establecer sus propios límites.
La inteligencia interpreta.
El código decide qué hacer con esa interpretación.
Choice: ¿cuál?
Hay decisiones que no son binarias.
El mismo ticket tiene que enrutarse:
¿A qué equipo corresponde?
[
"soporte",
"facturación",
"ventas"
]
El modelo tiene que interpretar el mensaje, pero nosotros definimos las opciones válidas.
Puede elegir facturación.
Puede evaluar las opciones con distintos grados de respaldo.
Pero marketing no puede aparecer como resultado porque no forma parte del espacio que definimos.
Eso es importante.
La IA no está definiendo el espacio de decisión. Está evaluando el estado dentro de un espacio que nosotros definimos.
Jev no tiene que inventar la opción. Nosotros la ponemos sobre la mesa.
Un Choice solo puede elegir dentro de las opciones que le permitimos.
La inteligencia está en la interpretación.
El control está en el tipo.
Score: ¿en qué grado?
Hay decisiones en las que las categorías no bastan.
Lo que importa es el grado.
¿Qué tan urgente es este ticket?
0 = baja
1 = moderada
2 = alta
Ahora la IA no tiene que inventar una descripción de la urgencia.
Tiene que ubicar el estado dentro de una escala que ya conocemos.
No estamos preguntando:
¿Qué opinas sobre este ticket?
Estamos preguntando:
Dentro de esta escala, ¿dónde cae este ticket?
Choice y Score se parecen si solo miramos la lista. La diferencia está en la relación entre los valores.
En Choice:
soporte ≠ facturación ≠ ventas
Son categorías. Una no es mayor o menor que otra.
En Score:
baja < moderada < alta
Los valores tienen un orden.
Esa diferencia es la que permite que el programa utilice una comparación como:
urgencia >= 2
No porque 2 sea simplemente un número, sino porque representa una posición dentro de una escala ordenada.
En un Choice, esa comparación no tendría ningún significado.
Y aquí aparece un detalle interesante.
Si defines tres niveles, 0, 1 y 2, esos son los niveles de la escala. Pero el score puede quedar entre ellos. Una urgencia podría resultar en 1.6, por ejemplo, situándose entre moderada y alta.
El 1.6 no es un cuarto nivel.
Es el resultado ponderado de la evaluación sobre la escala.
Eso permite algo que un Choice no puede expresar de la misma manera: no solo saber qué categoría describe mejor el estado, sino en qué posición de una escala ordenada se encuentra.
La diferencia importa
Podemos resumir las tres primitivas así:
Noul
¿Se cumple?
0 ───────────── 1
Choice
¿Cuál?
[soporte | facturación | ventas]
Score
¿En qué grado?
baja < moderada < alta
Las tres tienen algo en común.
La IA interpreta el contexto.
Pero el espacio de respuesta lo definimos nosotros.
No intentamos hacer determinista la inteligencia.
Hacemos explícitas las fronteras de la decisión.
El código conserva la última palabra
Jev no convierte una decisión ambigua en una decisión determinista.
La evaluación sigue siendo probabilística.
El modelo puede equivocarse.
Puede interpretar mal el contexto.
Puede asignar un valor que no refleje la realidad.
Lo que cambia es lo que hacemos con ese resultado.
El mismo ticket puede pasar por las tres preguntas. A grandes rasgos, el programa podría consumirlas así:
reembolso = Noul(¿solicita un reembolso?)
equipo = Choice(soporte, facturación, ventas)
urgencia = Score(baja, moderada, alta)
si reembolso < 0.97
enviar a revisión
si equipo == facturación y urgencia >= 2
escalar
si urgencia < 1
continuar flujo normal
si urgencia >= 1 y urgencia < 2
revisión prioritaria
La decisión de la IA no reemplazó al código.
Se convirtió en una entrada más para el código.
Y como cualquier otra entrada relevante, necesita una política alrededor: umbrales explícitos, registro de resultados, revisión de casos límite y una salida segura cuando el costo de equivocarse es alto.
Ese detalle es más importante que la capacidad de Jev para clasificar, puntuar o enrutar.
El valor está en poder usar una interpretación incierta sin entregar al modelo el control de las consecuencias.
Porque estamos empezando a tener una nueva categoría de componente.
Una decisión que no sabemos expresar completamente con reglas, pero cuyo resultado sí sabemos exactamente cómo consumir.
El tipo no vuelve correcta esa decisión.
La vuelve legible.
Si el caso necesita una explicación, si las opciones todavía no están estables, o si mandar el ticket al equipo equivocado sale caro, Noul, Choice y Score no alcanzan. Ahí sigue haciendo falta una persona, o un modelo capaz de argumentar.
Durante mucho tiempo hemos pensado el software casi como una dicotomía.
Si conocemos la regla, escribimos un if y sabemos qué ocurrirá.
Si el problema es ambiguo, le pasamos contexto a un LLM y recibimos texto que el programa todavía tiene que interpretar.
Noul, Choice y Score ocupan ese espacio de en medio: problemas cuya interpretación no sabemos describir por completo con reglas, pero cuyas consecuencias sí podemos limitar con precisión.
ambigüedad → decisión → código
En este ticket actúan como los nuevos tres mosqueteros: Noul evalúa una proposición, Choice selecciona una categoría y Score sitúa el caso en una escala.
Pero en la novela de Dumas los mosqueteros nunca fueron solo tres.
Estaba D’Artagnan.
Aquí también hay un cuarto.
Los tres interpretan.
El código decide qué hacer con lo que interpretan.
Y conserva la última palabra.
Tal vez el futuro de la IA aplicada al software no consista en hacer que los modelos hablen cada vez mejor.
Tal vez consista en hacer que sepan responder exactamente a las preguntas que nuestro código necesita hacer.