Christoph K. ee7b4cc74f /deepask: Multi-Step Reasoning mit iterativer RAG-Suche
Neuer Discord-Command für tiefe Recherche in 3 Phasen:
1. Initiale Qdrant-Suche mit der Originalfrage
2. LLM generiert Folgefragen, sucht erneut (max 2 Iterationen)
3. Synthese aller gesammelten Chunks zu umfassender Antwort

Nutzbar via /deepask oder @bot deepask.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 19:49:38 +01:00
2026-03-19 13:12:57 +01:00
2026-03-20 23:24:56 +01:00
2026-03-20 07:07:38 +01:00
2026-03-19 21:46:12 +01:00
2026-03-20 23:24:56 +01:00
2026-03-20 07:07:38 +01:00
2026-03-20 07:08:00 +01:00
2026-03-20 23:24:56 +01:00
2026-03-20 23:24:56 +01:00
2026-03-20 23:24:56 +01:00
2026-03-20 23:24:56 +01:00
2026-03-20 07:08:00 +01:00

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

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 <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.01.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

# 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

  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
Description
No description provided
Readme 9.2 MiB
Languages
Go 95.4%
Shell 4.5%