Wiki mit semantischer Suche ueber pgvector, Diktat ueber faster-whisper, Berichtsassistent und Belegerfassung — alles gegen selbst betriebene Modelle. Dazu zwei Protokollansichten: was die Maschine getan hat und was Menschen getan haben. Nichts, was ein Modell erzeugt, gelangt ungeprueft in einen Nachweis. Jede Ausgabe ist zuerst ein eigener Datensatz, gekennzeichnet, mit zwei gleich grossen Knoepfen davor. Die Datenbank haelt gegen: Eingabe, Ausgabe und Modell sind unveraenderlich, geloescht wird nicht, entschieden wird einmal. Kein Rueckfall auf einen Anbieter: fehlen die Container, fehlt die Funktion. Dafuer bleibt alles andere ohne Modell vollstaendig benutzbar — geprueft von vierzehn E2E-Tests, die ohne laufendes Modell durchlaufen. Postgres bringt jetzt PostGIS und pgvector mit (eigenes Image), Ollama und faster-whisper stehen im Compose-Profil "ki". 73 neue Unit-Tests, 14 neue E2E-Tests, Version 0.9.0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
243 lines
9.7 KiB
SQL
243 lines
9.7 KiB
SQL
-- =============================================================================
|
|
-- Phase 8 — Lokale KI
|
|
--
|
|
-- HINWEIS zu den entfernten DropIndex-Blöcken:
|
|
-- Prisma erzeugt bei jeder Migration erneut den Vorschlag, die von Hand
|
|
-- angelegten GiST- und Trigram-Indizes aus Phase 1 abzuräumen — es kennt sie
|
|
-- nicht, weil sie nicht aus dem Schema stammen, sondern aus einer eigenen
|
|
-- Migration. Die Blöcke sind deshalb hier entfernt worden, wie in jeder
|
|
-- Migration seit Phase 1. Ohne sie wäre die Volltext- und Umkreissuche nach
|
|
-- diesem Schritt ein vollständiger Tabellendurchlauf.
|
|
--
|
|
-- Neu ist die Erweiterung "vector": sie kommt aus dem eigenen Postgres-Image
|
|
-- (docker/postgres/Dockerfile). Bei einer bestehenden Installation muss es
|
|
-- vorher gebaut werden — `docker compose build postgres`, dann den Dienst neu
|
|
-- starten. Ohne die Erweiterung bricht diese Migration mit einer klaren
|
|
-- Meldung ab, und das ist besser als eine halb angelegte Tabelle.
|
|
-- =============================================================================
|
|
|
|
-- CreateExtension
|
|
CREATE EXTENSION IF NOT EXISTS "vector";
|
|
|
|
-- CreateEnum
|
|
CREATE TYPE "KiArt" AS ENUM ('DIKTAT', 'BERICHTSENTWURF', 'WIKI_ANTWORT', 'BELEGERFASSUNG', 'EINBETTUNG');
|
|
|
|
-- CreateEnum
|
|
CREATE TYPE "KiStand" AS ENUM ('ENTWURF', 'UEBERNOMMEN', 'VERWORFEN', 'FEHLGESCHLAGEN');
|
|
|
|
-- CreateEnum
|
|
CREATE TYPE "WikiKategorie" AS ENUM ('DIENSTVORSCHRIFT', 'AUSBILDUNG', 'GERAETE', 'EINSATZ', 'ORGANISATION', 'SONSTIGES');
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "ki_vorgang" (
|
|
"id" UUID NOT NULL,
|
|
"organisationId" UUID NOT NULL,
|
|
"art" "KiArt" NOT NULL,
|
|
"stand" "KiStand" NOT NULL DEFAULT 'ENTWURF',
|
|
"modell" VARCHAR(120) NOT NULL,
|
|
"entitaet" VARCHAR(80),
|
|
"entitaetId" UUID,
|
|
"eingabe" VARCHAR(8000) NOT NULL,
|
|
"ausgabe" VARCHAR(40000),
|
|
"fehler" VARCHAR(1000),
|
|
"dauerMs" INTEGER,
|
|
"tokenEingabe" INTEGER,
|
|
"tokenAusgabe" INTEGER,
|
|
"erzeugtVonId" UUID,
|
|
"entschiedenVonId" UUID,
|
|
"entschiedenAm" TIMESTAMPTZ(3),
|
|
"erstelltAm" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
CONSTRAINT "ki_vorgang_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "wikiseite" (
|
|
"id" UUID NOT NULL,
|
|
"organisationId" UUID NOT NULL,
|
|
"slug" VARCHAR(160) NOT NULL,
|
|
"titel" VARCHAR(200) NOT NULL,
|
|
"art" "WikiKategorie" NOT NULL DEFAULT 'SONSTIGES',
|
|
"inhalt" TEXT NOT NULL,
|
|
"quelle" VARCHAR(300),
|
|
"schlagworte" TEXT[],
|
|
"veroeffentlicht" BOOLEAN NOT NULL DEFAULT true,
|
|
"autorId" UUID,
|
|
"eingebettetAm" TIMESTAMPTZ(3),
|
|
"erstelltAm" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"geaendertAm" TIMESTAMPTZ(3) NOT NULL,
|
|
"geloeschtAm" TIMESTAMPTZ(3),
|
|
|
|
CONSTRAINT "wikiseite_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "wikiabschnitt" (
|
|
"id" UUID NOT NULL,
|
|
"organisationId" UUID NOT NULL,
|
|
"seiteId" UUID NOT NULL,
|
|
"reihenfolge" INTEGER NOT NULL,
|
|
"ueberschrift" VARCHAR(300),
|
|
"text" TEXT NOT NULL,
|
|
"einbettung" vector(768),
|
|
"modell" VARCHAR(120),
|
|
"erstelltAm" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
CONSTRAINT "wikiabschnitt_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "wikiversion" (
|
|
"id" UUID NOT NULL,
|
|
"organisationId" UUID NOT NULL,
|
|
"seiteId" UUID NOT NULL,
|
|
"nummer" INTEGER NOT NULL,
|
|
"titel" VARCHAR(200) NOT NULL,
|
|
"inhalt" TEXT NOT NULL,
|
|
"bemerkung" VARCHAR(500),
|
|
"autorId" UUID,
|
|
"erstelltAm" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
CONSTRAINT "wikiversion_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "diktat" (
|
|
"id" UUID NOT NULL,
|
|
"organisationId" UUID NOT NULL,
|
|
"audioDateiId" UUID NOT NULL,
|
|
"dauerSekunden" INTEGER,
|
|
"sprache" VARCHAR(10) NOT NULL DEFAULT 'de',
|
|
"transkript" TEXT,
|
|
"stand" "KiStand" NOT NULL DEFAULT 'ENTWURF',
|
|
"entitaet" VARCHAR(80),
|
|
"entitaetId" UUID,
|
|
"kiVorgangId" UUID,
|
|
"erzeugtVonId" UUID,
|
|
"erstelltAm" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"geaendertAm" TIMESTAMPTZ(3) NOT NULL,
|
|
"geloeschtAm" TIMESTAMPTZ(3),
|
|
|
|
CONSTRAINT "diktat_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "belegentwurf" (
|
|
"id" UUID NOT NULL,
|
|
"organisationId" UUID NOT NULL,
|
|
"fotoDateiId" UUID NOT NULL,
|
|
"betrag" DECIMAL(12,2),
|
|
"waehrung" VARCHAR(3) NOT NULL DEFAULT 'EUR',
|
|
"belegdatum" DATE,
|
|
"haendler" VARCHAR(200),
|
|
"kategorie" VARCHAR(80),
|
|
"steuersatz" DECIMAL(5,2),
|
|
"rohantwort" JSONB,
|
|
"stand" "KiStand" NOT NULL DEFAULT 'ENTWURF',
|
|
"kiVorgangId" UUID,
|
|
"erfasstVonId" UUID,
|
|
"bestaetigtVonId" UUID,
|
|
"bestaetigtAm" TIMESTAMPTZ(3),
|
|
"erstelltAm" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"geaendertAm" TIMESTAMPTZ(3) NOT NULL,
|
|
"geloeschtAm" TIMESTAMPTZ(3),
|
|
|
|
CONSTRAINT "belegentwurf_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "ki_vorgang_organisationId_art_erstelltAm_idx" ON "ki_vorgang"("organisationId", "art", "erstelltAm" DESC);
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "ki_vorgang_organisationId_entitaet_entitaetId_idx" ON "ki_vorgang"("organisationId", "entitaet", "entitaetId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "ki_vorgang_organisationId_stand_idx" ON "ki_vorgang"("organisationId", "stand");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "wikiseite_organisationId_art_idx" ON "wikiseite"("organisationId", "art");
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "wikiseite_organisationId_slug_key" ON "wikiseite"("organisationId", "slug");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "wikiabschnitt_organisationId_idx" ON "wikiabschnitt"("organisationId");
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "wikiabschnitt_seiteId_reihenfolge_key" ON "wikiabschnitt"("seiteId", "reihenfolge");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "wikiversion_organisationId_seiteId_idx" ON "wikiversion"("organisationId", "seiteId");
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "wikiversion_seiteId_nummer_key" ON "wikiversion"("seiteId", "nummer");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "diktat_organisationId_stand_idx" ON "diktat"("organisationId", "stand");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "diktat_organisationId_entitaet_entitaetId_idx" ON "diktat"("organisationId", "entitaet", "entitaetId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "belegentwurf_organisationId_stand_idx" ON "belegentwurf"("organisationId", "stand");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "belegentwurf_organisationId_belegdatum_idx" ON "belegentwurf"("organisationId", "belegdatum");
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "ki_vorgang" ADD CONSTRAINT "ki_vorgang_organisationId_fkey" FOREIGN KEY ("organisationId") REFERENCES "organisation"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "ki_vorgang" ADD CONSTRAINT "ki_vorgang_erzeugtVonId_fkey" FOREIGN KEY ("erzeugtVonId") REFERENCES "person"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "ki_vorgang" ADD CONSTRAINT "ki_vorgang_entschiedenVonId_fkey" FOREIGN KEY ("entschiedenVonId") REFERENCES "person"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "wikiseite" ADD CONSTRAINT "wikiseite_organisationId_fkey" FOREIGN KEY ("organisationId") REFERENCES "organisation"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "wikiseite" ADD CONSTRAINT "wikiseite_autorId_fkey" FOREIGN KEY ("autorId") REFERENCES "person"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "wikiabschnitt" ADD CONSTRAINT "wikiabschnitt_organisationId_fkey" FOREIGN KEY ("organisationId") REFERENCES "organisation"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "wikiabschnitt" ADD CONSTRAINT "wikiabschnitt_seiteId_fkey" FOREIGN KEY ("seiteId") REFERENCES "wikiseite"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "wikiversion" ADD CONSTRAINT "wikiversion_organisationId_fkey" FOREIGN KEY ("organisationId") REFERENCES "organisation"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "wikiversion" ADD CONSTRAINT "wikiversion_seiteId_fkey" FOREIGN KEY ("seiteId") REFERENCES "wikiseite"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "wikiversion" ADD CONSTRAINT "wikiversion_autorId_fkey" FOREIGN KEY ("autorId") REFERENCES "person"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "diktat" ADD CONSTRAINT "diktat_organisationId_fkey" FOREIGN KEY ("organisationId") REFERENCES "organisation"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "diktat" ADD CONSTRAINT "diktat_audioDateiId_fkey" FOREIGN KEY ("audioDateiId") REFERENCES "datei"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "diktat" ADD CONSTRAINT "diktat_kiVorgangId_fkey" FOREIGN KEY ("kiVorgangId") REFERENCES "ki_vorgang"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "diktat" ADD CONSTRAINT "diktat_erzeugtVonId_fkey" FOREIGN KEY ("erzeugtVonId") REFERENCES "person"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "belegentwurf" ADD CONSTRAINT "belegentwurf_organisationId_fkey" FOREIGN KEY ("organisationId") REFERENCES "organisation"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "belegentwurf" ADD CONSTRAINT "belegentwurf_fotoDateiId_fkey" FOREIGN KEY ("fotoDateiId") REFERENCES "datei"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "belegentwurf" ADD CONSTRAINT "belegentwurf_kiVorgangId_fkey" FOREIGN KEY ("kiVorgangId") REFERENCES "ki_vorgang"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "belegentwurf" ADD CONSTRAINT "belegentwurf_erfasstVonId_fkey" FOREIGN KEY ("erfasstVonId") REFERENCES "person"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "belegentwurf" ADD CONSTRAINT "belegentwurf_bestaetigtVonId_fkey" FOREIGN KEY ("bestaetigtVonId") REFERENCES "person"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|