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

Reviewed-on: #3
This commit is contained in:
Lars 2025-12-08 16:17:48 +01:00
commit 4f171e5245
20 changed files with 1016 additions and 524 deletions

View File

@ -1,6 +1,6 @@
# mindnet v2.2 — Programmplan
**Version:** 2.2.1 (Post-WP04 Update)
**Stand:** 2025-12-07
**Version:** 2.3.0 (Post-WP05 RAG Integration)
**Stand:** 2025-12-08
**Status:** Aktiv
---
@ -14,7 +14,7 @@ mindnet v2.2 entwickelt ein persönliches, wachsendes KI-Gedächtnis, das:
- einen KI-Zwilling aufbaut, der ähnlich argumentiert, entscheidet und reflektiert wie du,
- über mehrere Kanäle gefüttert wird:
- Obsidian-Markdown (primäre Quelle),
- Chat-basierter Agent (mindnet-Assistent),
- Chat-basierter Agent (RAG-Chat aktiv),
- später: Interview-Assistent (strukturierte Dialogerfassung),
- automatisch neue Zusammenhänge erkennt und vernetzt (Edges, Typen, Hinweise),
- sich durch Rückmeldungen (Feedback) selbst verbessert (Self-Tuning).
@ -31,7 +31,7 @@ Langfristig soll mindnet als **digitales Gegenüber** funktionieren, das:
mindnet ist **kein statisches Wissensarchiv**, sondern ein **lebendes, lernendes Gedächtnismodell** mit Fokus auf:
- persönliche Perspektive,
- erklärbare Begründungspfade,
- erklärbare Begründungspfade (Why-Layer),
- kontinuierliche Erweiterbarkeit,
- robuste technische Basis (lokal, nachvollziehbar, versioniert).
@ -44,13 +44,13 @@ mindnet ist **kein statisches Wissensarchiv**, sondern ein **lebendes, lernendes
Kernprinzipien der Vision:
- **Erklärbarkeit:**
Jede Antwort muss über Edges, Scores, Werte-Bezüge und Herkunftsnotizen begründbar sein.
Jede Antwort muss über Edges, Scores, Werte-Bezüge und Herkunftsnotizen begründbar sein. Das System kann Entscheidungen auf zugrundeliegende `[DECISION]`-Notizen zurückführen.
- **Wachstum:**
Das System arbeitet von Anfang an mit unvollständigen Daten, kann aber schrittweise dichter werden, ohne dass alte Notizen massenhaft manuell angepasst werden müssen.
- **Flexibilität (Late Binding):**
Semantik wird überwiegend in Konfiguration (z. B. `types.yaml`, Policies, Knowledge-Design) festgelegt, nicht in jeder einzelnen Markdown-Datei.
Semantik wird überwiegend in Konfiguration (z. B. `types.yaml`, `prompts.yaml`, Policies) festgelegt. Die Persönlichkeit entsteht durch das Prompt-Design, nicht durch Hardcoding.
- **Autonomie & Self-Healing:**
mindnet schlägt fehlende Typen, Relationen und Edges vor (z. B. aus Inline-Relationen, Edge-Defaults, Ähnlichkeitsbeziehungen) und baut damit einen „self-healing graph“ auf.
@ -59,7 +59,7 @@ Kernprinzipien der Vision:
Feedback zu Antworten (gut/schlecht, relevant/nicht relevant) fließt in Score-Gewichte, Policies und ggf. Edge-Struktur ein.
- **Persönlichkeit:**
Entscheidungen werden wert- und erfahrungsbasiert begründet; mittelfristig entsteht ein Persönlichkeitsmodell, das den KI-Zwilling trägt.
Entscheidungen werden wert- und erfahrungsbasiert begründet; das System agiert als KI-Zwilling durch Nutzung lokaler LLMs (z.B. Phi-3/Mistral).
- **Incremental Growth:**
Das System muss mit wenigen, heterogenen Notizen starten und sich fortlaufend verdichten können ohne Retro-Massenmigrationen im Vault.
@ -68,32 +68,29 @@ Kernprinzipien der Vision:
## 3. Programmziele
### 3.1 Kurzfristig (nächste Monate)
### 3.1 Kurzfristig (Abgeschlossen / Laufend)
- Automatische Sammlung von Wissen aus Markdown-Vault und ersten Chat-Dialogen.
- Automatische Sammlung von Wissen aus Markdown-Vault.
- Aufbau eines stabilen Graph-Speichers in Qdrant (`*_notes`, `*_chunks`, `*_edges`).
- Semantischer und graphbasierter Retriever (WP-04a abgeschlossen).
- **Erklärbarkeit:** Why-Layer liefert Begründungen zu Treffern (WP-04b abgeschlossen).
- **Feedback-Loop:** Systematisches Logging von Suche und Bewertung (WP-04c abgeschlossen).
- **RAG-Chat:** KI antwortet in natürlicher Sprache auf Basis von Wissen und Persönlichkeit (WP-05 abgeschlossen).
- Technische Basis: FastAPI, Qdrant, Ollama (Local LLM).
- Automatisierte Erkennung von Beziehungen:
- Wikilinks, Inline-Relationen, Callout-Edges, Typ-Defaults.
- Schrittweises Lernen über Feedback (Score-Tuning, noch „manuell“ konfiguriert).
- „Mitwachsendes“ Schema ohne Obsidian-Umstrukturierungen:
- Neues Wissen kann sofort erfasst werden,
- bestehende Notizen bleiben gültig (Virtual Schema Layer).
- Technische Basis für die späteren Agenten und den KI-Zwilling:
- lokaler FastAPI-Dienst,
- Qdrant als Graph-Backend,
- konfigurierbarer Retriever (`retriever.yaml`).
### 3.2 Mittelfristig
### 3.2 Mittelfristig (Nächste Schritte)
- Persönlichkeitsprofil wird stabil erkannt und für Antworten genutzt (Typen, Werte-Notizen, Entscheidungsnotizen).
- **RAG-Chat:** KI antwortet in natürlicher Sprache auf Basis von Wissen und Persönlichkeit (WP-05).
- Why-Layer: KI-Assistenz für Entscheidungen mit nachvollziehbarer Argumentation.
- **Decision Engine (WP-06):** Das System berät aktiv bei Entscheidungen, indem es `type: value` und `type: principle` Notizen gegen eine Fragestellung abwägt.
- **Chat Interface (WP-10):** Ablösung der Terminal-Interaktion durch ein Web-Frontend (Streamlit/React) für bessere UX und einfacheres Feedback-Geben.
- Interview-Assistent erstellt neue Notes automatisch (strukturierte Dialoge → Markdown).
- mindnet erzeugt Vorschläge für neue Notes & Edges und bietet einen „Vernetzungs-Assistenten“ für manuell angelegte Notizen.
- Agenten können über MCP-Tools (`mindnet_query`, `mindnet_subgraph`, `mindnet_store_note`, `mindnet_explain`) auf mindnet zugreifen.
- Agenten können über MCP-Tools (`mindnet_query`, `mindnet_chat`) auf mindnet zugreifen.
### 3.3 Langfristig
@ -112,54 +109,53 @@ Kernprinzipien der Vision:
Die folgenden Prinzipien steuern alle Workpackages und Entscheidungen:
1. **Late Binding (späte Semantik)**
- Struktur und Interpretation werden in Konfigurationen (z. B. `types.yaml`, Policies, `knowledge_design.md`) definiert, nicht direkt in den Vault-Dateien.
- Erlaubt spätere Anpassungen ohne manuelle Massenänderungen.
- Struktur und Interpretation werden in Konfigurationen (z. B. `types.yaml`, `prompts.yaml`, Policies) definiert, nicht direkt in den Vault-Dateien.
- Die "Persönlichkeit" des Chats ist ein Prompt-Template, kein Code.
2. **Virtual Schema Layer**
- Typen, Regeln, Policies, Edge-Defaults werden im Schema-Layer beschrieben (z. B. `knowledge_design.md`, `mindnet_functional_architecture.md`, `types.yaml`).
- Typen, Regeln, Policies, Edge-Defaults werden im Schema-Layer beschrieben.
- Markdown-Dateien benötigen nur minimale, robuste Angaben (Titel, Typ, optionale Properties).
3. **Self-Healing Graph**
- Der Graph wird regelmäßig analysiert (Edges, Centrality, fehlende Links).
- Automatisierte Jobs ergänzen fehlende Kanten (ähnliche Inhalte, typisierte Standardbeziehungen, Backlinks).
- Automatisierte Jobs ergänzen fehlende Kanten.
4. **Deterministische IDs**
- Notes, Chunks und Edges erhalten stabile IDs (z. B. UUIDv5, deterministische `note_id`/`chunk_id`/`edge_id`).
- Notes, Chunks und Edges erhalten stabile IDs (z. B. UUIDv5).
- Der Graph ist jederzeit wiederaufbaubar (Import-Pipeline idempotent).
5. **Full Explainability**
- Jeder Score, jede Beziehung, jeder Vorschlag muss über:
- Edges,
- Scoring-Komponenten (Semantik, Typ, Edge-Bonus, Centrality),
- Provenienz (Notizen, Chunks, rule_id, confidence) nachvollziehbar sein.
5. **Full Explainability & Context Enrichment**
- Jeder Score, jede Beziehung muss nachvollziehbar sein.
- Dem LLM werden Metadaten (`[DECISION]`, `[PROJECT]`) injiziert, um Halluzinationen zu vermeiden und logische Schlüsse zu ermöglichen.
6. **Persistence First**
- Obsidian bleibt die primäre Quelle der Wahrheit.
- mindnet ergänzt, schlägt Änderungen vor, schreibt aber nur kontrolliert (z. B. über Rewriter oder Vernetzungs-Assistent mit Freigabe).
- mindnet ergänzt, schlägt Änderungen vor, schreibt aber nur kontrolliert.
7. **Minimalinvasives Schreiben**
- Automatische Veränderungen an Markdown-Dateien erfolgen ausschließlich nach expliziter Zustimmung.
- Rewriter und Vernetzungs-Assistent arbeiten grundsätzlich im Vorschlagsmodus.
8. **Incremental Growth & Early Value**
- Das System muss bereits mit wenigen Notizen und einem kleinen Vault sinnvolle Antworten liefern (Retrieval-MVP).
- Spätere Erweiterungen (Typen, Policies, zusätzliche Edges) dürfen bestehende Notizen nicht „ungültig“ machen, sondern ergänzen sie.
- Feedback-Mechanismen werden früh eingeführt, auch wenn sie anfangs noch nicht vollautomatisch ausgewertet werden.
- Das System muss bereits mit wenigen Notizen und einem kleinen Vault sinnvolle Antworten liefern.
- Feedback-Mechanismen werden früh eingeführt.
9. **Observability & Testbarkeit**
- Jeder Importlauf, jede Retriever-Anfrage und jede Policy-Änderung soll prüfbar sein (Logs, Traces, Tests, Reports).
- Jeder Importlauf, jede Retriever-Anfrage und jede Policy-Änderung soll prüfbar sein.
10. **Local First & Privacy**
- Nutzung lokaler LLMs (Ollama/Phi-3) für Inference. Keine Daten verlassen den Server.
---
## 5. Programmstruktur (Phasenmodell)
Phase A Fundament & Import
Phase B Semantik, Graph & Lernen
Phase C Persönlichkeitsmodell & KI-Zwilling
Phase D Agenten, MCP & Interaktion
Phase A Fundament & Import (Fertig)
Phase B Semantik, Graph & Lernen (Fertig)
Phase C Persönlichkeitsmodell & KI-Zwilling (Laufend)
Phase D Agenten, MCP & Interaktion (Startend)
Phase E Review, Refactoring, Dokumentation
Alle Workpackages sind einer Phase zugeordnet. WP-01 bis WP-04c sind bereits erfolgreich abgeschlossen.
Alle Workpackages sind einer Phase zugeordnet. WP-01 bis WP-05 sind bereits erfolgreich abgeschlossen.
---
@ -236,7 +232,7 @@ Aufbau eines hybriden Retrievers, der Semantik, Typwissen und Graph-Informatione
### WP-04b Explanation Layer ("Why-Layer") (abgeschlossen)
**Phase:** B
**Status:** 🟢 abgeschlossen (Neu)
**Status:** 🟢 abgeschlossen
**Ziel:**
Treffererklärungen liefern, die verständlich machen, warum ein Ergebnis gewählt wurde.
@ -251,7 +247,7 @@ Treffererklärungen liefern, die verständlich machen, warum ein Ergebnis gewäh
### WP-04c Feedback Logging & Bewertungsdaten (abgeschlossen)
**Phase:** B
**Status:** 🟢 abgeschlossen (Neu)
**Status:** 🟢 abgeschlossen
**Ziel:**
Grundlage für Self-Tuning schaffen durch systematisches Logging.
@ -264,19 +260,19 @@ Grundlage für Self-Tuning schaffen durch systematisches Logging.
---
### WP-05 Persönlichkeitsmodell & RAG-Chat (Startbereit)
### WP-05 Persönlichkeitsmodell & RAG-Chat (abgeschlossen)
**Phase:** C
**Status:** 🟡 geplant (Nächster Schritt)
**Status:** 🟢 abgeschlossen
**Ziel:**
Aufbau eines RAG-Systems, das nicht nur sucht, sondern in natürlicher Sprache antwortet und dabei eine definierte Persönlichkeit (Werte, Prinzipien) einnimmt.
Aufbau eines RAG-Systems, das in natürlicher Sprache antwortet und Kontext versteht.
**Umfang:**
- **Config:** `config/prompts.yaml` für System-Prompts, Werte und RAG-Templates.
- **Service:** `llm_service.py` für Text-Generierung (Ollama-Anbindung).
- **Router:** `/chat` Endpoint (Kontext-Assembly -> Prompt -> LLM).
- **Persönlichkeit:** Definition von "Mindnet" als KI-Zwilling (Stil, Haltung).
**Erreichte Ergebnisse:**
- **Infrastruktur:** Integration von Ollama (Phi-3 Mini) in den Service-Layer.
- **Router:** `/chat` Endpoint mit "Hybrid Retrieval Enforcement".
- **Context Enrichment:** Injection von Metadaten (`[TYPE]`, `[SCORE]`) in den Prompt, damit kleine Modelle (SLMs) komplexe Zusammenhänge ("Warum?") verstehen.
- **Config:** `prompts.yaml` steuert System-Prompt und RAG-Template.
**Aufwand / Komplexität:**
- Aufwand: Mittel
@ -284,18 +280,32 @@ Aufbau eines RAG-Systems, das nicht nur sucht, sondern in natürlicher Sprache a
---
### WP-05b Advanced Chat (Optional)
**Phase:** C
**Status:** ⚪ optional
**Ziel:**
Erweiterung des Chats um Gedächtnis (History) und einfache Tools.
**Umfang:**
- Implementierung von `ChatHistory` (vergangene Nachrichten in den Kontext).
- Einfache Tool-Nutzung (z.B. "Suche in Web").
---
### WP-06 Decision Engine (geplant)
**Phase:** C
**Status:** 🟡 geplant
**Status:** 🟡 geplant (Priorität A)
**Ziel:**
Entscheidungsunterstützung auf Basis von Wissen, Persönlichkeit und Zielen inklusive nachvollziehbarer Begründung.
**Umfang:**
- Identifikation typischer Entscheidungssituationen.
- Integration von fachlichem Wissen, Erfahrungen und Persönlichkeitsmodell.
- Ableitung von Entscheidungs-Templates / Heuristiken.
- Erweiterung des RAG-Kontexts um gezieltes Nachladen von `type: value` und `type: principle`.
- Prompt-Engineering für "Trade-off Analyse" (Pro/Contra basierend auf Werten).
- Output-Formatierung als Entscheidungsvorlage.
**Aufwand / Komplexität:**
- Aufwand: Mittel
@ -360,17 +370,18 @@ Sicherstellen, dass bestehende und neue Obsidian-Vaults schrittweise in mindnet
### WP-10 Chat-Interface & Writeback (geplant)
**Phase:** D
**Status:** 🟡 geplant
**Status:** 🟡 geplant (Priorität B - "Quick Win")
**Ziel:**
Chat-basierter mindnet-Agent, der sowohl Fragen beantwortet als auch neue Notizen erzeugen/aktualisieren kann (Writeback Richtung Vault).
Ablösung der Terminal-Interaktion durch ein grafisches Interface.
**Umfang:**
- Chat-Frontend.
- Technologie: Streamlit oder einfaches HTML/JS Frontend.
- Funktionen: Chat-Verlauf, Anzeige der Quellen (mit Scores), Daumen-hoch/runter für WP-04c Feedback.
- Funktionen für Q&A sowie Vorschlag neuer Notes/Edges.
**Aufwand / Komplexität:**
- Aufwand: Hoch
- Aufwand: Hoch (durch Writeback) / Mittel (für reines Frontend)
- Komplexität: Hoch
---
@ -475,7 +486,8 @@ Aufräumen, dokumentieren, stabilisieren insbesondere für Onboarding Dritte
| WP04a | 🟢 |
| WP04b | 🟢 |
| WP04c | 🟢 |
| WP05 | 🟡 |
| WP05 | 🟢 |
| WP05b | ⚪ |
| WP06 | 🟡 |
| WP07 | 🟡 |
| WP08 | 🟡 |
@ -495,6 +507,7 @@ Aufräumen, dokumentieren, stabilisieren insbesondere für Onboarding Dritte
- Jede wesentliche Änderung an Architektur/Schema erhält einen eigenen Commit.
- Jedes Workpackage erhält ein eigenes Chat-Fenster mit dediziertem Prompt (WP-Hand-Over).
- Programmleitung verantwortet Konsistenz und Priorisierung.
- **Modell-Governance:** Das verwendete LLM (z.B. `phi3:mini`) wird in der `.env` und `config.py` festgeschrieben. Updates erfordern Tests gegen die "Why-Layer" Referenzfragen.
---
@ -510,4 +523,4 @@ mindnet v2.2 ist so aufgesetzt, dass:
- langfristig ein **KI-Zwilling** aufgebaut wird, der deine Werte, Erfahrungen und Denkweise spiegelt,
- die technische Architektur (FastAPI, Qdrant, YAML-Policies, MCP-Integration) lokal, nachvollziehbar und erweiterbar bleibt.
Dieser Programmplan bildet die konsolidierte Grundlage (v2.2.1) für alle weiteren Arbeiten.
Dieser Programmplan bildet die konsolidierte Grundlage (v2.3.0) für alle weiteren Arbeiten.

