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