¿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

El esquema de la base en el prompt

Después de esta lección: Puedo describir el esquema de una base de datos de forma breve para que un modelo escriba SQL a partir de él.

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

Lo que el texto a SQL necesita saber

El texto a SQL es un modelo de lenguaje que convierte una pregunta en palabras normales en una consulta SQL. El modelo nunca ha visto tu base de datos, así que adivina las tablas que no describes.

El módulo 4 enseña SQL y el módulo 63 pide a un modelo una forma fija. Esta lección decide qué contarle al modelo sobre tus tablas.

Al final de esta lección

Nota

Puedo describir el esquema de una base de datos de forma breve para que un modelo escriba SQL a partir de él.

Palabras clave

PalabraQué significaEjemplo
Texto a SQLun modelo que convierte una pregunta en una consulta
Esquemalas tablas y columnas de una base de datos
Clave foráneauna columna que señala una fila de otra tablaloans.book_id
Uniónemparejar las filas de dos tablas por una columna
Notauna línea que dice qué significan los valores de una columna

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

Lo mínimo: tablas, columnas y uniones

Para escribir una consulta, el modelo necesita tres cosas. Necesita el nombre de las tablas, las columnas de cada una y cómo se unen las tablas entre sí.

Sin las uniones, adivina. Unir loans.id con books.id, por ejemplo, funciona sin error y devuelve los libros equivocados. Los nombres de tablas y columnas van en inglés, como los identificadores del código.

Ejemplo resuelto: el esquema de la biblioteca

TablaColumnasSe une con
booksid, title, yearNada
membersid, nameNada
loansid, book_id, member_id, month, returnedbook_id con books.id, member_id con members.id

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

Algunas columnas necesitan una nota

El nombre de una columna no siempre dice qué significan sus valores. En la biblioteca, returned vale 1 cuando el libro ha vuelto y 0 mientras sigue fuera.

Un modelo que solo lee el nombre podría probar returned = «no» o returned IS NULL. Una nota corta para cada columna poco clara evita esa suposición.

Ejemplo resuelto: nombres que piden una nota

ColumnaUna suposición sin notaLa nota
loans.returnedreturned = «no»1 si ha vuelto, 0 si sigue fuera
loans.monthUna fecha como 2026-03-01De 1 a 12, de este año
books.yearAño de la compraAño de publicación

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

Un error frecuente

Advertencia

Mucha gente cree que el modelo ya conoce su base de datos. Nunca la ha visto. Envía las tablas, las uniones y las notas con cada pregunta, porque cada petición empieza de cero.

Una descripción breve mejor que todo el CREATE

Podrías pegar cada sentencia CREATE TABLE. Una línea corta por tabla, seguida de las uniones y las notas, dice lo mismo de una forma fácil de leer.

En una base de cuarenta tablas, envía solo las que necesita la pregunta. Menos palabras cuestan menos y dejan menos sitio a errores de lectura.

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 del esquema, 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 tables = db.prepare(\"SELECT name, sql FROM sqlite_master WHERE type = ? ORDER BY name\").all(\"table\");' 'function brief(t) {' '  const cols = db.prepare(\"SELECT name, type FROM pragma_table_info(?)\").all(t.name)' '    .map(function (c) { return c.name + \" \" + c.type; });' '  return t.name + \"(\" + cols.join(\", \") + \")\";' '}' 'const links = [];' 'tables.forEach(function (t) {' '  db.prepare(\"SELECT * FROM pragma_foreign_key_list(?)\").all(t.name).forEach(function (k) {' '    links.push(t.name + \".\" + k.from + \" -> \" + k.table + \".\" + k.to);' '  });' '});' > schema.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 esquema, parte 2

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const notes = [\"loans.returned vale 1 cuando el libro ha vuelto, 0 mientras sigue fuera\", \"loans.month va de 1 a 12, de este año\"];' 'const schema = tables.map(brief).concat(links, notes).join(\"\n\");' 'const whole = tables.map(function (t) { return t.sql; }).join(\"\n\");' 'console.log(schema);' 'console.log(\"texto CREATE completo: \" + whole.length + \" caracteres, versión breve con notas: \" + schema.length);' >> schema.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 esquema, parte 3

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' '  const question = \"¿Cuántos préstamos se hicieron en el mes 3?\";' '  const system = \"Responde con una sola consulta SELECT de SQLite y nada más.\";' '  const sample = \"SELECT COUNT(*) FROM loans WHERE month = 3;\";' '  const reply = await ask([{ role: \"system\", content: system },' '    { role: \"user\", content: schema + \"\nPregunta: \" + question }], sample, 120);' '  console.log(\"el modelo escribió: \" + sqlOf(reply.text));' '  console.log(\"aún sin comprobar: la comprueban las lecciones 2 a 5\");' '}' 'main();' >> schema.js"

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

Describe el esquema y pregunta

Comando
docker exec -w /home/node/project agent node --experimental-sqlite --no-warnings schema.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
books(id INTEGER, title TEXT, year INTEGER)Una línea corta por tabla
loans.book_id -> books.idUna línea por unión, leída de las claves foráneas
loans.returned vale 1 cuando el libro ha vuelto, 0 mientras sigue fueraUna nota escrita a mano
texto CREATE completo: 283 caracteres, versión breve con notas: 327El tamaño de las dos formas
el modelo escribió: SELECT COUNT(*) FROM loans WHERE month = 3;La consulta, o una muestra grabada si ningún modelo responde

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.