¿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

Puntuar la búsqueda aparte de la respuesta

Después de esta lección: Puedo puntuar la búsqueda aparte de la respuesta.

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

Métricas de búsqueda: recuerdo y rango recíproco

Muchas funciones de IA buscan en un conjunto de documentos y responden con lo que encuentran. Cuando la respuesta es mala, ¿qué parte falló: la búsqueda o la respuesta?

Puedes puntuar la búsqueda por separado. Aquí la búsqueda es un contador de palabras clave escrito en código, así que no hay embeddings.

Al final de esta lección

Nota

Puedo puntuar la búsqueda aparte de la respuesta.

Palabras clave

PalabraQué significaEjemplo
Buscadorla parte que encuentra documentos para una pregunta
Lista ordenadadocumentos en orden, el mejor primero
Relevanteun documento que de verdad tiene la respuesta
Recuerdo a kla parte de los documentos relevantes que está entre los k primeros0,5
Rango recíprocouno dividido entre el puesto del primer documento relevante0,5

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

Un buscador de palabras clave cuenta palabras compartidas

Toma la pregunta y quita las palabras pequeñas, como el, la y de. Para cada documento, cuenta cuántas palabras de la pregunta contiene. Ordena por ese número, el mejor primero.

También pliega las tildes, para que pagina y página sean una sola palabra. Es sencillo y tiene límites. No sabe que abre y abren son la misma palabra, ni que prolongar significa renovar.

Ejemplo resuelto: una pregunta, tres documentos

Palabras de la preguntaDocumentoPalabras compartidasPuesto
cuánto, tiempo, puedo, guardar, libros, antes, retrasoLos libros con retraso cuestan veinte céntimos al día21
cuánto, tiempo, puedo, guardar, libros, antes, retrasoLos socios pueden llevarse seis libros12
cuánto, tiempo, puedo, guardar, libros, antes, retrasoPide en el mostrador renovar los libros13

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

El recuerdo y el rango puntúan la lista

El recuerdo a k pregunta cuántos documentos correctos están entre los k primeros. Si dos son correctos y uno está entre los 2 primeros, el recuerdo a 2 es 0,5.

El rango recíproco mira solo el primer documento correcto. En el puesto 1 vale 1. En el puesto 2 vale 0,5. En el 4 vale 0,25. La media sobre muchas preguntas es el rango recíproco medio.

Ejemplo resuelto: cinco preguntas

PreguntaDocumento correcto en el puestoRango recíprocoRecuerdo a 1
biblioteca abre111
biblioteca cerrada (dos correctos)1 y 210,5
cuánto tiempo puedo guardar los libros antes del retraso20,50
prolongar mi préstamono se halla00
imprimir pagina111

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

Un error frecuente

Advertencia

Mucha gente cree que una respuesta mala significa que el modelo es malo. Si el documento correcto nunca se halló, ni un modelo perfecto puede responder. Puntúa primero la búsqueda y después la respuesta.

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;' '}' > 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 };' >> 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 búsqueda por palabras clave, parte 1

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' 'const docs = {' '  d1: \"La biblioteca abre a las nueve y cierra a las cinco\",' '  d2: \"Los socios pueden llevarse seis libros durante tres semanas\",' '  d3: \"Los libros con retraso cuestan veinte céntimos al día\",' '  d4: \"La cafetería vende sopa y té\",' '  d5: \"Los ordenadores son gratis durante una hora\",' '  d6: \"Pide en el mostrador renovar los libros por internet\",' '  d7: \"La biblioteca está cerrada los domingos\",' '  d8: \"Imprimir una página cuesta diez céntimos\",' '};' > search.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 búsqueda por palabras clave, parte 2

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const stop = [\"el\", \"la\", \"los\", \"las\", \"un\", \"una\", \"y\", \"a\", \"de\", \"del\", \"en\", \"al\", \"con\", \"por\", \"para\", \"es\", \"son\", \"mi\", \"me\", \"que\"];' 'const con = \"áéíóúüñ\";' 'const sin = \"aeiouun\";' 'function plain(text) {' '  let out = \"\";' '  for (const c of text) { const i = con.indexOf(c); out += i >= 0 ? sin[i] : c; }' '  return out;' '}' >> search.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 búsqueda por palabras clave, parte 3

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function tokens(text) {' '  const w = plain(text.toLowerCase()).split(\" \");' '  return w.filter(function (x) { return x.length > 0 && stop.indexOf(x) < 0; });' '}' 'function search(query) {' '  const q = tokens(query);' '  const scored = Object.keys(docs).map(function (id) {' '    const have = tokens(docs[id]);' '    return { id: id, score: q.filter(function (w) { return have.indexOf(w) >= 0; }).length };' '  }).filter(function (s) { return s.score > 0; });' '  scored.sort(function (a, b) { return b.score - a.score || (a.id < b.id ? -1 : 1); });' '  return scored.map(function (s) { return s.id; });' '}' >> search.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 búsqueda por palabras clave, parte 4

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const queries = [' '  [\"biblioteca abre\", [\"d1\"]], [\"biblioteca cerrada\", [\"d1\", \"d7\"]],' '  [\"cuánto tiempo puedo guardar los libros antes del retraso\", [\"d2\"]],' '  [\"prolongar mi préstamo\", [\"d6\"]], [\"imprimir pagina\", [\"d8\"]],' '];' 'let sumRr = 0;' 'let sumRecall = 0;' 'queries.forEach(function (item) {' '  const ranked = search(item[0]);' '  const at = ranked.findIndex(function (id) { return item[1].indexOf(id) >= 0; });' '  const rr = at < 0 ? 0 : 1 / (at + 1);' '  const hit = ranked.slice(0, 1).filter(function (id) { return item[1].indexOf(id) >= 0; }).length;' '  sumRr += rr;' '  sumRecall += hit / item[1].length;' '  console.log(item[0] + \" -> \" + (ranked.join(\",\") || \"nada\") + \"  rr \" + rr.toFixed(2));' '});' >> search.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 búsqueda por palabras clave, parte 5

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' '  console.log(\"rango recíproco medio: \" + (sumRr / queries.length).toFixed(2));' '  console.log(\"recuerdo medio a 1: \" + (sumRecall / queries.length).toFixed(2));' '  const q = \"Usando solo este texto, responde en una frase: \" + docs.d3 + \". Pregunta: ¿cuánto tiempo puedo guardar los libros antes del retraso?\";' '  const sample = \"El texto no lo dice. Solo dice que los libros con retraso cuestan veinte céntimos al día.\";' '  const reply = await ask([{ role: \"user\", content: q }], sample, 60);' '  console.log(\"respuesta desde el primer documento: \" + reply.text);' '}' 'main();' >> search.js"

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

Ejecuta la búsqueda y puntúala

Comando
docker exec -w /home/node/project agent node search.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
biblioteca abre -> d1,d7 rr 1.00La lista ordenada y su nota de rango
cuánto tiempo puedo guardar los libros antes del retraso -> d3,d2,d6 rr 0.50El documento correcto es el segundo
prolongar mi préstamo -> nada rr 0.00Un sinónimo que el contador no ve
rango recíproco medio: 0.70La media de las cinco preguntas
respuesta desde el primer documento: ...Una frase del laboratorio. 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.