WP25 #19

Merged
Lars merged 8 commits from WP25 into main 2026-01-01 20:26:05 +01:00
22 changed files with 1516 additions and 689 deletions

37
app/core/logging_setup.py Normal file
View File

@ -0,0 +1,37 @@
import logging
import os
from logging.handlers import RotatingFileHandler
def setup_logging():
# 1. Log-Verzeichnis erstellen (falls nicht vorhanden)
log_dir = "logs"
if not os.path.exists(log_dir):
os.makedirs(log_dir)
log_file = os.path.join(log_dir, "mindnet.log")
# 2. Formatter definieren (Zeitstempel | Level | Modul | Nachricht)
formatter = logging.Formatter(
'%(asctime)s | %(levelname)-8s | %(name)s | %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
# 3. File Handler: Schreibt in Datei (max. 5MB pro Datei, behält 5 Backups)
file_handler = RotatingFileHandler(
log_file, maxBytes=5*1024*1024, backupCount=5, encoding='utf-8'
)
file_handler.setFormatter(formatter)
file_handler.setLevel(logging.INFO)
# 4. Stream Handler: Schreibt weiterhin auf die Konsole
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
console_handler.setLevel(logging.INFO)
# 5. Root Logger konfigurieren
logging.basicConfig(
level=logging.INFO,
handlers=[file_handler, console_handler]
)
logging.info(f"📝 Logging initialized. Writing to {log_file}")

View File

@ -0,0 +1,204 @@
"""
FILE: app/core/retrieval/decision_engine.py
DESCRIPTION: Der Agentic Orchestrator für WP-25.
Realisiert Multi-Stream Retrieval, Intent-basiertes Routing
und parallele Wissens-Synthese.
VERSION: 1.0.3
STATUS: Active
FIX:
- WP-25 STREAM-TRACING: Kennzeichnung der Treffer mit ihrem Ursprungs-Stream.
- WP-25 ROBUSTNESS: Pre-Initialization der Stream-Variablen zur Vermeidung von KeyErrors.
"""
import asyncio
import logging
import yaml
import os
from typing import List, Dict, Any, Optional
# Core & Service Imports
from app.models.dto import QueryRequest, QueryResponse
from app.core.retrieval.retriever import Retriever
from app.services.llm_service import LLMService
from app.config import get_settings
logger = logging.getLogger(__name__)
class DecisionEngine:
def __init__(self):
"""Initialisiert die Engine und lädt die modularen Konfigurationen."""
self.settings = get_settings()
self.retriever = Retriever()
self.llm_service = LLMService()
self.config = self._load_engine_config()
def _load_engine_config(self) -> Dict[str, Any]:
"""Lädt die Multi-Stream Konfiguration (WP-25)."""
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:
return yaml.safe_load(f) or {}
except Exception as e:
logger.error(f"❌ Failed to load decision_engine.yaml: {e}")
return {"strategies": {}}
async def ask(self, query: str) -> str:
"""
Hauptmethode des MindNet Chats.
Orchestriert den gesamten Prozess: Routing -> Retrieval -> Synthese.
"""
# 1. Intent Recognition
strategy_key = await self._determine_strategy(query)
strategies = self.config.get("strategies", {})
strategy = strategies.get(strategy_key)
if not strategy:
logger.warning(f"⚠️ Unknown strategy '{strategy_key}'. Fallback to FACT_WHAT.")
strategy_key = "FACT_WHAT"
strategy = strategies.get("FACT_WHAT")
if not strategy and strategies:
strategy_key = next(iter(strategies))
strategy = strategies[strategy_key]
if not strategy:
return "Entschuldigung, meine Wissensbasis ist aktuell nicht konfiguriert."
# 2. Multi-Stream Retrieval
stream_results = await self._execute_parallel_streams(strategy, query)
# 3. 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."""
prompt_key = self.config.get("settings", {}).get("router_prompt_key", "intent_router_v1")
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:
response = await self.llm_service.generate_raw_response(
full_prompt, max_retries=1, priority="realtime"
)
return str(response).strip().upper()
except Exception as e:
logger.error(f"Strategy Routing failed: {e}")
return "FACT_WHAT"
async def _execute_parallel_streams(self, strategy: Dict, query: str) -> Dict[str, str]:
"""Führt Such-Streams gleichzeitig aus."""
stream_keys = strategy.get("use_streams", [])
library = self.config.get("streams_library", {})
tasks = []
active_streams = []
for key in stream_keys:
stream_cfg = library.get(key)
if stream_cfg:
active_streams.append(key)
tasks.append(self._run_single_stream(key, stream_cfg, query))
results = await asyncio.gather(*tasks, return_exceptions=True)
mapped_results = {}
for name, res in zip(active_streams, results):
if isinstance(res, Exception):
logger.error(f"Stream '{name}' failed: {res}")
mapped_results[name] = "[Fehler beim Abruf dieses Wissens-Streams]"
else:
mapped_results[name] = self._format_stream_context(res)
return mapped_results
async def _run_single_stream(self, name: str, cfg: Dict, query: str) -> QueryResponse:
"""
Bereitet eine spezialisierte Suche vor.
WP-25: Taggt die Treffer mit ihrem Ursprungs-Stream.
"""
transformed_query = cfg.get("query_template", "{query}").format(query=query)
request = QueryRequest(
query=transformed_query,
top_k=cfg.get("top_k", 5),
filters={"type": cfg.get("filter_types", [])},
expand={"depth": 1},
boost_edges=cfg.get("edge_boosts", {}),
explain=True
)
# Retrieval ausführen
response = await self.retriever.search(request)
# WP-25: STREAM-TRACING
# Markiere jeden Treffer mit dem Namen des Quell-Streams
for hit in response.results:
hit.stream_origin = name
return response
def _format_stream_context(self, response: QueryResponse) -> str:
"""Wandelt QueryHits in Kontext-Strings 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(
self,
strategy_key: str,
strategy: Dict,
query: str,
stream_results: Dict[str, str]
) -> str:
"""Führt die Synthese durch."""
provider = strategy.get("preferred_provider") or self.settings.MINDNET_LLM_PROVIDER
template_key = strategy.get("prompt_template", "rag_template")
template = self.llm_service.get_prompt(template_key, provider=provider)
system_prompt = self.llm_service.get_prompt("system_prompt", provider=provider)
# WP-25 ROBUSTNESS: Pre-Initialization
all_possible_streams = ["values_stream", "facts_stream", "biography_stream", "risk_stream", "tech_stream"]
template_vars = {s: "" for s in all_possible_streams}
template_vars.update(stream_results)
template_vars["query"] = query
prepend = strategy.get("prepend_instruction", "")
try:
final_prompt = template.format(**template_vars)
if prepend:
final_prompt = f"{prepend}\n\n{final_prompt}"
response = await self.llm_service.generate_raw_response(
final_prompt, system=system_prompt, provider=provider, priority="realtime"
)
if not response or len(response.strip()) < 5:
return await self.llm_service.generate_raw_response(
final_prompt, system=system_prompt, provider="ollama", 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])
return await self.llm_service.generate_raw_response(
f"Beantworte: {query}\n\nKontext:\n{fallback_context}",
system=system_prompt, priority="realtime"
)
except Exception as e:
logger.error(f"Final Synthesis failed: {e}")
return "Ich konnte keine Antwort generieren."

View File

@ -1,25 +1,28 @@
"""
FILE: app/main.py
DESCRIPTION: Bootstrap der FastAPI Anwendung. Inkludiert Router und Middleware.
VERSION: 0.6.0
DESCRIPTION: Bootstrap der FastAPI Anwendung für WP-25 (Agentic RAG).
Orchestriert Lifespan-Events, globale Fehlerbehandlung und Routing.
VERSION: 1.0.0 (WP-25 Release)
STATUS: Active
DEPENDENCIES: app.config, app.routers.* (embed, qdrant, query, graph, tools, feedback, chat, ingest, admin)
LAST_ANALYSIS: 2025-12-15
DEPENDENCIES: app.config, app.routers.*, app.services.llm_service
"""
from __future__ import annotations
from fastapi import FastAPI
from .config import get_settings
#from .routers.embed_router import router as embed_router
#from .routers.qdrant_router import router as qdrant_router
import logging
import os
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from .config import get_settings
from .services.llm_service import LLMService
# Import der Router
from .routers.query import router as query_router
from .routers.graph import router as graph_router
from .routers.tools import router as tools_router
from .routers.feedback import router as feedback_router
# NEU: Chat Router (WP-05)
from .routers.chat import router as chat_router
# NEU: Ingest Router (WP-11)
from .routers.ingest import router as ingest_router
try:
@ -27,26 +30,86 @@ try:
except Exception:
admin_router = None
from .core.logging_setup import setup_logging
# Initialisierung noch VOR create_app()
setup_logging()
logger = logging.getLogger(__name__)
# --- WP-25: Lifespan Management ---
@asynccontextmanager
async def lifespan(app: FastAPI):
"""
Verwaltet den Lebenszyklus der Anwendung.
Führt Startup-Prüfungen durch und bereinigt Ressourcen beim Shutdown.
"""
settings = get_settings()
logger.info("🚀 mindnet API: Starting up (WP-25 Agentic RAG Mode)...")
# 1. Startup: Integritäts-Check der WP-25 Konfiguration
# Wir prüfen, ob die für die DecisionEngine kritischen Dateien vorhanden sind.
decision_cfg = os.getenv("MINDNET_DECISION_CONFIG", "config/decision_engine.yaml")
prompts_cfg = settings.PROMPTS_PATH
if not os.path.exists(decision_cfg):
logger.error(f"❌ CRITICAL: Decision Engine config missing at {decision_cfg}")
if not os.path.exists(prompts_cfg):
logger.error(f"❌ CRITICAL: Prompts config missing at {prompts_cfg}")
yield
# 2. Shutdown: Ressourcen bereinigen
logger.info("🛑 mindnet API: Shutting down...")
llm = LLMService()
await llm.close()
logger.info("✨ Cleanup complete. Goodbye.")
# --- App Factory ---
def create_app() -> FastAPI:
app = FastAPI(title="mindnet API", version="0.6.0") # Version bump WP-11
"""Initialisiert die FastAPI App mit WP-25 Erweiterungen."""
app = FastAPI(
title="mindnet API",
version="1.0.0", # WP-25 Milestone
lifespan=lifespan,
description="Digital Twin Knowledge Engine mit Agentic Multi-Stream RAG."
)
s = get_settings()
# --- Globale Fehlerbehandlung (WP-25 Resilienz) ---
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
"""Fängt unerwartete Fehler in der Multi-Stream Kette ab."""
logger.error(f"❌ Unhandled Engine Error: {exc}", exc_info=True)
return JSONResponse(
status_code=500,
content={
"detail": "Ein interner Fehler ist aufgetreten. Die DecisionEngine konnte die Anfrage nicht finalisieren.",
"error_type": type(exc).__name__
}
)
# Healthcheck
@app.get("/healthz")
def healthz():
return {"status": "ok", "qdrant": s.QDRANT_URL, "prefix": s.COLLECTION_PREFIX}
# app.include_router(embed_router)
# app.include_router(qdrant_router)
return {
"status": "ok",
"version": "1.0.0",
"qdrant": s.QDRANT_URL,
"prefix": s.COLLECTION_PREFIX,
"agentic_mode": True
}
# Inkludieren der Router (100% Kompatibilität erhalten)
app.include_router(query_router, prefix="/query", tags=["query"])
app.include_router(graph_router, prefix="/graph", tags=["graph"])
app.include_router(tools_router, prefix="/tools", tags=["tools"])
app.include_router(feedback_router, prefix="/feedback", tags=["feedback"])
# NEU: Chat Endpoint
app.include_router(chat_router, prefix="/chat", tags=["chat"])
# NEU: Ingest Endpoint
app.include_router(chat_router, prefix="/chat", tags=["chat"]) # Nutzt nun WP-25 DecisionEngine
app.include_router(ingest_router, prefix="/ingest", tags=["ingest"])
if admin_router:
@ -54,4 +117,5 @@ def create_app() -> FastAPI:
return app
# Instanziierung der App
app = create_app()

View File

@ -1,10 +1,9 @@
"""
FILE: app/models/dto.py
DESCRIPTION: Pydantic-Modelle (DTOs) für Request/Response Bodies. Definiert das API-Schema.
VERSION: 0.6.7 (WP-Fix: Target Section Support)
VERSION: 0.7.1 (WP-25: Stream-Tracing Support)
STATUS: Active
DEPENDENCIES: pydantic, typing, uuid
LAST_ANALYSIS: 2025-12-29
"""
from __future__ import annotations
@ -12,8 +11,14 @@ from pydantic import BaseModel, Field
from typing import List, Literal, Optional, Dict, Any
import uuid
# Gültige Kanten-Typen gemäß Manual
EdgeKind = Literal["references", "references_at", "backlink", "next", "prev", "belongs_to", "depends_on", "related_to", "similar_to", "caused_by", "derived_from", "based_on", "solves", "blocks", "uses", "guides"]
# WP-25: Erweiterte Kanten-Typen gemäß neuer decision_engine.yaml
EdgeKind = Literal[
"references", "references_at", "backlink", "next", "prev",
"belongs_to", "depends_on", "related_to", "similar_to",
"caused_by", "derived_from", "based_on", "solves", "blocks",
"uses", "guides", "enforced_by", "implemented_in", "part_of",
"experienced_in", "impacts", "risk_of"
]
# --- Basis-DTOs ---
@ -43,14 +48,14 @@ class EdgeDTO(BaseModel):
direction: Literal["out", "in", "undirected"] = "out"
provenance: Optional[Literal["explicit", "rule", "smart", "structure"]] = "explicit"
confidence: float = 1.0
target_section: Optional[str] = None # Neu: Speichert den Anker (z.B. #Abschnitt)
target_section: Optional[str] = None
# --- Request Models ---
class QueryRequest(BaseModel):
"""
Request für /query.
Request für /query. Unterstützt Multi-Stream Isolation via filters.
"""
mode: Literal["semantic", "edge", "hybrid"] = "hybrid"
query: Optional[str] = None
@ -61,14 +66,12 @@ class QueryRequest(BaseModel):
ret: Dict = {"with_paths": True, "with_notes": True, "with_chunks": True}
explain: bool = False
# WP-22: Semantic Graph Routing
# WP-22/25: Dynamische Gewichtung der Graphen-Highways
boost_edges: Optional[Dict[str, float]] = None
class FeedbackRequest(BaseModel):
"""
User-Feedback zu einem spezifischen Treffer oder der Gesamtantwort (WP-08 Basis).
"""
"""User-Feedback zu einem spezifischen Treffer oder der Gesamtantwort."""
query_id: str = Field(..., description="ID der ursprünglichen Suche")
node_id: str = Field(..., description="ID des bewerteten Treffers oder 'generated_answer'")
score: int = Field(..., ge=1, le=5, description="1 (Irrelevant) bis 5 (Perfekt)")
@ -76,16 +79,14 @@ class FeedbackRequest(BaseModel):
class ChatRequest(BaseModel):
"""
WP-05: Request für /chat.
"""
"""Request für /chat (WP-25 Einstieg)."""
message: str = Field(..., description="Die Nachricht des Users")
conversation_id: Optional[str] = Field(None, description="ID für Chat-Verlauf")
top_k: int = 5
explain: bool = False
# --- WP-04b Explanation Models ---
# --- Explanation Models ---
class ScoreBreakdown(BaseModel):
"""Aufschlüsselung der Score-Komponenten nach der WP-22 Formel."""
@ -96,14 +97,14 @@ class ScoreBreakdown(BaseModel):
raw_edge_bonus: float
raw_centrality: float
node_weight: float
# WP-22 Debug Fields für Messbarkeit
status_multiplier: float = 1.0
graph_boost_factor: float = 1.0
class Reason(BaseModel):
"""Ein semantischer Grund für das Ranking."""
kind: Literal["semantic", "edge", "type", "centrality", "lifecycle"]
# WP-25: 'status' hinzugefügt für Synchronität mit retriever.py
kind: Literal["semantic", "edge", "type", "centrality", "lifecycle", "status"]
message: str
score_impact: Optional[float] = None
details: Optional[Dict[str, Any]] = None
@ -114,7 +115,6 @@ class Explanation(BaseModel):
breakdown: ScoreBreakdown
reasons: List[Reason]
related_edges: Optional[List[EdgeDTO]] = None
# WP-22 Debug: Verifizierung des Routings
applied_intent: Optional[str] = None
applied_boosts: Optional[Dict[str, float]] = None
@ -122,7 +122,10 @@ class Explanation(BaseModel):
# --- Response Models ---
class QueryHit(BaseModel):
"""Einzelnes Trefferobjekt für /query."""
"""
Einzelnes Trefferobjekt.
WP-25: stream_origin hinzugefügt für Tracing und Feedback-Optimierung.
"""
node_id: str
note_id: str
semantic_score: float
@ -133,10 +136,11 @@ class QueryHit(BaseModel):
source: Optional[Dict] = None
payload: Optional[Dict] = None
explanation: Optional[Explanation] = None
stream_origin: Optional[str] = Field(None, description="Name des Ursprungs-Streams")
class QueryResponse(BaseModel):
"""Antwortstruktur für /query."""
"""Antwortstruktur für /query (wird von DecisionEngine Streams genutzt)."""
query_id: str = Field(default_factory=lambda: str(uuid.uuid4()))
results: List[QueryHit]
used_mode: str
@ -153,11 +157,12 @@ class GraphResponse(BaseModel):
class ChatResponse(BaseModel):
"""
WP-05/06: Antwortstruktur für /chat.
Antwortstruktur für /chat.
WP-25: 'intent' spiegelt nun die gewählte Strategie wider.
"""
query_id: str = Field(..., description="Traceability ID")
answer: str = Field(..., description="Generierte Antwort vom LLM")
sources: List[QueryHit] = Field(..., description="Die genutzten Quellen")
sources: List[QueryHit] = Field(..., description="Die genutzten Quellen (alle Streams)")
latency_ms: int
intent: Optional[str] = Field("FACT", description="WP-06: Erkannter Intent")
intent_source: Optional[str] = Field("Unknown", description="Quelle der Intent-Erkennung")
intent: Optional[str] = Field("FACT", description="Die gewählte WP-25 Strategie")
intent_source: Optional[str] = Field("LLM_Router", description="Quelle der Intent-Erkennung")

View File

@ -1,12 +1,15 @@
"""
FILE: app/routers/chat.py
DESCRIPTION: Haupt-Chat-Interface (RAG & Interview). Enthält Intent-Router (Keywords/LLM) und Prompt-Construction.
VERSION: 2.7.8 (Full Unabridged Stability Edition)
DESCRIPTION: Haupt-Chat-Interface (WP-25 Agentic Edition).
Kombiniert die spezialisierte Interview-Logik und Keyword-Erkennung
mit der neuen Multi-Stream Orchestrierung der DecisionEngine.
VERSION: 3.0.2
STATUS: Active
FIX:
1. Implementiert Context-Throttling für Ollama (MAX_OLLAMA_CHARS).
2. Deaktiviert LLM-Retries für den Chat (max_retries=0).
3. Behebt Double-Fallback-Schleifen und Silent Refusals.
- 100% Wiederherstellung der v2.7.8 Logik (Interview, Schema-Resolution, Keywords).
- Integration der DecisionEngine für paralleles RAG-Retrieval.
- Erhalt der Ollama Context-Throttling Parameter (WP-20).
- Beibehaltung der No-Retry Logik (max_retries=0) für Chat-Stabilität.
"""
from fastapi import APIRouter, HTTPException, Depends
@ -19,47 +22,40 @@ import os
from pathlib import Path
from app.config import get_settings
from app.models.dto import ChatRequest, ChatResponse, QueryRequest, QueryHit
from app.models.dto import ChatRequest, ChatResponse, QueryHit
from app.services.llm_service import LLMService
from app.core.retrieval.retriever import Retriever
from app.services.feedback_service import log_search
router = APIRouter()
logger = logging.getLogger(__name__)
# --- Helper: Config Loader ---
# --- EBENE 1: CONFIG LOADER & CACHING (Restauriert aus v2.7.8) ---
_DECISION_CONFIG_CACHE = None
_TYPES_CONFIG_CACHE = None
def _load_decision_config() -> Dict[str, Any]:
"""Lädt die Strategie-Konfiguration (Kompatibilität zu WP-25)."""
settings = get_settings()
path = Path(settings.DECISION_CONFIG_PATH)
default_config = {
"strategies": {
"FACT": {"trigger_keywords": [], "preferred_provider": "openrouter"}
}
}
if not path.exists():
logger.warning(f"Decision config not found at {path}, using defaults.")
return default_config
try:
with open(path, "r", encoding="utf-8") as f:
return yaml.safe_load(f)
if path.exists():
with open(path, "r", encoding="utf-8") as f:
return yaml.safe_load(f) or {}
except Exception as e:
logger.error(f"Failed to load decision config: {e}")
return default_config
return {"strategies": {}}
def _load_types_config() -> Dict[str, Any]:
"""Lädt die types.yaml für Keyword-Erkennung."""
"""Lädt die types.yaml für die Typerkennung im Interview-Modus."""
path = os.getenv("MINDNET_TYPES_FILE", "config/types.yaml")
try:
with open(path, "r", encoding="utf-8") as f:
return yaml.safe_load(f) or {}
except Exception:
return {}
if os.path.exists(path):
with open(path, "r", encoding="utf-8") as f:
return yaml.safe_load(f) or {}
except Exception as e:
logger.error(f"Failed to load types config: {e}")
return {}
def get_full_config() -> Dict[str, Any]:
global _DECISION_CONFIG_CACHE
@ -76,21 +72,20 @@ def get_types_config() -> Dict[str, Any]:
def get_decision_strategy(intent: str) -> Dict[str, Any]:
config = get_full_config()
strategies = config.get("strategies", {})
return strategies.get(intent, strategies.get("FACT", {}))
return strategies.get(intent, strategies.get("FACT_WHAT", {}))
# --- Helper: Target Type Detection (WP-07) ---
# --- EBENE 2: SPEZIAL-LOGIK (INTERVIEW & DETECTION) ---
def _detect_target_type(message: str, configured_schemas: Dict[str, Any]) -> str:
"""
Versucht zu erraten, welchen Notiz-Typ der User erstellen will.
Nutzt Keywords aus types.yaml UND Mappings.
WP-07: Identifiziert den gewünschten Notiz-Typ (Keyword-basiert).
100% identisch mit v2.7.8 zur Sicherstellung des Interview-Workflows.
"""
message_lower = message.lower()
# 1. Check types.yaml detection_keywords (Priority!)
types_cfg = get_types_config()
types_def = types_cfg.get("types", {})
# 1. Check types.yaml detection_keywords
for type_name, type_data in types_def.items():
keywords = type_data.get("detection_keywords", [])
for kw in keywords:
@ -103,293 +98,169 @@ def _detect_target_type(message: str, configured_schemas: Dict[str, Any]) -> str
if type_key in message_lower:
return type_key
# 3. Synonym-Mapping (Legacy Fallback)
# 3. Synonym-Mapping (Legacy)
synonyms = {
"projekt": "project", "vorhaben": "project",
"entscheidung": "decision", "beschluss": "decision",
"ziel": "goal",
"erfahrung": "experience", "lektion": "experience",
"wert": "value",
"prinzip": "principle",
"notiz": "default", "idee": "default"
"projekt": "project", "entscheidung": "decision", "ziel": "goal",
"erfahrung": "experience", "wert": "value", "prinzip": "principle"
}
for term, schema_key in synonyms.items():
if term in message_lower:
return schema_key
return "default"
# --- Dependencies ---
def get_llm_service():
return LLMService()
def get_retriever():
return Retriever()
# --- Logic ---
def _build_enriched_context(hits: List[QueryHit]) -> str:
context_parts = []
for i, hit in enumerate(hits, 1):
source = hit.source or {}
content = (
source.get("text") or source.get("content") or
source.get("page_content") or source.get("chunk_text") or
"[Kein Text]"
)
title = hit.note_id or "Unbekannt"
payload = hit.payload or {}
note_type = payload.get("type") or source.get("type", "unknown")
note_type = str(note_type).upper()
entry = (
f"### QUELLE {i}: {title}\n"
f"TYP: [{note_type}] (Score: {hit.total_score:.2f})\n"
f"INHALT:\n{content}\n"
)
context_parts.append(entry)
return "\n\n".join(context_parts)
def _is_question(query: str) -> bool:
"""Prüft, ob der Input wahrscheinlich eine Frage ist."""
"""Prüft, ob der Input eine Frage ist (W-Fragen Erkennung)."""
q = query.strip().lower()
if "?" in q: return True
# W-Fragen Indikatoren
starters = ["wer", "wie", "was", "wo", "wann", "warum", "weshalb", "wozu", "welche", "bist du", "entspricht"]
if any(q.startswith(s + " ") for s in starters):
return True
return False
starters = ["wer", "wie", "was", "wo", "wann", "warum", "weshalb", "wozu", "welche", "bist du"]
return any(q.startswith(s + " ") for s in starters)
async def _classify_intent(query: str, llm: LLMService) -> tuple[str, str]:
"""
Hybrid Router v5:
1. Decision Keywords (Strategie) -> Prio 1
2. Type Keywords (Interview Trigger) -> Prio 2
3. LLM (Fallback) -> Prio 3
WP-25 Hybrid Router:
Nutzt erst Keyword-Fast-Paths (Router) und delegiert dann an die DecisionEngine.
"""
config = get_full_config()
strategies = config.get("strategies", {})
settings = config.get("settings", {})
query_lower = query.lower()
# 1. FAST PATH A: Strategie Keywords
# 1. FAST PATH: Keyword Trigger
for intent_name, strategy in strategies.items():
if intent_name == "FACT": continue
keywords = strategy.get("trigger_keywords", [])
for k in keywords:
if k.lower() in query_lower:
return intent_name, "Keyword (Strategy)"
return intent_name, "Keyword (FastPath)"
# 2. FAST PATH B: Type Keywords -> INTERVIEW
if not _is_question(query_lower):
types_cfg = get_types_config()
types_def = types_cfg.get("types", {})
for type_name, type_data in types_def.items():
keywords = type_data.get("detection_keywords", [])
for kw in keywords:
for type_name, type_data in types_cfg.get("types", {}).items():
for kw in type_data.get("detection_keywords", []):
if kw.lower() in query_lower:
return "INTERVIEW", f"Keyword (Type: {type_name})"
return "INTERVIEW", "Keyword (Interview)"
# 3. SLOW PATH: LLM Router
if settings.get("llm_fallback_enabled", False):
router_prompt_template = llm.get_prompt("llm_router_prompt")
if router_prompt_template:
prompt = router_prompt_template.replace("{query}", query)
logger.info("Keywords failed (or Question detected). Asking LLM for Intent...")
try:
# FIX: Auch beim Routing keine Retries im Chat-Fluss
raw_response = await llm.generate_raw_response(prompt, priority="realtime", max_retries=0)
llm_output_upper = raw_response.upper()
if "INTERVIEW" in llm_output_upper or "CREATE" in llm_output_upper:
return "INTERVIEW", "LLM Router"
# 3. SLOW PATH: DecisionEngine LLM Router
intent = await llm.decision_engine._determine_strategy(query)
return intent, "DecisionEngine (LLM)"
for strat_key in strategies.keys():
if strat_key in llm_output_upper:
return strat_key, "LLM Router"
except Exception as e:
logger.error(f"Router LLM failed: {e}")
return "FACT", "Default (No Match)"
# --- EBENE 3: RETRIEVAL AGGREGATION ---
def _collect_all_hits(stream_responses: Dict[str, Any]) -> List[QueryHit]:
"""Sammelt und dedupliziert Treffer aus allen parallelen Streams."""
all_hits = []
seen_node_ids = set()
for _, response in stream_responses.items():
if hasattr(response, 'results'):
for hit in response.results:
if hit.node_id not in seen_node_ids:
all_hits.append(hit)
seen_node_ids.add(hit.node_id)
return sorted(all_hits, key=lambda h: h.total_score, reverse=True)
# --- EBENE 4: ENDPUNKT ---
def get_llm_service():
return LLMService()
@router.post("/", response_model=ChatResponse)
async def chat_endpoint(
request: ChatRequest,
llm: LLMService = Depends(get_llm_service),
retriever: Retriever = Depends(get_retriever)
llm: LLMService = Depends(get_llm_service)
):
start_time = time.time()
query_id = str(uuid.uuid4())
logger.info(f"Chat request [{query_id}]: {request.message[:50]}...")
settings = get_settings()
logger.info(f"🚀 [WP-25] Chat request [{query_id}]: {request.message[:50]}...")
try:
# 1. Intent Detection
intent, intent_source = await _classify_intent(request.message, llm)
logger.info(f"[{query_id}] Final Intent: {intent} via {intent_source}")
logger.info(f"[{query_id}] Intent: {intent} via {intent_source}")
# Strategy Load
strategy = get_decision_strategy(intent)
prompt_key = strategy.get("prompt_template", "rag_template")
preferred_provider = strategy.get("preferred_provider")
engine = llm.decision_engine
sources_hits = []
final_prompt = ""
context_str = ""
answer_text = ""
# 2. INTERVIEW MODE (Kompatibilität zu v2.7.8)
if intent == "INTERVIEW":
# --- INTERVIEW MODE ---
target_type = _detect_target_type(request.message, strategy.get("schemas", {}))
types_cfg = get_types_config()
type_def = types_cfg.get("types", {}).get(target_type, {})
fields_list = type_def.get("schema", [])
if not fields_list:
configured_schemas = strategy.get("schemas", {})
fallback_schema = configured_schemas.get(target_type, configured_schemas.get("default"))
if isinstance(fallback_schema, dict):
fields_list = fallback_schema.get("fields", [])
else:
fields_list = fallback_schema or []
fallback = configured_schemas.get(target_type, configured_schemas.get("default", {}))
fields_list = fallback.get("fields", []) if isinstance(fallback, dict) else (fallback or [])
logger.info(f"[{query_id}] Interview Type: {target_type}. Fields: {len(fields_list)}")
fields_str = "\n- " + "\n- ".join(fields_list)
template = llm.get_prompt(strategy.get("prompt_template", "interview_template"))
template = llm.get_prompt(prompt_key)
final_prompt = template.replace("{context_str}", "Dialogverlauf...") \
.replace("{query}", request.message) \
final_prompt = template.replace("{query}", request.message) \
.replace("{target_type}", target_type) \
.replace("{schema_fields}", fields_str) \
.replace("{schema_hint}", "")
sources_hits = []
.replace("{schema_fields}", fields_str)
else:
# --- RAG MODE (FACT, DECISION, EMPATHY, CODING) ---
inject_types = strategy.get("inject_types", [])
prepend_instr = strategy.get("prepend_instruction", "")
edge_boosts = strategy.get("edge_boosts", {})
query_req = QueryRequest(
query=request.message,
mode="hybrid",
top_k=request.top_k,
explain=request.explain,
boost_edges=edge_boosts
)
retrieve_result = await retriever.search(query_req)
hits = retrieve_result.results
if inject_types:
strategy_req = QueryRequest(
query=request.message,
mode="hybrid",
top_k=3,
filters={"type": inject_types},
explain=False,
boost_edges=edge_boosts
)
strategy_result = await retriever.search(strategy_req)
existing_ids = {h.node_id for h in hits}
for strat_hit in strategy_result.results:
if strat_hit.node_id not in existing_ids:
hits.append(strat_hit)
context_str = _build_enriched_context(hits) if hits else "Keine relevanten Notizen gefunden."
# --- STABILITY FIX: OLLAMA CONTEXT THROTTLE ---
# Begrenzt den Text, um den "decode: cannot decode batches" Fehler zu vermeiden.
# MAX_OLLAMA_CHARS = 10000
settings = get_settings() # Falls noch nicht im Scope vorhanden
max_chars = getattr(settings, "MAX_OLLAMA_CHARS", 10000)
if preferred_provider == "ollama" and len(context_str) > max_chars:
logger.warning(f"⚠️ [{query_id}] Context zu groß für Ollama ({len(context_str)} chars). Kürze auf {max_chars}.")
context_str = context_str[:max_chars] + "\n[...gekürzt zur Stabilität...]"
template = llm.get_prompt(prompt_key) or "{context_str}\n\n{query}"
if prepend_instr:
context_str = f"{prepend_instr}\n\n{context_str}"
final_prompt = template.replace("{context_str}", context_str).replace("{query}", request.message)
sources_hits = hits
# --- DEBUG SPOT 1: PROMPT CONSTRUCTION ---
logger.info(f"[{query_id}] PROMPT CONSTRUCTION COMPLETE. Length: {len(final_prompt)} chars.")
if not final_prompt.strip():
logger.error(f"[{query_id}] CRITICAL: Final prompt is empty before sending to LLM!")
# --- GENERATION WITH NO-RETRY & DEEP FALLBACK ---
system_prompt = llm.get_prompt("system_prompt")
# --- DEBUG SPOT 2: PRIMARY CALL ---
logger.info(f"[{query_id}] PRIMARY CALL: Sending request to provider '{preferred_provider}' (No Retries)...")
answer_text = ""
try:
# FIX: max_retries=0 verhindert Hänger durch Retry-Kaskaden im Chat
answer_text = await llm.generate_raw_response(
prompt=final_prompt,
system=system_prompt,
priority="realtime",
provider=preferred_provider,
max_retries=0
final_prompt, system=llm.get_prompt("system_prompt"),
priority="realtime", provider=strategy.get("preferred_provider"), max_retries=0
)
except Exception as e:
logger.error(f"🛑 [{query_id}] Primary Provider '{preferred_provider}' failed: {e}")
sources_hits = []
# DEEP FALLBACK: Wenn die Antwort leer ist (Silent Refusal) oder der Primary abgestürzt ist
if not answer_text.strip() and preferred_provider != "ollama":
# --- DEBUG SPOT 3: FALLBACK TRIGGER ---
logger.warning(f"🛑 [{query_id}] PRIMARY '{preferred_provider}' returned EMPTY or FAILED. Triggering Deep Fallback to Ollama...")
# 3. RAG MODE (WP-25 Multi-Stream)
else:
stream_keys = strategy.get("use_streams", [])
library = engine.config.get("streams_library", {})
try:
answer_text = await llm.generate_raw_response(
prompt=final_prompt,
system=system_prompt,
priority="realtime",
provider="ollama",
max_retries=0
)
except Exception as e:
logger.error(f"🛑 [{query_id}] Deep Fallback to Ollama also failed: {e}")
answer_text = "Entschuldigung, das System ist aktuell überlastet. Bitte versuche es in einem Moment erneut."
tasks = []
active_streams = []
for key in stream_keys:
stream_cfg = library.get(key)
if stream_cfg:
active_streams.append(key)
tasks.append(engine._run_single_stream(key, stream_cfg, request.message))
import asyncio
responses = await asyncio.gather(*tasks, return_exceptions=True)
raw_stream_map = {}
formatted_context_map = {}
max_chars = getattr(settings, "MAX_OLLAMA_CHARS", 10000)
provider = strategy.get("preferred_provider") or settings.MINDNET_LLM_PROVIDER
for name, res in zip(active_streams, responses):
if not isinstance(res, Exception):
raw_stream_map[name] = res
context_text = engine._format_stream_context(res)
# WP-20 Stability Fix: Throttling
if provider == "ollama" and len(context_text) > max_chars:
context_text = context_text[:max_chars] + "\n[...]"
formatted_context_map[name] = context_text
answer_text = await engine._generate_final_answer(
intent, strategy, request.message, formatted_context_map
)
sources_hits = _collect_all_hits(raw_stream_map)
duration_ms = int((time.time() - start_time) * 1000)
# Logging
try:
log_search(
query_id=query_id,
query_text=request.message,
results=sources_hits,
mode="interview" if intent == "INTERVIEW" else "chat_rag",
metadata={"intent": intent, "source": intent_source, "provider": preferred_provider}
query_id=query_id, query_text=request.message, results=sources_hits,
mode=f"wp25_{intent.lower()}", metadata={"strategy": intent, "source": intent_source}
)
except: pass
return ChatResponse(
query_id=query_id,
answer=answer_text,
sources=sources_hits,
latency_ms=duration_ms,
intent=intent,
intent_source=intent_source
query_id=query_id, answer=answer_text, sources=sources_hits,
latency_ms=duration_ms, intent=intent, intent_source=intent_source
)
except Exception as e:
logger.error(f"Error in chat endpoint: {e}", exc_info=True)
# Wir geben eine benutzerfreundliche Meldung zurück, statt nur den Error-Stack
raise HTTPException(status_code=500, detail="Das System konnte die Anfrage nicht verarbeiten.")
logger.error(f"❌ Chat Endpoint Failure: {e}", exc_info=True)
raise HTTPException(status_code=500, detail="Fehler bei der Verarbeitung.")

View File

@ -3,14 +3,14 @@ FILE: app/services/llm_service.py
DESCRIPTION: Hybrid-Client für Ollama, Google GenAI (Gemini) und OpenRouter.
Verwaltet provider-spezifische Prompts und Background-Last.
WP-20: Optimiertes Fallback-Management zum Schutz von Cloud-Quoten.
WP-20 Fix: Bulletproof Prompt-Auflösung für format() Aufrufe.
WP-22/JSON: Optionales JSON-Schema + strict (für OpenRouter structured outputs).
FIX: Intelligente Rate-Limit Erkennung (429 Handling), v1-API Sync & Timeouts.
VERSION: 3.3.9
WP-22/JSON: Optionales JSON-Schema + strict (für OpenRouter).
WP-25: Integration der DecisionEngine für Agentic Multi-Stream RAG.
VERSION: 3.4.2 (WP-25: Ingest-Stability Patch)
STATUS: Active
FIX:
- Importiert clean_llm_text von app.core.registry zur Vermeidung von Circular Imports.
- Wendet clean_llm_text auf Text-Antworten in generate_raw_response an.
- Ingest-Stability: Entfernung des <5-Zeichen Guards (ermöglicht YES/NO Validierungen).
- OpenRouter-Fix: Sicherung gegen leere 'choices' zur Vermeidung von JSON-Errors.
- Erhalt der vollständigen v3.3.9 Logik für Rate-Limits, Retries und Background-Tasks.
"""
import httpx
import yaml
@ -29,7 +29,6 @@ from app.core.registry import clean_llm_text
logger = logging.getLogger(__name__)
class LLMService:
# GLOBALER SEMAPHOR für Hintergrund-Last Steuerung (WP-06)
_background_semaphore = None
@ -37,6 +36,9 @@ class LLMService:
def __init__(self):
self.settings = get_settings()
self.prompts = self._load_prompts()
# WP-25: Lazy Initialization der DecisionEngine zur Vermeidung von Circular Imports
self._decision_engine = None
# Initialisiere Semaphore einmalig auf Klassen-Ebene
if LLMService._background_semaphore is None:
@ -71,6 +73,14 @@ class LLMService:
)
logger.info("🛰️ LLMService: OpenRouter Integration active.")
@property
def decision_engine(self):
"""Lazy Initialization der Decision Engine (WP-25)."""
if self._decision_engine is None:
from app.core.retrieval.decision_engine import DecisionEngine
self._decision_engine = DecisionEngine()
return self._decision_engine
def _load_prompts(self) -> dict:
"""Lädt die Prompt-Konfiguration aus der YAML-Datei."""
path = Path(self.settings.PROMPTS_PATH)
@ -87,17 +97,13 @@ class LLMService:
def get_prompt(self, key: str, provider: str = None) -> str:
"""
Hole provider-spezifisches Template mit intelligenter Text-Kaskade.
HINWEIS: Dies ist nur ein Text-Lookup und verbraucht kein API-Kontingent.
Kaskade: Gewählter Provider -> Gemini (Cloud-Stil) -> Ollama (Basis-Stil).
Kaskade: Gewählter Provider -> Gemini -> Ollama.
"""
active_provider = provider or self.settings.MINDNET_LLM_PROVIDER
data = self.prompts.get(key, "")
if isinstance(data, dict):
# Wir versuchen erst den Provider, dann Gemini, dann Ollama
val = data.get(active_provider, data.get("gemini", data.get("ollama", "")))
# Falls val durch YAML-Fehler immer noch ein Dict ist, extrahiere ersten String
if isinstance(val, dict):
logger.warning(f"⚠️ [LLMService] Nested dictionary detected for key '{key}'. Using first entry.")
val = next(iter(val.values()), "") if val else ""
@ -120,8 +126,8 @@ class LLMService:
strict_json_schema: bool = True
) -> str:
"""
Haupteinstiegspunkt für LLM-Anfragen mit Priorisierung.
Wendet die Bereinigung auf Text-Antworten an.
Haupteinstiegspunkt für LLM-Anfragen.
WP-25 FIX: Schwellenwert entfernt, um kurze Ingest-Validierungen (YES/NO) zu unterstützen.
"""
target_provider = provider or self.settings.MINDNET_LLM_PROVIDER
@ -132,14 +138,18 @@ class LLMService:
max_retries, base_delay, model_override,
json_schema, json_schema_name, strict_json_schema
)
# WP-14 Fix: Bereinige Text-Antworten vor Rückgabe
return clean_llm_text(res) if not force_json else res
else:
res = await self._dispatch(
target_provider, prompt, system, force_json,
max_retries, base_delay, model_override,
json_schema, json_schema_name, strict_json_schema
)
# WP-25 FIX: Nur noch auf absolut leere Antwort prüfen (ermöglicht YES/NO Antworten).
if not res and target_provider != "ollama":
logger.warning(f"⚠️ [WP-25] Empty response from {target_provider}. Falling back to OLLAMA.")
res = await self._execute_ollama(prompt, system, force_json, max_retries, base_delay)
res = await self._dispatch(
target_provider, prompt, system, force_json,
max_retries, base_delay, model_override,
json_schema, json_schema_name, strict_json_schema
)
# WP-14 Fix: Bereinige Text-Antworten vor Rückgabe
return clean_llm_text(res) if not force_json else res
@ -156,12 +166,8 @@ class LLMService:
json_schema_name: str,
strict_json_schema: bool
) -> str:
"""
Routet die Anfrage mit intelligenter Rate-Limit Erkennung.
Nutzt max_retries um die Rate-Limit Schleife zu begrenzen.
"""
"""Routet die Anfrage mit intelligenter Rate-Limit Erkennung."""
rate_limit_attempts = 0
# FIX: Wir nutzen max_retries als Limit für Rate-Limit Versuche, wenn explizit klein gewählt (z.B. Chat)
max_rate_retries = min(max_retries, getattr(self.settings, "LLM_RATE_LIMIT_RETRIES", 3))
wait_time = getattr(self.settings, "LLM_RATE_LIMIT_WAIT", 60.0)
@ -181,33 +187,24 @@ class LLMService:
if provider == "gemini" and self.google_client:
return await self._execute_google(prompt, system, force_json, model_override)
# Default/Fallback zu Ollama
return await self._execute_ollama(prompt, system, force_json, max_retries, base_delay)
except Exception as e:
err_str = str(e)
# Intelligente 429 Erkennung
is_rate_limit = any(x in err_str for x in ["429", "RESOURCE_EXHAUSTED", "rate_limited", "Too Many Requests"])
if is_rate_limit and rate_limit_attempts < max_rate_retries:
rate_limit_attempts += 1
logger.warning(
f"⏳ [LLMService] Rate Limit detected from {provider}. "
f"Attempt {rate_limit_attempts}/{max_rate_retries}. Waiting {wait_time}s..."
)
logger.warning(f"⏳ Rate Limit from {provider}. Attempt {rate_limit_attempts}. Waiting {wait_time}s...")
await asyncio.sleep(wait_time)
continue
# Wenn kein Rate-Limit oder Retries erschöpft -> Fallback zu Ollama (falls aktiviert)
if self.settings.LLM_FALLBACK_ENABLED and provider != "ollama":
logger.warning(
f"🔄 Provider {provider} failed ({err_str}). Falling back to LOCAL OLLAMA."
)
logger.warning(f"🔄 Provider {provider} failed ({err_str}). Falling back to OLLAMA.")
return await self._execute_ollama(prompt, system, force_json, max_retries, base_delay)
raise e
async def _execute_google(self, prompt, system, force_json, model_override):
"""Native Google SDK Integration (Gemini) mit v1 Fix."""
model = model_override or self.settings.GEMINI_MODEL
clean_model = model.replace("models/", "")
@ -234,7 +231,7 @@ class LLMService:
json_schema_name: str = "mindnet_json",
strict_json_schema: bool = True
) -> str:
"""OpenRouter API Integration (OpenAI-kompatibel)."""
"""OpenRouter API Integration. WP-25 FIX: Sicherung gegen leere 'choices'."""
model = model_override or self.settings.OPENROUTER_MODEL
messages = []
if system:
@ -247,9 +244,7 @@ class LLMService:
kwargs["response_format"] = {
"type": "json_schema",
"json_schema": {
"name": json_schema_name,
"strict": strict_json_schema,
"schema": json_schema
"name": json_schema_name, "strict": strict_json_schema, "schema": json_schema
}
}
else:
@ -260,23 +255,23 @@ class LLMService:
messages=messages,
**kwargs
)
return response.choices[0].message.content.strip()
# WP-25 FIX: Sicherung gegen leere Antwort-Arrays
if not response.choices or len(response.choices) == 0:
logger.warning(f"🛰️ OpenRouter returned no choices for model {model}")
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):
"""Lokaler Ollama Call mit striktem Retry-Limit."""
payload = {
"model": self.settings.LLM_MODEL,
"prompt": prompt,
"stream": False,
"options": {
"temperature": 0.1 if force_json else 0.7,
"num_ctx": 8192 # Begrenzung für Stabilität (WP-20)
}
"options": {"temperature": 0.1 if force_json else 0.7, "num_ctx": 8192}
}
if force_json:
payload["format"] = "json"
if system:
payload["system"] = system
if force_json: payload["format"] = "json"
if system: payload["system"] = system
attempt = 0
while True:
@ -286,32 +281,17 @@ class LLMService:
return res.json().get("response", "").strip()
except Exception as e:
attempt += 1
# WICHTIG: Wenn max_retries=0 (Chat), bricht dies nach dem 1. Versuch (attempt=1) sofort ab.
if attempt > max_retries:
logger.error(f"❌ Ollama request failed after {attempt} attempt(s): {e}")
logger.error(f"❌ Ollama request failed: {e}")
raise e
wait_time = base_delay * (2 ** (attempt - 1))
logger.warning(f"⚠️ Ollama attempt {attempt} failed. Retrying in {wait_time}s...")
await asyncio.sleep(wait_time)
async def generate_rag_response(self, query: str, context_str: str) -> str:
"""Vollständiges RAG Chat-Interface."""
provider = self.settings.MINDNET_LLM_PROVIDER
system_prompt = self.get_prompt("system_prompt", provider)
rag_template = self.get_prompt("rag_template", provider)
final_prompt = rag_template.format(context_str=context_str, query=query)
# RAG Aufrufe im Chat nutzen nun standardmäßig max_retries=2 (überschreibbar)
# Durch den Aufruf von generate_raw_response wird die Bereinigung automatisch angewendet.
return await self.generate_raw_response(
final_prompt,
system=system_prompt,
priority="realtime"
)
async def generate_rag_response(self, query: str, context_str: Optional[str] = None) -> str:
"""WP-25: Orchestrierung via DecisionEngine."""
logger.info(f"🚀 [WP-25] Chat Query: {query[:50]}...")
return await self.decision_engine.ask(query)
async def close(self):
"""Schließt die HTTP-Verbindungen."""
if self.ollama_client:
await self.ollama_client.aclose()

