fix: lessons

This commit is contained in:
2026-06-07 13:35:02 +02:00
parent 39ec1bac72
commit 83779e0d7a
90 changed files with 6361 additions and 7215 deletions

View File

@@ -0,0 +1,310 @@
# Les 18 — Advanced AI Toolbox
## Docenttekst (Klas A — 3 uur, fysiek, demo-driven, laatste les)
**Les:** 18 van 18
**Onderwerp:** Voice + Vision + Image gen + Local LLMs + Edge AI + wrap-up
**Duur:** 180 minuten
**Demo-app:** Vijf mini-demo's, niet één
---
## VÓÓR DE LES (60 min)
1. Alle vijf demo's lokaal werkend op laptop
2. OpenAI account (Whisper + Vision werkend)
3. Fal.ai account (image gen)
4. Ollama geïnstalleerd + llama4:8b gedownload (vooraf — duurt 10 min)
5. Vercel AI Gateway demo-project klaar
6. Browser tabs: ollama.com, fal.ai, vercel.com/docs/ai-gateway
7. Mentaal voorbereid: laatste les, ruimte voor reflectie aan eind
---
## HET SCRIPT
### BLOK 1 — Welkom + Recap (10 min)
`[SLIDE 1]`
**Vertel:** "Welkom bij de allerlaatste les. Les 18. Vandaag: alle leuke modaliteiten die we nog niet hebben gezien. Voice, vision, image gen, local LLMs, edge AI."
`[SLIDE 2]`
**Vertel:** "Even kort terug. 18 lessen. Foundations 1-10, AI SDK 11, Tool Calling 12, Cursor+Vercel 13, Agents 14, RAG 15, MCP 16, Production Polish 17, vandaag.
Vandaag is anders dan voorgaande lessen. Geen één onderwerp dat we diep in gaan. Vijf modaliteiten, korte demo's, je weet wat mogelijk is. Voor je eindopdracht: kies wat past."
`[SLIDE 3]`
**Vertel:** "180 minuten. 25 min theorie en landschap. Vijf demo's van 20-25 min. Pauze rond minuut 80. Aan eind 25 min voor reflectie + vragen."
---
### BLOK 2 — Theorie + landschap (25 min)
`[SLIDE 4]`
**Vertel:** "Het landschap eind 2026. Verschillende providers, verschillende sterke punten.
OpenAI heeft GPT-5 voor algemeen, o3 voor reasoning, GPT-realtime voor voice. Anthropic Claude Opus 4.6 en Sonnet 4.6 — best voor code en lange context. Google Gemini 2.5 met 2 miljoen context window. Meta Llama 4 — open-source, je kunt 'm lokaal draaien.
Modaliteiten — text, vision, voice, image, video. En modi — cloud API, edge, lokaal.
Vandaag zien we alle drie de modi."
`[SLIDE 5]`
**Vertel:** "Waarom multimodal? Tekst is niet altijd genoeg. Mobile? Voice is sneller dan typen. Foto-upload? Vision. Marketing-assets? Image gen.
Denk per feature: welke modaliteit past het best? Niet alles is chat."
---
### BLOK 3 — DEMO 1: Voice (25 min)
`[SLIDE 6]` `[SCHERM: editor + browser]`
**Vertel:** "Whisper voor transcriptie. Drie regels code."
`*[Maak app/voice/page.tsx + api/transcribe/route.ts]*`
```typescript
const result = await transcribe({
model: openai.transcription("whisper-1"),
audio: audioBlob,
});
```
`*[Demo: klik record, spreek, zie tekst verschijnen]*`
**Vertel:** "Werkt. Cost: $0.006 per minuut. Voor productie: ElevenLabs voor TTS-stemmen die natuurlijker klinken.
Volledige voice-loop? Drie calls. Whisper + LLM + TTS. Latency ongeveer 2-4 seconden. Voor 'echte' voice met interruptions: Realtime API. Complexer, magisch."
💬 *Vraag: 'Hoe duur is dit op schaal?'*
**Antwoord:** "Voor 1000 vragen van 30 seconden: Whisper $3, LLM $1, TTS $5. Negen dollar voor duizend voice-interacties. Schaalbaar."
---
### BLOK 4 — DEMO 2: Vision (20 min)
`[SLIDE 7]`
`*[Maak app/vision/page.tsx + api/analyze/route.ts]*`
```typescript
const result = await generateText({
model: openai("gpt-4o"),
messages: [{
role: "user",
content: [
{ type: "text", text: "Beschrijf wat er op deze foto staat." },
{ type: "image", image: buffer },
],
}],
});
```
`*[Demo: upload foto van eten, krijg beschrijving]*`
**Vertel:** "Bouw varianten. Defect detection in fabrieken. OCR voor formulieren. Receipt scanning — bonnetjes naar gestructureerde data. Code-from-screenshot.
Cost: vision is 2-5× duurder dan text. Een 1024×1024 image = ~1100 tokens. Voor productie: resize client-side."
---
### BLOK 5 — Pauze (15 min)
`[SLIDE 8]`
---
### BLOK 6 — DEMO 3: Image generation (25 min)
`[SLIDE 9]`
**Vertel:** "Image generation. Flux is het beste open-source model. Via Fal.ai of Replicate."
`*[Fal account, key]*`
```bash
pnpm add @ai-sdk/fal
```
`*[app/generate/page.tsx]*`
```typescript
const { image } = await generateImage({
model: fal.image("fal-ai/flux/schnell"),
prompt: "Retro festival-poster voor Polderfest 2027, oranje + cream, vintage typografie",
size: "1024x1024",
});
```
`*[Demo: type prompt, klik generate, zie image]*`
**Vertel:** "Drie cent per image op schnell variant. Voor productie: save in Supabase Storage, gebruik CDN.
Goede prompts zijn specifiek. Stijl, palette, compositie, wat NIET te tonen. 'Een mooie afbeelding' werkt niet. 'Retro festival-poster, 70s rockconcert-flyer stijl, oranje en cream, decoratieve geometrische randen' wel."
---
### BLOK 7 — DEMO 4: Local LLMs (25 min)
`[SLIDE 10]`
**Vertel:** "Tijd om lokaal te draaien. Ollama."
`*[Terminal]*`
```bash
brew install ollama # of via download
ollama pull llama4:8b
ollama run llama4:8b
```
`*[Interactieve chat in terminal — werkt offline]*`
**Vertel:** "Dat draait lokaal op mijn M-series Mac. Geen internet, geen API-cost.
Via AI SDK:"
```bash
pnpm add ollama-ai-provider
```
```typescript
import { createOllama } from "ollama-ai-provider";
const ollama = createOllama();
const result = await generateText({
model: ollama("llama4:8b"),
prompt: "...",
});
```
`*[Demo: chat-app met dropdown 'Cloud (gpt-4o-mini)' vs 'Local (llama4)'. Vergelijk antwoorden]*`
**Vertel:** "Lokaal model is iets minder slim dan GPT-5 voor complex. Voor 70% van vragen prima. Voor privacy-critical apps — healthcare, legal — is dit dé oplossing. Voor cost — als je veel calls maakt, lokaal is gratis na hardware-investering."
---
### BLOK 8 — DEMO 5: Edge AI + Gateway (20 min)
`[SLIDE 11]`
**Vertel:** "Twee edge-opties.
Vercel AI Gateway. Eén endpoint, alle providers. Automatic fallback bij OpenAI-down."
```typescript
import { vercel } from "@ai-sdk/vercel";
const result = await generateText({
model: vercel.fallback([
"openai/gpt-4o",
"anthropic/claude-sonnet-4.5",
]),
prompt: "...",
});
```
**Vertel:** "Als OpenAI down: switch automatisch naar Claude. Geen downtime voor je users.
Cloudflare Workers AI. LLMs op Cloudflare's edge. 250+ datacenters wereldwijd. Latency overal laag."
```typescript
import { workersai } from "@ai-sdk/cloudflare";
const result = await generateText({
model: workersai("@cf/meta/llama-3.1-8b-instruct"),
prompt: "...",
});
```
**Vertel:** "Gratis tier 10k requests per dag. Voor MVP en lichte apps: gratis productie."
---
### BLOK 9 — Resources + community (10 min)
`[SLIDE 12]`
**Vertel:** "Hoe blijf je leren? Communities. r/LocalLLaMA op Reddit voor open-source updates. AI Engineer Foundation op Discord voor productie-focus. Latent Space — podcast en Discord.
Blogs. Simon Willison's blog is de absolute referentie. Daily updates. The Rundown AI newsletter. Twitter accounts: Karpathy, swyx, sama.
Voor diepere kennis. Karpathy's Zero to Hero op YouTube — bouw neural network from scratch. Gratis, briljant.
Conferenties. AI Engineer Summit in SF jaarlijks. NeurIPS voor research. In Nederland: Devbase, MAINSTAGE."
---
### BLOK 10 — Eindopdracht + Afsluiting (15 min)
`[SLIDE 13]`
**Vertel:** "Tijd voor eindopdracht. Wat we hebben:
Volledige toolkit. Next.js, TypeScript, Tailwind. Supabase. AI SDK. Tool calling. Agents. RAG. MCP. Production polish. Multimodal.
Voor je eindopdracht — moet bevatten Next.js stack, Supabase met auth en RLS, AI SDK met Tool Calling, externe API, deployed op Vercel.
Werk er gestaag aan. 8 tot 10 uur per week voor 4 tot 6 weken. Niet één laat weekend.
Pitch volgt na inleveren. Acht tot twaalf minuten demonstratie.
Eindopdracht-voorstel deze week via Teams indienen."
`[SLIDE 14]`
**Vertel:** "Een laatste woord.
18 lessen. 54 uur klassikaal samen. Plus jullie thuiswerk. Een vol vak.
Wat jullie nu kunnen. Een AI-app van scratch naar productie. Tool calling en agents. RAG en MCP. Production-niveau observability en evals. Multimodal features. Lokale modellen draaien.
Drie jaar geleden was dit een specialist-skill. Nu — jullie kunnen dit.
Voor je eindopdracht en daarna. Doe het ding waarvan je dacht: dat kan ik niet. Bouw iets dat je écht zelf zou gebruiken. Vraag hulp tijdig. En als je iets bouwt waar je trots op bent — laat het me weten. Echt waar.
Bedankt voor jullie inzet. Veel succes."
`[SLIDE 15]`
**Vertel:** "Open ruimte. Vragen over de stof. Vragen over je eindopdracht. Vragen over je carrière in AI development. Feedback op de leerlijn."
`*[Vragenronde — laat dit echt open. 10-15 min over]*`
---
## NA DE LES — Wrap-up
- Brightspace: eindopdracht-instructies + voorstel-template
- Teams: nieuwe channel "Eindopdracht support"
- Office hours-schema voor 4-6 weken
- Bewaar deze 18 lessen als referentie voor Klas B / volgende cyclus
- Voor jezelf: noteer wat werkte en wat minder — voor verbetering
---
## Veelvoorkomende fouten in demo's
| Fout | Oplossing |
|------|-----------|
| MediaRecorder API niet beschikbaar | Test in Chrome eerst, Safari heeft soms anders |
| Fal API key issue | Sign up + generate key, gratis tier |
| Ollama "connection refused" | `ollama serve` runnen, check `:11434` poort |
| Vision image te groot | Resize naar <4MB, of compress JPEG |
| Vercel AI Gateway niet beschikbaar | Vercel Pro account nodig in 2026 |
---
## Mentale model voor afsluiting
Voor reflectie aan eind van de les:
> 18 lessen leiden niet tot "AI-meester". Wel tot iemand die weet welke vragen te stellen, welke tools te kiezen, en hoe te beginnen. Verdere groei = blijven bouwen, vragen stellen, community + nieuwe modellen volgen.
Hou de toon praktisch, niet sentimenteel. Studenten waarderen oprechte erkenning + duidelijke volgende stap.

View File

@@ -0,0 +1,130 @@
# Les 18 — Huiswerk
## Eén modaliteit naar productie + reflectie
**Vak:** AI-Assisted Development
**Deadline:** Geen — dit is de laatste les van de leerlijn
**Inleveren:** Brightspace + Teams
---
## Doel
Twee dingen:
1. **Praktisch** — neem de demo uit de lesopdracht en breng 'm naar een werkende, gedeployde mini-app
2. **Reflectief** — korte schriftelijke reflectie op de 18 lessen
---
## Onderdeel A — Demo afmaken + deployen (verplicht)
Pak je voice / vision / image / local demo uit de les en breng 'm verder.
### Eisen
- [ ] Demo draait lokaal zonder errors
- [ ] Gedeployed op Vercel (alle vereiste env vars geconfigureerd)
- [ ] Productie URL werkt en is publiek
- [ ] README.md met beschrijving + installatie-instructies
- [ ] Mooie UI — niet ruw, geen unstyled HTML
### Tips per modaliteit
**Voice:** denk aan UX — toon dat AI 'luistert' (rec-indicator), error states (mic permission), korte audio-clips opslaan (Supabase Storage).
**Vision:** image preview vóór analysis, loading state, resize images client-side voor cost.
**Image gen:** save generated images (Supabase Storage), gallery van eerdere generaties, regenerate-button.
**Local LLM:** alleen lokaal zinvol — voor productie ofwel local-only, ofwel Vercel deploy met fallback naar OpenAI.
---
## Onderdeel B — Reflectie (verplicht)
Schrijf in `REFLECTIE.md` (in repo-root) — max 500 woorden.
### Vragen
1. **Wat was de meest verrassende les?** Welke stof gaf je een "aha"-moment?
2. **Wat ga je in je eindopdracht gebruiken?** Welke 2-3 concepten zijn voor jou meest direct toepasbaar?
3. **Wat zou je willen leren dat we niet hebben gedaan?** Welke richting wil je verder verkennen?
4. **Wat is de grootste verandering in je werk als AI Developer?** Wat doe je nu anders dan vóór deze leerlijn?
### Vorm
- Persoonlijk, eerlijk
- Geen ChatGPT — eigen woorden
- 300-500 woorden
---
## Onderdeel C — Eindopdracht voorstel (verplicht)
Lever via Teams je voorstel in voor de eindopdracht. Max 250 woorden:
1. Welk probleem lost jouw applicatie op?
2. Wat doet de AI concreet met de data?
3. Welke externe API ga je gebruiken?
4. Vier kernfunctionaliteiten (inclusief registreren+inloggen en min 1 AI-feature)
Na goedkeuring kun je aan de slag met deelopdracht 1 van de eindopdracht.
---
## Inleveren
1. **GitHub repo URL** (lesopdracht-demo + Vercel deploy) — Brightspace
2. **`REFLECTIE.md`** in repo-root
3. **Eindopdracht-voorstel** via Teams (separate channel)
---
## Beoordeling
| Criterium | Punten |
|-----------|--------|
| A — Demo deployed + werkt op productie | 5 |
| B — Reflectie persoonlijk + concreet | 3 |
| C — Eindopdracht-voorstel ingeleverd | 2 |
| **Totaal** | **10** |
Voldoende = 6+.
---
## Tijd-indicatie
| Onderdeel | Tijd |
|-----------|------|
| A — Demo + deploy | 60 min |
| B — Reflectie | 30 min |
| C — Eindopdracht voorstel | 30 min |
| **Totaal** | **~2 uur** |
---
## Wat nu?
Dit was de laatste les. Wat je hierna doet:
- **Eindopdracht** — thuis, 4-6 weken, 8-10 uur/week
- **Eindopdracht-pitch** — na inleveren, in 8-12 min demonstratie
- **Beoordeling + feedback** — binnen 4 weken na inleveren
- **Daarna** — je bent klaar voor de praktijk
---
## Tips voor je eindopdracht
- **Start klein, voeg toe** — eerst basisflow werkend, dan polish
- **Push klein en vaak** — geen 1 grote commit aan eind
- **Vraag hulp tijdig** — Slack, mail, office hours
- **Test op productie** — niet alleen lokaal
- **Schrijf je verantwoordingsdoc gaandeweg** — niet aan eind
---
## Bedankt
Bedankt voor jullie inzet de afgelopen 18 lessen. Veel succes met de eindopdracht. Tot bij de pitch!

View File

