zwischenstand
This commit is contained in:
102
CLAUDE.md
102
CLAUDE.md
@@ -48,11 +48,13 @@ cmd/discord/main.go
|
||||
├── internal/agents/research/ → brain.AskQuery() + Konversationsverlauf
|
||||
├── internal/agents/memory/ → brain.RunIngest(), brain.IngestChatMessage()
|
||||
├── internal/agents/task/ → tasks.json (atomisches JSON, DueDate + Priority)
|
||||
└── internal/agents/tool/email/ → IMAP + LLM-Zusammenfassung + Move to Processed
|
||||
└── internal/agents/tool/email/ → IMAP + LLM-Triage + Zusammenfassung + Move to Archive + Cleanup
|
||||
↓
|
||||
[Daemon-Goroutine] startDaemon()
|
||||
├── Email-Check (alle N min) → #localagent Discord-Channel
|
||||
└── Morgen-Briefing (täglich 8h) → Tasks + Emails kombiniert
|
||||
├── IMAP IDLE (pro Account) → Echtzeit-Email-Benachrichtigung + LLM-Triage
|
||||
├── Morgen-Briefing (täglich 8h) → Tasks + Emails kombiniert
|
||||
├── Archiv-Aufräumen (täglich 2h) → CleanupArchiveFolders() nach retention_days
|
||||
└── Nacht-Ingest (täglich 23h) → brain.IngestEmailFolder() für alle Archiv-Ordner
|
||||
|
||||
cmd/ingest/ + cmd/ask/ (CLI-Tools, direkt nutzbar)
|
||||
↓
|
||||
@@ -76,7 +78,9 @@ Qdrant (gRPC) + LocalAI (HTTP, OpenAI-kompatibel)
|
||||
| `internal/agents/memory/` | Memory-Agent: Ingest + Chat-Speicherung |
|
||||
| `internal/agents/task/` | Task-Agent: Aufgabenverwaltung (tasks.json) |
|
||||
| `internal/agents/tool/` | Tool-Dispatcher |
|
||||
| `internal/agents/tool/email/` | IMAP-Client + LLM-Email-Analyse + Move to Processed |
|
||||
| `internal/agents/tool/email/` | IMAP-Client + LLM-Triage + Email-Analyse + IDLE-Watcher + Move to Archive + CleanupOldEmails |
|
||||
| `internal/agents/tool/rss/` | RSS-Feed-Watcher: Feeds fetchen, Artikel in Qdrant importieren, Daemon-Integration |
|
||||
| `internal/triage/` | RAG-basiertes Lernen: Triage-Entscheidungen in Qdrant speichern + suchen (eigenes Package um Import-Zyklus brain↔email zu vermeiden) |
|
||||
|
||||
### Discord Commands
|
||||
|
||||
@@ -86,11 +90,21 @@ Qdrant (gRPC) + LocalAI (HTTP, OpenAI-kompatibel)
|
||||
| `/asknobrain` | – | Direkt an LLM (kein RAG) |
|
||||
| `/memory store` | `@bot remember <text>` | Text speichern |
|
||||
| `/memory ingest` | `@bot ingest` | Markdown neu einlesen |
|
||||
| `/memory url <url>` | – | URL-Inhalt in Wissensdatenbank importieren |
|
||||
| `/memory profile <text>` | – | Fakt zum Kerngedächtnis hinzufügen (wird in jeden LLM-Prompt eingebaut) |
|
||||
| `/memory profile-show` | – | Kerngedächtnis anzeigen |
|
||||
| `/knowledge list` | – | Gespeicherte Quellen auflisten |
|
||||
| `/knowledge delete <source>` | – | Quelle aus Wissensdatenbank löschen |
|
||||
| `/task add <text> [faellig] [prioritaet]` | `@bot task add <text> [--due YYYY-MM-DD] [--priority hoch]` | Task hinzufügen |
|
||||
| `/task list/done/delete` | `@bot task <aktion>` | Aufgaben verwalten |
|
||||
| `/email summary/unread/remind` | `@bot email <aktion>` | Email-Analyse |
|
||||
| `/email ingest [ordner]` | `@bot email ingest [ordner]` | Emails aus IMAP-Ordner in Wissensdatenbank importieren (Standard: Archiv) |
|
||||
| `/email move <ordner>` | `@bot email move <ordner>` | Ungelesene Emails in Archivordner verschieben (Choices dynamisch aus `archive_folders`) |
|
||||
| `/status` | – | Bot-Gesundheit: alle Dienste + offene Tasks |
|
||||
| `/clear` | `@bot clear` | Gesprächsverlauf für diesen Channel löschen |
|
||||
| `/remember` | – | Alias für `/memory store` |
|
||||
| `/ingest` | – | Alias für `/memory ingest` |
|
||||
| *(PDF-Anhang)* | `@bot` + PDF-Datei | PDF direkt per Discord importieren |
|
||||
|
||||
## Key Patterns
|
||||
|
||||
@@ -102,19 +116,91 @@ Qdrant (gRPC) + LocalAI (HTTP, OpenAI-kompatibel)
|
||||
- **LLM-Fallback**: Email-Zusammenfassung zeigt Rohliste wenn LLM nicht erreichbar
|
||||
- **Daemon**: läuft als Goroutine im Discord-Bot-Prozess (`startDaemon()`)
|
||||
- **config.Cfg**: globale Variable — bei Tests muss `config.LoadConfig()` aufgerufen oder Cfg direkt gesetzt werden
|
||||
- **Konversationsverlauf**: Pro Discord-Channel werden die letzten 10 Frage-Antwort-Paare in-memory gehalten und als History an `brain.AskQuery()` übergeben
|
||||
- **Konversationsverlauf**: Pro Discord-Channel werden die letzten 10 Frage-Antwort-Paare in-memory gehalten und als History an `brain.AskQuery()` übergeben — Reset via `/clear` oder `@bot clear`
|
||||
- **Task-Felder**: `DueDate *time.Time` und `Priority string` (hoch/mittel/niedrig) — rückwärtskompatibel (omitempty)
|
||||
- **Email processed_folder**: Nach Zusammenfassung werden ungelesene Emails in konfigurierten IMAP-Ordner verschoben (leer = deaktiviert)
|
||||
- **Morgen-Briefing**: `dailyBriefing()` kombiniert offene Tasks (mit Fälligkeits-Highlighting) + ungelesene Emails täglich um 8:00
|
||||
- **Archiv-Cleanup**: `email.CleanupArchiveFolders()` läuft täglich um `cleanup_hour` (Standard: 2:00) — iteriert alle Accounts/`archive_folders`, löscht Emails älter als `retention_days` via IMAP `\Deleted` + `EXPUNGE`. `retention_days: 0` = dauerhaft behalten (No-op).
|
||||
- **Email-Triage**: `email.triageUnread()` klassifiziert ungelesene Emails sequentiell (eine nach der anderen) als wichtig/unwichtig via LLM. Unwichtige Emails werden in `triage_folder` verschoben. Jede Entscheidung wird in Qdrant gespeichert (`type: email_triage`). Bei nächster Klassifizierung sucht `triage.SearchSimilar()` ähnliche Entscheidungen (Score ≥ 0.7) als Few-Shot-Kontext — das Modell lernt aus der Geschichte. Triage läuft vor `SummarizeUnreadAccount()`.
|
||||
- **Nacht-Ingest**: `nightlyIngest()` läuft täglich um `ingest_hour` (Standard: 23:00) — importiert alle Emails aller Archiv-Ordner in Qdrant via `brain.IngestEmailFolder()`.
|
||||
- **User-Permissions**: `discord.allowed_users: ["user-id1", "user-id2"]` — wenn gesetzt, dürfen nur diese Discord-User-IDs den Bot nutzen. Leer = keine Einschränkung.
|
||||
- **URL-Ingest**: `brain.IngestURL(url)` — fetcht URL, extrahiert sichtbaren HTML-Text (skippt script/style/nav/footer), chunked und importiert in Qdrant. Via `/memory url <url>`.
|
||||
- **PDF-Ingest**: `brain.IngestPDF(path, source)` — extrahiert Text aus PDF via `github.com/ledongthuc/pdf`, chunked und importiert. Trigger: PDF-Anhang an @Bot-Mention.
|
||||
- **Knowledge Management**: `brain.ListSources()` (Qdrant Scroll) + `brain.DeleteBySource()` (Qdrant Filter-Delete). Via `/knowledge list` und `/knowledge delete <source>`.
|
||||
- **Core Memory**: `brain_root/core_memory.md` — persistente Fakten über den Nutzer. `brain.LoadCoreMemory()` wird in `AskQuery()` in den System-Prompt eingefügt. Via `/memory profile <text>` und `/memory profile-show`.
|
||||
- **RSS-Watcher**: `rss.Watcher` — fetcht alle `rss_feeds` aus Config, importiert neue Artikel via `brain.IngestText()`. Läuft als Goroutine im Daemon.
|
||||
- **IngestText**: `brain.IngestText(text, source, type)` — generische Ingest-Funktion für beliebige Texte (kein Datei-I/O nötig).
|
||||
- **Dynamische /email move Choices**: `patchEmailMoveChoices()` wird in `main()` nach `config.LoadConfig()` aufgerufen und ersetzt die statischen Discord-Choices mit konfigurierten `archive_folders`. Fallback auf Legacy-Hardcoding (`2Jahre`/`5Jahre`/`Archiv`) wenn keine `archive_folders` konfiguriert.
|
||||
- **Archive folder resolution**: `resolveArchiveFolder(name)` in `tool/agent.go` sucht case-insensitiv in `acc.ArchiveFolders` (Name oder IMAPFolder), dann Legacy-Fallback. Gilt für Slash-Commands und `@bot email move <name>`.
|
||||
- **IMAP IDLE**: `email.IdleWatcher` pro Account — Echtzeit-Benachrichtigung bei neuen Emails, kein Polling mehr. Race-sichere Implementierung mit `atomic.Uint32` für `numMsgs`. Automatischer Reconnect nach 60s bei Fehler.
|
||||
- **Mehrere Email-Accounts**: `config.AllEmailAccounts()` gibt alle Accounts zurück — zuerst `email_accounts:` (Liste), Fallback auf Legacy `email:` Block. Alle Email-Funktionen iterieren über alle Accounts.
|
||||
- **`/status`**: Ruft `diag.RunAll()` auf + Task-Zähler — zeigt Echtzeit-Status aller externen Dienste
|
||||
|
||||
## config.yml – Neue Felder
|
||||
## config.yml – Alle Felder
|
||||
|
||||
```yaml
|
||||
# Einzelner Email-Account (Legacy, abwärtskompatibel):
|
||||
email:
|
||||
processed_folder: "Processed" # Zielordner nach Zusammenfassung (leer = kein Verschieben)
|
||||
host: imap.strato.de
|
||||
port: 143
|
||||
user: user@example.de
|
||||
password: "..."
|
||||
starttls: true # oder tls: true für implizites TLS (Port 993)
|
||||
folder: INBOX # optional, Standard: INBOX
|
||||
processed_folder: "Processed" # nach Zusammenfassung verschieben (leer = deaktiviert)
|
||||
triage_folder: "Unwichtig" # LLM-Triage: unwichtige Emails hier ablegen (leer = deaktiviert)
|
||||
model: "" # optional: eigenes LLM-Modell für Email-Analyse
|
||||
archive_folders: # optional: Archivordner mit automatischer Bereinigung
|
||||
- name: "Archiv"
|
||||
imap_folder: "Archiv"
|
||||
retention_days: 0 # 0 = dauerhaft behalten (kein Cleanup)
|
||||
- name: "5Jahre"
|
||||
imap_folder: "5Jahre"
|
||||
retention_days: 1825
|
||||
- name: "2Jahre"
|
||||
imap_folder: "2Jahre"
|
||||
retention_days: 730
|
||||
|
||||
# Mehrere Email-Accounts (hat Vorrang vor email:):
|
||||
email_accounts:
|
||||
- name: "Privat"
|
||||
host: imap.strato.de
|
||||
port: 143
|
||||
starttls: true
|
||||
user: privat@example.de
|
||||
password: "..."
|
||||
processed_folder: "Processed"
|
||||
archive_folders: # optional, wie im email:-Block oben
|
||||
- name: "Archiv"
|
||||
imap_folder: "Archiv"
|
||||
retention_days: 0
|
||||
- name: "Arbeit"
|
||||
host: imap.firma.de
|
||||
port: 993
|
||||
tls: true
|
||||
user: jacek@firma.de
|
||||
password: "..."
|
||||
|
||||
daemon:
|
||||
task_reminder_hour: 8 # Uhrzeit des Morgen-Briefings (Standard: 8)
|
||||
channel_id: "123456789" # Discord-Channel für Daemon-Nachrichten
|
||||
email_interval_min: 30 # (veraltet, IDLE ersetzt Polling)
|
||||
task_reminder_hour: 8 # Uhrzeit des Morgen-Briefings (Standard: 8)
|
||||
cleanup_hour: 2 # Uhrzeit des täglichen Archiv-Aufräumens (Standard: 2)
|
||||
ingest_hour: 23 # Uhrzeit des nächtlichen Email-Ingests (Standard: 23)
|
||||
|
||||
# Discord User-Permissions (optional):
|
||||
discord:
|
||||
token: "..."
|
||||
guild_id: "..." # optional: nur für diese Guild registrieren
|
||||
allowed_users: # optional: leer = alle dürfen den Bot nutzen
|
||||
- "123456789" # Discord User-ID
|
||||
|
||||
# RSS-Feeds (optional):
|
||||
rss_feeds:
|
||||
- url: "https://example.com/feed.xml"
|
||||
interval_hours: 24 # Polling-Intervall (Standard: 24h)
|
||||
- url: "https://news.example.de/rss"
|
||||
interval_hours: 6
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
Reference in New Issue
Block a user