Files
ClawdDotNet/docs/Audit-Konzept.md
T

4.3 KiB

Audit-Log & Receipts — Nachvollziehbarkeit

Setzt A3 aus der Roadmap um (F-A2). Zwei zusammengehörige Dinge:

  • Audit-Log — ein Eintrag je Tool-Aufruf: wer, wann, welches Tool, mit welchem Ausgang.
  • Receipts — ein Abschluss-Beleg je Lauf: Ergebnis, Schritte, Tokens, Kosten, verknüpft mit dem Task.

A3 ist das Fundament für A2 (jede Staging-Entscheidung wird als Datensatz verankert) und für C7 („Kosten pro Ergebnis", fällt aus den Receipts ab). Die Tool-Fehlerquote aus der Leistungsanalyse liest sich direkt aus dem Log.

Warum eigene Tabellen, nicht der State-Store

Dieselbe Überlegung wie bei Gedächtnis und Taskboard: IStateStore ist Schlüssel-Wert. Ein Log, das man nach Lauf, Task oder Tool filtern und dessen Fehlerquote man auswerten will, braucht typisierte Spalten. Zwei Tabellen auf dem vorhandenen SqliteStorage: AuditLog und RunReceipts.

Provenienz — von der Engine gestempelt, nie vom Agenten behauptet

Die entscheidende Regel (aus dem OpenAlice-Provenance-Konzept):

  • Herkunft stempelt die Engine. AgentId, Model und Source kommen aus dem Wissen der Engine über den Lauf, nicht aus dem Tool-Ergebnis. Ein Tool kann seine Herkunft nicht fälschen, weil es sie gar nicht schreibt.
  • Einträge sind unveränderlich. Das Repository hat kein Update und kein Delete — eine Korrektur ist ein neuer Eintrag. Das ist die eigentliche Zusage, keine fehlende Funktion.
  • Unbekanntes wird als unbekannt markiert, nicht geraten. Fehlt die Quelle, steht unknown, nicht ein plausibel geratener Kanal.
  • Worker-Typ und Session sind getrennt. Model (das ausführende Modell) und Source (die verantwortliche Session: webview, telegram, task, agentcomm, job, direct) sind verschiedene Begriffe und stehen in eigenen Spalten.

Audit-Log

Gestempelt an genau einer Stelle: AgentEngine.ExecuteToolCallAsync — dort, wo jeder Tool-Aufruf durchläuft. Je Aufruf ein Eintrag mit Ausgang:

Status Wann
Ok Tool lief und lieferte ein Ergebnis
Error Tool meldete einen Fehler oder warf
Denied das PermissionGate hat abgelehnt
NotFound Tool dem Agenten nicht zugewiesen/unbekannt

Ein Abbruch (Cancellation) wird nicht protokolliert — der Aufruf kam nicht zum Abschluss. Die Argumente werden roh, aber gekappt abgelegt (4 000 Zeichen); die Ausgangsnotiz kurz (500).

Best effort: Ein Fehler beim Schreiben des Audits darf den Lauf nie scheitern lassen — dieselbe Linie wie bei der Verbrauchserfassung. Der Eintrag wird geschrieben, nachdem die eigentliche Arbeit getan ist.

Receipts

Je Lauf ein Beleg, geschrieben beim Abschluss von RunAsync/ChatAsync (neben der vorhandenen RunUsage-Erfassung). Er trägt Status, Schritte, Prompt-/Completion-/ Cached-Tokens, geschätzte Kosten (aus dem ModelPricingCatalog, mit CostIsKnown-Flag) und einen kurzen Ergebnis-Verweis.

Verknüpfung RunUsage ↔ Task: Der Receipt trägt die TaskId, wenn der Lauf aus dem Taskboard kam — der EngineTaskDispatcher reicht sie (samt source: task) durch. Damit ist „Kosten pro Ergebnis" (C7) ein Abfallprodukt: ListReceiptsForTaskAsync liefert alle Belege zu einem Task.

RunId — die Klammer

Jeder Lauf bekommt zu Beginn eine RunId (GUID). Alle Audit-Einträge und der Receipt eines Laufs tragen sie. So lässt sich ein Lauf lückenlos rekonstruieren: ListForRunAsync(runId) gibt die Aufrufe in Reihenfolge, GetReceiptForRunAsync(runId) den Abschluss.

Verdrahtung

IAuditRepository ist optional (wie Gedächtnis und Taskboard): ohne Repo läuft die Engine unverändert. In Program.cs wird ein SqliteAuditRepository auf der Instanz-DB erzeugt und der Engine übergeben.

Offen

  • Output-Scrubbing — die Arguments können Secrets enthalten. Das zentrale Maskieren bekannter Secret-Werte (eigener beschlossener Roadmap-Punkt) greift, sobald es steht; der Andockpunkt (ExecuteToolCallAsync) ist derselbe.
  • Review-Oberfläche — die Anzeige/Durchsicht des Logs und der Receipts gehört zu A2 (Staging-Review im Hauptfenster); die Abfragemethoden dafür stehen bereit.
  • Export — ein JSONL-Export des Logs wäre für externe Auswertung nützlich (später, passt zum A6-Spiegel).