WP25 #19
37
app/core/logging_setup.py
Normal file
37
app/core/logging_setup.py
Normal 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}")
|
||||
204
app/core/retrieval/decision_engine.py
Normal file
204
app/core/retrieval/decision_engine.py
Normal 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."
|
||||
104
app/main.py
104
app/main.py
|
|
@ -1,25 +1,28 @@
|
|||
"""
|
||||
FILE: app/main.py
|
||||
DESCRIPTION: Bootstrap der FastAPI Anwendung. Inkludiert Router und Middleware.
|
||||
VERSION: 0.6.0
|
||||
DESCRIPTION: Bootstrap der FastAPI Anwendung für WP-25 (Agentic RAG).
|
||||
Orchestriert Lifespan-Events, globale Fehlerbehandlung und Routing.
|
||||
VERSION: 1.0.0 (WP-25 Release)
|
||||
STATUS: Active
|
||||
DEPENDENCIES: app.config, app.routers.* (embed, qdrant, query, graph, tools, feedback, chat, ingest, admin)
|
||||
LAST_ANALYSIS: 2025-12-15
|
||||
DEPENDENCIES: app.config, app.routers.*, app.services.llm_service
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
from fastapi import FastAPI
|
||||
from .config import get_settings
|
||||
#from .routers.embed_router import router as embed_router
|
||||
#from .routers.qdrant_router import router as qdrant_router
|
||||
import logging
|
||||
import os
|
||||
from contextlib import asynccontextmanager
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
from .config import get_settings
|
||||
from .services.llm_service import LLMService
|
||||
|
||||
# Import der Router
|
||||
from .routers.query import router as query_router
|
||||
from .routers.graph import router as graph_router
|
||||
from .routers.tools import router as tools_router
|
||||
from .routers.feedback import router as feedback_router
|
||||
# NEU: Chat Router (WP-05)
|
||||
from .routers.chat import router as chat_router
|
||||
# NEU: Ingest Router (WP-11)
|
||||
from .routers.ingest import router as ingest_router
|
||||
|
||||
try:
|
||||
|
|
@ -27,26 +30,86 @@ try:
|
|||
except Exception:
|
||||
admin_router = None
|
||||
|
||||
from .core.logging_setup import setup_logging
|
||||
|
||||
# Initialisierung noch VOR create_app()
|
||||
setup_logging()
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# --- WP-25: Lifespan Management ---
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
"""
|
||||
Verwaltet den Lebenszyklus der Anwendung.
|
||||
Führt Startup-Prüfungen durch und bereinigt Ressourcen beim Shutdown.
|
||||
"""
|
||||
settings = get_settings()
|
||||
logger.info("🚀 mindnet API: Starting up (WP-25 Agentic RAG Mode)...")
|
||||
|
||||
# 1. Startup: Integritäts-Check der WP-25 Konfiguration
|
||||
# Wir prüfen, ob die für die DecisionEngine kritischen Dateien vorhanden sind.
|
||||
decision_cfg = os.getenv("MINDNET_DECISION_CONFIG", "config/decision_engine.yaml")
|
||||
prompts_cfg = settings.PROMPTS_PATH
|
||||
|
||||
if not os.path.exists(decision_cfg):
|
||||
logger.error(f"❌ CRITICAL: Decision Engine config missing at {decision_cfg}")
|
||||
if not os.path.exists(prompts_cfg):
|
||||
logger.error(f"❌ CRITICAL: Prompts config missing at {prompts_cfg}")
|
||||
|
||||
yield
|
||||
|
||||
# 2. Shutdown: Ressourcen bereinigen
|
||||
logger.info("🛑 mindnet API: Shutting down...")
|
||||
llm = LLMService()
|
||||
await llm.close()
|
||||
logger.info("✨ Cleanup complete. Goodbye.")
|
||||
|
||||
# --- App Factory ---
|
||||
|
||||
def create_app() -> FastAPI:
|
||||
app = FastAPI(title="mindnet API", version="0.6.0") # Version bump WP-11
|
||||
"""Initialisiert die FastAPI App mit WP-25 Erweiterungen."""
|
||||
app = FastAPI(
|
||||
title="mindnet API",
|
||||
version="1.0.0", # WP-25 Milestone
|
||||
lifespan=lifespan,
|
||||
description="Digital Twin Knowledge Engine mit Agentic Multi-Stream RAG."
|
||||
)
|
||||
|
||||
s = get_settings()
|
||||
|
||||
# --- Globale Fehlerbehandlung (WP-25 Resilienz) ---
|
||||
|
||||
@app.exception_handler(Exception)
|
||||
async def global_exception_handler(request: Request, exc: Exception):
|
||||
"""Fängt unerwartete Fehler in der Multi-Stream Kette ab."""
|
||||
logger.error(f"❌ Unhandled Engine Error: {exc}", exc_info=True)
|
||||
return JSONResponse(
|
||||
status_code=500,
|
||||
content={
|
||||
"detail": "Ein interner Fehler ist aufgetreten. Die DecisionEngine konnte die Anfrage nicht finalisieren.",
|
||||
"error_type": type(exc).__name__
|
||||
}
|
||||
)
|
||||
|
||||
# Healthcheck
|
||||
@app.get("/healthz")
|
||||
def healthz():
|
||||
return {"status": "ok", "qdrant": s.QDRANT_URL, "prefix": s.COLLECTION_PREFIX}
|
||||
|
||||
# app.include_router(embed_router)
|
||||
# app.include_router(qdrant_router)
|
||||
return {
|
||||
"status": "ok",
|
||||
"version": "1.0.0",
|
||||
"qdrant": s.QDRANT_URL,
|
||||
"prefix": s.COLLECTION_PREFIX,
|
||||
"agentic_mode": True
|
||||
}
|
||||
|
||||
# Inkludieren der Router (100% Kompatibilität erhalten)
|
||||
app.include_router(query_router, prefix="/query", tags=["query"])
|
||||
app.include_router(graph_router, prefix="/graph", tags=["graph"])
|
||||
app.include_router(tools_router, prefix="/tools", tags=["tools"])
|
||||
app.include_router(feedback_router, prefix="/feedback", tags=["feedback"])
|
||||
|
||||
# NEU: Chat Endpoint
|
||||
app.include_router(chat_router, prefix="/chat", tags=["chat"])
|
||||
|
||||
# NEU: Ingest Endpoint
|
||||
app.include_router(chat_router, prefix="/chat", tags=["chat"]) # Nutzt nun WP-25 DecisionEngine
|
||||
app.include_router(ingest_router, prefix="/ingest", tags=["ingest"])
|
||||
|
||||
if admin_router:
|
||||
|
|
@ -54,4 +117,5 @@ def create_app() -> FastAPI:
|
|||
|
||||
return app
|
||||
|
||||
# Instanziierung der App
|
||||
app = create_app()
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
"""
|
||||
FILE: app/models/dto.py
|
||||
DESCRIPTION: Pydantic-Modelle (DTOs) für Request/Response Bodies. Definiert das API-Schema.
|
||||
VERSION: 0.6.7 (WP-Fix: Target Section Support)
|
||||
VERSION: 0.7.1 (WP-25: Stream-Tracing Support)
|
||||
STATUS: Active
|
||||
DEPENDENCIES: pydantic, typing, uuid
|
||||
LAST_ANALYSIS: 2025-12-29
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -12,8 +11,14 @@ from pydantic import BaseModel, Field
|
|||
from typing import List, Literal, Optional, Dict, Any
|
||||
import uuid
|
||||
|
||||
# Gültige Kanten-Typen gemäß Manual
|
||||
EdgeKind = Literal["references", "references_at", "backlink", "next", "prev", "belongs_to", "depends_on", "related_to", "similar_to", "caused_by", "derived_from", "based_on", "solves", "blocks", "uses", "guides"]
|
||||
# WP-25: Erweiterte Kanten-Typen gemäß neuer decision_engine.yaml
|
||||
EdgeKind = Literal[
|
||||
"references", "references_at", "backlink", "next", "prev",
|
||||
"belongs_to", "depends_on", "related_to", "similar_to",
|
||||
"caused_by", "derived_from", "based_on", "solves", "blocks",
|
||||
"uses", "guides", "enforced_by", "implemented_in", "part_of",
|
||||
"experienced_in", "impacts", "risk_of"
|
||||
]
|
||||
|
||||
|
||||
# --- Basis-DTOs ---
|
||||
|
|
@ -43,14 +48,14 @@ class EdgeDTO(BaseModel):
|
|||
direction: Literal["out", "in", "undirected"] = "out"
|
||||
provenance: Optional[Literal["explicit", "rule", "smart", "structure"]] = "explicit"
|
||||
confidence: float = 1.0
|
||||
target_section: Optional[str] = None # Neu: Speichert den Anker (z.B. #Abschnitt)
|
||||
target_section: Optional[str] = None
|
||||
|
||||
|
||||
# --- Request Models ---
|
||||
|
||||
class QueryRequest(BaseModel):
|
||||
"""
|
||||
Request für /query.
|
||||
Request für /query. Unterstützt Multi-Stream Isolation via filters.
|
||||
"""
|
||||
mode: Literal["semantic", "edge", "hybrid"] = "hybrid"
|
||||
query: Optional[str] = None
|
||||
|
|
@ -61,14 +66,12 @@ class QueryRequest(BaseModel):
|
|||
ret: Dict = {"with_paths": True, "with_notes": True, "with_chunks": True}
|
||||
explain: bool = False
|
||||
|
||||
# WP-22: Semantic Graph Routing
|
||||
# WP-22/25: Dynamische Gewichtung der Graphen-Highways
|
||||
boost_edges: Optional[Dict[str, float]] = None
|
||||
|
||||
|
||||
class FeedbackRequest(BaseModel):
|
||||
"""
|
||||
User-Feedback zu einem spezifischen Treffer oder der Gesamtantwort (WP-08 Basis).
|
||||
"""
|
||||
"""User-Feedback zu einem spezifischen Treffer oder der Gesamtantwort."""
|
||||
query_id: str = Field(..., description="ID der ursprünglichen Suche")
|
||||
node_id: str = Field(..., description="ID des bewerteten Treffers oder 'generated_answer'")
|
||||
score: int = Field(..., ge=1, le=5, description="1 (Irrelevant) bis 5 (Perfekt)")
|
||||
|
|
@ -76,16 +79,14 @@ class FeedbackRequest(BaseModel):
|
|||
|
||||
|
||||
class ChatRequest(BaseModel):
|
||||
"""
|
||||
WP-05: Request für /chat.
|
||||
"""
|
||||
"""Request für /chat (WP-25 Einstieg)."""
|
||||
message: str = Field(..., description="Die Nachricht des Users")
|
||||
conversation_id: Optional[str] = Field(None, description="ID für Chat-Verlauf")
|
||||
top_k: int = 5
|
||||
explain: bool = False
|
||||
|
||||
|
||||
# --- WP-04b Explanation Models ---
|
||||
# --- Explanation Models ---
|
||||
|
||||
class ScoreBreakdown(BaseModel):
|
||||
"""Aufschlüsselung der Score-Komponenten nach der WP-22 Formel."""
|
||||
|
|
@ -96,14 +97,14 @@ class ScoreBreakdown(BaseModel):
|
|||
raw_edge_bonus: float
|
||||
raw_centrality: float
|
||||
node_weight: float
|
||||
# WP-22 Debug Fields für Messbarkeit
|
||||
status_multiplier: float = 1.0
|
||||
graph_boost_factor: float = 1.0
|
||||
|
||||
|
||||
class Reason(BaseModel):
|
||||
"""Ein semantischer Grund für das Ranking."""
|
||||
kind: Literal["semantic", "edge", "type", "centrality", "lifecycle"]
|
||||
# WP-25: 'status' hinzugefügt für Synchronität mit retriever.py
|
||||
kind: Literal["semantic", "edge", "type", "centrality", "lifecycle", "status"]
|
||||
message: str
|
||||
score_impact: Optional[float] = None
|
||||
details: Optional[Dict[str, Any]] = None
|
||||
|
|
@ -114,7 +115,6 @@ class Explanation(BaseModel):
|
|||
breakdown: ScoreBreakdown
|
||||
reasons: List[Reason]
|
||||
related_edges: Optional[List[EdgeDTO]] = None
|
||||
# WP-22 Debug: Verifizierung des Routings
|
||||
applied_intent: Optional[str] = None
|
||||
applied_boosts: Optional[Dict[str, float]] = None
|
||||
|
||||
|
|
@ -122,7 +122,10 @@ class Explanation(BaseModel):
|
|||
# --- Response Models ---
|
||||
|
||||
class QueryHit(BaseModel):
|
||||
"""Einzelnes Trefferobjekt für /query."""
|
||||
"""
|
||||
Einzelnes Trefferobjekt.
|
||||
WP-25: stream_origin hinzugefügt für Tracing und Feedback-Optimierung.
|
||||
"""
|
||||
node_id: str
|
||||
note_id: str
|
||||
semantic_score: float
|
||||
|
|
@ -133,10 +136,11 @@ class QueryHit(BaseModel):
|
|||
source: Optional[Dict] = None
|
||||
payload: Optional[Dict] = None
|
||||
explanation: Optional[Explanation] = None
|
||||
stream_origin: Optional[str] = Field(None, description="Name des Ursprungs-Streams")
|
||||
|
||||
|
||||
class QueryResponse(BaseModel):
|
||||
"""Antwortstruktur für /query."""
|
||||
"""Antwortstruktur für /query (wird von DecisionEngine Streams genutzt)."""
|
||||
query_id: str = Field(default_factory=lambda: str(uuid.uuid4()))
|
||||
results: List[QueryHit]
|
||||
used_mode: str
|
||||
|
|
@ -153,11 +157,12 @@ class GraphResponse(BaseModel):
|
|||
|
||||
class ChatResponse(BaseModel):
|
||||
"""
|
||||
WP-05/06: Antwortstruktur für /chat.
|
||||
Antwortstruktur für /chat.
|
||||
WP-25: 'intent' spiegelt nun die gewählte Strategie wider.
|
||||
"""
|
||||
query_id: str = Field(..., description="Traceability ID")
|
||||
answer: str = Field(..., description="Generierte Antwort vom LLM")
|
||||
sources: List[QueryHit] = Field(..., description="Die genutzten Quellen")
|
||||
sources: List[QueryHit] = Field(..., description="Die genutzten Quellen (alle Streams)")
|
||||
latency_ms: int
|
||||
intent: Optional[str] = Field("FACT", description="WP-06: Erkannter Intent")
|
||||
intent_source: Optional[str] = Field("Unknown", description="Quelle der Intent-Erkennung")
|
||||
intent: Optional[str] = Field("FACT", description="Die gewählte WP-25 Strategie")
|
||||
intent_source: Optional[str] = Field("LLM_Router", description="Quelle der Intent-Erkennung")
|
||||
|
|
@ -1,12 +1,15 @@
|
|||
"""
|
||||
FILE: app/routers/chat.py
|
||||
DESCRIPTION: Haupt-Chat-Interface (RAG & Interview). Enthält Intent-Router (Keywords/LLM) und Prompt-Construction.
|
||||
VERSION: 2.7.8 (Full Unabridged Stability Edition)
|
||||
DESCRIPTION: Haupt-Chat-Interface (WP-25 Agentic Edition).
|
||||
Kombiniert die spezialisierte Interview-Logik und Keyword-Erkennung
|
||||
mit der neuen Multi-Stream Orchestrierung der DecisionEngine.
|
||||
VERSION: 3.0.2
|
||||
STATUS: Active
|
||||
FIX:
|
||||
1. Implementiert Context-Throttling für Ollama (MAX_OLLAMA_CHARS).
|
||||
2. Deaktiviert LLM-Retries für den Chat (max_retries=0).
|
||||
3. Behebt Double-Fallback-Schleifen und Silent Refusals.
|
||||
- 100% Wiederherstellung der v2.7.8 Logik (Interview, Schema-Resolution, Keywords).
|
||||
- Integration der DecisionEngine für paralleles RAG-Retrieval.
|
||||
- Erhalt der Ollama Context-Throttling Parameter (WP-20).
|
||||
- Beibehaltung der No-Retry Logik (max_retries=0) für Chat-Stabilität.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, HTTPException, Depends
|
||||
|
|
@ -19,46 +22,39 @@ import os
|
|||
from pathlib import Path
|
||||
|
||||
from app.config import get_settings
|
||||
from app.models.dto import ChatRequest, ChatResponse, QueryRequest, QueryHit
|
||||
from app.models.dto import ChatRequest, ChatResponse, QueryHit
|
||||
from app.services.llm_service import LLMService
|
||||
from app.core.retrieval.retriever import Retriever
|
||||
from app.services.feedback_service import log_search
|
||||
|
||||
router = APIRouter()
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# --- Helper: Config Loader ---
|
||||
# --- EBENE 1: CONFIG LOADER & CACHING (Restauriert aus v2.7.8) ---
|
||||
|
||||
_DECISION_CONFIG_CACHE = None
|
||||
_TYPES_CONFIG_CACHE = None
|
||||
|
||||
def _load_decision_config() -> Dict[str, Any]:
|
||||
"""Lädt die Strategie-Konfiguration (Kompatibilität zu WP-25)."""
|
||||
settings = get_settings()
|
||||
path = Path(settings.DECISION_CONFIG_PATH)
|
||||
default_config = {
|
||||
"strategies": {
|
||||
"FACT": {"trigger_keywords": [], "preferred_provider": "openrouter"}
|
||||
}
|
||||
}
|
||||
|
||||
if not path.exists():
|
||||
logger.warning(f"Decision config not found at {path}, using defaults.")
|
||||
return default_config
|
||||
|
||||
try:
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
return yaml.safe_load(f)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to load decision config: {e}")
|
||||
return default_config
|
||||
|
||||
def _load_types_config() -> Dict[str, Any]:
|
||||
"""Lädt die types.yaml für Keyword-Erkennung."""
|
||||
path = os.getenv("MINDNET_TYPES_FILE", "config/types.yaml")
|
||||
try:
|
||||
if path.exists():
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
return yaml.safe_load(f) or {}
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to load decision config: {e}")
|
||||
return {"strategies": {}}
|
||||
|
||||
def _load_types_config() -> Dict[str, Any]:
|
||||
"""Lädt die types.yaml für die Typerkennung im Interview-Modus."""
|
||||
path = os.getenv("MINDNET_TYPES_FILE", "config/types.yaml")
|
||||
try:
|
||||
if os.path.exists(path):
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
return yaml.safe_load(f) or {}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to load types config: {e}")
|
||||
return {}
|
||||
|
||||
def get_full_config() -> Dict[str, Any]:
|
||||
|
|
@ -76,21 +72,20 @@ def get_types_config() -> Dict[str, Any]:
|
|||
def get_decision_strategy(intent: str) -> Dict[str, Any]:
|
||||
config = get_full_config()
|
||||
strategies = config.get("strategies", {})
|
||||
return strategies.get(intent, strategies.get("FACT", {}))
|
||||
return strategies.get(intent, strategies.get("FACT_WHAT", {}))
|
||||
|
||||
# --- Helper: Target Type Detection (WP-07) ---
|
||||
# --- EBENE 2: SPEZIAL-LOGIK (INTERVIEW & DETECTION) ---
|
||||
|
||||
def _detect_target_type(message: str, configured_schemas: Dict[str, Any]) -> str:
|
||||
"""
|
||||
Versucht zu erraten, welchen Notiz-Typ der User erstellen will.
|
||||
Nutzt Keywords aus types.yaml UND Mappings.
|
||||
WP-07: Identifiziert den gewünschten Notiz-Typ (Keyword-basiert).
|
||||
100% identisch mit v2.7.8 zur Sicherstellung des Interview-Workflows.
|
||||
"""
|
||||
message_lower = message.lower()
|
||||
|
||||
# 1. Check types.yaml detection_keywords (Priority!)
|
||||
types_cfg = get_types_config()
|
||||
types_def = types_cfg.get("types", {})
|
||||
|
||||
# 1. Check types.yaml detection_keywords
|
||||
for type_name, type_data in types_def.items():
|
||||
keywords = type_data.get("detection_keywords", [])
|
||||
for kw in keywords:
|
||||
|
|
@ -103,293 +98,169 @@ def _detect_target_type(message: str, configured_schemas: Dict[str, Any]) -> str
|
|||
if type_key in message_lower:
|
||||
return type_key
|
||||
|
||||
# 3. Synonym-Mapping (Legacy Fallback)
|
||||
# 3. Synonym-Mapping (Legacy)
|
||||
synonyms = {
|
||||
"projekt": "project", "vorhaben": "project",
|
||||
"entscheidung": "decision", "beschluss": "decision",
|
||||
"ziel": "goal",
|
||||
"erfahrung": "experience", "lektion": "experience",
|
||||
"wert": "value",
|
||||
"prinzip": "principle",
|
||||
"notiz": "default", "idee": "default"
|
||||
"projekt": "project", "entscheidung": "decision", "ziel": "goal",
|
||||
"erfahrung": "experience", "wert": "value", "prinzip": "principle"
|
||||
}
|
||||
|
||||
for term, schema_key in synonyms.items():
|
||||
if term in message_lower:
|
||||
return schema_key
|
||||
|
||||
return "default"
|
||||
|
||||
# --- Dependencies ---
|
||||
|
||||
def get_llm_service():
|
||||
return LLMService()
|
||||
|
||||
def get_retriever():
|
||||
return Retriever()
|
||||
|
||||
|
||||
# --- Logic ---
|
||||
|
||||
def _build_enriched_context(hits: List[QueryHit]) -> str:
|
||||
context_parts = []
|
||||
for i, hit in enumerate(hits, 1):
|
||||
source = hit.source or {}
|
||||
content = (
|
||||
source.get("text") or source.get("content") or
|
||||
source.get("page_content") or source.get("chunk_text") or
|
||||
"[Kein Text]"
|
||||
)
|
||||
title = hit.note_id or "Unbekannt"
|
||||
|
||||
payload = hit.payload or {}
|
||||
note_type = payload.get("type") or source.get("type", "unknown")
|
||||
note_type = str(note_type).upper()
|
||||
|
||||
entry = (
|
||||
f"### QUELLE {i}: {title}\n"
|
||||
f"TYP: [{note_type}] (Score: {hit.total_score:.2f})\n"
|
||||
f"INHALT:\n{content}\n"
|
||||
)
|
||||
context_parts.append(entry)
|
||||
|
||||
return "\n\n".join(context_parts)
|
||||
|
||||
def _is_question(query: str) -> bool:
|
||||
"""Prüft, ob der Input wahrscheinlich eine Frage ist."""
|
||||
"""Prüft, ob der Input eine Frage ist (W-Fragen Erkennung)."""
|
||||
q = query.strip().lower()
|
||||
if "?" in q: return True
|
||||
|
||||
# W-Fragen Indikatoren
|
||||
starters = ["wer", "wie", "was", "wo", "wann", "warum", "weshalb", "wozu", "welche", "bist du", "entspricht"]
|
||||
if any(q.startswith(s + " ") for s in starters):
|
||||
return True
|
||||
|
||||
return False
|
||||
starters = ["wer", "wie", "was", "wo", "wann", "warum", "weshalb", "wozu", "welche", "bist du"]
|
||||
return any(q.startswith(s + " ") for s in starters)
|
||||
|
||||
async def _classify_intent(query: str, llm: LLMService) -> tuple[str, str]:
|
||||
"""
|
||||
Hybrid Router v5:
|
||||
1. Decision Keywords (Strategie) -> Prio 1
|
||||
2. Type Keywords (Interview Trigger) -> Prio 2
|
||||
3. LLM (Fallback) -> Prio 3
|
||||
WP-25 Hybrid Router:
|
||||
Nutzt erst Keyword-Fast-Paths (Router) und delegiert dann an die DecisionEngine.
|
||||
"""
|
||||
config = get_full_config()
|
||||
strategies = config.get("strategies", {})
|
||||
settings = config.get("settings", {})
|
||||
|
||||
query_lower = query.lower()
|
||||
|
||||
# 1. FAST PATH A: Strategie Keywords
|
||||
# 1. FAST PATH: Keyword Trigger
|
||||
for intent_name, strategy in strategies.items():
|
||||
if intent_name == "FACT": continue
|
||||
keywords = strategy.get("trigger_keywords", [])
|
||||
for k in keywords:
|
||||
if k.lower() in query_lower:
|
||||
return intent_name, "Keyword (Strategy)"
|
||||
return intent_name, "Keyword (FastPath)"
|
||||
|
||||
# 2. FAST PATH B: Type Keywords -> INTERVIEW
|
||||
if not _is_question(query_lower):
|
||||
types_cfg = get_types_config()
|
||||
types_def = types_cfg.get("types", {})
|
||||
|
||||
for type_name, type_data in types_def.items():
|
||||
keywords = type_data.get("detection_keywords", [])
|
||||
for kw in keywords:
|
||||
for type_name, type_data in types_cfg.get("types", {}).items():
|
||||
for kw in type_data.get("detection_keywords", []):
|
||||
if kw.lower() in query_lower:
|
||||
return "INTERVIEW", f"Keyword (Type: {type_name})"
|
||||
return "INTERVIEW", "Keyword (Interview)"
|
||||
|
||||
# 3. SLOW PATH: LLM Router
|
||||
if settings.get("llm_fallback_enabled", False):
|
||||
router_prompt_template = llm.get_prompt("llm_router_prompt")
|
||||
# 3. SLOW PATH: DecisionEngine LLM Router
|
||||
intent = await llm.decision_engine._determine_strategy(query)
|
||||
return intent, "DecisionEngine (LLM)"
|
||||
|
||||
if router_prompt_template:
|
||||
prompt = router_prompt_template.replace("{query}", query)
|
||||
logger.info("Keywords failed (or Question detected). Asking LLM for Intent...")
|
||||
# --- EBENE 3: RETRIEVAL AGGREGATION ---
|
||||
|
||||
try:
|
||||
# FIX: Auch beim Routing keine Retries im Chat-Fluss
|
||||
raw_response = await llm.generate_raw_response(prompt, priority="realtime", max_retries=0)
|
||||
llm_output_upper = raw_response.upper()
|
||||
def _collect_all_hits(stream_responses: Dict[str, Any]) -> List[QueryHit]:
|
||||
"""Sammelt und dedupliziert Treffer aus allen parallelen Streams."""
|
||||
all_hits = []
|
||||
seen_node_ids = set()
|
||||
for _, response in stream_responses.items():
|
||||
if hasattr(response, 'results'):
|
||||
for hit in response.results:
|
||||
if hit.node_id not in seen_node_ids:
|
||||
all_hits.append(hit)
|
||||
seen_node_ids.add(hit.node_id)
|
||||
return sorted(all_hits, key=lambda h: h.total_score, reverse=True)
|
||||
|
||||
if "INTERVIEW" in llm_output_upper or "CREATE" in llm_output_upper:
|
||||
return "INTERVIEW", "LLM Router"
|
||||
# --- EBENE 4: ENDPUNKT ---
|
||||
|
||||
for strat_key in strategies.keys():
|
||||
if strat_key in llm_output_upper:
|
||||
return strat_key, "LLM Router"
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Router LLM failed: {e}")
|
||||
|
||||
return "FACT", "Default (No Match)"
|
||||
def get_llm_service():
|
||||
return LLMService()
|
||||
|
||||
@router.post("/", response_model=ChatResponse)
|
||||
async def chat_endpoint(
|
||||
request: ChatRequest,
|
||||
llm: LLMService = Depends(get_llm_service),
|
||||
retriever: Retriever = Depends(get_retriever)
|
||||
llm: LLMService = Depends(get_llm_service)
|
||||
):
|
||||
start_time = time.time()
|
||||
query_id = str(uuid.uuid4())
|
||||
logger.info(f"Chat request [{query_id}]: {request.message[:50]}...")
|
||||
settings = get_settings()
|
||||
logger.info(f"🚀 [WP-25] Chat request [{query_id}]: {request.message[:50]}...")
|
||||
|
||||
try:
|
||||
# 1. Intent Detection
|
||||
intent, intent_source = await _classify_intent(request.message, llm)
|
||||
logger.info(f"[{query_id}] Final Intent: {intent} via {intent_source}")
|
||||
logger.info(f"[{query_id}] Intent: {intent} via {intent_source}")
|
||||
|
||||
# Strategy Load
|
||||
strategy = get_decision_strategy(intent)
|
||||
prompt_key = strategy.get("prompt_template", "rag_template")
|
||||
preferred_provider = strategy.get("preferred_provider")
|
||||
engine = llm.decision_engine
|
||||
|
||||
sources_hits = []
|
||||
final_prompt = ""
|
||||
context_str = ""
|
||||
answer_text = ""
|
||||
|
||||
# 2. INTERVIEW MODE (Kompatibilität zu v2.7.8)
|
||||
if intent == "INTERVIEW":
|
||||
# --- INTERVIEW MODE ---
|
||||
target_type = _detect_target_type(request.message, strategy.get("schemas", {}))
|
||||
|
||||
types_cfg = get_types_config()
|
||||
type_def = types_cfg.get("types", {}).get(target_type, {})
|
||||
fields_list = type_def.get("schema", [])
|
||||
|
||||
if not fields_list:
|
||||
configured_schemas = strategy.get("schemas", {})
|
||||
fallback_schema = configured_schemas.get(target_type, configured_schemas.get("default"))
|
||||
if isinstance(fallback_schema, dict):
|
||||
fields_list = fallback_schema.get("fields", [])
|
||||
else:
|
||||
fields_list = fallback_schema or []
|
||||
fallback = configured_schemas.get(target_type, configured_schemas.get("default", {}))
|
||||
fields_list = fallback.get("fields", []) if isinstance(fallback, dict) else (fallback or [])
|
||||
|
||||
logger.info(f"[{query_id}] Interview Type: {target_type}. Fields: {len(fields_list)}")
|
||||
fields_str = "\n- " + "\n- ".join(fields_list)
|
||||
template = llm.get_prompt(strategy.get("prompt_template", "interview_template"))
|
||||
|
||||
template = llm.get_prompt(prompt_key)
|
||||
final_prompt = template.replace("{context_str}", "Dialogverlauf...") \
|
||||
.replace("{query}", request.message) \
|
||||
final_prompt = template.replace("{query}", request.message) \
|
||||
.replace("{target_type}", target_type) \
|
||||
.replace("{schema_fields}", fields_str) \
|
||||
.replace("{schema_hint}", "")
|
||||
.replace("{schema_fields}", fields_str)
|
||||
|
||||
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 = []
|
||||
|
||||
# 3. RAG MODE (WP-25 Multi-Stream)
|
||||
else:
|
||||
# --- RAG MODE (FACT, DECISION, EMPATHY, CODING) ---
|
||||
inject_types = strategy.get("inject_types", [])
|
||||
prepend_instr = strategy.get("prepend_instruction", "")
|
||||
edge_boosts = strategy.get("edge_boosts", {})
|
||||
stream_keys = strategy.get("use_streams", [])
|
||||
library = engine.config.get("streams_library", {})
|
||||
|
||||
query_req = QueryRequest(
|
||||
query=request.message,
|
||||
mode="hybrid",
|
||||
top_k=request.top_k,
|
||||
explain=request.explain,
|
||||
boost_edges=edge_boosts
|
||||
)
|
||||
retrieve_result = await retriever.search(query_req)
|
||||
hits = retrieve_result.results
|
||||
tasks = []
|
||||
active_streams = []
|
||||
for key in stream_keys:
|
||||
stream_cfg = library.get(key)
|
||||
if stream_cfg:
|
||||
active_streams.append(key)
|
||||
tasks.append(engine._run_single_stream(key, stream_cfg, request.message))
|
||||
|
||||
if inject_types:
|
||||
strategy_req = QueryRequest(
|
||||
query=request.message,
|
||||
mode="hybrid",
|
||||
top_k=3,
|
||||
filters={"type": inject_types},
|
||||
explain=False,
|
||||
boost_edges=edge_boosts
|
||||
)
|
||||
strategy_result = await retriever.search(strategy_req)
|
||||
existing_ids = {h.node_id for h in hits}
|
||||
for strat_hit in strategy_result.results:
|
||||
if strat_hit.node_id not in existing_ids:
|
||||
hits.append(strat_hit)
|
||||
import asyncio
|
||||
responses = await asyncio.gather(*tasks, return_exceptions=True)
|
||||
|
||||
context_str = _build_enriched_context(hits) if hits else "Keine relevanten Notizen gefunden."
|
||||
|
||||
# --- STABILITY FIX: OLLAMA CONTEXT THROTTLE ---
|
||||
# Begrenzt den Text, um den "decode: cannot decode batches" Fehler zu vermeiden.
|
||||
# MAX_OLLAMA_CHARS = 10000
|
||||
|
||||
settings = get_settings() # Falls noch nicht im Scope vorhanden
|
||||
raw_stream_map = {}
|
||||
formatted_context_map = {}
|
||||
max_chars = getattr(settings, "MAX_OLLAMA_CHARS", 10000)
|
||||
if preferred_provider == "ollama" and len(context_str) > max_chars:
|
||||
logger.warning(f"⚠️ [{query_id}] Context zu groß für Ollama ({len(context_str)} chars). Kürze auf {max_chars}.")
|
||||
context_str = context_str[:max_chars] + "\n[...gekürzt zur Stabilität...]"
|
||||
provider = strategy.get("preferred_provider") or settings.MINDNET_LLM_PROVIDER
|
||||
|
||||
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:
|
||||
context_str = f"{prepend_instr}\n\n{context_str}"
|
||||
# WP-20 Stability Fix: Throttling
|
||||
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)
|
||||
sources_hits = hits
|
||||
formatted_context_map[name] = context_text
|
||||
|
||||
# --- DEBUG SPOT 1: PROMPT CONSTRUCTION ---
|
||||
logger.info(f"[{query_id}] PROMPT CONSTRUCTION COMPLETE. Length: {len(final_prompt)} chars.")
|
||||
if not final_prompt.strip():
|
||||
logger.error(f"[{query_id}] CRITICAL: Final prompt is empty before sending to LLM!")
|
||||
|
||||
# --- GENERATION WITH NO-RETRY & DEEP FALLBACK ---
|
||||
system_prompt = llm.get_prompt("system_prompt")
|
||||
|
||||
# --- DEBUG SPOT 2: PRIMARY CALL ---
|
||||
logger.info(f"[{query_id}] PRIMARY CALL: Sending request to provider '{preferred_provider}' (No Retries)...")
|
||||
|
||||
answer_text = ""
|
||||
try:
|
||||
# FIX: max_retries=0 verhindert Hänger durch Retry-Kaskaden im Chat
|
||||
answer_text = await llm.generate_raw_response(
|
||||
prompt=final_prompt,
|
||||
system=system_prompt,
|
||||
priority="realtime",
|
||||
provider=preferred_provider,
|
||||
max_retries=0
|
||||
answer_text = await engine._generate_final_answer(
|
||||
intent, strategy, request.message, formatted_context_map
|
||||
)
|
||||
except Exception as e:
|
||||
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."
|
||||
sources_hits = _collect_all_hits(raw_stream_map)
|
||||
|
||||
duration_ms = int((time.time() - start_time) * 1000)
|
||||
|
||||
# Logging
|
||||
try:
|
||||
log_search(
|
||||
query_id=query_id,
|
||||
query_text=request.message,
|
||||
results=sources_hits,
|
||||
mode="interview" if intent == "INTERVIEW" else "chat_rag",
|
||||
metadata={"intent": intent, "source": intent_source, "provider": preferred_provider}
|
||||
query_id=query_id, query_text=request.message, results=sources_hits,
|
||||
mode=f"wp25_{intent.lower()}", metadata={"strategy": intent, "source": intent_source}
|
||||
)
|
||||
except: pass
|
||||
|
||||
return ChatResponse(
|
||||
query_id=query_id,
|
||||
answer=answer_text,
|
||||
sources=sources_hits,
|
||||
latency_ms=duration_ms,
|
||||
intent=intent,
|
||||
intent_source=intent_source
|
||||
query_id=query_id, answer=answer_text, sources=sources_hits,
|
||||
latency_ms=duration_ms, intent=intent, intent_source=intent_source
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in chat endpoint: {e}", exc_info=True)
|
||||
# Wir geben eine benutzerfreundliche Meldung zurück, statt nur den Error-Stack
|
||||
raise HTTPException(status_code=500, detail="Das System konnte die Anfrage nicht verarbeiten.")
|
||||
logger.error(f"❌ Chat Endpoint Failure: {e}", exc_info=True)
|
||||
raise HTTPException(status_code=500, detail="Fehler bei der Verarbeitung.")
|
||||
|
|
@ -3,14 +3,14 @@ FILE: app/services/llm_service.py
|
|||
DESCRIPTION: Hybrid-Client für Ollama, Google GenAI (Gemini) und OpenRouter.
|
||||
Verwaltet provider-spezifische Prompts und Background-Last.
|
||||
WP-20: Optimiertes Fallback-Management zum Schutz von Cloud-Quoten.
|
||||
WP-20 Fix: Bulletproof Prompt-Auflösung für format() Aufrufe.
|
||||
WP-22/JSON: Optionales JSON-Schema + strict (für OpenRouter structured outputs).
|
||||
FIX: Intelligente Rate-Limit Erkennung (429 Handling), v1-API Sync & Timeouts.
|
||||
VERSION: 3.3.9
|
||||
WP-22/JSON: Optionales JSON-Schema + strict (für OpenRouter).
|
||||
WP-25: Integration der DecisionEngine für Agentic Multi-Stream RAG.
|
||||
VERSION: 3.4.2 (WP-25: Ingest-Stability Patch)
|
||||
STATUS: Active
|
||||
FIX:
|
||||
- Importiert clean_llm_text von app.core.registry zur Vermeidung von Circular Imports.
|
||||
- Wendet clean_llm_text auf Text-Antworten in generate_raw_response an.
|
||||
- Ingest-Stability: Entfernung des <5-Zeichen Guards (ermöglicht YES/NO Validierungen).
|
||||
- OpenRouter-Fix: Sicherung gegen leere 'choices' zur Vermeidung von JSON-Errors.
|
||||
- Erhalt der vollständigen v3.3.9 Logik für Rate-Limits, Retries und Background-Tasks.
|
||||
"""
|
||||
import httpx
|
||||
import yaml
|
||||
|
|
@ -29,7 +29,6 @@ from app.core.registry import clean_llm_text
|
|||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LLMService:
|
||||
# GLOBALER SEMAPHOR für Hintergrund-Last Steuerung (WP-06)
|
||||
_background_semaphore = None
|
||||
|
|
@ -38,6 +37,9 @@ class LLMService:
|
|||
self.settings = get_settings()
|
||||
self.prompts = self._load_prompts()
|
||||
|
||||
# WP-25: Lazy Initialization der DecisionEngine zur Vermeidung von Circular Imports
|
||||
self._decision_engine = None
|
||||
|
||||
# Initialisiere Semaphore einmalig auf Klassen-Ebene
|
||||
if LLMService._background_semaphore is None:
|
||||
limit = getattr(self.settings, "BACKGROUND_LIMIT", 2)
|
||||
|
|
@ -71,6 +73,14 @@ class LLMService:
|
|||
)
|
||||
logger.info("🛰️ LLMService: OpenRouter Integration active.")
|
||||
|
||||
@property
|
||||
def decision_engine(self):
|
||||
"""Lazy Initialization der Decision Engine (WP-25)."""
|
||||
if self._decision_engine is None:
|
||||
from app.core.retrieval.decision_engine import DecisionEngine
|
||||
self._decision_engine = DecisionEngine()
|
||||
return self._decision_engine
|
||||
|
||||
def _load_prompts(self) -> dict:
|
||||
"""Lädt die Prompt-Konfiguration aus der YAML-Datei."""
|
||||
path = Path(self.settings.PROMPTS_PATH)
|
||||
|
|
@ -87,17 +97,13 @@ class LLMService:
|
|||
def get_prompt(self, key: str, provider: str = None) -> str:
|
||||
"""
|
||||
Hole provider-spezifisches Template mit intelligenter Text-Kaskade.
|
||||
HINWEIS: Dies ist nur ein Text-Lookup und verbraucht kein API-Kontingent.
|
||||
Kaskade: Gewählter Provider -> Gemini (Cloud-Stil) -> Ollama (Basis-Stil).
|
||||
Kaskade: Gewählter Provider -> Gemini -> Ollama.
|
||||
"""
|
||||
active_provider = provider or self.settings.MINDNET_LLM_PROVIDER
|
||||
data = self.prompts.get(key, "")
|
||||
|
||||
if isinstance(data, dict):
|
||||
# Wir versuchen erst den Provider, dann Gemini, dann Ollama
|
||||
val = data.get(active_provider, data.get("gemini", data.get("ollama", "")))
|
||||
|
||||
# Falls val durch YAML-Fehler immer noch ein Dict ist, extrahiere ersten String
|
||||
if isinstance(val, dict):
|
||||
logger.warning(f"⚠️ [LLMService] Nested dictionary detected for key '{key}'. Using first entry.")
|
||||
val = next(iter(val.values()), "") if val else ""
|
||||
|
|
@ -120,8 +126,8 @@ class LLMService:
|
|||
strict_json_schema: bool = True
|
||||
) -> str:
|
||||
"""
|
||||
Haupteinstiegspunkt für LLM-Anfragen mit Priorisierung.
|
||||
Wendet die Bereinigung auf Text-Antworten an.
|
||||
Haupteinstiegspunkt für LLM-Anfragen.
|
||||
WP-25 FIX: Schwellenwert entfernt, um kurze Ingest-Validierungen (YES/NO) zu unterstützen.
|
||||
"""
|
||||
target_provider = provider or self.settings.MINDNET_LLM_PROVIDER
|
||||
|
||||
|
|
@ -132,14 +138,18 @@ class LLMService:
|
|||
max_retries, base_delay, model_override,
|
||||
json_schema, json_schema_name, strict_json_schema
|
||||
)
|
||||
# WP-14 Fix: Bereinige Text-Antworten vor Rückgabe
|
||||
return clean_llm_text(res) if not force_json else res
|
||||
|
||||
else:
|
||||
res = await self._dispatch(
|
||||
target_provider, prompt, system, force_json,
|
||||
max_retries, base_delay, model_override,
|
||||
json_schema, json_schema_name, strict_json_schema
|
||||
)
|
||||
|
||||
# WP-25 FIX: Nur noch auf absolut leere Antwort prüfen (ermöglicht YES/NO Antworten).
|
||||
if not res and target_provider != "ollama":
|
||||
logger.warning(f"⚠️ [WP-25] Empty response from {target_provider}. Falling back to OLLAMA.")
|
||||
res = await self._execute_ollama(prompt, system, force_json, max_retries, base_delay)
|
||||
|
||||
# WP-14 Fix: Bereinige Text-Antworten vor Rückgabe
|
||||
return clean_llm_text(res) if not force_json else res
|
||||
|
||||
|
|
@ -156,12 +166,8 @@ class LLMService:
|
|||
json_schema_name: str,
|
||||
strict_json_schema: bool
|
||||
) -> str:
|
||||
"""
|
||||
Routet die Anfrage mit intelligenter Rate-Limit Erkennung.
|
||||
Nutzt max_retries um die Rate-Limit Schleife zu begrenzen.
|
||||
"""
|
||||
"""Routet die Anfrage mit intelligenter Rate-Limit Erkennung."""
|
||||
rate_limit_attempts = 0
|
||||
# FIX: Wir nutzen max_retries als Limit für Rate-Limit Versuche, wenn explizit klein gewählt (z.B. Chat)
|
||||
max_rate_retries = min(max_retries, getattr(self.settings, "LLM_RATE_LIMIT_RETRIES", 3))
|
||||
wait_time = getattr(self.settings, "LLM_RATE_LIMIT_WAIT", 60.0)
|
||||
|
||||
|
|
@ -181,33 +187,24 @@ class LLMService:
|
|||
if provider == "gemini" and self.google_client:
|
||||
return await self._execute_google(prompt, system, force_json, model_override)
|
||||
|
||||
# Default/Fallback zu Ollama
|
||||
return await self._execute_ollama(prompt, system, force_json, max_retries, base_delay)
|
||||
|
||||
except Exception as e:
|
||||
err_str = str(e)
|
||||
# Intelligente 429 Erkennung
|
||||
is_rate_limit = any(x in err_str for x in ["429", "RESOURCE_EXHAUSTED", "rate_limited", "Too Many Requests"])
|
||||
|
||||
if is_rate_limit and rate_limit_attempts < max_rate_retries:
|
||||
rate_limit_attempts += 1
|
||||
logger.warning(
|
||||
f"⏳ [LLMService] Rate Limit detected from {provider}. "
|
||||
f"Attempt {rate_limit_attempts}/{max_rate_retries}. Waiting {wait_time}s..."
|
||||
)
|
||||
logger.warning(f"⏳ Rate Limit from {provider}. Attempt {rate_limit_attempts}. Waiting {wait_time}s...")
|
||||
await asyncio.sleep(wait_time)
|
||||
continue
|
||||
|
||||
# Wenn kein Rate-Limit oder Retries erschöpft -> Fallback zu Ollama (falls aktiviert)
|
||||
if self.settings.LLM_FALLBACK_ENABLED and provider != "ollama":
|
||||
logger.warning(
|
||||
f"🔄 Provider {provider} failed ({err_str}). Falling back to LOCAL OLLAMA."
|
||||
)
|
||||
logger.warning(f"🔄 Provider {provider} failed ({err_str}). Falling back to OLLAMA.")
|
||||
return await self._execute_ollama(prompt, system, force_json, max_retries, base_delay)
|
||||
raise e
|
||||
|
||||
async def _execute_google(self, prompt, system, force_json, model_override):
|
||||
"""Native Google SDK Integration (Gemini) mit v1 Fix."""
|
||||
model = model_override or self.settings.GEMINI_MODEL
|
||||
clean_model = model.replace("models/", "")
|
||||
|
||||
|
|
@ -234,7 +231,7 @@ class LLMService:
|
|||
json_schema_name: str = "mindnet_json",
|
||||
strict_json_schema: bool = True
|
||||
) -> str:
|
||||
"""OpenRouter API Integration (OpenAI-kompatibel)."""
|
||||
"""OpenRouter API Integration. WP-25 FIX: Sicherung gegen leere 'choices'."""
|
||||
model = model_override or self.settings.OPENROUTER_MODEL
|
||||
messages = []
|
||||
if system:
|
||||
|
|
@ -247,9 +244,7 @@ class LLMService:
|
|||
kwargs["response_format"] = {
|
||||
"type": "json_schema",
|
||||
"json_schema": {
|
||||
"name": json_schema_name,
|
||||
"strict": strict_json_schema,
|
||||
"schema": json_schema
|
||||
"name": json_schema_name, "strict": strict_json_schema, "schema": json_schema
|
||||
}
|
||||
}
|
||||
else:
|
||||
|
|
@ -260,23 +255,23 @@ class LLMService:
|
|||
messages=messages,
|
||||
**kwargs
|
||||
)
|
||||
return response.choices[0].message.content.strip()
|
||||
|
||||
# WP-25 FIX: Sicherung gegen leere Antwort-Arrays
|
||||
if not response.choices or len(response.choices) == 0:
|
||||
logger.warning(f"🛰️ OpenRouter returned no choices for model {model}")
|
||||
return ""
|
||||
|
||||
return response.choices[0].message.content.strip() if response.choices[0].message.content else ""
|
||||
|
||||
async def _execute_ollama(self, prompt, system, force_json, max_retries, base_delay):
|
||||
"""Lokaler Ollama Call mit striktem Retry-Limit."""
|
||||
payload = {
|
||||
"model": self.settings.LLM_MODEL,
|
||||
"prompt": prompt,
|
||||
"stream": False,
|
||||
"options": {
|
||||
"temperature": 0.1 if force_json else 0.7,
|
||||
"num_ctx": 8192 # Begrenzung für Stabilität (WP-20)
|
||||
"options": {"temperature": 0.1 if force_json else 0.7, "num_ctx": 8192}
|
||||
}
|
||||
}
|
||||
if force_json:
|
||||
payload["format"] = "json"
|
||||
if system:
|
||||
payload["system"] = system
|
||||
if force_json: payload["format"] = "json"
|
||||
if system: payload["system"] = system
|
||||
|
||||
attempt = 0
|
||||
while True:
|
||||
|
|
@ -286,32 +281,17 @@ class LLMService:
|
|||
return res.json().get("response", "").strip()
|
||||
except Exception as e:
|
||||
attempt += 1
|
||||
# WICHTIG: Wenn max_retries=0 (Chat), bricht dies nach dem 1. Versuch (attempt=1) sofort ab.
|
||||
if attempt > max_retries:
|
||||
logger.error(f"❌ Ollama request failed after {attempt} attempt(s): {e}")
|
||||
logger.error(f"❌ Ollama request failed: {e}")
|
||||
raise e
|
||||
|
||||
wait_time = base_delay * (2 ** (attempt - 1))
|
||||
logger.warning(f"⚠️ Ollama attempt {attempt} failed. Retrying in {wait_time}s...")
|
||||
await asyncio.sleep(wait_time)
|
||||
|
||||
async def generate_rag_response(self, query: str, context_str: str) -> str:
|
||||
"""Vollständiges RAG Chat-Interface."""
|
||||
provider = self.settings.MINDNET_LLM_PROVIDER
|
||||
system_prompt = self.get_prompt("system_prompt", provider)
|
||||
rag_template = self.get_prompt("rag_template", provider)
|
||||
|
||||
final_prompt = rag_template.format(context_str=context_str, query=query)
|
||||
|
||||
# RAG Aufrufe im Chat nutzen nun standardmäßig max_retries=2 (überschreibbar)
|
||||
# Durch den Aufruf von generate_raw_response wird die Bereinigung automatisch angewendet.
|
||||
return await self.generate_raw_response(
|
||||
final_prompt,
|
||||
system=system_prompt,
|
||||
priority="realtime"
|
||||
)
|
||||
async def generate_rag_response(self, query: str, context_str: Optional[str] = None) -> str:
|
||||
"""WP-25: Orchestrierung via DecisionEngine."""
|
||||
logger.info(f"🚀 [WP-25] Chat Query: {query[:50]}...")
|
||||
return await self.decision_engine.ask(query)
|
||||
|
||||
async def close(self):
|
||||
"""Schließt die HTTP-Verbindungen."""
|
||||
if self.ollama_client:
|
||||
await self.ollama_client.aclose()
|
||||
|
|
@ -1,145 +1,144 @@
|
|||
# config/decision_engine.yaml
|
||||
# Steuerung der Decision Engine (Intent Recognition & Graph Routing)
|
||||
# VERSION: 2.6.1 (WP-20: Hybrid LLM & WP-22: Semantic Graph Routing)
|
||||
# VERSION: 3.1.6 (WP-25: Multi-Stream Agentic RAG - Final Release)
|
||||
# STATUS: Active
|
||||
# DoD: Keine Hardcoded Modelle, volle Integration der strategischen Boosts.
|
||||
# DoD:
|
||||
# - Strikte Nutzung der Typen aus types.yaml (v2.7.0).
|
||||
# - Fix für Projekt-Klassifizierung via Keyword-Fast-Path (Auflösung Kollision).
|
||||
# - 100% Erhalt aller Stream-Parameter und Edge-Boosts.
|
||||
|
||||
version: 2.6
|
||||
version: 3.1
|
||||
|
||||
settings:
|
||||
llm_fallback_enabled: true
|
||||
|
||||
# Strategie für den Router selbst (Welches Modell erkennt den Intent?)
|
||||
# "auto" nutzt den in MINDNET_LLM_PROVIDER gesetzten Standard (z.B. openrouter).
|
||||
# "auto" nutzt den in MINDNET_LLM_PROVIDER gesetzten Standard.
|
||||
router_provider: "auto"
|
||||
# Verweist auf das Template in prompts.yaml
|
||||
router_prompt_key: "intent_router_v1"
|
||||
|
||||
# Few-Shot Prompting für den LLM-Router
|
||||
llm_router_prompt: |
|
||||
Du bist der zentrale Intent-Klassifikator für Mindnet, einen digitalen Zwilling.
|
||||
Analysiere die Nachricht und wähle die passende Strategie.
|
||||
Antworte NUR mit dem Namen der Strategie.
|
||||
# --- EBENE 1: STREAM-LIBRARY (Bausteine basierend auf types.yaml) ---
|
||||
# Synchronisiert mit types.yaml v2.7.0
|
||||
|
||||
STRATEGIEN:
|
||||
- INTERVIEW: User will Wissen erfassen, Notizen anlegen oder Dinge festhalten.
|
||||
- DECISION: Rat, Strategie, Abwägung von Werten, "Soll ich tun X?".
|
||||
- EMPATHY: Gefühle, Reflexion der eigenen Verfassung, Frust, Freude.
|
||||
- CODING: Code-Erstellung, Debugging, technische Dokumentation.
|
||||
- FACT: Reine Wissensabfrage, Definitionen, Suchen von Informationen.
|
||||
streams_library:
|
||||
values_stream:
|
||||
name: "Identität & Ethik"
|
||||
query_template: "Welche meiner Werte und Prinzipien betreffen: {query}"
|
||||
# Nur Typen aus types.yaml
|
||||
filter_types: ["value", "principle", "belief", "trait", "boundary", "need", "motivation"]
|
||||
top_k: 5
|
||||
edge_boosts:
|
||||
guides: 3.0
|
||||
enforced_by: 2.5
|
||||
based_on: 2.0
|
||||
|
||||
BEISPIELE:
|
||||
User: "Wie funktioniert die Qdrant-Vektor-DB?" -> FACT
|
||||
User: "Soll ich mein Startup jetzt verkaufen?" -> DECISION
|
||||
User: "Notiere mir kurz meine Gedanken zum Meeting." -> INTERVIEW
|
||||
User: "Ich fühle mich heute sehr erschöpft." -> EMPATHY
|
||||
User: "Schreibe eine FastAPI-Route für den Ingest." -> CODING
|
||||
|
||||
NACHRICHT: "{query}"
|
||||
|
||||
STRATEGIE:
|
||||
|
||||
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
|
||||
facts_stream:
|
||||
name: "Operative Realität"
|
||||
query_template: "Status, Ressourcen und Fakten zu: {query}"
|
||||
# Nur Typen aus types.yaml
|
||||
filter_types: ["project", "decision", "task", "goal", "event", "state"]
|
||||
top_k: 5
|
||||
edge_boosts:
|
||||
part_of: 2.0
|
||||
composed_of: 2.0
|
||||
similar_to: 1.5
|
||||
caused_by: 0.5
|
||||
prompt_template: "rag_template"
|
||||
prepend_instruction: null
|
||||
depends_on: 1.5
|
||||
implemented_in: 1.5
|
||||
|
||||
# 2. Entscheidungs-Frage (Power-Strategie via Gemini)
|
||||
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:
|
||||
description: "Der User sucht Rat, Strategie oder Abwägung."
|
||||
preferred_provider: "gemini"
|
||||
trigger_keywords:
|
||||
- "soll ich"
|
||||
- "meinung"
|
||||
- "besser"
|
||||
- "empfehlung"
|
||||
- "strategie"
|
||||
- "entscheidung"
|
||||
- "abwägung"
|
||||
- "vergleich"
|
||||
inject_types: ["value", "principle", "goal", "risk"]
|
||||
# WP-22: Risiken und Konsequenzen im Graphen priorisieren
|
||||
edge_boosts:
|
||||
blocks: 2.5
|
||||
solves: 2.0
|
||||
depends_on: 1.5
|
||||
risk_of: 2.5
|
||||
impacts: 2.0
|
||||
prompt_template: "decision_template"
|
||||
# FIX v3.1.6: Trigger erweitert, um "Soll ich... Projekt..." sicher zu fangen.
|
||||
trigger_keywords: ["soll ich", "sollte ich", "entscheidung", "abwägen", "priorität", "empfehlung"]
|
||||
use_streams:
|
||||
- "values_stream"
|
||||
- "facts_stream"
|
||||
- "risk_stream"
|
||||
prompt_template: "decision_synthesis_v1"
|
||||
prepend_instruction: |
|
||||
!!! ENTSCHEIDUNGS-MODUS (HYBRID AI) !!!
|
||||
BITTE WÄGE FAKTEN GEGEN FOLGENDE WERTE, PRINZIPIEN UND ZIELE AB:
|
||||
!!! ENTSCHEIDUNGS-MODUS (AGENTIC MULTI-STREAM) !!!
|
||||
Analysiere die Fakten vor dem Hintergrund meiner Werte und evaluiere die Risiken.
|
||||
Wäge ab, ob das Vorhaben mit meiner langfristigen Identität kompatibel ist.
|
||||
|
||||
# 3. Empathie / "Ich"-Modus (Lokal & Privat via Ollama)
|
||||
# Emotionale Reflexion
|
||||
EMPATHY:
|
||||
description: "Reaktion auf emotionale Zustände."
|
||||
preferred_provider: "openrouter"
|
||||
trigger_keywords:
|
||||
- "ich fühle"
|
||||
- "traurig"
|
||||
- "glücklich"
|
||||
- "gestresst"
|
||||
- "angst"
|
||||
- "nervt"
|
||||
- "überfordert"
|
||||
- "müde"
|
||||
inject_types: ["experience", "belief", "profile"]
|
||||
edge_boosts:
|
||||
based_on: 2.0
|
||||
related_to: 2.0
|
||||
experienced_in: 2.5
|
||||
blocks: 0.1
|
||||
trigger_keywords: ["fühle", "traurig", "glücklich", "stress", "angst"]
|
||||
use_streams:
|
||||
- "biography_stream"
|
||||
- "values_stream"
|
||||
prompt_template: "empathy_template"
|
||||
prepend_instruction: null
|
||||
|
||||
# 4. Coding / Technical (Gemini Power)
|
||||
# Technischer Support
|
||||
CODING:
|
||||
description: "Technische Anfragen und Programmierung."
|
||||
preferred_provider: "gemini"
|
||||
trigger_keywords:
|
||||
- "code"
|
||||
- "python"
|
||||
- "script"
|
||||
- "funktion"
|
||||
- "bug"
|
||||
- "syntax"
|
||||
- "json"
|
||||
- "yaml"
|
||||
- "bash"
|
||||
inject_types: ["snippet", "reference", "source"]
|
||||
# WP-22: Technische Abhängigkeiten priorisieren
|
||||
edge_boosts:
|
||||
uses: 2.5
|
||||
depends_on: 2.0
|
||||
implemented_in: 3.0
|
||||
trigger_keywords: ["code", "python", "script", "bug", "syntax"]
|
||||
use_streams:
|
||||
- "tech_stream"
|
||||
- "facts_stream"
|
||||
prompt_template: "technical_template"
|
||||
prepend_instruction: null
|
||||
|
||||
# 5. Interview / Datenerfassung (Lokal)
|
||||
# Eingabe-Modus (WP-07)
|
||||
INTERVIEW:
|
||||
description: "Der User möchte Wissen erfassen."
|
||||
description: "Der User möchte Wissen erfassen (Eingabemodus)."
|
||||
preferred_provider: "openrouter"
|
||||
trigger_keywords:
|
||||
- "neue notiz"
|
||||
- "etwas notieren"
|
||||
- "festhalten"
|
||||
- "erstellen"
|
||||
- "dokumentieren"
|
||||
- "anlegen"
|
||||
- "interview"
|
||||
- "erfassen"
|
||||
- "idee speichern"
|
||||
- "draft"
|
||||
inject_types: []
|
||||
edge_boosts: {}
|
||||
use_streams: []
|
||||
prompt_template: "interview_template"
|
||||
prepend_instruction: null
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
# config/prompts.yaml — Final V2.6.0 (WP-15b Candidate-Validation)
|
||||
# WP-20: Optimierte Cloud-Templates zur Unterdrückung von Modell-Geschwätz.
|
||||
# FIX: Explizite Verbote für Einleitungstexte zur Vermeidung von JSON-Parsing-Fehlern.
|
||||
# WP-15b: Integration der binären edge_validation für den Two-Pass Workflow.
|
||||
# OLLAMA: UNVERÄNDERT laut Benutzeranweisung.
|
||||
# config/prompts.yaml — VERSION 3.1.2 (WP-25 Cleanup: Multi-Stream Sync)
|
||||
# STATUS: Active
|
||||
# FIX:
|
||||
# - 100% Wiederherstellung der Ingest- & Validierungslogik (Sektion 5-8).
|
||||
# - Überführung der Kategorien 1-4 in die Multi-Stream Struktur unter Beibehaltung des Inhalts.
|
||||
# - Konsolidierung: Sektion 9 (v3.0.0) wurde in Sektion 1 & 2 integriert (keine Redundanz).
|
||||
|
||||
system_prompt: |
|
||||
Du bist 'mindnet', mein digitaler Zwilling und strategischer Partner.
|
||||
|
|
@ -17,13 +18,21 @@ system_prompt: |
|
|||
3. Antworte auf Deutsch (außer bei Code/Fachbegriffen).
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# 1. STANDARD: Fakten & Wissen (Intent: FACT)
|
||||
# 1. STANDARD: Fakten & Wissen (Intent: FACT_WHAT / FACT_WHEN)
|
||||
# ---------------------------------------------------------
|
||||
rag_template:
|
||||
# Ersetzt das alte 'rag_template'. Nutzt jetzt parallele Streams.
|
||||
fact_synthesis_v1:
|
||||
ollama: |
|
||||
QUELLEN (WISSEN):
|
||||
WISSENS-STREAMS:
|
||||
=========================================
|
||||
{context_str}
|
||||
FAKTEN & STATUS:
|
||||
{facts_stream}
|
||||
|
||||
ERFAHRUNG & BIOGRAFIE:
|
||||
{biography_stream}
|
||||
|
||||
WISSEN & TECHNIK:
|
||||
{tech_stream}
|
||||
=========================================
|
||||
|
||||
FRAGE:
|
||||
|
|
@ -31,25 +40,35 @@ rag_template:
|
|||
|
||||
ANWEISUNG:
|
||||
Beantworte die Frage präzise basierend auf den Quellen.
|
||||
Kombiniere harte Fakten mit persönlichen Erfahrungen, falls vorhanden.
|
||||
Fasse die Informationen zusammen. Sei objektiv und neutral.
|
||||
gemini: |
|
||||
Kontext meines digitalen Zwillings: {context_str}
|
||||
Beantworte strukturiert und präzise: {query}
|
||||
Beantworte die Wissensabfrage "{query}" basierend auf diesen Streams:
|
||||
FAKTEN: {facts_stream}
|
||||
BIOGRAFIE/ERFAHRUNG: {biography_stream}
|
||||
TECHNIK: {tech_stream}
|
||||
Kombiniere harte Fakten mit persönlichen Erfahrungen, falls vorhanden. Antworte strukturiert und präzise.
|
||||
openrouter: |
|
||||
Kontext-Analyse für den digitalen Zwilling:
|
||||
{context_str}
|
||||
|
||||
Anfrage: {query}
|
||||
Antworte basierend auf dem Kontext.
|
||||
Synthese der Wissens-Streams für: {query}
|
||||
Inhalt: {facts_stream} | {biography_stream} | {tech_stream}
|
||||
Antworte basierend auf dem bereitgestellten Kontext.
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# 2. DECISION: Strategie & Abwägung (Intent: DECISION)
|
||||
# ---------------------------------------------------------
|
||||
decision_template:
|
||||
# Ersetzt das alte 'decision_template'. Nutzt jetzt parallele Streams.
|
||||
decision_synthesis_v1:
|
||||
ollama: |
|
||||
KONTEXT (FAKTEN & STRATEGIE):
|
||||
ENTSCHEIDUNGS-STREAMS:
|
||||
=========================================
|
||||
{context_str}
|
||||
WERTE & PRINZIPIEN (Identität):
|
||||
{values_stream}
|
||||
|
||||
OPERATIVE FAKTEN (Realität):
|
||||
{facts_stream}
|
||||
|
||||
RISIKO-RADAR (Konsequenzen):
|
||||
{risk_stream}
|
||||
=========================================
|
||||
|
||||
ENTSCHEIDUNGSFRAGE:
|
||||
|
|
@ -58,7 +77,7 @@ decision_template:
|
|||
ANWEISUNG:
|
||||
Du agierst als mein Entscheidungs-Partner.
|
||||
1. Analysiere die Faktenlage aus den Quellen.
|
||||
2. Prüfe dies hart gegen meine strategischen Notizen (Typ [VALUE], [PRINCIPLE], [GOAL]).
|
||||
2. Prüfe dies hart gegen meine strategischen Notizen (Werte & Prinzipien).
|
||||
3. Wäge ab: Passt die technische/faktische Lösung zu meinen Werten?
|
||||
|
||||
FORMAT:
|
||||
|
|
@ -66,19 +85,26 @@ decision_template:
|
|||
- **Abgleich:** (Gibt es Konflikte mit Werten/Zielen? Nenne die Quelle!)
|
||||
- **Empfehlung:** (Klare Meinung: Ja/No/Vielleicht mit Begründung)
|
||||
gemini: |
|
||||
Agiere als strategischer Partner. Analysiere die Frage {query} basierend auf meinen Werten im Kontext {context_str}.
|
||||
Agiere als mein strategischer Partner. Analysiere die Frage: {query}
|
||||
Werte: {values_stream} | Fakten: {facts_stream} | Risiken: {risk_stream}.
|
||||
Wäge ab und gib eine klare strategische Empfehlung ab.
|
||||
openrouter: |
|
||||
Strategische Entscheidungsanalyse: {query}
|
||||
Wertebasis aus dem Graphen: {context_str}
|
||||
Strategische Multi-Stream Analyse für: {query}
|
||||
Werte-Basis: {values_stream} | Fakten: {facts_stream} | Risiken: {risk_stream}
|
||||
Bitte wäge ab und gib eine Empfehlung.
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# 3. EMPATHY: Der Spiegel / "Ich"-Modus (Intent: EMPATHY)
|
||||
# ---------------------------------------------------------
|
||||
empathy_template:
|
||||
ollama: |
|
||||
KONTEXT (ERFAHRUNGEN & GLAUBENSSÄTZE):
|
||||
KONTEXT (ERFAHRUNGEN & WERTE):
|
||||
=========================================
|
||||
{context_str}
|
||||
ERLEBNISSE & BIOGRAFIE:
|
||||
{biography_stream}
|
||||
|
||||
WERTE & BEDÜRFNISSE:
|
||||
{values_stream}
|
||||
=========================================
|
||||
|
||||
SITUATION:
|
||||
|
|
@ -87,22 +113,26 @@ empathy_template:
|
|||
ANWEISUNG:
|
||||
Du agierst jetzt als mein empathischer Spiegel.
|
||||
1. Versuche nicht sofort, das Problem technisch zu lösen.
|
||||
2. Zeige Verständnis für die Situation basierend auf meinen eigenen Erfahrungen ([EXPERIENCE]) oder Glaubenssätzen ([BELIEF]), falls im Kontext vorhanden.
|
||||
2. Zeige Verständnis für die Situation basierend auf meinen eigenen Erfahrungen ([EXPERIENCE]) oder Werten, falls im Kontext vorhanden.
|
||||
3. Antworte in der "Ich"-Form oder "Wir"-Form. Sei unterstützend.
|
||||
|
||||
TONFALL:
|
||||
Ruhig, verständnisvoll, reflektiert. Keine Aufzählungszeichen, sondern fließender Text.
|
||||
gemini: "Sei mein digitaler Spiegel für {query}. Kontext: {context_str}"
|
||||
openrouter: "Empathische Reflexion der Situation {query}. Persönlicher Kontext: {context_str}"
|
||||
gemini: "Sei mein digitaler Spiegel für {query}. Kontext: {biography_stream}, {values_stream}"
|
||||
openrouter: "Empathische Reflexion der Situation {query}. Persönlicher Kontext: {biography_stream}, {values_stream}"
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# 4. TECHNICAL: Der Coder (Intent: CODING)
|
||||
# ---------------------------------------------------------
|
||||
technical_template:
|
||||
ollama: |
|
||||
KONTEXT (DOCS & SNIPPETS):
|
||||
KONTEXT (WISSEN & PROJEKTE):
|
||||
=========================================
|
||||
{context_str}
|
||||
TECHNIK & SNIPPETS:
|
||||
{tech_stream}
|
||||
|
||||
PROJEKT-STATUS:
|
||||
{facts_stream}
|
||||
=========================================
|
||||
|
||||
TASK:
|
||||
|
|
@ -118,11 +148,11 @@ technical_template:
|
|||
- Kurze Erklärung des Ansatzes.
|
||||
- Markdown Code-Block (Copy-Paste fertig).
|
||||
- Wichtige Edge-Cases.
|
||||
gemini: "Generiere Code für {query} unter Berücksichtigung von {context_str}."
|
||||
openrouter: "Technischer Support für {query}. Code-Referenzen: {context_str}"
|
||||
gemini: "Generiere Code für {query} unter Berücksichtigung von {tech_stream} und {facts_stream}."
|
||||
openrouter: "Technischer Support für {query}. Referenzen: {tech_stream}, Projekt-Kontext: {facts_stream}"
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# 5. INTERVIEW: Der "One-Shot Extractor" (Performance Mode)
|
||||
# 5. INTERVIEW: Der "One-Shot Extractor" (WP-07)
|
||||
# ---------------------------------------------------------
|
||||
interview_template:
|
||||
ollama: |
|
||||
|
|
@ -160,7 +190,7 @@ interview_template:
|
|||
openrouter: "Strukturiere den Input {query} nach dem Schema {schema_fields} für Typ {target_type}."
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# 6. EDGE_ALLOCATION: Kantenfilter (Intent: OFFLINE_FILTER)
|
||||
# 6. EDGE_ALLOCATION: Kantenfilter (Ingest)
|
||||
# ---------------------------------------------------------
|
||||
edge_allocation_template:
|
||||
ollama: |
|
||||
|
|
@ -200,7 +230,7 @@ edge_allocation_template:
|
|||
OUTPUT:
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# 7. SMART EDGE ALLOCATION: Extraktion (Intent: INGEST)
|
||||
# 7. SMART EDGE ALLOCATION: Extraktion (Ingest)
|
||||
# ---------------------------------------------------------
|
||||
edge_extraction:
|
||||
ollama: |
|
||||
|
|
@ -240,7 +270,7 @@ edge_extraction:
|
|||
OUTPUT:
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# 8. WP-15b: EDGE VALIDATION (Intent: VALIDATE)
|
||||
# 8. WP-15b: EDGE VALIDATION (Ingest/Validate)
|
||||
# ---------------------------------------------------------
|
||||
edge_validation:
|
||||
gemini: |
|
||||
|
|
@ -271,3 +301,37 @@ edge_validation:
|
|||
ZIEL: {target_title} ({target_summary})
|
||||
BEZIEHUNG: {edge_kind}
|
||||
Ist diese Verbindung valide? Antworte NUR mit YES oder NO.
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# 10. WP-25: INTENT ROUTING (Intent: CLASSIFY)
|
||||
# ---------------------------------------------------------
|
||||
intent_router_v1:
|
||||
ollama: |
|
||||
Analysiere die Nutzeranfrage und wähle die passende Strategie.
|
||||
Antworte NUR mit dem Namen der Strategie.
|
||||
|
||||
STRATEGIEN:
|
||||
- FACT_WHEN: Nur für explizite Fragen nach einem exakten Datum, Uhrzeit oder dem "Wann" eines Ereignisses.
|
||||
- FACT_WHAT: Fragen nach Inhalten, Listen von Objekten/Projekten, Definitionen oder "Was/Welche" Anfragen (auch bei Zeiträumen).
|
||||
- DECISION: Rat, Meinung, "Soll ich?", Abwägung gegen Werte.
|
||||
- EMPATHY: Emotionen, Reflexion, Befindlichkeit.
|
||||
- CODING: Programmierung, Skripte, technische Syntax.
|
||||
- INTERVIEW: Dokumentation neuer Informationen, Notizen anlegen.
|
||||
|
||||
NACHRICHT: "{query}"
|
||||
STRATEGIE:
|
||||
gemini: |
|
||||
Classify intent:
|
||||
- FACT_WHEN: Exact dates/times only.
|
||||
- FACT_WHAT: Content, lists of entities (projects, etc.), definitions, "What/Which" queries.
|
||||
- DECISION: Strategic advice/values.
|
||||
- EMPATHY: Emotions.
|
||||
- CODING: Tech/Code.
|
||||
- INTERVIEW: Data entry.
|
||||
Query: "{query}"
|
||||
Result (One word only):
|
||||
openrouter: |
|
||||
Select strategy for Mindnet:
|
||||
FACT_WHEN (timing/dates), FACT_WHAT (entities/lists/what/which), DECISION, EMPATHY, CODING, INTERVIEW.
|
||||
Query: "{query}"
|
||||
Response:
|
||||
|
|
@ -151,8 +151,8 @@ Damit dieses System wartbar bleibt (auch für KI-Agenten wie NotebookLM), gelten
|
|||
|
||||
## 6. Dokumentations-Status
|
||||
|
||||
**Aktuelle Version:** 2.9.1
|
||||
**Letzte Aktualisierung:** 2025-01-XX
|
||||
**Aktuelle Version:** 2.9.3
|
||||
**Letzte Aktualisierung:** 2025-12-31
|
||||
**Status:** ✅ Vollständig und aktiv gepflegt
|
||||
|
||||
**Hinweis:** Diese Dokumentation wird kontinuierlich aktualisiert. Bei Fragen oder Verbesserungsvorschlägen bitte im Repository melden.
|
||||
|
|
@ -2,8 +2,8 @@
|
|||
doc_type: glossary
|
||||
audience: all
|
||||
status: active
|
||||
version: 2.9.1
|
||||
context: "Zentrales Glossar für Mindnet v2.8. Enthält Definitionen zu Hybrid-Cloud Resilienz, WP-14 Modularisierung, WP-15b Two-Pass Ingestion und Mistral-safe Parsing."
|
||||
version: 2.9.3
|
||||
context: "Zentrales Glossar für Mindnet v2.9.3. Enthält Definitionen zu Hybrid-Cloud Resilienz, WP-14 Modularisierung, WP-15b Two-Pass Ingestion, WP-15c Multigraph-Support, WP-25 Agentic Multi-Stream RAG und Mistral-safe Parsing."
|
||||
---
|
||||
|
||||
# Mindnet Glossar
|
||||
|
|
@ -23,7 +23,11 @@ context: "Zentrales Glossar für Mindnet v2.8. Enthält Definitionen zu Hybrid-C
|
|||
* **Edge Registry:** Der zentrale Dienst (SSOT), der Kanten-Typen validiert und Aliase in kanonische Typen auflöst. Nutzt `01_edge_vocabulary.md` als Basis.
|
||||
* **LLM Service:** Der Hybrid-Client (v3.3.6), der Anfragen zwischen OpenRouter, Google Gemini und lokalem Ollama routet. Verwaltet Cloud-Timeouts und Quoten-Management. Nutzt zur Text-Bereinigung nun die neutrale `registry.py`, um Circular Imports zu vermeiden.
|
||||
* **Retriever:** Besteht in v2.7+ aus der Orchestrierung (`retriever.py`) und der mathematischen Scoring-Engine (`retriever_scoring.py`). Seit WP-14 im Paket `app.core.retrieval` gekapselt.
|
||||
* **Decision Engine:** Teil des Routers, der Intents erkennt und entsprechende **Boost-Faktoren** für das Retrieval injiziert.
|
||||
* **Decision Engine (WP-25):** Der zentrale **Agentic Orchestrator**, der Intents erkennt, parallele Wissens-Streams orchestriert und die Ergebnisse synthetisiert. Implementiert Multi-Stream Retrieval und Intent-basiertes Routing.
|
||||
* **Agentic Multi-Stream RAG (WP-25):** Architektur-Paradigma, bei dem Nutzeranfragen in parallele, spezialisierte Wissens-Streams aufgeteilt werden (Values, Facts, Biography, Risk, Tech), die gleichzeitig abgefragt und zu einer kontextreichen Antwort synthetisiert werden.
|
||||
* **Stream-Tracing (WP-25):** Kennzeichnung jedes Treffers mit seinem Ursprungs-Stream (`stream_origin`), um Feedback-Optimierung pro Wissensbereich zu ermöglichen.
|
||||
* **Intent-basiertes Routing (WP-25):** Hybrid-Modus zur Intent-Erkennung mit Keyword Fast-Path (sofortige Erkennung von Triggern) und LLM Slow-Path (semantische Analyse für unklare Anfragen).
|
||||
* **Wissens-Synthese (WP-25):** Template-basierte Zusammenführung der Ergebnisse aus parallelen Streams mit expliziten Stream-Variablen (z.B. `{values_stream}`, `{risk_stream}`), um dem LLM eine differenzierte Abwägung zu ermöglichen.
|
||||
* **Traffic Control:** Verwaltet Prioritäten und drosselt Hintergrund-Tasks (z.B. Smart Edges) mittels Semaphoren und Timeouts (45s) zur Vermeidung von System-Hangs.
|
||||
* **Unknown Edges Log:** Die Datei `unknown_edges.jsonl`, in der das System Kanten-Typen protokolliert, die nicht im Dictionary gefunden wurden.
|
||||
* **Database Package (WP-14):** Zentralisiertes Infrastruktur-Paket (`app.core.database`), das den Qdrant-Client (`qdrant.py`) und das Point-Mapping (`qdrant_points.py`) verwaltet.
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ doc_type: user_manual
|
|||
audience: user, mindmaster
|
||||
scope: chat, ui, feedback, graph
|
||||
status: active
|
||||
version: 2.6
|
||||
context: "Anleitung zur Nutzung der Web-Oberfläche, der Chat-Personas und des Graph Explorers."
|
||||
version: 2.9.3
|
||||
context: "Anleitung zur Nutzung der Web-Oberfläche, der Chat-Personas, Multi-Stream RAG und des Graph Explorers."
|
||||
---
|
||||
|
||||
# Chat & Graph Usage Guide
|
||||
|
|
@ -60,21 +60,49 @@ Ein Editor mit **"File System First"** Garantie.
|
|||
|
||||
---
|
||||
|
||||
## 3. Den Chat steuern (Intents)
|
||||
## 3. Den Chat steuern (Intents & Multi-Stream RAG)
|
||||
|
||||
Du steuerst die Persönlichkeit von Mindnet durch deine Wortwahl. Der **Hybrid Router v5** unterscheidet intelligent:
|
||||
Du steuerst die Persönlichkeit von Mindnet durch deine Wortwahl. Seit WP-25 nutzt Mindnet **Agentic Multi-Stream RAG**, das deine Anfrage in parallele Wissens-Streams aufteilt:
|
||||
|
||||
### 3.1 Frage-Modus (Wissen abrufen)
|
||||
Ausgelöst durch `?` oder W-Wörter.
|
||||
### 3.1 Intent-Erkennung (Hybrid-Router)
|
||||
|
||||
Der Router erkennt deine Absicht auf zwei Wegen:
|
||||
|
||||
**Schnelle Erkennung (Keyword Fast-Path):**
|
||||
* **"Soll ich..."** → Sofortige Erkennung als `DECISION` (Berater)
|
||||
* **"Wann..."** → Sofortige Erkennung als `FACT_WHEN` (Zeitpunkte)
|
||||
* **"Was ist..."** → Sofortige Erkennung als `FACT_WHAT` (Wissen)
|
||||
* **"Ich fühle..."** → Sofortige Erkennung als `EMPATHY` (Spiegel)
|
||||
|
||||
**Intelligente Analyse (LLM Slow-Path):**
|
||||
* Bei unklaren Anfragen analysiert die KI semantisch deine Absicht
|
||||
|
||||
### 3.2 Multi-Stream Retrieval (WP-25)
|
||||
|
||||
Anstelle einer einzelnen Suche führt Mindnet nun **parallele Abfragen** in spezialisierten Wissens-Streams aus:
|
||||
|
||||
**Die Streams:**
|
||||
* **Values Stream:** Deine Identität, Ethik und Prinzipien (`value`, `principle`, `belief`)
|
||||
* **Facts Stream:** Operative Daten zu Projekten, Tasks und Status (`project`, `decision`, `task`)
|
||||
* **Biography Stream:** Persönliche Erfahrungen und Journal-Einträge (`experience`, `journal`)
|
||||
* **Risk Stream:** Hindernisse und potenzielle Gefahren (`risk`, `obstacle`)
|
||||
* **Tech Stream:** Technisches Wissen, Code und Dokumentation (`concept`, `source`, `glossary`)
|
||||
|
||||
**Vorteil:** Jeder Stream fokussiert auf spezifische Wissensbereiche, was zu präziseren und kontextreicheren Antworten führt.
|
||||
|
||||
### 3.3 Frage-Modi (Strategien)
|
||||
|
||||
* **Entscheidung ("Soll ich?"):** Der **Berater**.
|
||||
* Mindnet lädt deine Werte (`type: value`) und Ziele (`type: goal`).
|
||||
* Nutzt: Values Stream, Facts Stream, Risk Stream
|
||||
* Wägt Fakten gegen deine Werte ab und evaluiert Risiken
|
||||
* *Beispiel:* "Soll ich Tool X nutzen?" -> "Nein, Tool X speichert Daten in den USA. Das verstößt gegen dein Prinzip 'Privacy First'."
|
||||
* **Empathie ("Ich fühle..."):** Der **Spiegel**.
|
||||
* Mindnet lädt deine Erfahrungen (`type: experience`).
|
||||
* Nutzt: Biography Stream, Values Stream
|
||||
* Greift auf deine Erfahrungen und Werte zurück
|
||||
* *Beispiel:* "Ich bin frustriert." -> "Das erinnert mich an Projekt Y, da ging es uns ähnlich..."
|
||||
* **Fakten ("Was ist?"):** Der **Bibliothekar**.
|
||||
* Liefert präzise Definitionen.
|
||||
* **Fakten ("Was ist?", "Wann..."):** Der **Bibliothekar**.
|
||||
* Nutzt: Facts Stream, Tech Stream, Biography Stream
|
||||
* Liefert präzise Definitionen und zeitliche Informationen
|
||||
|
||||
### 3.2 Befehls-Modus (Interview)
|
||||
Ausgelöst durch Aussagen wie "Neues Projekt", "Ich will festhalten".
|
||||
|
|
@ -84,13 +112,17 @@ Ausgelöst durch Aussagen wie "Neues Projekt", "Ich will festhalten".
|
|||
|
||||
---
|
||||
|
||||
## 4. Ergebnisse interpretieren (Explanation Layer)
|
||||
## 4. Ergebnisse interpretieren (Explanation Layer & Stream-Tracing)
|
||||
|
||||
Mindnet liefert eine **Begründung** ("Reasons"), warum es etwas gefunden hat.
|
||||
Öffne eine Quellen-Karte, um zu sehen:
|
||||
* *"Hohe textuelle Übereinstimmung."* (Semantik)
|
||||
* *"Bevorzugt aufgrund des Typs 'decision'."* (Wichtigkeit)
|
||||
* *"Verweist auf 'Projekt X' via 'depends_on'."* (Graph-Kontext)
|
||||
* *"Quelle: Values Stream"* (Stream-Tracing - WP-25)
|
||||
|
||||
**Stream-Tracing (WP-25):**
|
||||
Jede Quelle wird mit ihrem Ursprungs-Stream markiert (z.B. "Values Stream", "Facts Stream"). Dies hilft dir zu verstehen, aus welchem Wissensbereich die Information stammt.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
---
|
||||
doc_type: concept
|
||||
audience: architect, product_owner
|
||||
scope: ai, router, personas, resilience
|
||||
scope: ai, router, personas, resilience, agentic_rag
|
||||
status: active
|
||||
version: 2.8.1
|
||||
context: "Fachkonzept der hybriden KI-Persönlichkeit, der Provider-Kaskade und der kognitiven Resilienz (Deep Fallback)."
|
||||
version: 2.9.3
|
||||
context: "Fachkonzept der hybriden KI-Persönlichkeit, Agentic Multi-Stream RAG, Provider-Kaskade und kognitiven Resilienz (Deep Fallback)."
|
||||
---
|
||||
|
||||
# Konzept: KI-Persönlichkeit & Router
|
||||
|
|
@ -13,13 +13,45 @@ context: "Fachkonzept der hybriden KI-Persönlichkeit, der Provider-Kaskade und
|
|||
|
||||
Mindnet soll nicht wie eine Suchmaschine wirken, sondern wie ein **Digitaler Zwilling**. Dazu muss das System erkennen, **was** der Nutzer will, und seine „Persönlichkeit“ sowie seine technische Infrastruktur dynamisch anpassen.
|
||||
|
||||
## 1. Der Hybrid Router (Das Gehirn)
|
||||
## 1. Der Hybrid Router & Agentic Multi-Stream RAG (Das Gehirn)
|
||||
|
||||
Jede Eingabe durchläuft den **Hybrid Router**. Er entscheidet über die fachliche Strategie und die technische Ausführung.
|
||||
Jede Eingabe durchläuft den **Hybrid Router**. Seit WP-25 agiert das System als **Agentic Orchestrator**, der Nutzeranfragen analysiert, in parallele Wissens-Streams aufteilt und diese zu einer kontextreichen, wertebasierten Antwort synthetisiert.
|
||||
|
||||
### Modus A: RAG (Retrieval Augmented Generation)
|
||||
* **Intent:** Der Nutzer hat eine Frage oder ein Problem (`FACT`, `DECISION`, `EMPATHY`).
|
||||
* **Aktion:** Das System sucht im Gedächtnis und generiert eine Antwort.
|
||||
### Intent-basiertes Routing (WP-25)
|
||||
|
||||
Der Router nutzt einen **Hybrid-Modus** mit Keyword Fast-Path und LLM Slow-Path:
|
||||
|
||||
**Keyword Fast-Path:**
|
||||
* Sofortige Erkennung von Triggern wie "Soll ich", "Wann", "Was ist"
|
||||
* Reduziert Latenz durch schnelle Keyword-Erkennung ohne LLM-Call
|
||||
|
||||
**LLM Slow-Path:**
|
||||
* Komplexe semantische Analyse für unklare Anfragen
|
||||
* Nutzt `intent_router_v1` Prompt zur Klassifizierung
|
||||
|
||||
**Strategien:**
|
||||
* **FACT_WHAT/FACT_WHEN:** Wissensabfrage (Wissen/Listen, Zeitpunkte)
|
||||
* **DECISION:** Beratung (Rat, Strategie, Abwägung)
|
||||
* **EMPATHY:** Reflexion (Emotionale Resonanz)
|
||||
* **CODING:** Technik (Programmierung, Syntax)
|
||||
* **INTERVIEW:** Datenerfassung (Wissen speichern)
|
||||
|
||||
### Modus A: Agentic Multi-Stream RAG (WP-25)
|
||||
|
||||
Anstelle einer einzelnen Suche führt das System **parallele Abfragen** in spezialisierten Wissens-Streams aus:
|
||||
|
||||
**Stream-Library:**
|
||||
* **Values Stream:** Identität, Ethik und Prinzipien (`value`, `principle`, `belief`, `trait`, `boundary`, `need`, `motivation`)
|
||||
* **Facts Stream:** Operative Daten (`project`, `decision`, `task`, `goal`, `event`, `state`)
|
||||
* **Biography Stream:** Persönliche Erfahrungen (`experience`, `journal`, `profile`, `person`)
|
||||
* **Risk Stream:** Hindernisse und Gefahren (`risk`, `obstacle`, `bias`)
|
||||
* **Tech Stream:** Technisches Wissen (`concept`, `source`, `glossary`, `idea`, `insight`, `skill`, `habit`)
|
||||
|
||||
**Wissens-Synthese:**
|
||||
Die Zusammenführung erfolgt über spezialisierte Templates mit expliziten Stream-Variablen (z.B. `{values_stream}`, `{risk_stream}`). Dies ermöglicht dem LLM eine differenzierte Abwägung zwischen Fakten und persönlichen Werten.
|
||||
|
||||
**Stream-Tracing:**
|
||||
Jeder Treffer wird mit `stream_origin` markiert, um Feedback-Optimierung pro Wissensbereich zu ermöglichen.
|
||||
|
||||
### Modus B: Interview (Knowledge Capture)
|
||||
* **Intent:** Der Nutzer will Wissen speichern (`INTERVIEW`).
|
||||
|
|
@ -45,18 +77,22 @@ Ein intelligenter Zwilling muss jederzeit verfügbar sein. Mindnet v2.8.1 nutzt
|
|||
Mindnet wechselt den Hut, je nach Situation.
|
||||
|
||||
### 3.1 Der Berater (Strategy: DECISION)
|
||||
* **Auslöser:** Fragen wie „Soll ich...?“, „Was ist besser?“.
|
||||
* **Strategic Retrieval:** Lädt aktiv Notizen der Typen `value` (Werte), `goal` (Ziele) und `risk` (Risiken), auch wenn sie im Text nicht direkt vorkommen.
|
||||
* **Reasoning:** *„Wäge die Fakten gegen meine Werte ab. Sei strikt bei Risiken.“*
|
||||
* **Auslöser:** Fragen wie „Soll ich...?", „Was ist besser?", „Empfehlung...".
|
||||
* **Multi-Stream Retrieval (WP-25):** Führt parallele Abfragen in Values Stream, Facts Stream und Risk Stream aus.
|
||||
* **Wissens-Synthese:** Wägt Fakten gegen Werte ab, evaluiert Risiken und prüft Kompatibilität mit langfristiger Identität.
|
||||
* **Reasoning:** *„Wäge die Fakten gegen meine Werte ab. Sei strikt bei Risiken."*
|
||||
|
||||
### 3.2 Der Spiegel (Strategy: EMPATHY)
|
||||
* **Auslöser:** Emotionale Aussagen („Ich bin frustriert“).
|
||||
* **Strategic Retrieval:** Lädt `experience` (Erfahrungen) und `belief` (Glaubenssätze).
|
||||
* **Reasoning:** *„Nutze meine eigenen Erfahrungen, um die Situation einzuordnen.“*
|
||||
* **Auslöser:** Emotionale Aussagen („Ich bin frustriert", „Ich fühle...", „Stress...").
|
||||
* **Multi-Stream Retrieval (WP-25):** Führt parallele Abfragen in Biography Stream und Values Stream aus.
|
||||
* **Wissens-Synthese:** Greift auf persönliche Erfahrungen und Werte zurück, um emotionale Resonanz zu schaffen.
|
||||
* **Reasoning:** *„Nutze meine eigenen Erfahrungen, um die Situation einzuordnen."*
|
||||
|
||||
### 3.3 Der Bibliothekar (Strategy: FACT)
|
||||
* **Auslöser:** Sachfragen („Was ist Qdrant?“).
|
||||
* **Behavior:** Präzise, neutral, kurz.
|
||||
### 3.3 Der Bibliothekar (Strategy: FACT_WHAT / FACT_WHEN)
|
||||
* **Auslöser:** Sachfragen („Was ist...?", „Welche sind...?", „Wann...?", „Datum...").
|
||||
* **Multi-Stream Retrieval (WP-25):** Führt parallele Abfragen in Facts Stream, Tech Stream und Biography Stream aus.
|
||||
* **Wissens-Synthese:** Kombiniert harte Fakten mit persönlichen Erfahrungen, falls vorhanden.
|
||||
* **Behavior:** Präzise, neutral, strukturiert.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
---
|
||||
doc_type: technical_reference
|
||||
audience: developer, integrator
|
||||
scope: api, endpoints, integration
|
||||
scope: api, endpoints, integration, agentic_rag
|
||||
status: active
|
||||
version: 2.9.1
|
||||
context: "Vollständige API-Referenz für alle Mindnet-Endpunkte. Basis für Integration und Entwicklung."
|
||||
version: 2.9.3
|
||||
context: "Vollständige API-Referenz für alle Mindnet-Endpunkte inklusive WP-25 Agentic Multi-Stream RAG. Basis für Integration und Entwicklung."
|
||||
---
|
||||
|
||||
# API Reference
|
||||
|
|
@ -96,21 +96,37 @@ Hauptendpunkt für RAG-Chat und Interview-Modus. Unterstützt Streaming.
|
|||
**Response (Non-Streaming):**
|
||||
```json
|
||||
{
|
||||
"intent": "FACT",
|
||||
"response": "Mindnet ist ein persönliches KI-Gedächtnis...",
|
||||
"sources": [...],
|
||||
"query_id": "uuid"
|
||||
"query_id": "uuid",
|
||||
"answer": "Mindnet ist ein persönliches KI-Gedächtnis...",
|
||||
"sources": [
|
||||
{
|
||||
"node_id": "uuid",
|
||||
"note_id": "uuid",
|
||||
"semantic_score": 0.85,
|
||||
"total_score": 0.92,
|
||||
"stream_origin": "values_stream", // WP-25: Ursprungs-Stream
|
||||
"explanation": {...}
|
||||
}
|
||||
],
|
||||
"latency_ms": 450,
|
||||
"intent": "DECISION", // WP-25: Gewählte Strategie
|
||||
"intent_source": "Keyword (FastPath)" // WP-25: Quelle der Intent-Erkennung
|
||||
}
|
||||
```
|
||||
|
||||
**Response (Streaming):**
|
||||
Server-Sent Events (SSE) mit Chunks der Antwort.
|
||||
|
||||
**Intent-Typen:**
|
||||
- `FACT`: Wissensabfrage
|
||||
- `DECISION`: Entscheidungsfrage
|
||||
- `EMPATHY`: Emotionale Anfrage
|
||||
- `INTERVIEW`: Wissen erfassen
|
||||
**Intent-Typen (WP-25):**
|
||||
- `FACT_WHAT`: Wissensabfrage (Wissen/Listen)
|
||||
- `FACT_WHEN`: Zeitpunkte (Termine, Daten)
|
||||
- `DECISION`: Entscheidungsfrage (Beratung, Strategie)
|
||||
- `EMPATHY`: Emotionale Anfrage (Reflexion)
|
||||
- `CODING`: Technische Anfrage (Programmierung)
|
||||
- `INTERVIEW`: Wissen erfassen (Datenerfassung)
|
||||
|
||||
**Stream-Tracing (WP-25):**
|
||||
Jeder Treffer in `sources` enthält `stream_origin`, um die Zuordnung zum Quell-Stream zu ermöglichen (z.B. "values_stream", "facts_stream", "risk_stream").
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,30 +1,32 @@
|
|||
---
|
||||
doc_type: technical_reference
|
||||
audience: developer, architect
|
||||
scope: backend, chat, llm_service, traffic_control, resilience
|
||||
scope: backend, chat, llm_service, traffic_control, resilience, agentic_rag
|
||||
status: active
|
||||
version: 2.9.1
|
||||
context: "Technische Implementierung des FastAPI-Routers, des hybriden LLMService (v3.3.6) und der WP-20 Resilienz-Logik."
|
||||
version: 2.9.3
|
||||
context: "Technische Implementierung des FastAPI-Routers, des hybriden LLMService (v3.4.2), WP-25 Agentic Multi-Stream RAG und WP-20 Resilienz-Logik."
|
||||
---
|
||||
|
||||
# Chat Backend & Traffic Control
|
||||
# Chat Backend & Agentic Multi-Stream RAG
|
||||
|
||||
## 1. Hybrid Router (Decision Engine)
|
||||
## 1. Hybrid Router & Intent-basiertes Routing (WP-25)
|
||||
|
||||
Der zentrale Einstiegspunkt für jede Chatanfrage ist der **Hybrid Router** (`app/routers/chat.py`). Er entscheidet dynamisch über die Strategie und nutzt den `LLMService` zur provider-agnostischen Generierung.
|
||||
Der zentrale Einstiegspunkt für jede Chatanfrage ist der **Hybrid Router** (`app/routers/chat.py`). Seit WP-25 agiert das System als **Agentic Orchestrator**, der Nutzeranfragen analysiert, in parallele Wissens-Streams aufteilt und diese zu einer kontextreichen, wertebasierten Antwort synthetisiert.
|
||||
|
||||
### 1.1 Intent-Erkennung (Logik)
|
||||
### 1.1 Intent-Erkennung (Hybrid-Modus)
|
||||
|
||||
Der Router prüft den Input in drei Stufen (Wasserfall-Prinzip):
|
||||
Der Router nutzt einen **Hybrid-Modus** mit Keyword-Fast-Path und LLM-Slow-Path:
|
||||
|
||||
1. **Question Detection (Regelbasiert):**
|
||||
* Prüfung auf Vorhandensein von `?` oder W-Wörtern (Wer, Wie, Was, Soll ich).
|
||||
* Wenn positiv: **RAG Modus** (Interview wird blockiert).
|
||||
2. **Keyword Scan (Fast Path):**
|
||||
* Lädt `types.yaml` (Objekte) und `decision_engine.yaml` (Handlungen).
|
||||
* Wenn Match (z.B. "Projekt" + "neu"): **INTERVIEW Modus**.
|
||||
3. **LLM Fallback (Slow Path):**
|
||||
* Wenn unklar: Anfrage an LLM zur Klassifizierung mittels `router_prompt`.
|
||||
1. **Keyword Fast-Path (Sofortige Erkennung):**
|
||||
* Prüft `trigger_keywords` aus `decision_engine.yaml` (z.B. "Soll ich", "Wann", "Was ist").
|
||||
* Wenn Match: Sofortige Intent-Zuordnung ohne LLM-Call.
|
||||
* **Strategien:** FACT_WHAT, FACT_WHEN, DECISION, EMPATHY, CODING, INTERVIEW.
|
||||
2. **Type Keywords (Interview-Modus):**
|
||||
* Lädt `types.yaml` und prüft `detection_keywords` für Objekt-Erkennung.
|
||||
* Wenn Match und keine Frage: **INTERVIEW Modus** (Datenerfassung).
|
||||
3. **LLM Slow-Path (Semantische Analyse):**
|
||||
* Wenn unklar: Anfrage an `DecisionEngine._determine_strategy()` zur LLM-basierten Klassifizierung.
|
||||
* Nutzt `intent_router_v1` Prompt aus `prompts.yaml`.
|
||||
|
||||
### 1.2 Prompt-Auflösung (Bulletproof Resolution)
|
||||
|
||||
|
|
@ -34,24 +36,70 @@ Um Kompatibilitätsprobleme mit verschachtelten YAML-Prompts zu vermeiden, nutzt
|
|||
* **Basis-Fallback:** Als letzte Instanz wird das `ollama`-Template geladen.
|
||||
* **String-Garantie:** Die Methode garantiert die Rückgabe eines Strings (selbst bei verschachtelten YAML-Dicts), was 500-Fehler bei String-Operationen wie `.replace()` oder `.format()` verhindert.
|
||||
|
||||
### 1.3 RAG Flow (Technisch)
|
||||
### 1.2 Multi-Stream Retrieval (WP-25)
|
||||
|
||||
Wenn der Intent `FACT` oder `DECISION` ist, wird folgender Flow ausgeführt:
|
||||
Anstelle einer einzelnen Suche führt die `DecisionEngine` nun **parallele Abfragen** in spezialisierten Streams aus:
|
||||
|
||||
1. **Pre-Processing:** Query Rewriting (optional).
|
||||
2. **Context Enrichment:**
|
||||
* Abruf via `retriever.py` (Hybrid Search).
|
||||
* Integration von **Edge Boosts** aus der `decision_engine.yaml` zur Beeinflussung der Graph-Gewichtung.
|
||||
* Injection von Metadaten (`[TYPE]`, `[SCORE]`) in den Prompt.
|
||||
3. **Prompt Construction:** Assembly aus System-Prompt (Persona) + Context + Query.
|
||||
4. **Streaming:** LLM-Antwort wird via **SSE (Server-Sent Events)** an den Client gestreamt.
|
||||
5. **Post-Processing:** Anhängen des `Explanation` Layers (JSON-Breakdown) an das Ende des Streams.
|
||||
**Stream-Library (definiert in `decision_engine.yaml`):**
|
||||
* **Values Stream:** Extrahiert Identität, Ethik und Prinzipien (`value`, `principle`, `belief`, etc.).
|
||||
* **Facts Stream:** Liefert operative Daten zu Projekten, Tasks und Status (`project`, `decision`, `task`, etc.).
|
||||
* **Biography Stream:** Greift auf persönliche Erfahrungen und Journal-Einträge zu (`experience`, `journal`, `profile`).
|
||||
* **Risk Stream:** Identifiziert Hindernisse und potenzielle Gefahren (`risk`, `obstacle`, `bias`).
|
||||
* **Tech Stream:** Bündelt technisches Wissen, Code und Dokumentation (`concept`, `source`, `glossary`, etc.).
|
||||
|
||||
**Stream-Konfiguration:**
|
||||
* Jeder Stream nutzt individuelle **Edge-Boosts** (z.B. `guides: 3.0` für Values Stream).
|
||||
* **Filter-Types** sind strikt mit `types.yaml` (v2.7.0) synchronisiert.
|
||||
* **Query-Templates** transformieren die ursprüngliche Anfrage für spezialisierte Suche.
|
||||
|
||||
**Parallele Ausführung:**
|
||||
* `asyncio.gather()` führt alle aktiven Streams gleichzeitig aus.
|
||||
* **Stream-Tracing:** Jeder Treffer wird mit `stream_origin` markiert für Feedback-Optimierung.
|
||||
* **Fehlerbehandlung:** Einzelne Stream-Fehler blockieren nicht die gesamte Anfrage.
|
||||
|
||||
### 1.3 Wissens-Synthese (WP-25)
|
||||
|
||||
Die Zusammenführung der Daten erfolgt über spezialisierte Templates in der `prompts.yaml`:
|
||||
|
||||
**Template-Struktur:**
|
||||
* Explizite Variablen für jeden Stream (z.B. `{values_stream}`, `{risk_stream}`).
|
||||
* **Pre-Initialization:** Alle möglichen Stream-Variablen werden vorab initialisiert (verhindert KeyErrors).
|
||||
* **Provider-spezifische Templates:** Separate Versionen für Ollama, Gemini und OpenRouter.
|
||||
|
||||
**Synthese-Strategien:**
|
||||
* **FACT_WHAT/FACT_WHEN:** Kombiniert Fakten, Biographie und Technik.
|
||||
* **DECISION:** Wägt Fakten gegen Werte ab, evaluiert Risiken.
|
||||
* **EMPATHY:** Fokus auf Biographie und Werte.
|
||||
* **CODING:** Technik und Fakten.
|
||||
|
||||
### 1.4 RAG Flow (Technisch)
|
||||
|
||||
Wenn der Intent nicht `INTERVIEW` ist, wird folgender Flow ausgeführt:
|
||||
|
||||
1. **Intent Detection:** Hybrid Router klassifiziert die Anfrage.
|
||||
2. **Multi-Stream Retrieval:**
|
||||
* Parallele Abfragen in spezialisierten Streams via `DecisionEngine._execute_parallel_streams()`.
|
||||
* Jeder Stream nutzt individuelle Filter, Edge-Boosts und Query-Templates.
|
||||
3. **Context Formatting:**
|
||||
* Stream-Ergebnisse werden in formatierte Kontext-Strings umgewandelt.
|
||||
* **Ollama Context-Throttling:** Kontext wird auf `MAX_OLLAMA_CHARS` begrenzt (Standard: 10.000).
|
||||
4. **Synthese:**
|
||||
* `DecisionEngine._generate_final_answer()` kombiniert alle Streams.
|
||||
* Template-basierte Prompt-Konstruktion mit Stream-Variablen.
|
||||
5. **Response:**
|
||||
* LLM-Antwort wird generiert (provider-spezifisch).
|
||||
* **Sources:** Alle Treffer aus allen Streams werden dedupliziert und zurückgegeben.
|
||||
|
||||
---
|
||||
|
||||
## 2. LLM Service & Traffic Control (WP-20)
|
||||
## 2. LLM Service & Traffic Control (WP-20 / WP-25)
|
||||
|
||||
Der `LLMService` (`app/services/llm_service.py`) fungiert als zentraler Hybrid-Client für OpenRouter, Google Gemini und Ollama. Er schützt das System vor Überlastung und verwaltet Quoten.
|
||||
Der `LLMService` (`app/services/llm_service.py`, v3.4.2) fungiert als zentraler Hybrid-Client für OpenRouter, Google Gemini und Ollama. Er schützt das System vor Überlastung und verwaltet Quoten.
|
||||
|
||||
**WP-25 Integration:**
|
||||
* **Lazy Initialization:** `DecisionEngine` wird erst bei Bedarf initialisiert (verhindert Circular Imports).
|
||||
* **Ingest-Stability Patch:** Entfernung des <5-Zeichen Guards ermöglicht YES/NO Validierungen beim Vault-Import.
|
||||
* **Empty Response Guard:** Sicherung gegen leere `choices` Arrays bei OpenRouter (verhindert JSON-Errors).
|
||||
|
||||
Mit Version 2.8.1 wurde die Architektur der Antwort-Generierung grundlegend gehärtet:
|
||||
|
||||
|
|
@ -107,10 +155,27 @@ In v2.8 wurde ein intelligentes Fehler-Handling für Cloud-Provider implementier
|
|||
|
||||
---
|
||||
|
||||
## 4. Feedback Traceability
|
||||
## 4. Feedback Traceability & Stream-Tracing (WP-25)
|
||||
|
||||
Unterstützt das geplante Self-Tuning (WP08).
|
||||
Unterstützt das geplante Self-Tuning (WP08) und ermöglicht Stream-spezifische Optimierung.
|
||||
|
||||
1. **Query ID:** Generiert bei jedem `/query` Call eine `UUIDv4`.
|
||||
2. **Logging:** Speichert einen Snapshot in `data/logs/query_snapshot.jsonl` (Input + Retrieved Context).
|
||||
3. **Feedback:** Der `/feedback` Endpoint verknüpft das User-Rating (1-5) mit der `query_id`.
|
||||
1. **Query ID:** Generiert bei jedem `/chat` Call eine `UUIDv4`.
|
||||
2. **Stream-Tracing:** Jeder Treffer enthält `stream_origin` für Zuordnung zum Quell-Stream.
|
||||
3. **Logging:** Speichert einen Snapshot in `data/logs/query_snapshot.jsonl` (Input + Retrieved Context + Intent).
|
||||
4. **Feedback:** Der `/feedback` Endpoint verknüpft das User-Rating (1-5) mit der `query_id` und `stream_origin`.
|
||||
|
||||
## 5. Lifespan Management (WP-25)
|
||||
|
||||
Die FastAPI-Anwendung (`app/main.py`, v1.0.0) implementiert **Lifespan-Management** für sauberen Startup und Shutdown:
|
||||
|
||||
**Startup:**
|
||||
* Integritäts-Check der WP-25 Konfiguration (`decision_engine.yaml`, `prompts.yaml`).
|
||||
* Validierung kritischer Dateien vor dem Start.
|
||||
|
||||
**Shutdown:**
|
||||
* Ressourcen-Cleanup (LLMService-Connections schließen).
|
||||
* Graceful Shutdown für asynchrone Prozesse.
|
||||
|
||||
**Globale Fehlerbehandlung:**
|
||||
* Fängt unerwartete Fehler in der Multi-Stream Kette ab.
|
||||
* Strukturierte JSON-Responses bei Engine-Fehlern.
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
---
|
||||
doc_type: technical_reference
|
||||
audience: developer, admin
|
||||
scope: configuration, env, registry, scoring, resilience, modularization
|
||||
scope: configuration, env, registry, scoring, resilience, modularization, agentic_rag
|
||||
status: active
|
||||
version: 2.9.1
|
||||
context: "Umfassende Referenztabellen für Umgebungsvariablen (inkl. Hybrid-Cloud & WP-76), YAML-Konfigurationen und die Edge Registry Struktur unter Berücksichtigung von WP-14."
|
||||
version: 2.9.3
|
||||
context: "Umfassende Referenztabellen für Umgebungsvariablen (inkl. Hybrid-Cloud & WP-76), YAML-Konfigurationen, Edge Registry Struktur und WP-25 Multi-Stream RAG unter Berücksichtigung von WP-14."
|
||||
---
|
||||
|
||||
# Konfigurations-Referenz
|
||||
|
|
@ -224,15 +224,16 @@ Die Datei muss eine Markdown-Tabelle enthalten, die vom Regex-Parser gelesen wir
|
|||
|
||||
---
|
||||
|
||||
## 5. Decision Engine (`decision_engine.yaml`)
|
||||
## 5. Decision Engine (`decision_engine.yaml` v3.1.6)
|
||||
|
||||
Die Decision Engine fungiert als zentraler Orchestrator für die Intent-Erkennung und das dynamische Retrieval-Routing. Sie bestimmt, wie das System auf eine Nutzeranfrage reagiert, welche Informationstypen bevorzugt werden und wie der Wissensgraph für die spezifische Situation verformt wird.
|
||||
Die Decision Engine fungiert als zentraler **Agentic Orchestrator** für die Intent-Erkennung und das dynamische Multi-Stream Retrieval-Routing (WP-25). Sie bestimmt, wie das System auf eine Nutzeranfrage reagiert, welche Wissens-Streams aktiviert werden und wie die Ergebnisse synthetisiert werden.
|
||||
|
||||
### 5.1 Intent Recognition: Dual-Path Routing
|
||||
Das System nutzt ein zweistufiges Verfahren, um die Absicht des Nutzers zu identifizieren:
|
||||
### 5.1 Intent Recognition: Hybrid-Routing (WP-25)
|
||||
Das System nutzt einen **Hybrid-Modus** mit Keyword Fast-Path und LLM Slow-Path:
|
||||
|
||||
1. **Fast Path (Keyword Trigger):** Das System scannt die Anfrage nach definierten `trigger_keywords`. Wird ein Treffer gefunden, wird die entsprechende Strategie sofort ohne LLM-Einsatz gewählt.
|
||||
2. **Slow Path (LLM Router):** Wenn kein Keyword matcht und `llm_fallback_enabled: true` gesetzt ist, analysiert ein LLM die Nachricht mittels Few-Shot Prompting.
|
||||
1. **Fast Path (Keyword Trigger):** Das System scannt die Anfrage nach definierten `trigger_keywords`. Wird ein Treffer gefunden, wird die entsprechende Strategie sofort ohne LLM-Einsatz gewählt (z.B. "Soll ich" → `DECISION`).
|
||||
2. **Type Keywords:** Prüft `detection_keywords` aus `types.yaml` für Interview-Modus (z.B. "Projekt" + "neu" → `INTERVIEW`).
|
||||
3. **Slow Path (LLM Router):** Wenn kein Keyword matcht und `llm_fallback_enabled: true` gesetzt ist, analysiert ein LLM die Nachricht mittels Few-Shot Prompting (`intent_router_v1`).
|
||||
|
||||
#### LLM Router Konfiguration
|
||||
Der Router nutzt den `llm_router_prompt`, um Anfragen in eine der fünf Kern-Strategien (`FACT`, `DECISION`, `EMPATHY`, `CODING`, `INTERVIEW`) zu klassifizieren.
|
||||
|
|
@ -244,36 +245,90 @@ Der Router nutzt den `llm_router_prompt`, um Anfragen in eine der fünf Kern-Str
|
|||
|
||||
---
|
||||
|
||||
### 5.2 Strategie-Mechaniken (Graph Shaping)
|
||||
Jede Strategie definiert drei Hebel, um das Ergebnis des Retrievers zu beeinflussen:
|
||||
### 5.2 Multi-Stream Konfiguration (WP-25)
|
||||
|
||||
* **`inject_types`:** Erzwingt die Einbindung bestimmter Notiz-Typen (z. B. `value` bei Entscheidungen), auch wenn diese semantisch eine geringere Ähnlichkeit aufweisen.
|
||||
Seit WP-25 nutzt die Decision Engine eine **Stream-Library** mit spezialisierten Wissens-Streams:
|
||||
|
||||
**Stream-Library (`streams_library`):**
|
||||
* **`values_stream`:** Identität, Ethik und Prinzipien (filter_types: `value`, `principle`, `belief`, `trait`, `boundary`, `need`, `motivation`)
|
||||
* **`facts_stream`:** Operative Daten (filter_types: `project`, `decision`, `task`, `goal`, `event`, `state`)
|
||||
* **`biography_stream`:** Persönliche Erfahrungen (filter_types: `experience`, `journal`, `profile`, `person`)
|
||||
* **`risk_stream`:** Hindernisse und Gefahren (filter_types: `risk`, `obstacle`, `bias`)
|
||||
* **`tech_stream`:** Technisches Wissen (filter_types: `concept`, `source`, `glossary`, `idea`, `insight`, `skill`, `habit`)
|
||||
|
||||
**Stream-Parameter:**
|
||||
* **`query_template`:** Transformiert die ursprüngliche Anfrage für spezialisierte Suche (z.B. "Welche meiner Werte und Prinzipien betreffen: {query}")
|
||||
* **`filter_types`:** Strikte Synchronisation mit `types.yaml` (v2.7.0)
|
||||
* **`top_k`:** Anzahl der Treffer pro Stream (z.B. 5 für Values, 3 für Risk)
|
||||
* **`edge_boosts`:** Individuelle Edge-Gewichtung pro Stream (z.B. `guides: 3.0` für Values Stream)
|
||||
|
||||
**Strategie-Komposition (`strategies`):**
|
||||
Jede Strategie definiert, welche Streams aktiviert werden:
|
||||
|
||||
* **`use_streams`:** Liste der Stream-Keys, die parallel abgefragt werden (z.B. `["values_stream", "facts_stream", "risk_stream"]` für `DECISION`)
|
||||
* **`prompt_template`:** Template-Key aus `prompts.yaml` für die Wissens-Synthese (z.B. `decision_synthesis_v1`)
|
||||
* **`prepend_instruction`:** Optional: Zusätzliche Anweisung für das LLM (z.B. "Analysiere die Fakten vor dem Hintergrund meiner Werte")
|
||||
* **`preferred_provider`:** Optional: Provider-Präferenz für diese Strategie (z.B. `gemini` für DECISION)
|
||||
|
||||
### 5.3 Strategie-Mechaniken (Graph Shaping)
|
||||
Jede Strategie definiert mehrere Hebel, um das Ergebnis zu beeinflussen:
|
||||
|
||||
* **`use_streams`:** Aktiviert parallele Wissens-Streams (WP-25).
|
||||
* **`edge_boosts`:** Erhöht die Gewichtung spezifischer Kanten-Typen in der Scoring-Formel. Dies ermöglicht es dem Graphen, die Textsuche situativ zu "überstimmen".
|
||||
* **`prepend_instruction`:** Injiziert eine spezifische Systemanweisung in das LLM-Prompt, um den Antwortstil anzupassen (z. B. "Wäge Fakten gegen Werte ab").
|
||||
|
||||
---
|
||||
|
||||
### 5.3 Übersicht der Strategien
|
||||
### 5.4 Übersicht der Strategien (WP-25)
|
||||
|
||||
| Strategie | Fokus | Bevorzugte Kanten (`edge_boosts`) | Injektionstypen |
|
||||
| Strategie | Fokus | Aktive Streams | Bevorzugte Kanten (`edge_boosts`) |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **FACT** | Wissensabfrage & Definitionen | `part_of` (2.0), `composed_of` (2.0), `similar_to` (1.5) | *(Keine)* |
|
||||
| **DECISION** | Rat, Strategie & Abwägung | `blocks` (2.5), `solves` (2.0), `risk_of` (2.5) | `value`, `principle`, `goal`, `risk` |
|
||||
| **EMPATHY** | Emotionale Resonanz | `based_on` (2.0), `experienced_in` (2.5), `related_to` (2.0) | `experience`, `belief`, `profile` |
|
||||
| **CODING** | Programmierung & Syntax | `implemented_in` (3.0), `uses` (2.5), `depends_on` (2.0) | `snippet`, `reference`, `source` |
|
||||
| **INTERVIEW** | Erfassung neuer Daten | *(Keine)* | *(Keine)* |
|
||||
| **FACT_WHAT** | Wissensabfrage & Listen | `facts_stream`, `tech_stream`, `biography_stream` | `part_of` (2.0), `depends_on` (1.5), `implemented_in` (1.5) |
|
||||
| **FACT_WHEN** | Zeitpunkte & Termine | `facts_stream`, `biography_stream`, `tech_stream` | `part_of` (2.0), `depends_on` (1.5) |
|
||||
| **DECISION** | Rat, Strategie & Abwägung | `values_stream`, `facts_stream`, `risk_stream` | `blocks` (2.5), `impacts` (2.0), `risk_of` (2.5) |
|
||||
| **EMPATHY** | Emotionale Resonanz | `biography_stream`, `values_stream` | `related_to` (1.5), `experienced_in` (2.0) |
|
||||
| **CODING** | Programmierung & Syntax | `tech_stream`, `facts_stream` | `uses` (2.5), `implemented_in` (3.0) |
|
||||
| **INTERVIEW** | Erfassung neuer Daten | *(Keine Streams)* | *(Keine)* |
|
||||
|
||||
---
|
||||
|
||||
### 5.4 Der Interview-Modus & Schemas
|
||||
### 5.5 Der Interview-Modus & Schemas
|
||||
Die Strategie `INTERVIEW` dient der strukturierten Datenerfassung.
|
||||
|
||||
* **Trigger:** Aktiviert durch Phrasen wie "neue notiz", "festhalten" oder "dokumentieren".
|
||||
* **Trigger:** Aktiviert durch Phrasen wie "neue notiz", "festhalten" oder "dokumentieren" (Type Keywords aus `types.yaml`).
|
||||
* **Schema-Logik:** Nutzt das `default`-Schema mit den Feldern `Titel`, `Thema/Inhalt` und `Tags`, sofern kein spezifisches Typ-Schema aus der `types.yaml` greift.
|
||||
* **Dynamik:** In diesem Modus wird der Fokus vom Retrieval (Wissen finden) auf die Extraktion (Wissen speichern) verschoben.
|
||||
* **Streams:** Keine Streams aktiviert (leere `use_streams` Liste).
|
||||
|
||||
> **Hinweis:** Da spezifische Schemas für Projekte oder Erfahrungen direkt in der `types.yaml` definiert werden, dient die `decision_engine.yaml` hier primär als Fallback für generische Datenaufnahmen.
|
||||
|
||||
### 5.6 Prompts-Konfiguration (`prompts.yaml` v3.1.2)
|
||||
|
||||
Seit WP-25 nutzen die Synthese-Templates explizite Stream-Variablen:
|
||||
|
||||
**Template-Struktur:**
|
||||
```yaml
|
||||
decision_synthesis_v1:
|
||||
ollama: |
|
||||
WERTE & PRINZIPIEN (Identität):
|
||||
{values_stream}
|
||||
|
||||
OPERATIVE FAKTEN (Realität):
|
||||
{facts_stream}
|
||||
|
||||
RISIKO-RADAR (Konsequenzen):
|
||||
{risk_stream}
|
||||
|
||||
ENTSCHEIDUNGSFRAGE:
|
||||
{query}
|
||||
```
|
||||
|
||||
**Pre-Initialization:**
|
||||
Alle möglichen Stream-Variablen werden vorab initialisiert (verhindert KeyErrors bei unvollständigen Konfigurationen).
|
||||
|
||||
**Provider-spezifische Templates:**
|
||||
Separate Versionen für Ollama, Gemini und OpenRouter.
|
||||
|
||||
|
||||
Auszug aus der decision_engine.yaml
|
||||
```yaml
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
---
|
||||
doc_type: developer_guide
|
||||
audience: developer
|
||||
scope: workflow, testing, architecture, modules, modularization
|
||||
scope: workflow, testing, architecture, modules, modularization, agentic_rag
|
||||
status: active
|
||||
version: 2.9.1
|
||||
context: "Umfassender Guide für Entwickler: Modularisierte Architektur (WP-14), Two-Pass Ingestion (WP-15b), Modul-Interna, Setup und Git-Workflow."
|
||||
version: 2.9.3
|
||||
context: "Umfassender Guide für Entwickler: Modularisierte Architektur (WP-14), Two-Pass Ingestion (WP-15b), WP-25 Agentic Multi-Stream RAG, Modul-Interna, Setup und Git-Workflow."
|
||||
---
|
||||
|
||||
# Mindnet Developer Guide & Workflow
|
||||
|
|
@ -236,7 +236,7 @@ Das Backend ist das Herzstück. Es stellt die Logik via REST-API bereit.
|
|||
| :--- | :--- | :--- | :--- |
|
||||
| **Entry** | `app/main.py` | 🟢 **Core** | **Entrypoint.** Initialisiert FastAPI, CORS, und bindet alle Router ein. |
|
||||
| **Config** | `app/config.py` | 🟢 **Core** | **Settings.** Zentrale Konfiguration (Pydantic). Lädt Env-Vars für Qdrant, LLM und Pfade. |
|
||||
| **Router** | `app/routers/chat.py` | 🟢 **API** | **Conversation API.** Haupt-Endpunkt für Chat. Entscheidet zwischen Interview- und RAG-Modus. |
|
||||
| **Router** | `app/routers/chat.py` | 🟢 **API** | **Conversation API (WP-25).** Haupt-Endpunkt für Chat. Hybrid Router mit Intent-Erkennung, Multi-Stream Orchestration und Wissens-Synthese. |
|
||||
| | `app/routers/ingest.py` | 🟢 **API** | **Write API.** Nimmt Markdown entgegen, steuert Ingestion und Discovery-Analyse. |
|
||||
| | `app/routers/query.py` | 🟢 **API** | **Search API.** Klassischer Hybrid-Retriever Endpunkt. |
|
||||
| | `app/routers/graph.py` | 🟢 **API** | **Viz API.** Liefert Knoten/Kanten für Frontend-Graphen (Cytoscape). |
|
||||
|
|
@ -393,12 +393,13 @@ Mindnet lernt nicht durch Training (Fine-Tuning), sondern durch **Konfiguration*
|
|||
edge_defaults: ["blocks"] # Automatische Kante
|
||||
detection_keywords: ["gefahr", "risiko"]
|
||||
```
|
||||
2. **Strategie (`config/decision_engine.yaml`):**
|
||||
2. **Strategie (`config/decision_engine.yaml` v3.1.6, WP-25):**
|
||||
```yaml
|
||||
DECISION:
|
||||
inject_types: ["value", "risk"] # <--- "risk" hinzufügen
|
||||
use_streams: ["values_stream", "facts_stream", "risk_stream"] # WP-25: Multi-Stream
|
||||
inject_types: ["value", "risk"] # Legacy: Fallback für nicht-Stream-Typen
|
||||
```
|
||||
*Ergebnis:* Wenn der Intent `DECISION` erkannt wird, sucht das System nun auch aktiv nach Risiken.
|
||||
*Ergebnis (WP-25):* Wenn der Intent `DECISION` erkannt wird, führt das System parallele Abfragen in Values, Facts und Risk Streams aus und synthetisiert die Ergebnisse.
|
||||
|
||||
### Workflow B: Graph-Farben ändern
|
||||
1. Öffne `app/frontend/ui_config.py`.
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ doc_type: developer_guide
|
|||
audience: developer, tester
|
||||
scope: testing, quality_assurance, test_strategies
|
||||
status: active
|
||||
version: 2.9.1
|
||||
context: "Umfassender Test-Guide für Mindnet: Test-Strategien, Test-Frameworks, Test-Daten und Best Practices."
|
||||
version: 2.9.3
|
||||
context: "Umfassender Test-Guide für Mindnet: Test-Strategien, Test-Frameworks, Test-Daten und Best Practices inklusive WP-25 Multi-Stream RAG."
|
||||
---
|
||||
|
||||
# Testing Guide
|
||||
|
|
@ -245,19 +245,29 @@ class TestIngest(unittest.IsolatedAsyncioTestCase):
|
|||
- `tests/test_edges_defaults_smoke.py`
|
||||
- `scripts/edges_full_check.py`
|
||||
|
||||
### 4.4 Chat & Intent-Tests
|
||||
### 4.4 Chat & Intent-Tests (WP-25)
|
||||
|
||||
**Was wird getestet:**
|
||||
- Intent-Erkennung (FACT, DECISION, EMPATHY, INTERVIEW)
|
||||
- Decision Engine
|
||||
- Intent-Erkennung (FACT_WHAT, FACT_WHEN, DECISION, EMPATHY, CODING, INTERVIEW)
|
||||
- Hybrid Router (Keyword Fast-Path + LLM Slow-Path)
|
||||
- Decision Engine (Multi-Stream Orchestration)
|
||||
- Parallele Stream-Abfragen (Values, Facts, Biography, Risk, Tech)
|
||||
- Stream-Tracing (`stream_origin` Markierung)
|
||||
- Wissens-Synthese (Template-basierte Zusammenführung)
|
||||
- Interview-Modus
|
||||
- Feedback-Loop
|
||||
|
||||
**Tests:**
|
||||
- `tests/test_wp06_decision.py`
|
||||
- `tests/test_interview_intent.py`
|
||||
- `tests/test_chat_wp05.py`
|
||||
- `tests/test_feedback_smoke.py`
|
||||
- `tests/test_wp06_decision.py` - Decision Engine (Legacy)
|
||||
- `tests/test_interview_intent.py` - Interview-Modus
|
||||
- `tests/test_chat_wp05.py` - Chat-Backend (Legacy)
|
||||
- `tests/test_feedback_smoke.py` - Feedback-Loop
|
||||
|
||||
**WP-25 Spezifische Tests (geplant):**
|
||||
- Multi-Stream Retrieval (parallele Abfragen)
|
||||
- Stream-Tracing (stream_origin Zuordnung)
|
||||
- Template-Robustheit (Pre-Initialization)
|
||||
- Intent-Kollision (Keyword-Fast-Path Präzision)
|
||||
|
||||
### 4.5 Ingestion-Tests
|
||||
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@
|
|||
doc_type: roadmap
|
||||
audience: product_owner, developer
|
||||
status: active
|
||||
version: 2.9.1
|
||||
context: "Aktuelle Planung für kommende Features (ab WP16), Release-Strategie und Historie der abgeschlossenen WPs nach WP-14/15b."
|
||||
version: 2.9.3
|
||||
context: "Aktuelle Planung für kommende Features (ab WP16), Release-Strategie und Historie der abgeschlossenen WPs nach WP-14/15b/15c/25."
|
||||
---
|
||||
|
||||
# Mindnet Active Roadmap
|
||||
|
||||
**Aktueller Stand:** v2.9.1 (Post-WP14 / WP-15b)
|
||||
**Fokus:** Modularisierung, Two-Pass Ingestion & Graph Intelligence.
|
||||
**Aktueller Stand:** v2.9.3 (Post-WP25: Agentic Multi-Stream RAG)
|
||||
**Fokus:** Agentic Orchestration, Multi-Stream Retrieval & Wissens-Synthese.
|
||||
|
||||
| Phase | Fokus | Status |
|
||||
| :--- | :--- | :--- |
|
||||
|
|
@ -48,6 +48,8 @@ Eine Übersicht der implementierten Features zum schnellen Auffinden von Funktio
|
|||
| **WP-20** | **Cloud Hybrid Mode & Resilienz** | **Ergebnis:** Integration von OpenRouter (Mistral 7B) & Gemini 2.5 Lite. Implementierung von WP-76 (Rate-Limit Wait) & Mistral-safe JSON Parsing. |
|
||||
| **WP-21** | Semantic Graph Routing & Canonical Edges | Transformation des Graphen von statischen Verbindungen zu dynamischen, kontextsensitiven Pfaden. Das System soll verstehen, *welche* Art von Verbindung für die aktuelle Frage relevant ist ("Warum?" vs. "Was kommt danach?"). |
|
||||
| **WP-22** | **Content Lifecycle & Registry** | **Ergebnis:** SSOT via `01_edge_vocabulary.md`, Alias-Mapping, Status-Scoring (`stable`/`draft`) und Modularisierung der Scoring-Engine. |
|
||||
| **WP-15c** | **Multigraph-Support & Diversity Engine** | **Ergebnis:** Section-basierte Links, Note-Level Diversity Pooling, Super-Edge Aggregation, Provenance Firewall. Transformation zu einem hochpräzisen Multigraphen. |
|
||||
| **WP-25** | **Agentic Multi-Stream RAG Orchestration** | **Ergebnis:** Übergang von linearer RAG-Architektur zu paralleler Multi-Stream Engine. Intent-basiertes Routing (Hybrid Fast/Slow-Path), parallele Wissens-Streams (Values, Facts, Biography, Risk, Tech), Stream-Tracing und Template-basierte Wissens-Synthese. |
|
||||
|
||||
### 2.1 WP-22 Lessons Learned
|
||||
* **Architektur:** Die Trennung von `retriever.py` und `retriever_scoring.py` war notwendig, um LLM-Context-Limits zu wahren und die Testbarkeit der mathematischen Formeln zu erhöhen.
|
||||
|
|
@ -191,53 +193,29 @@ Der bisherige WP-15 Ansatz litt unter Halluzinationen (erfundene Kantentypen), h
|
|||
2. **Single Source of Truth (SSOT):** Die Registry nutzt `01_edge_vocabulary.md` als führende Konfiguration.
|
||||
3. **Self-Learning Loop:** Protokollierung unbekannter Kanten in `unknown_edges.jsonl`.
|
||||
|
||||
### WP-23: Agentic Multi-Stream Reasoning (Mindnet 2025)
|
||||
### WP-25: Agentic Multi-Stream RAG Orchestration
|
||||
**Status:** ✅ Fertig (v2.9.3)
|
||||
|
||||
#### 1. Zielsetzung & Problemstellung
|
||||
Das bisherige System basiert auf einem globalen Scoring-Modell, bei dem Notizen unterschiedlicher Typen (z. B. `insight` vs. `belief`) in einem einzigen Retrieval-Topf konkurrieren. Dies führt dazu, dass leiser gewichtete, aber fundamentale Identitätsmerkmale oft durch hochgewichtete aktuelle Erkenntnisse verdrängt werden. Ziel dieses Pakets ist die Einführung einer parallelen **Stream-Architektur**, um die Vielschichtigkeit menschlicher Entscheidungsprozesse (Werte + Erfahrung + Absicht) im LLM-Kontext zu garantieren.
|
||||
**Ergebnis:** Transformation von Mindnet von einer klassischen, linearen RAG-Architektur zu einer **Agentic Multi-Stream Engine**. Das System agiert nun als intelligenter Orchestrator, der Nutzeranfragen analysiert, in parallele Wissens-Streams aufteilt und diese zu einer kontextreichen, wertebasierten Antwort synthetisiert.
|
||||
|
||||
#### 2. Funktionsbeschreibung: Die Streams
|
||||
Die Daten aus der `types.yaml` werden in drei logische Verarbeitungseinheiten unterteilt:
|
||||
**Kern-Features:**
|
||||
1. **Intent-basiertes Routing:** Hybrid-Modus mit Keyword Fast-Path und LLM Slow-Path
|
||||
2. **Multi-Stream Retrieval:** Parallele Abfragen in spezialisierten Streams (Values, Facts, Biography, Risk, Tech)
|
||||
3. **Stream-Tracing:** Jeder Treffer wird mit `stream_origin` markiert
|
||||
4. **Wissens-Synthese:** Template-basierte Zusammenführung mit expliziten Stream-Variablen
|
||||
5. **Fehler-Resilienz:** Einzelne Stream-Fehler blockieren nicht die gesamte Anfrage
|
||||
|
||||
##### A. Identity Stream (Die Wahrheitsebene)
|
||||
* **Inhalt:** `value`, `belief`, `trait`, `principle`, `need`, `boundary`, `bias`.
|
||||
* **Zweck:** Definition des moralischen Kompasses, der psychologischen Grundbedürfnisse und kognitiven Muster.
|
||||
* **Wirkung:** Liefert das "Warum" hinter jeder Handlung.
|
||||
**Technische Details:**
|
||||
- Decision Engine v1.0.3: Multi-Stream Orchestrator
|
||||
- Chat Router v3.0.2: Hybrid Router Integration
|
||||
- LLM Service v3.4.2: Ingest-Stability Patch
|
||||
- decision_engine.yaml v3.1.6: Multi-Stream Konfiguration
|
||||
- prompts.yaml v3.1.2: Stream-Templates
|
||||
|
||||
##### B. History Stream (Die Evidenzebene)
|
||||
* **Inhalt:** `experience`, `event`, `source`, `journal`, `person`.
|
||||
* **Zweck:** Bereitstellung empirischer Belege aus der Vergangenheit und sozialer Kontexte.
|
||||
* **Wirkung:** Verankert die Antwort in real erlebten Mustern und Fakten.
|
||||
|
||||
##### C. Action Stream (Die Dynamikebene)
|
||||
* **Inhalt:** `project`, `decision`, `goal`, `task`, `risk`, `motivation`, `habit`, `state`.
|
||||
* **Zweck:** Analyse der aktuellen Richtung, geplanter Vorhaben und des gegenwärtigen Zustands.
|
||||
* **Wirkung:** Liefert den Kontext für die Umsetzung und zukünftige Ziele.
|
||||
|
||||
|
||||
#### 3. Technische Wirkungsweise (Solution Sketch)
|
||||
|
||||
##### Schritt 1: Query-Decomposition
|
||||
Ein initialer Klassifizierungs-Agent analysiert die Nutzeranfrage und bestimmt, welcher Stream primär angesprochen werden muss (z. B. "Wie soll ich mich entscheiden?" boostet den Identity Stream).
|
||||
|
||||
##### Schritt 2: Parallel Stream Retrieval
|
||||
Anstelle einer Suche werden drei unabhängige Vektor-Suchen mit Typ-Filtern durchgeführt:
|
||||
* **Search_A (Identity):** Top-5 Ergebnisse aus Identitäts-Notizen.
|
||||
* **Search_B (History):** Top-5 Ergebnisse aus biografischen/externen Notizen.
|
||||
* **Search_C (Action):** Top-5 Ergebnisse aus operativen/strategischen Notizen.
|
||||
|
||||
##### Schritt 3: Agentic Synthesis (The Reasoning)
|
||||
Ein Synthese-Agent (LLM) erhält die aggregierten Ergebnisse in getrennten Sektionen. Die Anweisung lautet:
|
||||
1. **Prüfung:** Steht das aktuelle Vorhaben (Action) im Einklang mit den Werten (Identity)?
|
||||
2. **Abgleich:** Welche vergangenen Erfahrungen (History) stützen oder widersprechen diesem Weg?
|
||||
3. **Korrektur:** Identifiziere mögliche Biases oder Grenzüberschreitungen (Boundary).
|
||||
|
||||
|
||||
|
||||
#### 4. Erwartete Ergebnisse
|
||||
* **Höhere Resonanz:** Antworten wirken authentischer, da sie explizit auf das Wertesystem des Nutzers Bezug nehmen.
|
||||
* **Widerspruchs-Erkennung:** Das System kann den Nutzer aktiv warnen, wenn ein Projekt gegen seine `principles` oder `needs` verstößt.
|
||||
* **Robustes Retrieval:** Wichtige Identitäts-Informationen gehen nicht mehr im "Rauschen" von hunderten Journal-Einträgen verloren.
|
||||
**Ausblick (WP-25a):**
|
||||
- Pre-Synthesis: LLM-basierte Komprimierung überlanger Streams
|
||||
- Kontext-Budgeting: Intelligente Token-Verteilung
|
||||
- Stream-specific Provider: Unterschiedliche KI-Modelle pro Wissensbereich
|
||||
---
|
||||
|
||||
### WP-24 – Proactive Discovery & Agentic Knowledge Mining
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Release Notes: Mindnet v2.9.1 (WP15c)
|
||||
# Release Notes: Mindnet v2.9.3 (WP15c)
|
||||
|
||||
**Release Date:** 2025-12-31
|
||||
**Type:** Feature Release - Multigraph & Diversity Engine
|
||||
|
|
|
|||
138
docs/99_Archive/WP25_merge_commit.md
Normal file
138
docs/99_Archive/WP25_merge_commit.md
Normal 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]
|
||||
```
|
||||
268
docs/99_Archive/WP25_release_notes.md
Normal file
268
docs/99_Archive/WP25_release_notes.md
Normal 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)
|
||||
|
|
@ -2,13 +2,13 @@
|
|||
doc_type: documentation_index
|
||||
audience: all
|
||||
status: active
|
||||
version: 2.9.1
|
||||
version: 2.9.3
|
||||
context: "Zentraler Einstiegspunkt für die Mindnet-Dokumentation"
|
||||
---
|
||||
|
||||
# Mindnet Dokumentation
|
||||
|
||||
Willkommen in der Dokumentation von Mindnet v2.9.1! Diese Dokumentation hilft dir dabei, das System zu verstehen, zu nutzen und weiterzuentwickeln.
|
||||
Willkommen in der Dokumentation von Mindnet v2.9.3! Diese Dokumentation hilft dir dabei, das System zu verstehen, zu nutzen und weiterzuentwickeln.
|
||||
|
||||
## 🚀 Schnellstart
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user