WP25b #21

Merged
Lars merged 6 commits from WP25b into main 2026-01-03 15:12:58 +01:00
6 changed files with 589 additions and 203 deletions
Showing only changes of commit 7026fc4fed - Show all commits

View File

@ -1,13 +1,13 @@
"""
FILE: app/core/ingestion/ingestion_validation.py
DESCRIPTION: WP-15b semantische Validierung von Kanten gegen den LocalBatchCache.
WP-25a: Integration der Mixture of Experts (MoE) Profil-Steuerung.
VERSION: 2.13.0 (WP-25a: MoE & Profile Support)
WP-25b: Umstellung auf Lazy-Prompt-Orchestration (prompt_key + variables).
VERSION: 2.14.0 (WP-25b: Lazy Prompt Integration)
STATUS: Active
FIX:
- Umstellung auf generate_raw_response mit profile_name="ingest_validator".
- Automatische Nutzung der Fallback-Kaskade (Cloud -> Lokal) via LLMService.
- Erhalt der sparsamen LLM-Nutzung (Validierung nur für Kandidaten-Pool).
- WP-25b: Entfernung manueller Prompt-Formatierung zur Unterstützung modell-spezifischer Prompts.
- WP-25b: Umstellung auf generate_raw_response mit prompt_key="edge_validation".
- WP-25a: Voller Erhalt der MoE-Profilsteuerung und Fallback-Kaskade via LLMService.
"""
import logging
from typing import Dict, Any, Optional
@ -27,8 +27,8 @@ async def validate_edge_candidate(
profile_name: str = "ingest_validator"
) -> bool:
"""
WP-15b: Validiert einen Kandidaten semantisch gegen das Ziel im Cache.
Nutzt das MoE-Profil 'ingest_validator' für deterministische YES/NO Prüfungen.
WP-15b/25b: Validiert einen Kandidaten semantisch gegen das Ziel im Cache.
Nutzt Lazy-Prompt-Loading zur Unterstützung modell-spezifischer Validierungs-Templates.
"""
target_id = edge.get("to")
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.")
return True
# Prompt-Abruf (Nutzt Provider-String als Fallback-Key für die prompts.yaml)
template = llm_service.get_prompt("edge_validation", provider)
try:
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)
# Nutzt ingest_validator (Cloud Mistral/Gemini -> Local Phi3:mini Kaskade)
# WP-25b: Lazy-Prompt Aufruf.
# 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(
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",
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)
# Semantische Prüfung des Ergebnisses
@ -75,6 +73,7 @@ async def validate_edge_candidate(
else:
logger.info(f"🚫 [REJECTED] Relation to '{target_id}' irrelevant for this chunk.")
return is_valid
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

View File

@ -1,16 +1,15 @@
"""
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
und die neue Pre-Synthesis Kompression (Module A).
VERSION: 1.2.1 (WP-25a: Profile-Driven Orchestration & Optimized Cascade)
und die neue Lazy-Prompt Orchestrierung (Module A & B).
VERSION: 1.3.0 (WP-25b: Lazy Prompt Orchestration)
STATUS: Active
FIX:
- WP-25a: Volle Integration der Profil-Kaskade (Delegation an LLMService v3.5.2).
- WP-25a: Dynamische Nutzung des 'router_profile' für die Intent-Erkennung.
- WP-25a: Parallelisierte Kompression überlanger Wissens-Streams.
- WP-25b: Umstellung auf Lazy-Loading (Übergabe von prompt_key + variables).
- WP-25b: Entfernung lokaler String-Formatierung zur Ermöglichung modell-spezifischer Prompts.
- WP-25a: Volle Integration der Profil-Kaskade via LLMService v3.5.5.
- WP-25: Beibehaltung von Stream-Tracing und Pre-Initialization Robustness.
- CLEANUP: Entfernung redundanter Fallback-Blocks (jetzt im LLMService).
"""
import asyncio
import logging
@ -70,29 +69,27 @@ class DecisionEngine:
if not strategy:
return "Entschuldigung, meine Wissensbasis ist aktuell nicht konfiguriert."
# 2. Multi-Stream Retrieval & Pre-Synthesis (Parallel Tasks)
# WP-25a: Diese Methode übernimmt nun auch die Kompression.
# 2. Multi-Stream Retrieval & Pre-Synthesis (Parallel Tasks inkl. Kompression)
stream_results = await self._execute_parallel_streams(strategy, query)
# 3. Finale Synthese
return await self._generate_final_answer(strategy_key, strategy, query, stream_results)
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 prompt_key."""
settings_cfg = self.config.get("settings", {})
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_prompt_template = self.llm_service.get_prompt(prompt_key)
if not router_prompt_template:
return "FACT_WHAT"
full_prompt = router_prompt_template.format(query=query)
try:
# Der LLMService übernimmt hier über das Profil bereits die Fallback-Kaskade
# WP-25b: Keine manuelle Formatierung mehr. Wir übergeben nur Key und Variablen.
# Der LLMService wählt den passenden Prompt für das router_profile Modell.
response = await self.llm_service.generate_raw_response(
full_prompt, max_retries=1, priority="realtime", profile_name=router_profile
prompt_key=prompt_key,
variables={"query": query},
max_retries=1,
priority="realtime",
profile_name=router_profile
)
return str(response).strip().upper()
except Exception as e:
@ -102,7 +99,7 @@ class DecisionEngine:
async def _execute_parallel_streams(self, strategy: Dict, query: str) -> Dict[str, str]:
"""
Führt Such-Streams aus und komprimiert überlange Ergebnisse (Pre-Synthesis).
WP-25a: MoE-Profile werden für die Kompression berücksichtigt.
WP-25b: Unterstützt Lazy-Compression über Experten-Profile.
"""
stream_keys = strategy.get("use_streams", [])
library = self.config.get("streams_library", {})
@ -116,7 +113,7 @@ class DecisionEngine:
active_streams.append(key)
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)
# Phase 2: Formatierung und optionale Kompression
@ -137,38 +134,32 @@ class DecisionEngine:
threshold = stream_cfg.get("compression_threshold", 4000)
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")
final_stream_tasks.append(
self._compress_stream_content(name, formatted_context, query, comp_profile)
)
else:
# Direkt-Übernahme als Coroutine für gather()
async def _direct(c=formatted_context): return c
final_stream_tasks.append(_direct())
# Finale Inhalte (evtl. komprimiert) parallel fertigstellen
# Finale Inhalte parallel fertigstellen
final_contents = await asyncio.gather(*final_stream_tasks)
return dict(zip(active_streams, final_contents))
async def _compress_stream_content(self, stream_name: str, content: str, query: str, profile: Optional[str]) -> str:
"""
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:"
)
"""WP-25b Module A: Inhaltsverdichtung via Lazy-Loading 'compression_template'."""
try:
# WP-25b: Wir übergeben den Auftrag an den LLMService.
# Das Modell-spezifische Template wird erst beim Call aufgelöst.
summary = await self.llm_service.generate_raw_response(
compression_prompt,
profile_name=profile, # WP-25a: MoE Support
prompt_key="compression_template",
variables={
"stream_name": stream_name,
"content": content,
"query": query
},
profile_name=profile,
priority="background",
max_retries=1
)
@ -191,24 +182,19 @@ class DecisionEngine:
)
response = await self.retriever.search(request)
# WP-25: STREAM-TRACING
for hit in response.results:
hit.stream_origin = name
return response
def _format_stream_context(self, response: QueryResponse) -> str:
"""Wandelt QueryHits in einen formatierten Kontext-String um."""
if not response.results:
return "Keine spezifischen Informationen in diesem Stream gefunden."
lines = []
for i, hit in enumerate(response.results, 1):
source = hit.source.get("path", "Unbekannt")
content = hit.source.get("text", "").strip()
lines.append(f"[{i}] QUELLE: {source}\nINHALT: {content}")
return "\n\n".join(lines)
async def _generate_final_answer(
@ -218,12 +204,9 @@ class DecisionEngine:
query: str,
stream_results: Dict[str, str]
) -> str:
"""Führt die finale Synthese basierend auf dem Strategie-Profil durch."""
# WP-25a: Nutzt das llm_profile der Strategie
"""WP-25b: Finale Synthese via Lazy-Prompt 'rag_template'."""
profile = strategy.get("llm_profile")
template_key = strategy.get("prompt_template", "rag_template")
template = self.llm_service.get_prompt(template_key)
system_prompt = self.llm_service.get_prompt("system_prompt")
# WP-25 ROBUSTNESS: Pre-Initialization
@ -232,28 +215,15 @@ class DecisionEngine:
template_vars.update(stream_results)
template_vars["query"] = query
prepend = strategy.get("prepend_instruction", "")
# WP-25b: Wir reichen die Variablen direkt an den Service weiter.
# Formatierung erfolgt erst nach Profil-Auflösung (Gemini vs. Llama vs. Phi3).
try:
final_prompt = template.format(**template_vars)
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(
final_prompt, system=system_prompt, profile_name=profile, priority="realtime"
)
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
prompt_key=template_key,
variables=template_vars,
system=system_prompt,
profile_name=profile,
priority="realtime"
)
except Exception as e:
logger.error(f"Final Synthesis failed: {e}")