View File

@ -1,145 +1,144 @@
# config/decision_engine.yaml
# Steuerung der Decision Engine (Intent Recognition & Graph Routing)
# VERSION: 2.6.1 (WP-20: Hybrid LLM & WP-22: Semantic Graph Routing)
# VERSION: 3.1.6 (WP-25: Multi-Stream Agentic RAG - Final Release)
# STATUS: Active
# DoD: Keine Hardcoded Modelle, volle Integration der strategischen Boosts.
# DoD:
# - Strikte Nutzung der Typen aus types.yaml (v2.7.0).
# - Fix für Projekt-Klassifizierung via Keyword-Fast-Path (Auflösung Kollision).
# - 100% Erhalt aller Stream-Parameter und Edge-Boosts.
version: 2.6
version: 3.1
settings:
llm_fallback_enabled: true
# Strategie für den Router selbst (Welches Modell erkennt den Intent?)
# "auto" nutzt den in MINDNET_LLM_PROVIDER gesetzten Standard (z.B. openrouter).
# "auto" nutzt den in MINDNET_LLM_PROVIDER gesetzten Standard.
router_provider: "auto"
# Verweist auf das Template in prompts.yaml
router_prompt_key: "intent_router_v1"
# Few-Shot Prompting für den LLM-Router
llm_router_prompt: |
Du bist der zentrale Intent-Klassifikator für Mindnet, einen digitalen Zwilling.
Analysiere die Nachricht und wähle die passende Strategie.
Antworte NUR mit dem Namen der Strategie.
STRATEGIEN:
- INTERVIEW: User will Wissen erfassen, Notizen anlegen oder Dinge festhalten.
- DECISION: Rat, Strategie, Abwägung von Werten, "Soll ich tun X?".
- EMPATHY: Gefühle, Reflexion der eigenen Verfassung, Frust, Freude.
- CODING: Code-Erstellung, Debugging, technische Dokumentation.
- FACT: Reine Wissensabfrage, Definitionen, Suchen von Informationen.
BEISPIELE:
User: "Wie funktioniert die Qdrant-Vektor-DB?" -> FACT
User: "Soll ich mein Startup jetzt verkaufen?" -> DECISION
User: "Notiere mir kurz meine Gedanken zum Meeting." -> INTERVIEW
User: "Ich fühle mich heute sehr erschöpft." -> EMPATHY
User: "Schreibe eine FastAPI-Route für den Ingest." -> CODING
NACHRICHT: "{query}"
STRATEGIE:
# --- EBENE 1: STREAM-LIBRARY (Bausteine basierend auf types.yaml) ---
# Synchronisiert mit types.yaml v2.7.0
strategies:
# 1. Fakten-Abfrage (Turbo-Modus via OpenRouter / Primary)
FACT:
description: "Reine Wissensabfrage."
preferred_provider: "openrouter"
trigger_keywords: []
inject_types: []
# WP-22: Definitionen & Hierarchien im Graphen bevorzugen
streams_library:
values_stream:
name: "Identität & Ethik"
query_template: "Welche meiner Werte und Prinzipien betreffen: {query}"
# Nur Typen aus types.yaml
filter_types: ["value", "principle", "belief", "trait", "boundary", "need", "motivation"]
top_k: 5
edge_boosts:
guides: 3.0
enforced_by: 2.5
based_on: 2.0
facts_stream:
name: "Operative Realität"
query_template: "Status, Ressourcen und Fakten zu: {query}"
# Nur Typen aus types.yaml
filter_types: ["project", "decision", "task", "goal", "event", "state"]
top_k: 5
edge_boosts:
part_of: 2.0
composed_of: 2.0
similar_to: 1.5
caused_by: 0.5
prompt_template: "rag_template"
prepend_instruction: null
depends_on: 1.5
implemented_in: 1.5
# 2. Entscheidungs-Frage (Power-Strategie via Gemini)
DECISION:
description: "Der User sucht Rat, Strategie oder Abwägung."
preferred_provider: "gemini"
trigger_keywords:
- "soll ich"
- "meinung"
- "besser"
- "empfehlung"
- "strategie"
- "entscheidung"
- "abwägung"
- "vergleich"
inject_types: ["value", "principle", "goal", "risk"]
# WP-22: Risiken und Konsequenzen im Graphen priorisieren
biography_stream:
name: "Persönliche Erfahrung"
query_template: "Welche Erlebnisse habe ich im Kontext von {query} gemacht?"
# Nur Typen aus types.yaml
filter_types: ["experience", "journal", "profile", "person"]
top_k: 3
edge_boosts:
related_to: 1.5
experienced_in: 2.0
risk_stream:
name: "Risiko-Radar"
query_template: "Gefahren, Hindernisse oder Risiken bei: {query}"
# Nur Typen aus types.yaml
filter_types: ["risk", "obstacle", "bias"]
top_k: 3
edge_boosts:
blocks: 2.5
solves: 2.0
depends_on: 1.5
risk_of: 2.5
impacts: 2.0
prompt_template: "decision_template"
prepend_instruction: |
!!! ENTSCHEIDUNGS-MODUS (HYBRID AI) !!!
BITTE WÄGE FAKTEN GEGEN FOLGENDE WERTE, PRINZIPIEN UND ZIELE AB:
risk_of: 2.5
# 3. Empathie / "Ich"-Modus (Lokal & Privat via Ollama)
EMPATHY:
description: "Reaktion auf emotionale Zustände."
preferred_provider: "openrouter"
trigger_keywords:
- "ich fühle"
- "traurig"
- "glücklich"
- "gestresst"
- "angst"
- "nervt"
- "überfordert"
- "müde"
inject_types: ["experience", "belief", "profile"]
edge_boosts:
based_on: 2.0
related_to: 2.0
experienced_in: 2.5
blocks: 0.1
prompt_template: "empathy_template"
prepend_instruction: null
# 4. Coding / Technical (Gemini Power)
CODING:
description: "Technische Anfragen und Programmierung."
preferred_provider: "gemini"
trigger_keywords:
- "code"
- "python"
- "script"
- "funktion"
- "bug"
- "syntax"
- "json"
- "yaml"
- "bash"
inject_types: ["snippet", "reference", "source"]
# WP-22: Technische Abhängigkeiten priorisieren
tech_stream:
name: "Wissen & Technik"
query_template: "Inhaltliche Details und Definitionen zu: {query}"
# Nur Typen aus types.yaml
filter_types: ["concept", "source", "glossary", "idea", "insight", "skill", "habit"]
top_k: 5
edge_boosts:
uses: 2.5
depends_on: 2.0
implemented_in: 3.0
prompt_template: "technical_template"
prepend_instruction: null
# 5. Interview / Datenerfassung (Lokal)
# --- EBENE 2: STRATEGIEN (Komposition & Routing) ---
# Orchestriert das Zusammenspiel der Streams basierend auf dem Intent.
strategies:
# Spezialisierte Fact-Strategie für zeitliche Fragen
FACT_WHEN:
description: "Abfrage von exakten Zeitpunkten und Terminen."
preferred_provider: "openrouter"
# FAST PATH: Harte Keywords für zeitliche Fragen
trigger_keywords: ["wann", "datum", "uhrzeit", "zeitpunkt"]
use_streams:
- "facts_stream"
- "biography_stream"
- "tech_stream"
prompt_template: "fact_synthesis_v1"
# Spezialisierte Fact-Strategie für inhaltliche Fragen & Listen
FACT_WHAT:
description: "Abfrage von Definitionen, Listen und Inhalten."
preferred_provider: "openrouter"
# FIX v3.1.6: "projekt" entfernt, um Kollision mit DECISION ("Soll ich Projekt...") zu vermeiden.
trigger_keywords: ["was ist", "welche sind", "liste", "übersicht", "zusammenfassung"]
use_streams:
- "facts_stream"
- "tech_stream"
- "biography_stream"
prompt_template: "fact_synthesis_v1"
# Entscheidungs-Frage
DECISION:
description: "Der User sucht Rat, Strategie oder Abwägung."
preferred_provider: "gemini"
# FIX v3.1.6: Trigger erweitert, um "Soll ich... Projekt..." sicher zu fangen.
trigger_keywords: ["soll ich", "sollte ich", "entscheidung", "abwägen", "priorität", "empfehlung"]
use_streams:
- "values_stream"
- "facts_stream"
- "risk_stream"
prompt_template: "decision_synthesis_v1"
prepend_instruction: |
!!! ENTSCHEIDUNGS-MODUS (AGENTIC MULTI-STREAM) !!!
Analysiere die Fakten vor dem Hintergrund meiner Werte und evaluiere die Risiken.
Wäge ab, ob das Vorhaben mit meiner langfristigen Identität kompatibel ist.
# Emotionale Reflexion
EMPATHY:
description: "Reaktion auf emotionale Zustände."
preferred_provider: "openrouter"
trigger_keywords: ["fühle", "traurig", "glücklich", "stress", "angst"]
use_streams:
- "biography_stream"
- "values_stream"
prompt_template: "empathy_template"
# Technischer Support
CODING:
description: "Technische Anfragen und Programmierung."
preferred_provider: "gemini"
trigger_keywords: ["code", "python", "script", "bug", "syntax"]
use_streams:
- "tech_stream"
- "facts_stream"
prompt_template: "technical_template"
# Eingabe-Modus (WP-07)
INTERVIEW:
description: "Der User möchte Wissen erfassen."
preferred_provider: "openrouter"
trigger_keywords:
- "neue notiz"
- "etwas notieren"
- "festhalten"
- "erstellen"
- "dokumentieren"
- "anlegen"
- "interview"
- "erfassen"
- "idee speichern"
- "draft"
inject_types: []
edge_boosts: {}
prompt_template: "interview_template"
prepend_instruction: null
description: "Der User möchte Wissen erfassen (Eingabemodus)."
preferred_provider: "openrouter"
use_streams: []
prompt_template: "interview_template"