@@ -0,0 +1,137 @@
%PDF-1.4
%<25><><EFBFBD><EFBFBD> ReportLab Generated PDF document (opensource)
1 0 obj
<<
/F1 2 0 R /F2 3 0 R /F3 5 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
>>
endobj
4 0 obj
<<
/Contents 12 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 11 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
5 0 obj
<<
/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font
>>
endobj
6 0 obj
<<
/Contents 13 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 11 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
7 0 obj
<<
/Contents 14 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 11 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
8 0 obj
<<
/Contents 15 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 11 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
9 0 obj
<<
/PageMode /UseNone /Pages 11 0 R /Type /Catalog
>>
endobj
10 0 obj
<<
/Author (NOVI Hogeschool Utrecht) /CreationDate (D:20260607101945+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260607101945+00'00') /Producer (ReportLab PDF Library - \(opensource\))
/Subject (\(unspecified\)) /Title (Huiswerk) /Trapped /False
>>
endobj
11 0 obj
<<
/Count 4 /Kids [ 4 0 R 6 0 R 7 0 R 8 0 R ] /Type /Pages
>>
endobj
12 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1941
>>
stream
GauHL>>s99'Roe[3,]73E$b<,mi*9\b7*3)RPHt2f`tq96,]*hG/DU5^IP_B3sc3"FB`X>+s!6C45[TY'F908l?AHo]H>#&%hpFEi>AM/].gmNo:bipZC0#?f)sdd`!I=6Tk"(QSlckT^?p\,UqfQ0o=tNr;k*B?"2f1G5Qf@G`c!F9G2SR7]3o^Fq:sjen(9g6GdV=6^)sMMDuAY8^!QHln'K>U)i`edg43q?3j)2U(O.W-@IVm8Rg]"U:Lk'nj`ppb9.cpN&k7f`E-_OtEZpWo-M7rZmpZ"B>gP:lqUG3@'!*#M1fqYKeC3-oQ'eLD!:<qaMA.[;poO#/GGlO8SqGP]jO`tm>\^g#=-H6!_)J(4Li5KAOmR4FFmja7XS*+"N_MWsR^:h=D`[t/Ts[@(=RTuXCKD^\pMjY"QaJ?>ZUVOL,)`*5$3"i92h0%$9$Y#S0WI%mS2lCrTe`-1,95D3FkE+7-Co\]Z*V4mS9V]acGF0pHWdsiYd\OSm-P/EXHDqV=(=2/4PQWqdgha\1QR9X)U"'p?a]C\ID@plP8bo$eg*a]]9pr%E0p_.\k#@'<nb6O"]/MUGH'V&m8kTb<[GT8BfECd7Z@Xdp3bqG1kRWG7A<DR9#u\40[k;0O%L_W%n&*HUj;oTU>QK3W@O>ar=*UuHNA%Dr3/`bnWFRXO&Wj"[[8Ym,JU$<K!=P/eImH81LLWrNKL4b%W95WcP<AT?A5=*_ns-rl)O."Mj)Va:dlhnAMHeg<c5S\6+dK@7@'Th/[IU%]ElT*XX87'0b]ZUp/$XRfXU23g5Tsj]n"ao@q)Nn]\@sjGe%7hX^$@o`J5ps32BZa_`E,uHf<]pK:'qWK0NE,@<s'3)HOmgO,feU3Z84,-'Zbm>T+NN8J*="PFE=O8t"5/).NlM!b)Im&1*cu";>u&Z>)s'SZ)F%_t$um44>M`HUVV3-S4ii[]QZoEB._lll7!G3;-R=<o'=s)EQ&Qo"7Vua&d.@8>a90.?WG&Z7?UB9&l'u\@jC%#:DKiO/J2-g07j)8McG^k@G:?dUHB0*/c^S=:$[F1,W6nAX^LVeQD(=aChanAq34AhP/@Dn0Y81]ofI3-8LM@0hc+/B1`(>\(,qhW_At>fk`/aAF3H[`qCPB.F-^].D&o0Yjp249ll;NMNhj5/gY,n<-DOXnsSGJ,D.fg1>BQ'+pBqZGlH8.FAT0a%BiLB0,Qkn9:<S0%I2.C5`AWmOi=V'LXhWg!EPXlU/28nUZ[il_$R^Aj$?7`-)PW7@?E&k?120p$C,9!6D\.#(6UcmagtGQ,e>\&oQFefIZbEuC!GK1hsF$X/%9fcp:5K!c5T3,;:<)IJ0M=iJ6nQq,8p\hS]&p]Sk-K*%Z9%+n8GEO//nXXPN5YnU&(/rR=WjKf@uc+LI3rt)MBcnVEt!^WHQqh1!=L2N\GrP"-FkW-r6;"!9L`+#-J?8NDZ:,eRYhfO2j>F#7DaUIJCFd%`X"KNqqS90<6^%2$b10B(ObFals:kh;%r.6[[jODBN8XT:UHp\'l0l=i.;BWr2es#72r=.P"+"[Qk$lb\LLHYtNa92hHK5k0J<j6@.$Q":B_Gq1EDL(u..2Hq[B,T8&skpMXZ.ZX6a?LMDPN_`H4/G!99kWQTVu*'F"c(:6*3a"j-9_!1doh_ePJNX[HRV>3?Q2p'hh!+!=TR\45F&rBU6b$:!fNp@ShW1O],ed/1La?W?t@="Hs:eqg8*io2a8e?#);<>,.0pdn)hrZ_7.gcU?cmnb-dEs#;N]L\i'@4uk2cUCFA>3t`1G'D85/:Xgpl>0t+>T'AimZ:0p&JM*n/CmQ:.U*8.I2Ti+@:Hf3[n$EqE6SIj[H]bR';IJ(VVH%(R<U9bm1-I'Ojpf,.0*8G)_te;3$KA._^A5T$q4-EO86@*#uG=drK0ljE=jqH+"1oXT&:2f+i0~>endstream
endobj
13 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1734
>>
stream
GauHL9m>;P&DcMNbit"6i*,\!h=NOPBq'0WS+b6O_a`:2;6VW%S#"<4Ij<\ZP5kV)9`j8VbG2Ul7X&Ig^ntI>rJ`R_dfI,\^jpDAN.JO*$\LX5%8MAC=PW(jl[e/N[aX8Eoc3Q*3]Y<uo.@_tHON5&Yen0Z2e5r+p-R\Z?P'FA1rB%,#m2^Im3$Me$CU(4re((gbNGD(FtM&7HhZuIr!SL24j.cV,<+p))5@qJ!O]<"\<bCP^k4oWSM+>'cf('[Y_XVuV=:80(emU>EZW$.F8-!D0i^$ZcXCbrK,8-PQ;Q7HQ64bqWmOU`=ko[AelFCk,'*dtA`4S*\1ln5`ijo6/+dYEW2Ih@:$<T9]<(#lo`l\Z2NTNI]Q=4G^5>;crVkt=eNR!gW`:&NBo89FWhtB6grfP`J6&gY/#?[O<BQ=7M4hjIR*n%$e5^ejk>h,u-0g:M@[s:[%qJeZQu%dN1kf1j,rM6s1X+aI>:Vtj6Y3^f\@uUKY$KrjmW-.pCg3UT2_7u,<%5X6^,fWm(j1c?i3YuP*QKU0J;9\2e?`+P`.uY\GMDk:bH$o1h0+>$\cD[J9shPOK?U$GOL'bipTkD/ZhK+D1FnA*6uU_O4#G5^="?^Weeg14)a)l0)iA$^q$rqlI"bI!;gA?65,I\1;R[9iYbLbfMZiX2HS1AJf8B1''>(h\mr4'/W(Wm8Mu,l7HhWEqSX[%33q@)C/[!p"f>ULSUUg*.r=gR5Lk<nTSHg]<TL@5&.$0T$;CpZgLuV9)V[,_j`fQ!2>ToJ%q+OK#ZYlWqbfdI7]aa5E*.ng:"!LuiQiJ!n&n,`V^)D)KO4>f4_T6GC,<_AlbW#(/]+.4YO)S,4/%>(!YJo`ubo;7E8]k9j[AHn_'?,dgB,b\_E"(Hj*GBNFdC*7#h%E1$lpd.5P>%aK^Tkm6KNs\m:2i%aY7ceJfLVYn+sup"Qnd0pNTOsH6*05Win(NTNl$sk$VQ9<QE1R#@RRFK.t#2>]7F37>ZGc4>-*tU20=t_=J<8MT0m1B/.9V0U#S5sI94d)]JZT)F\2u9OMPk)[+5]^n[)?]luAkE3Bp2OSa[K$+0m$AkbQ*IO0OeSp0,IZX`:dar.6,gW#q.'doT?NA%EtU\c$W+me5?Bd75G[mjFk-GVQ/_3D(d"Me]s8g_4-Y#b#8Xh,\$ka-g@r'tSd6:ZqqtEl%9l_Y5qfBC0GF.M$5e\o3)^Gi"=5HP+jWaWeEa!X:'YU=/Ob+0ql#nS247i%qclgfq)_k&I*Q!GhM@LJ[ck;mg.[47,J0WInfM)3J<>1rW;;5i'<eLma*mW!8dF[PI%f"lV'sQ?sak\oa2a&#dR;IRtd_S(1kCcQK[Tj%$qp9/FKsO!&$/mh0b<#VXJmL@S[F\=qH"iZ;]-CmU;[pQU2fk;geY.Mo'8;4;CS9R'Dq0-lQlcq@nHC=<:!P^08A;\nS^OT[?FH?Y=BS82@S7iR_YHd:TUoL2$>0V7::C:0r1Wn3t7e7;gHMPS?Xdr%&6al'bE]V8Deb9qM%jZ&sKYl\n!>;#]c(#V2MNSs4[`A/fSD7bR6*_0%MO\Bt)A,Z2pnPPUEk+h5+!rViM?h<W87bL35].mn)?EQI$>!.j*eM<PW`Cr\pf+hs8L#n@.iiFA8l][_0F-s]o6p$Bu#K=@3l#[^^`WN;dIE8Wkd%mcIc!8XacmY=MUHOQk<_.kb`\ZH2eb,\ZFf/?L5q*,j4V-N456hj0PQ~>endstream
endobj
14 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1489
>>
stream
Gb!l_;01_T&BE]('`48(K$KNj=met]F(qejlZ@GE6dqBK;/?77@o:p4kt=6f=#LU42MA]3b[_Ut9ZHDSeUMdmP!^D.(TfpN)8P>6hYp3"1)LFNEl)8S)n[9brpC@F=Ni!4Ta\ek!D,WX")cU=+;.'f/>42=R)kWsQ]t%/876[L*7GapL>eUC"VQ_9Q5k^VOc\SEr?2c3@;&C[+d2g$=8>F[?:)R[4%uu+UGK9MX$pm<L7AB'MB`t^i9^26G9>dr*U,=">FWO&Q.,!-K$TCpnEqN+km*d6\TZlH?iks>5/<I_/gq<O&FV[YYV>0:]4p<e>)c4L+PcO,K+^Ai98!>.;8?'*@g_7E/9mDechKt,s'")$U<S?:?Ki$p^K3A5m&48D^VDM_#Q<5M`(C8WX0GYR\!.YQNS!Ipj@lD@=j5Sm_RL]pkK1aCH;RYQfJ3qObB6Vg8U!j"CDZH.8m^bMKhTg6qiB%\*.3_rY]H0nRk<(2kT_OYgdUUB!i!3=IbBDO6P"8*]`Rn7Bl0!%aV;Xi[Rct?eWF5iB44jqCo1C]-J3ZDA9OP)L#(X9Yh#))cG:2>S`:!clq"@G1Y*9lW@ncuV,Vo^<F\XR/MfUC^Z+^X8#VY'`3E^+NN`VC'OIhIH6e+(]'j9mc%^`0E/PrbZSQcVXiHGjk`et0G3oK)Z*f.T95*1oFMXsGC"Kk2HFS^kfq$ADX,BZh%j90>%Kfn070uJYi"U@2'1HqOE"E[Efnh%;S?SHJXN>?f-<VDuDc4!t]Jo56ieXOG`VuJSl4XrgAQg0kosMK:_`%\Iq$L!FA!%%)Ar77QoM"fqFPVou=QARibW-[*AFTN?@'OZ("<WZn$%b/$S9@X2M/^$e%<2#!#%*:@^JkBUilC)@$B[X/5u0i,Q)S/j9'fHsQ)_(8OR&'[O$:rF2VR,0$)q'UJ"0&'ZE6go#A7sU+7GQ[iAAOK7^;?gI&:o=R:Ji[6uhDB`-U^QEV&C;91h1uSr#MLj-%]VWN!m^d_#S:(O-^`lJ_kU*t>H(h_)=.G%N)+D-h,UlW@N!r;PJNl*t"%C_ft!LD2Cl.D,l/,69lBArEs8#<+Sh.LBEVMfl*7[CF_B`(]?=*^dXP[Ee2?3c3L/h@tYrctV;K47X@(i<;+6+pkE:68#Vu)6n1bM[H<e'N]2pSh>)^VA1/?T4HH.@pF=-B\B@0drj`?QVE4%[,.GEK<1h13#Qf-n]/ai<nDJq'+9.k.Ha?Z@U,Q]H3BM`m<."T*)sbf.SVqImBV0@_o)-NA?(0jbtU2^d,M5/q:COFgdhH*\dqDUbkO`hAI9YpEVE/oCkd;:64J<0]l*,m3Mf2!?K?1Cg!.a>?%CZLq0)iqAb$55_kBZljBre,I,1;7kd':l$;\,nUTr[$m^jUNe(pedX!@LY3d,*joK^?#8eon)Dg&Y@Q'k?TKRKEUV=&#M7Z+A"mj('8S9Z<iTDFbm?Oon"7Q^2p2=bIm:O)+%#7`iOMZ~>endstream
endobj
15 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 422
>>
stream
Gar>B9iJn_&A@g>4E&;L:!tjiO#$"9N2&.,3I.T8JO4(U/NDdo2l5_g-9JbKpO0/sn5g[gC^Z17gcS+uKk(4`8L&N!I1go1Ul;T?!ejC&Yl_mQG>KrF&s4=NX'!4F9?q9c-crpO62<NVV+&X2$j-gA7TcQS:.E6kJ+Fg$V*)/_aI)bkr(3OHjMbY)lFb?T4IGT!-W@dYTdCp-)^3mDnFSi4?sG:oa];DAi3f[I>IA]Sb2RSr4$IU\mC9l"Z:6V9#gP]CO^tPtQ_<AehL'@Wkttd8AR;7\B*_EXi=CT=-XAVej<C:GoZ)X4f;/iUh,,0D,5S]h[Ke%KVraepTrMk/pI13#o(%]CbSfYSGr/Ab3Y^@+>S:Ym"e'k*EJ&,ca*B\KG4Rcf#nW9"`I#>e5'D4@Xfa8b9EPFTBI/LP~>endstream
endobj
xref
0 16
0000000000 65535 f
0000000061 00000 n
0000000112 00000 n
0000000219 00000 n
0000000331 00000 n
0000000536 00000 n
0000000641 00000 n
0000000846 00000 n
0000001051 00000 n
0000001256 00000 n
0000001325 00000 n
0000001611 00000 n
0000001689 00000 n
0000003722 00000 n
0000005548 00000 n
0000007129 00000 n
trailer
<<
/ID
[<9af241f85a8fc004d31ff2b8bf21662e><9af241f85a8fc004d31ff2b8bf21662e>]
% ReportLab generated PDF document -- digest (opensource)
/Info 10 0 R
/Root 9 0 R
/Size 16
>>
startxref
7642
%%EOF

View File

@@ -0,0 +1,282 @@
# Les 18 — Lesopdracht
## Kies één modaliteit, bouw een mini-demo
**Duur:** 30 min in-class
---
## Doel
Pak één van de modaliteiten uit deze les en bouw daar een minimale werkende demo van. Voice, vision, image gen, of local LLM — jouw keuze.
---
## Opties
### Optie 1 — Voice transcriptie
Bouw een mic-button + transcribe. Werkende voice → text in een Next.js page.
```typescript
// app/voice/page.tsx
"use client";
import { useState, useRef } from "react";
export default function VoicePage() {
const [text, setText] = useState("");
const [recording, setRecording] = useState(false);
const recorder = useRef<MediaRecorder | null>(null);
const chunks = useRef<Blob[]>([]);
async function start() {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
recorder.current = new MediaRecorder(stream);
recorder.current.ondataavailable = (e) => chunks.current.push(e.data);
recorder.current.onstop = async () => {
const blob = new Blob(chunks.current, { type: "audio/webm" });
chunks.current = [];
const fd = new FormData();
fd.append("audio", blob);
const res = await fetch("/api/transcribe", { method: "POST", body: fd });
const { text } = await res.json();
setText(text);
};
recorder.current.start();
setRecording(true);
}
function stop() {
recorder.current?.stop();
setRecording(false);
}
return (
<main className="p-8">
<button onClick={recording ? stop : start} className="bg-blue-600 text-white px-6 py-3 rounded-full">
{recording ? "Stop" : "Start opnemen"}
</button>
<p className="mt-6">{text}</p>
</main>
);
}
```
`app/api/transcribe/route.ts`:
```typescript
import { experimental_transcribe as transcribe } from "ai";
import { openai } from "@ai-sdk/openai";
export async function POST(req: Request) {
const fd = await req.formData();
const audio = fd.get("audio") as Blob;
const result = await transcribe({
model: openai.transcription("whisper-1"),
audio: new Uint8Array(await audio.arrayBuffer()),
});
return Response.json({ text: result.text });
}
```
---
### Optie 2 — Vision foto-analyse
Bouw een upload-form + analyse. User upload foto → AI beschrijft.
```typescript
// app/vision/page.tsx
"use client";
import { useState } from "react";
export default function VisionPage() {
const [desc, setDesc] = useState("");
const [loading, setLoading] = useState(false);
async function analyze(e: React.ChangeEvent<HTMLInputElement>) {
const file = e.target.files?.[0];
if (!file) return;
setLoading(true);
const fd = new FormData();
fd.append("image", file);
const res = await fetch("/api/analyze", { method: "POST", body: fd });
const { description } = await res.json();
setDesc(description);
setLoading(false);
}
return (
<main className="p-8">
<input type="file" accept="image/*" onChange={analyze} />
{loading && <p>Bezig...</p>}
<p className="mt-6">{desc}</p>
</main>
);
}
```
`app/api/analyze/route.ts`:
```typescript
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
export async function POST(req: Request) {
const fd = await req.formData();
const image = fd.get("image") as File;
const buffer = await image.arrayBuffer();
const result = await generateText({
model: openai("gpt-4o"),
messages: [{
role: "user",
content: [
{ type: "text", text: "Beschrijf wat er op deze foto staat in 3 zinnen." },
{ type: "image", image: new Uint8Array(buffer) },
],
}],
});
return Response.json({ description: result.text });
}
```
---
### Optie 3 — Image generation
Bouw een prompt-input → generate → preview.
```typescript
// app/generate/page.tsx
"use client";
import { useState } from "react";
export default function GeneratePage() {
const [prompt, setPrompt] = useState("");
const [imageUrl, setImageUrl] = useState("");
const [loading, setLoading] = useState(false);
async function generate() {
setLoading(true);
const res = await fetch("/api/generate", {
method: "POST",
body: JSON.stringify({ prompt }),
});
const { image } = await res.json();
setImageUrl(`data:image/png;base64,${image}`);
setLoading(false);
}
return (
<main className="p-8 max-w-2xl mx-auto">
<textarea value={prompt} onChange={(e) => setPrompt(e.target.value)}
placeholder="Beschrijf je afbeelding..." rows={3}
className="w-full border p-2 rounded" />
<button onClick={generate} disabled={loading}
className="mt-3 bg-blue-600 text-white px-4 py-2 rounded">
{loading ? "..." : "Generate"}
</button>
{imageUrl && <img src={imageUrl} alt="" className="mt-6 rounded" />}
</main>
);
}
```
`app/api/generate/route.ts`:
```typescript
import { experimental_generateImage as generateImage } from "ai";
import { fal } from "@ai-sdk/fal";
export async function POST(req: Request) {
const { prompt } = await req.json();
const result = await generateImage({
model: fal.image("fal-ai/flux/schnell"),
prompt,
size: "1024x1024",
});
return Response.json({ image: result.image.base64 });
}
```
`pnpm add @ai-sdk/fal` — gratis tier op fal.ai.
---
### Optie 4 — Local LLM met Ollama
Run llama lokaal, chat ermee via je app.
```bash
brew install ollama
ollama pull llama4:8b
ollama serve
```
```bash
pnpm add ollama-ai-provider
```
```typescript
// app/local/page.tsx + /api/local-chat
import { createOllama } from "ollama-ai-provider";
import { streamText } from "ai";
const ollama = createOllama();
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: ollama("llama4:8b"),
messages,
});
return result.toUIMessageStreamResponse();
}
```
Frontend kan dezelfde `useChat` gebruiken als in Les 12.
---
## Eisen
Kies één optie. Aan het eind:
- [ ] Demo werkt lokaal
- [ ] Eén feature compleet (transcribe, analyze, generate, of chat)
- [ ] Geen errors in console
- [ ] Optioneel: gedeeld in Teams met screenshot
---
## Tijdsindeling (30 min)
| Stap | Tijd |
|------|------|
| 1 — Project setup | 5 min |
| 2 — Backend route | 10 min |
| 3 — Frontend page | 10 min |
| 4 — Test + debug | 5 min |
---
## Veelvoorkomende problemen
| Symptoom | Oplossing |
|----------|-----------|
| `MediaRecorder undefined` | Browser support — Chrome werkt, Safari iets anders |
| Fal API key | Sign up + create key — gratis |
| Ollama "connection refused" | `ollama serve` runnen + check `:11434` |
| Vision error | Image te groot? Resize to <4MB |
| Transcribe error op localhost | Gebruik buffer (Uint8Array) niet Blob direct |
---
## Klaar?
Dat was de laatste lesopdracht van de leerlijn. Voor je eindopdracht — wat je nu kunt:
- Multi-modal features toevoegen (voice/vision)
- Lokale modellen voor privacy
- Image generation voor branding
- Edge AI voor performance
Veel succes!

View File

@@ -0,0 +1,200 @@
%PDF-1.4
%<25><><EFBFBD><EFBFBD> ReportLab Generated PDF document (opensource)
1 0 obj
<<
/F1 2 0 R /F2 3 0 R /F3 4 0 R /F4 6 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
>>
endobj
4 0 obj
<<
/BaseFont /Symbol /Name /F3 /Subtype /Type1 /Type /Font
>>
endobj
5 0 obj
<<
/Contents 16 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 15 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
6 0 obj
<<
/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F4 /Subtype /Type1 /Type /Font
>>
endobj
7 0 obj
<<
/Contents 17 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 15 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
8 0 obj
<<
/Contents 18 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 15 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
9 0 obj
<<
/Contents 19 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 15 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
10 0 obj
<<
/Contents 20 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 15 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
11 0 obj
<<
/Contents 21 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 15 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
12 0 obj
<<
/Contents 22 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 15 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
13 0 obj
<<
/PageMode /UseNone /Pages 15 0 R /Type /Catalog
>>
endobj
14 0 obj
<<
/Author (NOVI Hogeschool Utrecht) /CreationDate (D:20260607101945+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260607101945+00'00') /Producer (ReportLab PDF Library - \(opensource\))
/Subject (\(unspecified\)) /Title (Lesopdracht) /Trapped /False
>>
endobj
15 0 obj
<<
/Count 7 /Kids [ 5 0 R 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R ] /Type /Pages
>>
endobj
16 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1151
>>
stream
Gatm9a]@1d']&(*h@_nHY'J(u+3+[8`'`<')`C#g1]k&fOrAB=om%j_92adS@Y^PRW`SrNq^+%jpgW''[K^ugJ2cp70Fj"6@$4&_SAHq;j=VT\F!DD2@B"2cL_J_Xl;^`6dR61b(g(O/2%l]r`*-f)j#pn>J_J$'l\#9nRMm]-=JiIUO7!CM3Ch\a.';=nrILRMoGl6\4/CQE&I^8D+=a)m8LRM,_.!iY+$Q9e^i.cG2q_22(%>:/qs:>:F3Y47T>3O<mu-J^f<8S%%?&iS_$!^e8sGEL%XRE0l?6=.&a30)b0(LO"QV@.EJpRe5YVVUN)KeQs($p!NVD"\1ocTeK`W'NE2pgt,i75^5-?bR8R*qr#ZZb9]AoJ0_Cu3,NA9$h7;$Tf9.%\J1,t1RF@;n!ZRhe@UVQ?7IeX5]=EV_cJr(et=tWW5a5RY9R95Efs/2CqKG;F$[<"^6V94hP\JqWEZa^[Ua$nK!GNaiP7qY&f4b+uU(pe#m-4*bt@<ZOW._PW3J+F_0kg1AQ=FB=n8blgk1F'C0K+oalDHjijCgX`:;P&(t2],XU(rVWbN]>;c#t4bU8*G#f=SS,bY*Nda[m"^@iM,$gJG6RAQhf%WYH)<*"^RKL?EdS0#-lR@Z-hJ^U##5s/kD(IjE[80g\Vgtg0[-OrHg_"YfMEb&DR;^)iP#7l-7O1dSj0VCFY"rV:]UEI=-'Wgba<*qW2lIXaSmjFb_0T9>eBdVLF)Pn'W=l81rub;-'Jeo>o,<*H*[]b##r,p;B_'7g%#1_;g<nT>0CJ6e=I:ef_Bd)[@;u^?blb+p-VL6X-a-N8>fmdhn)g3.U2A5NX(:1&*TXX'.P%%A%qmchQgnp[5T6LV-^&Gb1'tG33d?q?-:Io)C/PR5Fql>/J]`AcVQ(-?!nmX+pc>"KoA1YY<M,:53mpG^/`NNA'gB(_Zq+^fcq8rXeMh-EkS0Xc`dn?[5cLndqfo.&ngMi:QZ&I^WMeoGbR@lH'F098sTfl2B:C1`H:*2Ep!+3%:II,qP\bAA+&[hu0*!]Bn1t-_>OUl,!aDSX)uNkMbJ*AFti)J"$mca75cB?#86'.cI9ri5-0&H^j[)Nn_of@LdJ)Q/:c<i<3?a:Q+&\3Ve4*PNrEjYr[&L&';SgoQc#~>endstream
endobj
17 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1694
>>
stream
GasapD0)1+&H9tYf\r%K8RtBu8`Z%VY'8L.cLgtup.Lnd(s<C@"q#BW-C=a].r6oR1r6FE`j5;`G+W&`&HdnDf)/<E!3[b(I`$\UQn3ao,%0sOrgJ'r4i/LH+p8Nl)FPQ9'Y$B+Q<k&p,Vn?k"&0NX8q>9IBH:gFBVuPC:`3BJoGA,cWR:j(Atd`FOaTJ?k=@0q-tW%#qlPA%\JGjgoa]g5]FbQrY\d2WC*3dnTaG56K$jH+hql6-`R<8X<Dd;reQ+$-Z7tBF_=)=$:NLBYU,:C.R!a\291R>2'd4l3+OiU=4&ZsW]S\eDO7>!MA)KM3:T.`\,5KeIN\H%4.)&1gAW]B7]$HAa:*C,o,s9-=#ZdNnC85[UQ?beqB=M12;BQ8\<:s=7]-m-[[?0AtVdT\XXt`O:JE"&r+lf%dSHmsX6LDKt*[=;ChTp9K7X&*o7sE6hpndS)CVPd>GF<\W?;jJa[E84=NI[rV=\A=p.!dn38AlC4(ks;0TF3A`j"\J`3PI,o`/MlaBo&m0J"?2V?D/$`jGgjfXplXZC3&Y?2IM=)%Ka6#9ikn'=h"^Ds'fJRNni/tZ[`9_<5f4Bb/DCU-J"mK@H5Ne])k]'[10IQD4'Ku&[*tuMfkWD[)h3qP5l!A9LmPJE'M.]nZH>1UB*VXjL'[LCMrMOeAb8QiY'PJi2.aVT=u/""Zq^<>4#hZD-9j01g`/VTA+;]k_hF$bqK1["E,Wq8$KfgeHQNtE;GW`Vt&qnq-V4p=`utL\W9%6+MuQbPh`$)Y1EE0H:RHc.qTTa#^LDM%&c">nFHAX#>u/6AP3uoGBpL;Ci/4T*g5%r%'eWm<hS%D)LGu,6N?)8344jgfBG'!77C6P.I<:$3&OhWqmA=^UY^*7&+Ot;(+3Lj[=O+l3(6*B$=u``k(P)HWku@Ul#UnJHc_\92QdLa#7UJ[AP]%a,+ln6Nj%J"MeAGD[cn&J$I6r+l0EMYlhUs<,B!N80oLjLip!\';n+6H\4tD?1a2*[VJsD,@C-E@c.*5<CFbZ#Zfu/\q]kGep9$5"i&CHLhjE*>59N0Q$Af1DYYVcaN2.8V6QeE49'Ac?0BQK*h\=W+:T*MlfHGt/$!UaICb[<ib=t1@G1/kB+#S?Q@tf.BW(2;:VXCI5@;LO@pW%9ANboNoF],%lj#]\2`a0C$eF_dV0,$iZbjS;3<!VV:1r5Il52sRE@2$UocoQ^&ORI=S+/2J['9:6Wk-cX57h0XnSr:j(D?1f5$uj#VK6UAW>mhPPU,^J4,W_`McU[H/R1*OYNRJB0RC3qC"1sgD@ALm6%@0T54K<6QH`a\(&*^/HjS1-u`01PA+t_;@F93hKB$h8!*B"3Aa/Pf/3)CfRdm_Yt5U)*'6\[1)AaI"NZ(F1?*F[]@j_r!r?,^N<:K2=>^Q2IYbV,$bJ,VP95,ea[^gkHgi(n!$CGA+Dr^W8$5L/]/k6Q=_A.;9lF7GsM2-V-JZF)]YPW$#.n]b'K%@"?B'$@iLSWMLgL"<p""Mc(\rrm[r,fU4&L8)o:b2iPPr#rLp%'JLR:WJso#':I0Ze_m\B3=lF?.i`PTAR7O+&9l<!*Xot45bJkG<0oX)R/5RVVU3JCGf4_mAJ^q(Zs?4-77Jge'C2pKr1MFF)Z9B!_2"VJFMs<>/J?Dp@%bHEE0*B3rh+EbmCm7)kH?kn]:n%5I=G-B`~>endstream
endobj
18 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1842
>>
stream
Gau`R?#SIU'Rf_Z\FW)HFq$l<+/H1f>I-5$JZS6>[kDkY0J_agP9iYlS#/[Sq^s0tQJF/cJROIraJ-Uo5BuW[LB5@)X8b[_JC>r,B+,W4Bai31*_ao=flV<n,!=f#o7=E7\fh:k](QZW`@(gQ+@4(9#*K<;Ka42[7*8=-a*g9.!MtPGd>.f8!>X#b,+0Ooi2cf/#rkH(*q=k*gu%#c-`q_=$!QfEc/splp]bMdl;1cI;!&--VG!;hI.cn24O4hTMD"K_Kt!r/dA;C3k5q6fJ>`7-:@gf=%q5;HVN7g0*iS7#ik(f7"[1I;?Hc'm!PMNl,ZEXp5^pa)DGUDk]&jA/_\Efj59c&l0YeBFf-]$+73g#*iBFE"YT.pT2!ODYdZGoVh7-bUc6+7ZUS>LI2t'/0DN1>c@6fG`Wa#'$'+F98i%,/&QT>Yg'nk;tUa&?^PeG+Z$V%V6U(Q3BPN#="CV,'8c1Yd,\fB8.A3\>$4(fUfFj3f.gT'HX>"D!*G^s@ZO!P2"1J=$&?5cpurf!bG/9PQ(qPPcB<^gS?Tg;]*]=Wd,ba1"aanVcPQUYVA9+m,tgU2VZQ)UR3q*cS7ebh(u=WW6,Jl"aKa7"82@YP!"0oSt4_lC;i,)B<'A<K'-f<=%)B9Q5#*PropC8EF:HO/rLNJB,u5*QL-5a5oHJTq[FTWV:(YViUTS<qEsb,F!(p2D,i/2#QKE-1a2e7tT9L`"I[6CZY=>VD&`3BW#E1qsk-%79'";XtjT;HKpKFFJ*<F#FmAfb_T<aBCQRANn`1@=*f@K2gKnU9iI<8VK$D^sM&\G9_H`e*e763mL+6oJO9U1*`EUV7N'h)NX3>7+nEc<?1>QVK^C?lOr/s^9MZ>GTn-^h2^036`J>1bcj:2!n4*ia3"Us#,=DP_`"L4hV83ZF1')uK>Z&Kh(V7:@1C-@?[%8fKt8n-1J8YZ"K;3Ind<]4")k(,];2aO+6Ot8:mjpo._Xlc\%Qd*]Ks]@^].[_B+\uOZSm<%YHC1W$s#fcX8.>6&,6`?irn4>\nn"hDF*ZNF6;o-bd\b%:\G3!Wk,DS<[Jnu6\`L`j#;)!s%bJSn(,2+-Q:UW[Knp_EO#Q/=$$67MPZa/VPBPsKl3RIf$8b=1,q1J.qg_#*!]G?31oI;F:,G&`)N?6qNp.p$bW?N`iD^!PU=IH6R:Wbq""BFmICbTIq+_r&m2,51l0+<bbFS&G7tIQ3,1S/kF9#BPo[FVj#"H)H:ZfL28h@W?q#=qhjNNnk?-r/U!i'PrqM!GI1m=?hXU)[o>A01G9Cbc-/#"^4`>f<!lldmoJ!^$$$FotT%nj4VBG5(E]k)MTLE]X):+'8HE"3(IeF)or93tK\+:D=YqjX'D'd?H4`\WKME1>kNbK@m8Mrtn"fP&S=CUa"T?2HV@?;NiBg\u\Or.ep+7+1CV'1a?"A4M@6a)-p-@Id,j_?d$9M4=)OP5c;WY!@EBD:""9E_.=B>A6llqs=uZ'ph%(@(ZL1slaU"^X>spRFR)lnh569lu6%N[b[>4_]RV_buuII!u?2X2o8A,+ma4g3A?4Q2)E_QLOUlY/.P<.n[&#\+6V8.=;uCYE.FZZ7S#)oUpt:qYGF9[-uUIID'DBbeVsrh0X&XgT$dsjL`nk^sQ%+),=^B<A2m#+-2<^3Bm6kF]u[.[].*u'F8-JD%%nOFCC3!)K[$[+jZTKE4g'=k8'Qg94&!,J9<s=i5#LT.S`b3QXiAf3HC"@J\:Dd<V3ld8%peb_>%PXCZF!_I?uKR0ZU&2@+Uu.rHADmo>F*3?%#)j2,_WGINuOP)A"6]epC-YPkiI(Eo3'rPJ]Ot#,Ap65Wh4G~>endstream
endobj
19 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1226
>>
stream
Gat%";/b2I&:W673;CTEdL/WD,irIM)K`ds'MZ'IH^XW$fsINuQ5#3oRKiTV4&LlGB3DUp7Mq\@hgN07a<LpZ?QT2!G6$fI34n+a"jGT6,taPSJ03t%D1k4^/JGqPYW*AJC#"*OD5%(EKdmfd#K&A:/O:;8`hWeAq.\)23",%2&I6gCBd3Wf:R"`Vn;gEQ6(oSO8u9l44+='#Q_Nct&^C#>?o3L[Ms=/LM2WfCJb]eQ+0U.*KV-DHXd@XN\V8Adg#]VsKb,0Z?,Csc`GWlA\eCIg_#-n?huH_'%$OfYA'bap/ut8\HD"'e-8.-9#9Z60(8"Efkbq'):`:Q,q[mkg;ai/#a#;IBMB^1lMM1=FllrEOS)3#1NML!Bj'_.fTTDaB%kZpXc8$:fV9,>c&YOH>^f^*'Sh2onTUjRgkUcop4DZ7&g6dNEC]Is4XC]<S10](g\T[tiC5.5)d<-/1e9p#6T3XuYK4Z!i:)+"&9/./d-kOfLMg8O5HrlTY_ZO^k@9r$_gj\qc#]!N0@$9W8mp9$[%i-[3`Zm#I:6FX$aq%YS[U/oTes])@"g(Y+5jst@SFY=7_0ui5ZhhC<_KoQ?I9m]Yl0?mr@d<=I%">IjHX$Bd#s=0bAdftYAUjqdQ::1L]n5\r]?>4jqsP<PV;&]0I(#Cp:.CPdNPU:tHN]](ZAg0Koa3Y/0;aq@Qk6g0L5UFpa$@&L,W3U^s4jg0f(D--,\G>5pZLM[Ij(k'fT*/uSL9"GWK3f9>OT&8O+&LR`H3Be>`r1uJ'&A=Y$BZIXO@`WB2eW8ZD@DgNW9B<WE+$>Z?8lUW+YVs(^I4+Bf9o1E95Zr@'Zu<Ru@M_$$)<P")gei[tUa$YIgYdD<IUYmH4NJIFSJpPeY<h1]on32eTPADq8loH(\W5's-a\l3O4>U%T@,o)`CrQ5>6,Duu(F`9Df1'1qTW`TXET:KoEqTWiY03A8$jf;NF=D&#R@*K-XiPi6R'UAQb*P%\ZVn_WAIbX3_U@Vn/]a&0+^1$VjH1Npj4Bf^/;l`B!UJkhN@,2$25WQTnc9i\-Y0!C^BB@cGRo\gW-jmb/c!?Obc,i&]sg*GKCG^K104s%1_#!lL,(X7(HoS1^1VH61;P#t&C^'mM0[r#go7mp[\,*&VK9dor=,rW.Zf&!5im.57I#M'Ye;1"tlSJTi56O8!]&Kf>)LdZB(StYesbl@F=SGeW*4++J3M#IUe&n:8~>endstream
endobj
20 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1960
>>
stream
Gau`TgN)%,&:N/3lu5!'3FdUC#_Jj^V>%@]\8drcIAo7:9L0:3hFphP>`J`rb<)PrU&<>B*g)&1ZNlEMfgAUjn3tNE0*M&f#5S]7Peh/BPS4P5/,4*B\f25.R*@3*VIPuG"6,cg+/Gr7"I:1IAM+/,Y_ck2i!q&nj#XtpSo%CF;?b7A,7;@)L@&q&%^gs75+-=%_&.ph`"KpMSmV)1$@4<:2*7U:r?E.c60gtIW$Wkr[;64_+cKc6s8EX[N`R_%eo;\F]0'*M3eo_*2LPB7IS_+d&18qjK@qj#Se_?QT\M;YATIYZoL`\7Z:JO7oNJEq8:o@oq$j[_TEs+F@rAhb0,tR=:fh%Zg1-E4,"!SK.:F!P]+C"4D-,D-a+=nIN+-6Te8Bg["ukoJLsoVhNS6*^)0'N09;m$X65$_W(7^9IU5hMqiY</s&&F&7gMqOH)]b<n`UZJf7jc"^/_=T`]:/0;)m)VA.2B%$a7U5ggp1H"im^EEgG>2+N%tF2MdKuPEm&PFA<Q.,'rb0p8l+M?OZ_BIIbS'4gEA2@Q4Vas;r@hnMK8+;^cZ$=_\#m^5>:&nLE5=N[lFYJ4Y5&A">*X%)of1@9[]*A(TuaE@CB0)<801f67q:GaC2W9]\JPmpo.R_QE-I8k@_"<#]3IKg.&Jt!&'A<*<&KjH]k!gVXi@#l(9YHf/=^dgAGc#8^o%kE,g^"LIg`:)O/;rlOT&s7SU0>pP<l%bCq%?9'E>@r"+-&K1l`+1Y3hf19E4u"`m@HF/NUmZ\QWfp7SD#;tA7N]JV^UC*`#JT9&ScErobXcV1qSW7$g.$l:ScV2rThEc6r*eRdaL[tf,j8<#^L0PO#d+B8.t?<i$SqYKSQ4!0i.$;qkUkr8.li<"IRHi8h4H&>8eQOi]X%]XQ%rVUI;P+cPCUSG1<:(KrqIh>\,G#4JKhPO#10Oa$#Mkab1L*-qXE5Uf94_oq#5(:8.\\'?Lod!^(]9/Kor;<U$3OF9##YuIr=P,#c>8!hK0sq1i>c2#?+1PMDZ?^l/jJg*p_X.O`<V[u$?p5XIk,?W:m1sa\8j>Y%Q!*fQL2*_7'*gk7S_R#n2Loa.OdeJaSe&UlVeS1sEF%^ARnM78paCcSRdR2Ug-Zj.^R!7S8*4;T*DE)L\qVF4-`p?Cb$">BTj1HZIG8^fJH:p!H>4H*$^FJpmg:/"qR9,-<?RVe8d32b&OJ<Fj83Z(1P]EVfFESR%q>/dG!B,IJcP8:'E^ZW5t;N*U4lcaTF^,.`>6lPN&#<1Rlj3k_DDG1pL1XK'OhR\5Ee$W>"j.J\.0'?RpfJ?4lj?h&+bVa2lk%0ApPX0",\\"gol-BR)sNg+aN9'Amd%h)[pp1FqC_u$=AEdAfS4YBo1ODHOIFmP,J96kZ)X-W,QPjkTbBeG>4uf-Vr[K%p+jhhR"<ZK+.=MVB$sQoJ^f@XbdM.A?6/bW>Bd0!)nX.VXWL9X?e"DNlC,W60h0X@t!hB@-A+/!*VR5'[D^WKs`/,r_HTL/t?.\4mA>j]Wc<cd..dGS2@r>@.@A@'H9iIci:T^VoZspL?Nu']X%pPWRH04.R\DVbFfk)4F$asp#,4nd=E0@XT-q*+"2MPK[HEle.9qtbM]R9;p-MED@$dX)Qt^j+esE-qe[nNM(Q_+d[t'.%m#^[K(Yr;6ntiMpQ=>`bg:a-Wk/X`U[C#ibD1N'OUsISUiA^a>B4qf/NH_decuu:#7]HSimE'8;I[OgTn81HiP_o21d%C$?XF#,XcAAo;F-rbqRB=s[XR.ZSBPUEh[9%I)N8Tm[in+b,akQT`ri`N/^GB$-(%n:mn7LD0BuM<M'8/M4!DV='UDXp<OPCjC_A$;aMe4,pW0VN<kidYYXJBtEZa[;S<Vb[I53@dDWQeYMB8JA%nam__r#QNk5#HdZ:D;O[:XfW@!MUP,]jcJG`Q3;rE;=7^i^40:`e#8d6FfBT9K~>endstream
endobj
21 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1879
>>
stream
Gb!#\968iG&AII3m._j(2?\OQ#eH3edO&\^ZqbG6Q`::ZJn3S_RAB13-NCaeOod(&''")RX^mhFK`1V$`<S,jph'4!0EHD^KulslN5K`<9Fq6R2t19;\eu)4\L1W)NhVQ[JbY7N6*VE+i(>2AmP@\h,K^ZB'I-6G$961R*qQ&OY]qp45WhV$-%IE+8gV68GDP<"<=@eYl/.hGm8M"eT?,SE1pd!F0V#s_V_"hm#\C1%_DHi6+/aV&L0q6_0#Pj`MrB(akjYm0)<W&'HOW4*>9.@e/e2<9"k+hg5n?r)PdlbC2F66a1<\iL@"I/=b:!fbp-@JC'\=ar\#+G2,g)qbUL+6DR_MQe-jZTEc4"PF@ltj*X\su8+:019Y(Ii\K((Oo5So:.E#St"V'3p$Z=1[P1Se!g1PnIT#c#V1QmmB!12\j)_UK4dij]N@89eI)ANLZgKVCr?)mZ:_#o1tUO/V`AE+I!o(Ft,8?:1j8'VQLRYQf=MG*98KNZ_<q&<@[>V0ZLt%ZGSB_@c0oM=JF9g<+WrFLU5mE`AC4GrH&!]G0;U$)0hq`S&G0:OLHt"NM[W_mud?kG$=I&>`qQ`%beF3k&2I>+/r#bbD&4)Ze#]-sYYe'6UQeIXUU?Qb']W6_u9C_TI*&#;tLW<!P$Dc<+-fNpOmimnH++`KgS^;_(3Eeg"hB+9+IJ'+!al2Y"gRFIX0#D)C-`5?lS[Y_l]*e:4)#VBi-fFPIs:`)ka'=^aL2-<U`CLrka,Y^*N!Zid(oY")5*PG4(aS[HSX[+.a<Kg@iCK;Bnpq#Pqp8WZHekcnj?qj/IIH.-dC@[,qV;8HlN$QW4CZrr]%?r:G@*.2$3#<r]K$hQnCIhTK5XY*/AqEnhfK4kf'QbDk0g<MjfQq+)F)hh^TmAqmRGe2k_Ccjd!;Fk*1UcY6rG;-#1C;Ql5e/ehUa#&Ig.LiF,9B#:&q/eMIoZ[]4l'mqjX4\YDhQIpb(L%mM)?UAoGk)^b?'^l@''&["K"E/*OHA7ZX"m>__'A1X+dCt*TS5""EJ8@;<bC@mlWfJfj&cZlOCWDQT<t;`N=Y@kE%j@9g5dOb+rsI%:p#WX@N0Y1^m@@M2mt5%\#WMk[bd2tHiNs:7e(aEL,3,t(`rVb;=4,*08&)D\[G`!CuJD(UZr[sO,:^JAW`gS:?QR:UbLDmFM*uVW)];[d7B3tV>b2Bl8tXn/G2H:MQV(/6!WDE%nIIU<IFs"'?GnWomC9Aed7B/m"E%dID]LHO@a!9236l/R=#U33ZYZN_7Jm3Hk9`b/OJ'%Nm#WoKe0DO:#HmYohm#QNRIXE:(&VBkI@$C$Y]M3\sHWLg`B_OnjiiD_r;qXSGGFnR-3indU@9`Z(A*'"m@W7)\4&_#'[YgY$,#D;]Cl;olr*3\fU4dM^I<hg]pl)O:/B?O#:Ns2Qho[/]/dDcpnYC62^6dhCNfL$f!Lt+C!>cRb?Yk(i]=r1$E^B3!QQCFoYe^=]%*YZO(0IhWNK8](OlcS`XObeRfIul8`26`jmOAne-/2$8AsQGBoTV?Wlo:=,Z#MO4`[^cF1d(YS7"QgRCZ6o+E"'Nh^]Q"mJiSU3'2IffnK?g'BEH7;CnfE\'477c!Cf6WtgOk4iuO']PV>8K>6iD1t)9Lb$u!i,Z(=\*g^&I^e^`g[M_-S=N_I^3qBl65!W2hbH&NkY8bJg\osN<5XPpqs-n;?0Mp$&';hQmT.anC#;,)n?.'IDpY*]iO*(r7CK5g!.$0;kW\d-DE86>AK0cB0`^*'mY>^O\S0?f8;$H21=m>CjT)g*2)&Vgr/.*s4EVjGFg0r;VhA"K[&7jT^tGb#ET(e@dn*V3p_:XAmZii'533(7mTfRF'??dfCF6j\+6is:&c~>endstream
endobj
22 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1220
>>
stream
GauHJgN)%,&:N/3D"D28%8;9p$"c5^Z(!^q@lLBhpdm+M\WUW'KE@=b\6kli71pnN<pe17Ui,8]3IAXE3B2-MqD4Q@SFtk%S-'SS*s%^%6N7Z^om8oL9_]fn*\^YUDCC$hDY4&mmUb>PbR[.`5resEJHVr3;^t`ha=dgD!#B#8[`<q&^(Vn$9QAB[rW@Y:q*GY#A=i7F]DcYpWX:CiIN';]fpi17(;*@VNTs-uE%g^R5D,`G^YW;4T4X%-1bg>2mtJ_o;SYPR!)>3*,;(Z-F0s!7^OuO?MP(&$4bn<c%(8GT0fS^Vl8?pI4\daA5mQHmWo=B)(N!i:n<rhe8_&=%aINk^n12qNF/i/E#uS1rkS>mY(h9IUL09Hh[q;!OrS@mU)ofHU?6K8P(g5rJ3t6X/jK%-<a3$k2O4gbAC:XebhlTuD*s<(l$ZMq'Lb#(BW?+1%[o4pgd10K\.6_$;odnnRcfTp9ZcD6!8=tT6L26Cao@2iHP)VQs:2RY<jUaVll800!KlNij0etXV$RPIqP[kUQ$DR2WZY#Vh?XD%@`jR]$a2HCO_%Q.Bp^M-58D=7[/99)?DtD#SI8ISr4P)$PI_1+ZhB.QJFgt#_)3f4;'dARI^rj!?A8HCNm,dhjVN>MlB,]Vp-He(m&sH0+7qI_NRfiR?J8#"^%LWDD&kZ3D1;RW2ml?`;g_U!nN=+T%aX^E!f'H7p2.c)n1"0V88p+s0.6bO)k/kA[_8,O1l>.PJ=SqfocLT%'6YmUUbTSj+:+%AA%a[-?T5ePfA.$4p1ns+CNkQlDTh_`?pOctsW7>jQRN]TU8RrXC;NSZWIFht6.H-Fh#SM9hQ?PdW!S<rjb_,"\$YGmY<W,YNVO!rH2:;"_K&sWXUHJekUKp1P2\:"Hn:+I29<,DuYZF.MS;u7]K]_&9OUVV(8P);?;&?Id3KRth?'3T(Un')KTsOS2U.pi`N57*f4PWfoP:YSIGa="N2mO'nAQHH./H(`TTM=4Rb-N=;)h19^cRZ2(Qa`I[J-WpI+C38T:8?1pH<+1Pb-X^rbtLf/1a5L?/`sFME+*]ZMgKEM0M-pFMgF1hNq^Pe^f@S)m"Xh/a^1F8:(\mY9N0*:H5*#bJ7P@Kq^4-4cKf&MiHeO:NCm*hYhB=US(`tr0AbPE+6<-QWP'^d:GF.b&"QFud#O'Ukq'[VI!`h13pPh4;`uk`Sf^Cgs0=uu]q8F%gIh~>endstream
endobj
xref
0 23
0000000000 65535 f
0000000061 00000 n
0000000122 00000 n
0000000229 00000 n
0000000341 00000 n
0000000418 00000 n
0000000623 00000 n
0000000728 00000 n
0000000933 00000 n
0000001138 00000 n
0000001343 00000 n
0000001549 00000 n
0000001755 00000 n
0000001961 00000 n
0000002031 00000 n
0000002320 00000 n
0000002419 00000 n
0000003662 00000 n
0000005448 00000 n
0000007382 00000 n
0000008700 00000 n
0000010752 00000 n
0000012723 00000 n
trailer
<<
/ID
[<a091d7cd8a415e8f493ae7ffe4c57a53><a091d7cd8a415e8f493ae7ffe4c57a53>]
% ReportLab generated PDF document -- digest (opensource)
/Info 14 0 R
/Root 13 0 R
/Size 23
>>
startxref
14035
%%EOF

View File

@@ -0,0 +1,465 @@
# Les 18 — Lesstof
## Advanced AI Toolbox — voice, vision, image, local, edge
**Vak:** AI-Assisted Development
**Vorige les:** Les 17 — AI Production Polish
**Volgende les:** Geen — eindopdracht thuis
---
## Inhoud
1. [Het AI-landschap 2026](#1-het-ai-landschap-2026)
2. [Voice — Whisper + TTS](#2-voice--whisper--tts)
3. [Realtime API — voice chat met lage latency](#3-realtime-api--voice-chat-met-lage-latency)
4. [Vision — foto's analyseren](#4-vision--fotos-analyseren)
5. [Image generation — Flux + DALL-E](#5-image-generation--flux--dall-e)
6. [Local LLMs — Ollama](#6-local-llms--ollama)
7. [Edge AI — Vercel AI Gateway + Cloudflare Workers AI](#7-edge-ai--vercel-ai-gateway--cloudflare-workers-ai)
8. [Wanneer welke modaliteit](#8-wanneer-welke-modaliteit)
9. [Resources + community](#9-resources--community)
---
## 1. Het AI-landschap 2026
### Wat is er beschikbaar
**Models per provider (eind 2026):**
| Provider | Text | Vision | Voice | Image |
|----------|------|--------|-------|-------|
| OpenAI | GPT-5, o3 | GPT-5 | GPT-realtime, Whisper, TTS | DALL-E 4 |
| Anthropic | Claude Opus 4.6, Sonnet 4.6 | Sonnet 4.6 | (via partners) | — |
| Google | Gemini 2.5 Pro/Flash | 2.5 Pro | (via partners) | Imagen 3 |
| Meta | Llama 4 (open) | Llama 4 Vision | — | — |
| xAI | Grok 4 | Grok 4 Vision | — | Grok image |
| Black Forest | — | — | — | Flux 1.1 Pro |
| ElevenLabs | — | — | TTS (best in class) | — |
### Modi om te draaien
- **Cloud API** — direct via provider
- **Edge** — Vercel AI Gateway, Cloudflare Workers AI
- **Lokaal** — Ollama, llama.cpp, LM Studio
Vandaag zien we alle drie.
---
## 2. Voice — Whisper + TTS
### Whisper voor transcriptie (audio → text)
```typescript
import { experimental_transcribe as transcribe } from "ai";
import { openai } from "@ai-sdk/openai";
const result = await transcribe({
model: openai.transcription("whisper-1"),
audio: audioBlob, // Blob, Buffer, of file path
});
console.log(result.text);
```
**Whisper varianten:**
- `whisper-1` — OpenAI cloud, 99 talen, $0.006/minute
- `whisper-large-v3` — open-source, lokaal te draaien
- Distil-Whisper — sneller, ~6× kleiner
### TTS — text → audio
```typescript
import { experimental_generateSpeech as speak } from "ai";
const result = await speak({
model: openai.speech("tts-1-hd"),
text: "Welkom bij Polderfest 2027!",
voice: "alloy", // alloy, echo, fable, onyx, nova, shimmer
});
// result.audio.uint8Array of result.audio.base64
```
**Voor productie:** ElevenLabs voor natuurlijkere stemmen, of OpenAI tts-1-hd. Voor demo: tts-1 is goed genoeg.
### Volledige voice-loop
```typescript
// 1. User spreekt → audio blob
const transcription = await transcribe({ model, audio: audioBlob });
// 2. LLM antwoordt op tekst
const { text } = await generateText({
model: openai("gpt-4o-mini"),
prompt: transcription.text,
});
// 3. AI spreekt antwoord
const speech = await speak({ model: openai.speech("tts-1"), text });
// 4. Play in browser
const audio = new Audio(`data:audio/mp3;base64,${speech.audio.base64}`);
audio.play();
```
Latency: ~2-4 seconden total. Voor "echte" voice-chat met lagere latency: Realtime API.
---
## 3. Realtime API — voice chat met lage latency
### Wat is anders
Standaard voice-loop heeft 3 calls: Whisper + LLM + TTS. Elk een roundtrip.
OpenAI Realtime API = één persistent WebSocket. Audio streamt naar OpenAI, audio streamt terug. Latency 200-500ms.
**Features:**
- Interruptions (user kan AI onderbreken)
- Function calling (tools tijdens voice)
- Verschillende voices
- Multi-modal (text + voice in zelfde sessie)
### Setup (concept)
```typescript
// Browser
const ws = new WebSocket("wss://api.openai.com/v1/realtime?model=gpt-realtime");
ws.onopen = () => {
ws.send(JSON.stringify({
type: "session.update",
session: { voice: "alloy", instructions: "Je bent een helpdesk." }
}));
};
ws.onmessage = (e) => {
const event = JSON.parse(e.data);
if (event.type === "response.audio.delta") {
playAudio(event.delta); // streaming audio
}
};
// User microphone audio → ws.send(audio)
```
Voor demo: complex maar magisch. Voor productie: hire een specialist of gebruik bestaande wrappers.
---
## 4. Vision — foto's analyseren
### Basis call
```typescript
const result = await generateText({
model: openai("gpt-4o"),
messages: [{
role: "user",
content: [
{ type: "text", text: "Beschrijf wat er op deze foto staat." },
{ type: "image", image: "https://example.com/foto.jpg" },
// of: { type: "image", image: buffer }, voor lokale file
],
}],
});
```
### Multiple images
```typescript
content: [
{ type: "text", text: "Vergelijk deze twee foto's." },
{ type: "image", image: url1 },
{ type: "image", image: url2 },
],
```
### Use cases
- **OCR** — tekst uit foto's halen
- **Defect detection** — productie-foto's checken
- **Visual search** — "toon producten zoals deze"
- **Accessibility** — automatische alt-tekst
- **Receipt scanning** — bonnetjes naar gestructureerde data
- **Code from screenshot** — Excel-screenshot → JSON
### Cost
Vision is 2-5× duurder dan tekst voor zelfde input. Reden: image wordt naar veel tokens omgezet (een 1024×1024 plaatje = ~1100 tokens).
### Provider keuze
- GPT-4o — beste algemeen, snel
- Claude Sonnet 4.6 — beste voor diagrammen en tabellen
- Gemini 2.5 — goed + cheap
---
## 5. Image generation — Flux + DALL-E
### Flux via Fal
```typescript
import { experimental_generateImage as generateImage } from "ai";
import { fal } from "@ai-sdk/fal";
const { image } = await generateImage({
model: fal.image("fal-ai/flux/dev"),
prompt: "Een retro festival-poster voor Polderfest 2027, oranje en cream kleuren, vintage typografie",
size: "1024x1024",
});
// image.base64 of image.url
```
**Flux varianten:**
- `flux/schnell` — snelle, goedkope variant (~$0.003/image)
- `flux/dev` — middle ground
- `flux/pro` — hoogste kwaliteit (~$0.05/image)
### DALL-E via OpenAI
```typescript
const { image } = await generateImage({
model: openai.image("dall-e-3"),
prompt: "...",
size: "1024x1024",
quality: "hd",
});
```
DALL-E is duurder maar bekender. Voor productie: Flux meestal de winnaar (kwaliteit + cost).
### Goede prompts
**Slecht:** "Een mooie afbeelding"
**Goed:** "Een retro festival-poster, stijl van 70s rockconcert-flyers, hoofdtitel 'POLDERFEST 2027' in art-deco lettertype, oranje en cream kleurpallet, decoratieve geometrische randen, geen mensen, op afgeleefd papier"
Specifieker = beter. Style, palette, compositie, wat NIET te tonen.
### Use cases
- Landing page hero-images
- Social media posts on-the-fly
- Avatar-generatie per user
- Product-mockups
- Email illustrations
### Storage
Images zijn groot. Voor productie:
- Generate → upload to Supabase Storage of S3
- Save URL in database
- Gebruik CDN voor delivery
---
## 6. Local LLMs — Ollama
### Wat is Ollama
Tool om open-source LLMs lokaal te runnen. Eén command per model. Werkt op macOS, Linux, Windows.
### Setup
```bash
# Install
brew install ollama # macOS
# of via download van ollama.com
# Pull a model
ollama pull llama4:8b
ollama pull qwen3:14b
ollama pull mistral-small:24b
```
### Run
```bash
ollama run llama4:8b
# Interactieve chat in terminal — 100% lokaal
```
### Via AI SDK
```bash
pnpm add ollama-ai-provider
```
```typescript
import { createOllama } from "ollama-ai-provider";
const ollama = createOllama({
baseURL: "http://localhost:11434/api",
});
const result = await generateText({
model: ollama("llama4:8b"),
prompt: "Wat zijn de hoofdfuncties van een AI Developer?",
});
```
### Wanneer lokaal
**Voordelen:**
- Privacy — data verlaat je laptop niet
- Cost — geen API-cost
- Offline — werkt zonder internet
- Latency — vaak snel op M-series Mac
**Nadelen:**
- Kwaliteit < GPT-5 / Claude voor complex
- Hardware vereist (8GB+ RAM voor klein, 32GB+ voor groot)
- Eerste keer downloaden duurt lang (5-50GB per model)
**Use cases:**
- POC zonder cost
- Privacy-critical apps (healthcare, legal)
- Offline tools (developer tooling)
- Cost-sensitive massa-apps
### Aanrader modellen 2026
| Model | Size | Best voor |
|-------|------|-----------|
| `llama4:8b` | 5GB | Algemeen, klein |
| `qwen3:14b` | 9GB | Code, reasoning |
| `mistral-small:24b` | 15GB | Best kwaliteit / size |
| `llama4:70b` | 40GB | Top kwaliteit |
---
## 7. Edge AI — Vercel AI Gateway + Cloudflare Workers AI
### Vercel AI Gateway
Eén endpoint voor alle providers. Voordelen:
- **Unified API** — switch providers met 1 line change
- **Fallback chains** — als OpenAI down, automatisch Claude
- **Cost optimization** — cheapest available model
- **Key management** — Vercel beheert keys, jij niet
- **Caching** — built-in response caching
```typescript
import { vercel } from "@ai-sdk/vercel";
// Direct gebruik
const result = await generateText({
model: vercel("openai/gpt-4o"),
prompt: "...",
});
// Met fallback
const result = await generateText({
model: vercel.fallback([
"openai/gpt-4o",
"anthropic/claude-sonnet-4.5",
"google/gemini-2.5-flash",
]),
prompt: "...",
});
```
### Cloudflare Workers AI
LLMs op Cloudflare's edge (250+ datacenters wereldwijd). Lage latency overal.
```bash
pnpm add @ai-sdk/cloudflare
```
```typescript
import { workersai } from "@ai-sdk/cloudflare";
const result = await generateText({
model: workersai("@cf/meta/llama-3.1-8b-instruct"),
prompt: "...",
});
```
**Models beschikbaar:**
- Llama 4 (klein + groot)
- Mistral, Qwen
- Whisper (voice)
- Stable Diffusion (image gen)
**Cost:** gratis tier 10k requests/dag. Daarna betaal per gebruik.
### Wanneer edge
- Globale app, latency belangrijk
- Cost-sensitive (free tiers ruim)
- Geen vendor lock-in
- Vercel deploy stack (logical extension)
---
## 8. Wanneer welke modaliteit
Vuistregel — denk per feature:
| Feature | Beste modaliteit |
|---------|------------------|
| FAQ-zoeken | Text RAG |
| Foto-upload met analyse | Vision |
| Handsfree input | Voice |
| Marketing-asset generen | Image gen |
| Real-time conversation | Voice (Realtime API) |
| Privacy-critical data | Local LLM |
| Globale snelle response | Edge AI |
| Massa goedkope calls | Edge AI of Local |
Niet één tool voor alles — combineer.
---
## 9. Resources + community
### Communities
- **r/LocalLLaMA** — Reddit, dagelijkse model-updates
- **AI Engineer Foundation** — Discord, productie-focus
- **Latent Space** — podcast + Discord, breed
- **Hugging Face Discord** — modellen + papers
- **AI SDK Discord** — Vercel's eigen
### Blogs + nieuws
- **Simon Willison** — https://simonwillison.net (#1 AI blog)
- **Latent Space** — wekelijks podcast
- **The Rundown AI** — daily newsletter
- **Stratechery** — strategisch perspectief (paid)
### Twitter/X-accounts
- @karpathy, @swyx, @sama, @AnthropicAI, @OpenAIDevs, @simonw
### Voor diepere kennis
- **Karpathy Zero to Hero** — neural networks vanaf nul (YouTube, gratis)
- **Andrew Ng Courses** — Coursera, structureel
- **Hugging Face NLP course** — gratis, hands-on
### Conferenties
- **AI Engineer Summit** — SF, jaarlijks
- **NeurIPS, ICLR** — research-focus
- **Devbase, MAINSTAGE** — Nederlands
---
## Bronnen — alle docs
- **AI SDK voice + vision:** https://ai-sdk.dev/docs/ai-sdk-core/transcription
- **AI SDK image gen:** https://ai-sdk.dev/docs/ai-sdk-core/image-generation
- **Ollama models library:** https://ollama.com/library
- **Vercel AI Gateway:** https://vercel.com/docs/ai/ai-gateway
- **Cloudflare Workers AI:** https://developers.cloudflare.com/workers-ai
- **Fal models:** https://fal.ai/models
- **Replicate models:** https://replicate.com
- **OpenAI Realtime:** https://platform.openai.com/docs/guides/realtime
- **ElevenLabs:** https://elevenlabs.io

View File

@@ -0,0 +1,277 @@
%PDF-1.4
%<25><><EFBFBD><EFBFBD> ReportLab Generated PDF document (opensource)
1 0 obj
<<
/F1 2 0 R /F2 3 0 R /F3 5 0 R /F4 6 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
>>
endobj
4 0 obj
<<
/Contents 20 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
5 0 obj
<<
/BaseFont /Symbol /Name /F3 /Subtype /Type1 /Type /Font
>>
endobj
6 0 obj
<<
/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F4 /Subtype /Type1 /Type /Font
>>
endobj
7 0 obj
<<
/Contents 21 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
8 0 obj
<<
/Contents 22 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
9 0 obj
<<
/Contents 23 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
10 0 obj
<<
/Contents 24 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
11 0 obj
<<
/Contents 25 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
12 0 obj
<<
/Contents 26 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
13 0 obj
<<
/Contents 27 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
14 0 obj
<<
/Contents 28 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
15 0 obj
<<
/Contents 29 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
16 0 obj
<<
/Contents 30 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
17 0 obj
<<
/PageMode /UseNone /Pages 19 0 R /Type /Catalog
>>
endobj
18 0 obj
<<
/Author (NOVI Hogeschool Utrecht) /CreationDate (D:20260607101945+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260607101945+00'00') /Producer (ReportLab PDF Library - \(opensource\))
/Subject (\(unspecified\)) /Title (Lesstof) /Trapped /False
>>
endobj
19 0 obj
<<
/Count 11 /Kids [ 4 0 R 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R 15 0 R
16 0 R ] /Type /Pages
>>
endobj
20 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1970
>>
stream
Gatm<>C5*_%"6a2^qMtUm6XeuFeSMpX_I(F?.7A?*gSsf2IieM-!-7nk2-[pFgL+di];7!MZc<c!!Eg(_8-r<T9BGompgV8L&tp?^'3fU/rW;>0t+f!2Y1)0H=1gQn;r2AEqK\5bY#_CD*MPS"lTTm#[D<P[=nki0H:l31k8iJm7W@h*V`AP>d+D)I,R:-cY]:/SD(A6h[_Zd=7ur3?>05$F_IXtUGA(,'rXSPGAW+]JHt3=%/t_UGmJ>pR4K]V/5+on17+2M#rdeHE'As,nm3Zr;W]W?r"[4)eH?nhXtL?<+06t5aeC2i>GL7/R`/tD[%lO]HEbuYZcXhk;`b7JNh5.o?H^B/$d/uAj&60qHjFTOpo:7PCL;&355R%=RKlHqm<B[PA(rp!joGpbd7.h`UmfDoQAO/V:9839VT!8W3UL>jb<[osr2et[0@bptB?`":W_J1T*3h:n%'EMsTJoW$ST.WN"4>*5!o"<I-X,jfXJD<,RMLUjH"aE@/P`@kBd`ddlq3[YH^"lk!cr5@d1BAK]qElujSc,<3/=]'"st+dej_$0EBVd$1CaGSkVtk1V_1eS1:Km9ouu"kJa.j&pSn\iOATQhCAj[A'6sfVE=ljJ)8qGH-=J7kB9Gu`"@f/1`>Q`G[[n!?/ejg":h9eH8lqZgi1A+1JT$jL]5+%3JW;X[279M,n"9k@"EBk(H3M+f;eVD*.j_bL%R*C,^TAC0[49+B;Jt^!S$)'@+-Q5iLl5/+lWUZWBt8:G#Il`#`*Z)8*(J=W3IRnIcJ`BN@C=8:p9BG8*<n`c8jil?/:P0UYm1q`NXLfin$?O/g%$i&^/Jhje*A<Y.Xdm_=-F7q*3QRm0'<$B*<uktaE/nY^c;bH7@[,ZjYW%j(-!`*X16dpQk(."2G;N:D3,&T?lMbF'u1G_$U!8D5@.gZK._9J(:^o0VTN>4KhpG<\fku+ITqS`AIL:3A?@4@*pKc]ee8/<Z/hPjX;,.tnp%$qpSj0j=s/`JOr7JgRE08IG:@+'Q.;QFaDReqS9CbtlRUrH!E+lId;b:]eH1(XhO:)8-<2o8@j[Iqkr`0EG8f!GCSlu31M?/cNDq//SnI6[,t73l2S)Z&krMa+CH;@j<gG5+OslkF^PnI\AXNMTc/<5,k-?70gia:[[AO9S@.,@k=WbH9Ob!*oDpnW2*Z)neiE$kW5:Og8goFaJ*_[(fA\"qSF:e&-+Qe(`o-4isPYk>u$R@?+6N^Sg"H7%$$]KS0\5![%17^NW2fOp]6V+SC8`^X4:/IH/EDhY^G7HcmF5/t)TuX$_-@Efb[oo-t;F$I9a^ZK8R4;jZ`,J%0<-LX^hS%RZP<ekg5S&oD5--39bbMK6_4,!.*>cZ[o?iA!./Y.6>aVmMkQqqIA/Za#OaFc\RKD^InA;I'Dq)"<KE$@_7#OS:pPEM],Y8aT+dm6*$hcEr0B`GfjTiR>@dt0"KfQmbW5q+L1/X+!\<nA47u[<e$#YU/8VDl,lR]f7H*_30cW=EZLcPX;I=^2>n`o;]N$i/a$muQt3-Ob5)WR`7Rcr]eG0"AZ1?:Jg;i/-)K1t\Y/2Bor\<M!4]HP@Kkp<p-[(UfPT*05f"7qY:5KY0S@;lUuLe5$WXO=W.4%"fc6e-n,Y'ZbX^R=*=JN7I_'qh76"FcTo;&ct7S&7XY##<bWC1c<"hAn"G]2NAnY&Tqj$c!JQU@.O1].[$GpFsEf1Z<W"Y]nc"-3Yf@ZcocQG`"_'1aNCEic^s?)W#62iIqL]Th[tXBWldh\prbK8)FcdHUXqBTaa3A_2;lPVXJ9&.R`#E4K657Fe)M=O6-+VLEHZJpaSj&"h)U[cr35KAJHNF0$6P]n%`[8^!Cji2\-JYDS5,Klhs_YCNiNX^`(><X"j.i+QE\,GGGbbD'`siGZN;U1W>dn\A/M3A-1PHYY)Ahnc.q#$Um0lgKo$P`ai4oY"mg<YcV5>r=;,QH!L~>endstream
endobj
21 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1950
>>
stream
Gb!;dD/\/e&H;*)E<u9Sko<0g.#Mao@*.,5):Gou]_%,5J[&oZN@_W'S7F.]hnGlBD+daS!Z`1@OX8m*mlB7J_bdT,7C0bX-,B$f%SnKL#3.^?:,n3YiRN2BEBon!/pDiud]u"?`3[]!`/?neNZc2K*X-8g7(OAJUdRgN!^f-&V(T\)k0F5GLat#XEZgN+6Ace3Rg@#QBhfWk]P[Va5AApELa]o'2tJsH6O?qHWiXpE=O_=2(b]=mqjYbbpbc7=A9o7b!W8A*H?u.!KJ=:'cJYP5eNpRjnNMmr_"a=#TOF&mM%G?PC%PXX8K,ip,B),L%i)='JThsI*Od$1Lib2.Om`Z3l3UT`omlQ^T]T7sE8Dp/4;2EJ[]cMCZ[+3MiX61,XOf(C"k@+L[A'bhAiYGlA7[dFMB$>HT`FA^CtmgddB,Y!g1k@W@_8q/*p`^16dilJE#c%o^%;t&$JU9&k7DC_%H5C#l?6=bUVrk5p>Khe(/7p^'.d!Zn/_SS]Ze;kQsRh\b+JC#0#W5Ge9\"Nc"`dL-Xm2p;&JTJI-qA3@A-Jj%S39I);b2s<[pj3*@I'L^=\Zb=FBM1#i<]3oe_OA5tA.0jJAqaqOrs%a8nu!_im>3f6r_geO$/ED^#'Y]S0OT$R+/.QmL"q?L(QR%(Hj2&(FJXLWL+^?eFI0Klp&S;(5l9FRN04Z<'qSj(9M07qW@M/*'IckQZ8BW"%G#@4G4tJZ3A=h^[[rAD$U8i,PEe2fKDK3pCBA"iNa$gP6,DY@N?f=0f\C8=ftMH_?%8B>/o/;I9SeAA`J2ProJUYHBBTI=`34>];g>^b!7;Y_!t6'FVOfl<9N.;uJZQr@f?n8nR,YB#nlVN9<_OkjZXW!.2Of24p6.lnZ$N>Tc%O[m:Q..g^gnU01(t6"h(m?6t+1K'&##qmsn,E9J2cfne_B[#em'$suL\+/1'W)J66P^2E*3]64CrATRh;=gU&;COODDfh6WP>g@1u]Q?-,Y1r3:SaRN"f%N!KS#OXF<kRIHR=!>-#"NV'k#14Wjs4m-,Ag*tCYZNQ@ac'f_:H*5>H[We]WYp95MC[7WT0rf$o^@MWP2;\O!"hU4A\S^hFI)F:+b,^_E7P-+oEu[IidDo't!"N':9@jK'Yh$X47L$?c,=DT)$co5Fod;?.uC1HLY&"F;+MI$>2s_i#aLC\C7+&^m(h%8S:<RKcNbS%)k9>#\U4OGl/V>C6,[L(s<q2">W$-8+->`TAW9JIK]#IrLV^aD`[GhmAsRh2Mts7@hg?>fO(I=H8\`7kFgIDHW+k"*)V*iAJ%#aIC_-[3aaZ#&5*@=?k@'M:Z+42Y'F!k;.b@A(r@8?m_NqZZ.Wc"Tm@I_\?q3(rMZ/*JlpfKRnmQD1EFsYL-!8]nWY%NT=G*fL,jM?`[US$Tur#Q$(@l4JcR1#P`;lKg6bmD28qj(CCF82CS>+1WBX,_f7TV]*&!=.(6Q36)NT-%11V(Wg@/X="[ddMMW,=6J\K1LpQmp7<Ua)#rE!)\#rM'6_`Yr$\#9/;([^7t>Qpm841l[seLH;$/&*k2ZgnWig!*t)\Bgu?#j4_sN6,rd`rt&'F#H.?6DQ/'#M26K5CpCEIbMRn)1HmV8a[-uGa5N;Y`*0TG[>nCN>dYja`-]$0;&EY2q]99Ct?%OCGT#Fc-#?tIQ7n4WtE/++lo8UfCQP57hEpRmUHc3WO1\Z<>oM;\)r0*RISUerLTsRf.pT.TLf\H-Gqr\,VS-n)cbZo_q_A/\A)6d%B?H&W7Y0[Wl+#u</MlbWYbSNW>'7iY#QrobZ@J',SsCs.7W6@J2OCF&c?#Kb7]ST#[aM+]-15d7K==T66G":m=?sVG`Y=/Ug(nu1.LgO3rXWUX!dJ)Q[Y+o_*M3@);6@caC;@ir$s*PhVSIi%pN.X-_mDf2J_rckX'G:1gZULn8rE<>.>[`rW@)I:4<~>endstream
endobj
22 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2010
>>
stream
Gb!#\>Ar7S'Roe[3&C9dZo0.,eBLE(;Ja/NUhdQOc&7.<[M`;S,e=WE1^gnP^-h0p\TM@o"+nL3A5gu<k<$0A0GGLS;>YR+!k]DenL4Xa?uujB'+UdYpiY*^g`amS!V_,SU#STN8E?3]aQK*&4J2B\M^X6R5f/$R%k.h&-j5:N"T]&TQZ2nt6\,BS'8.>r83K30IsBfr5%CO['@6EJn"6fM$]TEDnLNqPbjlR(i=9%BC8AG237J<u(o>UVrS>LMpeB"E0jQ2m^^$FRksE:TYlIPEi"u2X->Fju,8f7Pk_2m\'$C.a+o&m^cuW>(>$[I]0#Tlt"Lp#an'Qcq"J8\+q:$Xla'J=bLJWaq5.[Z&Q>SVKl(C6Io5HjE[)7NhFD\@MFEm#:#<,;8s*`mG-YT]:oq#lq=edMjN,r`&2/<f_T'&ElS@Z)_e6()!TW$,%UdI]bL994U7[UO02=3@B@hS^ffNLGQ6*pAHm4::VO3FX5p[K\faQBNj_C?d6!K0Le..cu38#Ylr)m;5,WEd>(aKHHF30fmIae*XBa]rM)\WW<m+:9Mt;CpcG>j!SjY)']nG%X7G,]_;Z.'uSfhM!R?EHeN6;jA(9e%8^0)IA'+'sTCaNdLM]C\,rQ4:IXC"-BON9SS).Ru*LH(<!`7e[cUE_.(t9+8$_Bk68ek74WXeIapgL?HH`7kU5m0>c;0[o4s0e1PZ,uFnZ1&*qA&/XQ4e,nPl5JOX04pgBT]%#5bqABW::7[?r5F%k>3gg4r@M&Uc&CELK'jMmXJT-%X6AWTW%2BMX^,B"7(TNsDYu)OY8P)'C-SZWN?4SE]Mgcc$o4d;Rj0LdLrc?g2p2gQ_^#[W[%&(t<`cj&9cJNlc.TWo&16]oPeIYfbL44k6eW>%P-ZgT<X628L?"SVp'j50'dnd-GhG`&C>GD7%Y&?(1GGl0g<-YijOc6GSJMa]&_m7>M>=.ljX2orpoMS<GOT]-2K,$R28j?$q.Xmar$%b-A1]/SSG9#WPE:/=t$?LB`T5X8NgDS.X%/p-7cE;+ND"=M)='#MW5qnts027?s>\$^qgZ?^i]CIr/5VNA,"?lacn.i01bFR=_+84qocm<gHChe50_U].L'BAtEAcqG'`qCdl/c;3T$.Rekfo][]f#0!\rTa"&12XD+a.l9o*"/c0+oe1rR.Dbq8:mi^+ike^_'S#>$EKgrI?(kM;M[idGSMcD[afVu-r/do?_)%0"Mpq2=KhY(RBV&EI\>U*M19,j*"`!MrI(Mt:%bZB,+eKBjXGX#bUYpu-<Pcg*)[bOZ;Z$YQ=M=Z65hIb+:q%3_e&i8Ir$!G=-$(P+D^LYn;\GB)%24gn$[MN2,2%Sb1`5W*g,fiJq#*W<J(Ag(t9nF+_ZJ8bR-?%k]Hli#Gc%Hj"HfI(k\Z\VKli2?>U>+\?DkH_ICNF7dkR)/JpU21!h96aTR^kG<1j$6k5ADf%Bs`)EV3+L@L@@,@E%!!kS]M;I_Ps`u]WO6taDOF#dtQ/18e$WE)pV,l!ImY&BUfmtjPoM+&p,Q[O=^e,gqWupg$n2KCC?urYbXDgYnA@[GMBe$0OjRf(7lGhIe+Efg=H3M%_k=<E0q4#q7J[P2;;%-[!5qm<$OdpCX!6kORcQW$H/@=(rFfI`[&jgV/=@.@5PP@3&rQV@V4\9S]s"bY6/6-,OZ1:bIR$G;l^4@.r_\#m!@0o1I)V3(e;#._j-`sWp*Dr;O/\*Ek)m``q`BjeL4MH=`[6ZI(bs!l9[2r_SC5_ZoA1qn<?JJD0VrkB29M/LC`Za:?a_?[IcHPG!1C5:1MO5,jOP[Pjl'FfkosjEB\%]Sn'uV6e]u]UkCkR0G+'6dU<1:hV(Xm99C)U1;,*.jDl]Un^2t)K]%U2#BQ[+8MErlg!gL:VT.V3N_$QU8HCld_1l0YjnL5rjuaa.NNOY&r.lXZ?,Ut5_8qn'*c>YIkSXF".m.b?oGS6ZH4bu?Ql/e.<kO"4`*Y:17[Sl\MKi4dhV/igrW/r.iIL~>endstream
endobj
23 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1780
>>
stream
Gb!#[?#SIU'ReT:\F@\3U8(;;U8SLWcsR74U2*i[m2kCQ0J[JqOX3_blXiTbq^s0tP24&K;4sC!=:n9`&"\-o\Rn+sc)T*/")ouIaX%&/@C?:cLk+/OpgrUcm1:n\00CA[U2Ibt"Km/=^2akU,`r*<(b"Y_jU2WW&Z#O4*1uhNk!O#oq8?],HqA`4J10PO,sQb5-'E=k/VkccE;Y(sT<dDZ6oNG$R.VO>_]>gMWjV+!6CI"`0\aD<ea)tUiR`0_MK#,`0t]#X'<XuqF7-rPkO>hR@,a"!q&.DZ@J7W#kSY+5@1(FI2#>W77@>cD,JWPEP\:Uk")mZ3pD,#m_X^u-PXbnK]KKY'+6`7f0II4E3h0I/1TE]<p(55^9tAMV4q?$e1g)h'4)fYPH_CT$<;>$-@?^p.d:odAaqc!&i?[3ro`-n1nGO9YFtIljU@uE@WJ+[l.]D63;>WMMdhN1\V&QpnAZB$'oVeuif#G8$l:l*um0]<IDISA5?`Do6Znk05?_Kj!?t-c_@r7h+ZK_>tS[DC]Z$k6F9J-JO'bZb0VBC59"XN#b9>D(tZFX3d'=c-4?1CaN[EhZA":Zj!/[@eO])njkQlZ*5Y26&7N@F&NdB4nC-PN%>"R@tn&IHq2j9(n47`T)I0gBO'Ko)_.XSdO^1n('8E8!<NMKi5+f(k/.^*I$Xl4A.C8J<$E`],'dcX?SoHlDJu79,Auo76(f_Bb'8pr2">Y=MOQ;6l(F\+B-O)36"U^1b^jPdd&@LJ`VN)5RYmAd_n5dF/+H![1MoBWX+PT>N&OO@GqOW8fkN*Bf)^dE&e*M-h(/r5-778P1\(r7]*B_A"q>"EsqX\KZSXpL2t(c\>k:5+t0BA%Wc79Kg7g-Ve.g\6.Sm(iD$G]eU=]k#"*IgU`tA\uIQcM\VWoO(8S4R$l5-KB<F`Ta0&2!>.XIhUmaP2A0ec=Cbi$S\cd67>?7"dG%*S&hNQN@pKV!lM8@fb$(%9=Pl<K-MB&A9,JiQFt1@FoEOUm.Q=8B$]dj1=*m&*Dg#E<'#<%mT]F1P<XK))Cp"5j]L.R-9bc`([S@K7B)n3laea>T>N7a<AFn=`UQ)n"X=P?jbC/(gAZID+G(BGX!A-YoV=hW$H$c0\3@rB$4i]V;A?kq9)m(h,e5[3"!nE#V:D^J>YUS09:'\6!MTga9N[(Vg']YIhb.#b-2J,g$SLb39Eh/o0(ZZ1nkTN!O.\"9Q)mBqK)buD`#8t]c]rN$*f,4t?.C)WG%hQpk9YW)C-T\t1"ALWkY,d/'1g`+5>j*@]+@N(Gp<38<:B0W'JpE;OcXZnsJg#qT9",fbR-3"hg'HCI4%-G7m1@[Jd*<KqI!t#ui]o/3N^XT9`g10.:.HV]Ht%,\\s`@r%]a2K9)tt@i>h,#!lkX008L;[F\"E;.V$`Qidn-)kCER7iNZ(HVm4e)/Je;H<Z_WC,rm972gYEHA[/F7qf/=J;aYE2(VIAa58!JMH3lZm@[?&r+>*P%Wr<.E.A`=)JNuG`S"@A#.F$LR_Hub]?'^m!jig53H*<[sX>=mfXbJtZMN<1Z"Jj64#Q7qFQV1$<mO:miaJa*)ULVM#MTlqCQhBt%5\,<5ZFW$&^Fs)@LU4k2/;.ljZI%4/hf%.B&KZCL8B!7d1<Kn]pA]XErP8DWVI#krmF??if2A8^Rn[`&m>@aUp84\%G(k+ZB]n\#;rBr'4gsMa46(m8IrFnCM50C+]#i-d0oZf1U<AU$ih;CejtRg^FjqujD"0eqrD#E;\i1*YrrUqT1Nr~>endstream
endobj
24 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1856
>>
stream
Gb!#\D/\/e&H;*)EA.U@l!dhl-q_WY?s/q7)1j2Ph0o\b0J[LOO[N!.h(2"lgNSlmfhVInUd^U%Y\]30]BAVSgg)<fpnHk_PQ:X<LY`=@b+Ls%apEk&)'&BTs0N%Fbi^#Y2fRQ6!=<'/#MH&@JgZ?[=\']THkQ2oGX+h3KE3bhkPNba9Yp3eMZWF&is-Pp5Wo^8HfnZY(dliDGX@(-ce%jof"@*#g,_'sHD<1L6#0;V<0n8RHG0X_"?Na45FfT_Au,!2CeP9+MbHS"!\PA^iU@OfWL8Xg6tL_lrs8Q><l'qq/cp\Kr)KG'.aJJYXFsj"O\R3I9*3tC#Nl^&X3L3@7KUopk]uK%=ul6CFVU)Bga_Z]=<Eeo$EOn%mrJYpbZ2BOFBNJ@%7,Um[8)"Z;Gr8+.Aa<snFQ5P@+6]"2lFLs9.QEA13G4Kmr(&2V8T,cTT-,O!\5u5Z%UZrC]p=#UUR)d"d2UqP<rR<"\a%>6W9AEEu<)&V&Se-^unJuG-:.^?sfn,M@(3!!\`9'l]Fsu1RR0MeZFs(@hi9f`U8S\_`l]F5jTS75*[k`8nQIq4n*W'9KIs4;ReS>cI#k*WkASJ)a#/7\ZQ0H5E$e^)h"Pm?2V;U]:9W`ObH8b;]t+p>NYr$fZ\V%fQ1=/K`+q?8l#f8,FPDi*N_h\A^XNm+&rfMPSkUg(E(5BROOL2'F\Lq\%1H(^$mEt*#Sl6bY3V+5Y,TQ2T9u4*8a.6mOiZK^!46#/G$e#!VIK.leVC&HT9m;_:)@>06DYF2*3QtP'*H19A5QKKpu+C=II9ZGfV#KR@nV/?1631Nt%sb9T]a),d$COba8fV<BN[IlZ=m6#eX.>PobHNj@I*7P7<C!d:Q"i:`(J/*)&+SQGJl]UD>uHTap*`O1iCUQ4gM<?G]c9*N))H\OX@72@Fu$UG"AYdh_P_Rbk(#]n>^$*n<@M+1/U6,P]uGpuA++jecW%U^]te,F)XBpE<=4r63gbJ`r,'3Q3Ou=4/=4m%'JnQ?b[@Eg4Zk_,?-Y6=-X9H].OgAGOT.W^Na'B*b[B4#1ON^Xr`]:;3:O\8[8IKkF+^^Oboe_md6F2!uci)-30l&NDAAAY`4h8__fr[\(Le)moRoiRqB.GW/4(Tiebf]1AYq4jt$=jQ;5jdEFQp+m5QL9W1<GJO<dcXaFT5^(sJ#KR('K6IOl-?O_t=>28+g@h12XR]00rLHGM+8hf-9[@/u<L4o+oPbR;US.\m_W2CD+\Q`oc/8/'\eDa!RU;RRoV;'9^R'Y-S+Y*a)%*l`@qYUE>ibR$^\'!sf^?0gc+Y87rA[Nl7V-&EsA<[Cne0g:FK]NoJPDRBgp^HElk?!X=EZsJXlV(2V?HF0f(VWl96sJsMYNn^87[N-I.QkGFCPZj&>fL\[GOrXn&(bt2$XElW#,SQK].e'(a"^Mpb/gBheYm8q?\82$EM?[RF4pB%?*OYAk<ZmUOT5\G<:PN.6$.7`NSh?GpP,(/]n)W[0DG1LMtDC+NMNsQ*''0RE<,B9jO$X9R=QO0XIc-@$$J4/DG.j[0JSl"HOEE9q>4PTR:8sBVm's#$!bLXqc5741Q@M:D)[&n7(@\BgQ:`*Kbi<Ij2QPTG<g+Qik(.La1kmIm>)h$a3"<jApa14="CW/X5ZUOA?H%?1TZ(%ZW0n+7k%!5F%Y+k"RZtkMS3PtAVsFE_<rA"$F-DoRJ/\1`s9=rHmj%SLI,fiiq6f6m;ugF?e3A81Jr5F@5IfR-\j[AgT-p0]k>n,4iS01FKbF*U'H;fMf<:*l/5^P5`^JQKX;f_fU.fgs/nT.=;ORg;>)mPH"3=3h-g%%rN;0Ul>k%=j*[TL_p^\Z5jLYV$TnO~>endstream
endobj
25 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1734
>>
stream
Gb!;d8TW?O'Y`m7]ZLD5Yo@h$CG@[OB&Z(U49Vm>M_?+8fhirmNL#5_n_`N;,VN;ZP73^+i[np<gV1/*38!l_c$,BQr&oTILE*gSaUG]&>aUIX33g2"9_]fN4^a;WSPT:o*V0NTgrdJ)R3.<JK<t_-!uG4aR?XBJ6$,E.""4^mcRfVXnEiKP^a/Z%l//4ZQnJT!G+%OjiD'/D5AAUTRGV^KC:8%i7QYj-e;C<e17NWA#EoRBDi[+'_&GPX1-3rHKo]5bd:ib_+Tcn2":'1&WEru;&!Jf:4?!="R(C!W'E$cLdh.mWV.VBDGA<`1$N(>;mg6fV#@DRfq&X0l@#'EJ`OO/[TAWlN[P/YF/AdSWO4H\>/2mi<BNJuKj\"8=Y*W2aqoj6N?unahpI*SI/`b+hJ`etnQ>6s<*#\OUA0-'QVNh@d'k4Sb81i*g.5u/!CM*9D<b0m!AQ$*Al.XOgX]bZC=f5:68*8@5qo']bDrT;ZZAXMJLP78n>km64b]T`rNP?[.QPFn',ZAc;+7L)F99iI1`uGIeiK798p:fE)"&"qd^c^4gGf(.:dkR^QJRU!.#tnG;Ha-2l!=kX5l$iZqls,VI:*;W9MaD'7l6YdO`qFRP3X/n6QDA*_%>g2$_Yn5`G**/Q\Oj,fr(F0?Uq.`[VsuCfjfbBU1#;kNB?ZR>bEt4W"kr_D`3m_I`3?4/G$Bf^?[E$]7\dd2OqQcc/n-@S;lY?]4NEtp(jscC0YkYM<$_[`@9S/7%'cm5$8KD/P37R/g,m&+2Bo^0%u>=1:&!<ueaHPqp,83!D$@TJ]>YQ5=@'AP1"puVOVDQ$>j`Bpa\C0lq6pVe;B!J3OG2ON2&5Oj,@q=!&,(aF;q1(86-W.9dl>J4HhU@V$+DADLn36:bI?\5?r^m-Oh[%ePn"Sj(bGMKQY.aPW#PlNKL/2lPeReGeR4\L@8Z`L.etWuaIt%FHZqS5(k+FaN[Grc&Nk[k?^fJC6ka(++iT=@='#drK.^,PL-Y=)!`]Hf.FuIidMAlIR,(^R'']g'*HXah-Ib$eLa^9U:Q#EFbY*#N(McmS3MqV=(`$kK?u[8n"b1o?N#*<WZ\3@mTQ54-Pt?dOFd8,DA;me&:SF?lW!Sm@LJ(X`gS<^1.YLFWR,9+jgsGk.)=Xn!LfB5cUOV+8_dGj5h:u<PrtPrO26/:m-."'g@C^N,fqR^XZr8"K>Es</T5cLZL9(XUD+&A3Bh(@]:3;AI*]0Et4":k:o6`$gf!6>4NB0\1k%5GX$;;M-T^tHlMC)na@[\O6cEt!\#FY;N6<1$&FJAof:7;ok0!>_C2pE!/e![V0ocUTt[;BuH;0f8[579^GKXO"cP[UGP'Tt;TbK@>NL:pfchJXoaNZ\8+&iZtlGKb&N?$4rlnWgj,<_N4RSE2FX['"9U6G"a!I3)HuItNE&h$s&(%3<_'fu!?!N$I.!W#)F60m,u'f*c^fbmHLu!Zh!ScL7=o@mr2u`t'C5O<CEhB'fDce,D4r'QnF+ruA\*$^1R4*(qog\D;)3-GPY:II9Akn)!"!Wt"gD_I$a!4Ll$*^-713Ni5U27fSeAUP%S`<LMQH0*IW2CAb7ZopOr-3`=d81mdVf"Lbgrb<(,/0ooR]ef9hXV65^^>OtdAXp>BWd\>%30FG^sj71o`X;R!g9p]]lfF0rREF'4b3rVRgj$&Eb]`,K'W:iD&H)r[BH]5;do7%X:A$"t7%?Y&s#>>k^3W~>endstream
endobj
26 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1741
>>
stream
Gb!;d=``%_&:W67kR+^t$&ch[RZU)1ma`.4.09f`i5I@%4Ea,7+iuVn`IHdr/LN^pW!X(tOk!Lih-]o0!Q>j*s*>j(K/!e,0OQ5dB>=W^0Kt'.A)ib=9\YfK!l>u#>n`%;M]YLQ7Kf#OCtEbH3)gM\$4m[lNoN0.,+k)n6E4I;f3gk>9r0(b36\ZKq[,P8[V;IN5qLc60D!fh3JD`2Z"r[Pbj(2)n-N,WeOnqT:hE9,`ZZ>94FMD^cf2L\]^n)mi,,'#mlZA^BhGmb%$1$Y#B?Ke,8bo`n_HMt:T1K<FMX:/6;M]hjmfDbEi.uJUd4VV]YXq4&o=m4$Nj!p$\RMo#%<b,2MS-C<adVt@phFh6);+()FDRrZn3j`KL6-eNmu)+-<*gpV`;pWT$bt'ZO#`]NM'(2`"Icq=u^#0oh]Ei90lG29jfY!*N/Whm=`k^QY4[jQiM&.>AO5`S*m%aS/Z#['sB+C_^>N@Z<eu"lAFdObU'F69H.5C#$[>f\c]uMQE).\SE,D0A&0aJ<F50B8G*>fM@JPf:n[r@3tNn1oZ_86[S)9m*4+4UKsL=c9]K+e`+fWP("EAB(PfUk=[aGnB8*;O:C2s3>WT:,ZNXO6+H34EO2IJ$a\B*NXF3bLmdd+i3?NG9RohdA_d;=%[*fO.0sQMK\R&kYr@C`E0ZZQhrlA=XZa$tjf55jB_2D<$FnrV/(DP$O(d9D<V_"dPS.8mZU,@(_'af\$@Z$f8b^pLp=r-E_Z`jaG[Ua52$X#^D=jU!&Ao;P@Ao9')'1P&h\(o2sl7?4/f.GI#R2&P[lVr/?O2qjg\]V2#!D+H/GYg[eU\#'&iX;T7]p6W_>6mGsH1):jE$W,BoR),h$JQnf7=TjijCZ1!E))i1(Q([3mgTh7N=Oea)S?n'%1b%$h=dB&eOC/t+fOY47d#?n#>#gerTW$ZYV@/g<LEgXe%rOeQuLZ44/*J!BlCLk-^P?d_nDb._nEVFL3l:]SM8d^]&f_&qZW87/[OSp8Y"r4'tdMH>b>'dn-ruD*5]PB<$(tF)_Do7#dZ+fHfV?s(LE&I6F.s8ZY%;>nEUOL%Y,'>dr/TcN8WVVc]caq(rW!QcJpGt0hd09Wl.-3GD#kSgZ3%/_`-I-.XtBf/V4u@<@d1o0\6orC9GVRF2$8l[ESfR?"Hu*E,p3M!rKgpBTKK8ZN1'4*%!\#ml^aLJ,[IoVAaFFT1rsVVuZdtgqU4Vf!bU.pVjM#':2%s&J4H@"b[tg$h%(0Bq9.18=ObF2;NM'/)T*HdH^X2%A>aV0c<"jD,o)W[$YY%'[cL;VNG"+enbk^%DRN.)V_\A1Tt6PKLr`)8C!%1R)=6]G/o09$U7;_V=M?"$c,g($C3^^'NWaS%=q.B3.]0&q,RT:\2h%]-,4'#07!@)JYdC,?FIf*n\@N?%]<!HHVS^pDWK=3C_lf.Y5DJfoE(-tNY)&,e&+U-g6jeh(>\j?mnOF(US]`38mO[tBfr+:n7%LDZp;,;b.FDHeMad";njD+?E.dN7;kQ)&Q\gg0"NO:'%<9&M\@'o?`Cie2%II;iT]$?@)3Bq#&8^aB8LR7Gg&qbrV?3UWb3fPY#MD/*7TE<)i)_f#CjVVq_'EP0*:N2pMbm@W$Q#H`c41&qOP7cYthW1_aQ;0S,_Ch:ehf7gc5HObqTXX^nOUH!3Qpa,.#,GmG19^?rk%en-"XW@s3k2PKd!Me,K7+;fm3ZYeg'nZ`AsH)#j\ta<QB~>endstream
endobj
27 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1901
>>
stream
Gb!#\gN)%,&:N/3m.a.I/VjBm!-G+pFj_O0Rl!^3U^pHI?j`&.o:h>R>pkhcn+%Zcer=[<EGN[n"YL;1/f)'SU>XsfQ@FMmi8FgY:FK$DP;uH73)#p]j2p5sJ2"ebpOKcrEIA&0GDdP6(P<Pe6%mR3$HeK!8If!&U6Mj3iB/QgK[dg8_12TL_NB]X;:H7OCs@0\?jrCa`"U#FS6tkd"[/K6RN9hXh?\iO[!&H8FZFH?-U!hb0`:>"]4^VSZ7oFc9TftDk,AZQ3D:<SE:s]h105er<[Ktaaa9C;-l2#sEhUh`+`Dus2d)Foo*XQD?J&A4l2\K:#]A$&^*(R>3(=s-46>kPK5qKmEXu9Y"7,ai>ZFKYYU:I2%e,)1p^(/7QFf2J;CQN]m#lH>nL.DEZPHO/4lGrjpoDnNl/6.fUc,]5%RI:!),,fYUVhHskgZ+fBGNY!*q/3@g#Lk[U[;6/EqJI@*no73Nk&+K3*W:V3F_$f@0c8K^TV\H;3L%9betB:j->0dB:0eUHb$QBl]$T%>GjKQQo<1qX^'VnmWXq_FAtip9ZO-(@;quPB#;p5\8=q+FhTkN*bSagLM@^P_c1:VfSK'pFqq47`YZ6\LgSXgFDBi>:QhdqQ`I=*Ac*Zg=]!a_60UdAi4Uo&_\YaDG8EFm0UU)Y)r!=FUY0ML1.djgACdUg9pW[sP&tL)B"dg1Zbu,]:r6%!R\-VO*eLf1p[hl)^"Vs,>aWU"p]N39A)lC/"F.5Zrbu^`pQY54(:C^1bG$nBKV:WHXj^DcA(fMT!n4N\Nbk#Z3ZM^n%`;5rbGLk#qh%dNQ<SNPX[AQsS&6)K^"Y,r]kd@43dkNgF7/[@W=1/rWb'VH;4R5bnFo']'r@:U#F_?nE(c=en;,!N:C"gjlh-*og1,`,6?oiOBR-aK:eHXtZ.jdXK$&c62(4-pg>L.)]^%CsY7s!u.`7Fbj4V9`qP9>mmn=XeYk[umkN5t*^j#8r9J0uL&6g[_Oe`d?D//_4m+L5.g%+6"Qg_[=dp)IY@!Qp2q7CdKa&CTb0pm3o8o.at)Sgu6@$N'?()o_4i#N;:FYiQ%J%orjkchUM1m@kik*pMcoig+#'hf"i#q$/=`cc7E%*i0Z=j_OPXHG*G[X#)n6O/aq&\F8f_,A;]F$S,tir0"`?L*UPG_2?'Y(MYL)&m5#K:l)d]j-sBF*O!U@rK_\GnTAsiaW'3217JH'SZQRU#.R3KsH>AQ.sB/7(8>(G8pZ`m&QiskUUL;I?%qig(4Sj#M"8nkC5?JW+BIE2#]Jlm<N!UcHXe)'RQOaa\Ld-I1NS?q'#M$?.7\RkB4"6.uZ'`cS'.&O)0bI,[2R7L.Asfr^DFrpX!"+PeE8WL\_'6Ec\@W;(1#sb#[m#3:9-ji)3UX\JYb[>WKdAG,XZ_JiMPJh%@''#mTe+%u_*\d\)8.`]3P]$cuY)1T^8G;8]%E6)aKE31rYhe[b(S?r"=7JGhgB4ub.GH@G0Sqen/j%^n2UY=5]`e@$q%M9dR`GQJl%*PW6@2HhDoX.S=M9&@lJ:Ql'EIaiP*bq@#dnBq!BFk7NEeJ.OmRgR4=<`0JCC":)\1&PQ\'BHAOkNShVkZL4@10Mc1!N'"+jP+75_[iED[4PcqPG3;hR(r7Wn5Bjfa"Xm+U&n>G3iumG*?O2"Gc^:WZ-#R!3LV"]oUqlj;Mt<.)YV!\Q(Po*+loS?.]PgBMMfL<l#C('W?dLGq?$&/KF4mhoF#KUo=B\t`qS:e8+UX5AirD_a6Nt`NA-PRUZq9o_^NAL]K@SW$Ga[\oRtk\Uu4MmkrR+VBsP%\3%4[p#bRc!K36(X"61F7Y#I"(,"WQD).R$XYoPRYNDNJbb<?ot-.6@@%J3_?N\RV)`Aa8nO^m_CNYHh'JAFHXlMW"~>endstream
endobj
28 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1887
>>
stream
Gb!Sl?ZV]+&:`<0f^VVr,W`h_^l_<'i\\('NnhX2lFc?B"=G)Q":CZp\s[s?mK!C2ObDQ9\4nR0Lu\4/mdG>(_8;7=r"!E?D[(0$AqNfd0F"!Y#(o,;4Qq1\&F*,b\Nh-P3<99r39CIO8LFPj,@3V;)n=uCmPA8+-Cb;\.+7q*m6`4/r2aB1nNG(Y$ja=JO*fd242dm.75NqL?>uONdZ5@?!od56Tpk'r1;b,UAQ6q)@DoHWhhAlT]"e5fI0&fbM9Nl,1fa%Wc=D0p@7[@.V\ei'/u;2'1-:6,/I\3g!?4_#a3b%9he(T/H*5V9alL.&`/0"PK>A:KE6Z4oQ!1mh$$RtUSi7.f98DB=DVJ\&1BL@tN/ri=ET;`?:F]X<@jTJrT@.O*ZQ*i8;IHs@p6H%&f&Cn79M>kq9T2;M^OMY=3ep2u%lq^E,%9-#Y4WbYTg^@CD7!Hf9WDb@haYLn!e#X3PgD/lmiYoY9IS>lXbXW7"(#Y6Cs*;Nbbt;@k,$1t%ON<l0!5F5lS.3LF0T%M*QBZufIYPZlOf%XroOPpmg2)KRR7H0lT8WeA'M*c3Zcd;nN50eMsAs5kIap&C)Y:b$9M957c@(D77GYgfmF5j2k?\=bbpEPaVf`(Llb&M<?)s/$qNX#lIAt"\as>q!tduk>/\*:R\#1T1u%R;lPHUh#QK64!h6^LGJ27o":jrBmX'(U!k6l_CJ_4%#h:kVSE^ii@:hC&SiCCi5T&OO(/^!&o/+qc"]f3>M\s1F3(mn'oPGS/?L<KQ2.tMg/or#eE8J?f\\rQ:?AK-@,hE=Dq*Jfj&]@;[)C@5U3nA<os3S[OQbtR!j%r?@Fo\j*P3QUgc*2Qe3ZOd9U_Y=MM4*Qf`:!.m01Ne2,1K-#itmIs:s9#_G#T+:ka]24#tKZ1UDD;&qY3*6IaqU-Y#Q9)4,VI''YQ?#k:e[S;*5j*OhKP.K_:2AI't3G.NYe"WCCT*+5UdZ%OQ+,LQD$3kWH5#J$5"F(5QgKHUHBENYN4GHoC=dQ&HB$%[/G<p$d407"eP=FCmD9*bEo6N0u3G$b0B^jFA]/H9+tC=cY?"dnQ?BK7jh?VK"!p7YX+#21Nf:O'7L,KX^Jr]ZB+i6*'"5Bho;dG$&pqY;p#nS4uS(/%?;FR'r\8[Vol_bHc'YlXJ]Y*Ts)*oX/O1&TBrfA?PNf85'<%7B"![F+bAZ'UX,=j="6Ndg'tmN:K]]qF@[N-]oYeV45c6*P%[lXSdPkf==LD!cprWk:c,gFsN>D-<Y^XrP%gFUhDEJ8KR2BBj1FS-=r=/3FLF_%\pgh+4ST49OK<5.*Y#Ri=oLUmhs]ZAJRlAOdrKE%WWQ-VAM+S:>ui4!a@Jpan/F9k:fLJgGS9_IVsGsk7@<G=*pe]l=m4\-a8>jPuL[r)FU9./S!THDNqJ%4W`dpKfQ$l"C5*QnrZVC"h'Gfd#1R]TJtmNV6cg8XqF)<ct9ZLU%RaG,h79=m.'kR4X;^7:_9bt?.W&7[<'>kGs(9*CLul$[&Sg5gM,dc?[CA%iY8*\:Q^X@HXQ_^?[l+rF8$eBR7@=uJpR/14EgHEiVauP3$?su9%g+D]g#FUh<G)dMQ[<08-L)1cZa:t`\](]V`O2sYi8(S3'2[^KY#?2NNB(^@tP&6AQ\&pY'2(HncJ^KAA6E-]IU!tdjXWoc&9uUHRWq1dr@/-M*.-T5hYFtMYLZ:ZgAFcQBU53.9'*`1C6%Z0$UW"pDR>\W+ZP`&4I%hK#R!OM_+qb''PRbH_88M&`32t$kH*c^#lG!MnFqST:5`WroH<UA_bjR8"N8*(ECW]31mZK]qkV[]WU"8;eQa0(]:na[ZGRq+EUJQ1T,J?b7VtV_g.$IYc=sBf$pM+<Ud7S6_O#eW"g.M~>endstream
endobj
29 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1685
>>
stream
Gb!Sl>Bed\&:WeDbW5R)Ba_qdNA#]0Bp4i7V/9VPQdZbi(.O.C=NM&BJ%t"O"G.eYc,hCD&0DCFB'/u&5&!F#`"sm%LJr`P&[]jj&.:s;+HGErW"3O9`]jpME\5W%J78>P*EZ\PG5_^rD@#I9L)X4q!T3MXhfHU'M;e>_%i3I^4Q:p$0Rh[t@!Jj=q$n6*l+"I>1#Flob<j[<d;e"#rqso'5Beb7gr_^#Z[CeB3.b*q/Q]>#_^$8+"/@<8$pK1,_$:i6*DWD(i:g3UE0A(^bOV=\K6Q[:-$%#T$Q*0ck[eX>APpKj%%I%KqP&D8FXkGn,%#$-"U/A<XRHrJh!iKC/]7%E%RU1,_BqTqLMG"QQ@S,$C5D!<*)_<BfU;uS<X%>PCbF@@&^ED3Br4uq!g_jbJ`?lk_5Bu)%&PeS#u;F7=([l>P_EfQ_04!fZ.:;&22,/<kSbn#O=#g5^l*6m=VS344li7hESAR2*;JF\F4Dt#moq[oCgdP%75"Fm3F'48(`ets0V#;AY6,(>OhIr6%(]KYTGr-c-m:U'o`Mj73?Y4:b4g4V,.Vt17HKfq'QnUTi!f5*mZ@>k+O/C.7S3gi$e&[mKlVL]"_5\WbI2_0>g,=VZaP3rS'(go'6;?C[r`ZIX/J[lF?VD:Jn^XP&7'^Lq`2n(:N,uH'?ak($W:0As4d[L<Id"\HpKFB<b=A@>7E7OZ"(?qL@6MY]t1bDmt:@R$#f?VQ1"+Zo<p\o';IoKPErX8(G(o`>BV=Ii'au2]'/nQVf#r(/u4[dP:frOn[&T7NEO-%Y'!Uk/88\%Lo*9'o+Ah%Mgo1$_"^bGc9to:$Co:5?q=O[H6h?e*D.&(@A[S"KpY!)YmdsE#h].J+gA1n+9Hs:;5>n9d?)o!4d/`6Fc+_\7,Urt(@?q417p5fKrkI;rfG+?HOT'SpEHj<pks5iQp:X`'Me_@#X;-1s&\!::&*\ZO1PMn`_`4i?SsMY1U5o\enrb'r$PG,dmV4O"sGtLk'MokA"&Glkp$b=+eM@-J++`BHCHlgdWD?n\(`qc40OAW,!RNHr'Ks1cm9n4rPd)8<c`FiDGpqr3c:3$9c.-n[Nt#;3NN`OU-,si_<*deBtDlh*1]2$;hYp@';f[5K@Rqk)!,N7\JELn^(Fk5FY]^SWla_3C1V`fGFff!H&d:LcP?sW4@Z,n.$j8qO&N.C-h4tVjioY_1R_'VS+&nnDu\'(F@u)N($S-.)%X>Br:YEU7(7rB[E_8V_K>j,QMG(`IET7#Lbh^Q#1lNS^'B.RqoBf@#^#cM73MNQPf+%F52`c]+9fq&:%4:jV*`kKTn8:jA-nR_?SWR3Q`LKJ(+n-"GFp)kj0"1PS&6A'\(JL]WqY13V#4(u^8"e=T9/#?3B:\K?99VIqHpIBcQfNP^8/=H'.P3^Op;sK/c-t9HniWM(Ai7LA8Njl\Oq.T_]<@<Blt)u_;"Il$!3@kip_'FFZMcZl?sO.U%c!F;'2oLRSaep*phg7pu96Zo9*k<fMC>[?o[Gc5HBii^`oY=]I<NX8E)Z7na,X%K>d>I"9.c6i2Ml+@$b/DRpT_n,f;d&f.VQZ%mNS_pmGB0lSk!%o"K\pY.57M2[E_HJ!@EaFDXG4VO_3uI;`Plce<V](-5EB[_1NZdY+eXiDUXNLtO$3.&OIm3`EC$Og6C.nK3h6-1V~>endstream
endobj
30 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 705
>>
stream
GauHH9lldX&A@sBm*X'O";4$6\b.1b1QuOA$r&Cc-s0k\@C^t8dsk^J^inut*TmFNE7++.pYKY=+`n+hIoZSt80M)C^]`(*0LZ](CUd&N^Lr?V_J'E,&n."95Y9IS@#N\V@YRuO&e#uF88no8?mJ,aJ@I_e0aFa%5FZ(HS:t3E5Q\u"Y_2iZ0g],ZMIOSgJ+J#9U8=2!f!BES]C!C="R\]q;'Urb*:k#q$Kb]6m]YQ,.9)k?N7%..Yprs8n3@87pK-iU4Y(A-b.`N#-_tBlJNZiP.#P7eY<f?l>rO,!5eb@7>YDkW&)l)BGef4LMF8F^B>$A:rGa4$2^.ff+PRY?q8,FQ?-YuBp,Igm>F**W%%t><o*<M8.usKX,E-)j"l9tU7-nl9\;9e3(,3MN>#l7cZW+L?>s;G+s'5>F4*bmL29i#4#Qj&(;.ci$O$-*0J<N$^rh7o"o'_jgP\tEhBA'4"3?nFTLO/2XPLY48h**L'Zk4*6S^)q_eql#,eE1J5C[&k5LU"UYXdp]WSQHB>=/0T$l&BuQZHm)B@S"_?1M!G,U*0oDqiNl+67t-9GG#<_OW0/Aj:8RmP^B;d]$/r(ri]us7(2J(&PXU"qT'U_^5Z=.\\D'@2eAhGCYiaXjMk6A?2:lZG:u5O<%_2<4R!Esm$Q.Z2jnZbh+Y#Ujl8o#Q&j<F#*n>$aK8f'q[5&\"I/~>endstream
endobj
xref
0 31
0000000000 65535 f
0000000061 00000 n
0000000122 00000 n
0000000229 00000 n
0000000341 00000 n
0000000546 00000 n
0000000623 00000 n
0000000728 00000 n
0000000933 00000 n
0000001138 00000 n
0000001343 00000 n
0000001549 00000 n
0000001755 00000 n
0000001961 00000 n
0000002167 00000 n
0000002373 00000 n
0000002579 00000 n
0000002785 00000 n
0000002855 00000 n
0000003140 00000 n
0000003271 00000 n
0000005333 00000 n
0000007375 00000 n
0000009477 00000 n
0000011349 00000 n
0000013297 00000 n
0000015123 00000 n
0000016956 00000 n
0000018949 00000 n
0000020928 00000 n
0000022705 00000 n
trailer
<<
/ID
[<18c18010543f3b7d15dd1d9c569e1c22><18c18010543f3b7d15dd1d9c569e1c22>]
% ReportLab generated PDF document -- digest (opensource)
/Info 18 0 R
/Root 17 0 R
/Size 31
>>
startxref
23501
%%EOF

View File

@@ -0,0 +1,430 @@
# Les 18 — Advanced AI Toolbox
## Slide Overzicht (Klas A — 3 uur fysiek, demo-driven, **laatste les!**)
**Lesvorm:** Tim demonstreert klassikaal. Studenten kijken. Korte demo's, geen lange theorie.
**Demo-app:** Vijf kleine demo-apps — voice, vision, image gen, local LLMs, edge AI
**Vervolg op:** Les 17 — AI Production Polish
**Aansluit op:** Eindopdracht (thuis)
---
## Slide 1: Title
### Les 18 — Advanced AI Toolbox
**Visual:** "Les 18" BLUE, "Advanced AI Toolbox" BLACK, subtitle "Wat er nog meer mogelijk is — voice, vision, image, local, edge"
---
## Slide 2: Terugblik 18 lessen
### Wat we samen hebben gedaan
| Lessen | Topic |
|--------|-------|
| 1-10 | Foundations (Next.js, TS, Supabase basics) |
| 11 | AI SDK basics |
| 12 | Tool Calling |
| 13 | Cursor + Vercel deploy |
| 14 | Agents |
| 15 | RAG + Embeddings |
| 16 | MCP servers |
| 17 | Production Polish |
| **18** | **Vandaag: Advanced AI Toolbox** |
**Vandaag:** alles wat we nog NIET hebben gezien. Demo-driven, korte introducties, je weet wat mogelijk is.
---
## Slide 3: Planning
### Vandaag — 180 minuten
| Onderwerp | Duur |
|-----------|------|
| Terugblik 18 lessen | 10 min |
| Theorie: het AI-landschap 2026 | 15 min |
| **Live Demo 1** — Voice (Whisper + TTS) | 25 min |
| **Live Demo 2** — Vision (GPT-4o foto-analyse) | 20 min |
| **Pauze** | 15 min |
| **Live Demo 3** — Image generation (Flux) | 25 min |
| **Live Demo 4** — Local LLMs (Ollama) | 25 min |
| **Live Demo 5** — Edge AI (Vercel AI Gateway) | 20 min |
| Wat nu? Resources + community | 10 min |
| Vragen + afsluiting cyclus | 15 min |
---
## Slide 4: Het AI-landschap 2026
### Waar staan we nu?
**Modellen (eind 2026):**
- **OpenAI:** GPT-5, o3 reasoning, GPT-5-mini, GPT-realtime (voice)
- **Anthropic:** Claude Opus 4.6, Sonnet 4.6, Haiku 4.5
- **Google:** Gemini 2.5 Pro (2M context), Gemini Flash
- **Meta:** Llama 4 (open-source)
- **xAI:** Grok 4, Grok 4 Vision
**Modaliteiten:**
- Text (alle modellen)
- Vision (GPT-4o, Claude, Gemini)
- Voice (GPT-realtime, ElevenLabs)
- Image generation (DALL-E 4, Flux, Midjourney)
- Video (Sora, Veo, Runway Gen-4)
**Modi:**
- API direct
- Edge (Vercel AI Gateway, Cloudflare AI)
- Lokaal (Ollama, llama.cpp)
---
## Slide 5: Waarom multimodal?
### Tekst is niet genoeg
**Wat krijg je met multimodal:**
- **Voice input** — handsfree, sneller dan typen, voor mobile dominant
- **Vision** — user upload foto, AI analyseert (defect-detectie, OCR, beschrijving)
- **Image gen** — assets ter plaatse genereren (landing pages, social media, avatars)
- **Voice output** — voor accessibility en cars/IoT
- **Real-time** — conversational, geen wachten op response
**In je apps:** denk niet "chat only" — vraag je af welke modaliteit het best past per feature.
---
## Slide 6: LIVE DEMO 1 — Voice (Whisper + TTS)
### ~25 min
**Wat ik laat zien:**
**Whisper voor transcriptie:**
```typescript
import { experimental_transcribe as transcribe } from "ai";
import { openai } from "@ai-sdk/openai";
const result = await transcribe({
model: openai.transcription("whisper-1"),
audio: audioBlob,
});
console.log(result.text);
```
**TTS voor uitspraak:**
```typescript
import { experimental_generateSpeech as speak } from "ai";
const audio = await speak({
model: openai.speech("tts-1"),
text: "Welkom bij Polderfest!",
voice: "alloy",
});
```
**Demo-app:** voice-chat met Polderfest. Mic-button → spreek vraag → Whisper → LLM → TTS → spreekt antwoord. Volledige loop in 2 seconden.
**Realtime API (kort):** voor lagere latency en interruptions. Vereist WebRTC, complexer maar voelt magisch.
---
## Slide 7: LIVE DEMO 2 — Vision (GPT-4o foto-analyse)
### ~20 min
**Wat ik laat zien:**
```typescript
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
const result = await generateText({
model: openai("gpt-4o"),
messages: [{
role: "user",
content: [
{ type: "text", text: "Beschrijf wat er op deze foto staat." },
{ type: "image", image: imageUrl }, // of base64 buffer
],
}],
});
```
**Demo-cases:**
- Upload foto van eten → AI noemt ingrediënten
- Upload screenshot van error → AI legt fout uit
- Upload handtekening → AI beschrijft stijl
**Use cases in productie:**
- Defect-detectie in fabrieken
- OCR voor formulieren
- Visual search ("toon me schoenen zoals deze")
- Accessibility — alt-tekst automatisch genereren
**Cost:** vision is duurder dan text — check pricing per provider.
---
## Slide 8: Pauze
### 15 min
---
## Slide 9: LIVE DEMO 3 — Image generation
### ~25 min
**Wat ik laat zien:**
**Flux via Replicate of Fal:**
```typescript
import { experimental_generateImage as generateImage } from "ai";
import { fal } from "@ai-sdk/fal";
const { image } = await generateImage({
model: fal.image("fal-ai/flux/dev"),
prompt: "Een poster voor Polderfest 2027, retro stijl, oranje + cream kleuren",
size: "1024x1024",
});
// image.base64 of image.url
```
**DALL-E 4 via OpenAI:**
```typescript
const { image } = await generateImage({
model: openai.image("dall-e-3"),
prompt: "...",
size: "1024x1024",
});
```
**Demo-app:** form met prompt-input → generate button → image preview + download.
**Use cases:**
- Landing page hero-images
- Social media posts on-the-fly
- Avatar generation per user
- Product mockups
- Marketing visuals
**Tips:**
- Specifieke prompts werken beter (stijl, kleur, compositie)
- Flux Pro voor hoogste kwaliteit, Flux Schnell voor speed
- Cost: $0.02-0.10 per image, schaal-bewust
---
## Slide 10: LIVE DEMO 4 — Local LLMs (Ollama)
### ~25 min
**Wat ik laat zien:**
**Ollama installeren:**
```bash
# macOS
brew install ollama
# Download een model
ollama pull llama4:8b
ollama pull qwen3:14b
```
**Run lokaal:**
```bash
ollama run llama4:8b
# Interactieve chat — runt op je laptop, geen internet
```
**Via AI SDK:**
```typescript
import { createOllama } from "ollama-ai-provider";
const ollama = createOllama();
const result = await generateText({
model: ollama("llama4:8b"),
prompt: "Wat zijn de hoofdfuncties van een AI Developer?",
});
```
**Wanneer lokaal:**
- **Privacy** — geen data naar third party
- **Cost** — geen API-cost (alleen elektriciteit)
- **Offline** — werkt zonder internet
- **Latency** — afhankelijk van hardware, op M-series Mac vaak snel
**Nadelen:**
- Kwaliteit < GPT-5 / Claude voor complex
- Hardware vereist (8GB+ RAM voor klein model, 32GB+ voor groot)
- Trager dan API voor grote modellen
**Demo-app:** chat-form met dropdown "Cloud (gpt-4o-mini)" of "Local (llama4)". Vergelijk antwoorden side-by-side.
---
## Slide 11: LIVE DEMO 5 — Edge AI + AI Gateway
### ~20 min
**Wat ik laat zien:**
**Vercel AI Gateway:**
- Eén endpoint, alle providers
- Automatic fallback bij outages
- Cost optimization
- Provider-agnostic key management
```typescript
import { vercel } from "@ai-sdk/vercel";
const result = await generateText({
model: vercel("openai/gpt-4o"), // routed via Vercel
prompt: "...",
});
// Of fallback chain:
const result = await generateText({
model: vercel.fallback([
"openai/gpt-4o",
"anthropic/claude-sonnet-4.5", // fallback if OpenAI down
]),
prompt: "...",
});
```
**Cloudflare Workers AI:**
- Lokale modellen op Cloudflare edge — ms latency wereldwijd
- Veel modellen gratis tier
- Voor: globale apps die snel willen zijn
```typescript
import { workersai } from "@ai-sdk/cloudflare";
const result = await generateText({
model: workersai("@cf/meta/llama-3.1-8b-instruct"),
prompt: "...",
});
```
**Wanneer edge:**
- Globale app, latency belangrijk
- Cost-sensitive (free tiers ruimer)
- Geen vendor lock-in
---
## Slide 12: Wat nu? Resources + community
### Hoe blijf je leren?
**Communities (2026):**
- **r/LocalLLaMA** — Reddit, dagelijkse update over open-source modellen
- **AI Engineer Foundation** — Discord, productie-focus
- **Latent Space** — podcast + Discord, breed
- **Hugging Face Discord** — modellen + papers
- **AI SDK Discord** — Vercel's eigen, direct contact
**Nieuwsbronnen:**
- **Simon Willison's blog** — beste AI-blog 2024-2026
- **Latent Space** — wekelijkse podcast
- **The Rundown AI** — daily newsletter
- **Twitter/X:** @karpathy, @swyx, @sama, @AnthropicAI
**Voor diepere kennis:**
- **Karpathy's Zero to Hero** — neural networks vanaf nul (YouTube, gratis)
- **Andrew Ng's courses** — Coursera, structureel
- **Hugging Face NLP course** — gratis, hands-on
**Conferenties:**
- AI Engineer Summit (jaarlijks, SF)
- NeurIPS, ICLR (research-focus)
---
## Slide 13: Eindopdracht reminder
### Volgende stap
**Wat je nu hebt:**
- Volledige toolkit voor moderne AI-apps
- 17 lesopdrachten + huiswerkopdrachten aan deelgenomen
- Eigen mini-projecten gebouwd
**Wat nog komt:**
- **Eindopdracht** — thuis bouwen (geen klassikale werkdagen)
- Eindopdracht moet bevatten:
- Next.js + TypeScript + Tailwind
- Supabase (DB + Auth + RLS)
- Vercel AI SDK met Tool Calling
- Externe API
- Deployed naar Vercel
**Praktische tips:**
- Werk er gestaag aan — 8-10 uur/week voor 4-6 weken
- Push klein en vaak
- Vraag hulp tijdig (Slack, mail, office hours)
- Begin met je voorstel (in te leveren via Teams)
---
## Slide 14: De afsluiting
### Bedankt en succes!
**Wat we samen hebben gedaan:**
- 18 lessen, ~54 uur klassikaal
- Van localhost naar productie
- Van simple fetch naar agents + RAG + MCP
- Van mock-data naar productie-app met observability
**Wat je nu zelfstandig kunt:**
- Volledige AI-app van scratch naar productie
- Multi-modal features (voice, vision, image gen)
- Observability + evals + security in productie
- Lokale LLMs draaien
- Open-source AI ecosysteem navigeren
**Voor je eindopdracht en daarna:**
- Doe het ding waarvan je dacht: dat kan ik niet
- Vraag hulp + deel je werk
- Blijf bouwen
**Bedankt voor jullie inzet de afgelopen 18 lessen. Succes!**
---
## Slide 15: Vragen?
### Open ruimte
**~15 min voor:**
- Vragen over leerstof
- Vragen over eindopdracht
- Vragen over je carrière in AI development
- Feedback op de leerlijn
---
## Slide Summary
| # | Title | Type |
|---|-------|------|
| 1 | Title | Opening |
| 2 | Terugblik 18 lessen | Recap |
| 3 | Planning | 180-min |
| 4 | AI landschap 2026 | Theorie |
| 5 | Waarom multimodal | Theorie |
| 6 | **DEMO 1** — Voice | Demo |
| 7 | **DEMO 2** — Vision | Demo |
| 8 | Pauze | Break |
| 9 | **DEMO 3** — Image gen | Demo |
| 10 | **DEMO 4** — Local LLMs | Demo |
| 11 | **DEMO 5** — Edge AI | Demo |
| 12 | Wat nu? Resources | Reflectie |
| 13 | Eindopdracht reminder | Praktijk |
| 14 | Afsluiting cyclus | Closing |
| 15 | Vragen | Open |
---
## Bronnen
- **AI SDK Voice + Vision:** https://ai-sdk.dev/docs/ai-sdk-core/transcription, /speech
- **Ollama:** https://ollama.com/library
- **Vercel AI Gateway:** https://vercel.com/docs/ai/ai-gateway
- **Fal (image gen):** https://fal.ai/models
- **Replicate:** https://replicate.com
- **Simon Willison blog:** https://simonwillison.net
- **Latent Space podcast:** https://www.latent.space
- **AI Engineer:** https://www.ai.engineer
- **r/LocalLLaMA:** https://reddit.com/r/LocalLLaMA

Binary file not shown.

Binary file not shown.