Files
novi-lessons/Les12-Tool-Calling/Les12-Huiswerk.md
2026-05-21 08:52:47 +02:00

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

  1. 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 item
    • user_votes — like/dislike per item
    • user_watchlist — bewaar voor later
    • user_visited — track wat al gezien is
  2. 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 };
      },
    });
    
  3. 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) ?? [];
      },
    });
    
  4. 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:

  1. Vraag: "..." Tool die triggered: searchItems({ ... }) Antwoord (samenvatting): ...

  2. (idem)

  3. (idem)

1 vraag die 2+ tools combineert (multi-step):

Vraag: "..." Tools die triggeren in volgorde:

  1. searchItems(...) — krijgt 5 items
  2. getItemById(...) — 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

  1. GitHub repo URL in Brightspace
  2. TOOLS.md in repo-root
  3. Schema-update in schema.sql (met nieuwe tabel)
  4. Updated app/api/chat/route.ts met write-tool
  5. Updated app/chat/page.tsx met 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 meer tool-invocation). Oudere versies hadden toolInvocations array.
  • details collapsed in dev maar uitgeklapt in prod — Tailwind / browser-default. Test in productie.
  • useChat returnt parts undefined — check je AI SDK versie, run npm 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/docs voor 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!