import json from typing import List from langchain_core.output_parsers import StrOutputParser from langchain_core.prompts import PromptTemplate from app.llm import llm from app.repository.milvus.intent_classification_repository import IntentClassificationRepository intent_prompt = """ 你是一个专业的意图提取专家。请分析以下文本内容,并结合当前已有的意图标签,判断是否符合;若不符合则生成一个新意图,符合则直接返回原意图。 ## 输入说明 - **文本内容**:`{text}` - **目前的意图**:`{intent}`(可能为空) ## 核心规则 ### 1. 意图提取与对比规则 - 首先从文本中提取1个核心意图(提取方法见下方“意图提取规则”)。 - 将提取出的意图与给定的`{{intent}}`进行对比: - 若两者**完全相同**(字符串完全一致),则最终输出意图为原意图`{{intent}}`。 - 若两者不同,或`{{intent}}`为空,则最终输出意图为新提取的意图。 - 无论采用原意图还是新意图,都必须来自或可扩充自“意图分类体系”。 ### 2. 意图提取规则(用于从文本中提取核心意图) - 若文本包含用户明确的问题,提取其核心诉求对应的意图。 - 若文本为销售介绍/陈述内容,**聚焦于其最想传达的核心功能或核心信息**,提取总结性意图。**不得将补充说明信息(如附带提到的地点、时间等)误判为核心意图。** - 例如:文本为“线上课程后期可以转到线下,我们线下校区在上海”,核心是“线上转线下”这一功能,而非“校区分布”,因此应扩充为“线上转线下”,而不是使用“校区分布”。 - 意图必须基于实际内容,不编造。 - 仅提取1个意图标签。 ### 3. 单意图选择规则(关键) **只能输出一个意图标签,不能以顿号、逗号或“和”分隔多个意图。** 当内容可能对应多个意图时,按以下优先级选择唯一意图: | 优先级 | 选择规则 | 说明 | |-------|---------|------| | **P0** | **优先匹配已有意图标签** | 必须优先从已有意图体系(含扩展示例)中选择最匹配的标签。**若已有标签中无准确对应,则按扩充规则创建新标签,严禁使用仅部分相关或语义模糊的已有标签。** 当销售单方面介绍课程内容(无用户明确提问)时,直接使用对应的主题标签(如“3D场景建模”),若主题为已有标签未覆盖的功能/服务(如“线上转线下”),则应扩充新标签,不得强行归类为“校区分布”等。 | | **P1** | **核心诉求优先** | 选择最能代表文本核心诉求的意图,**忽略次要的补充信息**。例如:文本“线上课程可转线下,校区在上海” → 核心是“线上转线下”,而非“校区分布”;报名流程说明 → “报名咨询”,而非“线上直播教学”。 | | **P2** | **主题维度优先于动作维度** | 当主题和动作诉求冲突时,优先选择主题维度。例如:3D建模费用 → “3D建模”(主题) | | **P3** | **直接提及优先于间接关联** | 文本明确说出的核心意图优先于推断出的意图 | | **P4** | **去重合并** | 多个子诉求属于同一类别时,只输出一个意图标签 | ### 4. 意图分类体系(必须从中选择或基于其扩充) {intent_list} **扩充规则:** 当文本核心意图无法被已有标签准确覆盖时,必须创建更精准的新标签。创建时: - 新标签应简洁体现核心语义(如“线上转线下”)。 - 严格对比已有标签,确保无重复语义。 - **示例**:文本“线上课程后期可以转到线下” → 已有标签无对应,扩充为“线上转线下”;文本“原画好就业吗” → 扩充为“原画就业”。 ### 5. 多意图处理 若同一段文本明确涉及多个不重叠的核心意图,仅选择最能代表核心内容的1个意图。 ## 输出格式要求 - 输出纯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