Update Ollama integration
release-tag / release-image (push) Successful in 1m32s

This commit is contained in:
2026-07-29 09:43:02 +02:00
parent afa519b72a
commit a33ff09c41
16 changed files with 1238 additions and 31 deletions
+136 -2
View File
@@ -4,6 +4,7 @@ Ein einziges Go-/Docker-Image für zwei Rollen auf derselben JSON-Wissensbasis:
1. **Editor-Modus** – vollständiger Einzel- und Masseneditor mit Backups.
2. **Google-Modus** – moderne, schreibgeschützte interne Helpdesk-Suche mit Artikel-Viewer.
3. **Optionaler Ollama-Fallback** – nur bei 0 Treffern einen strukturierten KI-Entwurf erzeugen und getrennt im Staging ablegen.
Der Betriebsmodus wird ausschließlich über `APP_MODE` gewählt. Es ist kein zweiter Build und kein anderes Image nötig.
@@ -47,6 +48,8 @@ Reiner Helpdesk-/Viewer-Modus:
- automatisches Neu-Einlesen des Dateiindex (standardmäßig alle 60 Sekunden)
- **keine Bearbeitungsoberfläche**
- **PUT-/Bulk-/Reload-Endpunkte werden serverseitig mit HTTP 403 gesperrt**
- optionaler Ollama-Fallback bei exakt 0 KB-Treffern
- KI-Ergebnisse werden als ungeprüfte JSON-Artikel in einem separaten Staging-Verzeichnis gespeichert
`viewer` und `search` werden zusätzlich als Alias für `google` akzeptiert. Für Deployments sollte aus Gründen der Eindeutigkeit `editor` oder `google` verwendet werden.
@@ -109,6 +112,106 @@ Für den Google-Modus wird `KB_DATA_MOUNT_MODE=ro` empfohlen. Damit existieren z
Der Backup-Pfad wird im Google-Modus nicht benutzt; er bleibt nur Teil derselben Compose-Konfiguration.
## Optionaler Ollama-Fallback mit Staging
Der KI-Fallback ist standardmäßig **aus**. Wird er im Google-Modus aktiviert, ist der Ablauf:
```text
Suchanfrage
│
├─ normale KB hat Treffer ─────────────► normale Trefferliste
│
└─ normale KB hat 0 Treffer
│
▼
POST /api/ai/fallback
│
▼
Ollama /api/chat
stream=false + JSON-Schema
│
▼
STAGING_DIR/*.json
│
▼
GET /api/staging/{id}
│
▼
Artikel-Viewer mit
"AI-STAGING · UNGEPRÜFT"
```
Beispiel `.env` für den Search-Container:
```dotenv
APP_MODE=google
KB_DATA_MOUNT_MODE=ro
AI_FALLBACK_ENABLED=true
OLLAMA_BASE_URL=http://ollama:11434
OLLAMA_MODEL=dein-bereits-gepulltes-modell
OLLAMA_TIMEOUT=10m
OLLAMA_MAX_CONCURRENT=1
KB_STAGING_PATH=./staging
OLLAMA_STAGING_AUTO_REPLY=false
OLLAMA_STAGING_MIN_SCORE=0.78
```
`OLLAMA_MODEL` hat absichtlich keinen hartcodierten Standard. Bei aktiviertem Fallback muss ein auf deiner Ollama-Instanz vorhandenes Modell angegeben werden.
### Docker-Netzwerk zu Ollama
`OLLAMA_BASE_URL=http://ollama:11434` funktioniert, wenn der Search-Container den Ollama-Container im selben Docker-Netzwerk unter dem Service-/Containernamen `ollama` erreichen kann.
Wenn Ollama in einem anderen Compose-Stack läuft, verbindest du beide Stacks am einfachsten mit demselben externen Docker-Netzwerk und verwendest dort den Ollama-Service-Namen. Alternativ kann `OLLAMA_BASE_URL` auf einen anderen vom Search-Container erreichbaren Host gesetzt werden.
Der Browser spricht **nie direkt mit Ollama**. Nur das Go-Backend kennt `OLLAMA_BASE_URL`.
### Warum ein getrenntes Staging-Verzeichnis?
Das produktive `DATA_DIR` bleibt im Google-Modus read-only. KI-Ergebnisse werden ausschließlich in `STAGING_DIR` geschrieben. Der Pfad darf weder innerhalb von `DATA_DIR` liegen noch `DATA_DIR` enthalten; die Anwendung verweigert sonst den Start. Dadurch werden ungeprüfte KI-Entwürfe nicht durch den normalen Index aufgenommen.
Ein Staging-Artikel verwendet dasselbe JSON-Format wie die restliche Wissensbasis, zum Beispiel:
```json
{
"id": "KB-AI-STAGING-20260729-120000-A1B2C3D4",
"title": "...",
"text": "...",
"answer": "...",
"auto_reply": false,
"min_score": 0.78,
"categories": ["AI-Staging", "Windows"],
"keywords": ["..."],
"source": "Ollama / modellname (AI-Staging)",
"source_uri": "",
"language": "de-DE",
"communication_style": "formal"
}
```
`auto_reply` ist im Staging standardmäßig bewusst `false`. Das kann über `OLLAMA_STAGING_AUTO_REPLY=true` geändert werden, wird für ungeprüfte KI-Inhalte aber nicht empfohlen.
### 10-Minuten-Timeout
`OLLAMA_TIMEOUT=10m` ist der Standard. Der Timeout wird im Request-Kontext und im Go-HTTP-Client durchgesetzt. Zusätzlich passt der Server seinen HTTP-`WriteTimeout` an, damit eine erlaubte 10-Minuten-Generierung nicht bereits nach dem normalen 60-Sekunden-Timeout abgebrochen wird.
Im Browser bleibt der Fetch-Request offen. Währenddessen zeigt die Oberfläche einen Laufzeitzähler und einen Staging-Status. Nach erfolgreicher Generierung lädt der Browser den gespeicherten Artikel erneut über die Staging-API und öffnet ihn automatisch.
### Schutz vor Missbrauch
Der KI-Endpunkt ist kein freier Chat-Proxy. Das Backend:
- akzeptiert nur eine Suchanfrage,
- begrenzt deren Länge,
- prüft unmittelbar vor Ollama erneut, dass die produktive KB wirklich `0` Treffer hat,
- begrenzt parallele Generierungen über `OLLAMA_MAX_CONCURRENT`,
- fordert von Ollama Structured Output nach einem festen JSON-Schema,
- setzt kritische Metadaten wie ID, Sprache, Quelle, `auto_reply` und `min_score` serverseitig,
- speichert atomar über Temp-Datei + Rename,
- lässt Ollama keine angeblichen Quellen/URLs in diese Metadaten schreiben.
## Ein Image, zwei Container
Als fertiges Beispiel liegt `docker-compose.dual.yml` bei. Es startet denselben Build gleichzeitig als Editor auf Port 8080 und als read-only Helpdesk-Suche auf Port 8081:
@@ -168,6 +271,15 @@ Beide Container lesen damit denselben Bestand. Im Google-Modus wird der Dateiind
| `KB_DATA_MOUNT_MODE` | `rw` | `rw` für Editor, empfohlen `ro` für Google-Modus |
| `KB_BACKUP_PATH` | `./backups` | Hostpfad für Backups |
| `KB_EDITOR_PORT` | `8080` | veröffentlichter Host-Port |
| `KB_STAGING_PATH` | `./staging` | Hostpfad für ungeprüfte KI-Entwürfe |
| `STAGING_DIR` | neben `DATA_DIR` als `staging` | Staging-Pfad im Prozess/Container |
| `AI_FALLBACK_ENABLED` | `false` | Ollama-Fallback im Google-Modus aktivieren |
| `OLLAMA_BASE_URL` | `http://ollama:11434` | Vom Go-Container erreichbare Ollama-Basis-URL |
| `OLLAMA_MODEL` | leer / erforderlich wenn aktiv | Modellname auf der Ollama-Instanz |
| `OLLAMA_TIMEOUT` | `10m` | Maximale Dauer einer Ollama-Anfrage |
| `OLLAMA_MAX_CONCURRENT` | `1` | Maximale parallele KI-Generierungen, 1–16 |
| `OLLAMA_STAGING_AUTO_REPLY` | `false` | `auto_reply` für neu erzeugte Staging-Artikel |
| `OLLAMA_STAGING_MIN_SCORE` | `0.78` | `min_score` für Staging-Artikel |
## Suche und Ranking im Google-Modus
@@ -189,7 +301,7 @@ Die Such-URL ist teilbar:
/?q=0x80070005
```
Ein geöffneter Artikel erhält zusätzlich `doc=<interner-key>` und kann so intern direkt verlinkt werden.
Ein geöffneter produktiver Artikel erhält zusätzlich `doc=<interner-key>`. Ein KI-Staging-Artikel verwendet stattdessen `staging=<staging-id>` und kann damit ebenfalls intern direkt verlinkt werden.
## Tastatur
@@ -251,6 +363,11 @@ Lesend in beiden Modi:
- `GET /api/facets?limit=10`
- `GET /api/items/{key}`
Optional bei aktiviertem Ollama-Fallback:
- `POST /api/ai/fallback` mit `{"query":"..."}` – nur zulässig, wenn die normale KB 0 Treffer liefert
- `GET /api/staging/{key}` – gespeicherten Staging-Entwurf laden
Nur im Editor-Modus:
- `PUT /api/items/{key}`
@@ -269,7 +386,9 @@ Das Compose-Setup:
- entfernt Linux-Capabilities,
- setzt `no-new-privileges`,
- verwendet `/tmp` als kleines tmpfs,
- kann den Knowledge-Mount im Google-Modus zusätzlich read-only einbinden.
- kann den Knowledge-Mount im Google-Modus zusätzlich read-only einbinden,
- mountet bei aktiviertem KI-Fallback nur das getrennte Staging-Verzeichnis schreibbar,
- verbindet den Browser nicht direkt mit Ollama.
Die Oberfläche hat keine externen CDN-/JavaScript-Abhängigkeiten.
@@ -292,6 +411,18 @@ APP_SUBTITLE="Interne Wissenssuche" \
go run ./cmd/server -data /pfad/zum/knowledge
```
Google-Modus mit Ollama-Fallback:
```bash
APP_MODE=google \
AI_FALLBACK_ENABLED=true \
OLLAMA_BASE_URL=http://127.0.0.1:11434 \
OLLAMA_MODEL=dein-modell \
OLLAMA_TIMEOUT=10m \
STAGING_DIR=/pfad/zum/staging \
go run ./cmd/server -data /pfad/zum/knowledge
```
Tests/Build:
```bash
@@ -315,10 +446,13 @@ go build -o kb-helpdesk ./cmd/server
│ ├── index.html
│ ├── app.js
│ └── style.css
├── internal/aifallback/ # Ollama-Client + Structured Output
├── internal/staging/ # atomisches Speichern/Laden ungeprüfter Entwürfe
├── internal/store/
│ ├── store.go
│ └── store_test.go
├── knowledge/
├── staging/
├── backups/
├── Dockerfile
├── docker-compose.yml