# 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 — Cursor + Vercel deploy

---

## 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 — Cursor + Vercel deploy
### 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):**
- Externe APIs in Next.js Server Components
- Cursor Composer + Background Agents
- Deploy naar Vercel productie + preview per branch
- GitHub Actions CI (lint + build)

**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:** Cursor + Vercel deploy

**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: Cursor + Vercel | 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
