WP05 #3

Merged
Lars merged 12 commits from WP05 into main 2025-12-08 16:17:49 +01:00
20 changed files with 1016 additions and 524 deletions

View File

@ -1,6 +1,6 @@
# mindnet v2.2 — Programmplan # mindnet v2.2 — Programmplan
**Version:** 2.2.1 (Post-WP04 Update) **Version:** 2.3.0 (Post-WP05 RAG Integration)
**Stand:** 2025-12-07 **Stand:** 2025-12-08
**Status:** Aktiv **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, - einen KI-Zwilling aufbaut, der ähnlich argumentiert, entscheidet und reflektiert wie du,
- über mehrere Kanäle gefüttert wird: - über mehrere Kanäle gefüttert wird:
- Obsidian-Markdown (primäre Quelle), - Obsidian-Markdown (primäre Quelle),
- Chat-basierter Agent (mindnet-Assistent), - Chat-basierter Agent (RAG-Chat aktiv),
- später: Interview-Assistent (strukturierte Dialogerfassung), - später: Interview-Assistent (strukturierte Dialogerfassung),
- automatisch neue Zusammenhänge erkennt und vernetzt (Edges, Typen, Hinweise), - automatisch neue Zusammenhänge erkennt und vernetzt (Edges, Typen, Hinweise),
- sich durch Rückmeldungen (Feedback) selbst verbessert (Self-Tuning). - 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: mindnet ist **kein statisches Wissensarchiv**, sondern ein **lebendes, lernendes Gedächtnismodell** mit Fokus auf:
- persönliche Perspektive, - persönliche Perspektive,
- erklärbare Begründungspfade, - erklärbare Begründungspfade (Why-Layer),
- kontinuierliche Erweiterbarkeit, - kontinuierliche Erweiterbarkeit,
- robuste technische Basis (lokal, nachvollziehbar, versioniert). - robuste technische Basis (lokal, nachvollziehbar, versioniert).
@ -44,13 +44,13 @@ mindnet ist **kein statisches Wissensarchiv**, sondern ein **lebendes, lernendes
Kernprinzipien der Vision: Kernprinzipien der Vision:
- **Erklärbarkeit:** - **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:** - **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. 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):** - **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:** - **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. 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. Feedback zu Antworten (gut/schlecht, relevant/nicht relevant) fließt in Score-Gewichte, Policies und ggf. Edge-Struktur ein.
- **Persönlichkeit:** - **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:** - **Incremental Growth:**
Das System muss mit wenigen, heterogenen Notizen starten und sich fortlaufend verdichten können ohne Retro-Massenmigrationen im Vault. 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. 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`). - Aufbau eines stabilen Graph-Speichers in Qdrant (`*_notes`, `*_chunks`, `*_edges`).
- Semantischer und graphbasierter Retriever (WP-04a abgeschlossen). - Semantischer und graphbasierter Retriever (WP-04a abgeschlossen).
- **Erklärbarkeit:** Why-Layer liefert Begründungen zu Treffern (WP-04b abgeschlossen). - **Erklärbarkeit:** Why-Layer liefert Begründungen zu Treffern (WP-04b abgeschlossen).
- **Feedback-Loop:** Systematisches Logging von Suche und Bewertung (WP-04c 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: - Automatisierte Erkennung von Beziehungen:
- Wikilinks, Inline-Relationen, Callout-Edges, Typ-Defaults. - Wikilinks, Inline-Relationen, Callout-Edges, Typ-Defaults.
- Schrittweises Lernen über Feedback (Score-Tuning, noch „manuell“ konfiguriert). - Schrittweises Lernen über Feedback (Score-Tuning, noch „manuell“ konfiguriert).
- „Mitwachsendes“ Schema ohne Obsidian-Umstrukturierungen: - „Mitwachsendes“ Schema ohne Obsidian-Umstrukturierungen:
- Neues Wissen kann sofort erfasst werden, - Neues Wissen kann sofort erfasst werden,
- bestehende Notizen bleiben gültig (Virtual Schema Layer). - 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). - **Decision Engine (WP-06):** Das System berät aktiv bei Entscheidungen, indem es `type: value` und `type: principle` Notizen gegen eine Fragestellung abwägt.
- **RAG-Chat:** KI antwortet in natürlicher Sprache auf Basis von Wissen und Persönlichkeit (WP-05). - **Chat Interface (WP-10):** Ablösung der Terminal-Interaktion durch ein Web-Frontend (Streamlit/React) für bessere UX und einfacheres Feedback-Geben.
- Why-Layer: KI-Assistenz für Entscheidungen mit nachvollziehbarer Argumentation.
- Interview-Assistent erstellt neue Notes automatisch (strukturierte Dialoge → Markdown). - 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. - 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 ### 3.3 Langfristig
@ -112,54 +109,53 @@ Kernprinzipien der Vision:
Die folgenden Prinzipien steuern alle Workpackages und Entscheidungen: Die folgenden Prinzipien steuern alle Workpackages und Entscheidungen:
1. **Late Binding (späte Semantik)** 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. - Struktur und Interpretation werden in Konfigurationen (z. B. `types.yaml`, `prompts.yaml`, Policies) definiert, nicht direkt in den Vault-Dateien.
- Erlaubt spätere Anpassungen ohne manuelle Massenänderungen. - Die "Persönlichkeit" des Chats ist ein Prompt-Template, kein Code.
2. **Virtual Schema Layer** 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). - Markdown-Dateien benötigen nur minimale, robuste Angaben (Titel, Typ, optionale Properties).
3. **Self-Healing Graph** 3. **Self-Healing Graph**
- Der Graph wird regelmäßig analysiert (Edges, Centrality, fehlende Links). - 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** 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). - Der Graph ist jederzeit wiederaufbaubar (Import-Pipeline idempotent).
5. **Full Explainability** 5. **Full Explainability & Context Enrichment**
- Jeder Score, jede Beziehung, jeder Vorschlag muss über: - Jeder Score, jede Beziehung muss nachvollziehbar sein.
- Edges, - Dem LLM werden Metadaten (`[DECISION]`, `[PROJECT]`) injiziert, um Halluzinationen zu vermeiden und logische Schlüsse zu ermöglichen.
- Scoring-Komponenten (Semantik, Typ, Edge-Bonus, Centrality),
- Provenienz (Notizen, Chunks, rule_id, confidence) nachvollziehbar sein.
6. **Persistence First** 6. **Persistence First**
- Obsidian bleibt die primäre Quelle der Wahrheit. - 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** 7. **Minimalinvasives Schreiben**
- Automatische Veränderungen an Markdown-Dateien erfolgen ausschließlich nach expliziter Zustimmung. - 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** 8. **Incremental Growth & Early Value**
- Das System muss bereits mit wenigen Notizen und einem kleinen Vault sinnvolle Antworten liefern (Retrieval-MVP). - Das System muss bereits mit wenigen Notizen und einem kleinen Vault sinnvolle Antworten liefern.
- 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.
- Feedback-Mechanismen werden früh eingeführt, auch wenn sie anfangs noch nicht vollautomatisch ausgewertet werden.
9. **Observability & Testbarkeit** 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) ## 5. Programmstruktur (Phasenmodell)
Phase A Fundament & Import Phase A Fundament & Import (Fertig)
Phase B Semantik, Graph & Lernen Phase B Semantik, Graph & Lernen (Fertig)
Phase C Persönlichkeitsmodell & KI-Zwilling Phase C Persönlichkeitsmodell & KI-Zwilling (Laufend)
Phase D Agenten, MCP & Interaktion Phase D Agenten, MCP & Interaktion (Startend)
Phase E Review, Refactoring, Dokumentation 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) ### WP-04b Explanation Layer ("Why-Layer") (abgeschlossen)
**Phase:** B **Phase:** B
**Status:** 🟢 abgeschlossen (Neu) **Status:** 🟢 abgeschlossen
**Ziel:** **Ziel:**
Treffererklärungen liefern, die verständlich machen, warum ein Ergebnis gewählt wurde. 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) ### WP-04c Feedback Logging & Bewertungsdaten (abgeschlossen)
**Phase:** B **Phase:** B
**Status:** 🟢 abgeschlossen (Neu) **Status:** 🟢 abgeschlossen
**Ziel:** **Ziel:**
Grundlage für Self-Tuning schaffen durch systematisches Logging. 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 **Phase:** C
**Status:** 🟡 geplant (Nächster Schritt) **Status:** 🟢 abgeschlossen
**Ziel:** **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:** **Erreichte Ergebnisse:**
- **Config:** `config/prompts.yaml` für System-Prompts, Werte und RAG-Templates. - **Infrastruktur:** Integration von Ollama (Phi-3 Mini) in den Service-Layer.
- **Service:** `llm_service.py` für Text-Generierung (Ollama-Anbindung). - **Router:** `/chat` Endpoint mit "Hybrid Retrieval Enforcement".
- **Router:** `/chat` Endpoint (Kontext-Assembly -> Prompt -> LLM). - **Context Enrichment:** Injection von Metadaten (`[TYPE]`, `[SCORE]`) in den Prompt, damit kleine Modelle (SLMs) komplexe Zusammenhänge ("Warum?") verstehen.
- **Persönlichkeit:** Definition von "Mindnet" als KI-Zwilling (Stil, Haltung). - **Config:** `prompts.yaml` steuert System-Prompt und RAG-Template.
**Aufwand / Komplexität:** **Aufwand / Komplexität:**
- Aufwand: Mittel - 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) ### WP-06 Decision Engine (geplant)
**Phase:** C **Phase:** C
**Status:** 🟡 geplant **Status:** 🟡 geplant (Priorität A)
**Ziel:** **Ziel:**
Entscheidungsunterstützung auf Basis von Wissen, Persönlichkeit und Zielen inklusive nachvollziehbarer Begründung. Entscheidungsunterstützung auf Basis von Wissen, Persönlichkeit und Zielen inklusive nachvollziehbarer Begründung.
**Umfang:** **Umfang:**
- Identifikation typischer Entscheidungssituationen. - Erweiterung des RAG-Kontexts um gezieltes Nachladen von `type: value` und `type: principle`.
- Integration von fachlichem Wissen, Erfahrungen und Persönlichkeitsmodell. - Prompt-Engineering für "Trade-off Analyse" (Pro/Contra basierend auf Werten).
- Ableitung von Entscheidungs-Templates / Heuristiken. - Output-Formatierung als Entscheidungsvorlage.
**Aufwand / Komplexität:** **Aufwand / Komplexität:**
- Aufwand: Mittel - Aufwand: Mittel
@ -360,17 +370,18 @@ Sicherstellen, dass bestehende und neue Obsidian-Vaults schrittweise in mindnet
### WP-10 Chat-Interface & Writeback (geplant) ### WP-10 Chat-Interface & Writeback (geplant)
**Phase:** D **Phase:** D
**Status:** 🟡 geplant **Status:** 🟡 geplant (Priorität B - "Quick Win")
**Ziel:** **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:** **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. - Funktionen für Q&A sowie Vorschlag neuer Notes/Edges.
**Aufwand / Komplexität:** **Aufwand / Komplexität:**
- Aufwand: Hoch - Aufwand: Hoch (durch Writeback) / Mittel (für reines Frontend)
- Komplexität: Hoch - Komplexität: Hoch
--- ---
@ -475,7 +486,8 @@ Aufräumen, dokumentieren, stabilisieren insbesondere für Onboarding Dritte
| WP04a | 🟢 | | WP04a | 🟢 |
| WP04b | 🟢 | | WP04b | 🟢 |
| WP04c | 🟢 | | WP04c | 🟢 |
| WP05 | 🟡 | | WP05 | 🟢 |
| WP05b | ⚪ |
| WP06 | 🟡 | | WP06 | 🟡 |
| WP07 | 🟡 | | WP07 | 🟡 |
| WP08 | 🟡 | | 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. - 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). - Jedes Workpackage erhält ein eigenes Chat-Fenster mit dediziertem Prompt (WP-Hand-Over).
- Programmleitung verantwortet Konsistenz und Priorisierung. - 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, - 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. - 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) app/config.py zentrale Konfiguration (ENV Settings)
Version: 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: Stand:
2025-10-06 2025-12-08
Hinweis:
Bestehende Attribute bleiben erhalten; neue WP-04 Felder sind optional.
""" """
from __future__ import annotations from __future__ import annotations
import os import os
from functools import lru_cache from functools import lru_cache
from pathlib import Path
class Settings: class Settings:
# Qdrant # Qdrant
@ -20,11 +19,20 @@ class Settings:
COLLECTION_PREFIX: str = os.getenv("MINDNET_PREFIX", "mindnet") COLLECTION_PREFIX: str = os.getenv("MINDNET_PREFIX", "mindnet")
VECTOR_SIZE: int = int(os.getenv("MINDNET_VECTOR_SIZE", "384")) VECTOR_SIZE: int = int(os.getenv("MINDNET_VECTOR_SIZE", "384"))
DISTANCE: str = os.getenv("MINDNET_DISTANCE", "Cosine") DISTANCE: str = os.getenv("MINDNET_DISTANCE", "Cosine")
# Embeddings # Embeddings
MODEL_NAME: str = os.getenv("MINDNET_MODEL", "sentence-transformers/all-MiniLM-L6-v2") 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 # API
DEBUG: bool = os.getenv("DEBUG", "false").lower() == "true" 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_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_EDGE: float = float(os.getenv("MINDNET_WP04_W_EDGE", "0.25"))
RETRIEVER_W_CENT: float = float(os.getenv("MINDNET_WP04_W_CENT", "0.05")) 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 from __future__ import annotations
import os import os
@ -22,24 +28,18 @@ import app.core.graph_adapter as ga
try: try:
import yaml # type: ignore[import] 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] yaml = None # type: ignore[assignment]
@lru_cache @lru_cache
def _get_scoring_weights() -> Tuple[float, float, float]: def _get_scoring_weights() -> Tuple[float, float, float]:
"""Liefert (semantic_weight, edge_weight, centrality_weight) für den Retriever. """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).
"""
settings = get_settings() settings = get_settings()
sem = float(getattr(settings, "RETRIEVER_W_SEM", 1.0)) sem = float(getattr(settings, "RETRIEVER_W_SEM", 1.0))
edge = float(getattr(settings, "RETRIEVER_W_EDGE", 0.0)) edge = float(getattr(settings, "RETRIEVER_W_EDGE", 0.0))
cent = float(getattr(settings, "RETRIEVER_W_CENT", 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") config_path = os.getenv("MINDNET_RETRIEVER_CONFIG", "config/retriever.yaml")
if yaml is None: if yaml is None:
return sem, edge, cent return sem, edge, cent
@ -52,22 +52,19 @@ def _get_scoring_weights() -> Tuple[float, float, float]:
edge = float(scoring.get("edge_weight", edge)) edge = float(scoring.get("edge_weight", edge))
cent = float(scoring.get("centrality_weight", cent)) cent = float(scoring.get("centrality_weight", cent))
except Exception: except Exception:
# Bei Fehlern in der YAML-Konfiguration defensiv auf Defaults zurückfallen
return sem, edge, cent return sem, edge, cent
return sem, edge, cent return sem, edge, cent
def _get_client_and_prefix() -> Tuple[Any, str]: def _get_client_and_prefix() -> Tuple[Any, str]:
"""Liefert (QdrantClient, prefix) basierend auf QdrantConfig.from_env().""" """Liefert (QdrantClient, prefix)."""
cfg = qdr.QdrantConfig.from_env() cfg = qdr.QdrantConfig.from_env()
client = qdr.get_client(cfg) client = qdr.get_client(cfg)
return client, cfg.prefix return client, cfg.prefix
def _get_query_vector(req: QueryRequest) -> List[float]: 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: if req.query_vector:
return list(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 model_name = settings.MODEL_NAME
try: 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: except TypeError:
return ec.embed_text(req.query) # type: ignore[call-arg] return ec.embed_text(req.query)
def _semantic_hits( def _semantic_hits(
@ -90,7 +87,7 @@ def _semantic_hits(
top_k: int, top_k: int,
filters: Dict[str, Any] | None = None, filters: Dict[str, Any] | None = None,
) -> List[Tuple[str, float, Dict[str, Any]]]: ) -> 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 flt = filters or None
raw_hits = qp.search_chunks_by_vector(client, prefix, vector, top=top_k, filters=flt) raw_hits = qp.search_chunks_by_vector(client, prefix, vector, top=top_k, filters=flt)
results: List[Tuple[str, float, Dict[str, Any]]] = [] results: List[Tuple[str, float, Dict[str, Any]]] = []
@ -105,7 +102,7 @@ def _compute_total_score(
edge_bonus: float = 0.0, edge_bonus: float = 0.0,
cent_bonus: float = 0.0, cent_bonus: float = 0.0,
) -> Tuple[float, float, float]: ) -> 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) raw_weight = payload.get("retriever_weight", 1.0)
try: try:
weight = float(raw_weight) weight = float(raw_weight)
@ -129,10 +126,7 @@ def _build_explanation(
subgraph: Optional[ga.Subgraph], subgraph: Optional[ga.Subgraph],
node_key: Optional[str] node_key: Optional[str]
) -> Explanation: ) -> Explanation:
""" """Erstellt ein Explanation-Objekt."""
Erstellt ein detailliertes Explanation-Objekt für einen Treffer.
Analysiert Scores, Typen und Kanten (Incoming & Outgoing).
"""
sem_w, edge_w, cent_w = _get_scoring_weights() sem_w, edge_w, cent_w = _get_scoring_weights()
try: try:
@ -155,100 +149,49 @@ def _build_explanation(
reasons: List[Reason] = [] reasons: List[Reason] = []
edges_dto: List[EdgeDTO] = [] edges_dto: List[EdgeDTO] = []
# 1. Semantische Gründe
if semantic_score > 0.85: if semantic_score > 0.85:
reasons.append(Reason( reasons.append(Reason(kind="semantic", message="Sehr hohe textuelle Übereinstimmung.", score_impact=breakdown.semantic_contribution))
kind="semantic",
message="Sehr hohe textuelle Übereinstimmung.",
score_impact=breakdown.semantic_contribution
))
elif semantic_score > 0.70: elif semantic_score > 0.70:
reasons.append(Reason( reasons.append(Reason(kind="semantic", message="Gute textuelle Übereinstimmung.", score_impact=breakdown.semantic_contribution))
kind="semantic",
message="Gute textuelle Übereinstimmung.",
score_impact=breakdown.semantic_contribution
))
# 2. Typ-Gründe
if type_weight != 1.0: if type_weight != 1.0:
msg = "Bevorzugt" if type_weight > 1.0 else "Leicht abgewertet" msg = "Bevorzugt" if type_weight > 1.0 else "Leicht abgewertet"
reasons.append(Reason( reasons.append(Reason(kind="type", message=f"{msg} aufgrund des Typs '{note_type}'.", score_impact=(sem_w * semantic_score * (type_weight - 1.0))))
kind="type",
message=f"{msg} aufgrund des Typs '{note_type}' (Gewicht: {type_weight}).",
score_impact=(sem_w * semantic_score * (type_weight - 1.0))
))
# 3. Graph-Gründe (Edges)
if subgraph and node_key and edge_bonus > 0: 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"): if hasattr(subgraph, "get_outgoing_edges"):
outgoing = subgraph.get_outgoing_edges(node_key) outgoing = subgraph.get_outgoing_edges(node_key)
for edge in outgoing: for edge in outgoing:
target = edge.get("target", "Unknown") target = edge.get("target", "Unknown")
kind = edge.get("kind", "edge") kind = edge.get("kind", "edge")
weight = edge.get("weight", 0.0) weight = edge.get("weight", 0.0)
# Nur relevante Kanten aufnehmen
if weight > 0.05: if weight > 0.05:
edges_dto.append(EdgeDTO( edges_dto.append(EdgeDTO(id=f"{node_key}->{target}:{kind}", kind=kind, source=node_key, target=target, weight=weight, direction="out"))
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"): if hasattr(subgraph, "get_incoming_edges"):
incoming = subgraph.get_incoming_edges(node_key) incoming = subgraph.get_incoming_edges(node_key)
for edge in incoming: for edge in incoming:
src = edge.get("source", "Unknown") src = edge.get("source", "Unknown")
kind = edge.get("kind", "edge") kind = edge.get("kind", "edge")
weight = edge.get("weight", 0.0) weight = edge.get("weight", 0.0)
if weight > 0.05: if weight > 0.05:
edges_dto.append(EdgeDTO( edges_dto.append(EdgeDTO(id=f"{src}->{node_key}:{kind}", kind=kind, source=src, target=node_key, weight=weight, direction="in"))
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) all_edges = sorted(edges_dto, key=lambda e: e.weight, reverse=True)
for top_edge in all_edges[:3]: for top_edge in all_edges[:3]:
# Impact schätzen (grob, da Edge-Bonus eine Summe ist)
impact = edge_w * top_edge.weight 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: if cent_bonus > 0.01:
reasons.append(Reason( reasons.append(Reason(kind="centrality", message="Knoten liegt zentral im Kontext.", score_impact=breakdown.centrality_contribution))
kind="centrality",
message="Knoten liegt zentral im Kontext.",
score_impact=breakdown.centrality_contribution
))
return Explanation( return Explanation(breakdown=breakdown, reasons=reasons, related_edges=edges_dto if edges_dto else None)
breakdown=breakdown,
reasons=reasons,
related_edges=edges_dto if edges_dto else None
)
# --- End Explanation Logic ---
def _extract_expand_options(req: QueryRequest) -> Tuple[int, List[str] | 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) expand = getattr(req, "expand", None)
if not expand: if not expand:
return 0, None return 0, None
@ -278,14 +221,10 @@ def _build_hits_from_semantic(
top_k: int, top_k: int,
used_mode: str, used_mode: str,
subgraph: ga.Subgraph | None = None, subgraph: ga.Subgraph | None = None,
explain: bool = False, # WP-04b explain: bool = False,
) -> QueryResponse: ) -> QueryResponse:
"""Baut aus Raw-Hits und optionalem Subgraph strukturierte QueryHits. """Baut strukturierte QueryHits."""
WP-04b: Wenn explain=True, wird _build_explanation aufgerufen.
"""
t0 = time.time() t0 = time.time()
enriched: List[Tuple[str, float, Dict[str, Any], float, float, float]] = [] enriched: List[Tuple[str, float, Dict[str, Any], float, float, float]] = []
for pid, semantic_score, payload in hits: for pid, semantic_score, payload in hits:
@ -303,26 +242,14 @@ def _build_hits_from_semantic(
except Exception: except Exception:
cent_bonus = 0.0 cent_bonus = 0.0
total, edge_bonus, cent_bonus = _compute_total_score( total, edge_bonus, cent_bonus = _compute_total_score(semantic_score, payload, edge_bonus=edge_bonus, cent_bonus=cent_bonus)
semantic_score,
payload,
edge_bonus=edge_bonus,
cent_bonus=cent_bonus,
)
enriched.append((pid, float(semantic_score), payload, total, edge_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) enriched_sorted = sorted(enriched, key=lambda h: h[3], reverse=True)
limited = enriched_sorted[: max(1, top_k)] limited = enriched_sorted[: max(1, top_k)]
results: List[QueryHit] = [] results: List[QueryHit] = []
for pid, semantic_score, payload, total, edge_bonus, cent_bonus in limited: 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 explanation_obj = None
if explain: if explain:
explanation_obj = _build_explanation( explanation_obj = _build_explanation(
@ -331,59 +258,51 @@ def _build_hits_from_semantic(
edge_bonus=edge_bonus, edge_bonus=edge_bonus,
cent_bonus=cent_bonus, cent_bonus=cent_bonus,
subgraph=subgraph, subgraph=subgraph,
node_key=node_key node_key=payload.get("chunk_id") or payload.get("note_id")
) )
results.append( # FIX: Hier holen wir jetzt den Textinhalt (text, content oder page_content) aus dem Payload
QueryHit( text_content = payload.get("page_content") or payload.get("text") or payload.get("content")
node_id=str(pid),
note_id=note_id, results.append(QueryHit(
semantic_score=float(semantic_score), node_id=str(pid),
edge_bonus=edge_bonus, note_id=payload.get("note_id"),
centrality_bonus=cent_bonus, semantic_score=float(semantic_score),
total_score=total, edge_bonus=edge_bonus,
paths=None, centrality_bonus=cent_bonus,
source={ total_score=total,
"path": path, paths=None,
"section": section, source={
}, "path": payload.get("path"),
explanation=explanation_obj "section": payload.get("section") or payload.get("section_title"),
) "text": text_content # WICHTIG: Inhalt durchreichen
) },
explanation=explanation_obj
))
dt = int((time.time() - t0) * 1000) dt = int((time.time() - t0) * 1000)
return QueryResponse(results=results, used_mode=used_mode, latency_ms=dt) return QueryResponse(results=results, used_mode=used_mode, latency_ms=dt)
def semantic_retrieve(req: QueryRequest) -> QueryResponse: def semantic_retrieve(req: QueryRequest) -> QueryResponse:
"""Reiner semantischer Retriever (ohne Edge-Expansion).""" """Reiner semantischer Retriever."""
client, prefix = _get_client_and_prefix() client, prefix = _get_client_and_prefix()
vector = _get_query_vector(req) vector = _get_query_vector(req)
top_k = req.top_k or get_settings().RETRIEVER_TOP_K top_k = req.top_k or get_settings().RETRIEVER_TOP_K
hits = _semantic_hits(client, prefix, vector, top_k=top_k, filters=req.filters) hits = _semantic_hits(client, prefix, vector, top_k=top_k, filters=req.filters)
return _build_hits_from_semantic(hits, top_k=top_k, used_mode="semantic", subgraph=None, explain=req.explain)
# explain Flag durchreichen
return _build_hits_from_semantic(
hits,
top_k=top_k,
used_mode="semantic",
subgraph=None,
explain=req.explain
)
def hybrid_retrieve(req: QueryRequest) -> QueryResponse: def hybrid_retrieve(req: QueryRequest) -> QueryResponse:
"""Hybrid-Retriever: semantische Suche + optionale Edge-Expansion.""" """Hybrid-Retriever: semantische Suche + optionale Edge-Expansion."""
client, prefix = _get_client_and_prefix() client, prefix = _get_client_and_prefix()
if req.query_vector: if req.query_vector:
vector = list(req.query_vector) vector = list(req.query_vector)
else: else:
vector = _get_query_vector(req) vector = _get_query_vector(req)
top_k = req.top_k or get_settings().RETRIEVER_TOP_K top_k = req.top_k or get_settings().RETRIEVER_TOP_K
hits = _semantic_hits(client, prefix, vector, top_k=top_k, filters=req.filters) hits = _semantic_hits(client, prefix, vector, top_k=top_k, filters=req.filters)
depth, edge_types = _extract_expand_options(req) 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") key = payload.get("chunk_id") or payload.get("note_id")
if key and key not in seed_ids: if key and key not in seed_ids:
seed_ids.append(key) seed_ids.append(key)
if seed_ids: if seed_ids:
try: try:
subgraph = ga.expand(client, prefix, seed_ids, depth=depth, edge_types=edge_types) subgraph = ga.expand(client, prefix, seed_ids, depth=depth, edge_types=edge_types)
except Exception: except Exception:
subgraph = None 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, class Retriever:
used_mode="hybrid", """
subgraph=subgraph, Wrapper-Klasse für WP-05 (Chat).
explain=req.explain """
) 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.query import router as query_router
from .routers.graph import router as graph_router from .routers.graph import router as graph_router
from .routers.tools import router as tools_router from .routers.tools import router as tools_router
# NEU: Feedback Router
from .routers.feedback import router as feedback_router from .routers.feedback import router as feedback_router
# NEU: Chat Router (WP-05)
from .routers.chat import router as chat_router
try: try:
from .routers.admin import router as admin_router from .routers.admin import router as admin_router
@ -19,7 +20,7 @@ except Exception:
admin_router = None admin_router = None
def create_app() -> FastAPI: 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() s = get_settings()
@app.get("/healthz") @app.get("/healthz")
@ -32,9 +33,11 @@ def create_app() -> FastAPI:
app.include_router(query_router, prefix="/query", tags=["query"]) app.include_router(query_router, prefix="/query", tags=["query"])
app.include_router(graph_router, prefix="/graph", tags=["graph"]) app.include_router(graph_router, prefix="/graph", tags=["graph"])
app.include_router(tools_router, prefix="/tools", tags=["tools"]) app.include_router(tools_router, prefix="/tools", tags=["tools"])
# NEU:
app.include_router(feedback_router, prefix="/feedback", tags=["feedback"]) app.include_router(feedback_router, prefix="/feedback", tags=["feedback"])
# NEU: Chat Endpoint
app.include_router(chat_router, prefix="/chat", tags=["chat"])
if admin_router: if admin_router:
app.include_router(admin_router, prefix="/admin", tags=["admin"]) 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: Zweck:
Laufzeit-Modelle für FastAPI (Requests/Responses), getrennt von JSON-Schemas. Laufzeit-Modelle für FastAPI (Requests/Responses).
Deckt die Graph-/Retriever-Endpunkte ab. WP-05 Update: Chat-Modelle.
Enthält Erweiterungen für WP-04b (Explanation Layer) und WP-04c (Feedback).
Kompatibilität:
Python 3.12+, Pydantic 2.x, FastAPI 0.110+
Version: Version:
0.3.0 (Update für WP-04c Feedback) 0.4.0 (Update für WP-05 Chat)
Stand: Stand:
2025-12-07 2025-12-08
""" """
from __future__ import annotations from __future__ import annotations
@ -42,7 +39,7 @@ class NodeDTO(BaseModel):
class EdgeDTO(BaseModel): class EdgeDTO(BaseModel):
"""Darstellung einer Kante im API-Graph.""" """Darstellung einer Kante im API-Graph."""
id: str id: str
kind: str # String statt Literal, um flexibel für Custom-Types zu bleiben kind: str
source: str source: str
target: str target: str
weight: float weight: float
@ -53,11 +50,7 @@ class EdgeDTO(BaseModel):
class QueryRequest(BaseModel): class QueryRequest(BaseModel):
""" """
Request für /query: 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)
""" """
mode: Literal["semantic", "edge", "hybrid"] = "hybrid" mode: Literal["semantic", "edge", "hybrid"] = "hybrid"
query: Optional[str] = None query: Optional[str] = None
@ -71,7 +64,7 @@ class QueryRequest(BaseModel):
class FeedbackRequest(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") query_id: str = Field(..., description="ID der ursprünglichen Suche")
node_id: str = Field(..., description="ID des bewerteten Treffers") node_id: str = Field(..., description="ID des bewerteten Treffers")
@ -79,19 +72,28 @@ class FeedbackRequest(BaseModel):
comment: Optional[str] = None 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 --- # --- WP-04b Explanation Models ---
class ScoreBreakdown(BaseModel): class ScoreBreakdown(BaseModel):
"""Aufschlüsselung der Score-Komponenten.""" """Aufschlüsselung der Score-Komponenten."""
semantic_contribution: float = Field(..., description="W_sem * semantic_score * weight") semantic_contribution: float
edge_contribution: float = Field(..., description="W_edge * edge_bonus") edge_contribution: float
centrality_contribution: float = Field(..., description="W_cent * centrality_bonus") centrality_contribution: float
# Rohwerte
raw_semantic: float raw_semantic: float
raw_edge_bonus: float raw_edge_bonus: float
raw_centrality: float raw_centrality: float
node_weight: float = Field(..., description="Typ-Gewicht (retriever_weight)") node_weight: float
class Reason(BaseModel): class Reason(BaseModel):
@ -115,25 +117,17 @@ class QueryHit(BaseModel):
"""Einzelnes Trefferobjekt für /query.""" """Einzelnes Trefferobjekt für /query."""
node_id: str node_id: str
note_id: Optional[str] note_id: Optional[str]
# Flache Scores
semantic_score: float semantic_score: float
edge_bonus: float edge_bonus: float
centrality_bonus: float centrality_bonus: float
total_score: float total_score: float
paths: Optional[List[List[Dict]]] = None paths: Optional[List[List[Dict]]] = None
source: Optional[Dict] = None source: Optional[Dict] = None
# WP-04b: Erklärungsobjekt
explanation: Optional[Explanation] = None explanation: Optional[Explanation] = None
class QueryResponse(BaseModel): class QueryResponse(BaseModel):
""" """Antwortstruktur für /query."""
Antwortstruktur für /query (Liste von Treffern + Telemetrie).
Enthält query_id für Traceability (WP-04c).
"""
query_id: str = Field(default_factory=lambda: str(uuid.uuid4())) query_id: str = Field(default_factory=lambda: str(uuid.uuid4()))
results: List[QueryHit] results: List[QueryHit]
used_mode: str used_mode: str
@ -146,3 +140,13 @@ class GraphResponse(BaseModel):
nodes: List[NodeDTO] nodes: List[NodeDTO]
edges: List[EdgeDTO] edges: List[EdgeDTO]
stats: Dict[str, int] 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 # mindnet v2.2 Knowledge Design Manual
**Datei:** `docs/mindnet_knowledge_design_manual_v2.2.md` **Datei:** `docs/mindnet_knowledge_design_manual_v2.2.md`
**Stand:** 2025-12-07 **Stand:** 2025-12-08
**Status:** **FINAL** (Konsolidiert aus WP01WP04a) **Status:** **FINAL** (Integrierter Stand WP01WP05)
**Quellen:** `knowledge_design.md`, `TYPE_REGISTRY_MANUAL.md`, `chunking_strategy.md`, `mindnet_functional_architecture.md`, `WP03/WP04 Hinweise`. **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. 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 ### 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“ ### 1.2 Der Vault als „Source of Truth“
Die Markdown-Dateien in deinem Vault sind die **einzige Quelle der Wahrheit**. 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 aliases: [Alpha Projekt, Project A] # Synonyme für die Suche
visibility: internal # internal (default), public, private 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 ### 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: Mindnet unterscheidet verschiedene Wissensarten. Wähle den Typ, der die **Rolle** der Notiz am besten beschreibt:
| Typ | Beschreibung & Einsatzzweck | | Typ | Beschreibung & Einsatzzweck | Wichtigkeit für Chat |
| :--- | :--- | | :--- | :--- | :--- |
| **`concept`** | Fachbegriffe, Theorien, Modelle. Zeitloses Wissen. (z. B. "Vektordatenbank") | | **`concept`** | Fachbegriffe, Theorien. Zeitloses Wissen. | Mittel (Basiswissen) |
| **`project`** | Ein Vorhaben mit Ziel, Dauer und Aufgaben. (z. B. "Aufbau Homelab") | | **`project`** | Ein Vorhaben mit Ziel, Dauer und Aufgaben. | Hoch (Kontext) |
| **`experience`** | Persönliche Erfahrung, Lektion oder Erkenntnis. (z. B. "Learning: Docker Networking") | | **`experience`** | Persönliche Erfahrung, Lektion oder Erkenntnis. | Sehr Hoch (Persönlichkeit) |
| **`decision`** | Eine bewusst getroffene Entscheidung inkl. Alternativen und Begründung. (z. B. "ADR-001: Nutzung von Qdrant") | | **`decision`** | Eine bewusst getroffene Entscheidung (ADR). | **Kritisch** (Begründung "Warum") |
| **`value`** | Ein persönlicher Wert oder ein Prinzip. (z. B. "Datensparsamkeit") | | **`value`** | Ein persönlicher Wert oder ein Prinzip. | **Kritisch** (Moralischer Kompass) |
| **`person`** | Eine reale Person (Netzwerk, Autor). | | **`person`** | Eine reale Person (Netzwerk, Autor). | Niedrig |
| **`event`** | Ein Ereignis (Konferenz, Meeting). | | **`journal`** | Zeitbezogener Log-Eintrag, Daily Note. | Mittel (Historie) |
| **`journal`** | Zeitbezogener Log-Eintrag, Daily Note. | | **`source`** | Externe Quelle (Buch, PDF, Artikel). | Niedrig (Faktenbasis) |
| **`source`** | Externe Quelle (Buch, PDF, Artikel), die exzerpiert wird. |
### 3.2 Zusammenspiel mit `types.yaml` ### 3.2 Zusammenspiel mit `types.yaml`
@ -87,20 +89,19 @@ Der `type` steuert im Hintergrund drei technische Mechanismen:
1. **`retriever_weight` (Wichtigkeit):** 1. **`retriever_weight` (Wichtigkeit):**
* Ein `concept` (0.6) wiegt weniger als ein `project` (0.97) oder eine `decision` (1.0). * 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):** 2. **`chunk_profile` (Textzerlegung):**
* `journal` (short): Wird fein zerlegt, da sich Themen schnell ändern. * `journal` (short): Wird fein zerlegt.
* `concept` (medium): Standard-Absätze. * `project` (long): Längere Kontext-Fenster.
* `project` (long): Längere Kontext-Fenster, um Zusammenhänge zu wahren.
3. **`edge_defaults` (Automatische Vernetzung):** 3. **`edge_defaults` (Automatische Vernetzung):**
* Mindnet ergänzt automatisch Kanten. * 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 ## 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) ### 4.1 Wikilinks (Die Basis-Referenz)
Der klassische Obsidian-Link. Der klassische Obsidian-Link.
@ -109,37 +110,27 @@ Der klassische Obsidian-Link.
* **Bedeutung:** "Diese Notiz erwähnt Qdrant." * **Bedeutung:** "Diese Notiz erwähnt Qdrant."
* **Edge-Typ:** `references` * **Edge-Typ:** `references`
* **Confidence:** 1.0
### 4.2 Inline-Relationen (Semantische Verknüpfung) ### 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]]. Daher [[rel:depends_on Qdrant]].
Dieses Konzept ist [[rel:similar_to Pinecone]]. 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:** * **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. * `similar_to`: Ähnelt / Ist vergleichbar mit.
* `related_to`: Hat zu tun mit (allgemein). * `related_to`: Hat zu tun mit (allgemein).
* `caused_by`: Wurde verursacht durch. * `caused_by`: Wurde verursacht durch.
* `solves`: Löst (Problem). * `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) ### 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]] > [!edge] related_to: [[Vector Embeddings]] [[AI Agents]]
* **Funktion:** Erzeugt `related_to`-Kanten zu allen genannten Zielen in dieser Zeile. * **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 ## Begründung
Qdrant erlaubt lokalen Betrieb und [[rel:solves Payload Filtering Requirements]]. 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 ## 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? ### 6.2 Was bedeutet das für dich?
* Du kannst dich auf den Inhalt konzentrieren. * 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. * 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.
* Deine Notizen bleiben sauber und zukunftssicher.
---
## 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 # Mindnet v2.2 Admin Guide
**Datei:** `docs/mindnet_admin_guide_v2.2.md` **Datei:** `docs/mindnet_admin_guide_v2.2.md`
**Stand:** 2025-12-08 **Stand:** 2025-12-08
**Status:** **FINAL** (Konsolidiert WP02WP04c) **Status:** **FINAL** (Inkl. RAG & LLM Ops)
**Quellen:** `Handbuch.md`, `mindnet_v2_implementation_playbook.md`, `mindnet_technical_architecture.md`, `Programmplan_V2.2.md`. **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 ### 2.1 Systemvoraussetzungen
* **OS:** Linux (Ubuntu 22.04+ empfohlen) oder macOS. * **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:** * **Hardware:**
* CPU: 2+ Cores. * CPU: 4+ Cores empfohlen (für Import & Inference).
* RAM: Min. 4GB (abhängig von der Vault-Größe und Qdrant-Index). * RAM: Min. 8GB empfohlen (4GB System + 4GB für Phi-3/Qdrant).
* Disk: SSD empfohlen für Qdrant-Performance. * Disk: SSD empfohlen für Qdrant-Performance.
### 2.2 Installation (Code) ### 2.2 Installation (Code)
@ -49,36 +49,40 @@ Wir nutzen Qdrant als Vektor-Datenbank. Persistenz ist wichtig.
-v $(pwd)/qdrant_storage:/qdrant/storage \ -v $(pwd)/qdrant_storage:/qdrant/storage \
qdrant/qdrant qdrant/qdrant
### 2.4 Konfiguration (ENV) ### 2.4 Ollama Setup (LLM Service)
Erstelle eine `.env` Datei im Root-Verzeichnis. Diese Variablen steuern das Verhalten der Skripte und der API. 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 Verbindung
QDRANT_URL="http://localhost:6333" QDRANT_URL="http://localhost:6333"
QDRANT_API_KEY="" # Leer lassen für lokale Instanzen ohne Auth
# Mindnet Core Settings # Mindnet Core Settings
COLLECTION_PREFIX="mindnet" # "mindnet_dev" für Dev-Umgebung COLLECTION_PREFIX="mindnet"
MINDNET_TYPES_FILE="./config/types.yaml" 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) # LLM Settings
VECTOR_DIM=384 MINDNET_LLM_MODEL="phi3:mini"
MINDNET_OLLAMA_URL="http://127.0.0.1:11434"
# Import-Strategie ### 2.6 Deployment via Systemd
MINDNET_HASH_COMPARE="Body" Mindnet wird als Systemdienst gestartet. Ollama läuft meist als eigener Dienst (`ollama.service`).
MINDNET_HASH_SOURCE="parsed"
### 2.5 Deployment via Systemd (Standard ab v2.2.1)
Mindnet wird nicht mehr manuell gestartet, sondern als Systemdienst.
**Production Service (`/etc/systemd/system/mindnet-prod.service`):** **Production Service (`/etc/systemd/system/mindnet-prod.service`):**
* Läuft auf Port 8001. * Läuft auf Port 8001.
* Autostart (`enabled`). * Autostart (`enabled`).
* Restart Policy: `always` (heilt Abstürze automatisch). * Restart Policy: `always`.
* Abhängigkeit: Sollte nach `docker` und `ollama` starten.
**Development Service (`/etc/systemd/system/mindnet-dev.service`):**
* Läuft auf Port 8002.
* Getrennter Ordner (`~/mindnet_dev`).
--- ---
@ -90,28 +94,24 @@ Der Vault-Zustand sollte regelmäßig (z.B. stündlich per Cronjob) nach Qdrant
**Cronjob-Beispiel (stündlich):** **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 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 ### 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 mindnet-prod
sudo systemctl status ollama
**Logischer Smoke-Test:** **Logischer Smoke-Test:**
python3 scripts/test_retriever_smoke.py --mode hybrid --url http://localhost:8001/query python3 scripts/test_retriever_smoke.py --mode hybrid --url http://localhost:8001/query
### 3.3 Logs & Monitoring ### 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):** **Troubleshooting Chat:**
`journalctl -u mindnet-prod -f` * Wenn `/chat` in den Timeout läuft (>300s): Prüfe, ob `phi3:mini` geladen ist und ob der Server überlastet ist.
* **Fachliche Logs (Data Flywheel):** * Wenn `/chat` halluziniert: Prüfe `config/prompts.yaml` und ob der Import aktuell ist.
`/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.
--- ---
@ -128,7 +128,6 @@ Wenn neue Versionen ausgerollt werden (Deployment):
3. **Dienst neustarten (Zwingend!):** 3. **Dienst neustarten (Zwingend!):**
sudo systemctl restart mindnet-prod sudo systemctl restart mindnet-prod
4. **Schema-Migration (falls nötig):** 4. **Schema-Migration (falls nötig):**
Bei Änderungen an `types.yaml` oder Index-Strukturen:
python3 -m scripts.import_markdown ... --apply python3 -m scripts.import_markdown ... --apply
--- ---
@ -162,8 +161,9 @@ Wenn die Datenbank korrupt ist:
### 6.1 Zugriffsschutz ### 6.1 Zugriffsschutz
Mindnet hat aktuell **keine integrierte Authentifizierung**. 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. * **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 ### 6.2 Typen-Governance
Änderungen an der `types.yaml` (z.B. neue Gewichte) wirken global. Änderungen an der `types.yaml` (z.B. neue Gewichte) wirken global.

View File

@ -1,7 +1,7 @@
# Mindnet v2.2 Appendices & Referenzen # Mindnet v2.2 Appendices & Referenzen
**Datei:** `docs/mindnet_appendices_v2.2.md` **Datei:** `docs/mindnet_appendices_v2.2.md`
**Stand:** 2025-12-08 **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`. **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. > 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. | | **person** | `short` | 0.50 | `related_to` | Personen-Profile. |
| **source** | `long` | 0.50 | *(keine)* | Externe Quellen (Bücher, PDFs). | | **source** | `long` | 0.50 | *(keine)* | Externe Quellen (Bücher, PDFs). |
| **event** | `short` | 0.60 | `related_to` | Meetings, Konferenzen. | | **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. | | **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). | | `COLLECTION_PREFIX` | `mindnet` | Namensraum für Collections (`{prefix}_notes` etc). |
| `MINDNET_TYPES_FILE` | `config/types.yaml` | Pfad zur Typ-Registry. | | `MINDNET_TYPES_FILE` | `config/types.yaml` | Pfad zur Typ-Registry. |
| `MINDNET_RETRIEVER_CONFIG`| `config/retriever.yaml`| Pfad zur Scoring-Konfiguration. | | `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_COMPARE` | `Body` | Vergleichsmodus für Import (`Body`, `Frontmatter`, `Full`). |
| `MINDNET_HASH_SOURCE` | `parsed` | Quelle für Hash (`parsed`, `raw`, `file`). | | `MINDNET_HASH_SOURCE` | `parsed` | Quelle für Hash (`parsed`, `raw`, `file`). |
| `VECTOR_DIM` | `384` | Dimension der Embeddings (Modellabhängig). | | `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. * **Feedback Loop:** Prozess des Loggens und Auswertens von User-Reaktionen.
* **Idempotenz:** Mehrfache Ausführung liefert gleiches Ergebnis. * **Idempotenz:** Mehrfache Ausführung liefert gleiches Ergebnis.
* **Inline-Edge:** Kante via `[[rel:type Ziel]]`. * **Inline-Edge:** Kante via `[[rel:type Ziel]]`.
* **RAG (Retrieval Augmented Generation):** Kombination aus Suche und Text-Generierung.
* **Retriever:** Suchmaschine (FastAPI). * **Retriever:** Suchmaschine (FastAPI).
* **Vault:** Ordner mit Markdown-Dateien. * **Vault:** Ordner mit Markdown-Dateien.
--- ---
## Anhang F: Workpackage Status (v2.2.1) ## Anhang F: Workpackage Status (v2.3.0)
Aktueller Implementierungsstand der Module. Aktueller Implementierungsstand der Module.
@ -131,6 +136,7 @@ Aktueller Implementierungsstand der Module.
| **WP04a**| Retriever Scoring | 🟢 Live | Hybrider Score (Semantik + Graph). | | **WP04a**| Retriever Scoring | 🟢 Live | Hybrider Score (Semantik + Graph). |
| **WP04b**| Explanation Layer | 🟢 Live | API liefert Reasons & Breakdown. | | **WP04b**| Explanation Layer | 🟢 Live | API liefert Reasons & Breakdown. |
| **WP04c**| Feedback Loop | 🟢 Live | Logging (JSONL) & Traceability aktiv. | | **WP04c**| Feedback Loop | 🟢 Live | Logging (JSONL) & Traceability aktiv. |
| **WP05** | Persönlichkeit / Chat | 🟡 Geplant | Nächster Schritt (RAG). | | **WP05** | Persönlichkeit / Chat | 🟢 Live | RAG-Chat mit Context Enrichment. |
| **WP06** | Self-Healing | 🔴 Geplant | Auto-Fixing von Broken Links. | | **WP06** | Decision Engine | 🟡 Geplant | Nächster Schritt (Logik). |
| **WP08** | Self-Tuning | 🔴 Geplant | Auto-Adjustment der Gewichte. | | **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 # Mindnet v2.2 Developer Guide
**Datei:** `docs/mindnet_developer_guide_v2.2.md` **Datei:** `docs/mindnet_developer_guide_v2.2.md`
**Stand:** 2025-12-07 **Stand:** 2025-12-08
**Status:** **FINAL** (Konsolidiert WP02WP04a) **Status:** **FINAL** (Inkl. RAG & LLM Setup)
**Quellen:** `mindnet_technical_architecture.md`, `Handbuch.md`, `docs_mindnet_retriever.md`, `mindnet_v2_implementation_playbook.md`. **Quellen:** `mindnet_technical_architecture.md`, `Handbuch.md`, `DEV_WORKFLOW.md`.
> **Zielgruppe:** Entwickler:innen. > **Zielgruppe:** Entwickler:innen.
> **Zweck:** Anleitung zum Aufsetzen der Entwicklungsumgebung, Verständnis der Modulstruktur und Durchführung von Tests. > **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. 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 │ ├── core/ # Kernlogik
│ │ ├── chunker.py # Text-Zerlegung │ │ ├── chunker.py # Text-Zerlegung
│ │ ├── derive_edges.py # Edge-Erzeugung (WP03 Logik) │ │ ├── derive_edges.py # Edge-Erzeugung (WP03 Logik)
│ │ ├── retriever.py # Scoring & Graph-Expansion (WP04 Logik) │ │ ├── retriever.py # Scoring & Hybrid Search
│ │ ├── qdrant.py # DB-Verbindung │ │ ├── qdrant.py # DB-Verbindung
│ │ └── ... │ │ └── ...
│ ├── models/ # Pydantic DTOs (Request/Response) │ ├── models/ # Pydantic DTOs
│ │ └── dto.py # Zentrale DTO-Definition
│ ├── routers/ # FastAPI Endpoints │ ├── 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 │ └── main.py # Entrypoint der API
├── config/ # YAML-Konfigurationen (Single Source of Truth) ├── config/ # YAML-Konfigurationen (Single Source of Truth)
│ ├── types.yaml # Import-Regeln │ ├── types.yaml # Import-Regeln
│ ├── prompts.yaml # LLM Prompts & RAG Templates (WP05)
│ └── retriever.yaml # Scoring-Regeln │ └── retriever.yaml # Scoring-Regeln
├── data/
│ └── logs/ # Lokale Logs (search_history.jsonl, feedback.jsonl)
├── scripts/ # CLI-Tools (Import, Diagnose, Reset) ├── scripts/ # CLI-Tools (Import, Diagnose, Reset)
├── tests/ # Pytest Suite ├── tests/ # Pytest Suite & Smoke Scripts
└── vault/ # Dein lokaler Markdown-Content (Git-ignored) └── 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 ### 2.1 Voraussetzungen
* **Python:** 3.10 oder höher. * **Python:** 3.10 oder höher.
* **Docker:** Für Qdrant. * **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). * **Vault:** Ein Ordner mit Markdown-Dateien (z.B. `./mindnet_v2_test_vault` für Tests).
### 2.2 Installation ### 2.2 Installation
@ -53,25 +65,47 @@ Der Code ist modular in `app` (Logik), `scripts` (CLI) und `config` (Steuerung)
# 3. Abhängigkeiten installieren # 3. Abhängigkeiten installieren
pip install -r requirements.txt 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) ### 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 Verbindung
QDRANT_URL="http://localhost:6333" QDRANT_URL="http://localhost:6333"
QDRANT_API_KEY="" # Leer lassen für lokal QDRANT_API_KEY="" # Leer lassen für lokal
# Mindnet Settings # Mindnet Core Settings
COLLECTION_PREFIX="mindnet_dev" COLLECTION_PREFIX="mindnet_dev"
MINDNET_TYPES_FILE="./config/types.yaml" MINDNET_TYPES_FILE="./config/types.yaml"
MINDNET_RETRIEVER_CONFIG="./config/retriever.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_COMPARE="Body"
MINDNET_HASH_SOURCE="parsed" MINDNET_HASH_SOURCE="parsed"
### 2.4 Dienste starten ### 2.4 Dienste starten (Systemd bevorzugt)
Starten von Qdrant via Docker: Auf dem Entwicklungsserver (Beelink) nutzen wir Systemd.
docker run -d --name mindnet_qdrant_dev -p 6333:6333 -p 6334:6334 qdrant/qdrant
# 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`) ### 3.1 Der Importer (`scripts.import_markdown`)
Dies ist das komplexeste Modul. Dies ist das komplexeste Modul.
* **Einstieg:** `scripts/import_markdown.py` -> `main()`. * **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. * **Idempotenz:** Der Importer muss mehrfach laufen können, ohne Duplikate zu erzeugen. Wir nutzen deterministische IDs (UUIDv5).
* **Debugging:** Wenn du am Importer arbeitest, nutze `--dry-run` oder `scripts/payload_dryrun.py`, um DB-Schreibvorgänge zu vermeiden. * **Debugging:** Nutze `--dry-run` oder `scripts/payload_dryrun.py`.
### 3.2 Edge-Logik (`app.core.derive_edges`) ### 3.2 Edge-Logik (`app.core.derive_edges`)
Hier wird entschieden, welche Kanten entstehen. 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 für die Explanation nachvollziehbar bleibt.
* **Rule-ID:** Vergib zwingend eine eindeutige `rule_id` (z.B. `custom:my_rule`), damit die Herkunft nachvollziehbar bleibt.
### 3.3 Der Retriever (`app.core.retriever`) ### 3.3 Der Retriever & Chat (`app.core.retriever` / `app.routers.chat`)
Hier passiert das Scoring und die Graph-Expansion. Hier passiert das Scoring und die Generation.
* **Gewichtung:** Hardcodiere niemals Gewichte (`0.5` etc.) im Code. Nutze `app.core.config.get_retriever_config()`, um `retriever.yaml` zu lesen. * **Hybrid Search:** Der Chat-Endpoint erzwingt `mode="hybrid"`.
* **Graph-Adapter:** Nutze `graph_adapter.expand()`, um Nachbarn zu laden. Vermeide direkte Qdrant-Point-Queries im Retriever-Code, um die Abstraktion zu wahren. * **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 ### 4.2 Integration / Pipeline Tests
Prüfen den Datenfluss von Markdown bis Qdrant-JSON. 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 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 python3 -m scripts.edges_full_check
### 4.3 Smoke Tests (E2E) ### 4.3 Smoke Tests (E2E)
Prüfen das laufende System (API) gegen eine echte (lokale) Qdrant-Instanz. Prüfen das laufende System (API) gegen eine echte Qdrant-Instanz und Ollama.
# 1. API im Hintergrund starten
uvicorn app.main:app --port 8000 &
# 2. Smoke Test feuern # 1. Retriever Test (Hybrid + Explanation)
python scripts/test_retriever_smoke.py --query "Test" --mode hybrid --top-k 5 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. Guidelines für Erweiterungen
### 5.1 Neuen Note-Typ hinzufügen ### 5.1 Neuen Note-Typ hinzufügen
* **Nicht im Code!** Bearbeite `config/types.yaml`. * Bearbeite `config/types.yaml`.
* Definiere `chunk_profile` und `retriever_weight`. * Definiere `chunk_profile`, `retriever_weight` und `edge_defaults`.
* Führe einen **Re-Import** durch, damit die Änderungen wirksam werden. * Führe einen **Re-Import** durch.
### 5.2 Schema-Änderungen ### 5.2 Persönlichkeit anpassen (Prompt Engineering)
Wenn du Felder in `mindnet_notes` oder `mindnet_chunks` hinzufügst: * Bearbeite `config/prompts.yaml`.
1. Passe `app/core/note_payload.py` bzw. `chunk_payload.py` an. * Änderungen sind sofort aktiv (kein Neustart nötig, da pro Request geladen, sofern nicht gecached).
2. Aktualisiere `tests/assert_payload_schema.py`. * **Prompt-Regel:** Kleine Modelle (Phi-3) brauchen strikte Anweisungen (z.B. "Achte auf [DECISION]").
3. Führe `scripts/reset_qdrant.py` (Wipe) aus, um die Indizes neu anzulegen.
### 5.3 Neue API-Endpunkte ### 5.3 Neue API-Endpunkte
* Erstelle einen neuen Router in `app/routers/`. * 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`. * 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):** **Einen einzelnen File inspizieren (Parser-Sicht):**
python3 tests/inspect_one_note.py --file ./vault/MeinFile.md python3 tests/inspect_one_note.py --file ./vault/MeinFile.md
**Warum wird mein File nicht importiert?** **Live-Logs sehen (Beelink):**
python3 -m scripts.diagnose_changed --vault ./vault --all journalctl -u mindnet-dev -f

View File

@ -1,9 +1,9 @@
# Mindnet v2.2 Fachliche Architektur # Mindnet v2.2 Fachliche Architektur
**Datei:** `docs/mindnet_functional_architecture_v2.2.md` **Datei:** `docs/mindnet_functional_architecture_v2.2.md`
**Stand:** 2025-12-08 **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. 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):** 1. **Suche (Situation):**
Wenn ein Nutzer eine Anfrage stellt, loggt Mindnet die "Situation": Wenn ein Nutzer eine Anfrage stellt, loggt Mindnet die "Situation":
* Den Query-Text. * 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: Der Retriever kann Edges gewichten:
- **provenance**: *explicit* > *rule* - **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. - `references` „Nutzt/erwähnt“; neutral, aber stützend für Kontext.
- `related_to` Ähnlichkeit/Verwandtschaft (symmetrisch interpretierbar). - `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**: Frontmatter-Eigenschaften (Properties) bleiben **minimiert**:
- **type** steuert Typ-Defaults via Registry (Pflicht für differenziertes Verhalten). - **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). - 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. - 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)** **Markdown (Auszug)**
# Relations Showcase # 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) ## 13) Referenzen (Projektdateien & Leitlinien)
- Import-Pipeline & Registry-Auflösung: `scripts/import_markdown.py`. - Import-Pipeline & Registry-Auflösung: `scripts/import_markdown.py`.
- Kantenbildung (V2-Logic): `app/core/derive_edges.py`. - Kantenbildung (V2-Logic): `app/core/derive_edges.py`.
- Typ-Registry: `config/types.yaml` & `TYPE_REGISTRY_MANUAL.md`. - Typ-Registry: `config/types.yaml` & `TYPE_REGISTRY_MANUAL.md`.
- Retriever-Scoring & Explanation: `app/core/retriever.py`. - Retriever-Scoring & Explanation: `app/core/retriever.py`.
- Persönlichkeit & Chat: `config/prompts.yaml` & `app/routers/chat.py`.
- Logging Service: `app/services/feedback_service.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 # Mindnet v2.2 Technische Architektur
**Datei:** `docs/mindnet_technical_architecture_v2.2.md` **Datei:** `docs/mindnet_technical_architecture_v2.2.md`
**Stand:** 2025-12-08 **Stand:** 2025-12-08
**Status:** **FINAL** (Integrierter Stand WP01WP04c) **Status:** **FINAL** (Integrierter Stand WP01WP05)
**Quellen:** `mindnet_technical_architecture.md`, `chunking_strategy.md`, `Handbuch.md`, `wp04_retriever_scoring.md`. **Quellen:** `Programmplan_V2.2.md`, `Handbuch.md`, `chunking_strategy.md`, `wp04_retriever_scoring.md`.
> **Ziel dieses Dokuments:** > **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. Systemüberblick
### 1.1 Architektur-Zielbild ### 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). 1. **Source:** Markdown-Notizen in einem Vault (Obsidian-kompatibel).
2. **Pipeline:** Ein Python-Importer transformiert diese in **Notes**, **Chunks** und **Edges**. 2. **Pipeline:** Ein Python-Importer transformiert diese in **Notes**, **Chunks** und **Edges**.
3. **Storage:** 3. **Storage:**
* **Qdrant:** Vektor-Datenbank für Graph und Semantik (Collections: notes, chunks, edges). * **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). * **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. 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 ### 1.2 Verzeichnisstruktur & Komponenten (Post-WP05)
Die Codebasis ist modular aufgebaut.
/mindnet/ /mindnet/
├── app/ ├── app/
@ -38,17 +38,20 @@ Die Codebasis ist modular aufgebaut.
│ │ ├── derive_edges.py # Logik der Kantenableitung (WP03) │ │ ├── derive_edges.py # Logik der Kantenableitung (WP03)
│ │ ├── graph_adapter.py # Subgraph & Reverse-Lookup (WP04b) │ │ ├── graph_adapter.py # Subgraph & Reverse-Lookup (WP04b)
│ │ └── retriever.py # Scoring, Expansion & Explanation (WP04a/b) │ │ └── retriever.py # Scoring, Expansion & Explanation (WP04a/b)
│ ├── models/ # Pydantic DTOs (QueryHit, Explanation, FeedbackRequest) │ ├── models/ # Pydantic DTOs (QueryHit, Explanation, FeedbackRequest, ChatRequest)
│ ├── routers/ │ ├── routers/
│ │ ├── query.py # Such-Endpunkt │ │ ├── query.py # Such-Endpunkt
│ │ ├── chat.py # RAG-Chat Endpunkt (WP05)
│ │ ├── feedback.py # Feedback-Endpunkt (WP04c) │ │ ├── feedback.py # Feedback-Endpunkt (WP04c)
│ │ └── ... │ │ └── ...
│ ├── services/ │ ├── services/
│ │ ├── feedback_service.py # Logging-Logik (WP04c) │ │ ├── llm_service.py # Ollama Client (WP05)
│ │ ├── feedback_service.py # JSONL Logging (WP04c)
│ │ └── embeddings_client.py │ │ └── embeddings_client.py
├── config/ ├── config/
│ ├── types.yaml # Typ-Definitionen (Import-Zeit) │ ├── types.yaml # Typ-Definitionen (Import-Zeit)
│ └── retriever.yaml # Scoring-Gewichte (Laufzeit) │ ├── retriever.yaml # Scoring-Gewichte (Laufzeit)
│ └── prompts.yaml # LLM System-Prompts & Templates (WP05)
├── data/ ├── data/
│ └── logs/ # Lokale JSONL-Logs (WP04c) │ └── logs/ # Lokale JSONL-Logs (WP04c)
├── scripts/ ├── scripts/
@ -122,24 +125,37 @@ Die Logik ist ausgelagert in YAML-Dateien.
### 3.1 Typ-Registry (`config/types.yaml`) ### 3.1 Typ-Registry (`config/types.yaml`)
Steuert den Import-Prozess. Steuert den Import-Prozess.
* **Priorität:** Frontmatter > Pfad > Default. * **Priorität:** Frontmatter > Pfad > Default.
* **Inhalt:** * **Inhalt (Beispiel):**
```yaml
types: types:
concept: concept:
chunk_profile: medium chunk_profile: medium
edge_defaults: ["references", "related_to"] edge_defaults: ["references", "related_to"]
retriever_weight: 0.60 retriever_weight: 0.60
```
### 3.2 Retriever-Config (`config/retriever.yaml`) ### 3.2 Retriever-Config (`config/retriever.yaml`)
Steuert das Scoring zur Laufzeit (WP04a). Steuert das Scoring zur Laufzeit (WP04a).
* **Inhalt:** * **Inhalt (Beispiel):**
```yaml
scoring: scoring:
semantic_weight: 1.0 semantic_weight: 1.0
edge_weight: 0.7 edge_weight: 0.7
centrality_weight: 0.5 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 ## 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 ### 5.1 Betriebsmodi
* **Semantic:** Reine Vektorsuche. Schnell. * **Semantic:** Reine Vektorsuche. Schnell.
@ -193,10 +209,10 @@ $$
### 5.3 Explanation Layer (WP04b) ### 5.3 Explanation Layer (WP04b)
Der Retriever kann Ergebnisse erklären (`explain=True`). Der Retriever kann Ergebnisse erklären (`explain=True`).
* **Logik:** * **Logik:**
* Berechnung des `ScoreBreakdown` (Anteile von Semantik, Graph, Typ). * Berechnung des `ScoreBreakdown` (Anteile von Semantik, Graph, Typ).
* Analyse des lokalen Subgraphen mittels `graph_adapter.py`. * Analyse des lokalen Subgraphen mittels `graph_adapter.py`.
* **Incoming Edges (Authority):** Wer zeigt auf diesen Treffer? (z.B. "Referenziert von...") * **Incoming Edges (Authority):** Wer zeigt auf diesen Treffer? (z.B. "Referenziert von...")
* **Outgoing Edges (Hub):** Worauf zeigt dieser Treffer? (z.B. "Verweist auf...") * **Outgoing Edges (Hub):** Worauf zeigt dieser Treffer? (z.B. "Verweist auf...")
* **Output:** `QueryHit` enthält ein `explanation` Objekt mit menschenlesbaren `reasons` und `related_edges`. * **Output:** `QueryHit` enthält ein `explanation` Objekt mit menschenlesbaren `reasons` und `related_edges`.
### 5.4 Graph-Expansion ### 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). 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. * **Feedback Service (`app/services/feedback_service.py`):** Kapselt die Schreibzugriffe.
* **Storage:** Lokales Dateisystem (`data/logs/`), Format JSONL (Line-delimited JSON). * **Storage:** Lokales Dateisystem (`data/logs/`), Format JSONL (Line-delimited JSON).
### 6.2 Log-Dateien ### 7.2 Log-Dateien
1. **`search_history.jsonl`**: 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). * Enthält: `query_id`, `query_text`, `timestamp`, `hits` (inkl. `score_breakdown` Snapshots).
* Zweck: Trainingsdaten ("Was hat das System gesehen?"). * Zweck: Trainingsdaten ("Was hat das System gesehen?").
2. **`feedback.jsonl`**: 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`. * Enthält: `query_id`, `node_id`, `score` (1-5), `comment`.
* Zweck: Labels ("War es gut?"). * 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. 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. 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:** 1. **Multi-Target Inline-Relations:**
* `rel: similar_to [[A]] [[B]]` wird aktuell parser-seitig nicht unterstützt. * `rel: similar_to [[A]] [[B]]` wird aktuell parser-seitig nicht unterstützt.

View File

@ -1,25 +1,25 @@
# mindnet v2.2 Pipeline Playbook # mindnet v2.2 Pipeline Playbook
**Datei:** `docs/mindnet_pipeline_playbook_v2.2.md` **Datei:** `docs/mindnet_pipeline_playbook_v2.2.md`
**Stand:** 2025-12-07 **Stand:** 2025-12-08
**Status:** **FINAL** (Konsolidiert aus WP02, WP03, WP04a) **Status:** **FINAL** (Inkl. WP05 RAG Pipeline)
**Quellen:** `mindnet_v2_implementation_playbook.md`, `Handbuch.md`, `chunking_strategy.md`, `docs_mindnet_retriever.md`, `wp04_retriever_scoring.md`. **Quellen:** `mindnet_v2_implementation_playbook.md`, `Handbuch.md`, `chunking_strategy.md`, `docs_mindnet_retriever.md`, `mindnet_admin_guide_v2.2.md`.
--- ---
## 1. Zweck & Einordnung ## 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. **Zielgruppe:** Dev/Ops, Tech-Leads.
**Scope:** **Scope:**
* **Ist-Stand (WP01WP04a):** Markdown-Import, Chunking, Edge-Erzeugung, Hybrider Retriever. * **Ist-Stand (WP01WP05):** Import, Chunking, Edge-Erzeugung, Hybrider Retriever, RAG-Chat, Feedback Loop.
* **Roadmap (Ausblick):** Self-Healing (WP06), Feedback-Loops (WP08). * **Roadmap (Ausblick):** Technische Skizzen für Self-Healing (WP06) und Self-Tuning (WP08).
--- ---
## 2. Die Import-Pipeline (Runbook) ## 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 ### 2.1 Der 12-Schritte-Prozess
Gemäß WP03-Spezifikation läuft der Import intern wie folgt ab: 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" export COLLECTION_PREFIX="mindnet"
# Import starten (Apply = Schreiben, Purge = Sauberer Upsert) # 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 \ --vault ./vault \
--prefix "$COLLECTION_PREFIX" \ --prefix "$COLLECTION_PREFIX" \
--apply \ --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. * `--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. * `--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. 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) # 1. Qdrant Collections löschen und neu anlegen (Wipe inkl. Schema)
python3 -m scripts.reset_qdrant --mode wipe --prefix "mindnet" --yes python3 -m scripts.reset_qdrant --mode wipe --prefix "mindnet" --yes
# 2. Vollständiger Import aller Dateien # 2. Vollständiger Import aller Dateien
python3 -m scripts.import_markdown \ python3 -m scripts.import_markdown --vault ./vault --prefix "mindnet" --apply
--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.
--- ---
@ -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`) ### 5.1 Retrieval (Hybrid)
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
total_score = total_score =
semantic_weight * semantic_score semantic_weight * semantic_score
+ edge_weight * edge_bonus + edge_weight * edge_bonus
+ centrality_weight * centrality_bonus + centrality_weight * centrality_bonus
+ type_weight * retriever_weight + type_weight * retriever_weight
* `retriever_weight`: Kommt aus dem Note-Payload (via `types.yaml`). Der `/chat` Endpunkt nutzt **Hybrid Retrieval** (Semantic + Graph), um auch logisch verbundene, aber textlich unterschiedliche Notizen zu finden (z.B. Decisions zu einem Projekt).
* `edge_bonus`: Summe der `confidence` aller relevanten Kanten im Subgraph.
### 5.3 Hybrider Modus ### 5.2 Context Enrichment (Das "Context Intelligence" Pattern)
Der Request muss `mode="hybrid"` und `expand.depth > 0` setzen, damit der Graph genutzt wird. Bevor der Text an das LLM geht, reichert der Router (`chat.py`) ihn an.
* **Expand:** Lädt Nachbarn der Vektor-Treffer. * **Metadaten-Injection:** `[DECISION]`, `[PROJECT]`, `[SCORE: 0.9]`.
* **Re-Rank:** Berechnet Boni auf diesem lokalen Graphen. * **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. 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):** 1. **Payload Dryrun (Schema-Check):**
Simuliert Import, prüft JSON-Schema Konformität. 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 python3 -m scripts.payload_dryrun --vault ./test_vault
2. **Full Edge Check (Graph-Integrität):** 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 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) # Chat Test (Neu WP05)
Prüft am laufenden System, ob Semantik und Graph funktionieren. python tests/test_chat_wp05.py
python scripts/test_retriever_smoke.py \ # Feedback Test
--query "mindnet" \ python tests/test_feedback_smoke.py --url http://localhost:8001/query
--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
--- ---
## 7. Ausblick & Roadmap ## 8. Ausblick & Roadmap (Technische Skizzen)
### 7.1 Self-Healing (WP06) Wie entwickeln wir die Pipeline weiter?
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`.
### 7.2 Feedback-Logging (WP04c/WP08) ### 8.1 WP-06: Decision Engine (Skizze)
Geplant: Speicherung von User-Feedback zu Suchergebnissen, um `retriever.yaml` Gewichte mittelfristig automatisch zu justieren ("Self-Tuning"). **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 # Mindnet v2.2 User Guide
**Datei:** `docs/mindnet_user_guide_v2.2.md` **Datei:** `docs/mindnet_user_guide_v2.2.md`
**Stand:** 2025-12-07 **Stand:** 2025-12-08
**Status:** **FINAL** (Konsolidiert WP01WP04a) **Status:** **FINAL** (Inkl. RAG & Chat)
**Quellen:** `knowledge_design.md`, `wp04_retriever_scoring.md`, `Programmplan_V2.2.md`, `Handbuch.md`. **Quellen:** `knowledge_design.md`, `wp04_retriever_scoring.md`, `Programmplan_V2.2.md`, `Handbuch.md`.
> **Willkommen bei Mindnet.** > **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: Um gute Antworten zu erhalten, formuliere deine Anfragen präzise:
* **Faktensuche:** "Wie installiere ich Qdrant?" * **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?" * **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?" * **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 ### 2.2 Tipps für bessere Ergebnisse
1. **Nutze deine Sprache:** Verwende die Begriffe, die du auch in deinen Notizen benutzt hast. 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?) ### 3.1 Die Gründe ("Reasons")
Mindnet berechnet für jeden Treffer einen `total_score`. Dieser ist keine Magie, sondern Mathematik: 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? Ein Treffer mit niedrigem Text-Score kann trotzdem auf Platz 1 landen, wenn er extrem gut vernetzt ist ("Hidden Champion").
* **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".
--- ---
@ -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. 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" ### 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]]` * Hängt es davon ab? -> `[[rel:depends_on Ziel]]`
* Ist es ähnlich? -> `[[rel:similar_to Ziel]]` * Ist es ähnlich? -> `[[rel:similar_to Ziel]]`
* Ist es eine Folge davon? -> `[[rel:caused_by 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: Mindnet zerlegt deinen Text in Häppchen ("Chunks"). Hilf dabei:
* Verwende **Überschriften** (##), um Themen zu trennen. * Verwende **Überschriften** (##), um Themen zu trennen.
* Schreibe **Absätze**, die einen Gedanken fassen. * Schreibe **Absätze**, die einen Gedanken fassen.
* Vermeide endlose Textwüsten.
### 4.3 Typen nutzen ### 4.3 Typen nutzen
Setze im Frontmatter deiner Notizen den richtigen Typ. Setze im Frontmatter deiner Notizen den richtigen Typ (z.B. `type: project`, `type: decision`). Das steuert automatisch, wie wichtig die Notiz ist.
* `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]].
--- ---
@ -101,14 +87,35 @@ Setze im Frontmatter deiner Notizen den richtigen Typ.
Mindnet wird schlauer, wenn du es pflegst. 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? * Existiert die Notiz im Vault?
* Ist sie isoliert (keine Links)? Isolierte Notizen haben keinen Graph-Bonus und werden schlechter gefunden. **Verlinke sie!** * Ist sie isoliert (keine Links)? Isolierte Notizen haben keinen Graph-Bonus. **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`.
### 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) ## 6. Chat mit Mindnet (Neu in v2.2)
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.
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()