196 lines
6.0 KiB
Markdown
196 lines
6.0 KiB
Markdown
# Les 11 — Lesopdracht
|
||
## Bouw je eigen AI + Supabase app
|
||
|
||
**Vak:** AI-Assisted Development
|
||
**Opleiding:** NOVI Hogeschool Utrecht
|
||
**Wanneer:** Thuis, vóór volgende les
|
||
**Inleveren:** GitHub URL + screenshot van werkende chat
|
||
|
||
---
|
||
|
||
## Doel
|
||
|
||
Bouw een complete versie van wat Tim live demonstreerde — **met je eigen thema**. Tim bouwde Polderfest 2027. Jij bouwt iets anders.
|
||
|
||
Je oefent end-to-end:
|
||
- Next.js project from scratch
|
||
- Nieuwe Supabase aanmaken + koppelen
|
||
- Eigen seed-script schrijven (mag AI bij helpen)
|
||
- Chat-route + chat-UI met `streamText` + `useChat`
|
||
- 3 vragen kunnen stellen die alleen via jouw data beantwoord kunnen worden
|
||
|
||
> **Belangrijk:** dit is **niet** dezelfde Polderfest-demo namaken. Je kiest een eigen thema. Anders leer je vooral kopiëren.
|
||
|
||
---
|
||
|
||
## Vereisten
|
||
|
||
### Jouw thema moet:
|
||
- [ ] **Volledig fictief zijn** — geen Spotify, geen restaurants in Amsterdam, geen Wikipedia-data
|
||
- [ ] Minstens 5 velden hebben (waarvan 1 categorisch, 1 numeriek, 1 tekstrijk)
|
||
- [ ] Minstens **100 records** bevatten
|
||
- [ ] Vragen oproepen die je écht niet aan ChatGPT kunt stellen zonder jouw data
|
||
|
||
### Verboden thema's (te bekend voor LLMs):
|
||
- Restaurants in een echte stad
|
||
- Films / muziek / boeken die echt bestaan
|
||
- Sport-statistieken uit de echte wereld
|
||
- Klimaat / financiële data uit publieke bronnen
|
||
|
||
### Geschikte thema-richtingen (kies of bedenk eigen):
|
||
- Fictief restaurant-aggregator in een verzonnen stad (bv. "Polderstad")
|
||
- Galactische bestuurders archief (sci-fi)
|
||
- Verzonnen scriptie-archief van NOVI (1000 nep-titels)
|
||
- Fictieve museumcollectie met verzonnen kunstenaars
|
||
- Fictief NPO-programma overzicht voor 2030
|
||
- Verzonnen mysteries / cryptid-sightings database NL
|
||
- D&D campagne-NPCs voor een fictieve wereld
|
||
- Fictieve nederlands ondernemers ecosysteem
|
||
|
||
---
|
||
|
||
## Stappenplan
|
||
|
||
### Stap 1 — Thema kiezen + schema ontwerpen (30 min)
|
||
|
||
- Bedenk thema
|
||
- Schrijf op papier (of in Notion): welke velden? Welke vragen wil je kunnen stellen?
|
||
- Vertaal naar Supabase SQL schema (zie `schema.sql` van Polderfest als voorbeeld)
|
||
|
||
### Stap 2 — Next.js project + Supabase (15 min)
|
||
|
||
```bash
|
||
npx create-next-app@latest mijn-thema \
|
||
--typescript --tailwind --app --eslint --no-src-dir --turbopack
|
||
cd mijn-thema
|
||
npm i @supabase/supabase-js ai @ai-sdk/openai zod
|
||
npm i tsx dotenv --save-dev
|
||
```
|
||
|
||
- Maak nieuwe Supabase project aan
|
||
- Run je schema in SQL Editor
|
||
- Vul `.env.local` met URL + keys + OpenAI key
|
||
|
||
### Stap 3 — Seed script (45 min)
|
||
|
||
Open `seed-polderfest.ts` (zie bijlage) als referentie. Je hebt twee opties:
|
||
|
||
**Optie A — Met de hand:**
|
||
- Kopieer de structuur
|
||
- Vervang Polderfest-bouwstenen (genres, stages, cities…) door jouw thema-bouwstenen
|
||
- Pas de `generateBand` functie aan naar `generateItem` voor jouw thema
|
||
|
||
**Optie B — Met AI hulp (aanbevolen):**
|
||
- Open OpenCode / Cursor met `seed-polderfest.ts` als context
|
||
- Vraag: "Pas dit seed-script aan voor [mijn thema]. Schema is [paste schema]. Genereer 100+ records."
|
||
- Review wat AI maakt — vragen om aanpassingen waar nodig
|
||
|
||
Run je seed:
|
||
```bash
|
||
npx tsx scripts/seed-mijn-thema.ts
|
||
```
|
||
|
||
Verifieer in Supabase Table Editor: 100+ records.
|
||
|
||
### Stap 4 — Chat route (20 min)
|
||
|
||
`app/api/chat/route.ts` — gebruik Polderfest-versie als template:
|
||
|
||
```typescript
|
||
import { streamText } from "ai";
|
||
import { openai } from "@ai-sdk/openai";
|
||
import { createClient } from "@supabase/supabase-js";
|
||
|
||
const supabase = createClient(
|
||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
||
);
|
||
|
||
export async function POST(req: Request) {
|
||
const { messages } = await req.json();
|
||
const { data: items } = await supabase.from("items").select("*");
|
||
|
||
const context = items!.map((i) =>
|
||
`- ${i.name} (${i.category}, ${i.rating})`
|
||
).join("\n");
|
||
|
||
const system = `Je bent een assistent voor [thema-naam].
|
||
Hier is alle data:
|
||
|
||
${context}
|
||
|
||
Beantwoord vragen op basis van bovenstaande data. Verzin niets.
|
||
Antwoord in het Nederlands.`;
|
||
|
||
const result = streamText({
|
||
model: openai("gpt-4o-mini"),
|
||
system,
|
||
messages,
|
||
});
|
||
|
||
return result.toDataStreamResponse();
|
||
}
|
||
```
|
||
|
||
### Stap 5 — Chat UI (15 min)
|
||
|
||
`app/chat/page.tsx` — kopieer Polderfest UI, pas titel + placeholder aan.
|
||
|
||
### Stap 6 — Testen + 3 vragen (15 min)
|
||
|
||
Browse naar `/chat`. Stel deze 3 vragen aan jouw AI:
|
||
|
||
1. Een **filter-vraag** ("Welke X hebben Y?")
|
||
2. Een **aggregatie-vraag** ("Hoeveel X zijn er in totaal?" / "Wie heeft de hoogste Z?")
|
||
3. Een **samenvatting-vraag** ("Vat de Z-categorie samen in 3 zinnen")
|
||
|
||
Screenshots van werkende antwoorden bewaren — die heb je nodig.
|
||
|
||
---
|
||
|
||
## Inleveren (vóór volgende les)
|
||
|
||
1. **GitHub repo** met je code
|
||
2. **3 screenshots** van werkende chat-antwoorden in je README
|
||
3. **Eén alinea** onder de screenshots: waarom kan een gewone LLM deze vragen niet beantwoorden zonder jouw data?
|
||
|
||
---
|
||
|
||
## Veelvoorkomende problemen
|
||
|
||
| Probleem | Oplossing |
|
||
|----------|-----------|
|
||
| `OPENAI_API_KEY is not defined` | Dev server herstarten na `.env.local` aanpassen |
|
||
| Supabase insert: `permission denied` | Gebruik `SUPABASE_SERVICE_ROLE_KEY` in seed-script (geen anon) |
|
||
| AI verzint dingen | System prompt verstevigen: "Verzin niets. Gebruik alleen onze data." |
|
||
| AI antwoordt in Engels | Voeg toe aan prompt: "Antwoord in het Nederlands." |
|
||
| Chat hangt / streamt niet | API endpoint moet `result.toDataStreamResponse()` returnen |
|
||
| `tsx command not found` | `npm i tsx --save-dev`, run met `npx tsx ...` |
|
||
|
||
---
|
||
|
||
## Tijd-indicatie
|
||
|
||
| Stap | Tijd |
|
||
|------|------|
|
||
| Thema + schema bedenken | 30 min |
|
||
| Project + Supabase setup | 15 min |
|
||
| Seed script (met AI hulp) | 45 min |
|
||
| Chat route + UI | 35 min |
|
||
| Testen + screenshots | 15 min |
|
||
| **Totaal** | **~2,5 uur** |
|
||
|
||
Loop je vast? Vraag in Brightspace of plan korte 1-op-1 met Tim.
|
||
|
||
---
|
||
|
||
## Tips
|
||
|
||
- **Begin klein.** 100 records is genoeg. 500 als je extra wil.
|
||
- **AI laten helpen bij seed.** Schaal je productiviteit 10×.
|
||
- **System prompt is je hefboom.** Goede prompt = goede antwoorden. Slechte prompt = AI verzint.
|
||
- **Test met simpele vraag eerst** ("Hoeveel records zijn er?"). Dan opbouwen.
|
||
- **Token cost in de gaten houden** — onze hele les kostte <2 cent. Jouw test ook.
|
||
|
||
Succes!
|