Ai Workspace
Find a file
2026-10-28 10:19:27 +01:00
app Fehlerhandling ausgearbeitet 2026-10-28 10:19:27 +01:00
beispiele initial commit 2026-10-27 08:34:17 +01:00
docs Update auf Postgres18 2026-10-28 10:02:54 +01:00
scripts Fehlerhandling ausgearbeitet 2026-10-28 10:19:27 +01:00
services/db-mcp initial commit 2026-10-27 08:34:17 +01:00
tests Erweiterung um Demo, Bugfixes, Dokumentation und Änderungen 2026-10-27 22:35:29 +01:00
.env.example initial commit 2026-10-27 08:34:17 +01:00
.gitignore initial commit 2026-10-27 08:34:17 +01:00
ai_workspace_architekturkonzept.md initial commit 2026-10-27 08:34:17 +01:00
docker-compose.yml Update auf Postgres18 2026-10-28 10:02:54 +01:00
pyproject.toml initial commit 2026-10-27 08:34:17 +01:00
README.md Update auf Postgres18 2026-10-28 10:02:54 +01:00
uv.lock initial commit 2026-10-27 08:34:17 +01:00

Bifröst – AI Workspace

Kontextgebundene KI-Arbeitsplattform, zusammen mit Hugin (Register für Regeln, Skills und Befehle; von dort stammt auch das Layout der Oberfläche) und Munin (Gedächtnis). Bifröst ist die Brücke zwischen den Welten: Sie verbindet Daten, KI und die Menschen, die jeden Schritt nach außen freigeben. In Texten Bifröst, technisch ohne Umlaut (bifrost); CLI aiw, Variablen AIW_* und Metriken aiw_* bleiben unverändert. Architektur und Entscheidungen: ai_workspace_architekturkonzept.md.

Stand: Phase 3 plus MCP-Server zentral und je Workspace, Uploads mit DuckDB-Analyse, lesende Datenbanken (PostgreSQL, Oracle) als MCP-Verbindungen, bereinigtes Markdown, Munin als Gedächtnis, Regeln, Skills und Befehle aus Hugin, Bereich Repository, abgeglichene Ordner mit Dokumenten-Auslese, feinere Policies mit globalen Approvern sowie Nutzung, Kosten und Audit-Log im UI. Es gibt Anmeldung, Teams, Nutzer, Workspaces mit Rollen, Git-Quellen und Chat. Jede Nachricht wird als Run im Worker verarbeitet; das Modell antwortet über die Responses API und liest Repositories mit eingestuften, lesenden Git-Tools. Jeder Chat hat seinen eigenen Branch ai/…: Die KI committet Änderungen frei darauf (commit_change); pushen und den Pull Request (Bitbucket Data Center) erstellen nur Menschen. Tools, die eine Freigabe brauchen, pausieren den Run, ohne einen Worker zu belegen. Antworten und Tool-Schritte werden per SSE gestreamt. Modelle, API-Keys und Tool-Einstufung werden im UI verwaltet. Antworten werden als Markdown serverseitig gerendert und mit nh3 bereinigt (keine externen Bilder, Linkziele sichtbar). Chats lassen sich umbenennen und löschen.

Lokal starten

Voraussetzungen: Docker, uv, git.

docker compose up -d          # PostgreSQL 18 auf 127.0.0.1:55433
cp .env.example .env
uv sync
uv run aiw gen-key           # Ausgabe als AIW_SECRET_KEY in .env eintragen
uv run aiw migrate
uv run aiw seed-demo          # Team "demo": admin (Plattform-Admin), entwicklung, fachbereich, freigabe
uv run aiw demo               # optional: Demo-Workspaces Bestellservice und MCP-Entwicklung
uv run aiw web                # http://127.0.0.1:8000
uv run aiw worker             # zweites Terminal

Kürzer mit dem Startskript (PostgreSQL, Migrationen, Web und Worker in einem Terminal; Ersteinrichtung mit --setup, von null mit --reset, Oracle mit --oracle; Details in docs/lokal-starten.md):

scripts/dev.sh                # http://127.0.0.1:8765, Strg+C stoppt alles
scripts/dev.sh --reset        # von null mit Demo-Workspaces; löscht alle lokalen Daten (mit Rückfrage)

Zum Entwickeln mit sichtbaren Logs, Health-Checks, Metriken und Oracle-Quellen: docs/lokal-starten.md. Vergleich mit OpenCode, Lücken und Arbeitsplan: docs/opencode-vergleich.md.

Anmelden mit admin und dem Passwort aus AIW_DEMO_PASSWORD. Weitere Nutzer legt ein Plattform-Admin unter Administration → Nutzer an; sie bekommen ein Einmal-Passwort und setzen beim ersten Login ein eigenes. Ohne Demo-Passwort (z. B. auf einem Server) den ersten Admin mit uv run aiw create-admin <name> anlegen. Danach einen Workspace anlegen und ein Git-Repository verbinden. Zum Ausprobieren reicht ein lokales Repository, z. B. file:///pfad/zu/einem/repo (dafür AIW_GIT_ALLOW_LOCAL=true setzen). Der Chat erscheint erst, wenn eine Quelle erfolgreich getestet wurde.

