Clef de Cloudflare: modelos de decisión en tu backend Bun
Cuenta cuántas llamadas a LLM hay en tu backend que solo sirven para elegir una opción de una lista. Clasificar un ticket, decidir si un comentario es spam, elegir a qué cola va una tarea. Cloudflare anunció Clef, una familia de modelos de decisión con pesos abiertos, junto con una plataforma de fine-tuning por refuerzo (RL). La propuesta apunta justo a ese tipo de llamada. Como mi backend del día a día corre en Bun, quiero mirar el anuncio desde ese ángulo: qué cambia para quien escribe TypeScript y solo quiere que la decisión salga correcta, rápida y barata.
El anuncio original está en el blog de Cloudflare. Ahí encuentras los detalles de tamaño, licencia y benchmarks. Aquí va mi lectura como dev.
Qué lanzó Cloudflare con Clef
Son dos piezas.
La primera son los modelos. Son open-weight, o sea, puedes descargar los pesos y correrlos donde quieras, sin depender de la API de nadie. El foco no es conversar ni escribir redacciones. El foco es tomar una decisión a partir de una entrada y un conjunto de opciones.
La segunda es la plataforma de fine-tuning por refuerzo. En lugar de armar un dataset gigante con "entrada → respuesta correcta", le enseñas al modelo poniéndole nota a lo que decidió. Si acierta, gana recompensa. Si falla, la pierde. Con el tiempo el modelo se ajusta a tu dominio.
Lo interesante es la combinación. Modelos abiertos solos ya hay de sobra. Una plataforma de RL sola es cosa de equipos de ML. Las dos juntas, empaquetadas para quien ya usa Workers, se convierten en una herramienta de producto.
Qué es un modelo de decisión, en la práctica
Piensa en un endpoint de triaje de soporte. Hoy mucha gente lo resuelve así: manda el texto del ticket a un LLM grande con un prompt tipo "responde solo con una de estas categorías: bug, billing, feature, spam". Después parsea la respuesta, reza para que no llegue "¡Claro! La categoría es bug." y maneja el caso en que el modelo se inventa una quinta categoría.
Funciona. Pero estás pagando un modelo que sabe escribir sonetos para que te devuelva una palabra.
Un modelo de decisión le da la vuelta a eso. Recibe la entrada y las opciones y devuelve una elección. La salida ya nace estructurada. No hay parseo de texto libre, no hay respuestas creativas, no hay "como modelo de lenguaje...".
La mayoría de las llamadas a LLM en producción son una decisión disfrazada de texto.
Esa es mi opinión fuerte del día. Si abres los logs de tu app y cuentas, apuesto a que más de la mitad de las llamadas terminan en un switch o en un if. Para esos casos, generar texto es un desperdicio de latencia, de tokens y de paciencia.
Por qué el fine-tuning por RL le interesa a quien no es de ML
El fine-tuning tradicional asusta porque pide un dataset etiquetado. Necesitas miles de ejemplos con la respuesta correcta, limpios y balanceados. Casi ningún equipo de producto tiene eso listo.
RL cambia la pregunta. No necesitas saber la respuesta correcta de antemano. Necesitas poder decir, después, si la decisión fue buena. Y eso tu sistema muchas veces ya lo sabe:
- ¿El agente de soporte movió el ticket a otra cola? La decisión fue mala.
- ¿El usuario hizo clic en la recomendación? Fue buena.
- ¿Un moderador restauró el comentario marcado como spam? Fue mala.
Esa señal ya está en tu base de datos. Solo que no se está usando para entrenar nada. Con una plataforma de RL gestionada, ese historial se vuelve materia prima. Para un dev full stack, este es el punto que hace que la noticia pase de "qué bien, otro modelo más" a "ok, esto sí lo puedo usar".
Cómo encaja esto en un backend Bun
Voy a mostrar el flujo con un ejemplo de triaje. El formato exacto del payload y el nombre del modelo están en la documentación de Cloudflare. Aquí el código es ilustrativo, para mostrar cómo encaja.
Primero, la llamada. Del lado de Bun es solo un fetch a la API REST de Workers AI:
// triage.ts
const options = ["bug", "billing", "feature", "spam"] as const
type Choice = (typeof options)[number]
const url = `https://api.cloudflare.com/client/v4/accounts/${Bun.env.CF_ACCOUNT}/ai/run/${Bun.env.DECISION_MODEL}`
export async function triage(ticket: string): Promise<Choice> {
const res = await fetch(url, {
method: "POST",
headers: { Authorization: `Bearer ${Bun.env.CF_TOKEN}` },
body: JSON.stringify({ input: ticket, options }), // formato ilustrativo
})
if (!res.ok) throw new Error(`la decisión falló: ${res.status}`)
const { result } = await res.json()
return result.choice
}
Fíjate que no hay regex para limpiar la respuesta ni un JSON.parse dentro de un try rezando. El tipo Choice cierra el contrato.
Segundo, y aquí es donde la mayoría se va a saltar un paso: guardar la decisión y el resultado. Sin eso, el RL no tiene de qué aprender. Con el Bun.sql nativo y Postgres, queda corto:
import { sql } from "bun"
// cuando decide
const [row] = await sql`
insert into decisions (input, choice, model)
values (${ticket}, ${choice}, ${Bun.env.DECISION_MODEL})
returning id
`
// cuando un humano corrige (o no) la cola
await sql`
update decisions
set reward = ${finalQueue === choice ? 1 : 0}
where id = ${row.id}
`
Es menos planificación de la que parece. Una columna reward y ya tienes el esqueleto de un ciclo de mejora continua. Dentro de un mes, ese historial alimenta el fine-tuning, cambias el DECISION_MODEL por la versión ajustada y comparas la tasa de acierto. Variable de entorno, deploy, listo.
Y como los pesos son abiertos, hay un plan B. Si algún día Cloudflare cambia precios o términos, tomas el modelo ajustado y lo corres en otro lado. Eso no es un detalle. Ya vi equipos reescribir medio backend porque el proveedor de IA cambió la API de un mes para otro.
Dónde tendría mis dudas
No todo es fiesta. Algunos puntos que revisaría antes de llevarlo a producción:
Una mala señal de recompensa genera un mal modelo. RL optimiza exactamente lo que mides. Si tu recompensa es "el usuario hizo clic", el modelo aprende a generar clics, no a generar valor. Clásico. Piensa dos veces qué significa "acertar" en tu dominio.
El volumen importa. Si tu endpoint toma 30 decisiones al día, el historial va a tardar mucho en enseñar algo. Con poco volumen, un prompt bien escrito en un modelo genérico todavía resuelve, y con menos piezas móviles.
Lock-in disfrazado. Los pesos son abiertos, pero la plataforma de RL es de Cloudflare. Si tu pipeline de entrenamiento depende de ella, la parte más valiosa (el proceso de mejora) sigue atada. Vale la pena ver si puedes exportar el modelo ajustado y los datos de entrenamiento sin dolor.
Decidir no lo es todo. Si la tarea pide explicación, resumen o texto para el usuario final, un modelo de decisión no sirve. Es un bisturí, no una navaja suiza. Y está bien, solo no intentes que escriba el email de respuesta al cliente.
¿Vale la pena probar Clef?
Para mí, sí, con un recorte claro. Toma una decisión de tu sistema que corra con un volumen decente y que ya tenga una señal natural de acierto o error. Triaje, moderación, enrutamiento. Pon el modelo de decisión al lado de lo que usas hoy, loguea todo y compara acierto, latencia y costo por cada mil llamadas durante dos semanas.
Si empata en acierto y gana en latencia y costo, acabas de sacar un modelo gigante de un lugar donde nunca debió estar. Si pierde, te quedas con una tabla decisions con datos reales, que ya es mejor que el tanteo de antes.
Lo que más me gusta de este anuncio es que empuja la conversación sobre IA hacia un lugar más honesto. Menos "agente que lo hace todo", más "función que decide bien una sola cosa". Es el tipo de IA que cabe en un backend de verdad, con tipos, tests y métricas. Si quieres ver cómo suelo estructurar este tipo de integración en proyectos reales, échale un vistazo a mis proyectos.
Resumen para LinkedIn
La mayoría de las llamadas a LLM en producción son solo una decisión disfrazada de texto. Clasificar un ticket, marcar spam, elegir una cola. Pagamos un modelo que escribe sonetos para que nos devuelva una palabra. Cloudflare lanzó Clef: modelos de decisión con pesos abiertos y fine-tuning por refuerzo. Lo bueno es que no necesitas un dataset etiquetado. La señal de acierto ya está en tu base de datos: el ticket que cambió de cola, el comentario que el moderador restauró. En mi backend Bun, todo esto se reduce a un fetch tipado y una columna reward en Postgres. Menos "agente que lo hace todo", más "función que decide bien una sola cosa". Escribí mi análisis completo en el blog, con código y los puntos que me generan dudas. #Cloudflare #IA #TypeScript #Bun #Backend