View File

@ -1,8 +1,9 @@
# config/prompts.yaml — Final V2.6.0 (WP-15b Candidate-Validation)
# WP-20: Optimierte Cloud-Templates zur Unterdrückung von Modell-Geschwätz.
# FIX: Explizite Verbote für Einleitungstexte zur Vermeidung von JSON-Parsing-Fehlern.
# WP-15b: Integration der binären edge_validation für den Two-Pass Workflow.
# OLLAMA: UNVERÄNDERT laut Benutzeranweisung.
# 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.
@ -17,39 +18,57 @@ system_prompt: |
3. Antworte auf Deutsch (außer bei Code/Fachbegriffen).
# ---------------------------------------------------------
# 1. STANDARD: Fakten & Wissen (Intent: FACT)
# 1. STANDARD: Fakten & Wissen (Intent: FACT_WHAT / FACT_WHEN)
# ---------------------------------------------------------
rag_template:
# Ersetzt das alte 'rag_template'. Nutzt jetzt parallele Streams.
fact_synthesis_v1:
ollama: |
QUELLEN (WISSEN):
WISSENS-STREAMS:
=========================================
{context_str}
FAKTEN & STATUS:
{facts_stream}
ERFAHRUNG & BIOGRAFIE:
{biography_stream}
WISSEN & TECHNIK:
{tech_stream}
=========================================
FRAGE:
{query}
ANWEISUNG:
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.
Fasse die Informationen zusammen. Sei objektiv und neutral.
gemini: |
Kontext meines digitalen Zwillings: {context_str}
Beantworte strukturiert und präzise: {query}
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: |
Kontext-Analyse für den digitalen Zwilling:
{context_str}
Anfrage: {query}
Antworte basierend auf dem Kontext.
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)
# ---------------------------------------------------------
decision_template:
# Ersetzt das alte 'decision_template'. Nutzt jetzt parallele Streams.
decision_synthesis_v1:
ollama: |
KONTEXT (FAKTEN & STRATEGIE):
ENTSCHEIDUNGS-STREAMS:
=========================================
{context_str}
WERTE & PRINZIPIEN (Identität):
{values_stream}
OPERATIVE FAKTEN (Realität):
{facts_stream}
RISIKO-RADAR (Konsequenzen):
{risk_stream}
=========================================
ENTSCHEIDUNGSFRAGE:
@ -58,7 +77,7 @@ decision_template:
ANWEISUNG:
Du agierst als mein Entscheidungs-Partner.
1. Analysiere die Faktenlage aus den Quellen.
2. Prüfe dies hart gegen meine strategischen Notizen (Typ [VALUE], [PRINCIPLE], [GOAL]).
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:
@ -66,19 +85,26 @@ decision_template:
- **Abgleich:** (Gibt es Konflikte mit Werten/Zielen? Nenne die Quelle!)
- **Empfehlung:** (Klare Meinung: Ja/No/Vielleicht mit Begründung)
gemini: |
Agiere als strategischer Partner. Analysiere die Frage {query} basierend auf meinen Werten im Kontext {context_str}.
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 Entscheidungsanalyse: {query}
Wertebasis aus dem Graphen: {context_str}
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 & GLAUBENSSÄTZE):
KONTEXT (ERFAHRUNGEN & WERTE):
=========================================
{context_str}
ERLEBNISSE & BIOGRAFIE:
{biography_stream}
WERTE & BEDÜRFNISSE:
{values_stream}
=========================================
SITUATION:
@ -87,22 +113,26 @@ empathy_template:
ANWEISUNG:
Du agierst jetzt als mein empathischer Spiegel.
1. Versuche nicht sofort, das Problem technisch zu lösen.
2. Zeige Verständnis für die Situation basierend auf meinen eigenen Erfahrungen ([EXPERIENCE]) oder Glaubenssätzen ([BELIEF]), falls im Kontext vorhanden.
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: {context_str}"
openrouter: "Empathische Reflexion der Situation {query}. Persönlicher Kontext: {context_str}"
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 (DOCS & SNIPPETS):
KONTEXT (WISSEN & PROJEKTE):
=========================================
{context_str}
TECHNIK & SNIPPETS:
{tech_stream}
PROJEKT-STATUS:
{facts_stream}
=========================================
TASK:
@ -118,11 +148,11 @@ 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 {context_str}."
openrouter: "Technischer Support für {query}. Code-Referenzen: {context_str}"
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" (Performance Mode)
# 5. INTERVIEW: Der "One-Shot Extractor" (WP-07)
# ---------------------------------------------------------
interview_template:
ollama: |
@ -160,7 +190,7 @@ interview_template:
openrouter: "Strukturiere den Input {query} nach dem Schema {schema_fields} für Typ {target_type}."
# ---------------------------------------------------------
# 6. EDGE_ALLOCATION: Kantenfilter (Intent: OFFLINE_FILTER)
# 6. EDGE_ALLOCATION: Kantenfilter (Ingest)
# ---------------------------------------------------------
edge_allocation_template:
ollama: |
@ -200,7 +230,7 @@ edge_allocation_template:
OUTPUT:
# ---------------------------------------------------------
# 7. SMART EDGE ALLOCATION: Extraktion (Intent: INGEST)
# 7. SMART EDGE ALLOCATION: Extraktion (Ingest)
# ---------------------------------------------------------
edge_extraction:
ollama: |
@ -240,7 +270,7 @@ edge_extraction:
OUTPUT:
# ---------------------------------------------------------
# 8. WP-15b: EDGE VALIDATION (Intent: VALIDATE)
# 8. WP-15b: EDGE VALIDATION (Ingest/Validate)
# ---------------------------------------------------------
edge_validation:
gemini: |
@ -270,4 +300,38 @@ edge_validation:
QUELLE: {chunk_text}
ZIEL: {target_title} ({target_summary})
BEZIEHUNG: {edge_kind}
Ist diese Verbindung valide? Antworte NUR mit YES oder NO.
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

