RAG Support Desk â OZM Chat Hook
Ziel
Logfiles und Dokumente rein â Wissen aufbauen â Crew fragt â Antwort kommt.
Multi-Channel-Isolation, anonymisierte Log-Analyse, persistente Chat-History.
Stack
- FastAPI (api.py) â REST-Backend, Uvicorn auf Port 8080
- PostgreSQL 17 + pgvector â Vektoren (768-dim) + Chat-History + Channels
- Ollama (
nomic-embed-text) â Lokale Embeddings via Docker - Anthropic Claude Sonnet 4.6 â Reasoning via API
- Vanilla HTML/JS (
public/index.html) â IRC-Style SPA, kein Build-Schritt - Docker Compose â 3 Services:
db,ollama,app
Schnellstart
docker compose up --build
# Browser: http://localhost:8080
# Ollama zieht nomic-embed-text beim ersten Start â kurz warten
Projektstruktur
api.py â FastAPI App â alle Endpoints, Routing
ingest.py â chunk_lines(), get_embedding(), ingest_text()
query.py â similarity_search(), ask(), analyze_log()
public/index.html â Frontend SPA (807 Zeilen, kein Framework)
schema.sql â PostgreSQL-Tabellen + pgvector Index
docker-compose.yml â db + ollama + app
Dockerfile â python:3.14-slim
.env â DB_URL, OLLAMA_URL, ANTHROPIC_API_KEY (nicht im Git)
.env.example â Vorlage
logs/ â Testlogs (*.log in .gitignore, test.log nicht)
migrate_*.sql â DB-Migrationen (bei Schema-Ănderungen ausfĂŒhren)
DB Schema
channels (id TEXT PK, name TEXT UNIQUE, category TEXT)
chunks (id SERIAL, source TEXT, content TEXT, embedding vector(768),
channel_id TEXT DEFAULT 'general', created_at TIMESTAMPTZ)
chat_history(id SERIAL, channel_id TEXT, question TEXT, answer TEXT,
sources JSONB, created_at TIMESTAMPTZ)
IVFFlat-Index auf chunks.embedding (cosine distance, lists=1 fĂŒr kleine Datasets).
Key Endpoints
| Methode | Pfad | Funktion |
|---|---|---|
GET |
/health |
DB + Ollama + Claude Status |
GET/POST |
/channels |
Channels abrufen / anlegen |
POST |
/ingest-text |
Text direkt ingestieren |
POST |
/ingest |
Datei ingestieren |
POST |
/query |
RAG-Anfrage (embedden â search â Claude) |
GET |
/history |
Chat-History eines Channels |
POST |
/analyze-log |
Anonymisierte Log-Analyse (Rohdaten nie gespeichert) |
POST |
/save-analysis |
Analyse in general Channel persistieren |
POST |
/admin/reset |
Chunks löschen (channel-scoped oder global) |
Architektur-Muster
Ingest-Pipeline: Text â chunk_lines(size=20) â Ollama Embedding â INSERT chunks
Query-Pipeline: Frage â Embedding â similarity_search(top_k=5) â Claude (max 1024 Token)
Channel-Scoping: Jede Query sucht im Ziel-Channel + immer auch in general
Log-Analyse: Anonymisierung durch System-Prompt â IPs/Hosts/User werden maskiert, Rohdaten werden nicht gespeichert
Ollama-Retry: 2 Versuche, 30s Timeout, 2s Pause â bei OllamaError Fehlermeldung zurĂŒckgeben.
HĂ€ufige Fallstricke
- Schema-Ănderungen brauchen eine
migrate_*.sqlâ nicht direktschema.sqlĂ€ndern, wenn DB schon lĂ€uft lists=1im IVFFlat-Index ist fĂŒr kleine Datasets gesetzt â bei >10k chunks erhöhen- Ollama-Health-Check kann beim ersten Start lĂ€nger dauern (Modell-Download)
python-multipartmuss in requirements.txt sein, sonst schlÀgt Form-Parsing fehl
URLs
- App: http://localhost:8080
- Health: http://localhost:8080/health