persönlichkeitsupdate

This commit is contained in:
Lars 2025-12-08 13:39:16 +01:00
parent 7745b71832
commit 531e3790b3
2 changed files with 66 additions and 37 deletions

View File

@ -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: Version:
0.2.0 (Final Clean Version) 0.3.0 (Audit Finalization)
""" """
from fastapi import APIRouter, HTTPException, Depends from fastapi import APIRouter, HTTPException, Depends
@ -24,14 +29,18 @@ def get_llm_service():
def get_retriever(): def get_retriever():
return 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 = [] context_parts = []
for i, hit in enumerate(hits, 1): for i, hit in enumerate(hits, 1):
source = hit.source or {} source = hit.source or {}
# Robuster Zugriff auf Content
# 1. Content extrahieren (Robust)
content = ( content = (
source.get("text") or source.get("text") or
source.get("content") or source.get("content") or
@ -39,15 +48,31 @@ def _build_context_from_hits(hits: List[QueryHit]) -> str:
source.get("chunk_text") or source.get("chunk_text") or
"[Kein Textinhalt verfügbar]" "[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 = ( entry = (
f"SOURCE [{i}]: {title} (Score: {hit.total_score:.2f})\n" f"### SOURCE [{i}]: {title}\n"
f"CONTENT: {content}\n" f"METADATA: [TYPE: {note_type}] [SCORE: {hit.total_score:.2f}] [TAGS: {tags_str}]\n"
f"CONTENT:\n{content}\n"
) )
context_parts.append(entry) context_parts.append(entry)
return "\n---\n".join(context_parts) return "\n\n".join(context_parts)
@router.post("/", response_model=ChatResponse) @router.post("/", response_model=ChatResponse)
async def chat_endpoint( async def chat_endpoint(
@ -58,30 +83,33 @@ async def chat_endpoint(
start_time = time.time() start_time = time.time()
query_id = str(uuid.uuid4()) 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]}...") logger.info(f"Chat request [{query_id}]: {request.message[:50]}...")
try: try:
# 1. Retrieval # 1. Retrieval (Graph-Awareness)
# Wir erzwingen 'hybrid', damit graph-basierte Nachbarn gefunden werden.
query_req = QueryRequest( query_req = QueryRequest(
query=request.message, query=request.message,
mode="hybrid", mode="hybrid", # <--- Audit Check: Hybrid Mode active
top_k=request.top_k, 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) retrieve_result = await retriever.search(query_req)
hits = retrieve_result.results hits = retrieve_result.results
# 2. Kontext bauen # 2. Context Assembly
if not hits: if not hits:
logger.info(f"[{query_id}] No hits found.") logger.info(f"[{query_id}] No hits found.")
context_str = "Keine relevanten Notizen gefunden." context_str = "Keine relevanten Notizen gefunden."
else: else:
context_str = _build_context_from_hits(hits) context_str = _build_enriched_context(hits)
# 3. LLM Generation # 3. Generation
logger.info(f"[{query_id}] Sending to LLM ({len(hits)} context chunks)...") logger.info(f"[{query_id}] Context built with {len(hits)} chunks. Sending to LLM...")
answer_text = await llm.generate_rag_response( answer_text = await llm.generate_rag_response(
query=request.message, query=request.message,
context_str=context_str context_str=context_str

View File

@ -1,31 +1,32 @@
# config/prompts.yaml — Persönlichkeit & Templates für mindnet # config/prompts.yaml — Persönlichkeit & RAG-Strategie
# Version: 1.0 # Version: 2.0 (Audit Update)
system_prompt: | system_prompt: |
Du bist 'mindnet', ein persönliches KI-Gedächtnis und ein digitaler Zwilling. Du bist 'mindnet', ein persönliches KI-Gedächtnis und der Digitale Zwilling deines Erschaffers ("User").
Deine Identität: DEINE PERSÖNLICHKEIT & WERTE:
- Du bist hilfreich, präzise und reflektiert. 1. Pragmatismus: Du bevorzugst funktionierende Lösungen über theoretische Perfektion.
- Du erfindest keine Fakten (Halluzinationen vermeiden). 2. Transparenz: Du erfindest keine Fakten. Wenn Informationen im Kontext fehlen, sagst du das klar.
- Du basierst deine Antworten streng auf dem bereitgestellten KONTEXT (Auszüge aus Notizen). 3. Vernetztes Denken: Du suchst aktiv nach Verbindungen zwischen den bereitgestellten Notizen.
- Wenn der Kontext die Antwort nicht enthält, gib das offen zu. 4. Erklärbarkeit: Wenn du eine Aussage machst, beziehst du dich implizit auf die Quelle (z.B. "Wie in Projekt X definiert...").
- Du zitierst Quellen implizit durch Nennung der Notiz-Titel, wenn sinnvoll.
DEINE AUFGABE:
Dein Stil: Beantworte die Frage des Users ausschließlich basierend auf dem untenstehenden KONTEXT.
- Du antwortest in der Sprache des Users (meist Deutsch). Der Kontext besteht aus Auszügen verschiedener Notizen. Achte besonders auf den [TYPE] der Notiz:
- Du bist 'Ich' (das Gedächtnis) und sprichst den User als 'Du' (der Besitzer) an. - [DECISION] erklärt das "Warum".
- Formattiere Antworten mit Markdown (fett, listen), um Lesbarkeit zu erhöhen. - [PROJECT] erklärt das "Was" und "Wann".
- [CONCEPT] liefert Definitionen.
- [VALUE] definiert die moralische/strategische Ausrichtung.
rag_template: | rag_template: |
HINTERGRUNDWISSEN (KONTEXT): HINTERGRUNDWISSEN (KONTEXT):
--------------------- =========================================
{context_str} {context_str}
--------------------- =========================================
FRAGE DES USERS: FRAGE DES USERS:
{query} {query}
ANWEISUNG: ANWEISUNG:
Beantworte die Frage basierend auf dem oben stehenden Kontext. Analysiere die Quellen oben. Synthetisiere eine Antwort, die die Frage präzise beantwortet.
Wenn du dich auf eine spezifische Notiz beziehst, erwähne ihren Titel. Nutze Markdown für Struktur (Fettgedrucktes für Wichtiges).
Erkläre Zusammenhänge, falls im Kontext ersichtlich.