rag: 19/20 E2E-Tests bestanden — Dedup + Anti-Halluzination + Pflicht-Prompt
- Dedup auf Dateinamen-Ebene (Extension + Kopie-Marker ignorieren) - docnm_kwd boost 1.5→3.0 fuer bessere Ordner-Treffer - SYSTEM_PROMPT_EXTRA verschaerft: IMMER rag_search bei Dokument-Fragen - Expliziter Ende-Marker gegen LLM-Halluzination - MIN_TOP_K=5, Default top_k=8 - Content-Snippet 400→600 Zeichen Ref: Issue #51
This commit is contained in:
parent
a43c0b913b
commit
59e53a2750
1 changed files with 41 additions and 19 deletions
|
|
@ -58,20 +58,27 @@ TOOLS = [
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
SYSTEM_PROMPT_EXTRA = """RAG DOKUMENTENSUCHE:
|
SYSTEM_PROMPT_EXTRA = """RAG DOKUMENTENSUCHE — PFLICHT-REGELN:
|
||||||
Du hast Zugriff auf eine private Wissensbasis mit >21.000 Dokumenten (Vertraege, Versicherungen, Rente, Finanzamt, Familiendokumente, Anleitungen, Buecher, persoenliche Unterlagen).
|
Du hast Zugriff auf eine private Wissensbasis mit >21.000 Dokumenten (Vertraege, Versicherungen, Rente, Finanzamt, Familiendokumente, Anleitungen, Buecher, persoenliche Unterlagen, Arbeitsvertraege, Kindergeld, Reisepass, Personalausweis, KFZ, Mietvertraege, Bausparvertraege, Rechnungen).
|
||||||
Nutze rag_search wenn der User nach Dokumenten, Vertraegen, persoenlichen Unterlagen oder Informationen aus seinen Dateien fragt.
|
|
||||||
Die Suchanfrage sollte kurze Keywords sein, KEINE ganzen Saetze. Beispiele:
|
WANN rag_search AUFRUFEN — IMMER bei diesen Fragen:
|
||||||
- "Familienbuch Opa Oma"
|
- "habe ich..." / "gibt es..." / "wo ist..." / "finde..." / "zeig mir..." + Dokument/Vertrag/Versicherung/Bescheid
|
||||||
- "Grundsteuer Erklaerung"
|
- Jede Frage nach persoenlichen Unterlagen, Vertraegen, Versicherungen, Rechnungen, Bescheiden
|
||||||
- "Nuernberger Versicherung"
|
- AUCH wenn du glaubst die Antwort zu kennen — das Gedaechtnis ist NICHT die Wissensbasis!
|
||||||
- "Allianz Beitraege"
|
- AUCH wenn das Thema im Gedaechtnis steht — trotzdem rag_search aufrufen fuer vollstaendige Antwort
|
||||||
Bei schlechten Ergebnissen: andere Keywords versuchen oder Dokumentnamen direkt suchen.
|
|
||||||
WICHTIG:
|
WANN NICHT: Nur bei reinen Homelab/IT-Fragen, Smalltalk, oder wenn der User explizit NICHT nach Dokumenten fragt.
|
||||||
- Bei breiten Kategorie-Fragen ("welche Versicherungen", "alle Vertraege"): top_k=10
|
|
||||||
- Liste NUR die Dokumente auf die rag_search zurueckliefert. ERFINDE KEINE Details die nicht im Ergebnis stehen.
|
SUCHANFRAGE: Kurze Keywords, KEINE ganzen Saetze. Beispiele:
|
||||||
- Der Ordnerpfad im Dokumentnamen (vor dem Dateinamen, getrennt durch __) zeigt die Kategorie.
|
- "Familienbuch" / "Grundsteuer Erklaerung" / "Haftpflicht" / "Kindergeld" / "Mietvertrag" / "Arbeitsvertrag" / "Reisepass"
|
||||||
- Wenn das Ergebnis Dokumente zeigt, liste sie auf — auch wenn du den Inhalt nicht vollstaendig kennst."""
|
|
||||||
|
ERGEBNISSE AUSWERTEN:
|
||||||
|
- Bei breiten Fragen ("welche Versicherungen", "alle Vertraege"): top_k=10
|
||||||
|
- Liste die gefundenen Dokumente mit Ordner und kurzem Inhalt auf
|
||||||
|
- ERFINDE KEINE Details die nicht im Ergebnis stehen
|
||||||
|
- Der Ordnerpfad (vor dem Dateinamen, getrennt durch __) zeigt die Kategorie
|
||||||
|
- Wenn rag_search Treffer liefert: IMMER auflisten, auch wenn Inhalt unvollstaendig
|
||||||
|
- Antworte NIEMALS "keine gefunden" oder "nicht gespeichert" OHNE vorher rag_search aufgerufen zu haben"""
|
||||||
|
|
||||||
|
|
||||||
def _basic_auth_header() -> str:
|
def _basic_auth_header() -> str:
|
||||||
|
|
@ -121,6 +128,19 @@ def _folder_from_docname(name: str) -> str:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def _dedup_key(name: str) -> str:
|
||||||
|
"""Normalisiert Dokumentnamen fuer Deduplizierung.
|
||||||
|
|
||||||
|
Extrahiert nur den Dateinamen (nach letztem __), ignoriert
|
||||||
|
Dateiendung und Kopie-Marker wie (1), (2).
|
||||||
|
'Ordner__Foo(1).pdf' und 'Anderer__Foo.txt' werden als gleich behandelt.
|
||||||
|
"""
|
||||||
|
fname = name.rsplit("__", 1)[-1] if "__" in name else name
|
||||||
|
key = re.sub(r"\.(pdf|txt|docx?|xlsx?|csv|png|jpg|jpeg)$", "", fname, flags=re.IGNORECASE)
|
||||||
|
key = re.sub(r"\s*\(\d+\)\s*$", "", key).rstrip()
|
||||||
|
return key.lower()
|
||||||
|
|
||||||
|
|
||||||
def _es_hybrid_search(query: str, es_size: int) -> dict:
|
def _es_hybrid_search(query: str, es_size: int) -> dict:
|
||||||
qvec = _ollama_embed(query)
|
qvec = _ollama_embed(query)
|
||||||
if not qvec:
|
if not qvec:
|
||||||
|
|
@ -187,7 +207,6 @@ def handle_rag_search(query: str, top_k: int = 8, **kw):
|
||||||
|
|
||||||
seen_docs: set[str] = set()
|
seen_docs: set[str] = set()
|
||||||
lines: list[str] = []
|
lines: list[str] = []
|
||||||
lines.append(f"**{len(hits)} Treffer fuer '{query}'** (Top {top_k} Dokumente):\n")
|
|
||||||
|
|
||||||
count = 0
|
count = 0
|
||||||
for h in hits:
|
for h in hits:
|
||||||
|
|
@ -195,10 +214,10 @@ def handle_rag_search(query: str, top_k: int = 8, **kw):
|
||||||
break
|
break
|
||||||
src = h.get("_source") or {}
|
src = h.get("_source") or {}
|
||||||
doc_name = src.get("docnm_kwd") or "?"
|
doc_name = src.get("docnm_kwd") or "?"
|
||||||
doc_key = str(doc_name)
|
dk = _dedup_key(doc_name)
|
||||||
if doc_key in seen_docs:
|
if dk in seen_docs:
|
||||||
continue
|
continue
|
||||||
seen_docs.add(doc_key)
|
seen_docs.add(dk)
|
||||||
|
|
||||||
score = h.get("_score") or 0.0
|
score = h.get("_score") or 0.0
|
||||||
raw = src.get("content_with_weight") or src.get("content_de") or ""
|
raw = src.get("content_with_weight") or src.get("content_de") or ""
|
||||||
|
|
@ -216,7 +235,10 @@ def handle_rag_search(query: str, top_k: int = 8, **kw):
|
||||||
count += 1
|
count += 1
|
||||||
|
|
||||||
if count == 0:
|
if count == 0:
|
||||||
return f"Keine eindeutigen Dokumente fuer '{query}' (nach Deduplizierung)."
|
return f"Keine Dokumente fuer '{query}' gefunden."
|
||||||
|
|
||||||
|
lines.insert(0, f"**{count} verschiedene Dokumente fuer '{query}':**\n")
|
||||||
|
lines.append("\n---\n(Ende der Ergebnisse. Nur diese Dokumente wurden gefunden.)")
|
||||||
|
|
||||||
return "\n".join(lines)
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue