Audit-Trail, M6-Abschluss und Rückblick
Track KI · M6 Datenseite, Baustein 03 und Abschluss-Check, Teil 2 · ca. 50 Min. plus Projektaufgabe
Worum es geht
Was du aus Teil 1 brauchst: Lineage ist ein Graph aus “entstand aus”, Impact-Analyse heißt “wer ist betroffen, wenn sich X ändert”, und ein Hash über kanonisches JSON ist der Fingerabdruck von Daten. Dieser Teil überträgt das auf KI-Antworten: den Audit-Trail (Prüfpfad). Welche Chunks, welche Modellversion und welche Prompt-Version haben eine Antwort erzeugt, und welche Antworten sind betroffen, wenn sich etwas davon ändert?
Danach folgen die Projektaufgabe (Abschluss-Check von M6) und ein Rückblick auf den ganzen Lernpfad: Welche Lektion beantwortet welches “Fertig, wenn” der Module M0 bis M6?
Was im Browser läuft: alles, in reinem Python. Das Modell ist ein Fake, wie in Lektion 06 und 15.
Zeitplan ehrlich: etwa 20 Minuten Lesen, 30 Minuten für die drei Übungen. Projektaufgabe und Rückblick kommen obendrauf (die Quelle plant für Lineage und Abschluss-Check zusammen 5 bis 7 Stunden ein).
Konzept
Schritt 1: Audit-Trail für KI-Antworten
Dasselbe Prinzip wie bei den Daten in Teil 1 gilt für eine RAG-Antwort (Retrieval-Augmented Generation, Lektionen 08 bis 10a und 10b). Wenn ein Kunde fragt, warum der Bot vor drei Wochen etwas Falsches sagte, brauchst du den Audit-Trail (Prüfpfad) dieser einen Antwort:
- Chunks: welche Textstücke aus welchem Dokument, mit dem Hash des Dokuments zum damaligen Zeitpunkt (Dokumente ändern sich, und “heute steht da etwas anderes” ist kein Beweis),
- Modellversion (ein Modell-Alias kann sich ändern, die Version nicht),
- Prompt-Version (am einfachsten ein kurzer Hash des Prompt-Textes),
- Trace-ID, die auf den Span-Verlauf aus Lektion 15b zeigt,
- die Frage als Hash, nicht im Klartext (sie kann personenbezogene Daten enthalten, siehe Lektion 13b).
Ein Fake-Aufbau, damit du jede Zahl nachvollziehen kannst. Die Suche ist hier ein fester Nachschlag (die echte hast du in Lektion 08 bis 10 gebaut), das Modell antwortet mit einem Satz aus dem Beleg. Die Namen von Modell und Dokumenten sind erfunden:
Jeder Chunk trägt den Hash seines Textes. Jetzt die Antwort mit Audit-Eintrag:
Der Audit-Eintrag ist ein einzelnes dict, das du in dieselbe Logdatei oder Tabelle schreiben kannst wie die Spans. Entscheidend ist, dass er zum Zeitpunkt der Antwort entsteht. Nachträglich lässt er sich nicht ehrlich rekonstruieren.
Jetzt wird das Tarifdokument geändert (14 statt 12 Euro). Welche früheren Antworten stützen sich auf einen veralteten Beleg? Vergleiche den Hash im Audit mit dem aktuellen Hash des Dokuments:
Der Beleg von a-001 ist veraltet (anderer Hash). Das ist die Verbindung zur Impact-Analyse aus Teil 1, Schritt 2: Ändert sich eine Quelle, fragst du, welche Ergebnisse darauf beruhen. Bei Berichten ist das ein Graph, bei KI-Antworten eine Liste von Belegen. Das schreibst du in Übung 1.
Schritt 2: Impact-Analyse für KI-Antworten
Die Frage “wer ist betroffen, wenn sich X ändert” gilt für alle Teile des Audit-Eintrags, nicht nur für Dokumente. Wird ein Modell abgelöst, willst du alle Antworten dieses Modells finden. Wird ein Prompt korrigiert, alle Antworten mit diesem Prompt-Hash. Ein Beispiel mit drei Einträgen (erfunden):
Ausgabe: ['a-1', 'a-3'] und ['a-1']. Antwort a-3 hat keine Belege und taucht darum nur bei der Modellsuche auf. Die Mengenschreibweise {...} sorgt dafür, dass eine ID nur einmal vorkommt, auch wenn mehrere Belege passen. In Übung 2 verbindest du die drei Kriterien.
Falle
- KI-Antworten ohne Audit. Das Modell, der Prompt und die Dokumente ändern sich. Ohne Eintrag zum Antwortzeitpunkt bleibt dir nur der Antworttext, und damit kannst du nichts belegen.
- Geheimnisse im Audit. Frage und Antwort im Klartext können personenbezogene Daten enthalten. Speichere Hashes oder maskiere (Lektion 13).
Übungen
Übung 1: Veraltete KI-Antworten finden (mittel bis anspruchsvoll)
Ein Mietwagen-Bot speichert für jede Antwort einen Audit-Eintrag. Beispiel:
{"antwort": "a-17", "modell": "m-1", "prompt": "p3",
"belege": [{"dokument": "preise.md", "hash": "3fa9c1d2"}, {"dokument": "agb.md", "hash": "b07e5a11"}]}Schreibe veraltete_antworten(audit, aktuell). audit ist eine Liste solcher Einträge, aktuell ein dict Dokumentname -> aktueller Hash. Gib die sortierte Liste der Antwort-IDs zurück, bei denen mindestens ein Beleg veraltet ist: Sein Hash weicht vom aktuellen ab, oder das Dokument steht gar nicht mehr in aktuell (gelöscht).
Die Hashes sind unterschiedlich lang gekürzt: Ältere Audit-Einträge speichern nur die ersten 8 Zeichen, aktuell kann dagegen den vollen SHA-256 (64 Zeichen) enthalten, und umgekehrt. Zwei Hashes gelten als gleich, wenn der kürzere der Anfang des längeren ist. Groß- und Kleinschreibung spielt keine Rolle (Hex-Schreibweise). Hashes sind nie leer. Jede ID steht höchstens einmal drin. Eine Antwort ohne Belege wird hier nicht gemeldet (das ist ein anderes Problem, Lektion 10). Die Eingaben dürfen nicht verändert werden.
Wie viele Belege prüfst du pro Antwort, und was soll mit einer Antwort passieren, die mehrere veraltete Belege hat? Was bedeutet es, wenn ein Dokument nicht mehr in aktuell steht? Reicht == für zwei Hashes unterschiedlicher Länge?
def veraltete_antworten(audit, aktuell):
def gleich(a, b):
a, b = a.lower(), b.lower()
n = min(len(a), len(b))
return a[:n] == b[:n]
ids = set()
for eintrag in audit:
for b in eintrag["belege"]:
neu = aktuell.get(b["dokument"])
if neu is None or not gleich(neu, b["hash"]):
ids.add(eintrag["antwort"])
return sorted(ids)
veraltete_antwortenÜbung 2: Betroffene Antworten finden (mittel)
Schreibe betroffene_antworten(audit, dokument=None, modell=None, prompt=None). audit ist eine Liste von Einträgen wie in Schritt 2 (antwort, modell, prompt, belege). Eine Antwort ist betroffen, wenn mindestens eines der angegebenen Kriterien auf sie zutrifft:
dokumentist angegeben und einer ihrer Belege nennt dieses Dokument,modellist angegeben und der Eintrag hat dieses Modell,promptist angegeben und der Eintrag hat diesen Prompt-Hash.
Kriterien mit dem Wert None zählen nicht mit. Sind alle drei None, ist niemand betroffen (leere Liste). Ein Eintrag kann das Feld belege ganz weglassen, das gilt wie eine leere Liste. Rückgabe: sortierte Liste der Antwort-IDs, jede höchstens einmal. Die Eingabe darf nicht verändert werden.
Beispiel mit dem audit aus Schritt 2: betroffene_antworten(audit, dokument="agb.md", modell="m-1") ergibt ["a-1", "a-2", "a-3"].
Wie verknüpfst du die drei Kriterien, wenn schon eines genügt? Was darf ein nicht angegebenes Kriterium nie auslösen, und was passiert mit Einträgen ohne Belege-Feld?
def betroffene_antworten(audit, dokument=None, modell=None, prompt=None):
ids = set()
for e in audit:
treffer = (
(dokument is not None and any(b["dokument"] == dokument for b in e.get("belege", [])))
or (modell is not None and e["modell"] == modell)
or (prompt is not None and e["prompt"] == prompt)
)
if treffer:
ids.add(e["antwort"])
return sorted(ids)
betroffene_antwortenÜbung 3: Was muss damals gespeichert worden sein? (leicht)
Ein Kunde beschwert sich: Dein Support-Bot nannte vor drei Wochen einen falschen Tarifpreis. Seitdem wurden zwei Dokumente neu eingespielt, und das Modell wurde einmal aktualisiert. Du sollst heute belegen, wie genau diese eine Antwort zustande kam. Was hätte zum Antwortzeitpunkt gespeichert werden müssen?
- a) Pro Antwort ein Eintrag mit Beleg-Hashes, Modellversion und Prompt-Version, damit sich der damalige Weg klären lässt.
- b) Der Antworttext mit Zeitstempel, denn alles Weitere lässt sich mit dem heutigen Stand der Dokumente jederzeit neu erzeugen.
- c) Nur die Modellversion, weil dasselbe Modell mit Temperatur 0 stets exakt dieselbe Antwort erzeugen würde, egal was es liest.
- d) Die Frage im Klartext samt Antwort, denn aus beidem lässt sich später ableiten, welche Belege verwendet wurden.
Trage den Buchstaben als String ein.
Was davon hat sich seit damals geändert, und kannst du den damaligen Stand aus dem heutigen zuverlässig zurückrechnen?
antwort = "a"
antwortMerksatz
Ein KI-Audit-Eintrag entsteht zum Antwortzeitpunkt und hält Belege mit Hash, Modell und Prompt fest, damit du bei jeder Änderung die betroffenen Antworten findest.
Prüfstein
Ein Kunde meldet eine falsche Tarifauskunft von vor drei Wochen. Welche vier Angaben liest du im Audit-Eintrag, wie erkennst du, ob der Beleg inzwischen veraltet ist, und welche Abfrage nutzt du, wenn du das Modell inzwischen gewechselt hast?
Karteikasten: so nutzt du die Karten
(Kurzer Einschub für den Lernalltag, nicht prüfungsrelevant. Du kannst ihn überfliegen.)
Zu jeder Lektion gibt es eine Datei im Ordner lernplattform/karteikarten/ (für diese Lektion ki-19-lineage-abschluss.md). Das Obsidian-Plugin “Spaced Repetition” findet sie im Vault. Die Quelle beschreibt für die Webversion ihres M0-Karteikastens das Leitner-Prinzip: 16 Karten, Boxen 1 bis 5, je höher die Box, desto seltener ist die Karte fällig. Umdrehen mit Klick oder Leertaste, bewerten mit den Tasten 1 (“nochmal”) und 2 (“kann ich”). Der Kasten wächst modulweise weiter. Wie oft eine Box fällig ist, nennt die Quelle nicht. Lege das selbst fest, aber lege es fest (bitte prüfen, was das Plugin bei dir einstellt).
Das Prinzip in sechs Zeilen Code. Die Regeln dieses Spielzeugs sind die übliche Leitner-Variante und keine Aussage der Quelle (sie nennt nur Boxen 1 bis 5 und die beiden Tasten): “Kann ich” schiebt die Karte eine Box nach oben (höchstens bis Box 5), “nochmal” schickt sie zurück in Box 1. Das Obsidian-Plugin plant nach eigenem Verfahren, nicht nach diesen Boxen (bitte prüfen).
k1 wandert in Box 2, dann in Box 3, und ein Patzer schickt sie zurück in Box 1. Anregungen für eigene Karten, abgeleitet aus dem M0-Kasten:
- Eine Karte, ein Gedanke. Die Frage hat genau eine richtige Antwort. Lieber drei kurze Karten als eine lange.
- Fachbegriff immer mit dem englischen Wort, weil Doku und Prüfungen englisch sind.
- Code-Vorhersagen nur nach dem Ausführen in eine Karte schreiben.
- Fortschritt sichtbar machen: Zähle ab und zu, wie viele Karten in Box 4 und 5 liegen. Das ist dein Lernstand in einer Zahl.
- Nach jedem Modul mischen: Der Track hat 19 Lektionen. Wer nur die neueste wiederholt, vergisst M0 wieder.
Projektaufgabe: M6-Abschluss-Check
Die Quelle (Baustein 04) fordert: Eine Pipeline generiert Prüfregeln automatisch aus einem Data Contract (nicht von Hand gepflegt) und prüft damit Rohdaten, mit einem mitgeführten Herkunftsprotokoll für jeden gefundenen Fehler. Die Aufgabenliste von M6 (Quelle, zusammen ca. 17 bis 22 h): Polars-Grundlagen an einem CSV-Export (3 bis 5 h), Data-Contract-Parser für FeldRegel-Objekte (5 bis 7 h), Prüffunktion gegen Rohdaten (3 bis 4 h), Herkunftsprotokoll einbauen (3 bis 4 h), Abschluss-Check end-to-end mit echtem Contract-Ausschnitt und zwei bewussten Fehlern (2 bis 3 h). Die ersten drei Punkte hast du in Lektion 18 geübt, der vierte war diese Lektion.
Aufgabe
Baue auf deinem Ergebnis aus Lektion 18 auf (Contract, abgeleitete Regeln, Prüfung) und ergänze:
- Herkunft je Zeile. Jede Zeile aus den Rohdaten bekommt
_herkunftmit Quelle (Dateiname), Version (Version des Contracts oder der Lieferung), Zeitpunkt und Hash des Zeileninhalts (Teil 1, Schritt 3, Übung 2). - Fehler mit Herkunft. Jeder gefundene Verstoß enthält die Regel, das Feld, den Wert und die Herkunft der Zeile (Quelle, Version, Hash). Aus einem Fehler allein lässt sich die Quellzeile wiederfinden.
- Zwei bewusste Fehler. Baue in synthetische Rohdaten (oder in
uebung/beispieldaten.json) zwei Verstöße ein, am besten von verschiedener Art, und prüfe, dass beide gefunden und mit Herkunft gemeldet werden. - Lineage-Graph. Beschreibe deine Pipeline als Graph (Quelle, Rohdaten, geprüfte Daten, Fehlerbericht) und beantworte mit einer Abfrage: Welche Berichte sind betroffen, wenn sich die Contract-Datei ändert?
- Reproduzierbarkeit. Zwei Läufe mit gleicher Eingabe und gleicher Version liefern denselben Hash für den Fehlerbericht. Der Zeitpunkt steht in den Metadaten, nicht im gehashten Inhalt.
- Kein Contract-Schweigen als Freigabe. Felder in den Rohdaten, die der Contract nicht nennt, werden als “nicht spezifiziert” gemeldet, nicht als gültig durchgewunken (Lektion 18).
Fertig, wenn
- Die Pipeline erzeugt die Prüfregeln aus der Contract-Datei, ohne dass du Regeln von Hand einträgst.
- Beide eingebauten Fehler werden gefunden, und jede Fehlermeldung nennt Quelle, Version und Hash der Quellzeile.
- Eine Abfrage auf dem Lineage-Graphen nennt die betroffenen Berichte, wenn sich eine Quelle ändert.
- Zwei Läufe mit gleicher Eingabe und Version ergeben denselben Hash des Fehlerberichts.
- Es steht in keiner Datei ein API-Schlüssel.
Selbstcheck
Optionale Lokal-Datei
lernlabor/uebung/ki/ki_19_m6_abschluss.py ist ein lauffähiges Gerüst für diese Aufgabe. Es braucht keinen Schlüssel und keine Installation: Es nutzt nur die Standardbibliothek, und wenn pyyaml fehlt, nimmt es einen eingebauten Ausschnitt des Contracts. Es liest uebung/data_contract_marktlokation.yaml und uebung/beispieldaten.json (nur lesen, nichts wird verändert).
cd lernlabor && uv run python uebung/ki/ki_19_m6_abschluss.pyDas Gerüst leitet Regeln ab, prüft die Zeilen, hängt jedem Fehler die Herkunft an, baut einen kleinen Lineage-Graphen, beantwortet die Impact-Frage und prüft die Reproduzierbarkeit. Deine Aufgaben stehen im Kopf der Datei (zum Beispiel ein zweiter Datensatz im Graphen und die Regel “nicht spezifiziertes Feld”). Ausgabe (ausgeführt mit Python 3.13.9, pyyaml vorhanden; ohne pyyaml, wie im Lernlabor-Projekt, steht in der ersten Zeile 8 Regeln statt 10, der Rest bleibt gleich):
10 Regeln aus dem Contract abgeleitet, 4 Zeilen geprueft, 2 Fehler gefunden.
Fehler: marktlokations_id verletzt pattern (Wert '12345') | Quelle beispieldaten.json, Version 1.0, Hash ef3e5c8c49
Fehler: sparte verletzt erlaubt (Wert 'WASSER') | Quelle beispieldaten.json, Version 1.0, Hash 9cdfa048a6
Lineage, upstream von 'fehlerbericht': ['data_contract.yaml', 'regeln', 'rohdaten.json', 'zeilen_mit_herkunft']
Aendert sich data_contract.yaml, betroffene Berichte: ['fehlerbericht']
Aendert sich rohdaten.json, betroffene Berichte: ['fehlerbericht']
Hash Lauf 1: 56370308227d Hash Lauf 2: 56370308227d reproduzierbar: True
Rückblick auf den Lernpfad: Track KI
Der Lernplan nennt für jedes Modul ein “Fertig, wenn”. Hier steht, welche Lektion welchen Punkt beantwortet. Die Zeilen sind aus der Quelle (quellen/lernplan-technisch.md), die Lektionen sind die Dateien in lernplattform/ki/.
| Modul | Fertig, wenn (Quelle) | Lektionen |
|---|---|---|
| M0 Python produktiv | Repo läuft bei jemand anderem mit uv sync, Tests grün, ruff/Typechecker sauber |
01 uv, src-Layout, ruff, 02a Type Hints, 02b Pydantic, 03a async, 03b pytest, 04 Logging, Fehler, Abschluss |
| M1 LLM-Anwendungen | Unstrukturierter Text wird zuverlässig zu validiertem Pydantic-Objekt, mit Tests und Kostenlogging | 05 Messages-API, Prompting, 06a Structured Outputs, 06b Tool Calling, 07a Kosten, Streaming, 07b Retries, Abschluss |
| M2 RAG | Belegbare Antworten auf eigenem Korpus, die 5 schlechtesten Antworten sind erklärbar | 08 Chunking, Ähnlichkeitssuche, 09 pgvector, Hybrid Search, 10a RAG, Reranking, 10b Belege, Fehleranalyse, Abschluss |
| M3 Evaluation und Guardrails | Belastbare Zahl für M2 genannt, Modellwechsel löst automatisch Testlauf aus | 11a Fehleranalyse, Golden Set, 11b Metriken, 12a Judge-Prompt, 12b Judge-Bias, 13a Injection, 13b Guards, PII, Abschluss |
| M4 Agenten und Workflows | Agent löst mehrstufige Aufgabe, protokolliert, fragt bei Unsicherheit nach | 14a Agent oder Workflow, 14b Tool-Use-Schleife, 15a MCP, Human-in-the-Loop, 15b Fehler, Tracing, Abschluss |
| M5 Betrieb | Containerisiert, Kosten und Latenz für 100 Anfragen sichtbar, einmal komplett lokal gelaufen | 16a Docker, 16b Observability, 17 Kosten, Latenz, lokale Modelle, EU AI Act, Abschluss |
| M6 Datenseite | Pipeline generiert Prüfregeln aus einem Data Contract und prüft Rohdaten danach | 18 Polars, Datenqualität, Data Contract, 19a Lineage, diese Lektion 19b (Audit-Trail, Abschluss) |
So liest du die Tabelle: Wenn du bei einem “Fertig, wenn” zögerst, öffne die Lektion in der letzten Spalte und mache die Projektaufgabe dort noch einmal. Die Abschluss-Checks sind absichtlich die Prüfung, ob du das Modul wirklich kannst.
Zwei Verbindungen, die der Rückblick sichtbar macht: Lineage und Audit-Trail sind Tracing für Daten (Lektion 15 und 16), und die Menge besuchter Knoten in der Graphsuche ist dieselbe Idee wie das Schrittlimit der Agenten-Schleife (Lektion 14 und 15): Jede Schleife braucht eine Grenze. Wer auf Zyklen und fehlerhafte Daten nicht vorbereitet ist, hängt irgendwann.
Was nach dem Track kommt, steht in der Quelle: Die Verwertung läuft parallel mit (1 bis 2 Stunden je Modul: Repo öffentlich machen, kurzer Beitrag zur überraschendsten Erkenntnis, eine Folie für die Angebotsmappe). Nach M3 reicht es laut Quelle für ein erstes verkaufbares Format (“LLM Eval Audit”), nach M5 für einen kompletten Piloten. Die Soft-Skills-Module S1 bis S4 laufen im selben Wochenzyklus (Track softskills). Veröffentlichen gehört dir: Das macht diese Plattform nicht für dich.
Quelle: quellen/kursbuch-lerninhalte.md, Modul M6, Baustein “03 Lineage nachvollziehbar machen” und “04 Abschluss-Check” mit der Aufgabenliste M6 (Stundenangaben) und der Abschnitt “Karteikasten · M0 Python produktiv” (Leitner-Prinzip, Boxen 1 bis 5, 16 Karten, Bedienung). Dazu quellen/lernplan-technisch.md (Modulplan M0 bis M6, “Fertig, wenn”, Verwertung). Über die Quelle hinaus (allgemeines Fachwissen, mit echtem Python 3.13 ausgeführt): Audit-Trail für RAG-Antworten (Belege mit Dokument-Hash, Modellversion, Prompt-Hash, Trace-ID, Frage als Hash), Vergleich gekürzter Hashes über den gemeinsamen Anfang, Impact-Analyse nach Dokument, Modell und Prompt. Modell- und Dokumentnamen in den Beispielen sind erfunden. Dass Lineage-Werkzeuge wie OpenLineage Graphen automatisch erzeugen, ist eine Aussage der Quelle. Ob es für deinen Anwendungsfall passt, ist nicht geprüft (bitte prüfen).