Sesión 4 · Tool Use
Crea la herramienta findCourses
Define el catálogo didáctico, valida las entradas con Zod y devuelve coincidencias por título y nivel.
Qué aprenderás
- Separar descripción, esquema de entrada y ejecución de una herramienta.
- Permitir filtros opcionales de texto y nivel.
- Devolver datos del catálogo y un recuento sin inventar resultados.
Referencias de código
Índice de esta lección
Paso 1
Toma el catálogo del gist
El gist de Klerith aporta cinco cursos con ID, título, horas, precio, nivel y alumnos. Trata estos datos como una base local del ejercicio.
Las búsquedas de TypeScript tienen dos coincidencias: TypeScript desde cero y Agentes de IA con TypeScript. No elijas una automáticamente si la petición es ambigua.
La versión adaptada separa el catálogo en catalog.ts y las herramientas en tools.ts. Mantiene el patrón del material y permite probar las reglas sin llamar a un modelo.
// Catálogo didáctico adaptado del gist de Klerith (Fernando Herrera).// https://gist.github.com/Klerith/1f32130c61219bbe7be68e72d0626c40// Estos valores son datos del ejercicio, no precios comerciales actuales.export type Course = { id: string; title: string; hours: number; priceUSD: number; level: "basic" | "intermediate" | "advanced"; students: number;}; export const COURSE_CATALOG: Course[] = [ { id: "ts-01", title: "TypeScript desde cero", hours: 22, priceUSD: 19.99, level: "basic", students: 48_120 }, { id: "nest-02", title: "NestJS: API REST modular", hours: 31, priceUSD: 24.99, level: "intermediate", students: 22_450 }, { id: "flu-03", title: "Flutter: apps multiplataforma", hours: 46, priceUSD: 29.99, level: "intermediate", students: 61_300 }, { id: "agt-04", title: "Agentes de IA con TypeScript", hours: 18, priceUSD: 34.99, level: "advanced", students: 1_890 }, { id: "dkr-05", title: "Docker para desarrolladores", hours: 14, priceUSD: 17.99, level: "basic", students: 35_770 },];Qué deberías observar
El catálogo contiene IDs y datos explícitos que podrán devolver las herramientas.
Paso 2
Define filtros que se pueden omitir
text y level son opcionales. La transcripción primero deja level obligatorio y lo corrige después; aquí incorporamos la corrección desde el inicio.
Zod describe la forma válida de los argumentos. Las descripciones ayudan a seleccionar entradas, pero no garantizan que el modelo escoja el curso correcto.
import { tool } from "ai";import { z } from "zod";import { COURSE_CATALOG } from "./catalog.js"; export const searchSchema = z.object({ text: z.string().optional().describe("Texto incluido en el título del curso"), level: z.enum(["basic", "intermediate", "advanced"]).optional() .describe("Nivel del curso; omitir para buscar en todos los niveles"),});Qué deberías observar
Una búsqueda por texto no exige inventar un nivel.
Paso 3
Busca por título y por nivel
Normaliza el texto a minúsculas y elimina espacios exteriores. Combina los filtros con AND: un curso debe cumplir cada filtro enviado.
includes realiza una búsqueda por fragmento contiguo. No es una búsqueda semántica ni divide una frase en palabras. Una cadena sin coincidencias devuelve cero cursos.
export function findCoursesData(input: z.input<typeof searchSchema>) { const { text, level } = searchSchema.parse(input); const query = text?.trim().toLowerCase(); const courses = COURSE_CATALOG.filter((course) => { const matchesText = !query || course.title.toLowerCase().includes(query); const matchesLevel = !level || course.level === level; return matchesText && matchesLevel; }); return { found: courses.length, courses };}Qué deberías observar
Puedes buscar TypeScript en todos los niveles o restringirlo a basic.
Paso 4
Envuelve la función con tool
tool recibe una descripción, inputSchema y execute. El modelo propone argumentos y la aplicación ejecuta la operación con datos del catálogo.
La función devuelve un objeto con found y courses. Una ejecución asíncrona permite sustituir la búsqueda local por una consulta externa después; una función puramente síncrona también es válida.
export const findCourses = tool({ description: "Busca cursos por texto en el título y/o nivel. Consulta esta herramienta antes de responder sobre cursos, precios, horas o alumnos.", inputSchema: searchSchema, execute: async (input) => findCoursesData(input),});Qué deberías observar
La herramienta está definida, pero solo estará disponible al registrarla en tools.
Paso 5
Comprueba las reglas sin gastar tokens
Antes de integrar el modelo, llama a la función local. Estas pruebas no requieren claves ni una API.
Para TypeScript sin nivel hay dos coincidencias; con basic hay ts-01. Una búsqueda ausente devuelve found: 0 y courses: []. Un nivel fuera de la enumeración falla en la validación.
findCoursesData({ text: " TYPESCRIPT " }); // found: 2findCoursesData({ text: "TypeScript", level: "basic" }); // ts-01findCoursesData({ text: "no-existe" }); // found: 0findCoursesData({}); // los cinco cursosQué deberías observar
Puedes separar errores de la función de errores de integración con el proveedor.
Ahora te toca
Prueba la idea por tu cuenta
Busca TypeScript sin filtro de nivel y después con basic. Explica por qué aparecen dos resultados en la primera búsqueda y cómo tratarías esa ambigüedad antes de cotizar.
Necesito una pista
Examina los títulos del catálogo; dos incluyen la palabra TypeScript.
Comparar con una solución
Sin level aparecen ts-01 y agt-04. Con basic solo ts-01. Si el usuario no ha precisado el curso, pide una aclaración o el ID antes de calcular.
Antes de seguir
Comprueba lo que aprendiste
No es una nota. Es una pausa para comprobar que la idea quedó clara.
¿Terminaste la práctica?
Haz el ejercicio y comprueba tus respuestas antes de marcar la lección como completada.