-- PROJ: ML-Retraining-Klassifizierung Phase 1 (Naive-Bayes) -- Doku-only (siehe README.md in diesem Verzeichnis) — die tatsächliche -- Ausführung passiert idempotent über -- internal/storage/ml_classifier.go (*Store).initMLClassifierSchema(), -- eingehängt in internal/storage/documents.go (*Store).initSchema() NACH -- initTaxonomySchema (setzt document_types/correspondents/tags/document_tags/ -- documents voraus). -- -- Ergänzt die bestehende Regel-Engine (Taxonomie-Matching) um ein optionales, -- pro Mandant trainiertes Naive-Bayes-Modell: aus bereits klassifizierten -- Dokumenten werden Token-Häufigkeiten je Klasse (document_type/correspondent/ -- tag) gelernt (ml_classifier_tokens/ml_classifier_classes) und je Trainingslauf -- protokolliert (ml_classifier_runs). -- -- Provenienz-Spalten (assigned_via auf document_tags, doc_type_assigned_via/ -- correspondent_assigned_via auf documents) unterscheiden 'manual' (Nutzer -- hat gesetzt), 'rule' (Regel-Engine) und 'ml_accepted' (vom Klassifizierer -- vorgeschlagen und vom Nutzer akzeptiert). DEFAULT 'manual' bewusst konservativ -- gewählt: bestehende Zeilen werden NICHT rückwirkend als 'rule' fehlklassifiziert -- und fließen dadurch weiterhin normal als Trainingsdaten in den Klassifizierer ein. CREATE TABLE IF NOT EXISTS ml_classifier_tokens ( id BIGSERIAL PRIMARY KEY, tenant_id BIGINT NOT NULL, kind TEXT NOT NULL CHECK (kind IN ('document_types','correspondents','tags')), entity_id BIGINT NOT NULL, token TEXT NOT NULL, count BIGINT NOT NULL DEFAULT 0, UNIQUE (tenant_id, kind, entity_id, token) ); CREATE INDEX IF NOT EXISTS idx_ml_tokens_lookup ON ml_classifier_tokens(tenant_id, kind, token); CREATE TABLE IF NOT EXISTS ml_classifier_classes ( tenant_id BIGINT NOT NULL, kind TEXT NOT NULL, entity_id BIGINT NOT NULL, doc_count BIGINT NOT NULL DEFAULT 0, total_tokens BIGINT NOT NULL DEFAULT 0, PRIMARY KEY (tenant_id, kind, entity_id) ); CREATE TABLE IF NOT EXISTS ml_classifier_runs ( id BIGSERIAL PRIMARY KEY, tenant_id BIGINT NOT NULL, started_at TIMESTAMPTZ NOT NULL DEFAULT now(), completed_at TIMESTAMPTZ, doc_count BIGINT NOT NULL DEFAULT 0, status TEXT NOT NULL DEFAULT 'running' CHECK (status IN ('running','completed','failed','skipped_insufficient_data')), error TEXT ); CREATE INDEX IF NOT EXISTS idx_ml_classifier_runs_tenant ON ml_classifier_runs(tenant_id); ALTER TABLE document_tags ADD COLUMN IF NOT EXISTS assigned_via TEXT NOT NULL DEFAULT 'manual' CHECK (assigned_via IN ('manual','rule','ml_accepted')); ALTER TABLE documents ADD COLUMN IF NOT EXISTS doc_type_assigned_via TEXT NOT NULL DEFAULT 'manual' CHECK (doc_type_assigned_via IN ('manual','rule','ml_accepted')); ALTER TABLE documents ADD COLUMN IF NOT EXISTS correspondent_assigned_via TEXT NOT NULL DEFAULT 'manual' CHECK (correspondent_assigned_via IN ('manual','rule','ml_accepted'));