@ -151,8 +151,8 @@ Damit dieses System wartbar bleibt (auch für KI-Agenten wie NotebookLM), gelten
## 6. Dokumentations-Status
**Aktuelle Version:** 2.9.1
**Letzte Aktualisierung:** 2025-01-XX
**Aktuelle Version:** 2.9.3
**Letzte Aktualisierung:** 2025-12-31
**Status:** ✅ Vollständig und aktiv gepflegt
**Hinweis:** Diese Dokumentation wird kontinuierlich aktualisiert. Bei Fragen oder Verbesserungsvorschlägen bitte im Repository melden.

View File

@ -2,8 +2,8 @@
doc_type: glossary
audience: all
status: active
version: 2.9.1
context: "Zentrales Glossar für Mindnet v2.8. Enthält Definitionen zu Hybrid-Cloud Resilienz, WP-14 Modularisierung, WP-15b Two-Pass Ingestion und Mistral-safe Parsing."
version: 2.9.3
context: "Zentrales Glossar für Mindnet v2.9.3. 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 und Mistral-safe Parsing."
---
# Mindnet Glossar
@ -23,7 +23,11 @@ context: "Zentrales Glossar für Mindnet v2.8. Enthält Definitionen zu Hybrid-C
* **Edge Registry:** Der zentrale Dienst (SSOT), der Kanten-Typen validiert und Aliase in kanonische Typen auflöst. Nutzt `01_edge_vocabulary.md` als Basis.
* **LLM Service:** Der Hybrid-Client (v3.3.6), der Anfragen zwischen OpenRouter, Google Gemini und lokalem Ollama routet. Verwaltet Cloud-Timeouts und Quoten-Management. Nutzt zur Text-Bereinigung nun die neutrale `registry.py`, um Circular Imports zu vermeiden.
* **Retriever:** Besteht in v2.7+ aus der Orchestrierung (`retriever.py`) und der mathematischen Scoring-Engine (`retriever_scoring.py`). Seit WP-14 im Paket `app.core.retrieval` gekapselt.
* **Decision Engine:** Teil des Routers, der Intents erkennt und entsprechende **Boost-Faktoren** für das Retrieval injiziert.
* **Decision Engine (WP-25):** Der zentrale **Agentic Orchestrator**, der Intents erkennt, parallele Wissens-Streams orchestriert und die Ergebnisse synthetisiert. Implementiert Multi-Stream Retrieval und Intent-basiertes Routing.
* **Agentic Multi-Stream RAG (WP-25):** Architektur-Paradigma, bei dem Nutzeranfragen in parallele, spezialisierte Wissens-Streams aufgeteilt werden (Values, Facts, Biography, Risk, Tech), die gleichzeitig abgefragt und zu einer kontextreichen Antwort synthetisiert werden.
* **Stream-Tracing (WP-25):** Kennzeichnung jedes Treffers mit seinem Ursprungs-Stream (`stream_origin`), um Feedback-Optimierung pro Wissensbereich zu ermöglichen.
* **Intent-basiertes Routing (WP-25):** Hybrid-Modus zur Intent-Erkennung mit Keyword Fast-Path (sofortige Erkennung von Triggern) und LLM Slow-Path (semantische Analyse für unklare Anfragen).
* **Wissens-Synthese (WP-25):** Template-basierte Zusammenführung der Ergebnisse aus parallelen Streams mit expliziten Stream-Variablen (z.B. `{values_stream}`, `{risk_stream}`), um dem LLM eine differenzierte Abwägung zu ermöglichen.
* **Traffic Control:** Verwaltet Prioritäten und drosselt Hintergrund-Tasks (z.B. Smart Edges) mittels Semaphoren und Timeouts (45s) zur Vermeidung von System-Hangs.
* **Unknown Edges Log:** Die Datei `unknown_edges.jsonl`, in der das System Kanten-Typen protokolliert, die nicht im Dictionary gefunden wurden.
* **Database Package (WP-14):** Zentralisiertes Infrastruktur-Paket (`app.core.database`), das den Qdrant-Client (`qdrant.py`) und das Point-Mapping (`qdrant_points.py`) verwaltet.

