10 KiB
Executable File
10 KiB
Executable File
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
# 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 <frage> |
Wissensdatenbank abfragen (mit Gesprächsgedächtnis) |
/research <frage> |
Alias für /ask |
/asknobrain <frage> |
Direkt ans LLM, kein RAG |
/clear |
Gesprächsverlauf dieses Channels löschen |
Wissen speichern
| Command | Beschreibung |
|---|---|
/memory store <text> |
Text direkt in Wissensdatenbank speichern |
/memory ingest |
Alle Markdown-Dateien aus brain_root importieren |
/memory url <url> |
Webseite fetchen und importieren |
/memory profile <text> |
Fakt zum Kerngedächtnis hinzufügen |
/memory profile-show |
Kerngedächtnis anzeigen |
/remember <text> |
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 <source> |
Quelle und alle ihre Chunks löschen |
Tasks
| Command | Beschreibung |
|---|---|
/task add <text> [--due YYYY-MM-DD] [--priority hoch|mittel|niedrig] |
Task anlegen |
/task list |
Alle offenen Tasks anzeigen |
/task done <id> |
Task als erledigt markieren |
/task delete <id> |
Task löschen |
| 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 <ordner> |
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 <frage>
@Brain task add <text> [--due YYYY-MM-DD] [--priority hoch]
@Brain task list / done <id> / delete <id>
@Brain email summary / unread / remind / ingest [ordner] / move <ordner>
@Brain remember <text>
@Brain clear
PDF-Datei an @Brain anhängen → wird automatisch importiert.
Konfiguration (config.yml)
# 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.modeloderdimensionsgeändert wird, muss die Qdrant-Collection neu erstellt werden (im Dashboard löschen, dann/memory ingesterneut ausführen).
Deployment
# 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.
# Systemd-Service (einmalig auf dem Server einrichten)
sudo systemctl unmask brain-bot # falls masked
sudo systemctl enable brain-bot
Entwicklung
# 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
- Nutzer stellt Frage via Discord
- Frage wird in Vektor umgewandelt (Embedding-Modell)
- Qdrant liefert die
top_kähnlichsten Chunks (Score ≥score_threshold) - Core Memory + Chunks + Gesprächshistory werden in LLM-Prompt eingefügt
- LLM generiert Antwort (Streaming)
Email-Triage
- IMAP IDLE meldet neue Email in Echtzeit
triageUnread()klassifiziert jede Email via LLM (wichtig/unwichtig)- Ähnliche frühere Entscheidungen aus Qdrant werden als Few-Shot-Kontext mitgegeben
- Jede Entscheidung wird in Qdrant gespeichert (Typ
email_triage) → Modell lernt - Wichtige Emails bleiben in INBOX, unwichtige werden in
triage_unimportant_folderverschoben
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 |