Módulo 4: Recuperación agéntica en el bucle

Módulo 4: Recuperación agéntica en el bucle

Descripción

Los tres módulos anteriores construyeron, en capas, una sola pieza: un pipeline de ingestión (Módulo 1) que produce 57 chunks con metadatos, un índice BM25 (Módulo 2) que los hace buscables con search(query, k), y un contrato de tool (Módulo 3) que envuelve esa búsqueda en search_docs(query, k) — declarada, acotada, ejecutada de punta a punta. Las tres piezas funcionan. Lo que falta es la pregunta que le da sentido a todo el ecosistema agentic de esta guía: ¿quién decide cuándo usar search_docs, y qué hace con lo que devuelve?

Este módulo responde eso. No construye un pipeline nuevo — retoma search_docs tal como quedó en el Módulo 3, sin cambiarle una línea, y la pone a trabajar dentro del bucle de un agente: el mismo while/for que ya conoces de agent-fundamentals-and-tool-calling, ahora con una tool de recuperación en el menú junto a get_quote, book_room y cancel_booking. La diferencia central frente a un RAG "de tutorial" es esta: un pipeline RAG monolítico siempre busca primero, sin importar la pregunta. Un agente con search_docs como una tool más decide — a veces busca, a veces cotiza, a veces hace las dos cosas en el mismo turno, a veces reformula una búsqueda que no alcanzó. Eso es recuperación agéntica, y es el tema completo de este módulo.

Conexión con el módulo

Esta lección no ejecuta código nuevo — ubica el módulo en la guía, fija qué se reusa de dónde, y traza la frontera con las dos guías vecinas (agent-fundamentals-and-tool-calling y context-engineering) antes de que las lecciones 02-08 construyan sobre ese terreno ya delimitado.


Dónde estamos en la guía

RAG de producción e ingesta de documentos — el agente de Reservo
├── Módulo 1: Parsing y chunking de documentos
│   → 13 documentos crudos → 57 chunks con metadatos (doc_id, sección, posición)
├── Módulo 2: Indexar chunks para recuperación
│   → El índice BM25 hand-rolled, search(query, k) -> list[Chunk]
├── Módulo 3: search_docs como herramienta del agente
│   → El contrato, qué devuelve, la description, acotarla, ejecutada de punta a punta
├── Módulo 4: Recuperación agéntica en el bucle  ← ESTÁS AQUÍ
│   → Cuándo buscar, reformular, multi-hop, combinar con tools de Reservo, grounding
├── Módulo 5: Ingestión incremental e idempotente
├── Módulo 6: Evaluar la calidad de la recuperación
├── Módulo 7: Operar RAG en producción
└── Módulo 8: Proyecto — search_docs lista para producción

Los Módulos 1-3 dejaron search_docs construida, probada y aislada — una tool que funciona, pero que hasta ahora nunca vivió dentro de una conversación de varios turnos. El propio cierre del Módulo 3 (Ejercicio 3 de la Lección 08) planteó exactamente el problema que abre este módulo: combinar search_docs con get_quote en un solo script "excede el alcance" de M3 — es, palabra por palabra, el trabajo de este módulo.


Analogía: el investigador, no el archivista automático

Piensa en dos formas de resolver la misma consulta en una oficina. La primera es un archivista automático: cada vez que alguien le hace una pregunta, sin excepción, va primero al archivo, trae una carpeta, y recién después mira si la pregunta en realidad necesitaba esa carpeta. Si le preguntas "¿cuánto cuesta reservar la sala Studio dos horas?", igual va al archivo — pierde tiempo, y probablemente vuelve con una carpeta que no contiene ningún precio, porque el archivo tiene políticas y manuales, no una calculadora.

La segunda forma es un investigador. Antes de moverse, el investigador se pregunta: ¿esto lo tengo que buscar en el archivo, o me alcanza con la calculadora que tengo en el escritorio? Si la pregunta es "¿cuánto cuesta reservarla?", usa la calculadora (get_quote) directamente. Si la pregunta es "¿cuál es la política de cancelación?", va al archivo (search_docs). Si la primera carpeta que trae del archivo no responde la pregunta —una nota de "ver también" en vez del texto real de la política—, el investigador no se rinde ni inventa una respuesta: reformula su búsqueda con otras palabras y vuelve a intentar. Si la pregunta mezcla las dos cosas —"¿cuál es la política de cancelación de Focus, y cuánto me costaría reservarla?"—, pide la carpeta y usa la calculadora en el mismo viaje, no en dos viajes separados. Y cuando por fin responde, cita exactamente qué carpeta y qué número usó — no dice "creo que era más o menos así".

