# rag_pipeline.py – SUPABASE RAG VERSION from typing import List, Dict, Any, Tuple from langchain_core.messages import SystemMessage, HumanMessage MAX_CHARS = 900 def build_sources_metadata(docs: List) -> List[Dict[str, Any]]: srcs = [] for i, d in enumerate(docs): meta = d.metadata src = meta.get("source") page = meta.get("page") snippet = d.page_content[:300].replace("\n", " ") if src == "Prüfungsordnung (PDF)": pdf_url = meta["pdf_url"] if isinstance(page, int) and pdf_url: url = f"{pdf_url}#page={page + 1}" else: url = pdf_url elif src == "Hochschulgesetz NRW": url = meta["url"] page = None else: url = None srcs.append( { "id": i + 1, "source": src, "page": page + 1 if isinstance(page, int) else None, "url": url, "snippet": snippet, } ) return srcs def format_context(docs): if not docs: return "(Kein relevanter Kontext gefunden.)" out_lines = [] for i, d in enumerate(docs): txt = d.page_content[:MAX_CHARS] src = d.metadata.get("source") page = d.metadata.get("page") if src == "Prüfungsordnung (PDF)" and isinstance(page, int): src_str = f"{src}, Seite {page + 1}" else: src_str = src out_lines.append(f"[KONTEXT {i+1}] ({src_str})\n{txt}") return "\n\n".join(out_lines) SYSTEM_PROMPT = """ Du bist ein juristisch präziser Chatbot für Prüfungsrecht. Du nutzt ausschließlich: - die Prüfungsordnung (PDF) und - das Hochschulgesetz NRW (Absätze aus der Datenbank) Regeln: 1. Keine Halluzinationen – nur Inhalte aus dem gelieferten Kontext. 2. Wenn der Kontext unklar ist, sage ausdrücklich, dass keine sichere Aussage möglich ist. 3. Antworte immer in gut verständlichem, ganzen Sätzen. 4. Nenne, soweit im Kontext erkennbar: - Paragraphen oder Überschriften, - das Dokument (Prüfungsordnung / Hochschulgesetz NRW), - Seitenzahl (bei der Prüfungsordnung). """ def answer(question: str, retriever, chat_model) -> Tuple[str, List[Dict[str, Any]]]: docs = retriever.invoke(question) context_str = format_context(docs) human = f""" FRAGE: {question} NUTZE AUSSCHLIESSLICH DIESEN KONTEXT: {context_str} AUFGABE: Erstelle eine juristisch korrekte Antwort ausschließlich auf Basis des obigen Kontextes. Wenn der Kontext keine sichere Antwort zulässt, sage das ausdrücklich und verzichte auf Spekulationen. """ msgs = [ SystemMessage(content=SYSTEM_PROMPT), HumanMessage(content=human), ] result = chat_model.invoke(msgs) answer_text = result.content.strip() sources = build_sources_metadata(docs) return answer_text, sources