6.4 KiB
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.gitBranch:mainArbeitsverzeichnis:/home/hermes/workspace/bsn-chatbot/Issue: #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:
-
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
-
answer_question(db, message: str, sender_name: str = "", history: list | None = None) -> str- Baut Kontext aus 3 Tabellen (wie
/api/chatinapp.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:cloudviahttps://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.yamllesen (wie/api/chat) - Timeout: 15 Sekunden, Fallback-Text bei Fehler
- Baut Kontext aus 3 Tabellen (wie
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
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:
# 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
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:
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
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:
# 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
# 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.stderrfür Debug - BSN-Blau
#20228afür Links in Antworten
Wichtige Hinweise
- Kein Circular Import:
chat_agent.pyimportiert NICHTS ausapp.py. Nursqlite3+requests+os+re. - Guard NICHT zu streng: „Katze" ist ein legitimes Brettspiel-Thema (Die Crew, Katze im Sack, etc.). Der Guard soll nur WIRKLICHEN Missbrauch abwehren.
- Timeout: LLM-Call max 15 Sekunden — WhatsApp erwartet schnelle Antwort (< 20s).
- Nie 500er: Alle Chat-Agent-Fehler werden gefangen → immer
return "OK", 200. bsn-secretsnicht nötig: Der API-Key kommt aus~/.hermes/config.yaml(lokale Datei).