7.9 KiB
Les 12 — Huiswerk
Write-tool + UI visualisatie + reflectie
Vak: AI-Assisted Development
Opleiding: NOVI Hogeschool Utrecht
Deadline: Voor de volgende les (Les 13 — Agents)
Inleveren: GitHub repo + TOOLS.md in root
Doel
Bouwt voort op de lesopdracht (jouw thema-app met 3 read-tools). Hier voeg je een write-tool toe, visualiseert tool-calls in je UI, en documenteert in TOOLS.md.
Niet klaar met de lesopdracht? Eerst die afmaken — het huiswerk heeft de 3 read-tools als startpunt nodig.
Onderdeel A — Write-tool toevoegen (verplicht)
Voeg een tool toe waarmee de gebruiker iets kan opslaan in je database.
Stappen
-
Nieuwe tabel in Supabase — voor user-acties. Voorbeelden:
create table user_favorites ( id bigserial primary key, user_email text not null, item_id bigint not null references items(id) on delete cascade, created_at timestamp default now(), unique(user_email, item_id) );Andere ideeën — afhankelijk van thema:
user_notes— eigen aantekeningen per itemuser_votes— like/dislike per itemuser_watchlist— bewaar voor lateruser_visited— track wat al gezien is
-
Write-tool schrijven
const addToFavorites = tool({ description: "Voeg een item toe aan de favorieten van de gebruiker. " + "Alleen gebruiken als de gebruiker expliciet vraagt om iets toe te voegen.", inputSchema: z.object({ userEmail: z.string().email(), itemName: z.string(), }), execute: async ({ userEmail, itemName }) => { const { data: item } = await supabase .from("items").select("id").ilike("name", itemName).single(); if (!item) return { error: `'${itemName}' niet gevonden.` }; const { error } = await supabase .from("user_favorites") .insert({ user_email: userEmail, item_id: item.id }); if (error) return { error: error.message }; return { success: true, itemName }; }, }); -
Lees-tool er bij (om de favorieten weer terug te halen):
const listFavorites = tool({ description: "Geef de favorieten van de gebruiker.", inputSchema: z.object({ userEmail: z.string().email() }), execute: async ({ userEmail }) => { const { data, error } = await supabase .from("user_favorites") .select("items(name, ...)") .eq("user_email", userEmail); if (error) return { error: error.message }; return data?.map((r) => r.items) ?? []; }, }); -
Test in chat:
- "Voeg X toe aan mijn favorieten, mijn email is test@test.nl"
- "Wat staat er nu in mijn favorieten?"
Eisen
- Nieuwe tabel in Supabase (zelfde regels: RLS aan, policies voor demo)
- Write-tool werkt — favorieten worden opgeslagen
- List-tool werkt — favorieten worden teruggehaald
- System prompt aangepast — "alleen toevoegen op expliciete request"
Onderdeel B — Tool-calls in UI tonen (verplicht)
Refactor je chat-UI om tool-invocations zichtbaar te maken.
Hoe
useChat returnt messages met parts — een array van delen. Tekst-parts én tool-invocation-parts.
{messages.map((m) => (
<div key={m.id}>
{m.parts?.map((part, i) => {
if (part.type === "text") {
return <div key={i}>{part.text}</div>;
}
// In AI SDK v6 zijn tool-parts genaamd `tool-<toolName>`
if (part.type?.startsWith("tool-")) {
const toolName = part.type.replace("tool-", "");
return (
<div key={i} className="bg-yellow-50 border border-yellow-300 p-2 rounded text-sm">
🔧 <strong>{toolName}</strong>({JSON.stringify(part.input)})
{part.state === "output-available" && (
<details className="mt-1">
<summary>Toon resultaat</summary>
<pre>{JSON.stringify(part.output, null, 2)}</pre>
</details>
)}
</div>
);
}
return null;
})}
</div>
))}
Eisen
- Tool-invocations zichtbaar als chip / badge / box
- Tool-naam + args getoond
- Resultaat (collapsed) zichtbaar bij open-klikken
- Multi-step vragen tonen meerdere chips
Onderdeel C — TOOLS.md documentatie (verplicht)
Schrijf in je repo-root een markdown-bestand met de volgende secties.
Sectie 1 — Mijn tools
Tabel met al je tools:
| Tool | Wat doet 't | Read / Write |
|---|---|---|
| searchItems | Filter op X, Y, Z | Read |
| getItemById | Detail van één item | Read |
| getStats | Verdeling per groep | Read |
| addToFavorites | User favoriet opslaan | Write |
| listFavorites | User favorieten ophalen | Read |
Sectie 2 — Voorbeeld-vragen
3 vragen die 1 tool gebruiken:
-
Vraag: "..." Tool die triggered:
searchItems({ ... })Antwoord (samenvatting): ... -
(idem)
-
(idem)
1 vraag die 2+ tools combineert (multi-step):
Vraag: "..." Tools die triggeren in volgorde:
searchItems(...)— krijgt 5 itemsgetItemById(...)— detail van item #3 Antwoord: ...
Sectie 3 — Eén edge-case die AI goed afhandelde
Wat gebeurde er: ... Welke tool was 't: ... Hoe handelde AI 't af: ...
Voorbeelden van edge cases:
- Ongeldige enum-value
- Lege resultaten
- Database error
- Vraag waar geen tool voor bestaat
Vorm
- Max 500 woorden totaal
- Concrete voorbeelden
- Mag wat informeel
Bonus (optioneel)
- Loading indicator per tool-execute (spinner naast tool-naam tijdens running)
- Mooie cards voor tool-results in plaats van JSON-dump
- Confirmation UI voor write-tools (bevestig vóór insert)
- Tool failure recovery — als tool faalt, AI probeert andere tool
Inleveren
- GitHub repo URL in Brightspace
TOOLS.mdin repo-root- Schema-update in
schema.sql(met nieuwe tabel) - Updated
app/api/chat/route.tsmet write-tool - Updated
app/chat/page.tsxmet tool-call rendering
Beoordeling
| Criterium | Punten |
|---|---|
| A — Write-tool werkt + list-tool werkt | 3 |
| B — Tool-calls in UI zichtbaar | 2 |
| C — TOOLS.md aanwezig met 3 secties | 3 |
| Chat werkt end-to-end (geen broken pages) | 1 |
| Documentatie sectie 3 (edge case) is concreet | 1 |
| Totaal | 10 |
Voldoende = 6+. Bonus telt mee bij twijfelgevallen.
Tijd-indicatie
| Onderdeel | Tijd |
|---|---|
| A — Write-tool (tabel + tool + test) | 40 min |
| B — UI tool-call rendering | 30 min |
| C — TOOLS.md schrijven | 20 min |
| Totaal | ~1,5 uur |
Veelvoorkomende valkuilen
- Write-tool zonder permissies — werkt voor demo, maar in productie crash je als anon-user iets probeert te schrijven. Voor nu: RLS-policies open zetten.
- Confusion AI tussen read en write — sterke description: "alleen gebruiken als gebruiker expliciet vraagt om..."
- UI part-types niet ondersteund — check welke versie AI SDK je hebt. In v6 zijn tool-parts genaamd
tool-<toolName>(niet meertool-invocation). Oudere versies haddentoolInvocationsarray. detailscollapsed in dev maar uitgeklapt in prod — Tailwind / browser-default. Test in productie.useChatreturnt parts undefined — check je AI SDK versie, runnpm update ai
Tips
- Schrijf TOOLS.md gaandeweg — niet aan eind. Sla goede voorbeelden + screenshots op zodra ze werken.
- Test write-tool eerst los — voordat je 'm via chat triggert, run de execute-functie handmatig. Sneller debuggen.
- UI parts is nieuw — als je oude tutorial volgt, kan API anders zijn. Check
ai-sdk.dev/docsvoor de actuele syntax. - Edge case 'goed' afhandelen is subjectief — schrijf in TOOLS.md wat jouw definitie van 'goed' was.
Volgende les: Agents. Met je tools werkend gaan we dan zien hoe AI 30+ tool-calls in één request kan plannen. Tot dan!