6.5 KiB
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)instreamText- 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? →
searchXmet filter-parameters - Wat is een unieke entiteit? →
getXByIdofgetXByName - Welke aggregaties zijn interessant? →
getStats
Stappenplan
Stap 1 — Backup huidige chat-route (2 min)
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
contextstring - De grote system prompt met alle data
Voeg toe:
import { tool } from "ai"enimport { z } from "zod"- Tool-definities boven je POST-functie
tools: { ... }enstopWhen: stepCountIs(5)instreamText(importeerstepCountIsuit"ai")- Kortere system prompt — alleen rol + tool-tips
Stap 3 — Eerste tool: searchX (15 min)
Hier het patroon — pas aan voor jouw thema:
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)
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:
-
Single tool (
searchItemstriggert):Welke [items] hebben [filter]? -
Single tool (
getItemByIdtriggert):Vertel me alles over [specifieke item]. -
Single tool (
getStatstriggert):Hoeveel [items] in totaal? En per [groep]? -
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
- GitHub URL in Brightspace
- 4 screenshots in je README (3× single + 1× multi-step)
- 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.