feat: 意图识别优化
This commit is contained in:
parent
20c7d1a25f
commit
2a469c6663
@ -45,7 +45,7 @@ if __name__ == '__main__':
|
|||||||
archive_messages_mysql_repository = ArchiveMessagesRepository(db_session)
|
archive_messages_mysql_repository = ArchiveMessagesRepository(db_session)
|
||||||
qa_milvus_repository = QARepository()
|
qa_milvus_repository = QARepository()
|
||||||
|
|
||||||
state: QueryQAState = QueryQAState(original_query="老师你好,我想咨询一下3d建模费用和就业情况", history=['2026-07-10T15:42:13 EXTERNAL 老师这个课程多少钱啊\n', '2026-07-10T15:40:34 EXTERNAL 老师 我想报班\n'])
|
state: QueryQAState = QueryQAState(original_query="老师现在建模课多少钱呢?", history=['2026-07-10T15:42:13 EXTERNAL 老师现在建模课多少钱呢?\n'])
|
||||||
context = QueryQAContext(meta_mysql_repository=archive_messages_mysql_repository,
|
context = QueryQAContext(meta_mysql_repository=archive_messages_mysql_repository,
|
||||||
qa_milvus_repository=qa_milvus_repository
|
qa_milvus_repository=qa_milvus_repository
|
||||||
)
|
)
|
||||||
|
|||||||
@ -15,95 +15,195 @@ problem_rewriting_prompt = """
|
|||||||
|
|
||||||
### 1. 双维度提取原则
|
### 1. 双维度提取原则
|
||||||
每个用户问题通常包含两个维度的意图,必须分别识别:
|
每个用户问题通常包含两个维度的意图,必须分别识别:
|
||||||
- **课程/服务主题维度**:用户想咨询的具体课程类型或业务主题(如3D建模、2D原画、UE引擎开发等)
|
- **课程/服务主题维度**:用户想咨询的具体课程类型或业务主题
|
||||||
- **诉求/动作维度**:用户想了解的方面(如课程咨询、就业咨询、校区分布等)
|
- **诉求/动作维度**:用户想了解的方面(费用、就业、难度等)
|
||||||
|
|
||||||
这两个维度相互独立,必须同时提取,不得遗漏任何一个。
|
这两个维度相互独立,必须同时提取,不得遗漏任何一个。
|
||||||
|
|
||||||
### 2. 主题维度提取规则
|
### 2. 并列结构强制拆分
|
||||||
- 如果用户明确提到了具体课程名称或类型(如"3D建模"、"2D原画"、"UE引擎开发"),必须作为独立意图标签输出
|
识别以下并列模式并强制拆分:
|
||||||
- 如果当前问题使用代词(如"这个"、"它"),结合历史会话进行指代消解,还原具体课程名称
|
- 顿号/逗号分隔:"A、B、C" → [A, B, C]
|
||||||
- 如果历史会话中也没有明确课程类型,则不输出主题维度标签
|
- "和/与/及/还有"连接:"A和B" → [A, B]
|
||||||
|
- 疑问词并列:"多少钱、怎么报名、有就业吗" → [多少钱, 怎么报名, 有就业吗]
|
||||||
|
|
||||||
### 3. 诉求维度提取规则
|
**规则**:拆分后,每个子项必须独立分析,不得合并。
|
||||||
- 从当前问题中提取用户明确表达的动作诉求(费用、就业、退费、住宿等)
|
|
||||||
- 将动作诉求映射到标准诉求类型标签
|
|
||||||
|
|
||||||
### 4. 信息补全与语义保持
|
### 3. 意图识别置信度评分体系
|
||||||
|
每个意图标签在输出前必须经过置信度评分,**只有总分≥60分的标签才会被输出**。
|
||||||
|
|
||||||
|
#### 评分维度:
|
||||||
|
|
||||||
|
| 评分维度 | 分值 | 触发条件 | 说明 |
|
||||||
|
|---------|------|---------|------|
|
||||||
|
| **直接匹配** | **60分** | 用户问题中直接出现标签关键词或同义词扩展词 | 关键词完全匹配或同义词匹配,直接给60分,超过阈值 |
|
||||||
|
| **语义关联** | **40分** | 用户表达与标签语义高度相关,通过语义场推断 | 语义场匹配给40分,需配合上下文支持达到阈值 |
|
||||||
|
| **上下文支持** | **20分** | 历史会话或当前上下文支持该意图推断 | 与语义关联配合:40+20=60分,刚好达到阈值 |
|
||||||
|
| **历史补全** | **10分** | 意图完全依赖历史会话补全 | 仅作为辅助加分,不单独使用 |
|
||||||
|
|
||||||
|
#### 阈值判定规则:
|
||||||
|
- **总分 ≥ 60分** → 进入候选池
|
||||||
|
- **总分 < 60分** → 直接过滤,不进入候选池
|
||||||
|
- 无任何意图≥60分 → 输出 `{{"intents": []}}`
|
||||||
|
|
||||||
|
### 4. 强制截断规则(新增)
|
||||||
|
**无论识别到多少个意图,最终输出最多只返回2个意图标签。**
|
||||||
|
|
||||||
|
当候选池中超过2个意图时,按以下优先级排序,只保留前2个:
|
||||||
|
|
||||||
|
| 优先级 | 排序规则 | 说明 |
|
||||||
|
|-------|---------|------|
|
||||||
|
| **P1(最高)** | 当前问题直接明确表达的 > 历史会话补全的 | 用户当前明确说出的诉求优先 |
|
||||||
|
| **P2** | 总分高的 > 总分低的 | 置信度更高的优先 |
|
||||||
|
| **P3** | 诉求维度 > 主题维度 | 用户更关心"做什么"而非"是什么" |
|
||||||
|
| **P4** | 出现顺序靠前的 > 出现顺序靠后的 | 用户先说的一般更关心 |
|
||||||
|
|
||||||
|
**截断规则**:排序后,只保留前2个,其余全部丢弃。
|
||||||
|
|
||||||
|
### 5. 语义同义词扩展匹配
|
||||||
|
匹配时不仅使用标准词,还必须覆盖扩展词:
|
||||||
|
|
||||||
|
**就业咨询** = 就业、找工作、工作推荐、就业支持、就业服务、就业保障、就业率、包就业、毕业安排、工作分配、推荐工作、就业方向、职业发展、入职、岗位推荐、能就业吗、好找工作吗、就业情况、就业数据、薪资、工资待遇、收入水平
|
||||||
|
|
||||||
|
**学不会怎么办** = 学不会、跟不上、没基础、怕学不会、能不能学、能学会吗、学得会吗、适合我学吗、零基础能学吗、没经验能学吗、纯小白能学吗、好不好学、难不难、难吗、简单吗、容易学吗、好学吗、有难度吗、门槛高吗、有门槛吗、担心自己学不会、不知道能不能学会、怕跟不上、怕太难、上手难、学不进、听不懂、理解不了
|
||||||
|
|
||||||
|
**课程咨询** = 多少钱、费用、价格、学费、学什么、内容、课程安排、怎么收费、贵不贵、收费标准、分期付款、优惠、折扣、教什么、学什么内容、课程体系、教学大纲、课程表、怎么报名、报名流程、入学流程、报名需要什么、报名条件、上课时间、上课周期、学多久、多长时间、几个月、课时、开班时间、什么时候开课、有班级吗、还能报名吗
|
||||||
|
|
||||||
|
**退费咨询** = 退费、退款、退钱、退款政策、不学了能退吗、中途退学、退课、退费规则、退费流程、能退多少、退款比例、退费条件、不想学了怎么办
|
||||||
|
|
||||||
|
**住宿咨询** = 住宿、宿舍、包住、住哪、住宿条件、有宿舍吗、包住宿吗、住宿环境、住宿费、房租、住校、外宿、租房、住宿安排、住宿费用
|
||||||
|
|
||||||
|
**学历咨询** = 学历、文凭、证书、学位、毕业证、大专、本科、学历认证、学历证书、学位认证
|
||||||
|
|
||||||
|
**校区分布** = 校区、地址、在哪、位置、分布、哪个城市、怎么去、交通、附近
|
||||||
|
|
||||||
|
**优势/竞争力** = 优势、竞争力、为什么选你们、和其他机构比、口碑、排名、实力、师资、教学质量
|
||||||
|
|
||||||
|
### 6. 信息补全与语义保持
|
||||||
- 如果当前问题缺少主语,从历史会话补全
|
- 如果当前问题缺少主语,从历史会话补全
|
||||||
- 不得改变用户原始意图,不得引入历史会话中不存在的新信息
|
- 不得改变用户原始意图,不得引入历史会话中不存在的新信息
|
||||||
- 不得遗漏当前问题中的任何关键诉求
|
- 不得遗漏当前问题中的任何关键诉求
|
||||||
|
|
||||||
## 输出格式
|
## 输出格式
|
||||||
- 仅输出JSON,不要任何解释性文字
|
- 仅输出JSON,不要任何解释性文字
|
||||||
- 多诉求时用数组形式输出,每个意图标签独立
|
- **最多只返回2个意图标签**,超出部分强制截断
|
||||||
- 标签从以下列表中选择,不得自创:
|
- 标签从以下列表中选择,不得自创:
|
||||||
|
|
||||||
**课程/服务主题标签**:公司介绍、3D建模、3D场景建模、3D角色建模、2D原画、二次元风格、写实风格、动作/分镜、游戏动作/分镜、开发、UE引擎开发、3D大师课、GGAC绘殓课程、造型力、大学生扶持计划、兴趣/提升班、学习模式、线下面授教学、线上直播教学、寒暑假教学、提供服务、优势/竞争力、校区分布
|
**课程/服务主题标签**:公司介绍、3D建模、3D场景建模、3D角色建模、2D原画、二次元风格、写实风格、动作/分镜、游戏动作/分镜、开发、UE引擎开发、3D大师课、GGAC绘殓课程、造型力、大学生扶持计划、兴趣/提升班、学习模式、线下面授教学、线上直播教学、寒暑假教学、提供服务、优势/竞争力、校区分布
|
||||||
|
|
||||||
**诉求/动作标签**:课程咨询、退费咨询、住宿咨询、学不会怎么办、就业咨询、学历咨询
|
**诉求/动作标签**:课程咨询、退费咨询、住宿咨询、学不会怎么办、就业咨询、学历咨询
|
||||||
|
|
||||||
|
## 分析流程(必须按顺序执行)
|
||||||
|
|
||||||
|
### 步骤1:并列拆分
|
||||||
|
将当前问题拆分为独立的子诉求。
|
||||||
|
|
||||||
|
### 步骤2:双维度扫描 + 置信度评分
|
||||||
|
为每个识别到的意图计算总分。
|
||||||
|
|
||||||
|
### 步骤3:阈值过滤
|
||||||
|
只保留总分≥60分的意图,进入候选池。
|
||||||
|
|
||||||
|
### 步骤4:优先级排序(当候选池超过2个时)
|
||||||
|
按P1→P2→P3→P4规则排序。
|
||||||
|
|
||||||
|
### 步骤5:强制截断
|
||||||
|
只保留排序后的前2个意图标签。
|
||||||
|
|
||||||
|
### 步骤6:完整性自检(输出前必须执行)
|
||||||
|
- [ ] 每个子诉求是否都经过了置信度评分?
|
||||||
|
- [ ] 是否有总分≥60分的意图被遗漏?
|
||||||
|
- [ ] 最终输出是否≤2个标签?
|
||||||
|
- [ ] 截断时是否按优先级规则执行?
|
||||||
|
|
||||||
|
如果任何一项为"否",重新分析。
|
||||||
|
|
||||||
## 分类示例
|
## 分类示例
|
||||||
|
|
||||||
### 示例1(主题+诉求双维度)
|
### 示例1(2个意图,无需截断)
|
||||||
历史会话:(空)
|
历史会话:(空)
|
||||||
当前问题:3D建模培训的课程内容有哪些?
|
当前问题:3D建模培训的课程内容有哪些?
|
||||||
分析:主题="3D建模",诉求="课程内容"→课程咨询
|
分析:
|
||||||
|
- 主题维度:"3D建模" → 直接匹配 = 60分 ≥ 60分 → 候选池
|
||||||
|
- 诉求维度:"课程内容" → 直接匹配"课程咨询" = 60分 ≥ 60分 → 候选池
|
||||||
|
- 候选池共2个,无需截断
|
||||||
分类结果:
|
分类结果:
|
||||||
{{
|
{{
|
||||||
"intents": ["3D建模", "课程咨询"]
|
"intents": ["3D建模", "课程咨询"]
|
||||||
}}
|
}}
|
||||||
|
|
||||||
### 示例2(主题+诉求双维度,信息补全)
|
### 示例2(并列拆分 + 阈值过滤 + 截断)
|
||||||
历史会话:
|
历史会话:(空)
|
||||||
- 用户:我想了解游戏特效培训
|
|
||||||
- 助手:好的,请问您想了解哪方面?
|
|
||||||
当前问题:培训的费用
|
|
||||||
分析:主题从历史会话补全="游戏特效培训",诉求="费用"→课程咨询
|
|
||||||
分类结果:
|
|
||||||
{{
|
|
||||||
"intents": ["课程咨询"]
|
|
||||||
}}
|
|
||||||
|
|
||||||
### 示例3(主题+诉求双维度,指代消解)
|
|
||||||
历史会话:
|
|
||||||
- 用户:你们有Python课程吗?
|
|
||||||
- 助手:有的,我们有Python基础班和进阶班。
|
|
||||||
当前问题:这个课程的时长是多久?
|
|
||||||
分析:主题="Python课程",诉求="课程时长"→课程咨询
|
|
||||||
分类结果:
|
|
||||||
{{
|
|
||||||
"intents": ["课程咨询"]
|
|
||||||
}}
|
|
||||||
|
|
||||||
### 示例4(多诉求提取)
|
|
||||||
历史会话:
|
|
||||||
- 用户:我想报名数据分析培训
|
|
||||||
当前问题:流程、费用和就业支持
|
当前问题:流程、费用和就业支持
|
||||||
分析:主题="数据分析培训",诉求="流程/费用"→课程咨询,"就业支持"→就业咨询
|
分析:
|
||||||
|
- 拆分:[流程, 费用, 就业支持]
|
||||||
|
- "流程" → 直接匹配"课程咨询" = 60分 → 候选池
|
||||||
|
- "费用" → 直接匹配"课程咨询" = 60分 → 候选池(去重)
|
||||||
|
- "就业支持" → 直接匹配"就业咨询" = 60分 → 候选池
|
||||||
|
- 候选池:["课程咨询", "就业咨询"],共2个,无需截断
|
||||||
分类结果:
|
分类结果:
|
||||||
{{
|
{{
|
||||||
"intents": ["课程咨询", "就业咨询"]
|
"intents": ["课程咨询", "就业咨询"]
|
||||||
}}
|
}}
|
||||||
|
|
||||||
### 示例5(主题+诉求双维度)
|
### 示例3(3个意图,强制截断为2个)
|
||||||
历史会话:(空)
|
历史会话:(空)
|
||||||
当前问题:老师3D大师课课程多少钱啊,老师我想报班
|
当前问题:我想学角色,不知道能不能学,好不好学
|
||||||
分析:主题="3D大师课",诉求="多少钱/报班"→课程咨询
|
分析:
|
||||||
|
- 主题维度:"学角色" → 直接匹配"3D角色建模" = 60分 → 候选池
|
||||||
|
- 诉求维度1:"想学" → 直接匹配"课程咨询" = 60分 → 候选池
|
||||||
|
- 诉求维度2:"不知道能不能学" → 语义关联(40) + 上下文支持(20) = 60分 → 候选池
|
||||||
|
- 诉求维度3:"好不好学" → 语义关联(40) + 上下文支持(20) = 60分 → 候选池(与上同标签,去重)
|
||||||
|
- 候选池:["3D角色建模", "课程咨询", "学不会怎么办"],共3个,需截断
|
||||||
|
- 排序:课程咨询(P1当前直接) > 学不会怎么办(P1当前直接) > 3D角色建模(P3主题维度)
|
||||||
|
- 截断后保留前2个
|
||||||
分类结果:
|
分类结果:
|
||||||
{{
|
{{
|
||||||
"intents": ["3D大师课", "课程咨询"]
|
"intents": ["课程咨询", "学不会怎么办"]
|
||||||
}}
|
}}
|
||||||
|
|
||||||
### 示例6(关键示例:主题必须独立提取)
|
### 示例4(5个意图,强制截断为2个)
|
||||||
历史会话:
|
历史会话:(空)
|
||||||
- 用户:老师 我想报班
|
当前问题:3D建模费用、就业、住宿、退费
|
||||||
- 用户:老师这个课程多少钱啊
|
分析:
|
||||||
当前问题:老师我想咨询3D建模费用问题
|
- "3D建模" → 直接匹配 = 60分 → 候选池
|
||||||
分析:主题="3D建模"(当前问题明确提及),诉求="费用问题"→课程咨询
|
- "费用" → 直接匹配"课程咨询" = 60分 → 候选池
|
||||||
|
- "就业" → 直接匹配"就业咨询" = 60分 → 候选池
|
||||||
|
- "住宿" → 直接匹配"住宿咨询" = 60分 → 候选池
|
||||||
|
- "退费" → 直接匹配"退费咨询" = 60分 → 候选池
|
||||||
|
- 候选池共5个,需截断
|
||||||
|
- 排序:课程咨询(P1当前直接,出现第2) > 就业咨询(P1当前直接,出现第3) > 住宿咨询 > 退费咨询 > 3D角色建模(P3主题维度)
|
||||||
|
- 截断后保留前2个
|
||||||
分类结果:
|
分类结果:
|
||||||
{{
|
{{
|
||||||
"intents": ["3D建模", "课程咨询"]
|
"intents": ["课程咨询", "就业咨询"]
|
||||||
|
}}
|
||||||
|
|
||||||
|
### 示例5(历史补全被过滤 + 截断)
|
||||||
|
历史会话:
|
||||||
|
- 用户:我想学3D建模
|
||||||
|
当前问题:多少钱、就业情况、住宿条件
|
||||||
|
分析:
|
||||||
|
- 主题维度:"3D建模"历史补全 = 10分 < 60分 → **不进入候选池**
|
||||||
|
- "多少钱" → 直接匹配"课程咨询" = 60分 → 候选池
|
||||||
|
- "就业情况" → 直接匹配"就业咨询" = 60分 → 候选池
|
||||||
|
- "住宿条件" → 直接匹配"住宿咨询" = 60分 → 候选池
|
||||||
|
- 候选池:["课程咨询", "就业咨询", "住宿咨询"],共3个,需截断
|
||||||
|
- 排序:课程咨询(P1,出现第1) > 就业咨询(P1,出现第2) > 住宿咨询(P1,出现第3)
|
||||||
|
- 截断后保留前2个
|
||||||
|
分类结果:
|
||||||
|
{{
|
||||||
|
"intents": ["课程咨询", "就业咨询"]
|
||||||
|
}}
|
||||||
|
|
||||||
|
### 示例6(语义关联不足60分,全部被过滤)
|
||||||
|
历史会话:(空)
|
||||||
|
当前问题:这个课有意思吗
|
||||||
|
分析:
|
||||||
|
- "有意思" → 语义关联"课程咨询" = 40分,无上下文支持 = 0分,总分40分 < 60分 → **不进入候选池**
|
||||||
|
- 候选池为空
|
||||||
|
分类结果:
|
||||||
|
{{
|
||||||
|
"intents": []
|
||||||
}}
|
}}
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@ -122,4 +122,8 @@ class IntentClassificationRepository:
|
|||||||
limit=1000
|
limit=1000
|
||||||
)
|
)
|
||||||
categories = list(set([item.get("intent_category", "") for item in result]))
|
categories = list(set([item.get("intent_category", "") for item in result]))
|
||||||
return categories
|
return categories
|
||||||
|
|
||||||
|
async def get_style_intent_categories(self) -> List[str]:
|
||||||
|
all_categories = await self.get_all_intent_categories()
|
||||||
|
return [c for c in all_categories if "风格" in c]
|
||||||
Loading…
Reference in New Issue
Block a user