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:
- ¿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.
- ¿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
-
Copiar la lista de chequeo de
get_quotesin adaptar la tercera pregunta. "Cuándo NO usarla" para una tool estructurada comoget_quotees 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ícitamenteget_quote/book_roomen ladescriptiondesearch_docs(y viceversa, si las escribieras juntas) es lo que resuelve esa ambigüedad específica. -
Prometer que
search_docs"siempre encuentra la respuesta". Ladescriptionno debería sugerir que la tool es infalible — BM25 es recuperación léxica, y unadescriptionhonesta no genera expectativas que el índice no puede cumplir. La Lección 07 muestra un caso de 0 resultados de verdad. -
Olvidar mencionar el límite
ken ladescription. 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áximokfragmentos") evita esa sorpresa. -
Escribir una
descriptiongené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_DESCRIPTIONdel 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
descriptiondesearch_docsresponde tres preguntas, no dos: qué hace, cuándo usarla, y cuándo NO —esta última nombrando explícitamente aget_quote/book_room, porque son las tools con las que más fácil se confunde una pregunta mixta (precio + política). - Reusamos
check_descriptiondeagent-fundamentals-and-tool-callingsin cambios y confirmamos, ejecutando, que ladescriptiondeSEARCH_DOCS_SCHEMApasa 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_docscorrectamente— solo por cambiar el texto de ladescription.
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
- 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.
agent-fundamentals-and-tool-calling-guide, Módulo 2, Lección 04 (Escribir descripciones) — fuente exacta decheck_description, reusado sin cambios en esta lección.- Anthropic — Tool use (function calling) overview — La referencia oficial que confirma que
descriptiones el único campo en lenguaje natural que guía la selección de tools. - Python —
re(Expresiones regulares) —re.search, la función con la que el chequeador busca señales de "cuándo usarla" en el texto.