diff --git a/00-sessie/README.md b/00-sessie/README.md new file mode 100644 index 0000000..b6b7f2a --- /dev/null +++ b/00-sessie/README.md @@ -0,0 +1,55 @@ +# Sessie: AI Agents, van voorbeeld naar je eigen project + +Een sessie van twee keer een uur voor een gemengd publiek, gemaakt voor het Kennisfestival van +AI Netwerk Limburg. Het eerste uur is inhoud, interactie en onze voorbeelden; het tweede uur +werkt iedereen aan een eigen project. Ontwerpen op papier hoort erbij, bouwen mag maar hoeft niet. + +- `sessie-agents-en-projecten.pdf`: de 23 dia's, klaar om te tonen. +- `sessie-agents-en-projecten-met-notities.pdf`: per dia een A4 met het beeld en de sprekersnotities + eronder, om te printen of op een tweede scherm te openen. +- `maak_dias.py`: de bron; bouwt de dia's als HTML (1920x1080) in `project/slides/` en `project/deck.json`. +- `bouw_pdf.py` (of `python3 build.py`): bouwt de dia's opnieuw en maakt de PDF met google-chrome. +- `bouw_notities_pdf.py`: maakt de notitieversie. + +## Het verloop + +| Tijd | Dia | Wat | Werkvorm | +| --- | --- | --- | --- | +| 0:00 | 1-2 | Welkom, hoe de twee uur werken | plenair | +| 0:03 | 3 | Peiling: handen omhoog | interactie | +| 0:07 | 4-6 | Drie lagen, wat een agent is, wat het niet is | plenair | +| 0:20 | 7 | Hoeveel ruimte geef je het model: vijf patronen | plenair | +| 0:23 | 8 | Drie stellingen, staan of zitten | interactie, 10 min | +| 0:33 | 9 | Acht voorbeelden die echt draaien | plenair | +| 0:36 | 10-13 | Vier demo's: schoolgids, schoolnieuws, planner, studiecoach | live, met sabotage | +| 0:57 | 14-15 | Vier lessen, en de opdracht mee de pauze in | plenair | +| 1:00 | 16-17 | Hoe het tweede uur werkt, de ontwerpkaart | plenair | +| 1:08 | 17 | Werken aan je eigen project | zelf doen, 35 min | +| 1:25 | 18 | Waar hoort de mens? (tijdens het werkblok) | interactie, tweetallen | +| 1:43 | 19 | Pitches, drie mensen | interactie | +| 1:53 | 20-21 | Waar bouw je het, en wat mag | plenair | +| 2:00 | 22-23 | Hoe je verder komt, slot | plenair | + +Vier interactiemomenten, twee demo's met de zaal erbij, en 35 minuten waarin iedereen zelf werkt. + +## Wat je nodig hebt + +- De acht voorbeeld-apps staan live op udify.app en werken zonder inloggen; de links staan in de + sprekersnotities bij de demo-dia's en in de README van de repository. +- Geprinte ontwerpkaarten: `../00-ontwerpkaart/ontwerpkaart.pdf`, één per deelnemer. +- Voor wie in het tweede uur wil bouwen: de handleidingen uit deze repository, of die van de + Copilot-, ChatGPT- of Gemini-reeks als ze in hun eigen omgeving werken. +- Geen laptops of accounts nodig om mee te doen. + +## Hoe dit verschilt van de andere twee presentaties + +| | Waar | Voor wie | +| --- | --- | --- | +| Sessie (deze, 23 dia's, 2 uur) | `00-sessie` | Gemengd publiek dat met een eigen project naar huis wil | +| Workshop (26 dia's, 2,5 uur) | `00-presentatie` en `00-workshop` | Groepen die stap voor stap dezelfde app bouwen, met draaiboek | +| Lezing (12 dia's, 20 min) | `00-lezing` | Een teamvergadering of bestuur dat het alleen moet snappen | + +De inhoudelijke dia's (wat is een agent, wat het niet is, vier omgevingen, wat mag) zijn in alle +drie dezelfde tekst, zodat ze elkaar niet tegenspreken. + +Licentie CC BY-SA 4.0, logo van AI Netwerk Limburg uitgezonderd; zie `../LICENSE.md`. diff --git a/00-sessie/beelden/logo-ai-netwerk-limburg.png b/00-sessie/beelden/logo-ai-netwerk-limburg.png new file mode 100644 index 0000000..4d72a61 Binary files /dev/null and b/00-sessie/beelden/logo-ai-netwerk-limburg.png differ diff --git a/00-sessie/beelden/logo-lex-wit.png b/00-sessie/beelden/logo-lex-wit.png new file mode 100644 index 0000000..67093bb Binary files /dev/null and b/00-sessie/beelden/logo-lex-wit.png differ diff --git a/00-sessie/beelden/logo-lex.png b/00-sessie/beelden/logo-lex.png new file mode 100644 index 0000000..df5e1f5 Binary files /dev/null and b/00-sessie/beelden/logo-lex.png differ diff --git a/00-sessie/beelden/schoolnieuws-afgekeurd.jpg b/00-sessie/beelden/schoolnieuws-afgekeurd.jpg new file mode 100644 index 0000000..395c911 Binary files /dev/null and b/00-sessie/beelden/schoolnieuws-afgekeurd.jpg differ diff --git a/00-sessie/beelden/studiecoach-docent-antwoord.jpg b/00-sessie/beelden/studiecoach-docent-antwoord.jpg new file mode 100644 index 0000000..9c589e8 Binary files /dev/null and b/00-sessie/beelden/studiecoach-docent-antwoord.jpg differ diff --git a/00-sessie/beelden/studiecoach-flow.jpg b/00-sessie/beelden/studiecoach-flow.jpg new file mode 100644 index 0000000..e421b8d Binary files /dev/null and b/00-sessie/beelden/studiecoach-flow.jpg differ diff --git a/00-sessie/bouw_notities_pdf.py b/00-sessie/bouw_notities_pdf.py new file mode 100644 index 0000000..f283733 --- /dev/null +++ b/00-sessie/bouw_notities_pdf.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +"""Maakt een notitieversie van de presentatie: per dia een A4 met het beeld bovenaan en de +sprekersnotities eronder. Om te printen of op een tweede scherm te openen terwijl je presenteert. + +Gebruik: python3 bouw_notities_pdf.py +Uitvoer: sessie-agents-en-projecten-met-notities.pdf (staat niet in git; de begeleidersnotities zijn privé) + +Staat notities-begeleider.json naast dit script, dan gebruikt hij die uitgebreide notities. +Anders de korte notities die in de dia's zelf zitten. Na afloop worden de dia-bestanden +teruggezet op de publieke versie, zodat bouw_pdf.py en de repo ongewijzigd blijven. +""" +import html +import json +import re +import subprocess +import sys +import tempfile +from pathlib import Path + +HERE = Path(__file__).resolve().parent +P = HERE / "project" +PRIVE = (HERE / "notities-begeleider.json").exists() + +# 1. dia's bouwen, met de privénotities als die er zijn +subprocess.run([sys.executable, str(HERE / "maak_dias.py")] + (["--notities"] if PRIVE else []), check=True) +deck = json.loads((P / "deck.json").read_text(encoding="utf-8")) +faces = "".join(f'' for f in deck["faces"].values()) + +# 2. eerst een tijdelijke dia-PDF, dan per dia een afbeelding +tmp = Path(tempfile.mkdtemp(prefix="notities-")) +css_dia = """ +@page { size: 1920px 1080px; margin: 0; } +html, body { margin: 0; padding: 0; } +* { box-sizing: border-box; margin: 0; padding: 0; } +section { position: relative; width: 1920px; height: 1080px; overflow: hidden; page-break-after: always; break-after: page; } +section:last-child { page-break-after: auto; } +aside { display: none; } +ul { padding-left: 1.1em; } +table { border-collapse: collapse; width: 100%; } +th, td { border: 1px solid #D8D2C4; padding: 12px 18px; text-align: left; vertical-align: top; } +img { max-width: 100%; } +""" +slides_html = "".join((P / "slides" / f"{sid}.html").read_text(encoding="utf-8") for sid in deck["order"]) +(tmp / "dias.html").write_text( + f'{faces}' + f"{slides_html}", encoding="utf-8") +subprocess.run(["google-chrome", "--headless=new", "--disable-gpu", "--no-pdf-header-footer", + "--virtual-time-budget=8000", f"--print-to-pdf={tmp / 'dias.pdf'}", (tmp / "dias.html").as_uri()], + check=True, capture_output=True) +subprocess.run(["pdftoppm", "-r", "80", "-jpeg", str(tmp / "dias.pdf"), str(tmp / "dia")], check=True) +beelden = sorted(tmp.glob("dia-*.jpg")) + + +def notities(sid): + tekst = re.search(r"", (P / "slides" / f"{sid}.html").read_text(encoding="utf-8"), re.S) + tekst = tekst.group(1).strip() if tekst else "" + if not tekst: + return "