Ese investigador es el agente de este módulo. El archivista automático es el pipeline RAG monolítico que la Lección 07 pone, con evidencia ejecutada, al lado del investigador — y muestra por qué buscar siempre, sin decidir, cuesta tiempo y ruido sin ganar nada quality.


Qué se reusa, y de dónde (léelo antes de seguir)

Este módulo no reintroduce mecánica que ya conoces — la declara reusada, con la fuente exacta, y construye encima:

  1. search_docs, SEARCH_DOCS_SCHEMA, rag_index (el índice BM25 de 57 chunks). Exactamente como quedaron en el Módulo 3, Lecciones 02-08 — mismo contrato, mismo MAX_K=5, mismo MAX_CHARS=280, mismo corpus canónico. Ninguna lección de este módulo modifica una línea de esos tres módulos.
  2. El bucle del agente (while/for acotado, el historial messages, el tope max_iterations). Es exactamente el runner de agent-fundamentals-and-tool-calling Módulo 4 (Lecciones 02-03): pedir el turno del modelo, si es tool_use ejecutar y alimentar el resultado de vuelta como tool_result, si no, devolver la respuesta final. Este módulo no re-explica por qué es un while y no una tubería, ni cómo se arma el historial — lo asume y lo usa.
  3. Las tool calls en paralelo (un turno, varios tool_use; todos los tool_result juntos en un turno). De agent-fundamentals-and-tool-calling Módulo 5, Lección 05 — la forma exacta que la Lección 05 de este módulo reusa para combinar search_docs con get_quote en el mismo turno.
  4. El chequeador de grounding (check_grounding, números de 3+ dígitos citados vs. observados). De agent-fundamentals-and-tool-calling Módulo 5, Lección 07 — reusado sin cambios en la Lección 06 de este módulo, ahora aplicado a hechos que vienen de un chunk recuperado en vez de solo a precios.
  5. Las tools de Reservo (get_quote, book_room, cancel_booking) y sus anclas de precio. reservo_tools.py de agent-fundamentals-and-tool-calling Módulo 2 — sin cambios. La ancla Focus pro 3h = 6000 centavos, ya establecida en esa guía, reaparece en varias lecciones de este módulo.

Si algo de esta lista no te resulta familiar, es prerequisito real, no un repaso opcional — este módulo avanza asumiendo que las cinco piezas ya están firmes.


Qué agrega este módulo (lo nuevo)

Módulo 3 te dejó:   search_docs(query, k) -> list[dict]     (una tool aislada, probada sola)
Módulo 4 te da:     CUÁNDO llamarla, CUÁNTAS veces, CON QUÉ  (la decisión, dentro del bucle)

Concretamente, seis piezas nuevas, una por lección de contenido:

  • Lección 02 — Cuándo busca el agente. El criterio de decisión: ¿la pregunta necesita un documento, o basta una tool estructurada como get_quote? Ninguna, una, o ambas.
  • Lección 03 — Reformular y reintentar. Qué hacer cuando el primer resultado de search_docs no alcanza — un segundo search_docs con una query distinta, no una respuesta inventada.
  • Lección 04 — Recuperación multi-hop. Una pregunta compuesta que necesita dos búsquedas independientes, con dos queries distintas, no una sola query que diluye ambos temas.
  • Lección 05 — Combinar search_docs con las tools de Reservo. search_docs y get_quote en el mismo turno, tool calls en paralelo, resueltas juntas.
  • Lección 06 — Grounding: la respuesta cita el chunk, no alucina. El chequeador que confirma —o refuta— que cada cifra de la respuesta final tiene una fuente rastreable en algún tool_result.
  • Lección 07 — Agéntico vs. monolítico. La comparación directa, ejecutada, entre buscar siempre y decidir cuándo buscar.

La Lección 08 (mini-proyecto) junta las seis en un solo guion de turnos, ejecutado de punta a punta.


Frontera de este módulo (léela antes de seguir)

