feat: refactor handoff logic and update UI descriptions for Graph Tool integration
This commit is contained in:
@@ -103,14 +103,6 @@ func (s *aiReplyService) TriggerReply(ctx context.Context, conversation models.C
|
||||
if pendingInterrupt := svc.ConversationInterruptService.FindLatestPendingByConversationID(conversation.ID); pendingInterrupt != nil {
|
||||
return s.resumePendingInterrupt(ctx, conversation, message, aiAgent, pendingInterrupt, trace, &summary)
|
||||
}
|
||||
if s.shouldHandoffByQuestion(message.Content, aiAgent) {
|
||||
return s.handoffConversation(conversation, aiAgent, "用户主动要求人工")
|
||||
}
|
||||
if aiAgent.ServiceMode != enums.IMConversationServiceModeAIOnly &&
|
||||
aiAgent.MaxAIReplyRounds > 0 &&
|
||||
conversation.AIReplyRounds >= aiAgent.MaxAIReplyRounds {
|
||||
return s.handoffConversation(conversation, aiAgent, "达到AI最大回复轮次")
|
||||
}
|
||||
aiConfig := svc.AIConfigService.Get(aiAgent.AIConfigID)
|
||||
if aiConfig == nil {
|
||||
return fmt.Errorf("ai config is nil")
|
||||
@@ -295,23 +287,6 @@ func (s *aiReplyService) sendAIReply(conversation models.Conversation, message m
|
||||
return replyMessage, err
|
||||
}
|
||||
|
||||
func (s *aiReplyService) shouldHandoffByQuestion(question string, aiAgent models.AIAgent) bool {
|
||||
if aiAgent.ServiceMode == enums.IMConversationServiceModeAIOnly {
|
||||
return false
|
||||
}
|
||||
normalized := strings.ReplaceAll(strings.ToLower(strings.TrimSpace(question)), " ", "")
|
||||
if normalized == "" {
|
||||
return false
|
||||
}
|
||||
keywords := []string{"转人工", "人工客服"}
|
||||
for _, keyword := range keywords {
|
||||
if strings.Contains(normalized, keyword) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *aiReplyService) writeRunLog(startedAt time.Time, message models.Message, conversation models.Conversation, aiAgent models.AIAgent,
|
||||
question string, runErr error, trace *aiReplyTraceData, summary *Summary) {
|
||||
errorMessage := ""
|
||||
@@ -661,7 +636,7 @@ func parseRuntimeTraceData(raw string) runtimeTraceProjection {
|
||||
|
||||
func isCancellationReply(replyText string) bool {
|
||||
replyText = strings.TrimSpace(replyText)
|
||||
return strings.Contains(replyText, "已取消本次工单创建")
|
||||
return strings.Contains(replyText, "已取消本次工单创建") || strings.Contains(replyText, "已取消本次转人工")
|
||||
}
|
||||
|
||||
func isCheckpointMissingError(err error) bool {
|
||||
@@ -672,16 +647,6 @@ func isCheckpointMissingError(err error) bool {
|
||||
return strings.Contains(message, "failed to load from checkpoint") && strings.Contains(message, "not exist")
|
||||
}
|
||||
|
||||
func (s *aiReplyService) handoffConversation(conversation models.Conversation, aiAgent models.AIAgent, reason string) error {
|
||||
if err := svc.ConversationService.HandoffByAI(conversation.ID, &aiAgent, reason); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := svc.MessageService.SendAIMessage(conversation.ID, aiAgent.ID, fmt.Sprintf("ai_handoff_%d", conversation.LastMessageID), enums.IMMessageTypeText, "已为你转接人工客服,请稍候。", "", s.buildAIPrincipal(aiAgent)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *aiReplyService) buildAIPrincipal(aiAgent models.AIAgent) *dto.AuthPrincipal {
|
||||
username := "AI"
|
||||
if strings.TrimSpace(aiAgent.Name) != "" {
|
||||
|
||||
@@ -95,7 +95,7 @@ func (s *service) prepareToolsForRun(req *Request) error {
|
||||
AIAgent: req.AIAgent,
|
||||
AIConfig: req.AIConfig,
|
||||
UserMessage: req.UserMessage,
|
||||
AllowedToolCodes: resolveAllowedToolCodes(req.AIAgent, req.SelectedSkill),
|
||||
AllowedToolCodes: ensureCoreGraphToolCodes(resolveAllowedToolCodes(req.AIAgent, req.SelectedSkill)),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -113,7 +113,7 @@ func (s *service) prepareToolsForResume(req *ResumeRequest) error {
|
||||
Conversation: req.Conversation,
|
||||
AIAgent: req.AIAgent,
|
||||
AIConfig: req.AIConfig,
|
||||
AllowedToolCodes: parseAgentAllowedToolCodes(req.AIAgent),
|
||||
AllowedToolCodes: ensureCoreGraphToolCodes(parseAgentAllowedToolCodes(req.AIAgent)),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -281,3 +281,18 @@ func resolveAllowedToolCodes(aiAgent *models.AIAgent, skill *models.SkillDefinit
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
func ensureCoreGraphToolCodes(toolCodes []string) []string {
|
||||
if len(toolCodes) == 0 {
|
||||
return nil
|
||||
}
|
||||
ret := make([]string, 0, len(toolCodes)+1)
|
||||
ret = append(ret, toolCodes...)
|
||||
for _, item := range ret {
|
||||
if strings.TrimSpace(item) == toolx.GraphHandoffConversationToolCode {
|
||||
return ret
|
||||
}
|
||||
}
|
||||
ret = append(ret, toolx.GraphHandoffConversationToolCode)
|
||||
return ret
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user