From 531e3790b303fe0a36b95466e5116472f6b6522e Mon Sep 17 00:00:00 2001 From: Lars Date: Mon, 8 Dec 2025 13:39:16 +0100 Subject: [PATCH] =?UTF-8?q?pers=C3=B6nlichkeitsupdate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/routers/chat.py | 62 ++++++++++++++++++++++++++++++++------------- config/prompts.yaml | 41 +++++++++++++++--------------- 2 files changed, 66 insertions(+), 37 deletions(-) diff --git a/app/routers/chat.py b/app/routers/chat.py index 043606d..913c44f 100644 --- a/app/routers/chat.py +++ b/app/routers/chat.py @@ -1,8 +1,13 @@ """ -app/routers/chat.py — RAG Endpunkt (WP-05) +app/routers/chat.py — RAG Endpunkt (WP-05 Final) + +Zweck: + Verbindet Retrieval mit LLM-Generation. + Enriched Context: Fügt Typen und Metadaten in den Prompt ein, + damit das LLM komplexe Zusammenhänge versteht. Version: - 0.2.0 (Final Clean Version) + 0.3.0 (Audit Finalization) """ from fastapi import APIRouter, HTTPException, Depends @@ -24,14 +29,18 @@ def get_llm_service(): def get_retriever(): return Retriever() -def _build_context_from_hits(hits: List[QueryHit]) -> str: +def _build_enriched_context(hits: List[QueryHit]) -> str: """ - Formatiert die Suchtreffer zu einem String für den Prompt. + Baut einen 'Rich Context' String. + Statt nur Text, injizieren wir Metadaten (Typ, Tags), damit das LLM + die semantische Rolle des Schnipsels versteht. """ context_parts = [] + for i, hit in enumerate(hits, 1): source = hit.source or {} - # Robuster Zugriff auf Content + + # 1. Content extrahieren (Robust) content = ( source.get("text") or source.get("content") or @@ -39,15 +48,31 @@ def _build_context_from_hits(hits: List[QueryHit]) -> str: source.get("chunk_text") or "[Kein Textinhalt verfügbar]" ) - title = hit.note_id or "Unknown Note" + # 2. Metadaten für "Context Intelligence" + title = hit.note_id or "Unbekannte Notiz" + + # Versuche, den Typ aus dem Payload zu lesen (wichtig für Decision/Project Unterscheidung) + # In WP-03 Import landen diese Infos oft in 'metadata' oder direkt im Payload root. + # Wir schauen defensiv an beiden Orten. + note_type = source.get("type", "unknown").upper() + tags = source.get("tags", []) + if isinstance(tags, list): + tags_str = ", ".join(tags[:3]) # Nur die ersten 3 Tags + else: + tags_str = str(tags) + + # 3. Formatierung für das LLM + # Wir nutzen ein Format, das wie ein strukturiertes Dokument aussieht. + # Das hilft dem Modell, Grenzen zwischen Quellen zu erkennen. entry = ( - f"SOURCE [{i}]: {title} (Score: {hit.total_score:.2f})\n" - f"CONTENT: {content}\n" + f"### SOURCE [{i}]: {title}\n" + f"METADATA: [TYPE: {note_type}] [SCORE: {hit.total_score:.2f}] [TAGS: {tags_str}]\n" + f"CONTENT:\n{content}\n" ) context_parts.append(entry) - return "\n---\n".join(context_parts) + return "\n\n".join(context_parts) @router.post("/", response_model=ChatResponse) async def chat_endpoint( @@ -58,30 +83,33 @@ async def chat_endpoint( start_time = time.time() query_id = str(uuid.uuid4()) - # Minimales Logging für Traceability + # Logging verkürzt für Übersichtlichkeit logger.info(f"Chat request [{query_id}]: {request.message[:50]}...") try: - # 1. Retrieval + # 1. Retrieval (Graph-Awareness) + # Wir erzwingen 'hybrid', damit graph-basierte Nachbarn gefunden werden. query_req = QueryRequest( query=request.message, - mode="hybrid", + mode="hybrid", # <--- Audit Check: Hybrid Mode active top_k=request.top_k, - explain=request.explain + explain=request.explain, + # Wir fordern Explizit Metadaten an, falls der Retriever das unterstützt + # (passiert implizit durch Payload-Return in WP-04) ) retrieve_result = await retriever.search(query_req) hits = retrieve_result.results - # 2. Kontext bauen + # 2. Context Assembly if not hits: logger.info(f"[{query_id}] No hits found.") context_str = "Keine relevanten Notizen gefunden." else: - context_str = _build_context_from_hits(hits) + context_str = _build_enriched_context(hits) - # 3. LLM Generation - logger.info(f"[{query_id}] Sending to LLM ({len(hits)} context chunks)...") + # 3. Generation + logger.info(f"[{query_id}] Context built with {len(hits)} chunks. Sending to LLM...") answer_text = await llm.generate_rag_response( query=request.message, context_str=context_str diff --git a/config/prompts.yaml b/config/prompts.yaml index 5d684c3..0b7438e 100644 --- a/config/prompts.yaml +++ b/config/prompts.yaml @@ -1,31 +1,32 @@ -# config/prompts.yaml — Persönlichkeit & Templates für mindnet -# Version: 1.0 +# config/prompts.yaml — Persönlichkeit & RAG-Strategie +# Version: 2.0 (Audit Update) system_prompt: | - Du bist 'mindnet', ein persönliches KI-Gedächtnis und ein digitaler Zwilling. - - Deine Identität: - - Du bist hilfreich, präzise und reflektiert. - - Du erfindest keine Fakten (Halluzinationen vermeiden). - - Du basierst deine Antworten streng auf dem bereitgestellten KONTEXT (Auszüge aus Notizen). - - Wenn der Kontext die Antwort nicht enthält, gib das offen zu. - - Du zitierst Quellen implizit durch Nennung der Notiz-Titel, wenn sinnvoll. - - Dein Stil: - - Du antwortest in der Sprache des Users (meist Deutsch). - - Du bist 'Ich' (das Gedächtnis) und sprichst den User als 'Du' (der Besitzer) an. - - Formattiere Antworten mit Markdown (fett, listen), um Lesbarkeit zu erhöhen. + Du bist 'mindnet', ein persönliches KI-Gedächtnis und der Digitale Zwilling deines Erschaffers ("User"). + + DEINE PERSÖNLICHKEIT & WERTE: + 1. Pragmatismus: Du bevorzugst funktionierende Lösungen über theoretische Perfektion. + 2. Transparenz: Du erfindest keine Fakten. Wenn Informationen im Kontext fehlen, sagst du das klar. + 3. Vernetztes Denken: Du suchst aktiv nach Verbindungen zwischen den bereitgestellten Notizen. + 4. Erklärbarkeit: Wenn du eine Aussage machst, beziehst du dich implizit auf die Quelle (z.B. "Wie in Projekt X definiert..."). + + DEINE AUFGABE: + Beantworte die Frage des Users ausschließlich basierend auf dem untenstehenden KONTEXT. + Der Kontext besteht aus Auszügen verschiedener Notizen. Achte besonders auf den [TYPE] der Notiz: + - [DECISION] erklärt das "Warum". + - [PROJECT] erklärt das "Was" und "Wann". + - [CONCEPT] liefert Definitionen. + - [VALUE] definiert die moralische/strategische Ausrichtung. rag_template: | HINTERGRUNDWISSEN (KONTEXT): - --------------------- + ========================================= {context_str} - --------------------- + ========================================= FRAGE DES USERS: {query} ANWEISUNG: - Beantworte die Frage basierend auf dem oben stehenden Kontext. - Wenn du dich auf eine spezifische Notiz beziehst, erwähne ihren Titel. - Erkläre Zusammenhänge, falls im Kontext ersichtlich. \ No newline at end of file + Analysiere die Quellen oben. Synthetisiere eine Antwort, die die Frage präzise beantwortet. + Nutze Markdown für Struktur (Fettgedrucktes für Wichtiges). \ No newline at end of file