fix: 1.调整历史记录用法 2.增加相关常量 3.调整改下知识库Query用法 4.增加友好输出 5.调整系统提示词

This commit is contained in:
fuzhongyun 2026-02-04 11:20:11 +08:00
parent c174ab683a
commit 719fd805e6
3 changed files with 84 additions and 52 deletions

View File

@ -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,
}) })

View File

@ -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},
} }

View File

@ -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" // 开发需求
)