Dos límites duros, con la guía hermana a cada lado nombrada en ambos sentidos:

  1. El bucle genérico del agente no se re-enseña aquí. El tope de iteraciones, el multi-tool, cómo se acumula el historial turno a turno — todo eso es agent-fundamentals-and-tool-calling Módulo 4 y Módulo 5. Este módulo los reusa para una tool nueva (search_docs), no repite su mecánica desde cero. Si necesitas repasar por qué es un while y no una tubería de pasos fijos, ese repaso vive en esa guía, no en esta.
  2. Qué chunks recuperados caben en la ventana de contexto, y en qué orden, no se decide aquí. Este módulo recupera — produce la lista de chunks candidatos que search_docs devuelve. context-engineering-guide decide cómo entra eso al prompt: qué se prioriza, qué se resume, cuándo hay que recortar. La frontera se nombra en los dos sentidos: aquí se recupera, allá se cura. Un runner de producción real necesita las dos piezas, pero son trabajos distintos, con guías distintas.

Un tercer límite, más chico pero real: actualizar el índice cuando los documentos cambian (reingesta incremental, idempotencia por hash) es el Módulo 5, no este. Este módulo asume que el índice del Módulo 2 es estático durante toda la conversación.


El caso que sigue siendo el mismo: Reservo

El corpus, el índice y las anclas de precio no cambian:

57 chunks, 13 documentos (Módulo 1) -- indexados con BM25 k1=1.5/b=0.75 (Módulo 2)
search_docs(query, k) -> hasta 5 chunks, texto truncado a 280 caracteres (Módulo 3)

Focus       2500  ($25.00/h)
Studio      4000  ($40.00/h)
Boardroom   8000  ($80.00/h)
Lounge      5000  ($50.00/h)
Phonebooth  1500  ($15.00/h)

Lo que cambia en este módulo es el guion: en vez de una query aislada por lección, cada lección arma una conversación de uno o más turnos, con search_docs conviviendo con get_quote/book_room/cancel_booking en el mismo registro de tools — exactamente TOOLS = [GET_QUOTE_TOOL, BOOK_ROOM_TOOL, CANCEL_BOOKING_TOOL, SEARCH_DOCS_SCHEMA], tal como quedó armado al cierre del Módulo 3.


Regla dura de ejecución (se mantiene de M1-M3)

  • La ingeniería de recuperación SÍ se ejecuta. Cada llamada a search_docs, cada ejecución del runner del bucle, cada chequeo de grounding de este módulo corre con Python 3.14 real, sobre el corpus canónico de 57 chunks, y cada bloque "Qué esperar" es salida real de tu propia terminal.
  • La decisión del modelo es concepto, con ejemplos realistas (claude-sonnet-5). Cuándo el modelo decide llamar search_docs, cuándo decide que ya tiene suficiente, cuándo decide reformular — esa decisión no se puede ejecutar sin una API real. Se muestra como razonamiento realista, etiquetado siempre como concepto, nunca mezclado con la salida ejecutada. El guion de turnos que alimenta al runner (qué tool pide el modelo en cada paso) está escrito a mano para representar esa decisión — pero el runner que lo procesa, y cada tool que ejecuta, son código real.
  • BM25 sigue siendo recuperación léxica, nunca semántica. Cada vez que este módulo mira un chunk recuperado, lo hace con la misma honestidad de los Módulos 2 y 3: coincidencia de términos, no comprensión del significado.
  • Modelos actuales únicamente (claude-sonnet-5/claude-opus-5); nunca claude-3/gpt-*. Sin random/datetime.now().

Prerequisitos

Conocimiento requerido:

  • ✅ Módulos 1-3 de esta guía completos: el corpus de 57 chunks, el índice BM25, y search_docs con su contrato, acotada y probada.
  • agent-fundamentals-and-tool-calling Módulo 4 (el bucle del agente, el historial, el tope de iteraciones) y Módulo 5 (selección entre varias tools, tool calls en paralelo, grounding). Este módulo reusa ambos sin re-explicarlos.
  • ✅ Python: dataclasses, concurrent.futures.ThreadPoolExecutor (para el paralelismo real), re (para el chequeador de grounding).

NO requerido:

  • ❌ No necesitas una API key: la decisión del modelo sigue siendo concepto.
  • ❌ No necesitas reimplementar el índice BM25 ni search_docs — se importan tal cual del Módulo 3.
  • ❌ No necesitas saber cómo se actualiza el índice cuando los documentos cambian — eso es el Módulo 5.

