# Les 12 — Tool Calling ## Slide Overzicht (Klas A — 3 uur fysiek, demo-driven) **Lesvorm:** Tim demonstreert klassikaal. Studenten kijken. Zelf bouwen = thuis. **Demo-app:** Polderfest 2027 (verder bouwen op Les 11) **Vervolg op:** Les 11 — Vercel AI SDK + chat met data **Aansluit op:** Les 13 — Agents + autonome multi-step workflows --- ## Slide 1: Title ### Les 12 — Tool Calling **Visual:** - Background: CREAM - "Les 12" in BLUE - "Tool Calling" in BLACK - Subtitle: "Laat AI zelf kiezen welke functie aan te roepen" --- ## Slide 2: Terugblik ### Waar staan we? **Vorige les:** - Vercel AI SDK basics + 4 kern-functies - Polderfest 2027 demo — 500 bands in Supabase - Chat-route met `streamText` + `useChat` - AI antwoordt op vragen over de data **Het probleem dat we toen lieten zien:** - Vandaag sturen we ALLE 500 bands mee als context bij elke vraag - ~30.000 tokens per call — werkt voor 500, niet voor 50.000 **Vandaag lossen we dat op met Tool Calling.** **Visual:** Pijltje van "alles meesturen" naar "AI kiest tools" --- ## Slide 3: Planning ### Vandaag — 180 minuten | Onderwerp | Duur | |-----------|------| | Welkom + Terugblik + schaalprobleem recap | 10 min | | Theorie: wat is Tool Calling? | 30 min | | **Live Demo 1** — Eerste tool: searchBands | 20 min | | **Live Demo 2** — Multi-step + meer tools | 20 min | | **Pauze** | 15 min | | **Live Demo 3** — Tool-calls in UI tonen | 25 min | | **Live Demo 4** — Edge cases + error handling | 15 min | | Waarom Tool Calling > context-all? | 5 min | | Lesopdracht + Huiswerk uitleg | 20 min | | Vragen + Afsluiting | 15 min | **Format:** Demo-driven, jullie kijken mee. --- ## Slide 4: Wat is Tool Calling? ### AI besluit zelf welke functie te gebruiken **Het idee:** In plaats van **alle data** mee te sturen, geef je AI **tools** (functies). AI ziet een vraag, kiest welke tool relevant is, roept 'm aan met de juiste parameters, krijgt resultaat, antwoordt. **Voorbeeld-flow:** ``` User: "Welke bands spelen vrijdag op de Main Stage?" ↓ AI: ik roep searchBands({ day: "Vrijdag", stage: "Main Stage" }) aan ↓ Supabase: 12 bands ↓ AI: "Op vrijdag op de Main Stage spelen: ..." ``` **Wat krijg je:** - Schaalbaar (10 records of 10 miljoen — werkt hetzelfde) - Real-time data (geen verouderde context) - Type-safe (Zod schema voor parameters) - Multi-step (AI kan meerdere tools combineren) **Visual:** Schema diagram chat → tool → DB → AI → antwoord. --- ## Slide 5: Anatomie van een Tool ### description + inputSchema + execute ```typescript import { tool } from "ai"; import { z } from "zod"; const searchBands = tool({ description: "Zoek bands op dag, stage, of genre", inputSchema: z.object({ day: z.enum(["Vrijdag", "Zaterdag", "Zondag"]).optional(), stage: z.string().optional(), genre: z.string().optional(), }), execute: async ({ day, stage, genre }) => { // Supabase query const { data } = await supabase.from("bands").select("*") .eq("day", day || undefined) .eq("stage", stage || undefined); return data; }, }); ``` **Drie verplichte delen:** 1. **`description`** — Wat doet de tool? AI leest dit om te beslissen. 2. **`inputSchema`** — Wat heeft de tool nodig? Zod schema = type-safe. 3. **`execute`** — Wat gebeurt er als de tool wordt aangeroepen? **Belangrijk:** beschrijvingen zijn **kritiek**. AI kiest op basis van descriptions — vaag = verkeerde keuze. --- ## Slide 6: Multi-step met stopWhen ### Eén vraag = meerdere tool-calls **Het concept:** Met `stopWhen: stepCountIs(5)` geef je AI toestemming om tot 5 keer een tool aan te roepen voordat hij antwoordt. **Voorbeeld:** ``` User: "Vergelijk de top headliner met de drukst geplande opener" ↓ AI: stap 1 — searchBands({ tier: "headliner" }) → 50 bands ↓ AI: stap 2 — searchBands({ tier: "opener" }) → 100 bands ↓ AI: stap 3 — verwerkt + vergelijkt ↓ AI: antwoordt met vergelijking ``` **In code:** ```typescript const result = streamText({ model: openai("gpt-4o-mini"), tools: { searchBands, getStats, getBandByName }, stopWhen: stepCountIs(5), messages, }); ``` **Visual:** Flowchart met meerdere tool-blokjes. --- ## Slide 7: Vandaag — refactor Polderfest naar Tool Calling ### Wat gaan we bouwen? **Stap voor stap:** 1. **Refactor** de chat-route van Les 11 — weg met alle bands meesturen 2. Eerste tool: `searchBands` met filter-parameters 3. Tweede en derde tool: `getStats`, `getBandByName`, `getScheduleByDay` 4. Multi-step in actie — vragen die 2-3 tools combineren 5. UI uitbreiden — tonen welke tools AI aanriep (transparantie) 6. Edge cases: ongeldige input, lege resultaten, errors **Tools die we vandaag bouwen (6 stuks):** | Tool | Wat | Tier | |------|-----|------| | `searchBands` | Filter op dag, stage, genre, tier | Read | | `getBandByName` | Exact lookup | Read | | `getStats` | Aggregate (count per groep) | Read | | `getScheduleByDay` | Slot-overzicht per dag | Read | | `addFavorite` | User favorite toevoegen | **Write** | | `listFavorites` | User favorieten ophalen | Read | **Visual:** Tools-lijst met read/write icons. --- ## Slide 8: LIVE DEMO 1 — Eerste tool: searchBands ### ~20 min **Wat ik laat zien:** 1. Refactor `app/api/chat/route.ts` van Les 11 — weg met de hele context-string 2. Import `tool` van `ai` 3. Eerste tool definiëren: `searchBands` met description + inputSchema + execute 4. System prompt aanpassen: "Gebruik tools, verzin niet" 5. `tools: { searchBands }` + `stopWhen: stepCountIs(5)` toevoegen aan `streamText` 6. Browse naar `/chat`, vraag: "Welke bands spelen zaterdag op de Beach Stage?" 7. Zien: AI roept tool aan met juiste params → antwoordt **Visual:** Side-by-side mock-up: oude chat-route vs nieuwe met tools. --- ## Slide 9: LIVE DEMO 2 — Meer tools + multi-step ### ~20 min **Wat ik laat zien:** 1. Tweede tool: `getStats` — voor "hoeveel jazz acts?" 2. Derde tool: `getBandByName` — voor "vertel me over Lost Tigers" 3. Vierde tool: `getScheduleByDay` — voor "tijdschema vrijdag Main Stage" 4. System prompt verfijnen — wanneer welke tool 5. Vraag stellen die **meerdere tools** triggert: "Vergelijk twee genres qua headliners" 6. Tonen: `stopWhen` in actie — 2-3 tool-calls in één antwoord **Visual:** Multi-step flow diagram. --- ## Slide 10: Pauze ### 15 minuten --- ## Slide 11: LIVE DEMO 3 — Tool-calls in UI tonen ### ~25 min **Wat ik laat zien:** 1. `useChat` returnt `messages` met **parts** — text én tool-invocations 2. UI uitbreiden: parts loopen i.p.v. content 3. Tool-call rendering: "🔧 searchBands({ day: 'Vrijdag', stage: 'Main' })" 4. Tool-result rendering: collapsed by default, klik om uit te klappen 5. Streaming tool-calls visualiseren — typing-effect ook bij tool-naam 6. Bonus: loading-icoon tijdens tool-execute **Waarom transparantie?** - Studenten zien wat AI doet (debug-hulp) - Vertrouwen — gebruiker ziet "ja, hij heeft echt de DB geraadpleegd" - Voor productie: optioneel kunt verstoppen, voor demo onmisbaar **Visual:** Mock-up van chat met tool-call chips. --- ## Slide 12: LIVE DEMO 4 — Edge cases + error handling ### ~15 min **Wat ik laat zien:** **Edge case 1: ongeldige input** - Vraag: "Welke bands op Donderdag?" - AI ziet: `day` parameter is enum — Donderdag bestaat niet - Twee opties: AI weigert, of probeert ander filter **Edge case 2: lege resultaat** - Vraag: "Death metal bands?" - Tool returnt lege array - AI legt uit: "Geen death metal acts op Polderfest 2027" **Edge case 3: database error** - Wat als Supabase down is? Tool returnt `{ error: "..." }` - AI moet dit netjes communiceren — niet hallucineren **Edge case 4: write tool — addFavorite** - Demo: AI voegt favoriet toe - Confirmation tonen — "✓ toegevoegd aan favorieten" - Belangrijk: AI mag write-tools niet zonder expliciete user-intent gebruiken **Visual:** 4 edge-case scenarios + fixes. --- ## Slide 13: Waarom Tool Calling > context-all? ### De vergelijking | Aspect | Les 11 (context-all) | Les 12 (tool calling) | |--------|---------------------|----------------------| | Tokens per call | ~30.000 (500 bands) | ~2.000 (tools + result) | | Schaal | Tot ~1000 records | Tot duizenden | | Live data | Snapshot bij start | Actueel per call | | Write operaties | Niet mogelijk | Wel (addFavorite) | | Multi-step | Beperkt | Native (`stopWhen`) | | Cost | Hoger | Lager | | Complexiteit | Lager | Iets hoger | **Wanneer toch context-all?** - Heel kleine dataset (<100 records) - Snel prototype - Geen schaal nodig **Voor productie: bijna altijd Tool Calling.** --- ## Slide 14: Lesopdracht ### Bouw tools voor jouw eigen thema-app **Voor thuis — bouw voort op je app uit Les 11:** 1. Refactor je chat-route — weg met de hele context-string 2. Definieer **minstens 3 tools** voor je eigen dataset 3. Voeg `stopWhen: stepCountIs(5)` toe aan je `streamText` 4. Pas system prompt aan: "gebruik tools, verzin niet" 5. Test 3 vragen die meerdere tools combineren **Tools voorbeelden (afhankelijk van jouw thema):** - `searchX(filter)` — read met filters - `getXById(id)` — exact lookup - `getStats(groupBy)` — aggregate - Voor jouw thema-specifieke acties **Eisen:** - Werkende refactor (chat werkt nog) - Min 3 tools waarvan minstens 1 met enum parameters - Min 1 vraag die 2+ tools combineert (multi-step) --- ## Slide 15: Huiswerk ### Tools uitbreiden + UI visualisatie + write-tool **Voor volgende week (Les 13):** **Onderdeel A — Write-tool toevoegen** - Maak een `user_X` tabel in Supabase (favorites, notes, votes, ...) - Schrijf write-tool: `addX(userId, itemId)` - Stel een vraag die deze tool triggert: "voeg X toe aan mijn lijst" **Onderdeel B — Tool-calls in UI** - Refactor je chat UI om tool-invocations te tonen - Style: chip / badge / collapsed result - Reden: transparantie + debug **Onderdeel C — `TOOLS.md`** Schrijf in repo-root: - Welke tools heb je gedefinieerd? (lijst + descriptions) - 3 vragen die 1 tool gebruiken - 1 vraag die 2+ tools combineert (multi-step) - 1 voorbeeld van een edge-case die AI goed afhandelde **Bonus:** Loading indicator per tool-execute, tool-result formatten als kaartjes. --- ## Slide 16: Volgende les — Agents + autonomie ### Hoe ver kan een AI autonoom? **Wat we vandaag deden:** - AI roept tools aan in 1 ronde, maximaal 5 stappen - Antwoord komt terug naar gebruiker **Volgende les (Les 13):** - **AI Agents** — langere autonome workflows - `stopWhen: stepCountIs(20)` of meer — AI plant, voert uit, evalueert, herhaalt - Tools die andere tools triggeren - Stop-condities, retries, error recovery - Voorbeeld: "Plan mijn volledige Polderfest weekend" — AI bekijkt alle dagen, maakt schema, voegt toe aan favorieten **Daarna in deze leerlijn:** - Les 14: RAG + embeddings (semantic search op grote datasets) - Les 15-16: Testing + Deployment + Performance - Les 17-18: Eindopdracht-werkdagen + Pitch --- ## Slide 17: Afsluiting ### Vragen? **Vandaag gezien:** - Schaalprobleem van context-all opgelost met Tool Calling - Anatomie van een tool: description + inputSchema + execute - `stopWhen` voor multi-step workflows - Zes tools voor Polderfest gebouwd - Tool-invocations in UI gevisualiseerd - Edge cases + error handling **Volgende les:** Agents + autonomie **Vragen? Feedback?** --- ## Slide Summary | # | Title | Type | |---|-------|------| | 1 | Title | Opening | | 2 | Terugblik + schaalprobleem | Recap | | 3 | Planning | 180-min | | 4 | Wat is Tool Calling | Theorie | | 5 | Anatomie van een tool | Theorie | | 6 | Multi-step met stopWhen | Theorie | | 7 | Vandaag bouwen we | Intro demo | | 8 | **LIVE DEMO 1** — searchBands | Demo | | 9 | **LIVE DEMO 2** — Multi-step + meer tools | Demo | | 10 | Pauze | Break | | 11 | **LIVE DEMO 3** — Tool-calls in UI | Demo | | 12 | **LIVE DEMO 4** — Edge cases | Demo | | 13 | Tool Calling vs context-all | Reflectie | | 14 | Lesopdracht | Praktijk | | 15 | Huiswerk | Praktijk | | 16 | Volgende les: Agents | Preview | | 17 | Afsluiting | Closing | --- ## Bronnen - Vercel AI SDK Tools: https://ai-sdk.dev/docs/foundations/tools - Multi-step: https://ai-sdk.dev/docs/foundations/agents - Zod docs: https://zod.dev - OpenAI Function Calling docs: https://platform.openai.com/docs/guides/function-calling - Supabase JS query builder: https://supabase.com/docs/reference/javascript/select