Geen notities bij deze dia.

" + regels = [] + for deel in [d.strip() for d in tekst.split(" | ") if d.strip()]: + m = re.match(r"^([A-Z]{4,10}):\s*(.*)$", deel, re.S) + if m: + regels.append(f"

{html.escape(m.group(1))} {html.escape(m.group(2))}

") + else: + regels.append(f"

{html.escape(deel)}

") + return "".join(regels) + + +# 3. per dia een A4 met beeld en notities +css = """ +@page { size: A4 portrait; margin: 14mm 14mm 12mm; } +html, body { margin: 0; padding: 0; } +* { box-sizing: border-box; } +body { font-family: "Nunito Sans", Verdana, sans-serif; color: #1E2A3A; } +.kaart { page-break-after: always; break-after: page; } +.kaart:last-child { page-break-after: auto; } +.kop { display: flex; justify-content: space-between; align-items: baseline; font-size: 10pt; color: #8A8F98; + border-bottom: 1px solid #D8D2C4; padding-bottom: 4px; margin-bottom: 8px; } +.kop b { font-family: "Rubik", Arial, sans-serif; font-size: 12pt; color: #1E2A3A; } +img { width: 100%; border: 1px solid #D8D2C4; display: block; } +.notities { margin-top: 10px; font-size: 11pt; line-height: 1.45; } +.notities p { margin: 0 0 7px; } +.notities b { font-family: "Rubik", Arial, sans-serif; color: #D9480F; letter-spacing: .5px; font-size: 10pt; } +.leeg { color: #8A8F98; font-style: italic; } +""" +kaarten = [] +for n, sid in enumerate(deck["order"], 1): + kaarten.append( + f'
Dia {n} · {html.escape(sid)}' + f'{html.escape(deck["title"])} · notitieversie
' + f'Dia {n}' + f'
{notities(sid)}
') +pagina = tmp / "notities.html" +pagina.write_text(f'{faces}' + f'{"".join(kaarten)}', encoding="utf-8") +pdf = HERE / "sessie-agents-en-projecten-met-notities.pdf" +subprocess.run(["google-chrome", "--headless=new", "--disable-gpu", "--no-pdf-header-footer", + "--virtual-time-budget=8000", f"--print-to-pdf={pdf}", pagina.as_uri()], + check=True, capture_output=True) + +# 4. de dia-bestanden terugzetten op de publieke versie +subprocess.run([sys.executable, str(HERE / "maak_dias.py")], check=True, capture_output=True) +soort = "uitgebreide begeleidersnotities" if PRIVE else "korte notities uit de dia's" +print(f"{pdf.name}: {pdf.stat().st_size // 1024} kB, {len(deck['order'])} dia's ({soort})") diff --git a/00-sessie/bouw_pdf.py b/00-sessie/bouw_pdf.py new file mode 100644 index 0000000..e8f907f --- /dev/null +++ b/00-sessie/bouw_pdf.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +"""Maakt van de dia's in project/slides één PDF (1920x1080 per pagina) met google-chrome. + +Gebruik: python3 bouw_pdf.py (draait eerst maak_dias.py, dan Chrome headless) +Sprekersnotities (