diff --git a/homelab-ai-bot/llm.py b/homelab-ai-bot/llm.py index 300c2017..f3bb6afc 100644 --- a/homelab-ai-bot/llm.py +++ b/homelab-ai-bot/llm.py @@ -428,6 +428,11 @@ def ask_with_tools(question: str, tool_handlers: dict, session_id: str = None, d "lvm", "allianz", "ergo", "huk", "nuernberger", "jahreskosten", "jährlichen", "jährliche", "jaehrlichen", "jaehrliche", "monatliche kosten", "versicherungskosten", "beitragsrechnung", + "wohnung", "wohnungen", "immobilie", "immobilien", "mietwohnung", + "condo", "apartment", "eigentumswohnung", "häuser", "haeuser", + "haus ", " haus", + "grundstueck", "grundstück", "kambodscha", "cambodia", "takeo", + "phnom", "sihanouk", "siem reap", ] _q_low = question.lower() if route == MODEL_LOCAL and (document_mode or any(k in _q_low for k in _DOC_KW)): @@ -450,12 +455,48 @@ def ask_with_tools(question: str, tool_handlers: dict, session_id: str = None, d question + " Versicherung Beitrag Beitragsrechnung Jahresbetrag" ) + elif any( + x in _q_low + for x in ( + "wohnung", + "immobilie", + "condo", + "apartment", + "grundstueck", + "grundstück", + "kambodscha", + "cambodia", + "takeo", + "phnom", + ) + ): + _rag_q = question + " Wohnung Immobilie Mietvertrag Kambodscha" + _rag_finance_focus = any( + x in _q_low + for x in ( + "kosten", + "beitrag", + "€", + " eur", + "eur,", + "zahlung", + "versicherung", + "jähr", + "jaehr", + "jahreskosten", + "monatliche kosten", + "beitragsrechnung", + "preis", + "gebühr", + "gebuehr", + ) + ) _rag_res = _rag_fn(query=_rag_q, top_k=60) if _rag_res and not _rag_res.startswith("Keine"): log.info("RAG-Pflicht: %d Zeichen — loesche Session-History", len(str(_rag_res))) - messages = [ - {"role": "system", "content": _full_prompt - + "\n\nWICHTIG: Ignoriere fruehere Antworten. " + if _rag_finance_focus: + _rag_extra = ( + "\n\nWICHTIG: Ignoriere fruehere Antworten. " + "STIL-OVERRIDE: Trotz globalem Hausmeister-Stil (kurz): diese Antwort bewusst etwas ausfuehrlicher " + "(mehrere Saetze, Aufzaehlungen), damit Kosten und Zeitraeume ohne Nachfrage klar sind. " + "Die Dokumentensuche unten ist die einzige Wahrheit. " @@ -472,14 +513,37 @@ def ask_with_tools(question: str, tool_handlers: dict, session_id: str = None, d + "wenn nicht erkennbar, ausdruecklich sagen. " + "VERBOTEN: Antwort nur als nackte Zahl (z.B. nur eine EUR-Zeile ohne Kontext). " + "Zu JEDEM Betrag mindestens einen Dateinamen aus den Treffern nennen. " - + "Bei Kfz/Fahrzeug: sagen welches Dokument sich darauf bezieht oder dass die Zuordnung unsicher ist."}, + + "Bei Kfz/Fahrzeug: sagen welches Dokument sich darauf bezieht oder dass die Zuordnung unsicher ist." + ) + _rag_user_suffix = ( + "\n\n[Strukturiert: Kurzfassung zu Jahr/jaehrlich, dann je Dokument Dateiname mit Betrag " + + "und Zeitraum aus den Treffern; keine reine Ein-Zahl-Antwort.]" + ) + else: + _rag_extra = ( + "\n\nWICHTIG: Ignoriere fruehere Antworten und Priorisiere die Dokumentensuche unten " + + "ueber Kurzinfos aus dem Gedaechtnis. " + + "STIL-OVERRIDE: etwas ausfuehrlicher als sonst (Aufzaehlungen ok). " + + "Die Treffer sind die einzige belastbare Grundlage. " + + "Struktur: (1) Kurzfassung: was in den Treffern zur Frage passt. " + + "(2) Je relevantem Treffer: Dateiname/Ordnerpfad und erkennbare Fakten aus dem Snippet " + + "(Objekt, Ort, Datum, Parteien) — nichts erfinden. " + + "Alle plausibel passenden Treffer nennen, nicht nur den ersten. " + + "Wenn nichts passt: klar sagen dass die Suche nichts Relevantes lieferte." + ) + _rag_user_suffix = ( + "\n\n[Strukturiert: Kurzfassung, dann Liste je Treffer mit Dateiname und Fakten nur aus dem Snippet; " + + "keine erfundenen Details.]" + ) + messages = [ + {"role": "system", "content": _full_prompt + _rag_extra}, {"role": "assistant", "content": None, "tool_calls": [{"id": "forced_rag", "type": "function", "function": {"name": "rag_search", "arguments": json.dumps({"query": _rag_q, "top_k": 60})}}]}, {"role": "tool", "tool_call_id": "forced_rag", "content": str(_rag_res)[:100000]}, - {"role": "user", "content": question + "\n\n[Strukturiert antworten: Kurzfassung zu Jahr/jaehrlich, dann je Dokument Dateiname mit Betrag und Zeitraum aus den Treffern; keine reine Ein-Zahl-Antwort.]"}, + {"role": "user", "content": question + _rag_user_suffix}, ] except Exception as e: log.warning("RAG-Pflicht Fehler: %s", e) diff --git a/homelab-ai-bot/tools/rag.py b/homelab-ai-bot/tools/rag.py index a2585960..154588ff 100644 --- a/homelab-ai-bot/tools/rag.py +++ b/homelab-ai-bot/tools/rag.py @@ -68,6 +68,7 @@ Du hast Zugriff auf eine private Wissensbasis mit >21.000 Dokumenten (Vertraege, WANN rag_search AUFRUFEN — IMMER bei diesen Fragen: - "habe ich..." / "gibt es..." / "wo ist..." / "finde..." / "zeig mir..." + Dokument/Vertrag/Versicherung/Bescheid - Jede Frage nach persoenlichen Unterlagen, Vertraegen, Versicherungen, Rechnungen, Bescheiden +- Wohnungen, Immobilien, Grundstuecke, Mietobjekte, Auslands-Objekte (z.B. Kambodscha): immer rag_search — auch wenn das Gedaechtnis schon einen Wohnort nennt - AUCH wenn du glaubst die Antwort zu kennen — das Gedaechtnis ist NICHT die Wissensbasis! - AUCH wenn das Thema im Gedaechtnis steht — trotzdem rag_search aufrufen fuer vollstaendige Antwort @@ -75,6 +76,7 @@ WANN NICHT: Nur bei reinen Homelab/IT-Fragen, Smalltalk, oder wenn der User expl SUCHANFRAGE: Kurze Keywords, KEINE ganzen Saetze. Beispiele: - "Familienbuch" / "Grundsteuer Erklaerung" / "Haftpflicht" / "Kindergeld" / "Mietvertrag" / "Arbeitsvertrag" / "Reisepass" +- "Wohnung Kambodscha" / "Immobilie" / "Condo" ERGEBNISSE AUSWERTEN: - Bei breiten Fragen ("welche Versicherungen", Jahreskosten, Listen): top_k=15-25, ALLE Treffer aus der Tool-Antwort abarbeiten