优化对话转工单功能,添加重试机制以提高稳定性,限制处理会话数量为前2000个,更新示例查询和文件路径,增强代码可读性和维护性。同时新增数据库客户端功能,支持批量处理会话数据并导出至Excel。

This commit is contained in:
2025-06-17 19:46:04 +08:00
parent a5c1548240
commit 22d48c951f
10 changed files with 718 additions and 96 deletions
+73 -12
View File
@@ -14,6 +14,8 @@ import json
from typing import List, Tuple, Dict, Any, Optional
import re
import jieba
import time
from .PromptTemplates import (classification_prompt, query_rewrite_prompt,
extract_nouns_prompt, classification_info,
slot_filling_prompt)
@@ -95,7 +97,9 @@ class IntentRecognizer:
except Exception as e:
raise RuntimeError(f"加载后缀关键词失败: {e}") from e
def _classify_intent(self, query: str) -> Classification:
def _classify_intent(self, query: str, conversation_context: str = "",
chat_history: List[Dict[str, str]] = None,
previous_slots: Dict[str, Any] = None) -> Classification:
"""
对用户输入进行意图分类
@@ -109,7 +113,9 @@ class IntentRecognizer:
classification_parser = PydanticOutputParser(pydantic_object=Classification)
formatted_prompt = classification_prompt.format(user_input=query,
classification_info=classification_info,
output_format=classification_parser.get_format_instructions())
output_format=classification_parser.get_format_instructions(),
conversation_context=conversation_context,
chat_history=json.dumps(chat_history, ensure_ascii=False))
# 调用LLM
response = self._llm.invoke(formatted_prompt, False)
@@ -208,7 +214,7 @@ class IntentRecognizer:
term_texts = ["名称:" + term.name + "|" + "同义词:" + ";".join(term.synonymous) for term in matched_terms]
# 使用重排序模型
xinference_reranker = SiliconFlowReRankerModel()
xinference_reranker = XinferenceReRankerModel()
rerank_results = xinference_reranker.rerank(query_key, term_texts, top_k=top_k)
# 将matched_terms转换为列表以便按索引访问
@@ -220,7 +226,7 @@ class IntentRecognizer:
return reranked_terms
except Exception as e:
raise RuntimeError(f"SiliconFlowReRankerModel重排失败:{e}") from e
raise RuntimeError(f"_rerank_matched_terms重排失败:{e}") from e
def _match_keywords(self, query: str, use_jieba: bool = False) -> Tuple[TermList, List[str]]:
"""
@@ -233,18 +239,23 @@ class IntentRecognizer:
Returns:
匹配到的关键词列表
"""
start_time = time.time()
query_keys=[]
# 步骤1: 使用LLM提取查询中的关键词
try:
llm_start_time = time.time()
extracted_terms = self._extract_keywords_with_llm(query, use_jieba)
for term in extracted_terms:
query_keys.append(term.name)
llm_end_time = time.time()
llm_time = llm_end_time - llm_start_time
except Exception as e:
raise RuntimeError(f"LLM关键词提取失败: {e}") from e
matched_terms = [] # 存储匹配到的Term对象
# 步骤2: 使用向量检索找到相似的专业名词
try:
vector_start_time = time.time()
# 对matched_terms中的每个关键字进行向量检索
for current_key in query_keys:
vector_results = self._noun_retriever.query(current_key, top_k=5, use_intersection=False)
@@ -262,12 +273,20 @@ class IntentRecognizer:
if len(current_key_terms) > 0:
reranked_terms = self._rerank_matched_terms(current_key, current_key_terms)
matched_terms.extend(reranked_terms)
vector_end_time = time.time()
vector_time = vector_end_time - vector_start_time
except Exception as e:
raise RuntimeError(f"向量检索关键词时出错: {e}") from e
# 提取所有Term对象的名称并排序
# 将set类型的matched_terms转换为TermList类型
term_list = TermList(terms=list(matched_terms))
end_time = time.time()
total_time = end_time - start_time
# 输出整合的时间日志
logging.info(f"关键词匹配耗时统计 - 总耗时: {total_time:.2f}秒, 问题关键词提取: {llm_time:.2f}秒, 向量检索+重排序: {vector_time:.2f}")
return term_list, query_keys
def _rewrite_query(self, query: str, keywords: TermList, query_keys:List[str], chat_history: List[Dict[str, str]] = None, context: str = "") -> QueryRewrite:
@@ -282,6 +301,8 @@ class IntentRecognizer:
Returns:
改写结果
"""
rewrite_start_time = time.time()
# 准备问题改写提示
# terms_dict = [term.model_dump(exclude={"description"}) for term in keywords.terms]
terms_dict = [term.model_dump() for term in keywords.terms]
@@ -295,7 +316,7 @@ class IntentRecognizer:
keywords=keywords_str,
chat_history=chat_history,
context=context)
# 调用LLM
response = self._llm.invoke(formatted_prompt, False)
@@ -303,6 +324,9 @@ class IntentRecognizer:
try:
# 尝试直接解析JSON响应
parsed_output = query_rewrite_parser.parse(response.content)
rewrite_end_time = time.time()
rewrite_time = rewrite_end_time - rewrite_start_time
logging.info(f"问题改写耗时统计 - 总耗时: {rewrite_time:.2f}")
return parsed_output
except Exception as e:
raise RuntimeError(f"解析问题改写结果时出错: {e}") from e
@@ -360,7 +384,10 @@ class IntentRecognizer:
# suffix_terms.append(suffix_term)
# return Classification(vertical_classification="安装下载", sub_classification="查询"), TermList(terms=suffix_terms), QueryRewrite(rewrite=query), matched_suffixes
if chat_history is None:
chat_history = []
if previous_slots is None:
previous_slots = {}
# 步骤1: 匹配关键词
keywords_terms, query_keys = self._match_keywords(query, use_jieba)
@@ -397,7 +424,9 @@ class IntentRecognizer:
# }
def _fill_slots(self, query: str, classification: Classification) -> Dict[str, Any]:
def _fill_slots(self, query: str, classification: Classification, conversation_context: str = "",
chat_history: List[Dict[str, str]] = None,
previous_slots: Dict[str, Any] = None,) -> Dict[str, Any]:
"""
根据分类结果对问题进行槽位填充
@@ -415,7 +444,7 @@ class IntentRecognizer:
raise RuntimeError("未找到匹配的槽位模型")
# 使用LLM进行槽位填充
filled_slots = self._fill_slots_with_llm(query, classification, slot_model)
filled_slots = self._fill_slots_with_llm(query, classification, slot_model, conversation_context, chat_history, previous_slots)
# 检查必填槽位是否都已填充
is_complete, missing_slots = filled_slots.check_required_slots()
@@ -467,7 +496,12 @@ class IntentRecognizer:
return None
def _fill_slots_with_llm(self, query: str, classification: Classification, slot_model_class: type) -> Any:
def _fill_slots_with_llm(self, query: str,
classification: Classification,
slot_model_class: type,
conversation_context: str = "",
chat_history: List[Dict[str, str]] = None,
previous_slots: Dict[str, Any] = None) -> Any:
"""
使用LLM进行槽位填充
@@ -486,7 +520,10 @@ class IntentRecognizer:
query=query,
vertical_classification=classification.vertical_classification,
sub_classification=classification.sub_classification,
output_format=slot_parser.get_format_instructions()
output_format=slot_parser.get_format_instructions(),
conversation_context=conversation_context,
chat_history=json.dumps(chat_history,ensure_ascii=False),
previous_slots=json.dumps(previous_slots,ensure_ascii=False),
)
# 调用LLM
@@ -537,9 +574,14 @@ class IntentRecognizer:
output_format=parser.get_format_instructions(),
classification_info=classification_info
)
# 调用LLM
llm_start_time = time.time()
response = self._llm.invoke(formatted_prompt + output_example, False)
llm_end_time = time.time()
llm_time = llm_end_time - llm_start_time
try:
# 解析LLM响应为JSON
result_json = parser.parse(response.content)
@@ -552,8 +594,19 @@ class IntentRecognizer:
if expected_slot_model is None:
# 添加容错处理,应对LLM返回错误分类信息,一级分类跟二级分类错乱
# 重新分类
classification = self._classify_intent(user_input)
fill_slots = self._fill_slots(user_input, classification)
classify_start_time = time.time()
classification = self._classify_intent(user_input, conversation_context, chat_history, previous_slots)
classify_end_time = time.time()
classify_time = classify_end_time - classify_start_time
# logging.info(f"重新分类耗时: {classify_time:.2f}秒")
fill_start_time = time.time()
fill_slots = self._fill_slots(user_input, classification, conversation_context, chat_history, previous_slots)
fill_end_time = time.time()
fill_time = fill_end_time - fill_start_time
all_time=fill_end_time-llm_start_time
logging.info(f"总耗时:{all_time:.2f}秒,首次槽位+分类:{llm_time:.2f}秒, 重新分类耗时: {classify_time:.2f}秒, 重新槽位填充耗时: {fill_time:.2f}")
result = {
"classification": classification.model_dump(),
"slot_filling": fill_slots
@@ -562,13 +615,21 @@ class IntentRecognizer:
return result
elif expected_slot_model.__name__ != type(slot_filling).__name__:
# 添加容错处理,应对LLM槽位与分类不匹配。重新填充槽位
fill_start_time = time.time()
slot_filling = self._fill_slots(user_input, classification)
fill_end_time = time.time()
fill_time = fill_end_time - fill_start_time
all_time=fill_end_time-llm_start_time
logging.info(f"总耗时:{all_time:.2f}秒,首次槽位+分类:{llm_time:.2f}秒, 重新槽位填充耗时: {fill_time:.2f}")
result = {
"classification": classification.model_dump(),
"slot_filling": slot_filling
}
logging.warning(f"重新填充槽点")
return result
logging.info(f"意图识别+槽位LLM调用耗时: {llm_time:.2f}")
# 构建最终结果
result = {