fix: add les 11
This commit is contained in:
195
Les11-AI-SDK/Les11-Lesopdracht.md
Normal file
195
Les11-AI-SDK/Les11-Lesopdracht.md
Normal file
@@ -0,0 +1,195 @@
|
||||
# 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!
|
||||
Reference in New Issue
Block a user