View File

@ -3,8 +3,8 @@ doc_type: user_manual
audience: user, mindmaster
scope: chat, ui, feedback, graph
status: active
version: 2.6
context: "Anleitung zur Nutzung der Web-Oberfläche, der Chat-Personas und des Graph Explorers."
version: 2.9.3
context: "Anleitung zur Nutzung der Web-Oberfläche, der Chat-Personas, Multi-Stream RAG und des Graph Explorers."
---
# Chat & Graph Usage Guide
@ -60,21 +60,49 @@ Ein Editor mit **"File System First"** Garantie.
---
## 3. Den Chat steuern (Intents)
## 3. Den Chat steuern (Intents & Multi-Stream RAG)
Du steuerst die Persönlichkeit von Mindnet durch deine Wortwahl. Der **Hybrid Router v5** unterscheidet intelligent:
Du steuerst die Persönlichkeit von Mindnet durch deine Wortwahl. Seit WP-25 nutzt Mindnet **Agentic Multi-Stream RAG**, das deine Anfrage in parallele Wissens-Streams aufteilt:
### 3.1 Frage-Modus (Wissen abrufen)
Ausgelöst durch `?` oder W-Wörter.
### 3.1 Intent-Erkennung (Hybrid-Router)
Der Router erkennt deine Absicht auf zwei Wegen:
**Schnelle Erkennung (Keyword Fast-Path):**
* **"Soll ich..."** → Sofortige Erkennung als `DECISION` (Berater)
* **"Wann..."** → Sofortige Erkennung als `FACT_WHEN` (Zeitpunkte)
* **"Was ist..."** → Sofortige Erkennung als `FACT_WHAT` (Wissen)
* **"Ich fühle..."** → Sofortige Erkennung als `EMPATHY` (Spiegel)
**Intelligente Analyse (LLM Slow-Path):**
* Bei unklaren Anfragen analysiert die KI semantisch deine Absicht
### 3.2 Multi-Stream Retrieval (WP-25)
Anstelle einer einzelnen Suche führt Mindnet nun **parallele Abfragen** in spezialisierten Wissens-Streams aus:
**Die Streams:**
* **Values Stream:** Deine Identität, Ethik und Prinzipien (`value`, `principle`, `belief`)
* **Facts Stream:** Operative Daten zu Projekten, Tasks und Status (`project`, `decision`, `task`)
* **Biography Stream:** Persönliche Erfahrungen und Journal-Einträge (`experience`, `journal`)
* **Risk Stream:** Hindernisse und potenzielle Gefahren (`risk`, `obstacle`)
* **Tech Stream:** Technisches Wissen, Code und Dokumentation (`concept`, `source`, `glossary`)
**Vorteil:** Jeder Stream fokussiert auf spezifische Wissensbereiche, was zu präziseren und kontextreicheren Antworten führt.
### 3.3 Frage-Modi (Strategien)
* **Entscheidung ("Soll ich?"):** Der **Berater**.
* Mindnet lädt deine Werte (`type: value`) und Ziele (`type: goal`).
* Nutzt: Values Stream, Facts Stream, Risk Stream
* Wägt Fakten gegen deine Werte ab und evaluiert Risiken
* *Beispiel:* "Soll ich Tool X nutzen?" -> "Nein, Tool X speichert Daten in den USA. Das verstößt gegen dein Prinzip 'Privacy First'."
* **Empathie ("Ich fühle..."):** Der **Spiegel**.
* Mindnet lädt deine Erfahrungen (`type: experience`).
* Nutzt: Biography Stream, Values Stream
* Greift auf deine Erfahrungen und Werte zurück
* *Beispiel:* "Ich bin frustriert." -> "Das erinnert mich an Projekt Y, da ging es uns ähnlich..."
* **Fakten ("Was ist?"):** Der **Bibliothekar**.
* Liefert präzise Definitionen.
* **Fakten ("Was ist?", "Wann..."):** Der **Bibliothekar**.
* Nutzt: Facts Stream, Tech Stream, Biography Stream
* Liefert präzise Definitionen und zeitliche Informationen
### 3.2 Befehls-Modus (Interview)
Ausgelöst durch Aussagen wie "Neues Projekt", "Ich will festhalten".
@ -84,13 +112,17 @@ Ausgelöst durch Aussagen wie "Neues Projekt", "Ich will festhalten".
---
## 4. Ergebnisse interpretieren (Explanation Layer)
## 4. Ergebnisse interpretieren (Explanation Layer & Stream-Tracing)
Mindnet liefert eine **Begründung** ("Reasons"), warum es etwas gefunden hat.
Öffne eine Quellen-Karte, um zu sehen:
* *"Hohe textuelle Übereinstimmung."* (Semantik)
* *"Bevorzugt aufgrund des Typs 'decision'."* (Wichtigkeit)
* *"Verweist auf 'Projekt X' via 'depends_on'."* (Graph-Kontext)
* *"Quelle: Values Stream"* (Stream-Tracing - WP-25)
**Stream-Tracing (WP-25):**
Jede Quelle wird mit ihrem Ursprungs-Stream markiert (z.B. "Values Stream", "Facts Stream"). Dies hilft dir zu verstehen, aus welchem Wissensbereich die Information stammt.
---

View File

