# Les 12 — Lesopdracht ## Refactor jouw thema-app naar Tool Calling **Vak:** AI-Assisted Development **Opleiding:** NOVI Hogeschool Utrecht **Wanneer:** Thuis, vóór volgende les **Inleveren:** GitHub URL + screenshots van werkende multi-step chat --- ## Doel Bouw voort op je **eigen thema-app** uit Les 11. Refactor de chat-route — weg met de hele dataset meesturen. Vervang door **tools** die AI zelf kan aanroepen. Je oefent: - Tools definiëren met description + inputSchema + execute - `stopWhen: stepCountIs(N)` voor multi-step queries - System prompts voor tool-gebruik aansturen - Multi-step vragen testen --- ## Vereisten - Werkende app uit Les 11 (Next.js + Supabase + chat met data) - Eigen thema (geen Polderfest namaken) - 100+ records in je Supabase - OpenAI key in `.env.local` > Heb je geen werkende Les 11 app? Eerst die afmaken. Deze opdracht bouwt erop voort. --- ## Wat moet er staan? ### Code - [ ] **Chat-route gerefactord** — geen `.select("*")` aan begin van POST meer - [ ] **Minstens 3 tools** gedefinieerd voor jouw dataset - [ ] Eén tool met **enum parameters** (vaste keuze) - [ ] Eén tool met **optional filters** (`.optional()` parameters) - [ ] `stopWhen: stepCountIs(5)` in `streamText` - [ ] System prompt aangepast: "gebruik tools, verzin niet" ### Werking - [ ] Chat werkt nog (geen 500-errors) - [ ] Minstens 3 vragen die elk **1 tool** triggeren - [ ] Minstens 1 vraag die **2+ tools** triggert (multi-step) - [ ] AI verzint niet meer — tool-results worden gebruikt --- ## Tools voor jouw thema — voorbeelden ### Restaurant-aggregator | Tool | Parameters | Wat | |------|-----------|-----| | `searchRestaurants` | cuisine?, price_range?, neighborhood? | Filter restaurants | | `getRestaurantById` | id | Detail + menu | | `getCuisineStats` | (geen) | Verdeling per cuisine | ### Scriptie-archief | Tool | Parameters | Wat | |------|-----------|-----| | `searchTheses` | year?, supervisor?, keyword? | Filter scripties | | `getThesisAbstract` | id | Volledige samenvatting | | `getYearStats` | (geen) | Telling per jaar | ### Museum-collectie | Tool | Parameters | Wat | |------|-----------|-----| | `searchArtworks` | artist?, period?, medium? | Filter kunstwerken | | `getArtworkDetails` | id | Detail + provenance | | `getPeriodStats` | (geen) | Verdeling per stroming | **Voor jouw thema — bedenk:** - Wat filtert iemand vaak? → `searchX` met filter-parameters - Wat is een unieke entiteit? → `getXById` of `getXByName` - Welke aggregaties zijn interessant? → `getStats` --- ## Stappenplan ### Stap 1 — Backup huidige chat-route (2 min) ```bash cp app/api/chat/route.ts app/api/chat/route.les11.ts.bak ``` Voor het geval de refactor faalt. ### Stap 2 — Refactor route.ts (20 min) Open `app/api/chat/route.ts`. Verwijder: - `const { data: bands } = await supabase...select("*")` aan begin - De grote `context` string - De grote system prompt met alle data Voeg toe: - `import { tool } from "ai"` en `import { z } from "zod"` - Tool-definities boven je POST-functie - `tools: { ... }` en `stopWhen: stepCountIs(5)` in `streamText` (importeer `stepCountIs` uit `"ai"`) - Kortere system prompt — alleen rol + tool-tips ### Stap 3 — Eerste tool: searchX (15 min) Hier het patroon — pas aan voor jouw thema: ```typescript const searchItems = tool({ description: "Zoek items in [thema-naam]. Filter op X, Y, of Z. " + "Gebruik dit voor filtervragen.", inputSchema: z.object({ category: z.enum(["A", "B", "C"]).optional(), minRating: z.number().min(1).max(5).optional(), keyword: z.string().optional().describe("Zoekterm in titel of beschrijving"), }), execute: async ({ category, minRating, keyword }) => { let q = supabase.from("items").select("*"); if (category) q = q.eq("category", category); if (minRating) q = q.gte("rating", minRating); if (keyword) q = q.ilike("title", `%${keyword}%`); const { data, error } = await q.limit(20); if (error) return { error: error.message }; return { count: data.length, items: data }; }, }); ``` ### Stap 4 — Twee extra tools (15 min) Voeg minstens 2 meer toe. Inspiratie boven. ### Stap 5 — System prompt aanpassen (5 min) ```typescript const system = `Je bent een assistent voor [thema-naam]. Gebruik de beschikbare tools om vragen te beantwoorden. Tips: - Voor "welke X met Y?" → searchItems - Voor "vertel me over X" → getItemById - Voor "hoeveel" of "verdeling" → getStats Verzin nooit data. Antwoord in het Nederlands.`; ``` ### Stap 6 — Test multi-step (15 min) In je chat, probeer: 1. **Single tool** (`searchItems` triggert): ``` Welke [items] hebben [filter]? ``` 2. **Single tool** (`getItemById` triggert): ``` Vertel me alles over [specifieke item]. ``` 3. **Single tool** (`getStats` triggert): ``` Hoeveel [items] in totaal? En per [groep]? ``` 4. **Multi-step** (2+ tools): ``` Geef me 3 [items] in categorie X, en vergelijk ze qua [eigenschap]. ``` ### Stap 7 — Screenshots maken (8 min) Voor je inleveren bij Brightspace: - 1 screenshot per single-tool vraag (3 stuks) - 1 screenshot van multi-step vraag (waar je 2 tool-calls ziet — als je UI ze toont) --- ## Inleveren 1. **GitHub URL** in Brightspace 2. **4 screenshots** in je README (3× single + 1× multi-step) 3. **Eén alinea** in README: welke tools heb je gedefinieerd? Waarom deze? --- ## Veelvoorkomende problemen | Probleem | Oplossing | |----------|-----------| | AI roept geen tools aan | Description verbeteren — wees specifieker | | AI gebruikt 1 tool meerdere keren | System prompt — zeg "kies juiste tool" | | AI returnt foutmelding "Tool args invalid" | Zod schema te strict — gebruik `.optional()` waar nodig | | `Cannot find module 'zod'` | `npm install zod` | | Tool execute crasht | Error throwen → niet doen, return `{ error: "..." }` | | Multi-step werkt niet | `stopWhen: stepCountIs(5)` toegevoegd? Default is 1 stap. | --- ## Tijd-indicatie | Stap | Tijd | |------|------| | Backup + refactor route | 22 min | | 3 tools definiëren | 30 min | | System prompt + multi-step | 20 min | | Testen + screenshots | 23 min | | **Totaal** | **~1,5 uur** | Loop je vast? Vraag op Brightspace. --- ## Tips - **Begin klein** — eerst één tool werkend. Dan twee. Dan drie. Niet alles tegelijk. - **Schrijf descriptions zoals voor een collega** — "Doe X als gebruiker vraagt om Y." - **Test direct na elke tool** — werkt 't? Goed. Werkt 't niet? Beschrijving aanpassen. - **Gebruik enums** voor vaste keuzes — AI respecteert ze automatisch. Succes! Volgende les zien we hoe ver dit kan met Agents.