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>
72 lines
2.3 KiB
Go
72 lines
2.3 KiB
Go
// research/agent.go – Research-Agent: wraps brain.AskQuery
|
||
package research
|
||
|
||
import (
|
||
"fmt"
|
||
"strings"
|
||
|
||
"my-brain-importer/internal/agents"
|
||
"my-brain-importer/internal/brain"
|
||
)
|
||
|
||
// Agent beantwortet Fragen mit der Wissensdatenbank.
|
||
type Agent struct{}
|
||
|
||
func New() *Agent { return &Agent{} }
|
||
|
||
func (a *Agent) Handle(req agents.Request) agents.Response {
|
||
if len(req.Args) == 0 {
|
||
return agents.Response{Text: "❌ Keine Frage angegeben."}
|
||
}
|
||
question := strings.Join(req.Args, " ")
|
||
|
||
if req.Action == agents.ActionDeepAsk {
|
||
return a.handleDeepAsk(question, req.History)
|
||
}
|
||
|
||
answer, chunks, err := brain.AskQuery(question, req.History)
|
||
if err != nil {
|
||
return agents.Response{Error: err, Text: fmt.Sprintf("❌ Fehler: %v", err)}
|
||
}
|
||
if len(chunks) == 0 {
|
||
return agents.Response{Text: "❌ Keine relevanten Informationen in der Datenbank gefunden.\nFüge mehr Daten mit `/ingest` hinzu."}
|
||
}
|
||
|
||
var sb strings.Builder
|
||
fmt.Fprintf(&sb, "💬 **Antwort auf:** _%s_\n\n", question)
|
||
sb.WriteString(answer)
|
||
sb.WriteString("\n\n📚 **Quellen:**\n")
|
||
for _, chunk := range chunks {
|
||
fmt.Fprintf(&sb, "• %.1f%% – %s\n", chunk.Score*100, chunk.Source)
|
||
}
|
||
return agents.Response{Text: sb.String(), RawAnswer: answer}
|
||
}
|
||
|
||
// handleDeepAsk führt eine tiefe Recherche mit Multi-Step Reasoning durch.
|
||
func (a *Agent) handleDeepAsk(question string, history []agents.HistoryMessage) agents.Response {
|
||
answer, chunks, err := brain.DeepAskQuery(question, history)
|
||
if err != nil {
|
||
return agents.Response{Error: err, Text: fmt.Sprintf("❌ Fehler: %v", err)}
|
||
}
|
||
if len(chunks) == 0 {
|
||
return agents.Response{Text: "❌ Keine relevanten Informationen in der Datenbank gefunden.\nFüge mehr Daten mit `/ingest` hinzu."}
|
||
}
|
||
|
||
// Quellen deduplizieren
|
||
seenSources := make(map[string]float32)
|
||
for _, chunk := range chunks {
|
||
if existing, ok := seenSources[chunk.Source]; !ok || chunk.Score > existing {
|
||
seenSources[chunk.Source] = chunk.Score
|
||
}
|
||
}
|
||
|
||
var sb strings.Builder
|
||
fmt.Fprintf(&sb, "🔬 **Tiefe Recherche:** _%s_\n\n", question)
|
||
sb.WriteString(answer)
|
||
fmt.Fprintf(&sb, "\n\n📚 **Quellen** (%d Chunks aus %d Quellen):\n", len(chunks), len(seenSources))
|
||
for source, score := range seenSources {
|
||
fmt.Fprintf(&sb, "• %.1f%% – %s\n", score*100, source)
|
||
}
|
||
return agents.Response{Text: sb.String(), RawAnswer: answer}
|
||
}
|