fix: 1.调整历史记录用法 2.增加相关常量 3.调整改下知识库Query用法 4.增加友好输出 5.调整系统提示词
This commit is contained in:
parent
c174ab683a
commit
719fd805e6
|
|
@ -161,11 +161,11 @@ func (d *DingTalkBotBiz) Do(ctx context.Context, requireData *entitys.RequireDat
|
||||||
|
|
||||||
// handleSingleChat 单聊处理
|
// handleSingleChat 单聊处理
|
||||||
// 先不接意图识别-仅提供问题处理
|
// 先不接意图识别-仅提供问题处理
|
||||||
func (d *DingTalkBotBiz) handleSingleChat(ctx context.Context, requireData *entitys.RequireDataDingTalkBot) (err error) {
|
func (d *DingTalkBotBiz) handleSingleChat(ctx context.Context, requireData *entitys.RequireDataDingTalkBot) error {
|
||||||
// 1. 获取用户信息
|
// 1. 获取用户信息
|
||||||
user, err := d.botUserImpl.GetByStaffId(requireData.Req.SenderStaffId)
|
user, err := d.botUserImpl.GetByStaffId(requireData.Req.SenderStaffId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
requireData.ID = int32(user.UserID)
|
requireData.ID = int32(user.UserID)
|
||||||
requireData.UserInfo = &entitys.DingTalkUserInfo{
|
requireData.UserInfo = &entitys.DingTalkUserInfo{
|
||||||
|
|
@ -174,40 +174,24 @@ func (d *DingTalkBotBiz) handleSingleChat(ctx context.Context, requireData *enti
|
||||||
Name: user.Name,
|
Name: user.Name,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 检查会话状态 (Redis)
|
// 2. 获取历史记录 (最近6轮用户输入)
|
||||||
// statusKey := fmt.Sprintf("ai_bot:session:status:%s", requireData.Req.SenderStaffId)
|
userHist, err := d.getRecentUserHistory(ctx, constants.ConversationTypeSingle, requireData.ID, d.conf.Sys.SessionLen)
|
||||||
// status, _ := d.redisCli.Get(ctx, statusKey).Result()
|
|
||||||
|
|
||||||
// if status == "WAITING_FOR_SYS_CONFIRM" {
|
|
||||||
// // 用户回复了系统名称
|
|
||||||
// sysName := requireData.Req.Text.Content
|
|
||||||
// d.redisCli.Del(ctx, statusKey)
|
|
||||||
// return d.handleWithSpecificSys(ctx, requireData, sysName)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 3. 获取历史记录 (最近6轮用户输入)
|
|
||||||
userHist, err := d.getRecentUserHistory(ctx, constants.ConversationTypeSingle, requireData.ID, 6)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 改写 Query (Query Rewriting)
|
// 3. 系统&问题分类(意图识别阶段)
|
||||||
rewrittenQuery, err := d.handle.RewriteQuery(ctx, userHist, requireData.Req.Text.Content)
|
resolveResult, err := d.resolveSystemAndIssueType(ctx, requireData, userHist)
|
||||||
var queryText = requireData.Req.Text.Content
|
|
||||||
if err == nil && rewrittenQuery != "" {
|
|
||||||
queryText = rewrittenQuery
|
|
||||||
}
|
|
||||||
|
|
||||||
// 分类
|
|
||||||
resolveResult, err := d.resolveSystemAndIssueType(ctx, requireData, queryText)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
log.Debugf("系统&分类结果: %s - %s", resolveResult.Sys.SysName, resolveResult.IssueType.Name)
|
||||||
|
|
||||||
|
// 4. 分类处理(后续考虑接各自的工作流/agent)
|
||||||
switch resolveResult.IssueType.Code {
|
switch resolveResult.IssueType.Code {
|
||||||
case "knowledge_qa":
|
case constants.IssueTypeKnowledgeQA:
|
||||||
// 知识库问答
|
// 知识库问答
|
||||||
return d.handleKnowledgeQA(ctx, requireData, queryText, resolveResult)
|
return d.handleKnowledgeQA(ctx, requireData, userHist, resolveResult)
|
||||||
default: // 其他问题类型
|
default: // 其他问题类型
|
||||||
// 系统为空,再次询问
|
// 系统为空,再次询问
|
||||||
if resolveResult.Sys.SysID == 0 {
|
if resolveResult.Sys.SysID == 0 {
|
||||||
|
|
@ -219,13 +203,21 @@ func (d *DingTalkBotBiz) handleSingleChat(ctx context.Context, requireData *enti
|
||||||
}
|
}
|
||||||
|
|
||||||
// 知识库问答
|
// 知识库问答
|
||||||
func (d *DingTalkBotBiz) handleKnowledgeQA(ctx context.Context, requireData *entitys.RequireDataDingTalkBot, queryText string, resolveResult *resolveSystemAndIssueTypeResult) error {
|
func (d *DingTalkBotBiz) handleKnowledgeQA(ctx context.Context, requireData *entitys.RequireDataDingTalkBot, userHist []model.AiBotChatHi, resolveResult *resolveSystemAndIssueTypeResult) error {
|
||||||
// 获取租户ID
|
// 获取租户ID
|
||||||
tenantId := constants.KnowledgeTenantIdDefault
|
tenantId := constants.KnowledgeTenantIdDefault
|
||||||
if resolveResult.Sys.KnowlegeTenantKey != "" {
|
if resolveResult.Sys.KnowlegeTenantKey != "" {
|
||||||
tenantId = resolveResult.Sys.KnowlegeTenantKey
|
tenantId = resolveResult.Sys.KnowlegeTenantKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 改写 Query (Query Rewriting)
|
||||||
|
rewrittenQuery, err := d.handle.RewriteQuery(ctx, userHist, requireData.Req.Text.Content)
|
||||||
|
var queryText = requireData.Req.Text.Content
|
||||||
|
if err == nil && rewrittenQuery != "" {
|
||||||
|
queryText = rewrittenQuery
|
||||||
|
}
|
||||||
|
log.Debugf("改写前后的Query: %s -> %s", requireData.Req.Text.Content, queryText)
|
||||||
|
|
||||||
// 获取知识库结果
|
// 获取知识库结果
|
||||||
isRetrieved, err := d.getKnowledgeAnswer(ctx, requireData, tenantId, queryText)
|
isRetrieved, err := d.getKnowledgeAnswer(ctx, requireData, tenantId, queryText)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -276,7 +268,7 @@ type resolveSystemAndIssueTypeResult struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析系统和问题类型
|
// 解析系统和问题类型
|
||||||
func (d *DingTalkBotBiz) resolveSystemAndIssueType(ctx context.Context, requireData *entitys.RequireDataDingTalkBot, queryText string) (*resolveSystemAndIssueTypeResult, error) {
|
func (d *DingTalkBotBiz) resolveSystemAndIssueType(ctx context.Context, requireData *entitys.RequireDataDingTalkBot, userHist []model.AiBotChatHi) (*resolveSystemAndIssueTypeResult, error) {
|
||||||
// 1. 获取所有系统和问题类型用于分类
|
// 1. 获取所有系统和问题类型用于分类
|
||||||
allSys, err := d.sysImpl.FindAll()
|
allSys, err := d.sysImpl.FindAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -294,7 +286,7 @@ func (d *DingTalkBotBiz) resolveSystemAndIssueType(ctx context.Context, requireD
|
||||||
})
|
})
|
||||||
|
|
||||||
// 2. LLM 分类
|
// 2. LLM 分类
|
||||||
classification, err := d.handle.ClassifyIssue(ctx, sysNames, issueTypeNames, requireData.Req.Text.Content)
|
classification, err := d.handle.ClassifyIssue(ctx, sysNames, issueTypeNames, requireData.Req.Text.Content, userHist)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -360,6 +352,8 @@ func (d *DingTalkBotBiz) getRecentUserHistory(ctx context.Context, conversationT
|
||||||
|
|
||||||
// 在已知系统&问题类型的情况下进行分类并拉群
|
// 在已知系统&问题类型的情况下进行分类并拉群
|
||||||
func (d *DingTalkBotBiz) fallbackToGroupCreation(ctx context.Context, requireData *entitys.RequireDataDingTalkBot, resolveResult *resolveSystemAndIssueTypeResult) error {
|
func (d *DingTalkBotBiz) fallbackToGroupCreation(ctx context.Context, requireData *entitys.RequireDataDingTalkBot, resolveResult *resolveSystemAndIssueTypeResult) error {
|
||||||
|
entitys.ResText(requireData.Ch, "", fmt.Sprintf("\n检测到您想咨询 %s-%s 问题。", resolveResult.Sys.SysName, resolveResult.IssueType.Name))
|
||||||
|
|
||||||
// 查找分配规则
|
// 查找分配规则
|
||||||
rule, found, _ := d.issueImpl.IssueAssignRule.FindOne(
|
rule, found, _ := d.issueImpl.IssueAssignRule.FindOne(
|
||||||
d.issueImpl.WithSysID(resolveResult.Sys.SysID),
|
d.issueImpl.WithSysID(resolveResult.Sys.SysID),
|
||||||
|
|
@ -371,7 +365,7 @@ func (d *DingTalkBotBiz) fallbackToGroupCreation(ctx context.Context, requireDat
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var staffIds []string
|
var groupMember, groupMemberName []string
|
||||||
if rule.ID != 0 {
|
if rule.ID != 0 {
|
||||||
// 获取配置的用户
|
// 获取配置的用户
|
||||||
assignUsers, err := d.issueImpl.IssueAssignUser.FindAll(d.issueImpl.WithRuleID(rule.ID))
|
assignUsers, err := d.issueImpl.IssueAssignUser.FindAll(d.issueImpl.WithRuleID(rule.ID))
|
||||||
|
|
@ -394,18 +388,23 @@ func (d *DingTalkBotBiz) fallbackToGroupCreation(ctx context.Context, requireDat
|
||||||
return bu.UserID == au.UserID
|
return bu.UserID == au.UserID
|
||||||
})
|
})
|
||||||
if found && botUser.StaffID != "" {
|
if found && botUser.StaffID != "" {
|
||||||
staffIds = append(staffIds, botUser.StaffID)
|
groupMember = append(groupMember, botUser.StaffID)
|
||||||
|
groupMemberName = append(groupMemberName, "@"+botUser.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 兜底处理人
|
// 兜底处理人
|
||||||
if len(staffIds) == 0 {
|
if len(groupMember) == 0 {
|
||||||
staffIds = []string{"17415698414368678"}
|
groupMember = []string{"17415698414368678"}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 合并提问者
|
// 合并提问者
|
||||||
staffIds = append([]string{requireData.Req.SenderStaffId}, staffIds...)
|
groupMember = append([]string{requireData.Req.SenderStaffId}, groupMember...)
|
||||||
|
groupMember = slice.Unique(groupMember)
|
||||||
|
|
||||||
|
// 先回复用户
|
||||||
|
entitys.ResText(requireData.Ch, "", fmt.Sprintf("\n已检索到处理人\n%s\n是否创建群聊?", strings.Join(groupMemberName, "、")))
|
||||||
|
|
||||||
// 发送确认卡片
|
// 发送确认卡片
|
||||||
groupName := fmt.Sprintf("[%s]-%s", resolveResult.IssueType.Name, resolveResult.Classification.Summary)
|
groupName := fmt.Sprintf("[%s]-%s", resolveResult.IssueType.Name, resolveResult.Classification.Summary)
|
||||||
|
|
@ -413,7 +412,7 @@ func (d *DingTalkBotBiz) fallbackToGroupCreation(ctx context.Context, requireDat
|
||||||
RobotCode: requireData.Req.RobotCode,
|
RobotCode: requireData.Req.RobotCode,
|
||||||
ConversationId: requireData.Req.ConversationId,
|
ConversationId: requireData.Req.ConversationId,
|
||||||
SenderStaffId: requireData.Req.SenderStaffId,
|
SenderStaffId: requireData.Req.SenderStaffId,
|
||||||
UserIds: staffIds,
|
UserIds: groupMember,
|
||||||
GroupName: groupName,
|
GroupName: groupName,
|
||||||
Summary: resolveResult.Classification.Summary,
|
Summary: resolveResult.Classification.Summary,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -131,27 +131,52 @@ type IssueClassification struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClassifyIssue 问题分类分析
|
// ClassifyIssue 问题分类分析
|
||||||
func (r *Handle) ClassifyIssue(ctx context.Context, systems []string, issueTypes []string, userInput string) (*IssueClassification, error) {
|
func (r *Handle) ClassifyIssue(ctx context.Context, systems []string, issueTypes []string, userInput string, userHist []model.AiBotChatHi) (*IssueClassification, error) {
|
||||||
systemPrompt := fmt.Sprintf(`## 角色
|
systemPrompt := fmt.Sprintf(`## 角色
|
||||||
你是一个技术支持路由专家。输出必须是 JSON 格式。
|
你是一个技术支持路由专家。你的核心能力是通过深度语义分析和上下文回溯,将碎片化的用户输入通过时间先后拼接成完整的意图。输出必须是严格的 JSON 格式。
|
||||||
## 任务
|
|
||||||
请分析用户的输入,并将其归类到最合适的系统和问题类型中。
|
## 核心推理引擎(关键逻辑)
|
||||||
- 系统名称:必须是可用系统列表中的一个,若未提及可用系统关键词,则为"全局",不要臆想!
|
|
||||||
- 问题类型名称:必须是可用问题类型列表中的一个,若未提及可用问题类型关键词,则为空字符串
|
请执行以下三步推理,不要只看当前这一句话:
|
||||||
- 问题简述:15字以内的问题简述(用于群聊命名)
|
|
||||||
- 分类判断理由:对系统名称和问题类型名称的判断理由
|
1. **构建完整意图**:
|
||||||
## 背景与材料:
|
* 将“当前输入”与“历史对话”合并视为用户的完整诉求。
|
||||||
可用系统列表: [%s]
|
* **特殊规则**:如果“当前输入”仅包含一个系统名称(例如用户只输入了“CRM”),这被视为**“上下文补充”**,而非新问题。此时,**必须保留最近历史对话中已识别的问题类型**。
|
||||||
可用问题类型: [%s]
|
|
||||||
## 输出
|
2. **系统判定 (sys_name)**
|
||||||
请仅以 JSON 格式回复,包含以下字段:
|
* **策略**:覆盖式更新。
|
||||||
- sys_name: 系统名称
|
* 如果当前输入提到了系统A,则 sys_name = 系统A(不管历史是什么)。
|
||||||
- issue_type_name: 问题类型名称
|
* 如果当前输入未提系统,但历史有,则继承最近历史。
|
||||||
- summary: 问题简述
|
* 如果都无,设为 "全局"。
|
||||||
- reason: 分类判断理由`, strings.Join(systems, ", "), strings.Join(issueTypes, ", "))
|
|
||||||
|
3. **问题类型判定 (issue_type_name)**
|
||||||
|
* **策略**:回溯与推断。
|
||||||
|
* **核心原则**:基于合并后的完整意图进行分析。推断出最近历史对话中的问题类型。
|
||||||
|
* **严禁清空**:除非用户是在闲聊(如“你好”),否则绝不允许为空。如果当前句没提问题,但历史有,必须继承历史的 issue_type_name。
|
||||||
|
|
||||||
|
## 背景数据
|
||||||
|
- 可用系统列表: [%s]
|
||||||
|
- 可用问题类型: [%s]
|
||||||
|
|
||||||
|
## 输出格式
|
||||||
|
{
|
||||||
|
"sys_name": "系统名称",
|
||||||
|
"issue_type_name": "问题类型名称",
|
||||||
|
"summary": "15字内标题",
|
||||||
|
"reason": "1. 系统:说明来源(当前/历史/默认)。2. 问题类型:说明是基于哪句话推断的,或说明是继承了历史意图。"
|
||||||
|
}`, strings.Join(systems, ", "), strings.Join(issueTypes, ", "))
|
||||||
|
|
||||||
|
historyStr := strings.Builder{}
|
||||||
|
historyStr.WriteString("### 历史对话:\n")
|
||||||
|
for _, h := range userHist {
|
||||||
|
if h.Role == "user" {
|
||||||
|
historyStr.WriteString(fmt.Sprintf("%s:%s\n", h.CreateAt, h.Content))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
messages := []api.Message{
|
messages := []api.Message{
|
||||||
{Role: "system", Content: systemPrompt},
|
{Role: "system", Content: systemPrompt},
|
||||||
|
{Role: "assistant", Content: historyStr.String()},
|
||||||
{Role: "user", Content: userInput},
|
{Role: "user", Content: userInput},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,3 +45,11 @@ const (
|
||||||
PermissionTypeNone = 1
|
PermissionTypeNone = 1
|
||||||
PermissionTypeDept = 2
|
PermissionTypeDept = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// IssueType 问题类型
|
||||||
|
const (
|
||||||
|
IssueTypeKnowledgeQA = "knowledge_qa" // 知识问答
|
||||||
|
IssueTypeUI = "ui" // UI需求
|
||||||
|
IssueTypeBug = "bug" // Bug
|
||||||
|
IssueTypeDemand = "demand" // 开发需求
|
||||||
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue