82 lines
3.7 KiB
PHP
82 lines
3.7 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/db.php';
|
|
|
|
header('Content-Type: application/json');
|
|
|
|
$config = require __DIR__ . '/config.php';
|
|
$pdo = ai_backend_db($config);
|
|
$user = ai_backend_authenticate($pdo);
|
|
|
|
if ((float) $user['balance_usd'] <= 0) {
|
|
ai_backend_fail(402, 'Kein Guthaben mehr vorhanden.');
|
|
}
|
|
|
|
$body = json_decode(file_get_contents('php://input'), true);
|
|
if (!is_array($body) || !isset($body['rows']) || !is_array($body['rows']) || count($body['rows']) === 0) {
|
|
ai_backend_fail(400, 'Ungültige Anfrage.');
|
|
}
|
|
|
|
// Datensparsamkeit (Nutzer-Feedback zum Untis-Hub): dieser Endpunkt bekommt bewusst NIE Namen,
|
|
// Klassen oder Datumsangaben - nur eine je Zeile rein technische, für die KI bedeutungslose "id"
|
|
// (für die Rückzuordnung im Desktop-Client) sowie die für die Statuseinschätzung tatsächlich
|
|
// relevanten Felder aus dem WebUntis-Fehlzeitendatensatz. Mehrere Zeilen eines Abgleichslaufs
|
|
// werden in einer Anfrage gebündelt statt je Zeile eine eigene Anfrage zu stellen (Kosten/Latenz).
|
|
$systemPrompt = <<<PROMPT
|
|
Du hilfst einer Lehrkraft beim Abgleich von WebUntis-Fehlzeiten mit dem internen Anwesenheitsstatus
|
|
ihrer Schulverwaltungs-App. Du bekommst eine Liste anonymisierter Fehlzeiten-Zeilen (KEINE Namen,
|
|
KEINE Klassen, KEINE Daten) und sollst je Zeile den passendsten internen Status vorschlagen.
|
|
|
|
## Eingabeschema
|
|
|
|
{
|
|
"rows": [
|
|
{
|
|
"id": "<rein technische Kennung ohne inhaltliche Bedeutung, exakt so zurückzugeben>",
|
|
"reasonText": "<Freitext-Grund aus WebUntis, kann leer sein>",
|
|
"absentMinutes": <Zahl, gemeldete Fehlminuten der Unterrichtsstunde>,
|
|
"handledOn": <true, wenn der Eintrag laut WebUntis bereits vom Klassenlehrer bearbeitet wurde>,
|
|
"externKeyInParentheses": <true/false/null - schulinterne Konvention: Klammerung der
|
|
Entschuldigungsnummer bedeutet unentschuldigt, ohne Klammern entschuldigt; null, wenn keine
|
|
Nummer hinterlegt ist>,
|
|
"currentGuess": "<bereits regelbasiert ermittelter Status, siehe Schlüsselliste unten>"
|
|
}
|
|
]
|
|
}
|
|
|
|
## Möglicher Zielstatus (exakt einer dieser Schlüssel je Zeile)
|
|
|
|
- "Present": eigentlich anwesend (z.B. wenn der Freitext klar nahelegt, dass die Fehlzeit ein Irrtum war)
|
|
- "Late": weniger als 10 Minuten verspätet
|
|
- "SignificantlyLate": mindestens 10, aber weniger als 45 Minuten verspätet
|
|
- "LeftDuringClass": während des Unterrichts nach Hause entlassen/gegangen
|
|
- "ExcusePending": krank gemeldet, Entschuldigung noch offen/unbearbeitet
|
|
- "Excused": krank, entschuldigt
|
|
- "Unexcused": unentschuldigt
|
|
|
|
## Aufgabe
|
|
|
|
"currentGuess" kommt aus einer festen Regel über die strukturierten Felder und ist im Zweifel schon
|
|
korrekt - weiche davon nur ab, wenn "reasonText" eindeutig eine andere Einordnung nahelegt (z.B. ein
|
|
Text, der klar auf eine Klassenfahrt, einen Arzttermin mit bereits vorliegendem Attest, oder einen
|
|
erkennbaren Fehleintrag hindeutet). Bei Unsicherheit "currentGuess" unverändert übernehmen, nicht raten.
|
|
|
|
## Antwortformat
|
|
|
|
Antworte AUSSCHLIESSLICH mit gültigem JSON (kein Freitext davor/danach), mit GENAU einem Eintrag je
|
|
Eingabe-"id" (dieselbe Anzahl, dieselben "id"-Werte unverändert, keine erfundenen oder ausgelassenen
|
|
ids):
|
|
{ "suggestions": [ { "id": "<unverändert aus der Eingabe>", "status": "<einer der Schlüssel oben>" } ] }
|
|
PROMPT;
|
|
|
|
$userContent = json_encode($body);
|
|
$result = ai_backend_call_and_charge($pdo, $config, $user, $systemPrompt, $userContent);
|
|
|
|
$parsed = ai_backend_decode_json_response($result['content']);
|
|
if (!is_array($parsed) || !isset($parsed['suggestions']) || !is_array($parsed['suggestions'])) {
|
|
ai_backend_fail(502, 'Die KI hat kein gültiges JSON im erwarteten Schema zurückgegeben.');
|
|
}
|
|
|
|
echo json_encode($parsed);
|