@ -1,10 +1,10 @@
---
doc_type: concept
audience: architect, product_owner
scope: ai, router, personas, resilience
scope: ai, router, personas, resilience, agentic_rag
status: active
version: 2.8.1
context: "Fachkonzept der hybriden KI-Persönlichkeit, der Provider-Kaskade und der kognitiven Resilienz (Deep Fallback)."
version: 2.9.3
context: "Fachkonzept der hybriden KI-Persönlichkeit, Agentic Multi-Stream RAG, Provider-Kaskade und kognitiven Resilienz (Deep Fallback)."
---
# Konzept: KI-Persönlichkeit & Router
@ -13,13 +13,45 @@ context: "Fachkonzept der hybriden KI-Persönlichkeit, der Provider-Kaskade und
Mindnet soll nicht wie eine Suchmaschine wirken, sondern wie ein **Digitaler Zwilling**. Dazu muss das System erkennen, **was** der Nutzer will, und seine „Persönlichkeit“ sowie seine technische Infrastruktur dynamisch anpassen.
## 1. Der Hybrid Router (Das Gehirn)
## 1. Der Hybrid Router & Agentic Multi-Stream RAG (Das Gehirn)
Jede Eingabe durchläuft den **Hybrid Router**. Er entscheidet über die fachliche Strategie und die technische Ausführung.
Jede Eingabe durchläuft den **Hybrid Router**. 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.
### Modus A: RAG (Retrieval Augmented Generation)
* **Intent:** Der Nutzer hat eine Frage oder ein Problem (`FACT`, `DECISION`, `EMPATHY`).
* **Aktion:** Das System sucht im Gedächtnis und generiert eine Antwort.
### Intent-basiertes Routing (WP-25)
Der Router nutzt einen **Hybrid-Modus** mit Keyword Fast-Path und LLM Slow-Path:
**Keyword Fast-Path:**
* Sofortige Erkennung von Triggern wie "Soll ich", "Wann", "Was ist"
* Reduziert Latenz durch schnelle Keyword-Erkennung ohne LLM-Call
**LLM Slow-Path:**
* Komplexe semantische Analyse für unklare Anfragen
* Nutzt `intent_router_v1` Prompt zur Klassifizierung
**Strategien:**
* **FACT_WHAT/FACT_WHEN:** Wissensabfrage (Wissen/Listen, Zeitpunkte)
* **DECISION:** Beratung (Rat, Strategie, Abwägung)
* **EMPATHY:** Reflexion (Emotionale Resonanz)
* **CODING:** Technik (Programmierung, Syntax)
* **INTERVIEW:** Datenerfassung (Wissen speichern)
### Modus A: Agentic Multi-Stream RAG (WP-25)
Anstelle einer einzelnen Suche führt das System **parallele Abfragen** in spezialisierten Wissens-Streams aus:
**Stream-Library:**
* **Values Stream:** Identität, Ethik und Prinzipien (`value`, `principle`, `belief`, `trait`, `boundary`, `need`, `motivation`)
* **Facts Stream:** Operative Daten (`project`, `decision`, `task`, `goal`, `event`, `state`)
* **Biography Stream:** Persönliche Erfahrungen (`experience`, `journal`, `profile`, `person`)
* **Risk Stream:** Hindernisse und Gefahren (`risk`, `obstacle`, `bias`)
* **Tech Stream:** Technisches Wissen (`concept`, `source`, `glossary`, `idea`, `insight`, `skill`, `habit`)
**Wissens-Synthese:**
Die Zusammenführung erfolgt über spezialisierte Templates mit expliziten Stream-Variablen (z.B. `{values_stream}`, `{risk_stream}`). Dies ermöglicht dem LLM eine differenzierte Abwägung zwischen Fakten und persönlichen Werten.
**Stream-Tracing:**
Jeder Treffer wird mit `stream_origin` markiert, um Feedback-Optimierung pro Wissensbereich zu ermöglichen.
### Modus B: Interview (Knowledge Capture)
* **Intent:** Der Nutzer will Wissen speichern (`INTERVIEW`).
@ -45,18 +77,22 @@ Ein intelligenter Zwilling muss jederzeit verfügbar sein. Mindnet v2.8.1 nutzt
Mindnet wechselt den Hut, je nach Situation.
### 3.1 Der Berater (Strategy: DECISION)
* **Auslöser:** Fragen wie „Soll ich...?“, „Was ist besser?“.
* **Strategic Retrieval:** Lädt aktiv Notizen der Typen `value` (Werte), `goal` (Ziele) und `risk` (Risiken), auch wenn sie im Text nicht direkt vorkommen.
* **Reasoning:** *„Wäge die Fakten gegen meine Werte ab. Sei strikt bei Risiken.“*
* **Auslöser:** Fragen wie „Soll ich...?", „Was ist besser?", „Empfehlung...".
* **Multi-Stream Retrieval (WP-25):** Führt parallele Abfragen in Values Stream, Facts Stream und Risk Stream aus.
* **Wissens-Synthese:** Wägt Fakten gegen Werte ab, evaluiert Risiken und prüft Kompatibilität mit langfristiger Identität.
* **Reasoning:** *„Wäge die Fakten gegen meine Werte ab. Sei strikt bei Risiken."*
### 3.2 Der Spiegel (Strategy: EMPATHY)
* **Auslöser:** Emotionale Aussagen („Ich bin frustriert“).
* **Strategic Retrieval:** Lädt `experience` (Erfahrungen) und `belief` (Glaubenssätze).
* **Reasoning:** *„Nutze meine eigenen Erfahrungen, um die Situation einzuordnen.“*
* **Auslöser:** Emotionale Aussagen („Ich bin frustriert", „Ich fühle...", „Stress...").
* **Multi-Stream Retrieval (WP-25):** Führt parallele Abfragen in Biography Stream und Values Stream aus.
* **Wissens-Synthese:** Greift auf persönliche Erfahrungen und Werte zurück, um emotionale Resonanz zu schaffen.
* **Reasoning:** *„Nutze meine eigenen Erfahrungen, um die Situation einzuordnen."*
### 3.3 Der Bibliothekar (Strategy: FACT)
* **Auslöser:** Sachfragen („Was ist Qdrant?“).
* **Behavior:** Präzise, neutral, kurz.
### 3.3 Der Bibliothekar (Strategy: FACT_WHAT / FACT_WHEN)
* **Auslöser:** Sachfragen („Was ist...?", „Welche sind...?", „Wann...?", „Datum...").
* **Multi-Stream Retrieval (WP-25):** Führt parallele Abfragen in Facts Stream, Tech Stream und Biography Stream aus.
* **Wissens-Synthese:** Kombiniert harte Fakten mit persönlichen Erfahrungen, falls vorhanden.
* **Behavior:** Präzise, neutral, strukturiert.
---

View File

@ -1,10 +1,10 @@
---
doc_type: technical_reference
audience: developer, integrator
scope: api, endpoints, integration
scope: api, endpoints, integration, agentic_rag
status: active
version: 2.9.1
context: "Vollständige API-Referenz für alle Mindnet-Endpunkte. Basis für Integration und Entwicklung."
version: 2.9.3
context: "Vollständige API-Referenz für alle Mindnet-Endpunkte inklusive WP-25 Agentic Multi-Stream RAG. Basis für Integration und Entwicklung."
---
# API Reference
@ -96,21 +96,37 @@ Hauptendpunkt für RAG-Chat und Interview-Modus. Unterstützt Streaming.
**Response (Non-Streaming):**
```json
{
"intent": "FACT",
"response": "Mindnet ist ein persönliches KI-Gedächtnis...",
"sources": [...],
"query_id": "uuid"
"query_id": "uuid",
"answer": "Mindnet ist ein persönliches KI-Gedächtnis...",
"sources": [
{
"node_id": "uuid",
"note_id": "uuid",
"semantic_score": 0.85,
"total_score": 0.92,
"stream_origin": "values_stream", // WP-25: Ursprungs-Stream
"explanation": {...}
}
],
"latency_ms": 450,
"intent": "DECISION", // WP-25: Gewählte Strategie
"intent_source": "Keyword (FastPath)" // WP-25: Quelle der Intent-Erkennung
}
```
**Response (Streaming):**
Server-Sent Events (SSE) mit Chunks der Antwort.
**Intent-Typen:**
- `FACT`: Wissensabfrage
- `DECISION`: Entscheidungsfrage
- `EMPATHY`: Emotionale Anfrage
- `INTERVIEW`: Wissen erfassen
**Intent-Typen (WP-25):**
- `FACT_WHAT`: Wissensabfrage (Wissen/Listen)
- `FACT_WHEN`: Zeitpunkte (Termine, Daten)
- `DECISION`: Entscheidungsfrage (Beratung, Strategie)
- `EMPATHY`: Emotionale Anfrage (Reflexion)
- `CODING`: Technische Anfrage (Programmierung)
- `INTERVIEW`: Wissen erfassen (Datenerfassung)
**Stream-Tracing (WP-25):**
Jeder Treffer in `sources` enthält `stream_origin`, um die Zuordnung zum Quell-Stream zu ermöglichen (z.B. "values_stream", "facts_stream", "risk_stream").
---

View File

@ -1,30 +1,32 @@
---
doc_type: technical_reference
audience: developer, architect
scope: backend, chat, llm_service, traffic_control, resilience
scope: backend, chat, llm_service, traffic_control, resilience, agentic_rag
status: active
version: 2.9.1
context: "Technische Implementierung des FastAPI-Routers, des hybriden LLMService (v3.3.6) und der WP-20 Resilienz-Logik."
version: 2.9.3
context: "Technische Implementierung des FastAPI-Routers, des hybriden LLMService (v3.4.2), WP-25 Agentic Multi-Stream RAG und WP-20 Resilienz-Logik."
---
# Chat Backend & Traffic Control
# Chat Backend & Agentic Multi-Stream RAG
## 1. Hybrid Router (Decision Engine)
## 1. Hybrid Router & Intent-basiertes Routing (WP-25)
Der zentrale Einstiegspunkt für jede Chatanfrage ist der **Hybrid Router** (`app/routers/chat.py`). Er entscheidet dynamisch über die Strategie und nutzt den `LLMService` zur provider-agnostischen Generierung.
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 (Logik)
### 1.1 Intent-Erkennung (Hybrid-Modus)
Der Router prüft den Input in drei Stufen (Wasserfall-Prinzip):
Der Router nutzt einen **Hybrid-Modus** mit Keyword-Fast-Path und LLM-Slow-Path:
1. **Question Detection (Regelbasiert):**
* Prüfung auf Vorhandensein von `?` oder W-Wörtern (Wer, Wie, Was, Soll ich).
* Wenn positiv: **RAG Modus** (Interview wird blockiert).
2. **Keyword Scan (Fast Path):**
* Lädt `types.yaml` (Objekte) und `decision_engine.yaml` (Handlungen).
* Wenn Match (z.B. "Projekt" + "neu"): **INTERVIEW Modus**.
3. **LLM Fallback (Slow Path):**
* Wenn unklar: Anfrage an LLM zur Klassifizierung mittels `router_prompt`.
1. **Keyword Fast-Path (Sofortige Erkennung):**
* Prüft `trigger_keywords` aus `decision_engine.yaml` (z.B. "Soll ich", "Wann", "Was ist").
* Wenn Match: Sofortige Intent-Zuordnung ohne LLM-Call.
* **Strategien:** FACT_WHAT, FACT_WHEN, DECISION, EMPATHY, CODING, INTERVIEW.
2. **Type Keywords (Interview-Modus):**
* Lädt `types.yaml` und prüft `detection_keywords` für Objekt-Erkennung.
* Wenn Match und keine Frage: **INTERVIEW Modus** (Datenerfassung).
3. **LLM Slow-Path (Semantische Analyse):**
* Wenn unklar: Anfrage an `DecisionEngine._determine_strategy()` zur LLM-basierten Klassifizierung.
* Nutzt `intent_router_v1` Prompt aus `prompts.yaml`.
### 1.2 Prompt-Auflösung (Bulletproof Resolution)
@ -34,24 +36,70 @@ Um Kompatibilitätsprobleme mit verschachtelten YAML-Prompts zu vermeiden, nutzt
* **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.3 RAG Flow (Technisch)
### 1.2 Multi-Stream Retrieval (WP-25)
Wenn der Intent `FACT` oder `DECISION` ist, wird folgender Flow ausgeführt:
Anstelle einer einzelnen Suche führt die `DecisionEngine` nun **parallele Abfragen** in spezialisierten Streams aus:
1. **Pre-Processing:** Query Rewriting (optional).
2. **Context Enrichment:**
* Abruf via `retriever.py` (Hybrid Search).
* Integration von **Edge Boosts** aus der `decision_engine.yaml` zur Beeinflussung der Graph-Gewichtung.
* Injection von Metadaten (`[TYPE]`, `[SCORE]`) in den Prompt.
3. **Prompt Construction:** Assembly aus System-Prompt (Persona) + Context + Query.
4. **Streaming:** LLM-Antwort wird via **SSE (Server-Sent Events)** an den Client gestreamt.
5. **Post-Processing:** Anhängen des `Explanation` Layers (JSON-Breakdown) an das Ende des Streams.
**Stream-Library (definiert in `decision_engine.yaml`):**
* **Values Stream:** Extrahiert Identität, Ethik und Prinzipien (`value`, `principle`, `belief`, etc.).
* **Facts Stream:** Liefert operative Daten zu Projekten, Tasks und Status (`project`, `decision`, `task`, etc.).
* **Biography Stream:** Greift auf persönliche Erfahrungen und Journal-Einträge zu (`experience`, `journal`, `profile`).
* **Risk Stream:** Identifiziert Hindernisse und potenzielle Gefahren (`risk`, `obstacle`, `bias`).
* **Tech Stream:** Bündelt technisches Wissen, Code und Dokumentation (`concept`, `source`, `glossary`, etc.).
**Stream-Konfiguration:**
* Jeder Stream nutzt individuelle **Edge-Boosts** (z.B. `guides: 3.0` für Values Stream).
* **Filter-Types** sind strikt mit `types.yaml` (v2.7.0) synchronisiert.
* **Query-Templates** transformieren die ursprüngliche Anfrage für spezialisierte Suche.
**Parallele Ausführung:**
* `asyncio.gather()` führt alle aktiven Streams gleichzeitig aus.
* **Stream-Tracing:** Jeder Treffer wird mit `stream_origin` markiert für Feedback-Optimierung.
* **Fehlerbehandlung:** Einzelne Stream-Fehler blockieren nicht die gesamte Anfrage.
### 1.3 Wissens-Synthese (WP-25)
Die Zusammenführung der Daten erfolgt über spezialisierte Templates in der `prompts.yaml`:
**Template-Struktur:**
* Explizite Variablen für jeden Stream (z.B. `{values_stream}`, `{risk_stream}`).
* **Pre-Initialization:** Alle möglichen Stream-Variablen werden vorab initialisiert (verhindert KeyErrors).
* **Provider-spezifische Templates:** Separate Versionen für Ollama, Gemini und OpenRouter.
**Synthese-Strategien:**
* **FACT_WHAT/FACT_WHEN:** Kombiniert Fakten, Biographie und Technik.
* **DECISION:** Wägt Fakten gegen Werte ab, evaluiert Risiken.
* **EMPATHY:** Fokus auf Biographie und Werte.
* **CODING:** Technik und Fakten.
### 1.4 RAG Flow (Technisch)
Wenn der Intent nicht `INTERVIEW` ist, wird folgender Flow ausgeführt:
1. **Intent Detection:** Hybrid Router klassifiziert die Anfrage.
2. **Multi-Stream Retrieval:**
* Parallele Abfragen in spezialisierten Streams via `DecisionEngine._execute_parallel_streams()`.
* Jeder Stream nutzt individuelle Filter, Edge-Boosts und Query-Templates.
3. **Context Formatting:**
* Stream-Ergebnisse werden in formatierte Kontext-Strings umgewandelt.
* **Ollama Context-Throttling:** Kontext wird auf `MAX_OLLAMA_CHARS` begrenzt (Standard: 10.000).
4. **Synthese:**
* `DecisionEngine._generate_final_answer()` kombiniert alle Streams.
* Template-basierte Prompt-Konstruktion mit Stream-Variablen.
5. **Response:**
* LLM-Antwort wird generiert (provider-spezifisch).
* **Sources:** Alle Treffer aus allen Streams werden dedupliziert und zurückgegeben.
---
## 2. LLM Service & Traffic Control (WP-20)
## 2. LLM Service & Traffic Control (WP-20 / WP-25)
Der `LLMService` (`app/services/llm_service.py`) fungiert als zentraler Hybrid-Client für OpenRouter, Google Gemini und Ollama. Er schützt das System vor Überlastung und verwaltet Quoten.
Der `LLMService` (`app/services/llm_service.py`, v3.4.2) fungiert als zentraler Hybrid-Client für OpenRouter, Google Gemini und Ollama. Er schützt das System vor Überlastung und verwaltet Quoten.
**WP-25 Integration:**
* **Lazy Initialization:** `DecisionEngine` wird erst bei Bedarf initialisiert (verhindert Circular Imports).
* **Ingest-Stability Patch:** Entfernung des <5-Zeichen Guards ermöglicht YES/NO Validierungen beim Vault-Import.
* **Empty Response Guard:** Sicherung gegen leere `choices` Arrays bei OpenRouter (verhindert JSON-Errors).
Mit Version 2.8.1 wurde die Architektur der Antwort-Generierung grundlegend gehärtet:
@ -107,10 +155,27 @@ In v2.8 wurde ein intelligentes Fehler-Handling für Cloud-Provider implementier
---
## 4. Feedback Traceability
## 4. Feedback Traceability & Stream-Tracing (WP-25)
Unterstützt das geplante Self-Tuning (WP08).
Unterstützt das geplante Self-Tuning (WP08) und ermöglicht Stream-spezifische Optimierung.
1. **Query ID:** Generiert bei jedem `/query` Call eine `UUIDv4`.
2. **Logging:** Speichert einen Snapshot in `data/logs/query_snapshot.jsonl` (Input + Retrieved Context).
3. **Feedback:** Der `/feedback` Endpoint verknüpft das User-Rating (1-5) mit der `query_id`.
1. **Query ID:** Generiert bei jedem `/chat` Call eine `UUIDv4`.
2. **Stream-Tracing:** Jeder Treffer enthält `stream_origin` für Zuordnung zum Quell-Stream.
3. **Logging:** Speichert einen Snapshot in `data/logs/query_snapshot.jsonl` (Input + Retrieved Context + Intent).
4. **Feedback:** Der `/feedback` Endpoint verknüpft das User-Rating (1-5) mit der `query_id` und `stream_origin`.
## 5. Lifespan Management (WP-25)
Die FastAPI-Anwendung (`app/main.py`, v1.0.0) implementiert **Lifespan-Management** für sauberen Startup und Shutdown:
**Startup:**
* Integritäts-Check der WP-25 Konfiguration (`decision_engine.yaml`, `prompts.yaml`).
* Validierung kritischer Dateien vor dem Start.
**Shutdown:**
* Ressourcen-Cleanup (LLMService-Connections schließen).
* Graceful Shutdown für asynchrone Prozesse.
**Globale Fehlerbehandlung:**
* Fängt unerwartete Fehler in der Multi-Stream Kette ab.
* Strukturierte JSON-Responses bei Engine-Fehlern.

View File

@ -1,10 +1,10 @@
---
doc_type: technical_reference
audience: developer, admin
scope: configuration, env, registry, scoring, resilience, modularization
scope: configuration, env, registry, scoring, resilience, modularization, agentic_rag
status: active
version: 2.9.1
context: "Umfassende Referenztabellen für Umgebungsvariablen (inkl. Hybrid-Cloud & WP-76), YAML-Konfigurationen und die Edge Registry Struktur unter Berücksichtigung von WP-14."
version: 2.9.3
context: "Umfassende Referenztabellen für Umgebungsvariablen (inkl. Hybrid-Cloud & WP-76), YAML-Konfigurationen, Edge Registry Struktur und WP-25 Multi-Stream RAG unter Berücksichtigung von WP-14."
---
# Konfigurations-Referenz
@ -224,15 +224,16 @@ Die Datei muss eine Markdown-Tabelle enthalten, die vom Regex-Parser gelesen wir
---
## 5. Decision Engine (`decision_engine.yaml`)
## 5. Decision Engine (`decision_engine.yaml` v3.1.6)
Die Decision Engine fungiert als zentraler Orchestrator für die Intent-Erkennung und das dynamische Retrieval-Routing. Sie bestimmt, wie das System auf eine Nutzeranfrage reagiert, welche Informationstypen bevorzugt werden und wie der Wissensgraph für die spezifische Situation verformt wird.
Die Decision Engine fungiert als zentraler **Agentic Orchestrator** für die Intent-Erkennung und das dynamische Multi-Stream Retrieval-Routing (WP-25). Sie bestimmt, wie das System auf eine Nutzeranfrage reagiert, welche Wissens-Streams aktiviert werden und wie die Ergebnisse synthetisiert werden.
### 5.1 Intent Recognition: Dual-Path Routing
Das System nutzt ein zweistufiges Verfahren, um die Absicht des Nutzers zu identifizieren:
### 5.1 Intent Recognition: Hybrid-Routing (WP-25)
Das System nutzt einen **Hybrid-Modus** mit Keyword Fast-Path und LLM Slow-Path:
1. **Fast Path (Keyword Trigger):** Das System scannt die Anfrage nach definierten `trigger_keywords`. Wird ein Treffer gefunden, wird die entsprechende Strategie sofort ohne LLM-Einsatz gewählt.
2. **Slow Path (LLM Router):** Wenn kein Keyword matcht und `llm_fallback_enabled: true` gesetzt ist, analysiert ein LLM die Nachricht mittels Few-Shot Prompting.
1. **Fast Path (Keyword Trigger):** Das System scannt die Anfrage nach definierten `trigger_keywords`. Wird ein Treffer gefunden, wird die entsprechende Strategie sofort ohne LLM-Einsatz gewählt (z.B. "Soll ich" → `DECISION`).
2. **Type Keywords:** Prüft `detection_keywords` aus `types.yaml` für Interview-Modus (z.B. "Projekt" + "neu" → `INTERVIEW`).
3. **Slow Path (LLM Router):** Wenn kein Keyword matcht und `llm_fallback_enabled: true` gesetzt ist, analysiert ein LLM die Nachricht mittels Few-Shot Prompting (`intent_router_v1`).
#### LLM Router Konfiguration
Der Router nutzt den `llm_router_prompt`, um Anfragen in eine der fünf Kern-Strategien (`FACT`, `DECISION`, `EMPATHY`, `CODING`, `INTERVIEW`) zu klassifizieren.
@ -244,36 +245,90 @@ Der Router nutzt den `llm_router_prompt`, um Anfragen in eine der fünf Kern-Str
---
### 5.2 Strategie-Mechaniken (Graph Shaping)
Jede Strategie definiert drei Hebel, um das Ergebnis des Retrievers zu beeinflussen:
### 5.2 Multi-Stream Konfiguration (WP-25)
* **`inject_types`:** Erzwingt die Einbindung bestimmter Notiz-Typen (z. B. `value` bei Entscheidungen), auch wenn diese semantisch eine geringere Ähnlichkeit aufweisen.
Seit WP-25 nutzt die Decision Engine eine **Stream-Library** mit spezialisierten Wissens-Streams:
**Stream-Library (`streams_library`):**
* **`values_stream`:** Identität, Ethik und Prinzipien (filter_types: `value`, `principle`, `belief`, `trait`, `boundary`, `need`, `motivation`)
* **`facts_stream`:** Operative Daten (filter_types: `project`, `decision`, `task`, `goal`, `event`, `state`)
* **`biography_stream`:** Persönliche Erfahrungen (filter_types: `experience`, `journal`, `profile`, `person`)
* **`risk_stream`:** Hindernisse und Gefahren (filter_types: `risk`, `obstacle`, `bias`)
* **`tech_stream`:** Technisches Wissen (filter_types: `concept`, `source`, `glossary`, `idea`, `insight`, `skill`, `habit`)
**Stream-Parameter:**
* **`query_template`:** Transformiert die ursprüngliche Anfrage für spezialisierte Suche (z.B. "Welche meiner Werte und Prinzipien betreffen: {query}")
* **`filter_types`:** Strikte Synchronisation mit `types.yaml` (v2.7.0)
* **`top_k`:** Anzahl der Treffer pro Stream (z.B. 5 für Values, 3 für Risk)
* **`edge_boosts`:** Individuelle Edge-Gewichtung pro Stream (z.B. `guides: 3.0` für Values Stream)
**Strategie-Komposition (`strategies`):**
Jede Strategie definiert, welche Streams aktiviert werden:
* **`use_streams`:** Liste der Stream-Keys, die parallel abgefragt werden (z.B. `["values_stream", "facts_stream", "risk_stream"]` für `DECISION`)
* **`prompt_template`:** Template-Key aus `prompts.yaml` für die Wissens-Synthese (z.B. `decision_synthesis_v1`)
* **`prepend_instruction`:** Optional: Zusätzliche Anweisung für das LLM (z.B. "Analysiere die Fakten vor dem Hintergrund meiner Werte")
* **`preferred_provider`:** Optional: Provider-Präferenz für diese Strategie (z.B. `gemini` für DECISION)
### 5.3 Strategie-Mechaniken (Graph Shaping)
Jede Strategie definiert mehrere Hebel, um das Ergebnis zu beeinflussen:
* **`use_streams`:** Aktiviert parallele Wissens-Streams (WP-25).
* **`edge_boosts`:** Erhöht die Gewichtung spezifischer Kanten-Typen in der Scoring-Formel. Dies ermöglicht es dem Graphen, die Textsuche situativ zu "überstimmen".
* **`prepend_instruction`:** Injiziert eine spezifische Systemanweisung in das LLM-Prompt, um den Antwortstil anzupassen (z. B. "Wäge Fakten gegen Werte ab").
---
### 5.3 Übersicht der Strategien
### 5.4 Übersicht der Strategien (WP-25)
| Strategie | Fokus | Bevorzugte Kanten (`edge_boosts`) | Injektionstypen |
| Strategie | Fokus | Aktive Streams | Bevorzugte Kanten (`edge_boosts`) |
| :--- | :--- | :--- | :--- |
| **FACT** | Wissensabfrage & Definitionen | `part_of` (2.0), `composed_of` (2.0), `similar_to` (1.5) | *(Keine)* |
| **DECISION** | Rat, Strategie & Abwägung | `blocks` (2.5), `solves` (2.0), `risk_of` (2.5) | `value`, `principle`, `goal`, `risk` |
| **EMPATHY** | Emotionale Resonanz | `based_on` (2.0), `experienced_in` (2.5), `related_to` (2.0) | `experience`, `belief`, `profile` |
| **CODING** | Programmierung & Syntax | `implemented_in` (3.0), `uses` (2.5), `depends_on` (2.0) | `snippet`, `reference`, `source` |
| **INTERVIEW** | Erfassung neuer Daten | *(Keine)* | *(Keine)* |
| **FACT_WHAT** | Wissensabfrage & Listen | `facts_stream`, `tech_stream`, `biography_stream` | `part_of` (2.0), `depends_on` (1.5), `implemented_in` (1.5) |
| **FACT_WHEN** | Zeitpunkte & Termine | `facts_stream`, `biography_stream`, `tech_stream` | `part_of` (2.0), `depends_on` (1.5) |
| **DECISION** | Rat, Strategie & Abwägung | `values_stream`, `facts_stream`, `risk_stream` | `blocks` (2.5), `impacts` (2.0), `risk_of` (2.5) |
| **EMPATHY** | Emotionale Resonanz | `biography_stream`, `values_stream` | `related_to` (1.5), `experienced_in` (2.0) |
| **CODING** | Programmierung & Syntax | `tech_stream`, `facts_stream` | `uses` (2.5), `implemented_in` (3.0) |
| **INTERVIEW** | Erfassung neuer Daten | *(Keine Streams)* | *(Keine)* |
---
### 5.4 Der Interview-Modus & Schemas
### 5.5 Der Interview-Modus & Schemas
Die Strategie `INTERVIEW` dient der strukturierten Datenerfassung.
* **Trigger:** Aktiviert durch Phrasen wie "neue notiz", "festhalten" oder "dokumentieren".
* **Trigger:** Aktiviert durch Phrasen wie "neue notiz", "festhalten" oder "dokumentieren" (Type Keywords aus `types.yaml`).
* **Schema-Logik:** Nutzt das `default`-Schema mit den Feldern `Titel`, `Thema/Inhalt` und `Tags`, sofern kein spezifisches Typ-Schema aus der `types.yaml` greift.
* **Dynamik:** In diesem Modus wird der Fokus vom Retrieval (Wissen finden) auf die Extraktion (Wissen speichern) verschoben.
* **Streams:** Keine Streams aktiviert (leere `use_streams` Liste).
> **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)
Seit WP-25 nutzen die Synthese-Templates explizite Stream-Variablen:
**Template-Struktur:**
```yaml
decision_synthesis_v1:
ollama: |
WERTE & PRINZIPIEN (Identität):
{values_stream}
OPERATIVE FAKTEN (Realität):
{facts_stream}
RISIKO-RADAR (Konsequenzen):
{risk_stream}
ENTSCHEIDUNGSFRAGE:
{query}
```
**Pre-Initialization:**
Alle möglichen Stream-Variablen werden vorab initialisiert (verhindert KeyErrors bei unvollständigen Konfigurationen).
**Provider-spezifische Templates:**
Separate Versionen für Ollama, Gemini und OpenRouter.
Auszug aus der decision_engine.yaml
```yaml

View File

@ -1,10 +1,10 @@
---
doc_type: developer_guide
audience: developer
scope: workflow, testing, architecture, modules, modularization
scope: workflow, testing, architecture, modules, modularization, agentic_rag
status: active
version: 2.9.1
context: "Umfassender Guide für Entwickler: Modularisierte Architektur (WP-14), Two-Pass Ingestion (WP-15b), Modul-Interna, Setup und Git-Workflow."
version: 2.9.3
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."
---
# Mindnet Developer Guide & Workflow
@ -236,7 +236,7 @@ Das Backend ist das Herzstück. Es stellt die Logik via REST-API bereit.
| :--- | :--- | :--- | :--- |
| **Entry** | `app/main.py` | 🟢 **Core** | **Entrypoint.** Initialisiert FastAPI, CORS, und bindet alle Router ein. |
| **Config** | `app/config.py` | 🟢 **Core** | **Settings.** Zentrale Konfiguration (Pydantic). Lädt Env-Vars für Qdrant, LLM und Pfade. |
| **Router** | `app/routers/chat.py` | 🟢 **API** | **Conversation API.** Haupt-Endpunkt für Chat. Entscheidet zwischen Interview- und RAG-Modus. |
| **Router** | `app/routers/chat.py` | 🟢 **API** | **Conversation API (WP-25).** Haupt-Endpunkt für Chat. Hybrid Router mit Intent-Erkennung, Multi-Stream Orchestration und Wissens-Synthese. |
| | `app/routers/ingest.py` | 🟢 **API** | **Write API.** Nimmt Markdown entgegen, steuert Ingestion und Discovery-Analyse. |
| | `app/routers/query.py` | 🟢 **API** | **Search API.** Klassischer Hybrid-Retriever Endpunkt. |
| | `app/routers/graph.py` | 🟢 **API** | **Viz API.** Liefert Knoten/Kanten für Frontend-Graphen (Cytoscape). |
@ -393,12 +393,13 @@ Mindnet lernt nicht durch Training (Fine-Tuning), sondern durch **Konfiguration*
edge_defaults: ["blocks"] # Automatische Kante
detection_keywords: ["gefahr", "risiko"]
```
2. **Strategie (`config/decision_engine.yaml`):**
2. **Strategie (`config/decision_engine.yaml` v3.1.6, WP-25):**
```yaml
DECISION:
inject_types: ["value", "risk"] # <--- "risk" hinzufügen
use_streams: ["values_stream", "facts_stream", "risk_stream"] # WP-25: Multi-Stream
inject_types: ["value", "risk"] # Legacy: Fallback für nicht-Stream-Typen
```
*Ergebnis:* Wenn der Intent `DECISION` erkannt wird, sucht das System nun auch aktiv nach Risiken.
*Ergebnis (WP-25):* Wenn der Intent `DECISION` erkannt wird, führt das System parallele Abfragen in Values, Facts und Risk Streams aus und synthetisiert die Ergebnisse.
### Workflow B: Graph-Farben ändern
1. Öffne `app/frontend/ui_config.py`.

