From 10f3c8323a2e854bc706fc7d89478163d5219dc3 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 17 Apr 2026 21:57:42 +0200 Subject: [PATCH] rag_mode: RAG-first als Default + Persistenz, Router haertet persoenliche Fragen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rag_mode.py: - Default fuer neue/unbekannte Chats: RAG-first (True statt False) - State wird auf Disk geschrieben (/opt/homelab-ai-bot/data/rag_mode.json), ueberlebt Bot-Restarts; threadsicher. llm.py: - _LOCAL_OVERRIDES erweitert um persoenliche Possessiv-/Besitz-Marker: wohnung(en), apartment, condo/kondo, immobilie, kambodscha/cambodia, phnom penh, arakawa, gekostet, kaufpreis, bezahlt, ausgegeben, ueberweisung, meine/mein/meines/..., was haben, wie viel habe ich, ich fuer/für. Damit werden klar persoenliche Fragen nie mehr faelschlich an Sonar geroutet, selbst wenn Web-Trigger wie "wie viel" im Text vorkommen. Hintergrund: Eine Frage der Form "wie viel habe ich fuer die Wohnungen in Kambodscha bezahlt" wurde an Perplexity/Sonar geroutet (Websuche) statt an RAG, weil der Mode-Schalter durch einen Bot-Restart im RAM verloren ging und der Router bei "wie viel" sofort MODEL_ONLINE waehlte. --- homelab-ai-bot/llm.py | 10 ++++++++ homelab-ai-bot/rag_mode.py | 52 +++++++++++++++++++++++++++++++++----- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/homelab-ai-bot/llm.py b/homelab-ai-bot/llm.py index 5caad8b8..d8357d12 100644 --- a/homelab-ai-bot/llm.py +++ b/homelab-ai-bot/llm.py @@ -48,6 +48,16 @@ _LOCAL_OVERRIDES = [ "habe ich", "welche habe", "was habe ich", "jahreskosten", "jährlichen", "jährliche", "jaehrlichen", "jaehrliche", "monatliche kosten", "versicherungskosten", "beitragsrechnung", + # Possessiv/Persoenlich (2026-04-16) + "meine", "mein ", "meiner", "meines", "meinem", "meinen", + "wohnung", "wohnungen", "apartment", "apartments", + "condo", "condos", "kondo", "kondos", "immobilie", "immobilien", + "kambodscha", "cambodia", "phnom penh", "arakawa", + "gekostet", "gekauft", "kaufpreis", "kaufpreise", "bezahlt", + "ausgegeben", "ueberweisung", "überweisung", + "was haben", "wieviel habe ich", "wie viel habe ich", + "fuer die wohnung", "für die wohnung", + "ich fuer", "ich für", ] _WEB_TRIGGERS = [ "recherche", "recherchiere", "suche im internet", "web search", diff --git a/homelab-ai-bot/rag_mode.py b/homelab-ai-bot/rag_mode.py index 7fe514a2..18526741 100644 --- a/homelab-ai-bot/rag_mode.py +++ b/homelab-ai-bot/rag_mode.py @@ -1,24 +1,64 @@ -"""Pro-Chat: Betriebsart Unterlagen zuerst (RAG vor Web).""" +"""Pro-Chat: Betriebsart Unterlagen zuerst (RAG vor Web). + +Persistenz: /opt/homelab-ai-bot/data/rag_mode.json (ueberlebt Bot-Restarts). +Default fuer neue Chats: True (RAG-first, keine Web-Suche ohne Zutun). +""" from __future__ import annotations +import json +import logging +import os +import threading +from pathlib import Path from typing import Optional, Tuple +log = logging.getLogger("rag_mode") + +_STATE_PATH = Path(os.environ.get("RAG_MODE_STATE", "/opt/homelab-ai-bot/data/rag_mode.json")) +_DEFAULT_ON = True +_lock = threading.Lock() _active: dict[str, bool] = {} +_loaded = False BTN_OFF = "📁 Unterlagen: AUS" BTN_ON = "📁 Unterlagen: AN" +def _load() -> None: + global _loaded, _active + if _loaded: + return + try: + if _STATE_PATH.exists(): + data = json.loads(_STATE_PATH.read_text(encoding="utf-8") or "{}") + if isinstance(data, dict): + _active = {str(k): bool(v) for k, v in data.items()} + log.info("rag_mode geladen: %s Eintraege aus %s", len(_active), _STATE_PATH) + except Exception as e: + log.warning("rag_mode konnte State nicht laden: %s", e) + _loaded = True + + +def _save() -> None: + try: + _STATE_PATH.parent.mkdir(parents=True, exist_ok=True) + _STATE_PATH.write_text(json.dumps(_active, ensure_ascii=False, indent=2), encoding="utf-8") + except Exception as e: + log.warning("rag_mode konnte State nicht schreiben: %s", e) + + def is_document_mode(channel_key: str) -> bool: - return _active.get(channel_key, False) + with _lock: + _load() + return _active.get(channel_key, _DEFAULT_ON) def set_document_mode(channel_key: str, on: bool) -> None: - if on: - _active[channel_key] = True - else: - _active.pop(channel_key, None) + with _lock: + _load() + _active[channel_key] = bool(on) + _save() def toggle_document_mode(channel_key: str) -> bool: