Módulo 3: `search_docs` como herramienta del agente

Escribir la `description` para que el modelo sepa cuándo

Descripción

Ya declaraste el input_schema completo de search_docs (Lección 03) y ya sabes qué forma tiene lo que devuelve (Lección 04). Falta la pieza que decide si el modelo la usa en el momento correcto: la description. En agent-fundamentals-and-tool-calling Módulo 2, Lección 04 ya viste, ejecutando, por qué ese campo no es un comentario que "estaría bien" escribir con cuidado — es la única prosa que el modelo lee para elegir entre tools disponibles. Esta lección aplica ese mismo criterio a un caso nuevo: un agente de Reservo que tiene, al mismo tiempo, tools estructuradas (get_quote, book_room) y una tool de recuperación (search_docs) — y necesita distinguir, por la description sola, cuándo cada una corresponde.

Vas a reusar el chequeador de calidad de descripciones (check_description) exactamente como lo dejaste en agent-fundamentals-and-tool-calling, y vas a ver, como concepto, cómo la misma pregunta de un usuario lleva al modelo a una decisión distinta según qué tan clara sea la description de search_docs.

Conexión con el módulo

La Lección 03 declaró la description de search_docs sin detenerse en ella. Esta lección la revisa a fondo, con el mismo rigor que agent-fundamentals-and-tool-calling le dio a la de book_room. La Lección 06 sigue construyendo sobre esta base: una description que dice bien qué hace la tool también deja más claro por qué necesita estar acotada.


Analogía: el letrero completo del mostrador

El mostrador de atención del archivo interno ya tenía, desde la Lección 03, su nombre y su formulario de pedido. Le faltaba el texto explicativo debajo del nombre — el que le dice a cualquiera que se acerque qué tipo de consultas atiende este mostrador en particular, y a cuál otro mostrador ir si la consulta es distinta. "Consultas de documentos: políticas, FAQ y manuales de sala. Para cotizar un precio o hacer una reserva, ve al mostrador de Reservas." Sin ese letrero, alguien con una pregunta de precio podría terminar en la fila equivocada — no porque el mostrador de documentos no supiera resolver su duda, sino porque nadie le dijo dónde debía preguntar. La description de search_docs es exactamente ese letrero.


Qué tiene que decir la description de una tool de recuperación

La misma pregunta doble de agent-fundamentals-and-tool-calling sigue aplicando: qué hace la tool y cuándo usarla. Pero search_docs agrega una tercera pregunta que get_quote no necesitaba responder con tanta fuerza, porque en Reservo ya existen otras tools con las que se podría confundir:

QUE hace      -> busca fragmentos de documentos por relevancia lexica
CUANDO usarla -> cuando la pregunta necesita citar una politica, FAQ o manual
CUANDO NO     -> no para cotizar (get_quote) ni para reservar/cancelar (book_room/cancel_booking)

La tercera pregunta importa especialmente aquí porque search_docs y get_quote pueden aparecer, a primera vista, relacionadas: una pregunta como "¿cuánto cuesta cancelar mi reserva de Boardroom?" tiene una parte de precio (get_quote) y una parte de política (search_docs) mezcladas en la misma oración. Sin una description que trace la línea con claridad, el modelo no tiene forma de saber que necesita las dos tools, o cuál de las dos responde qué parte.


Ejemplo trabajado: el chequeador reusado, sobre dos versiones de la description

Reusamos check_description tal cual quedó en agent-fundamentals-and-tool-calling Módulo 2, Lección 04 — mismo código, sin adaptarlo.

import re


def check_description(description):
    """Heuristica minima de calidad para una `description` de tool.
    Devuelve una lista de advertencias; lista vacia = pasa las heuristicas basicas."""
    warnings = []
    text = description.strip()

    if len(text) < 20:
        warnings.append("muy corta: no alcanza para decir QUE hace y CUANDO usarla")

    if not re.search(r"usa(r)? esta tool|cuando el usuario|cuando la|cuando el", text, re.IGNORECASE):
        warnings.append("no dice CUANDO usarla (falta una senal como 'usa esta tool cuando...')")

    words = re.findall(r"[a-záéíóúñ]+", text.lower())
    if len(set(words)) <= 3:
        warnings.append("solo repite un punado de palabras, casi no agrega informacion")

    return warnings

