docs: Alicia-Aufgaben für Chat-Agent (Guard + RAG für WhatsApp/Telegram)
Quality Gate / 🛡️ Lint + Type Check + Tests (push) Failing after 12m34s
Quality Gate / 🛡️ Lint + Type Check + Tests (push) Failing after 12m34s
This commit is contained in:
@@ -0,0 +1,184 @@
|
|||||||
|
# BSN-Chatsystem — Chat-Agent: RAG-Antworten auf WhatsApp/Telegram
|
||||||
|
|
||||||
|
> **Für Alicia (Qwen 3.6, 256K Context)**
|
||||||
|
> **Stand:** 25.06.2026
|
||||||
|
> **Repo:** `git@git.datenhimmel.work:danielkrause/BSN-Chatsystem.git`
|
||||||
|
> **Branch:** `main`
|
||||||
|
> **Arbeitsverzeichnis:** `/home/hermes/workspace/bsn-chatbot/`
|
||||||
|
> **Issue:** [#14](https://git.datenhimmel.work/danielkrause/BSN-Chatsystem/issues/14)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ziel
|
||||||
|
|
||||||
|
WhatsApp- und Telegram-Nutzer können natürliche Fragen an den Bot stellen und bekommen
|
||||||
|
RAG-basierte Antworten aus der Datenbank (Submissions, Aussteller, Neuheiten).
|
||||||
|
|
||||||
|
Beispielfragen:
|
||||||
|
- „Welches Spiel ist ausverkauft?"
|
||||||
|
- „Was passiert in Halle 3?"
|
||||||
|
- „Wo finde ich eine Katze?"
|
||||||
|
- „Gibt es Neuheiten von Kosmos?"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aufgabe 14a — Neues Modul: `chat_agent.py`
|
||||||
|
|
||||||
|
### Datei: `chat_agent.py` (neue Datei im Repo-Root)
|
||||||
|
|
||||||
|
**Funktionen:**
|
||||||
|
|
||||||
|
1. **`is_boardgame_question(text: str) -> bool`**
|
||||||
|
- Guard-Filter: Prüft ob die Frage Brettspiel/SPIEL-bezogen ist
|
||||||
|
- Keywords: `spiel`, `brettspiel`, `halle`, `stand`, `verlag`, `aussteller`, `messe`,
|
||||||
|
`essen`, `spiel'`, `ausverkauft`, `neuheit`, `katze`, `kOSMOS`, `feuerland`,
|
||||||
|
`catan`, `pegasus`, `asmodee`, `ravensburger`, `schmidt`, `hans im glück`,
|
||||||
|
`lookout`, `zoch`, `amigo`, `queen games`, `days of wonder`, `rebel`, `portal`
|
||||||
|
- Auch erlaubt: Fragen mit `?` am Ende + deutschem Text (mind. 10 Zeichen)
|
||||||
|
- IMMER True bei offensichtlichen Brettspiel-Fragen
|
||||||
|
- IMMER False bei: Politik, Erwachseneninhalte, Beleidigungen, irrelevante Themen
|
||||||
|
|
||||||
|
2. **`answer_question(db, message: str, sender_name: str = "", history: list | None = None) -> str`**
|
||||||
|
- Baut Kontext aus 3 Tabellen (wie `/api/chat` in `app.py`):
|
||||||
|
- `submissions` (veröffentlichte Beiträge, LIMIT 40)
|
||||||
|
- `aussteller` (Verlage/Stände, gefiltert nach Keywords)
|
||||||
|
- `neuheiten` (Spiele-Neuheiten, gefiltert nach Keywords, mit AUSVERKAUFT-Flag)
|
||||||
|
- Sendet Kontext + Frage an LLM (`deepseek-v4-flash:cloud` via `https://ui.datenhimmel.work/api/chat/completions`)
|
||||||
|
- System-Prompt: Nur antworten wenn Info im Kontext, sonst „Dazu habe ich leider keine Informationen."
|
||||||
|
- Antwort immer auf Deutsch, 2-4 Sätze, mit Markdown-Links zu `/beitrag/ID`
|
||||||
|
- API-Key aus `~/.hermes/config.yaml` lesen (wie `/api/chat`)
|
||||||
|
- Timeout: 15 Sekunden, Fallback-Text bei Fehler
|
||||||
|
|
||||||
|
**Referenz:** Die bestehende `/api/chat`-Route in `app.py` (Zeilen 4022–4310) enthält exakt
|
||||||
|
die gleiche Logik. Extrahiere die Kernlogik in `answer_question()` und lass `/api/chat`
|
||||||
|
die neue Funktion aufrufen (Refactoring).
|
||||||
|
|
||||||
|
### Qualität
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ruff check chat_agent.py --fix && ruff format chat_agent.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**Commit:** `feat(chat): Chat-Agent-Modul mit Guard-Filter + RAG-Antworten`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aufgabe 14b — WhatsApp-Webhook: Frage-Erkennung + Antwort
|
||||||
|
|
||||||
|
### Datei: `app.py` — WhatsApp-Webhook (ab Zeile ~1162)
|
||||||
|
|
||||||
|
**Einbau-Ort:** Nach dem Intake-Block (Speichern der Submission), VOR dem `db.commit()`.
|
||||||
|
|
||||||
|
**Logik:**
|
||||||
|
```python
|
||||||
|
# Nach dem Intake (nach Thread-Erstellung/Aktualisierung):
|
||||||
|
if msg_type == "text" and not thread_found:
|
||||||
|
from chat_agent import is_boardgame_question, answer_question
|
||||||
|
if is_boardgame_question(content):
|
||||||
|
try:
|
||||||
|
reply = answer_question(db, content, sender_name)
|
||||||
|
if reply:
|
||||||
|
send_whatsapp_message(sender_id, reply)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[chat-agent error] {e}", file=sys.stderr)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Wichtig:**
|
||||||
|
- Nur bei TEXT-Nachrichten (nicht bei Bild/Audio/Video)
|
||||||
|
- Nur bei NEUEN Threads (nicht bei Thread-Appends)
|
||||||
|
- NACH allen anderen Checks (#out, registrieren, code, „Meine Daten löschen", FAQ)
|
||||||
|
- Exception-Handling: Fehler NIE zum Webhook-500 führen → immer `return "OK", 200`
|
||||||
|
- Inline-Import (vermeidet Circular-Imports)
|
||||||
|
|
||||||
|
### Qualität
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ruff check app.py --fix && ruff format app.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**Commit:** `feat(whatsapp): Frage-Erkennung + Chat-Agent-Antwort im WhatsApp-Webhook`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aufgabe 14c — Telegram-Webhook: Gleiche Logik
|
||||||
|
|
||||||
|
### Datei: `app.py` — Telegram-Webhook (Zeilen 5293–5317)
|
||||||
|
|
||||||
|
Alternativ: `webhook_queue.py` → `telegram_intake()` (Zeilen 172–215).
|
||||||
|
|
||||||
|
**Wo einbauen:** Nachdem die Nachricht aus dem Telegram-Update extrahiert wurde.
|
||||||
|
|
||||||
|
**Logik:** Gleiche wie WhatsApp:
|
||||||
|
```python
|
||||||
|
text = msg.get("text", "")
|
||||||
|
if text:
|
||||||
|
from chat_agent import is_boardgame_question, answer_question
|
||||||
|
if is_boardgame_question(text):
|
||||||
|
chat_id = msg.get("chat", {}).get("id")
|
||||||
|
reply = answer_question(db, text, sender_name)
|
||||||
|
# Sende Antwort via Telegram API
|
||||||
|
requests.post(f"{TELEGRAM_API}/sendMessage", json={
|
||||||
|
"chat_id": chat_id, "text": reply, "parse_mode": "Markdown"
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Qualität
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ruff check app.py --fix && ruff format app.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**Commit:** `feat(telegram): Chat-Agent-Antwort im Telegram-Webhook`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aufgabe 14d — Testfragen (manuel)
|
||||||
|
|
||||||
|
**NICHT automatisieren.** Nachdem Alicia gebaut hat, testet Cody:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test 1: Brettspiel-Frage
|
||||||
|
curl -X POST http://localhost:5002/api/chat \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"message": "Welches Spiel ist ausverkauft?"}'
|
||||||
|
|
||||||
|
# Test 2: Halle-Frage
|
||||||
|
curl -X POST http://localhost:5002/api/chat \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"message": "Was passiert in Halle 3?"}'
|
||||||
|
|
||||||
|
# Test 3: Katzen-Frage (Guard muss durchlassen!)
|
||||||
|
curl -X POST http://localhost:5002/api/chat \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"message": "Wo finde ich eine Katze?"}'
|
||||||
|
|
||||||
|
# Test 4: Off-Topic (Guard muss blocken)
|
||||||
|
curl -X POST http://localhost:5002/api/chat \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"message": "Wie wird das Wetter morgen?"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Qualitätsregeln
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Vor JEDEM Commit:
|
||||||
|
ruff check . --fix && ruff format . && pytest -v
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Commit-Messages:** `feat(chat):` / `feat(whatsapp):` / `feat(telegram):`
|
||||||
|
- **Docstrings:** Deutsch, Google-Style
|
||||||
|
- **Typ-Annotationen:** Jede Funktion (Python 3.13, PEP 604)
|
||||||
|
- **Kein `print()`** → `sys.stderr` für Debug
|
||||||
|
- **BSN-Blau `#20228a`** für Links in Antworten
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Wichtige Hinweise
|
||||||
|
|
||||||
|
1. **Kein Circular Import:** `chat_agent.py` importiert NICHTS aus `app.py`. Nur `sqlite3` + `requests` + `os` + `re`.
|
||||||
|
2. **Guard NICHT zu streng:** „Katze" ist ein legitimes Brettspiel-Thema (Die Crew, Katze im Sack, etc.). Der Guard soll nur WIRKLICHEN Missbrauch abwehren.
|
||||||
|
3. **Timeout:** LLM-Call max 15 Sekunden — WhatsApp erwartet schnelle Antwort (< 20s).
|
||||||
|
4. **Nie 500er:** Alle Chat-Agent-Fehler werden gefangen → immer `return "OK", 200`.
|
||||||
|
5. **`bsn-secrets` nicht nötig:** Der API-Key kommt aus `~/.hermes/config.yaml` (lokale Datei).
|
||||||
Reference in New Issue
Block a user