View File

@ -2,16 +2,15 @@
app/config.py zentrale Konfiguration (ENV Settings)
Version:
0.2.0 (WP-04: Retriever-Gewichte & Defaults ergänzt; keine Verhaltensänderung für bestehende Nutzung)
0.3.1 (WP-05: Switch default to Mistral for CPU inference)
Stand:
2025-10-06
Hinweis:
Bestehende Attribute bleiben erhalten; neue WP-04 Felder sind optional.
2025-12-08
"""
from __future__ import annotations
import os
from functools import lru_cache
from pathlib import Path
class Settings:
# Qdrant
@ -20,11 +19,20 @@ class Settings:
COLLECTION_PREFIX: str = os.getenv("MINDNET_PREFIX", "mindnet")
VECTOR_SIZE: int = int(os.getenv("MINDNET_VECTOR_SIZE", "384"))
DISTANCE: str = os.getenv("MINDNET_DISTANCE", "Cosine")
# Embeddings
MODEL_NAME: str = os.getenv("MINDNET_MODEL", "sentence-transformers/all-MiniLM-L6-v2")
# WP-05 LLM / Ollama
OLLAMA_URL: str = os.getenv("MINDNET_OLLAMA_URL", "http://127.0.0.1:11434")
# ÄNDERUNG: Standard auf 'mistral' gesetzt, da bereits lokal vorhanden
LLM_MODEL: str = os.getenv("MINDNET_LLM_MODEL", "phi3:mini")
PROMPTS_PATH: str = os.getenv("MINDNET_PROMPTS_PATH", "config/prompts.yaml")
# API
DEBUG: bool = os.getenv("DEBUG", "false").lower() == "true"
# WP-04 Retriever Defaults (optional; können per ENV überschrieben werden)
# WP-04 Retriever Defaults
RETRIEVER_W_SEM: float = float(os.getenv("MINDNET_WP04_W_SEM", "0.70"))
RETRIEVER_W_EDGE: float = float(os.getenv("MINDNET_WP04_W_EDGE", "0.25"))
RETRIEVER_W_CENT: float = float(os.getenv("MINDNET_WP04_W_CENT", "0.05"))

View File

@ -1,3 +1,9 @@
"""
app/core/retriever.py Hybrider Such-Algorithmus
Version:
0.5.2 (WP-05 Fix: Pass content in QueryHit source)
"""
from __future__ import annotations
import os
@ -22,24 +28,18 @@ import app.core.graph_adapter as ga
try:
import yaml # type: ignore[import]
except Exception: # pragma: no cover - Fallback, falls PyYAML nicht installiert ist
except Exception: # pragma: no cover
yaml = None # type: ignore[assignment]
@lru_cache
def _get_scoring_weights() -> Tuple[float, float, float]:
"""Liefert (semantic_weight, edge_weight, centrality_weight) für den Retriever.
Priorität:
1. Werte aus config/retriever.yaml (falls vorhanden und gültig).
2. Fallback auf Settings.RETRIEVER_W_* (ENV-basiert).
"""
"""Liefert (semantic_weight, edge_weight, centrality_weight) für den Retriever."""
settings = get_settings()
sem = float(getattr(settings, "RETRIEVER_W_SEM", 1.0))
edge = float(getattr(settings, "RETRIEVER_W_EDGE", 0.0))
cent = float(getattr(settings, "RETRIEVER_W_CENT", 0.0))
# YAML-Override, falls konfiguriert
config_path = os.getenv("MINDNET_RETRIEVER_CONFIG", "config/retriever.yaml")
if yaml is None:
return sem, edge, cent
@ -52,22 +52,19 @@ def _get_scoring_weights() -> Tuple[float, float, float]:
edge = float(scoring.get("edge_weight", edge))
cent = float(scoring.get("centrality_weight", cent))
except Exception:
# Bei Fehlern in der YAML-Konfiguration defensiv auf Defaults zurückfallen
return sem, edge, cent
return sem, edge, cent
def _get_client_and_prefix() -> Tuple[Any, str]:
"""Liefert (QdrantClient, prefix) basierend auf QdrantConfig.from_env()."""
"""Liefert (QdrantClient, prefix)."""
cfg = qdr.QdrantConfig.from_env()
client = qdr.get_client(cfg)
return client, cfg.prefix
def _get_query_vector(req: QueryRequest) -> List[float]:
"""
Liefert den Query-Vektor aus dem Request.
"""
"""Liefert den Query-Vektor aus dem Request."""
if req.query_vector:
return list(req.query_vector)
@ -78,9 +75,9 @@ def _get_query_vector(req: QueryRequest) -> List[float]:
model_name = settings.MODEL_NAME
try:
return ec.embed_text(req.query, model_name=model_name) # type: ignore[call-arg]
return ec.embed_text(req.query, model_name=model_name)
except TypeError:
return ec.embed_text(req.query) # type: ignore[call-arg]
return ec.embed_text(req.query)
def _semantic_hits(
@ -90,7 +87,7 @@ def _semantic_hits(
top_k: int,
filters: Dict[str, Any] | None = None,
) -> List[Tuple[str, float, Dict[str, Any]]]:
"""Führt eine semantische Suche über mindnet_chunks aus und liefert Roh-Treffer."""
"""Führt eine semantische Suche aus."""
flt = filters or None
raw_hits = qp.search_chunks_by_vector(client, prefix, vector, top=top_k, filters=flt)
results: List[Tuple[str, float, Dict[str, Any]]] = []
@ -105,7 +102,7 @@ def _compute_total_score(
edge_bonus: float = 0.0,
cent_bonus: float = 0.0,
) -> Tuple[float, float, float]:
"""Berechnet total_score aus semantic_score, retriever_weight und Graph-Boni."""
"""Berechnet total_score."""
raw_weight = payload.get("retriever_weight", 1.0)
try:
weight = float(raw_weight)
@ -129,10 +126,7 @@ def _build_explanation(
subgraph: Optional[ga.Subgraph],
node_key: Optional[str]
) -> Explanation:
"""
Erstellt ein detailliertes Explanation-Objekt für einen Treffer.
Analysiert Scores, Typen und Kanten (Incoming & Outgoing).
"""
"""Erstellt ein Explanation-Objekt."""
sem_w, edge_w, cent_w = _get_scoring_weights()
try:
@ -155,100 +149,49 @@ def _build_explanation(
reasons: List[Reason] = []
edges_dto: List[EdgeDTO] = []
# 1. Semantische Gründe
if semantic_score > 0.85:
reasons.append(Reason(
kind="semantic",
message="Sehr hohe textuelle Übereinstimmung.",
score_impact=breakdown.semantic_contribution
))
reasons.append(Reason(kind="semantic", message="Sehr hohe textuelle Übereinstimmung.", score_impact=breakdown.semantic_contribution))
elif semantic_score > 0.70:
reasons.append(Reason(
kind="semantic",
message="Gute textuelle Übereinstimmung.",
score_impact=breakdown.semantic_contribution
))
reasons.append(Reason(kind="semantic", message="Gute textuelle Übereinstimmung.", score_impact=breakdown.semantic_contribution))
# 2. Typ-Gründe
if type_weight != 1.0:
msg = "Bevorzugt" if type_weight > 1.0 else "Leicht abgewertet"
reasons.append(Reason(
kind="type",
message=f"{msg} aufgrund des Typs '{note_type}' (Gewicht: {type_weight}).",
score_impact=(sem_w * semantic_score * (type_weight - 1.0))
))
reasons.append(Reason(kind="type", message=f"{msg} aufgrund des Typs '{note_type}'.", score_impact=(sem_w * semantic_score * (type_weight - 1.0))))
# 3. Graph-Gründe (Edges)
if subgraph and node_key and edge_bonus > 0:
# Wir sammeln die stärksten Kanten (egal ob rein oder raus),
# die zum Score beitragen.
# A) Outgoing (Ich verweise auf...) - Das ist oft der Hub-Score
if hasattr(subgraph, "get_outgoing_edges"):
outgoing = subgraph.get_outgoing_edges(node_key)
for edge in outgoing:
target = edge.get("target", "Unknown")
kind = edge.get("kind", "edge")
weight = edge.get("weight", 0.0)
# Nur relevante Kanten aufnehmen
if weight > 0.05:
edges_dto.append(EdgeDTO(
id=f"{node_key}->{target}:{kind}",
kind=kind, source=node_key, target=target, weight=weight, direction="out"
))
edges_dto.append(EdgeDTO(id=f"{node_key}->{target}:{kind}", kind=kind, source=node_key, target=target, weight=weight, direction="out"))
# B) Incoming (Ich werde verwiesen von...)
if hasattr(subgraph, "get_incoming_edges"):
incoming = subgraph.get_incoming_edges(node_key)
for edge in incoming:
src = edge.get("source", "Unknown")
kind = edge.get("kind", "edge")
weight = edge.get("weight", 0.0)
if weight > 0.05:
edges_dto.append(EdgeDTO(
id=f"{src}->{node_key}:{kind}",
kind=kind, source=src, target=node_key, weight=weight, direction="in"
))
edges_dto.append(EdgeDTO(id=f"{src}->{node_key}:{kind}", kind=kind, source=src, target=node_key, weight=weight, direction="in"))
# Sortieren nach Gewicht und Top-3 als Reasons generieren
all_edges = sorted(edges_dto, key=lambda e: e.weight, reverse=True)
for top_edge in all_edges[:3]:
# Impact schätzen (grob, da Edge-Bonus eine Summe ist)
impact = edge_w * top_edge.weight
dir_txt = "Verweist auf" if top_edge.direction == "out" else "Referenziert von"
tgt_txt = top_edge.target if top_edge.direction == "out" else top_edge.source
reasons.append(Reason(kind="edge", message=f"{dir_txt} '{tgt_txt}' via '{top_edge.kind}'", score_impact=impact, details={"kind": top_edge.kind}))
if top_edge.direction == "out":
msg = f"Verweist auf '{top_edge.target}' via '{top_edge.kind}'"
else:
msg = f"Referenziert von '{top_edge.source}' via '{top_edge.kind}'"
reasons.append(Reason(
kind="edge",
message=msg,
score_impact=impact,
details={"kind": top_edge.kind, "weight": top_edge.weight}
))
# 4. Centrality
if cent_bonus > 0.01:
reasons.append(Reason(
kind="centrality",
message="Knoten liegt zentral im Kontext.",
score_impact=breakdown.centrality_contribution
))
reasons.append(Reason(kind="centrality", message="Knoten liegt zentral im Kontext.", score_impact=breakdown.centrality_contribution))
return Explanation(
breakdown=breakdown,
reasons=reasons,
related_edges=edges_dto if edges_dto else None
)
# --- End Explanation Logic ---
return Explanation(breakdown=breakdown, reasons=reasons, related_edges=edges_dto if edges_dto else None)
def _extract_expand_options(req: QueryRequest) -> Tuple[int, List[str] | None]:
"""Extrahiert depth und edge_types aus req.expand."""
"""Extrahiert depth und edge_types."""
expand = getattr(req, "expand", None)
if not expand:
return 0, None
@ -278,14 +221,10 @@ def _build_hits_from_semantic(
top_k: int,
used_mode: str,
subgraph: ga.Subgraph | None = None,
explain: bool = False, # WP-04b
explain: bool = False,
) -> QueryResponse:
"""Baut aus Raw-Hits und optionalem Subgraph strukturierte QueryHits.
WP-04b: Wenn explain=True, wird _build_explanation aufgerufen.
"""
"""Baut strukturierte QueryHits."""
t0 = time.time()
enriched: List[Tuple[str, float, Dict[str, Any], float, float, float]] = []
for pid, semantic_score, payload in hits:
@ -303,26 +242,14 @@ def _build_hits_from_semantic(
except Exception:
cent_bonus = 0.0
total, edge_bonus, cent_bonus = _compute_total_score(
semantic_score,
payload,
edge_bonus=edge_bonus,
cent_bonus=cent_bonus,
)
total, edge_bonus, cent_bonus = _compute_total_score(semantic_score, payload, edge_bonus=edge_bonus, cent_bonus=cent_bonus)
enriched.append((pid, float(semantic_score), payload, total, edge_bonus, cent_bonus))
# Sortierung nach total_score absteigend
enriched_sorted = sorted(enriched, key=lambda h: h[3], reverse=True)
limited = enriched_sorted[: max(1, top_k)]
results: List[QueryHit] = []
for pid, semantic_score, payload, total, edge_bonus, cent_bonus in limited:
note_id = payload.get("note_id")
path = payload.get("path")
section = payload.get("section") or payload.get("section_title")
node_key = payload.get("chunk_id") or payload.get("note_id")
# WP-04b: Explanation bauen?
explanation_obj = None
if explain:
explanation_obj = _build_explanation(
@ -331,59 +258,51 @@ def _build_hits_from_semantic(
edge_bonus=edge_bonus,
cent_bonus=cent_bonus,
subgraph=subgraph,
node_key=node_key
node_key=payload.get("chunk_id") or payload.get("note_id")
)
results.append(
QueryHit(
node_id=str(pid),
note_id=note_id,
semantic_score=float(semantic_score),
edge_bonus=edge_bonus,
centrality_bonus=cent_bonus,
total_score=total,
paths=None,
source={
"path": path,
"section": section,
},
explanation=explanation_obj
)
)
# FIX: Hier holen wir jetzt den Textinhalt (text, content oder page_content) aus dem Payload
text_content = payload.get("page_content") or payload.get("text") or payload.get("content")
results.append(QueryHit(
node_id=str(pid),
note_id=payload.get("note_id"),
semantic_score=float(semantic_score),
edge_bonus=edge_bonus,
centrality_bonus=cent_bonus,
total_score=total,
paths=None,
source={
"path": payload.get("path"),
"section": payload.get("section") or payload.get("section_title"),
"text": text_content # WICHTIG: Inhalt durchreichen
},
explanation=explanation_obj
))
dt = int((time.time() - t0) * 1000)
return QueryResponse(results=results, used_mode=used_mode, latency_ms=dt)
def semantic_retrieve(req: QueryRequest) -> QueryResponse:
"""Reiner semantischer Retriever (ohne Edge-Expansion)."""
"""Reiner semantischer Retriever."""
client, prefix = _get_client_and_prefix()
vector = _get_query_vector(req)
top_k = req.top_k or get_settings().RETRIEVER_TOP_K
hits = _semantic_hits(client, prefix, vector, top_k=top_k, filters=req.filters)
# explain Flag durchreichen
return _build_hits_from_semantic(
hits,
top_k=top_k,
used_mode="semantic",
subgraph=None,
explain=req.explain
)
return _build_hits_from_semantic(hits, top_k=top_k, used_mode="semantic", subgraph=None, explain=req.explain)
def hybrid_retrieve(req: QueryRequest) -> QueryResponse:
"""Hybrid-Retriever: semantische Suche + optionale Edge-Expansion."""
client, prefix = _get_client_and_prefix()
if req.query_vector:
vector = list(req.query_vector)
else:
vector = _get_query_vector(req)
top_k = req.top_k or get_settings().RETRIEVER_TOP_K
hits = _semantic_hits(client, prefix, vector, top_k=top_k, filters=req.filters)
depth, edge_types = _extract_expand_options(req)
@ -394,18 +313,21 @@ def hybrid_retrieve(req: QueryRequest) -> QueryResponse:
key = payload.get("chunk_id") or payload.get("note_id")
if key and key not in seed_ids:
seed_ids.append(key)
if seed_ids:
try:
subgraph = ga.expand(client, prefix, seed_ids, depth=depth, edge_types=edge_types)
except Exception:
subgraph = None
# explain Flag durchreichen
return _build_hits_from_semantic(
hits,
top_k=top_k,
used_mode="hybrid",
subgraph=subgraph,
explain=req.explain
)
return _build_hits_from_semantic(hits, top_k=top_k, used_mode="hybrid", subgraph=subgraph, explain=req.explain)
class Retriever:
"""
Wrapper-Klasse für WP-05 (Chat).
"""
def __init__(self):
pass
async def search(self, request: QueryRequest) -> QueryResponse:
return hybrid_retrieve(request)

View File

@ -10,8 +10,9 @@ from .routers.qdrant_router import router as qdrant_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
# NEU: Feedback Router
from .routers.feedback import router as feedback_router
# NEU: Chat Router (WP-05)
from .routers.chat import router as chat_router
try:
from .routers.admin import router as admin_router
@ -19,7 +20,7 @@ except Exception:
admin_router = None
def create_app() -> FastAPI:
app = FastAPI(title="mindnet API", version="0.4.3") # Version bump
app = FastAPI(title="mindnet API", version="0.5.0") # Version bump WP-05
s = get_settings()
@app.get("/healthz")
@ -32,9 +33,11 @@ def create_app() -> FastAPI:
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"])
# NEU:
app.include_router(feedback_router, prefix="/feedback", tags=["feedback"])
# NEU: Chat Endpoint
app.include_router(chat_router, prefix="/chat", tags=["chat"])
if admin_router:
app.include_router(admin_router, prefix="/admin", tags=["admin"])

View File

@ -1,17 +1,14 @@
"""
app/models/dto.py Pydantic-Modelle (DTOs) für WP-04 Endpunkte
app/models/dto.py Pydantic-Modelle (DTOs) für WP-04/WP-05 Endpunkte
Zweck:
Laufzeit-Modelle für FastAPI (Requests/Responses), getrennt von JSON-Schemas.
Deckt die Graph-/Retriever-Endpunkte ab.
Enthält Erweiterungen für WP-04b (Explanation Layer) und WP-04c (Feedback).
Laufzeit-Modelle für FastAPI (Requests/Responses).
WP-05 Update: Chat-Modelle.
Kompatibilität:
Python 3.12+, Pydantic 2.x, FastAPI 0.110+
Version:
0.3.0 (Update für WP-04c Feedback)
0.4.0 (Update für WP-05 Chat)
Stand:
2025-12-07
2025-12-08
"""
from __future__ import annotations
@ -42,7 +39,7 @@ class NodeDTO(BaseModel):
class EdgeDTO(BaseModel):
"""Darstellung einer Kante im API-Graph."""
id: str
kind: str # String statt Literal, um flexibel für Custom-Types zu bleiben
kind: str
source: str
target: str
weight: float
@ -53,11 +50,7 @@ class EdgeDTO(BaseModel):
class QueryRequest(BaseModel):
"""
Request für /query:
- mode: 'semantic' | 'edge' | 'hybrid'
- query: (optional) Freitext
- query_vector: (optional) direkter Vektor
- explain: (optional) Fordert detaillierte Erklärungen an (WP-04b)
Request für /query.
"""
mode: Literal["semantic", "edge", "hybrid"] = "hybrid"
query: Optional[str] = None
@ -71,7 +64,7 @@ class QueryRequest(BaseModel):
class FeedbackRequest(BaseModel):
"""
User-Feedback zu einem spezifischen Treffer (WP-04c).
User-Feedback zu einem spezifischen Treffer.
"""
query_id: str = Field(..., description="ID der ursprünglichen Suche")
node_id: str = Field(..., description="ID des bewerteten Treffers")
@ -79,19 +72,28 @@ class FeedbackRequest(BaseModel):
comment: Optional[str] = None
class ChatRequest(BaseModel):
"""
WP-05: Request für /chat.
"""
message: str = Field(..., description="Die Nachricht des Users")
conversation_id: Optional[str] = Field(None, description="Optional: ID für Chat-Verlauf (noch nicht implementiert)")
# RAG Parameter (Override defaults)
top_k: int = 5
explain: bool = False
# --- WP-04b Explanation Models ---
class ScoreBreakdown(BaseModel):
"""Aufschlüsselung der Score-Komponenten."""
semantic_contribution: float = Field(..., description="W_sem * semantic_score * weight")
edge_contribution: float = Field(..., description="W_edge * edge_bonus")
centrality_contribution: float = Field(..., description="W_cent * centrality_bonus")
# Rohwerte
semantic_contribution: float
edge_contribution: float
centrality_contribution: float
raw_semantic: float
raw_edge_bonus: float
raw_centrality: float
node_weight: float = Field(..., description="Typ-Gewicht (retriever_weight)")
node_weight: float
class Reason(BaseModel):
@ -115,25 +117,17 @@ class QueryHit(BaseModel):
"""Einzelnes Trefferobjekt für /query."""
node_id: str
note_id: Optional[str]
# Flache Scores
semantic_score: float
edge_bonus: float
centrality_bonus: float
total_score: float
paths: Optional[List[List[Dict]]] = None
source: Optional[Dict] = None
# WP-04b: Erklärungsobjekt
explanation: Optional[Explanation] = None
class QueryResponse(BaseModel):
"""
Antwortstruktur für /query (Liste von Treffern + Telemetrie).
Enthält query_id für Traceability (WP-04c).
"""
"""Antwortstruktur für /query."""
query_id: str = Field(default_factory=lambda: str(uuid.uuid4()))
results: List[QueryHit]
used_mode: str
@ -146,3 +140,13 @@ class GraphResponse(BaseModel):
nodes: List[NodeDTO]
edges: List[EdgeDTO]
stats: Dict[str, int]
class ChatResponse(BaseModel):
"""
WP-05: Antwortstruktur für /chat.
"""
query_id: str = Field(..., description="Traceability ID (dieselbe wie für Search)")
answer: str = Field(..., description="Generierte Antwort vom LLM")
sources: List[QueryHit] = Field(..., description="Die für die Antwort genutzten Quellen")
latency_ms: int

114
app/routers/chat.py Normal file
View File

@ -0,0 +1,114 @@
"""
app/routers/chat.py RAG Endpunkt (WP-05 Final Audit Version)
Zweck:
Verbindet Retrieval mit LLM-Generation.
Enriched Context: Fügt Typen und Metadaten in den Prompt ein,
damit das LLM komplexe Zusammenhänge (z.B. Decisions) versteht.
"""
from fastapi import APIRouter, HTTPException, Depends
from typing import List
import time
import uuid
import logging
from app.models.dto import ChatRequest, ChatResponse, QueryRequest, QueryHit
from app.services.llm_service import LLMService
from app.core.retriever import Retriever
router = APIRouter()
logger = logging.getLogger(__name__)
def get_llm_service():
return LLMService()
def get_retriever():
return Retriever()
def _build_enriched_context(hits: List[QueryHit]) -> str:
"""
Baut einen 'Rich Context' String.
Statt nur Text, injizieren wir Metadaten (Typ, Tags), damit das LLM
die semantische Rolle des Schnipsels versteht.
"""
context_parts = []
for i, hit in enumerate(hits, 1):
source = hit.source or {}
# 1. Content extrahieren (Robust: prüft alle üblichen Felder)
content = (
source.get("text") or
source.get("content") or
source.get("page_content") or
source.get("chunk_text") or
"[Kein Textinhalt verfügbar]"
)
# 2. Metadaten für "Context Intelligence"
title = hit.note_id or "Unbekannte Notiz"
# Typ in Großbuchstaben (z.B. "DECISION"), damit das LLM es als Signal erkennt
note_type = source.get("type", "unknown").upper()
# 3. Formatierung als strukturiertes Dokument für das LLM
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)
@router.post("/", response_model=ChatResponse)
async def chat_endpoint(
request: ChatRequest,
llm: LLMService = Depends(get_llm_service),
retriever: Retriever = Depends(get_retriever)
):
start_time = time.time()
query_id = str(uuid.uuid4())
logger.info(f"Chat request [{query_id}]: {request.message[:50]}...")
try:
# 1. Retrieval (Hybrid erzwingen für Graph-Nutzung)
query_req = QueryRequest(
query=request.message,
mode="hybrid", # WICHTIG: Hybrid Mode für Graph-Nachbarn
top_k=request.top_k,
explain=request.explain
)
retrieve_result = await retriever.search(query_req)
hits = retrieve_result.results
# 2. Context Building (Enriched)
if not hits:
logger.info(f"[{query_id}] No hits found.")
context_str = "Keine relevanten Notizen gefunden."
else:
context_str = _build_enriched_context(hits)
# 3. Generation
logger.info(f"[{query_id}] Context built with {len(hits)} chunks. Sending to LLM...")
answer_text = await llm.generate_rag_response(
query=request.message,
context_str=context_str
)
# 4. Response
duration_ms = int((time.time() - start_time) * 1000)
logger.info(f"[{query_id}] Completed in {duration_ms}ms")
return ChatResponse(
query_id=retrieve_result.query_id,
answer=answer_text,
sources=hits,
latency_ms=duration_ms
)
except Exception as e:
logger.error(f"Error in chat endpoint: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=str(e))

View File

@ -0,0 +1,83 @@
"""
app/services/llm_service.py LLM Client (Ollama)
Version:
0.1.2 (WP-05 Fix: Increased Timeout for CPU Inference)
"""
import httpx
import yaml
import logging
import os
from pathlib import Path
from app.config import get_settings
logger = logging.getLogger(__name__)
class LLMService:
def __init__(self):
self.settings = get_settings()
self.prompts = self._load_prompts()
# FIX: Timeout auf 120 Sekunden erhöht für CPU-Only Server
self.client = httpx.AsyncClient(base_url=self.settings.OLLAMA_URL, timeout=120.0)
def _load_prompts(self) -> dict:
"""Lädt Prompts aus der konfigurierten YAML-Datei."""
path = Path(self.settings.PROMPTS_PATH)
if not path.exists():
logger.warning(f"Prompt config not found at {path}, using defaults.")
return {
"system_prompt": "You are a helpful AI assistant.",
"rag_template": "Context: {context_str}\nQuestion: {query}"
}
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 prompts: {e}")
return {}
async def generate_rag_response(self, query: str, context_str: str) -> str:
"""
Generiert eine Antwort basierend auf Query und Kontext.
"""
system_prompt = self.prompts.get("system_prompt", "")
template = self.prompts.get("rag_template", "{context_str}\n\n{query}")
# Template füllen
final_prompt = template.format(context_str=context_str, query=query)
payload = {
"model": self.settings.LLM_MODEL,
"system": system_prompt,
"prompt": final_prompt,
"stream": False,
"options": {
"temperature": 0.7,
# Kleinerer Context spart Rechenzeit, falls 4096 zu viel ist
"num_ctx": 2048
}
}
try:
response = await self.client.post("/api/generate", json=payload)
if response.status_code != 200:
error_msg = response.text
logger.error(f"Ollama API Error ({response.status_code}): {error_msg}")
return f"Fehler vom LLM (Modell '{self.settings.LLM_MODEL}' vorhanden?): {error_msg}"
data = response.json()
return data.get("response", "")
except httpx.ReadTimeout:
return "Timeout: Das Modell braucht zu lange zum Antworten (>120s). Hardware-Limit erreicht?"
except httpx.ConnectError:
return "Verbindungsfehler: Ist Ollama gestartet (Port 11434)?"
except Exception as e:
logger.error(f"LLM Service Exception: {e}")
return f"Interner Fehler: {str(e)}"
async def close(self):
await self.client.aclose()

24
config/prompts.yaml Normal file
View File

@ -0,0 +1,24 @@
# config/prompts.yaml — Final V2.1
# Optimiert für Phi-3 Mini (Small Language Model)
system_prompt: |
Du bist 'mindnet', das KI-Gedächtnis.
DEINE REGELN:
1. Deine Antwort muss zu 100% auf dem KONTEXT basieren. Erfinde nichts.
2. Wenn eine Quelle den Typ [DECISION] hat, ist sie die wichtigste Quelle für das "Warum".
3. Nenne konkrete technische Details aus dem Text (z.B. genannte Features, Gründe), statt nur allgemein zu antworten.
4. Antworte auf Deutsch.
rag_template: |
QUELLEN (WISSEN):
=========================================
{context_str}
=========================================
FRAGE:
{query}
ANWEISUNG:
Beantworte die Frage basierend auf den Quellen.
Nenne die spezifischen Gründe, die im Text stehen (besonders aus [DECISION] Quellen).

32
config/prompts.yaml_old Normal file
View File

@ -0,0 +1,32 @@
# config/prompts.yaml — Persönlichkeit & RAG-Strategie
# Version: 2.0 (Audit Update)
system_prompt: |
Du bist 'mindnet', ein persönliches KI-Gedächtnis und der Digitale Zwilling deines Erschaffers ("User").
DEINE PERSÖNLICHKEIT & WERTE:
1. Pragmatismus: Du bevorzugst funktionierende Lösungen über theoretische Perfektion.
2. Transparenz: Du erfindest keine Fakten. Wenn Informationen im Kontext fehlen, sagst du das klar.
3. Vernetztes Denken: Du suchst aktiv nach Verbindungen zwischen den bereitgestellten Notizen.
4. Erklärbarkeit: Wenn du eine Aussage machst, beziehst du dich implizit auf die Quelle (z.B. "Wie in Projekt X definiert...").
DEINE AUFGABE:
Beantworte die Frage des Users ausschließlich basierend auf dem untenstehenden KONTEXT.
Der Kontext besteht aus Auszügen verschiedener Notizen. Achte besonders auf den [TYPE] der Notiz:
- [DECISION] erklärt das "Warum".
- [PROJECT] erklärt das "Was" und "Wann".
- [CONCEPT] liefert Definitionen.
- [VALUE] definiert die moralische/strategische Ausrichtung.
rag_template: |
HINTERGRUNDWISSEN (KONTEXT):
=========================================
{context_str}
=========================================
FRAGE DES USERS:
{query}
ANWEISUNG:
Analysiere die Quellen oben. Synthetisiere eine Antwort, die die Frage präzise beantwortet.
Nutze Markdown für Struktur (Fettgedrucktes für Wichtiges).

24
config/prompts.yaml_old2 Normal file
View File

@ -0,0 +1,24 @@
system_prompt: |
Du bist 'mindnet', ein persönliches KI-Gedächtnis.
DEINE REGELN:
1. Antworte NUR basierend auf dem untenstehenden KONTEXT.
2. Achte auf den TYP der Quelle:
- [DECISION] enthält Begründungen (Warum?).
- [PROJECT] enthält Ziele (Was?).
- [CONCEPT] enthält Definitionen.
3. Sei präzise. Nenne konkrete technische Gründe, wenn sie im Text stehen.
4. Halluziniere nicht.
rag_template: |
KONTEXT (WISSEN):
=========================================
{context_str}
=========================================
FRAGE:
{query}
ANWEISUNG:
Analysiere den Kontext. Wenn eine [DECISION] Quelle dabei ist, nutze deren Inhalt für die Begründung.
Antworte kurz und präzise auf Deutsch.

View File

@ -1,8 +1,8 @@
# mindnet v2.2 Knowledge Design Manual
**Datei:** `docs/mindnet_knowledge_design_manual_v2.2.md`
**Stand:** 2025-12-07
**Status:** **FINAL** (Konsolidiert aus WP01WP04a)
**Quellen:** `knowledge_design.md`, `TYPE_REGISTRY_MANUAL.md`, `chunking_strategy.md`, `mindnet_functional_architecture.md`, `WP03/WP04 Hinweise`.
**Stand:** 2025-12-08
**Status:** **FINAL** (Integrierter Stand WP01WP05)
**Quellen:** `knowledge_design.md`, `TYPE_REGISTRY_MANUAL.md`, `chunking_strategy.md`, `mindnet_functional_architecture.md`.
---
@ -11,7 +11,10 @@
Dieses Handbuch ist die **primäre Arbeitsanweisung** für dich als Mindmaster (Owner) und alle Autoren, die Inhalte im mindnet-Vault erstellen.
### 1.1 Zielsetzung
Mindnet ist mehr als eine Dokumentablage. Es ist ein vernetztes System, das deine Persönlichkeit, Entscheidungen und Erfahrungen abbildet. Damit der **Retriever** (die Suchmaschine) und spätere **KI-Agenten** dieses Wissen nutzen können, müssen Notizen einer klaren Struktur folgen.
Mindnet ist mehr als eine Dokumentablage. Es ist ein vernetztes System, das deine Persönlichkeit, Entscheidungen und Erfahrungen abbildet.
Seit Version 2.2 verfügt Mindnet über:
* **Explanation Engine:** Das System erklärt, warum es Notizen findet (über Edges).
* **RAG-Chat (KI-Zwilling):** Das System antwortet in natürlicher Sprache. **Wie** du schreibst, bestimmt, **wie schlau** die KI antwortet.
### 1.2 Der Vault als „Source of Truth“
Die Markdown-Dateien in deinem Vault sind die **einzige Quelle der Wahrheit**.
@ -45,7 +48,7 @@ Diese Felder sind technisch nicht zwingend, aber für bestimmte Typen sinnvoll:
aliases: [Alpha Projekt, Project A] # Synonyme für die Suche
visibility: internal # internal (default), public, private
> **Hinweis:** Felder wie `retriever_weight` oder `chunk_profile` sollten **nicht** mehr manuell im Frontmatter gesetzt werden. Diese werden zentral über den `type` gesteuert (siehe Kap. 3).
> **Hinweis:** Felder wie `retriever_weight` oder `chunk_profile` sollten **nicht** mehr manuell im Frontmatter gesetzt werden. Diese werden zentral über den `type` gesteuert (siehe Kap. 3), um die Wartbarkeit zu sichern.
### 2.3 Empfehlungen für IDs und Pfade
@ -69,17 +72,16 @@ Der `type` ist der wichtigste Hebel im Knowledge Design. Er verknüpft die Notiz
Mindnet unterscheidet verschiedene Wissensarten. Wähle den Typ, der die **Rolle** der Notiz am besten beschreibt:
| Typ | Beschreibung & Einsatzzweck |
| :--- | :--- |
| **`concept`** | Fachbegriffe, Theorien, Modelle. Zeitloses Wissen. (z. B. "Vektordatenbank") |
| **`project`** | Ein Vorhaben mit Ziel, Dauer und Aufgaben. (z. B. "Aufbau Homelab") |
| **`experience`** | Persönliche Erfahrung, Lektion oder Erkenntnis. (z. B. "Learning: Docker Networking") |
| **`decision`** | Eine bewusst getroffene Entscheidung inkl. Alternativen und Begründung. (z. B. "ADR-001: Nutzung von Qdrant") |
| **`value`** | Ein persönlicher Wert oder ein Prinzip. (z. B. "Datensparsamkeit") |
| **`person`** | Eine reale Person (Netzwerk, Autor). |
| **`event`** | Ein Ereignis (Konferenz, Meeting). |
| **`journal`** | Zeitbezogener Log-Eintrag, Daily Note. |
| **`source`** | Externe Quelle (Buch, PDF, Artikel), die exzerpiert wird. |
| Typ | Beschreibung & Einsatzzweck | Wichtigkeit für Chat |
| :--- | :--- | :--- |
| **`concept`** | Fachbegriffe, Theorien. Zeitloses Wissen. | Mittel (Basiswissen) |
| **`project`** | Ein Vorhaben mit Ziel, Dauer und Aufgaben. | Hoch (Kontext) |
| **`experience`** | Persönliche Erfahrung, Lektion oder Erkenntnis. | Sehr Hoch (Persönlichkeit) |
| **`decision`** | Eine bewusst getroffene Entscheidung (ADR). | **Kritisch** (Begründung "Warum") |
| **`value`** | Ein persönlicher Wert oder ein Prinzip. | **Kritisch** (Moralischer Kompass) |
| **`person`** | Eine reale Person (Netzwerk, Autor). | Niedrig |
| **`journal`** | Zeitbezogener Log-Eintrag, Daily Note. | Mittel (Historie) |
| **`source`** | Externe Quelle (Buch, PDF, Artikel). | Niedrig (Faktenbasis) |
### 3.2 Zusammenspiel mit `types.yaml`
@ -87,20 +89,19 @@ Der `type` steuert im Hintergrund drei technische Mechanismen:
1. **`retriever_weight` (Wichtigkeit):**
* Ein `concept` (0.6) wiegt weniger als ein `project` (0.97) oder eine `decision` (1.0).
* Ziel: Mindnet soll bei Fragen eher *deine Entscheidungen* und *Projekte* finden als reine Definitionen.
* **Warum?** Bei einer Suche nach "Datenbank" soll Mindnet bevorzugt deine *Entscheidung* ("Warum wir X nutzen") anzeigen.
2. **`chunk_profile` (Textzerlegung):**
* `journal` (short): Wird fein zerlegt, da sich Themen schnell ändern.
* `concept` (medium): Standard-Absätze.
* `project` (long): Längere Kontext-Fenster, um Zusammenhänge zu wahren.
* `journal` (short): Wird fein zerlegt.
* `project` (long): Längere Kontext-Fenster.
3. **`edge_defaults` (Automatische Vernetzung):**
* Mindnet ergänzt automatisch Kanten.
* Beispiel: Ein Link in einem `project` wird automatisch als `depends_on` (Abhängigkeit) interpretiert, sofern nicht anders angegeben.
* Beispiel: Ein Link in einem `project` wird automatisch als `depends_on` (Abhängigkeit) interpretiert.
---
## 4. Edges & Referenzen in Notes
Um aus isolierten Dateien ein **Netzwerk** zu machen, nutzen wir Verlinkungen. Mindnet v2.2 (WP03) bietet hierfür präzise Werkzeuge.
Um aus isolierten Dateien ein **Netzwerk** zu machen, nutzen wir Verlinkungen. In Version 2.2 sind diese Verlinkungen die Basis für den **Hybrid Retriever** (Suche über Nachbarn).
### 4.1 Wikilinks (Die Basis-Referenz)
Der klassische Obsidian-Link.
@ -109,37 +110,27 @@ Der klassische Obsidian-Link.
* **Bedeutung:** "Diese Notiz erwähnt Qdrant."
* **Edge-Typ:** `references`
* **Confidence:** 1.0
### 4.2 Inline-Relationen (Semantische Verknüpfung)
Wenn du ausdrücken willst, **wie** Dinge zusammenhängen. Dies ist essenziell für die Erklärbarkeit ("Warum ist das relevant?").
Dies ist die **mächtigste** Methode. Du sagst dem System explizit, **wie** Dinge zusammenhängen.
Daher [[rel:depends_on Qdrant]].
Dieses Konzept ist [[rel:similar_to Pinecone]].
* **Syntax:** `[[rel:RELATION ZIEL]]`. (Wichtig: Das `rel:` Präfix steht *innerhalb* der Klammer).
* **Syntax:** `[[rel:RELATION ZIEL]]`.
* **Gültige Relationen:**
* `depends_on`: Hängt ab von / Benötigt.
* `depends_on`: Hängt ab von / Benötigt. (Trigger für hohe Graph-Relevanz).
* `similar_to`: Ähnelt / Ist vergleichbar mit.
* `related_to`: Hat zu tun mit (allgemein).
* `caused_by`: Wurde verursacht durch.
* `solves`: Löst (Problem).
* `implements`: Setzt um (Konzept).
* **Nicht unterstützt:** `rel: depends_on [[Ziel]]` (alte Syntax, funktioniert nicht mehr!).
### 4.3 Callout-Edges (Kuratierte Listen)
Für Zusammenfassungen oder "Siehe auch"-Blöcke am Ende einer Notiz, die nicht im Fließtext stehen sollen.
Für Zusammenfassungen oder "Siehe auch"-Blöcke am Ende einer Notiz.
> [!edge] related_to: [[Vector Embeddings]] [[AI Agents]]
* **Funktion:** Erzeugt `related_to`-Kanten zu allen genannten Zielen in dieser Zeile.
* **Nutzen:** Erlaubt schnelle Listenpflege ohne den Textfluss zu stören.
### 4.4 Strukturkanten (Automatisch)
Diese musst du nicht schreiben, sie entstehen automatisch:
* `belongs_to`: Verknüpft jeden Textabschnitt (Chunk) mit seiner Ursprungsnotiz.
* `next` / `prev`: Verknüpft Absätze in Lesereihenfolge (ermöglicht Agenten das "Weiterlesen").
* `has_part` / `part_of`: (Zukünftig) Basierend auf Ordnerstrukturen.
---
@ -186,17 +177,6 @@ Entscheidungen sind hoch gewichtet (`retriever_weight: 1.0`).
## Begründung
Qdrant erlaubt lokalen Betrieb und [[rel:solves Payload Filtering Requirements]].
### 5.3 Typische Anti-Patterns (Bitte vermeiden!)
1. **Der "Alles"-Zettel:** Eine Notiz "Ideen 2025", die 50 verschiedene Themen enthält.
* *Besser:* Atomare Notizen ("Idee A", "Idee B") erstellen und verlinken.
2. **Manuelle Gewichtung:** `retriever_weight: 0.9` im Frontmatter setzen.
* *Problem:* Wenn du das System tunen willst, musst du hunderte Dateien ändern.
* *Besser:* Den `type` korrekt setzen und das Gewicht zentral in `types.yaml` steuern.
3. **Link-Wüsten:** Eine Notiz, die nur aus einer Liste von 100 Links besteht ohne Text.
* *Problem:* Der Vektor-Sucher findet keinen kontextuellen "Anker" (Text).
* *Besser:* Kurze Beschreibung zu jedem Link hinzufügen.
---
## 6. Langfristige Stabilität & Virtual Schema Layer
@ -210,5 +190,24 @@ Wir vermeiden es, Logik in den Markdown-Dateien hart zu kodieren.
### 6.2 Was bedeutet das für dich?
* Du kannst dich auf den Inhalt konzentrieren.
* Wenn wir in Zukunft entscheiden, dass "Projekte" stärker gewichtet werden sollen, ändern wir **eine Zeile** in der Konfiguration, und das gesamte System passt sich beim nächsten Import an.
* Deine Notizen bleiben sauber und zukunftssicher.
* Wenn wir in Zukunft (WP08) basierend auf Feedback lernen, dass "Projekte" noch wichtiger sind, ändern wir **eine Zeile** in der Konfiguration, und das gesamte System passt sich beim nächsten Import an.
---
## 7. Schreiben für den KI-Zwilling (New in v2.2)
Damit der **RAG-Chat (WP05)** gute Antworten liefert, beachte diese Regeln:
1. **Atomare Konzepte:**
* Der Chatbot baut seine Antwort aus mehreren kleinen Text-Stücken ("Chunks") zusammen.
* Schreibe so, dass ein Absatz auch für sich allein verständlich ist.
2. **Explizite Entscheidungen:**
* Wenn du eine Meinung hast ("Tool X ist schlecht"), schreibe sie nicht in einen Nebensatz.
* Erstelle eine Notiz `type: experience` oder `decision` ("Warum Tool X nicht geeignet ist").
* Die KI sucht gezielt nach `[DECISION]`-Typen, um "Warum"-Fragen zu beantworten.
3. **Werte definieren:**
* Erstelle Notizen mit `type: value` (z.B. "Datenschutz First").
* Die KI nutzt diese, um bei Konflikten ("Soll ich Cloud oder Lokal nutzen?") in deinem Sinne zu argumentieren.
4. **Verlinken ist Pflicht:**
* Der Chatbot nutzt **Hybrid Search**. Er findet Notizen nur, wenn sie über Kanten verbunden sind.
* Eine isolierte Notiz (ohne Links) ist für die KI fast unsichtbar.

92
docs/Overview.md Normal file
View File

@ -0,0 +1,92 @@
# Mindnet v2.2 Overview & Einstieg
**Datei:** `docs/mindnet_overview_v2.2.md`
**Stand:** 2025-12-08
**Status:** **FINAL** (Post-WP05 Release)
**Version:** 2.3.0
---
## 1. Einführung: Was ist Mindnet?
**Mindnet** ist ein persönliches, lokales KI-Gedächtnis. Es ist der Versuch, einen **Digitalen Zwilling** deines Wissens und deiner Persönlichkeit zu erschaffen.
Anders als herkömmliche Notiz-Apps (wie Obsidian oder Evernote), die Texte nur passiv speichern, ist Mindnet ein **aktives System**:
* Es **versteht** Zusammenhänge über einen Wissensgraphen.
* Es **begründet** Antworten ("Warum ist das so?").
* Es **antwortet** im Dialog als Persona (RAG-Chat), basierend auf deinen Werten.
### Die Vision
> „Ein System, das nicht nur speichert, was ich weiß, sondern auch wie ich denke.“
---
## 2. Die drei Ebenen des Systems
Mindnet arbeitet auf drei Schichten, die aufeinander aufbauen:
### Ebene 1: Content (Das Gedächtnis)
* **Quelle:** Dein lokaler Obsidian-Vault (Markdown).
* **Funktion:** Speicherung von Fakten, Projekten und Logs.
* **Technik:** Import-Pipeline, Chunking, Vektor-Datenbank (Qdrant).
* **Status:** 🟢 Live (WP01WP03).
### Ebene 2: Semantik (Das Verstehen)
* **Funktion:** Verknüpfung von isolierten Notizen zu einem Netzwerk.
* **Logik:** "Projekt A *hängt ab von* Entscheidung B".
* **Technik:** Hybrider Retriever (Graph + Vektor), Explanation Engine.
* **Status:** 🟢 Live (WP04).
### Ebene 3: Identität (Die Persönlichkeit)
* **Funktion:** Interaktion und Bewertung. Das System nimmt eine Haltung ein.
* **Logik:** "Ich empfehle Lösung X, weil sie unserem Wert 'Datensparsamkeit' entspricht."
* **Technik:** RAG-Chat, LLM (Phi-3), Prompt Engineering, Feedback Loop.
* **Status:** 🟢 Live (WP05).
---
## 3. End-to-End Architektur
Der Datenfluss in Mindnet ist zyklisch ("Data Flywheel"):
1. **Input:** Du schreibst Notizen in Obsidian.
2. **Ingest:** Ein Python-Skript importiert, zerlegt (Chunking) und vernetzt (Edges) die Daten in Qdrant.
3. **Retrieval:** Bei einer Frage sucht das System semantisch (Text) und graph-basiert (Nachbarn).
4. **Generation:** Ein lokales LLM (Ollama) formuliert die Antwort, angereichert mit Kontext-Metadaten.
5. **Feedback:** Du bewertest die Antwort. Das System lernt (langfristig) daraus.
**Tech-Stack:**
* **Backend:** Python 3.12, FastAPI.
* **Datenbank:** Qdrant (Vektor & Graph).
* **KI:** Ollama (Phi-3 Mini / Mistral) 100% lokal.
* **Frontend:** Terminal (aktuell) / Web-UI (geplant WP10).
---
## 4. Dokumentations-Wegweiser
Wo findest du was?
| Wenn du... | ...lies dieses Dokument |
| :--- | :--- |
| **...wissen willst, wie man Notizen schreibt.** | `mindnet_knowledge_design_manual_v2.2.md` |
| **...das System installieren oder betreiben musst.** | `mindnet_admin_guide_v2.2.md` |
| **...am Python-Code entwickeln willst.** | `mindnet_developer_guide_v2.2.md` |
| **...die Pipeline (Import -> RAG) verstehen willst.** | `mindnet_pipeline_playbook_v2.2.md` |
| **...die genaue JSON-Struktur oder APIs suchst.** | `mindnet_technical_architecture.md` |
| **...verstehen willst, was fachlich passiert.** | `mindnet_functional_architecture.md` |
| **...den aktuellen Projektstatus suchst.** | `Programmplan_V2.2.md` |
---
## 5. Rollen im System
* **Mindmaster (User/Owner):** Du. Du erstellst Inhalte, stellst Fragen und gibst Feedback. Du definierst die Werte (`type: value`).
* **Mindnet (Der Agent):** Der digitale Zwilling. Er agiert als pragmatischer, transparenter Assistent im Chat.
* **Administrator:** Verantwortlich für Docker-Container, Backups und LLM-Ressourcen.
---
## 6. Aktueller Fokus
Wir befinden uns im Übergang von **Phase C (Persönlichkeit)** zu **Phase D (Interaktion)**.
Das "Gehirn" (WP05) ist fertig. Als Nächstes folgen die **Decision Engine (WP06)** für komplexe Entscheidungen und das **Frontend (WP10)** für bessere Usability.

View File

@ -1,10 +1,10 @@
# Mindnet v2.2 Admin Guide
**Datei:** `docs/mindnet_admin_guide_v2.2.md`
**Stand:** 2025-12-08
**Status:** **FINAL** (Konsolidiert WP02WP04c)
**Quellen:** `Handbuch.md`, `mindnet_v2_implementation_playbook.md`, `mindnet_technical_architecture.md`, `Programmplan_V2.2.md`.
**Status:** **FINAL** (Inkl. RAG & LLM Ops)
**Quellen:** `Handbuch.md`, `mindnet_developer_guide_v2.2.md`.
> Dieses Handbuch richtet sich an **Administratoren**. Es beschreibt Installation, Konfiguration, Backup-Strategien, Monitoring und den sicheren Betrieb der Mindnet-Instanz.
> Dieses Handbuch richtet sich an **Administratoren**. Es beschreibt Installation, Konfiguration, Backup-Strategien, Monitoring und den sicheren Betrieb der Mindnet-Instanz (API + DB + LLM).
---
@ -21,10 +21,10 @@ Wir unterscheiden strikt zwischen:
### 2.1 Systemvoraussetzungen
* **OS:** Linux (Ubuntu 22.04+ empfohlen) oder macOS.
* **Runtime:** Python 3.10+, Docker (für Qdrant).
* **Runtime:** Python 3.10+, Docker (für Qdrant), Ollama (für LLM).
* **Hardware:**
* CPU: 2+ Cores.
* RAM: Min. 4GB (abhängig von der Vault-Größe und Qdrant-Index).
* CPU: 4+ Cores empfohlen (für Import & Inference).
* RAM: Min. 8GB empfohlen (4GB System + 4GB für Phi-3/Qdrant).
* Disk: SSD empfohlen für Qdrant-Performance.
### 2.2 Installation (Code)
@ -49,36 +49,40 @@ Wir nutzen Qdrant als Vektor-Datenbank. Persistenz ist wichtig.
-v $(pwd)/qdrant_storage:/qdrant/storage \
qdrant/qdrant
### 2.4 Konfiguration (ENV)
Erstelle eine `.env` Datei im Root-Verzeichnis. Diese Variablen steuern das Verhalten der Skripte und der API.
### 2.4 Ollama Setup (LLM Service)
Mindnet benötigt einen lokalen LLM-Server für den Chat.
# 1. Installieren (Linux Script)
curl -fsSL https://ollama.com/install.sh | sh
# 2. Modell laden (Phi-3 Mini für CPU-Performance)
ollama pull phi3:mini
# 3. Testen
curl http://localhost:11434/api/generate -d '{"model": "phi3:mini", "prompt":"Hi"}'
### 2.5 Konfiguration (ENV)
Erstelle eine `.env` Datei im Root-Verzeichnis.
# Qdrant Verbindung
QDRANT_URL="http://localhost:6333"
QDRANT_API_KEY="" # Leer lassen für lokale Instanzen ohne Auth
# Mindnet Core Settings
COLLECTION_PREFIX="mindnet" # "mindnet_dev" für Dev-Umgebung
COLLECTION_PREFIX="mindnet"
MINDNET_TYPES_FILE="./config/types.yaml"
MINDNET_RETRIEVER_CONFIG="./config/retriever.yaml"
MINDNET_PROMPTS_PATH="./config/prompts.yaml"
# Embedding Settings (Default: all-MiniLM-L6-v2)
VECTOR_DIM=384
# LLM Settings
MINDNET_LLM_MODEL="phi3:mini"
MINDNET_OLLAMA_URL="http://127.0.0.1:11434"
# Import-Strategie
MINDNET_HASH_COMPARE="Body"
MINDNET_HASH_SOURCE="parsed"
### 2.5 Deployment via Systemd (Standard ab v2.2.1)
Mindnet wird nicht mehr manuell gestartet, sondern als Systemdienst.
### 2.6 Deployment via Systemd
Mindnet wird als Systemdienst gestartet. Ollama läuft meist als eigener Dienst (`ollama.service`).
**Production Service (`/etc/systemd/system/mindnet-prod.service`):**
* Läuft auf Port 8001.
* Autostart (`enabled`).
* Restart Policy: `always` (heilt Abstürze automatisch).
**Development Service (`/etc/systemd/system/mindnet-dev.service`):**
* Läuft auf Port 8002.
* Getrennter Ordner (`~/mindnet_dev`).
* Restart Policy: `always`.
* Abhängigkeit: Sollte nach `docker` und `ollama` starten.
---
@ -90,28 +94,24 @@ Der Vault-Zustand sollte regelmäßig (z.B. stündlich per Cronjob) nach Qdrant
**Cronjob-Beispiel (stündlich):**
0 * * * * cd /home/llmadmin/mindnet && .venv/bin/python3 -m scripts.import_markdown --vault /path/to/vault --prefix "mindnet" --apply --purge-before-upsert --sync-deletes >> ./logs/import.log 2>&1
* `--purge-before-upsert`: Entfernt Fragmente gelöschter Textstellen.
* `--sync-deletes`: Entfernt Notizen aus dem Index, die im Vault gelöscht wurden.
### 3.2 Health-Checks
Prüfe regelmäßig, ob die API und der Retriever korrekt arbeiten.
Prüfe regelmäßig, ob alle drei Komponenten (API, DB, LLM) laufen.
**System Status:**
**Status prüfen:**
sudo systemctl status mindnet-prod
sudo systemctl status ollama
**Logischer Smoke-Test:**
python3 scripts/test_retriever_smoke.py --mode hybrid --url http://localhost:8001/query
### 3.3 Logs & Monitoring
Seit v2.2.1 werden technische Logs im Systemd Journal und fachliche Logs in JSONL-Dateien gespeichert.
* **Technische Fehler (API):** `journalctl -u mindnet-prod -f`
* **LLM Fehler (Ollama):** `journalctl -u ollama -f`
* **Fachliche Logs:** `data/logs/search_history.jsonl`
* **Technische Fehler (Live):**
`journalctl -u mindnet-prod -f`
* **Fachliche Logs (Data Flywheel):**
`/home/llmadmin/mindnet/data/logs/search_history.jsonl`
`/home/llmadmin/mindnet/data/logs/feedback.jsonl`
> **Hinweis:** Die JSONL-Logs wachsen endlos ("Append Only"). Richte bei Bedarf `logrotate` ein oder archiviere alte Logs. Lösche sie nicht, da sie die Basis für WP08 (Self-Tuning) sind.
**Troubleshooting Chat:**
* Wenn `/chat` in den Timeout läuft (>300s): Prüfe, ob `phi3:mini` geladen ist und ob der Server überlastet ist.
* Wenn `/chat` halluziniert: Prüfe `config/prompts.yaml` und ob der Import aktuell ist.
---
@ -128,7 +128,6 @@ Wenn neue Versionen ausgerollt werden (Deployment):
3. **Dienst neustarten (Zwingend!):**
sudo systemctl restart mindnet-prod
4. **Schema-Migration (falls nötig):**
Bei Änderungen an `types.yaml` oder Index-Strukturen:
python3 -m scripts.import_markdown ... --apply
---
@ -162,8 +161,9 @@ Wenn die Datenbank korrupt ist:
### 6.1 Zugriffsschutz
Mindnet hat aktuell **keine integrierte Authentifizierung**.
* **API:** Muss hinter einem Reverse Proxy (Nginx, Traefik) mit Basic Auth laufen, wenn sie im Netzwerk freigegeben wird.
* **API:** Muss hinter einem Reverse Proxy (Nginx) mit Basic Auth laufen.
* **Qdrant:** Sollte via Firewall (ufw) auf `127.0.0.1` beschränkt sein.
* **Ollama:** Standardmäßig hört Ollama nur auf `localhost`. Das ist sicher.
### 6.2 Typen-Governance
Änderungen an der `types.yaml` (z.B. neue Gewichte) wirken global.

View File

@ -1,7 +1,7 @@
# Mindnet v2.2 Appendices & Referenzen
**Datei:** `docs/mindnet_appendices_v2.2.md`
**Stand:** 2025-12-08
**Status:** **FINAL**
**Status:** **FINAL** (Integrierter Stand WP01WP05)
**Quellen:** `TYPE_REGISTRY_MANUAL.md`, `chunking_strategy.md`, `mindnet_technical_architecture.md`, `Handbuch.md`.
> Dieses Dokument bündelt Tabellen, Schemata und technische Referenzen, die in den Prozess-Dokumenten (Playbook, Guides) den Lesefluss stören würden.
@ -22,6 +22,7 @@ Diese Tabelle zeigt die Standard-Konfiguration der `types.yaml` (Stand v2.2).
| **person** | `short` | 0.50 | `related_to` | Personen-Profile. |
| **source** | `long` | 0.50 | *(keine)* | Externe Quellen (Bücher, PDFs). |
| **event** | `short` | 0.60 | `related_to` | Meetings, Konferenzen. |
| **value** | `medium` | 1.00 | `related_to` | Persönliche Werte/Prinzipien. |
| **default** | `medium` | 1.00 | `references` | Fallback, wenn Typ unbekannt. |
---
@ -99,6 +100,9 @@ Diese Variablen steuern das Verhalten der Skripte und Container.
| `COLLECTION_PREFIX` | `mindnet` | Namensraum für Collections (`{prefix}_notes` etc). |
| `MINDNET_TYPES_FILE` | `config/types.yaml` | Pfad zur Typ-Registry. |
| `MINDNET_RETRIEVER_CONFIG`| `config/retriever.yaml`| Pfad zur Scoring-Konfiguration. |
| `MINDNET_PROMPTS_PATH` | `config/prompts.yaml` | Pfad zu LLM-Prompts (Neu in v2.2). |
| `MINDNET_LLM_MODEL` | `phi3:mini` | Name des Ollama-Modells (Neu in v2.2). |
| `MINDNET_OLLAMA_URL` | `http://127.0.0.1:11434`| URL zum LLM-Server (Neu in v2.2). |
| `MINDNET_HASH_COMPARE` | `Body` | Vergleichsmodus für Import (`Body`, `Frontmatter`, `Full`). |
| `MINDNET_HASH_SOURCE` | `parsed` | Quelle für Hash (`parsed`, `raw`, `file`). |
| `VECTOR_DIM` | `384` | Dimension der Embeddings (Modellabhängig). |
@ -114,12 +118,13 @@ Diese Variablen steuern das Verhalten der Skripte und Container.
* **Feedback Loop:** Prozess des Loggens und Auswertens von User-Reaktionen.
* **Idempotenz:** Mehrfache Ausführung liefert gleiches Ergebnis.
* **Inline-Edge:** Kante via `[[rel:type Ziel]]`.
* **RAG (Retrieval Augmented Generation):** Kombination aus Suche und Text-Generierung.
* **Retriever:** Suchmaschine (FastAPI).
* **Vault:** Ordner mit Markdown-Dateien.
---
## Anhang F: Workpackage Status (v2.2.1)
## Anhang F: Workpackage Status (v2.3.0)
Aktueller Implementierungsstand der Module.
@ -131,6 +136,7 @@ Aktueller Implementierungsstand der Module.
| **WP04a**| Retriever Scoring | 🟢 Live | Hybrider Score (Semantik + Graph). |
| **WP04b**| Explanation Layer | 🟢 Live | API liefert Reasons & Breakdown. |
| **WP04c**| Feedback Loop | 🟢 Live | Logging (JSONL) & Traceability aktiv. |
| **WP05** | Persönlichkeit / Chat | 🟡 Geplant | Nächster Schritt (RAG). |
| **WP06** | Self-Healing | 🔴 Geplant | Auto-Fixing von Broken Links. |
| **WP05** | Persönlichkeit / Chat | 🟢 Live | RAG-Chat mit Context Enrichment. |
| **WP06** | Decision Engine | 🟡 Geplant | Nächster Schritt (Logik). |
| **WP08** | Self-Tuning | 🔴 Geplant | Auto-Adjustment der Gewichte. |
| **WP10** | Chat Interface | 🟡 Geplant | Nächster Schritt (Frontend). |

View File

@ -1,15 +1,15 @@
# Mindnet v2.2 Developer Guide
**Datei:** `docs/mindnet_developer_guide_v2.2.md`
**Stand:** 2025-12-07
**Status:** **FINAL** (Konsolidiert WP02WP04a)
**Quellen:** `mindnet_technical_architecture.md`, `Handbuch.md`, `docs_mindnet_retriever.md`, `mindnet_v2_implementation_playbook.md`.
**Stand:** 2025-12-08
**Status:** **FINAL** (Inkl. RAG & LLM Setup)
**Quellen:** `mindnet_technical_architecture.md`, `Handbuch.md`, `DEV_WORKFLOW.md`.
> **Zielgruppe:** Entwickler:innen.
> **Zweck:** Anleitung zum Aufsetzen der Entwicklungsumgebung, Verständnis der Modulstruktur und Durchführung von Tests.
---
## 1. Projektstruktur
## 1. Projektstruktur (Aktualisiert)
Der Code ist modular in `app` (Logik), `scripts` (CLI) und `config` (Steuerung) getrennt.
@ -18,18 +18,29 @@ Der Code ist modular in `app` (Logik), `scripts` (CLI) und `config` (Steuerung)
│ ├── core/ # Kernlogik
│ │ ├── chunker.py # Text-Zerlegung
│ │ ├── derive_edges.py # Edge-Erzeugung (WP03 Logik)
│ │ ├── retriever.py # Scoring & Graph-Expansion (WP04 Logik)
│ │ ├── retriever.py # Scoring & Hybrid Search
│ │ ├── qdrant.py # DB-Verbindung
│ │ └── ...
│ ├── models/ # Pydantic DTOs (Request/Response)
│ ├── models/ # Pydantic DTOs
│ │ └── dto.py # Zentrale DTO-Definition
│ ├── routers/ # FastAPI Endpoints
│ ├── services/ # Externe Dienste (LLM, Embeddings)
│ │ ├── query.py # Suche
│ │ ├── chat.py # RAG-Chat (WP05)
│ │ ├── feedback.py # Feedback (WP04c)
│ │ └── ...
│ ├── services/ # Interne & Externe Dienste
│ │ ├── llm_service.py # Ollama Client (WP05)
│ │ ├── feedback_service.py # Logging (JSONL Writer)
│ │ └── embeddings_client.py
│ └── main.py # Entrypoint der API
├── config/ # YAML-Konfigurationen (Single Source of Truth)
│ ├── types.yaml # Import-Regeln
│ ├── prompts.yaml # LLM Prompts & RAG Templates (WP05)
│ └── retriever.yaml # Scoring-Regeln
├── data/
│ └── logs/ # Lokale Logs (search_history.jsonl, feedback.jsonl)
├── scripts/ # CLI-Tools (Import, Diagnose, Reset)
├── tests/ # Pytest Suite
├── tests/ # Pytest Suite & Smoke Scripts
└── vault/ # Dein lokaler Markdown-Content (Git-ignored)
---
@ -39,6 +50,7 @@ Der Code ist modular in `app` (Logik), `scripts` (CLI) und `config` (Steuerung)
### 2.1 Voraussetzungen
* **Python:** 3.10 oder höher.
* **Docker:** Für Qdrant.
* **Ollama:** Für lokale LLM-Inference (erforderlich für `/chat`).
* **Vault:** Ein Ordner mit Markdown-Dateien (z.B. `./mindnet_v2_test_vault` für Tests).
### 2.2 Installation
@ -53,25 +65,47 @@ Der Code ist modular in `app` (Logik), `scripts` (CLI) und `config` (Steuerung)
# 3. Abhängigkeiten installieren
pip install -r requirements.txt
# 4. Ollama Setup (Modell laden)
# Wir nutzen Phi-3 Mini für schnelle CPU-Inference
ollama pull phi3:mini
### 2.3 Konfiguration (Environment)
Erstelle eine `.env` Datei im Root-Verzeichnis oder exportiere die Variablen.
Erstelle eine `.env` Datei im Root-Verzeichnis.
# Qdrant Verbindung
QDRANT_URL="http://localhost:6333"
QDRANT_API_KEY="" # Leer lassen für lokal
# Mindnet Settings
# Mindnet Core Settings
COLLECTION_PREFIX="mindnet_dev"
MINDNET_TYPES_FILE="./config/types.yaml"
MINDNET_RETRIEVER_CONFIG="./config/retriever.yaml"
# Hash-Strategie (für Importer-Debugging)
# LLM / RAG Settings (WP05)
MINDNET_LLM_MODEL="phi3:mini"
MINDNET_OLLAMA_URL="http://127.0.0.1:11434"
MINDNET_PROMPTS_PATH="./config/prompts.yaml"
# Import-Strategie
MINDNET_HASH_COMPARE="Body"
MINDNET_HASH_SOURCE="parsed"
### 2.4 Dienste starten
Starten von Qdrant via Docker:
docker run -d --name mindnet_qdrant_dev -p 6333:6333 -p 6334:6334 qdrant/qdrant
### 2.4 Dienste starten (Systemd bevorzugt)
Auf dem Entwicklungsserver (Beelink) nutzen wir Systemd.
# Starten / Neustarten
sudo systemctl restart mindnet-dev
# Logs prüfen
journalctl -u mindnet-dev -f
Falls du lokal auf Windows entwickelst:
# 1. Qdrant starten
docker run -p 6333:6333 qdrant/qdrant
# 2. Ollama starten
ollama serve
# 3. API starten
uvicorn app.main:app --host 0.0.0.0 --port 8002 --env-file .env --reload
---
@ -80,18 +114,17 @@ Starten von Qdrant via Docker:
### 3.1 Der Importer (`scripts.import_markdown`)
Dies ist das komplexeste Modul.
* **Einstieg:** `scripts/import_markdown.py` -> `main()`.
* **Idempotenz:** Der Importer muss mehrfach laufen können, ohne Duplikate zu erzeugen. Wir nutzen deterministische IDs (UUIDv5) basierend auf Dateipfaden/Titeln.
* **Debugging:** Wenn du am Importer arbeitest, nutze `--dry-run` oder `scripts/payload_dryrun.py`, um DB-Schreibvorgänge zu vermeiden.
* **Idempotenz:** Der Importer muss mehrfach laufen können, ohne Duplikate zu erzeugen. Wir nutzen deterministische IDs (UUIDv5).
* **Debugging:** Nutze `--dry-run` oder `scripts/payload_dryrun.py`.
### 3.2 Edge-Logik (`app.core.derive_edges`)
Hier wird entschieden, welche Kanten entstehen.
* **Erweiterung:** Wenn du neue Edge-Regeln (z.B. Regex-Parser) hinzufügst, tue dies hier.
* **Rule-ID:** Vergib zwingend eine eindeutige `rule_id` (z.B. `custom:my_rule`), damit die Herkunft nachvollziehbar bleibt.
* **Rule-ID:** Vergib zwingend eine eindeutige `rule_id` (z.B. `custom:my_rule`), damit die Herkunft für die Explanation nachvollziehbar bleibt.
### 3.3 Der Retriever (`app.core.retriever`)
Hier passiert das Scoring und die Graph-Expansion.
* **Gewichtung:** Hardcodiere niemals Gewichte (`0.5` etc.) im Code. Nutze `app.core.config.get_retriever_config()`, um `retriever.yaml` zu lesen.
* **Graph-Adapter:** Nutze `graph_adapter.expand()`, um Nachbarn zu laden. Vermeide direkte Qdrant-Point-Queries im Retriever-Code, um die Abstraktion zu wahren.
### 3.3 Der Retriever & Chat (`app.core.retriever` / `app.routers.chat`)
Hier passiert das Scoring und die Generation.
* **Hybrid Search:** Der Chat-Endpoint erzwingt `mode="hybrid"`.
* **Context Enrichment:** In `_build_enriched_context` (chat.py) werden Metadaten (Typ, Score) in den Prompt injiziert. Achte darauf, dass neue Typen hier ggf. berücksichtigt werden, falls sie spezielle Behandlung brauchen (aktuell generisch gelöst).
---
@ -107,39 +140,43 @@ Für isolierte Logik (Parsing, Scoring).
### 4.2 Integration / Pipeline Tests
Prüfen den Datenfluss von Markdown bis Qdrant-JSON.
* **Payload Dryrun:** Prüft, ob `import_markdown` valide JSON-Objekte erzeugt, ohne in die DB zu schreiben. Essentiell bei Schema-Änderungen.
* **Payload Dryrun:** Prüft JSON-Schema Konformität.
python3 -m scripts.payload_dryrun --vault ./mindnet_v2_test_vault
* **Edge Checks:** Prüft Graph-Invarianten (z.B. `next` muss reziprok zu `prev` sein).
* **Edge Checks:** Prüft Graph-Invarianten.
python3 -m scripts.edges_full_check
### 4.3 Smoke Tests (E2E)
Prüfen das laufende System (API) gegen eine echte (lokale) Qdrant-Instanz.
# 1. API im Hintergrund starten
uvicorn app.main:app --port 8000 &
Prüfen das laufende System (API) gegen eine echte Qdrant-Instanz und Ollama.
# 2. Smoke Test feuern
python scripts/test_retriever_smoke.py --query "Test" --mode hybrid --top-k 5
# 1. Retriever Test (Hybrid + Explanation)
python scripts/test_retriever_smoke.py --query "Test" --mode hybrid --top-k 5 --explain
# 2. Chat / RAG Test (WP05)
# Prüft die gesamte Kette: Suche -> Kontext -> LLM -> Antwort
python tests/test_chat_wp05.py
# 3. Feedback Test (WP04c)
python tests/test_feedback_smoke.py --url http://localhost:8002/query
---
## 5. Guidelines für Erweiterungen
### 5.1 Neuen Note-Typ hinzufügen
* **Nicht im Code!** Bearbeite `config/types.yaml`.
* Definiere `chunk_profile` und `retriever_weight`.
* Führe einen **Re-Import** durch, damit die Änderungen wirksam werden.
* Bearbeite `config/types.yaml`.
* Definiere `chunk_profile`, `retriever_weight` und `edge_defaults`.
* Führe einen **Re-Import** durch.
### 5.2 Schema-Änderungen
Wenn du Felder in `mindnet_notes` oder `mindnet_chunks` hinzufügst:
1. Passe `app/core/note_payload.py` bzw. `chunk_payload.py` an.
2. Aktualisiere `tests/assert_payload_schema.py`.
3. Führe `scripts/reset_qdrant.py` (Wipe) aus, um die Indizes neu anzulegen.
### 5.2 Persönlichkeit anpassen (Prompt Engineering)
* Bearbeite `config/prompts.yaml`.
* Änderungen sind sofort aktiv (kein Neustart nötig, da pro Request geladen, sofern nicht gecached).
* **Prompt-Regel:** Kleine Modelle (Phi-3) brauchen strikte Anweisungen (z.B. "Achte auf [DECISION]").
### 5.3 Neue API-Endpunkte
* Erstelle einen neuen Router in `app/routers/`.
* Nutze Pydantic Models aus `app/models/dto.py` für Request/Response.
* Registriere den Router in `app/main.py`.
* Denke an **Traceability**: Generiere oder durchschleife `query_id`.
---
@ -151,5 +188,5 @@ Wenn du Felder in `mindnet_notes` oder `mindnet_chunks` hinzufügst:
**Einen einzelnen File inspizieren (Parser-Sicht):**
python3 tests/inspect_one_note.py --file ./vault/MeinFile.md
**Warum wird mein File nicht importiert?**
python3 -m scripts.diagnose_changed --vault ./vault --all
**Live-Logs sehen (Beelink):**
journalctl -u mindnet-dev -f

View File

@ -1,9 +1,9 @@
# Mindnet v2.2 Fachliche Architektur
**Datei:** `docs/mindnet_functional_architecture_v2.2.md`
**Stand:** 2025-12-08
**Status:** **FINAL** (Integrierter Stand WP01WP04c)
**Status:** **FINAL** (Integrierter Stand WP01WP05)
> Dieses Dokument beschreibt **was** Mindnet fachlich tut und **warum** mit Fokus auf die Erzeugung und Nutzung von **Edges** (Kanten) zwischen Notizen/Chunks sowie die Logik des Retrievers und der Feedback-Mechanismen. Die technische Umsetzung wird im technischen Dokument detailliert.
> Dieses Dokument beschreibt **was** Mindnet fachlich tut und **warum** mit Fokus auf die Erzeugung und Nutzung von **Edges** (Kanten), die Logik des Retrievers und den neuen **RAG-Chat** (Persönlichkeit). Die technische Umsetzung wird im technischen Dokument detailliert.
---
@ -164,11 +164,31 @@ Die API gibt diese Analysen als menschenlesbare Sätze (`reasons`) und als Daten
---
## 6) Feedback & Lernen WP04c
## 6) Der RAG-Chat & Persönlichkeit (WP05)
Seit WP-05 kann Mindnet nicht nur suchen, sondern als **KI-Zwilling** antworten.
### 6.1 Context Intelligence (Der "Enriched Context")
Kleine Sprachmodelle (SLMs wie Phi-3) scheitern oft an komplexen Zusammenhängen. Mindnet löst dies durch **explizite Metadaten-Injection** in den Prompt.
Dem LLM wird nicht nur der Text eines Chunks gezeigt, sondern auch sein Typ und Score:
* *"Hier ist eine Notiz vom Typ `[DECISION]`. Sie erklärt, warum wir etwas tun."*
* *"Hier ist eine Notiz vom Typ `[PROJECT]`. Sie erklärt, was wir tun."*
Dadurch kann das System Fragen wie *"Warum nutzen wir X?"* korrekt beantworten, indem es die Begründung aus der Decision-Notiz zieht, selbst wenn die Frage auf das Projekt abzielte.
### 6.2 Persönlichkeit (Late Binding)
Die "Persönlichkeit" von Mindnet (Stil, Werte, Tonfall) ist **nicht** im Python-Code verankert, sondern in `config/prompts.yaml`.
* **System Prompt:** Definiert die Rolle ("Ich bin dein digitales Gedächtnis...").
* **Werte:** Pragmatismus, Transparenz (kein Halluzinieren), Vernetztes Denken.
* **Flexibilität:** Die Persönlichkeit kann jederzeit angepasst werden (z.B. "Sei kritischer"), ohne das System neu zu starten.
---
## 7) Feedback & Lernen WP04c
Das System verfügt nun über ein **Kurzzeitgedächtnis für Interaktionen**, das die Basis für zukünftiges Lernen bildet.
### 6.1 Der Feedback-Loop
### 7.1 Der Feedback-Loop
1. **Suche (Situation):**
Wenn ein Nutzer eine Anfrage stellt, loggt Mindnet die "Situation":
* Den Query-Text.
@ -185,7 +205,7 @@ Das System verfügt nun über ein **Kurzzeitgedächtnis für Interaktionen**, da
---
## 7) Confidence & Provenance wozu?
## 8) Confidence & Provenance wozu?
Der Retriever kann Edges gewichten:
- **provenance**: *explicit* > *rule*
@ -197,7 +217,7 @@ Eine typische Gewichtung (konfigurierbar in `retriever.yaml`) ist:
---
## 8) Semantik ausgewählter `kind`-Werte
## 9) Semantik ausgewählter `kind`-Werte
- `references` „Nutzt/erwähnt“; neutral, aber stützend für Kontext.
- `related_to` Ähnlichkeit/Verwandtschaft (symmetrisch interpretierbar).
@ -209,7 +229,7 @@ Eine typische Gewichtung (konfigurierbar in `retriever.yaml`) ist:
---
## 9) Frontmatter-Eigenschaften Rolle & Empfehlung
## 10) Frontmatter-Eigenschaften Rolle & Empfehlung
Frontmatter-Eigenschaften (Properties) bleiben **minimiert**:
- **type** steuert Typ-Defaults via Registry (Pflicht für differenziertes Verhalten).
@ -219,7 +239,7 @@ Frontmatter-Eigenschaften (Properties) bleiben **minimiert**:
---
## 10) Lösch-/Update-Garantien (Idempotenz)
## 11) Lösch-/Update-Garantien (Idempotenz)
- Jede Note hat einen stabilen **note_id** (Frontmatter/Hash).
- Vor einem Upsert können *alte Chunks/Edges einer Note* gefiltert gelöscht werden (`note_id`-Filter) das hält Collections sauber bei Re-Imports.
@ -227,7 +247,7 @@ Frontmatter-Eigenschaften (Properties) bleiben **minimiert**:
---
## 11) Beispiel Von Markdown zu Kanten (v2.2)
## 12) Beispiel Von Markdown zu Kanten (v2.2)
**Markdown (Auszug)**
# Relations Showcase
@ -246,19 +266,29 @@ Frontmatter-Eigenschaften (Properties) bleiben **minimiert**:
---
## 12) Qualitäts- und Testkriterien (fachlich)
- **Keine Duplikate** gleicher `(src, relation, dst)` in der Collectionsicht (Dedup).
- **Explizite Links** werden vollständig materialisiert (*explicit_total*).
- **Typ-Defaults** ergänzen, aber überdecken nicht.
- **Retriever Smoke-Test:** Ein Query muss plausible Scores liefern, die den Einfluss von `edge_bonus` zeigen.
---
## 13) Referenzen (Projektdateien & Leitlinien)
- Import-Pipeline & Registry-Auflösung: `scripts/import_markdown.py`.
- Kantenbildung (V2-Logic): `app/core/derive_edges.py`.
- Typ-Registry: `config/types.yaml` & `TYPE_REGISTRY_MANUAL.md`.
- Retriever-Scoring & Explanation: `app/core/retriever.py`.
- Persönlichkeit & Chat: `config/prompts.yaml` & `app/routers/chat.py`.
- Logging Service: `app/services/feedback_service.py`.
---
## 14) Workpackage Status (v2.2.1)
Aktueller Implementierungsstand der Module.
| WP | Titel | Status | Anmerkung |
| :--- | :--- | :--- | :--- |
| **WP01** | Knowledge Design | 🟢 Live | Typen, Frontmatter definiert. |
| **WP02** | Chunking Strategy | 🟢 Live | Profilbasiertes Chunking aktiv. |
| **WP03** | Edge Logic / Import | 🟢 Live | Neue Importer-Pipeline mit Provenance. |
| **WP04a**| Retriever Scoring | 🟢 Live | Hybrider Score (Semantik + Graph). |
| **WP04b**| Explanation Layer | 🟢 Live | API liefert Reasons & Breakdown. |
| **WP04c**| Feedback Loop | 🟢 Live | Logging (JSONL) & Traceability aktiv. |
| **WP05** | Persönlichkeit / Chat | 🟢 Live | RAG-Integration mit Context Enrichment. |
| **WP06** | Decision Engine | 🟡 Geplant | Nächster Schritt. |
| **WP08** | Self-Tuning | 🔴 Geplant | Auto-Adjustment der Gewichte. |

View File

@ -1,29 +1,29 @@
# Mindnet v2.2 Technische Architektur
**Datei:** `docs/mindnet_technical_architecture_v2.2.md`
**Stand:** 2025-12-08
**Status:** **FINAL** (Integrierter Stand WP01WP04c)
**Quellen:** `mindnet_technical_architecture.md`, `chunking_strategy.md`, `Handbuch.md`, `wp04_retriever_scoring.md`.
**Status:** **FINAL** (Integrierter Stand WP01WP05)
**Quellen:** `Programmplan_V2.2.md`, `Handbuch.md`, `chunking_strategy.md`, `wp04_retriever_scoring.md`.
> **Ziel dieses Dokuments:**
> Vollständige, konsolidierte Beschreibung der aktuellen technischen Architektur von **Mindnet v2.2**. Es definiert die Datenstrukturen in Qdrant, die Verarbeitungspipelines (Importer, Chunker, Edges) und die Retrieval-Logik. Es bildet den **aktuellen Implementierungsstand** ab.
> Vollständige Beschreibung der technischen Architektur inkl. Graph-Datenbank, Retrieval-Logik und der **neuen RAG-Komponenten (LLM/Chat)**.
---
## 1. Systemüberblick
### 1.1 Architektur-Zielbild
Mindnet ist ein **persönliches Wissensnetz**. Technisch bedeutet das:
Mindnet ist ein **lokales RAG-System (Retrieval Augmented Generation)**.
1. **Source:** Markdown-Notizen in einem Vault (Obsidian-kompatibel).
2. **Pipeline:** Ein Python-Importer transformiert diese in **Notes**, **Chunks** und **Edges**.
3. **Storage:**
* **Qdrant:** Vektor-Datenbank für Graph und Semantik (Collections: notes, chunks, edges).
* **Local Files (JSONL):** Append-Only Logs für Feedback und Search-History (Data Flywheel).
4. **Service:** Eine FastAPI-Anwendung stellt Endpunkte für **Semantische** und **Hybride Suche** sowie **Feedback** bereit.
5. **Inference:** Lokales LLM (Ollama: Phi-3 Mini) für RAG-Chat und Antwortgenerierung.
Das System arbeitet **deterministisch** (stabile IDs) und ist **konfigurationsgetrieben** (`types.yaml`, `retriever.yaml`).
Das System arbeitet **deterministisch** (stabile IDs) und ist **konfigurationsgetrieben** (`types.yaml`, `retriever.yaml`, `prompts.yaml`).
### 1.2 Verzeichnisstruktur & Komponenten
Die Codebasis ist modular aufgebaut.
### 1.2 Verzeichnisstruktur & Komponenten (Post-WP05)
/mindnet/
├── app/
@ -38,17 +38,20 @@ Die Codebasis ist modular aufgebaut.
│ │ ├── derive_edges.py # Logik der Kantenableitung (WP03)
│ │ ├── graph_adapter.py # Subgraph & Reverse-Lookup (WP04b)
│ │ └── retriever.py # Scoring, Expansion & Explanation (WP04a/b)
│ ├── models/ # Pydantic DTOs (QueryHit, Explanation, FeedbackRequest)
│ ├── models/ # Pydantic DTOs (QueryHit, Explanation, FeedbackRequest, ChatRequest)
│ ├── routers/
│ │ ├── query.py # Such-Endpunkt
│ │ ├── chat.py # RAG-Chat Endpunkt (WP05)
│ │ ├── feedback.py # Feedback-Endpunkt (WP04c)
│ │ └── ...
│ ├── services/
│ │ ├── feedback_service.py # Logging-Logik (WP04c)
│ │ ├── llm_service.py # Ollama Client (WP05)
│ │ ├── feedback_service.py # JSONL Logging (WP04c)
│ │ └── embeddings_client.py
├── config/
│ ├── types.yaml # Typ-Definitionen (Import-Zeit)
│ └── retriever.yaml # Scoring-Gewichte (Laufzeit)
│ ├── retriever.yaml # Scoring-Gewichte (Laufzeit)
│ └── prompts.yaml # LLM System-Prompts & Templates (WP05)
├── data/
│ └── logs/ # Lokale JSONL-Logs (WP04c)
├── scripts/
@ -122,24 +125,37 @@ Die Logik ist ausgelagert in YAML-Dateien.
### 3.1 Typ-Registry (`config/types.yaml`)
Steuert den Import-Prozess.
* **Priorität:** Frontmatter > Pfad > Default.
* **Inhalt:**
```yaml
* **Inhalt (Beispiel):**
types:
concept:
chunk_profile: medium
edge_defaults: ["references", "related_to"]
retriever_weight: 0.60
```
### 3.2 Retriever-Config (`config/retriever.yaml`)
Steuert das Scoring zur Laufzeit (WP04a).
* **Inhalt:**
```yaml
* **Inhalt (Beispiel):**
scoring:
semantic_weight: 1.0
edge_weight: 0.7
centrality_weight: 0.5
```
### 3.3 Prompts (`config/prompts.yaml`)
Steuert die LLM-Persönlichkeit (WP05).
* **Inhalt (Beispiel):**
system_prompt: |
Du bist 'mindnet', das KI-Gedächtnis.
rag_template: |
QUELLEN (WISSEN): {context_str}
### 3.4 Environment (`.env`)
Erweiterung für LLM-Steuerung:
MINDNET_LLM_MODEL=phi3:mini
MINDNET_OLLAMA_URL=http://127.0.0.1:11434
---
@ -170,7 +186,7 @@ Das Skript `scripts/import_markdown.py` orchestriert den Prozess.
## 5. Retriever-Architektur & Scoring
Der Retriever (`app/core/retriever.py`) realisiert die Logik hinter `/query`.
Der Retriever (`app/core/retriever.py`) unterstützt zwei Modi. Für den Chat wird **zwingend** der Hybrid-Modus genutzt.
### 5.1 Betriebsmodi
* **Semantic:** Reine Vektorsuche. Schnell.
@ -193,10 +209,10 @@ $$
### 5.3 Explanation Layer (WP04b)
Der Retriever kann Ergebnisse erklären (`explain=True`).
* **Logik:**
* Berechnung des `ScoreBreakdown` (Anteile von Semantik, Graph, Typ).
* Analyse des lokalen Subgraphen mittels `graph_adapter.py`.
* **Incoming Edges (Authority):** Wer zeigt auf diesen Treffer? (z.B. "Referenziert von...")
* **Outgoing Edges (Hub):** Worauf zeigt dieser Treffer? (z.B. "Verweist auf...")
* Berechnung des `ScoreBreakdown` (Anteile von Semantik, Graph, Typ).
* Analyse des lokalen Subgraphen mittels `graph_adapter.py`.
* **Incoming Edges (Authority):** Wer zeigt auf diesen Treffer? (z.B. "Referenziert von...")
* **Outgoing Edges (Hub):** Worauf zeigt dieser Treffer? (z.B. "Verweist auf...")
* **Output:** `QueryHit` enthält ein `explanation` Objekt mit menschenlesbaren `reasons` und `related_edges`.
### 5.4 Graph-Expansion
@ -204,17 +220,44 @@ Der Hybrid-Modus lädt dynamisch die Nachbarschaft der Top-K Vektor-Treffer ("Se
---
## 6. Feedback & Logging Architektur (WP04c)
## 6. RAG & Chat Architektur (WP05)
Der Flow für eine Chat-Anfrage (`/chat`) ist eine Pipeline aus vier Schritten.
### 6.1 Schritt 1: Intent & Retrieval
* Der `ChatRouter` ruft den `Retriever` im **Hybrid-Modus** auf.
* Dies stellt sicher, dass logisch verknüpfte Notizen (z.B. Entscheidungen zu einem Projekt) gefunden werden.
### 6.2 Schritt 2: Context Enrichment (Das "Context Intelligence" Pattern)
* Der Router (`_build_enriched_context`) reichert die Chunks mit Metadaten an:
* **Typ-Injection:** `[DECISION]`, `[PROJECT]`, `[CONCEPT]`.
* **Score-Transparenz:** `(Score: 0.75)`.
* **Formatierung:** Markdown-Header pro Quelle.
* *Ziel:* Kleine Modelle (SLMs wie Phi-3) benötigen diese expliziten Signale, um komplexe Zusammenhänge ("Warum nutzen wir X?") aus den Texten abzuleiten.
### 6.3 Schritt 3: Generation (LLM Service)
* **Service:** `app/services/llm_service.py` nutzt `httpx` (async).
* **Backend:** Ollama (lokal).
* **Modell:** `phi3:mini` (3.8B Parameter).
* **Timeout:** Erhöht auf 300s für CPU-Inference Sicherheit.
### 6.4 Schritt 4: Response & Traceability
* Die Antwort enthält die generierte Nachricht **und** die verwendeten Quellen (`sources`).
* Eine `query_id` wird generiert und durchgereicht, um späteres Feedback (WP04c) zu ermöglichen.
---
## 7. Feedback & Logging Architektur (WP04c)
Mindnet implementiert ein "Data Flywheel" zur späteren Optimierung (Self-Tuning).
### 6.1 Komponenten
### 7.1 Komponenten
* **Feedback Service (`app/services/feedback_service.py`):** Kapselt die Schreibzugriffe.
* **Storage:** Lokales Dateisystem (`data/logs/`), Format JSONL (Line-delimited JSON).
### 6.2 Log-Dateien
### 7.2 Log-Dateien
1. **`search_history.jsonl`**:
* Speichert jede Anfrage an `/query`.
* Speichert jede Anfrage an `/query` und `/chat`.
* Enthält: `query_id`, `query_text`, `timestamp`, `hits` (inkl. `score_breakdown` Snapshots).
* Zweck: Trainingsdaten ("Was hat das System gesehen?").
2. **`feedback.jsonl`**:
@ -222,12 +265,12 @@ Mindnet implementiert ein "Data Flywheel" zur späteren Optimierung (Self-Tuning
* Enthält: `query_id`, `node_id`, `score` (1-5), `comment`.
* Zweck: Labels ("War es gut?").
### 6.3 Traceability
### 7.3 Traceability
Die `query_id` (UUIDv4) wird im `/query` Response generiert und muss vom Client beim `/feedback` Aufruf mitgesendet werden. Dies ermöglicht den Join zwischen Snapshot und Bewertung.
---
## 7. Indizes & Performance
## 8. Indizes & Performance
Damit Qdrant performant bleibt, sind Payload-Indizes essenziell.
@ -240,7 +283,7 @@ Validierung erfolgt über `tests/ensure_indexes_and_show.py`.
---
## 8. Offene Punkte / Known Limitations
## 9. Offene Punkte / Known Limitations
1. **Multi-Target Inline-Relations:**
* `rel: similar_to [[A]] [[B]]` wird aktuell parser-seitig nicht unterstützt.

View File

@ -1,25 +1,25 @@
# mindnet v2.2 Pipeline Playbook
**Datei:** `docs/mindnet_pipeline_playbook_v2.2.md`
**Stand:** 2025-12-07
**Status:** **FINAL** (Konsolidiert aus WP02, WP03, WP04a)
**Quellen:** `mindnet_v2_implementation_playbook.md`, `Handbuch.md`, `chunking_strategy.md`, `docs_mindnet_retriever.md`, `wp04_retriever_scoring.md`.
**Stand:** 2025-12-08
**Status:** **FINAL** (Inkl. WP05 RAG Pipeline)
**Quellen:** `mindnet_v2_implementation_playbook.md`, `Handbuch.md`, `chunking_strategy.md`, `docs_mindnet_retriever.md`, `mindnet_admin_guide_v2.2.md`.
---
## 1. Zweck & Einordnung
Dieses Playbook ist das zentrale operative Handbuch für die **mindnet-Pipeline**. Es beschreibt, wie Daten vom Markdown-Vault in den Wissensgraphen (Qdrant) gelangen und wie der Retriever konfiguriert und betrieben wird.
Dieses Playbook ist das zentrale operative Handbuch für die **mindnet-Pipeline**. Es beschreibt, wie Daten vom Markdown-Vault in den Wissensgraphen (Qdrant) gelangen, wie der Retriever betrieben wird und wie die **RAG-Generierung** funktioniert.
**Zielgruppe:** Dev/Ops, Tech-Leads.
**Scope:**
* **Ist-Stand (WP01WP04a):** Markdown-Import, Chunking, Edge-Erzeugung, Hybrider Retriever.
* **Roadmap (Ausblick):** Self-Healing (WP06), Feedback-Loops (WP08).
* **Ist-Stand (WP01WP05):** Import, Chunking, Edge-Erzeugung, Hybrider Retriever, RAG-Chat, Feedback Loop.
* **Roadmap (Ausblick):** Technische Skizzen für Self-Healing (WP06) und Self-Tuning (WP08).
---
## 2. Die Import-Pipeline (Runbook)
Der Import ist der kritischste Prozess. Er muss **deterministisch** und **idempotent** sein. Wir nutzen `scripts/import_markdown.py` als zentralen Entrypoint.
Der Import ist der kritischste Prozess ("Data Ingestion"). Er muss **deterministisch** und **idempotent** sein. Wir nutzen `scripts/import_markdown.py` als zentralen Entrypoint.
### 2.1 Der 12-Schritte-Prozess
Gemäß WP03-Spezifikation läuft der Import intern wie folgt ab:
@ -44,7 +44,8 @@ Für regelmäßige Updates (z.B. Cronjob). Erkennt Änderungen via Hash.
export COLLECTION_PREFIX="mindnet"
# Import starten (Apply = Schreiben, Purge = Sauberer Upsert)
python3 -m scripts.import_markdown \
# Nutzt das Venv der Produktionsumgebung
/home/llmadmin/mindnet/.venv/bin/python3 -m scripts.import_markdown \
--vault ./vault \
--prefix "$COLLECTION_PREFIX" \
--apply \
@ -55,24 +56,23 @@ Für regelmäßige Updates (z.B. Cronjob). Erkennt Änderungen via Hash.
* `--purge-before-upsert`: Löscht vor dem Schreiben einer Note ihre alten Chunks/Edges. Essentiell, um "Geister-Chunks" zu vermeiden, wenn Text gekürzt wurde.
* `--sync-deletes`: Entfernt Notizen aus Qdrant, die im Vault gelöscht wurden.
### 2.3 Full Rebuild (Clean Slate)
### 2.3 Deployment & Restart (Systemd)
Nach einem Import oder Code-Update muss der API-Prozess neu gestartet werden.
# Neustart des Produktions-Services
sudo systemctl restart mindnet-prod
# Prüfung
sudo systemctl status mindnet-prod
### 2.4 Full Rebuild (Clean Slate)
Notwendig bei Änderungen an `types.yaml` (z.B. neue Chunk-Größen) oder Embedding-Modellen.
# 1. Qdrant Collections löschen und neu anlegen (Wipe inkl. Schema)
python3 -m scripts.reset_qdrant --mode wipe --prefix "mindnet" --yes
# 2. Vollständiger Import aller Dateien
python3 -m scripts.import_markdown \
--vault ./vault \
--prefix "mindnet" \
--apply
### 2.4 Baseline-Builds & Hashing
Um unnötige Updates zu vermeiden, nutzt der Importer Hashes.
* **ENV `MINDNET_HASH_COMPARE`:** Steuert, was verglichen wird.
* `Body`: Nur Textänderungen triggern Update (Standard).
* `Full`: Auch Frontmatter-Änderungen (z.B. Tags) triggern Update.
* **Flag `--baseline-modes`:** Berechnet Hashes für alle Modi vor, um spätere Wechsel der Strategie ohne Massen-Update zu ermöglichen.
python3 -m scripts.import_markdown --vault ./vault --prefix "mindnet" --apply
---
@ -116,40 +116,47 @@ Wenn in `types.yaml` für einen Typ `edge_defaults` definiert sind, werden diese
---
## 5. Retriever & Scoring
## 5. Retriever, Chat & Generation (RAG Pipeline)
Der Retriever (`app/core/retriever.py`) kombiniert Signale zur Laufzeit.
Der Datenfluss endet nicht beim Finden. Er geht weiter bis zur Antwort.
### 5.1 Konfiguration (`retriever.yaml`)
Diese Datei steuert das Ranking. Änderungen wirken sofort (API-Neustart).
scoring:
semantic_weight: 1.0 # Vektor-Ähnlichkeit
edge_weight: 0.7 # Graph-Dichte Bonus
centrality_weight: 0.5 # Zentralitäts-Bonus
### 5.2 Scoring-Formel
### 5.1 Retrieval (Hybrid)
total_score =
semantic_weight * semantic_score
+ edge_weight * edge_bonus
+ centrality_weight * centrality_bonus
+ type_weight * retriever_weight
* `retriever_weight`: Kommt aus dem Note-Payload (via `types.yaml`).
* `edge_bonus`: Summe der `confidence` aller relevanten Kanten im Subgraph.
Der `/chat` Endpunkt nutzt **Hybrid Retrieval** (Semantic + Graph), um auch logisch verbundene, aber textlich unterschiedliche Notizen zu finden (z.B. Decisions zu einem Projekt).
### 5.3 Hybrider Modus
Der Request muss `mode="hybrid"` und `expand.depth > 0` setzen, damit der Graph genutzt wird.
* **Expand:** Lädt Nachbarn der Vektor-Treffer.
* **Re-Rank:** Berechnet Boni auf diesem lokalen Graphen.
### 5.2 Context Enrichment (Das "Context Intelligence" Pattern)
Bevor der Text an das LLM geht, reichert der Router (`chat.py`) ihn an.
* **Metadaten-Injection:** `[DECISION]`, `[PROJECT]`, `[SCORE: 0.9]`.
* **Zweck:** Ermöglicht kleinen Modellen (Phi-3) das Erkennen von logischen Rollen ("Warum?" vs "Was?").
### 5.3 Generation (LLM)
* **Engine:** Ollama (lokal).
* **Modell:** `phi3:mini` (Standard).
* **Prompting:** Gesteuert über `config/prompts.yaml`.
### 5.4 Explanation Mode (WP04b)
Wird `/query` mit `explain=True` aufgerufen, führt der Retriever eine Post-Processing-Analyse durch und liefert `reasons` ("Verweist auf...", "Hoher Typ-Bonus").
---
## 6. Quality Gates & Tests
## 6. Feedback & Lernen (WP04c)
Das System schreibt kontinuierlich Logs ("Data Flywheel"):
* `data/logs/search_history.jsonl`: Trainingsdaten (Query + Ergebnisse + Breakdown).
* `data/logs/feedback.jsonl`: Labels (User-Rating zur `query_id`).
---
## 7. Quality Gates & Tests
Diese Tests garantieren die Stabilität der Pipeline.
### 6.1 Pflicht-Tests vor Commit
### 7.1 Pflicht-Tests vor Commit
1. **Payload Dryrun (Schema-Check):**
Simuliert Import, prüft JSON-Schema Konformität.
@ -157,35 +164,35 @@ Diese Tests garantieren die Stabilität der Pipeline.
python3 -m scripts.payload_dryrun --vault ./test_vault
2. **Full Edge Check (Graph-Integrität):**
Prüft Invarianten (z.B. `next` muss reziprok zu `prev` sein; keine verwaisten Kanten).
Prüft Invarianten (z.B. `next` muss reziprok zu `prev` sein).
python3 -m scripts.edges_full_check
3. **Unit Tests:**
### 7.2 Smoke-Test (E2E)
Prüft am laufenden System (Prod oder Dev), ob Semantik, Graph und Feedback funktionieren.
pytest tests/test_retriever_basic.py tests/test_retriever_edges.py
# Retriever Test
python scripts/test_retriever_smoke.py --mode hybrid --top-k 5
### 6.2 Smoke-Test (E2E)
Prüft am laufenden System, ob Semantik und Graph funktionieren.
# Chat Test (Neu WP05)
python tests/test_chat_wp05.py
python scripts/test_retriever_smoke.py \
--query "mindnet" \
--mode hybrid \
--expand-depth 1 \
--top-k 5
### 6.3 Roundtrip-Test (Datenverlust-Check)
Exportiert Qdrant zurück nach Markdown und vergleicht mit Original.
python3 -m scripts.export_markdown --out ./_export
python3 tests/compare_vaults.py --src ./vault --dst ./_export --focus body
# Feedback Test
python tests/test_feedback_smoke.py --url http://localhost:8001/query
---
## 7. Ausblick & Roadmap
## 8. Ausblick & Roadmap (Technische Skizzen)
### 7.1 Self-Healing (WP06)
Geplant: Automatisierte Jobs, die `unresolved` Referenzen periodisch prüfen und heilen, wenn die Ziel-Note erstellt wurde. Aktuell manuell via `scripts/resolve_unresolved_references.py`.
Wie entwickeln wir die Pipeline weiter?
### 7.2 Feedback-Logging (WP04c/WP08)
Geplant: Speicherung von User-Feedback zu Suchergebnissen, um `retriever.yaml` Gewichte mittelfristig automatisch zu justieren ("Self-Tuning").
### 8.1 WP-06: Decision Engine (Skizze)
**Ziel:** Aktive Entscheidungsberatung.
**Erweiterung:** Der Chat-Router lädt bei Entscheidungfragen gezielt `type: value` Notizen nach, um Optionen gegen Werte abzuwägen.
### 8.2 WP-08: Self-Tuning (Skizze)
**Ziel:** Die Gewichte in `retriever.yaml` basierend auf `feedback.jsonl` optimieren.
**Ansatz:** Ein Offline-Learning-Skript `scripts/optimize_weights.py`.
1. **Load:** Liest `search_history.jsonl` und joint mit `feedback.jsonl` via `query_id`.
2. **Analyze:** Korrelation Scores vs. User-Rating.
3. **Optimize:** Vorschlag neuer Gewichte für `retriever.yaml`.

View File

@ -1,11 +1,11 @@
# Mindnet v2.2 User Guide
**Datei:** `docs/mindnet_user_guide_v2.2.md`
**Stand:** 2025-12-07
**Status:** **FINAL** (Konsolidiert WP01WP04a)
**Stand:** 2025-12-08
**Status:** **FINAL** (Inkl. RAG & Chat)
**Quellen:** `knowledge_design.md`, `wp04_retriever_scoring.md`, `Programmplan_V2.2.md`, `Handbuch.md`.
> **Willkommen bei Mindnet.**
> Dies ist dein persönliches Wissensnetzwerk. Im Gegensatz zu einer normalen Suche (wie Google Drive oder Spotlight), die nur Texte findet, "denkt" Mindnet mit. Dieser Guide zeigt dir, wie du das System nutzt.
> Dies ist dein persönliches Wissensnetzwerk. Im Gegensatz zu einer normalen Suche (wie Google Drive), die nur Texte findet, "denkt" Mindnet mit. Es erklärt dir, warum es etwas gefunden hat, und kann dir im Chat Fragen beantworten.
---
@ -32,36 +32,34 @@ Mindnet nutzt eine **Hybride Suche**. Das heißt, es schaut auf deine Worte (Sem
Um gute Antworten zu erhalten, formuliere deine Anfragen präzise:
* **Faktensuche:** "Wie installiere ich Qdrant?"
* *Mindnet sucht:* Chunks mit technischer Anleitung. Hier gewinnt oft die reine Text-Ähnlichkeit.
* *Mindnet sucht:* Chunks mit technischer Anleitung.
* **Zusammenhänge:** "Womit hängt Projekt Alpha zusammen?"
* *Mindnet sucht:* Den Projekt-Knoten und folgt den Kanten (`depends_on`, `related_to`). Hier gewinnt der Graph.
* *Mindnet sucht:* Den Projekt-Knoten und folgt den Kanten (`depends_on`, `related_to`).
* **Entscheidungen:** "Warum nutzen wir Vektordatenbanken?"
* *Mindnet sucht:* Notizen vom Typ `decision` oder `principle`. Hier gewinnt das Typ-Gewicht (`retriever_weight`).
* *Mindnet sucht:* Notizen vom Typ `decision` oder `principle`.
### 2.2 Tipps für bessere Ergebnisse
1. **Nutze deine Sprache:** Verwende die Begriffe, die du auch in deinen Notizen benutzt hast.
2. **Kontext geben:** Statt nur "Qdrant" zu tippen (was alles finden würde), frage "Qdrant Setup für Mindnet", um den Kontext einzuschränken.
2. **Kontext geben:** Statt nur "Qdrant" zu tippen, frage "Qdrant Setup für Mindnet", um den Kontext einzuschränken.
---
## 3. Ergebnisse interpretieren
## 3. Ergebnisse interpretieren (Explanation Layer)
Wenn Mindnet antwortet, siehst du oft mehr als nur Text. Du siehst eine **Begründung**.
Mindnet liefert nicht einfach nur Treffer. Es liefert eine **Begründung** (Explanation). Achte auf diese Hinweise in der Antwort:
### 3.1 Der Score (Warum ist das hier oben?)
Mindnet berechnet für jeden Treffer einen `total_score`. Dieser ist keine Magie, sondern Mathematik:
### 3.1 Die Gründe ("Reasons")
Das System sagt dir in natürlicher Sprache, warum ein Treffer relevant ist:
* *"Hohe textuelle Übereinstimmung."* (Semantik war stark)
* *"Bevorzugt aufgrund des Typs 'decision'."* (Typ war wichtig)
* *"Verweist auf 'Projekt X' via 'depends_on'."* (Dieser Treffer ist eine wichtige Grundlage für deine Suche)
* *"Wird referenziert von 'Wichtige Notiz Y'."* (Dieser Treffer ist eine Autorität im Netzwerk)
Score = (Text-Treffer) + (Wichtigkeit) + (Vernetzung)
### 3.2 Der Score Breakdown
Wenn du es genau wissen willst, schau auf die Aufschlüsselung:
Score = (Text) + (Typ-Bonus) + (Vernetzungs-Bonus)
* **Text-Treffer (Semantik):** Wie gut passen die Worte?
* **Wichtigkeit (Typ):** Ist die Quelle eine zentrale `decision` (hoch gewichtet) oder nur eine `source` (niedrig)? Ein Treffer in einer Entscheidung schlägt oft einen Treffer in einem Buch-Exzerpt.
* **Vernetzung (Edge Bonus):** Wird dieser Inhalt oft von anderen wichtigen Projekten referenziert? Ein "zentraler" Knoten wird bevorzugt.
### 3.2 Pfade (Der Kontext)
Oft zeigt Mindnet im Hintergrund (Explainability):
*"Gefunden via: Projekt Alpha -> depends_on -> Vektordatenbank"*
Das bedeutet: Dieser Treffer ist relevant, weil er eine **Abhängigkeit** deines aktuellen Projekts ist, auch wenn deine Suchworte vielleicht nicht exakt passen. Das ist der Moment, in dem Mindnet "mitdenkt".
Ein Treffer mit niedrigem Text-Score kann trotzdem auf Platz 1 landen, wenn er extrem gut vernetzt ist ("Hidden Champion").
---
@ -70,8 +68,7 @@ Das bedeutet: Dieser Treffer ist relevant, weil er eine **Abhängigkeit** deines
Mindnet lebt von deinem Input. Du musst kein Techniker sein, um gutes Wissen zu designen. Du schreibst einfach Markdown.
### 4.1 Die Goldene Regel: "Verlinke semantisch"
Statt einfach nur `[[Link]]` zu schreiben, versuche zu sagen, *wie* es zusammenhängt. Das hilft dem Retriever später, den "Pfad" zu finden.
Statt einfach nur `[[Link]]` zu schreiben, versuche zu sagen, *wie* es zusammenhängt.
* Hängt es davon ab? -> `[[rel:depends_on Ziel]]`
* Ist es ähnlich? -> `[[rel:similar_to Ziel]]`
* Ist es eine Folge davon? -> `[[rel:caused_by Ziel]]`
@ -80,20 +77,9 @@ Statt einfach nur `[[Link]]` zu schreiben, versuche zu sagen, *wie* es zusammenh
Mindnet zerlegt deinen Text in Häppchen ("Chunks"). Hilf dabei:
* Verwende **Überschriften** (##), um Themen zu trennen.
* Schreibe **Absätze**, die einen Gedanken fassen.
* Vermeide endlose Textwüsten.
### 4.3 Typen nutzen
Setze im Frontmatter deiner Notizen den richtigen Typ.
* `type: project` -> Mindnet weiß: Das hat Aufgaben und Abhängigkeiten.
* `type: decision` -> Mindnet weiß: Das ist wichtig und begründet Dinge.
* `type: journal` -> Mindnet weiß: Das ist zeitgebunden und weniger wichtig für Grundsatzfragen.
**Beispiel:**
---
title: Entscheidung für Python
type: decision
---
Wir nutzen Python, weil es [[rel:solves AI Library Requirements]].
Setze im Frontmatter deiner Notizen den richtigen Typ (z.B. `type: project`, `type: decision`). Das steuert automatisch, wie wichtig die Notiz ist.
---
@ -101,14 +87,35 @@ Setze im Frontmatter deiner Notizen den richtigen Typ.
Mindnet wird schlauer, wenn du es pflegst.
### 5.1 Ergebnis fehlt?
### 5.1 Feedback geben (Data Flywheel)
Das System zeichnet nun auf, welche Ergebnisse es liefert (`search_history`).
Du kannst Treffer bewerten (1-5 Sterne oder Daumen hoch/runter).
* **Was passiert damit?** Mindnet speichert "Situation" (Query) und "Reaktion" (Rating).
* **Wozu?** In Zukunft analysiert das System diese Daten, um zu lernen: "Aha, der Nutzer mag Entscheidungen lieber als Quellen". Es passt seine Gewichte dann selbstständig an (Self-Tuning).
### 5.2 Ergebnis fehlt?
* Existiert die Notiz im Vault?
* Ist sie isoliert (keine Links)? Isolierte Notizen haben keinen Graph-Bonus und werden schlechter gefunden. **Verlinke sie!**
* Ist der Typ `source`? Dann ist das Gewicht niedrig (0.5). Wenn es deine eigene Meinung ist, ändere den Typ auf `concept` oder `experience`.
* Ist sie isoliert (keine Links)? Isolierte Notizen haben keinen Graph-Bonus. **Verlinke sie!**
### 5.2 Falsches Ergebnis?
* Ist die Notiz veraltet? Setze `status: archived` (wenn du Filter konfiguriert hast) oder lösche sie.
* Wird ein Begriff falsch verstanden? Füge Synonyme oder eine Definition hinzu.
---
### 5.3 Ausblick (Roadmap)
In Zukunft (WP08) wirst du Ergebnisse direkt mit "Daumen hoch/runter" bewerten können. Mindnet passt dann seine Gewichte (`retriever.yaml`) automatisch an, um deine Präferenzen zu lernen.
## 6. Chat mit Mindnet (Neu in v2.2)
Neben der Suche (`/query`) gibt es jetzt den Chat (`/chat`).
### 6.1 Wann Chat, wann Suche?
* **Suche:** Wenn du ein konkretes Dokument oder einen Link brauchst.
* **Chat:** Wenn du eine Frage hast, die sich aus mehreren Dokumenten zusammensetzt.
* *Frage:* "Was ist der Status von Projekt X und welche Risiken gibt es?"
* *Antwort:* Mindnet liest die Projekt-Notiz UND verknüpfte Risiko-Notizen und fasst sie zusammen.
### 6.2 Die Persönlichkeit
Der Chatbot agiert als dein "Digitaler Zwilling". Er versucht:
1. **Pragmatisch** zu sein (Lösungen statt Theorie).
2. **Transparent** zu sein (er sagt, wenn er etwas nicht weiß).
3. **Wertebasiert** zu handeln (er bevorzugt Lösungen, die deinen `type: value` Notizen entsprechen).
### 6.3 Quellen-Check
Der Chatbot halluziniert nicht (oder sehr selten). Unter jeder Antwort listet er die **Quellen** auf, die er genutzt hat.
* Prüfe immer: Hat er die richtige `[DECISION]`-Notiz gefunden?
* Falls nein: Füge in deinem Vault einen Link (`[[rel:depends_on]]`) hinzu, damit er den Zusammenhang beim nächsten Mal sieht.

44
tests/test_chat_wp05.py Normal file
View File

@ -0,0 +1,44 @@
import requests
import json
import sys
# Konfiguration
API_URL = "http://localhost:8002/chat/" # Port ggf. anpassen
QUESTION = "Warum nutzen wir Qdrant für mindnet?" # Eine Frage, zu der du Notizen hast
def test_chat():
payload = {
"message": QUESTION,
"top_k": 3,
"explain": True
}
print(f"Sending Question: '{QUESTION}'...")
try:
response = requests.post(API_URL, json=payload)
response.raise_for_status()
data = response.json()
print("\n=== RESPONSE ===")
print(data["answer"])
print("================\n")
print(f"Query ID: {data['query_id']}")
print(f"Latency: {data['latency_ms']}ms")
print("\nUsed Sources:")
for source in data["sources"]:
score = source.get("total_score", 0)
note = source.get("note_id", "unknown")
print(f"- {note} (Score: {score:.3f})")
except requests.exceptions.ConnectionError:
print("Error: Could not connect to API. Is it running on port 8002?")
except Exception as e:
print(f"Error: {e}")
if 'response' in locals():
print(response.text)
if __name__ == "__main__":
test_chat()