sales-assistant-py-new/app/chat_qa/classification/text_intent.py

86 lines
4.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import json
from typing import List
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from app.llm import llm
intent_prompt = """
你是一个意图分类专家。你的任务是根据给定的文本内容,判断它是否匹配当前意图。如果不匹配,则从意图列表中选择最合适的意图。
## 输入信息
- **文本内容**{text}(可能为多段文本,需综合判断整体核心意图,而非逐段独立判断)
- **当前意图**{intent}(可能为空)
- **意图列表**{intent_list}
## 意图层级说明
意图列表中存在层级关系,分类时须优先识别:
- **概括性意图**:能够涵盖多个独立的具体子意图,例如 `学习模式`(涵盖线上直播教学、线下面授教学)、`课程咨询`(涵盖费用、周期、内容等综合问题)、`提供服务` 等。
- **具体意图**:描述单一、明确的独立事项,例如 `线上直播教学`、`线下面授教学`、`UE引擎开发`、`住宿咨询`、`上课时间` 等。
## 判断规则
1. 如果当前意图为空,直接从意图列表中选择最匹配的意图。
2. **多意图混合优先原则(最重要)**:仅当文本**同时涉及意图列表中两个或以上独立意图的实质性并列、对比或综合介绍**时,才优先选择能够概括这些内容的上层意图(如 `学习模式`、`课程咨询`)。**严禁**把单一意图内部的多维度展开(如某课程包含多个技术点、案例类型、学习阶段)误判为多意图混合。
3. **当前意图保持的严格条件**:只有当文本内容**主要且专门**围绕当前意图展开,且**不涉及其他独立意图的实质性并列或对比内容**时,才保持当前意图不变。如果文本在匹配当前意图的同时,还明显涉及其他独立意图的对比、补充或并列说明,则必须重新从意图列表中选择最合适的概括性意图,**不得因当前意图非空而强行保持**。
4. 如果文本内容与当前意图完全不匹配(包括语义偏差、主题不符、情感不一致等),从意图列表中选择最匹配的意图进行替换。
5. 如果意图列表中没有完全匹配的意图,选择最接近的一个,**不要自行创造新意图**。
6. 如果文本内容模糊、无法判断,选择当前意图(若为空则选择意图列表中的"其他"或第一个兜底意图)。
## 分析步骤(必须执行)
1. **提取核心主题**:综合分析文本列表的整体核心主题,忽略边缘细节。
2. **判定是否多意图混合**:检查文本是否涉及**意图列表中两个或以上独立意图**的并列、对比或综合。注意:单一意图内部的技术点、案例、学习阶段等展开**不构成**多意图混合。
3. **层级匹配**:若确认为多意图混合,匹配概括性意图;若为核心单一意图,匹配最具体的意图。
4. **对比当前意图**:如果当前意图不为空,检查当前意图是否能准确概括文本**整体**内容。若不能,则放弃当前意图,重新选择。
## 输出要求
- 输出纯JSON不要任何解释、不要markdown标记、不要注释
- 确保所有字符串用双引号包裹
- 输出格式固定为:
{{
"intent": "最终的意图标签(原意图或新意图,只能有一个)"
}}
"""
from app.core.log import logger
class TextIntent:
async def intent_message(self, text:str, intent:str, intent_list:list[str]) -> str:
prompt_template = PromptTemplate(template=intent_prompt, input_variables=["text","intent","intent_list"])
chain = prompt_template | llm
result = await chain.ainvoke({"text": text,"intent":intent,"intent_list": str(intent_list)})
res_new = str(result.content) if hasattr(
result, 'content') else str(result)
intent:str=""
try:
data = json.loads(res_new)
intent = data.get("intent", "")
except (json.JSONDecodeError, AttributeError):
logger.info(f"无法解析JSON{res_new}")
return intent
def _extract_jsons(self, content: str) -> List[str]:
results = []
start = 0
while start < len(content):
brace_start = content.find("{", start)
if brace_start == -1:
break
depth = 0
end = brace_start
for i in range(brace_start, len(content)):
if content[i] == "{":
depth += 1
elif content[i] == "}":
depth -= 1
if depth == 0:
end = i + 1
break
if end > brace_start:
results.append(content[brace_start:end])
start = end
else:
start = brace_start + 1
return results