# Les 15 — Huiswerk ## Eigen PDF + simpele chat-UI + RAG.md **Vak:** AI-Assisted Development **Opleiding:** NOVI Hogeschool Utrecht **Deadline:** Voor Les 16 — MCP servers **Inleveren:** GitHub repo + `RAG.md` in root --- ## Doel Bouwt voort op de **lesopdracht** (werkende PDF Q&A app met 1 PDF). In dit huiswerk: - Index een echte PDF van minimaal 20 pagina's - Maak een simpele upload + chat-UI - Documenteer 5 vragen + 1 fail-case in `RAG.md` --- ## Onderdeel A — Echte PDF + UI (verplicht) ### Stap 1 — Kies een PDF Kies een PDF van minimaal 20 pagina's, iets dat jou interesseert: - Studieboek-hoofdstuk - Open-source whitepaper (LLaMA, RAG-papers) - Bedrijfsjaarverslag - Wikipedia-artikel als PDF geprint - Game manual Moet text-based zijn (geen scan). ### Stap 2 — Upload-UI `app/page.tsx` — file upload + lijst van geïndexeerde docs: ```tsx "use client"; import { useState } from "react"; export default function Page() { const [file, setFile] = useState(null); const [indexing, setIndexing] = useState(false); const [result, setResult] = useState(""); async function handleUpload() { if (!file) return; setIndexing(true); const fd = new FormData(); fd.append("file", file); const res = await fetch("/api/index", { method: "POST", body: fd }); const data = await res.json(); setResult(`${data.chunks} chunks geïndexeerd`); setIndexing(false); } return (

PDF Q&A

setFile(e.target.files?.[0] ?? null)} /> {result &&

{result}

}
); } ``` ### Stap 3 — Chat-interface (simpel) Geen useChat / streaming nodig — gewoon een form + lijst van vragen+antwoorden: ```tsx "use client"; import { useState } from "react"; export function ChatBox() { const [q, setQ] = useState(""); const [history, setHistory] = useState<{q: string; a: string}[]>([]); const [loading, setLoading] = useState(false); async function ask() { if (!q.trim()) return; setLoading(true); const res = await fetch("/api/ask", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ question: q }), }); const data = await res.json(); setHistory((h) => [...h, { q, a: data.answer }]); setQ(""); setLoading(false); } return (
setQ(e.target.value)} placeholder="Stel een vraag..." className="flex-1 border p-2 rounded" />
    {history.map((item, i) => (
  • Q: {item.q}

    {item.a}

  • ))}
); } ``` ### Eisen - [ ] Upload werkt — file input + index endpoint call - [ ] Geïndexeerde docs zichtbaar (chunks count) - [ ] Chat-interface werkt (vraag/antwoord) - [ ] Antwoorden zijn duidelijk gebaseerd op PDF-inhoud --- ## Onderdeel B — `RAG.md` documentatie (verplicht) Schrijf in repo-root. ### Sectie 1 — Document info - Bestandsnaam + onderwerp - Aantal pagina's - Aantal chunks na indexing - Chunk-size + overlap die je gebruikt hebt ### Sectie 2 — 5 succesvolle vragen Voor elk: ```markdown **Vraag 1:** "Wat zijn de drie hoofdkenmerken van X?" **Antwoord (samenvatting):** ... **Top chunks (top 2-3):** - Source page 12 — "X heeft drie kenmerken..." - Source page 14 — "Het derde kenmerk..." ``` ### Sectie 3 — 1 fail-case Een vraag waar RAG het **niet goed deed**: ```markdown **Vraag:** "Wat staat er over Y?" **Antwoord (incorrect):** "Y wordt niet besproken in het document." **Wat ging mis:** Y wordt wel besproken op pagina 8, maar onder een ander woord (Z). Embedding van "Y" was te ver van embedding van paragraaf over "Z". **Wat zou helpen:** Synoniem-expansion in de query, of hybrid search met keyword. ``` ### Sectie 4 — Eén observatie Iets wat opviel: - Welke vragen werken het best? - Hoe lang duurt index van een PDF van X pagina's? - Verschil tussen vage en specifieke vragen? ### Vorm - Max 500 woorden totaal - Concrete cijfers + voorbeelden - Mag wat informeel --- ## Bonus (optioneel) ### Bonus 1 — Streaming antwoorden Gebruik `streamText` in plaats van `generateText` voor word-by-word output. ### Bonus 2 — Source-citations in UI Toon naast elk antwoord de source-page-nummers van de top chunks. ### Bonus 3 — Multi-document Index 2-3 verschillende PDFs. Documenteer hoe AI ermee omgaat. --- ## Inleveren 1. **GitHub repo URL** in Brightspace 2. **`RAG.md`** in repo-root (4 secties) 3. **Working app** lokaal demonstreerbaar (`pnpm dev`) --- ## Beoordeling | Criterium | Punten | |-----------|--------| | A — UI + indexing + chat werkend | 5 | | B — RAG.md compleet (4 secties) | 3 | | Fail-case analyse is concreet | 1 | | Antwoorden zijn echt gebaseerd op PDF | 1 | | **Totaal** | **10** | Voldoende = 6+. Bonus telt mee bij twijfelgevallen. --- ## Tijd-indicatie | Onderdeel | Tijd | |-----------|------| | A — UI + chat | 45 min | | B — RAG.md schrijven (incl. testen) | 30 min | | **Totaal** | **~1,5 uur** | --- ## Veelvoorkomende valkuilen | Probleem | Oplossing | |----------|-----------| | Scan-PDF — geen text | unpdf werkt niet op image-PDFs. Kies andere PDF | | Vector dimension mismatch | Index én query moeten zelfde model gebruiken | | Top-1 is altijd irrelevant | Chunks te klein, of vraag te abstract — experimenteer | | Cost-pieken bij grote PDF | Indexing per chunk-batch van 100 ipv all-at-once | | RAG hallucineert | Prompt strikter: "Als info niet in context staat, zeg dat" | --- ## Tips - **Test met simpele vragen eerst** — zorg dat basis werkt voor complexer wordt - **Log similarity scores** — onder 0.4 is meestal niet relevant - **Eén goede fail-case is meer waard dan 10 succesvolle** - **Houd het simpel** — geen agents, geen multi-step. Just RAG. Volgende les: MCP — Model Context Protocol. Tot dan!