zwischenstand

This commit is contained in:
Christoph K.
2026-03-20 23:24:56 +01:00
parent b1a576f61e
commit 905981cd1e
25 changed files with 3607 additions and 217 deletions

383
README.md
View File

@@ -1,43 +1,340 @@
# my-brain-importer
Persönlicher Wissens-Agent für den AI_Brain. Importiert Markdown-Notizen und Bildbeschreibungen in eine Qdrant-Vektordatenbank und beantwortet Fragen darüber mit einem lokalen LLM.
## Voraussetzungen
- Go 1.22+
- LocalAI läuft auf `embedding.url` mit dem konfigurierten Embedding-Modell geladen
- LocalAI läuft auf `chat.url` mit dem konfigurierten Chat-Modell geladen
- Qdrant läuft auf dem NAS (Port 6334 gRPC, Port 6333 Dashboard)
## Build
```bash
bash build.sh
```
Erzeugt `bin/ingest`, `bin/ingest.exe`, `bin/ask`, `bin/ask.exe`.
## Nutzung
```bash
# Markdown-Dateien aus brain_root importieren
./bin/ingest
# Alternatives Verzeichnis angeben
./bin/ingest /pfad/zum/verzeichnis
# Bildbeschreibungen aus JSON importieren
./bin/ingest image_descriptions.json
# Frage stellen
./bin/ask "Was sind meine Reisepläne für Norwegen?"
./bin/ask "Erzähl mir über Veronica Bellmore"
```
## Brain aktualisieren
Kein Löschen der Datenbank nötig — einfach `./bin/ingest` erneut ausführen:
- Bestehende Chunks → gleiche SHA256-ID → Qdrant überschreibt
- Neue Dateien → neue IDs → werden hinzugefügt
Architektur und Konfiguration: [doc/architecture.md](doc/architecture.md)
# 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 <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`)
```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.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
```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 |