feat: 优化客服会话及延迟消息逻辑
This commit is contained in:
@@ -6,11 +6,6 @@ var (
|
|||||||
PlatformXHS = newPlatform(gconv.PtrString("xiaohongshu"), "小红书")
|
PlatformXHS = newPlatform(gconv.PtrString("xiaohongshu"), "小红书")
|
||||||
PlatformDY = newPlatform(gconv.PtrString("douyin"), "抖音")
|
PlatformDY = newPlatform(gconv.PtrString("douyin"), "抖音")
|
||||||
PlatformKS = newPlatform(gconv.PtrString("kuaishou"), "快手")
|
PlatformKS = newPlatform(gconv.PtrString("kuaishou"), "快手")
|
||||||
platformMap = map[Platform]platform{
|
|
||||||
gconv.PtrString("xiaohongshu"): PlatformXHS,
|
|
||||||
gconv.PtrString("douyin"): PlatformDY,
|
|
||||||
gconv.PtrString("kuaishou"): PlatformKS,
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Platform *string
|
type Platform *string
|
||||||
@@ -32,8 +27,13 @@ func newPlatform(code Platform, desc string) platform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetDescByCode(code Platform) string {
|
func GetDescByCode(code Platform) string {
|
||||||
if p, ok := platformMap[code]; ok {
|
switch *code {
|
||||||
return p.Desc()
|
case *PlatformXHS.Code():
|
||||||
|
return PlatformXHS.Desc()
|
||||||
|
case *PlatformDY.Code():
|
||||||
|
return PlatformDY.Desc()
|
||||||
|
case *PlatformKS.Code():
|
||||||
|
return PlatformKS.Desc()
|
||||||
}
|
}
|
||||||
return "未知平台"
|
return "未知平台"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
package account
|
|
||||||
|
|
||||||
import "github.com/gogf/gf/v2/util/gconv"
|
|
||||||
|
|
||||||
var (
|
|
||||||
VectorStatusPending = newVectorStatus(gconv.PtrInt8(1), "pending")
|
|
||||||
VectorStatusProcessing = newVectorStatus(gconv.PtrInt8(2), "processing")
|
|
||||||
VectorStatusCompleted = newVectorStatus(gconv.PtrInt8(3), "completed")
|
|
||||||
VectorStatusFailed = newVectorStatus(gconv.PtrInt8(4), "failed")
|
|
||||||
)
|
|
||||||
|
|
||||||
type VectorStatus *int8
|
|
||||||
|
|
||||||
type vectorStatus struct {
|
|
||||||
code VectorStatus
|
|
||||||
desc string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s vectorStatus) Code() VectorStatus {
|
|
||||||
return s.code
|
|
||||||
}
|
|
||||||
func (s vectorStatus) Desc() string {
|
|
||||||
return s.desc
|
|
||||||
}
|
|
||||||
|
|
||||||
func newVectorStatus(code VectorStatus, desc string) vectorStatus {
|
|
||||||
return vectorStatus{code: code, desc: desc}
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,11 @@ package public
|
|||||||
|
|
||||||
const GmqMsgPluginsName = "gmq_msg"
|
const GmqMsgPluginsName = "gmq_msg"
|
||||||
|
|
||||||
const AccountDialogKeyUserId = "account:dialog:%s"
|
const (
|
||||||
|
AccountMsgKey = "account:%s:%s:%s"
|
||||||
|
AccountDialogHistoryKey = "account:dialog:history:%s"
|
||||||
|
AccountGreetingOptionsKey = "account:greeting:options:%s"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
AccountFollowupTopic = "account:followup:stream" // 请求 Stream 键名(与发消息的key一致)
|
AccountFollowupTopic = "account:followup:stream" // 请求 Stream 键名(与发消息的key一致)
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
package public
|
|
||||||
|
|
||||||
const KnowledgeLockEsKey = "rag:knowledge:lock:knowledgeIdEs-%v"
|
|
||||||
const KnowledgeLockSqlKey = "rag:knowledge:lock:knowledgeIdSql-%v"
|
|
||||||
const KnowledgeContentHashEsKey = "rag:knowledge:knowledgeId:contentHashEs-%v"
|
|
||||||
const KnowledgeContentHashSqlKey = "rag:knowledge:knowledgeId:contentHashSql-%v"
|
|
||||||
|
|
||||||
const KnowledgeDocumentChunkTopic = "knowledge:document:chunk:stream" // 请求 Stream 键名(与发消息的key一致)
|
|
||||||
|
|
||||||
const (
|
|
||||||
KnowledgeDocumentVectorStatusTopic = "knowledge:document:vector:status:stream"
|
|
||||||
KnowledgeDocumentVectorStatusConsumer = "knowledge-document-vector-status-consumer"
|
|
||||||
KnowledgeDocumentVectorStatusBatchSize = 1
|
|
||||||
KnowledgeDocumentVectorStatusAutoAck = false
|
|
||||||
)
|
|
||||||
18
consts/public/session_tool.go
Normal file
18
consts/public/session_tool.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package public
|
||||||
|
|
||||||
|
// 欢迎语
|
||||||
|
const (
|
||||||
|
GreetingBegin = "您好,很高兴为您服务!请问有什么可以帮您?"
|
||||||
|
GreetingBetween = "💗回复数字就好~"
|
||||||
|
GreetingEnd = "🌟也可直接点击下方咨询专业老师~"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 追问
|
||||||
|
const (
|
||||||
|
SceneOpeningRemark = "宝子,刚才给您发的信息您有看到吗?有任何问题都能直接问我,加微信也能更方便沟通~"
|
||||||
|
SceneDialog = "看您暂时没回复,是不是还有什么疑问?加微信我详细给您说明~"
|
||||||
|
SceneCardSend = "宝子,加上没~要及时加哦,不然卡片容易失效哒✨"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 对话超时时间
|
||||||
|
const DialogTimeout = 10
|
||||||
@@ -6,12 +6,6 @@ var (
|
|||||||
SceneTypeOpeningRemark = newSceneType(gconv.PtrInt8(1), "开场白无回应")
|
SceneTypeOpeningRemark = newSceneType(gconv.PtrInt8(1), "开场白无回应")
|
||||||
SceneTypeDialog = newSceneType(gconv.PtrInt8(2), "对话中途无回应")
|
SceneTypeDialog = newSceneType(gconv.PtrInt8(2), "对话中途无回应")
|
||||||
SceneTypeCardSend = newSceneType(gconv.PtrInt8(3), "卡片发送后无回应")
|
SceneTypeCardSend = newSceneType(gconv.PtrInt8(3), "卡片发送后无回应")
|
||||||
|
|
||||||
sceneTypeMap = map[SceneType]sceneType{
|
|
||||||
gconv.PtrInt8(1): SceneTypeOpeningRemark,
|
|
||||||
gconv.PtrInt8(2): SceneTypeDialog,
|
|
||||||
gconv.PtrInt8(3): SceneTypeCardSend,
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SceneType *int8
|
type SceneType *int8
|
||||||
@@ -33,8 +27,13 @@ func newSceneType(code SceneType, desc string) sceneType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetDescByCode(code SceneType) string {
|
func GetDescByCode(code SceneType) string {
|
||||||
if p, ok := sceneTypeMap[code]; ok {
|
switch *code {
|
||||||
return p.Desc()
|
case *SceneTypeOpeningRemark.Code():
|
||||||
|
return SceneTypeOpeningRemark.Desc()
|
||||||
|
case *SceneTypeDialog.Code():
|
||||||
|
return SceneTypeDialog.Desc()
|
||||||
|
case *SceneTypeCardSend.Code():
|
||||||
|
return SceneTypeCardSend.Desc()
|
||||||
}
|
}
|
||||||
return "未知场景类型"
|
return "未知场景类型"
|
||||||
}
|
}
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -12,7 +12,7 @@ require (
|
|||||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.10.0
|
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.10.0
|
||||||
github.com/gogf/gf/contrib/nosql/redis/v2 v2.10.0
|
github.com/gogf/gf/contrib/nosql/redis/v2 v2.10.0
|
||||||
github.com/gogf/gf/v2 v2.10.0
|
github.com/gogf/gf/v2 v2.10.0
|
||||||
github.com/gorilla/websocket v1.5.3
|
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|||||||
1
go.sum
1
go.sum
@@ -146,6 +146,7 @@ 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.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
|
||||||
github.com/grokify/html-strip-tags-go v0.1.0 h1:03UrQLjAny8xci+R+qjCce/MYnpNXCtgzltlQbOBae4=
|
github.com/grokify/html-strip-tags-go v0.1.0 h1:03UrQLjAny8xci+R+qjCce/MYnpNXCtgzltlQbOBae4=
|
||||||
github.com/grokify/html-strip-tags-go v0.1.0/go.mod h1:ZdzgfHEzAfz9X6Xe5eBLVblWIxXfYSQ40S/VKrAOGpc=
|
github.com/grokify/html-strip-tags-go v0.1.0/go.mod h1:ZdzgfHEzAfz9X6Xe5eBLVblWIxXfYSQ40S/VKrAOGpc=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
|
||||||
|
|||||||
@@ -2,13 +2,9 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"customer-server/consts/public"
|
|
||||||
"customer-server/model/dto"
|
"customer-server/model/dto"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
gmq "github.com/bjang03/gmq/core/gmq"
|
|
||||||
"github.com/bjang03/gmq/mq"
|
|
||||||
"github.com/bjang03/gmq/types"
|
|
||||||
"github.com/gogf/gf/v2/frame/g"
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,14 +14,6 @@ var (
|
|||||||
|
|
||||||
type accountHttpService struct{}
|
type accountHttpService struct{}
|
||||||
|
|
||||||
func (s *accountHttpService) DeleteDelayMsg(ctx context.Context) (err error) {
|
|
||||||
return gmq.GetGmq(public.GmqMsgPluginsName).GmqDeleteDelay(ctx, &mq.NatsDelMessage{
|
|
||||||
DelMessage: types.DelMessage{
|
|
||||||
Topic: public.AccountFollowupTopic,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *accountHttpService) Connect(ctx context.Context, req *dto.AccountHttpConnectReq) (res *dto.AccountHttpConnectRes, err error) {
|
func (s *accountHttpService) Connect(ctx context.Context, req *dto.AccountHttpConnectReq) (res *dto.AccountHttpConnectRes, err error) {
|
||||||
// 获取客服账号信息
|
// 获取客服账号信息
|
||||||
accountInfo, err := SessionToolService.GetAccountInfo(ctx, req.AccountCode)
|
accountInfo, err := SessionToolService.GetAccountInfo(ctx, req.AccountCode)
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import (
|
|||||||
gmq "github.com/bjang03/gmq/core/gmq"
|
gmq "github.com/bjang03/gmq/core/gmq"
|
||||||
"github.com/bjang03/gmq/mq"
|
"github.com/bjang03/gmq/mq"
|
||||||
"github.com/bjang03/gmq/types"
|
"github.com/bjang03/gmq/types"
|
||||||
|
"github.com/gogf/gf/v2/container/gvar"
|
||||||
"github.com/gogf/gf/v2/encoding/gjson"
|
"github.com/gogf/gf/v2/encoding/gjson"
|
||||||
"github.com/gogf/gf/v2/frame/g"
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
"github.com/gogf/gf/v2/util/gconv"
|
"github.com/gogf/gf/v2/util/gconv"
|
||||||
@@ -33,7 +34,7 @@ type sessionToolService struct{}
|
|||||||
func (s *sessionToolService) PushOpeningRemark(ctx context.Context, userId string, accountInfo *dto.AccountVO, headers map[string]string) (content string, err error) {
|
func (s *sessionToolService) PushOpeningRemark(ctx context.Context, userId string, accountInfo *dto.AccountVO, headers map[string]string) (content string, err error) {
|
||||||
content = ""
|
content = ""
|
||||||
var sceneType = scriptedSpeech.SceneTypeOpeningRemark
|
var sceneType = scriptedSpeech.SceneTypeOpeningRemark
|
||||||
var key = fmt.Sprintf("account:%s:%s:%s", accountInfo.AccountCode, account.GetDescByCode(accountInfo.Platform), userId)
|
var key = fmt.Sprintf(public.AccountMsgKey, accountInfo.AccountCode, account.GetDescByCode(accountInfo.Platform), userId)
|
||||||
get, err := g.Redis().Get(ctx, key)
|
get, err := g.Redis().Get(ctx, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@@ -50,13 +51,17 @@ func (s *sessionToolService) PushOpeningRemark(ctx context.Context, userId strin
|
|||||||
err = fmt.Errorf("数据集不存在")
|
err = fmt.Errorf("数据集不存在")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var datasetDescriptions []string
|
var datasetDescriptions [][]string
|
||||||
for _, dataset := range datasetInfo.List {
|
for _, dataset := range datasetInfo.List {
|
||||||
datasetDescriptions = append(datasetDescriptions, dataset.Name)
|
datasetDescriptions = append(datasetDescriptions, []string{dataset.Name, gconv.String(dataset.Id)})
|
||||||
}
|
}
|
||||||
content = SessionToolService.BuildMenuContent(accountInfo.Greeting, datasetDescriptions, len(accountInfo.DatasetIds))
|
content, err = SessionToolService.BuildGreeting(ctx, userId, accountInfo.Greeting, datasetDescriptions, len(accountInfo.DatasetIds))
|
||||||
} else {
|
} else {
|
||||||
content = SessionToolService.BuildMenuContent(accountInfo.Greeting, accountInfo.KeywordOption, len(accountInfo.DatasetIds))
|
var datasetDescriptions [][]string
|
||||||
|
for _, keyword := range accountInfo.KeywordOption {
|
||||||
|
datasetDescriptions = append(datasetDescriptions, []string{keyword, gconv.String(accountInfo.DatasetIds[0])})
|
||||||
|
}
|
||||||
|
content, err = SessionToolService.BuildGreeting(ctx, userId, accountInfo.Greeting, datasetDescriptions, len(accountInfo.DatasetIds))
|
||||||
}
|
}
|
||||||
err = s.pushDelayMsg(ctx, key, sceneType.Code(), sceneType.Desc(), accountInfo.DatasetIds)
|
err = s.pushDelayMsg(ctx, key, sceneType.Code(), sceneType.Desc(), accountInfo.DatasetIds)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -67,19 +72,52 @@ func (s *sessionToolService) PushOpeningRemark(ctx context.Context, userId strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *sessionToolService) PushDialog(ctx context.Context, userId string, questionContent string, accountInfo *dto.AccountVO, headers map[string]string) (content string, err error) {
|
func (s *sessionToolService) PushDialog(ctx context.Context, userId string, questionContent string, accountInfo *dto.AccountVO, headers map[string]string) (content string, err error) {
|
||||||
sceneType := scriptedSpeech.SceneTypeDialog
|
|
||||||
// 删除延迟消息
|
|
||||||
//err = s.DeleteDelayMsg(ctx)
|
|
||||||
//if err != nil {
|
|
||||||
// return nil, err
|
|
||||||
//}
|
|
||||||
content = ""
|
content = ""
|
||||||
var key = fmt.Sprintf("account:%s:%s:%s", accountInfo.AccountCode, account.GetDescByCode(accountInfo.Platform), userId)
|
// 删除延迟消息
|
||||||
|
if err = s.DeleteDelayMsg(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var key = fmt.Sprintf(public.AccountMsgKey, accountInfo.AccountCode, account.GetDescByCode(accountInfo.Platform), userId)
|
||||||
get, err := g.Redis().Get(ctx, key)
|
get, err := g.Redis().Get(ctx, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !g.IsEmpty(get) {
|
if !g.IsEmpty(get) {
|
||||||
|
|
||||||
|
sceneType := scriptedSpeech.SceneTypeDialog
|
||||||
|
var datasetIds []int64
|
||||||
|
|
||||||
|
var optionsMap *gvar.Var
|
||||||
|
optionsMap, err = g.Redis().Get(ctx, fmt.Sprintf(public.AccountGreetingOptionsKey, userId))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
jsonStr := gconv.String(optionsMap)
|
||||||
|
var data map[string]interface{}
|
||||||
|
if err = gconv.Scan(jsonStr, &data); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i, item := range data {
|
||||||
|
// 把每一项转成 map
|
||||||
|
if i == questionContent {
|
||||||
|
m := gconv.Map(item)
|
||||||
|
questionContent = gconv.String(m["datasetName"])
|
||||||
|
datasetIds = gconv.Int64s(m["datasetId"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if g.IsEmpty(datasetIds) {
|
||||||
|
var datasetRes []int64
|
||||||
|
datasetRes, err = s.getDatasetIdsByKeywords(ctx, questionContent, headers)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(datasetRes) > 0 {
|
||||||
|
datasetIds = datasetRes
|
||||||
|
} else {
|
||||||
|
datasetIds = accountInfo.DatasetIds
|
||||||
|
}
|
||||||
|
}
|
||||||
// 获取用户对话上下文
|
// 获取用户对话上下文
|
||||||
var history []*dto.Message
|
var history []*dto.Message
|
||||||
history, err = SessionToolService.GetUserHistory(ctx, userId)
|
history, err = SessionToolService.GetUserHistory(ctx, userId)
|
||||||
@@ -127,10 +165,10 @@ func (s *sessionToolService) PushDialog(ctx context.Context, userId string, ques
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if sceneType.Code() != scriptedSpeech.SceneTypeCardSend.Code() {
|
if *sceneType.Code() != *scriptedSpeech.SceneTypeCardSend.Code() {
|
||||||
// 通过HTTP调用rag服务的RAG查询接口
|
// 通过HTTP调用rag服务的RAG查询接口
|
||||||
var ragQuery *dto.RagQueryRes
|
var ragQuery *dto.RagQueryRes
|
||||||
ragQuery, err = SessionToolService.GetRagQuery(ctx, questionContent, accountInfo.DatasetIds, history, headers)
|
ragQuery, err = SessionToolService.GetRagQuery(ctx, questionContent, datasetIds, history, headers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("调用rag服务的RAG查询接口失败: %w", err)
|
err = fmt.Errorf("调用rag服务的RAG查询接口失败: %w", err)
|
||||||
return
|
return
|
||||||
@@ -148,7 +186,7 @@ func (s *sessionToolService) PushDialog(ctx context.Context, userId string, ques
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.pushDelayMsg(ctx, key, sceneType.Code(), sceneType.Desc(), accountInfo.DatasetIds)
|
err = s.pushDelayMsg(ctx, key, sceneType.Code(), sceneType.Desc(), datasetIds)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -157,7 +195,7 @@ func (s *sessionToolService) PushDialog(ctx context.Context, userId string, ques
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *sessionToolService) pushDelayMsg(ctx context.Context, key string, sceneTypeCode scriptedSpeech.SceneType, sceneTypeDesc string, datasetIds []int64) (err error) {
|
func (s *sessionToolService) pushDelayMsg(ctx context.Context, key string, sceneTypeCode scriptedSpeech.SceneType, sceneTypeDesc string, datasetIds []int64) (err error) {
|
||||||
err = g.Redis().SetEX(ctx, key, sceneTypeDesc, gconv.Int64(10*time.Second))
|
err = g.Redis().SetEX(ctx, key, sceneTypeDesc, gconv.Int64(public.DialogTimeout*time.Second))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -168,19 +206,19 @@ func (s *sessionToolService) pushDelayMsg(ctx context.Context, key string, scene
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("获取追问话术内容失败: %w", err)
|
return fmt.Errorf("获取追问话术内容失败: %w", err)
|
||||||
}
|
}
|
||||||
if g.IsEmpty(scriptedSpeechInfo) {
|
|
||||||
if sceneTypeCode == scriptedSpeech.SceneTypeOpeningRemark.Code() {
|
|
||||||
msg = "宝子,刚才给您发的信息您有看到吗?有任何问题都能直接问我,加微信也能更方便沟通~"
|
|
||||||
} else if sceneTypeCode == scriptedSpeech.SceneTypeDialog.Code() {
|
|
||||||
msg = "看您暂时没回复,是不是还有什么疑问?加微信我详细给您说明~"
|
|
||||||
} else if sceneTypeCode == scriptedSpeech.SceneTypeCardSend.Code() {
|
|
||||||
msg = "宝子,加上没~要及时加哦,不然卡片容易失效哒✨"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
msg = scriptedSpeechInfo.QuestionContent
|
msg = scriptedSpeechInfo.QuestionContent
|
||||||
} else {
|
|
||||||
msg = "宝子,刚才给您发的信息您有看到吗?有任何问题都能直接问我,加微信也能更方便沟通~"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if g.IsEmpty(msg) {
|
||||||
|
if *sceneTypeCode == *scriptedSpeech.SceneTypeOpeningRemark.Code() {
|
||||||
|
msg = public.SceneOpeningRemark
|
||||||
|
} else if *sceneTypeCode == *scriptedSpeech.SceneTypeDialog.Code() {
|
||||||
|
msg = public.SceneDialog
|
||||||
|
} else if *sceneTypeCode == *scriptedSpeech.SceneTypeCardSend.Code() {
|
||||||
|
msg = public.SceneCardSend
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var msgMap = map[string]string{
|
var msgMap = map[string]string{
|
||||||
"key": key,
|
"key": key,
|
||||||
"data": msg,
|
"data": msg,
|
||||||
@@ -197,6 +235,14 @@ func (s *sessionToolService) pushDelayMsg(ctx context.Context, key string, scene
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *sessionToolService) DeleteDelayMsg(ctx context.Context) (err error) {
|
||||||
|
return gmq.GetGmq(public.GmqMsgPluginsName).GmqDeleteDelay(ctx, &mq.NatsDelMessage{
|
||||||
|
DelMessage: types.DelMessage{
|
||||||
|
Topic: public.AccountFollowupTopic,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// GetAccountInfo 获取客服账号信息
|
// GetAccountInfo 获取客服账号信息
|
||||||
func (s *sessionToolService) GetAccountInfo(ctx context.Context, accountCode string) (res *dto.AccountVO, err error) {
|
func (s *sessionToolService) GetAccountInfo(ctx context.Context, accountCode string) (res *dto.AccountVO, err error) {
|
||||||
r, err := dao.Account.GetByAccountCode(ctx, &dto.GetByAccountCodeReq{
|
r, err := dao.Account.GetByAccountCode(ctx, &dto.GetByAccountCodeReq{
|
||||||
@@ -248,31 +294,34 @@ func (s *sessionToolService) GetDatasetInfo(ctx context.Context, datasetIds []in
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildMenuContent 生成菜单话术内容
|
// BuildGreeting 构建问候语
|
||||||
func (s *sessionToolService) BuildMenuContent(greeting string, options []string, datasetCount int) string {
|
func (s *sessionToolService) BuildGreeting(ctx context.Context, userId, greeting string, options [][]string, datasetCount int) (content string, err error) {
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
// 问候语
|
// 问候语
|
||||||
if datasetCount > 1 {
|
if datasetCount > 1 || greeting == "" {
|
||||||
greeting = "您好,很高兴为您服务!请问咨询什么方面问题?"
|
greeting = public.GreetingBegin
|
||||||
} else {
|
|
||||||
if greeting == "" {
|
|
||||||
greeting = "您好,很高兴为您服务!请问有什么可以帮您?"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.WriteString(greeting)
|
sb.WriteString(greeting)
|
||||||
sb.WriteByte('\n')
|
sb.WriteByte('\n')
|
||||||
// 拼接选项 1、xx 2、xx...
|
// 拼接选项 1、xx 2、xx...
|
||||||
|
var optionsMap = make(map[string]map[string]string, len(options))
|
||||||
for i, opt := range options {
|
for i, opt := range options {
|
||||||
|
optionsMap[gconv.String(i+1)] = map[string]string{
|
||||||
|
"datasetId": opt[1],
|
||||||
|
"datasetName": opt[0],
|
||||||
|
}
|
||||||
sb.WriteString(fmt.Sprintf("%d、%s\n", i+1, opt))
|
sb.WriteString(fmt.Sprintf("%d、%s\n", i+1, opt))
|
||||||
if i == len(options)-1 {
|
if i == len(options)-1 {
|
||||||
sb.WriteString(fmt.Sprintf("%s\n", "💗回复数字就好~"))
|
sb.WriteString(fmt.Sprintf("%s\n", public.GreetingBetween))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 固定结尾
|
// 固定结尾
|
||||||
sb.WriteString("🌟也可直接点击下方咨询专业老师~")
|
sb.WriteString(public.GreetingEnd)
|
||||||
|
content = sb.String()
|
||||||
|
err = g.Redis().SetEX(ctx, fmt.Sprintf(public.AccountGreetingOptionsKey, userId), optionsMap, gconv.Int64(public.DialogTimeout*time.Second))
|
||||||
|
|
||||||
return sb.String()
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetScriptedSpeechContent 获取话术内容
|
// GetScriptedSpeechContent 获取话术内容
|
||||||
@@ -304,7 +353,7 @@ func (s *sessionToolService) GetRagQuery(ctx context.Context, questionContent st
|
|||||||
|
|
||||||
// SaveUserHistory 保存用户对话历史到Redis
|
// SaveUserHistory 保存用户对话历史到Redis
|
||||||
func (s *sessionToolService) SaveUserHistory(ctx context.Context, userKey string, newMessages []*dto.Message) (err error) {
|
func (s *sessionToolService) SaveUserHistory(ctx context.Context, userKey string, newMessages []*dto.Message) (err error) {
|
||||||
key := fmt.Sprintf(public.AccountDialogKeyUserId, userKey)
|
key := fmt.Sprintf(public.AccountDialogHistoryKey, userKey)
|
||||||
|
|
||||||
// 1. 先读旧历史
|
// 1. 先读旧历史
|
||||||
var oldMessages []*dto.Message
|
var oldMessages []*dto.Message
|
||||||
@@ -327,7 +376,7 @@ func (s *sessionToolService) SaveUserHistory(ctx context.Context, userKey string
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return g.Redis().SetEX(ctx, key, data, gconv.Int64(15*time.Second))
|
return g.Redis().SetEX(ctx, key, data, gconv.Int64(public.DialogTimeout*time.Second))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserHistory 从Redis获取用户历史
|
// GetUserHistory 从Redis获取用户历史
|
||||||
@@ -346,19 +395,19 @@ func (s *sessionToolService) GetUserHistory(ctx context.Context, key string) ([]
|
|||||||
|
|
||||||
// ClearUserHistory 清空历史(可选)
|
// ClearUserHistory 清空历史(可选)
|
||||||
func (s *sessionToolService) ClearUserHistory(ctx context.Context, userKey string) (int64, error) {
|
func (s *sessionToolService) ClearUserHistory(ctx context.Context, userKey string) (int64, error) {
|
||||||
key := fmt.Sprintf(public.AccountDialogKeyUserId, userKey)
|
key := fmt.Sprintf(public.AccountDialogHistoryKey, userKey)
|
||||||
return g.Redis().Del(ctx, key)
|
return g.Redis().Del(ctx, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getDatasetIdsByKeywords 通过关键词查询数据集ID
|
// getDatasetIdsByKeywords 通过关键词查询数据集ID
|
||||||
func (s *sessionToolService) getDatasetIdsByKeywords(ctx context.Context, content string, headers map[string]string) (res []int64, err error) {
|
func (s *sessionToolService) getDatasetIdsByKeywords(ctx context.Context, questionContent string, headers map[string]string) (res []int64, err error) {
|
||||||
// 1. 提取关键词
|
// 1. 提取关键词
|
||||||
keywords := s.extractKeywords(content)
|
keywords := s.extractKeywords(questionContent)
|
||||||
g.Log().Infof(ctx, "提取关键词: %v", keywords)
|
g.Log().Infof(ctx, "提取关键词: %v", keywords)
|
||||||
|
|
||||||
// 通过HTTP调用rag服务的关键词查询接口
|
// 通过HTTP调用rag服务的关键词查询接口
|
||||||
respKeyword := &dto.RAGListKeywordRes{}
|
respKeyword := &dto.RAGListKeywordRes{}
|
||||||
if err = http.Get(ctx, "rag/keyword/listKeyword", headers, &respKeyword, &dto.RAGListKeywordReq{
|
if err = http.Get(ctx, "rag/keyword/list", headers, &respKeyword, &dto.RAGListKeywordReq{
|
||||||
Words: keywords,
|
Words: keywords,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
jaeger.RecordError(ctx, err, "RAG查询关键词失败")
|
jaeger.RecordError(ctx, err, "RAG查询关键词失败")
|
||||||
|
|||||||
Reference in New Issue
Block a user