Lo probamos contra una versión deliberadamente vaga y contra la description completa de SEARCH_DOCS_SCHEMA que ya declaraste en la Lección 03.

VAGUE_DESCRIPTION = "Busca documentos."

GOOD_DESCRIPTION = (
    "Busca fragmentos de documentos internos de Reservo (politicas de "
    "cancelacion/reembolso/no-show, FAQ de reservas/membresias/pagos, "
    "manuales de sala) que puedan contener la respuesta a una pregunta "
    "en lenguaje natural. Devuelve como maximo `k` fragmentos, cada uno "
    "con su doc_id de origen y un score de relevancia lexica. Usa esta "
    "tool cuando la pregunta del usuario requiere citar una politica, "
    "un FAQ o un manual; no la uses para cotizar precios ni crear una "
    "reserva (usa get_quote/book_room para eso)."
)

for label, text in [("vaga", VAGUE_DESCRIPTION), ("buena", GOOD_DESCRIPTION)]:
    print(f"{label!r:10} -> {check_description(text)}")

Qué esperar:

'vaga'     -> ['muy corta: no alcanza para decir QUE hace y CUANDO usarla', "no dice CUANDO usarla (falta una senal como 'usa esta tool cuando...')", 'solo repite un punado de palabras, casi no agrega informacion']
'buena'    -> []

"Busca documentos." dispara las tres advertencias, igual que le pasó a "Cotiza." en agent-fundamentals-and-tool-calling. La description completa de search_docs pasa limpio: dice qué hace (busca fragmentos, con qué límite y con qué forma de resultado), dice cuándo usarla, y dice explícitamente cuándo no —la cláusula final sobre get_quote/book_room no es relleno, es exactamente el tipo de deslinde que evita la confusión de la que habla la sección anterior.


Concepto: el modelo eligiendo entre search_docs y get_quote

Esta parte es concepto: ningún modelo se ejecuta, es un ejemplo realista de cómo razonaría claude-sonnet-5 con las tools de Reservo disponibles (get_quote, book_room, cancel_booking de agent-fundamentals-and-tool-calling, más search_docs de esta guía).

Supón que, además de SEARCH_DOCS_SCHEMA con VAGUE_DESCRIPTION, el modelo también tiene GET_QUOTE_TOOL con su description completa ("Cotiza el precio de una sala para un tier y una cantidad de horas, sin reservar nada. Usa esta tool cuando el usuario pregunta cuánto cuesta una reserva."). El usuario pregunta: "¿Cuál es la política de cancelación de Boardroom?"

[usuario]  ¿Cual es la politica de cancelacion de Boardroom?

[modelo·concepto, con VAGUE_DESCRIPTION en search_docs]
Las tools disponibles son "get_quote" (cotiza precios de sala) y
"search_docs" ("Busca documentos." - sin mas contexto sobre que tipo de
documentos ni cuando corresponde usarla). La pregunta menciona "Boardroom",
la misma palabra que aparece en el enum de room de get_quote. Sin una
description que aclare que search_docs cubre politicas, la señal mas fuerte
disponible es la coincidencia de "Boardroom" con un argumento conocido de
get_quote.
decision -> intenta resolver con texto generado, sin llamar ninguna tool,
            o llama a get_quote con datos incompletos (falta tier y hours,
            que la pregunta nunca dio)

Sin una description que le diga al modelo que "política de cancelación" es exactamente el tipo de pregunta que search_docs resuelve, el modelo se queda sin una señal clara de que existe una tool hecha para esto — y termina o inventando una respuesta sin fuente, o forzando una tool que no encaja. Ahora la misma pregunta, con GOOD_DESCRIPTION:

