add les 12

This commit is contained in:
2026-05-21 08:52:47 +02:00
parent 634789e615
commit eb1ba2e28d
42 changed files with 11012 additions and 8 deletions

View File

@@ -0,0 +1,404 @@
# 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