SRC-10: spracherkennung-ocr-qualitaetsbewertung

Spracherkennung für OCR-Texte und Qualitätsbewertung (Konfidenzwert), um
schlechte OCR-Ergebnisse kenntlich zu machen. Letztes Ticket vor QA-03.

- ocr/language.go: RecognizeWithLanguageAndConfidence erkennt ein Bild
  einzeln je Kandidatensprache (deu/eng) im Tesseract-TSV-Modus — die
  Sprache mit höherem Konfidenzwert gewinnt, derselbe Lauf liefert den
  Konfidenzwert direkt mit.
- search: neue Felder ocr_language/ocr_confidence (Migrationen 0006/0007,
  gleiches ALTER-Muster wie SRC-05), in Document/Result gespiegelt.
  Client.AttachmentsBelowConfidence filtert gezielt auf niedrige
  Konfidenz, schließt Dokumente ohne OCR-Anhang aus.
- Regressionsbug gefunden und behoben: reindex.go (SRC-09) kannte die
  neuen OCR-Spalten nicht, Reindex wäre mit "unknown column"
  fehlgeschlagen.

Prüfungen (alle real durchgeführt, siehe mail/docs/SRC-10-PRUEFPROTOKOLL.md):
1. TestRecognizeWithLanguageAndConfidence_MultilingualCorpus: deutsches
   und englisches Testbild real korrekt als deu/eng erkannt.
2. TestRecognizeWithLanguageAndConfidence_DegradedImageLowersConfidence:
   künstliche Verschlechterung senkt Konfidenz real von 91,76 auf 28,21.
3. TestAttachmentsBelowConfidence_QueryReturnsExpectedResults: Abfrage
   unterhalb Schwelle liefert real genau die erwarteten 2 von 4 Treffern.

Kein Umbau: Search/Facets/SearchWithFilters/Index/Delete-Verhalten sonst
unverändert, dedup/indexworker/storage/crypto/encstorage/savedsearch
unverändert.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HhgFcLS8tYMhDJpP74C6AQ
This commit is contained in:
sysops
2026-08-31 22:16:26 +02:00
co-authored by Claude Sonnet 5
parent bd37de529f
commit 1a246abdb3
10 changed files with 427 additions and 6 deletions
@@ -0,0 +1,53 @@
// Integrationstest (SRC-10): echte Manticore-Instanz, TEST_MANTICORE_URL
// (gleiche Konvention wie facets_test.go/ranking_test.go).
package search
import (
"context"
"testing"
)
// TestAttachmentsBelowConfidence_QueryReturnsExpectedResults ist die
// geforderte Pflichtprüfung 3: Abfrage aller Anhänge unterhalb einer
// Konfidenzschwelle liefert erwartete Treffer.
func TestAttachmentsBelowConfidence_QueryReturnsExpectedResults(t *testing.T) {
client := setupClient(t)
ctx := context.Background()
tenant := "mandant-src10-konfidenz"
docs := []Document{
{MessageID: "msg-conf-niedrig-1", Subject: "a", OCRLanguage: "deu", OCRConfidence: 22.5},
{MessageID: "msg-conf-niedrig-2", Subject: "b", OCRLanguage: "eng", OCRConfidence: 41.0},
{MessageID: "msg-conf-hoch", Subject: "c", OCRLanguage: "deu", OCRConfidence: 93.2},
{MessageID: "msg-conf-kein-ocr", Subject: "d"}, // kein OCR-Anhang, OCRConfidence bleibt 0
}
for _, d := range docs {
d.TenantSlug = tenant
d.ID = DocumentID(tenant, d.MessageID)
if err := client.Index(ctx, d); err != nil {
t.Fatalf("index %s: %v", d.MessageID, err)
}
}
results, err := client.AttachmentsBelowConfidence(ctx, tenant, 50.0)
if err != nil {
t.Fatalf("attachmentsbelowconfidence: %v", err)
}
ids := make(map[string]bool, len(results))
for _, r := range results {
ids[r.MessageID] = true
}
if !ids["msg-conf-niedrig-1"] || !ids["msg-conf-niedrig-2"] {
t.Fatalf("erwartete beide niedrig-konfidenten anhänge, habe: %+v", results)
}
if ids["msg-conf-hoch"] {
t.Fatalf("hoch-konfidenter anhang hätte nicht auftauchen dürfen: %+v", results)
}
if ids["msg-conf-kein-ocr"] {
t.Fatalf("dokument ohne ocr-anhang (confidence=0) hätte nicht auftauchen dürfen: %+v", results)
}
if len(results) != 2 {
t.Fatalf("erwartete genau 2 treffer, habe %d: %+v", len(results), results)
}
}