Entorno:

  • Python 3.14.0 + numpy 2.5.1 (ya instalado, $0, sin red) — nada nuevo respecto al Módulo 3.

Roadmap del módulo

Lección 01 — Introducción al módulo (esta)

La analogía del investigador, qué se reusa y de dónde, la frontera con agent-fundamentals-and-tool-calling y context-engineering.

Lección 02 — Cuándo busca el agente

El criterio de decisión: documento vs. tool estructurada vs. ninguna de las dos. Cuatro preguntas, concepto, con las tools ya declaradas.

Lección 03 — Reformular y reintentar la query

Un chequeador ejecutado que detecta cuándo un chunk es solo una referencia cruzada, y una segunda búsqueda con otra query que sí encuentra la respuesta.

Lección 04 — Recuperación multi-hop

Una pregunta compuesta, dos búsquedas independientes — y la evidencia ejecutada de por qué una sola query combinada pierde una de las dos señales.

Lección 05 — Combinar search_docs con las tools de Reservo

search_docs + get_quote en el mismo turno, tool calls en paralelo, la ancla Focus pro 3h = 6000.

Lección 06 — Grounding: anclar la respuesta en los chunks

check_grounding reusado, aplicado a un hecho citado de un chunk — con un caso limpio y uno alucinado.

Lección 07 — Agéntico vs. monolítico

La comparación directa: buscar siempre vs. decidir cuándo buscar, con números reales de las dos rutas.

Lección 08 — Mini-proyecto: una corrida de recuperación agéntica

Un guion de cuatro turnos que junta las seis piezas del módulo, ejecutado de punta a punta, con grounding confirmado.


Evidencia de éxito

Antes de avanzar al Módulo 5 (Ingestión incremental e idempotente), deberías poder:

  • Decidir, para una pregunta dada, si necesita search_docs, una tool estructurada, ninguna, o ambas.
  • Reconocer, con una señal ejecutable, cuándo un resultado de search_docs es solo una referencia cruzada y reformular la query.
  • Separar una pregunta compuesta en dos búsquedas independientes en vez de una sola query diluida.
  • Combinar search_docs con get_quote/book_room en el mismo turno, siguiendo la forma exacta del paralelismo de tool calls.
  • Verificar, con check_grounding, que una respuesta final se apoya en los chunks realmente recuperados.
  • Explicar, con evidencia ejecutada, por qué un agente que decide cuándo buscar es mejor que un pipeline que siempre busca primero.

Resumen

  • Este módulo no construye ingeniería nueva de recuperación — retoma search_docs del Módulo 3 y la pone a trabajar dentro del bucle de un agente, decidiendo cuándo llamarla, combinándola con las tools de Reservo, y verificando que la respuesta final cite lo que de verdad recuperó.
  • Reusa, sin repetir, cinco piezas ya construidas: search_docs/rag_index (Módulo 3), el bucle del agente y el paralelismo de tool calls (agent-fundamentals-and-tool-calling Módulos 4-5), el chequeador de grounding (misma guía, Módulo 5), y las tools de Reservo con sus anclas de precio.
  • Frontera doble: el bucle genérico no se re-enseña (agent-fundamentals-and-tool-calling); qué chunks caben en la ventana de contexto y en qué orden es trabajo de context-engineering-guide — aquí se recupera, allá se cura.
  • Todo lo que ejecuta el runner y cada tool es real; la decisión de qué tool pedir en cada turno es concepto, con claude-sonnet-5 como modelo de referencia.

Siguiente lección: 02 — Cuándo busca el agente. El criterio que decide, antes de pedir ninguna tool, si una pregunta necesita un documento, un cálculo, o ninguno de los dos.


Recursos adicionales

  1. Anthropic — Tool use (function calling) overview — El contrato y el protocolo que este módulo reusa sobre search_docs combinada con las demás tools de Reservo.
  2. agent-fundamentals-and-tool-calling-guide, Módulo 4 (El bucle del agente) y Módulo 5 (Multi-tool y selección) — la base completa que este módulo reusa sin repetir.
  3. context-engineering-guide — dónde se decide qué chunks recuperados caben en la ventana de contexto y en qué orden; la frontera exacta de dónde termina este módulo.
  4. Python 3.14 — What's New — La versión con la que se ejecuta toda la ingeniería de esta guía.