# Brain-Bot Persönlicher KI-Assistent und RAG-System in Go. Speichert Wissen in einer Qdrant-Vektordatenbank und beantwortet Fragen über ein lokales LLM. Primäres Interface: Discord-Bot. Läuft als systemd-Dienst auf einem Home-Server. ## Features - **Discord-Bot** mit Slash-Commands und @Mention - **RAG** über Markdown-Notizen, Emails, URLs, PDFs und RSS-Artikel - **Email-Management**: IMAP IDLE, Triage (wichtig/unwichtig via LLM), Archiv-Cleanup - **Task-Verwaltung** mit Fälligkeit und Priorität - **Morgen-Briefing** täglich um 8:00 (Tasks + ungelesene Emails) - **Core Memory**: persistente Nutzerfakten, automatisch in jeden LLM-Prompt eingebaut - **RSS-Watcher**: automatisches Importieren von Feed-Artikeln - **User-Permissions**: optionale Einschränkung auf bestimmte Discord-User-IDs --- ## Voraussetzungen | Dienst | Adresse | Zweck | |--------|---------|-------| | Qdrant | `192.168.1.4:6334` (gRPC) | Vektordatenbank | | LocalAI | `192.168.1.118:8080` | Embeddings + Chat (OpenAI-kompatibel) | | IMAP-Server | konfigurierbar | Email-Abruf (STARTTLS oder TLS) | | Discord | Bot-Token | Primäres Interface | --- ## Schnellstart ```bash # Einmalig: config.yml anlegen cp config.yml.example config.yml # Credentials eintragen # Bot starten go run ./cmd/discord/ # Oder: CLI-Tools go run ./cmd/ask/ "Was sind meine TODOs?" go run ./cmd/ingest/ # Markdown aus brain_root importieren ``` --- ## Discord-Commands ### Wissen abfragen | Command | Beschreibung | |---------|-------------| | `/ask ` | Wissensdatenbank abfragen (mit Gesprächsgedächtnis) | | `/research ` | Alias für `/ask` | | `/asknobrain ` | Direkt ans LLM, kein RAG | | `/clear` | Gesprächsverlauf dieses Channels löschen | ### Wissen speichern | Command | Beschreibung | |---------|-------------| | `/memory store ` | Text direkt in Wissensdatenbank speichern | | `/memory ingest` | Alle Markdown-Dateien aus `brain_root` importieren | | `/memory url ` | Webseite fetchen und importieren | | `/memory profile ` | Fakt zum Kerngedächtnis hinzufügen | | `/memory profile-show` | Kerngedächtnis anzeigen | | `/remember ` | Alias für `/memory store` | | `/ingest` | Alias für `/memory ingest` | | *(PDF-Anhang)* | PDF an @Bot schicken → automatisch importiert | ### Wissensdatenbank verwalten | Command | Beschreibung | |---------|-------------| | `/knowledge list` | Alle gespeicherten Quellen auflisten | | `/knowledge delete ` | Quelle und alle ihre Chunks löschen | ### Tasks | Command | Beschreibung | |---------|-------------| | `/task add [--due YYYY-MM-DD] [--priority hoch\|mittel\|niedrig]` | Task anlegen | | `/task list` | Alle offenen Tasks anzeigen | | `/task done ` | Task als erledigt markieren | | `/task delete ` | Task löschen | ### Email | Command | Beschreibung | |---------|-------------| | `/email summary` | Letzte Emails zusammenfassen | | `/email unread` | Ungelesene Emails zusammenfassen | | `/email remind` | Termine und Deadlines aus Emails extrahieren | | `/email ingest [ordner]` | Emails eines IMAP-Ordners in Qdrant importieren | | `/email move ` | Emails interaktiv in Archivordner verschieben | | `/email triage` | Letzte 10 Emails als wichtig/unwichtig klassifizieren | ### Sonstiges | Command | Beschreibung | |---------|-------------| | `/status` | Verbindungen prüfen, offene Tasks zählen | ### @Mention ``` @Brain @Brain task add [--due YYYY-MM-DD] [--priority hoch] @Brain task list / done / delete @Brain email summary / unread / remind / ingest [ordner] / move @Brain remember @Brain clear ``` PDF-Datei an @Brain anhängen → wird automatisch importiert. --- ## Konfiguration (`config.yml`) ```yaml # Qdrant-Vektordatenbank qdrant: host: "192.168.1.4" port: "6334" api_key: "geheimespasswort" collection: "jacek-brain" # Embedding-Modell (LocalAI) embedding: url: "http://192.168.1.118:8080/v1" model: "qwen3-embedding-4b" dimensions: 2560 # muss exakt zum Modell passen # Chat-Modell (LocalAI) chat: url: "http://192.168.1.118:8080/v1" model: "Qwen3.5-4B-Claude-4.6-Opus-Reasoning-Distilled-GGUF" # Discord-Bot discord: token: "Bot-Token" guild_id: "" # leer = global (bis 1h Verzögerung); Guild-ID = sofort allowed_users: # optional: leer = alle erlaubt - "123456789012345678" # Discord User-ID # Wissensbasis-Verzeichnis (Markdown-Dateien) brain_root: "/mnt/c/Users/jacek/AI_Brain" top_k: 5 # Anzahl der Suchergebnisse pro Anfrage score_threshold: 0.55 # Minimale Relevanz (0.0–1.0) # Task-Speicher tasks: store_path: "./tasks.json" # Daemon-Einstellungen daemon: channel_id: "1234567890" # Discord-Channel für proaktive Nachrichten task_reminder_hour: 8 # Morgen-Briefing Uhrzeit (0-23, Standard: 8) cleanup_hour: 2 # Archiv-Aufräumen Uhrzeit (0-23, Standard: 2) ingest_hour: 23 # Email-Ingest Uhrzeit (0-23, Standard: 23) # RSS-Feeds (optional) rss_feeds: - url: "https://example.com/feed.xml" interval_hours: 24 - url: "https://heise.de/rss/heise.rdf" interval_hours: 6 # Email — einzelner Account (Legacy) email: host: "imap.strato.de" port: 143 user: "user@example.de" password: "passwort" starttls: true # oder tls: true für Port 993 folder: "INBOX" processed_folder: "" # nach Zusammenfassung verschieben (leer = deaktiviert) model: "" # eigenes LLM-Modell für Email-Analyse (leer = chat.model) triage_important_folder: "Wichtig" triage_unimportant_folder: "Unwichtig" archive_folders: - name: "Archiv" imap_folder: "Archiv" retention_days: 0 # 0 = dauerhaft behalten - name: "5Jahre" imap_folder: "5Jahre" retention_days: 1825 - name: "2Jahre" imap_folder: "2Jahre" retention_days: 730 # Email — mehrere Accounts (hat Vorrang vor email:) email_accounts: - name: "Privat" host: "imap.strato.de" port: 143 starttls: true user: "privat@example.de" password: "passwort" processed_folder: "Processed" triage_important_folder: "Wichtig" triage_unimportant_folder: "Unwichtig" archive_folders: - name: "Archiv" imap_folder: "Archiv" retention_days: 0 - name: "Arbeit" host: "imap.firma.de" port: 993 tls: true user: "jacek@firma.de" password: "passwort" ``` > **Wichtig:** Wenn `embedding.model` oder `dimensions` geändert wird, muss die Qdrant-Collection neu erstellt werden (im Dashboard löschen, dann `/memory ingest` erneut ausführen). --- ## Deployment ```bash # deploy.env anlegen (einmalig) cp deploy.env.example deploy.env # Credentials eintragen: DEPLOY_HOST, DEPLOY_USER, DEPLOY_PASS, DEPLOY_DIR, SERVICE_NAME # Deploy bash deploy.sh # build + scp + systemctl restart ``` Das Script baut das Linux-Binary, überträgt es per `sshpass`/`scp` und startet den systemd-Service neu. ```bash # Systemd-Service (einmalig auf dem Server einrichten) sudo systemctl unmask brain-bot # falls masked sudo systemctl enable brain-bot ``` --- ## Entwicklung ```bash # Tests ausführen go test ./... # Bot lokal starten go run ./cmd/discord/ # Debug-Logging (LLM-Prompts + Antworten) DEBUG=1 go run ./cmd/discord/ # Abhängigkeiten aufräumen go mod tidy ``` ### Projektstruktur ``` cmd/ discord/main.go Discord-Bot + Daemon (primärer Einstiegspunkt) ask/main.go CLI: Fragen stellen ingest/main.go CLI: Markdown/JSON importieren mailtest/main.go CLI: IMAP + LLM testen internal/ config/ config.go Konfigurationsstruktur, Client-Factories config_test.go Config-Tests brain/ ask.go RAG-Suche + LLM-Antwort (AskQuery, ChatDirect) ingest.go Markdown-Import, Chunking, IngestText ingest_json.go JSON-Import (Bildbeschreibungen) ingest_email.go IMAP-Ordner → Qdrant ingest_url.go URL fetchen → Qdrant ingest_pdf.go PDF-Text → Qdrant knowledge.go ListSources, DeleteBySource core_memory.go Kerngedächtnis (core_memory.md) agents/ agent.go Agent-Interface (Request/Response/HistoryMessage) actions.go Typsichere Action-Konstanten memory/agent.go Memory-Agent (store, ingest, url, profile) research/agent.go Research-Agent (RAG-Suche mit History) task/ agent.go Task-Agent (add, list, done, delete) store.go Atomarer JSON-Speicher (tasks.json) tool/ agent.go Tool-Dispatcher + ResolveArchiveFolder email/ client.go IMAP-Client (Verbinden, Fetch, Move, Delete) summary.go Email-Zusammenfassung + LLM-Triage idle.go IMAP IDLE-Watcher (Echtzeit-Benachrichtigung) rss/ watcher.go RSS-Feed-Watcher (gofeed + brain.IngestText) triage/ triage.go RAG-basiertes Triage-Lernen (eigenes Package) diag/ diag.go Verbindungsdiagnose (/status) ``` --- ## Wie es funktioniert ### RAG-Pipeline 1. Nutzer stellt Frage via Discord 2. Frage wird in Vektor umgewandelt (Embedding-Modell) 3. Qdrant liefert die `top_k` ähnlichsten Chunks (Score ≥ `score_threshold`) 4. Core Memory + Chunks + Gesprächshistory werden in LLM-Prompt eingefügt 5. LLM generiert Antwort (Streaming) ### Email-Triage 1. IMAP IDLE meldet neue Email in Echtzeit 2. `triageUnread()` klassifiziert jede Email via LLM (wichtig/unwichtig) 3. Ähnliche frühere Entscheidungen aus Qdrant werden als Few-Shot-Kontext mitgegeben 4. Jede Entscheidung wird in Qdrant gespeichert (Typ `email_triage`) → Modell lernt 5. Wichtige Emails bleiben in INBOX, unwichtige werden in `triage_unimportant_folder` verschoben ### Core Memory - Datei: `brain_root/core_memory.md` - Inhalt: Eine Zeile pro Fakt (Markdown-Liste) - Wird bei **jeder** `AskQuery()`-Anfrage automatisch in den System-Prompt eingebaut - Kein Neustart nötig (wird bei jedem Call frisch geladen) ### Deterministische IDs Alle Qdrant-Punkte haben SHA256-basierte IDs (`source:text`). Derselbe Chunk kann beliebig oft importiert werden — Qdrant überschreibt denselben Punkt (Upsert), keine Duplikate. --- ## Externe Dienste | Dienst | Standard-Adresse | Protokoll | |--------|-----------------|-----------| | Qdrant | `192.168.1.4:6334` | gRPC | | LocalAI (Embedding) | `192.168.1.118:8080` | HTTP/OpenAI | | LocalAI (Chat) | `192.168.1.118:8080` | HTTP/OpenAI (Streaming) | | Strato IMAP | `imap.strato.de:143` | STARTTLS | | Discord | `discord.com` | WebSocket |