Demo-Workspaces: uv run aiw demo legt drei Workspaces an (Inhalte in app/demo/, reproduzierbar). Mitglieder sind jeweils die Demo-Nutzer, benannt nach ihrer Rolle: admin (Workspace-Admin), entwicklung und fachbereich (Mitglieder), freigabe (Freigeber). Die beiden Workspaces mit Daten haben je ein lokales Bare-Repository unter data/demo/ (drei Wochen Verlauf von Admin, Entwicklung und Fachbereich, ein Feature-Branch), einer Datei zur Analyse, einem Dokumentenordner und sechs Beispielfragen. Protokolle sind relativ zu den Commits datiert, damit die Geschichte an jedem Tag stimmt.

  • Demo: Bestellservice (bestellservice): kleiner Python-Dienst, bestellungen_2026.csv (1200 Bestellungen), Projektunterlagen (Protokolle, Wartungsvertrag, Betriebshandbuch). Eingebauter Widerspruch: Der Code rechnet mit 10 % Rabatt ab 500 €, die spätere Abstimmung beschließt 5 % ab 1.000 €. Dazu die Shop-Datenbank (app/demo/shop_db.py): eine eigene Datenbank <datenbank>_demo_shop im PostgreSQL von Bifröst mit Kunden, Artikeln, Bestellungen, Positionen und Retouren (2025/2026) und der nur lesenden Rolle demo_leser, angebunden über den PostgreSQL-Treiber. Läuft der DB-Dienst aus scripts/dev.sh (Port 8791), registriert die Demo den Treiber (oder nutzt einen vorhandenen mit derselben URL), stuft seine lesenden Tools ein und verbindet die Datenbank; sonst überspringt sie diesen Teil mit Hinweis. In einem schon vorhandenen Demo-Workspace kommt die Datenbank ohne --reset dazu. URL und Schlüssel des Dienstes lassen sich mit AIW_DEMO_DBMCP_URL und AIW_DEMO_DBMCP_KEY ändern.
  • Demo: MCP-Entwicklung (mcp): MCP-Server ticket-mcp für ein Service-Desk (FastMCP, Streamable HTTP, Tests, AGENTS.md), tool_aufrufe_2026_10.csv (2000 Aufrufe aus der Testumgebung), MCP-Unterlagen (interner Leitfaden, MCP in Kürze, Anbindung an Bifröst, Service-Desk-API, Review-Protokoll). Eingebaut: close_ticket ist als readOnlyHint markiert, Bifröst würde es also als lesend vorschlagen; search_tickets hat keine Obergrenze. Das Review hat beides beanstandet, das Log zeigt die Folgen (große, langsame Suchen).
  • Demo: Nur Chat (chat): keine Datenquellen; die Einstellung Ohne Datenquellen chatten ist an. Drei Beispielfragen stehen wörtlich auch im Bestellservice (Wartungsvertrag, Rabatt laut Protokoll, Retouren), dazu allgemeine Aufgaben. So sieht man nebeneinander, was die Datenanbindung ausmacht: Hier sagt die KI, dass sie Vertrag, Code und Datenbank nicht kennt.

Ein zweiter Aufruf ändert nichts (außer die Shop-Datenbank nachzutragen bzw. neu zu befüllen); aiw demo mcp --reset legt eine Demo neu an (löscht dabei ihre Chats). Braucht AIW_GIT_ALLOW_LOCAL=true; das Modell richtet weiterhin ein Plattform-Admin ein. Die eingebauten Tools stuft der Befehl nach Vorschlag ein, falls noch nicht geschehen.

Beispielfragen und Rückmeldungen: Workspace-Admins hinterlegen unter Einstellungen → Allgemein bis zu acht Beispielfragen (workspaces.settings["examples"]); Übersicht und leerer Chat zeigen sie, ein Klick stellt die Frage. Unter jeder Antwort fragt Bifröst „Hilfreich?“ (👍/👎, optional Kommentar; answer_feedback, eine Rückmeldung je Person und Antwort). Nutzung und Kosten zeigt den Anteil hilfreicher Antworten, je Modell und die Kommentare mit Link zur Antwort, dazu einen CSV-Export für die Auswertung des PoC.

Alles integrieren: Schritt für Schritt, was nach dem Start noch einzurichten ist (Modell, Tools, Munin, Hugin, Dokumenten-Auslese, Bitbucket, Benachrichtigungen), steht in docs/einrichtung.md, zum Abhaken in docs/checkliste.md.

