Puntuar lo que las reglas no alcanzan
Un modelo juez es una segunda llamada que puntúa la salida de la primera según una rúbrica. Importa porque una regla comprueba una etiqueta, pero no si una frase es amable o útil.
La frase que el clasificador envía al cliente es el caso típico: ninguna regla dice si es amable.
Al final de esta lección
Nota
Puedo pedir a un modelo que puntúe una respuesta y mantener esa puntuación comprobable.
Palabras clave
| Palabra | Qué significa | Ejemplo |
|---|---|---|
| Juez | una llamada a un modelo que puntúa otra respuesta | |
| Rúbrica | la escala fija y lo que significa cada nota | |
| Formato de salida | la forma exacta en que debe responder el juez | nota y motivo |
| Validar | comprobar una respuesta contra el formato con código | |
| Nota grabada | una nota guardada con su respuesta y su motivo |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Un juez necesita una rúbrica con niveles
No preguntes a un juez «¿está bien?». Dale una rúbrica: una escala corta donde cada nota dice qué significa. Tres niveles bastan para empezar.
Di qué debe mirar y qué debe ignorar. Una escala vaga da notas vagas.
Ejemplo resuelto: una rúbrica de tres niveles
| Nota | Significado |
|---|---|
| 3 | Clara, amable y sin promesas de dinero |
| 2 | Aceptable, pero vaga o fría |
| 1 | Inútil, grosera o promete dinero |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Fija el formato y valida cada nota
Di al juez que responda con una sola forma, aquí JSON con una nota y un motivo corto. Luego valida con código: se lee como JSON, la nota es un entero de 1 a 3 y el motivo está.
Una nota que falla la comprobación no está puntuada. Cuéntala. Nunca adivines lo que quiso decir el juez.
Guarda la nota junto a lo que puntúa
Guarda cada nota grabada con la respuesta, el motivo y la rúbrica usada. Un número suelto no se puede discutir después.
Si el servicio admite una temperatura, el ajuste que controla cuánto varía el modelo, fíjala baja para que el juez varíe menos. Aun así varía, y por eso la lección siguiente lo prueba contra personas.
Escribe el ayudante del modelo, parte 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' 'async function ask(messages, sample, max) {' ' try {' ' const res = await fetch(base + \"/v1/chat/completions\", {' ' method: \"POST\",' ' headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },' ' body: JSON.stringify({ model: \"lab\", messages: messages, max_tokens: max || 200 }),' ' signal: AbortSignal.timeout(30000),' ' });' ' if (res.ok === false) throw new Error(\"estado \" + res.status);' ' return (await res.json()).choices[0].message.content;' > model.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribe el ayudante del modelo, parte 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' } catch (error) {' ' console.log(\"Muestra grabada: ningún modelo respondió (\" + error.message + \").\");' ' const list = [].concat(sample);' ' const turn = messages.filter(function (m) { return m.role === \"assistant\"; }).length;' ' return list[Math.min(turn, list.length - 1)];' ' }' '}' 'module.exports = { ask };' >> model.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribe el script del juez, parte 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' 'const rubric = \"Puntúa de 1 a 3 la respuesta que una tienda envió a un cliente. 3: clara, amable y sin promesas de dinero. 2: aceptable pero vaga o fría. 1: inútil, grosera o promete dinero. Responde solo con JSON de dos claves: grade (1, 2 o 3) y reason (menos de 15 palabras).\";' > judge4.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribe el script del juez, parte 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function validate(text) {' ' let data = null;' ' try { data = JSON.parse(text); } catch (error) { return { ok: false, why: \"no es JSON\" }; }' ' if (data === null || Array.isArray(data) || typeof data === \"string\" || typeof data === \"number\") return { ok: false, why: \"no es un objeto\" };' ' if (Number.isInteger(data.grade) === false || data.grade < 1 || data.grade > 3) return { ok: false, why: \"nota no válida\" };' ' const reason = typeof data.reason === \"string\" ? data.reason.trim() : \"\";' ' if (reason.length === 0 || reason.length > 100) return { ok: false, why: \"motivo no válido\" };' ' return { ok: true, grade: data.grade, reason: reason };' '}' >> judge4.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribe el script del juez, parte 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function judged(grade, reason) {' ' return JSON.stringify({ grade: grade, reason: reason });' '}' 'const recorded = [' ' judged(2, \"Amable pero vaga.\"),' ' judged(1, \"Promete dinero.\"),' ' \"Yo le pondría un 3\",' ' judged(5, \"Grosera.\"),' ' judged(3, \"Clara y amable, sin promesas.\"),' '];' 'let total = 0;' 'let valid = 0;' 'recorded.forEach(function (text, at) {' ' const result = validate(text);' ' if (result.ok) { total = total + result.grade; valid = valid + 1; }' ' console.log(\"Grabada \" + (at + 1) + \": \" + (result.ok ? \"nota \" + result.grade + \" (\" + result.reason + \")\" : \"no válida, \" + result.why));' '});' 'console.log(\"Válidas: \" + valid + \" de \" + recorded.length + \". Nota media de las válidas: \" + (total / valid).toFixed(1).replace(\".\", \",\"));' >> judge4.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribe el script del juez, parte 4
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' const sample = judged(1, \"Promete dinero.\");' ' const text = await ask([{ role: \"user\", content: rubric + \"\n\nRespuesta que puntuar: Tu reembolso ha sido aprobado.\" }], sample, 80);' ' const result = validate(text);' ' console.log(\"El juez dijo: \" + text);' ' console.log(\"Juez: \" + (result.ok ? \"nota \" + result.grade : \"no válida, \" + result.why));' '}' 'main();' >> judge4.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Valida cinco notas y una real
docker exec -w /home/node/project agent node judge4.jsSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Una salida posible
| Línea | Qué muestra |
|---|---|
| Grabadas 1 y 2 | Notas válidas 2 y 1, cada una con un motivo |
| Grabada 3 | No válida, no es JSON |
| Grabada 4 | No válida, nota no válida: 5 se sale de la escala |
| Grabada 5 | Nota válida 3 |
| Línea Válidas | 3 de 5, nota media 2,0 |
| Juez | Una nota y un motivo, o no válida. Puede cambiar en cada ejecución |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Pruébalo
Consejo
En judge4.js cambia la respuesta que puntuar en main por Lo estamos mirando. Ejecuta otra vez y lee la nota y el motivo.
Un error frecuente
Advertencia
Mucha gente confía en un juez porque sus motivos suenan seguros. Un motivo fluido no es una prueba. El juez es un modelo y puede equivocarse con aplomo. Comprueba su formato con código y sus notas contra las de personas.