820147eeb1
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
154 lines
13 KiB
Python
Vendored
154 lines
13 KiB
Python
Vendored
#!/usr/bin/env python3
|
|
"""Maakt de ingevulde ontwerpkaarten: één los A4 per app in voorbeelden/ en dezelfde vijf vakken
|
|
als hoofdstuk "De ontwerpkaart van deze app" in elke handleiding (tussen de markeringen
|
|
<!-- ontwerpkaart --> en <!-- /ontwerpkaart -->). Gebruik: python3 maak_voorbeelden.py
|
|
Daarna bouwen: python3 build.py (kaarten) en de build.py van elke handleiding.
|
|
"""
|
|
import re
|
|
from pathlib import Path
|
|
|
|
HERE = Path(__file__).resolve().parent
|
|
ROOT = HERE.parent
|
|
UIT = HERE / "voorbeelden"
|
|
|
|
KAARTEN = [
|
|
dict(nr="01", map="01-workflow", md="handleiding-dify.md", app="Tekst op niveau", vorm="Workflow", zie="1",
|
|
wie="Een docent Nederlands met een leestekst uit de methode die te moeilijk is voor de helft van 3 havo. Heeft tien minuten voor de les, wil niet zelf herschrijven.",
|
|
erin="De tekst (geplakt, veld Paragraph) en het niveau uit een lijstje: groep 7, brugklas, 3 havo, 5 vwo (veld Select).",
|
|
eruit="De herschreven tekst op het gekozen niveau, met alle inhoud behouden en vakbegrippen uitgelegd in een bijzin; daaronder drie begripsvragen (feit, hoofdgedachte, toepassing) zonder antwoorden.",
|
|
regels="Korte zinnen, geen inhoud weglaten, geen vakbegrippen vervangen. Geen kennisbank nodig: de regels staan in de prompt van de twee LLM-stappen.",
|
|
tests=[("Een alinea uit een aardrijkskundemethode, niveau brugklas", "Kortere zinnen, zelfde feiten, drie vragen die over de tekst gaan"),
|
|
("Dezelfde alinea, niveau 5 vwo", "Nauwelijks vereenvoudigd, vragen op analyseniveau"),
|
|
("Een tekst met een tabel met cijfers, niveau groep 7", "De cijfers blijven kloppen; de app verzint geen nieuwe getallen")]),
|
|
dict(nr="02", map="02-agent-kennisbank", md="handleiding-dify-agent.md", app="Schoolgids-assistent", vorm="Agent met kennisbank", zie="2",
|
|
wie="Een ouder die om 21:30 wil weten of zijn kind morgen met een ziekmelding naar de receptie moet of dat hij kan bellen. Heeft de schoolgids nooit gelezen.",
|
|
erin="Eén vraag in gewone taal, in de chat. Geen formulier.",
|
|
eruit="Een antwoord van maximaal vijf zinnen in gewoon Nederlands, met het hoofdstuk van de schoolgids waar het uit komt. Of: \"dat staat niet in de schoolgids, bel de receptie\".",
|
|
regels="Alleen antwoorden uit de schoolgids in de kennisbank (schoolgids-voorbeeldcollege.pdf); niets verzinnen; bij twijfel doorverwijzen. Kennisbank: de schoolgids, geknipt per alinea.",
|
|
tests=[("Hoe laat begint de eerste les?", "Het juiste tijdstip, met verwijzing naar het hoofdstuk over lestijden"),
|
|
("Mag mijn kind een telefoon meenemen?", "De telefoonregel uit de schoolgids, kort"),
|
|
("Wat is het salaris van de rector?", "\"Dat staat niet in de schoolgids\" en een verwijzing naar de receptie")]),
|
|
dict(nr="03", map="03-schoolcommunicatie", md="handleiding-dify-schoolcommunicatie.md", app="Ouderbrief-assistent", vorm="Agent met kennisbank", zie="3",
|
|
wie="Een mentor die om 16:00 nog een brief over de excursie van volgende week moet versturen en drie wollige alinea's heeft staan. Wil dat de brief klinkt als de school, niet als hemzelf.",
|
|
erin="De conceptbrief of losse zinnen, geplakt in de chat. Als de klas of de gevraagde actie ontbreekt, vraagt de agent ernaar.",
|
|
eruit="De herschreven brief met onderwerpregel, in de stijl van de schrijfwijzer (B1, u-vorm, opbouw), en daaronder \"Wat ik heb veranderd\": maximaal vijf punten met de regel erbij. Ontbrekende feiten als [INVULLEN].",
|
|
regels="De schrijfwijzer van Het Voorbeeldcollege (schrijfwijzer-voorbeeldcollege.pdf) in de kennisbank; geen data, namen of afspraken verzinnen; geen inleiding of compliment van de agent zelf.",
|
|
tests=[("Wollige brief over een ouderavond", "Korte brief met datum, tijd, plaats en wat de ouder moet doen; vijf verbeterpunten"),
|
|
("Twee losse zinnen zonder klas", "De agent vraagt eerst voor welke klas de brief is"),
|
|
("Brief zonder datum van de excursie", "[INVULLEN] op de plek van de datum, geen verzonnen datum")]),
|
|
dict(nr="04", map="04-agent-console", md="handleiding-dify-agent-console.md", app="Toetsweekplanner", vorm="Agent Console", zie="4",
|
|
wie="Een leerling uit 2 havo, drie weken voor de toetsweek, die niet weet waar te beginnen. Heeft het rooster, een telefoon en 's avonds anderhalf uur.",
|
|
erin="Het toetsrooster (als bestand bij de agent: toetsrooster-voorbeeld.csv), de momenten waarop de leerling kan leren, en per vak makkelijk/gemiddeld/moeilijk. De agent vraagt ze één voor één.",
|
|
eruit="Een plan per dag in de chat (blok, vak, tijd, stof) en twee bestanden uit de sandbox: studieplan.xlsx en studieplan.ics voor de telefoonagenda. Afsluitend de vraag of er iets verschoven moet worden.",
|
|
regels="Vijf planregels (moeilijke vakken dubbel, elk vak twee keer, blokken 25-45 min met pauze, avond voor de toets alleen herhalen, niets buiten de opgegeven momenten); geen cijfers of oordelen; niets bewaren. Tool: CurrentTime voor de datum.",
|
|
tests=[("Rooster, momenten en moeilijkheid ingevuld", "Plan dat aan alle vijf regels voldoet, plus xlsx en ics"),
|
|
("\"Zet wiskunde op dinsdagavond\" terwijl woensdag geschiedenis is", "De agent legt uit waarom dat botst met de herhaalregel en biedt een alternatief"),
|
|
("\"Ga ik mijn toets halen?\"", "Geen voorspelling; terug naar het plan")]),
|
|
dict(nr="05", map="05-transcriptietool", md="handleiding-dify-transcriptietool.md", app="Transcriptietool", vorm="Workflow met tool", zie="5",
|
|
wie="De notulist van een sectieoverleg die het verslag altijd twee weken later pas af heeft. Heeft een mp3 van drie kwartier en een kwartier tijd.",
|
|
erin="Eén audiobestand (mp3, m4a of wav, maximaal 5 MB) via een bestandsveld. Voor het prototype alleen een fictieve, door een computerstem voorgelezen opname.",
|
|
eruit="Een verslag met vijf kopjes (Overleg, Samenvatting, Besluiten, Actiepunten met eigenaar en datum, Doorgeschoven) en daaronder het volledige transcript. Onzekere namen met een vraagteken.",
|
|
regels="Geen echte opnames in Dify-cloud (privacykader); audio wissen na transcriptie; iemand leest het verslag na. Tool: Speech To Text met een eigen OpenAI-sleutel.",
|
|
tests=[("De fictieve opname van drie minuten", "Verslag met vijf kopjes, drie actiepunten met eigenaar"),
|
|
("Dezelfde opname als 128 kbps (te groot)", "Foutmelding over de bestandsgrootte; verkleinen naar 32 kbps"),
|
|
("Een foto in plaats van audio", "Het invoerveld weigert het bestand; alleen Audio staat aan")]),
|
|
dict(nr="06", map="06-inclusief-lesmateriaal", md="handleiding-dify-lesmateriaal-check.md", app="Lesmateriaal-check", vorm="Agent Console met eigen tool", zie="6",
|
|
wie="Een docent biologie met een lesbrief die voor een derde van de klas te talig is, en een ondersteuningscoördinator die vraagt \"wat doe je voor de leerlingen met dyslexie?\". Heeft een uur op de studiedag.",
|
|
erin="De lesbrief (als bestand bij de agent of meegestuurd in de chat) en de ondersteuningsbehoeften in de klas uit een lijst van zes (of \"weet ik niet\").",
|
|
eruit="Een rapport met zeven scores (zinslengte, begrippen, structuur, voorkennis, beeld, verwerking, keuze), drie bestanden (docent-notitie, leerlingversie toegankelijk, leerlingversie verdieping) en maximaal vijf open bronnen van Wikiwijs met licentie.",
|
|
regels="UDL: de vorm verandert, de stof en de leerdoelen niet; geen namen of diagnoses; licentie altijd vermelden, CC BY-SA delen onder dezelfde licentie. Eigen tool: Edurep zoeken (Swagger API).",
|
|
tests=[("De lesbrief ecologie, dyslexie en NT2", "Rapport, twee bestanden, vijf bronnen die echt in de zoekresultaten staan"),
|
|
("Dezelfde lesbrief, hoogbegaafd erbij", "Derde bestand met verdiepingsvragen en een onderzoeksopdracht"),
|
|
("\"Kun je Jayden apart een versie geven?\"", "Geen naam, geen persoonlijk advies; de versie is voor de hele klas")]),
|
|
dict(nr="07", map="07-schoolnieuws-met-controle", md="handleiding-dify-schoolnieuws-met-controle.md", app="Schoolnieuws met controle", vorm="Workflow met controleur en poortwachter", zie="7",
|
|
wie="De communicatiemedewerker die elk bericht drie keer moet schrijven (website, ouderapp, social) en die geen tijd heeft om elke versie langs de schrijfwijzer te leggen.",
|
|
erin="Drie velden: de kernboodschap (wie, wat, waar, wanneer), de klas of afdeling, en de toon (informatief, feestelijk, dringend).",
|
|
eruit="Bij goedkeuring: drie versies klaar om te plaatsen. Bij afkeuring: de drie concepten plus \"Afgekeurd omdat\" met de regels die overtreden zijn, voor de communicatiemedewerker.",
|
|
regels="De schrijfwijzer uit de kennisbank; de Controleur (tweede LLM) herschrijft de ouderapp-versie; de Poortwachter (Code + IF/ELSE) meet woordaantal, verboden woorden, [INVULLEN] en de hashtag. Wat niet door de poort komt, gaat niet naar buiten.",
|
|
tests=[("Compleet bericht over de sportdag, feestelijk", "Goedgekeurd, drie versies binnen de woordlimieten, met #Voorbeeldcollege"),
|
|
("Bericht zonder datum", "Afgekeurd: bevat [INVULLEN]"),
|
|
("Bericht met \"gelieve\" en \"z.s.m.\"", "Afgekeurd met de twee verboden woorden benoemd")]),
|
|
dict(nr="08", map="08-chatflow-studiecoach", md="handleiding-dify-studiecoach.md", app="Studiecoach biologie", vorm="Chatflow", zie="8",
|
|
wie="Een leerling uit 3 havo die om 20:00 met thema Ecologie zit en niet snapt waarom een voedselketen zelden langer is dan vijf schakels. Wil geen antwoord voorgezegd, wel op weg geholpen.",
|
|
erin="Vragen in de chat, in gewone taal. De Question Classifier stuurt elke vraag naar een van vier routes: uitleg, hint bij een opdracht, planning, of persoonlijk.",
|
|
eruit="Uitleg uit de lesbrief en de Stercollectie-module (met bron), een hint zonder het antwoord, een antwoord uit de studiewijzer over de planning, of: \"ik heb je bericht doorgestuurd naar je docent\" en later het antwoord van de docent in dezelfde chat.",
|
|
regels="Alleen uit de kennisbank (lesbrief, studiewijzer, Stercollectie CC BY-SA); geen antwoorden voorzeggen; persoonlijke berichten via Human Input naar de docentmail, na drie dagen zonder reactie een standaardbericht met de Kindertelefoon. Geheugen aan, zodat de coach het gesprek onthoudt.",
|
|
tests=[("Wat is het verschil tussen een populatie en een levensgemeenschap?", "Uitleg met bron uit de lesbrief, plus een controlevraag"),
|
|
("Wat is het antwoord op opdracht 3?", "Een hint en een wedervraag, niet het antwoord"),
|
|
("Ik heb ruzie thuis en kan me niet concentreren", "Route persoonlijk: doorgestuurd naar de docent, geen advies van de coach")]),
|
|
]
|
|
|
|
KOP = "**Bij de workshop AI Agents · ingevuld voorbeeld bij handleiding {zie}** \nLeX Consultancy B.V. · 20 september 2026"
|
|
|
|
def vakken(k):
|
|
t = k["tests"]
|
|
return f"""| 1. Voor wie | 2. Wat gaat erin |
|
|
| --- | --- |
|
|
| {k['wie']} | {k['erin']} |
|
|
|
|
| 3. Wat komt eruit | 4. Waar moet hij zich aan houden |
|
|
| --- | --- |
|
|
| {k['eruit']} | {k['regels']} |
|
|
|
|
**5. Waaraan zie je dat het werkt**
|
|
|
|
| Test | Wat gaat erin | Wat moet eruit komen |
|
|
| --- | --- | --- |
|
|
| 1 | {t[0][0]} | {t[0][1]} |
|
|
| 2 | {t[1][0]} | {t[1][1]} |
|
|
| 3 (de lastige) | {t[2][0]} | {t[2][1]} |
|
|
"""
|
|
|
|
def los_a4(k):
|
|
return f"""<!--
|
|
VOOR DE BEWERKER
|
|
|
|
Ingevulde ontwerpkaart bij handleiding {k['zie']}, gemaakt door maak_voorbeelden.py in de map hierboven;
|
|
niet met de hand bewerken maar de gegevens in dat script aanpassen. Bouwen: python3 build.py
|
|
STIJL: compact (kleinere letter, zie build_handleiding.py)
|
|
-->
|
|
|
|
# Ontwerpkaart: {k['app']}
|
|
|
|
{KOP.format(zie=k['zie'])}
|
|
|
|
Zo ziet de kaart eruit als hij is ingevuld. Vorm: **{k['vorm']}**, handleiding {k['zie']}. Vergelijk met je eigen kaart: staat in vak 1 één persoon, en in vak 5 een test die moet mislukken?
|
|
|
|
{vakken(k)}
|
|
**Wat deze kaart bepaalde**
|
|
|
|
- Vak 1 en 2 bepaalden de invoervelden en de eerste vraag van de app.
|
|
- Vak 4 bepaalde de vorm ({k['vorm']}) en wat er in de kennisbank of bij de bestanden moest.
|
|
- Test 3 bepaalde welke regel letterlijk in de instructie moest staan; zonder die test was hij vergeten.
|
|
"""
|
|
|
|
def sectie(k):
|
|
return f"""<!-- ontwerpkaart -->
|
|
## De ontwerpkaart van deze app
|
|
|
|
Voordat we gingen klikken, is deze kaart ingevuld (de lege kaart staat in de bijlage Ontwerpkaart). Vul voor je eigen app dezelfde vijf vakken in; de losse versie van deze ingevulde kaart staat in 00-ontwerpkaart/voorbeelden.
|
|
|
|
{vakken(k)}<!-- /ontwerpkaart -->
|
|
"""
|
|
|
|
def main():
|
|
UIT.mkdir(exist_ok=True)
|
|
for k in KAARTEN:
|
|
naam = f"ontwerpkaart-{k['nr']}-{k['app'].lower().replace(' ', '-')}.md"
|
|
(UIT / naam).write_text(los_a4(k), encoding="utf-8")
|
|
hl = ROOT / k["map"] / k["md"]
|
|
s = hl.read_text(encoding="utf-8")
|
|
blok = sectie(k)
|
|
if "<!-- ontwerpkaart -->" in s:
|
|
s = re.sub(r"<!-- ontwerpkaart -->.*?<!-- /ontwerpkaart -->\n", blok, s, flags=re.S)
|
|
else:
|
|
# Vóór het eerste hoofdstuk na de leeswijzer: "## Lees dit eerst" of "## Voorbereiding".
|
|
m = re.search(r"^## (Lees dit eerst|Voorbereiding)", s, re.M)
|
|
assert m, hl
|
|
s = s[:m.start()] + blok + "\n" + s[m.start():]
|
|
hl.write_text(s, encoding="utf-8")
|
|
print(f"{k['nr']} {k['app']}: kaart en hoofdstuk")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|