discord kommunikation
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
bin/
|
bin/
|
||||||
config.yml
|
config.yml
|
||||||
|
ask
|
||||||
|
|||||||
7
build.sh
7
build.sh
@@ -16,8 +16,15 @@ GOOS=windows GOARCH=amd64 go build -o "$OUT_DIR/ask.exe" ./cmd/ask/
|
|||||||
echo " Linux: $OUT_DIR/ask"
|
echo " Linux: $OUT_DIR/ask"
|
||||||
echo " Windows: $OUT_DIR/ask.exe"
|
echo " Windows: $OUT_DIR/ask.exe"
|
||||||
|
|
||||||
|
echo "Baue discord-bot ..."
|
||||||
|
GOOS=linux GOARCH=amd64 go build -o "$OUT_DIR/discord-bot" ./cmd/discord/
|
||||||
|
GOOS=windows GOARCH=amd64 go build -o "$OUT_DIR/discord-bot.exe" ./cmd/discord/
|
||||||
|
echo " Linux: $OUT_DIR/discord-bot"
|
||||||
|
echo " Windows: $OUT_DIR/discord-bot.exe"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Fertig. Nutzung:"
|
echo "Fertig. Nutzung:"
|
||||||
echo " $OUT_DIR/ingest # Markdown importieren"
|
echo " $OUT_DIR/ingest # Markdown importieren"
|
||||||
echo " $OUT_DIR/ingest bild.json # JSON importieren"
|
echo " $OUT_DIR/ingest bild.json # JSON importieren"
|
||||||
echo " $OUT_DIR/ask \"Was sind meine Pläne?\""
|
echo " $OUT_DIR/ask \"Was sind meine Pläne?\""
|
||||||
|
echo " $OUT_DIR/discord-bot # Discord-Bot starten"
|
||||||
|
|||||||
190
cmd/discord/main.go
Normal file
190
cmd/discord/main.go
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
// discord – Discord-Bot für my-brain-importer
|
||||||
|
// Unterstützt /ask, /ingest und @Mention
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
|
|
||||||
|
"my-brain-importer/internal/brain"
|
||||||
|
"my-brain-importer/internal/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
dg *discordgo.Session
|
||||||
|
botUser *discordgo.User
|
||||||
|
|
||||||
|
commands = []*discordgo.ApplicationCommand{
|
||||||
|
{
|
||||||
|
Name: "ask",
|
||||||
|
Description: "Stelle eine Frage an deinen AI Brain",
|
||||||
|
Options: []*discordgo.ApplicationCommandOption{
|
||||||
|
{
|
||||||
|
Type: discordgo.ApplicationCommandOptionString,
|
||||||
|
Name: "frage",
|
||||||
|
Description: "Die Frage, die du stellen möchtest",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "ingest",
|
||||||
|
Description: "Importiert Markdown-Notizen aus brain_root in die Wissensdatenbank",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
config.LoadConfig()
|
||||||
|
|
||||||
|
token := config.Cfg.Discord.Token
|
||||||
|
if token == "" || token == "dein-discord-bot-token" {
|
||||||
|
log.Fatal("❌ Kein Discord-Token in config.yml konfiguriert (discord.token)")
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
dg, err = discordgo.New("Bot " + token)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("❌ Discord-Session konnte nicht erstellt werden: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dg.AddHandler(onReady)
|
||||||
|
dg.AddHandler(onInteraction)
|
||||||
|
dg.AddHandler(onMessage)
|
||||||
|
|
||||||
|
dg.Identify.Intents = discordgo.IntentsGuilds |
|
||||||
|
discordgo.IntentsGuildMessages |
|
||||||
|
discordgo.IntentMessageContent
|
||||||
|
|
||||||
|
if err = dg.Open(); err != nil {
|
||||||
|
log.Fatalf("❌ Verbindung zu Discord fehlgeschlagen: %v", err)
|
||||||
|
}
|
||||||
|
defer dg.Close()
|
||||||
|
|
||||||
|
registerCommands()
|
||||||
|
|
||||||
|
fmt.Println("✅ Bot läuft. Drücke CTRL+C zum Beenden.")
|
||||||
|
stop := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
<-stop
|
||||||
|
fmt.Println("\n👋 Bot wird beendet...")
|
||||||
|
}
|
||||||
|
|
||||||
|
func onReady(s *discordgo.Session, r *discordgo.Ready) {
|
||||||
|
botUser = r.User
|
||||||
|
fmt.Printf("✅ Eingeloggt als %s#%s\n", r.User.Username, r.User.Discriminator)
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerCommands() {
|
||||||
|
guildID := config.Cfg.Discord.GuildID
|
||||||
|
for _, cmd := range commands {
|
||||||
|
_, err := dg.ApplicationCommandCreate(dg.State.User.ID, guildID, cmd)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("⚠️ Slash-Command /%s konnte nicht registriert werden: %v", cmd.Name, err)
|
||||||
|
} else {
|
||||||
|
scope := "global"
|
||||||
|
if guildID != "" {
|
||||||
|
scope = "guild " + guildID
|
||||||
|
}
|
||||||
|
fmt.Printf("📝 Slash-Command /%s registriert (%s)\n", cmd.Name, scope)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func onInteraction(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||||
|
if i.Type != discordgo.InteractionApplicationCommand {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch i.ApplicationCommandData().Name {
|
||||||
|
case "ask":
|
||||||
|
handleAsk(s, i, i.ApplicationCommandData().Options[0].StringValue())
|
||||||
|
case "ingest":
|
||||||
|
handleIngest(s, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func onMessage(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||||
|
if m.Author.Bot {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if botUser == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mentioned := false
|
||||||
|
for _, u := range m.Mentions {
|
||||||
|
if u.ID == botUser.ID {
|
||||||
|
mentioned = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !mentioned {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mention aus der Nachricht entfernen
|
||||||
|
question := strings.TrimSpace(
|
||||||
|
strings.ReplaceAll(m.Content, "<@"+botUser.ID+">", ""),
|
||||||
|
)
|
||||||
|
if question == "" {
|
||||||
|
s.ChannelMessageSend(m.ChannelID, "Stell mir eine Frage! Beispiel: @Brain Was sind meine TODOs?")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.ChannelTyping(m.ChannelID)
|
||||||
|
reply := queryAndFormat(question)
|
||||||
|
s.ChannelMessageSendReply(m.ChannelID, reply, m.Reference())
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleAsk(s *discordgo.Session, i *discordgo.InteractionCreate, question string) {
|
||||||
|
// Sofort mit "Denke nach..." antworten (Discord-Timeout: 3s)
|
||||||
|
s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||||
|
Type: discordgo.InteractionResponseDeferredChannelMessageWithSource,
|
||||||
|
})
|
||||||
|
|
||||||
|
reply := queryAndFormat(question)
|
||||||
|
|
||||||
|
s.InteractionResponseEdit(i.Interaction, &discordgo.WebhookEdit{
|
||||||
|
Content: &reply,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleIngest(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||||
|
s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||||
|
Type: discordgo.InteractionResponseDeferredChannelMessageWithSource,
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Println("📥 Ingest gestartet via Discord...")
|
||||||
|
brain.RunIngest(config.Cfg.BrainRoot)
|
||||||
|
|
||||||
|
msg := fmt.Sprintf("✅ Ingest abgeschlossen! Quelle: `%s`", config.Cfg.BrainRoot)
|
||||||
|
s.InteractionResponseEdit(i.Interaction, &discordgo.WebhookEdit{
|
||||||
|
Content: &msg,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryAndFormat(question string) string {
|
||||||
|
answer, chunks, err := brain.AskQuery(question)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Sprintf("❌ Fehler: %v", err)
|
||||||
|
}
|
||||||
|
if len(chunks) == 0 {
|
||||||
|
return "❌ 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 sb.String()
|
||||||
|
}
|
||||||
3
go.mod
3
go.mod
@@ -10,6 +10,9 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/bwmarrin/discordgo v0.29.0 // indirect
|
||||||
|
github.com/gorilla/websocket v1.4.2 // indirect
|
||||||
|
golang.org/x/crypto v0.32.0 // indirect
|
||||||
golang.org/x/net v0.34.0 // indirect
|
golang.org/x/net v0.34.0 // indirect
|
||||||
golang.org/x/sys v0.29.0 // indirect
|
golang.org/x/sys v0.29.0 // indirect
|
||||||
golang.org/x/text v0.21.0 // indirect
|
golang.org/x/text v0.21.0 // indirect
|
||||||
|
|||||||
12
go.sum
12
go.sum
@@ -1,3 +1,5 @@
|
|||||||
|
github.com/bwmarrin/discordgo v0.29.0 h1:FmWeXFaKUwrcL3Cx65c20bTRW+vOb6k8AnaP+EgjDno=
|
||||||
|
github.com/bwmarrin/discordgo v0.29.0/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
|
||||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
@@ -8,6 +10,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
|||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||||
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/qdrant/go-client v1.12.0 h1:KqsIKDAw5iQmxDzRjbzRjhvQ+Igyr7Y84vDCinf1T4M=
|
github.com/qdrant/go-client v1.12.0 h1:KqsIKDAw5iQmxDzRjbzRjhvQ+Igyr7Y84vDCinf1T4M=
|
||||||
github.com/qdrant/go-client v1.12.0/go.mod h1:zFa6t5Y3Oqecoa0aSsGWhMqQWq3x3kTPvm0sMf5qplw=
|
github.com/qdrant/go-client v1.12.0/go.mod h1:zFa6t5Y3Oqecoa0aSsGWhMqQWq3x3kTPvm0sMf5qplw=
|
||||||
github.com/sashabaranov/go-openai v1.37.0 h1:hQQowgYm4OXJ1Z/wTrE+XZaO20BYsL0R3uRPSpfNZkY=
|
github.com/sashabaranov/go-openai v1.37.0 h1:hQQowgYm4OXJ1Z/wTrE+XZaO20BYsL0R3uRPSpfNZkY=
|
||||||
@@ -24,12 +28,20 @@ go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce
|
|||||||
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
|
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
|
||||||
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
|
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
|
||||||
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
|
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
|
||||||
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
|
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||||
|
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
|
||||||
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
|
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
|
||||||
|
|||||||
@@ -21,27 +21,21 @@ type KnowledgeChunk struct {
|
|||||||
Source string
|
Source string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ask sucht relevante Chunks und generiert eine LLM-Antwort per Streaming.
|
// AskQuery sucht relevante Chunks und generiert eine LLM-Antwort.
|
||||||
func Ask(question string) {
|
// Gibt die Antwort als String und die verwendeten Quellen zurück.
|
||||||
|
func AskQuery(question string) (string, []KnowledgeChunk, error) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx = metadata.AppendToOutgoingContext(ctx, "api-key", config.Cfg.Qdrant.APIKey)
|
ctx = metadata.AppendToOutgoingContext(ctx, "api-key", config.Cfg.Qdrant.APIKey)
|
||||||
|
|
||||||
fmt.Printf("🤔 Frage: \"%s\"\n\n", question)
|
|
||||||
|
|
||||||
embClient := config.NewEmbeddingClient()
|
embClient := config.NewEmbeddingClient()
|
||||||
chatClient := config.NewChatClient()
|
chatClient := config.NewChatClient()
|
||||||
|
|
||||||
fmt.Println("🔍 Durchsuche lokale Wissensdatenbank...")
|
|
||||||
chunks := searchKnowledge(ctx, embClient, question)
|
chunks := searchKnowledge(ctx, embClient, question)
|
||||||
|
|
||||||
if len(chunks) == 0 {
|
if len(chunks) == 0 {
|
||||||
fmt.Println("\n❌ Keine relevanten Informationen in der Datenbank gefunden.")
|
return "", nil, nil
|
||||||
fmt.Println(" Füge mehr Daten mit './bin/ingest' hinzu.")
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
contextText := buildContext(chunks)
|
contextText := buildContext(chunks)
|
||||||
fmt.Printf("✅ %d relevante Informationen gefunden\n\n", len(chunks))
|
|
||||||
|
|
||||||
systemPrompt := `Du bist ein hilfreicher persönlicher Assistent.
|
systemPrompt := `Du bist ein hilfreicher persönlicher Assistent.
|
||||||
Deine Aufgabe ist es, Fragen basierend auf den bereitgestellten Informationen zu beantworten.
|
Deine Aufgabe ist es, Fragen basierend auf den bereitgestellten Informationen zu beantworten.
|
||||||
@@ -60,9 +54,6 @@ WICHTIGE REGELN:
|
|||||||
Basierend auf diesen Informationen, beantworte bitte folgende Frage:
|
Basierend auf diesen Informationen, beantworte bitte folgende Frage:
|
||||||
%s`, contextText, question)
|
%s`, contextText, question)
|
||||||
|
|
||||||
fmt.Println("🧠 Generiere Antwort mit lokalem Modell...")
|
|
||||||
fmt.Println(strings.Repeat("═", 80))
|
|
||||||
|
|
||||||
stream, err := chatClient.CreateChatCompletionStream(ctx, openai.ChatCompletionRequest{
|
stream, err := chatClient.CreateChatCompletionStream(ctx, openai.ChatCompletionRequest{
|
||||||
Model: config.Cfg.Chat.Model,
|
Model: config.Cfg.Chat.Model,
|
||||||
Messages: []openai.ChatCompletionMessage{
|
Messages: []openai.ChatCompletionMessage{
|
||||||
@@ -73,21 +64,45 @@ Basierend auf diesen Informationen, beantworte bitte folgende Frage:
|
|||||||
MaxTokens: 500,
|
MaxTokens: 500,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("❌ LLM Fehler: %v", err)
|
return "", nil, fmt.Errorf("LLM Fehler: %w", err)
|
||||||
}
|
}
|
||||||
defer stream.Close()
|
defer stream.Close()
|
||||||
|
|
||||||
fmt.Print("\n💬 Antwort:\n\n")
|
var answer strings.Builder
|
||||||
for {
|
for {
|
||||||
response, err := stream.Recv()
|
response, err := stream.Recv()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if len(response.Choices) > 0 {
|
if len(response.Choices) > 0 {
|
||||||
fmt.Print(response.Choices[0].Delta.Content)
|
answer.WriteString(response.Choices[0].Delta.Content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return answer.String(), chunks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ask sucht relevante Chunks und gibt Antwort + Quellen auf stdout aus.
|
||||||
|
func Ask(question string) {
|
||||||
|
fmt.Printf("🤔 Frage: \"%s\"\n\n", question)
|
||||||
|
fmt.Println("🔍 Durchsuche lokale Wissensdatenbank...")
|
||||||
|
|
||||||
|
answer, chunks, err := AskQuery(question)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("❌ %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(chunks) == 0 {
|
||||||
|
fmt.Println("\n❌ Keine relevanten Informationen in der Datenbank gefunden.")
|
||||||
|
fmt.Println(" Füge mehr Daten mit './bin/ingest' hinzu.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("✅ %d relevante Informationen gefunden\n\n", len(chunks))
|
||||||
|
fmt.Println("🧠 Generiere Antwort mit lokalem Modell...")
|
||||||
|
fmt.Println(strings.Repeat("═", 80))
|
||||||
|
fmt.Print("\n💬 Antwort:\n\n")
|
||||||
|
fmt.Print(answer)
|
||||||
fmt.Print("\n\n")
|
fmt.Print("\n\n")
|
||||||
fmt.Println(strings.Repeat("═", 80))
|
fmt.Println(strings.Repeat("═", 80))
|
||||||
fmt.Print("\n📚 Verwendete Quellen:\n")
|
fmt.Print("\n📚 Verwendete Quellen:\n")
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ type Config struct {
|
|||||||
Model string `yaml:"model"`
|
Model string `yaml:"model"`
|
||||||
} `yaml:"chat"`
|
} `yaml:"chat"`
|
||||||
|
|
||||||
|
Discord struct {
|
||||||
|
Token string `yaml:"token"`
|
||||||
|
GuildID string `yaml:"guild_id"`
|
||||||
|
} `yaml:"discord"`
|
||||||
|
|
||||||
BrainRoot string `yaml:"brain_root"`
|
BrainRoot string `yaml:"brain_root"`
|
||||||
TopK uint64 `yaml:"top_k"`
|
TopK uint64 `yaml:"top_k"`
|
||||||
ScoreThreshold float32 `yaml:"score_threshold"`
|
ScoreThreshold float32 `yaml:"score_threshold"`
|
||||||
|
|||||||
Reference in New Issue
Block a user