86 lines
4.8 KiB
Python
86 lines
4.8 KiB
Python
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 |