新增同义词词典支持,优化IntentRecognition类以加载jieba自定义词典和同义词字典,调整关键词提取流程,简化日志记录,更新PromptTemplates以规范同义词处理规则。

This commit is contained in:
2025-07-31 09:23:27 +08:00
parent 728262cc65
commit 6a72233a97
3 changed files with 297 additions and 63 deletions
+53 -42
View File
@@ -80,8 +80,49 @@ class AsyncIntentRecognizer:
# 加载软件词条名称库
self._soft_wiki_library = self._load_soft_wiki_library()
# 异步检索器将在create方法中初始化
self._noun_retriever = None
# self._noun_retriever = None
# 初始化jieba自定义词典
self._init_jieba_dict()
self._synonymous_dict=self._init_synonymous_dict()
def _init_jieba_dict(self):
"""初始化jieba自定义词典"""
try:
current_dir = os.path.dirname(os.path.abspath(__file__))
dict_path = os.path.join(current_dir, "..", "..", "data", "nouns", "all_synonymous_jieba.txt")
# 检查字典文件是否存在
if os.path.exists(dict_path):
jieba.load_userdict(dict_path)
logging.info("成功加载jieba自定义词典")
else:
logging.warning(f"自定义词典文件不存在: {dict_path}")
except Exception as e:
logging.error(f"加载jieba自定义词典失败: {e}")
def _init_synonymous_dict(self):
"""加载同义词,key是同义词 val:是对应名词"""
try:
current_dir = os.path.dirname(os.path.abspath(__file__))
dict_path = os.path.join(current_dir, "..", "..", "data", "nouns", "merged_nouns.json")
# 检查字典文件是否存在
synonymous_dict={}
if os.path.exists(dict_path):
with open(dict_path, "r", encoding="utf-8") as f:
data = json.load(f)
for cur_data in data:
synonymous=cur_data["synonymous"]
name=cur_data["name"]
for cur_synonymous in synonymous:
synonymous_dict[cur_synonymous]=name
else:
logging.warning(f"名词库文件不存在: {dict_path}")
return synonymous_dict
except Exception as e:
logging.error(f"加载名词库文件失败: {e}")
return {}
def _load_soft_wiki_library(self):
"""
加载软件wiki库
@@ -105,7 +146,7 @@ class AsyncIntentRecognizer:
"""
instance = cls()
# 异步初始化名词检索器
instance._noun_retriever = await AsyncProfessionalNounRetriever.create()
# instance._noun_retriever = await AsyncProfessionalNounRetriever.create()
return instance
def _load_suffix_keywords(self, filepath: str = None) -> List[str]:
@@ -277,7 +318,7 @@ class AsyncIntentRecognizer:
"""
start_time = time.time()
query_keys=[]
# 步骤1: 使用LLM提取查询中的关键词
# 步骤1: 提取查询中的关键词
try:
llm_start_time = time.time()
extracted_terms = await self._extract_keywords_async(query, use_jieba)
@@ -289,44 +330,14 @@ class AsyncIntentRecognizer:
raise RuntimeError(f"异步LLM关键词提取失败: {e}") from e
matched_terms = [] # 存储匹配到的Term对象
# 步骤2: 使用向量检索找到相似的专业名词
try:
vector_start_time = time.time()
# 创建并行任务列表
async def process_single_keyword(current_key: str) -> List[Term]:
"""处理单个关键词的向量检索和重排序"""
vector_results = await self._noun_retriever.query_async(current_key, top_k=5, use_intersection=False)
current_key_terms = set()
# 添加向量检索结果
for result in vector_results:
if isinstance(result.get('synonymous', []), str):
result['synonymous'] = result['synonymous'].split(';')
term = Term(
name=result.get('name'),
synonymous=result.get('synonymous', []),
description=result.get('description', '')
)
current_key_terms.add(term)
if len(current_key_terms) > 0:
reranked_terms = await self._rerank_matched_terms_async(current_key, current_key_terms)
return reranked_terms
return []
# 并行处理所有关键词
keyword_tasks = [process_single_keyword(current_key) for current_key in query_keys]
keyword_results = await asyncio.gather(*keyword_tasks)
# 合并所有结果
for result in keyword_results:
if len(result) > 0:
matched_terms.extend(result)
vector_end_time = time.time()
vector_time = vector_end_time - vector_start_time
except Exception as e:
raise RuntimeError(f"异步向量检索关键词时出错: {e}") from e
# 查找同义词
for cur_key in query_keys:
if cur_key not in self._synonymous_dict:
continue
name = self._synonymous_dict[cur_key]
matched_terms.append(Term(name=name,synonymous=[cur_key],description=""))
# 提取所有Term对象的名称并排序
# 将set类型的matched_terms转换为TermList类型
term_list = TermList(terms=list(matched_terms))
@@ -334,7 +345,7 @@ class AsyncIntentRecognizer:
total_time = end_time - start_time
# 输出整合的时间日志
logging.info(f"异步关键词匹配耗时统计 - 总耗时: {total_time:.2f}, 问题关键词提取: {llm_time:.2f}秒, 向量检索+重排序: {vector_time:.2f}")
logging.info(f"异步关键词匹配耗时统计 - 总耗时: {total_time:.2f}")
return term_list, query_keys
+7 -21
View File
@@ -172,29 +172,22 @@ query_rewrite_prompt = """
query_rewrite_prompt_pro="""
# 电力造价问答优化工程师(精简版)
# 问答优化工程师
**角色**:基于历史对话和术语库重构问题,提升知识库检索准确率。
**最高准则**
1、保持问题核心意图,但允许在指代消除、背景继承下添加隐含功能词。
2. 所有新增内容必须源于历史对话或聊天背景,禁止捏造。
3. 归一化替换需严格全词匹配:查询中的词必须与术语库同义词完全一致(不区分大小写)。部分匹配(如子字符串)或不匹配,保留原词
禁止部分匹配或子字符串替换。仅当提问中的词 **完全等于** 术语库同义词(大小写不敏感)时方可替换,否则保留原词。
- 错误示例:`文件` ≠ `文件夹路径`(因`文件`是`文件夹路径`的子字符串,禁止替换)。
3. 归一化替换需严格全词匹配:查询中的词必须与术语库同义词完全一致(不区分大小写)。部分匹配(如子字符串)或不匹配,保留原词
## 核心原则
1. **指代消除 → 当指示代词(""/"")出现时,强制继承历史对话的最新核心主题(如功能或任务),并应用到当前主体。**
2. 背景继承 → 补充历史对话和聊天背景中的隐含信息(包括主题和功能)。
3. 术语规范 → 归一化标准词并【】标记。提问中出现的同义词(synonymous)替换为标准词(name)
3. 术语规范 → 提问中出现的同义词(synonymous)替换为标准词(name)并【】标记
4. 语义保真 → 保持问题核心意图,但允许在指代消除、背景继承下添加隐含功能词。
## 归一化替换规则
1. 必须严格全词匹配同义词(即synonymous全词出现在query中)
“错误示例:
- query:文件 !=> 文件夹路径(‘文件’不全等于‘文件夹路径’,部分匹配不替换)。
- query:费率查询 !=> 精准查询(‘费率查询’不全等于‘关键词查询’,不替换)。
**仅当全词匹配时替换**:如query:直接费率(全词匹配‘直接费率’)==> 固定费率。”
2. 只有当问题中的词与术语库中某一项的同义词列表中的某个词完全相同时,才替换为对应的标准词
1. 只有当问题中的词与术语库中某一项的同义词列表中的某个词完全相同时,才替换为对应的标准词
## 处理流程
@@ -211,11 +204,6 @@ query_rewrite_prompt_pro="""
<history>
{chat_history}
</history>
- 当前聊天背景:
<conversation_background>
{context}
</conversation_background>
### 二、重构决策树
```mermaid
@@ -227,7 +215,7 @@ graph TD
E --> F[执行重构]
D -- 否 --> F
F --> G[补充缺失背景]
G --> H[同义词替换+【】标记]
G --> H[同义词替换]
H --> I[保留原生专业术语]
B -- 否 --> I
```
@@ -235,9 +223,8 @@ graph TD
### 三、重构优先级
1. **指代消除 → 当指示代词出现时,优先继承历史对话的核心主题(如功能词),并替换当前问题的动词部分。**
2. 背景继承 → 历史对话中确定的背景信息需要保留。
3. 术语处理 → 同义词转标准词 + 【】标记
4. 同义词转标准词 → 将提问中出现的同义词(synonymous)替换为对应标准词(name)
5. 结构优化 → 保持原问题的5W2H特征,指代消除、背景继承下允许微调意图。
3. 同义词转标准词 → 将提问中出现的同义词(synonymous)替换为对应标准词(name) 并使用【】标记
4. 结构优化 → 保持原问题的5W2H特征,指代消除、背景继承下允许微调意图。
## 输出规范
{output_format}
@@ -246,7 +233,6 @@ graph TD
- [] **主题是否合理继承?**(当有代词时,历史主题必须注入)
- [] 核心诉求是否保留?
- [] 背景信息是否合理补充?
- [] 术语标记是否完整【】?
- [] 语句是否自然流畅?
- [] 避免补充无关信息
"""