feat(workshop): regiekaart en notitieversie van de presentatie voor de begeleider

- 00-workshop/regiekaart: het programma op een kaart, met schrapvolgorde en noodgevallen
- 00-presentatie/bouw_notities_pdf.py: per dia een A4 met beeld en notities
- de notitieversie zelf blijft buiten git (privenotities)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Guido van Dijk
2026-09-22 14:11:20 +02:00
parent b56fed0df5
commit 50f95a5403
8 changed files with 168 additions and 0 deletions
+1
View File
@@ -9,3 +9,4 @@ __pycache__/
_gereedschap/extract.py _gereedschap/extract.py
NOTITIES-intern.md NOTITIES-intern.md
00-presentatie/notities-begeleider.json 00-presentatie/notities-begeleider.json
00-presentatie/presentatie-met-notities.pdf
+4
View File
@@ -4,6 +4,10 @@
- `maak_dias.py`: de bron; bouwt de dia's als HTML (1920x1080) in `project/slides/` en `project/deck.json`. - `maak_dias.py`: de bron; bouwt de dia's als HTML (1920x1080) in `project/slides/` en `project/deck.json`.
- `project/slides/*.html`: één bestand per dia, met de sprekersnotities in `<aside>`. - `project/slides/*.html`: één bestand per dia, met de sprekersnotities in `<aside>`.
- `bouw_pdf.py` (of `python3 build.py`): bouwt de dia's opnieuw en maakt de PDF met google-chrome. - `bouw_pdf.py` (of `python3 build.py`): bouwt de dia's opnieuw en maakt de PDF met google-chrome.
- `bouw_notities_pdf.py`: maakt `presentatie-met-notities.pdf`, per dia een A4 met het beeld en de
sprekersnotities eronder. Om te printen of op een tweede scherm te openen terwijl je presenteert.
Staat `notities-begeleider.json` ernaast, dan gebruikt hij die uitgebreide notities; die versie
staat niet in git.
- `beelden/`: het logo en drie schermafbeeldingen uit de handleidingen. - `beelden/`: het logo en drie schermafbeeldingen uit de handleidingen.
Het draaiboek met tijden en werkvormen staat in `../00-workshop/`. Het draaiboek met tijden en werkvormen staat in `../00-workshop/`.
+104
View File
@@ -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: presentatie-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'<link rel="stylesheet" href="{f["href"]}">' 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'<!DOCTYPE html><html lang="nl"><head><meta charset="utf-8">{faces}<style>{css_dia}</style></head>'
f"<body>{slides_html}</body></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"<aside>(.*?)</aside>", (P / "slides" / f"{sid}.html").read_text(encoding="utf-8"), re.S)
tekst = tekst.group(1).strip() if tekst else ""
if not tekst:
return "<p class='leeg'>Geen notities bij deze dia.</p>"
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"<p><b>{html.escape(m.group(1))}</b> {html.escape(m.group(2))}</p>")
else:
regels.append(f"<p>{html.escape(deel)}</p>")
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'<div class="kaart"><div class="kop"><b>Dia {n} · {html.escape(sid)}</b>'
f'<span>{html.escape(deck["title"])} · notitieversie</span></div>'
f'<img src="{beelden[n - 1].as_uri()}" alt="Dia {n}">'
f'<div class="notities">{notities(sid)}</div></div>')
pagina = tmp / "notities.html"
pagina.write_text(f'<!DOCTYPE html><html lang="nl"><head><meta charset="utf-8">{faces}<style>{css}</style></head>'
f'<body>{"".join(kaarten)}</body></html>', encoding="utf-8")
pdf = HERE / "presentatie-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})")
+5
View File
@@ -55,6 +55,8 @@ Het doel is niet dat iedereen alle apps gezien heeft, maar dat iedereen naar hui
| De andere drie reeksen, als PDF op de laptops | 1 per duo | Copilot, ChatGPT en Gemini: dezelfde zes agents, voor het vergelijkingsblok en om mee te nemen | | De andere drie reeksen, als PDF op de laptops | 1 per duo | Copilot, ChatGPT en Gemini: dezelfde zes agents, voor het vergelijkingsblok en om mee te nemen |
| Demo-account met alle apps live | 1 | README.md, kopje "Gepubliceerde voorbeeld-apps" | | Demo-account met alle apps live | 1 | README.md, kopje "Gepubliceerde voorbeeld-apps" |
| Bijlage A van dit draaiboek, geprint | 1 voor jezelf | De teksten die je in de demo's plakt | | Bijlage A van dit draaiboek, geprint | 1 voor jezelf | De teksten die je in de demo's plakt |
| Regiekaart, geprint | 1 voor jezelf | `00-workshop/regiekaart.pdf`: het hele programma op één A4, naast je laptop |
| Presentatie met notities | 1 voor jezelf | `00-presentatie/presentatie-met-notities.pdf`: per dia je notities, op papier of op een tweede scherm |
**Techniek** **Techniek**
@@ -69,6 +71,8 @@ Het doel is niet dat iedereen alle apps gezien heeft, maar dat iedereen naar hui
- [ ] Credits op het demo-account gecontroleerd, of de eigen sleutel actief (Gebruiksprioriteit: API-sleutel). - [ ] Credits op het demo-account gecontroleerd, of de eigen sleutel actief (Gebruiksprioriteit: API-sleutel).
- [ ] Presentatie gedownload als PDF op de laptop waarmee je presenteert, niet alleen in de browser. - [ ] Presentatie gedownload als PDF op de laptop waarmee je presenteert, niet alleen in de browser.
- [ ] Bijlage A geprint of in een tekstbestand klaargezet, zodat je tijdens de demo's niets hoeft te typen. - [ ] Bijlage A geprint of in een tekstbestand klaargezet, zodat je tijdens de demo's niets hoeft te typen.
- [ ] Regiekaart geprint (`regiekaart.pdf`) en de notitieversie van de presentatie klaargezet
(`python3 bouw_notities_pdf.py` in `00-presentatie`); vanaf een PDF presenteren laat je notities niet zien.
- [ ] Ontwerpkaarten geprint: 12 stuks bij 20 deelnemers. - [ ] Ontwerpkaarten geprint: 12 stuks bij 20 deelnemers.
- [ ] De vier reeksen als PDF op een USB-stick of in een gedeelde map, voor wie ze meteen wil. - [ ] De vier reeksen als PDF op een USB-stick of in een gedeelde map, voor wie ze meteen wil.
- [ ] Post-its en stiften ingepakt. - [ ] Post-its en stiften ingepakt.
@@ -433,6 +437,7 @@ Balie: Hallo, mijn dochter zit in 1C en is morgen ziek. Moet ik haar afmelden en
**Meenemen** **Meenemen**
- [ ] Laptop met de presentatie als PDF, ingelogd op cloud.dify.ai - [ ] Laptop met de presentatie als PDF, ingelogd op cloud.dify.ai
- [ ] Regiekaart en de notitieversie van de presentatie, geprint
- [ ] Deze bijlage A, geprint of als tekstbestand - [ ] Deze bijlage A, geprint of als tekstbestand
- [ ] 12 ontwerpkaarten, post-its, stiften - [ ] 12 ontwerpkaarten, post-its, stiften
- [ ] De vier reeksen op een USB-stick - [ ] De vier reeksen op een USB-stick
Binary file not shown.
+46
View File
@@ -0,0 +1,46 @@
<!--
VOOR DE BEWERKER
Eén A4 om naast de laptop te leggen tijdens de workshop. Alles wat hierop staat komt uit
draaiboek-workshop.md; verander daar en pas dit daarna aan. Bouwen: python3 build.py regiekaart.md
(of: python3 ../_gereedschap/build_handleiding.py regiekaart.md)
-->
# Regiekaart
**Workshop AI Agents met Dify · 23 september 2026 · 13:00-14:00 en 14:15-15:30 · 26 dia's**
Het hele draaiboek staat in `draaiboek-workshop.md`. De teksten die je in de demo's plakt: bijlage A daarvan.
| Tijd | Dia | Wat je doet | Let op |
| --- | --- | --- | --- |
| 13:00 | 1-3 | Welkom, peiling staand | Doe de peiling ook als mensen binnendruppelen |
| 13:05 | 4-7 | Drie lagen, wat een agent is, wat niet, vijf patronen | Vier dia's in tien minuten; parkeer discussie |
| 13:15 | 8 | Demo Tekst op niveau + quiz "welk patroon?" | Plak A1, kies brugklas |
| 13:20 | 9 | **Bouwblok 1**, rondlopen | Model op gpt-5-mini; na 20 min: wie heeft een link? |
| 13:50 | 10 | Demo agent + kennisbank + sabotage | Ouderbrief-demo alleen als je op tijd ligt |
| 14:00 | 11 | Wissel- en netwerktijd | Zeg: 14:15 beginnen we met bouwen |
| 14:15 | 12 | **Bouwblok 2**, spoor A of B | Hardop: eigen document zonder namen |
| 14:45 | 13-15 | Demo poortwachter en studiecoach | Twee berichten, dan door |
| 14:51 | 16 | Waar hoort de mens? | Twee duo's noemen hun "nooit meer"-regel |
| 14:55 | 17 | Ontwerpkaart invullen | Vraag bij elk duo: wie is die ene persoon? |
| 15:03 | 18 | Pitches | Drie duo's, 30 seconden, geen jury |
| 15:08 | 19-20 | **Vier omgevingen** + handen omhoog | Wijs drie rijen aan; geen productdiscussie |
| 15:15 | 21-23 | Wat mag, AVG en AI Act, EU-kaders | Twee zinnen per kant, geen juridische les |
| 15:22 | 24-25 | Mee naar huis, exit ticket | Post-its bij de deur |
| 15:28 | 26 | Slot | 15:30 is hard |
**Loop je achter, schrap in deze volgorde**
1. Dia 15 (mens in de lus) alleen noemen (2 min) · 2. Pitches naar twee duo's (1 min) · 3. EU-kaders in één zin (2 min)
4. Vergelijkingsblok alleen de tabel (2 min) · 5. Ouderbrief-demo overslaan (3 min) · 6. Bouwblok 2 vijf minuten korter
**Nooit schrappen:** bouwblok 1, de ontwerpkaart, het exit ticket.
**Vier zinnen die moeten vallen**
- Een prompt is van jou en breekbaar; een agent staat er ook als jij er niet bent.
- Een agent krijgt een doel en bepaalt zelf welke stappen hij zet, met de middelen die jij hem geeft.
- Een prompt vraagt; code dwingt af. Daarom een poortwachter.
- De instructie is het werk, de omgeving is de keuze.
**Als het misgaat:** wifi weg, dan demo's op jouw scherm en bouwblok 1 als leesopdracht. Credits op: gpt-5-mini, anders meekijken bij het buurduo. Vijf apps bereikt: oude app verwijderen in Studio. Iemand wil echte leerlinggegevens invoeren: stoppen en uitleggen waarom.
Binary file not shown.
+8
View File
@@ -3,6 +3,14 @@
Dify verandert snel. Elke handleiding vermeldt op de titelpagina op welke dag de schermen zijn Dify verandert snel. Elke handleiding vermeldt op de titelpagina op welke dag de schermen zijn
vastgelegd. Hier staat per uitgave wat er veranderde. vastgelegd. Hier staat per uitgave wat er veranderde.
## 2026-09-22, materiaal voor wie de workshop leidt
- `00-workshop/regiekaart.md` en `.pdf`: het hele programma op één kaart naast je laptop, met de
schrapvolgorde bij tijdnood, de vier zinnen die moeten vallen en wat je doet als iets misgaat.
- `00-presentatie/bouw_notities_pdf.py`: maakt een notitieversie van de presentatie, per dia een
A4 met het beeld en de sprekersnotities eronder. Nodig omdat een PDF op volledig scherm geen
sprekersweergave heeft. De versie met de privénotities staat niet in git.
## 2026-09-22, losse lezing Wat is een AI-agent ## 2026-09-22, losse lezing Wat is een AI-agent
- Nieuwe map `00-lezing`: een presentatie van twintig minuten voor een teamvergadering of bestuur, - Nieuwe map `00-lezing`: een presentatie van twintig minuten voor een teamvergadering of bestuur,