fix: 拆分系统判断与问题分类判断
This commit is contained in:
parent
ec41a3d787
commit
c9c9bca9ce
|
|
@ -286,10 +286,23 @@ func (d *DingTalkBotBiz) resolveSystemAndIssueType(ctx context.Context, requireD
|
||||||
})
|
})
|
||||||
|
|
||||||
// 2. LLM 分类
|
// 2. LLM 分类
|
||||||
classification, err := d.handle.ClassifyIssue(ctx, sysNames, issueTypeNames, requireData.Req.Text.Content, userHist)
|
// 系统名称
|
||||||
|
classificationSys, err := d.handle.ClassifyIssueSystem(ctx, sysNames, requireData.Req.Text.Content, userHist)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// 问题类型
|
||||||
|
classificationIssueType, err := d.handle.ClassifyIssueType(ctx, issueTypeNames, requireData.Req.Text.Content, userHist)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// 合并
|
||||||
|
classification := &do.IssueClassification{
|
||||||
|
SysName: classificationSys.SysName,
|
||||||
|
IssueTypeName: classificationIssueType.IssueTypeName,
|
||||||
|
Summary: classificationIssueType.Summary,
|
||||||
|
Reason: fmt.Sprintf("系统名称推断理由:%s\n问题类型推断理由:%s", classificationSys.Reason, classificationIssueType.Reason),
|
||||||
|
}
|
||||||
|
|
||||||
// 3. 匹配系统
|
// 3. 匹配系统
|
||||||
var sys model.AiSy
|
var sys model.AiSy
|
||||||
|
|
|
||||||
|
|
@ -130,41 +130,96 @@ type IssueClassification struct {
|
||||||
Reason string `json:"reason"`
|
Reason string `json:"reason"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClassifyIssue 问题分类分析
|
// ClassifyIssueSys 问题系统分析
|
||||||
func (r *Handle) ClassifyIssue(ctx context.Context, systems []string, issueTypes []string, userInput string, userHist []model.AiBotChatHi) (*IssueClassification, error) {
|
func (r *Handle) ClassifyIssueSystem(ctx context.Context, systems []string, userInput string, userHist []model.AiBotChatHi) (*IssueClassification, error) {
|
||||||
systemPrompt := fmt.Sprintf(`## 角色
|
systemPrompt := fmt.Sprintf(`## 角色
|
||||||
你是一个技术支持路由专家。你的核心能力是通过深度语义分析和上下文回溯,将碎片化的用户输入通过时间先后拼接成完整的意图。输出必须是严格的 JSON 格式。
|
你是一个系统类型判定专家。你的唯一任务是基于多轮对话识别用户当前讨论的系统(sys_name)。不需要输出问题类型。输出必须严格遵守 JSON 格式。
|
||||||
|
|
||||||
## 核心推理引擎(关键逻辑)
|
## 推理规则
|
||||||
|
|
||||||
请执行以下三步推理,不要只看当前这一句话:
|
1. 系统判定逻辑:
|
||||||
|
- 当前输入明确提到系统 → 直接覆盖历史系统
|
||||||
|
- 当前输入未提系统,但历史对话有 → 继承最近历史系统
|
||||||
|
- 当前输入和历史均未出现 → "全局"
|
||||||
|
|
||||||
1. **构建完整意图**:
|
2. 特殊规则:
|
||||||
* 将“当前输入”与“历史对话”合并视为用户的完整诉求。
|
- 如果当前输入仅包含系统名称(如“CRM”),视为系统上下文补充,仅更新 sys_name,不做其他推断
|
||||||
* **特殊规则**:如果“当前输入”仅包含一个系统名称(例如用户只输入了“CRM”),这被视为**“上下文补充”**,而非新问题。此时,**必须保留最近历史对话中已识别的问题类型**。
|
|
||||||
|
|
||||||
2. **系统判定 (sys_name)**
|
|
||||||
* **策略**:覆盖式更新。
|
|
||||||
* 如果当前输入提到了系统A,则 sys_name = 系统A(不管历史是什么)。
|
|
||||||
* 如果当前输入未提系统,但历史有,则继承最近历史。
|
|
||||||
* 如果都无,设为 "全局"。
|
|
||||||
|
|
||||||
3. **问题类型判定 (issue_type_name)**
|
|
||||||
* **策略**:回溯与推断。
|
|
||||||
* **核心原则**:基于合并后的完整意图进行分析。推断出最近历史对话中的问题类型。
|
|
||||||
* **严禁清空**:除非用户是在闲聊(如“你好”),否则绝不允许为空。如果当前句没提问题,但历史有,必须继承历史的 issue_type_name。
|
|
||||||
|
|
||||||
## 背景数据
|
## 背景数据
|
||||||
- 可用系统列表: [%s]
|
可用系统列表:[%s]
|
||||||
- 可用问题类型: [%s]
|
|
||||||
|
|
||||||
## 输出格式
|
## 输出格式
|
||||||
{
|
{
|
||||||
"sys_name": "系统名称",
|
"sys_name": "系统名称",
|
||||||
|
"reason": "说明系统来源:当前输入 / 历史继承 / 默认"
|
||||||
|
}
|
||||||
|
`, strings.Join(systems, ", "))
|
||||||
|
|
||||||
|
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{
|
||||||
|
{Role: "system", Content: systemPrompt},
|
||||||
|
{Role: "assistant", Content: historyStr.String()},
|
||||||
|
{Role: "user", Content: userInput},
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := r.Ollama.Chat(ctx, messages)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试清理 JSON 内容(有时模型会返回 markdown 块)
|
||||||
|
resp = strings.TrimPrefix(resp, "```json")
|
||||||
|
resp = strings.TrimSuffix(resp, "```")
|
||||||
|
resp = strings.TrimSpace(resp)
|
||||||
|
|
||||||
|
var result IssueClassification
|
||||||
|
if err := json.Unmarshal([]byte(resp), &result); err != nil {
|
||||||
|
return nil, fmt.Errorf("解析分类结果失败: %w, 原文: %s", err, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClassifyIssueType 问题分类分析
|
||||||
|
func (r *Handle) ClassifyIssueType(ctx context.Context, issueTypes []string, userInput string, userHist []model.AiBotChatHi) (*IssueClassification, error) {
|
||||||
|
systemPrompt := fmt.Sprintf(`## 角色
|
||||||
|
你是一个业务问题类型分析专家。你的任务是基于多轮对话识别用户讨论的**问题类型(issue_type_name)**,问题类型必须严格来自可用问题类型列表 [%s]。
|
||||||
|
|
||||||
|
你不负责系统名称判断。输出必须严格遵守 JSON 格式。
|
||||||
|
|
||||||
|
## 推理规则
|
||||||
|
|
||||||
|
1. 构建完整问题意图
|
||||||
|
- 将当前输入与历史对话合并理解为完整问题演进
|
||||||
|
- 当前输入可能是补充条件、追问、修正或只给模块名/报错片段
|
||||||
|
- 不要只看当前一句
|
||||||
|
|
||||||
|
2. 问题类型判定逻辑
|
||||||
|
- 当前输入明确匹配列表中某个类型 → 使用该类型
|
||||||
|
- 当前输入未明确,但历史已有 → 继承历史类型
|
||||||
|
- 当前输入未匹配,历史也没有 → 选择最接近的列表类型(尽量匹配意图)
|
||||||
|
- 除非是闲聊(如“你好”“在吗”),禁止返回空值
|
||||||
|
|
||||||
|
3. 特殊规则
|
||||||
|
- 当前输入只包含系统名/模块名/参数名 → 视为问题补充,继承历史 issue_type_name
|
||||||
|
- 输出必须严格匹配列表中的类型,不允许生成列表外的自造类型
|
||||||
|
|
||||||
|
## 背景数据
|
||||||
|
可用问题类型列表:[%s]
|
||||||
|
|
||||||
|
## 输出格式
|
||||||
|
{
|
||||||
"issue_type_name": "问题类型名称",
|
"issue_type_name": "问题类型名称",
|
||||||
"summary": "15字内标题",
|
"summary": "15字内问题标题",
|
||||||
"reason": "1. 系统:说明来源(当前/历史/默认)。2. 问题类型:说明是基于哪句话推断的,或说明是继承了历史意图。"
|
"reason": "说明问题类型是基于哪句话判断,或说明继承自历史,继承自哪条历史"
|
||||||
}`, strings.Join(systems, ", "), strings.Join(issueTypes, ", "))
|
}`, strings.Join(issueTypes, ", "), strings.Join(issueTypes, ", "))
|
||||||
|
|
||||||
historyStr := strings.Builder{}
|
historyStr := strings.Builder{}
|
||||||
historyStr.WriteString("### 历史对话:\n")
|
historyStr.WriteString("### 历史对话:\n")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue