add les 12
This commit is contained in:
228
Les12-Tool-Calling/Les12-Lesopdracht.md
Normal file
228
Les12-Tool-Calling/Les12-Lesopdracht.md
Normal file
@@ -0,0 +1,228 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user