Modell einrichten (einmalig, als Plattform-Admin admin): unter Modelle und Zugänge einen Endpunkt anlegen (https://api.openai.com/v1), einen API-Key für alle Teams hinterlegen und das Modell gpt-6-luna hinzufügen. Das erste Modell wird automatisch Standard. Mit Verbindung testen prüfen. Danach unter Tools die Git-Tools einstufen (Vorschläge übernehmen); bis dahin sind sie gesperrt. Später kommt das AI Gateway als weiterer Endpunkt hinzu, mit einem Key je Team.

Schutz vor Prompt Injection (app/security): Die harte Grenze sind Plattform-Regeln, die nicht vom Modell abhängen (angebotene Tools, Policies, Freigaben, Rechte, Push nur durch Menschen). Zusätzlich gehen alle fremden Inhalte (Tool-Ergebnisse, Dateien, Kommentare, Konfliktdateien, Dienst-Anleitungen) eingefasst als <fremdinhalt …> an das Modell; reservierte Tags im Inhalt werden entschärft. signals.py erkennt typische Injection-Formulierungen, blockiert nichts, sondern protokolliert (untrusted_signal, Metrik aiw_untrusted_signals_total) und kennzeichnet den Inhalt. Neue Muster dort ergänzen und ein Beispiel in tests/injection_corpus.py aufnehmen; tests/test_security.py spielt Szenarien mit einem Modell durch, das der Injection folgt, und prüft, dass alle schreibenden Routen ein CSRF-Token verlangen.

Datenbank-Treiber (services/db-mcp): Lesende MCP-Server für Datenbanken, ein Dienst je Datenbanktyp mit gemeinsamem Kern: db-mcp-postgres, db-mcp-oracle. In Bifröst heißen sie Datenbank-Treiber: Ein Plattform-Admin registriert jeden einmal unter Administration → Datenbank-Treiber (URL und Schlüssel des Dienstes) und stuft seine vier Tools ein. Ein Treiber hat keine eigenen Daten; sein Schlüssel öffnet nur den Dienst. Die Zugangsdaten der Datenbanken tragen Workspace-Admins ein; Bifröst schickt sie je Aufruf als Header. Details im README des Dienstes.

Online-Hilfe: Der Knopf Hilfe oben führt zur passenden Stelle der Hilfe für die aktuelle Seite (/hilfe). Sie ist nur nach Anmeldung erreichbar und enthält Anleitungen für Nutzer, Freigeber und Prüfer, Workspace- und Plattform-Admins, dazu Begriffe und häufige Fragen, mit Suche. Die Seiten liegen als Markdown in app/help/pages/ und werden mit dem Code gepflegt. Ein Test meldet, wenn ein neues eingebautes Tool in der Admin-Hilfe fehlt.

Projektregeln: Liegt im Repository eine AGENTS.md (Code-Stil, Struktur, Befehle, Commit-Nachrichten), gibt die Plattform sie der KI als Projektkonvention mit, vom selben Stand, den die Tools lesen (Chat-Branch, sonst Ziel-Branch), höchstens 8 KB. Sie ist nachrangig zu den Regeln der Plattform und kann keine Rechte erweitern. Jede Antwort zeigt, welche Regeldatei galt.

Plan und Planungsmodus: Das Tool update_plan (Vorschlag write_internal) unter Tools einstufen. Bei größeren Aufgaben legt die KI damit einen Plan aus Schritten an, die sie als offen, in Arbeit oder erledigt markiert. Er steht oben im Chat, geht in jede Anfrage, in die Arbeitsnotiz und in die PR-Beschreibung ein, und Weiterarbeiten setzt beim nächsten offenen Punkt an. Mit Erst planen am Eingabefeld (bei vorhandenem Plan: Plan überarbeiten) schaltet man den Chat in den Planungsmodus, beliebig oft im Wechsel mit dem normalen Chat: Die KI bekommt nur lesende Tools und update_plan und ändert nichts. Ruft sie trotzdem ein schreibendes Tool auf, wird der Aufruf abgewiesen. Plan umsetzen beendet den Modus und startet die Umsetzung.

Lange Sessions: Der Verlauf geht nach Token-Budget an die KI (AIW_HISTORY_MAX_TOKENS, Standard 60 000, höchstens 60 % des Kontextfensters). Ältere Tool-Ausgaben werden gekürzt, und was nicht mehr passt, fasst die KI in einer Arbeitsnotiz zusammen, die oben im Chat einsehbar ist. Die Tool-Schritte je Antwort stellt der Workspace-Admin unter Einstellungen → Allgemein → Antworten ein (Standard AIW_MAX_TOOL_ROUNDS=12, höchstens AIW_MAX_TOOL_ROUNDS_LIMIT=40). Ist das Limit erreicht, fasst die KI den Stand zusammen, und Weiterarbeiten setzt fort. Jede Antwort zeigt, wie voll der Kontext war, und der Chat zeigt Tokens und Kosten bisher.

Code ändern (Chat = Branch): Die Tools commit_change (Vorschlag write_internal, erlaubt) und git_pr_comments (lesend) unter Tools einstufen. Der Ablauf:

  1. Committen: Die KI legt Änderungen mit commit_change als Commits auf den Branch des Chats ai/c<chat>-<titel>. Der Branch entsteht beim ersten Commit und beginnt auf dem aktuellen Stand des Ziel-Branches. Je Commit gibt es keine Freigabe; die lesenden Git-Tools sehen danach den Stand des Branches.
  2. Prüfen: Die Ansicht Änderungen oben im Chat zeigt Branch, Commits und Diff. Noch nicht übertragene Commits lassen sich einzeln zurücknehmen (Letzten Commit zurücknehmen).
  3. Übertragen: Die Commits bleiben in der Plattform, bis ein Mitglied des Workspace klickt:
    • Branch pushen, z. B. um lokal oder in der CI zu testen.
    • Pull Request erstellen: Pushen und den PR anlegen, standardmäßig als Entwurf, später Als bereit markieren. Die Beschreibung bekommt Commits, Autor und einen Link zum Chat (mit AIW_PUBLIC_URL).
    • Änderungen übertragen: Spätere Commits gehen erst auf diesen Klick in den offenen PR.
  4. Review: Der Worker fragt offene PRs alle AIW_PR_POLL_SECONDS (300) ab; Status aktualisieren fragt sofort ab. Bei neuen Kommentaren, Merge oder Ablehnung bekommen Chat-Ersteller und PR-Ersteller eine E-Mail. Kommentare einarbeiten startet eine Antwort, in der die KI die Kommentare mit git_pr_comments liest und neue Commits anlegt. In Bitbucket antworten kann die KI nicht. Nach Merge oder Ablehnung löscht die Plattform den Branch im Git-Server, wenn er noch auf dem zuletzt übertragenen Stand steht (abschaltbar je Quelle; der technische Account braucht das Recht, ai/* zu löschen).
  5. Abschluss: Nach Merge oder Ablehnung ist der Branch geschlossen; der nächste Commit im selben Chat beginnt einen neuen Branch (…-2).

commit_change nimmt einzelne Änderungen (ersetzen, neu schreiben, löschen) oder einen Unified Diff über mehrere Dateien (patch, auch Löschen und Umbenennen). Beim Commit prüft die Plattform Python, JSON, TOML, XML und YAML auf Syntax, ohne Code auszuführen. Fehler sind nur Warnungen: Der Commit entsteht trotzdem, die KI bekommt die Warnungen zurück, und sie stehen in der Ansicht Änderungen am Commit.

Repository: Hat ein Workspace eine Git-Quelle, zeigt die Seitenleiste Repository. Der Reiter Neu listet die Commits des Ziel-Branches nach Tagen (Filter: Zeitraum, Autor, Pfad) und hebt hervor, was seit dem letzten Besuch der Person dazugekommen ist (repo_visits). Ein Klick zeigt den Commit mit Diff. Branches listet alle Branches des Servers mit Vorsprung und Rückstand gegenüber dem Ziel-Branch; Chat-Branches verweisen auf ihren Chat und den PR-Status. Pull Requests listet offene PRs aus Bitbucket Data Center (Autor, Branches, Reviewer, Kommentare) mit Diff gegenüber dem gemeinsamen Stand; Dateien zeigt Baum und Dateien eines beliebigen Branches mit Verlauf. Von dort starten Chats, die neue Commits zusammenfassen, einen Commit oder eine Datei erklären, einen Pull Request reviewen oder auf einem bestehenden Branch arbeiten: Dann lesen die Tools diesen Branch, der Chat-Branch zweigt davon ab, und Abgleich und Pull Request beziehen sich auf ihn (chat_sessions.git_base). Die Seite liest aus dem Klon der Plattform und holt höchstens einmal pro Minute alle Branches (refs/remotes/origin/*); Aktualisieren holt sofort. CI-Status wird nicht angezeigt.

Abgeglichene Ordner: Für Dokumente auf dem Rechner einer Person, die sich regelmäßig ändern. Der Browser gleicht einen lokalen Ordner ab (showDirectoryPicker mit gemerktem Handle in IndexedDB, sonst <input webkitdirectory>): Er holt /folders/{id}/manifest (Pfad → Größe, Änderungszeit), lädt nur Neues und Geändertes einzeln hoch und meldet zum Schluss die vorhandenen Pfade (prune). Bifröst speichert nur den Text (doc_files, Volltextindex german); gleicher Inhalt (SHA-256) wird nicht neu ausgelesen. Textformate dekodiert Bifröst selbst; Office, PDF und Bilder schickt der Worker als Base64 an den zentralen MCP-Server der Art Dokumenten-Auslese (Tool und Parameter wählt der Plattform-Admin). Sichtbarkeit je Ordner: nur ich (nur in eigenen Chats bei eigenen Nachrichten; die Antworten sehen trotzdem alle Mitglieder) oder ganzer Workspace (mit Bestätigung). Die KI liest mit docs_list, docs_search, docs_read. Der Text zählt aufs Speicherkontingent. Lokale stdio-MCP-Server erreicht Bifröst nicht; Dienste laufen zentral über HTTP.

Mit main abgleichen: Hat der Ziel-Branch neue Commits, zeigt die Ansicht main ist weiter. Mit main abgleichen übernimmt sie per Merge-Commit in den Chat-Branch. Es gibt kein Rebase, bereits übertragene Commits bleiben also gültig, und der nächste Push ist ein normaler Fast-Forward. Der Diff zeigt danach weiter nur die eigenen Änderungen. Bei Konflikten passiert nichts. Die Ansicht zeigt dann Konflikt mit main, und Konflikte von der KI lösen lassen startet eine Antwort, in der die KI mit dem Tool merge_target (Vorschlag write_internal) die Konfliktdateien mit Markern liest und für jede eine Lösung angibt. Auch dieser Merge-Commit bleibt in der Plattform, bis ein Mensch ihn überträgt.

Gepusht wird nie mit Überschreiben. Steht der Branch auf dem Server auf einem anderen Stand, z. B. nach Commits von außen, bricht der Push ab. Geht der Klon verloren, holt die Plattform gepushte Branches vom Server oder baut die Commits aus den gespeicherten Änderungen exakt nach. Übertragen wird per git push über HTTPS aus dem Klon der Plattform; das Token geht als Header mit, nie in der URL. Ohne Nutzername an der Quelle als Bearer (Bitbucket Data Center), mit Nutzername als Basic mit dem Token als Passwort (Gitea, Forgejo, GitLab, GitHub, klassische HTTPS-Server). SSH wird nicht unterstützt. Für Bitbucket Data Center an der Quelle Pull Requests: Bitbucket Data Center wählen und ein HTTP Access Token mit Schreibrecht hinterlegen, das per Branch Permission auf ai/* beschränkt ist. Für ai/*-Branches sollte die CI keine Secrets bekommen.

Dateien analysieren: im Workspace unter Dateien zur Analyse CSV, TSV, JSON(L) oder Parquet hochladen; jede Datei wird zu einer DuckDB-Tabelle. Die Tools data_list_tables, data_describe_table und data_query einstufen (Vorschlag: lesend, erlaubt). Abfragen laufen schreibgeschützt ohne Datei- und Netzzugriff, mit Zeit-, Speicher- und Zeilenlimit. Ein Workspace mit Upload ist auch ohne Git-Quelle chatbereit.

Datenbank anbinden (lesend): Im Workspace unter Einstellungen → MCP-Server → Datenbank hinzufügen den Datenbanktyp (einen der Treiber), Kurzname, Host, Datenbank, Nutzer und Passwort angeben. Der Host muss in AIW_DB_ALLOWED_HOSTS stehen, lokal reicht AIW_DB_ALLOW_LOCAL=true. Empfohlen ist ein Nutzer nur mit SELECT; der Verbindungstest meldet Schreibrechte. Jede Abfrage läuft in einer schreibgeschützten, zurückgerollten Transaktion (genau eine Anweisung), mit Zeitlimit und Zeilenlimit. Die Tools heißen nach dem Kurznamen (shop__list_tables, shop__describe_table, shop__query) und haben die Einstufung des Treibers; unter Administration → Tools erscheinen nur die Tools des Treibers. Vorgaben im Workspace unter Tools gelten je Datenbank. Den SQL-Dialekt beschreibt der Treiber in seiner Anleitung. Ergebnisse mit Spalten und Zeilen erscheinen als Tabelle mit CSV und Diagramm. Zum Ausprobieren: beispiele/demo_shop.sql (Anleitung im Kopf der Datei).

Nutzung, Kosten, Audit: Unter Administration → Nutzung und Kosten Tokens und Kosten je Team, Workspace, Modell, Nutzer oder Tag (Preise je 1 Mio. Tokens am Modell, Währung AIW_COST_CURRENCY), dazu unter Administration → Runs die Run-Liste. Administration → Audit-Log mit Filtern und CSV-Export. Workspace-Admins sehen beides für ihren Workspace. Verbrauch wird je Modellaufruf festgehalten und bleibt beim Löschen von Chats erhalten.

Freigaben feiner steuern: Je Tool legt der Plattform-Admin die Freigabestufe fest (Workspace, also Admin oder Freigeber, oder globaler Approver; globale Approver unter Nutzer) und ob das Vier-Augen-Prinzip gilt. Unter Einstellungen → Allgemein → Freigaben kann ein Workspace Modus B wählen: Dann braucht jede Schreibaktion in ein Fremdsystem (write_external) eine Freigabe, auch wenn das Tool erlaubt ist. Workspaces können zusätzlich Vier Augen verlangen, Tools einer bestimmten Datenbank verschärfen (z. B. prod__query nur mit Freigabe) und Regeln je Quelle für Repositories setzen. Auch lesende Tools können so eine Freigabe verlangen; die KI erhält das Ergebnis erst nach der Entscheidung. Vor der Ausführung werden die Rechte des Entscheiders erneut geprüft.

Workspace-Aufbau: Die Übersicht zeigt die Chats (mit Suche über Titel und Nachrichten) und eine Zeile, welche Daten bereitstehen. Unter Daten liegen Datenquellen (verbinden, testen und bearbeiten nur Admins) und Dateien zur Analyse (hochladen dürfen alle Mitglieder). Für Admins liegt der Rest unter Einstellungen in Reitern: Allgemein (Name, Beschreibung, Modell, Webhook, Löschen), Mitglieder (Rolle direkt ändern), Tools und Regeln, MCP-Server.

Rollen im Workspace: Mitglied chattet und lädt Dateien hoch. Freigeber entscheidet zusätzlich Freigabeanträge des Workspace und bekommt die Benachrichtigungen dazu, verwaltet den Workspace aber nicht. Admin darf beides und verwaltet Einstellungen, Mitglieder, Quellen und Tools. Anträge der Stufe globaler Approver entscheiden weiterhin nur globale Approver.

Speicherkontingent: Dateien zur Analyse zählen je Workspace gegen ein Kontingent, Standard AIW_WORKSPACE_STORAGE_MB (500 MB). Gezählt wird die Größe der DuckDB-Tabelle (mindestens etwa 0,5 MB je Datei, bei großen Dateien meist weniger als das Original). Die Seite Daten zeigt die Belegung und warnt ab 80 %. Ist das Kontingent voll, wird ein Upload abgelehnt. Anpassen kann es der Plattform-Admin je Workspace unter Administration → Teams → Team. Git-Klone zählen nicht; für sie gilt AIW_GIT_MAX_REPO_MB je Repository.

Workspace löschen: Unter Einstellungen → Allgemein, mit Eingabe des Namens. Der Workspace kommt in den Papierkorb (für alle unsichtbar) und lässt sich auf der Workspace-Übersicht AIW_WORKSPACE_TRASH_DAYS Tage lang wiederherstellen oder sofort endgültig löschen; danach löscht ihn der Worker. Endgültig entfernt werden Quellen (mit verschlüsselten Zugangsdaten und Git-Klon), Chats, Runs, Freigabeanträge, Uploads, Tool-Vorgaben und MCP-Freischaltungen samt Keys. Audit-Log und Verbrauch bleiben. Nicht möglich, solange Runs laufen oder auf Freigabe warten.

Im Chat: Antwort neu erzeugen bzw. Erneut versuchen nach einem Fehler, Exportieren als Markdown, Antworten kopieren, Strg+Enter sendet. Ergebnisse von data_query und db_query erscheinen unter der Antwort als Tabelle mit CSV-Download und, wenn sie passen (eine Beschriftung, bis zu drei Zahlenspalten, höchstens 40 Zeilen), als Balken- oder Liniendiagramm.

Benachrichtigungen: Wartet ein Antrag, bekommen alle, die ihn entscheiden dürfen, eine E-Mail; die auslösende Person erfährt die Entscheidung. Jeder Workspace kann zusätzlich einen Teams- oder Slack-Webhook eintragen. E-Mail-Adresse und Abbestellen im Profil (Klick auf den Namen oben rechts). Zustellung über eine Outbox im Worker mit bis zu fünf Versuchen; Stand unter Administration → Benachrichtigungen. Einrichtung über AIW_SMTP_* und AIW_PUBLIC_URL (für Links).

Administration: Teams ohne Workspaces, Modelle (außer dem Standard) und Endpunkte ohne Modelle lassen sich löschen.

Munin als Gedächtnis: MCP-Server mit Art Gedächtnis registrieren, Tools abrufen, Empfehlung übernehmen (forget wird gesperrt), im Workspace mit dem Munin-Key des Projekts freischalten. Die Anleitung des Servers geht in den System-Prompt. Unter Gedächtnis sehen Mitglieder die Einträge; löschen können nur Workspace-Admins.

Hugin anbinden: Der Plattform-Admin trägt unter Administration → Hugin den Host ein (muss in AIW_MCP_ALLOWED_HOSTS stehen; AIW_HUGIN_URL füllt ihn nur vor) und sieht dort, welche Workspaces Hugin mit welchem Tenant nutzen. Workspace-Admins tragen unter Einstellungen → Hugin den Integrations-Key (hi_…) ihres Tenants ein, wählen Pakete, gleichen ab und übernehmen. Versionen und Prüfsummen werden festgeschrieben, Pflichtpakete des Tenants kommen immer mit. Regeln gehen in den System-Prompt, Skills lädt die KI mit load_skill/read_skill_file, Befehle ruft man im Chat mit /name auf. hugin_feedback meldet Unklares an Hugin. Skills mit Skripten, Befehle mit Shell-Einbettung und andere Arten werden nicht angeboten. Der Worker gleicht täglich im Hintergrund ab (AIW_HUGIN_CHECK_HOURS) und meldet Updates per E-Mail, ohne zu übernehmen. Eine .hugin.yaml im Repository lässt sich als Vorschlag in die Auswahl übernehmen.

MCP-Server anbinden: Zentral unter Administration → MCP-Server registrieren (URL muss in AIW_MCP_ALLOWED_HOSTS stehen; lokal AIW_MCP_ALLOW_LOCAL=true für http://localhost), Tools abrufen, die Tools unter Tools einstufen und im Workspace unter Einstellungen → MCP-Server freischalten. Workspace-Admins können dort auch eigene Verbindungen anlegen, mit Kurzname (über alle Workspaces eindeutig), URL, Token und weiteren Headern (verschlüsselt). Nutzt die Plattform die URL schon, ist die Verbindung sofort aktiv. Sonst wartet sie auf die Freigabe eines Plattform-Admins, der per E-Mail benachrichtigt wird. Gleiche Tools desselben Dienstes erkennt Bifröst am Fingerabdruck (URL, Name, Beschreibung, Schema) und übernimmt die Einstufung; neue Tools bleiben gesperrt, bis sie eingestuft sind.

Tests

docker compose up -d
uv run pytest

Die Tests legen je Lauf eine eigene Datenbank im selben PostgreSQL an und löschen sie danach. SQLite reicht nicht, weil SKIP LOCKED, LISTEN/NOTIFY und Teilindizes gebraucht werden.

Statische Prüfung mit uv run ruff check app tests (Konfiguration in pyproject.toml; --fix behebt Import-Reihenfolge und Ähnliches).

Betrieb

Health-Checks

Endpunkt Web (Port der App) Worker (AIW_WORKER_HTTP_PORT, Standard 9101)
/live Prozess antwortet Arbeitsschleife lief in den letzten AIW_WORKER_STALE_SECONDS (120 s)
/ready Datenbank erreichbar, Migrationen aktuell, Upload-Verzeichnis beschreibbar, AIW_SECRET_KEY gesetzt wie Web

/live prüft bewusst nicht die Datenbank, damit ein DB-Ausfall nicht alle Prozesse neu starten lässt. Bei Problemen antwortet /ready mit 503 und nennt im JSON die betroffene Prüfung. /healthz und /readyz bleiben als Aliase.

Metriken

/metrics liefert das Prometheus-Format und ist nur aktiv, wenn AIW_METRICS_TOKEN gesetzt ist. Prometheus schickt den Token als Authorization: Bearer <token>; ohne Token antwortet der Endpunkt mit 404.

  • Web: HTTP-Requests nach Route-Muster und Status mit Dauer. Dazu der Zustand aus der Datenbank:
    • Runs nach Status und Alter des ältesten fälligen Runs (aiw_runs_oldest_queued_seconds).
    • Offene Freigaben und Benachrichtigungen (wartend, fehlgeschlagen).
    • Lebende Worker (aiw_workers_alive) und Sekunden seit dem letzten Herzschlag.
    • Workspaces im Papierkorb, Uploads, belegter Upload-Speicher (aiw_upload_bytes), Workspaces ab 80 % Kontingent (aiw_workspaces_storage_high), Kosten seit Mitternacht.
  • Worker:
    • Runs nach Art und Ergebnis mit Dauer.
    • Modellaufrufe nach Modell und Ergebnis mit Dauer und Tokens.
    • Tool-Aufrufe (MCP-Tools je Server zusammengefasst).
    • Zustellversuche und der letzte Schleifendurchlauf.

Ausfälle: Fällt ein Worker aus, übernimmt nach Ablauf der Lease (AIW_RUN_LEASE_SECONDS) ein anderer. Der Stand wird nach jeder Tool-Runde gespeichert; ein neuer Versuch setzt dort fort, statt Commits und Modellaufrufe zu wiederholen. Steckte der Run in einer freigegebenen Schreibaktion, wechselt er nach Abgleich nötig. Ist die Datenbank kurz weg, wartet die Worker-Schleife und macht weiter. Zeitpunkte der Warteschlangen (Runs, Benachrichtigungen) kommen von der Uhr der Datenbank; abweichende Uhren von Web- und Worker-Rechnern verzögern also nichts. Pushes und Pull Requests sind wiederholbar: Kam ein Push an, nur die Antwort nicht, erkennt der nächste Versuch das; ein schon angelegter Pull Request wird gefunden statt doppelt angelegt.

Die Zähler gelten je Prozess. Prometheus fragt deshalb jeden Web- und Worker-Prozess einzeln ab. Den Zustand aus der Datenbank liefert nur der Web-Prozess.

Beispiele für Alarme:

- alert: AiwKeinWorker
  expr: max(aiw_workers_alive) == 0
  for: 2m
- alert: AiwWarteschlangeStaut
  expr: max(aiw_runs_oldest_queued_seconds) > 300
  for: 5m
- alert: AiwModellFehler
  expr: sum(rate(aiw_model_calls_total{outcome!="ok"}[10m])) / sum(rate(aiw_model_calls_total[10m])) > 0.1
  for: 10m
- alert: AiwBenachrichtigungenFehlgeschlagen
  expr: max(aiw_notifications{status="failed"}) > 0
- alert: AiwZustandNichtLesbar
  expr: min(aiw_state_up) == 0
  for: 5m

Logs

Mit AIW_LOG_FORMAT=json schreibt die App eine JSON-Zeile je Ereignis nach stdout; ohne diese Einstellung ist es lesbarer Text. Das Level stellt AIW_LOG_LEVEL ein. Jede Zeile trägt den Kontext, soweit vorhanden: request_id, user_id, workspace_id, run_id und worker_id. Über die run_id findet man alles zu einem Run, auch über Web und Worker hinweg.

  • Request-ID: Eine X-Request-ID vom Proxy wird übernommen, sonst erzeugt die App eine. Sie steht in der Antwort und bei unerwarteten Fehlern als „Fehler-ID“ auf der Fehlerseite.
  • Zugriffszeile: eine je Request mit Methode, Route-Muster, Status, Dauer und Nutzer. Query-Strings stehen nicht darin, Probes und Metrik-Abrufe werden nicht geloggt.
  • Ereignisse (event): run_started, run_finished (Ergebnis, Dauer), model_call (Modell, Tokens, Dauer, Fehler), tool_call (Tool, Status, Entscheidung, Dauer), notification_error.
  • Nie im Log: Prompts, Antworten, Abfrageergebnisse und Zugangsdaten. Ein Filter maskiert zusätzlich typische Muster: Bearer-Token, sk-…-Keys, password=… und Passwörter in URLs.

Backup

Die Datenbank wird zentral gesichert. Außerhalb davon liegen nur:

  • Uploads (AIW_UPLOAD_DIR): DuckDB-Dateien, nach dem Hochladen unveränderlich, deshalb im laufenden Betrieb sicherbar.
  • Git-Cache: wird bei Bedarf neu geklont und nicht gesichert.
  • AIW_SECRET_KEY: gehört in den Secret-Store. Ohne ihn sind die verschlüsselten Zugangsdaten in der Datenbank wertlos.
uv run aiw backup /backup/aiw          # uploads-<zeit>.tar.gz mit manifest.json (Prüfsummen, Migrationsstand)
uv run aiw restore /backup/aiw/uploads-20261022T020000Z.tar.gz [--force]
uv run aiw check                       # Exit-Code 1 bei Problemen
  • backup sichert die Uploads, die laut Datenbank verarbeitet sind. Fehlende Dateien meldet es mit Exit-Code 1.
  • restore prüft die Prüfsummen und überspringt Uploads, die es in der Datenbank nicht mehr gibt. Vorhandene Dateien mit anderem Inhalt überschreibt es nur mit --force.
  • check prüft:
    • Datenbank und Migrationen.
    • Ob alle Uploads ihre Datei haben; verwaiste Dateien meldet es nur als Hinweis.
    • Ob AIW_SECRET_KEY alle gespeicherten Geheimnisse entschlüsseln kann.
  • Zeitpunkt: Das Backup läuft am besten nach dem Datenbank-Backup, z. B. als CronJob. Nach einem Restore der Datenbank zeigt aiw check, ob Datenbank und Dateien zusammenpassen.

Aufbau

Pfad Inhalt
app/main.py Web-Prozess: Router, Sicherheitsköpfe (CSP), Request-ID und Zugriffslog, /live, /ready, /metrics
app/core/logs.py, metrics.py, health.py JSON-Logs mit Kontext, Prometheus-Metriken, Health-Checks
app/tasks/monitor.py Worker-Herzschlag und HTTP-Server des Workers für Probes und Metriken
app/backup.py aiw backup, aiw restore, aiw check
app/cli.py aiw migrate | seed-demo | demo | create-admin | gen-key | web | worker | backup | restore | check
app/identity Anmeldung (Demo-Provider, später OIDC), Sitzungen
app/workspaces Workspaces, Mitgliedschaften, Rollen
app/data_sources Quellen, Git-Connector, lesende Git-Tools, Bitbucket-API
app/changes Branch je Chat: commit_change, git_pr_comments, Push, Pull Request, Abfrage des PR-Status, Ansicht Änderungen
app/demo aiw demo: Demo-Workspaces Bestellservice und MCP-Entwicklung (Repository, Datei, Dokumente, Beispielfragen)
app/feedback Rückmeldungen zu Antworten (👍/👎, Kommentar), Auswertung und CSV unter Nutzung und Kosten
app/documents Abgeglichene Ordner: Abgleich aus dem Browser, Auslese über MCP, Volltextsuche, Tools docs_*
app/repo Bereich Repository: neue Commits seit dem letzten Besuch, Branches, Commit mit Diff, Chats mit Bezug
app/uploads Uploads, Umwandlung in DuckDB, gesperrte Abfrageverbindung, Tools data_*
app/core/markdown.py Bereinigte Darstellung von Modellausgaben (markdown-it-py, nh3)
app/usage, app/audit/views.py Verbrauch und Kosten je Modellaufruf, Run-Liste, Audit-Log im UI
app/notify Benachrichtigungen: Outbox, E-Mail, Webhooks, Profil
app/core/charts.py Diagramme zu Abfrageergebnissen als Inline-SVG
app/hugin Hugin: Host der Plattform, Key je Workspace, Auswahl, Abgleich und Festschreiben je Workspace, Laden für Runs, Befehle, Tools load_skill, read_skill_file, hugin_feedback
app/mcp/memory.py Gedächtnis (Munin): Einstufungsempfehlung, Einträge ansehen und löschen
app/mcp MCP-Server: zentrale Registrierung, Discovery, Freischaltung je Workspace, Verbindungen der Workspaces mit Freigabe, Datenbank-Treiber, Client (SDK mcp), /admin/mcp, /admin/databases
app/mcp/databases.py Datenbanken der Workspaces als MCP-Verbindungen über Datenbank-Treiber
app/approvals Freigabeanträge (z. B. für Datenbankabfragen mit Freigabe-Regel oder MCP-Tools), Entscheidung, Ablauf, Ausführung mit erneuter Prüfung, /approvals
app/ai Endpunkte, Keys, Modelle, Verwaltung /admin/models, Provider-Adapter (Responses API)
app/chat Chats, Nachrichten, Handler für Chat-Runs (runner.py)
app/tools Tool-Registry, Einstufung, Policy Engine, Ausführung, /admin/tools
app/tasks Run-Zustände, Queue mit Lease, Ereignisse, Worker, SSE
app/audit Audit-Log
app/migrations Alembic

Neue Migration nach Modelländerungen:

uv run python -c "from alembic import command; from app.cli import alembic_config; from app.core.config import get_settings; command.revision(alembic_config(get_settings()), message='…', autogenerate=True)"