View File

@ -3,8 +3,8 @@ doc_type: developer_guide
audience: developer, tester
scope: testing, quality_assurance, test_strategies
status: active
version: 2.9.1
context: "Umfassender Test-Guide für Mindnet: Test-Strategien, Test-Frameworks, Test-Daten und Best Practices."
version: 2.9.3
context: "Umfassender Test-Guide für Mindnet: Test-Strategien, Test-Frameworks, Test-Daten und Best Practices inklusive WP-25 Multi-Stream RAG."
---
# Testing Guide
@ -245,19 +245,29 @@ class TestIngest(unittest.IsolatedAsyncioTestCase):
- `tests/test_edges_defaults_smoke.py`
- `scripts/edges_full_check.py`
### 4.4 Chat & Intent-Tests
### 4.4 Chat & Intent-Tests (WP-25)
**Was wird getestet:**
- Intent-Erkennung (FACT, DECISION, EMPATHY, INTERVIEW)
- Decision Engine
- Intent-Erkennung (FACT_WHAT, FACT_WHEN, DECISION, EMPATHY, CODING, INTERVIEW)
- Hybrid Router (Keyword Fast-Path + LLM Slow-Path)
- Decision Engine (Multi-Stream Orchestration)
- Parallele Stream-Abfragen (Values, Facts, Biography, Risk, Tech)
- Stream-Tracing (`stream_origin` Markierung)
- Wissens-Synthese (Template-basierte Zusammenführung)
- Interview-Modus
- Feedback-Loop
**Tests:**
- `tests/test_wp06_decision.py`
- `tests/test_interview_intent.py`
- `tests/test_chat_wp05.py`
- `tests/test_feedback_smoke.py`
- `tests/test_wp06_decision.py` - Decision Engine (Legacy)
- `tests/test_interview_intent.py` - Interview-Modus
- `tests/test_chat_wp05.py` - Chat-Backend (Legacy)
- `tests/test_feedback_smoke.py` - Feedback-Loop
**WP-25 Spezifische Tests (geplant):**
- Multi-Stream Retrieval (parallele Abfragen)
- Stream-Tracing (stream_origin Zuordnung)
- Template-Robustheit (Pre-Initialization)
- Intent-Kollision (Keyword-Fast-Path Präzision)
### 4.5 Ingestion-Tests

View File

@ -2,14 +2,14 @@
doc_type: roadmap
audience: product_owner, developer
status: active
version: 2.9.1
context: "Aktuelle Planung für kommende Features (ab WP16), Release-Strategie und Historie der abgeschlossenen WPs nach WP-14/15b."
version: 2.9.3
context: "Aktuelle Planung für kommende Features (ab WP16), Release-Strategie und Historie der abgeschlossenen WPs nach WP-14/15b/15c/25."
---
# Mindnet Active Roadmap
**Aktueller Stand:** v2.9.1 (Post-WP14 / WP-15b)
**Fokus:** Modularisierung, Two-Pass Ingestion & Graph Intelligence.
**Aktueller Stand:** v2.9.3 (Post-WP25: Agentic Multi-Stream RAG)
**Fokus:** Agentic Orchestration, Multi-Stream Retrieval & Wissens-Synthese.
| Phase | Fokus | Status |
| :--- | :--- | :--- |
@ -48,6 +48,8 @@ Eine Übersicht der implementierten Features zum schnellen Auffinden von Funktio
| **WP-20** | **Cloud Hybrid Mode & Resilienz** | **Ergebnis:** Integration von OpenRouter (Mistral 7B) & Gemini 2.5 Lite. Implementierung von WP-76 (Rate-Limit Wait) & Mistral-safe JSON Parsing. |
| **WP-21** | Semantic Graph Routing & Canonical Edges | Transformation des Graphen von statischen Verbindungen zu dynamischen, kontextsensitiven Pfaden. Das System soll verstehen, *welche* Art von Verbindung für die aktuelle Frage relevant ist ("Warum?" vs. "Was kommt danach?"). |
| **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-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. |
### 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.
@ -191,53 +193,29 @@ Der bisherige WP-15 Ansatz litt unter Halluzinationen (erfundene Kantentypen), h
2. **Single Source of Truth (SSOT):** Die Registry nutzt `01_edge_vocabulary.md` als führende Konfiguration.
3. **Self-Learning Loop:** Protokollierung unbekannter Kanten in `unknown_edges.jsonl`.
### WP-23: Agentic Multi-Stream Reasoning (Mindnet 2025)
### WP-25: Agentic Multi-Stream RAG Orchestration
**Status:** ✅ Fertig (v2.9.3)
#### 1. Zielsetzung & Problemstellung
Das bisherige System basiert auf einem globalen Scoring-Modell, bei dem Notizen unterschiedlicher Typen (z. B. `insight` vs. `belief`) in einem einzigen Retrieval-Topf konkurrieren. Dies führt dazu, dass leiser gewichtete, aber fundamentale Identitätsmerkmale oft durch hochgewichtete aktuelle Erkenntnisse verdrängt werden. Ziel dieses Pakets ist die Einführung einer parallelen **Stream-Architektur**, um die Vielschichtigkeit menschlicher Entscheidungsprozesse (Werte + Erfahrung + Absicht) im LLM-Kontext zu garantieren.
**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.
#### 2. Funktionsbeschreibung: Die Streams
Die Daten aus der `types.yaml` werden in drei logische Verarbeitungseinheiten unterteilt:
**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
##### A. Identity Stream (Die Wahrheitsebene)
* **Inhalt:** `value`, `belief`, `trait`, `principle`, `need`, `boundary`, `bias`.
* **Zweck:** Definition des moralischen Kompasses, der psychologischen Grundbedürfnisse und kognitiven Muster.
* **Wirkung:** Liefert das "Warum" hinter jeder Handlung.
**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
##### B. History Stream (Die Evidenzebene)
* **Inhalt:** `experience`, `event`, `source`, `journal`, `person`.
* **Zweck:** Bereitstellung empirischer Belege aus der Vergangenheit und sozialer Kontexte.
* **Wirkung:** Verankert die Antwort in real erlebten Mustern und Fakten.
##### C. Action Stream (Die Dynamikebene)
* **Inhalt:** `project`, `decision`, `goal`, `task`, `risk`, `motivation`, `habit`, `state`.
* **Zweck:** Analyse der aktuellen Richtung, geplanter Vorhaben und des gegenwärtigen Zustands.
* **Wirkung:** Liefert den Kontext für die Umsetzung und zukünftige Ziele.
#### 3. Technische Wirkungsweise (Solution Sketch)
##### Schritt 1: Query-Decomposition
Ein initialer Klassifizierungs-Agent analysiert die Nutzeranfrage und bestimmt, welcher Stream primär angesprochen werden muss (z. B. "Wie soll ich mich entscheiden?" boostet den Identity Stream).
##### Schritt 2: Parallel Stream Retrieval
Anstelle einer Suche werden drei unabhängige Vektor-Suchen mit Typ-Filtern durchgeführt:
* **Search_A (Identity):** Top-5 Ergebnisse aus Identitäts-Notizen.
* **Search_B (History):** Top-5 Ergebnisse aus biografischen/externen Notizen.
* **Search_C (Action):** Top-5 Ergebnisse aus operativen/strategischen Notizen.
##### Schritt 3: Agentic Synthesis (The Reasoning)
Ein Synthese-Agent (LLM) erhält die aggregierten Ergebnisse in getrennten Sektionen. Die Anweisung lautet:
1. **Prüfung:** Steht das aktuelle Vorhaben (Action) im Einklang mit den Werten (Identity)?
2. **Abgleich:** Welche vergangenen Erfahrungen (History) stützen oder widersprechen diesem Weg?
3. **Korrektur:** Identifiziere mögliche Biases oder Grenzüberschreitungen (Boundary).
#### 4. Erwartete Ergebnisse
* **Höhere Resonanz:** Antworten wirken authentischer, da sie explizit auf das Wertesystem des Nutzers Bezug nehmen.
* **Widerspruchs-Erkennung:** Das System kann den Nutzer aktiv warnen, wenn ein Projekt gegen seine `principles` oder `needs` verstößt.
* **Robustes Retrieval:** Wichtige Identitäts-Informationen gehen nicht mehr im "Rauschen" von hunderten Journal-Einträgen verloren.
**Ausblick (WP-25a):**
- Pre-Synthesis: LLM-basierte Komprimierung überlanger Streams
- Kontext-Budgeting: Intelligente Token-Verteilung
- Stream-specific Provider: Unterschiedliche KI-Modelle pro Wissensbereich
---
### WP-24 Proactive Discovery & Agentic Knowledge Mining

View File

@ -1,4 +1,4 @@
# Release Notes: Mindnet v2.9.1 (WP15c)
# Release Notes: Mindnet v2.9.3 (WP15c)
**Release Date:** 2025-12-31
**Type:** Feature Release - Multigraph & Diversity Engine

View File

