feat: Chemikalien-Recherche über lokale RiSU-Stoffliste (kein KI-Aufruf mehr)
Ersetzt den ursprünglichen KI-Websuche-Ansatz (GESTIS scheiterte sowohl über Websuche als auch über direkten API-Zugriff an fehlenden schulspezifischen Tätigkeitsbeschränkungen) durch einen reinen lokalen Datenbank-Lookup gegen die offizielle RiSU-Stoffliste (1764 Stoffe inkl. Tätigkeitsbeschränkungs-Codes, Legende zur Abfragezeit übersetzt statt in die Datenbank gebacken). Keine KI-Kosten, keine Drittanbieter-Abhängigkeit mehr für diese Funktion. HazardSubstance.AgeRestriction zu ActivityRestriction umbenannt, da die Tätigkeitsbeschränkung mehr abdeckt als reine Altersgrenzen. Details und verworfene Zwischenstände in TODO.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -595,6 +595,103 @@ Bearbeiten, `ExperimentKindDisplay`/`GhsPictogramDisplay`), zwei neue Fälle in
|
||||
wie bei `RequestExplanationAsync`/`RequestPlanAsync` nicht Teil der automatisierten Tests (braucht
|
||||
einen echten Endpunkt, siehe Kommentar in `AiPlanningServiceTests.cs`).
|
||||
|
||||
**Nachtrag — Chemikalien-Recherche (Nutzerwunsch):** pro Gefahrstoff-Zeile im Wizard ein Button
|
||||
"🔍 Recherchieren", der Piktogramme, Signalwort, H-/P-Sätze, CAS-Nummer und Tätigkeitsbeschränkung
|
||||
befüllt — `HazardSubstance` bekommt dafür drei neue Felder (`Cas`, `SignalWord`,
|
||||
`ActivityRestriction`).
|
||||
|
||||
**Weg dorthin (drei verworfene Zwischenstände, der Reihe nach):**
|
||||
1. Zuerst versucht: KI mit Anthropics Websuche-Tool, auf die GESTIS-Domain eingeschränkt.
|
||||
Scheiterte durchgängig ("nicht sicher auffindbar"), auch für zweifelsfrei in GESTIS erfasste
|
||||
Stoffe.
|
||||
2. Ursachenanalyse ergab: `gestis.dguv.de` ist eine reine JavaScript-Anwendung ohne
|
||||
serverseitig gerenderten Inhalt (nur ein leeres `<div id="app">`) — weder eine Websuche noch
|
||||
ein einfacher HTTP-Abruf konnte dort je etwas finden, unabhängig von einer bekannten URL. Über
|
||||
das öffentlich ausgelieferte JS-Bundle der Seite wurde GESTIS' eigene (inoffizielle)
|
||||
Daten-API gefunden und direkt verifiziert (inkl. des ebenfalls öffentlich in
|
||||
`gestis.dguv.de/env-config.js` ausgelieferten Zugangsschlüssels) — technisch funktionierte der
|
||||
Direktabruf, aber GESTIS selbst hat keine strukturierte, schulspezifische
|
||||
Tätigkeitsbeschränkung hinterlegt (nur eine allgemeine Jugendarbeitsschutzgesetz-Floskel plus
|
||||
Verweis auf eine externe, nicht abrufbare Publikation) — genau die für den Unterricht
|
||||
wichtigste Angabe fehlte also weiterhin.
|
||||
3. Der Nutzer stellte stattdessen die offizielle **RiSU-Stoffliste** ("Richtlinie Sicheres
|
||||
Experimentieren im Unterricht") als PDF bereit (123 Seiten, wkhtmltopdf-Export einer
|
||||
Datenbanktabelle) — enthält Name, CAS-Nr., ZVG-Nr., GHS-Piktogramme, Signalwort, H-/P-Sätze
|
||||
UND eine eigene Tätigkeitsbeschränkungs-Spalte (Freigabe/Verbot/Altersgrenzen/
|
||||
Ersatzstoffprüfung/Lehrkraft-Vorbehalt) — exakt das gesuchte Datenmodell, gezielt für den
|
||||
Schuleinsatz zusammengestellt statt eines allgemeinen Arbeitsschutz-Nachschlagewerks.
|
||||
|
||||
**Endgültige Architektur — reiner lokaler Datenbank-Lookup, keine KI mehr beteiligt**
|
||||
(Nutzervorgabe: "wir brauchen die KI gar nicht mehr im Backend. Es reicht die reine Datenbank."):
|
||||
- Das 123-seitige PDF wurde einmalig (per `pdfplumber`, Tabellen-Extraktion je Seite, robuste
|
||||
CAS-Nummer-Rekonstruktion über zeilenweise wrappende Bindestrich-Enden) in
|
||||
[data/stoffliste.json](../ai-backend/data/stoffliste.json) umgewandelt (~1800 Stoffe, ~760 KB).
|
||||
- Neuer Endpunkt [substance.php](../ai-backend/substance.php): prüft weiterhin das Bearer-Token
|
||||
(`ai_backend_authenticate`), sucht den angefragten Namen aber nur noch exakt (normalisiert,
|
||||
auch gegen CAS-Nummer/verwandte CAS-Nummern) in [stoffliste.php](../ai-backend/stoffliste.php)
|
||||
— bewusst **kein** Fuzzy-/Teilstring-Match, ein falscher Treffer wäre hier schlimmer als gar
|
||||
keiner. Kein KI-Aufruf, keine Kosten, keine Drittanbieter-Netzwerkabhängigkeit mehr; ohne
|
||||
Treffer liefert der Endpunkt einen klaren 404 statt eines Rateversuchs.
|
||||
- Die gesamte für dieses Feature eingeführte Tool-Use-/Websuche-Infrastruktur wurde vollständig
|
||||
zurückgebaut, da nicht mehr gebraucht: `ProviderInterface`/`AnthropicProvider`/`FakeProvider`/
|
||||
`ai_backend_call_and_charge` wieder auf ihren ursprünglichen (Vor-Feature-)Stand, `gestis.php`,
|
||||
`cache.php`, `data/gestis-substances.json` und die Migration für
|
||||
`transactions.web_search_requests` wieder entfernt.
|
||||
- **Legende der Tätigkeitsbeschränkungs-Codes** (steht in keiner Form im PDF selbst; die erste
|
||||
vom Nutzer erinnerte Fassung war falsch und wurde nach einem zweiten Blick korrigiert — u.a.
|
||||
`S`/`L+` bedeuten das genaue GEGENTEIL der ersten Fassung, ein Verbot statt einer Freigabe):
|
||||
`+` generelle Erlaubnis · `X` generelles Tätigkeitsverbot an Schulen · `L+` Tätigkeitsverbot für
|
||||
Lehrkräfte (Ausnahme siehe RiSU I–3.5) · `S` Tätigkeitsverbot für Schüler:innen · `S4K`
|
||||
Tätigkeitsverbot bis einschließlich Jahrgangsstufe 4 · `S9K` bis einschließlich Jahrgangsstufe 9
|
||||
· `W` Tätigkeitsverbot für werdende/stillende Mütter · `ESP` gesonderte Ersatzstoffprüfung
|
||||
notwendig (unverändert). Aus diesem Anlass Architektur angepasst: `data/stoffliste.json`
|
||||
speichert nur noch die rohen Codes, die Übersetzung passiert erst zur Abfragezeit in
|
||||
`stoffliste.php` (`STOFFLISTE_RESTRICTION_LEGEND`) — eine künftige Korrektur braucht damit nur
|
||||
noch eine Codeänderung statt einer kompletten Neu-Generierung der ~1800-Stoffe-Datenbank.
|
||||
- **Datenqualität:** beim Regenerieren der Datenbank für obige Korrektur zusätzlich eine
|
||||
Kopfzeilen-Dublette gefunden und behoben (Erkennung war zu strikt auf unverzerrten Text
|
||||
angewiesen) sowie entdeckt, dass `pdfplumber`s Tabellenerkennung bei ca. 40 der ca. 1800 Stoffe
|
||||
(~2 %) die Spalten nicht sauber trennen konnte (ungewöhnlich lange Zellinhalte lassen die
|
||||
Spaltenerkennung für einzelne Zeilen kollabieren). Für diese Zeilen wurde eine Regex-basierte
|
||||
Rekonstruktion versucht (CAS-/GHS-/H-/P-Code-Muster sind positionsunabhängig eindeutig,
|
||||
Stoffname dagegen nicht) — nur bei einem eindeutig plausiblen Stoffnamen übernommen (11 von 51
|
||||
gerettet), der Rest bewusst ausgelassen statt mit einem möglicherweise falschen Namen
|
||||
aufgenommen zu werden (ein falscher Name bei sonst korrekten Gefahrendaten wäre gefährlicher als
|
||||
ein fehlender Eintrag). Endgültige Größe: 1764 Stoffe.
|
||||
- **Bekannte Lücke:** diese Stoffliste enthält keine Entsorgungshinweise (anders als der
|
||||
verworfene GESTIS-Ansatz) — `Disposal` bleibt bei einem Datenbank-Treffer leer und muss
|
||||
weiterhin von Hand ausgefüllt werden.
|
||||
- Deployment: `substance.php`, `stoffliste.php` und `data/stoffliste.json` hochladen — kein
|
||||
Migrations-Schritt, keine `config.php`-Änderung mehr nötig (siehe `ai-backend/README.md`).
|
||||
|
||||
**Feld umbenannt:** `HazardSubstance.AgeRestriction` → `ActivityRestriction` (Wizard-Feld
|
||||
"Umgangsbeschränkung für Schüler (nach Alter)" → "Tätigkeitsbeschränkung"), da die tatsächlich
|
||||
verfügbare Angabe breiter ist als reine Altersgrenzen (Freigabe/Verbot/Ersatzstoffprüfung/
|
||||
Lehrkraft-Vorbehalt gehören genauso dazu) — der ursprüngliche Feldname war eine zum Zeitpunkt der
|
||||
ersten Umsetzung noch zu enge Annahme.
|
||||
|
||||
**Auf dem Weg dorthin außerdem behoben (nicht mehr relevant für die Endarchitektur, aber
|
||||
allgemein nützlich):** der Desktop-Client zeigte bei jedem Backend-Fehler ≠ 401/402 nur die
|
||||
pauschale Meldung "Die Anfrage an den KI-Dienst ist fehlgeschlagen" statt der von
|
||||
`ai_backend_fail` tatsächlich mitgesendeten Ursache (`{"error": "..."}`) — betraf alle KI-Aufrufe
|
||||
(`RequestPlanAsync`/`RequestExplanationAsync`/`RequestHazardAssessmentDraftAsync`), nicht nur die
|
||||
Chemikalien-Recherche. Neue gemeinsame `AiPlanningService.BuildRequestFailedExceptionAsync` liest
|
||||
das `error`-Feld mit und hängt es an die Meldung an — bleibt auch nach diesem Umbau nützlich.
|
||||
|
||||
**PDF-Export:** Gefahrstoffe werden jetzt als eigener Block je Stoff statt als schmale
|
||||
Tabellenzeile gedruckt (`PdfExportService.SubstanceBlocks`/`SubstanceBlock`) — bei den vielen
|
||||
Datenpunkten je Stoff (Name, CAS, Menge, GHS, Signalwort, H-/P-Sätze, Tätigkeitsbeschränkung) wäre
|
||||
eine Tabelle auf A4-Hochformat zu schmal für lesbare Texte geworden; die Tätigkeitsbeschränkung
|
||||
wird farblich hervorgehoben, da sie die operativ wichtigste Angabe für die Lehrkraft ist.
|
||||
|
||||
Neue Tests: `HazardSubstanceEditItemTests` (Delegate-Aufruf, Feldübernahme, Fehlerbehandlung,
|
||||
leerer Name), zwei neue `CanUseAi`/`CanResearch`-Wiring-Tests in
|
||||
`HazardAssessmentWizardViewModelTests`. Die eigentliche HTTP-Anfrage von
|
||||
`RequestSubstanceResearchAsync` ist nicht Teil der automatisierten Tests (kein PHP im
|
||||
Implementierungsumfeld verfügbar); die PDF→JSON-Umwandlung und die Lookup-Logik wurden in Python
|
||||
nachgebaut und gegen reale Stichproben aus dem PDF verifiziert. Für `ai-backend/` selbst existiert
|
||||
weiterhin keine automatisierte Testsuite (kein PHP-Testframework im Projekt).
|
||||
|
||||
### 4.3 Stundenplan
|
||||
- [x] **4.3.1** Neues Modell `TimetableSlot` (Gruppe, Wochentag, Stunde, Raum) + Repository —
|
||||
[Planning.cs](LehrerApp.Core/Models/Planning.cs),
|
||||
|
||||
Reference in New Issue
Block a user