Email-Triage: Lernen aus IMAP-Ordnern, manuelle Korrektur, reichere Daten
- Automatisches Triage-Lernen aus Archiv-Ordnern im Nacht-Ingest: retention_days=0 (Archiv) → wichtig, retention_days>0 → unwichtig - Drei neue Discord-Commands: /email triage-history, triage-correct, triage-search - StoreDecision speichert jetzt Datum + Body-Zusammenfassung (max 200 Zeichen) - MIME-Multipart-Parsing mit PDF-Attachment-Extraktion (FetchWithBodyAndAttachments) - Deterministische IDs basierend auf Absender+Betreff (idempotente Upserts) - Rueckwaertskompatibles Parsing fuer alte Triage-Eintraege ohne Datum/Body Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,12 +3,14 @@ package tool
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"my-brain-importer/internal/agents"
|
||||
"my-brain-importer/internal/agents/tool/email"
|
||||
"my-brain-importer/internal/brain"
|
||||
"my-brain-importer/internal/config"
|
||||
"my-brain-importer/internal/triage"
|
||||
)
|
||||
|
||||
// Agent verteilt Tool-Anfragen an spezialisierte Sub-Agenten.
|
||||
@@ -50,8 +52,14 @@ func (a *Agent) handleEmail(req agents.Request) agents.Response {
|
||||
return a.handleEmailMove(req)
|
||||
case agents.ActionEmailTriage:
|
||||
return a.handleEmailTriage()
|
||||
case agents.ActionEmailTriageHistory:
|
||||
return a.handleTriageHistory(req)
|
||||
case agents.ActionEmailTriageCorrect:
|
||||
return a.handleTriageCorrect(req)
|
||||
case agents.ActionEmailTriageSearch:
|
||||
return a.handleTriageSearch(req)
|
||||
default:
|
||||
return agents.Response{Text: fmt.Sprintf("❌ Unbekannte Email-Aktion `%s`. Verfügbar: summary, unread, remind, ingest, move, triage", subAction)}
|
||||
return agents.Response{Text: fmt.Sprintf("❌ Unbekannte Email-Aktion `%s`. Verfügbar: summary, unread, remind, ingest, move, triage, triage-history, triage-correct, triage-search", subAction)}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -147,6 +155,68 @@ func (a *Agent) handleEmailTriage() agents.Response {
|
||||
return agents.Response{Text: "🗂️ **Email-Triage (letzte 10 Emails):**\n\n" + result}
|
||||
}
|
||||
|
||||
// handleTriageHistory zeigt die letzten N Triage-Entscheidungen.
|
||||
func (a *Agent) handleTriageHistory(req agents.Request) agents.Response {
|
||||
limit := uint32(10)
|
||||
if len(req.Args) > 1 && req.Args[1] != "" {
|
||||
if n, err := strconv.ParseUint(req.Args[1], 10, 32); err == nil && n > 0 {
|
||||
limit = uint32(n)
|
||||
}
|
||||
}
|
||||
|
||||
results, err := triage.ListRecent(limit)
|
||||
if err != nil {
|
||||
return agents.Response{Error: err, Text: fmt.Sprintf("❌ Triage-History fehlgeschlagen: %v", err)}
|
||||
}
|
||||
if len(results) == 0 {
|
||||
return agents.Response{Text: "📭 Keine Triage-Entscheidungen gespeichert."}
|
||||
}
|
||||
|
||||
var sb strings.Builder
|
||||
fmt.Fprintf(&sb, "🗂️ **Triage-History (%d Einträge):**\n\n", len(results))
|
||||
for i, r := range results {
|
||||
fmt.Fprintf(&sb, "**%d.** %s\n", i+1, r.Text)
|
||||
}
|
||||
return agents.Response{Text: sb.String()}
|
||||
}
|
||||
|
||||
// handleTriageCorrect korrigiert eine Triage-Entscheidung (wichtig↔unwichtig).
|
||||
func (a *Agent) handleTriageCorrect(req agents.Request) agents.Response {
|
||||
if len(req.Args) < 2 || req.Args[1] == "" {
|
||||
return agents.Response{Text: "❌ Betreff fehlt. Beispiel: `/email triage-correct Newsletter`"}
|
||||
}
|
||||
query := strings.Join(req.Args[1:], " ")
|
||||
|
||||
msg, err := triage.CorrectDecision(query)
|
||||
if err != nil {
|
||||
return agents.Response{Error: err, Text: fmt.Sprintf("❌ Korrektur fehlgeschlagen: %v", err)}
|
||||
}
|
||||
return agents.Response{Text: "✅ " + msg}
|
||||
}
|
||||
|
||||
// handleTriageSearch sucht ähnliche Triage-Entscheidungen.
|
||||
func (a *Agent) handleTriageSearch(req agents.Request) agents.Response {
|
||||
if len(req.Args) < 2 || req.Args[1] == "" {
|
||||
return agents.Response{Text: "❌ Suchbegriff fehlt. Beispiel: `/email triage-search Newsletter`"}
|
||||
}
|
||||
query := strings.Join(req.Args[1:], " ")
|
||||
|
||||
results, err := triage.SearchExtended(query, 10)
|
||||
if err != nil {
|
||||
return agents.Response{Error: err, Text: fmt.Sprintf("❌ Triage-Suche fehlgeschlagen: %v", err)}
|
||||
}
|
||||
if len(results) == 0 {
|
||||
return agents.Response{Text: "📭 Keine ähnlichen Triage-Entscheidungen gefunden."}
|
||||
}
|
||||
|
||||
var sb strings.Builder
|
||||
fmt.Fprintf(&sb, "🔍 **Triage-Suche** (query: `%s`, %d Treffer):\n\n", query, len(results))
|
||||
for i, r := range results {
|
||||
fmt.Fprintf(&sb, "**%d.** [%.0f%%] %s\n", i+1, r.Score*100, r.Text)
|
||||
}
|
||||
return agents.Response{Text: sb.String()}
|
||||
}
|
||||
|
||||
// ResolveArchiveFolder ist die exportierte Version von resolveArchiveFolder für den Discord-Layer.
|
||||
func ResolveArchiveFolder(name string) (imapFolder string, ok bool) {
|
||||
return resolveArchiveFolder(name)
|
||||
|
||||
Reference in New Issue
Block a user