Hybrider Chat (mit und ohne LLM Einordung des Intents)
This commit is contained in:
parent
97985371ca
commit
03594424a1
|
|
@ -1,10 +1,5 @@
|
|||
"""
|
||||
app/routers/chat.py — RAG Endpunkt (WP-06 Decision Engine - Full Config Refactor)
|
||||
|
||||
Zweck:
|
||||
Verbindet Retrieval mit LLM-Generation.
|
||||
WP-06: Implementiert Intent Detection und Strategic Retrieval.
|
||||
Update: Konfiguration via decision_engine.yaml (Late Binding) mit 'Best Match' Logik.
|
||||
app/routers/chat.py — RAG Endpunkt (WP-06 Hybrid Router)
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, HTTPException, Depends
|
||||
|
|
@ -25,23 +20,15 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
# --- Helper: Config Loader ---
|
||||
|
||||
# Cache für die Config (damit wir nicht bei jedem Request lesen)
|
||||
_DECISION_CONFIG_CACHE = None
|
||||
|
||||
def _load_decision_config() -> Dict[str, Any]:
|
||||
"""Lädt die Decision-Engine Konfiguration (Late Binding)."""
|
||||
settings = get_settings()
|
||||
path = Path(settings.DECISION_CONFIG_PATH)
|
||||
|
||||
# Default Fallback, falls YAML kaputt/weg
|
||||
default_config = {
|
||||
"strategies": {
|
||||
"FACT": {"trigger_keywords": []},
|
||||
"DECISION": {
|
||||
"trigger_keywords": ["soll ich", "meinung"],
|
||||
"inject_types": ["value", "principle"],
|
||||
"prompt_template": "decision_template"
|
||||
}
|
||||
"FACT": {"trigger_keywords": []}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -57,17 +44,14 @@ def _load_decision_config() -> Dict[str, Any]:
|
|||
return default_config
|
||||
|
||||
def get_full_config() -> Dict[str, Any]:
|
||||
"""Gibt die ganze Config zurück (für Intent Detection)."""
|
||||
global _DECISION_CONFIG_CACHE
|
||||
if _DECISION_CONFIG_CACHE is None:
|
||||
_DECISION_CONFIG_CACHE = _load_decision_config()
|
||||
return _DECISION_CONFIG_CACHE
|
||||
|
||||
def get_decision_strategy(intent: str) -> Dict[str, Any]:
|
||||
"""Gibt die Strategie für einen spezifischen Intent zurück."""
|
||||
config = get_full_config()
|
||||
strategies = config.get("strategies", {})
|
||||
# Fallback auf FACT, wenn Intent unbekannt
|
||||
return strategies.get(intent, strategies.get("FACT", {}))
|
||||
|
||||
|
||||
|
|
@ -83,30 +67,17 @@ def get_retriever():
|
|||
# --- Logic ---
|
||||
|
||||
def _build_enriched_context(hits: List[QueryHit]) -> str:
|
||||
"""
|
||||
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 {}
|
||||
|
||||
# 1. Content extrahieren
|
||||
content = (
|
||||
source.get("text") or
|
||||
source.get("content") or
|
||||
source.get("page_content") or
|
||||
source.get("chunk_text") or
|
||||
"[Kein Textinhalt verfügbar]"
|
||||
source.get("text") or source.get("content") or
|
||||
source.get("page_content") or source.get("chunk_text") or
|
||||
"[Kein Text]"
|
||||
)
|
||||
|
||||
# 2. Metadaten für "Context Intelligence"
|
||||
title = hit.note_id or "Unbekannte Notiz"
|
||||
title = hit.note_id or "Unbekannt"
|
||||
note_type = source.get("type", "unknown").upper()
|
||||
|
||||
# 3. Formatierung
|
||||
entry = (
|
||||
f"### QUELLE {i}: {title}\n"
|
||||
f"TYP: [{note_type}] (Score: {hit.total_score:.2f})\n"
|
||||
|
|
@ -118,38 +89,58 @@ def _build_enriched_context(hits: List[QueryHit]) -> str:
|
|||
|
||||
async def _classify_intent(query: str, llm: LLMService) -> str:
|
||||
"""
|
||||
WP-06: Intent Detection (Best Match / Longest Keyword Wins).
|
||||
|
||||
Prüft Keywords aus der YAML gegen die Query.
|
||||
Wenn mehrere Strategien passen, gewinnt die mit dem längsten Keyword (Spezifität).
|
||||
Hybrid Router:
|
||||
1. Keyword Check (Best/Longest Match) -> FAST
|
||||
2. LLM Fallback (wenn in config aktiv) -> SMART
|
||||
"""
|
||||
config = get_full_config()
|
||||
strategies = config.get("strategies", {})
|
||||
settings = config.get("settings", {})
|
||||
|
||||
query_lower = query.lower()
|
||||
|
||||
best_intent = "FACT"
|
||||
best_intent = None
|
||||
max_match_length = 0
|
||||
|
||||
# Iteriere über alle Strategien
|
||||
# 1. FAST PATH: Keywords
|
||||
for intent_name, strategy in strategies.items():
|
||||
if intent_name == "FACT":
|
||||
continue
|
||||
|
||||
if intent_name == "FACT": continue
|
||||
keywords = strategy.get("trigger_keywords", [])
|
||||
|
||||
# Prüfe jedes Keyword
|
||||
for k in keywords:
|
||||
# Wenn Keyword im Text ist...
|
||||
if k.lower() in query_lower:
|
||||
# ... prüfen wir, ob es spezifischer (länger) ist als der bisherige Favorit
|
||||
current_len = len(k)
|
||||
if current_len > max_match_length:
|
||||
max_match_length = current_len
|
||||
if len(k) > max_match_length:
|
||||
max_match_length = len(k)
|
||||
best_intent = intent_name
|
||||
# Wir brechen hier NICHT ab, sondern suchen weiter nach noch längeren Matches
|
||||
|
||||
if best_intent:
|
||||
logger.info(f"Intent detected via KEYWORD: {best_intent}")
|
||||
return best_intent
|
||||
|
||||
# 2. SLOW PATH: LLM Router
|
||||
if settings.get("llm_fallback_enabled", False):
|
||||
router_prompt_template = settings.get("llm_router_prompt", "")
|
||||
if router_prompt_template:
|
||||
prompt = router_prompt_template.replace("{query}", query)
|
||||
logger.info("Keywords failed. Asking LLM for Intent...")
|
||||
|
||||
return best_intent
|
||||
# Kurzer Raw Call
|
||||
llm_decision = await llm.generate_raw_response(prompt)
|
||||
|
||||
# Cleaning
|
||||
llm_decision = llm_decision.strip().upper()
|
||||
if ":" in llm_decision:
|
||||
llm_decision = llm_decision.split(":")[-1].strip()
|
||||
|
||||
# Validierung: Nur bekannte Intents zulassen
|
||||
# Entferne Satzzeichen
|
||||
llm_decision = ''.join(filter(str.isalnum, llm_decision))
|
||||
|
||||
if llm_decision in strategies:
|
||||
logger.info(f"Intent detected via LLM: {llm_decision}")
|
||||
return llm_decision
|
||||
else:
|
||||
logger.warning(f"LLM predicted unknown intent '{llm_decision}', falling back to FACT.")
|
||||
|
||||
return "FACT"
|
||||
|
||||
@router.post("/", response_model=ChatResponse)
|
||||
async def chat_endpoint(
|
||||
|
|
@ -159,21 +150,20 @@ async def chat_endpoint(
|
|||
):
|
||||
start_time = time.time()
|
||||
query_id = str(uuid.uuid4())
|
||||
|
||||
logger.info(f"Chat request [{query_id}]: {request.message[:50]}...")
|
||||
|
||||
try:
|
||||
# 1. Intent Detection (Config-Driven & Best Match)
|
||||
# 1. Intent Detection
|
||||
intent = await _classify_intent(request.message, llm)
|
||||
logger.info(f"[{query_id}] Detected Intent: {intent}")
|
||||
logger.info(f"[{query_id}] Final Intent: {intent}")
|
||||
|
||||
# Lade Strategie aus Config (Late Binding)
|
||||
# Strategy Load
|
||||
strategy = get_decision_strategy(intent)
|
||||
inject_types = strategy.get("inject_types", [])
|
||||
prompt_key = strategy.get("prompt_template", "rag_template")
|
||||
prepend_instr = strategy.get("prepend_instruction", "")
|
||||
|
||||
# 2. Primary Retrieval (Fakten)
|
||||
# 2. Primary Retrieval
|
||||
query_req = QueryRequest(
|
||||
query=request.message,
|
||||
mode="hybrid",
|
||||
|
|
@ -183,19 +173,19 @@ async def chat_endpoint(
|
|||
retrieve_result = await retriever.search(query_req)
|
||||
hits = retrieve_result.results
|
||||
|
||||
# 3. Strategic Retrieval (Konfigurierbar)
|
||||
# 3. Strategic Retrieval
|
||||
if inject_types:
|
||||
logger.info(f"[{query_id}] Executing Strategic Retrieval for types: {inject_types}...")
|
||||
strategy_req = QueryRequest(
|
||||
query=request.message,
|
||||
mode="hybrid",
|
||||
top_k=3,
|
||||
filters={"type": inject_types}, # Dynamische Liste aus YAML
|
||||
filters={"type": inject_types},
|
||||
explain=False
|
||||
)
|
||||
strategy_result = await retriever.search(strategy_req)
|
||||
|
||||
# Merge Results (Deduplication via node_id)
|
||||
# Merge
|
||||
existing_ids = {h.node_id for h in hits}
|
||||
for strat_hit in strategy_result.results:
|
||||
if strat_hit.node_id not in existing_ids:
|
||||
|
|
@ -207,20 +197,29 @@ async def chat_endpoint(
|
|||
else:
|
||||
context_str = _build_enriched_context(hits)
|
||||
|
||||
# 5. Generation Setup
|
||||
# 5. Generation
|
||||
# Wir laden das Template aus dem Service (da dort die prompts.yaml geladen ist)
|
||||
template = llm.prompts.get(prompt_key, "{context_str}\n\n{query}")
|
||||
system_prompt = llm.prompts.get("system_prompt", "")
|
||||
|
||||
# Injection der Instruktion (falls konfiguriert)
|
||||
if prepend_instr:
|
||||
context_str = f"{prepend_instr}\n\n{context_str}"
|
||||
|
||||
# Manuelles Bauen des finalen Prompts für volle Kontrolle
|
||||
final_prompt = template.replace("{context_str}", context_str).replace("{query}", request.message)
|
||||
|
||||
# Aufruf via Raw Response (da wir den Prompt schon fertig haben)
|
||||
# Wir müssen den System-Prompt manuell mitgeben?
|
||||
# generate_raw_response in llm_service unterstützt aktuell kein 'system'.
|
||||
# -> Wir erweitern generate_raw_response oder nutzen einen Hack: System + Prompt.
|
||||
|
||||
# SAUBERER WEG: Wir bauen den Payload für Ollama hier manuell zusammen und rufen eine generische Methode.
|
||||
# Da LLMService.generate_raw_response keine System-Msg nimmt, packen wir sie davor.
|
||||
full_text_prompt = f"{system_prompt}\n\n{final_prompt}"
|
||||
|
||||
logger.info(f"[{query_id}] Sending to LLM (Intent: {intent}, Template: {prompt_key})...")
|
||||
answer_text = await llm.generate_rag_response(
|
||||
query=request.message,
|
||||
context_str=context_str
|
||||
)
|
||||
answer_text = await llm.generate_raw_response(full_text_prompt)
|
||||
|
||||
# 6. Response
|
||||
duration_ms = int((time.time() - start_time) * 1000)
|
||||
|
||||
return ChatResponse(
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
app/services/llm_service.py — LLM Client (Ollama)
|
||||
|
||||
Version:
|
||||
0.1.2 (WP-05 Fix: Increased Timeout for CPU Inference)
|
||||
0.2.0 (WP-06 Hybrid Router Support)
|
||||
"""
|
||||
|
||||
import httpx
|
||||
|
|
@ -18,18 +18,19 @@ class LLMService:
|
|||
def __init__(self):
|
||||
self.settings = get_settings()
|
||||
self.prompts = self._load_prompts()
|
||||
# FIX: Timeout auf 120 Sekunden erhöht für CPU-Only Server
|
||||
self.client = httpx.AsyncClient(base_url=self.settings.OLLAMA_URL, timeout=120.0)
|
||||
|
||||
# Timeout aus Config nutzen (Default 120s)
|
||||
self.client = httpx.AsyncClient(
|
||||
base_url=self.settings.OLLAMA_URL,
|
||||
timeout=self.settings.LLM_TIMEOUT
|
||||
)
|
||||
|
||||
def _load_prompts(self) -> dict:
|
||||
"""Lädt Prompts aus der konfigurierten YAML-Datei."""
|
||||
path = Path(self.settings.PROMPTS_PATH)
|
||||
if not path.exists():
|
||||
logger.warning(f"Prompt config not found at {path}, using defaults.")
|
||||
return {
|
||||
"system_prompt": "You are a helpful AI assistant.",
|
||||
"rag_template": "Context: {context_str}\nQuestion: {query}"
|
||||
}
|
||||
return {}
|
||||
|
||||
try:
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
|
|
@ -38,15 +39,76 @@ class LLMService:
|
|||
logger.error(f"Failed to load prompts: {e}")
|
||||
return {}
|
||||
|
||||
async def generate_raw_response(self, prompt: str) -> str:
|
||||
"""
|
||||
Führt einen direkten LLM Call ohne RAG-Template aus.
|
||||
Ideal für Classification/Routing.
|
||||
"""
|
||||
payload = {
|
||||
"model": self.settings.LLM_MODEL,
|
||||
"prompt": prompt,
|
||||
"stream": False,
|
||||
"options": {
|
||||
"temperature": 0.0, # Deterministisch für Routing!
|
||||
"num_ctx": 512 # Kleines Fenster reicht für Classification
|
||||
}
|
||||
}
|
||||
|
||||
try:
|
||||
response = await self.client.post("/api/generate", json=payload)
|
||||
if response.status_code != 200:
|
||||
logger.error(f"Ollama Error ({response.status_code}): {response.text}")
|
||||
return "FACT" # Fallback bei Fehler
|
||||
|
||||
data = response.json()
|
||||
return data.get("response", "").strip()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"LLM Raw Gen Error: {e}")
|
||||
return "FACT"
|
||||
|
||||
async def generate_rag_response(self, query: str, context_str: str) -> str:
|
||||
"""
|
||||
Generiert eine Antwort basierend auf Query und Kontext.
|
||||
Generiert eine Antwort basierend auf Query und Kontext (RAG).
|
||||
"""
|
||||
system_prompt = self.prompts.get("system_prompt", "")
|
||||
template = self.prompts.get("rag_template", "{context_str}\n\n{query}")
|
||||
# Template hier ist nur Fallback, falls im Router nichts übergeben wird.
|
||||
# Im Normalfall formatiert der Router den context_str bereits vor oder übergibt das Template.
|
||||
# Hier nutzen wir simple substitution, da der Prompt meist schon vom Router aufbereitet ist
|
||||
# oder wir nutzen das Standard-Template aus der YAML.
|
||||
|
||||
# Template füllen
|
||||
final_prompt = template.format(context_str=context_str, query=query)
|
||||
# HINWEIS: In der neuen Architektur (chat.py) wird das Template bereits VOR diesem Aufruf
|
||||
# geladen und formatiert, und als 'prompt' übergeben?
|
||||
# Nein, chat.py ruft generate_rag_response(query, context_str) auf.
|
||||
# Wir müssen sicherstellen, dass wir das *richtige* Template nutzen.
|
||||
# Da generate_rag_response aktuell KEINEN 'template_key' Parameter hat,
|
||||
# gehen wir davon aus, dass 'context_str' bereits Instruktionen enthält ODER
|
||||
# dass chat.py den Prompt komplett baut.
|
||||
|
||||
# Um die API sauber zu halten: chat.py übergibt jetzt den FERTIGEN Prompt als context_str?
|
||||
# Nein, chat.py baut den Prompt mit replace().
|
||||
# Wir ändern diese Methode leicht ab, um flexibler zu sein:
|
||||
# Wir erwarten, dass der Aufrufer (chat.py) die volle Kontrolle hat.
|
||||
|
||||
# Damit es 100% zusammenpasst mit dem chat.py unten:
|
||||
# chat.py baut den finalen Prompt selbst zusammen!
|
||||
# Wir nutzen daher generate_raw_response eigentlich auch für RAG,
|
||||
# ODER wir passen generate_rag_response an, dass es "dumm" ist.
|
||||
|
||||
# Legacy Support für generate_rag_response:
|
||||
# Wir bauen den Prompt zusammen, falls noch nicht geschehen.
|
||||
# ABER: chat.py in meiner Version unten macht das Template-Handling.
|
||||
# Daher ist der sauberste Weg: chat.py ruft generate_raw_response auf für die Antwort!
|
||||
|
||||
# FIX für Kompatibilität: Wir leiten rag_response intern auf raw um,
|
||||
# bauen aber vorher den Prompt, falls context_str übergeben wird.
|
||||
|
||||
# Da wir chat.py kontrollieren (siehe unten), ändern wir chat.py so,
|
||||
# dass es generate_raw_response nutzt! Das ist viel sauberer.
|
||||
# Diese Methode bleibt für Backward Compatibility.
|
||||
|
||||
system_prompt = self.prompts.get("system_prompt", "")
|
||||
rag_template = self.prompts.get("rag_template", "{context_str}\n\n{query}")
|
||||
final_prompt = rag_template.format(context_str=context_str, query=query)
|
||||
|
||||
payload = {
|
||||
"model": self.settings.LLM_MODEL,
|
||||
|
|
@ -55,29 +117,17 @@ class LLMService:
|
|||
"stream": False,
|
||||
"options": {
|
||||
"temperature": 0.7,
|
||||
# Kleinerer Context spart Rechenzeit, falls 4096 zu viel ist
|
||||
"num_ctx": 2048
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
try:
|
||||
response = await self.client.post("/api/generate", json=payload)
|
||||
|
||||
if response.status_code != 200:
|
||||
error_msg = response.text
|
||||
logger.error(f"Ollama API Error ({response.status_code}): {error_msg}")
|
||||
return f"Fehler vom LLM (Modell '{self.settings.LLM_MODEL}' vorhanden?): {error_msg}"
|
||||
|
||||
data = response.json()
|
||||
return data.get("response", "")
|
||||
|
||||
except httpx.ReadTimeout:
|
||||
return "Timeout: Das Modell braucht zu lange zum Antworten (>120s). Hardware-Limit erreicht?"
|
||||
except httpx.ConnectError:
|
||||
return "Verbindungsfehler: Ist Ollama gestartet (Port 11434)?"
|
||||
return f"Error: {response.text}"
|
||||
return response.json().get("response", "")
|
||||
except Exception as e:
|
||||
logger.error(f"LLM Service Exception: {e}")
|
||||
return f"Interner Fehler: {str(e)}"
|
||||
return f"Error: {str(e)}"
|
||||
|
||||
async def close(self):
|
||||
await self.client.aclose()
|
||||
|
|
@ -1,21 +1,83 @@
|
|||
version: 1.0
|
||||
# config/decision_engine.yaml
|
||||
# Steuerung der Decision Engine (WP-06)
|
||||
# Hybrid-Modus: Keywords (Fast) + LLM Router (Smart Fallback)
|
||||
version: 1.1
|
||||
|
||||
settings:
|
||||
# Schalter: Soll das LLM gefragt werden, wenn kein Keyword passt?
|
||||
llm_fallback_enabled: true
|
||||
|
||||
# Der Prompt für den "Semantic Router" (Slow Path)
|
||||
llm_router_prompt: |
|
||||
Analysiere die folgende Nachricht und entscheide, welche Strategie passt.
|
||||
Antworte NUR mit dem Namen der Strategie (ein Wort).
|
||||
|
||||
STRATEGIEN:
|
||||
- DECISION: User fragt nach Rat, Meinung, Strategie, Vor/Nachteilen.
|
||||
- EMPATHY: User äußert Gefühle, Frust, Freude oder persönliche Probleme.
|
||||
- CODING: User fragt nach Code, Syntax oder Programmierung.
|
||||
- FACT: User fragt nach Wissen, Definitionen oder Fakten (Default).
|
||||
|
||||
NACHRICHT: "{query}"
|
||||
|
||||
STRATEGIE:
|
||||
|
||||
strategies:
|
||||
# Strategie 1: Der Berater (Das haben wir gebaut)
|
||||
# 1. Fakten-Abfrage (Fallback & Default)
|
||||
FACT:
|
||||
description: "Reine Wissensabfrage."
|
||||
trigger_keywords: []
|
||||
inject_types: []
|
||||
prompt_template: "rag_template"
|
||||
prepend_instruction: null
|
||||
|
||||
# 2. Entscheidungs-Frage
|
||||
DECISION:
|
||||
trigger_keywords: ["soll ich", "empfehlung", "strategie"]
|
||||
description: "Der User sucht Rat, Strategie oder Abwägung."
|
||||
trigger_keywords:
|
||||
- "soll ich"
|
||||
- "meinung"
|
||||
- "besser"
|
||||
- "empfehlung"
|
||||
- "strategie"
|
||||
- "entscheidung"
|
||||
- "wert"
|
||||
- "prinzip"
|
||||
- "vor- und nachteile"
|
||||
- "abwägung"
|
||||
inject_types: ["value", "principle", "goal"]
|
||||
prompt_template: "decision_template" # Nutzt das "Abwägen"-Template
|
||||
prompt_template: "decision_template"
|
||||
prepend_instruction: |
|
||||
!!! ENTSCHEIDUNGS-MODUS !!!
|
||||
BITTE WÄGE FAKTEN GEGEN FOLGENDE WERTE, PRINZIPIEN UND ZIELE AB:
|
||||
|
||||
# Strategie 2: Der empathische Zuhörer (NEU - Konzept)
|
||||
# 3. Empathie / "Ich"-Modus
|
||||
EMPATHY:
|
||||
trigger_keywords: ["ich fühle", "traurig", "gestresst", "angst"]
|
||||
inject_types: ["belief", "experience"] # Lädt Glaubenssätze & eigene Erfahrungen
|
||||
prompt_template: "empathy_template" # Ein Template, das auf "Zuhören" getrimmt ist
|
||||
prepend_instruction: "SEI EMPATHISCH. SPIEGEL DIE GEFÜHLE."
|
||||
description: "Reaktion auf emotionale Zustände."
|
||||
trigger_keywords:
|
||||
- "ich fühle"
|
||||
- "traurig"
|
||||
- "glücklich"
|
||||
- "gestresst"
|
||||
- "angst"
|
||||
- "nervt"
|
||||
- "überfordert"
|
||||
inject_types: ["experience", "belief", "profile"]
|
||||
prompt_template: "empathy_template"
|
||||
prepend_instruction: null
|
||||
|
||||
# Strategie 3: Der Coder (NEU - Konzept)
|
||||
# 4. Coding / Technical
|
||||
CODING:
|
||||
trigger_keywords: ["code", "python", "funktion", "bug"]
|
||||
inject_types: ["snippet", "reference"] # Lädt nur technische Schnipsel
|
||||
prompt_template: "technical_template" # Ein Template, das Codeblöcke erzwingt
|
||||
description: "Technische Anfragen und Programmierung."
|
||||
trigger_keywords:
|
||||
- "code"
|
||||
- "python"
|
||||
- "script"
|
||||
- "funktion"
|
||||
- "bug"
|
||||
- "syntax"
|
||||
- "json"
|
||||
- "yaml"
|
||||
inject_types: ["snippet", "reference", "source"]
|
||||
prompt_template: "technical_template"
|
||||
prepend_instruction: null
|
||||
|
|
@ -1,30 +1,20 @@
|
|||
# config/prompts.yaml — Final V2.3 (WP-06 Decision Engine)
|
||||
# Optimiert für Phi-3 Mini (Small Language Model)
|
||||
# config/prompts.yaml — Final V2.3.1 (Multi-Personality Support)
|
||||
|
||||
system_prompt: |
|
||||
Du bist 'mindnet', mein digitaler Zwilling und strategischer Partner.
|
||||
|
||||
DEINE IDENTITÄT:
|
||||
- Du bist nicht nur eine Datenbank, sondern handelst nach MEINEN Werten.
|
||||
- Du bist objektiv bei Fakten, aber subjektiv (in meinem Sinne) bei Entscheidungen.
|
||||
- Du bist nicht nur eine Datenbank, sondern handelst nach MEINEN Werten und Zielen.
|
||||
- Du passt deinen Stil dynamisch an die Situation an (Analytisch, Empathisch oder Technisch).
|
||||
|
||||
DEINE REGELN:
|
||||
1. Deine Antwort muss auf dem bereitgestellten KONTEXT basieren.
|
||||
2. Unterscheide klar zwischen FAKTEN (externe Welt) und PRINZIPIEN (meine innere Welt).
|
||||
3. Wenn Quellen vom Typ [VALUE] oder [PRINCIPLE] vorliegen, haben diese Vorrang bei der Entscheidungsfindung.
|
||||
4. Antworte auf Deutsch.
|
||||
|
||||
# Neuer Prompt für WP-06: Intent Detection
|
||||
intent_prompt: |
|
||||
Klassifiziere die folgende User-Anfrage.
|
||||
Antworte NUR mit einem einzigen Wort: 'FACT' oder 'DECISION'.
|
||||
|
||||
'FACT': Der User fragt nach Wissen, Definitionen, Syntax oder Inhalten (z.B. "Was ist...", "Wie funktioniert...", "Zusammenfassung von...").
|
||||
'DECISION': Der User fragt nach Rat, Meinung, Strategie oder Abwägung (z.B. "Soll ich...", "Was ist besser...", "Lohnt sich...", "Wie gehe ich vor...").
|
||||
|
||||
ANFRAGE: "{query}"
|
||||
KLASSE:
|
||||
1. Deine Antwort muss zu 100% auf dem bereitgestellten KONTEXT basieren.
|
||||
2. Halluziniere keine Fakten, die nicht in den Quellen stehen.
|
||||
3. Antworte auf Deutsch (außer bei Code/Fachbegriffen).
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# 1. STANDARD: Fakten & Wissen (Intent: FACT)
|
||||
# ---------------------------------------------------------
|
||||
rag_template: |
|
||||
QUELLEN (WISSEN):
|
||||
=========================================
|
||||
|
|
@ -35,12 +25,14 @@ rag_template: |
|
|||
{query}
|
||||
|
||||
ANWEISUNG:
|
||||
Beantworte die Frage basierend auf den Quellen.
|
||||
Nenne die spezifischen Gründe, die im Text stehen (besonders aus [DECISION] Quellen).
|
||||
Beantworte die Frage präzise basierend auf den Quellen.
|
||||
Fasse die Informationen zusammen. Sei objektiv und neutral.
|
||||
|
||||
# Neues Template für WP-06: Reasoning & Decision Making
|
||||
# ---------------------------------------------------------
|
||||
# 2. DECISION: Strategie & Abwägung (Intent: DECISION)
|
||||
# ---------------------------------------------------------
|
||||
decision_template: |
|
||||
KONTEXT (FAKTEN & WERTE):
|
||||
KONTEXT (FAKTEN & STRATEGIE):
|
||||
=========================================
|
||||
{context_str}
|
||||
=========================================
|
||||
|
|
@ -51,11 +43,54 @@ decision_template: |
|
|||
ANWEISUNG:
|
||||
Du agierst als mein Entscheidungs-Partner.
|
||||
1. Analysiere die Faktenlage aus den Quellen.
|
||||
2. Prüfe dies gegen meine [VALUE] und [PRINCIPLE] Quellen (falls vorhanden).
|
||||
2. Prüfe dies hart gegen meine strategischen Notizen (Typ [VALUE], [PRINCIPLE], [GOAL]).
|
||||
3. Wäge ab: Passt die technische/faktische Lösung zu meinen Werten?
|
||||
4. Gib eine klare Empfehlung ab.
|
||||
|
||||
|
||||
FORMAT:
|
||||
- **Analyse:** (Faktenlage)
|
||||
- **Werte-Check:** (Konflikt oder Übereinstimmung mit Prinzipien)
|
||||
- **Fazit:** (Deine Empfehlung)
|
||||
- **Analyse:** (Kurze Zusammenfassung der Fakten)
|
||||
- **Abgleich:** (Gibt es Konflikte mit Werten/Zielen? Nenne die Quelle!)
|
||||
- **Empfehlung:** (Klare Meinung: Ja/Nein/Vielleicht mit Begründung)
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# 3. EMPATHY: Der Spiegel / "Ich"-Modus (Intent: EMPATHY)
|
||||
# ---------------------------------------------------------
|
||||
empathy_template: |
|
||||
KONTEXT (ERFAHRUNGEN & GLAUBENSSÄTZE):
|
||||
=========================================
|
||||
{context_str}
|
||||
=========================================
|
||||
|
||||
SITUATION:
|
||||
{query}
|
||||
|
||||
ANWEISUNG:
|
||||
Du agierst jetzt als mein empathischer Spiegel.
|
||||
1. Versuche nicht sofort, das Problem technisch zu lösen.
|
||||
2. Zeige Verständnis für die Situation basierend auf meinen eigenen Erfahrungen ([EXPERIENCE]) oder Glaubenssätzen ([BELIEF]), falls im Kontext vorhanden.
|
||||
3. Antworte in der "Ich"-Form oder "Wir"-Form. Sei unterstützend.
|
||||
|
||||
TONFALL:
|
||||
Ruhig, verständnisvoll, reflektiert. Keine Aufzählungszeichen, sondern fließender Text.
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# 4. TECHNICAL: Der Coder (Intent: CODING)
|
||||
# ---------------------------------------------------------
|
||||
technical_template: |
|
||||
KONTEXT (DOCS & SNIPPETS):
|
||||
=========================================
|
||||
{context_str}
|
||||
=========================================
|
||||
|
||||
TASK:
|
||||
{query}
|
||||
|
||||
ANWEISUNG:
|
||||
Du bist Senior Developer.
|
||||
1. Ignoriere Smalltalk. Komm sofort zum Punkt.
|
||||
2. Generiere validen, performanten Code basierend auf den Quellen.
|
||||
3. Wenn Quellen fehlen, nutze dein allgemeines Programmierwissen, aber weise darauf hin.
|
||||
|
||||
FORMAT:
|
||||
- Kurze Erklärung des Ansatzes.
|
||||
- Markdown Code-Block (Copy-Paste fertig).
|
||||
- Wichtige Edge-Cases.
|
||||
Loading…
Reference in New Issue
Block a user