Qué muestra una respuesta de texto a SQL
Explicar una respuesta de texto a SQL es enseñar al usuario la consulta y sus filas, no solo una frase. Una frase sola parece segura, incluso cuando la consulta era errónea.
El módulo 84 diseña funciones de IA para personas. Esta lección escribe la respuesta de una función de texto a SQL: qué muestra, cuándo admite una duda y cuándo pregunta primero.
Al final de esta lección
Nota
Puedo escribir una respuesta que muestra la consulta, admite una duda y pregunta cuando la petición es vaga.
Palabras clave
| Palabra | Qué significa | Ejemplo |
|---|---|---|
| Respuesta | lo que ve el usuario después de su pregunta | |
| Lectura | una forma de convertir una pregunta en consulta | |
| Palabra vaga | una palabra que aquí tiene más de un sentido | popular |
| Pregunta de aclaración | una pregunta de vuelta que evita suponer | |
| Resultado vacío | una consulta que no devuelve ninguna fila |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Muestra la consulta y las filas
Pon la consulta y sus filas junto a la frase. Un usuario que conoce los datos detecta enseguida una unión equivocada.
Mantén las filas cortas: enseña las primeras y di cuántas quedan. La frase sale de las filas, nunca de la memoria del modelo.
Ejemplo resuelto: dos respuestas a una pregunta
| Respuesta | Lo que el usuario puede comprobar |
|---|---|
| Hay cinco libros prestados. | Nada |
| Respuesta: 5. Consulta: SELECT COUNT(*) FROM loans WHERE returned = 0 | El filtro, la tabla y el número |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Di cuándo no estás seguro
«¿Quién ha tomado prestados más libros?» tiene dos lecturas. Más préstamos da Alba, porque el empate se deshace por el nombre. Más libros distintos da Bruno.
Ejecuta las dos lecturas. Si coinciden, responde. Si no coinciden, di que no estás seguro y enseña las dos. Un resultado vacío también es una respuesta: di que ninguna fila coincide.
Pregunta antes de adivinar una palabra vaga
Palabras como popular, reciente, mejor o activo no tienen un único sentido en los datos. Popular puede querer decir prestado dos veces, o prestado este mes.
Cuando una pregunta lleva una, haz antes una pregunta de aclaración corta. Una pregunta de vuelta cuesta menos que una respuesta falsa y segura.
Un error frecuente
Advertencia
Mucha gente cree que una frase segura es la respuesta más útil. Una respuesta falsa y segura es peor que una pregunta de vuelta. Responde con firmeza solo si las lecturas coinciden y ninguna palabra es vaga.
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\";' '// 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
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
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
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 respuesta, parte 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' 'const { open } = require(\"./library\");' 'const db = open();' 'const VAGUE = [\"popular\", \"reciente\", \"mejor\", \"activo\"];' 'const M = \"SELECT m.name, COUNT(\";' 'const J = \") AS n FROM loans l JOIN members m ON m.id = l.member_id GROUP BY m.name ORDER BY n DESC, m.name LIMIT 1\";' > explain.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 respuesta, parte 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const asks = [' ' { q: \"¿Cuántos libros siguen prestados?\", a: \"SELECT COUNT(*) FROM loans WHERE returned = 0\" },' ' { q: \"¿Qué títulos ha tomado prestados el socio 2?\",' ' a: \"SELECT b.title FROM loans l JOIN books b ON l.book_id = b.id WHERE l.member_id = 2\" },' ' { q: \"¿Qué libros son populares?\", a: \"SELECT book_id FROM loans GROUP BY book_id HAVING COUNT(*) > 1\" },' ' { q: \"¿Quién ha tomado prestados más libros?\", a: M + \"*\" + J, b: M + \"DISTINCT l.book_id\" + J },' ' { q: \"¿Qué libros se prestaron en el mes 9?\",' ' a: \"SELECT b.title FROM loans l JOIN books b ON l.book_id = b.id WHERE l.month = 9\" },' '];' >> explain.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 respuesta, parte 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function rows(sql) {' ' return db.prepare(sql).all().map(function (r) { return Object.values(r).join(\" \"); });' '}' 'function reply(x) {' ' const vague = VAGUE.filter(function (w) { return x.q.toLowerCase().indexOf(w) >= 0; });' ' if (vague.length) return \"Antes de responder: ¿qué quieres decir con \" + vague[0] + \"?\";' ' const a = rows(x.a);' ' const b = x.b ? rows(x.b) : a;' ' if (a.join(\"|\") === b.join(\"|\")) {' ' if (a.length === 0) return \"Ninguna fila coincide. Consulta: \" + x.a;' ' return \"Respuesta: \" + a.join(\"; \") + \". Consulta: \" + x.a;' ' }' ' return \"No estoy seguro. Dos lecturas no coinciden: \" + a.join(\"; \") + \" o \" + b.join(\"; \");' '}' >> explain.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 respuesta, parte 4
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' asks.forEach(function (x, i) { console.log((i + 1) + \" \" + x.q + \"\n \" + reply(x)); });' ' const facts = asks[0].q + \" Filas: \" + JSON.stringify(rows(asks[0].a));' ' const sample = \"Cinco libros siguen prestados.\";' ' const res = await ask([{ role: \"system\", content: \"Da la respuesta en una frase corta. Usa solo las filas que se te dan.\" },' ' { role: \"user\", content: facts }], sample, 60);' ' console.log(\"el modelo dijo: \" + res.text);' ' console.log(\" se muestra con su consulta y sus filas, para que el usuario pueda comprobarla\");' '}' 'main();' >> explain.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Responde a cinco preguntas
docker exec -w /home/node/project agent node --experimental-sqlite --no-warnings explain.jsSe 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
| Pregunta | Respuesta |
|---|---|
| 1 ¿Cuántos libros siguen prestados? | Respuesta: 5. Consulta: ... |
| 2 ¿Qué títulos ha tomado prestados el socio 2? | Respuesta: Mapa de la lluvia; Motores en calma; Siete teteras. Consulta: ... |
| 3 ¿Qué libros son populares? | Antes de responder: ¿qué quieres decir con popular? |
| 4 ¿Quién ha tomado prestados más libros? | No estoy seguro. Dos lecturas no coinciden: Alba 3 o Bruno 3 |
| 5 ¿Qué libros se prestaron en el mes 9? | Ninguna fila coincide. Consulta: ... |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.