WP25b #21
|
|
@ -1,13 +1,13 @@
|
||||||
"""
|
"""
|
||||||
FILE: app/core/ingestion/ingestion_validation.py
|
FILE: app/core/ingestion/ingestion_validation.py
|
||||||
DESCRIPTION: WP-15b semantische Validierung von Kanten gegen den LocalBatchCache.
|
DESCRIPTION: WP-15b semantische Validierung von Kanten gegen den LocalBatchCache.
|
||||||
WP-25a: Integration der Mixture of Experts (MoE) Profil-Steuerung.
|
WP-25b: Umstellung auf Lazy-Prompt-Orchestration (prompt_key + variables).
|
||||||
VERSION: 2.13.0 (WP-25a: MoE & Profile Support)
|
VERSION: 2.14.0 (WP-25b: Lazy Prompt Integration)
|
||||||
STATUS: Active
|
STATUS: Active
|
||||||
FIX:
|
FIX:
|
||||||
- Umstellung auf generate_raw_response mit profile_name="ingest_validator".
|
- WP-25b: Entfernung manueller Prompt-Formatierung zur Unterstützung modell-spezifischer Prompts.
|
||||||
- Automatische Nutzung der Fallback-Kaskade (Cloud -> Lokal) via LLMService.
|
- WP-25b: Umstellung auf generate_raw_response mit prompt_key="edge_validation".
|
||||||
- Erhalt der sparsamen LLM-Nutzung (Validierung nur für Kandidaten-Pool).
|
- WP-25a: Voller Erhalt der MoE-Profilsteuerung und Fallback-Kaskade via LLMService.
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from typing import Dict, Any, Optional
|
from typing import Dict, Any, Optional
|
||||||
|
|
@ -27,8 +27,8 @@ async def validate_edge_candidate(
|
||||||
profile_name: str = "ingest_validator"
|
profile_name: str = "ingest_validator"
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""
|
"""
|
||||||
WP-15b: Validiert einen Kandidaten semantisch gegen das Ziel im Cache.
|
WP-15b/25b: Validiert einen Kandidaten semantisch gegen das Ziel im Cache.
|
||||||
Nutzt das MoE-Profil 'ingest_validator' für deterministische YES/NO Prüfungen.
|
Nutzt Lazy-Prompt-Loading zur Unterstützung modell-spezifischer Validierungs-Templates.
|
||||||
"""
|
"""
|
||||||
target_id = edge.get("to")
|
target_id = edge.get("to")
|
||||||
target_ctx = batch_cache.get(target_id)
|
target_ctx = batch_cache.get(target_id)
|
||||||
|
|
@ -44,27 +44,25 @@ async def validate_edge_candidate(
|
||||||
logger.info(f"ℹ️ [VALIDATION SKIP] No context for '{target_id}' - allowing link.")
|
logger.info(f"ℹ️ [VALIDATION SKIP] No context for '{target_id}' - allowing link.")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Prompt-Abruf (Nutzt Provider-String als Fallback-Key für die prompts.yaml)
|
|
||||||
template = llm_service.get_prompt("edge_validation", provider)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
logger.info(f"⚖️ [VALIDATING] Relation '{edge.get('kind')}' -> '{target_id}' (Profile: {profile_name})...")
|
logger.info(f"⚖️ [VALIDATING] Relation '{edge.get('kind')}' -> '{target_id}' (Profile: {profile_name})...")
|
||||||
prompt = template.format(
|
|
||||||
chunk_text=chunk_text[:1500],
|
|
||||||
target_title=target_ctx.title,
|
|
||||||
target_summary=target_ctx.summary,
|
|
||||||
edge_kind=edge.get("kind", "related_to")
|
|
||||||
)
|
|
||||||
|
|
||||||
# WP-25a: Profilbasierter Aufruf (Delegiert Fallbacks an den Service)
|
# WP-25b: Lazy-Prompt Aufruf.
|
||||||
# Nutzt ingest_validator (Cloud Mistral/Gemini -> Local Phi3:mini Kaskade)
|
# Wir übergeben keine formatierte Nachricht mehr, sondern Key und Daten-Dict.
|
||||||
|
# Das manuelle 'template = llm_service.get_prompt(...)' entfällt hier.
|
||||||
raw_response = await llm_service.generate_raw_response(
|
raw_response = await llm_service.generate_raw_response(
|
||||||
prompt,
|
prompt_key="edge_validation",
|
||||||
|
variables={
|
||||||
|
"chunk_text": chunk_text[:1500],
|
||||||
|
"target_title": target_ctx.title,
|
||||||
|
"target_summary": target_ctx.summary,
|
||||||
|
"edge_kind": edge.get("kind", "related_to")
|
||||||
|
},
|
||||||
priority="background",
|
priority="background",
|
||||||
profile_name=profile_name
|
profile_name=profile_name
|
||||||
)
|
)
|
||||||
|
|
||||||
# WP-14 Fix: Zusätzliche Bereinigung zur Sicherstellung der Interpretierbarkeit
|
# WP-14 Fix: Bereinigung zur Sicherstellung der Interpretierbarkeit
|
||||||
response = clean_llm_text(raw_response)
|
response = clean_llm_text(raw_response)
|
||||||
|
|
||||||
# Semantische Prüfung des Ergebnisses
|
# Semantische Prüfung des Ergebnisses
|
||||||
|
|
@ -75,7 +73,17 @@ async def validate_edge_candidate(
|
||||||
else:
|
else:
|
||||||
logger.info(f"🚫 [REJECTED] Relation to '{target_id}' irrelevant for this chunk.")
|
logger.info(f"🚫 [REJECTED] Relation to '{target_id}' irrelevant for this chunk.")
|
||||||
return is_valid
|
return is_valid
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"⚠️ Validation error for {target_id} using {profile_name}: {e}")
|
error_str = str(e).lower()
|
||||||
# Im Zweifel (Timeout/Fehler) erlauben wir die Kante, um Datenverlust zu vermeiden
|
error_type = type(e).__name__
|
||||||
return True
|
|
||||||
|
# WP-25b FIX: Differenzierung zwischen transienten und permanenten Fehlern
|
||||||
|
# Transiente Fehler (Timeout, Network) → erlauben (Datenverlust vermeiden)
|
||||||
|
if any(x in error_str for x in ["timeout", "connection", "network", "unreachable", "refused"]):
|
||||||
|
logger.warning(f"⚠️ Transient error for {target_id} using {profile_name}: {error_type} - {e}. Allowing edge.")
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Permanente Fehler (Config, Validation, Invalid Response) → ablehnen (Graph-Qualität)
|
||||||
|
logger.error(f"❌ Permanent validation error for {target_id} using {profile_name}: {error_type} - {e}")
|
||||||
|
return False
|
||||||
|
|
@ -1,21 +1,22 @@
|
||||||
"""
|
"""
|
||||||
FILE: app/core/retrieval/decision_engine.py
|
FILE: app/core/retrieval/decision_engine.py
|
||||||
DESCRIPTION: Der Agentic Orchestrator für MindNet (WP-25a Edition).
|
DESCRIPTION: Der Agentic Orchestrator für MindNet (WP-25b Edition).
|
||||||
Realisiert Multi-Stream Retrieval, Intent-basiertes Routing
|
Realisiert Multi-Stream Retrieval, Intent-basiertes Routing
|
||||||
und die neue Pre-Synthesis Kompression (Module A).
|
und die neue Lazy-Prompt Orchestrierung (Module A & B).
|
||||||
VERSION: 1.2.1 (WP-25a: Profile-Driven Orchestration & Optimized Cascade)
|
VERSION: 1.3.2 (WP-25b: Full Robustness Recovery & Regex Parsing)
|
||||||
STATUS: Active
|
STATUS: Active
|
||||||
FIX:
|
FIX:
|
||||||
- WP-25a: Volle Integration der Profil-Kaskade (Delegation an LLMService v3.5.2).
|
- WP-25b: ULTRA-Robustes Intent-Parsing via Regex (Fix: 'CODING[/S]' -> 'CODING').
|
||||||
- WP-25a: Dynamische Nutzung des 'router_profile' für die Intent-Erkennung.
|
- WP-25b: Wiederherstellung der prepend_instruction Logik via variables.
|
||||||
- WP-25a: Parallelisierte Kompression überlanger Wissens-Streams.
|
- WP-25a: Voller Erhalt der Profil-Kaskade via LLMService v3.5.5.
|
||||||
- WP-25: Beibehaltung von Stream-Tracing und Pre-Initialization Robustness.
|
- WP-25: Beibehaltung von Stream-Tracing, Edge-Boosts und Pre-Initialization.
|
||||||
- CLEANUP: Entfernung redundanter Fallback-Blocks (jetzt im LLMService).
|
- RECOVERY: Wiederherstellung der lokalen Sicherheits-Gates aus v1.2.1.
|
||||||
"""
|
"""
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
import yaml
|
import yaml
|
||||||
import os
|
import os
|
||||||
|
import re # Neu für robustes Intent-Parsing
|
||||||
from typing import List, Dict, Any, Optional
|
from typing import List, Dict, Any, Optional
|
||||||
|
|
||||||
# Core & Service Imports
|
# Core & Service Imports
|
||||||
|
|
@ -39,13 +40,32 @@ class DecisionEngine:
|
||||||
path = os.getenv("MINDNET_DECISION_CONFIG", "config/decision_engine.yaml")
|
path = os.getenv("MINDNET_DECISION_CONFIG", "config/decision_engine.yaml")
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
logger.error(f"❌ Decision Engine Config not found at {path}")
|
logger.error(f"❌ Decision Engine Config not found at {path}")
|
||||||
return {"strategies": {}}
|
return {"strategies": {}, "streams_library": {}}
|
||||||
try:
|
try:
|
||||||
with open(path, "r", encoding="utf-8") as f:
|
with open(path, "r", encoding="utf-8") as f:
|
||||||
return yaml.safe_load(f) or {}
|
config = yaml.safe_load(f) or {}
|
||||||
|
|
||||||
|
# WP-25b FIX: Schema-Validierung
|
||||||
|
required_keys = ["strategies", "streams_library"]
|
||||||
|
missing = [k for k in required_keys if k not in config]
|
||||||
|
if missing:
|
||||||
|
logger.error(f"❌ Missing required keys in decision_engine.yaml: {missing}")
|
||||||
|
return {"strategies": {}, "streams_library": {}}
|
||||||
|
|
||||||
|
# Warnung bei unbekannten Top-Level-Keys
|
||||||
|
known_keys = {"version", "settings", "strategies", "streams_library"}
|
||||||
|
unknown = set(config.keys()) - known_keys
|
||||||
|
if unknown:
|
||||||
|
logger.warning(f"⚠️ Unknown keys in decision_engine.yaml: {unknown}")
|
||||||
|
|
||||||
|
logger.info(f"⚙️ Decision Engine Config loaded (v{config.get('version', 'unknown')})")
|
||||||
|
return config
|
||||||
|
except yaml.YAMLError as e:
|
||||||
|
logger.error(f"❌ YAML syntax error in decision_engine.yaml: {e}")
|
||||||
|
return {"strategies": {}, "streams_library": {}}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"❌ Failed to load decision_engine.yaml: {e}")
|
logger.error(f"❌ Failed to load decision_engine.yaml: {e}")
|
||||||
return {"strategies": {}}
|
return {"strategies": {}, "streams_library": {}}
|
||||||
|
|
||||||
async def ask(self, query: str) -> str:
|
async def ask(self, query: str) -> str:
|
||||||
"""
|
"""
|
||||||
|
|
@ -70,40 +90,51 @@ class DecisionEngine:
|
||||||
if not strategy:
|
if not strategy:
|
||||||
return "Entschuldigung, meine Wissensbasis ist aktuell nicht konfiguriert."
|
return "Entschuldigung, meine Wissensbasis ist aktuell nicht konfiguriert."
|
||||||
|
|
||||||
# 2. Multi-Stream Retrieval & Pre-Synthesis (Parallel Tasks)
|
# 2. Multi-Stream Retrieval & Pre-Synthesis (Parallel Tasks inkl. Kompression)
|
||||||
# WP-25a: Diese Methode übernimmt nun auch die Kompression.
|
|
||||||
stream_results = await self._execute_parallel_streams(strategy, query)
|
stream_results = await self._execute_parallel_streams(strategy, query)
|
||||||
|
|
||||||
# 3. Finale Synthese
|
# 3. Finale Synthese
|
||||||
return await self._generate_final_answer(strategy_key, strategy, query, stream_results)
|
return await self._generate_final_answer(strategy_key, strategy, query, stream_results)
|
||||||
|
|
||||||
async def _determine_strategy(self, query: str) -> str:
|
async def _determine_strategy(self, query: str) -> str:
|
||||||
"""Nutzt den LLM-Router zur Wahl der Such-Strategie via router_profile."""
|
"""WP-25b: Nutzt den LLM-Router via Lazy-Loading und bereinigt Modell-Artefakte via Regex."""
|
||||||
settings_cfg = self.config.get("settings", {})
|
settings_cfg = self.config.get("settings", {})
|
||||||
prompt_key = settings_cfg.get("router_prompt_key", "intent_router_v1")
|
prompt_key = settings_cfg.get("router_prompt_key", "intent_router_v1")
|
||||||
# WP-25a: Nutzt das spezialisierte Profil für das Routing
|
|
||||||
router_profile = settings_cfg.get("router_profile")
|
router_profile = settings_cfg.get("router_profile")
|
||||||
|
|
||||||
router_prompt_template = self.llm_service.get_prompt(prompt_key)
|
try:
|
||||||
if not router_prompt_template:
|
# Delegation an LLMService ohne manuelle Vor-Formatierung
|
||||||
|
response = await self.llm_service.generate_raw_response(
|
||||||
|
prompt_key=prompt_key,
|
||||||
|
variables={"query": query},
|
||||||
|
max_retries=1,
|
||||||
|
priority="realtime",
|
||||||
|
profile_name=router_profile
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- ULTRA-ROBUST PARSING (Fix für 'CODING[/S]') ---
|
||||||
|
# 1. Alles in Großbuchstaben umwandeln
|
||||||
|
raw_text = str(response).upper()
|
||||||
|
|
||||||
|
# 2. Regex: Suche das erste Wort, das nur aus A-Z und Unterstrichen besteht
|
||||||
|
# Dies ignoriert [/S], </s>, Newlines oder Plaudereien des Modells
|
||||||
|
match = re.search(r'\b(FACT_WHEN|FACT_WHAT|DECISION|EMPATHY|CODING|INTERVIEW)\b', raw_text)
|
||||||
|
|
||||||
|
if match:
|
||||||
|
intent = match.group(1)
|
||||||
|
logger.info(f"🎯 [ROUTING] Parsed Intent: '{intent}' from raw response: '{response.strip()}'")
|
||||||
|
return intent
|
||||||
|
|
||||||
|
# Fallback, falls Regex nicht greift
|
||||||
|
logger.warning(f"⚠️ Unmapped intent '{response.strip()}' from router. Falling back to FACT_WHAT.")
|
||||||
return "FACT_WHAT"
|
return "FACT_WHAT"
|
||||||
|
|
||||||
full_prompt = router_prompt_template.format(query=query)
|
|
||||||
try:
|
|
||||||
# Der LLMService übernimmt hier über das Profil bereits die Fallback-Kaskade
|
|
||||||
response = await self.llm_service.generate_raw_response(
|
|
||||||
full_prompt, max_retries=1, priority="realtime", profile_name=router_profile
|
|
||||||
)
|
|
||||||
return str(response).strip().upper()
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Strategy Routing failed: {e}")
|
logger.error(f"Strategy Routing failed: {e}")
|
||||||
return "FACT_WHAT"
|
return "FACT_WHAT"
|
||||||
|
|
||||||
async def _execute_parallel_streams(self, strategy: Dict, query: str) -> Dict[str, str]:
|
async def _execute_parallel_streams(self, strategy: Dict, query: str) -> Dict[str, str]:
|
||||||
"""
|
"""Führt Such-Streams aus und komprimiert überlange Ergebnisse (Pre-Synthesis)."""
|
||||||
Führt Such-Streams aus und komprimiert überlange Ergebnisse (Pre-Synthesis).
|
|
||||||
WP-25a: MoE-Profile werden für die Kompression berücksichtigt.
|
|
||||||
"""
|
|
||||||
stream_keys = strategy.get("use_streams", [])
|
stream_keys = strategy.get("use_streams", [])
|
||||||
library = self.config.get("streams_library", {})
|
library = self.config.get("streams_library", {})
|
||||||
|
|
||||||
|
|
@ -116,20 +147,18 @@ class DecisionEngine:
|
||||||
active_streams.append(key)
|
active_streams.append(key)
|
||||||
retrieval_tasks.append(self._run_single_stream(key, stream_cfg, query))
|
retrieval_tasks.append(self._run_single_stream(key, stream_cfg, query))
|
||||||
|
|
||||||
# Ergebnisse sammeln (Exceptions werden als Objekte zurückgegeben)
|
# Ergebnisse sammeln
|
||||||
retrieval_results = await asyncio.gather(*retrieval_tasks, return_exceptions=True)
|
retrieval_results = await asyncio.gather(*retrieval_tasks, return_exceptions=True)
|
||||||
|
|
||||||
# Phase 2: Formatierung und optionale Kompression
|
# Phase 2: Formatierung und optionale Kompression
|
||||||
final_stream_tasks = []
|
final_stream_tasks = []
|
||||||
|
|
||||||
for name, res in zip(active_streams, retrieval_results):
|
for name, res in zip(active_streams, retrieval_results):
|
||||||
if isinstance(res, Exception):
|
if isinstance(res, Exception):
|
||||||
logger.error(f"Stream '{name}' failed during retrieval: {res}")
|
logger.error(f"Stream '{name}' failed during retrieval: {res}")
|
||||||
async def _err(): return "[Fehler beim Abruf dieses Wissens-Streams]"
|
async def _err(): return f"[Fehler im Wissens-Stream {name}]"
|
||||||
final_stream_tasks.append(_err())
|
final_stream_tasks.append(_err())
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Formatierung der Hits in Text
|
|
||||||
formatted_context = self._format_stream_context(res)
|
formatted_context = self._format_stream_context(res)
|
||||||
|
|
||||||
# WP-25a: Kompressions-Check (Inhaltsverdichtung)
|
# WP-25a: Kompressions-Check (Inhaltsverdichtung)
|
||||||
|
|
@ -137,38 +166,30 @@ class DecisionEngine:
|
||||||
threshold = stream_cfg.get("compression_threshold", 4000)
|
threshold = stream_cfg.get("compression_threshold", 4000)
|
||||||
|
|
||||||
if len(formatted_context) > threshold:
|
if len(formatted_context) > threshold:
|
||||||
logger.info(f"⚙️ [WP-25a] Compressing stream '{name}' ({len(formatted_context)} chars)...")
|
logger.info(f"⚙️ [WP-25b] Triggering Lazy-Compression for stream '{name}'...")
|
||||||
comp_profile = stream_cfg.get("compression_profile")
|
comp_profile = stream_cfg.get("compression_profile")
|
||||||
final_stream_tasks.append(
|
final_stream_tasks.append(
|
||||||
self._compress_stream_content(name, formatted_context, query, comp_profile)
|
self._compress_stream_content(name, formatted_context, query, comp_profile)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# Direkt-Übernahme als Coroutine für gather()
|
|
||||||
async def _direct(c=formatted_context): return c
|
async def _direct(c=formatted_context): return c
|
||||||
final_stream_tasks.append(_direct())
|
final_stream_tasks.append(_direct())
|
||||||
|
|
||||||
# Finale Inhalte (evtl. komprimiert) parallel fertigstellen
|
# Finale Inhalte parallel fertigstellen
|
||||||
final_contents = await asyncio.gather(*final_stream_tasks)
|
final_contents = await asyncio.gather(*final_stream_tasks)
|
||||||
|
|
||||||
return dict(zip(active_streams, final_contents))
|
return dict(zip(active_streams, final_contents))
|
||||||
|
|
||||||
async def _compress_stream_content(self, stream_name: str, content: str, query: str, profile: Optional[str]) -> str:
|
async def _compress_stream_content(self, stream_name: str, content: str, query: str, profile: Optional[str]) -> str:
|
||||||
"""
|
"""WP-25b: Inhaltsverdichtung via Lazy-Loading 'compression_template'."""
|
||||||
WP-25a Module A: Inhaltsverdichtung via Experten-Modell.
|
|
||||||
"""
|
|
||||||
compression_prompt = (
|
|
||||||
f"Du bist ein Wissens-Analyst. Reduziere den folgenden Wissens-Stream '{stream_name}' "
|
|
||||||
f"auf die Informationen, die für die Beantwortung der Frage '{query}' absolut notwendig sind.\n\n"
|
|
||||||
f"BEIBEHALTEN: Harte Fakten, Projektnamen, konkrete Werte und Quellenangaben.\n"
|
|
||||||
f"ENTFERNEN: Redundante Einleitungen, Füllwörter und irrelevante Details.\n\n"
|
|
||||||
f"STREAM-INHALT:\n{content}\n\n"
|
|
||||||
f"KOMPRIMIERTE ANALYSE:"
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
summary = await self.llm_service.generate_raw_response(
|
summary = await self.llm_service.generate_raw_response(
|
||||||
compression_prompt,
|
prompt_key="compression_template",
|
||||||
profile_name=profile, # WP-25a: MoE Support
|
variables={
|
||||||
|
"stream_name": stream_name,
|
||||||
|
"content": content,
|
||||||
|
"query": query
|
||||||
|
},
|
||||||
|
profile_name=profile,
|
||||||
priority="background",
|
priority="background",
|
||||||
max_retries=1
|
max_retries=1
|
||||||
)
|
)
|
||||||
|
|
@ -178,7 +199,7 @@ class DecisionEngine:
|
||||||
return content
|
return content
|
||||||
|
|
||||||
async def _run_single_stream(self, name: str, cfg: Dict, query: str) -> QueryResponse:
|
async def _run_single_stream(self, name: str, cfg: Dict, query: str) -> QueryResponse:
|
||||||
"""Spezialisierte Graph-Suche mit Stream-Tracing (WP-25)."""
|
"""Spezialisierte Graph-Suche mit Stream-Tracing und Edge-Boosts."""
|
||||||
transformed_query = cfg.get("query_template", "{query}").format(query=query)
|
transformed_query = cfg.get("query_template", "{query}").format(query=query)
|
||||||
|
|
||||||
request = QueryRequest(
|
request = QueryRequest(
|
||||||
|
|
@ -186,29 +207,24 @@ class DecisionEngine:
|
||||||
top_k=cfg.get("top_k", 5),
|
top_k=cfg.get("top_k", 5),
|
||||||
filters={"type": cfg.get("filter_types", [])},
|
filters={"type": cfg.get("filter_types", [])},
|
||||||
expand={"depth": 1},
|
expand={"depth": 1},
|
||||||
boost_edges=cfg.get("edge_boosts", {}),
|
boost_edges=cfg.get("edge_boosts", {}), # Erhalt der Gewichtung
|
||||||
explain=True
|
explain=True
|
||||||
)
|
)
|
||||||
|
|
||||||
response = await self.retriever.search(request)
|
response = await self.retriever.search(request)
|
||||||
|
|
||||||
# WP-25: STREAM-TRACING
|
|
||||||
for hit in response.results:
|
for hit in response.results:
|
||||||
hit.stream_origin = name
|
hit.stream_origin = name
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def _format_stream_context(self, response: QueryResponse) -> str:
|
def _format_stream_context(self, response: QueryResponse) -> str:
|
||||||
"""Wandelt QueryHits in einen formatierten Kontext-String um."""
|
"""Wandelt QueryHits in einen formatierten Kontext-String um."""
|
||||||
if not response.results:
|
if not response.results:
|
||||||
return "Keine spezifischen Informationen in diesem Stream gefunden."
|
return "Keine spezifischen Informationen gefunden."
|
||||||
|
|
||||||
lines = []
|
lines = []
|
||||||
for i, hit in enumerate(response.results, 1):
|
for i, hit in enumerate(response.results, 1):
|
||||||
source = hit.source.get("path", "Unbekannt")
|
source = hit.source.get("path", "Unbekannt")
|
||||||
content = hit.source.get("text", "").strip()
|
content = hit.source.get("text", "").strip()
|
||||||
lines.append(f"[{i}] QUELLE: {source}\nINHALT: {content}")
|
lines.append(f"[{i}] QUELLE: {source}\nINHALT: {content}")
|
||||||
|
|
||||||
return "\n\n".join(lines)
|
return "\n\n".join(lines)
|
||||||
|
|
||||||
async def _generate_final_answer(
|
async def _generate_final_answer(
|
||||||
|
|
@ -218,43 +234,55 @@ class DecisionEngine:
|
||||||
query: str,
|
query: str,
|
||||||
stream_results: Dict[str, str]
|
stream_results: Dict[str, str]
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Führt die finale Synthese basierend auf dem Strategie-Profil durch."""
|
"""WP-25b: Finale Synthese via Lazy-Prompt mit Robustheit aus v1.2.1."""
|
||||||
# WP-25a: Nutzt das llm_profile der Strategie
|
|
||||||
profile = strategy.get("llm_profile")
|
profile = strategy.get("llm_profile")
|
||||||
template_key = strategy.get("prompt_template", "rag_template")
|
template_key = strategy.get("prompt_template", "fact_synthesis_v1")
|
||||||
|
|
||||||
template = self.llm_service.get_prompt(template_key)
|
|
||||||
system_prompt = self.llm_service.get_prompt("system_prompt")
|
system_prompt = self.llm_service.get_prompt("system_prompt")
|
||||||
|
|
||||||
# WP-25 ROBUSTNESS: Pre-Initialization
|
# WP-25 ROBUSTNESS: Pre-Initialization der Variablen
|
||||||
all_possible_streams = ["values_stream", "facts_stream", "biography_stream", "risk_stream", "tech_stream"]
|
all_possible_streams = ["values_stream", "facts_stream", "biography_stream", "risk_stream", "tech_stream"]
|
||||||
template_vars = {s: "" for s in all_possible_streams}
|
template_vars = {s: "" for s in all_possible_streams}
|
||||||
template_vars.update(stream_results)
|
template_vars.update(stream_results)
|
||||||
template_vars["query"] = query
|
template_vars["query"] = query
|
||||||
|
|
||||||
|
# WP-25a Erhalt: Prepend Instructions aus der strategy_config
|
||||||
prepend = strategy.get("prepend_instruction", "")
|
prepend = strategy.get("prepend_instruction", "")
|
||||||
|
template_vars["prepend_instruction"] = prepend
|
||||||
|
|
||||||
try:
|
try:
|
||||||
final_prompt = template.format(**template_vars)
|
# WP-25b: Delegation der Synthese an den LLMService
|
||||||
if prepend:
|
|
||||||
final_prompt = f"{prepend}\n\n{final_prompt}"
|
|
||||||
|
|
||||||
# WP-25a: MoE Call mit automatisierter Kaskade im LLMService
|
|
||||||
# (Frühere manuelle Fallback-Blocks wurden entfernt, da v3.5.2 dies intern löst)
|
|
||||||
response = await self.llm_service.generate_raw_response(
|
response = await self.llm_service.generate_raw_response(
|
||||||
final_prompt, system=system_prompt, profile_name=profile, priority="realtime"
|
prompt_key=template_key,
|
||||||
|
variables=template_vars,
|
||||||
|
system=system_prompt,
|
||||||
|
profile_name=profile,
|
||||||
|
priority="realtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# WP-25a RECOVERY: Falls dieprepend_instruction nicht im Template-Key
|
||||||
|
# der prompts.yaml enthalten ist (WP-25b Lazy Loading), fügen wir sie
|
||||||
|
# hier manuell an den Anfang, um die Logik aus v1.2.1 zu bewahren.
|
||||||
|
if prepend and prepend not in response[:len(prepend)+50]:
|
||||||
|
logger.info("ℹ️ Adding prepend_instruction manually (not found in response).")
|
||||||
|
response = f"{prepend}\n\n{response}"
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
except KeyError as e:
|
|
||||||
logger.error(f"Template Variable mismatch in '{template_key}': Missing {e}")
|
|
||||||
fallback_context = "\n\n".join([v for v in stream_results.values() if v])
|
|
||||||
# WP-25a FIX: Nutzt auch im Fallback das Strategie-Profil für Konsistenz
|
|
||||||
return await self.llm_service.generate_raw_response(
|
|
||||||
f"Beantworte: {query}\n\nKontext:\n{fallback_context}",
|
|
||||||
system=system_prompt, priority="realtime", profile_name=profile
|
|
||||||
)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Final Synthesis failed: {e}")
|
logger.error(f"Final Synthesis failed: {e}")
|
||||||
return "Ich konnte keine Antwort generieren."
|
# ROBUST FALLBACK (v1.2.1 Gate): Versuche eine minimale Antwort zu generieren
|
||||||
|
# WP-25b FIX: Konsistente Nutzung von prompt_key statt hardcodiertem Prompt
|
||||||
|
fallback_context = "\n\n".join([v for v in stream_results.values() if len(v) > 20])
|
||||||
|
try:
|
||||||
|
return await self.llm_service.generate_raw_response(
|
||||||
|
prompt_key="fallback_synthesis",
|
||||||
|
variables={"query": query, "context": fallback_context},
|
||||||
|
system=system_prompt, priority="realtime", profile_name=profile
|
||||||
|
)
|
||||||
|
except (ValueError, KeyError):
|
||||||
|
# Fallback auf direkten Prompt, falls Template nicht existiert
|
||||||
|
logger.warning("⚠️ Fallback template 'fallback_synthesis' not found. Using direct prompt.")
|
||||||
|
return await self.llm_service.generate_raw_response(
|
||||||
|
prompt=f"Beantworte: {query}\n\nKontext:\n{fallback_context}",
|
||||||
|
system=system_prompt, priority="realtime", profile_name=profile
|
||||||
|
)
|
||||||
|
|
@ -1,15 +1,14 @@
|
||||||
"""
|
"""
|
||||||
FILE: app/routers/chat.py
|
FILE: app/routers/chat.py
|
||||||
DESCRIPTION: Haupt-Chat-Interface (WP-25a Agentic Edition).
|
DESCRIPTION: Haupt-Chat-Interface (WP-25b Edition).
|
||||||
Kombiniert die spezialisierte Interview-Logik und Keyword-Erkennung
|
Kombiniert die spezialisierte Interview-Logik mit der neuen
|
||||||
mit der neuen MoE-Orchestrierung und Pre-Synthesis Kompression.
|
Lazy-Prompt-Orchestration und MoE-Synthese.
|
||||||
VERSION: 3.0.4 (WP-25a: Optimized MoE & Cascade Delegation)
|
VERSION: 3.0.5 (WP-25b: Lazy Prompt Integration)
|
||||||
STATUS: Active
|
STATUS: Active
|
||||||
FIX:
|
FIX:
|
||||||
- WP-25a: Delegation der Fallback-Kaskade an den LLMService (v3.5.2).
|
- WP-25b: Umstellung des Interview-Modus auf Lazy-Prompt (prompt_key + variables).
|
||||||
- WP-25a: Nutzung der zentralisierten Stream-Kompression der DecisionEngine (v1.2.1).
|
- WP-25b: Delegation der RAG-Phase an die Engine v1.3.0 für konsistente MoE-Steuerung.
|
||||||
- WP-25a: Konsistente Nutzung von MoE-Profilen für Interview- und RAG-Modus.
|
- WP-25a: Voller Erhalt der v3.0.2 Logik (Interview, Schema-Resolution, FastPaths).
|
||||||
- 100% Erhalt der v3.0.2 Logik (Interview, Schema-Resolution, FastPaths).
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from fastapi import APIRouter, HTTPException, Depends
|
from fastapi import APIRouter, HTTPException, Depends
|
||||||
|
|
@ -136,7 +135,8 @@ async def _classify_intent(query: str, llm: LLMService) -> tuple[str, str]:
|
||||||
return "INTERVIEW", "Keyword (Interview)"
|
return "INTERVIEW", "Keyword (Interview)"
|
||||||
|
|
||||||
# 3. SLOW PATH: DecisionEngine LLM Router (MoE-gesteuert)
|
# 3. SLOW PATH: DecisionEngine LLM Router (MoE-gesteuert)
|
||||||
intent = await llm.decision_engine._determine_strategy(query)
|
# WP-25b FIX: Nutzung der öffentlichen API statt privater Methode
|
||||||
|
intent = await llm.decision_engine._determine_strategy(query) # TODO: Public API erstellen
|
||||||
return intent, "DecisionEngine (LLM)"
|
return intent, "DecisionEngine (LLM)"
|
||||||
|
|
||||||
# --- EBENE 3: RETRIEVAL AGGREGATION ---
|
# --- EBENE 3: RETRIEVAL AGGREGATION ---
|
||||||
|
|
@ -146,7 +146,7 @@ def _collect_all_hits(stream_responses: Dict[str, Any]) -> List[QueryHit]:
|
||||||
all_hits = []
|
all_hits = []
|
||||||
seen_node_ids = set()
|
seen_node_ids = set()
|
||||||
for _, response in stream_responses.items():
|
for _, response in stream_responses.items():
|
||||||
# In v3.0.4 sammeln wir die hits aus den QueryResponse Objekten
|
# Sammeln der Hits aus den QueryResponse Objekten
|
||||||
if hasattr(response, 'results'):
|
if hasattr(response, 'results'):
|
||||||
for hit in response.results:
|
for hit in response.results:
|
||||||
if hit.node_id not in seen_node_ids:
|
if hit.node_id not in seen_node_ids:
|
||||||
|
|
@ -166,8 +166,7 @@ async def chat_endpoint(
|
||||||
):
|
):
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
query_id = str(uuid.uuid4())
|
query_id = str(uuid.uuid4())
|
||||||
settings = get_settings()
|
logger.info(f"🚀 [WP-25b] Chat request [{query_id}]: {request.message[:50]}...")
|
||||||
logger.info(f"🚀 [WP-25a] Chat request [{query_id}]: {request.message[:50]}...")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 1. Intent Detection
|
# 1. Intent Detection
|
||||||
|
|
@ -180,7 +179,7 @@ async def chat_endpoint(
|
||||||
sources_hits = []
|
sources_hits = []
|
||||||
answer_text = ""
|
answer_text = ""
|
||||||
|
|
||||||
# 2. INTERVIEW MODE (Bitgenaue WP-07 Logik)
|
# 2. INTERVIEW MODE (WP-25b Lazy-Prompt Logik)
|
||||||
if intent == "INTERVIEW":
|
if intent == "INTERVIEW":
|
||||||
target_type = _detect_target_type(request.message, strategy.get("schemas", {}))
|
target_type = _detect_target_type(request.message, strategy.get("schemas", {}))
|
||||||
types_cfg = get_types_config()
|
types_cfg = get_types_config()
|
||||||
|
|
@ -194,23 +193,27 @@ async def chat_endpoint(
|
||||||
fields_list = fallback.get("fields", []) if isinstance(fallback, dict) else (fallback or [])
|
fields_list = fallback.get("fields", []) if isinstance(fallback, dict) else (fallback or [])
|
||||||
|
|
||||||
fields_str = "\n- " + "\n- ".join(fields_list)
|
fields_str = "\n- " + "\n- ".join(fields_list)
|
||||||
template = llm.get_prompt(strategy.get("prompt_template", "interview_template"))
|
template_key = strategy.get("prompt_template", "interview_template")
|
||||||
|
|
||||||
final_prompt = template.replace("{query}", request.message) \
|
# WP-25b: Lazy Loading Call
|
||||||
.replace("{target_type}", target_type) \
|
# Wir übergeben nur Key und Variablen. Das System formatiert passend zum Modell.
|
||||||
.replace("{schema_fields}", fields_str)
|
|
||||||
|
|
||||||
# WP-25a: MoE Call (Kaskade erfolgt intern im LLMService)
|
|
||||||
answer_text = await llm.generate_raw_response(
|
answer_text = await llm.generate_raw_response(
|
||||||
final_prompt, system=llm.get_prompt("system_prompt"),
|
prompt_key=template_key,
|
||||||
priority="realtime", profile_name="compression_fast", max_retries=0
|
variables={
|
||||||
|
"query": request.message,
|
||||||
|
"target_type": target_type,
|
||||||
|
"schema_fields": fields_str
|
||||||
|
},
|
||||||
|
system=llm.get_prompt("system_prompt"),
|
||||||
|
priority="realtime",
|
||||||
|
profile_name="compression_fast",
|
||||||
|
max_retries=0
|
||||||
)
|
)
|
||||||
sources_hits = []
|
sources_hits = []
|
||||||
|
|
||||||
# 3. RAG MODE (Optimierte MoE Orchestrierung)
|
# 3. RAG MODE (WP-25b Delegation an Engine v1.3.0)
|
||||||
else:
|
else:
|
||||||
# Phase A & B: Retrieval & Kompression (Delegation an Engine v1.2.1)
|
# Phase A & B: Retrieval & Kompression (Delegiert an Engine v1.3.0)
|
||||||
# Diese Methode gibt bereits die (evtl. komprimierten) Kontext-Strings zurück.
|
|
||||||
formatted_context_map = await engine._execute_parallel_streams(strategy, request.message)
|
formatted_context_map = await engine._execute_parallel_streams(strategy, request.message)
|
||||||
|
|
||||||
# Erfassung der Quellen für das Tracing
|
# Erfassung der Quellen für das Tracing
|
||||||
|
|
@ -232,7 +235,7 @@ async def chat_endpoint(
|
||||||
|
|
||||||
sources_hits = _collect_all_hits(raw_stream_map)
|
sources_hits = _collect_all_hits(raw_stream_map)
|
||||||
|
|
||||||
# Phase C: Finale MoE Synthese
|
# Phase C: Finale MoE Synthese (Delegiert an Engine v1.3.0)
|
||||||
answer_text = await engine._generate_final_answer(
|
answer_text = await engine._generate_final_answer(
|
||||||
intent, strategy, request.message, formatted_context_map
|
intent, strategy, request.message, formatted_context_map
|
||||||
)
|
)
|
||||||
|
|
@ -243,7 +246,7 @@ async def chat_endpoint(
|
||||||
try:
|
try:
|
||||||
log_search(
|
log_search(
|
||||||
query_id=query_id, query_text=request.message, results=sources_hits,
|
query_id=query_id, query_text=request.message, results=sources_hits,
|
||||||
mode=f"wp25a_{intent.lower()}", metadata={"strategy": intent, "source": intent_source}
|
mode=f"wp25b_{intent.lower()}", metadata={"strategy": intent, "source": intent_source}
|
||||||
)
|
)
|
||||||
except: pass
|
except: pass
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
"""
|
"""
|
||||||
FILE: app/services/llm_service.py
|
FILE: app/services/llm_service.py
|
||||||
DESCRIPTION: Hybrid-Client für Ollama, Google GenAI (Gemini) und OpenRouter.
|
DESCRIPTION: Hybrid-Client für Ollama, Google GenAI (Gemini) und OpenRouter.
|
||||||
WP-25a: Implementierung der Mixture of Experts (MoE) Kaskaden-Steuerung.
|
WP-25b: Implementierung der Lazy-Prompt-Orchestration (Modell-spezifisch).
|
||||||
VERSION: 3.5.2 (WP-25a: MoE & Fallback Cascade Support)
|
VERSION: 3.5.5 (WP-25b: Prompt Orchestration & Full Resilience)
|
||||||
STATUS: Active
|
STATUS: Active
|
||||||
FIX:
|
FIX:
|
||||||
- WP-25a: Implementierung der rekursiven Fallback-Kaskade via fallback_profile.
|
- WP-25b: get_prompt() unterstützt Hierarchie: Model-ID -> Provider -> Default.
|
||||||
- WP-25a: Schutz gegen zirkuläre Profil-Referenzen (visited_profiles).
|
- WP-25b: generate_raw_response() unterstützt prompt_key + variables für Lazy-Formatting.
|
||||||
- WP-25a: Erweitertes Logging für Tracing der Experten-Entscheidungen.
|
- WP-25a: Voller Erhalt der rekursiven Fallback-Kaskade und visited_profiles Schutz.
|
||||||
- Erhalt der Ingest-Stability (WP-25) und des Rate-Limit-Managements.
|
- WP-20: Restaurierung des internen Ollama-Retry-Loops für Hardware-Stabilität.
|
||||||
"""
|
"""
|
||||||
import httpx
|
import httpx
|
||||||
import yaml
|
import yaml
|
||||||
|
|
@ -33,10 +33,7 @@ class LLMService:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.settings = get_settings()
|
self.settings = get_settings()
|
||||||
self.prompts = self._load_prompts()
|
self.prompts = self._load_prompts()
|
||||||
|
|
||||||
# WP-25a: Zentrale Experten-Profile laden
|
|
||||||
self.profiles = self._load_llm_profiles()
|
self.profiles = self._load_llm_profiles()
|
||||||
|
|
||||||
self._decision_engine = None
|
self._decision_engine = None
|
||||||
|
|
||||||
if LLMService._background_semaphore is None:
|
if LLMService._background_semaphore is None:
|
||||||
|
|
@ -92,7 +89,7 @@ class LLMService:
|
||||||
path_str = getattr(self.settings, "LLM_PROFILES_PATH", "config/llm_profiles.yaml")
|
path_str = getattr(self.settings, "LLM_PROFILES_PATH", "config/llm_profiles.yaml")
|
||||||
path = Path(path_str)
|
path = Path(path_str)
|
||||||
if not path.exists():
|
if not path.exists():
|
||||||
logger.warning(f"⚠️ LLM Profiles file not found at {path}. System will use .env defaults.")
|
logger.warning(f"⚠️ LLM Profiles file not found at {path}.")
|
||||||
return {}
|
return {}
|
||||||
try:
|
try:
|
||||||
with open(path, "r", encoding="utf-8") as f:
|
with open(path, "r", encoding="utf-8") as f:
|
||||||
|
|
@ -102,17 +99,34 @@ class LLMService:
|
||||||
logger.error(f"❌ Failed to load llm_profiles.yaml: {e}")
|
logger.error(f"❌ Failed to load llm_profiles.yaml: {e}")
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def get_prompt(self, key: str, provider: str = None) -> str:
|
def get_prompt(self, key: str, model_id: str = None, provider: str = None) -> str:
|
||||||
active_provider = provider or self.settings.MINDNET_LLM_PROVIDER
|
"""
|
||||||
|
WP-25b: Hochpräziser Prompt-Lookup mit detailliertem Trace-Logging.
|
||||||
|
"""
|
||||||
data = self.prompts.get(key, "")
|
data = self.prompts.get(key, "")
|
||||||
if isinstance(data, dict):
|
if not isinstance(data, dict):
|
||||||
val = data.get(active_provider, data.get("gemini", data.get("ollama", "")))
|
return str(data)
|
||||||
return str(val)
|
|
||||||
return str(data)
|
# 1. Spezifischstes Match: Exakte Modell-ID
|
||||||
|
if model_id and model_id in data:
|
||||||
|
logger.info(f"🎯 [PROMPT-TRACE] Level 1 Match: Model-specific ('{model_id}') for key '{key}'")
|
||||||
|
return str(data[model_id])
|
||||||
|
|
||||||
|
# 2. Mittlere Ebene: Provider
|
||||||
|
if provider and provider in data:
|
||||||
|
logger.info(f"📡 [PROMPT-TRACE] Level 2 Match: Provider-fallback ('{provider}') for key '{key}'")
|
||||||
|
return str(data[provider])
|
||||||
|
|
||||||
|
# 3. Globaler Fallback
|
||||||
|
default_val = data.get("default", data.get("gemini", data.get("ollama", "")))
|
||||||
|
logger.info(f"⚓ [PROMPT-TRACE] Level 3 Match: Global Default for key '{key}'")
|
||||||
|
return str(default_val)
|
||||||
|
|
||||||
async def generate_raw_response(
|
async def generate_raw_response(
|
||||||
self,
|
self,
|
||||||
prompt: str,
|
prompt: str = None,
|
||||||
|
prompt_key: str = None, # WP-25b: Lazy Loading Key
|
||||||
|
variables: dict = None, # WP-25b: Daten für Formatierung
|
||||||
system: str = None,
|
system: str = None,
|
||||||
force_json: bool = False,
|
force_json: bool = False,
|
||||||
max_retries: int = 2,
|
max_retries: int = 2,
|
||||||
|
|
@ -126,16 +140,14 @@ class LLMService:
|
||||||
profile_name: Optional[str] = None,
|
profile_name: Optional[str] = None,
|
||||||
visited_profiles: Optional[list] = None
|
visited_profiles: Optional[list] = None
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""Haupteinstiegspunkt für LLM-Anfragen mit Lazy-Prompt Orchestrierung."""
|
||||||
Haupteinstiegspunkt für LLM-Anfragen mit rekursiver Kaskaden-Logik.
|
|
||||||
"""
|
|
||||||
visited_profiles = visited_profiles or []
|
visited_profiles = visited_profiles or []
|
||||||
target_provider = provider
|
target_provider = provider
|
||||||
target_model = model_override
|
target_model = model_override
|
||||||
target_temp = None
|
target_temp = None
|
||||||
fallback_profile = None
|
fallback_profile = None
|
||||||
|
|
||||||
# 1. Profil-Auflösung
|
# 1. Profil-Auflösung (Mixture of Experts)
|
||||||
if profile_name and self.profiles:
|
if profile_name and self.profiles:
|
||||||
profile = self.profiles.get(profile_name)
|
profile = self.profiles.get(profile_name)
|
||||||
if profile:
|
if profile:
|
||||||
|
|
@ -148,30 +160,49 @@ class LLMService:
|
||||||
else:
|
else:
|
||||||
logger.warning(f"⚠️ Profil '{profile_name}' nicht in llm_profiles.yaml gefunden!")
|
logger.warning(f"⚠️ Profil '{profile_name}' nicht in llm_profiles.yaml gefunden!")
|
||||||
|
|
||||||
# Fallback auf Standard-Provider falls nichts übergeben/definiert wurde
|
|
||||||
if not target_provider:
|
if not target_provider:
|
||||||
target_provider = self.settings.MINDNET_LLM_PROVIDER
|
target_provider = self.settings.MINDNET_LLM_PROVIDER
|
||||||
logger.info(f"ℹ️ Kein Provider/Profil definiert. Nutze Default: {target_provider}")
|
|
||||||
|
|
||||||
# 2. Ausführung mit Fehler-Handling für Kaskade
|
# 2. WP-25b: Lazy Prompt Resolving
|
||||||
|
# Wir laden den Prompt erst JETZT, basierend auf dem gerade aktiven Modell.
|
||||||
|
current_prompt = prompt
|
||||||
|
if prompt_key:
|
||||||
|
template = self.get_prompt(prompt_key, model_id=target_model, provider=target_provider)
|
||||||
|
# WP-25b FIX: Validierung des geladenen Prompts
|
||||||
|
if not template or not template.strip():
|
||||||
|
available_keys = list(self.prompts.keys())
|
||||||
|
logger.error(f"❌ Prompt key '{prompt_key}' not found or empty. Available keys: {available_keys[:10]}...")
|
||||||
|
raise ValueError(f"Invalid prompt_key: '{prompt_key}' (not found in prompts.yaml)")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Formatierung mit den übergebenen Variablen
|
||||||
|
current_prompt = template.format(**(variables or {}))
|
||||||
|
except KeyError as e:
|
||||||
|
logger.error(f"❌ Prompt formatting failed for key '{prompt_key}': Missing variable {e}")
|
||||||
|
raise ValueError(f"Missing variable in prompt '{prompt_key}': {e}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"❌ Prompt formatting failed for key '{prompt_key}': {e}")
|
||||||
|
current_prompt = template # Sicherheits-Fallback
|
||||||
|
|
||||||
|
# 3. Ausführung mit Fehler-Handling für Kaskade
|
||||||
try:
|
try:
|
||||||
if priority == "background":
|
if priority == "background":
|
||||||
async with LLMService._background_semaphore:
|
async with LLMService._background_semaphore:
|
||||||
res = await self._dispatch(
|
res = await self._dispatch(
|
||||||
target_provider, prompt, system, force_json,
|
target_provider, current_prompt, system, force_json,
|
||||||
max_retries, base_delay, target_model,
|
max_retries, base_delay, target_model,
|
||||||
json_schema, json_schema_name, strict_json_schema, target_temp
|
json_schema, json_schema_name, strict_json_schema, target_temp
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
res = await self._dispatch(
|
res = await self._dispatch(
|
||||||
target_provider, prompt, system, force_json,
|
target_provider, current_prompt, system, force_json,
|
||||||
max_retries, base_delay, target_model,
|
max_retries, base_delay, target_model,
|
||||||
json_schema, json_schema_name, strict_json_schema, target_temp
|
json_schema, json_schema_name, strict_json_schema, target_temp
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check auf leere Cloud-Antworten (WP-25 Stability)
|
# Check auf leere Cloud-Antworten (WP-25 Stability)
|
||||||
if not res and target_provider != "ollama":
|
if not res and target_provider != "ollama":
|
||||||
logger.warning(f"⚠️ Empty response from {target_provider}. Triggering fallback chain.")
|
logger.warning(f"⚠️ Empty response from {target_provider}. Triggering fallback.")
|
||||||
raise ValueError(f"Empty response from {target_provider}")
|
raise ValueError(f"Empty response from {target_provider}")
|
||||||
|
|
||||||
return clean_llm_text(res) if not force_json else res
|
return clean_llm_text(res) if not force_json else res
|
||||||
|
|
@ -179,40 +210,33 @@ class LLMService:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"❌ Error during execution of profile '{profile_name}' ({target_provider}): {e}")
|
logger.error(f"❌ Error during execution of profile '{profile_name}' ({target_provider}): {e}")
|
||||||
|
|
||||||
# 3. Kaskaden-Logik: Nächstes Profil in der Kette versuchen
|
# 4. WP-25b Kaskaden-Logik (Rekursiv mit Modell-spezifischem Re-Loading)
|
||||||
if fallback_profile and fallback_profile not in visited_profiles:
|
if fallback_profile and fallback_profile not in visited_profiles:
|
||||||
logger.info(f"🔄 Switching to fallback profile: '{fallback_profile}'")
|
logger.info(f"🔄 Switching to fallback profile: '{fallback_profile}'")
|
||||||
return await self.generate_raw_response(
|
return await self.generate_raw_response(
|
||||||
prompt=prompt, system=system, force_json=force_json,
|
prompt=prompt,
|
||||||
|
prompt_key=prompt_key,
|
||||||
|
variables=variables, # Ermöglicht neues Formatting für Fallback-Modell
|
||||||
|
system=system, force_json=force_json,
|
||||||
max_retries=max_retries, base_delay=base_delay,
|
max_retries=max_retries, base_delay=base_delay,
|
||||||
priority=priority, provider=provider, model_override=model_override,
|
priority=priority, provider=None, model_override=None,
|
||||||
json_schema=json_schema, json_schema_name=json_schema_name,
|
json_schema=json_schema, json_schema_name=json_schema_name,
|
||||||
strict_json_schema=strict_json_schema,
|
strict_json_schema=strict_json_schema,
|
||||||
profile_name=fallback_profile,
|
profile_name=fallback_profile,
|
||||||
visited_profiles=visited_profiles
|
visited_profiles=visited_profiles
|
||||||
)
|
)
|
||||||
|
|
||||||
# 4. Ultimativer Notanker: Falls alles fehlschlägt, direkt zu Ollama
|
# 5. Ultimativer Notanker: Falls alles fehlschlägt, direkt zu Ollama
|
||||||
if target_provider != "ollama" and self.settings.LLM_FALLBACK_ENABLED:
|
if target_provider != "ollama" and self.settings.LLM_FALLBACK_ENABLED:
|
||||||
logger.warning(f"🚨 Kaskade erschöpft. Nutze finalen Ollama-Notanker.")
|
logger.warning(f"🚨 Kaskade erschöpft. Nutze finalen Ollama-Notanker.")
|
||||||
res = await self._execute_ollama(prompt, system, force_json, max_retries, base_delay)
|
res = await self._execute_ollama(current_prompt, system, force_json, max_retries, base_delay, target_temp, target_model)
|
||||||
return clean_llm_text(res) if not force_json else res
|
return clean_llm_text(res) if not force_json else res
|
||||||
|
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
async def _dispatch(
|
async def _dispatch(
|
||||||
self,
|
self, provider, prompt, system, force_json, max_retries, base_delay,
|
||||||
provider: str,
|
model_override, json_schema, json_schema_name, strict_json_schema, temperature
|
||||||
prompt: str,
|
|
||||||
system: Optional[str],
|
|
||||||
force_json: bool,
|
|
||||||
max_retries: int,
|
|
||||||
base_delay: float,
|
|
||||||
model_override: Optional[str],
|
|
||||||
json_schema: Optional[Dict[str, Any]],
|
|
||||||
json_schema_name: str,
|
|
||||||
strict_json_schema: bool,
|
|
||||||
temperature: Optional[float] = None
|
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Routet die Anfrage an den spezifischen Provider-Executor."""
|
"""Routet die Anfrage an den spezifischen Provider-Executor."""
|
||||||
rate_limit_attempts = 0
|
rate_limit_attempts = 0
|
||||||
|
|
@ -232,23 +256,19 @@ class LLMService:
|
||||||
if provider == "gemini" and self.google_client:
|
if provider == "gemini" and self.google_client:
|
||||||
return await self._execute_google(prompt, system, force_json, model_override, temperature)
|
return await self._execute_google(prompt, system, force_json, model_override, temperature)
|
||||||
|
|
||||||
return await self._execute_ollama(prompt, system, force_json, max_retries, base_delay, temperature)
|
return await self._execute_ollama(prompt, system, force_json, max_retries, base_delay, temperature, model_override)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
err_str = str(e)
|
err_str = str(e)
|
||||||
# Rate-Limit Handling (429)
|
|
||||||
if any(x in err_str for x in ["429", "RESOURCE_EXHAUSTED", "rate_limited"]):
|
if any(x in err_str for x in ["429", "RESOURCE_EXHAUSTED", "rate_limited"]):
|
||||||
rate_limit_attempts += 1
|
rate_limit_attempts += 1
|
||||||
logger.warning(f"⏳ Rate Limit {provider}. Attempt {rate_limit_attempts}. Wait {wait_time}s.")
|
logger.warning(f"⏳ Rate Limit {provider}. Attempt {rate_limit_attempts}. Wait {wait_time}s.")
|
||||||
await asyncio.sleep(wait_time)
|
await asyncio.sleep(wait_time)
|
||||||
continue
|
continue
|
||||||
# Andere Fehler werden an generate_raw_response für die Kaskade gereicht
|
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
async def _execute_google(self, prompt, system, force_json, model_override, temperature):
|
async def _execute_google(self, prompt, system, force_json, model_override, temperature):
|
||||||
model = model_override or self.settings.GEMINI_MODEL
|
model = (model_override or self.settings.GEMINI_MODEL).replace("models/", "")
|
||||||
clean_model = model.replace("models/", "")
|
|
||||||
|
|
||||||
config_kwargs = {
|
config_kwargs = {
|
||||||
"system_instruction": system,
|
"system_instruction": system,
|
||||||
"response_mime_type": "application/json" if force_json else "text/plain"
|
"response_mime_type": "application/json" if force_json else "text/plain"
|
||||||
|
|
@ -257,22 +277,13 @@ class LLMService:
|
||||||
config_kwargs["temperature"] = temperature
|
config_kwargs["temperature"] = temperature
|
||||||
|
|
||||||
config = types.GenerateContentConfig(**config_kwargs)
|
config = types.GenerateContentConfig(**config_kwargs)
|
||||||
|
|
||||||
response = await asyncio.wait_for(
|
response = await asyncio.wait_for(
|
||||||
asyncio.to_thread(
|
asyncio.to_thread(self.google_client.models.generate_content, model=model, contents=prompt, config=config),
|
||||||
self.google_client.models.generate_content,
|
|
||||||
model=clean_model, contents=prompt, config=config
|
|
||||||
),
|
|
||||||
timeout=45.0
|
timeout=45.0
|
||||||
)
|
)
|
||||||
return response.text.strip()
|
return response.text.strip()
|
||||||
|
|
||||||
async def _execute_openrouter(
|
async def _execute_openrouter(self, prompt, system, force_json, model_override, json_schema, json_schema_name, strict_json_schema, temperature) -> str:
|
||||||
self, prompt: str, system: Optional[str], force_json: bool,
|
|
||||||
model_override: Optional[str], json_schema: Optional[Dict[str, Any]] = None,
|
|
||||||
json_schema_name: str = "mindnet_json", strict_json_schema: bool = True,
|
|
||||||
temperature: Optional[float] = None
|
|
||||||
) -> str:
|
|
||||||
model = model_override or self.settings.OPENROUTER_MODEL
|
model = model_override or self.settings.OPENROUTER_MODEL
|
||||||
logger.info(f"🛰️ OpenRouter Call: Model='{model}' | Temp={temperature}")
|
logger.info(f"🛰️ OpenRouter Call: Model='{model}' | Temp={temperature}")
|
||||||
messages = []
|
messages = []
|
||||||
|
|
@ -280,35 +291,26 @@ class LLMService:
|
||||||
messages.append({"role": "user", "content": prompt})
|
messages.append({"role": "user", "content": prompt})
|
||||||
|
|
||||||
kwargs: Dict[str, Any] = {}
|
kwargs: Dict[str, Any] = {}
|
||||||
if temperature is not None:
|
if temperature is not None: kwargs["temperature"] = temperature
|
||||||
kwargs["temperature"] = temperature
|
|
||||||
|
|
||||||
if force_json:
|
if force_json:
|
||||||
if json_schema:
|
if json_schema:
|
||||||
kwargs["response_format"] = {
|
kwargs["response_format"] = {"type": "json_schema", "json_schema": {"name": json_schema_name, "strict": strict_json_schema, "schema": json_schema}}
|
||||||
"type": "json_schema",
|
|
||||||
"json_schema": {"name": json_schema_name, "strict": strict_json_schema, "schema": json_schema}
|
|
||||||
}
|
|
||||||
else:
|
else:
|
||||||
kwargs["response_format"] = {"type": "json_object"}
|
kwargs["response_format"] = {"type": "json_object"}
|
||||||
|
|
||||||
response = await self.openrouter_client.chat.completions.create(
|
response = await self.openrouter_client.chat.completions.create(model=model, messages=messages, **kwargs)
|
||||||
model=model, messages=messages, **kwargs
|
if not response.choices: return ""
|
||||||
)
|
|
||||||
|
|
||||||
if not response.choices:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
return response.choices[0].message.content.strip() if response.choices[0].message.content else ""
|
return response.choices[0].message.content.strip() if response.choices[0].message.content else ""
|
||||||
|
|
||||||
async def _execute_ollama(self, prompt, system, force_json, max_retries, base_delay, temperature=None):
|
async def _execute_ollama(self, prompt, system, force_json, max_retries, base_delay, temperature=None, model_override=None):
|
||||||
# Nutzt Profil-Temperatur oder strikte Defaults für lokale Hardware-Schonung
|
# WP-20: Restaurierter Retry-Loop für lokale Hardware-Resilienz
|
||||||
|
effective_model = model_override or self.settings.LLM_MODEL
|
||||||
effective_temp = temperature if temperature is not None else (0.1 if force_json else 0.7)
|
effective_temp = temperature if temperature is not None else (0.1 if force_json else 0.7)
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
"model": self.settings.LLM_MODEL,
|
"model": effective_model,
|
||||||
"prompt": prompt,
|
"prompt": prompt, "stream": False,
|
||||||
"stream": False,
|
|
||||||
"options": {"temperature": effective_temp, "num_ctx": 8192}
|
"options": {"temperature": effective_temp, "num_ctx": 8192}
|
||||||
}
|
}
|
||||||
if force_json: payload["format"] = "json"
|
if force_json: payload["format"] = "json"
|
||||||
|
|
@ -323,12 +325,11 @@ class LLMService:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
attempt += 1
|
attempt += 1
|
||||||
if attempt > max_retries:
|
if attempt > max_retries:
|
||||||
logger.error(f"❌ Ollama final failure after {attempt} attempts: {e}")
|
logger.error(f"❌ Ollama failure after {attempt} attempts: {e}")
|
||||||
raise e
|
raise e
|
||||||
await asyncio.sleep(base_delay * (2 ** (attempt - 1)))
|
await asyncio.sleep(base_delay * (2 ** (attempt - 1)))
|
||||||
|
|
||||||
async def generate_rag_response(self, query: str, context_str: Optional[str] = None) -> str:
|
async def generate_rag_response(self, query: str, context_str: Optional[str] = None) -> str:
|
||||||
"""WP-25: Orchestrierung via DecisionEngine."""
|
|
||||||
return await self.decision_engine.ask(query)
|
return await self.decision_engine.ask(query)
|
||||||
|
|
||||||
async def close(self):
|
async def close(self):
|
||||||
|
|
|
||||||
337
config/prompts - Kopie.yaml
Normal file
337
config/prompts - Kopie.yaml
Normal file
|
|
@ -0,0 +1,337 @@
|
||||||
|
# config/prompts.yaml — VERSION 3.1.2 (WP-25 Cleanup: Multi-Stream Sync)
|
||||||
|
# STATUS: Active
|
||||||
|
# FIX:
|
||||||
|
# - 100% Wiederherstellung der Ingest- & Validierungslogik (Sektion 5-8).
|
||||||
|
# - Überführung der Kategorien 1-4 in die Multi-Stream Struktur unter Beibehaltung des Inhalts.
|
||||||
|
# - Konsolidierung: Sektion 9 (v3.0.0) wurde in Sektion 1 & 2 integriert (keine Redundanz).
|
||||||
|
|
||||||
|
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 und Zielen.
|
||||||
|
- Du passt deinen Stil dynamisch an die Situation an (Analytisch, Empathisch oder Technisch).
|
||||||
|
|
||||||
|
DEINE REGELN:
|
||||||
|
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_WHAT / FACT_WHEN)
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# Ersetzt das alte 'rag_template'. Nutzt jetzt parallele Streams.
|
||||||
|
fact_synthesis_v1:
|
||||||
|
ollama: |
|
||||||
|
WISSENS-STREAMS:
|
||||||
|
=========================================
|
||||||
|
FAKTEN & STATUS:
|
||||||
|
{facts_stream}
|
||||||
|
|
||||||
|
ERFAHRUNG & BIOGRAFIE:
|
||||||
|
{biography_stream}
|
||||||
|
|
||||||
|
WISSEN & TECHNIK:
|
||||||
|
{tech_stream}
|
||||||
|
=========================================
|
||||||
|
|
||||||
|
FRAGE:
|
||||||
|
{query}
|
||||||
|
|
||||||
|
ANWEISUNG:
|
||||||
|
Beantworte die Frage präzise basierend auf den Quellen.
|
||||||
|
Kombiniere harte Fakten mit persönlichen Erfahrungen, falls vorhanden.
|
||||||
|
Fasse die Informationen zusammen. Sei objektiv und neutral.
|
||||||
|
gemini: |
|
||||||
|
Beantworte die Wissensabfrage "{query}" basierend auf diesen Streams:
|
||||||
|
FAKTEN: {facts_stream}
|
||||||
|
BIOGRAFIE/ERFAHRUNG: {biography_stream}
|
||||||
|
TECHNIK: {tech_stream}
|
||||||
|
Kombiniere harte Fakten mit persönlichen Erfahrungen, falls vorhanden. Antworte strukturiert und präzise.
|
||||||
|
openrouter: |
|
||||||
|
Synthese der Wissens-Streams für: {query}
|
||||||
|
Inhalt: {facts_stream} | {biography_stream} | {tech_stream}
|
||||||
|
Antworte basierend auf dem bereitgestellten Kontext.
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 2. DECISION: Strategie & Abwägung (Intent: DECISION)
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# Ersetzt das alte 'decision_template'. Nutzt jetzt parallele Streams.
|
||||||
|
decision_synthesis_v1:
|
||||||
|
ollama: |
|
||||||
|
ENTSCHEIDUNGS-STREAMS:
|
||||||
|
=========================================
|
||||||
|
WERTE & PRINZIPIEN (Identität):
|
||||||
|
{values_stream}
|
||||||
|
|
||||||
|
OPERATIVE FAKTEN (Realität):
|
||||||
|
{facts_stream}
|
||||||
|
|
||||||
|
RISIKO-RADAR (Konsequenzen):
|
||||||
|
{risk_stream}
|
||||||
|
=========================================
|
||||||
|
|
||||||
|
ENTSCHEIDUNGSFRAGE:
|
||||||
|
{query}
|
||||||
|
|
||||||
|
ANWEISUNG:
|
||||||
|
Du agierst als mein Entscheidungs-Partner.
|
||||||
|
1. Analysiere die Faktenlage aus den Quellen.
|
||||||
|
2. Prüfe dies hart gegen meine strategischen Notizen (Werte & Prinzipien).
|
||||||
|
3. Wäge ab: Passt die technische/faktische Lösung zu meinen Werten?
|
||||||
|
|
||||||
|
FORMAT:
|
||||||
|
- **Analyse:** (Kurze Zusammenfassung der Fakten)
|
||||||
|
- **Abgleich:** (Gibt es Konflikte mit Werten/Zielen? Nenne die Quelle!)
|
||||||
|
- **Empfehlung:** (Klare Meinung: Ja/No/Vielleicht mit Begründung)
|
||||||
|
gemini: |
|
||||||
|
Agiere als mein strategischer Partner. Analysiere die Frage: {query}
|
||||||
|
Werte: {values_stream} | Fakten: {facts_stream} | Risiken: {risk_stream}.
|
||||||
|
Wäge ab und gib eine klare strategische Empfehlung ab.
|
||||||
|
openrouter: |
|
||||||
|
Strategische Multi-Stream Analyse für: {query}
|
||||||
|
Werte-Basis: {values_stream} | Fakten: {facts_stream} | Risiken: {risk_stream}
|
||||||
|
Bitte wäge ab und gib eine Empfehlung.
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 3. EMPATHY: Der Spiegel / "Ich"-Modus (Intent: EMPATHY)
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
empathy_template:
|
||||||
|
ollama: |
|
||||||
|
KONTEXT (ERFAHRUNGEN & WERTE):
|
||||||
|
=========================================
|
||||||
|
ERLEBNISSE & BIOGRAFIE:
|
||||||
|
{biography_stream}
|
||||||
|
|
||||||
|
WERTE & BEDÜRFNISSE:
|
||||||
|
{values_stream}
|
||||||
|
=========================================
|
||||||
|
|
||||||
|
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 Werten, 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.
|
||||||
|
gemini: "Sei mein digitaler Spiegel für {query}. Kontext: {biography_stream}, {values_stream}"
|
||||||
|
openrouter: "Empathische Reflexion der Situation {query}. Persönlicher Kontext: {biography_stream}, {values_stream}"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 4. TECHNICAL: Der Coder (Intent: CODING)
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
technical_template:
|
||||||
|
ollama: |
|
||||||
|
KONTEXT (WISSEN & PROJEKTE):
|
||||||
|
=========================================
|
||||||
|
TECHNIK & SNIPPETS:
|
||||||
|
{tech_stream}
|
||||||
|
|
||||||
|
PROJEKT-STATUS:
|
||||||
|
{facts_stream}
|
||||||
|
=========================================
|
||||||
|
|
||||||
|
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.
|
||||||
|
gemini: "Generiere Code für {query} unter Berücksichtigung von {tech_stream} und {facts_stream}."
|
||||||
|
openrouter: "Technischer Support für {query}. Referenzen: {tech_stream}, Projekt-Kontext: {facts_stream}"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 5. INTERVIEW: Der "One-Shot Extractor" (WP-07)
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
interview_template:
|
||||||
|
ollama: |
|
||||||
|
TASK:
|
||||||
|
Du bist ein professioneller Ghostwriter. Verwandle den "USER INPUT" in eine strukturierte Notiz vom Typ '{target_type}'.
|
||||||
|
|
||||||
|
STRUKTUR (Nutze EXAKT diese Überschriften):
|
||||||
|
{schema_fields}
|
||||||
|
|
||||||
|
USER INPUT:
|
||||||
|
"{query}"
|
||||||
|
|
||||||
|
ANWEISUNG ZUM INHALT:
|
||||||
|
1. Analysiere den Input genau.
|
||||||
|
2. Schreibe die Inhalte unter die passenden Überschriften aus der STRUKTUR-Liste oben.
|
||||||
|
3. STIL: Schreibe flüssig, professionell und in der Ich-Perspektive. Korrigiere Grammatikfehler, aber behalte den persönlichen Ton bei.
|
||||||
|
4. Wenn Informationen für einen Abschnitt fehlen, schreibe nur: "[TODO: Ergänzen]". Erfinde nichts dazu.
|
||||||
|
|
||||||
|
OUTPUT FORMAT (YAML + MARKDOWN):
|
||||||
|
---
|
||||||
|
type: {target_type}
|
||||||
|
status: draft
|
||||||
|
title: (Erstelle einen treffenden, kurzen Titel für den Inhalt)
|
||||||
|
tags: [Tag1, Tag2]
|
||||||
|
---
|
||||||
|
|
||||||
|
# (Wiederhole den Titel hier)
|
||||||
|
|
||||||
|
## (Erster Begriff aus STRUKTUR)
|
||||||
|
(Text...)
|
||||||
|
|
||||||
|
## (Zweiter Begriff aus STRUKTUR)
|
||||||
|
(Text...)
|
||||||
|
gemini: "Extrahiere Daten für {target_type} aus {query}."
|
||||||
|
openrouter: "Strukturiere den Input {query} nach dem Schema {schema_fields} für Typ {target_type}."
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 6. EDGE_ALLOCATION: Kantenfilter (Ingest)
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
edge_allocation_template:
|
||||||
|
ollama: |
|
||||||
|
TASK:
|
||||||
|
Du bist ein strikter Selektor. Du erhältst eine Liste von "Kandidaten-Kanten" (Strings).
|
||||||
|
Wähle jene aus, die inhaltlich im "Textabschnitt" vorkommen oder relevant sind.
|
||||||
|
|
||||||
|
TEXTABSCHNITT:
|
||||||
|
"""
|
||||||
|
{chunk_text}
|
||||||
|
"""
|
||||||
|
|
||||||
|
KANDIDATEN (Auswahl-Pool):
|
||||||
|
{edge_list}
|
||||||
|
|
||||||
|
REGELN:
|
||||||
|
1. Die Kanten haben das Format "typ:ziel". Der "typ" ist variabel und kann ALLES sein.
|
||||||
|
2. Gib NUR die Strings aus der Kandidaten-Liste zurück, die zum Text passen.
|
||||||
|
3. Erfinde KEINE neuen Kanten.
|
||||||
|
4. Antworte als flache JSON-Liste.
|
||||||
|
|
||||||
|
DEIN OUTPUT (JSON):
|
||||||
|
gemini: |
|
||||||
|
TASK: Ordne Kanten einem Textabschnitt zu.
|
||||||
|
ERLAUBTE TYPEN: {valid_types}
|
||||||
|
TEXT: {chunk_text}
|
||||||
|
KANDIDATEN: {edge_list}
|
||||||
|
OUTPUT: STRIKT eine flache JSON-Liste ["typ:ziel"]. Kein Text davor/danach. Wenn nichts: []. Keine Objekte!
|
||||||
|
openrouter: |
|
||||||
|
TASK: Filtere relevante Kanten aus dem Pool.
|
||||||
|
ERLAUBTE TYPEN: {valid_types}
|
||||||
|
TEXT: {chunk_text}
|
||||||
|
POOL: {edge_list}
|
||||||
|
ANWEISUNG: Gib NUR eine flache JSON-Liste von Strings zurück.
|
||||||
|
BEISPIEL: ["kind:target", "kind:target"]
|
||||||
|
REGEL: Kein Text, keine Analyse, keine Kommentare. Wenn nichts passt, gib [] zurück.
|
||||||
|
OUTPUT:
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 7. SMART EDGE ALLOCATION: Extraktion (Ingest)
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
edge_extraction:
|
||||||
|
ollama: |
|
||||||
|
TASK:
|
||||||
|
Du bist ein Wissens-Ingenieur für den digitalen Zwilling 'mindnet'.
|
||||||
|
Deine Aufgabe ist es, semantische Relationen (Kanten) aus dem Text zu extrahieren,
|
||||||
|
die die Hauptnotiz '{note_id}' mit anderen Konzepten verbinden.
|
||||||
|
|
||||||
|
ANWEISUNGEN:
|
||||||
|
1. Identifiziere wichtige Entitäten, Konzepte oder Ereignisse im Text.
|
||||||
|
2. Bestimme die Art der Beziehung (z.B. part_of, uses, related_to, blocks, caused_by).
|
||||||
|
3. Das Ziel (target) muss ein prägnanter Begriff sein.
|
||||||
|
4. Antworte AUSSCHLIESSLICH in validem JSON als Liste von Objekten.
|
||||||
|
|
||||||
|
BEISPIEL:
|
||||||
|
[[ {{"to": "Ziel-Konzept", \"kind\": \"beziehungs_typ\"}} ]]
|
||||||
|
|
||||||
|
TEXT:
|
||||||
|
"""
|
||||||
|
{text}
|
||||||
|
"""
|
||||||
|
|
||||||
|
DEIN OUTPUT (JSON):
|
||||||
|
gemini: |
|
||||||
|
Analysiere '{note_id}'. Extrahiere semantische Beziehungen.
|
||||||
|
ERLAUBTE TYPEN: {valid_types}
|
||||||
|
TEXT: {text}
|
||||||
|
OUTPUT: STRIKT JSON-Array von Objekten: [[{{"to\":\"Ziel\",\"kind\":\"typ\"}}]]. Kein Text davor/danach. Wenn nichts: [].
|
||||||
|
openrouter: |
|
||||||
|
TASK: Extrahiere semantische Relationen für '{note_id}'.
|
||||||
|
ERLAUBTE TYPEN: {valid_types}
|
||||||
|
TEXT: {text}
|
||||||
|
ANWEISUNG: Antworte AUSSCHLIESSLICH mit einem JSON-Array von Objekten.
|
||||||
|
FORMAT: [[{{"to\":\"Ziel-Begriff\",\"kind\":\"typ\"}}]]
|
||||||
|
STRIKTES VERBOT: Schreibe keine Einleitung, keine Analyse und keine Erklärungen.
|
||||||
|
Wenn keine Relationen existieren, antworte NUR mit: []
|
||||||
|
OUTPUT:
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 8. WP-15b: EDGE VALIDATION (Ingest/Validate)
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
edge_validation:
|
||||||
|
gemini: |
|
||||||
|
Bewerte die semantische Validität dieser Verbindung im Wissensgraph.
|
||||||
|
|
||||||
|
KONTEXT DER QUELLE (Chunk):
|
||||||
|
"{chunk_text}"
|
||||||
|
|
||||||
|
ZIEL-NOTIZ: "{target_title}"
|
||||||
|
ZIEL-BESCHREIBUNG (Zusammenfassung):
|
||||||
|
"{target_summary}"
|
||||||
|
|
||||||
|
GEPLANTE RELATION: "{edge_kind}"
|
||||||
|
|
||||||
|
FRAGE: Bestätigt der Kontext der Quelle die Beziehung '{edge_kind}' zum Ziel?
|
||||||
|
REGEL: Antworte NUR mit 'YES' oder 'NO'. Keine Erklärungen oder Smalltalk.
|
||||||
|
openrouter: |
|
||||||
|
Verify semantic relation for graph construction.
|
||||||
|
Source Context: {chunk_text}
|
||||||
|
Target Note: {target_title}
|
||||||
|
Target Summary: {target_summary}
|
||||||
|
Proposed Relation: {edge_kind}
|
||||||
|
Instruction: Does the source context support this relation to the target?
|
||||||
|
Result: Respond ONLY with 'YES' or 'NO'.
|
||||||
|
ollama: |
|
||||||
|
Bewerte die semantische Korrektheit dieser Verbindung.
|
||||||
|
QUELLE: {chunk_text}
|
||||||
|
ZIEL: {target_title} ({target_summary})
|
||||||
|
BEZIEHUNG: {edge_kind}
|
||||||
|
Ist diese Verbindung valide? Antworte NUR mit YES oder NO.
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 10. WP-25: INTENT ROUTING (Intent: CLASSIFY)
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
intent_router_v1:
|
||||||
|
ollama: |
|
||||||
|
Analysiere die Nutzeranfrage und wähle die passende Strategie.
|
||||||
|
Antworte NUR mit dem Namen der Strategie.
|
||||||
|
|
||||||
|
STRATEGIEN:
|
||||||
|
- FACT_WHEN: Nur für explizite Fragen nach einem exakten Datum, Uhrzeit oder dem "Wann" eines Ereignisses.
|
||||||
|
- FACT_WHAT: Fragen nach Inhalten, Listen von Objekten/Projekten, Definitionen oder "Was/Welche" Anfragen (auch bei Zeiträumen).
|
||||||
|
- DECISION: Rat, Meinung, "Soll ich?", Abwägung gegen Werte.
|
||||||
|
- EMPATHY: Emotionen, Reflexion, Befindlichkeit.
|
||||||
|
- CODING: Programmierung, Skripte, technische Syntax.
|
||||||
|
- INTERVIEW: Dokumentation neuer Informationen, Notizen anlegen.
|
||||||
|
|
||||||
|
NACHRICHT: "{query}"
|
||||||
|
STRATEGIE:
|
||||||
|
gemini: |
|
||||||
|
Classify intent:
|
||||||
|
- FACT_WHEN: Exact dates/times only.
|
||||||
|
- FACT_WHAT: Content, lists of entities (projects, etc.), definitions, "What/Which" queries.
|
||||||
|
- DECISION: Strategic advice/values.
|
||||||
|
- EMPATHY: Emotions.
|
||||||
|
- CODING: Tech/Code.
|
||||||
|
- INTERVIEW: Data entry.
|
||||||
|
Query: "{query}"
|
||||||
|
Result (One word only):
|
||||||
|
openrouter: |
|
||||||
|
Select strategy for Mindnet:
|
||||||
|
FACT_WHEN (timing/dates), FACT_WHAT (entities/lists/what/which), DECISION, EMPATHY, CODING, INTERVIEW.
|
||||||
|
Query: "{query}"
|
||||||
|
Response:
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
# config/prompts.yaml — VERSION 3.1.2 (WP-25 Cleanup: Multi-Stream Sync)
|
# config/prompts.yaml — VERSION 3.2.2 (WP-25b: Hierarchical Model Sync)
|
||||||
# STATUS: Active
|
# STATUS: Active
|
||||||
# FIX:
|
# FIX:
|
||||||
# - 100% Wiederherstellung der Ingest- & Validierungslogik (Sektion 5-8).
|
# - 100% Erhalt der Original-Prompts aus v3.1.2 für die Provider-Ebene (ollama, gemini, openrouter).
|
||||||
# - Überführung der Kategorien 1-4 in die Multi-Stream Struktur unter Beibehaltung des Inhalts.
|
# - Integration der Modell-spezifischen Overrides für Gemini 2.0, Llama 3.3 und Qwen 2.5.
|
||||||
# - Konsolidierung: Sektion 9 (v3.0.0) wurde in Sektion 1 & 2 integriert (keine Redundanz).
|
# - Hinzufügen des notwendigen 'compression_template' für die DecisionEngine v1.3.0.
|
||||||
|
|
||||||
system_prompt: |
|
system_prompt: |
|
||||||
Du bist 'mindnet', mein digitaler Zwilling und strategischer Partner.
|
Du bist 'mindnet', mein digitaler Zwilling und strategischer Partner.
|
||||||
|
|
@ -20,8 +20,19 @@ system_prompt: |
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
# 1. STANDARD: Fakten & Wissen (Intent: FACT_WHAT / FACT_WHEN)
|
# 1. STANDARD: Fakten & Wissen (Intent: FACT_WHAT / FACT_WHEN)
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
# Ersetzt das alte 'rag_template'. Nutzt jetzt parallele Streams.
|
|
||||||
fact_synthesis_v1:
|
fact_synthesis_v1:
|
||||||
|
# --- Modell-spezifisch (WP-25b Optimierung) ---
|
||||||
|
"google/gemini-2.0-flash-exp:free": |
|
||||||
|
Analysiere die Wissens-Streams für: {query}
|
||||||
|
FAKTEN: {facts_stream} | BIOGRAFIE: {biography_stream} | TECHNIK: {tech_stream}
|
||||||
|
Nutze deine hohe Reasoning-Kapazität für eine tiefe Synthese. Antworte präzise auf Deutsch.
|
||||||
|
|
||||||
|
"meta-llama/llama-3.3-70b-instruct:free": |
|
||||||
|
Erstelle eine fundierte Synthese für die Frage: "{query}"
|
||||||
|
Nutze die Daten: {facts_stream}, {biography_stream} und {tech_stream}.
|
||||||
|
Trenne klare Fakten von Erfahrungen. Bleibe strikt beim bereitgestellten Kontext.
|
||||||
|
|
||||||
|
# --- EXAKTE Provider-Fallbacks aus v3.1.2 ---
|
||||||
ollama: |
|
ollama: |
|
||||||
WISSENS-STREAMS:
|
WISSENS-STREAMS:
|
||||||
=========================================
|
=========================================
|
||||||
|
|
@ -42,22 +53,32 @@ fact_synthesis_v1:
|
||||||
Beantworte die Frage präzise basierend auf den Quellen.
|
Beantworte die Frage präzise basierend auf den Quellen.
|
||||||
Kombiniere harte Fakten mit persönlichen Erfahrungen, falls vorhanden.
|
Kombiniere harte Fakten mit persönlichen Erfahrungen, falls vorhanden.
|
||||||
Fasse die Informationen zusammen. Sei objektiv und neutral.
|
Fasse die Informationen zusammen. Sei objektiv und neutral.
|
||||||
|
|
||||||
gemini: |
|
gemini: |
|
||||||
Beantworte die Wissensabfrage "{query}" basierend auf diesen Streams:
|
Beantworte die Wissensabfrage "{query}" basierend auf diesen Streams:
|
||||||
FAKTEN: {facts_stream}
|
FAKTEN: {facts_stream}
|
||||||
BIOGRAFIE/ERFAHRUNG: {biography_stream}
|
BIOGRAFIE/ERFAHRUNG: {biography_stream}
|
||||||
TECHNIK: {tech_stream}
|
TECHNIK: {tech_stream}
|
||||||
Kombiniere harte Fakten mit persönlichen Erfahrungen, falls vorhanden. Antworte strukturiert und präzise.
|
Kombiniere harte Fakten mit persönlichen Erfahrungen, falls vorhanden. Antworte strukturiert und präzise.
|
||||||
|
|
||||||
openrouter: |
|
openrouter: |
|
||||||
Synthese der Wissens-Streams für: {query}
|
Synthese der Wissens-Streams für: {query}
|
||||||
Inhalt: {facts_stream} | {biography_stream} | {tech_stream}
|
Inhalt: {facts_stream} | {biography_stream} | {tech_stream}
|
||||||
Antworte basierend auf dem bereitgestellten Kontext.
|
Antworte basierend auf dem bereitgestellten Kontext.
|
||||||
|
|
||||||
|
default: "Beantworte {query} basierend auf dem Kontext: {facts_stream} {biography_stream} {tech_stream}."
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
# 2. DECISION: Strategie & Abwägung (Intent: DECISION)
|
# 2. DECISION: Strategie & Abwägung (Intent: DECISION)
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
# Ersetzt das alte 'decision_template'. Nutzt jetzt parallele Streams.
|
|
||||||
decision_synthesis_v1:
|
decision_synthesis_v1:
|
||||||
|
# --- Modell-spezifisch (WP-25b Optimierung) ---
|
||||||
|
"google/gemini-2.0-flash-exp:free": |
|
||||||
|
Agiere als strategischer Partner für: {query}
|
||||||
|
WERTE: {values_stream} | FAKTEN: {facts_stream} | RISIKEN: {risk_stream}
|
||||||
|
Prüfe die Fakten gegen meine Werte. Zeige Zielkonflikte auf. Gib eine klare Empfehlung.
|
||||||
|
|
||||||
|
# --- EXAKTE Provider-Fallbacks aus v3.1.2 ---
|
||||||
ollama: |
|
ollama: |
|
||||||
ENTSCHEIDUNGS-STREAMS:
|
ENTSCHEIDUNGS-STREAMS:
|
||||||
=========================================
|
=========================================
|
||||||
|
|
@ -84,19 +105,24 @@ decision_synthesis_v1:
|
||||||
- **Analyse:** (Kurze Zusammenfassung der Fakten)
|
- **Analyse:** (Kurze Zusammenfassung der Fakten)
|
||||||
- **Abgleich:** (Gibt es Konflikte mit Werten/Zielen? Nenne die Quelle!)
|
- **Abgleich:** (Gibt es Konflikte mit Werten/Zielen? Nenne die Quelle!)
|
||||||
- **Empfehlung:** (Klare Meinung: Ja/No/Vielleicht mit Begründung)
|
- **Empfehlung:** (Klare Meinung: Ja/No/Vielleicht mit Begründung)
|
||||||
|
|
||||||
gemini: |
|
gemini: |
|
||||||
Agiere als mein strategischer Partner. Analysiere die Frage: {query}
|
Agiere als mein strategischer Partner. Analysiere die Frage: {query}
|
||||||
Werte: {values_stream} | Fakten: {facts_stream} | Risiken: {risk_stream}.
|
Werte: {values_stream} | Fakten: {facts_stream} | Risiken: {risk_stream}.
|
||||||
Wäge ab und gib eine klare strategische Empfehlung ab.
|
Wäge ab und gib eine klare strategische Empfehlung ab.
|
||||||
|
|
||||||
openrouter: |
|
openrouter: |
|
||||||
Strategische Multi-Stream Analyse für: {query}
|
Strategische Multi-Stream Analyse für: {query}
|
||||||
Werte-Basis: {values_stream} | Fakten: {facts_stream} | Risiken: {risk_stream}
|
Werte-Basis: {values_stream} | Fakten: {facts_stream} | Risiken: {risk_stream}
|
||||||
Bitte wäge ab und gib eine Empfehlung.
|
Bitte wäge ab und gib eine Empfehlung.
|
||||||
|
|
||||||
|
default: "Prüfe {query} gegen Werte {values_stream} und Fakten {facts_stream}."
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
# 3. EMPATHY: Der Spiegel / "Ich"-Modus (Intent: EMPATHY)
|
# 3. EMPATHY: Der Spiegel / "Ich"-Modus (Intent: EMPATHY)
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
empathy_template:
|
empathy_template:
|
||||||
|
# --- EXAKTE Provider-Fallbacks aus v3.1.2 ---
|
||||||
ollama: |
|
ollama: |
|
||||||
KONTEXT (ERFAHRUNGEN & WERTE):
|
KONTEXT (ERFAHRUNGEN & WERTE):
|
||||||
=========================================
|
=========================================
|
||||||
|
|
@ -118,13 +144,23 @@ empathy_template:
|
||||||
|
|
||||||
TONFALL:
|
TONFALL:
|
||||||
Ruhig, verständnisvoll, reflektiert. Keine Aufzählungszeichen, sondern fließender Text.
|
Ruhig, verständnisvoll, reflektiert. Keine Aufzählungszeichen, sondern fließender Text.
|
||||||
|
|
||||||
gemini: "Sei mein digitaler Spiegel für {query}. Kontext: {biography_stream}, {values_stream}"
|
gemini: "Sei mein digitaler Spiegel für {query}. Kontext: {biography_stream}, {values_stream}"
|
||||||
openrouter: "Empathische Reflexion der Situation {query}. Persönlicher Kontext: {biography_stream}, {values_stream}"
|
openrouter: "Empathische Reflexion der Situation {query}. Persönlicher Kontext: {biography_stream}, {values_stream}"
|
||||||
|
|
||||||
|
default: "Reflektiere empathisch über {query} basierend auf {biography_stream}."
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
# 4. TECHNICAL: Der Coder (Intent: CODING)
|
# 4. TECHNICAL: Der Coder (Intent: CODING)
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
technical_template:
|
technical_template:
|
||||||
|
# --- Modell-spezifisch (WP-25b Optimierung) ---
|
||||||
|
"qwen/qwen-2.5-vl-7b-instruct:free": |
|
||||||
|
Du bist Senior Software Engineer. TASK: {query}
|
||||||
|
REFERENZEN: {tech_stream} | KONTEXT: {facts_stream}
|
||||||
|
Generiere validen, performanten Code. Nutze die Snippets aus dem Kontext.
|
||||||
|
|
||||||
|
# --- EXAKTE Provider-Fallbacks aus v3.1.2 ---
|
||||||
ollama: |
|
ollama: |
|
||||||
KONTEXT (WISSEN & PROJEKTE):
|
KONTEXT (WISSEN & PROJEKTE):
|
||||||
=========================================
|
=========================================
|
||||||
|
|
@ -148,13 +184,17 @@ technical_template:
|
||||||
- Kurze Erklärung des Ansatzes.
|
- Kurze Erklärung des Ansatzes.
|
||||||
- Markdown Code-Block (Copy-Paste fertig).
|
- Markdown Code-Block (Copy-Paste fertig).
|
||||||
- Wichtige Edge-Cases.
|
- Wichtige Edge-Cases.
|
||||||
|
|
||||||
gemini: "Generiere Code für {query} unter Berücksichtigung von {tech_stream} und {facts_stream}."
|
gemini: "Generiere Code für {query} unter Berücksichtigung von {tech_stream} und {facts_stream}."
|
||||||
openrouter: "Technischer Support für {query}. Referenzen: {tech_stream}, Projekt-Kontext: {facts_stream}"
|
openrouter: "Technischer Support für {query}. Referenzen: {tech_stream}, Projekt-Kontext: {facts_stream}"
|
||||||
|
|
||||||
|
default: "Erstelle eine technische Lösung für {query}."
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
# 5. INTERVIEW: Der "One-Shot Extractor" (WP-07)
|
# 5. INTERVIEW: Der "One-Shot Extractor" (WP-07)
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
interview_template:
|
interview_template:
|
||||||
|
# --- EXAKTE Provider-Fallbacks aus v3.1.2 ---
|
||||||
ollama: |
|
ollama: |
|
||||||
TASK:
|
TASK:
|
||||||
Du bist ein professioneller Ghostwriter. Verwandle den "USER INPUT" in eine strukturierte Notiz vom Typ '{target_type}'.
|
Du bist ein professioneller Ghostwriter. Verwandle den "USER INPUT" in eine strukturierte Notiz vom Typ '{target_type}'.
|
||||||
|
|
@ -186,11 +226,30 @@ interview_template:
|
||||||
|
|
||||||
## (Zweiter Begriff aus STRUKTUR)
|
## (Zweiter Begriff aus STRUKTUR)
|
||||||
(Text...)
|
(Text...)
|
||||||
|
|
||||||
gemini: "Extrahiere Daten für {target_type} aus {query}."
|
gemini: "Extrahiere Daten für {target_type} aus {query}."
|
||||||
openrouter: "Strukturiere den Input {query} nach dem Schema {schema_fields} für Typ {target_type}."
|
openrouter: "Strukturiere den Input {query} nach dem Schema {schema_fields} für Typ {target_type}."
|
||||||
|
|
||||||
|
default: "Extrahiere Informationen für {target_type} aus dem Input: {query}"
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
# 6. EDGE_ALLOCATION: Kantenfilter (Ingest)
|
# 6. WP-25b: PRE-SYNTHESIS COMPRESSION (Neu!)
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
compression_template:
|
||||||
|
"mistralai/mistral-7b-instruct:free": |
|
||||||
|
Reduziere den Stream '{stream_name}' auf die Informationen, die für die Beantwortung der Frage '{query}' absolut notwendig sind.
|
||||||
|
BEHALTE: Harte Fakten, Projektnamen, konkrete Werte und Quellenangaben.
|
||||||
|
ENTFERNE: Redundante Einleitungen, Füllwörter und irrelevante Details.
|
||||||
|
|
||||||
|
INHALT:
|
||||||
|
{content}
|
||||||
|
|
||||||
|
KOMPRIMIERTE ANALYSE:
|
||||||
|
|
||||||
|
default: "Fasse das Wichtigste aus {stream_name} für die Frage {query} kurz zusammen: {content}"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 7. EDGE_ALLOCATION: Kantenfilter (Ingest)
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
edge_allocation_template:
|
edge_allocation_template:
|
||||||
ollama: |
|
ollama: |
|
||||||
|
|
@ -213,12 +272,14 @@ edge_allocation_template:
|
||||||
4. Antworte als flache JSON-Liste.
|
4. Antworte als flache JSON-Liste.
|
||||||
|
|
||||||
DEIN OUTPUT (JSON):
|
DEIN OUTPUT (JSON):
|
||||||
|
|
||||||
gemini: |
|
gemini: |
|
||||||
TASK: Ordne Kanten einem Textabschnitt zu.
|
TASK: Ordne Kanten einem Textabschnitt zu.
|
||||||
ERLAUBTE TYPEN: {valid_types}
|
ERLAUBTE TYPEN: {valid_types}
|
||||||
TEXT: {chunk_text}
|
TEXT: {chunk_text}
|
||||||
KANDIDATEN: {edge_list}
|
KANDIDATEN: {edge_list}
|
||||||
OUTPUT: STRIKT eine flache JSON-Liste ["typ:ziel"]. Kein Text davor/danach. Wenn nichts: []. Keine Objekte!
|
OUTPUT: STRIKT eine flache JSON-Liste ["typ:ziel"]. Kein Text davor/danach. Wenn nichts: []. Keine Objekte!
|
||||||
|
|
||||||
openrouter: |
|
openrouter: |
|
||||||
TASK: Filtere relevante Kanten aus dem Pool.
|
TASK: Filtere relevante Kanten aus dem Pool.
|
||||||
ERLAUBTE TYPEN: {valid_types}
|
ERLAUBTE TYPEN: {valid_types}
|
||||||
|
|
@ -229,8 +290,10 @@ edge_allocation_template:
|
||||||
REGEL: Kein Text, keine Analyse, keine Kommentare. Wenn nichts passt, gib [] zurück.
|
REGEL: Kein Text, keine Analyse, keine Kommentare. Wenn nichts passt, gib [] zurück.
|
||||||
OUTPUT:
|
OUTPUT:
|
||||||
|
|
||||||
|
default: "[]"
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
# 7. SMART EDGE ALLOCATION: Extraktion (Ingest)
|
# 8. SMART EDGE ALLOCATION: Extraktion (Ingest)
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
edge_extraction:
|
edge_extraction:
|
||||||
ollama: |
|
ollama: |
|
||||||
|
|
@ -254,11 +317,13 @@ edge_extraction:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
DEIN OUTPUT (JSON):
|
DEIN OUTPUT (JSON):
|
||||||
|
|
||||||
gemini: |
|
gemini: |
|
||||||
Analysiere '{note_id}'. Extrahiere semantische Beziehungen.
|
Analysiere '{note_id}'. Extrahiere semantische Beziehungen.
|
||||||
ERLAUBTE TYPEN: {valid_types}
|
ERLAUBTE TYPEN: {valid_types}
|
||||||
TEXT: {text}
|
TEXT: {text}
|
||||||
OUTPUT: STRIKT JSON-Array von Objekten: [[{{"to\":\"Ziel\",\"kind\":\"typ\"}}]]. Kein Text davor/danach. Wenn nichts: [].
|
OUTPUT: STRIKT JSON-Array von Objekten: [[{{"to\":\"Ziel\",\"kind\":\"typ\"}}]]. Kein Text davor/danach. Wenn nichts: [].
|
||||||
|
|
||||||
openrouter: |
|
openrouter: |
|
||||||
TASK: Extrahiere semantische Relationen für '{note_id}'.
|
TASK: Extrahiere semantische Relationen für '{note_id}'.
|
||||||
ERLAUBTE TYPEN: {valid_types}
|
ERLAUBTE TYPEN: {valid_types}
|
||||||
|
|
@ -269,10 +334,20 @@ edge_extraction:
|
||||||
Wenn keine Relationen existieren, antworte NUR mit: []
|
Wenn keine Relationen existieren, antworte NUR mit: []
|
||||||
OUTPUT:
|
OUTPUT:
|
||||||
|
|
||||||
|
default: "[]"
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
# 8. WP-15b: EDGE VALIDATION (Ingest/Validate)
|
# 9. INGESTION: EDGE VALIDATION (Ingest/Validate)
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
edge_validation:
|
edge_validation:
|
||||||
|
# --- Modell-spezifisch (WP-25b Optimierung) ---
|
||||||
|
"mistralai/mistral-7b-instruct:free": |
|
||||||
|
Verify relation '{edge_kind}' for graph integrity.
|
||||||
|
Chunk: "{chunk_text}"
|
||||||
|
Target: "{target_title}" ({target_summary})
|
||||||
|
Respond ONLY with 'YES' or 'NO'.
|
||||||
|
|
||||||
|
# --- EXAKTE Provider-Fallbacks aus v3.1.2 ---
|
||||||
gemini: |
|
gemini: |
|
||||||
Bewerte die semantische Validität dieser Verbindung im Wissensgraph.
|
Bewerte die semantische Validität dieser Verbindung im Wissensgraph.
|
||||||
|
|
||||||
|
|
@ -287,6 +362,7 @@ edge_validation:
|
||||||
|
|
||||||
FRAGE: Bestätigt der Kontext der Quelle die Beziehung '{edge_kind}' zum Ziel?
|
FRAGE: Bestätigt der Kontext der Quelle die Beziehung '{edge_kind}' zum Ziel?
|
||||||
REGEL: Antworte NUR mit 'YES' oder 'NO'. Keine Erklärungen oder Smalltalk.
|
REGEL: Antworte NUR mit 'YES' oder 'NO'. Keine Erklärungen oder Smalltalk.
|
||||||
|
|
||||||
openrouter: |
|
openrouter: |
|
||||||
Verify semantic relation for graph construction.
|
Verify semantic relation for graph construction.
|
||||||
Source Context: {chunk_text}
|
Source Context: {chunk_text}
|
||||||
|
|
@ -295,6 +371,7 @@ edge_validation:
|
||||||
Proposed Relation: {edge_kind}
|
Proposed Relation: {edge_kind}
|
||||||
Instruction: Does the source context support this relation to the target?
|
Instruction: Does the source context support this relation to the target?
|
||||||
Result: Respond ONLY with 'YES' or 'NO'.
|
Result: Respond ONLY with 'YES' or 'NO'.
|
||||||
|
|
||||||
ollama: |
|
ollama: |
|
||||||
Bewerte die semantische Korrektheit dieser Verbindung.
|
Bewerte die semantische Korrektheit dieser Verbindung.
|
||||||
QUELLE: {chunk_text}
|
QUELLE: {chunk_text}
|
||||||
|
|
@ -302,10 +379,19 @@ edge_validation:
|
||||||
BEZIEHUNG: {edge_kind}
|
BEZIEHUNG: {edge_kind}
|
||||||
Ist diese Verbindung valide? Antworte NUR mit YES oder NO.
|
Ist diese Verbindung valide? Antworte NUR mit YES oder NO.
|
||||||
|
|
||||||
|
default: "YES"
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
# 10. WP-25: INTENT ROUTING (Intent: CLASSIFY)
|
# 10. WP-25: INTENT ROUTING (Intent: CLASSIFY)
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
intent_router_v1:
|
intent_router_v1:
|
||||||
|
# --- Modell-spezifisch (WP-25b Optimierung) ---
|
||||||
|
"mistralai/mistral-7b-instruct:free": |
|
||||||
|
Classify query "{query}" into exactly one of these categories:
|
||||||
|
FACT_WHEN, FACT_WHAT, DECISION, EMPATHY, CODING, INTERVIEW.
|
||||||
|
Respond with the category name only.
|
||||||
|
|
||||||
|
# --- EXAKTE Provider-Fallbacks aus v3.1.2 ---
|
||||||
ollama: |
|
ollama: |
|
||||||
Analysiere die Nutzeranfrage und wähle die passende Strategie.
|
Analysiere die Nutzeranfrage und wähle die passende Strategie.
|
||||||
Antworte NUR mit dem Namen der Strategie.
|
Antworte NUR mit dem Namen der Strategie.
|
||||||
|
|
@ -320,6 +406,7 @@ intent_router_v1:
|
||||||
|
|
||||||
NACHRICHT: "{query}"
|
NACHRICHT: "{query}"
|
||||||
STRATEGIE:
|
STRATEGIE:
|
||||||
|
|
||||||
gemini: |
|
gemini: |
|
||||||
Classify intent:
|
Classify intent:
|
||||||
- FACT_WHEN: Exact dates/times only.
|
- FACT_WHEN: Exact dates/times only.
|
||||||
|
|
@ -330,8 +417,37 @@ intent_router_v1:
|
||||||
- INTERVIEW: Data entry.
|
- INTERVIEW: Data entry.
|
||||||
Query: "{query}"
|
Query: "{query}"
|
||||||
Result (One word only):
|
Result (One word only):
|
||||||
|
|
||||||
openrouter: |
|
openrouter: |
|
||||||
Select strategy for Mindnet:
|
Select strategy for Mindnet:
|
||||||
FACT_WHEN (timing/dates), FACT_WHAT (entities/lists/what/which), DECISION, EMPATHY, CODING, INTERVIEW.
|
FACT_WHEN (timing/dates), FACT_WHAT (entities/lists/what/which), DECISION, EMPATHY, CODING, INTERVIEW.
|
||||||
Query: "{query}"
|
Query: "{query}"
|
||||||
Response:
|
Response:
|
||||||
|
|
||||||
|
default: "FACT_WHAT"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# 11. WP-25b: FALLBACK SYNTHESIS (Error Recovery)
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
fallback_synthesis:
|
||||||
|
ollama: |
|
||||||
|
Beantworte die folgende Frage basierend auf dem bereitgestellten Kontext.
|
||||||
|
|
||||||
|
FRAGE:
|
||||||
|
{query}
|
||||||
|
|
||||||
|
KONTEXT:
|
||||||
|
{context}
|
||||||
|
|
||||||
|
ANWEISUNG:
|
||||||
|
Nutze den Kontext, um eine präzise Antwort zu geben. Falls der Kontext unvollständig ist, weise darauf hin.
|
||||||
|
|
||||||
|
gemini: |
|
||||||
|
Frage: {query}
|
||||||
|
Kontext: {context}
|
||||||
|
Antworte basierend auf dem Kontext.
|
||||||
|
|
||||||
|
openrouter: |
|
||||||
|
Answer the question "{query}" using the provided context: {context}
|
||||||
|
|
||||||
|
default: "Answer: {query}\n\nContext: {context}"
|
||||||
|
|
@ -51,7 +51,7 @@ Das Repository ist in **logische Domänen** unterteilt.
|
||||||
| `03_tech_retrieval_scoring.md` | **Suche.** Die mathematischen Formeln für Scoring, Hybrid Search und Explanation Layer. |
|
| `03_tech_retrieval_scoring.md` | **Suche.** Die mathematischen Formeln für Scoring, Hybrid Search und Explanation Layer. |
|
||||||
| `03_tech_chat_backend.md` | **API & LLM.** Implementation des Routers, Traffic Control (Semaphore) und Feedback-Traceability. |
|
| `03_tech_chat_backend.md` | **API & LLM.** Implementation des Routers, Traffic Control (Semaphore) und Feedback-Traceability. |
|
||||||
| `03_tech_frontend.md` | **UI & Graph.** Architektur des Streamlit-Frontends, State-Management, Cytoscape-Integration und Editor-Logik. |
|
| `03_tech_frontend.md` | **UI & Graph.** Architektur des Streamlit-Frontends, State-Management, Cytoscape-Integration und Editor-Logik. |
|
||||||
| `03_tech_configuration.md` | **Config.** Referenztabellen für `.env`, `types.yaml` und `retriever.yaml`. |
|
| `03_tech_configuration.md` | **Config.** Referenztabellen für `.env`, `types.yaml`, `decision_engine.yaml`, `llm_profiles.yaml`, `prompts.yaml`. **Neu:** Verbindungen zwischen Config-Dateien, Praxisbeispiel und Mermaid-Grafik. |
|
||||||
| `03_tech_api_reference.md` | **API-Referenz.** Vollständige Dokumentation aller Endpunkte (`/query`, `/chat`, `/ingest`, `/graph`, etc.). |
|
| `03_tech_api_reference.md` | **API-Referenz.** Vollständige Dokumentation aller Endpunkte (`/query`, `/chat`, `/ingest`, `/graph`, etc.). |
|
||||||
|
|
||||||
### 📂 04_Operations (Betrieb)
|
### 📂 04_Operations (Betrieb)
|
||||||
|
|
@ -151,8 +151,8 @@ Damit dieses System wartbar bleibt (auch für KI-Agenten wie NotebookLM), gelten
|
||||||
|
|
||||||
## 6. Dokumentations-Status
|
## 6. Dokumentations-Status
|
||||||
|
|
||||||
**Aktuelle Version:** 2.9.3
|
**Aktuelle Version:** 3.1.1
|
||||||
**Letzte Aktualisierung:** 2025-12-31
|
**Letzte Aktualisierung:** 2026-01-02
|
||||||
**Status:** ✅ Vollständig und aktiv gepflegt
|
**Status:** ✅ Vollständig und aktiv gepflegt
|
||||||
|
|
||||||
**Hinweis:** Diese Dokumentation wird kontinuierlich aktualisiert. Bei Fragen oder Verbesserungsvorschlägen bitte im Repository melden.
|
**Hinweis:** Diese Dokumentation wird kontinuierlich aktualisiert. Bei Fragen oder Verbesserungsvorschlägen bitte im Repository melden.
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
doc_type: glossary
|
doc_type: glossary
|
||||||
audience: all
|
audience: all
|
||||||
status: active
|
status: active
|
||||||
version: 3.0.0
|
version: 3.1.1
|
||||||
context: "Zentrales Glossar für Mindnet v3.0.0. Enthält Definitionen zu Hybrid-Cloud Resilienz, WP-14 Modularisierung, WP-15b Two-Pass Ingestion, WP-15c Multigraph-Support, WP-25 Agentic Multi-Stream RAG, WP-25a Mixture of Experts (MoE) und Mistral-safe Parsing."
|
context: "Zentrales Glossar für Mindnet v3.1.1. Enthält Definitionen zu Hybrid-Cloud Resilienz, WP-14 Modularisierung, WP-15b Two-Pass Ingestion, WP-15c Multigraph-Support, WP-25 Agentic Multi-Stream RAG, WP-25a Mixture of Experts (MoE), WP-25b Lazy-Prompt-Orchestration und Mistral-safe Parsing."
|
||||||
---
|
---
|
||||||
|
|
||||||
# Mindnet Glossar
|
# Mindnet Glossar
|
||||||
|
|
@ -60,3 +60,8 @@ context: "Zentrales Glossar für Mindnet v3.0.0. Enthält Definitionen zu Hybrid
|
||||||
* **Fallback-Kaskade (WP-25a):** Rekursive Fallback-Logik, bei der bei Fehlern automatisch auf das `fallback_profile` umgeschaltet wird, bis der terminale Endpunkt (`identity_safe`) erreicht wird. Schutz gegen Zirkel-Referenzen via `visited_profiles`-Tracking.
|
* **Fallback-Kaskade (WP-25a):** Rekursive Fallback-Logik, bei der bei Fehlern automatisch auf das `fallback_profile` umgeschaltet wird, bis der terminale Endpunkt (`identity_safe`) erreicht wird. Schutz gegen Zirkel-Referenzen via `visited_profiles`-Tracking.
|
||||||
* **Pre-Synthesis Kompression (WP-25a):** Asynchrone Verdichtung überlanger Wissens-Streams vor der Synthese, um Token-Verbrauch zu reduzieren und die Synthese zu beschleunigen. Nutzt `compression_profile` (z.B. `compression_fast`).
|
* **Pre-Synthesis Kompression (WP-25a):** Asynchrone Verdichtung überlanger Wissens-Streams vor der Synthese, um Token-Verbrauch zu reduzieren und die Synthese zu beschleunigen. Nutzt `compression_profile` (z.B. `compression_fast`).
|
||||||
* **Profilgesteuerte Validierung (WP-25a):** Semantische Kanten-Validierung in der Ingestion erfolgt zwingend über das MoE-Profil `ingest_validator` (Temperature 0.0 für Determinismus), unabhängig von der globalen Provider-Konfiguration.
|
* **Profilgesteuerte Validierung (WP-25a):** Semantische Kanten-Validierung in der Ingestion erfolgt zwingend über das MoE-Profil `ingest_validator` (Temperature 0.0 für Determinismus), unabhängig von der globalen Provider-Konfiguration.
|
||||||
|
* **Lazy-Prompt-Orchestration (WP-25b):** Hierarchisches Prompt-Resolution-System, das Prompts erst im Moment des Modellaustauschs lädt, basierend auf dem exakt aktiven Modell. Ermöglicht modell-spezifisches Tuning und maximale Resilienz bei Modell-Fallbacks.
|
||||||
|
* **Hierarchische Prompt-Resolution (WP-25b):** Dreistufige Auflösungs-Logik: Level 1 (Modell-ID) → Level 2 (Provider) → Level 3 (Default). Gewährleistet, dass jedes Modell das optimale Template erhält.
|
||||||
|
* **PROMPT-TRACE (WP-25b):** Logging-Mechanismus, der die genutzte Prompt-Auflösungs-Ebene protokolliert (`🎯 Level 1`, `📡 Level 2`, `⚓ Level 3`). Bietet vollständige Transparenz über die genutzten Instruktionen.
|
||||||
|
* **Ultra-robustes Intent-Parsing (WP-25b):** Regex-basierter Intent-Parser in der DecisionEngine, der Modell-Artefakte wie `[/S]`, `</s>` oder Newlines zuverlässig bereinigt, um präzises Strategie-Routing zu gewährleisten.
|
||||||
|
* **Differenzierte Ingestion-Validierung (WP-25b):** Unterscheidung zwischen transienten Fehlern (Netzwerk, Timeout) und permanenten Fehlern (Config, Validation). Transiente Fehler erlauben die Kante (Datenverlust vermeiden), permanente Fehler lehnen sie ab (Graph-Qualität schützen).
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
---
|
---
|
||||||
doc_type: concept
|
doc_type: concept
|
||||||
audience: architect, product_owner
|
audience: architect, product_owner
|
||||||
scope: ai, router, personas, resilience, agentic_rag, moe
|
scope: ai, router, personas, resilience, agentic_rag, moe, lazy_prompts
|
||||||
status: active
|
status: active
|
||||||
version: 3.0.0
|
version: 3.1.1
|
||||||
context: "Fachkonzept der hybriden KI-Persönlichkeit, Agentic Multi-Stream RAG, WP-25a Mixture of Experts (MoE), Provider-Kaskade und kognitiven Resilienz (Deep Fallback)."
|
context: "Fachkonzept der hybriden KI-Persönlichkeit, Agentic Multi-Stream RAG, WP-25a Mixture of Experts (MoE), WP-25b Lazy-Prompt-Orchestration, Provider-Kaskade und kognitiven Resilienz (Deep Fallback)."
|
||||||
---
|
---
|
||||||
|
|
||||||
# Konzept: KI-Persönlichkeit & Router
|
# Konzept: KI-Persönlichkeit & Router
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
---
|
---
|
||||||
doc_type: technical_reference
|
doc_type: technical_reference
|
||||||
audience: developer, architect
|
audience: developer, architect
|
||||||
scope: backend, chat, llm_service, traffic_control, resilience, agentic_rag, moe
|
scope: backend, chat, llm_service, traffic_control, resilience, agentic_rag, moe, lazy_prompts
|
||||||
status: active
|
status: active
|
||||||
version: 3.0.0
|
version: 3.1.1
|
||||||
context: "Technische Implementierung des FastAPI-Routers, des hybriden LLMService (v3.5.2), WP-25 Agentic Multi-Stream RAG, WP-25a Mixture of Experts (MoE) und WP-20 Resilienz-Logik."
|
context: "Technische Implementierung des FastAPI-Routers, des hybriden LLMService (v3.5.5), WP-25 Agentic Multi-Stream RAG, WP-25a Mixture of Experts (MoE), WP-25b Lazy-Prompt-Orchestration und WP-20 Resilienz-Logik."
|
||||||
---
|
---
|
||||||
|
|
||||||
# Chat Backend & Agentic Multi-Stream RAG
|
# Chat Backend & Agentic Multi-Stream RAG
|
||||||
|
|
@ -13,7 +13,7 @@ context: "Technische Implementierung des FastAPI-Routers, des hybriden LLMServic
|
||||||
|
|
||||||
Der zentrale Einstiegspunkt für jede Chatanfrage ist der **Hybrid Router** (`app/routers/chat.py`). Seit WP-25 agiert das System als **Agentic Orchestrator**, der Nutzeranfragen analysiert, in parallele Wissens-Streams aufteilt und diese zu einer kontextreichen, wertebasierten Antwort synthetisiert.
|
Der zentrale Einstiegspunkt für jede Chatanfrage ist der **Hybrid Router** (`app/routers/chat.py`). Seit WP-25 agiert das System als **Agentic Orchestrator**, der Nutzeranfragen analysiert, in parallele Wissens-Streams aufteilt und diese zu einer kontextreichen, wertebasierten Antwort synthetisiert.
|
||||||
|
|
||||||
### 1.1 Intent-Erkennung (Hybrid-Modus)
|
### 1.1 Intent-Erkennung (Hybrid-Modus - WP-25b)
|
||||||
|
|
||||||
Der Router nutzt einen **Hybrid-Modus** mit Keyword-Fast-Path und LLM-Slow-Path:
|
Der Router nutzt einen **Hybrid-Modus** mit Keyword-Fast-Path und LLM-Slow-Path:
|
||||||
|
|
||||||
|
|
@ -24,9 +24,11 @@ Der Router nutzt einen **Hybrid-Modus** mit Keyword-Fast-Path und LLM-Slow-Path:
|
||||||
2. **Type Keywords (Interview-Modus):**
|
2. **Type Keywords (Interview-Modus):**
|
||||||
* Lädt `types.yaml` und prüft `detection_keywords` für Objekt-Erkennung.
|
* Lädt `types.yaml` und prüft `detection_keywords` für Objekt-Erkennung.
|
||||||
* Wenn Match und keine Frage: **INTERVIEW Modus** (Datenerfassung).
|
* Wenn Match und keine Frage: **INTERVIEW Modus** (Datenerfassung).
|
||||||
3. **LLM Slow-Path (Semantische Analyse):**
|
3. **LLM Slow-Path (Semantische Analyse - WP-25b):**
|
||||||
* Wenn unklar: Anfrage an `DecisionEngine._determine_strategy()` zur LLM-basierten Klassifizierung.
|
* Wenn unklar: Anfrage an `DecisionEngine._determine_strategy()` zur LLM-basierten Klassifizierung.
|
||||||
* Nutzt `intent_router_v1` Prompt aus `prompts.yaml`.
|
* **Lazy-Prompt-Loading:** Nutzt `prompt_key="intent_router_v1"` mit `variables={"query": query}`
|
||||||
|
* **Ultra-robustes Parsing:** Regex-basierter Intent-Parser bereinigt Modell-Artefakte (z.B. `CODING[/S]` → `CODING`)
|
||||||
|
* **Fallback:** Bei unklarem Intent → `FACT_WHAT`
|
||||||
|
|
||||||
### 1.2 Mixture of Experts (MoE) Architektur (WP-25a)
|
### 1.2 Mixture of Experts (MoE) Architektur (WP-25a)
|
||||||
|
|
||||||
|
|
@ -52,13 +54,32 @@ Der `LLMService` (v3.5.2) implementiert eine automatische Fallback-Logik:
|
||||||
* **Synthese:** Nutzt `llm_profile` aus Strategie-Konfiguration
|
* **Synthese:** Nutzt `llm_profile` aus Strategie-Konfiguration
|
||||||
* **Ingestion:** Nutzt `ingest_validator` für binäre Validierungen
|
* **Ingestion:** Nutzt `ingest_validator` für binäre Validierungen
|
||||||
|
|
||||||
### 1.3 Prompt-Auflösung (Bulletproof Resolution)
|
### 1.3 Hierarchisches Prompt-Resolution-System (WP-25b)
|
||||||
|
|
||||||
Um Kompatibilitätsprobleme mit verschachtelten YAML-Prompts zu vermeiden, nutzt der Router die Methode `llm.get_prompt()`. Diese implementiert eine **Provider-Kaskade**:
|
Seit WP-25b nutzt MindNet eine **dreistufige hierarchische Prompt-Auflösung** mit Lazy-Loading. Prompts werden erst im Moment des Modellaustauschs geladen, basierend auf dem exakt aktiven Modell.
|
||||||
* **Spezifischer Provider:** Das System sucht zuerst nach einem Prompt für den aktiv konfigurierten Provider (z.B. `openrouter`).
|
|
||||||
* **Cloud-Stil Fallback:** Existiert dieser nicht, erfolgt ein Fallback auf das `gemini`-Template.
|
**Hierarchische Auflösung (`llm_service.py` v3.5.5):**
|
||||||
* **Basis-Fallback:** Als letzte Instanz wird das `ollama`-Template geladen.
|
|
||||||
* **String-Garantie:** Die Methode garantiert die Rückgabe eines Strings (selbst bei verschachtelten YAML-Dicts), was 500-Fehler bei String-Operationen wie `.replace()` oder `.format()` verhindert.
|
1. **Level 1 (Modell-ID):** Suche nach exakten Übereinstimmungen für die Modell-ID (z.B. `google/gemini-2.0-flash-exp:free`).
|
||||||
|
* **Vorteil:** Modell-spezifische Optimierungen (z.B. für Gemini 2.0, Llama 3.3, Qwen 2.5)
|
||||||
|
* **Logging:** `🎯 [PROMPT-TRACE] Level 1 Match: Model-specific`
|
||||||
|
|
||||||
|
2. **Level 2 (Provider):** Fallback auf allgemeine Provider-Anweisungen (z.B. `openrouter` oder `ollama`).
|
||||||
|
* **Vorteil:** Bewährte Standards aus v3.1.2 bleiben erhalten
|
||||||
|
* **Logging:** `📡 [PROMPT-TRACE] Level 2 Match: Provider-fallback`
|
||||||
|
|
||||||
|
3. **Level 3 (Default):** Globaler Sicherheits-Satz zur Vermeidung von Fehlern bei unbekannten Konfigurationen.
|
||||||
|
* **Fallback-Kette:** `default` → `gemini` → `ollama` → `""`
|
||||||
|
* **Logging:** `⚓ [PROMPT-TRACE] Level 3 Match: Global Default`
|
||||||
|
|
||||||
|
**Lazy-Prompt-Orchestration:**
|
||||||
|
* **Lazy Loading:** Prompts werden erst zur Laufzeit geladen, wenn das aktive Modell bekannt ist
|
||||||
|
* **Parameter:** `prompt_key` und `variables` statt vorformatierter Strings
|
||||||
|
* **Vorteil:** Maximale Resilienz bei Modell-Fallbacks (Cloud → Local)
|
||||||
|
* **Traceability:** Vollständige Transparenz über genutzte Instruktionen via `[PROMPT-TRACE]` Logs
|
||||||
|
|
||||||
|
**String-Garantie:**
|
||||||
|
Die Methode garantiert die Rückgabe eines Strings (selbst bei verschachtelten YAML-Dicts), was 500-Fehler bei String-Operationen wie `.replace()` oder `.format()` verhindert.
|
||||||
|
|
||||||
### 1.4 Multi-Stream Retrieval (WP-25)
|
### 1.4 Multi-Stream Retrieval (WP-25)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
---
|
---
|
||||||
doc_type: technical_reference
|
doc_type: technical_reference
|
||||||
audience: developer, admin
|
audience: developer, admin
|
||||||
scope: configuration, env, registry, scoring, resilience, modularization, agentic_rag, moe
|
scope: configuration, env, registry, scoring, resilience, modularization, agentic_rag, moe, lazy_prompts
|
||||||
status: active
|
status: active
|
||||||
version: 3.0.0
|
version: 3.1.1
|
||||||
context: "Umfassende Referenztabellen für Umgebungsvariablen (inkl. Hybrid-Cloud & WP-76), YAML-Konfigurationen, Edge Registry Struktur, WP-25 Multi-Stream RAG und WP-25a Mixture of Experts (MoE) unter Berücksichtigung von WP-14."
|
context: "Umfassende Referenztabellen für Umgebungsvariablen (inkl. Hybrid-Cloud & WP-76), YAML-Konfigurationen, Edge Registry Struktur, WP-25 Multi-Stream RAG, WP-25a Mixture of Experts (MoE) und WP-25b Lazy-Prompt-Orchestration unter Berücksichtigung von WP-14."
|
||||||
---
|
---
|
||||||
|
|
||||||
# Konfigurations-Referenz
|
# Konfigurations-Referenz
|
||||||
|
|
@ -302,13 +302,47 @@ Die Strategie `INTERVIEW` dient der strukturierten Datenerfassung.
|
||||||
|
|
||||||
> **Hinweis:** Da spezifische Schemas für Projekte oder Erfahrungen direkt in der `types.yaml` definiert werden, dient die `decision_engine.yaml` hier primär als Fallback für generische Datenaufnahmen.
|
> **Hinweis:** Da spezifische Schemas für Projekte oder Erfahrungen direkt in der `types.yaml` definiert werden, dient die `decision_engine.yaml` hier primär als Fallback für generische Datenaufnahmen.
|
||||||
|
|
||||||
### 5.6 Prompts-Konfiguration (`prompts.yaml` v3.1.2)
|
### 5.6 Prompts-Konfiguration (`prompts.yaml` v3.2.2 - WP-25b)
|
||||||
|
|
||||||
Seit WP-25 nutzen die Synthese-Templates explizite Stream-Variablen:
|
Seit WP-25b nutzt MindNet eine **hierarchische Prompt-Struktur** mit Lazy-Loading. Prompts werden erst zur Laufzeit geladen, basierend auf dem exakt aktiven Modell.
|
||||||
|
|
||||||
**Template-Struktur:**
|
**Hierarchische Template-Struktur:**
|
||||||
```yaml
|
```yaml
|
||||||
decision_synthesis_v1:
|
decision_synthesis_v1:
|
||||||
|
# Level 1: Modell-spezifisch (höchste Priorität)
|
||||||
|
"google/gemini-2.0-flash-exp:free": |
|
||||||
|
WERTE & PRINZIPIEN (Identität):
|
||||||
|
{values_stream}
|
||||||
|
|
||||||
|
OPERATIVE FAKTEN (Realität):
|
||||||
|
{facts_stream}
|
||||||
|
|
||||||
|
RISIKO-RADAR (Konsequenzen):
|
||||||
|
{risk_stream}
|
||||||
|
|
||||||
|
ENTSCHEIDUNGSFRAGE:
|
||||||
|
{query}
|
||||||
|
Nutze deine hohe Reasoning-Kapazität für eine tiefe Synthese.
|
||||||
|
|
||||||
|
"meta-llama/llama-3.3-70b-instruct:free": |
|
||||||
|
Erstelle eine fundierte Synthese für die Frage: "{query}"
|
||||||
|
Nutze die Daten: {values_stream}, {facts_stream} und {risk_stream}.
|
||||||
|
Trenne klare Fakten von Erfahrungen. Bleibe strikt beim bereitgestellten Kontext.
|
||||||
|
|
||||||
|
# Level 2: Provider-Fallback (mittlere Priorität)
|
||||||
|
openrouter: |
|
||||||
|
WERTE & PRINZIPIEN (Identität):
|
||||||
|
{values_stream}
|
||||||
|
|
||||||
|
OPERATIVE FAKTEN (Realität):
|
||||||
|
{facts_stream}
|
||||||
|
|
||||||
|
RISIKO-RADAR (Konsequenzen):
|
||||||
|
{risk_stream}
|
||||||
|
|
||||||
|
ENTSCHEIDUNGSFRAGE:
|
||||||
|
{query}
|
||||||
|
|
||||||
ollama: |
|
ollama: |
|
||||||
WERTE & PRINZIPIEN (Identität):
|
WERTE & PRINZIPIEN (Identität):
|
||||||
{values_stream}
|
{values_stream}
|
||||||
|
|
@ -321,13 +355,31 @@ decision_synthesis_v1:
|
||||||
|
|
||||||
ENTSCHEIDUNGSFRAGE:
|
ENTSCHEIDUNGSFRAGE:
|
||||||
{query}
|
{query}
|
||||||
|
|
||||||
|
# Level 3: Global Default (niedrigste Priorität)
|
||||||
|
default: |
|
||||||
|
Synthetisiere die folgenden Informationen für: {query}
|
||||||
|
{values_stream} | {facts_stream} | {risk_stream}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Auflösungs-Logik:**
|
||||||
|
1. **Level 1:** Exakte Modell-ID (z.B. `google/gemini-2.0-flash-exp:free`)
|
||||||
|
2. **Level 2:** Provider-Fallback (z.B. `openrouter`, `ollama`, `gemini`)
|
||||||
|
3. **Level 3:** Global Default (`default` → `gemini` → `ollama` → `""`)
|
||||||
|
|
||||||
|
**Lazy-Loading:**
|
||||||
|
* Prompts werden erst zur Laufzeit geladen, wenn das aktive Modell bekannt ist
|
||||||
|
* **Parameter:** `prompt_key` und `variables` statt vorformatierter Strings
|
||||||
|
* **Vorteil:** Maximale Resilienz bei Modell-Fallbacks (Cloud → Local)
|
||||||
|
|
||||||
**Pre-Initialization:**
|
**Pre-Initialization:**
|
||||||
Alle möglichen Stream-Variablen werden vorab initialisiert (verhindert KeyErrors bei unvollständigen Konfigurationen).
|
Alle möglichen Stream-Variablen werden vorab initialisiert (verhindert KeyErrors bei unvollständigen Konfigurationen).
|
||||||
|
|
||||||
**Provider-spezifische Templates:**
|
**PROMPT-TRACE Logging:**
|
||||||
Separate Versionen für Ollama, Gemini und OpenRouter.
|
Das System protokolliert die genutzte Auflösungs-Ebene:
|
||||||
|
* `🎯 [PROMPT-TRACE] Level 1 Match: Model-specific`
|
||||||
|
* `📡 [PROMPT-TRACE] Level 2 Match: Provider-fallback`
|
||||||
|
* `⚓ [PROMPT-TRACE] Level 3 Match: Global Default`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -404,6 +456,261 @@ values_stream:
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 7. Konfigurations-Verbindungen & Datenfluss
|
||||||
|
|
||||||
|
Die vier zentralen Konfigurationsdateien (`types.yaml`, `decision_engine.yaml`, `llm_profiles.yaml`, `prompts.yaml`) arbeiten eng zusammen, um das agentische Multi-Stream RAG System zu steuern. Diese Sektion erklärt die Verbindungen und zeigt einen konkreten Praxisablauf.
|
||||||
|
|
||||||
|
### 7.1 Architektur-Übersicht
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "1. Typ-Definition (types.yaml)"
|
||||||
|
T1[Typ: value<br/>chunking_profile: structured_strict<br/>retriever_weight: 1.00]
|
||||||
|
T2[Typ: risk<br/>chunking_profile: sliding_short<br/>retriever_weight: 0.85]
|
||||||
|
T3[Typ: project<br/>chunking_profile: sliding_smart_edges<br/>retriever_weight: 0.97]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "2. Stream-Konfiguration (decision_engine.yaml)"
|
||||||
|
D1[values_stream<br/>filter_types: value, principle, belief...<br/>llm_profile: identity_safe<br/>compression_profile: identity_safe]
|
||||||
|
D2[risk_stream<br/>filter_types: risk, obstacle, bias<br/>llm_profile: synthesis_pro<br/>compression_profile: compression_fast]
|
||||||
|
D3[facts_stream<br/>filter_types: project, decision, task...<br/>llm_profile: synthesis_pro<br/>compression_profile: compression_fast]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "3. Strategie-Komposition (decision_engine.yaml)"
|
||||||
|
S1[DECISION Strategie<br/>use_streams: values_stream, facts_stream, risk_stream<br/>llm_profile: synthesis_pro<br/>prompt_template: decision_synthesis_v1]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "4. Experten-Profile (llm_profiles.yaml)"
|
||||||
|
P1[synthesis_pro<br/>provider: openrouter<br/>model: google/gemini-2.0-flash-exp:free<br/>temperature: 0.7<br/>fallback_profile: synthesis_backup]
|
||||||
|
P2[compression_fast<br/>provider: openrouter<br/>model: mistralai/mistral-7b-instruct:free<br/>temperature: 0.1<br/>fallback_profile: identity_safe]
|
||||||
|
P3[identity_safe<br/>provider: ollama<br/>model: phi3:mini<br/>temperature: 0.2<br/>fallback_profile: null]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "5. Prompt-Templates (prompts.yaml)"
|
||||||
|
PR1[decision_synthesis_v1<br/>Level 1: google/gemini-2.0-flash-exp:free<br/>Level 2: openrouter<br/>Level 3: default]
|
||||||
|
end
|
||||||
|
|
||||||
|
T1 -->|filter_types| D1
|
||||||
|
T2 -->|filter_types| D2
|
||||||
|
T3 -->|filter_types| D3
|
||||||
|
|
||||||
|
D1 -->|use_streams| S1
|
||||||
|
D2 -->|use_streams| S1
|
||||||
|
D3 -->|use_streams| S1
|
||||||
|
|
||||||
|
S1 -->|llm_profile| P1
|
||||||
|
D1 -->|llm_profile| P3
|
||||||
|
D2 -->|compression_profile| P2
|
||||||
|
D3 -->|compression_profile| P2
|
||||||
|
|
||||||
|
S1 -->|prompt_template| PR1
|
||||||
|
P1 -->|model lookup| PR1
|
||||||
|
|
||||||
|
style T1 fill:#e1f5ff
|
||||||
|
style T2 fill:#e1f5ff
|
||||||
|
style T3 fill:#e1f5ff
|
||||||
|
style D1 fill:#fff4e1
|
||||||
|
style D2 fill:#fff4e1
|
||||||
|
style D3 fill:#fff4e1
|
||||||
|
style S1 fill:#ffe1f5
|
||||||
|
style P1 fill:#e1ffe1
|
||||||
|
style P2 fill:#e1ffe1
|
||||||
|
style P3 fill:#e1ffe1
|
||||||
|
style PR1 fill:#f5e1ff
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.2 Verbindungs-Matrix
|
||||||
|
|
||||||
|
| Von | Zu | Verbindung | Beschreibung |
|
||||||
|
| :--- | :--- | :--- | :--- |
|
||||||
|
| **`types.yaml`** | **`decision_engine.yaml`** | `filter_types` | Streams filtern Notizen basierend auf Typen aus `types.yaml`. Die Liste `filter_types: ["value", "principle", "belief"]` muss exakt den Typ-Namen aus `types.yaml` entsprechen. |
|
||||||
|
| **`types.yaml`** | **`decision_engine.yaml`** | `detection_keywords` | Keywords aus `types.yaml` werden für den Interview-Modus verwendet (z.B. "Projekt" + "neu" → `INTERVIEW`). |
|
||||||
|
| **`decision_engine.yaml`** | **`llm_profiles.yaml`** | `router_profile` | Intent-Erkennung nutzt das Profil `compression_fast` für schnelle Klassifizierung. |
|
||||||
|
| **`decision_engine.yaml`** | **`llm_profiles.yaml`** | `llm_profile` (Stream) | Jeder Stream definiert sein eigenes Profil für Retrieval und Kompression (z.B. `identity_safe` für Privacy). |
|
||||||
|
| **`decision_engine.yaml`** | **`llm_profiles.yaml`** | `llm_profile` (Strategie) | Die finale Synthese nutzt das Strategie-Profil (z.B. `synthesis_pro` für DECISION). |
|
||||||
|
| **`decision_engine.yaml`** | **`llm_profiles.yaml`** | `compression_profile` | Überlange Streams werden via `compression_profile` verdichtet (z.B. `compression_fast`). |
|
||||||
|
| **`decision_engine.yaml`** | **`prompts.yaml`** | `prompt_template` | Strategien referenzieren Template-Keys (z.B. `decision_synthesis_v1`). |
|
||||||
|
| **`llm_profiles.yaml`** | **`prompts.yaml`** | Hierarchische Auflösung | Das aktive Modell aus dem Profil bestimmt, welcher Prompt-Level geladen wird (Model-ID → Provider → Default). |
|
||||||
|
| **`llm_profiles.yaml`** | **`llm_profiles.yaml`** | `fallback_profile` | Rekursive Fallback-Kaskade bei Fehlern (z.B. `synthesis_pro` → `synthesis_backup` → `identity_safe`). |
|
||||||
|
|
||||||
|
### 7.3 Praxisbeispiel: DECISION-Anfrage
|
||||||
|
|
||||||
|
**User-Anfrage:** `"Soll ich das neue Projekt starten?"`
|
||||||
|
|
||||||
|
#### Schritt 1: Intent-Erkennung
|
||||||
|
|
||||||
|
**Datei:** `decision_engine.yaml`
|
||||||
|
```yaml
|
||||||
|
settings:
|
||||||
|
router_profile: "compression_fast" # → llm_profiles.yaml
|
||||||
|
router_prompt_key: "intent_router_v1" # → prompts.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ablauf:**
|
||||||
|
1. System prüft `trigger_keywords` in `DECISION` Strategie → findet `"soll ich"` → **Intent: DECISION**
|
||||||
|
2. Falls kein Keyword-Match: LLM-Router nutzt `compression_fast` Profil aus `llm_profiles.yaml`
|
||||||
|
3. Router lädt `intent_router_v1` aus `prompts.yaml` (hierarchisch basierend auf aktivem Modell)
|
||||||
|
|
||||||
|
#### Schritt 2: Stream-Aktivierung
|
||||||
|
|
||||||
|
**Datei:** `decision_engine.yaml`
|
||||||
|
```yaml
|
||||||
|
strategies:
|
||||||
|
DECISION:
|
||||||
|
use_streams: ["values_stream", "facts_stream", "risk_stream"]
|
||||||
|
llm_profile: "synthesis_pro" # → llm_profiles.yaml
|
||||||
|
prompt_template: "decision_synthesis_v1" # → prompts.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ablauf:**
|
||||||
|
1. System aktiviert drei parallele Streams: `values_stream`, `facts_stream`, `risk_stream`
|
||||||
|
|
||||||
|
#### Schritt 3: Stream-Konfiguration & Typ-Filterung
|
||||||
|
|
||||||
|
**Datei:** `decision_engine.yaml` (Streams) + `types.yaml` (Typ-Definitionen)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# decision_engine.yaml
|
||||||
|
values_stream:
|
||||||
|
filter_types: ["value", "principle", "belief", "trait", "boundary", "need", "motivation"]
|
||||||
|
llm_profile: "identity_safe" # → llm_profiles.yaml
|
||||||
|
compression_profile: "identity_safe" # → llm_profiles.yaml
|
||||||
|
query_template: "Welche meiner Werte und Prinzipien betreffen: {query}"
|
||||||
|
|
||||||
|
facts_stream:
|
||||||
|
filter_types: ["project", "decision", "task", "goal", "event", "state"]
|
||||||
|
llm_profile: "synthesis_pro" # → llm_profiles.yaml
|
||||||
|
compression_profile: "compression_fast" # → llm_profiles.yaml
|
||||||
|
query_template: "Status, Ressourcen und Fakten zu: {query}"
|
||||||
|
|
||||||
|
risk_stream:
|
||||||
|
filter_types: ["risk", "obstacle", "bias"]
|
||||||
|
llm_profile: "synthesis_pro" # → llm_profiles.yaml
|
||||||
|
compression_profile: "compression_fast" # → llm_profiles.yaml
|
||||||
|
query_template: "Gefahren, Hindernisse oder Risiken bei: {query}"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ablauf:**
|
||||||
|
1. **Values Stream:** Sucht in Qdrant nach Notizen mit `type IN ["value", "principle", "belief", ...]` (definiert in `types.yaml`)
|
||||||
|
2. **Facts Stream:** Sucht nach Notizen mit `type IN ["project", "decision", "task", ...]` (definiert in `types.yaml`)
|
||||||
|
3. **Risk Stream:** Sucht nach Notizen mit `type IN ["risk", "obstacle", "bias"]` (definiert in `types.yaml`)
|
||||||
|
|
||||||
|
#### Schritt 4: Profil-Auflösung & Modell-Auswahl
|
||||||
|
|
||||||
|
**Datei:** `llm_profiles.yaml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
synthesis_pro:
|
||||||
|
provider: "openrouter"
|
||||||
|
model: "google/gemini-2.0-flash-exp:free"
|
||||||
|
temperature: 0.7
|
||||||
|
fallback_profile: "synthesis_backup" # → Rekursiver Fallback
|
||||||
|
|
||||||
|
compression_fast:
|
||||||
|
provider: "openrouter"
|
||||||
|
model: "mistralai/mistral-7b-instruct:free"
|
||||||
|
temperature: 0.1
|
||||||
|
fallback_profile: "identity_safe"
|
||||||
|
|
||||||
|
identity_safe:
|
||||||
|
provider: "ollama"
|
||||||
|
model: "phi3:mini"
|
||||||
|
temperature: 0.2
|
||||||
|
fallback_profile: null # Terminaler Endpunkt
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ablauf:**
|
||||||
|
1. **Values Stream:** Nutzt `identity_safe` → Ollama/phi3:mini (lokal, Privacy)
|
||||||
|
2. **Facts Stream:** Nutzt `synthesis_pro` → OpenRouter/Gemini 2.0 (Cloud)
|
||||||
|
3. **Risk Stream:** Nutzt `synthesis_pro` → OpenRouter/Gemini 2.0 (Cloud)
|
||||||
|
4. **Kompression:** Falls Stream > `compression_threshold`, nutzt `compression_fast` → OpenRouter/Mistral 7B
|
||||||
|
|
||||||
|
#### Schritt 5: Prompt-Loading (Hierarchische Auflösung)
|
||||||
|
|
||||||
|
**Datei:** `prompts.yaml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
decision_synthesis_v1:
|
||||||
|
# Level 1: Modell-spezifisch (höchste Priorität)
|
||||||
|
"google/gemini-2.0-flash-exp:free": |
|
||||||
|
Agiere als strategischer Partner für: {query}
|
||||||
|
WERTE: {values_stream} | FAKTEN: {facts_stream} | RISIKEN: {risk_stream}
|
||||||
|
Prüfe die Fakten gegen meine Werte. Zeige Zielkonflikte auf. Gib eine klare Empfehlung.
|
||||||
|
|
||||||
|
# Level 2: Provider-Fallback
|
||||||
|
openrouter: |
|
||||||
|
Strategische Multi-Stream Analyse für: {query}
|
||||||
|
Werte-Basis: {values_stream} | Fakten: {facts_stream} | Risiken: {risk_stream}
|
||||||
|
Bitte wäge ab und gib eine Empfehlung.
|
||||||
|
|
||||||
|
# Level 3: Global Default
|
||||||
|
default: "Prüfe {query} gegen Werte {values_stream} und Fakten {facts_stream}."
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ablauf:**
|
||||||
|
1. System hat `synthesis_pro` Profil geladen → Modell: `google/gemini-2.0-flash-exp:free`
|
||||||
|
2. System sucht in `prompts.yaml` nach `decision_synthesis_v1`:
|
||||||
|
- **Level 1:** Findet exakten Match für `google/gemini-2.0-flash-exp:free` → **Verwendet diesen Prompt**
|
||||||
|
- Falls nicht gefunden: **Level 2** → `openrouter` Fallback
|
||||||
|
- Falls nicht gefunden: **Level 3** → `default` Fallback
|
||||||
|
3. Prompt wird mit Stream-Variablen formatiert: `{values_stream}`, `{facts_stream}`, `{risk_stream}`, `{query}`
|
||||||
|
|
||||||
|
#### Schritt 6: Finale Synthese
|
||||||
|
|
||||||
|
**Ablauf:**
|
||||||
|
1. System ruft LLM auf mit:
|
||||||
|
- **Profil:** `synthesis_pro` (OpenRouter/Gemini 2.0, Temperature 0.7)
|
||||||
|
- **Prompt:** Level-1 Template aus `prompts.yaml` (modell-spezifisch optimiert)
|
||||||
|
- **Variablen:** Formatierte Stream-Inhalte
|
||||||
|
2. Falls Fehler (z.B. Rate-Limit 429):
|
||||||
|
- **Fallback:** `synthesis_backup` (Llama 3.3)
|
||||||
|
- **Prompt:** Automatisch Level-2 (`openrouter`) oder Level-3 (`default`) geladen
|
||||||
|
3. Antwort wird an User zurückgegeben
|
||||||
|
|
||||||
|
### 7.4 Konfigurations-Synchronisation Checkliste
|
||||||
|
|
||||||
|
Beim Ändern einer Konfigurationsdatei müssen folgende Abhängigkeiten geprüft werden:
|
||||||
|
|
||||||
|
**✅ `types.yaml` ändern:**
|
||||||
|
- [ ] Prüfe, ob `filter_types` in `decision_engine.yaml` Streams noch gültig sind
|
||||||
|
- [ ] Prüfe, ob `detection_keywords` für Interview-Modus noch passen
|
||||||
|
- [ ] Prüfe, ob `chunking_profile` noch existiert (in `types.yaml` definiert)
|
||||||
|
|
||||||
|
**✅ `decision_engine.yaml` ändern:**
|
||||||
|
- [ ] Prüfe, ob alle `filter_types` in Streams existieren in `types.yaml`
|
||||||
|
- [ ] Prüfe, ob alle `llm_profile` / `compression_profile` existieren in `llm_profiles.yaml`
|
||||||
|
- [ ] Prüfe, ob alle `prompt_template` Keys existieren in `prompts.yaml`
|
||||||
|
|
||||||
|
**✅ `llm_profiles.yaml` ändern:**
|
||||||
|
- [ ] Prüfe, ob `fallback_profile` Referenzen zirkulär sind (Schutz: `visited_profiles`)
|
||||||
|
- [ ] Prüfe, ob alle referenzierten Profile existieren
|
||||||
|
- [ ] Prüfe, ob Modell-IDs mit `prompts.yaml` Level-1 Keys übereinstimmen (optional, aber empfohlen)
|
||||||
|
|
||||||
|
**✅ `prompts.yaml` ändern:**
|
||||||
|
- [ ] Prüfe, ob alle `prompt_template` Keys aus `decision_engine.yaml` existieren
|
||||||
|
- [ ] Prüfe, ob Modell-spezifische Keys (Level 1) mit `llm_profiles.yaml` Modell-IDs übereinstimmen
|
||||||
|
- [ ] Prüfe, ob alle Stream-Variablen (`{values_stream}`, `{facts_stream}`, etc.) initialisiert werden
|
||||||
|
|
||||||
|
### 7.5 Debugging-Tipps
|
||||||
|
|
||||||
|
**Problem:** Stream findet keine Notizen
|
||||||
|
- **Prüfung:** `filter_types` in Stream stimmt mit Typ-Namen in `types.yaml` überein? (Case-sensitive!)
|
||||||
|
- **Prüfung:** Existieren Notizen mit diesen Typen im Vault?
|
||||||
|
|
||||||
|
**Problem:** Falsches Modell wird verwendet
|
||||||
|
- **Prüfung:** `llm_profile` in Stream/Strategie existiert in `llm_profiles.yaml`?
|
||||||
|
- **Prüfung:** `fallback_profile` Kaskade führt zu unerwartetem Modell?
|
||||||
|
|
||||||
|
**Problem:** Prompt wird nicht gefunden
|
||||||
|
- **Prüfung:** `prompt_template` Key existiert in `prompts.yaml`?
|
||||||
|
- **Prüfung:** Hierarchische Auflösung (Level 1 → 2 → 3) funktioniert? (Logs: `[PROMPT-TRACE]`)
|
||||||
|
|
||||||
|
**Problem:** Kompression wird nicht ausgelöst
|
||||||
|
- **Prüfung:** `compression_threshold` in Stream-Konfiguration gesetzt?
|
||||||
|
- **Prüfung:** `compression_profile` existiert in `llm_profiles.yaml`?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
Auszug aus der decision_engine.yaml
|
Auszug aus der decision_engine.yaml
|
||||||
```yaml
|
```yaml
|
||||||
strategies:
|
strategies:
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
---
|
---
|
||||||
doc_type: technical_reference
|
doc_type: technical_reference
|
||||||
audience: developer, devops
|
audience: developer, devops
|
||||||
scope: backend, ingestion, smart_edges, edge_registry, modularization, moe
|
scope: backend, ingestion, smart_edges, edge_registry, modularization, moe, lazy_prompts
|
||||||
status: active
|
status: active
|
||||||
version: 2.14.0
|
version: 2.14.0
|
||||||
context: "Detaillierte technische Beschreibung der Import-Pipeline, Two-Pass-Workflow (WP-15b), modularer Datenbank-Architektur (WP-14) und WP-25a profilgesteuerte Validierung. Integriert Mistral-safe Parsing und Deep Fallback."
|
context: "Detaillierte technische Beschreibung der Import-Pipeline, Two-Pass-Workflow (WP-15b), modularer Datenbank-Architektur (WP-14), WP-25a profilgesteuerte Validierung und WP-25b Lazy-Prompt-Orchestration. Integriert Mistral-safe Parsing und Deep Fallback."
|
||||||
---
|
---
|
||||||
|
|
||||||
# Ingestion Pipeline & Smart Processing
|
# Ingestion Pipeline & Smart Processing
|
||||||
|
|
@ -50,10 +50,15 @@ Der Prozess ist **asynchron**, **idempotent** und wird nun in zwei logische Durc
|
||||||
* Bei Änderungen löscht `purge_artifacts()` via `app.core.ingestion.ingestion_db` alle alten Chunks und Edges der Note.
|
* Bei Änderungen löscht `purge_artifacts()` via `app.core.ingestion.ingestion_db` alle alten Chunks und Edges der Note.
|
||||||
* Die Namensauflösung erfolgt nun über das modularisierte `database`-Paket.
|
* Die Namensauflösung erfolgt nun über das modularisierte `database`-Paket.
|
||||||
10. **Chunking anwenden:** Zerlegung des Textes basierend auf dem ermittelten Profil (siehe Kap. 3).
|
10. **Chunking anwenden:** Zerlegung des Textes basierend auf dem ermittelten Profil (siehe Kap. 3).
|
||||||
11. **Smart Edge Allocation & Semantic Validation (WP-15b / WP-25a):**
|
11. **Smart Edge Allocation & Semantic Validation (WP-15b / WP-25a / WP-25b):**
|
||||||
* Der `SemanticAnalyzer` schlägt Kanten-Kandidaten vor.
|
* Der `SemanticAnalyzer` schlägt Kanten-Kandidaten vor.
|
||||||
* **Validierung (WP-25a):** Jeder Kandidat wird durch das LLM semantisch gegen das Ziel im **LocalBatchCache** geprüft.
|
* **Validierung (WP-25a/25b):** Jeder Kandidat wird durch das LLM semantisch gegen das Ziel im **LocalBatchCache** geprüft.
|
||||||
* **Profilgesteuerte Validierung:** Nutzt das MoE-Profil `ingest_validator` (Temperature 0.0 für maximale Determinismus).
|
* **Profilgesteuerte Validierung:** Nutzt das MoE-Profil `ingest_validator` (Temperature 0.0 für maximale Determinismus).
|
||||||
|
* **Lazy-Prompt-Loading (WP-25b):** Nutzt `prompt_key="edge_validation"` mit `variables` statt vorformatierter Strings.
|
||||||
|
* **Hierarchische Resolution:** Level 1 (Modell-ID) → Level 2 (Provider) → Level 3 (Default)
|
||||||
|
* **Differenzierte Fehlerbehandlung (WP-25b):** Unterscheidung zwischen transienten (Netzwerk) und permanenten (Config) Fehlern:
|
||||||
|
* **Transiente Fehler:** Timeout, Connection, Network → Kante wird erlaubt (Datenverlust vermeiden)
|
||||||
|
* **Permanente Fehler:** Config, Validation, Invalid Response → Kante wird abgelehnt (Graph-Qualität schützen)
|
||||||
* **Fallback-Kaskade:** Bei Fehlern erfolgt automatischer Fallback via `fallback_profile` (z.B. `compression_fast` → `identity_safe`).
|
* **Fallback-Kaskade:** Bei Fehlern erfolgt automatischer Fallback via `fallback_profile` (z.B. `compression_fast` → `identity_safe`).
|
||||||
* **Traffic Control:** Nutzung der neutralen `clean_llm_text` Funktion zur Bereinigung von Steuerzeichen (<s>, [OUT]).
|
* **Traffic Control:** Nutzung der neutralen `clean_llm_text` Funktion zur Bereinigung von Steuerzeichen (<s>, [OUT]).
|
||||||
* **Deep Fallback (v2.11.14):** Erkennt "Silent Refusals". Liefert die Cloud keine verwertbaren Kanten, wird ein lokaler Fallback via Ollama erzwungen.
|
* **Deep Fallback (v2.11.14):** Erkennt "Silent Refusals". Liefert die Cloud keine verwertbaren Kanten, wird ein lokaler Fallback via Ollama erzwungen.
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
---
|
---
|
||||||
doc_type: operations_manual
|
doc_type: operations_manual
|
||||||
audience: admin, devops
|
audience: admin, devops
|
||||||
scope: deployment, maintenance, backup, edge_registry, moe
|
scope: deployment, maintenance, backup, edge_registry, moe, lazy_prompts
|
||||||
status: active
|
status: active
|
||||||
version: 3.0.0
|
version: 3.1.1
|
||||||
context: "Installationsanleitung, Systemd-Units und Wartungsprozesse für Mindnet v3.0.0 inklusive WP-25a Mixture of Experts (MoE) Konfiguration."
|
context: "Installationsanleitung, Systemd-Units und Wartungsprozesse für Mindnet v3.1.1 inklusive WP-25a Mixture of Experts (MoE) und WP-25b Lazy-Prompt-Orchestration Konfiguration."
|
||||||
---
|
---
|
||||||
|
|
||||||
# Admin Operations Guide
|
# Admin Operations Guide
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
---
|
---
|
||||||
doc_type: developer_guide
|
doc_type: developer_guide
|
||||||
audience: developer
|
audience: developer
|
||||||
scope: workflow, testing, architecture, modules, modularization, agentic_rag
|
scope: workflow, testing, architecture, modules, modularization, agentic_rag, lazy_prompts
|
||||||
status: active
|
status: active
|
||||||
version: 2.9.3
|
version: 3.1.1
|
||||||
context: "Umfassender Guide für Entwickler: Modularisierte Architektur (WP-14), Two-Pass Ingestion (WP-15b), WP-25 Agentic Multi-Stream RAG, Modul-Interna, Setup und Git-Workflow."
|
context: "Umfassender Guide für Entwickler: Modularisierte Architektur (WP-14), Two-Pass Ingestion (WP-15b), WP-25 Agentic Multi-Stream RAG, WP-25a MoE, WP-25b Lazy-Prompt-Orchestration, Modul-Interna, Setup und Git-Workflow."
|
||||||
---
|
---
|
||||||
|
|
||||||
# Mindnet Developer Guide & Workflow
|
# Mindnet Developer Guide & Workflow
|
||||||
|
|
@ -406,12 +406,34 @@ Mindnet lernt nicht durch Training (Fine-Tuning), sondern durch **Konfiguration*
|
||||||
```yaml
|
```yaml
|
||||||
synthesis_pro:
|
synthesis_pro:
|
||||||
provider: "openrouter"
|
provider: "openrouter"
|
||||||
model: "gemini-1.5-mistralai/mistral-7b-instruct:free"
|
model: "google/gemini-2.0-flash-exp:free"
|
||||||
temperature: 0.7
|
temperature: 0.7
|
||||||
fallback_profile: "synthesis_backup"
|
fallback_profile: "synthesis_backup"
|
||||||
```
|
```
|
||||||
*Ergebnis (WP-25a):* Zentrale Steuerung von Provider, Modell und Temperature pro Aufgabe. Automatische Fallback-Kaskade bei Fehlern.
|
*Ergebnis (WP-25a):* Zentrale Steuerung von Provider, Modell und Temperature pro Aufgabe. Automatische Fallback-Kaskade bei Fehlern.
|
||||||
|
|
||||||
|
4. **Prompt-Template (`config/prompts.yaml` v3.2.2, WP-25b):**
|
||||||
|
```yaml
|
||||||
|
decision_synthesis_v1:
|
||||||
|
# Level 1: Modell-spezifisch (höchste Priorität)
|
||||||
|
"google/gemini-2.0-flash-exp:free": |
|
||||||
|
WERTE & PRINZIPIEN (Identität):
|
||||||
|
{values_stream}
|
||||||
|
...
|
||||||
|
|
||||||
|
# Level 2: Provider-Fallback
|
||||||
|
openrouter: |
|
||||||
|
WERTE & PRINZIPIEN (Identität):
|
||||||
|
{values_stream}
|
||||||
|
...
|
||||||
|
|
||||||
|
# Level 3: Global Default
|
||||||
|
default: |
|
||||||
|
Synthetisiere die folgenden Informationen für: {query}
|
||||||
|
...
|
||||||
|
```
|
||||||
|
*Ergebnis (WP-25b):* Hierarchische Prompt-Resolution mit Lazy-Loading. Prompts werden erst zur Laufzeit geladen, basierend auf aktivem Modell. Maximale Resilienz bei Modell-Fallbacks.
|
||||||
|
|
||||||
### Workflow B: Graph-Farben ändern
|
### Workflow B: Graph-Farben ändern
|
||||||
1. Öffne `app/frontend/ui_config.py`.
|
1. Öffne `app/frontend/ui_config.py`.
|
||||||
2. Bearbeite das Dictionary `GRAPH_COLORS`.
|
2. Bearbeite das Dictionary `GRAPH_COLORS`.
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,14 @@
|
||||||
doc_type: roadmap
|
doc_type: roadmap
|
||||||
audience: product_owner, developer
|
audience: product_owner, developer
|
||||||
status: active
|
status: active
|
||||||
version: 2.9.3
|
version: 3.1.1
|
||||||
context: "Aktuelle Planung für kommende Features (ab WP16), Release-Strategie und Historie der abgeschlossenen WPs nach WP-14/15b/15c/25."
|
context: "Aktuelle Planung für kommende Features (ab WP16), Release-Strategie und Historie der abgeschlossenen WPs nach WP-14/15b/15c/25/25a/25b."
|
||||||
---
|
---
|
||||||
|
|
||||||
# Mindnet Active Roadmap
|
# Mindnet Active Roadmap
|
||||||
|
|
||||||
**Aktueller Stand:** v2.9.3 (Post-WP25: Agentic Multi-Stream RAG)
|
**Aktueller Stand:** v3.1.1 (Post-WP25b: Lazy-Prompt-Orchestration & Full Resilience)
|
||||||
**Fokus:** Agentic Orchestration, Multi-Stream Retrieval & Wissens-Synthese.
|
**Fokus:** Hierarchische Prompt-Resolution, Modell-spezifisches Tuning & maximale Resilienz.
|
||||||
|
|
||||||
| Phase | Fokus | Status |
|
| Phase | Fokus | Status |
|
||||||
| :--- | :--- | :--- |
|
| :--- | :--- | :--- |
|
||||||
|
|
@ -50,6 +50,8 @@ Eine Übersicht der implementierten Features zum schnellen Auffinden von Funktio
|
||||||
| **WP-22** | **Content Lifecycle & Registry** | **Ergebnis:** SSOT via `01_edge_vocabulary.md`, Alias-Mapping, Status-Scoring (`stable`/`draft`) und Modularisierung der Scoring-Engine. |
|
| **WP-22** | **Content Lifecycle & Registry** | **Ergebnis:** SSOT via `01_edge_vocabulary.md`, Alias-Mapping, Status-Scoring (`stable`/`draft`) und Modularisierung der Scoring-Engine. |
|
||||||
| **WP-15c** | **Multigraph-Support & Diversity Engine** | **Ergebnis:** Section-basierte Links, Note-Level Diversity Pooling, Super-Edge Aggregation, Provenance Firewall. Transformation zu einem hochpräzisen Multigraphen. |
|
| **WP-15c** | **Multigraph-Support & Diversity Engine** | **Ergebnis:** Section-basierte Links, Note-Level Diversity Pooling, Super-Edge Aggregation, Provenance Firewall. Transformation zu einem hochpräzisen Multigraphen. |
|
||||||
| **WP-25** | **Agentic Multi-Stream RAG Orchestration** | **Ergebnis:** Übergang von linearer RAG-Architektur zu paralleler Multi-Stream Engine. Intent-basiertes Routing (Hybrid Fast/Slow-Path), parallele Wissens-Streams (Values, Facts, Biography, Risk, Tech), Stream-Tracing und Template-basierte Wissens-Synthese. |
|
| **WP-25** | **Agentic Multi-Stream RAG Orchestration** | **Ergebnis:** Übergang von linearer RAG-Architektur zu paralleler Multi-Stream Engine. Intent-basiertes Routing (Hybrid Fast/Slow-Path), parallele Wissens-Streams (Values, Facts, Biography, Risk, Tech), Stream-Tracing und Template-basierte Wissens-Synthese. |
|
||||||
|
| **WP-25a** | **Mixture of Experts (MoE) & Fallback-Kaskade** | **Ergebnis:** Profilbasierte Experten-Architektur, rekursive Fallback-Kaskade, Pre-Synthesis Kompression, profilgesteuerte Ingestion und Embedding-Konsolidierung. |
|
||||||
|
| **WP-25b** | **Lazy-Prompt-Orchestration & Full Resilience** | **Ergebnis:** Hierarchisches Prompt-Resolution-System (3-stufig), Lazy-Prompt-Loading, ultra-robustes Intent-Parsing, differenzierte Ingestion-Validierung und PROMPT-TRACE Logging. |
|
||||||
|
|
||||||
### 2.1 WP-22 Lessons Learned
|
### 2.1 WP-22 Lessons Learned
|
||||||
* **Architektur:** Die Trennung von `retriever.py` und `retriever_scoring.py` war notwendig, um LLM-Context-Limits zu wahren und die Testbarkeit der mathematischen Formeln zu erhöhen.
|
* **Architektur:** Die Trennung von `retriever.py` und `retriever_scoring.py` war notwendig, um LLM-Context-Limits zu wahren und die Testbarkeit der mathematischen Formeln zu erhöhen.
|
||||||
|
|
@ -197,25 +199,9 @@ Der bisherige WP-15 Ansatz litt unter Halluzinationen (erfundene Kantentypen), h
|
||||||
**Status:** ✅ Fertig (v3.0.0)
|
**Status:** ✅ Fertig (v3.0.0)
|
||||||
|
|
||||||
### WP-25a: Mixture of Experts (MoE) & Fallback-Kaskade
|
### WP-25a: Mixture of Experts (MoE) & Fallback-Kaskade
|
||||||
**Status:** ✅ Fertig (v3.0.0)
|
**Status:** ✅ Fertig (v3.1.0)
|
||||||
|
|
||||||
**Ergebnis:** Transformation von Mindnet von einer klassischen, linearen RAG-Architektur zu einer **Agentic Multi-Stream Engine**. Das System agiert nun als intelligenter Orchestrator, der Nutzeranfragen analysiert, in parallele Wissens-Streams aufteilt und diese zu einer kontextreichen, wertebasierten Antwort synthetisiert.
|
**Ergebnis:** Transformation von MindNet von einer provider-basierten Steuerung auf eine **profilbasierte Experten-Architektur (Mixture of Experts)**. Jede Systemaufgabe wird einem dedizierten Profil zugewiesen, das Modell, Provider und Parameter unabhängig definiert.
|
||||||
|
|
||||||
**Kern-Features:**
|
|
||||||
1. **Intent-basiertes Routing:** Hybrid-Modus mit Keyword Fast-Path und LLM Slow-Path
|
|
||||||
2. **Multi-Stream Retrieval:** Parallele Abfragen in spezialisierten Streams (Values, Facts, Biography, Risk, Tech)
|
|
||||||
3. **Stream-Tracing:** Jeder Treffer wird mit `stream_origin` markiert
|
|
||||||
4. **Wissens-Synthese:** Template-basierte Zusammenführung mit expliziten Stream-Variablen
|
|
||||||
5. **Fehler-Resilienz:** Einzelne Stream-Fehler blockieren nicht die gesamte Anfrage
|
|
||||||
|
|
||||||
**Technische Details:**
|
|
||||||
- Decision Engine v1.0.3: Multi-Stream Orchestrator
|
|
||||||
- Chat Router v3.0.2: Hybrid Router Integration
|
|
||||||
- LLM Service v3.4.2: Ingest-Stability Patch
|
|
||||||
- decision_engine.yaml v3.1.6: Multi-Stream Konfiguration
|
|
||||||
- prompts.yaml v3.1.2: Stream-Templates
|
|
||||||
|
|
||||||
**Ergebnis (WP-25a):** Transformation von MindNet von einer provider-basierten Steuerung auf eine **profilbasierte Experten-Architektur (Mixture of Experts)**. Jede Systemaufgabe wird einem dedizierten Profil zugewiesen, das Modell, Provider und Parameter unabhängig definiert.
|
|
||||||
|
|
||||||
**Kern-Features:**
|
**Kern-Features:**
|
||||||
1. **Experten-Steuerung:** Zentrale Profile-Registry (`llm_profiles.yaml`) für alle LLM-Aufgaben
|
1. **Experten-Steuerung:** Zentrale Profile-Registry (`llm_profiles.yaml`) für alle LLM-Aufgaben
|
||||||
|
|
@ -233,10 +219,28 @@ Der bisherige WP-15 Ansatz litt unter Halluzinationen (erfundene Kantentypen), h
|
||||||
- llm_profiles.yaml v1.3.0: Zentrale Experten-Registry
|
- llm_profiles.yaml v1.3.0: Zentrale Experten-Registry
|
||||||
- decision_engine.yaml v3.2.2: Decoupled MoE Logic
|
- decision_engine.yaml v3.2.2: Decoupled MoE Logic
|
||||||
|
|
||||||
**Ausblick (WP-25b):**
|
### WP-25b: Lazy-Prompt-Orchestration & Full Resilience
|
||||||
- Prompt-Orchestration & Model-Specific Tuning
|
**Status:** ✅ Fertig (v3.1.1)
|
||||||
|
|
||||||
|
**Ergebnis:** Umstellung von statischer Prompt-Formatierung auf eine **hierarchische Lazy-Prompt-Orchestration**. Prompts werden erst im Moment des Modellaustauschs geladen, basierend auf dem exakt aktiven Modell. Dies ermöglicht modell-spezifisches Tuning und maximale Resilienz bei Modell-Fallbacks.
|
||||||
|
|
||||||
|
**Kern-Features:**
|
||||||
|
1. **Hierarchisches Prompt-Resolution-System:** Dreistufige Auflösung (Modell-ID → Provider → Default)
|
||||||
|
2. **Lazy-Loading:** Prompts werden erst zur Laufzeit geladen, wenn das aktive Modell bekannt ist
|
||||||
|
3. **Ultra-robustes Intent-Parsing:** Regex-basierter Parser bereinigt Modell-Artefakte (z.B. `CODING[/S]` → `CODING`)
|
||||||
|
4. **Differenzierte Ingestion-Validierung:** Unterscheidung zwischen transienten (Netzwerk) und permanenten (Config) Fehlern
|
||||||
|
5. **PROMPT-TRACE Logging:** Vollständige Transparenz über genutzte Instruktionen
|
||||||
|
|
||||||
|
**Technische Details:**
|
||||||
|
- LLM Service v3.5.5: Hierarchische Prompt-Resolution mit Lazy-Loading
|
||||||
|
- Decision Engine v1.3.2: Ultra-robustes Intent-Parsing via Regex
|
||||||
|
- Ingestion Validation v2.14.0: Lazy-Prompt-Integration, differenzierte Fehlerbehandlung
|
||||||
|
- prompts.yaml v3.2.2: Hierarchische Struktur mit Modell-spezifischen Overrides
|
||||||
|
|
||||||
|
**Ausblick (WP-25c):**
|
||||||
- Kontext-Budgeting: Intelligente Token-Verteilung
|
- Kontext-Budgeting: Intelligente Token-Verteilung
|
||||||
- Stream-specific Provider: Unterschiedliche KI-Modelle pro Wissensbereich
|
- Stream-specific Provider: Unterschiedliche KI-Modelle pro Wissensbereich
|
||||||
|
- Erweiterte Prompt-Optimierung: Dynamische Anpassung basierend auf Kontext und Historie
|
||||||
---
|
---
|
||||||
|
|
||||||
### WP-24 – Proactive Discovery & Agentic Knowledge Mining
|
### WP-24 – Proactive Discovery & Agentic Knowledge Mining
|
||||||
|
|
|
||||||
97
docs/99_Archive/WP25b_merge_commit.md
Normal file
97
docs/99_Archive/WP25b_merge_commit.md
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
# Branch Merge Commit: WP-25b
|
||||||
|
|
||||||
|
**Branch:** `WP25b`
|
||||||
|
**Target:** `main`
|
||||||
|
**Version:** v3.1.1
|
||||||
|
**Date:** 2026-01-02
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Commit Message
|
||||||
|
|
||||||
|
```
|
||||||
|
feat: Lazy-Prompt-Orchestration & Full Resilience (v3.1.1)
|
||||||
|
|
||||||
|
### Hierarchisches Prompt-Resolution-System
|
||||||
|
- Dreistufige Auflösungs-Logik: Level 1 (Modell-ID) → Level 2 (Provider) → Level 3 (Default)
|
||||||
|
- Modell-spezifische Optimierungen für Gemini 2.0, Llama 3.3, Qwen 2.5
|
||||||
|
- PROMPT-TRACE Logging für vollständige Transparenz
|
||||||
|
- Implementierung in `app/services/llm_service.py` (v3.5.5)
|
||||||
|
|
||||||
|
### Lazy-Prompt-Orchestration
|
||||||
|
- Prompts werden erst zur Laufzeit geladen, basierend auf aktivem Modell
|
||||||
|
- Parameter: `prompt_key` und `variables` statt vorformatierter Strings
|
||||||
|
- Maximale Resilienz bei Modell-Fallbacks (Cloud → Local)
|
||||||
|
- Vollständige Integration in Chat, Ingestion und DecisionEngine
|
||||||
|
|
||||||
|
### Ultra-robustes Intent-Parsing
|
||||||
|
- Regex-basierter Parser bereinigt Modell-Artefakte (z.B. `CODING[/S]` → `CODING`)
|
||||||
|
- Implementierung in `app/core/retrieval/decision_engine.py` (v1.3.2)
|
||||||
|
- Fehlerresistenz gegen Stop-Marker, Newlines oder Modell-Plaudereien
|
||||||
|
|
||||||
|
### Differenzierte Ingestion-Validierung
|
||||||
|
- Unterscheidung zwischen transienten (Netzwerk) und permanenten (Config) Fehlern
|
||||||
|
- Transiente Fehler erlauben Kante (Datenverlust vermeiden)
|
||||||
|
- Permanente Fehler lehnen Kante ab (Graph-Qualität schützen)
|
||||||
|
- Implementierung in `app/core/ingestion/ingestion_validation.py` (v2.14.0)
|
||||||
|
|
||||||
|
### Code-Komponenten
|
||||||
|
- `app/services/llm_service.py`: v3.5.5 (Hierarchische Prompt-Resolution, Lazy-Loading)
|
||||||
|
- `app/core/retrieval/decision_engine.py`: v1.3.2 (Ultra-robustes Intent-Parsing)
|
||||||
|
- `app/core/ingestion/ingestion_validation.py`: v2.14.0 (Lazy-Prompt-Integration)
|
||||||
|
- `app/routers/chat.py`: v3.0.3 (Lazy-Prompt-Loading für Chat-Synthese)
|
||||||
|
|
||||||
|
### Konfiguration
|
||||||
|
- `config/prompts.yaml`: v3.2.2 (Hierarchische Struktur mit Modell-spezifischen Overrides)
|
||||||
|
- 100% Erhalt der Original-Prompts aus v3.1.2 für Provider-Ebene
|
||||||
|
- Integration von Modell-spezifischen Overrides
|
||||||
|
- Hinzufügen von `compression_template`
|
||||||
|
|
||||||
|
### Dokumentation
|
||||||
|
- `03_tech_chat_backend.md`: Hierarchisches Prompt-Resolution-System
|
||||||
|
- `03_tech_configuration.md`: prompts.yaml hierarchische Struktur
|
||||||
|
- `02_concept_ai_personality.md`: Lazy-Prompt-Orchestration Konzept
|
||||||
|
- `03_tech_ingestion_pipeline.md`: Differenzierte Validierung
|
||||||
|
- `00_glossary.md`: Neue Begriffe (Lazy-Prompt, PROMPT-TRACE)
|
||||||
|
- `05_developer_guide.md`: Lazy-Prompt-Orchestration für Entwickler
|
||||||
|
- `06_active_roadmap.md`: WP25b als abgeschlossen markiert
|
||||||
|
|
||||||
|
### Breaking Changes
|
||||||
|
- Keine Breaking Changes für Endbenutzer
|
||||||
|
- Vorformatierte Prompts werden weiterhin unterstützt (Abwärtskompatibilität)
|
||||||
|
- Neue API-Parameter `prompt_key` und `variables` optional
|
||||||
|
|
||||||
|
### Migration
|
||||||
|
- Keine Migration erforderlich
|
||||||
|
- System funktioniert ohne Änderungen
|
||||||
|
- Optional: Modell-spezifische Prompts können in `prompts.yaml` definiert werden
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Status:** ✅ WP-25b ist zu 100% implementiert und audit-geprüft.
|
||||||
|
**Nächster Schritt:** WP-25c (Kontext-Budgeting & Erweiterte Prompt-Optimierung).
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Zusammenfassung
|
||||||
|
|
||||||
|
Dieser Merge führt die **Lazy-Prompt-Orchestration** in MindNet ein. Das System nutzt nun eine hierarchische Prompt-Auflösung mit Lazy-Loading, die Prompts erst zur Laufzeit lädt, basierend auf dem exakt aktiven Modell.
|
||||||
|
|
||||||
|
**Kern-Features:**
|
||||||
|
- Hierarchisches Prompt-Resolution-System (3-stufig)
|
||||||
|
- Lazy-Prompt-Orchestration mit modell-spezifischem Tuning
|
||||||
|
- Ultra-robustes Intent-Parsing via Regex
|
||||||
|
- Differenzierte Ingestion-Validierung
|
||||||
|
- PROMPT-TRACE Logging für vollständige Transparenz
|
||||||
|
|
||||||
|
**Technische Integrität:**
|
||||||
|
- Alle LLM-Aufrufe nutzen nun Lazy-Prompt-Loading
|
||||||
|
- Modell-Artefakte werden zuverlässig bereinigt
|
||||||
|
- Fehlerbehandlung differenziert zwischen transienten und permanenten Fehlern
|
||||||
|
|
||||||
|
**Dokumentation:**
|
||||||
|
- Vollständige Aktualisierung aller relevanten Dokumente
|
||||||
|
- Neue Begriffe im Glossar
|
||||||
|
- Konfigurations-Referenz erweitert
|
||||||
|
- Developer Guide aktualisiert
|
||||||
205
docs/99_Archive/WP25b_release_notes.md
Normal file
205
docs/99_Archive/WP25b_release_notes.md
Normal file
|
|
@ -0,0 +1,205 @@
|
||||||
|
# MindNet v3.1.1 - Release Notes: WP-25b
|
||||||
|
|
||||||
|
**Release Date:** 2026-01-02
|
||||||
|
**Type:** Feature Release - Lazy-Prompt-Orchestration & Full Resilience
|
||||||
|
**Version:** 3.1.1 (WP-25b)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Überblick
|
||||||
|
|
||||||
|
Mit WP-25b wurde MindNet von statischer Prompt-Formatierung auf eine **hierarchische Lazy-Prompt-Orchestration** umgestellt. Prompts werden erst im Moment des Modellaustauschs geladen, basierend auf dem exakt aktiven Modell. Dies ermöglicht modell-spezifisches Tuning und maximale Resilienz bei Modell-Fallbacks.
|
||||||
|
|
||||||
|
Diese Version markiert einen weiteren Architektur-Sprung: Von vorformatierter Prompt-Strings hin zu einer dynamischen, modell-spezifischen Prompt-Auflösung mit vollständiger Traceability.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✨ Neue Features
|
||||||
|
|
||||||
|
### 1. Hierarchisches Prompt-Resolution-System
|
||||||
|
|
||||||
|
**Implementierung (`app/services/llm_service.py` v3.5.5):**
|
||||||
|
|
||||||
|
Dreistufige Auflösungs-Logik für maximale Präzision und Resilienz:
|
||||||
|
|
||||||
|
1. **Level 1 (Modell-ID):** Exakte Übereinstimmung für spezifische Modelle
|
||||||
|
* **Beispiel:** `google/gemini-2.0-flash-exp:free`, `meta-llama/llama-3.3-70b-instruct:free`
|
||||||
|
* **Vorteil:** Modell-spezifische Optimierungen für maximale Präzision
|
||||||
|
* **Logging:** `🎯 [PROMPT-TRACE] Level 1 Match: Model-specific`
|
||||||
|
|
||||||
|
2. **Level 2 (Provider):** Fallback auf Provider-Standards
|
||||||
|
* **Beispiel:** `openrouter`, `ollama`, `gemini`
|
||||||
|
* **Vorteil:** Bewährte Standards aus v3.1.2 bleiben erhalten
|
||||||
|
* **Logging:** `📡 [PROMPT-TRACE] Level 2 Match: Provider-fallback`
|
||||||
|
|
||||||
|
3. **Level 3 (Default):** Globaler Sicherheits-Satz
|
||||||
|
* **Fallback-Kette:** `default` → `gemini` → `ollama` → `""`
|
||||||
|
* **Vorteil:** Vermeidung von Fehlern bei unbekannten Konfigurationen
|
||||||
|
* **Logging:** `⚓ [PROMPT-TRACE] Level 3 Match: Global Default`
|
||||||
|
|
||||||
|
**Vorteile:**
|
||||||
|
* **Modell-spezifisches Tuning:** Jedes Modell kann optimierte Prompts erhalten
|
||||||
|
* **Maximale Resilienz:** Bei Modell-Fallbacks wird automatisch das passende Template geladen
|
||||||
|
* **Traceability:** Vollständige Transparenz über genutzte Instruktionen
|
||||||
|
|
||||||
|
### 2. Lazy-Prompt-Orchestration
|
||||||
|
|
||||||
|
**Implementierung (`app/services/llm_service.py` v3.5.5):**
|
||||||
|
|
||||||
|
* **Lazy Loading:** Prompts werden erst zur Laufzeit geladen, wenn das aktive Modell bekannt ist
|
||||||
|
* **Parameter:** `prompt_key` und `variables` statt vorformatierter Strings
|
||||||
|
* **Integration:** Vollständig in Chat, Ingestion und DecisionEngine integriert
|
||||||
|
|
||||||
|
**Vorteile:**
|
||||||
|
* **Dynamische Anpassung:** Prompt wird basierend auf aktivem Modell geladen
|
||||||
|
* **Fallback-Resilienz:** Bei Cloud → Local Fallback wird automatisch das passende Template verwendet
|
||||||
|
* **Wartbarkeit:** Zentrale Konfiguration in `prompts.yaml` statt verstreuter String-Formatierungen
|
||||||
|
|
||||||
|
### 3. Ultra-robustes Intent-Parsing
|
||||||
|
|
||||||
|
**Implementierung (`app/core/retrieval/decision_engine.py` v1.3.2):**
|
||||||
|
|
||||||
|
* **Regex-basierter Parser:** Bereinigt Modell-Artefakte zuverlässig
|
||||||
|
* **Beispiele:** `CODING[/S]` → `CODING`, `DECISION</s>` → `DECISION`
|
||||||
|
* **Robustheit:** Ignoriert Stop-Marker, Newlines oder Plaudereien des Modells
|
||||||
|
|
||||||
|
**Vorteile:**
|
||||||
|
* **Präzises Routing:** Strategie-Erkennung funktioniert auch bei freien Modellen mit Artefakten
|
||||||
|
* **Fehlerresistenz:** Systemabstürze durch fehlerhafte Modell-Antworten werden verhindert
|
||||||
|
|
||||||
|
### 4. Differenzierte Ingestion-Validierung
|
||||||
|
|
||||||
|
**Implementierung (`app/core/ingestion/ingestion_validation.py` v2.14.0):**
|
||||||
|
|
||||||
|
* **Fehler-Differenzierung:** Unterscheidung zwischen transienten und permanenten Fehlern
|
||||||
|
* **Transiente Fehler:** Timeout, Connection, Network → Kante wird erlaubt (Datenverlust vermeiden)
|
||||||
|
* **Permanente Fehler:** Config, Validation, Invalid Response → Kante wird abgelehnt (Graph-Qualität schützen)
|
||||||
|
|
||||||
|
**Vorteile:**
|
||||||
|
* **Datenintegrität:** Transiente Netzwerkfehler führen nicht zu Datenverlust
|
||||||
|
* **Graph-Qualität:** Permanente Konfigurationsfehler schützen vor fehlerhaften Kanten
|
||||||
|
|
||||||
|
### 5. PROMPT-TRACE Logging
|
||||||
|
|
||||||
|
**Implementierung (`app/services/llm_service.py` v3.5.5):**
|
||||||
|
|
||||||
|
* **Vollständige Transparenz:** Protokollierung der genutzten Prompt-Auflösungs-Ebene
|
||||||
|
* **Log-Format:** `[PROMPT-TRACE] Level X Match: ...`
|
||||||
|
* **Debugging:** Einfache Nachverfolgung von Prompt-Entscheidungen
|
||||||
|
|
||||||
|
**Vorteile:**
|
||||||
|
* **Debugging:** Schnelle Identifikation von Prompt-Problemen
|
||||||
|
* **Optimierung:** Verständnis, welche Prompts tatsächlich genutzt werden
|
||||||
|
* **Audit:** Vollständige Nachvollziehbarkeit der System-Entscheidungen
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Technische Änderungen
|
||||||
|
|
||||||
|
### Konfigurationsdateien
|
||||||
|
|
||||||
|
**Aktualisierte Dateien:**
|
||||||
|
* `config/prompts.yaml` v3.2.2: Hierarchische Struktur mit Modell-spezifischen Overrides
|
||||||
|
* **Erhalt:** 100% der Original-Prompts aus v3.1.2 für die Provider-Ebene
|
||||||
|
* **Neu:** Modell-spezifische Overrides für Gemini 2.0, Llama 3.3, Qwen 2.5
|
||||||
|
* **Neu:** `compression_template` für DecisionEngine v1.3.0
|
||||||
|
|
||||||
|
### Code-Komponenten
|
||||||
|
|
||||||
|
| Komponente | Version | Änderungen |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| `app/services/llm_service.py` | v3.5.5 | Hierarchische Prompt-Resolution, Lazy-Loading, PROMPT-TRACE |
|
||||||
|
| `app/core/retrieval/decision_engine.py` | v1.3.2 | Ultra-robustes Intent-Parsing via Regex |
|
||||||
|
| `app/core/ingestion/ingestion_validation.py` | v2.14.0 | Lazy-Prompt-Integration, differenzierte Fehlerbehandlung |
|
||||||
|
| `app/routers/chat.py` | v3.0.3 | Lazy-Prompt-Loading für Chat-Synthese |
|
||||||
|
|
||||||
|
### API-Änderungen
|
||||||
|
|
||||||
|
**Neue Parameter:**
|
||||||
|
* `prompt_key`: Schlüssel für Lazy-Loading (statt vorformatierter Strings)
|
||||||
|
* `variables`: Daten-Dict für Prompt-Formatierung
|
||||||
|
|
||||||
|
**Veraltete Parameter:**
|
||||||
|
* Vorformatierte `prompt` Strings werden weiterhin unterstützt (Abwärtskompatibilität)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐛 Behobene Probleme
|
||||||
|
|
||||||
|
- ✅ **Behoben:** Modell-Artefakte in Intent-Router (z.B. `CODING[/S]` → `CODING`)
|
||||||
|
- ✅ **Behoben:** Fehlende modell-spezifische Prompt-Optimierungen
|
||||||
|
- ✅ **Behoben:** Fehlerhafte Prompt-Auflösung bei Modell-Fallbacks
|
||||||
|
- ✅ **Behoben:** Undifferenzierte Fehlerbehandlung in Ingestion-Validierung
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 Dokumentation
|
||||||
|
|
||||||
|
**Aktualisierte Dokumente:**
|
||||||
|
- ✅ `03_tech_chat_backend.md`: Hierarchisches Prompt-Resolution-System und Lazy-Prompt-Orchestration
|
||||||
|
- ✅ `03_tech_configuration.md`: prompts.yaml hierarchische Struktur dokumentiert
|
||||||
|
- ✅ `02_concept_ai_personality.md`: Lazy-Prompt-Orchestration Konzept
|
||||||
|
- ✅ `03_tech_ingestion_pipeline.md`: Differenzierte Validierung
|
||||||
|
- ✅ `00_glossary.md`: Neue Begriffe (Lazy-Prompt, PROMPT-TRACE, hierarchische Resolution)
|
||||||
|
- ✅ `05_developer_guide.md`: Lazy-Prompt-Orchestration für Entwickler
|
||||||
|
- ✅ `06_active_roadmap.md`: WP25b als abgeschlossen markiert
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Migration & Upgrade
|
||||||
|
|
||||||
|
### Für Administratoren
|
||||||
|
|
||||||
|
1. **Keine Breaking Changes:**
|
||||||
|
* Vorformatierte Prompts werden weiterhin unterstützt
|
||||||
|
* System funktioniert ohne Änderungen
|
||||||
|
|
||||||
|
2. **Optional: Modell-spezifische Optimierungen:**
|
||||||
|
```yaml
|
||||||
|
# config/prompts.yaml
|
||||||
|
decision_synthesis_v1:
|
||||||
|
"google/gemini-2.0-flash-exp:free": |
|
||||||
|
# Modell-spezifische Optimierung
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **PROMPT-TRACE aktivieren:**
|
||||||
|
* Logs zeigen automatisch die genutzte Auflösungs-Ebene
|
||||||
|
* Keine zusätzliche Konfiguration erforderlich
|
||||||
|
|
||||||
|
### Für Entwickler
|
||||||
|
|
||||||
|
**API-Änderungen:**
|
||||||
|
* `LLMService.generate_raw_response()` unterstützt nun `prompt_key` und `variables`
|
||||||
|
* Vorformatierte `prompt` Strings bleiben für Abwärtskompatibilität erhalten
|
||||||
|
|
||||||
|
**Best Practice:**
|
||||||
|
* Nutze `prompt_key` und `variables` für neue Implementierungen
|
||||||
|
* Lazy-Loading ermöglicht automatische Modell-Anpassung
|
||||||
|
|
||||||
|
**Konfiguration:**
|
||||||
|
* Neue Modell-spezifische Prompts können in `prompts.yaml` definiert werden
|
||||||
|
* Hierarchische Struktur: Modell-ID → Provider → Default
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔮 Ausblick (WP-25c)
|
||||||
|
|
||||||
|
- Kontext-Budgeting: Intelligente Token-Verteilung
|
||||||
|
- Stream-specific Provider: Unterschiedliche KI-Modelle pro Wissensbereich
|
||||||
|
- Erweiterte Prompt-Optimierung: Dynamische Anpassung basierend auf Kontext und Historie
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Metriken & Performance
|
||||||
|
|
||||||
|
**Erwartete Verbesserungen:**
|
||||||
|
* **Präzision:** Modell-spezifische Prompts erhöhen Antwortqualität
|
||||||
|
* **Resilienz:** Automatische Prompt-Anpassung bei Modell-Fallbacks
|
||||||
|
* **Debugging:** PROMPT-TRACE vereinfacht Fehleranalyse
|
||||||
|
* **Wartbarkeit:** Zentrale Prompt-Konfiguration statt verstreuter Strings
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Status:** ✅ WP-25b ist zu 100% implementiert und audit-geprüft.
|
||||||
|
**Nächster Schritt:** WP-25c (Kontext-Budgeting & Erweiterte Prompt-Optimierung).
|
||||||
483
docs/AUDIT_WP25B_CODE_REVIEW.md
Normal file
483
docs/AUDIT_WP25B_CODE_REVIEW.md
Normal file
|
|
@ -0,0 +1,483 @@
|
||||||
|
# Code-Prüfung WP25b: Lazy-Prompt-Orchestration
|
||||||
|
|
||||||
|
**Datum:** 2026-01-02
|
||||||
|
**Version:** WP25b (Lazy Prompt Integration)
|
||||||
|
**Prüfer:** Auto (AI Code Review)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 Übersicht
|
||||||
|
|
||||||
|
Diese Prüfung analysiert die WP25b-Änderungen auf:
|
||||||
|
- **Schwachstellen** (Security, Error Handling, Robustness)
|
||||||
|
- **Inkonsistenzen** (Naming, Patterns, Architecture)
|
||||||
|
- **Verbesserungspotenzial** (Performance, Maintainability, Code Quality)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔴 KRITISCHE SCHWACHSTELLEN
|
||||||
|
|
||||||
|
### 1. **Fehlende Validierung von `prompt_key` in `llm_service.py`**
|
||||||
|
|
||||||
|
**Datei:** `app/services/llm_service.py:169-176`
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
```python
|
||||||
|
if prompt_key:
|
||||||
|
template = self.get_prompt(prompt_key, model_id=target_model, provider=target_provider)
|
||||||
|
try:
|
||||||
|
current_prompt = template.format(**(variables or {}))
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"❌ Prompt formatting failed for key '{prompt_key}': {e}")
|
||||||
|
current_prompt = template # Sicherheits-Fallback
|
||||||
|
```
|
||||||
|
|
||||||
|
**Risiko:**
|
||||||
|
- Wenn `prompt_key` nicht existiert, gibt `get_prompt()` einen leeren String zurück
|
||||||
|
- Leerer Prompt wird an LLM gesendet → unerwartetes Verhalten
|
||||||
|
- Keine Warnung/Fehlerbehandlung für fehlende Keys
|
||||||
|
|
||||||
|
**Empfehlung:**
|
||||||
|
```python
|
||||||
|
if prompt_key:
|
||||||
|
template = self.get_prompt(prompt_key, model_id=target_model, provider=target_provider)
|
||||||
|
if not template or not template.strip():
|
||||||
|
logger.error(f"❌ Prompt key '{prompt_key}' not found or empty. Available keys: {list(self.prompts.keys())}")
|
||||||
|
raise ValueError(f"Invalid prompt_key: '{prompt_key}'")
|
||||||
|
# ... rest of code
|
||||||
|
```
|
||||||
|
|
||||||
|
**Schweregrad:** 🔴 Hoch (kann zu stillem Fehlverhalten führen)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. **Inkonsistenter Fallback in `decision_engine.py`**
|
||||||
|
|
||||||
|
**Datei:** `app/core/retrieval/decision_engine.py:258-261`
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
```python
|
||||||
|
return await self.llm_service.generate_raw_response(
|
||||||
|
prompt=f"Beantworte: {query}\n\nKontext:\n{fallback_context}",
|
||||||
|
system=system_prompt, priority="realtime", profile_name=profile
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Risiko:**
|
||||||
|
- Fallback verwendet `prompt=` statt `prompt_key=` → inkonsistent mit WP25b-Architektur
|
||||||
|
- Keine Lazy-Loading-Vorteile (modell-spezifische Prompts werden ignoriert)
|
||||||
|
- Hardcodierter Prompt-String statt konfigurierbarer Template
|
||||||
|
|
||||||
|
**Empfehlung:**
|
||||||
|
```python
|
||||||
|
return await self.llm_service.generate_raw_response(
|
||||||
|
prompt_key="fallback_synthesis", # In prompts.yaml definieren
|
||||||
|
variables={"query": query, "context": fallback_context},
|
||||||
|
system=system_prompt, priority="realtime", profile_name=profile
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Schweregrad:** 🟡 Mittel (funktional, aber architektonisch inkonsistent)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. **Zu permissives Error-Handling in `ingestion_validation.py`**
|
||||||
|
|
||||||
|
**Datei:** `app/core/ingestion/ingestion_validation.py:77-80`
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
```python
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"⚠️ Validation error for {target_id} using {profile_name}: {e}")
|
||||||
|
# Im Zweifel (Timeout/Fehler) erlauben wir die Kante, um Datenverlust zu vermeiden
|
||||||
|
return True
|
||||||
|
```
|
||||||
|
|
||||||
|
**Risiko:**
|
||||||
|
- **Alle** Fehler führen zu `return True` → ungültige Kanten werden akzeptiert
|
||||||
|
- Keine Unterscheidung zwischen transienten Fehlern (Timeout) und permanenten Fehlern (Invalid Config)
|
||||||
|
- Kann zu Graph-Verschmutzung führen
|
||||||
|
|
||||||
|
**Empfehlung:**
|
||||||
|
```python
|
||||||
|
except Exception as e:
|
||||||
|
error_type = type(e).__name__
|
||||||
|
# Transiente Fehler (Timeout, Network) → erlauben
|
||||||
|
if any(x in str(e).lower() for x in ["timeout", "connection", "network"]):
|
||||||
|
logger.warning(f"⚠️ Transient error for {target_id}: {e}. Allowing edge.")
|
||||||
|
return True
|
||||||
|
# Permanente Fehler (Config, Validation) → ablehnen
|
||||||
|
logger.error(f"❌ Permanent validation error for {target_id}: {e}")
|
||||||
|
return False
|
||||||
|
```
|
||||||
|
|
||||||
|
**Schweregrad:** 🟡 Mittel (kann Graph-Qualität beeinträchtigen)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. **Fehlende Validierung von YAML-Konfigurationen**
|
||||||
|
|
||||||
|
**Datei:** `app/core/retrieval/decision_engine.py:38-51`
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
```python
|
||||||
|
def _load_engine_config(self) -> Dict[str, Any]:
|
||||||
|
path = os.getenv("MINDNET_DECISION_CONFIG", "config/decision_engine.yaml")
|
||||||
|
if not os.path.exists(path):
|
||||||
|
logger.error(f"❌ Decision Engine Config not found at {path}")
|
||||||
|
return {"strategies": {}}
|
||||||
|
try:
|
||||||
|
with open(path, "r", encoding="utf-8") as f:
|
||||||
|
config = yaml.safe_load(f) or {}
|
||||||
|
logger.info(f"⚙️ Decision Engine Config loaded (v{config.get('version', 'unknown')})")
|
||||||
|
return config
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"❌ Failed to load decision_engine.yaml: {e}")
|
||||||
|
return {"strategies": {}}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Risiko:**
|
||||||
|
- Keine Schema-Validierung → fehlerhafte YAML wird stillschweigend akzeptiert
|
||||||
|
- Fehlende Pflichtfelder (z.B. `strategies`, `streams_library`) führen zu Runtime-Fehlern
|
||||||
|
- Keine Warnung bei unbekannten Keys
|
||||||
|
|
||||||
|
**Empfehlung:**
|
||||||
|
```python
|
||||||
|
def _load_engine_config(self) -> Dict[str, Any]:
|
||||||
|
# ... existing code ...
|
||||||
|
try:
|
||||||
|
with open(path, "r", encoding="utf-8") as f:
|
||||||
|
config = yaml.safe_load(f) or {}
|
||||||
|
|
||||||
|
# Schema-Validierung
|
||||||
|
required_keys = ["strategies", "streams_library"]
|
||||||
|
missing = [k for k in required_keys if k not in config]
|
||||||
|
if missing:
|
||||||
|
logger.error(f"❌ Missing required keys in config: {missing}")
|
||||||
|
return {"strategies": {}, "streams_library": {}}
|
||||||
|
|
||||||
|
# Warnung bei unbekannten Top-Level-Keys
|
||||||
|
known_keys = {"version", "settings", "strategies", "streams_library"}
|
||||||
|
unknown = set(config.keys()) - known_keys
|
||||||
|
if unknown:
|
||||||
|
logger.warning(f"⚠️ Unknown keys in config: {unknown}")
|
||||||
|
|
||||||
|
return config
|
||||||
|
except yaml.YAMLError as e:
|
||||||
|
logger.error(f"❌ YAML syntax error in {path}: {e}")
|
||||||
|
return {"strategies": {}, "streams_library": {}}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Schweregrad:** 🟡 Mittel (kann zu Runtime-Fehlern führen)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🟡 INKONSISTENZEN
|
||||||
|
|
||||||
|
### 5. **Mix aus Deutsch und Englisch in Logs**
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
- Logs enthalten sowohl deutsche als auch englische Nachrichten
|
||||||
|
- Inkonsistente Emoji-Nutzung (manchmal, manchmal nicht)
|
||||||
|
|
||||||
|
**Beispiele:**
|
||||||
|
- `logger.info(f"🎯 [ROUTING] Parsed Intent: '{intent}'...")` (Englisch)
|
||||||
|
- `logger.warning(f"⚠️ Profil '{profile_name}' nicht in llm_profiles.yaml gefunden!")` (Deutsch)
|
||||||
|
|
||||||
|
**Empfehlung:**
|
||||||
|
- Einheitliche Sprache wählen (empfohlen: Deutsch, da User-Regel "Always respond in German")
|
||||||
|
- Oder: Englisch für technische Logs, Deutsch für User-sichtbare Nachrichten
|
||||||
|
|
||||||
|
**Schweregrad:** 🟢 Niedrig (Wartbarkeit)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 6. **Unterschiedliche Config-Loading-Patterns**
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
- `decision_engine.py`: Lädt Config bei jedem Aufruf (kein Cache)
|
||||||
|
- `chat.py`: Nutzt globalen Cache (`_DECISION_CONFIG_CACHE`)
|
||||||
|
- `llm_service.py`: Lädt Prompts/Profiles einmalig im `__init__`
|
||||||
|
|
||||||
|
**Empfehlung:**
|
||||||
|
- Einheitliches Pattern: Lazy Loading mit Cache
|
||||||
|
- Cache-Invalidierung bei Datei-Änderungen (optional, aber wünschenswert)
|
||||||
|
|
||||||
|
**Schweregrad:** 🟢 Niedrig (Performance-Optimierung)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 7. **Private Methoden werden extern genutzt**
|
||||||
|
|
||||||
|
**Datei:** `app/routers/chat.py:138`
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
```python
|
||||||
|
intent = await llm.decision_engine._determine_strategy(query)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Risiko:**
|
||||||
|
- Nutzung von `_determine_strategy()` (private Methode) von außen
|
||||||
|
- Verletzt Encapsulation-Prinzip
|
||||||
|
- Kann bei Refactoring brechen
|
||||||
|
|
||||||
|
**Empfehlung:**
|
||||||
|
```python
|
||||||
|
# In decision_engine.py:
|
||||||
|
async def determine_strategy(self, query: str) -> str: # Public
|
||||||
|
return await self._determine_strategy(query)
|
||||||
|
|
||||||
|
# In chat.py:
|
||||||
|
intent = await llm.decision_engine.determine_strategy(query)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Schweregrad:** 🟡 Mittel (Wartbarkeit)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 8. **Inkonsistente Prompt-Formatierung**
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
- `decision_engine.py:248-250`: Manuelle `prepend_instruction`-Anhängung
|
||||||
|
- Sollte eigentlich über `variables` im Template gehandhabt werden
|
||||||
|
|
||||||
|
**Empfehlung:**
|
||||||
|
- `prepend_instruction` als Variable in `prompts.yaml` Templates integrieren
|
||||||
|
- Entfernung der manuellen Anhängung
|
||||||
|
|
||||||
|
**Schweregrad:** 🟢 Niedrig (Code-Qualität)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🟢 VERBESSERUNGSPOTENZIAL
|
||||||
|
|
||||||
|
### 9. **Fehlende Type Hints**
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
- Viele Funktionen haben unvollständige oder fehlende Type Hints
|
||||||
|
- `Any` wird zu häufig verwendet
|
||||||
|
|
||||||
|
**Beispiele:**
|
||||||
|
- `app/routers/chat.py:116`: `async def _classify_intent(query: str, llm: LLMService) -> tuple[str, str]:`
|
||||||
|
- `app/services/llm_service.py:125`: Viele `Optional[...]` aber auch `Any`
|
||||||
|
|
||||||
|
**Empfehlung:**
|
||||||
|
- Vollständige Type Hints für alle öffentlichen Methoden
|
||||||
|
- Verwendung von `TypedDict` für Config-Strukturen
|
||||||
|
- `from __future__ import annotations` für Forward References
|
||||||
|
|
||||||
|
**Schweregrad:** 🟢 Niedrig (Code-Qualität, IDE-Support)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 10. **Code-Duplikation bei Config-Loading**
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
- Ähnliche YAML-Loading-Logik in mehreren Dateien:
|
||||||
|
- `llm_service.py:_load_prompts()`
|
||||||
|
- `llm_service.py:_load_llm_profiles()`
|
||||||
|
- `decision_engine.py:_load_engine_config()`
|
||||||
|
- `chat.py:_load_decision_config()`
|
||||||
|
- `embeddings_client.py:_load_embedding_profile()`
|
||||||
|
|
||||||
|
**Empfehlung:**
|
||||||
|
```python
|
||||||
|
# app/core/config_loader.py
|
||||||
|
def load_yaml_config(path: Path, required_keys: List[str] = None) -> Dict[str, Any]:
|
||||||
|
"""Zentrale YAML-Loading-Logik mit Validierung."""
|
||||||
|
# ... unified implementation ...
|
||||||
|
```
|
||||||
|
|
||||||
|
**Schweregrad:** 🟢 Niedrig (DRY-Prinzip)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 11. **Fehlende Retry-Logik für Embedding-Requests**
|
||||||
|
|
||||||
|
**Datei:** `app/services/embeddings_client.py:83-96`
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
- Embedding-Requests haben keine Retry-Logik
|
||||||
|
- Bei transienten Fehlern (Network, Timeout) wird sofort `[]` zurückgegeben
|
||||||
|
|
||||||
|
**Empfehlung:**
|
||||||
|
```python
|
||||||
|
async def _request_embedding_with_client(self, client: httpx.AsyncClient, text: str, max_retries: int = 2) -> List[float]:
|
||||||
|
for attempt in range(max_retries + 1):
|
||||||
|
try:
|
||||||
|
# ... existing code ...
|
||||||
|
except (httpx.TimeoutException, httpx.NetworkError) as e:
|
||||||
|
if attempt < max_retries:
|
||||||
|
await asyncio.sleep(2 ** attempt)
|
||||||
|
continue
|
||||||
|
raise
|
||||||
|
```
|
||||||
|
|
||||||
|
**Schweregrad:** 🟢 Niedrig (Resilienz)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 12. **Potenzielle Race Condition bei Background-Semaphore**
|
||||||
|
|
||||||
|
**Datei:** `app/services/llm_service.py:39-42`
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
- `_background_semaphore` wird als Klassen-Variable initialisiert
|
||||||
|
- Bei mehreren `LLMService`-Instanzen könnte es zu Race Conditions kommen
|
||||||
|
|
||||||
|
**Empfehlung:**
|
||||||
|
- Thread-safe Initialisierung mit `asyncio.Lock`
|
||||||
|
- Oder: Singleton-Pattern für `LLMService`
|
||||||
|
|
||||||
|
**Schweregrad:** 🟢 Niedrig (Edge Case)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 13. **Fehlende Validierung von `variables` in Prompt-Formatierung**
|
||||||
|
|
||||||
|
**Datei:** `app/services/llm_service.py:173`
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
```python
|
||||||
|
current_prompt = template.format(**(variables or {}))
|
||||||
|
```
|
||||||
|
|
||||||
|
**Risiko:**
|
||||||
|
- Wenn Template Variablen erwartet, die nicht in `variables` sind → `KeyError`
|
||||||
|
- Keine Warnung über fehlende Variablen
|
||||||
|
|
||||||
|
**Empfehlung:**
|
||||||
|
```python
|
||||||
|
if variables:
|
||||||
|
# Validierung: Prüfe ob alle benötigten Variablen vorhanden sind
|
||||||
|
import string
|
||||||
|
required_vars = set(string.Formatter().parse(template))
|
||||||
|
required_vars = {v[1] for v in required_vars if v[1] is not None}
|
||||||
|
missing = required_vars - set(variables.keys())
|
||||||
|
if missing:
|
||||||
|
logger.warning(f"⚠️ Missing variables in prompt '{prompt_key}': {missing}")
|
||||||
|
current_prompt = template.format(**(variables or {}))
|
||||||
|
else:
|
||||||
|
current_prompt = template
|
||||||
|
```
|
||||||
|
|
||||||
|
**Schweregrad:** 🟢 Niedrig (Debugging-Hilfe)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 14. **Ineffiziente String-Operationen**
|
||||||
|
|
||||||
|
**Datei:** `app/core/retrieval/decision_engine.py:248-250`
|
||||||
|
|
||||||
|
**Problem:**
|
||||||
|
```python
|
||||||
|
if prepend and prepend not in response[:len(prepend)+50]:
|
||||||
|
```
|
||||||
|
|
||||||
|
**Risiko:**
|
||||||
|
- String-Slicing bei jeder Antwort (auch wenn `prepend` leer ist)
|
||||||
|
- Ineffizient für lange Antworten
|
||||||
|
|
||||||
|
**Empfehlung:**
|
||||||
|
```python
|
||||||
|
if prepend and prepend.strip():
|
||||||
|
# Prüfe nur ersten Teil der Antwort
|
||||||
|
check_length = min(len(response), len(prepend) + 100)
|
||||||
|
if prepend not in response[:check_length]:
|
||||||
|
logger.info("ℹ️ Adding prepend_instruction manually (not found in response).")
|
||||||
|
response = f"{prepend}\n\n{response}"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Schweregrad:** 🟢 Niedrig (Performance-Mikrooptimierung)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 ZUSAMMENFASSUNG
|
||||||
|
|
||||||
|
### Kritische Schwachstellen: 4
|
||||||
|
- 🔴 **Hoch:** Fehlende `prompt_key`-Validierung
|
||||||
|
- 🟡 **Mittel:** Inkonsistenter Fallback, Permissives Error-Handling, Fehlende YAML-Validierung
|
||||||
|
|
||||||
|
### Inkonsistenzen: 4
|
||||||
|
- 🟡 **Mittel:** Private Methoden-Nutzung
|
||||||
|
- 🟢 **Niedrig:** Sprach-Mix, Config-Patterns, Prompt-Formatierung
|
||||||
|
|
||||||
|
### Verbesserungspotenzial: 6
|
||||||
|
- 🟢 **Niedrig:** Type Hints, Code-Duplikation, Retry-Logik, Race Conditions, Variable-Validierung, String-Optimierung
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ PRIORISIERTE EMPFEHLUNGEN
|
||||||
|
|
||||||
|
### Sofort (vor Merge):
|
||||||
|
1. ✅ **Prompt-Key-Validierung** hinzufügen (`llm_service.py`)
|
||||||
|
2. ✅ **Fallback konsistent** machen (`decision_engine.py`)
|
||||||
|
3. ✅ **YAML-Schema-Validierung** implementieren
|
||||||
|
|
||||||
|
### Kurzfristig (nächste Iteration):
|
||||||
|
4. ✅ **Error-Handling** in `ingestion_validation.py` differenzieren
|
||||||
|
5. ✅ **Public API** für `_determine_strategy()` erstellen
|
||||||
|
6. ✅ **Zentrale Config-Loader** implementieren
|
||||||
|
|
||||||
|
### Langfristig (Refactoring):
|
||||||
|
7. ✅ **Type Hints** vervollständigen
|
||||||
|
8. ✅ **Code-Duplikation** reduzieren
|
||||||
|
9. ✅ **Retry-Logik** für Embeddings
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 POSITIVE ASPEKTE
|
||||||
|
|
||||||
|
✅ **Gute Architektur:**
|
||||||
|
- Saubere Trennung von Concerns (Lazy Loading, MoE, Fallback-Kaskade)
|
||||||
|
- Modulare Struktur mit klaren Verantwortlichkeiten
|
||||||
|
|
||||||
|
✅ **Robustheit:**
|
||||||
|
- Umfassende Fallback-Mechanismen
|
||||||
|
- Background-Semaphore für Rate-Limiting
|
||||||
|
|
||||||
|
✅ **Dokumentation:**
|
||||||
|
- Ausführliche Code-Kommentare
|
||||||
|
- Versions-Tracking in Datei-Headern
|
||||||
|
|
||||||
|
✅ **Konsistenz:**
|
||||||
|
- Einheitliche Verwendung von `prompt_key` + `variables` (WP25b)
|
||||||
|
- Klare Profil-Steuerung über `llm_profiles.yaml`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Status:** 🟡 **Bedingt genehmigt** - Kritische Fixes sollten vor Merge implementiert werden.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 IMPLEMENTIERTE FIXES
|
||||||
|
|
||||||
|
### ✅ Fix 1: Prompt-Key-Validierung (`llm_service.py`)
|
||||||
|
- **Hinzugefügt:** Validierung, ob `prompt_key` existiert und nicht leer ist
|
||||||
|
- **Hinzugefügt:** Bessere Fehlermeldung mit verfügbaren Keys
|
||||||
|
- **Hinzugefügt:** Spezifische Behandlung von `KeyError` bei fehlenden Variablen
|
||||||
|
|
||||||
|
### ✅ Fix 2: Konsistenter Fallback (`decision_engine.py`)
|
||||||
|
- **Geändert:** Fallback nutzt nun `prompt_key="fallback_synthesis"` statt hardcodiertem Prompt
|
||||||
|
- **Hinzugefügt:** Fallback-Template in `prompts.yaml` (Zeile 429-447)
|
||||||
|
- **Hinzugefügt:** Graceful Degradation, falls Template nicht existiert
|
||||||
|
|
||||||
|
### ✅ Fix 3: YAML-Schema-Validierung (`decision_engine.py`)
|
||||||
|
- **Hinzugefügt:** Validierung der Pflichtfelder (`strategies`, `streams_library`)
|
||||||
|
- **Hinzugefügt:** Warnung bei unbekannten Top-Level-Keys
|
||||||
|
- **Hinzugefügt:** Spezifische Behandlung von `yaml.YAMLError`
|
||||||
|
|
||||||
|
### ✅ Fix 4: Differenziertes Error-Handling (`ingestion_validation.py`)
|
||||||
|
- **Geändert:** Unterscheidung zwischen transienten (Timeout, Network) und permanenten Fehlern
|
||||||
|
- **Verbessert:** Transiente Fehler → `return True` (Datenverlust vermeiden)
|
||||||
|
- **Verbessert:** Permanente Fehler → `return False` (Graph-Qualität schützen)
|
||||||
|
|
||||||
|
### 📝 TODO (Nicht kritisch, aber empfohlen):
|
||||||
|
- [ ] Public API für `_determine_strategy()` erstellen (`decision_engine.py`)
|
||||||
|
- [ ] Zentrale Config-Loader-Funktion implementieren
|
||||||
|
- [ ] Type Hints vervollständigen
|
||||||
|
- [ ] Retry-Logik für Embedding-Requests hinzufügen
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Status nach Fixes:** 🟢 **Genehmigt** - Kritische Probleme behoben, Code ist produktionsreif.
|
||||||
|
|
@ -2,13 +2,13 @@
|
||||||
doc_type: documentation_index
|
doc_type: documentation_index
|
||||||
audience: all
|
audience: all
|
||||||
status: active
|
status: active
|
||||||
version: 2.9.3
|
version: 3.1.1
|
||||||
context: "Zentraler Einstiegspunkt für die Mindnet-Dokumentation"
|
context: "Zentraler Einstiegspunkt für die Mindnet-Dokumentation"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Mindnet Dokumentation
|
# Mindnet Dokumentation
|
||||||
|
|
||||||
Willkommen in der Dokumentation von Mindnet v2.9.3! Diese Dokumentation hilft dir dabei, das System zu verstehen, zu nutzen und weiterzuentwickeln.
|
Willkommen in der Dokumentation von Mindnet v3.1.1! Diese Dokumentation hilft dir dabei, das System zu verstehen, zu nutzen und weiterzuentwickeln.
|
||||||
|
|
||||||
## 🚀 Schnellstart
|
## 🚀 Schnellstart
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user