¿Te resulta más fácil leer con fondo crema, un tono de color, más contraste o un texto más grande? Elige tus colores y tu texto una sola vez, para todas las páginas.

Lección gratuita

Un conjunto de evaluación para texto a SQL

Después de esta lección: Puedo puntuar consultas candidatas con un conjunto de preguntas de respuesta conocida.

Lección de 70 min10 comandosLaboratorio de agente de programación con IA: usa opencode para explicar y corregir código, y comprueba su trabajo

Cómo probar una función de texto a SQL

Un conjunto de evaluación para texto a SQL es una lista de preguntas con respuestas conocidas. Se vuelve a ejecutar cada vez que cambia el prompt o el modelo. Te dice si un cambio ha mejorado o estropeado algo.

Los módulos 68 y 85 puntúan respuestas en palabras. Esta lección puntúa SQL por las filas que devuelve.

Al final de esta lección

Nota

Puedo puntuar consultas candidatas con un conjunto de preguntas de respuesta conocida.

Palabras clave

PalabraQué significa
Conjunto de evaluaciónpreguntas con respuesta conocida que se repiten en cada cambio
Consulta de referenciauna consulta comprobada a mano que da las filas correctas
Coincidencia por resultadolas mismas filas que la consulta de referencia
Regresiónuna pregunta que antes pasaba y ahora falla
Controluna regla que bloquea un cambio

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Cada caso tiene una pregunta y una referencia

Un caso guarda una pregunta, una consulta de referencia y si el orden importa o no. Escribe tú la referencia y comprueba sus filas a mano con los datos de prueba.

Ejecuta la referencia para obtener las filas esperadas. Así el conjunto sigue funcionando cuando crecen los datos, mientras las referencias sigan siendo correctas.

Ejemplo resuelto: cinco casos de la biblioteca

CasoPreguntaFilas de referencia
1Préstamos hechos en el mes 34
2Socios distintos en el mes 33
3Libros que siguen prestados5
4Títulos que tomó el socio 2Mapa de la lluvia, Motores en calma, Siete teteras
5Libros que nunca se han prestadoAl norte del mediodía

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Puntúa por el resultado, no por el texto

COUNT(id) y COUNT(*) dan aquí el mismo número, y una unión se puede escribir como subconsulta. Dos textos distintos pueden ser correctos los dos.

Por eso ejecuta cada candidata y compara sus filas con las de referencia. Comparar el texto exacto marca como fallidas respuestas correctas, como muestra el Run.

Ejemplo resuelto: texto y resultado no coinciden

CandidataMismo texto que la referenciaMismas filas que la referencia
SELECT COUNT(id) FROM loans WHERE month = 3NoSí
Una unión escrita como IN (SELECT book_id ...)NoSí
SELECT COUNT(*) FROM loansNoNo

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Una regresión se esconde tras la misma nota

Un prompt nuevo arregla la pregunta 2 y rompe la pregunta 3. Las dos versiones aciertan cuatro preguntas de cinco: la nota no se ha movido.

Compara caso a caso. Una pregunta que pasaba y ahora falla es una regresión, y el control bloquea el cambio hasta que se corrija.

Un error frecuente

Advertencia

Mucha gente cree que una tasa de acierto igual significa que nada ha empeorado. Una pregunta puede arreglarse mientras otra se rompe. Compara cada caso con la última pasada y bloquea cualquier cambio que pierda un acierto.