View File

@ -1,15 +1,14 @@
"""
FILE: app/routers/chat.py
DESCRIPTION: Haupt-Chat-Interface (WP-25a Agentic Edition).
Kombiniert die spezialisierte Interview-Logik und Keyword-Erkennung
mit der neuen MoE-Orchestrierung und Pre-Synthesis Kompression.
VERSION: 3.0.4 (WP-25a: Optimized MoE & Cascade Delegation)
DESCRIPTION: Haupt-Chat-Interface (WP-25b Edition).
Kombiniert die spezialisierte Interview-Logik mit der neuen
Lazy-Prompt-Orchestration und MoE-Synthese.
VERSION: 3.0.5 (WP-25b: Lazy Prompt Integration)
STATUS: Active
FIX:
- WP-25a: Delegation der Fallback-Kaskade an den LLMService (v3.5.2).
- WP-25a: Nutzung der zentralisierten Stream-Kompression der DecisionEngine (v1.2.1).
- WP-25a: Konsistente Nutzung von MoE-Profilen für Interview- und RAG-Modus.
- 100% Erhalt der v3.0.2 Logik (Interview, Schema-Resolution, FastPaths).
- WP-25b: Umstellung des Interview-Modus auf Lazy-Prompt (prompt_key + variables).
- WP-25b: Delegation der RAG-Phase an die Engine v1.3.0 für konsistente MoE-Steuerung.
- WP-25a: Voller Erhalt der v3.0.2 Logik (Interview, Schema-Resolution, FastPaths).
"""
from fastapi import APIRouter, HTTPException, Depends
@ -146,7 +145,7 @@ def _collect_all_hits(stream_responses: Dict[str, Any]) -> List[QueryHit]:
all_hits = []
seen_node_ids = set()
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'):
for hit in response.results:
if hit.node_id not in seen_node_ids:
@ -166,8 +165,7 @@ async def chat_endpoint(
):
start_time = time.time()
query_id = str(uuid.uuid4())
settings = get_settings()
logger.info(f"🚀 [WP-25a] Chat request [{query_id}]: {request.message[:50]}...")
logger.info(f"🚀 [WP-25b] Chat request [{query_id}]: {request.message[:50]}...")
try:
# 1. Intent Detection
@ -180,7 +178,7 @@ async def chat_endpoint(
sources_hits = []
answer_text = ""
# 2. INTERVIEW MODE (Bitgenaue WP-07 Logik)
# 2. INTERVIEW MODE (WP-25b Lazy-Prompt Logik)
if intent == "INTERVIEW":
target_type = _detect_target_type(request.message, strategy.get("schemas", {}))
types_cfg = get_types_config()
@ -194,23 +192,27 @@ async def chat_endpoint(
fields_list = fallback.get("fields", []) if isinstance(fallback, dict) else (fallback or [])
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) \
.replace("{target_type}", target_type) \
.replace("{schema_fields}", fields_str)
# WP-25a: MoE Call (Kaskade erfolgt intern im LLMService)
# WP-25b: Lazy Loading Call
# Wir übergeben nur Key und Variablen. Das System formatiert passend zum Modell.
answer_text = await llm.generate_raw_response(
final_prompt, system=llm.get_prompt("system_prompt"),
priority="realtime", profile_name="compression_fast", max_retries=0
prompt_key=template_key,
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 = []
# 3. RAG MODE (Optimierte MoE Orchestrierung)
# 3. RAG MODE (WP-25b Delegation an Engine v1.3.0)
else:
# Phase A & B: Retrieval & Kompression (Delegation an Engine v1.2.1)
# Diese Methode gibt bereits die (evtl. komprimierten) Kontext-Strings zurück.
# Phase A & B: Retrieval & Kompression (Delegiert an Engine v1.3.0)
formatted_context_map = await engine._execute_parallel_streams(strategy, request.message)
# Erfassung der Quellen für das Tracing
@ -232,7 +234,7 @@ async def chat_endpoint(
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(
intent, strategy, request.message, formatted_context_map
)
@ -243,7 +245,7 @@ async def chat_endpoint(
try:
log_search(
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

View File

@ -1,14 +1,14 @@
"""
FILE: app/services/llm_service.py
DESCRIPTION: Hybrid-Client für Ollama, Google GenAI (Gemini) und OpenRouter.
WP-25a: Implementierung der Mixture of Experts (MoE) Kaskaden-Steuerung.
VERSION: 3.5.2 (WP-25a: MoE & Fallback Cascade Support)
WP-25b: Implementierung der Lazy-Prompt-Orchestration (Modell-spezifisch).
VERSION: 3.5.5 (WP-25b: Prompt Orchestration & Full Resilience)
STATUS: Active
FIX:
- WP-25a: Implementierung der rekursiven Fallback-Kaskade via fallback_profile.
- WP-25a: Schutz gegen zirkuläre Profil-Referenzen (visited_profiles).
- WP-25a: Erweitertes Logging für Tracing der Experten-Entscheidungen.
- Erhalt der Ingest-Stability (WP-25) und des Rate-Limit-Managements.
- WP-25b: get_prompt() unterstützt Hierarchie: Model-ID -> Provider -> Default.
- WP-25b: generate_raw_response() unterstützt prompt_key + variables für Lazy-Formatting.
- WP-25a: Voller Erhalt der rekursiven Fallback-Kaskade und visited_profiles Schutz.
- WP-20: Restaurierung des internen Ollama-Retry-Loops für Hardware-Stabilität.
"""
import httpx
import yaml
@ -33,10 +33,7 @@ class LLMService:
def __init__(self):
self.settings = get_settings()
self.prompts = self._load_prompts()
# WP-25a: Zentrale Experten-Profile laden
self.profiles = self._load_llm_profiles()
self._decision_engine = 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 = Path(path_str)
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 {}
try:
with open(path, "r", encoding="utf-8") as f:
@ -102,17 +99,31 @@ class LLMService:
logger.error(f"❌ Failed to load llm_profiles.yaml: {e}")
return {}
def get_prompt(self, key: str, provider: str = None) -> str:
active_provider = provider or self.settings.MINDNET_LLM_PROVIDER
def get_prompt(self, key: str, model_id: str = None, provider: str = None) -> str:
"""
WP-25b: Hochpräziser Prompt-Lookup.
Hierarchie: Exakte Modell-ID -> Provider-Name -> Globaler Default.
"""
data = self.prompts.get(key, "")
if isinstance(data, dict):
val = data.get(active_provider, data.get("gemini", data.get("ollama", "")))
return str(val)
return str(data)
if not isinstance(data, dict):
return str(data)
# 1. Spezifischstes Match: Exakte Modell-ID (z.B. 'meta-llama/llama-3.3-70b-instruct:free')
if model_id and model_id in data:
return str(data[model_id])
# 2. Mittlere Ebene: Provider (z.B. 'ollama' oder 'openrouter')
if provider and provider in data:
return str(data[provider])
# 3. Fallback: Bekannte Keys oder Default aus prompts.yaml
return str(data.get("default", data.get("gemini", data.get("ollama", ""))))
async def generate_raw_response(
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,
force_json: bool = False,
max_retries: int = 2,
@ -126,16 +137,14 @@ class LLMService:
profile_name: Optional[str] = None,
visited_profiles: Optional[list] = None
) -> str:
"""
Haupteinstiegspunkt für LLM-Anfragen mit rekursiver Kaskaden-Logik.
"""
"""Haupteinstiegspunkt für LLM-Anfragen mit Lazy-Prompt Orchestrierung."""
visited_profiles = visited_profiles or []
target_provider = provider
target_model = model_override
target_temp = None
fallback_profile = None
# 1. Profil-Auflösung
# 1. Profil-Auflösung (Mixture of Experts)
if profile_name and self.profiles:
profile = self.profiles.get(profile_name)
if profile:
@ -148,30 +157,39 @@ class LLMService:
else:
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:
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)
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
# 3. Ausführung mit Fehler-Handling für Kaskade
try:
if priority == "background":
async with LLMService._background_semaphore:
res = await self._dispatch(
target_provider, prompt, system, force_json,
target_provider, current_prompt, system, force_json,
max_retries, base_delay, target_model,
json_schema, json_schema_name, strict_json_schema, target_temp
)
else:
res = await self._dispatch(
target_provider, prompt, system, force_json,
target_provider, current_prompt, system, force_json,
max_retries, base_delay, target_model,
json_schema, json_schema_name, strict_json_schema, target_temp
)
# Check auf leere Cloud-Antworten (WP-25 Stability)
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}")
return clean_llm_text(res) if not force_json else res
@ -179,40 +197,33 @@ class LLMService:
except Exception as 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:
logger.info(f"🔄 Switching to fallback profile: '{fallback_profile}'")
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,
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,
strict_json_schema=strict_json_schema,
profile_name=fallback_profile,
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:
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
raise e
async def _dispatch(
self,
provider: str,
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
self, provider, prompt, system, force_json, max_retries, base_delay,
model_override, json_schema, json_schema_name, strict_json_schema, temperature
) -> str:
"""Routet die Anfrage an den spezifischen Provider-Executor."""
rate_limit_attempts = 0
@ -232,23 +243,19 @@ class LLMService:
if provider == "gemini" and self.google_client:
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:
err_str = str(e)
# Rate-Limit Handling (429)
if any(x in err_str for x in ["429", "RESOURCE_EXHAUSTED", "rate_limited"]):
rate_limit_attempts += 1
logger.warning(f"⏳ Rate Limit {provider}. Attempt {rate_limit_attempts}. Wait {wait_time}s.")
await asyncio.sleep(wait_time)
continue
# Andere Fehler werden an generate_raw_response für die Kaskade gereicht
raise e
async def _execute_google(self, prompt, system, force_json, model_override, temperature):
model = model_override or self.settings.GEMINI_MODEL
clean_model = model.replace("models/", "")
model = (model_override or self.settings.GEMINI_MODEL).replace("models/", "")
config_kwargs = {
"system_instruction": system,
"response_mime_type": "application/json" if force_json else "text/plain"
@ -257,22 +264,13 @@ class LLMService:
config_kwargs["temperature"] = temperature
config = types.GenerateContentConfig(**config_kwargs)
response = await asyncio.wait_for(
asyncio.to_thread(
self.google_client.models.generate_content,
model=clean_model, contents=prompt, config=config
),
asyncio.to_thread(self.google_client.models.generate_content, model=model, contents=prompt, config=config),
timeout=45.0
)
return response.text.strip()
async def _execute_openrouter(
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:
async def _execute_openrouter(self, prompt, system, force_json, model_override, json_schema, json_schema_name, strict_json_schema, temperature) -> str:
model = model_override or self.settings.OPENROUTER_MODEL
logger.info(f"🛰️ OpenRouter Call: Model='{model}' | Temp={temperature}")
messages = []
@ -280,35 +278,26 @@ class LLMService:
messages.append({"role": "user", "content": prompt})
kwargs: Dict[str, Any] = {}
if temperature is not None:
kwargs["temperature"] = temperature
if temperature is not None: kwargs["temperature"] = temperature
if force_json:
if json_schema:
kwargs["response_format"] = {
"type": "json_schema",
"json_schema": {"name": json_schema_name, "strict": strict_json_schema, "schema": json_schema}
}
kwargs["response_format"] = {"type": "json_schema", "json_schema": {"name": json_schema_name, "strict": strict_json_schema, "schema": json_schema}}
else:
kwargs["response_format"] = {"type": "json_object"}
response = await self.openrouter_client.chat.completions.create(
model=model, messages=messages, **kwargs
)
if not response.choices:
return ""
response = await self.openrouter_client.chat.completions.create(model=model, messages=messages, **kwargs)
if not response.choices: return ""
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):
# Nutzt Profil-Temperatur oder strikte Defaults für lokale Hardware-Schonung
async def _execute_ollama(self, prompt, system, force_json, max_retries, base_delay, temperature=None, model_override=None):
# 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)
payload = {
"model": self.settings.LLM_MODEL,
"prompt": prompt,
"stream": False,
"model": effective_model,
"prompt": prompt, "stream": False,
"options": {"temperature": effective_temp, "num_ctx": 8192}
}
if force_json: payload["format"] = "json"
@ -323,12 +312,11 @@ class LLMService:
except Exception as e:
attempt += 1
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
await asyncio.sleep(base_delay * (2 ** (attempt - 1)))
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)
async def close(self):

337
config/prompts - Kopie.yaml Normal file
View 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:

View File

@ -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
# 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).
# - 100% Erhalt der Original-Prompts aus v3.1.2 für die Provider-Ebene (ollama, gemini, openrouter).
# - Integration der Modell-spezifischen Overrides für Gemini 2.0, Llama 3.3 und Qwen 2.5.
# - Hinzufügen des notwendigen 'compression_template' für die DecisionEngine v1.3.0.
system_prompt: |
Du bist 'mindnet', mein digitaler Zwilling und strategischer Partner.
@ -20,8 +20,19 @@ system_prompt: |
# ---------------------------------------------------------
# 1. STANDARD: Fakten & Wissen (Intent: FACT_WHAT / FACT_WHEN)
# ---------------------------------------------------------
# Ersetzt das alte 'rag_template'. Nutzt jetzt parallele Streams.
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: |
WISSENS-STREAMS:
=========================================
@ -42,22 +53,32 @@ fact_synthesis_v1:
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.
default: "Beantworte {query} basierend auf dem Kontext: {facts_stream} {biography_stream} {tech_stream}."
# ---------------------------------------------------------
# 2. DECISION: Strategie & Abwägung (Intent: DECISION)
# ---------------------------------------------------------
# Ersetzt das alte 'decision_template'. Nutzt jetzt parallele Streams.
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: |
ENTSCHEIDUNGS-STREAMS:
=========================================
@ -84,19 +105,24 @@ decision_synthesis_v1:
- **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.
default: "Prüfe {query} gegen Werte {values_stream} und Fakten {facts_stream}."
# ---------------------------------------------------------
# 3. EMPATHY: Der Spiegel / "Ich"-Modus (Intent: EMPATHY)
# ---------------------------------------------------------
empathy_template:
# --- EXAKTE Provider-Fallbacks aus v3.1.2 ---
ollama: |
KONTEXT (ERFAHRUNGEN & WERTE):
=========================================
@ -118,13 +144,23 @@ empathy_template:
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}"
default: "Reflektiere empathisch über {query} basierend auf {biography_stream}."
# ---------------------------------------------------------
# 4. TECHNICAL: Der Coder (Intent: CODING)
# ---------------------------------------------------------
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: |
KONTEXT (WISSEN & PROJEKTE):
=========================================
@ -148,13 +184,17 @@ technical_template:
- 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}"
default: "Erstelle eine technische Lösung für {query}."
# ---------------------------------------------------------
# 5. INTERVIEW: Der "One-Shot Extractor" (WP-07)
# ---------------------------------------------------------
interview_template:
# --- EXAKTE Provider-Fallbacks aus v3.1.2 ---
ollama: |
TASK:
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)
(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}."
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:
ollama: |
@ -213,12 +272,14 @@ edge_allocation_template:
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}
@ -229,8 +290,10 @@ edge_allocation_template:
REGEL: Kein Text, keine Analyse, keine Kommentare. Wenn nichts passt, gib [] zurück.
OUTPUT:
default: "[]"
# ---------------------------------------------------------
# 7. SMART EDGE ALLOCATION: Extraktion (Ingest)
# 8. SMART EDGE ALLOCATION: Extraktion (Ingest)
# ---------------------------------------------------------
edge_extraction:
ollama: |
@ -254,11 +317,13 @@ edge_extraction:
"""
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}
@ -269,10 +334,20 @@ edge_extraction:
Wenn keine Relationen existieren, antworte NUR mit: []
OUTPUT:
default: "[]"
# ---------------------------------------------------------
# 8. WP-15b: EDGE VALIDATION (Ingest/Validate)
# 9. INGESTION: EDGE VALIDATION (Ingest/Validate)
# ---------------------------------------------------------
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: |
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?
REGEL: Antworte NUR mit 'YES' oder 'NO'. Keine Erklärungen oder Smalltalk.
openrouter: |
Verify semantic relation for graph construction.
Source Context: {chunk_text}
@ -295,6 +371,7 @@ edge_validation:
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}
@ -302,10 +379,19 @@ edge_validation:
BEZIEHUNG: {edge_kind}
Ist diese Verbindung valide? Antworte NUR mit YES oder NO.
default: "YES"
# ---------------------------------------------------------
# 10. WP-25: INTENT ROUTING (Intent: CLASSIFY)
# ---------------------------------------------------------
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: |
Analysiere die Nutzeranfrage und wähle die passende Strategie.
Antworte NUR mit dem Namen der Strategie.
@ -320,6 +406,7 @@ intent_router_v1:
NACHRICHT: "{query}"
STRATEGIE:
gemini: |
Classify intent:
- FACT_WHEN: Exact dates/times only.
@ -330,8 +417,11 @@ intent_router_v1:
- 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:
Response:
default: "FACT_WHAT"