Una búsqueda no bastó
La recuperación agéntica significa que un agente decide buscar otra vez cuando los primeros resultados no responden a la pregunta. Importa porque una pregunta real suele necesitar dos miradas: la primera halla el tema y la segunda el detalle.
Alguien pregunta cuánto tiempo queda bloqueado. La primera búsqueda halla las reglas de la contraseña, no el tiempo de bloqueo. El agente busca de nuevo.
Al final de esta lección
Nota
Puedo dejar que un agente busque más de una vez, con una regla de parada y un límite, y puntuar sus respuestas sobre preguntas conocidas.
Palabras clave
| Palabra | Qué significa |
|---|---|
| Recuperación agéntica | una búsqueda que el agente puede repetir y cambiar |
| Regla de parada | la condición que termina el bucle de búsqueda |
| Límite de búsquedas | lo máximo que puede usar una pregunta |
| Cobertura | la parte de las fuentes necesarias que se encontraron |
| Respuesta respaldada | una respuesta cuyas fuentes se recuperaron de verdad |
| Visto | las fuentes halladas hasta ahora |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
La búsqueda es una herramienta reutilizable
En el módulo de Construir agentes construiste un bucle de agente con un presupuesto de pasos. Aquí la herramienta es la búsqueda. El agente busca, lee lo que vuelve y responde o busca otra vez con una consulta nueva.
Hay que cuidar dos cosas. El bucle debe terminar. Y la respuesta final debe apoyarse en lo que de verdad se encontró.
Tres formas de parar
Se para cuando el agente responde. Se para cuando una búsqueda no aporta nada nuevo, porque buscar otra vez con las mismas palabras no ayudará. Se para en un límite de búsquedas, fijado por el coste y el tiempo.
Anota por qué paró el bucle: respondido, parado, límite o sin respuesta. El motivo dice qué corregir.
Ejemplo resuelto: tres recorridos de la misma pregunta
| Recorrido | Búsquedas | Termina con | Respaldada |
|---|---|---|---|
| Bueno | 2 | respondido | sí |
| Atascado | 2 | parada, nada nuevo | no |
| Límite de 1 | 1 | parada, límite | no |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Comprueba que la respuesta tiene fuentes
Una respuesta lista sus fuentes. Está respaldada solo si cada fuente figura entre las páginas que devolvieron las búsquedas. Una fuente que el agente nunca vio es inventada.
Mide también la cobertura: de las páginas que necesita una pregunta, ¿cuántas encontraron las búsquedas? Puntúa ambas sobre preguntas con fuentes conocidas.
Un error frecuente
Advertencia
Mucha gente cree que un agente que cita una fuente la ha usado. Una cita es solo texto. Comprueba que cada id citado lo devolvió una búsqueda de este recorrido, o una fuente inventada pasará por real.
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;' '}' > 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 };' >> 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 bucle de búsqueda, parte 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' 'const index = {' ' \"reglas de contraseña\": [\"p2\", \"p1\"],' ' \"tiempo de bloqueo\": [\"p3\"],' ' \"restablecer contraseña\": [\"p1\"],' '};' 'const docs = { p1: \"Restablece tu contraseña desde la página de acceso.\", p2: \"Una contraseña tiene diez letras.\", p3: \"Tras cinco contraseñas erróneas la cuenta se bloquea quince minutos.\" };' > agentic1.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 bucle de búsqueda, parte 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function run(steps, max) {' ' const seen = [];' ' let n = 0;' ' for (const s of steps) {' ' if (\"answer\" in s) {' ' const ok = s.sources.length > 0 && s.sources.every(function (p) { return seen.includes(p); });' ' return \"respondido, respaldado: \" + (ok ? \"sí\" : \"no\");' ' }' ' if (n === max) return \"parada: límite de \" + max + \" búsqueda(s)\";' ' n++;' ' const found = index[s.search] || [];' ' const fresh = found.filter(function (p) { return seen.includes(p) === false; });' ' fresh.forEach(function (p) { seen.push(p); });' ' console.log(\" búsqueda \" + n + \": \" + s.search + \" -> \" + (found.join(\" \") || \"nada\") + \", nuevas \" + fresh.length);' ' if (fresh.length === 0) return \"parada: nada nuevo\";' ' }' ' return \"sin respuesta\";' '}' >> agentic1.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 bucle de búsqueda, parte 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const good = [{ search: \"reglas de contraseña\" }, { search: \"tiempo de bloqueo\" }, { answer: \"Quince minutos.\", sources: [\"p3\"] }];' 'const stuck = [{ search: \"restablecer contraseña\" }, { search: \"restablecer contraseña\" }, { answer: \"No estoy seguro.\", sources: [\"p1\"] }];' 'console.log(\"recorrido bueno:\"); console.log(run(good, 3));' 'console.log(\"recorrido atascado:\"); console.log(run(stuck, 3));' 'console.log(\"recorrido con límite:\"); console.log(run(good, 1));' 'async function main() {' ' const text = \"Pregunta: cuánto tiempo estoy bloqueado. La primera búsqueda halló: \" + docs.p2 + \" Escribe una consulta mejor de 5 palabras o menos. Responde solo con la consulta.\";' ' const reply = await ask([{ role: \"user\", content: text }], \"tiempo de bloqueo de la cuenta\", 20);' ' console.log(\"un modelo sugiere:\", reply.text.trim());' '}' 'main();' >> agentic1.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 los tres bucles
docker exec -w /home/node/project agent node agentic1.jsSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Esto es un sustituto
El bucle sigue pasos grabados, así que cada estudiante ve los mismos tres recorridos. Un agente real elige sus pasos, y la llamada al modelo aquí solo sugiere una consulta, que el Run imprime sin seguirla.
El índice tiene tres entradas. Uno real es la búsqueda híbrida que construiste, con el filtro de la lección 5.
Lo que imprime el Run
| Recorrido | Última línea |
|---|---|
| recorrido bueno | respondido, respaldado: sí |
| recorrido atascado | parada: nada nuevo |
| recorrido con límite | parada: límite de 1 búsqueda(s) |
| sugerencia del modelo | Una salida posible: tiempo de bloqueo de la cuenta |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.