工具路由还没实现
This commit is contained in:
@@ -31,6 +31,7 @@ desktop.ini
|
|||||||
# 项目运行时数据(Windows 测试路径)
|
# 项目运行时数据(Windows 测试路径)
|
||||||
win/etc/
|
win/etc/
|
||||||
win/srv/
|
win/srv/
|
||||||
|
win/var/
|
||||||
|
|
||||||
# 数据库文件
|
# 数据库文件
|
||||||
*.db
|
*.db
|
||||||
|
|||||||
@@ -18,11 +18,17 @@ import (
|
|||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// SystemPromptStore is the interface for getting the system prompt
|
||||||
|
type SystemPromptStore interface {
|
||||||
|
GetLLMPrimaryConfigSystemPrompt() (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
// Config holds the AI service configuration
|
// Config holds the AI service configuration
|
||||||
type Config struct {
|
type Config struct {
|
||||||
LLMProviders []llm.ProviderConfig
|
LLMProviders []llm.ProviderConfig
|
||||||
DataDir string
|
DataDir string
|
||||||
Enabled bool
|
Enabled bool
|
||||||
|
SystemPromptStore SystemPromptStore
|
||||||
}
|
}
|
||||||
|
|
||||||
// Service manages all AI-related components
|
// Service manages all AI-related components
|
||||||
@@ -91,6 +97,7 @@ func NewService(cfg Config, db *gorm.DB, botSender autoreply.BotSender) (*Servic
|
|||||||
convStore,
|
convStore,
|
||||||
msgQueue,
|
msgQueue,
|
||||||
botSender,
|
botSender,
|
||||||
|
cfg.SystemPromptStore,
|
||||||
)
|
)
|
||||||
|
|
||||||
return &Service{
|
return &Service{
|
||||||
|
|||||||
+19
-8
@@ -7,22 +7,33 @@ import (
|
|||||||
|
|
||||||
// BotServiceAdapter adapts the bot service to the BotSender interface
|
// BotServiceAdapter adapts the bot service to the BotSender interface
|
||||||
type BotServiceAdapter struct {
|
type BotServiceAdapter struct {
|
||||||
sendTextFn func(ctx context.Context, botID uint64, toNodeNum int64, text string) error
|
sendDirectTextFn func(ctx context.Context, botID uint64, toNodeNum int64, text string) error
|
||||||
|
sendChannelTextFn func(ctx context.Context, botID uint64, channelID string, text string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBotServiceAdapter creates a new bot service adapter
|
// NewBotServiceAdapter creates a new bot service adapter
|
||||||
func NewBotServiceAdapter(
|
func NewBotServiceAdapter(
|
||||||
sendTextFn func(ctx context.Context, botID uint64, toNodeNum int64, text string) error,
|
sendDirectTextFn func(ctx context.Context, botID uint64, toNodeNum int64, text string) error,
|
||||||
|
sendChannelTextFn func(ctx context.Context, botID uint64, channelID string, text string) error,
|
||||||
) *BotServiceAdapter {
|
) *BotServiceAdapter {
|
||||||
return &BotServiceAdapter{
|
return &BotServiceAdapter{
|
||||||
sendTextFn: sendTextFn,
|
sendDirectTextFn: sendDirectTextFn,
|
||||||
|
sendChannelTextFn: sendChannelTextFn,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendText sends a text message via the bot service
|
// SendDirectText sends a direct/private message to a specific node
|
||||||
func (a *BotServiceAdapter) SendText(ctx context.Context, botID uint64, toNodeNum int64, text string) error {
|
func (a *BotServiceAdapter) SendDirectText(ctx context.Context, botID uint64, toNodeNum int64, text string) error {
|
||||||
if a.sendTextFn == nil {
|
if a.sendDirectTextFn == nil {
|
||||||
return fmt.Errorf("send text function is nil")
|
return fmt.Errorf("send direct text function is nil")
|
||||||
}
|
}
|
||||||
return a.sendTextFn(ctx, botID, toNodeNum, text)
|
return a.sendDirectTextFn(ctx, botID, toNodeNum, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendChannelText sends a channel message to a specific channel
|
||||||
|
func (a *BotServiceAdapter) SendChannelText(ctx context.Context, botID uint64, channelID string, text string) error {
|
||||||
|
if a.sendChannelTextFn == nil {
|
||||||
|
return fmt.Errorf("send channel text function is nil")
|
||||||
|
}
|
||||||
|
return a.sendChannelTextFn(ctx, botID, channelID, text)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ type llmMessageQueueRecord struct {
|
|||||||
PacketID int64 `gorm:"column:packet_id;not null"`
|
PacketID int64 `gorm:"column:packet_id;not null"`
|
||||||
ChannelID *string `gorm:"column:channel_id"`
|
ChannelID *string `gorm:"column:channel_id"`
|
||||||
Topic string `gorm:"column:topic;not null"`
|
Topic string `gorm:"column:topic;not null"`
|
||||||
|
MessageType string `gorm:"column:message_type;not null;default:'direct'"` // "channel" 或 "direct"
|
||||||
Status string `gorm:"column:status;not null;index"`
|
Status string `gorm:"column:status;not null;index"`
|
||||||
Error string `gorm:"column:error;type:text"`
|
Error string `gorm:"column:error;type:text"`
|
||||||
Reply string `gorm:"column:reply;type:text"`
|
Reply string `gorm:"column:reply;type:text"`
|
||||||
@@ -79,6 +80,7 @@ func (q *DBMessageQueue) GetPendingMessages(botID uint64, limit int) ([]QueuedMe
|
|||||||
PacketID: r.PacketID,
|
PacketID: r.PacketID,
|
||||||
ChannelID: r.ChannelID,
|
ChannelID: r.ChannelID,
|
||||||
Topic: r.Topic,
|
Topic: r.Topic,
|
||||||
|
MessageType: r.MessageType,
|
||||||
ReceivedAt: r.ReceivedAt,
|
ReceivedAt: r.ReceivedAt,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-7
@@ -51,12 +51,21 @@ type QueuedMessage struct {
|
|||||||
PacketID int64
|
PacketID int64
|
||||||
ChannelID *string
|
ChannelID *string
|
||||||
Topic string
|
Topic string
|
||||||
|
MessageType string // "channel" 或 "direct"
|
||||||
ReceivedAt time.Time
|
ReceivedAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// BotSender is the interface for sending bot messages
|
// BotSender is the interface for sending bot messages
|
||||||
type BotSender interface {
|
type BotSender interface {
|
||||||
SendText(ctx context.Context, botID uint64, toNodeNum int64, text string) error
|
// SendDirectText 发送私聊消息给指定节点
|
||||||
|
SendDirectText(ctx context.Context, botID uint64, toNodeNum int64, text string) error
|
||||||
|
// SendChannelText 发送频道消息到指定频道
|
||||||
|
SendChannelText(ctx context.Context, botID uint64, channelID string, text string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// SystemPromptStore is the interface for getting the system prompt
|
||||||
|
type SystemPromptStore interface {
|
||||||
|
GetLLMPrimaryConfigSystemPrompt() (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Service manages automatic AI replies for bots
|
// Service manages automatic AI replies for bots
|
||||||
@@ -67,6 +76,7 @@ type Service struct {
|
|||||||
convStore *conversation.Store
|
convStore *conversation.Store
|
||||||
msgQueue MessageQueue
|
msgQueue MessageQueue
|
||||||
botSender BotSender
|
botSender BotSender
|
||||||
|
systemPromptStore SystemPromptStore
|
||||||
|
|
||||||
running bool
|
running bool
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
@@ -82,6 +92,7 @@ func NewService(
|
|||||||
convStore *conversation.Store,
|
convStore *conversation.Store,
|
||||||
msgQueue MessageQueue,
|
msgQueue MessageQueue,
|
||||||
botSender BotSender,
|
botSender BotSender,
|
||||||
|
systemPromptStore SystemPromptStore,
|
||||||
) *Service {
|
) *Service {
|
||||||
return &Service{
|
return &Service{
|
||||||
llmState: llmState,
|
llmState: llmState,
|
||||||
@@ -90,6 +101,7 @@ func NewService(
|
|||||||
convStore: convStore,
|
convStore: convStore,
|
||||||
msgQueue: msgQueue,
|
msgQueue: msgQueue,
|
||||||
botSender: botSender,
|
botSender: botSender,
|
||||||
|
systemPromptStore: systemPromptStore,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,13 +253,22 @@ func (s *Service) processMessage(ctx context.Context, msg QueuedMessage) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get system prompt from primary config
|
||||||
|
var systemPrompt string
|
||||||
|
if s.systemPromptStore != nil {
|
||||||
|
systemPrompt, err = s.systemPromptStore.GetLLMPrimaryConfigSystemPrompt()
|
||||||
|
if err != nil {
|
||||||
|
printJSON(map[string]any{"event": "llm_system_prompt_warning", "msg_id": msg.ID, "error": err.Error()})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Run the tool loop to get augmented messages
|
// Run the tool loop to get augmented messages
|
||||||
augmentedMessages, err := toolrouter.RunAgentToolLoop(procCtx, s.toolRouter, profile, conv.Messages, s.toolMgr, nil)
|
augmentedMessages, err := toolrouter.RunAgentToolLoop(procCtx, s.toolRouter, profile, conv.Messages, s.toolMgr, nil)
|
||||||
_ = augmentedMessages // We'll use this in the future with proper tool support
|
_ = augmentedMessages // We'll use this in the future with proper tool support
|
||||||
|
|
||||||
// For now, use simple completion since we don't have tools registered yet
|
// For now, use simple completion since we don't have tools registered yet
|
||||||
printJSON(map[string]any{"event": "llm_process_completion_start", "msg_id": msg.ID})
|
printJSON(map[string]any{"event": "llm_process_completion_start", "msg_id": msg.ID, "has_system_prompt": systemPrompt != ""})
|
||||||
reply, err := completion.CompleteText(procCtx, profile, conv.Messages, 512)
|
reply, err := completion.CompleteText(procCtx, profile, systemPrompt, conv.Messages, 512)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errMsg := fmt.Sprintf("LLM completion failed: %v", err)
|
errMsg := fmt.Sprintf("LLM completion failed: %v", err)
|
||||||
printJSON(map[string]any{"event": "llm_process_failed", "msg_id": msg.ID, "step": "llm_completion", "error": errMsg})
|
printJSON(map[string]any{"event": "llm_process_failed", "msg_id": msg.ID, "step": "llm_completion", "error": errMsg})
|
||||||
@@ -304,10 +325,20 @@ func (s *Service) processMessage(ctx context.Context, msg QueuedMessage) {
|
|||||||
// Non-fatal, continue
|
// Non-fatal, continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the reply via the bot
|
// Send the reply via the bot - 根据消息类型决定发送方式
|
||||||
printJSON(map[string]any{"event": "llm_process_send_start", "msg_id": msg.ID, "to_node_num": msg.FromNodeNum})
|
// 频道消息:回复到原频道;私聊消息:回复给发送节点
|
||||||
if err := s.botSender.SendText(procCtx, msg.BotID, msg.FromNodeNum, reply); err != nil {
|
var sendErr error
|
||||||
errMsg := fmt.Sprintf("failed to send reply: %v", err)
|
if msg.MessageType == "channel" && msg.ChannelID != nil && *msg.ChannelID != "" {
|
||||||
|
// 频道消息 - 回复到原频道
|
||||||
|
printJSON(map[string]any{"event": "llm_process_send_start", "msg_id": msg.ID, "channel_id": *msg.ChannelID, "message_type": "channel"})
|
||||||
|
sendErr = s.botSender.SendChannelText(procCtx, msg.BotID, *msg.ChannelID, reply)
|
||||||
|
} else {
|
||||||
|
// 私聊消息 - 回复给发送节点
|
||||||
|
printJSON(map[string]any{"event": "llm_process_send_start", "msg_id": msg.ID, "to_node_num": msg.FromNodeNum, "message_type": "direct"})
|
||||||
|
sendErr = s.botSender.SendDirectText(procCtx, msg.BotID, msg.FromNodeNum, reply)
|
||||||
|
}
|
||||||
|
if sendErr != nil {
|
||||||
|
errMsg := fmt.Sprintf("failed to send reply: %v", sendErr)
|
||||||
printJSON(map[string]any{"event": "llm_process_failed", "msg_id": msg.ID, "step": "send_reply", "error": errMsg})
|
printJSON(map[string]any{"event": "llm_process_failed", "msg_id": msg.ID, "step": "send_reply", "error": errMsg})
|
||||||
_ = s.msgQueue.MarkAsFailed(msg.ID, errMsg)
|
_ = s.msgQueue.MarkAsFailed(msg.ID, errMsg)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -290,7 +290,8 @@ func insertInboundBotDirectMessage(s *store, record map[string]any, clientInfo m
|
|||||||
}
|
}
|
||||||
_ = clientInfo // mqtt 元数据已经记录在 content_json 里,这里保留参数以保持队列签名一致
|
_ = clientInfo // mqtt 元数据已经记录在 content_json 里,这里保留参数以保持队列签名一致
|
||||||
|
|
||||||
// 同时将消息添加到 LLM 队列
|
// 同时将消息添加到 LLM 队列(忽略机器人自己发送的消息)
|
||||||
|
if peerNodeID != bot.NodeID {
|
||||||
longName := nullableString(record["long_name"])
|
longName := nullableString(record["long_name"])
|
||||||
shortName := nullableString(record["short_name"])
|
shortName := nullableString(record["short_name"])
|
||||||
channelID := nullableString(record["channel_id"])
|
channelID := nullableString(record["channel_id"])
|
||||||
@@ -306,8 +307,10 @@ func insertInboundBotDirectMessage(s *store, record map[string]any, clientInfo m
|
|||||||
PacketID: int64(packetID),
|
PacketID: int64(packetID),
|
||||||
ChannelID: channelID,
|
ChannelID: channelID,
|
||||||
Topic: topic,
|
Topic: topic,
|
||||||
|
MessageType: "direct",
|
||||||
ContentJSON: contentPtr,
|
ContentJSON: contentPtr,
|
||||||
})
|
})
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
printJSON(map[string]any{
|
printJSON(map[string]any{
|
||||||
"event": "llm_queue_enqueue_failed",
|
"event": "llm_queue_enqueue_failed",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package completion
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"meshtastic_mqtt_server/llm"
|
"meshtastic_mqtt_server/llm"
|
||||||
@@ -35,12 +36,25 @@ func CompleteChat(ctx context.Context, profile *llm.Profile, req model.CreateCha
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CompleteText completes a text prompt using conversation messages
|
// CompleteText completes a text prompt using conversation messages
|
||||||
func CompleteText(ctx context.Context, profile *llm.Profile, messages []message.ChatMessage, maxTokens int) (string, error) {
|
// If systemPrompt is not empty, it will be added as the first message
|
||||||
|
func CompleteText(ctx context.Context, profile *llm.Profile, systemPrompt string, messages []message.ChatMessage, maxTokens int) (string, error) {
|
||||||
if profile == nil || profile.Client == nil {
|
if profile == nil || profile.Client == nil {
|
||||||
return "", fmt.Errorf("llm profile or client is nil")
|
return "", fmt.Errorf("llm profile or client is nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
arkMessages := make([]*model.ChatCompletionMessage, 0, len(messages))
|
arkMessages := make([]*model.ChatCompletionMessage, 0, len(messages)+1)
|
||||||
|
|
||||||
|
// Add system prompt if provided
|
||||||
|
if strings.TrimSpace(systemPrompt) != "" {
|
||||||
|
content := &model.ChatCompletionMessageContent{
|
||||||
|
StringValue: &systemPrompt,
|
||||||
|
}
|
||||||
|
arkMessages = append(arkMessages, &model.ChatCompletionMessage{
|
||||||
|
Role: "system",
|
||||||
|
Content: content,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
for _, msg := range messages {
|
for _, msg := range messages {
|
||||||
content := &model.ChatCompletionMessageContent{
|
content := &model.ChatCompletionMessageContent{
|
||||||
StringValue: &msg.Content,
|
StringValue: &msg.Content,
|
||||||
|
|||||||
@@ -363,6 +363,7 @@ type llmMessageQueueRecord struct {
|
|||||||
PacketID int64 `gorm:"column:packet_id;not null;index"`
|
PacketID int64 `gorm:"column:packet_id;not null;index"`
|
||||||
ChannelID *string `gorm:"column:channel_id"`
|
ChannelID *string `gorm:"column:channel_id"`
|
||||||
Topic string `gorm:"column:topic;not null"`
|
Topic string `gorm:"column:topic;not null"`
|
||||||
|
MessageType string `gorm:"column:message_type;not null;default:'direct'"` // "channel" 或 "direct"
|
||||||
Status string `gorm:"column:status;not null;index"`
|
Status string `gorm:"column:status;not null;index"`
|
||||||
Error string `gorm:"column:error;type:text"`
|
Error string `gorm:"column:error;type:text"`
|
||||||
Reply string `gorm:"column:reply;type:text"`
|
Reply string `gorm:"column:reply;type:text"`
|
||||||
@@ -752,6 +753,12 @@ func migrateBotNodePSK(tx *gorm.DB, migrator gorm.Migrator, driver string) error
|
|||||||
return fmt.Errorf("migrate llm_message_queue reply column: %w", err)
|
return fmt.Errorf("migrate llm_message_queue reply column: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 迁移 LLM 消息队列 message_type 列
|
||||||
|
if migrator.HasTable(&llmMessageQueueRecord{}) && !migrator.HasColumn(&llmMessageQueueRecord{}, "MessageType") {
|
||||||
|
if err := tx.Exec("ALTER TABLE llm_message_queue ADD COLUMN message_type text NOT NULL DEFAULT 'direct'").Error; err != nil {
|
||||||
|
return fmt.Errorf("migrate llm_message_queue message_type column: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+30
-1
@@ -156,6 +156,20 @@ func (s *store) GetLLMPrimaryConfig() (*llmPrimaryConfigRecord, error) {
|
|||||||
return &record, nil
|
return &record, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetLLMPrimaryConfigSystemPrompt 获取主 AI 回复配置中的系统提示词
|
||||||
|
// 如果没有配置或出错,返回空字符串(autoreply service 会处理这种情况)
|
||||||
|
func (s *store) GetLLMPrimaryConfigSystemPrompt() (string, error) {
|
||||||
|
record, err := s.GetLLMPrimaryConfig()
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
// 没有配置时返回空字符串,使用默认行为
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return record.SystemPrompt, nil
|
||||||
|
}
|
||||||
|
|
||||||
// CreateLLMPrimaryConfig 创建主 AI 回复配置
|
// CreateLLMPrimaryConfig 创建主 AI 回复配置
|
||||||
func (s *store) CreateLLMPrimaryConfig(record *llmPrimaryConfigRecord) error {
|
func (s *store) CreateLLMPrimaryConfig(record *llmPrimaryConfigRecord) error {
|
||||||
if err := s.db.Create(record).Error; err != nil {
|
if err := s.db.Create(record).Error; err != nil {
|
||||||
@@ -206,6 +220,7 @@ type LLMMessageQueueInput struct {
|
|||||||
PacketID int64
|
PacketID int64
|
||||||
ChannelID *string
|
ChannelID *string
|
||||||
Topic string
|
Topic string
|
||||||
|
MessageType string // "channel" 或 "direct"
|
||||||
ContentJSON *string
|
ContentJSON *string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,6 +241,11 @@ func (s *store) EnqueueLLMMessage(input LLMMessageQueueInput) (*llmMessageQueueR
|
|||||||
return nil, nil // 机器人的 LLM 队列未启用,静默返回
|
return nil, nil // 机器人的 LLM 队列未启用,静默返回
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 忽略机器人自己发送的消息,避免自循环
|
||||||
|
if input.FromNodeID == bot.NodeID {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
if input.FromNodeID == "" {
|
if input.FromNodeID == "" {
|
||||||
return nil, fmt.Errorf("from_node_id is required")
|
return nil, fmt.Errorf("from_node_id is required")
|
||||||
}
|
}
|
||||||
@@ -258,6 +278,10 @@ func (s *store) EnqueueLLMMessage(input LLMMessageQueueInput) (*llmMessageQueueR
|
|||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
messageType := input.MessageType
|
||||||
|
if messageType == "" {
|
||||||
|
messageType = "direct"
|
||||||
|
}
|
||||||
record := &llmMessageQueueRecord{
|
record := &llmMessageQueueRecord{
|
||||||
BotID: input.BotID,
|
BotID: input.BotID,
|
||||||
BotNodeID: input.BotNodeID,
|
BotNodeID: input.BotNodeID,
|
||||||
@@ -270,6 +294,7 @@ func (s *store) EnqueueLLMMessage(input LLMMessageQueueInput) (*llmMessageQueueR
|
|||||||
PacketID: input.PacketID,
|
PacketID: input.PacketID,
|
||||||
ChannelID: input.ChannelID,
|
ChannelID: input.ChannelID,
|
||||||
Topic: input.Topic,
|
Topic: input.Topic,
|
||||||
|
MessageType: messageType,
|
||||||
Status: llmMessageStatusPending,
|
Status: llmMessageStatusPending,
|
||||||
ReceivedAt: now,
|
ReceivedAt: now,
|
||||||
ContentJSON: input.ContentJSON,
|
ContentJSON: input.ContentJSON,
|
||||||
@@ -450,8 +475,11 @@ func enqueueChannelMessageToLLM(s *store, record map[string]any) error {
|
|||||||
return fmt.Errorf("query bots for channel message enqueue: %w", err)
|
return fmt.Errorf("query bots for channel message enqueue: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 为每个符合条件的机器人创建一条队列记录
|
// 为每个符合条件的机器人创建一条队列记录(忽略机器人自己发送的消息)
|
||||||
for _, bot := range bots {
|
for _, bot := range bots {
|
||||||
|
if fromNodeID == bot.NodeID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
_, err = s.EnqueueLLMMessage(LLMMessageQueueInput{
|
_, err = s.EnqueueLLMMessage(LLMMessageQueueInput{
|
||||||
BotID: bot.ID,
|
BotID: bot.ID,
|
||||||
BotNodeID: bot.NodeID,
|
BotNodeID: bot.NodeID,
|
||||||
@@ -464,6 +492,7 @@ func enqueueChannelMessageToLLM(s *store, record map[string]any) error {
|
|||||||
PacketID: packetID,
|
PacketID: packetID,
|
||||||
ChannelID: channelID,
|
ChannelID: channelID,
|
||||||
Topic: topic,
|
Topic: topic,
|
||||||
|
MessageType: "channel",
|
||||||
ContentJSON: contentPtr,
|
ContentJSON: contentPtr,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -272,8 +272,9 @@ func run(cfg *config) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create bot sender adapter
|
// Create bot sender adapter - 支持频道消息和私聊消息两种发送方式
|
||||||
botSenderAdapter := autoreply.NewBotServiceAdapter(
|
botSenderAdapter := autoreply.NewBotServiceAdapter(
|
||||||
|
// SendDirectText: 发送私聊消息
|
||||||
func(ctx context.Context, botID uint64, toNodeNum int64, text string) error {
|
func(ctx context.Context, botID uint64, toNodeNum int64, text string) error {
|
||||||
_, err := botSender.SendText(ctx, botSendTextRequest{
|
_, err := botSender.SendText(ctx, botSendTextRequest{
|
||||||
BotID: botID,
|
BotID: botID,
|
||||||
@@ -283,12 +284,23 @@ func run(cfg *config) error {
|
|||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
|
// SendChannelText: 发送频道消息
|
||||||
|
func(ctx context.Context, botID uint64, channelID string, text string) error {
|
||||||
|
_, err := botSender.SendText(ctx, botSendTextRequest{
|
||||||
|
BotID: botID,
|
||||||
|
MessageType: "channel",
|
||||||
|
ChannelID: channelID,
|
||||||
|
Text: text,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
aiService, err = ai.NewService(ai.Config{
|
aiService, err = ai.NewService(ai.Config{
|
||||||
LLMProviders: providerConfigs,
|
LLMProviders: providerConfigs,
|
||||||
DataDir: cfg.DataDir,
|
DataDir: cfg.DataDir,
|
||||||
Enabled: cfg.AI.Enabled,
|
Enabled: cfg.AI.Enabled,
|
||||||
|
SystemPromptStore: store,
|
||||||
}, store.db, botSenderAdapter)
|
}, store.db, botSenderAdapter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Warning: failed to initialize AI service: %v\n", err)
|
fmt.Fprintf(os.Stderr, "Warning: failed to initialize AI service: %v\n", err)
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "bot_1_1781711397025279500",
|
|
||||||
"bot_id": 1,
|
|
||||||
"bot_node_id": "!4217495a",
|
|
||||||
"title": "[来自 !8f821aa5] 🌞",
|
|
||||||
"created_at": "2026-06-17T23:49:57.0252795+08:00",
|
|
||||||
"updated_at": "2026-06-17T23:50:03.9827715+08:00",
|
|
||||||
"messages": [
|
|
||||||
{
|
|
||||||
"role": "user",
|
|
||||||
"content": "[来自 !8f821aa5] 🌞"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"role": "assistant",
|
|
||||||
"content": "你好呀!☀️\n\n看到你发来的这个像是系统标识符的字符串,还有那个温暖的太阳表情,感觉像是某种问候或者测试信号?\n\n如果你是想让我解读这个 ..."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "bot_1_1781713830520078500",
|
|
||||||
"bot_id": 1,
|
|
||||||
"bot_node_id": "!1abcead5",
|
|
||||||
"title": "[来自 !72d17be4] 你好",
|
|
||||||
"created_at": "2026-06-18T00:30:30.5200785+08:00",
|
|
||||||
"updated_at": "2026-06-18T00:30:34.3123551+08:00",
|
|
||||||
"messages": [
|
|
||||||
{
|
|
||||||
"role": "user",
|
|
||||||
"content": "[来自 !72d17be4] 你好"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"role": "assistant",
|
|
||||||
"content": "你好!很高兴见到你!😊\n\n我是DeepSeek,由深度求索公司创造的AI助手。无论你有什么问题、需要帮助,还是只是想聊聊天,我都很乐意陪你!\n\n有什\ufffd\ufffd..."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "bot_1_1781713890525027000",
|
|
||||||
"bot_id": 1,
|
|
||||||
"bot_node_id": "!1abcead5",
|
|
||||||
"title": "[来自 !72d17be4] 你好",
|
|
||||||
"created_at": "2026-06-18T00:31:30.525027+08:00",
|
|
||||||
"updated_at": "2026-06-18T00:31:33.1653132+08:00",
|
|
||||||
"messages": [
|
|
||||||
{
|
|
||||||
"role": "user",
|
|
||||||
"content": "[来自 !72d17be4] 你好"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"role": "assistant",
|
|
||||||
"content": "你好!😊 有什么可以帮你的吗?"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "bot_1_1781713920523402600",
|
|
||||||
"bot_id": 1,
|
|
||||||
"bot_node_id": "!1abcead5",
|
|
||||||
"title": "[来自 !72d17be4] 你好",
|
|
||||||
"created_at": "2026-06-18T00:32:00.5234026+08:00",
|
|
||||||
"updated_at": "2026-06-18T00:32:04.2936935+08:00",
|
|
||||||
"messages": [
|
|
||||||
{
|
|
||||||
"role": "user",
|
|
||||||
"content": "[来自 !72d17be4] 你好"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"role": "assistant",
|
|
||||||
"content": "你好!很高兴见到你!😊\n\n我是DeepSeek,一个由深度求索公司创造的AI助手。无论你有什么问题、需要帮助,还是只是想聊聊天,我都很乐意陪伴你、\ufffd..."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "bot_1_1781714154187832400",
|
|
||||||
"bot_id": 1,
|
|
||||||
"bot_node_id": "!1abcead5",
|
|
||||||
"title": "[来自 !72d17be4] 你好",
|
|
||||||
"created_at": "2026-06-18T00:35:54.1878324+08:00",
|
|
||||||
"updated_at": "2026-06-18T00:35:58.3371209+08:00",
|
|
||||||
"messages": [
|
|
||||||
{
|
|
||||||
"role": "user",
|
|
||||||
"content": "[来自 !72d17be4] 你好"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"role": "assistant",
|
|
||||||
"content": "你好!很高兴见到你!😊\n\n我是DeepSeek,由深度求索公司创造的AI助手。无论你有什么问题、需要帮助,还是只是想聊聊天,我都很乐意陪你一起探索\ufffd..."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "bot_1_1781714169190665700",
|
|
||||||
"bot_id": 1,
|
|
||||||
"bot_node_id": "!1abcead5",
|
|
||||||
"title": "[来自 !72d17be4] 你好",
|
|
||||||
"created_at": "2026-06-18T00:36:09.1906657+08:00",
|
|
||||||
"updated_at": "2026-06-18T00:36:13.5432077+08:00",
|
|
||||||
"messages": [
|
|
||||||
{
|
|
||||||
"role": "user",
|
|
||||||
"content": "[来自 !72d17be4] 你好"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"role": "assistant",
|
|
||||||
"content": "你好!很高兴见到你。😊\n\n我是DeepSeek,由深度求索公司创造的AI助手。我可以帮你解答问题、处理文档、进行创作、分析数据……几乎任何文字相关\ufffd..."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "bot_1_1781714179192265800",
|
|
||||||
"bot_id": 1,
|
|
||||||
"bot_node_id": "!1abcead5",
|
|
||||||
"title": "[来自 !72d17be4] 111",
|
|
||||||
"created_at": "2026-06-18T00:36:19.1922658+08:00",
|
|
||||||
"updated_at": "2026-06-18T00:36:23.4404859+08:00",
|
|
||||||
"messages": [
|
|
||||||
{
|
|
||||||
"role": "user",
|
|
||||||
"content": "[来自 !72d17be4] 111"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"role": "assistant",
|
|
||||||
"content": "你好!你发送了“111”,请问有什么需要帮助的吗?如果有具体问题或指令,请告诉我。"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "bot_1_1781714224192405100",
|
|
||||||
"bot_id": 1,
|
|
||||||
"bot_node_id": "!1abcead5",
|
|
||||||
"title": "[来自 !72d17be4] 你好",
|
|
||||||
"created_at": "2026-06-18T00:37:04.1924051+08:00",
|
|
||||||
"updated_at": "2026-06-18T00:47:31.9941255+08:00",
|
|
||||||
"messages": [
|
|
||||||
{
|
|
||||||
"role": "user",
|
|
||||||
"content": "[来自 !72d17be4] 你好"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"role": "assistant",
|
|
||||||
"content": "你好!很高兴见到你!😊\n\n我收到了你的消息,不过看起来前面那个“!72d17be4”像是一个标识符或者系统生成的代码。如果你有什么具体问题或者需��..."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"role": "user",
|
|
||||||
"content": "[来自 !72d17be4] 你好"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"role": "assistant",
|
|
||||||
"content": "你好!再次见到你很高兴!😊\n\n我看到你又一次带着“!72d17be4”这个标识出现了。这个标识似乎一直在跟随你的消息。如果你需要我帮你处理、解析��..."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"role": "user",
|
|
||||||
"content": "[来自 !72d17be4] 你好"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"role": "assistant",
|
|
||||||
"content": "你好!😊\n\n我看到你第三次发来了相同的消息,并且每次前面都带着 **`!72d17be4`** 这个标识。\n\n如果你是在**测试**或者**重复发送**,我理解这可能是��..."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"role": "user",
|
|
||||||
"content": "[来自 !72d17be4] 你好"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"role": "assistant",
|
|
||||||
"content": "你好! 我注意到你连续四次发送了相同格式的消息,前面都带有 `[来自 !72d17be4]` 标识。这可能是系统自动发送测试或者你希望引起我的注意。 无论..."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user