@ -0,0 +1,138 @@
# Branch Merge Commit Message: WP25
```
feat: Agentic Multi-Stream RAG Orchestration (v3.0)
## Architektur-Transformation (WP-25)
### Agentic Multi-Stream RAG Orchestration
- Übergang von linearer RAG-Architektur zu paralleler Multi-Stream Engine
- Parallele Abfragen in spezialisierten Wissens-Streams (Values, Facts, Biography, Risk, Tech)
- Stream-Tracing: Jeder Treffer wird mit `stream_origin` markiert
- Fehler-Resilienz: Einzelne Stream-Fehler blockieren nicht die gesamte Anfrage
**Geänderte Dateien:**
- `app/core/retrieval/decision_engine.py`: Multi-Stream Orchestrator (v1.0.3)
- `app/routers/chat.py`: Hybrid Router Integration (v3.0.2)
- `app/models/dto.py`: Stream-Tracing Support (v0.7.1)
### Intent-basiertes Routing ("The Brain")
- Hybrid-Modus: Keyword Fast-Path + LLM Slow-Path
- Strategien: FACT_WHAT, FACT_WHEN, DECISION, EMPATHY, CODING, INTERVIEW
- Sofortige Erkennung von Triggern wie "Soll ich" oder "Wann" ohne LLM-Call
- Komplexe semantische Analyse für unklare Anfragen
**Geänderte Dateien:**
- `app/routers/chat.py`: Hybrid Router mit Keyword-Fast-Path (v3.0.2)
- `app/core/retrieval/decision_engine.py`: LLM-basiertes Routing (v1.0.3)
### Wissens-Synthese
- Spezialisierte Templates in `prompts.yaml` mit expliziten Stream-Variablen
- Pre-Initialization aller Stream-Variablen (verhindert KeyErrors)
- Provider-spezifische Templates für Ollama, Gemini und OpenRouter
- Differenzierte Abwägung zwischen Fakten und persönlichen Werten
**Geänderte Dateien:**
- `config/prompts.yaml`: Stream-Templates (v3.1.2)
- `app/core/retrieval/decision_engine.py`: Synthese-Logik (v1.0.3)
## Stream-Konfiguration & Typ-Synchronisation
### Stream-Library (decision_engine.yaml v3.1.6)
- **Values Stream:** Identität, Ethik und Prinzipien (filter_types: value, principle, belief, etc.)
- **Facts Stream:** Operative Daten (filter_types: project, decision, task, etc.)
- **Biography Stream:** Persönliche Erfahrungen (filter_types: experience, journal, profile)
- **Risk Stream:** Hindernisse und Gefahren (filter_types: risk, obstacle, bias)
- **Tech Stream:** Technisches Wissen (filter_types: concept, source, glossary, etc.)
**Edge-Boosts pro Stream:**
- Values: `guides: 3.0`, `enforced_by: 2.5`, `based_on: 2.0`
- Facts: `part_of: 2.0`, `depends_on: 1.5`, `implemented_in: 1.5`
- Risk: `blocks: 2.5`, `impacts: 2.0`, `risk_of: 2.5`
**Geänderte Dateien:**
- `config/decision_engine.yaml`: Multi-Stream Konfiguration (v3.1.6)
## Bugfixes & Optimierungen
### Vault-Import Performance
- "Empty Response Guard" korrigiert: Kurze Ingest-Antworten (YES/NO) lösen nicht mehr fälschlicherweise langsame Fallbacks aus
- Entfernung des <5-Zeichen Guards ermöglicht YES/NO Validierungen
**Geänderte Dateien:**
- `app/services/llm_service.py`: Ingest-Stability Patch (v3.4.2)
### Cloud-Resilienz
- Sicherheitscheck für das `choices`-Array in `_execute_openrouter` implementiert
- Verhindert JSON-Errors bei überlasteten APIs
**Geänderte Dateien:**
- `app/services/llm_service.py`: Empty Response Guard (v3.4.2)
### Template-Robustheit
- Automatische Vor-Initialisierung aller Stream-Variablen in der `DecisionEngine`
- Verhindert `KeyError`-Abstürze bei unvollständigen Konfigurationen
- Fallback-Mechanismus bei Template-Fehlern
**Geänderte Dateien:**
- `app/core/retrieval/decision_engine.py`: Pre-Initialization (v1.0.3)
### Intent-Kollision
- Generische Begriffe (wie "Projekt") wurden aus den Keyword-Triggern entfernt
- Sicherstellt, dass strategische Fragen (z.B. "Soll ich...?") korrekt als `DECISION` geroutet werden
**Geänderte Dateien:**
- `config/decision_engine.yaml`: Keyword-Fix (v3.1.6)
## Ollama Context-Throttling
- Vor der Übergabe an Ollama prüft der Chat-Router, ob der Kontext die Grenze von `MAX_OLLAMA_CHARS` überschreitet (Standard: 10.000)
- Automatische Kürzung bei großen Kontexten
**Geänderte Dateien:**
- `app/routers/chat.py`: Context-Throttling (v3.0.2)
## Lifespan Management
- FastAPI-Anwendung implementiert Lifespan-Management für sauberen Startup und Shutdown
- Integritäts-Check der WP-25 Konfiguration beim Startup
- Ressourcen-Cleanup beim Shutdown
- Globale Fehlerbehandlung für asynchrone Prozesse
**Geänderte Dateien:**
- `app/main.py`: Lifespan Management (v1.0.0)
## Impact & Breaking Changes
### Keine Migration erforderlich
**WICHTIG:** Diese Version ist **rückwärtskompatibel**. Bestehende Vaults funktionieren ohne Re-Import.
**Konfigurations-Updates:**
- `decision_engine.yaml` wurde auf v3.1.6 aktualisiert (Multi-Stream Struktur)
- `prompts.yaml` wurde auf v3.1.2 aktualisiert (Stream-Templates)
- **Empfehlung:** Backup der alten Konfigurationsdateien vor dem Update
### API-Erweiterungen
- `QueryHit.stream_origin`: Name des Ursprungs-Streams (optional)
- `ChatResponse.intent`: Die gewählte WP-25 Strategie (optional)
- `ChatResponse.intent_source`: Quelle der Intent-Erkennung (optional)
## Dokumentation
Alle relevanten Dokumente aktualisiert:
- `03_tech_chat_backend.md`: Agentic Multi-Stream RAG, Intent-basiertes Routing, Wissens-Synthese
- `03_tech_configuration.md`: decision_engine.yaml, prompts.yaml (Stream-Struktur)
- `03_tech_api_reference.md`: Erweiterte DTOs (stream_origin, intent)
## Versionen
- Decision Engine: v1.0.3
- Chat Router: v3.0.2
- LLM Service: v3.4.2
- DTOs: v0.7.1
- Main: v1.0.0
- decision_engine.yaml: v3.1.6
- prompts.yaml: v3.1.2
Closes #[issue-number]
```

View File

@ -0,0 +1,268 @@
# Release Notes: Mindnet v3.0.0 (WP25)
**Release Date:** 2026-01-01
**Type:** Feature Release - Agentic Multi-Stream RAG Orchestration
**Branch:** WP25
---
## 🎯 Übersicht
Diese Version markiert den Übergang 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.
---
## ✨ Neue Features
### Agentic Multi-Stream RAG Orchestration
Mindnet führt nun **parallele Abfragen** in spezialisierten Wissens-Streams aus, anstatt einer einzelnen Suche:
**Stream-Library:**
* **Values Stream:** Extrahiert Identität, Ethik und Prinzipien (`value`, `principle`, `belief`, `trait`, `boundary`, `need`, `motivation`).
* **Facts Stream:** Liefert operative Daten zu Projekten, Tasks und Status (`project`, `decision`, `task`, `goal`, `event`, `state`).
* **Biography Stream:** Greift auf persönliche Erfahrungen und Journal-Einträge zu (`experience`, `journal`, `profile`, `person`).
* **Risk Stream:** Identifiziert Hindernisse und potenzielle Gefahren (`risk`, `obstacle`, `bias`).
* **Tech Stream:** Bündelt technisches Wissen, Code und Dokumentation (`concept`, `source`, `glossary`, `idea`, `insight`, `skill`, `habit`).
**Vorteile:**
* **Präzise Kontext-Ladung:** Jeder Stream fokussiert auf spezifische Wissensbereiche.
* **Parallele Ausführung:** Alle Streams werden gleichzeitig abgefragt (asyncio.gather).
* **Stream-Tracing:** Jeder Treffer wird mit `stream_origin` markiert für Feedback-Optimierung.
* **Fehler-Resilienz:** Einzelne Stream-Fehler blockieren nicht die gesamte Anfrage.
### Intent-basiertes Routing ("The Brain")
Der Router wurde zu einem **hybriden System** erweitert, das Anfragen klassifiziert, bevor die Suche beginnt:
**Strategien:**
* **FACT_WHAT:** Wissen/Listen (z.B. "Was ist...", "Welche sind...").
* **FACT_WHEN:** Zeitpunkte (z.B. "Wann...", "Datum...").
* **DECISION:** Beratung (z.B. "Soll ich...", "Sollte ich...", "Empfehlung...").
* **EMPATHY:** Reflexion (z.B. "Fühle...", "Stress...", "Angst...").
* **CODING:** Technik (z.B. "Code...", "Python...", "Bug...").
* **INTERVIEW:** Datenerfassung (z.B. "Neues Projekt...", "Erfahrung...").
**Hybrid-Modus:**
* **Keyword Fast-Path:** Sofortige Erkennung von Triggern wie "Soll ich" oder "Wann" ohne LLM-Call.
* **LLM Slow-Path:** Komplexe semantische Analyse für unklare Anfragen.
* **Type Keywords:** Automatische Erkennung von Objekt-Typen für Interview-Modus.
**Vorteil:** Reduziert Latenz durch schnelle Keyword-Erkennung, nutzt LLM nur bei Bedarf.
### Wissens-Synthese
Die Zusammenführung der Daten erfolgt über spezialisierte Templates in der `prompts.yaml`:
**Template-Struktur:**
* Explizite Variablen für jeden Stream (z.B. `{values_stream}`, `{risk_stream}`).
* **Pre-Initialization:** Alle möglichen Stream-Variablen werden vorab initialisiert (verhindert KeyErrors).
* **Provider-spezifische Templates:** Separate Versionen für Ollama, Gemini und OpenRouter.
**Synthese-Strategien:**
* **FACT_WHAT/FACT_WHEN:** Kombiniert Fakten, Biographie und Technik.
* **DECISION:** Wägt Fakten gegen Werte ab, evaluiert Risiken.
* **EMPATHY:** Fokus auf Biographie und Werte.
* **CODING:** Technik und Fakten.
**Vorteil:** Ermöglicht dem LLM eine differenzierte Abwägung zwischen Fakten und persönlichen Werten.
---
## 🔧 Verbesserungen
### Stream-Konfiguration & Typ-Synchronisation
Jeder Stream nutzt individuelle **Edge-Boosts** und **Filter-Types**, die strikt mit der `types.yaml` (v2.7.0) synchronisiert sind:
**Beispiele:**
* **Values Stream:** `guides: 3.0`, `enforced_by: 2.5`, `based_on: 2.0`.
* **Facts Stream:** `part_of: 2.0`, `depends_on: 1.5`, `implemented_in: 1.5`.
* **Risk Stream:** `blocks: 2.5`, `impacts: 2.0`, `risk_of: 2.5`.
**Vorteil:** Präzise Gewichtung der Graph-Kanten je nach Wissensbereich.
### Template-Robustheit
**Pre-Initialization:** Automatische Vor-Initialisierung aller Stream-Variablen in der `DecisionEngine` verhindert `KeyError`-Abstürze bei unvollständigen Konfigurationen.
**Fallback-Mechanismus:** Bei Template-Fehlern wird ein vereinfachter Prompt mit allen verfügbaren Stream-Ergebnissen verwendet.
### Ollama Context-Throttling
Vor der Übergabe an Ollama prüft der Chat-Router, ob der Kontext (RAG-Hits) die Grenze von `MAX_OLLAMA_CHARS` überschreitet (Standard: 10.000) und kürzt diesen ggf.
**Vorteil:** Verhindert VRAM-Überlastung bei großen Kontexten.
---
## 🐛 Bugfixes
- ✅ **Behoben:** Vault-Import Performance - "Empty Response Guard" korrigiert, damit kurze Ingest-Antworten (YES/NO) nicht mehr fälschlicherweise langsame Fallbacks auslösen.
- ✅ **Behoben:** Cloud-Resilienz - Sicherheitscheck für das `choices`-Array in `_execute_openrouter` implementiert, um JSON-Errors bei überlasteten APIs zu verhindern.
- ✅ **Behoben:** Template-Robustheit - Automatische Vor-Initialisierung aller Stream-Variablen verhindert `KeyError`-Abstürze.
- ✅ **Behoben:** Intent-Kollision - Generische Begriffe (wie "Projekt") wurden aus den Keyword-Triggern entfernt, um sicherzustellen, dass strategische Fragen (z.B. "Soll ich...?") korrekt als `DECISION` geroutet werden.
---
## ⚠️ Breaking Changes & Migration
### Keine Migration erforderlich
**WICHTIG:** Diese Version ist **rückwärtskompatibel**. Bestehende Vaults funktionieren ohne Re-Import.
**Konfigurations-Updates:**
* `decision_engine.yaml` wurde auf v3.1.6 aktualisiert (Multi-Stream Struktur).
* `prompts.yaml` wurde auf v3.1.2 aktualisiert (Stream-Templates).
* **Empfehlung:** Backup der alten Konfigurationsdateien vor dem Update.
---
## 📚 API-Änderungen
### Erweiterte DTOs
**QueryHit (erweitert):**
```python
class QueryHit(BaseModel):
# ... bestehende Felder ...
stream_origin: Optional[str] = None # Neu: Name des Ursprungs-Streams
```
**ChatResponse (erweitert):**
```python
class ChatResponse(BaseModel):
# ... bestehende Felder ...
intent: Optional[str] = "FACT" # Neu: Die gewählte WP-25 Strategie
intent_source: Optional[str] = "LLM_Router" # Neu: Quelle der Intent-Erkennung
```
**Impact für API-Consumer:**
* Frontend kann `stream_origin` für visuelle Kennzeichnung der Quellen nutzen.
* `intent` und `intent_source` ermöglichen Debugging und Analytics.
---
## 📖 Dokumentation
Alle relevanten Dokumente wurden aktualisiert:
- ✅ `03_tech_chat_backend.md`: Agentic Multi-Stream RAG, Intent-basiertes Routing, Wissens-Synthese
- ✅ `03_tech_configuration.md`: decision_engine.yaml, prompts.yaml (Stream-Struktur)
- ✅ `03_tech_api_reference.md`: Erweiterte DTOs (stream_origin, intent)
---
## 🔄 Technische Details
### Geänderte Module
**Decision Engine:**
- `app/core/retrieval/decision_engine.py`: Multi-Stream Orchestrator (v1.0.3)
- Paralleles Retrieval via `_execute_parallel_streams()`
- Stream-Tracing mit `stream_origin`
- Pre-Initialization der Stream-Variablen
**Chat Router:**
- `app/routers/chat.py`: Hybrid Router Integration (v3.0.2)
- Keyword Fast-Path + LLM Slow-Path
- Multi-Stream Orchestrierung
- Ollama Context-Throttling
**LLM Service:**
- `app/services/llm_service.py`: Ingest-Stability Patch (v3.4.2)
- Entfernung des <5-Zeichen Guards
- Empty Response Guard für OpenRouter
- Lazy Initialization der DecisionEngine
**DTOs:**
- `app/models/dto.py`: Stream-Tracing Support (v0.7.1)
- `stream_origin` in `QueryHit`
- `intent` und `intent_source` in `ChatResponse`
**Main:**
- `app/main.py`: Lifespan Management (v1.0.0)
- Startup-Integritäts-Check
- Ressourcen-Cleanup beim Shutdown
- Globale Fehlerbehandlung
**Konfiguration:**
- `config/decision_engine.yaml`: Multi-Stream Konfiguration (v3.1.6)
- `config/prompts.yaml`: Stream-Templates (v3.1.2)
### Versionsnummern
- Decision Engine: **v1.0.3**
- Chat Router: **v3.0.2**
- LLM Service: **v3.4.2**
- DTOs: **v0.7.1**
- Main: **v1.0.0**
- decision_engine.yaml: **v3.1.6**
- prompts.yaml: **v3.1.2**
---
## 🚀 Upgrade-Pfad
### Für Administratoren
1. **Code aktualisieren:**
```bash
git pull origin main
source .venv/bin/activate
pip install -r requirements.txt
```
2. **Konfiguration prüfen:**
```bash
# Backup der alten Konfigurationen
cp config/decision_engine.yaml config/decision_engine.yaml.backup
cp config/prompts.yaml config/prompts.yaml.backup
# Prüfen, ob neue Konfigurationen vorhanden sind
ls -la config/decision_engine.yaml config/prompts.yaml
```
3. **Services neu starten:**
```bash
sudo systemctl restart mindnet-prod
sudo systemctl restart mindnet-ui-prod
```
### Für Entwickler
- Keine Code-Änderungen erforderlich, wenn nur API genutzt wird
- Frontend kann neue Felder (`stream_origin`, `intent`, `intent_source`) optional nutzen
- Stream-Tracing ermöglicht Feedback-Optimierung pro Wissensbereich
---
## 📝 Bekannte Einschränkungen
- **Stream-Parallelität:** Maximale Anzahl paralleler Streams ist durch asyncio-Limits begrenzt (typischerweise 5-10 Streams).
- **Kontext-Größe:** Große Kontexte (>10.000 Zeichen) werden bei Ollama automatisch gekürzt.
---
## 🔮 Ausblick (WP-25a: Agentic Refinement)
Auf Basis des stabilen WP-25 Fundaments sind folgende Erweiterungen geplant:
* **Pre-Synthesis:** LLM-basierte Komprimierung überlanger Streams vor der finalen Antwortgenerierung.
* **Kontext-Budgeting:** Intelligente Verteilung der verfügbaren Token auf die aktivierten Streams.
* **Stream-specific Provider:** Zuweisung unterschiedlicher KI-Modelle pro Wissensbereich (z.B. lokales Ollama für Identitäts-Werte, Gemini für technische Fakten).
---
## 🙏 Danksagungen
Diese Version wurde durch umfangreiche Architektur-Überarbeitung ermöglicht. Besonderer Fokus lag auf:
- Transformation zu einer Agentic Multi-Stream Engine
- Intelligente Intent-Erkennung mit Hybrid-Routing
- Parallele Wissens-Synthese mit wertebasierter Abwägung
- Robustheit und Fehler-Resilienz
---
**Vollständige Changelog:** Siehe Git-Commits für detaillierte Änderungen
**Support:** Bei Fragen siehe [Admin Operations Guide](../04_Operations/04_admin_operations.md)

View File

@ -2,13 +2,13 @@
doc_type: documentation_index
audience: all
status: active
version: 2.9.1
version: 2.9.3
context: "Zentraler Einstiegspunkt für die Mindnet-Dokumentation"
---
# Mindnet Dokumentation
Willkommen in der Dokumentation von Mindnet v2.9.1! Diese Dokumentation hilft dir dabei, das System zu verstehen, zu nutzen und weiterzuentwickeln.
Willkommen in der Dokumentation von Mindnet v2.9.3! Diese Dokumentation hilft dir dabei, das System zu verstehen, zu nutzen und weiterzuentwickeln.
## 🚀 Schnellstart