Merge pull request 'WP25' (#19) from WP25 into main
All checks were successful
Deploy mindnet to llm-node / deploy (push) Successful in 5s

Reviewed-on: #19
This commit is contained in:
Lars 2026-01-01 20:26:05 +01:00
commit 3d2f3d12d9
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 FILE: app/main.py
DESCRIPTION: Bootstrap der FastAPI Anwendung. Inkludiert Router und Middleware. DESCRIPTION: Bootstrap der FastAPI Anwendung für WP-25 (Agentic RAG).
VERSION: 0.6.0 Orchestriert Lifespan-Events, globale Fehlerbehandlung und Routing.
VERSION: 1.0.0 (WP-25 Release)
STATUS: Active STATUS: Active
DEPENDENCIES: app.config, app.routers.* (embed, qdrant, query, graph, tools, feedback, chat, ingest, admin) DEPENDENCIES: app.config, app.routers.*, app.services.llm_service
LAST_ANALYSIS: 2025-12-15
""" """
from __future__ import annotations from __future__ import annotations
from fastapi import FastAPI import logging
from .config import get_settings import os
#from .routers.embed_router import router as embed_router from contextlib import asynccontextmanager
#from .routers.qdrant_router import router as qdrant_router 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.query import router as query_router
from .routers.graph import router as graph_router from .routers.graph import router as graph_router
from .routers.tools import router as tools_router from .routers.tools import router as tools_router
from .routers.feedback import router as feedback_router from .routers.feedback import router as feedback_router
# NEU: Chat Router (WP-05)
from .routers.chat import router as chat_router from .routers.chat import router as chat_router
# NEU: Ingest Router (WP-11)
from .routers.ingest import router as ingest_router from .routers.ingest import router as ingest_router
try: try:
@ -27,26 +30,86 @@ try:
except Exception: except Exception:
admin_router = None 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: 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() 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") @app.get("/healthz")
def healthz(): def healthz():
return {"status": "ok", "qdrant": s.QDRANT_URL, "prefix": s.COLLECTION_PREFIX} return {
"status": "ok",
# app.include_router(embed_router) "version": "1.0.0",
# app.include_router(qdrant_router) "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(query_router, prefix="/query", tags=["query"])
app.include_router(graph_router, prefix="/graph", tags=["graph"]) app.include_router(graph_router, prefix="/graph", tags=["graph"])
app.include_router(tools_router, prefix="/tools", tags=["tools"]) app.include_router(tools_router, prefix="/tools", tags=["tools"])
app.include_router(feedback_router, prefix="/feedback", tags=["feedback"]) app.include_router(feedback_router, prefix="/feedback", tags=["feedback"])
app.include_router(chat_router, prefix="/chat", tags=["chat"]) # Nutzt nun WP-25 DecisionEngine
# NEU: Chat Endpoint
app.include_router(chat_router, prefix="/chat", tags=["chat"])
# NEU: Ingest Endpoint
app.include_router(ingest_router, prefix="/ingest", tags=["ingest"]) app.include_router(ingest_router, prefix="/ingest", tags=["ingest"])
if admin_router: if admin_router:
@ -54,4 +117,5 @@ def create_app() -> FastAPI:
return app return app
# Instanziierung der App
app = create_app() app = create_app()

View File

@ -1,10 +1,9 @@
""" """
FILE: app/models/dto.py FILE: app/models/dto.py
DESCRIPTION: Pydantic-Modelle (DTOs) für Request/Response Bodies. Definiert das API-Schema. 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 STATUS: Active
DEPENDENCIES: pydantic, typing, uuid DEPENDENCIES: pydantic, typing, uuid
LAST_ANALYSIS: 2025-12-29
""" """
from __future__ import annotations from __future__ import annotations
@ -12,8 +11,14 @@ from pydantic import BaseModel, Field
from typing import List, Literal, Optional, Dict, Any from typing import List, Literal, Optional, Dict, Any
import uuid import uuid
# Gültige Kanten-Typen gemäß Manual # 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"] 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 --- # --- Basis-DTOs ---
@ -43,14 +48,14 @@ class EdgeDTO(BaseModel):
direction: Literal["out", "in", "undirected"] = "out" direction: Literal["out", "in", "undirected"] = "out"
provenance: Optional[Literal["explicit", "rule", "smart", "structure"]] = "explicit" provenance: Optional[Literal["explicit", "rule", "smart", "structure"]] = "explicit"
confidence: float = 1.0 confidence: float = 1.0
target_section: Optional[str] = None # Neu: Speichert den Anker (z.B. #Abschnitt) target_section: Optional[str] = None
# --- Request Models --- # --- Request Models ---
class QueryRequest(BaseModel): class QueryRequest(BaseModel):
""" """
Request für /query. Request für /query. Unterstützt Multi-Stream Isolation via filters.
""" """
mode: Literal["semantic", "edge", "hybrid"] = "hybrid" mode: Literal["semantic", "edge", "hybrid"] = "hybrid"
query: Optional[str] = None query: Optional[str] = None
@ -61,14 +66,12 @@ class QueryRequest(BaseModel):
ret: Dict = {"with_paths": True, "with_notes": True, "with_chunks": True} ret: Dict = {"with_paths": True, "with_notes": True, "with_chunks": True}
explain: bool = False explain: bool = False
# WP-22: Semantic Graph Routing # WP-22/25: Dynamische Gewichtung der Graphen-Highways
boost_edges: Optional[Dict[str, float]] = None boost_edges: Optional[Dict[str, float]] = None
class FeedbackRequest(BaseModel): class FeedbackRequest(BaseModel):
""" """User-Feedback zu einem spezifischen Treffer oder der Gesamtantwort."""
User-Feedback zu einem spezifischen Treffer oder der Gesamtantwort (WP-08 Basis).
"""
query_id: str = Field(..., description="ID der ursprünglichen Suche") query_id: str = Field(..., description="ID der ursprünglichen Suche")
node_id: str = Field(..., description="ID des bewerteten Treffers oder 'generated_answer'") 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)") score: int = Field(..., ge=1, le=5, description="1 (Irrelevant) bis 5 (Perfekt)")
@ -76,16 +79,14 @@ class FeedbackRequest(BaseModel):
class ChatRequest(BaseModel): class ChatRequest(BaseModel):
""" """Request für /chat (WP-25 Einstieg)."""
WP-05: Request für /chat.
"""
message: str = Field(..., description="Die Nachricht des Users") message: str = Field(..., description="Die Nachricht des Users")
conversation_id: Optional[str] = Field(None, description="ID für Chat-Verlauf") conversation_id: Optional[str] = Field(None, description="ID für Chat-Verlauf")
top_k: int = 5 top_k: int = 5
explain: bool = False explain: bool = False
# --- WP-04b Explanation Models --- # --- Explanation Models ---
class ScoreBreakdown(BaseModel): class ScoreBreakdown(BaseModel):
"""Aufschlüsselung der Score-Komponenten nach der WP-22 Formel.""" """Aufschlüsselung der Score-Komponenten nach der WP-22 Formel."""
@ -96,14 +97,14 @@ class ScoreBreakdown(BaseModel):
raw_edge_bonus: float raw_edge_bonus: float
raw_centrality: float raw_centrality: float
node_weight: float node_weight: float
# WP-22 Debug Fields für Messbarkeit
status_multiplier: float = 1.0 status_multiplier: float = 1.0
graph_boost_factor: float = 1.0 graph_boost_factor: float = 1.0
class Reason(BaseModel): class Reason(BaseModel):
"""Ein semantischer Grund für das Ranking.""" """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 message: str
score_impact: Optional[float] = None score_impact: Optional[float] = None
details: Optional[Dict[str, Any]] = None details: Optional[Dict[str, Any]] = None
@ -114,7 +115,6 @@ class Explanation(BaseModel):
breakdown: ScoreBreakdown breakdown: ScoreBreakdown
reasons: List[Reason] reasons: List[Reason]
related_edges: Optional[List[EdgeDTO]] = None related_edges: Optional[List[EdgeDTO]] = None
# WP-22 Debug: Verifizierung des Routings
applied_intent: Optional[str] = None applied_intent: Optional[str] = None
applied_boosts: Optional[Dict[str, float]] = None applied_boosts: Optional[Dict[str, float]] = None
@ -122,7 +122,10 @@ class Explanation(BaseModel):
# --- Response Models --- # --- Response Models ---
class QueryHit(BaseModel): 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 node_id: str
note_id: str note_id: str
semantic_score: float semantic_score: float
@ -133,10 +136,11 @@ class QueryHit(BaseModel):
source: Optional[Dict] = None source: Optional[Dict] = None
payload: Optional[Dict] = None payload: Optional[Dict] = None
explanation: Optional[Explanation] = None explanation: Optional[Explanation] = None
stream_origin: Optional[str] = Field(None, description="Name des Ursprungs-Streams")
class QueryResponse(BaseModel): 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())) query_id: str = Field(default_factory=lambda: str(uuid.uuid4()))
results: List[QueryHit] results: List[QueryHit]
used_mode: str used_mode: str
@ -153,11 +157,12 @@ class GraphResponse(BaseModel):
class ChatResponse(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") query_id: str = Field(..., description="Traceability ID")
answer: str = Field(..., description="Generierte Antwort vom LLM") 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 latency_ms: int
intent: Optional[str] = Field("FACT", description="WP-06: Erkannter Intent") intent: Optional[str] = Field("FACT", description="Die gewählte WP-25 Strategie")
intent_source: Optional[str] = Field("Unknown", description="Quelle der Intent-Erkennung") intent_source: Optional[str] = Field("LLM_Router", description="Quelle der Intent-Erkennung")

View File

@ -1,12 +1,15 @@
""" """
FILE: app/routers/chat.py FILE: app/routers/chat.py
DESCRIPTION: Haupt-Chat-Interface (RAG & Interview). Enthält Intent-Router (Keywords/LLM) und Prompt-Construction. DESCRIPTION: Haupt-Chat-Interface (WP-25 Agentic Edition).
VERSION: 2.7.8 (Full Unabridged Stability Edition) Kombiniert die spezialisierte Interview-Logik und Keyword-Erkennung
mit der neuen Multi-Stream Orchestrierung der DecisionEngine.
VERSION: 3.0.2
STATUS: Active STATUS: Active
FIX: FIX:
1. Implementiert Context-Throttling für Ollama (MAX_OLLAMA_CHARS). - 100% Wiederherstellung der v2.7.8 Logik (Interview, Schema-Resolution, Keywords).
2. Deaktiviert LLM-Retries für den Chat (max_retries=0). - Integration der DecisionEngine für paralleles RAG-Retrieval.
3. Behebt Double-Fallback-Schleifen und Silent Refusals. - 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 from fastapi import APIRouter, HTTPException, Depends
@ -19,47 +22,40 @@ import os
from pathlib import Path from pathlib import Path
from app.config import get_settings 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.services.llm_service import LLMService
from app.core.retrieval.retriever import Retriever
from app.services.feedback_service import log_search from app.services.feedback_service import log_search
router = APIRouter() router = APIRouter()
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# --- Helper: Config Loader --- # --- EBENE 1: CONFIG LOADER & CACHING (Restauriert aus v2.7.8) ---
_DECISION_CONFIG_CACHE = None _DECISION_CONFIG_CACHE = None
_TYPES_CONFIG_CACHE = None _TYPES_CONFIG_CACHE = None
def _load_decision_config() -> Dict[str, Any]: def _load_decision_config() -> Dict[str, Any]:
"""Lädt die Strategie-Konfiguration (Kompatibilität zu WP-25)."""
settings = get_settings() settings = get_settings()
path = Path(settings.DECISION_CONFIG_PATH) 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: try:
with open(path, "r", encoding="utf-8") as f: if path.exists():
return yaml.safe_load(f) with open(path, "r", encoding="utf-8") as f:
return yaml.safe_load(f) or {}
except Exception as e: except Exception as e:
logger.error(f"Failed to load decision config: {e}") logger.error(f"Failed to load decision config: {e}")
return default_config return {"strategies": {}}
def _load_types_config() -> Dict[str, Any]: 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") path = os.getenv("MINDNET_TYPES_FILE", "config/types.yaml")
try: try:
with open(path, "r", encoding="utf-8") as f: if os.path.exists(path):
return yaml.safe_load(f) or {} with open(path, "r", encoding="utf-8") as f:
except Exception: return yaml.safe_load(f) or {}
return {} except Exception as e:
logger.error(f"Failed to load types config: {e}")
return {}
def get_full_config() -> Dict[str, Any]: def get_full_config() -> Dict[str, Any]:
global _DECISION_CONFIG_CACHE global _DECISION_CONFIG_CACHE
@ -76,21 +72,20 @@ def get_types_config() -> Dict[str, Any]:
def get_decision_strategy(intent: str) -> Dict[str, Any]: def get_decision_strategy(intent: str) -> Dict[str, Any]:
config = get_full_config() config = get_full_config()
strategies = config.get("strategies", {}) 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: def _detect_target_type(message: str, configured_schemas: Dict[str, Any]) -> str:
""" """
Versucht zu erraten, welchen Notiz-Typ der User erstellen will. WP-07: Identifiziert den gewünschten Notiz-Typ (Keyword-basiert).
Nutzt Keywords aus types.yaml UND Mappings. 100% identisch mit v2.7.8 zur Sicherstellung des Interview-Workflows.
""" """
message_lower = message.lower() message_lower = message.lower()
# 1. Check types.yaml detection_keywords (Priority!)
types_cfg = get_types_config() types_cfg = get_types_config()
types_def = types_cfg.get("types", {}) types_def = types_cfg.get("types", {})
# 1. Check types.yaml detection_keywords
for type_name, type_data in types_def.items(): for type_name, type_data in types_def.items():
keywords = type_data.get("detection_keywords", []) keywords = type_data.get("detection_keywords", [])
for kw in 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: if type_key in message_lower:
return type_key return type_key
# 3. Synonym-Mapping (Legacy Fallback) # 3. Synonym-Mapping (Legacy)
synonyms = { synonyms = {
"projekt": "project", "vorhaben": "project", "projekt": "project", "entscheidung": "decision", "ziel": "goal",
"entscheidung": "decision", "beschluss": "decision", "erfahrung": "experience", "wert": "value", "prinzip": "principle"
"ziel": "goal",
"erfahrung": "experience", "lektion": "experience",
"wert": "value",
"prinzip": "principle",
"notiz": "default", "idee": "default"
} }
for term, schema_key in synonyms.items(): for term, schema_key in synonyms.items():
if term in message_lower: if term in message_lower:
return schema_key return schema_key
return "default" 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: 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() q = query.strip().lower()
if "?" in q: return True if "?" in q: return True
starters = ["wer", "wie", "was", "wo", "wann", "warum", "weshalb", "wozu", "welche", "bist du"]
# W-Fragen Indikatoren return any(q.startswith(s + " ") for s in starters)
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
async def _classify_intent(query: str, llm: LLMService) -> tuple[str, str]: async def _classify_intent(query: str, llm: LLMService) -> tuple[str, str]:
""" """
Hybrid Router v5: WP-25 Hybrid Router:
1. Decision Keywords (Strategie) -> Prio 1 Nutzt erst Keyword-Fast-Paths (Router) und delegiert dann an die DecisionEngine.
2. Type Keywords (Interview Trigger) -> Prio 2
3. LLM (Fallback) -> Prio 3
""" """
config = get_full_config() config = get_full_config()
strategies = config.get("strategies", {}) strategies = config.get("strategies", {})
settings = config.get("settings", {})
query_lower = query.lower() query_lower = query.lower()
# 1. FAST PATH A: Strategie Keywords # 1. FAST PATH: Keyword Trigger
for intent_name, strategy in strategies.items(): for intent_name, strategy in strategies.items():
if intent_name == "FACT": continue
keywords = strategy.get("trigger_keywords", []) keywords = strategy.get("trigger_keywords", [])
for k in keywords: for k in keywords:
if k.lower() in query_lower: if k.lower() in query_lower:
return intent_name, "Keyword (Strategy)" return intent_name, "Keyword (FastPath)"
# 2. FAST PATH B: Type Keywords -> INTERVIEW # 2. FAST PATH B: Type Keywords -> INTERVIEW
if not _is_question(query_lower): if not _is_question(query_lower):
types_cfg = get_types_config() types_cfg = get_types_config()
types_def = types_cfg.get("types", {}) for type_name, type_data in types_cfg.get("types", {}).items():
for kw in type_data.get("detection_keywords", []):
for type_name, type_data in types_def.items():
keywords = type_data.get("detection_keywords", [])
for kw in keywords:
if kw.lower() in query_lower: if kw.lower() in query_lower:
return "INTERVIEW", f"Keyword (Type: {type_name})" return "INTERVIEW", "Keyword (Interview)"
# 3. SLOW PATH: LLM Router # 3. SLOW PATH: DecisionEngine LLM Router
if settings.get("llm_fallback_enabled", False): intent = await llm.decision_engine._determine_strategy(query)
router_prompt_template = llm.get_prompt("llm_router_prompt") return intent, "DecisionEngine (LLM)"
if router_prompt_template: # --- EBENE 3: RETRIEVAL AGGREGATION ---
prompt = router_prompt_template.replace("{query}", query)
logger.info("Keywords failed (or Question detected). Asking LLM for Intent...")
try: def _collect_all_hits(stream_responses: Dict[str, Any]) -> List[QueryHit]:
# FIX: Auch beim Routing keine Retries im Chat-Fluss """Sammelt und dedupliziert Treffer aus allen parallelen Streams."""
raw_response = await llm.generate_raw_response(prompt, priority="realtime", max_retries=0) all_hits = []
llm_output_upper = raw_response.upper() 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)
if "INTERVIEW" in llm_output_upper or "CREATE" in llm_output_upper: # --- EBENE 4: ENDPUNKT ---
return "INTERVIEW", "LLM Router"
for strat_key in strategies.keys(): def get_llm_service():
if strat_key in llm_output_upper: return LLMService()
return strat_key, "LLM Router"
except Exception as e:
logger.error(f"Router LLM failed: {e}")
return "FACT", "Default (No Match)"
@router.post("/", response_model=ChatResponse) @router.post("/", response_model=ChatResponse)
async def chat_endpoint( async def chat_endpoint(
request: ChatRequest, request: ChatRequest,
llm: LLMService = Depends(get_llm_service), llm: LLMService = Depends(get_llm_service)
retriever: Retriever = Depends(get_retriever)
): ):
start_time = time.time() start_time = time.time()
query_id = str(uuid.uuid4()) 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: try:
# 1. Intent Detection # 1. Intent Detection
intent, intent_source = await _classify_intent(request.message, llm) 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) strategy = get_decision_strategy(intent)
prompt_key = strategy.get("prompt_template", "rag_template") engine = llm.decision_engine
preferred_provider = strategy.get("preferred_provider")
sources_hits = [] sources_hits = []
final_prompt = "" answer_text = ""
context_str = ""
# 2. INTERVIEW MODE (Kompatibilität zu v2.7.8)
if intent == "INTERVIEW": if intent == "INTERVIEW":
# --- INTERVIEW MODE ---
target_type = _detect_target_type(request.message, strategy.get("schemas", {})) target_type = _detect_target_type(request.message, strategy.get("schemas", {}))
types_cfg = get_types_config() types_cfg = get_types_config()
type_def = types_cfg.get("types", {}).get(target_type, {}) type_def = types_cfg.get("types", {}).get(target_type, {})
fields_list = type_def.get("schema", []) fields_list = type_def.get("schema", [])
if not fields_list: if not fields_list:
configured_schemas = strategy.get("schemas", {}) configured_schemas = strategy.get("schemas", {})
fallback_schema = configured_schemas.get(target_type, configured_schemas.get("default")) fallback = configured_schemas.get(target_type, configured_schemas.get("default", {}))
if isinstance(fallback_schema, dict): fields_list = fallback.get("fields", []) if isinstance(fallback, dict) else (fallback or [])
fields_list = fallback_schema.get("fields", [])
else:
fields_list = fallback_schema or []
logger.info(f"[{query_id}] Interview Type: {target_type}. Fields: {len(fields_list)}")
fields_str = "\n- " + "\n- ".join(fields_list) fields_str = "\n- " + "\n- ".join(fields_list)
template = llm.get_prompt(strategy.get("prompt_template", "interview_template"))
template = llm.get_prompt(prompt_key) final_prompt = template.replace("{query}", request.message) \
final_prompt = template.replace("{context_str}", "Dialogverlauf...") \
.replace("{query}", request.message) \
.replace("{target_type}", target_type) \ .replace("{target_type}", target_type) \
.replace("{schema_fields}", fields_str) \ .replace("{schema_fields}", fields_str)
.replace("{schema_hint}", "")
answer_text = await llm.generate_raw_response(
final_prompt, system=llm.get_prompt("system_prompt"),
priority="realtime", provider=strategy.get("preferred_provider"), max_retries=0
)
sources_hits = [] sources_hits = []
# 3. RAG MODE (WP-25 Multi-Stream)
else: else:
# --- RAG MODE (FACT, DECISION, EMPATHY, CODING) --- stream_keys = strategy.get("use_streams", [])
inject_types = strategy.get("inject_types", []) library = engine.config.get("streams_library", {})
prepend_instr = strategy.get("prepend_instruction", "")
edge_boosts = strategy.get("edge_boosts", {})
query_req = QueryRequest( tasks = []
query=request.message, active_streams = []
mode="hybrid", for key in stream_keys:
top_k=request.top_k, stream_cfg = library.get(key)
explain=request.explain, if stream_cfg:
boost_edges=edge_boosts active_streams.append(key)
) tasks.append(engine._run_single_stream(key, stream_cfg, request.message))
retrieve_result = await retriever.search(query_req)
hits = retrieve_result.results
if inject_types: import asyncio
strategy_req = QueryRequest( responses = await asyncio.gather(*tasks, return_exceptions=True)
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." raw_stream_map = {}
formatted_context_map = {}
# --- 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) max_chars = getattr(settings, "MAX_OLLAMA_CHARS", 10000)
if preferred_provider == "ollama" and len(context_str) > max_chars: provider = strategy.get("preferred_provider") or settings.MINDNET_LLM_PROVIDER
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}" 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)
if prepend_instr: # WP-20 Stability Fix: Throttling
context_str = f"{prepend_instr}\n\n{context_str}" if provider == "ollama" and len(context_text) > max_chars:
context_text = context_text[:max_chars] + "\n[...]"
final_prompt = template.replace("{context_str}", context_str).replace("{query}", request.message) formatted_context_map[name] = context_text
sources_hits = hits
# --- DEBUG SPOT 1: PROMPT CONSTRUCTION --- answer_text = await engine._generate_final_answer(
logger.info(f"[{query_id}] PROMPT CONSTRUCTION COMPLETE. Length: {len(final_prompt)} chars.") intent, strategy, request.message, formatted_context_map
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
) )
except Exception as e: sources_hits = _collect_all_hits(raw_stream_map)
logger.error(f"🛑 [{query_id}] Primary Provider '{preferred_provider}' failed: {e}")
# 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...")
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."
duration_ms = int((time.time() - start_time) * 1000) duration_ms = int((time.time() - start_time) * 1000)
# Logging # Logging
try: try:
log_search( log_search(
query_id=query_id, query_id=query_id, query_text=request.message, results=sources_hits,
query_text=request.message, mode=f"wp25_{intent.lower()}", metadata={"strategy": intent, "source": intent_source}
results=sources_hits,
mode="interview" if intent == "INTERVIEW" else "chat_rag",
metadata={"intent": intent, "source": intent_source, "provider": preferred_provider}
) )
except: pass except: pass
return ChatResponse( return ChatResponse(
query_id=query_id, query_id=query_id, answer=answer_text, sources=sources_hits,
answer=answer_text, latency_ms=duration_ms, intent=intent, intent_source=intent_source
sources=sources_hits,
latency_ms=duration_ms,
intent=intent,
intent_source=intent_source
) )
except Exception as e: except Exception as e:
logger.error(f"Error in chat endpoint: {e}", exc_info=True) logger.error(f"❌ Chat Endpoint Failure: {e}", exc_info=True)
# Wir geben eine benutzerfreundliche Meldung zurück, statt nur den Error-Stack raise HTTPException(status_code=500, detail="Fehler bei der Verarbeitung.")
raise HTTPException(status_code=500, detail="Das System konnte die Anfrage nicht verarbeiten.")

View File

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

View File

@ -1,145 +1,144 @@
# config/decision_engine.yaml # config/decision_engine.yaml
# Steuerung der Decision Engine (Intent Recognition & Graph Routing) # VERSION: 3.1.6 (WP-25: Multi-Stream Agentic RAG - Final Release)
# VERSION: 2.6.1 (WP-20: Hybrid LLM & WP-22: Semantic Graph Routing)
# STATUS: Active # 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: settings:
llm_fallback_enabled: true llm_fallback_enabled: true
# "auto" nutzt den in MINDNET_LLM_PROVIDER gesetzten Standard.
# Strategie für den Router selbst (Welches Modell erkennt den Intent?)
# "auto" nutzt den in MINDNET_LLM_PROVIDER gesetzten Standard (z.B. openrouter).
router_provider: "auto" router_provider: "auto"
# Verweist auf das Template in prompts.yaml
router_prompt_key: "intent_router_v1"
# Few-Shot Prompting für den LLM-Router # --- EBENE 1: STREAM-LIBRARY (Bausteine basierend auf types.yaml) ---
llm_router_prompt: | # Synchronisiert mit types.yaml v2.7.0
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: streams_library:
- INTERVIEW: User will Wissen erfassen, Notizen anlegen oder Dinge festhalten. values_stream:
- DECISION: Rat, Strategie, Abwägung von Werten, "Soll ich tun X?". name: "Identität & Ethik"
- EMPATHY: Gefühle, Reflexion der eigenen Verfassung, Frust, Freude. query_template: "Welche meiner Werte und Prinzipien betreffen: {query}"
- CODING: Code-Erstellung, Debugging, technische Dokumentation. # Nur Typen aus types.yaml
- FACT: Reine Wissensabfrage, Definitionen, Suchen von Informationen. 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
BEISPIELE: facts_stream:
User: "Wie funktioniert die Qdrant-Vektor-DB?" -> FACT name: "Operative Realität"
User: "Soll ich mein Startup jetzt verkaufen?" -> DECISION query_template: "Status, Ressourcen und Fakten zu: {query}"
User: "Notiere mir kurz meine Gedanken zum Meeting." -> INTERVIEW # Nur Typen aus types.yaml
User: "Ich fühle mich heute sehr erschöpft." -> EMPATHY filter_types: ["project", "decision", "task", "goal", "event", "state"]
User: "Schreibe eine FastAPI-Route für den Ingest." -> CODING top_k: 5
NACHRICHT: "{query}"
STRATEGIE:
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
edge_boosts: edge_boosts:
part_of: 2.0 part_of: 2.0
composed_of: 2.0 depends_on: 1.5
similar_to: 1.5 implemented_in: 1.5
caused_by: 0.5
prompt_template: "rag_template"
prepend_instruction: null
# 2. Entscheidungs-Frage (Power-Strategie via Gemini) 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
impacts: 2.0
risk_of: 2.5
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
implemented_in: 3.0
# --- 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: DECISION:
description: "Der User sucht Rat, Strategie oder Abwägung." description: "Der User sucht Rat, Strategie oder Abwägung."
preferred_provider: "gemini" preferred_provider: "gemini"
trigger_keywords: # FIX v3.1.6: Trigger erweitert, um "Soll ich... Projekt..." sicher zu fangen.
- "soll ich" trigger_keywords: ["soll ich", "sollte ich", "entscheidung", "abwägen", "priorität", "empfehlung"]
- "meinung" use_streams:
- "besser" - "values_stream"
- "empfehlung" - "facts_stream"
- "strategie" - "risk_stream"
- "entscheidung" prompt_template: "decision_synthesis_v1"
- "abwägung"
- "vergleich"
inject_types: ["value", "principle", "goal", "risk"]
# WP-22: Risiken und Konsequenzen im Graphen priorisieren
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: | prepend_instruction: |
!!! ENTSCHEIDUNGS-MODUS (HYBRID AI) !!! !!! ENTSCHEIDUNGS-MODUS (AGENTIC MULTI-STREAM) !!!
BITTE WÄGE FAKTEN GEGEN FOLGENDE WERTE, PRINZIPIEN UND ZIELE AB: 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.
# 3. Empathie / "Ich"-Modus (Lokal & Privat via Ollama) # Emotionale Reflexion
EMPATHY: EMPATHY:
description: "Reaktion auf emotionale Zustände." description: "Reaktion auf emotionale Zustände."
preferred_provider: "openrouter" preferred_provider: "openrouter"
trigger_keywords: trigger_keywords: ["fühle", "traurig", "glücklich", "stress", "angst"]
- "ich fühle" use_streams:
- "traurig" - "biography_stream"
- "glücklich" - "values_stream"
- "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" prompt_template: "empathy_template"
prepend_instruction: null
# 4. Coding / Technical (Gemini Power) # Technischer Support
CODING: CODING:
description: "Technische Anfragen und Programmierung." description: "Technische Anfragen und Programmierung."
preferred_provider: "gemini" preferred_provider: "gemini"
trigger_keywords: trigger_keywords: ["code", "python", "script", "bug", "syntax"]
- "code" use_streams:
- "python" - "tech_stream"
- "script" - "facts_stream"
- "funktion"
- "bug"
- "syntax"
- "json"
- "yaml"
- "bash"
inject_types: ["snippet", "reference", "source"]
# WP-22: Technische Abhängigkeiten priorisieren
edge_boosts:
uses: 2.5
depends_on: 2.0
implemented_in: 3.0
prompt_template: "technical_template" prompt_template: "technical_template"
prepend_instruction: null
# 5. Interview / Datenerfassung (Lokal) # Eingabe-Modus (WP-07)
INTERVIEW: INTERVIEW:
description: "Der User möchte Wissen erfassen." description: "Der User möchte Wissen erfassen (Eingabemodus)."
preferred_provider: "openrouter" preferred_provider: "openrouter"
trigger_keywords: use_streams: []
- "neue notiz"
- "etwas notieren"
- "festhalten"
- "erstellen"
- "dokumentieren"
- "anlegen"
- "interview"
- "erfassen"
- "idee speichern"
- "draft"
inject_types: []
edge_boosts: {}
prompt_template: "interview_template" prompt_template: "interview_template"
prepend_instruction: null

View File

@ -1,8 +1,9 @@
# config/prompts.yaml — Final V2.6.0 (WP-15b Candidate-Validation) # config/prompts.yaml — VERSION 3.1.2 (WP-25 Cleanup: Multi-Stream Sync)
# WP-20: Optimierte Cloud-Templates zur Unterdrückung von Modell-Geschwätz. # STATUS: Active
# FIX: Explizite Verbote für Einleitungstexte zur Vermeidung von JSON-Parsing-Fehlern. # FIX:
# WP-15b: Integration der binären edge_validation für den Two-Pass Workflow. # - 100% Wiederherstellung der Ingest- & Validierungslogik (Sektion 5-8).
# OLLAMA: UNVERÄNDERT laut Benutzeranweisung. # - Ü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: | system_prompt: |
Du bist 'mindnet', mein digitaler Zwilling und strategischer Partner. Du bist 'mindnet', mein digitaler Zwilling und strategischer Partner.
@ -17,13 +18,21 @@ system_prompt: |
3. Antworte auf Deutsch (außer bei Code/Fachbegriffen). 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: | ollama: |
QUELLEN (WISSEN): WISSENS-STREAMS:
========================================= =========================================
{context_str} FAKTEN & STATUS:
{facts_stream}
ERFAHRUNG & BIOGRAFIE:
{biography_stream}
WISSEN & TECHNIK:
{tech_stream}
========================================= =========================================
FRAGE: FRAGE:
@ -31,25 +40,35 @@ rag_template:
ANWEISUNG: 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. Fasse die Informationen zusammen. Sei objektiv und neutral.
gemini: | gemini: |
Kontext meines digitalen Zwillings: {context_str} Beantworte die Wissensabfrage "{query}" basierend auf diesen Streams:
Beantworte strukturiert und präzise: {query} 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: | openrouter: |
Kontext-Analyse für den digitalen Zwilling: Synthese der Wissens-Streams für: {query}
{context_str} Inhalt: {facts_stream} | {biography_stream} | {tech_stream}
Antworte basierend auf dem bereitgestellten Kontext.
Anfrage: {query}
Antworte basierend auf dem Kontext.
# --------------------------------------------------------- # ---------------------------------------------------------
# 2. DECISION: Strategie & Abwägung (Intent: DECISION) # 2. DECISION: Strategie & Abwägung (Intent: DECISION)
# --------------------------------------------------------- # ---------------------------------------------------------
decision_template: # Ersetzt das alte 'decision_template'. Nutzt jetzt parallele Streams.
decision_synthesis_v1:
ollama: | 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: ENTSCHEIDUNGSFRAGE:
@ -58,7 +77,7 @@ decision_template:
ANWEISUNG: ANWEISUNG:
Du agierst als mein Entscheidungs-Partner. Du agierst als mein Entscheidungs-Partner.
1. Analysiere die Faktenlage aus den Quellen. 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? 3. Wäge ab: Passt die technische/faktische Lösung zu meinen Werten?
FORMAT: FORMAT:
@ -66,19 +85,26 @@ decision_template:
- **Abgleich:** (Gibt es Konflikte mit Werten/Zielen? Nenne die Quelle!) - **Abgleich:** (Gibt es Konflikte mit Werten/Zielen? Nenne die Quelle!)
- **Empfehlung:** (Klare Meinung: Ja/No/Vielleicht mit Begründung) - **Empfehlung:** (Klare Meinung: Ja/No/Vielleicht mit Begründung)
gemini: | gemini: |
Agiere als 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: | openrouter: |
Strategische Entscheidungsanalyse: {query} Strategische Multi-Stream Analyse für: {query}
Wertebasis aus dem Graphen: {context_str} 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) # 3. EMPATHY: Der Spiegel / "Ich"-Modus (Intent: EMPATHY)
# --------------------------------------------------------- # ---------------------------------------------------------
empathy_template: empathy_template:
ollama: | ollama: |
KONTEXT (ERFAHRUNGEN & GLAUBENSSÄTZE): KONTEXT (ERFAHRUNGEN & WERTE):
========================================= =========================================
{context_str} ERLEBNISSE & BIOGRAFIE:
{biography_stream}
WERTE & BEDÜRFNISSE:
{values_stream}
========================================= =========================================
SITUATION: SITUATION:
@ -87,22 +113,26 @@ empathy_template:
ANWEISUNG: ANWEISUNG:
Du agierst jetzt als mein empathischer Spiegel. Du agierst jetzt als mein empathischer Spiegel.
1. Versuche nicht sofort, das Problem technisch zu lösen. 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. 3. Antworte in der "Ich"-Form oder "Wir"-Form. Sei unterstützend.
TONFALL: TONFALL:
Ruhig, verständnisvoll, reflektiert. Keine Aufzählungszeichen, sondern fließender Text. Ruhig, verständnisvoll, reflektiert. Keine Aufzählungszeichen, sondern fließender Text.
gemini: "Sei mein digitaler Spiegel für {query}. Kontext: {context_str}" gemini: "Sei mein digitaler Spiegel für {query}. Kontext: {biography_stream}, {values_stream}"
openrouter: "Empathische Reflexion der Situation {query}. Persönlicher Kontext: {context_str}" openrouter: "Empathische Reflexion der Situation {query}. Persönlicher Kontext: {biography_stream}, {values_stream}"
# --------------------------------------------------------- # ---------------------------------------------------------
# 4. TECHNICAL: Der Coder (Intent: CODING) # 4. TECHNICAL: Der Coder (Intent: CODING)
# --------------------------------------------------------- # ---------------------------------------------------------
technical_template: technical_template:
ollama: | ollama: |
KONTEXT (DOCS & SNIPPETS): KONTEXT (WISSEN & PROJEKTE):
========================================= =========================================
{context_str} TECHNIK & SNIPPETS:
{tech_stream}
PROJEKT-STATUS:
{facts_stream}
========================================= =========================================
TASK: TASK:
@ -118,11 +148,11 @@ technical_template:
- Kurze Erklärung des Ansatzes. - Kurze Erklärung des Ansatzes.
- Markdown Code-Block (Copy-Paste fertig). - Markdown Code-Block (Copy-Paste fertig).
- Wichtige Edge-Cases. - Wichtige Edge-Cases.
gemini: "Generiere Code für {query} unter Berücksichtigung von {context_str}." gemini: "Generiere Code für {query} unter Berücksichtigung von {tech_stream} und {facts_stream}."
openrouter: "Technischer Support für {query}. Code-Referenzen: {context_str}" 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: interview_template:
ollama: | ollama: |
@ -160,7 +190,7 @@ interview_template:
openrouter: "Strukturiere den Input {query} nach dem Schema {schema_fields} für Typ {target_type}." openrouter: "Strukturiere den Input {query} nach dem Schema {schema_fields} für Typ {target_type}."
# --------------------------------------------------------- # ---------------------------------------------------------
# 6. EDGE_ALLOCATION: Kantenfilter (Intent: OFFLINE_FILTER) # 6. EDGE_ALLOCATION: Kantenfilter (Ingest)
# --------------------------------------------------------- # ---------------------------------------------------------
edge_allocation_template: edge_allocation_template:
ollama: | ollama: |
@ -200,7 +230,7 @@ edge_allocation_template:
OUTPUT: OUTPUT:
# --------------------------------------------------------- # ---------------------------------------------------------
# 7. SMART EDGE ALLOCATION: Extraktion (Intent: INGEST) # 7. SMART EDGE ALLOCATION: Extraktion (Ingest)
# --------------------------------------------------------- # ---------------------------------------------------------
edge_extraction: edge_extraction:
ollama: | ollama: |
@ -240,7 +270,7 @@ edge_extraction:
OUTPUT: OUTPUT:
# --------------------------------------------------------- # ---------------------------------------------------------
# 8. WP-15b: EDGE VALIDATION (Intent: VALIDATE) # 8. WP-15b: EDGE VALIDATION (Ingest/Validate)
# --------------------------------------------------------- # ---------------------------------------------------------
edge_validation: edge_validation:
gemini: | gemini: |
@ -271,3 +301,37 @@ edge_validation:
ZIEL: {target_title} ({target_summary}) ZIEL: {target_title} ({target_summary})
BEZIEHUNG: {edge_kind} BEZIEHUNG: {edge_kind}
Ist diese Verbindung valide? Antworte NUR mit YES oder NO. Ist diese Verbindung valide? Antworte NUR mit YES oder NO.
# ---------------------------------------------------------
# 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 ## 6. Dokumentations-Status
**Aktuelle Version:** 2.9.1 **Aktuelle Version:** 2.9.3
**Letzte Aktualisierung:** 2025-01-XX **Letzte Aktualisierung:** 2025-12-31
**Status:** ✅ Vollständig und aktiv gepflegt **Status:** ✅ Vollständig und aktiv gepflegt
**Hinweis:** Diese Dokumentation wird kontinuierlich aktualisiert. Bei Fragen oder Verbesserungsvorschlägen bitte im Repository melden. **Hinweis:** Diese Dokumentation wird kontinuierlich aktualisiert. Bei Fragen oder Verbesserungsvorschlägen bitte im Repository melden.

View File

@ -2,8 +2,8 @@
doc_type: glossary doc_type: glossary
audience: all audience: all
status: active status: active
version: 2.9.1 version: 2.9.3
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." 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 # 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. * **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. * **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. * **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. * **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. * **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. * **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 audience: user, mindmaster
scope: chat, ui, feedback, graph scope: chat, ui, feedback, graph
status: active status: active
version: 2.6 version: 2.9.3
context: "Anleitung zur Nutzung der Web-Oberfläche, der Chat-Personas und des Graph Explorers." context: "Anleitung zur Nutzung der Web-Oberfläche, der Chat-Personas, Multi-Stream RAG und des Graph Explorers."
--- ---
# Chat & Graph Usage Guide # 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) ### 3.1 Intent-Erkennung (Hybrid-Router)
Ausgelöst durch `?` oder W-Wörter.
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**. * **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'." * *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**. * **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..." * *Beispiel:* "Ich bin frustriert." -> "Das erinnert mich an Projekt Y, da ging es uns ähnlich..."
* **Fakten ("Was ist?"):** Der **Bibliothekar**. * **Fakten ("Was ist?", "Wann..."):** Der **Bibliothekar**.
* Liefert präzise Definitionen. * Nutzt: Facts Stream, Tech Stream, Biography Stream
* Liefert präzise Definitionen und zeitliche Informationen
### 3.2 Befehls-Modus (Interview) ### 3.2 Befehls-Modus (Interview)
Ausgelöst durch Aussagen wie "Neues Projekt", "Ich will festhalten". 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. Mindnet liefert eine **Begründung** ("Reasons"), warum es etwas gefunden hat.
Öffne eine Quellen-Karte, um zu sehen: Öffne eine Quellen-Karte, um zu sehen:
* *"Hohe textuelle Übereinstimmung."* (Semantik) * *"Hohe textuelle Übereinstimmung."* (Semantik)
* *"Bevorzugt aufgrund des Typs 'decision'."* (Wichtigkeit) * *"Bevorzugt aufgrund des Typs 'decision'."* (Wichtigkeit)
* *"Verweist auf 'Projekt X' via 'depends_on'."* (Graph-Kontext) * *"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 doc_type: concept
audience: architect, product_owner audience: architect, product_owner
scope: ai, router, personas, resilience scope: ai, router, personas, resilience, agentic_rag
status: active status: active
version: 2.8.1 version: 2.9.3
context: "Fachkonzept der hybriden KI-Persönlichkeit, der Provider-Kaskade und der kognitiven Resilienz (Deep Fallback)." context: "Fachkonzept der hybriden KI-Persönlichkeit, Agentic Multi-Stream RAG, Provider-Kaskade und kognitiven Resilienz (Deep Fallback)."
--- ---
# Konzept: KI-Persönlichkeit & Router # 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. 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-basiertes Routing (WP-25)
* **Intent:** Der Nutzer hat eine Frage oder ein Problem (`FACT`, `DECISION`, `EMPATHY`).
* **Aktion:** Das System sucht im Gedächtnis und generiert eine Antwort. 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) ### Modus B: Interview (Knowledge Capture)
* **Intent:** Der Nutzer will Wissen speichern (`INTERVIEW`). * **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. Mindnet wechselt den Hut, je nach Situation.
### 3.1 Der Berater (Strategy: DECISION) ### 3.1 Der Berater (Strategy: DECISION)
* **Auslöser:** Fragen wie „Soll ich...?“, „Was ist besser?“. * **Auslöser:** Fragen wie „Soll ich...?", „Was ist besser?", „Empfehlung...".
* **Strategic Retrieval:** Lädt aktiv Notizen der Typen `value` (Werte), `goal` (Ziele) und `risk` (Risiken), auch wenn sie im Text nicht direkt vorkommen. * **Multi-Stream Retrieval (WP-25):** Führt parallele Abfragen in Values Stream, Facts Stream und Risk Stream aus.
* **Reasoning:** *„Wäge die Fakten gegen meine Werte ab. Sei strikt bei Risiken.“* * **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) ### 3.2 Der Spiegel (Strategy: EMPATHY)
* **Auslöser:** Emotionale Aussagen („Ich bin frustriert“). * **Auslöser:** Emotionale Aussagen („Ich bin frustriert", „Ich fühle...", „Stress...").
* **Strategic Retrieval:** Lädt `experience` (Erfahrungen) und `belief` (Glaubenssätze). * **Multi-Stream Retrieval (WP-25):** Führt parallele Abfragen in Biography Stream und Values Stream aus.
* **Reasoning:** *„Nutze meine eigenen Erfahrungen, um die Situation einzuordnen.“* * **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) ### 3.3 Der Bibliothekar (Strategy: FACT_WHAT / FACT_WHEN)
* **Auslöser:** Sachfragen („Was ist Qdrant?“). * **Auslöser:** Sachfragen („Was ist...?", „Welche sind...?", „Wann...?", „Datum...").
* **Behavior:** Präzise, neutral, kurz. * **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 doc_type: technical_reference
audience: developer, integrator audience: developer, integrator
scope: api, endpoints, integration scope: api, endpoints, integration, agentic_rag
status: active status: active
version: 2.9.1 version: 2.9.3
context: "Vollständige API-Referenz für alle Mindnet-Endpunkte. Basis für Integration und Entwicklung." 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 # API Reference
@ -96,21 +96,37 @@ Hauptendpunkt für RAG-Chat und Interview-Modus. Unterstützt Streaming.
**Response (Non-Streaming):** **Response (Non-Streaming):**
```json ```json
{ {
"intent": "FACT", "query_id": "uuid",
"response": "Mindnet ist ein persönliches KI-Gedächtnis...", "answer": "Mindnet ist ein persönliches KI-Gedächtnis...",
"sources": [...], "sources": [
"query_id": "uuid" {
"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):** **Response (Streaming):**
Server-Sent Events (SSE) mit Chunks der Antwort. Server-Sent Events (SSE) mit Chunks der Antwort.
**Intent-Typen:** **Intent-Typen (WP-25):**
- `FACT`: Wissensabfrage - `FACT_WHAT`: Wissensabfrage (Wissen/Listen)
- `DECISION`: Entscheidungsfrage - `FACT_WHEN`: Zeitpunkte (Termine, Daten)
- `EMPATHY`: Emotionale Anfrage - `DECISION`: Entscheidungsfrage (Beratung, Strategie)
- `INTERVIEW`: Wissen erfassen - `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 doc_type: technical_reference
audience: developer, architect audience: developer, architect
scope: backend, chat, llm_service, traffic_control, resilience scope: backend, chat, llm_service, traffic_control, resilience, agentic_rag
status: active status: active
version: 2.9.1 version: 2.9.3
context: "Technische Implementierung des FastAPI-Routers, des hybriden LLMService (v3.3.6) und der WP-20 Resilienz-Logik." 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):** 1. **Keyword Fast-Path (Sofortige Erkennung):**
* Prüfung auf Vorhandensein von `?` oder W-Wörtern (Wer, Wie, Was, Soll ich). * Prüft `trigger_keywords` aus `decision_engine.yaml` (z.B. "Soll ich", "Wann", "Was ist").
* Wenn positiv: **RAG Modus** (Interview wird blockiert). * Wenn Match: Sofortige Intent-Zuordnung ohne LLM-Call.
2. **Keyword Scan (Fast Path):** * **Strategien:** FACT_WHAT, FACT_WHEN, DECISION, EMPATHY, CODING, INTERVIEW.
* Lädt `types.yaml` (Objekte) und `decision_engine.yaml` (Handlungen). 2. **Type Keywords (Interview-Modus):**
* Wenn Match (z.B. "Projekt" + "neu"): **INTERVIEW Modus**. * Lädt `types.yaml` und prüft `detection_keywords` für Objekt-Erkennung.
3. **LLM Fallback (Slow Path):** * Wenn Match und keine Frage: **INTERVIEW Modus** (Datenerfassung).
* Wenn unklar: Anfrage an LLM zur Klassifizierung mittels `router_prompt`. 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) ### 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. * **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. * **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). **Stream-Library (definiert in `decision_engine.yaml`):**
2. **Context Enrichment:** * **Values Stream:** Extrahiert Identität, Ethik und Prinzipien (`value`, `principle`, `belief`, etc.).
* Abruf via `retriever.py` (Hybrid Search). * **Facts Stream:** Liefert operative Daten zu Projekten, Tasks und Status (`project`, `decision`, `task`, etc.).
* Integration von **Edge Boosts** aus der `decision_engine.yaml` zur Beeinflussung der Graph-Gewichtung. * **Biography Stream:** Greift auf persönliche Erfahrungen und Journal-Einträge zu (`experience`, `journal`, `profile`).
* Injection von Metadaten (`[TYPE]`, `[SCORE]`) in den Prompt. * **Risk Stream:** Identifiziert Hindernisse und potenzielle Gefahren (`risk`, `obstacle`, `bias`).
3. **Prompt Construction:** Assembly aus System-Prompt (Persona) + Context + Query. * **Tech Stream:** Bündelt technisches Wissen, Code und Dokumentation (`concept`, `source`, `glossary`, etc.).
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-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: 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`. 1. **Query ID:** Generiert bei jedem `/chat` Call eine `UUIDv4`.
2. **Logging:** Speichert einen Snapshot in `data/logs/query_snapshot.jsonl` (Input + Retrieved Context). 2. **Stream-Tracing:** Jeder Treffer enthält `stream_origin` für Zuordnung zum Quell-Stream.
3. **Feedback:** Der `/feedback` Endpoint verknüpft das User-Rating (1-5) mit der `query_id`. 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 doc_type: technical_reference
audience: developer, admin audience: developer, admin
scope: configuration, env, registry, scoring, resilience, modularization scope: configuration, env, registry, scoring, resilience, modularization, agentic_rag
status: active status: active
version: 2.9.1 version: 2.9.3
context: "Umfassende Referenztabellen für Umgebungsvariablen (inkl. Hybrid-Cloud & WP-76), YAML-Konfigurationen und die Edge Registry Struktur unter Berücksichtigung von WP-14." 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 # 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 ### 5.1 Intent Recognition: Hybrid-Routing (WP-25)
Das System nutzt ein zweistufiges Verfahren, um die Absicht des Nutzers zu identifizieren: 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. 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. **Slow Path (LLM Router):** Wenn kein Keyword matcht und `llm_fallback_enabled: true` gesetzt ist, analysiert ein LLM die Nachricht mittels Few-Shot Prompting. 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 #### 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. 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) ### 5.2 Multi-Stream Konfiguration (WP-25)
Jede Strategie definiert drei Hebel, um das Ergebnis des Retrievers zu beeinflussen:
* **`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". * **`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"). * **`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)* | | **FACT_WHAT** | Wissensabfrage & Listen | `facts_stream`, `tech_stream`, `biography_stream` | `part_of` (2.0), `depends_on` (1.5), `implemented_in` (1.5) |
| **DECISION** | Rat, Strategie & Abwägung | `blocks` (2.5), `solves` (2.0), `risk_of` (2.5) | `value`, `principle`, `goal`, `risk` | | **FACT_WHEN** | Zeitpunkte & Termine | `facts_stream`, `biography_stream`, `tech_stream` | `part_of` (2.0), `depends_on` (1.5) |
| **EMPATHY** | Emotionale Resonanz | `based_on` (2.0), `experienced_in` (2.5), `related_to` (2.0) | `experience`, `belief`, `profile` | | **DECISION** | Rat, Strategie & Abwägung | `values_stream`, `facts_stream`, `risk_stream` | `blocks` (2.5), `impacts` (2.0), `risk_of` (2.5) |
| **CODING** | Programmierung & Syntax | `implemented_in` (3.0), `uses` (2.5), `depends_on` (2.0) | `snippet`, `reference`, `source` | | **EMPATHY** | Emotionale Resonanz | `biography_stream`, `values_stream` | `related_to` (1.5), `experienced_in` (2.0) |
| **INTERVIEW** | Erfassung neuer Daten | *(Keine)* | *(Keine)* | | **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. 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. * **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. * **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. > **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 Auszug aus der decision_engine.yaml
```yaml ```yaml

View File

@ -1,10 +1,10 @@
--- ---
doc_type: developer_guide doc_type: developer_guide
audience: developer audience: developer
scope: workflow, testing, architecture, modules, modularization scope: workflow, testing, architecture, modules, modularization, agentic_rag
status: active status: active
version: 2.9.1 version: 2.9.3
context: "Umfassender Guide für Entwickler: Modularisierte Architektur (WP-14), Two-Pass Ingestion (WP-15b), Modul-Interna, Setup und Git-Workflow." context: "Umfassender Guide für Entwickler: Modularisierte Architektur (WP-14), Two-Pass Ingestion (WP-15b), WP-25 Agentic Multi-Stream RAG, Modul-Interna, Setup und Git-Workflow."
--- ---
# Mindnet Developer Guide & 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. | | **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. | | **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/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/query.py` | 🟢 **API** | **Search API.** Klassischer Hybrid-Retriever Endpunkt. |
| | `app/routers/graph.py` | 🟢 **API** | **Viz API.** Liefert Knoten/Kanten für Frontend-Graphen (Cytoscape). | | | `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 edge_defaults: ["blocks"] # Automatische Kante
detection_keywords: ["gefahr", "risiko"] detection_keywords: ["gefahr", "risiko"]
``` ```
2. **Strategie (`config/decision_engine.yaml`):** 2. **Strategie (`config/decision_engine.yaml` v3.1.6, WP-25):**
```yaml ```yaml
DECISION: 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 ### Workflow B: Graph-Farben ändern
1. Öffne `app/frontend/ui_config.py`. 1. Öffne `app/frontend/ui_config.py`.

View File

@ -3,8 +3,8 @@ doc_type: developer_guide
audience: developer, tester audience: developer, tester
scope: testing, quality_assurance, test_strategies scope: testing, quality_assurance, test_strategies
status: active status: active
version: 2.9.1 version: 2.9.3
context: "Umfassender Test-Guide für Mindnet: Test-Strategien, Test-Frameworks, Test-Daten und Best Practices." context: "Umfassender Test-Guide für Mindnet: Test-Strategien, Test-Frameworks, Test-Daten und Best Practices inklusive WP-25 Multi-Stream RAG."
--- ---
# Testing Guide # Testing Guide
@ -245,19 +245,29 @@ class TestIngest(unittest.IsolatedAsyncioTestCase):
- `tests/test_edges_defaults_smoke.py` - `tests/test_edges_defaults_smoke.py`
- `scripts/edges_full_check.py` - `scripts/edges_full_check.py`
### 4.4 Chat & Intent-Tests ### 4.4 Chat & Intent-Tests (WP-25)
**Was wird getestet:** **Was wird getestet:**
- Intent-Erkennung (FACT, DECISION, EMPATHY, INTERVIEW) - Intent-Erkennung (FACT_WHAT, FACT_WHEN, DECISION, EMPATHY, CODING, INTERVIEW)
- Decision Engine - 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 - Interview-Modus
- Feedback-Loop - Feedback-Loop
**Tests:** **Tests:**
- `tests/test_wp06_decision.py` - `tests/test_wp06_decision.py` - Decision Engine (Legacy)
- `tests/test_interview_intent.py` - `tests/test_interview_intent.py` - Interview-Modus
- `tests/test_chat_wp05.py` - `tests/test_chat_wp05.py` - Chat-Backend (Legacy)
- `tests/test_feedback_smoke.py` - `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 ### 4.5 Ingestion-Tests

View File

@ -2,14 +2,14 @@
doc_type: roadmap doc_type: roadmap
audience: product_owner, developer audience: product_owner, developer
status: active status: active
version: 2.9.1 version: 2.9.3
context: "Aktuelle Planung für kommende Features (ab WP16), Release-Strategie und Historie der abgeschlossenen WPs nach WP-14/15b." 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 # Mindnet Active Roadmap
**Aktueller Stand:** v2.9.1 (Post-WP14 / WP-15b) **Aktueller Stand:** v2.9.3 (Post-WP25: Agentic Multi-Stream RAG)
**Fokus:** Modularisierung, Two-Pass Ingestion & Graph Intelligence. **Fokus:** Agentic Orchestration, Multi-Stream Retrieval & Wissens-Synthese.
| Phase | Fokus | Status | | 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-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-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-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 ### 2.1 WP-22 Lessons Learned
* **Architektur:** Die Trennung von `retriever.py` und `retriever_scoring.py` war notwendig, um LLM-Context-Limits zu wahren und die Testbarkeit der mathematischen Formeln zu erhöhen. * **Architektur:** Die Trennung von `retriever.py` und `retriever_scoring.py` war notwendig, um LLM-Context-Limits zu wahren und die Testbarkeit der mathematischen Formeln zu erhöhen.
@ -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. 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`. 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 **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.
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.
#### 2. Funktionsbeschreibung: Die Streams **Kern-Features:**
Die Daten aus der `types.yaml` werden in drei logische Verarbeitungseinheiten unterteilt: 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) **Technische Details:**
* **Inhalt:** `value`, `belief`, `trait`, `principle`, `need`, `boundary`, `bias`. - Decision Engine v1.0.3: Multi-Stream Orchestrator
* **Zweck:** Definition des moralischen Kompasses, der psychologischen Grundbedürfnisse und kognitiven Muster. - Chat Router v3.0.2: Hybrid Router Integration
* **Wirkung:** Liefert das "Warum" hinter jeder Handlung. - 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) **Ausblick (WP-25a):**
* **Inhalt:** `experience`, `event`, `source`, `journal`, `person`. - Pre-Synthesis: LLM-basierte Komprimierung überlanger Streams
* **Zweck:** Bereitstellung empirischer Belege aus der Vergangenheit und sozialer Kontexte. - Kontext-Budgeting: Intelligente Token-Verteilung
* **Wirkung:** Verankert die Antwort in real erlebten Mustern und Fakten. - Stream-specific Provider: Unterschiedliche KI-Modelle pro Wissensbereich
##### 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.
--- ---
### WP-24 Proactive Discovery & Agentic Knowledge Mining ### 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 **Release Date:** 2025-12-31
**Type:** Feature Release - Multigraph & Diversity Engine **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 doc_type: documentation_index
audience: all audience: all
status: active status: active
version: 2.9.1 version: 2.9.3
context: "Zentraler Einstiegspunkt für die Mindnet-Dokumentation" context: "Zentraler Einstiegspunkt für die Mindnet-Dokumentation"
--- ---
# Mindnet Dokumentation # Mindnet Dokumentation
Willkommen in der Dokumentation von Mindnet v2.9.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 ## 🚀 Schnellstart