[modelo·concepto, con GOOD_DESCRIPTION en search_docs]
"get_quote" cotiza precios; su description dice explicitamente que no
reserva ni resuelve nada mas. "search_docs" dice, tambien explicito, que
cubre "politicas de cancelacion" -textual- y que es la tool para preguntas
que requieren citar una politica. La pregunta del usuario es literalmente
sobre una politica de cancelacion, no sobre un precio.
decision -> use_tool "search_docs" con {"query": "What is the cancellation
            policy for Boardroom bookings?", "k": 3}

Nada cambió en el usuario ni en las tools disponibles — solo el texto de una description. Esa es, otra vez, la magnitud de lo que escribes en ese campo: no es documentación de referencia para un desarrollador que ya conoce el sistema, es la señal operativa que decide si el agente busca en los documentos correctos o improvisa una respuesta sin fuente.


Lista de chequeo para la description de una tool de recuperación

Además de la lista general de agent-fundamentals-and-tool-calling (qué hace, cuándo usarla, cuándo no, si tiene efectos reales, sin jerga interna), una tool de recuperación como search_docs se beneficia de dos puntos específicos:

  1. ¿Dice qué tipo de contenido cubre el corpus? "Busca documentos" no dice nada; "políticas de cancelación/reembolso/no-show, FAQ de reservas/membresías/pagos, manuales de sala" le da al modelo una lista concreta de temas para reconocer cuándo una pregunta encaja.
  2. ¿Aclara que el resultado viene con un score, no una respuesta final? El modelo necesita saber que lo que recibe son fragmentos candidatos, no una respuesta ya redactada — eso condiciona cómo debería usar el resultado (leerlo y sintetizar, no repetirlo tal cual sin verificar relevancia).

Errores comunes

  1. Copiar la lista de chequeo de get_quote sin adaptar la tercera pregunta. "Cuándo NO usarla" para una tool estructurada como get_quote es distinto de "cuándo NO usarla" para una tool de recuperación — en el segundo caso, el riesgo típico no es confundir dos acciones (cotizar vs. reservar), sino confundir "necesito datos de un documento" con "necesito un cálculo estructurado". Nombrar explícitamente get_quote/book_room en la description de search_docs (y viceversa, si las escribieras juntas) es lo que resuelve esa ambigüedad específica.

  2. Prometer que search_docs "siempre encuentra la respuesta". La description no debería sugerir que la tool es infalible — BM25 es recuperación léxica, y una description honesta no genera expectativas que el índice no puede cumplir. La Lección 07 muestra un caso de 0 resultados de verdad.

  3. Olvidar mencionar el límite k en la description. Si el modelo no sabe que el resultado viene acotado a un máximo de fragmentos, puede sorprenderle recibir menos de lo esperado. Decirlo explícito ("como máximo k fragmentos") evita esa sorpresa.

  4. Escribir una description genérica que serviría para cualquier tool de búsqueda, no específicamente para el corpus de Reservo. "Busca información relevante" es casi tan vaga como "Busca documentos." — nombrar los temas reales del corpus (políticas, FAQ, manuales) es lo que la vuelve accionable para el modelo en este dominio específico.


Ejercicios

Ejercicio 1: Vaga o buena (Fácil)

Para cada description de una hipotética tool de búsqueda, di si es "vaga" o "buena" y por qué, sin ejecutar nada: (a) "Consulta info."; (b) "Busca en los manuales de sala de Reservo (Focus, Studio, Boardroom, Lounge, Phonebooth) detalles de capacidad y equipo. Usa esta tool cuando el usuario pregunta qué hay en una sala específica, no para cotizar su precio."; (c) "Herramienta de documentos.".

Ver solución
  • (a) Vaga. No dice qué documentos busca, no dice cuándo usarla, y es casi tan corta como el ejemplo VAGUE_DESCRIPTION del ejemplo trabajado.
  • (b) Buena. Dice el qué (busca en los manuales de sala, con ejemplos concretos de las cinco salas), dice cuándo usarla (preguntas sobre qué hay en una sala), y dice cuándo no (cotizar precio, deslindándola de get_quote).
  • (c) Vaga. Es una etiqueta ("Herramienta de documentos"), no una descripción — no dice qué tipo de documentos, ni cuándo corresponde usarla frente a otra tool.

Ejercicio 2: Corre el chequeador sobre tres variantes (Medio)

Usando check_description, evalúa y ejecuta: (a) "Busca fragmentos de politicas y manuales de Reservo. Usa esta tool cuando el usuario pregunta por una politica o un manual."; (b) "Documentos."; (c) "Esta tool busca cosas relacionadas con Reservo en general.".

Ver solución
cases = [
    "Busca fragmentos de politicas y manuales de Reservo. Usa esta tool cuando el usuario pregunta por una politica o un manual.",
    "Documentos.",
    "Esta tool busca cosas relacionadas con Reservo en general.",
]

for text in cases:
    print(f"{text!r}\n  -> {check_description(text)}\n")

Salida esperada:

'Busca fragmentos de politicas y manuales de Reservo. Usa esta tool cuando el usuario pregunta por una politica o un manual.'
  -> []

'Documentos.'
  -> ['muy corta: no alcanza para decir QUE hace y CUANDO usarla', "no dice CUANDO usarla (falta una senal como 'usa esta tool cuando...')", 'solo repite un punado de palabras, casi no agrega informacion']

'Esta tool busca cosas relacionadas con Reservo en general.'
  -> ["no dice CUANDO usarla (falta una senal como 'usa esta tool cuando...')"]

Explicación: (a) pasa las tres heurísticas. (b) falla las tres, como es de esperar de dos palabras. (c) es larga y usa vocabulario variado, así que pasa dos heurísticas, pero "cosas relacionadas con Reservo en general" no dice cuándo corresponde usarla frente a otra tool — exactamente el mismo patrón que NAME_ONLY_DESCRIPTION mostró en agent-fundamentals-and-tool-calling.

Ejercicio 3: Reescribe y explica el riesgo (Difícil)

Toma esta description real de search_docs, deliberadamente mal escrita para este ejercicio: "Busca cosas en Reservo.". (a) Reescríbela siguiendo la lista de chequeo de esta lección. (b) Confírmala con check_description. (c) En una frase, explica qué podría salir mal si el agente tuviera esta tool con la description vaga, frente a la pregunta "¿Reservo acepta transferencia bancaria?".

Ver solución

(a) Reescritura:

SEARCH_DOCS_DESCRIPTION_FIXED = (
    "Busca fragmentos de documentos internos de Reservo (politicas, FAQ "
    "de pagos y membresias, manuales de sala) relevantes para una "
    "pregunta en lenguaje natural. Usa esta tool cuando el usuario "
    "pregunta algo que un documento de Reservo podria responder; no la "
    "uses para cotizar precios ni crear una reserva."
)

(b) Verificación ejecutada:

print(check_description("Busca cosas en Reservo."))
print(check_description(SEARCH_DOCS_DESCRIPTION_FIXED))

Salida esperada:

['muy corta: no alcanza para decir QUE hace y CUANDO usarla', "no dice CUANDO usarla (falta una senal como 'usa esta tool cuando...')"]
[]

(c) El riesgo (concepto): con "Busca cosas en Reservo." como única pista, el modelo no tiene forma de saber que preguntas sobre métodos de pago están cubiertas por esta tool — "cosas" no menciona pagos, políticas ni FAQ. Frente a "¿Reservo acepta transferencia bancaria?", el modelo podría no reconocer search_docs como la tool correcta y responder de memoria, sin ninguna fuente real, con el riesgo de inventar una respuesta sobre métodos de pago que el corpus real de payment-methods-faq sí tiene, correcta y verificable.


Resumen y siguiente paso

  • La description de search_docs responde tres preguntas, no dos: qué hace, cuándo usarla, y cuándo NO —esta última nombrando explícitamente a get_quote/book_room, porque son las tools con las que más fácil se confunde una pregunta mixta (precio + política).
  • Reusamos check_description de agent-fundamentals-and-tool-calling sin cambios y confirmamos, ejecutando, que la description de SEARCH_DOCS_SCHEMA pasa limpio mientras que una versión vaga dispara las tres advertencias.
  • Vimos, como concepto, cómo la misma pregunta sobre la política de cancelación de Boardroom lleva al modelo a decisiones distintas —inventar una respuesta sin fuente vs. llamar search_docs correctamente— solo por cambiar el texto de la description.

Siguiente lección: 06 — Acotar la tool: k y truncado. La description ya deja claro qué hace la tool y cuándo usarla; ahora ponemos límites reales a cuánto devuelve cada vez que se ejecuta.


Recursos adicionales

  1. Anthropic — Implement tool use — Buenas prácticas de Anthropic para redactar descripciones, incluida la de ser explícito sobre cuándo NO usar una tool.
  2. agent-fundamentals-and-tool-calling-guide, Módulo 2, Lección 04 (Escribir descripciones) — fuente exacta de check_description, reusado sin cambios en esta lección.
  3. Anthropic — Tool use (function calling) overview — La referencia oficial que confirma que description es el único campo en lenguaje natural que guía la selección de tools.
  4. Python — re (Expresiones regulares)re.search, la función con la que el chequeador busca señales de "cuándo usarla" en el texto.