diff --git a/README.md b/README.md new file mode 100644 index 0000000..4e986a5 --- /dev/null +++ b/README.md @@ -0,0 +1,88 @@ +# 🧠 Second Brain + +Zweites Gehirn fΓΌr OpenClaw - Langzeit- und KurzzeitgedΓ€chtnis mit Bewertung, ProaktivitΓ€t und Selbstheilung. + +## Features + +- **Engramme** - GedΓ€chtniseinheiten mit Confidence, Korrektheit, VerknΓΌpfungen +- **SQLite + FTS5** - Lokaler Speicher ohne externe AbhΓ€ngigkeiten +- **Hybrid-Retrieval** - Keyword-Suche + Reranking (spΓ€ter + Embeddings) +- **Correctness-Tracking** - Richtig/Falsch-Feedback mit Lern-Loop +- **ProaktivitΓ€t** - Heartbeat + Cron fΓΌr selbstΓ€ndige Checks +- **Fehlerheilung** - Fehler als Engramme, Mustererkennung, Auto-Fix +- **Dashboard** - HTML-Visualisierung, kein Framework nΓΆtig +- **OpenClaw-Bridge** - Direkte Integration in Agent-Sessions + +## Schnellstart + +```bash +cd /root/.openclaw/workspace/second-brain + +# Engramm hinzufΓΌgen +python3 -m src.cli add "Das ist wichtig" --tag wichtig --source user + +# Suchen +python3 -m src.cli search "wichtig" + +# Feedback geben +python3 -m src.cli confirm +python3 -m src.cli reject + +# Dashboard ΓΆffnen +python3 -m src.dashboard + +# Stats +python3 -m src.cli stats + +# Backup +python3 -m src.openclaw_bridge backup + +# Tests +python3 -m tests.test_core +``` + +## Architektur + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ OpenClaw │────▢│ Bridge │────▢│ Engram Store β”‚ +β”‚ Agent β”‚ β”‚ (Session) β”‚ β”‚ (SQLite) β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ β”‚ + β–Ό β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Heartbeat β”‚ β”‚ Retriever β”‚ +β”‚ (Cron/Check) β”‚ β”‚ (FTS + RR) β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β–Ό + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ Dashboard β”‚ + β”‚ (HTML) β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +## Module + +| Datei | Zweck | +|-------|-------| +| `src/engram.py` | Engramm-Modell, Confidence, Correctness | +| `src/store.py` | SQLite-CRUD, FTS5-Index, Backup/Export | +| `src/retriever.py` | Suche, Reranking, VerknΓΌpfungen | +| `src/cli.py` | Kommandozeilen-Interface | +| `src/openclaw_bridge.py` | OpenClaw-Integration, Heartbeat, Fehler-Handling | +| `src/dashboard.py` | HTML-Dashboard-Generator | + +## CI/CD + +- **Repo**: http://192.168.6.31:3000/Otto/second-brain +- **Issues**: 8 offen (Features, Bugs) +- **Cron**: TΓ€glich 2 Uhr Backup + +## NΓ€chste Schritte (Phase 2) + +1. Vektor-Embeddings via sentence-transformers +2. ChromaDB-Store als Alternative zu SQLite +3. PyTorch Neural Scorer +4. Streamlit-Dashboard +5. Graph-Visualisierung (cytoscape.js) diff --git a/tests/test_core.py b/tests/test_core.py new file mode 100644 index 0000000..2170702 --- /dev/null +++ b/tests/test_core.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +"""Tests fΓΌr Second Brain Kern-Module.""" + +import sys +import os +import tempfile +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src")) + +try: + from src.engram import Engram, Grounding, Correctness + from src.store import EngramStore + from src.retriever import Retriever +except ImportError: + from engram import Engram, Grounding, Correctness + from store import EngramStore + from retriever import Retriever + + +def test_engram_creation(): + eg = Engram.create("Test content", source="test", tags=["a", "b"]) + assert eg.content == "Test content" + assert eg.metadata["source"] == "test" + assert "a" in eg.metadata["tags"] + assert eg.compute_confidence() > 0 + print("βœ… test_engram_creation") + + +def test_correctness(): + c = Correctness() + assert c.score() == 0.5 + c.confirm("user") + assert c.score() == 1.0 + c.reject("user") + assert c.score() == 0.5 + print("βœ… test_correctness") + + +def test_store_crud(): + with tempfile.NamedTemporaryFile(suffix=".sqlite", delete=False) as f: + db_path = f.name + + store = EngramStore(db_path) + eg = Engram.create("Store test", tags=["store"]) + store.save(eg) + + loaded = store.get(str(eg.id)) + assert loaded is not None + assert loaded.content == "Store test" + assert store.count() == 1 + + store.delete(str(eg.id)) + assert store.count() == 0 + + store.close() + os.unlink(db_path) + print("βœ… test_store_crud") + + +def test_search(): + with tempfile.NamedTemporaryFile(suffix=".sqlite", delete=False) as f: + db_path = f.name + + store = EngramStore(db_path) + store.save(Engram.create("Python ist eine Programmiersprache", tags=["coding"])) + store.save(Engram.create("SQLite ist eine Datenbank", tags=["db"])) + store.save(Engram.create("JavaScript ist eine Sprache", tags=["coding"])) + + ret = Retriever(store) + results = ret.retrieve("Programmiersprache Datenbank") + assert len(results) >= 2 + + results = ret.retrieve("nichtsexistenterbegriff12345") + assert len(results) == 0 + + store.close() + os.unlink(db_path) + print("βœ… test_search") + + +def test_links(): + eg1 = Engram.create("Parent") + eg2 = Engram.create("Child") + eg1.add_link(eg2) + assert eg2.id in eg1.links + assert eg1.id in eg2.links + print("βœ… test_links") + + +def test_grounding(): + eg = Engram.create("Test", grounding=Grounding.VERIFIED) + assert eg.metadata["grounding"] == 4 + conf = eg.compute_confidence() + # VERIFIED sollte hΓΆheren Confidence geben als UNKNOWN + eg2 = Engram.create("Test2", grounding=Grounding.UNKNOWN) + assert eg.compute_confidence() > eg2.compute_confidence() + print("βœ… test_grounding") + + +if __name__ == "__main__": + test_engram_creation() + test_correctness() + test_store_crud() + test_search() + test_links() + test_grounding() + print("\nπŸŽ‰ Alle 6 Tests bestanden!")