fix(runtime): dispatch explicit human handoff requests without model

This commit is contained in:
t
2026-09-24 17:04:57 +08:00
parent 71c42b98e9
commit cd2f7af003
2 changed files with 132 additions and 0 deletions
@@ -4,9 +4,11 @@ import (
"context"
"fmt"
"log/slog"
"regexp"
"strconv"
"strings"
"time"
"unicode"
"code.tczkiot.com/wlw/ai-agent/contract"
applicationruntime "code.tczkiot.com/wlw/ai-agent/internal/ai/application/runtime"
@@ -27,6 +29,26 @@ const aiReplyInvocationToolCode = "runtime/ai_reply"
const aiReplyInvocationRecoveryGrace = 30 * time.Second
// Match complete commands, not mentions such as “不要转人工” or “转人工是什么意思”.
// These requests must not depend on the model choosing to call a tool.
var explicitHumanHandoffCommand = regexp.MustCompile(`^(请|麻烦)?(帮我|给我)?(我要|我想要|我想)?(转接|转|找|联系|接通)?(一下)?人工(客服|服务)?(谢谢|吧)?$`)
func isExplicitHumanHandoffRequest(message models.Message) bool {
if message.SenderType != enums.IMSenderTypeCustomer || message.MessageType != enums.IMMessageTypeText {
return false
}
content := strings.TrimFunc(strings.TrimSpace(message.Content), func(r rune) bool {
return unicode.IsSpace(r) || strings.ContainsRune("。!!,,??;;", r)
})
content = strings.Map(func(r rune) rune {
if unicode.IsSpace(r) {
return -1
}
return r
}, content)
return explicitHumanHandoffCommand.MatchString(content)
}
func (s *aiReplyService) resolveReplyTimeout(aiAgent models.AIAgent) time.Duration {
if aiAgent.ReplyTimeoutSeconds <= 0 {
return time.Duration(defaultAIReplyAsyncTimeoutSeconds) * time.Second
@@ -313,6 +335,12 @@ func (s *aiReplyService) TriggerReply(ctx context.Context, conversation models.C
if !IsAIAgentRolloutEligible(conversation, aiAgent, svc.ChannelService.Get(conversation.ChannelID)) {
return nil
}
if isExplicitHumanHandoffRequest(message) {
_, err := svc.ConversationHumanDispatchService.HandoffByAIWithRequestID(
conversation.ID, aiAgent, strings.TrimSpace(message.Content), message.RequestID,
)
return err
}
identityResolution, identityErr := resolveGuestBusinessIdentity(ctx, conversation, message)
if identityErr != nil || identityResolution.NeedsPrompt {
_, err := s.commit.CommitAIReply(replyCommitInput{