Escribe el ayudante del modelo, parte 1

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' '// Los tokens de una respuesta, o null si no trae ninguno.' 'function count(data, name) {' '  return data.usage && typeof data.usage[name] === \"number\" ? data.usage[name] : null;' '}' '// Una respuesta puede envolver la consulta en un bloque delimitado: nos quedamos con el interior.' 'function sqlOf(text) {' '  const parts = text.split(String.fromCharCode(96).repeat(3));' '  const inner = (parts.length > 2 ? parts[1] : text).trim();' '  return inner.toLowerCase().startsWith(\"sql\") ? inner.slice(3).trim() : inner;' '}' > 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

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' '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 || 100 }),' '      signal: AbortSignal.timeout(30000),' '    });' '    if (res.ok === false) throw new Error(\"estado \" + res.status);' '    const d = await res.json();' '    return { text: d.choices[0].message.content, inTokens: count(d, \"prompt_tokens\"), outTokens: count(d, \"completion_tokens\"), recorded: false };' '  } catch (error) {' '    console.log(\"Muestra grabada: ningún modelo respondió (\" + error.message + \").\");' '    return { text: sample, inTokens: null, outTokens: null, recorded: true };' '  }' '}' 'module.exports = { ask, sqlOf };' >> 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 la base de la biblioteca, parte 1

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { DatabaseSync } = require(\"node:sqlite\");' '// Una biblioteca inventada: seis libros, cuatro socios, nueve préstamos.' 'const BOOKS = [[1, \"La calle de la sal\", 2019], [2, \"Puerto de cristal\", 2021], [3, \"Mapa de la lluvia\", 2015],' '  [4, \"Motores en calma\", 2023], [5, \"Siete teteras\", 2018], [6, \"Al norte del mediodía\", 2022]];' 'const MEMBERS = [[1, \"Alba\"], [2, \"Bruno\"], [3, \"Carmen\"], [4, \"Diego\"]];' '// id, book_id, member_id, month, returned (1 devuelto, 0 aún fuera)' 'const LOANS = [[1, 1, 1, 3, 1], [2, 2, 1, 3, 1], [3, 3, 2, 3, 0], [4, 1, 3, 3, 1], [5, 1, 1, 4, 0],' '  [6, 4, 2, 4, 0], [7, 2, 3, 4, 1], [8, 5, 2, 5, 0], [9, 2, 4, 5, 0]];' > library.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 la base de la biblioteca, parte 2

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function put(db, table, rows) {' '  const marks = rows[0].map(function () { return \"?\"; }).join(\", \");' '  const insert = db.prepare(\"INSERT INTO \" + table + \" VALUES (\" + marks + \")\");' '  rows.forEach(function (row) { insert.run(...row); });' '}' 'function open(file) {' '  const db = new DatabaseSync(file || \":memory:\");' '  db.exec(\"CREATE TABLE books (id INTEGER PRIMARY KEY, title TEXT, year INTEGER)\");' '  db.exec(\"CREATE TABLE members (id INTEGER PRIMARY KEY, name TEXT)\");' '  db.exec(\"CREATE TABLE loans (id INTEGER PRIMARY KEY, book_id INTEGER REFERENCES books(id), \"' '    + \"member_id INTEGER REFERENCES members(id), month INTEGER, returned INTEGER)\");' '  put(db, \"books\", BOOKS);' '  put(db, \"members\", MEMBERS);' '  put(db, \"loans\", LOANS);' '  return db;' '}' 'module.exports = { open };' >> library.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 de evaluación, parte 1

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask, sqlOf } = require(\"./model\");' 'const { open } = require(\"./library\");' 'const db = open();' 'const cases = [' '  { gold: \"SELECT COUNT(*) FROM loans WHERE month = 3\" },' '  { gold: \"SELECT COUNT(DISTINCT member_id) FROM loans WHERE month = 3\" },' '  { gold: \"SELECT COUNT(*) FROM loans WHERE returned = 0\" },' '  { gold: \"SELECT b.title FROM loans l JOIN books b ON l.book_id = b.id WHERE l.member_id = 2\" },' '  { gold: \"SELECT title FROM books WHERE id NOT IN (SELECT book_id FROM loans)\" },' '];' > eval.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 de evaluación, parte 2

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const A = [\"SELECT COUNT(id) FROM loans WHERE month = 3\",' '  \"SELECT COUNT(member_id) FROM loans WHERE month = 3\", cases[2].gold,' '  \"SELECT b.title FROM books b JOIN loans l ON l.book_id = b.id WHERE l.member_id = 2\",' '  \"SELECT b.title FROM books b LEFT JOIN loans l ON l.book_id = b.id WHERE l.id IS NULL\"];' 'const B = [cases[0].gold, cases[1].gold, \"SELECT COUNT(*) FROM loans\",' '  \"SELECT title FROM books WHERE id IN (SELECT book_id FROM loans WHERE member_id = 2)\", cases[4].gold];' 'function rows(sql) {' '  try { return db.prepare(sql).all().map(function (r) { return JSON.stringify(Object.values(r)); }).sort().join(\"|\"); }' '  catch (error) { return null; }' '}' >> eval.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 de evaluación, parte 3

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function score(sql, i) {' '  const got = rows(sql);' '  return got === null ? \"error\" : got === rows(cases[i].gold) ? \"pasa\" : \"falla\";' '}' 'function rate(list, test) {' '  return Math.floor(100 * list.filter(test).length / list.length) + \" por ciento\";' '}' 'const a = A.map(score);' 'const b = B.map(score);' 'const same = function (sql, i) { return sql === cases[i].gold; };' '// Una regresión: pasaba en A y no pasa en B.' 'const lost = [];' 'a.forEach(function (v, i) { if (v === \"pasa\" && b[i] === \"falla\" || v === \"pasa\" && b[i] === \"error\") lost.push(i + 1); });' >> eval.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 de evaluación, parte 4

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' '  a.forEach(function (v, i) { console.log(\"pregunta \" + (i + 1) + \": A \" + v + \", B \" + b[i]); });' '  const pass = function (v) { return v === \"pasa\"; };' '  console.log(\"por el resultado: A \" + rate(a, pass) + \", B \" + rate(b, pass));' '  console.log(\"por el texto exacto: A \" + rate(A, same) + \", B \" + rate(B, same));' '  console.log(\"regresiones en B: \" + (lost.length ? lost.join(\", \") : \"ninguna\"));' '  console.log(\"control: \" + (lost.length ? \"bloquear, una pregunta que pasaba ahora falla\" : \"publicar\"));' '  await live();' '}' >> eval.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 de evaluación, parte 5

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function live() {' '  const q = \"loans(id, book_id, member_id, month, returned)\nPregunta: ¿Cuántos socios distintos tomaron prestado un libro en el mes 3?\";' '  const sample = \"SELECT COUNT(DISTINCT member_id) FROM loans WHERE month = 3;\";' '  const reply = await ask([{ role: \"system\", content: \"Responde con una sola consulta SELECT de SQLite y nada más.\" },' '    { role: \"user\", content: q }], sample, 120);' '  const sql = sqlOf(reply.text);' '  console.log(\"el modelo escribió: \" + sql);' '  console.log(\"  puntuada aparte de los totales: \" + score(sql, 1));' '}' 'main();' >> eval.js"

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Puntúa dos versiones con cinco preguntas

Comando
docker exec -w /home/node/project agent node --experimental-sqlite --no-warnings eval.js

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Lo que imprime el Run

LíneaQué muestra
pregunta 2: A falla, B pasaEl prompt nuevo arregló la pregunta 2
pregunta 3: A pasa, B fallaEl prompt nuevo rompió la pregunta 3
por el resultado: A 80 por ciento, B 80 por cientoLa nota no se ha movido
por el texto exacto: A 20 por ciento, B 60 por cientoEl texto marca como fallidas respuestas correctas
regresiones en B: 3La pregunta perdida
control: bloquear, una pregunta que pasaba ahora fallaLa decisión

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Sobre este laboratorio y su código: Laboratorio de agente de programación con IA: usa opencode para explicar y corregir código, y comprueba su trabajo

¿Enseñas a una clase? Asigna este módulo a tus alumnos con guía del profesor, tareas con corrección automática y un laboratorio para cada alumno.