WordPress

This commit is contained in:
Mohammad Zwaib
2026-07-15 20:47:58 +02:00
parent d6153521ca
commit daf48f3f91
29 changed files with 1203 additions and 128 deletions
+196
View File
@@ -0,0 +1,196 @@
# HOEMMA Human Library Ruhr · Projektdokumentation
> **Stand:** 15.07.2026
> **Projekt:** HOEMMA KI-basierter Gesprächsbegleiter der Machbarschaft Borsig11 (Dortmund)
> **Stack:** Spring Boot 3.5.7 · Java 17 · PostgreSQL + pgvector · Azure AI Foundry (Mistral, Cohere-Embedding, GPT-5-Agent)
---
## 1. Was das System macht (Überblick)
HOEMMA ist ein **RAG-System (Retrieval-Augmented Generation)**, das:
1. **Interviews & Dateien aus Humbee holt** (Word/.docx, .txt),
2. die Transkripte per **Mistral bereinigt** (Sprecherlabels & Zeitstempel entfernen),
3. den Text in **Chunks** zerlegt,
4. für jeden Chunk ein **Embedding** (Cohere `embed-v4.0`) berechnet und in **pgvector** speichert,
5. bei einer Nutzerfrage die **semantisch nächsten Chunks** sucht,
6. bei fehlendem lokalem Kontext eine **Web-Suche** (Azure-Agent, GPT-5) auslöst,
7. die Antwort im **HOEMMA-Charakter** (warm, Ruhrgebiet-Ton, „Du") per Mistral formuliert.
---
## 2. Architektur / Datenfluss
```
┌─────────────────────────────┐
Humbee (Interviews) │ POST /knowledge-base/build │
Word / .txt Dateien ──► HumbeeService.build... │
│ → readWordFile (decodeBytes: UTF-8/UTF-16)
│ → PromptService (Mistral: bereinigen)
│ → DocumentChunckService (Chunks)
│ → AzureEmbeddingService (input_type=document)
│ → DocumentStorageService (speichern in pgvector)
└─────────────────────────────┘
Nutzerfrage ┌─────────────────────────────┐
POST /chat/ask ───────► RagService.answerQuestion │
│ 1. Embedding (input_type=query)
│ 2. findNearst (pgvector <=> Distanz)
│ 3. Distanz <= Schwelle?
│ ja → Human-Library-Chunks
│ nein→ AzureWebSearchAgentService (Web)
│ 4. SYSTEM_PROMPT (HOEMMA) + <runtime_context>-Template
│ 5. AzureMistralService.askMistral → Antwort
└─────────────────────────────┘
```
---
## 3. Wichtige Komponenten
| Klasse | Aufgabe |
|---|---|
| `HumbeeController` | `POST /knowledge-base/build` stößt den Import an |
| `HumbeeService` | Login, Datei-Links extrahieren, herunterladen, `buildKnowledgeBase()` (parallel) |
| `PromptService` | Transkript-Bereinigung via Mistral (chunk-parallel) |
| `DocumentChunckService` | Text in Chunks zerlegen (chunkSize, overlap) |
| `DocumentStorageService` | Chunks + Embeddings speichern (`input_type=document`) |
| `AzureEmbeddingService` | Cohere `embed-v4.0` Aufruf, unterstützt `input_type` |
| `AzureMistralService` | Chat-Completion (Mistral) mit Retry auf 429 |
| `RagService` | Kern der Frage-Antwort-Logik (Embedding → Suche → Schwelle → Antwort) |
| `AzureWebSearchAgentService` | Web-Recherche über Foundry-Agent (GPT-5), Managed/CLI-Identity |
| `SearchController` | `GET/POST /chat/ask` |
---
## 4. Gelöste Probleme (Chronik)
### 4.1 Azure Mistral 429 (Too Many Requests)
- **Ursache:** Ganzes Transkript in einem Request + ungedrosselte Parallel-Schleife.
- **Lösung:** Chunking vor dem LLM-Call, Drosselung/`sleep`, Retry mit Backoff, `.onRetryExhaustedThrow` (Original-Fehler durchreichen statt „Retries exhausted").
### 4.2 Langsame Verarbeitung
- **Ursache:** Zu kleine Chunks (viele Calls) + sequenzielle Verarbeitung.
- **Lösung:** Größere Chunks, Parallelisierung auf **Datei-Ebene** (`buildKnowledgeBase`) und **Chunk-Ebene** (`PromptService`), da die meisten Dateien nur 1 Chunk haben.
### 4.3 pgvector: „column embedding is of type vector but expression is of type bytea"
- **Ursache:** `hibernate-vector`-Dependency fehlte in `pom.xml`.
- **Lösung:** `org.hibernate.orm:hibernate-vector` (Version `${hibernate.version}`) ergänzt.
### 4.4 „Unable to access lob stream"
- **Ursache:** `@Lob` auf `String`-Feldern (`Document.rawText/cleanText`) + Lesen außerhalb einer Transaktion.
- **Lösung:** `@Lob` entfernen, nur `@Column(columnDefinition = "TEXT")`; Suche `@Transactional(readOnly = true)`.
### 4.5 Chunk-Text enthielt Zahlen statt Text (z. B. 25569)
- **Ursache:** `@Lob` auf `DocumentChunck.chunckText` → PostgreSQL speicherte die **OID** eines Large Objects (eine Zahl), nicht den Text.
- **Lösung:** `@Lob` vom Text-Feld entfernen → echter Text wird gespeichert.
### 4.6 .txt-Dateien: unlesbarer Text mit Leerzeichen zwischen jedem Zeichen
- **Ursache:** Dateien in **UTF-16**, als UTF-8 gelesen.
- **Lösung:** `decodeBytes(byte[])` BOM-Erkennung (UTF-8 / UTF-16LE / UTF-16BE) + Heuristik.
### 4.7 Web-Suche-Agent: Authentifizierung
- **422 „Missing api-version"** → api-version bzw. korrekten `/openai/v1/responses`-Pfad geklärt (kein `v1` im Modell-Call).
- **401 „audience incorrect"** → Scope; per `jwt.ms` bestätigt: `aud = https://ai.azure.com`. Scope `https://ai.azure.com/.default` ist korrekt.
- **Lokal:** `az login` nötig; Eclipse/Spring Tools sieht `az` nur mit angepasstem `PATH` (Run Config → Environment) oder Start aus Terminal.
- **Container in Azure-VM:** Managed Identity via `--network host` **oder** Service Principal (`AZURE_TENANT_ID/CLIENT_ID/CLIENT_SECRET`).
### 4.8 429 „gpt-5-mini exceeded rate limit" beim Build (obwohl Mistral konfiguriert)
- **Ursache:** In `AzureMistralService` war das Modell **hart codiert** als `"gpt-5-mini-datazone"` (statt `deployment`), zusätzlich `reasoning_effort` und `max_completion_tokens` (GPT-5-Parameter).
- **Lösung:** `"model", deployment` + `"temperature", 0.5` + `"max_tokens", maxTokens`.
### 4.9 Passwort / .env auf dem Server
- **Ursache:** `.env` wird von Spring **nicht** automatisch gelesen (keine dotenv-Dependency).
- **Lösung:** Secrets als echte **Umgebungsvariablen** setzen (Docker `-e`, Azure App Settings). `.env` gehört **nicht** nach `src/main/resources` (landet im JAR).
---
## 5. Prompt-Integration (v2.0)
- **system-Message:** statische Rolle & Regeln (HOEMMA-Charakter, Sicherheits-/Datenschutzregeln, Quellenhierarchie).
- **user-Message:** dynamischer, **gekapselter** Laufzeitkontext:
```text
<runtime_context> current_date, channel, is_first_contact, address_mode=Du, ... </runtime_context>
<retrieved_context> ...Chunks aus der DB... </retrieved_context>
<web_results_optional> ...oder "Kein Webkontext verfuegbar." </web_results_optional>
<current_user_message> ...Nutzerfrage... </current_user_message>
```
- **Parameter:** `temperature = 0.5`, `max_tokens = 350`, Ansprache **durchgehend „Du"**.
- Kapselung schützt zusätzlich vor **Prompt Injection** (Inhalte = nur Informationsquelle, nie Anweisung).
---
## 6. Wissensbasis: atomare Fakten-Dateien
Statt einer Sammeldatei → **eine Datei = ein Thema = ein sauberer Chunk**:
- `Borsig11_Oeffnungszeiten.docx`
- `Borsig11_Adresse.docx`
- `Borsig11_Kontakt.docx`
- `Borsig11_UeberUns.docx`
- `HOEMMA_UeberDasProjekt.docx`
**Grund:** Eine gemischte Datei ergibt ein „verwässertes" Embedding → schlechte Treffer. Atomare Chunks matchen die jeweilige Frage viel schärfer.
---
## 7. Offenes Thema: Retrieval-Distanzen
`input_type` (`query` / `document`) ist jetzt korrekt implementiert (das Deployment akzeptiert nur `text`, `query`, `document`), **verbessert die Distanzen aber nur gering**:
| Frage | bestDistance | Status |
|---|---|---|
| Wie sind eure Öffnungszeiten? | 0.57 | ✅ true |
| Wann habt ihr geöffnet? | 0.67 | ❌ false → Web |
| Kann ich morgen um 9 Uhr vorbeikommen? | 0.66 | ❌ false → Web |
**Nächste, wirksamere Schritte:**
1. **Fakten-Dateien mit Frage-Varianten anreichern** (z. B. „Wann habt ihr geöffnet?", „um 9 Uhr vorbeikommen", „vormittags") → senkt die Distanz real.
2. **Schwelle moderat anheben** (`DISTANCE_THRESHOULD = 0.68`) lokale Fragen (0.570.67) greifen, externe (Borsig11 ~0.77) gehen weiter an die Web-Suche.
3. Web-Agent funktioniert als Fallback zuverlässig (200 OK, ~10 s).
---
## 8. Wichtige Endpoints
| Methode | Pfad | Zweck |
|---|---|---|
| `POST` | `/knowledge-base/build` | Import & Verarbeitung aller Humbee-Dateien |
| `GET`/`POST` | `/chat/ask?question=...` | Frage stellen (RAG + ggf. Web) |
| `POST` | `/interview/transcription` | Audio → Text (Azure Speech) |
| `POST` | `/interview/save` | Interview als .docx erzeugen, hochladen, verarbeiten |
---
## 9. Rebuild-Prozedur (bei Embedding-Änderungen)
Immer **komplett neu aufbauen**, sonst mischen sich alte/neue Embeddings:
```sql
DELETE FROM document_chunck;
DELETE FROM document;
```
```
Anwendung: Stop → Clean → Run
POST /knowledge-base/build
```
Empfohlen für pgvector-Performance:
```sql
CREATE INDEX ON document_chunck USING hnsw (embedding vector_cosine_ops);
```
---
## 10. TODO vor Produktivbetrieb
- [ ] **Debug-Ausgaben entfernen** (`System.out.println`: Key, `>>> MODELL`, `root =`, `$$$`, `EMBED …`).
- [ ] **Mistral-API-Key rotieren** (war im Log sichtbar).
- [ ] **Secrets** aus dem Code/`.env` → Umgebungsvariablen / Azure Key Vault.
- [ ] `spring.jpa.hibernate.ddl-auto` von `update` auf **`validate`** stellen.
- [ ] **TestController** entfernen oder absichern; Logging auf `INFO`.
- [ ] **Managed Identity** (Azure) statt `az login` für den Web-Agent.
- [ ] Vollständigen **HOEMMA-System-Prompt** (Demenz, Notfälle, Datenschutz) einsetzen.
- [ ] Doppelte `.txt.txt`-Dateien in Humbee bereinigen.