chatbot / rag_pipeline.py
Nguyen5's picture
commit
70e8124
"""
RAG PIPELINE – Version 26.11 (ohne Modi, stabil, juristisch korrekt)
"""
# from typing import List, Dict, Any, Tuple
# from langchain_core.messages import SystemMessage, HumanMessage
# from load_documents import DATASET, PDF_FILE, HTML_FILE
# from typing import List, Dict, Any, Tuple
# import os
# from langchain_core.messages import SystemMessage, HumanMessage
# from load_documents import DATASET, PDF_FILE
# 5.12_2:13
from typing import List, Dict, Any, Tuple
from langchain_core.messages import SystemMessage, HumanMessage
MAX_CHARS = 900
# ============================================================
# Quellenaufbereitung – NUR metadata verwenden!
# ============================================================
def build_sources_metadata(docs: List) -> List[Dict[str, Any]]:
sources = []
for idx, d in enumerate(docs):
meta = d.metadata
snippet = d.page_content[:300].replace("\n", " ")
# PDF
if meta.get("type") == "pdf":
sources.append({
"id": idx + 1,
"source": "Prüfungsordnung (PDF)",
"page": meta.get("page"),
"url": meta.get("pdf_url"), # KHÔNG tạo lại!
"snippet": snippet,
})
continue
# Hochschulgesetz NRW
if meta.get("type") == "hg":
sources.append({
"id": idx + 1,
"source": "Hochschulgesetz NRW",
"page": None,
"url": meta.get("viewer_url"), # KHÔNG tạo lại!
"snippet": snippet,
})
continue
return sources
# ============================================================
# Kontextaufbereitung
# ============================================================
def format_context(docs: List) -> str:
if not docs:
return "(Kein relevanter Kontext gefunden.)"
blocks = []
for i, d in enumerate(docs):
meta = d.metadata
doc_type = meta.get("type")
label = "Prüfungsordnung" if doc_type == "pdf" else "Hochschulgesetz NRW"
if doc_type == "pdf":
page = meta.get("page")
label += f", Seite {page+1}" if isinstance(page, int) else ""
blocks.append(
f"[KONTEXT {i+1}] ({label})\n{d.page_content[:MAX_CHARS]}"
)
return "\n\n".join(blocks)
# -----------------------------
# Systemprompt — verschärft
# -----------------------------
SYSTEM_PROMPT = """
Du bist ein hochpräziser juristischer Chatbot für Prüfungsrecht
mit Zugriff nur auf:
- die Prüfungsordnung (als PDF) und
- das Hochschulgesetz NRW (als HTML aus der offiziellen Druckversion).
Strenge Regeln:
1. Antworte ausschließlich anhand des bereitgestellten Kontextes
(KONTEXT-Abschnitte). Wenn die Information nicht im Kontext steht,
sage ausdrücklich, dass dies aus den vorliegenden Dokumenten nicht
hervorgeht und du dazu nichts Sicheres sagen kannst.
2.
Keine Spekulationen, keine Vermutungen.
3. Antworte in zusammenhängenden, ganzen Sätzen. Verwende keine Mischung aus Deutsch und Englisch.
4. Nenne, soweit aus dem Kontext erkennbar,
- die rechtliche Grundlage (z.B. Paragraph, Artikel),
- das Dokument (Prüfungsordnung / Hochschulgesetz NRW),
- die Seite (bei der Prüfungsordnung), wenn im Kontext vorhanden.
5. Füge KEINE externen Informationen hinzu, z.B. aus anderen Gesetzen,
Webseiten oder allgemeinem Wissen. Nur das, was im Kontext steht,
darf in der Antwort verwendet werden.
Wenn der Kontext keine eindeutige Antwort zulässt, erkläre klar,
warum keine sichere Antwort möglich ist und welche Informationen
im Dokument fehlen.
"""
# -----------------------------
# Hauptfunktion
# -----------------------------
def answer(question: str, retriever, chat_model) -> Tuple[str, List[Dict[str, Any]]]:
"""
Haupt-RAG-Funktion:
- ruft retriever.invoke(question) auf,
- baut einen präzisen Prompt mit KONTEXT,
- ruft LLM auf,
- gibt Antworttext + Quellenliste zurück.
"""
# 1. Dokumente holen
docs = retriever.invoke(question)
context_str = format_context(docs)
# 2. Prompt bauen
user_prompt = f"""
FRAGE:
{question}
NUTZE AUSSCHLIESSLICH DIESEN KONTEXT:
{context_str}
AUFGABE:
Formuliere eine juristisch korrekte, gut verständliche Antwort
ausschließlich anhand des obigen Kontextes.
- Wenn der Kontext aus den Dokumenten eine klare Antwort erlaubt,
erläutere diese strukturiert und in vollständigen Sätzen.
- Wenn der Kontext KEINE klare Antwort erlaubt oder wichtige Informationen
fehlen, erkläre das offen und formuliere KEINE Vermutung.
"""
msgs = [
SystemMessage(content=SYSTEM_PROMPT),
HumanMessage(content=user_prompt),
]
# 3. LLM aufrufen
result = chat_model.invoke(msgs)
answer_text = result.content.strip()
# 4. Quellenliste bauen
sources = build_sources_metadata(docs)
return answer_text, sources