采用自然语言编程技术提升提示词

This commit is contained in:
weiweiw 2025-05-20 15:31:00 +08:00
parent 722c9cc7d8
commit b2738705db
6 changed files with 326 additions and 226 deletions

View File

@ -44,3 +44,15 @@ TEAM_NAME = "teamName"
PAGE = "page"
PROGRAM_NAVIGATION = "programNavigation"
# 意图识别和槽位抽取服务返回的关键提示语列表
SLOT_KEYWORDS = [
"未找到匹配的",
"请提供更准确的公司名信息",
"请提供更准确的信息",
"请确认您要选择哪一个",
"请确认风险等级后再次提问",
"请补充该项目部所属的分公司名称",
"请问你想查询什么时间的"
]

View File

@ -10,7 +10,7 @@ from pydantic import ValidationError
from logger_util import setup_logger
from intentRecognition import IntentRecognition
from slotRecognition import SlotRecognition
from utils import CheckResult, check_standard_name_slot_probability, check_lost
from utils import CheckResult, check_standard_name_slot_probability, check_lost, process_msg_content
from config import *
from globalData import GlobalData
from apscheduler.schedulers.background import BackgroundScheduler
@ -272,128 +272,144 @@ def agent():
return jsonify({"error": str(e)}), 500 # 捕捉其他错误并返回
def format_chat_history(history_messages):
reset_keywords = ["当前", "今天", "昨天", "本周", "下周", "明天", "今日", "打开","工程进度"]
keep_index = 0
# Step 1: 查找最靠近当前的 user 消息,若其包含关键词,则记录其索引
for i in reversed(range(len(history_messages))):
msg = history_messages[i]
if msg.role == "user" and any(kw in msg.content and len(msg.content) > 5 for kw in reset_keywords):
keep_index = i
break
# Step 2: 截取需要保留的历史消息
filtered_messages = history_messages[keep_index:]
# Step 3: 构建格式化历史
formatted_history = ""
for i, msg in enumerate(filtered_messages):
formatted_history += f"\n<turn id={i+1}>\n"
formatted_history += f"<role>{msg.role}</role>\n"
formatted_history += f"<content>{process_msg_content(msg.content)}</content>\n"
formatted_history += "</turn>\n"
return formatted_history
def extract_multi_chat(messages):
from openai import OpenAI
client = OpenAI(base_url=api_base_url, api_key=api_key)
latest_message = messages[-1]
latest_user_question = latest_message.content if latest_message.role == "user" else ""
history_messages = messages[-7:] if len(messages) >= 7 else messages
chat_history = format_chat_history(history_messages)
logger.info(f"chat_history:{chat_history}")
time_prefixes = ["当前","今天", "昨天", "本周", "下周", "明天", "今日","打开"]
history_messages = [] if any(prefix in latest_user_question and len(latest_user_question) > 3 for prefix in time_prefixes) else messages[:-1]
logger.info(f"len(history_messages):{len(history_messages)}")
#最新问题的上一个问题里如果含有时间,则清空最老的历史对话
last_two_messages = history_messages[-2:]
has_time_prefix = any(
msg.role == "user" and any(prefix in msg.content and len(msg.content) > 3 for prefix in time_prefixes)
for msg in last_two_messages
)
last_chat_history = "\n".join([f"{msg.role}: {msg.content}" for msg in last_two_messages])
oldest_chat_history = "" if has_time_prefix else "\n".join([f"{msg.role}: {msg.content}" for msg in history_messages[:2]])
logger.info(f"last_chat_history:{last_chat_history}")
logger.info(f"oldest_chat_history:{oldest_chat_history}")
prompt = f'''
你是一个意图识别与补全助手你的任务是根据用户的最新问题判断是否需要补全如果不需要补全则原样返回用户的最新问题否则需要结合最新对话历史和最老对话历史补全用户的最新问题并只返回最终的完整问题
请严格按照如下逻辑判断并执行
你是一个多轮对话理解和还原专家擅长从复杂的上下文中提取关键信息理清语义逻辑最终还原出用户的真实意图请你以"自然语言编程"的方式逐步思考并处理以下任务最终生成完整明确的用户查询
请严格按照以下步骤执行
## 第一步:初始化变量
初始化以下变量用于逻辑推理请勿输出
- 当前_用户问题 = "" # 表示最终需要还原的完整问题
- 当前_实体 = "" # 表示用户所提及的公司、项目部、工程名称等具体业务实体
- 当前_时间 = "" # 表示与查询相关的时间点或时间段
- 下一步_操作 = "" # 表示当前对话中模型需要用户补充的信息类型
- 下一步_选择列表 = "" # 表示需要从中选择具体内容的候选项列表
## 第二步:逐轮解析对话历史
从最早的对话轮次开始依次处理每一轮对话
### 轮次 XX从1开始递增
### 如果当前角色为user:
请根据之前 assistant 的引导语结合当前用户输入判断是否需要进行补全操作并按以下规则处理
- 如果 `下一步_操作 == "补充时间"`调用函数 `补全时间(当前_用户问题, 用户输入)` 并更新 `当前_用户问题`
- 否则 如果 `下一步_操作 == "补充分公司"`调用函数 `补全分公司(当前_用户问题, 用户输入)` 并更新 `当前_用户问题`
- 否则 如果 `下一步_操作 == "选择列表"`调用函数 `替换序号为实体(当前_用户问题, 用户输入, 下一步_选择列表)` 并更新 `当前_用户问题`
- 否则 如果 有完整的句意(用户输入)将当前用户输入作为 `当前_用户问题`
- 否则 如果 包含模糊表达(用户输入)调用函数 `补全模糊表达(当前_用户问题, 用户输入)`并更新 `当前_用户问题`
- 否则 如果 是查询新属性(用户输入)调用函数 `替换新属性(当前_用户问题, 用户输入)`并更新 `当前_用户问题`
- 否则 将当前用户输入作为 `当前_用户问题`
处理完成后请清空 `下一步_操作` `下一步_选择列表`
### 如果当前角色为 assistant:
请根据 assistant 的输出内容判断接下来用户是否被引导进行补全并更新 `下一步_操作`
- 如果 assistant 的回复中包含"请问你想查询什么时间的"或类似引导时间的内容设定 `下一步_操作 = "补充时间"`
- 否则 如果包含"请补充该项目部所属的分公司名称"设定 `下一步_操作 = "补充分公司"`
- 否则 如果包含"请确认您要选择哪一个"设定 `下一步_操作 = "选择列表"`并将当前 assistant 回复中列出的选项存入 `下一步_选择列表`
- 否则 什么都不做
### 重复以上解析过程
请逐条处理每轮对话直到所有历史对话处理完毕然后进入第三步
## 第三步:输出还原后的最终用户问题
在完成全部历史消息处理后请输出变量 `当前_用户问题`它即为根据上下文补全后的完整查询
---
辅助函数说明
函数 补全时间(文本, 时间词):
支持识别如"2025-5-15""昨天""等模糊时间
从时间词里提取时间
在文本开头添加提取到的时间并返回保持其他内容不变
如果没有提取到时间则直接返回原文本内容
示例补全时间(""第一项目部有多少作业计划", "今天的") 返回 "今天第一项目部有多少作业计划"
规则判断与补全流程
函数 补全分公司(文本, 分公司词):
从分公司词里提取分公司
在文本里的时间词之后添加提取到的分公司并返回保持其他内容不变
如果没有提取到分公司信息则直接返回原文本内容
示例补全分公司("今天第一项目有多少作业计划", "送二分公司") 返回 "今天送二分公司第一项目有多少作业计划"
第一步用户最新问题是否存在指代词 结合用户最新问题和历史用户问题进行补全
- 若用户最新问题中出现模糊表达具体是哪些项是哪两个作业计划分别是什么合肥中心变工程呢具体是哪20项考勤人数呢请根据需要结合最新对话历史和最老对话历史的补全问题信息
- 需要补全的信息包括时间主语查询对象等
- **特别地若最新用户问题中未提及时间但最新对话历史中的用户问题中出现今天昨日本周等时间词则需要将其补全进最终问题中**
- **特别地若最新用户问题中未提及时间但最老对话历史中的用户问题中出现今天昨日本周等时间词则需要将其补全进最终问题中**
- 示例1
- 用户最新问题具体的作业内容是什么
- 最新对话历史的用户问题送一分公司第一项目部今天有多少项作业计划
- 最新对话历史的AI回答送电一分公司第一项目管理部有21项作业计划
- 则最终提问应为
今天送电一分公司第一项目管理部的21项作业计划分别是什么
函数 替换序号为实体(文本, 选择项, 选择列表):
从选择项如"第X个"中提取X的值并处理中文数字和阿拉伯数字作为序号
根据序号识别出"第1个XXX第2个YYY"格式的选项列表提取出完整的实体名称
保留文本中的动作词和目标对象将文本中的实体引用替换为完整实体名称
示例替换序号为实体("中心变工程进度", "第1个", "第1个燃气工程第2个给水工程") 返回 "燃气工程进度"
- 示例2
- 用户最新问题2项作业计划是什么
- 最老对话历史的用户问题宋1分公司第一项目部今天有多少作业计划
- 最老对话历史的AI回答未匹配到您说的分公司名宋1分公司请提供更准确的分公司名
- 最新对话历史的用户问题送一分公司
- 最新对话历史的AI回答送电一分公司第一项目管理部有2项作业计划
- 则最终提问应为
今天送电一分公司第一项目管理部的2项作业计划是什么
函数 包含模糊表达(文本):
检查是否包含"具体是哪些项""是哪两个"等模糊表达
返回布尔值表示是否包含
示例包含模糊表达("具体是哪些项") 返回 True
- 示例3
- 用户最新问题具体的班组详情
- 最新对话历史的用户问题送一分公司第一项目部今天有多少班组
- 最新对话历史的AI回答送电一分公司第一项目部金上有20个班组
- 则最终提问应为
送电一分公司第一项目部金上今天具体的20个班组详情
函数 补全模糊表达(文本模糊表达):
结合文本内容和模糊表达补全语义并返回
示例补全模糊表达("今天送一分公司有多少作业计划", "具体是哪些") 返回 "今天送一分公司具体有哪些作业计划"
函数 是查询新属性(文本, 新问题):
如果新问题中提取不到主体 且仅能提取到查询属性
且这个查询属性和文本中提取到的查询属性不同 则返回TRUE
其他情况均返回FALSE
示例是查询新属性("今天送一分公司有多少作业计划", "作业内容") 返回 True
第二步用户最新问题是否为序号指代第一个/第2个 用完整工程/项目/公司名替换补全
- 精确提取用户所指的序号第3个指第3个工程名公司名或项目部名
- 将该工程公司或项目部的完整名称包括括号中的编号提取出来
- 用完整工程公司或项目部的名称替换掉最新对话历史的用户问题中出现的简称或模糊表达,但保留用户问题中的其余所有内容包括操作动词打开查一下时间和目标对象摄像头视频"进度情况""作业计划""作业内容"不得遗漏
- 禁止添加用户原始问题中未出现的动作性动词例如打开查看查询显示展示
- 最终问题必须贴近原始提问语气和意图只替换模糊对象的全称不改变句式和动作行为表达
- 示例1
- 用户最新问题:"第二个" "第2个"
- 最新对话历史的用户问题"2025年南苑调相机检修(PROJ-2023-0179)今天有多少作业计划""
- 最新对话历史的AI回答你说的工程名可能是第一个检修公司调相机一二次设备检修维护和改造服务框架-2025年南苑调相机检修(PROJ-2023-0179)第二个:黄阳-仙河110kV线路工程(PROJ-2024-0047)请确认您要选择哪一个
- 则最终提问应为
"黄阳-仙河110kV线路工程(PROJ-2024-0047))今天有多少作业计划"
函数 替换新属性(文本新查询属性):
先删除文本中的"有多少"等类似的表达数量表达
再将文本里的查询属性替换为新查询属性并保持其他内容不变并返回 且保持新查询属性的语气
示例替换新属性("今天送一分公司有多少作业计划", "作业内容") 返回 "今天送一分公司的作业内容"
- 示例2
- 用户最新问题:"第2个" "第二个"
- 最新对话历史的用户问题"请帮我查一下今天芦集变电站的进度情况"
- 最新对话历史的AI回答你说的工程名可能是第1个芦集-古沟π入潘集变电站220kV线路工程(PROJ-2024-0189)第二个淮南芦集220千伏变电站220千伏配电装置改造工程(PROJ-2024-0265)请确认您要选择哪一个
- 则最终提问应为
"请帮我查一下今天淮南芦集220千伏变电站220千伏配电装置改造工程(PROJ-2024-0265)的进度情况"
函数 有完整的句意(新问题):
如果新问题里有主体同时有操作对象或查询对象则返回TRUE
其他情况均返回FALSE
- 示例3
- 用户最新问题:"第2个"
- 最新对话历史的用户问题"宏源电力公司第三项目部今天有多少项作业计划"
- 最新对话历史的AI回答您说的实施组织名可能是,第1个安徽宏源电力建设有限公司(线路)第2个安徽宏源电力建设有限公司(变电),请选择哪一个
- 则最终提问应为
"安徽宏源电力建设有限公司(变电)第三项目部今天有多少项作业计划"
对话历史如下
{chat_history}
- 示例4
- 用户最新问题:"第2个"
- 最新对话历史的用户问题"打开中心变摄像头"
- 最新对话历史的AI回答您说的工程名可能是,第1个锦绣-常青π入中心变电站220kV架空线路工程(PROJ-2024-1206)第2个合肥中心变B包(PROJ-2024-0176),请选择哪一个
- 则最终提问应为
"打开合肥中心变B包(PROJ-2024-0176)摄像头"
- 示例5
- 最新对话历史的用户问题今天循环园工程有多少项作业计划
- 最新对话历史的AI回答您说的工程名可能是第1个... 第2个... 第3个黄山巷联-水西220kV线路工程(PROJ-2024-0220)请确认您要选择哪一个
- 用户最新问题第3个
- 错误输出不要这样打开黄山巷联-水西220kV线路工程(PROJ-2024-0220)今天有多少项作业计划
- 正确输出你应该输出黄山巷联-水西220kV线路工程(PROJ-2024-0220)今天有多少项作业计划
第三步输出最终问题
- 直接输出最终问题无解释无多余前缀或后缀
- 保持句式自然清晰
---
最老对话历史:
{oldest_chat_history}
最新对话历史
{last_chat_history}
用户最新问题
{latest_user_question}
请输出最终问题'''
请你仅输出还原后的完整问题不要输出任何变量中间步骤或解释说明确保结果自然通顺语义完整
'''
message = [
{"role": "user", "content": prompt}
]
# logger.info(f"*********messages:{prompt}")
response = client.chat.completions.create(
messages=message,
model=model_name,

View File

@ -10,13 +10,13 @@ from pydantic import ValidationError
from logger_util import setup_logger
from intentRecognition import IntentRecognition
from slotRecognition import SlotRecognition
from utils import CheckResult, check_standard_name_slot_probability, check_lost
from utils import CheckResult, check_standard_name_slot_probability, check_lost, process_msg_content
from config import *
from globalData import GlobalData
from apscheduler.schedulers.background import BackgroundScheduler
MODEL_ERNIE_PATH = R"../ernie/output/checkpoint-14672"
MODEL_UIE_PATH = R"../uie/output/checkpoint-18190"
MODEL_UIE_PATH = R"../uie/output/checkpoint-16380"
# 类别名称列表
@ -272,128 +272,165 @@ def agent():
return jsonify({"error": str(e)}), 500 # 捕捉其他错误并返回
def format_chat_history(history_messages):
reset_keywords = ["当前", "今天", "昨天", "本周", "下周", "明天", "今日", "打开","工程进度"]
keep_index = 0
# Step 1: 查找最靠近当前的 user 消息,若其包含关键词,则记录其索引
for i in reversed(range(len(history_messages))):
msg = history_messages[i]
if msg.role == "user" and any(kw in msg.content and len(msg.content) > 5 for kw in reset_keywords):
keep_index = i
break
# Step 2: 截取需要保留的历史消息
filtered_messages = history_messages[keep_index:]
# Step 3: 构建格式化历史
formatted_history = ""
for i, msg in enumerate(filtered_messages):
formatted_history += f"\n<turn id={i+1}>\n"
formatted_history += f"<role>{msg.role}</role>\n"
formatted_history += f"<content>{process_msg_content(msg.content)}</content>\n"
formatted_history += "</turn>\n"
return formatted_history
def extract_multi_chat(messages):
from openai import OpenAI
client = OpenAI(base_url=api_base_url, api_key=api_key)
latest_message = messages[-1]
latest_user_question = latest_message.content if latest_message.role == "user" else ""
history_messages = messages[-7:] if len(messages) >= 7 else messages
chat_history = format_chat_history(history_messages)
logger.info(f"chat_history:{chat_history}")
time_prefixes = ["当前","今天", "昨天", "本周", "下周", "明天", "今日","打开"]
history_messages = [] if any(prefix in latest_user_question and len(latest_user_question) > 3 for prefix in time_prefixes) else messages[:-1]
logger.info(f"len(history_messages):{len(history_messages)}")
#最新问题的上一个问题里如果含有时间,则清空最老的历史对话
last_two_messages = history_messages[-2:]
has_time_prefix = any(
msg.role == "user" and any(prefix in msg.content and len(msg.content) > 3 for prefix in time_prefixes)
for msg in last_two_messages
)
last_chat_history = "\n".join([f"{msg.role}: {msg.content}" for msg in last_two_messages])
oldest_chat_history = "" if has_time_prefix else "\n".join([f"{msg.role}: {msg.content}" for msg in history_messages[:2]])
logger.info(f"last_chat_history:{last_chat_history}")
logger.info(f"oldest_chat_history:{oldest_chat_history}")
prompt = f'''
你是一个意图识别与补全助手你的任务是根据用户的最新问题判断是否需要补全如果不需要补全则原样返回用户的最新问题否则需要结合最新对话历史和最老对话历史补全用户的最新问题并只返回最终的完整问题
请严格按照如下逻辑判断并执行
你是一个多轮对话理解和还原专家擅长从复杂的上下文中提取关键信息理清语义逻辑最终还原出用户的真实意图请你以"自然语言编程"的方式逐步思考并处理以下任务最终生成完整明确的用户查询
请严格按照以下步骤执行
## 第一步:初始化变量
初始化以下变量用于逻辑推理请勿输出
- 当前_用户问题 = "" # 表示最终需要还原的完整问题
- 当前_实体 = "" # 表示用户所提及的公司、项目部、工程名称等具体业务实体
- 当前_时间 = "" # 表示与查询相关的时间点或时间段
- 下一步_操作 = "" # 表示当前对话中模型需要用户补充的信息类型
- 下一步_选择列表 = "" # 表示需要从中选择具体内容的候选项列表
## 第二步:逐轮解析对话历史
从最早的对话轮次开始依次处理每一轮对话
### 轮次 XX从1开始递增
### 如果当前角色为user:
请根据之前 assistant 的引导语结合当前用户输入判断是否需要进行补全操作并按以下规则处理
- 如果 `下一步_操作 == "补充时间"`调用函数 `补全时间(当前_用户问题, 用户输入)` 并更新 `当前_用户问题`
- 否则 如果 `下一步_操作 == "补充分公司"`调用函数 `补全分公司(当前_用户问题, 用户输入)` 并更新 `当前_用户问题`
- 否则 如果 `下一步_操作 == "选择列表"`调用函数 `替换序号为实体(当前_用户问题, 用户输入, 下一步_选择列表)` 并更新 `当前_用户问题`
- 否则 如果 有完整的句意(用户输入)将当前用户输入作为 `当前_用户问题`
- 否则 如果 包含模糊表达(用户输入)调用函数 `补全模糊表达(当前_用户问题, 用户输入)`并更新 `当前_用户问题`
- 否则 如果 是查询新属性(用户输入)调用函数 `替换新属性(当前_用户问题, 用户输入)`并更新 `当前_用户问题`
- 否则 将当前用户输入作为 `当前_用户问题`
处理完成后请清空 `下一步_操作` `下一步_选择列表`
### 如果当前角色为 assistant:
请根据 assistant 的输出内容判断接下来用户是否被引导进行补全并更新 `下一步_操作`
- 如果 assistant 的回复中包含"请问你想查询什么时间的"或类似引导时间的内容设定 `下一步_操作 = "补充时间"`
- 否则 如果包含"请补充该项目部所属的分公司名称"设定 `下一步_操作 = "补充分公司"`
- 否则 如果包含"请确认您要选择哪一个"设定 `下一步_操作 = "选择列表"`并将当前 assistant 回复中列出的选项存入 `下一步_选择列表`
- 否则 什么都不做
### 重复以上解析过程
请逐条处理每轮对话直到所有历史对话处理完毕然后进入第三步
## 第三步:输出还原后的最终用户问题
在完成全部历史消息处理后请输出变量 `当前_用户问题`它即为根据上下文补全后的完整查询
---
辅助函数说明
函数 补全时间(文本, 时间词):
支持识别如"2025-5-15""昨天""等模糊时间
从时间词里提取时间
在文本开头添加提取到的时间并返回保持其他内容不变
如果没有提取到时间则直接返回原文本内容
示例补全时间(""第一项目部有多少作业计划", "今天的") 返回 "今天第一项目部有多少作业计划"
规则判断与补全流程
函数 补全分公司(文本, 分公司词):
从分公司词里提取分公司
在文本里的时间词之后添加提取到的分公司并返回保持其他内容不变
如果没有提取到分公司信息则直接返回原文本内容
示例补全分公司("今天第一项目有多少作业计划", "送二分公司") 返回 "今天送二分公司第一项目有多少作业计划"
第一步用户最新问题是否存在指代词 结合用户最新问题和历史用户问题进行补全
- 若用户最新问题中出现模糊表达具体是哪些项是哪两个作业计划分别是什么合肥中心变工程呢具体是哪20项考勤人数呢请根据需要结合最新对话历史和最老对话历史的补全问题信息
- 需要补全的信息包括时间主语查询对象等
- **特别地若最新用户问题中未提及时间但最新对话历史中的用户问题中出现今天昨日本周等时间词则需要将其补全进最终问题中**
- **特别地若最新用户问题中未提及时间但最老对话历史中的用户问题中出现今天昨日本周等时间词则需要将其补全进最终问题中**
- 示例1
- 用户最新问题具体的作业内容是什么
- 最新对话历史的用户问题送一分公司第一项目部今天有多少项作业计划
- 最新对话历史的AI回答送电一分公司第一项目管理部有21项作业计划
- 则最终提问应为
今天送电一分公司第一项目管理部的21项作业计划分别是什么
函数 替换序号为实体(文本, 选择项, 选择列表):
从选择项如"第X个"中提取X的值并处理中文数字和阿拉伯数字作为序号
根据序号识别出"第1个XXX第2个YYY"格式的选项列表提取出完整的实体名称
保留文本中的动作词和目标对象将文本中的实体引用替换为完整实体名称
示例替换序号为实体("中心变工程进度", "第1个", "第1个燃气工程第2个给水工程") 返回 "燃气工程进度"
- 示例2
- 用户最新问题2项作业计划是什么
- 最老对话历史的用户问题宋1分公司第一项目部今天有多少作业计划
- 最老对话历史的AI回答未匹配到您说的分公司名宋1分公司请提供更准确的分公司名
- 最新对话历史的用户问题送一分公司
- 最新对话历史的AI回答送电一分公司第一项目管理部有2项作业计划
- 则最终提问应为
今天送电一分公司第一项目管理部的2项作业计划是什么
函数 包含模糊表达(文本):
检查是否包含"具体是哪些项""是哪两个"等模糊表达
返回布尔值表示是否包含
示例包含模糊表达("具体是哪些项") 返回 True
- 示例3
- 用户最新问题具体的班组详情
- 最新对话历史的用户问题送一分公司第一项目部今天有多少班组
- 最新对话历史的AI回答送电一分公司第一项目部金上有20个班组
- 则最终提问应为
送电一分公司第一项目部金上今天具体的20个班组详情
函数 补全模糊表达(文本模糊表达):
结合文本内容和模糊表达补全语义并返回
示例补全模糊表达("今天送一分公司有多少作业计划", "具体是哪些") 返回 "今天送一分公司具体有哪些作业计划"
函数 是查询新属性(文本, 新问题):
如果新问题中没有查询主体仅有查询对象 则返回TRUE
如果新问题中仅有查询主体但没有查询对象 则返回TRUE
其他情况均返回FALSE
示例是查询新属性("今天送一分公司有多少作业计划", "作业内容") 返回 True
第二步用户最新问题是否为序号指代第一个/第2个 用完整工程/项目/公司名替换补全
- 精确提取用户所指的序号第3个指第3个工程名公司名或项目部名
- 将该工程公司或项目部的完整名称包括括号中的编号提取出来
- 用完整工程公司或项目部的名称替换掉最新对话历史的用户问题中出现的简称或模糊表达,但保留用户问题中的其余所有内容包括操作动词打开查一下时间和目标对象摄像头视频"进度情况""作业计划""作业内容"不得遗漏
- 禁止添加用户原始问题中未出现的动作性动词例如打开查看查询显示展示
- 最终问题必须贴近原始提问语气和意图只替换模糊对象的全称不改变句式和动作行为表达
- 示例1
- 用户最新问题:"第二个" "第2个"
- 最新对话历史的用户问题"2025年南苑调相机检修(PROJ-2023-0179)今天有多少作业计划""
- 最新对话历史的AI回答你说的工程名可能是第一个检修公司调相机一二次设备检修维护和改造服务框架-2025年南苑调相机检修(PROJ-2023-0179)第二个:黄阳-仙河110kV线路工程(PROJ-2024-0047)请确认您要选择哪一个
- 则最终提问应为
"黄阳-仙河110kV线路工程(PROJ-2024-0047))今天有多少作业计划"
函数 删除数量词(文本):
删除有多少多少几条几个等数量问句词
- 示例2
- 用户最新问题:"第2个" "第二个"
- 最新对话历史的用户问题"请帮我查一下今天芦集变电站的进度情况"
- 最新对话历史的AI回答你说的工程名可能是第1个芦集-古沟π入潘集变电站220kV线路工程(PROJ-2024-0189)第二个淮南芦集220千伏变电站220千伏配电装置改造工程(PROJ-2024-0265)请确认您要选择哪一个
- 则最终提问应为
"请帮我查一下今天淮南芦集220千伏变电站220千伏配电装置改造工程(PROJ-2024-0265)的进度情况"
函数 替换查询属性(文本, 新查询属性):
说明
本函数用于在删除数量词后将原句中与新查询属性同类型的核心查询词替换为新查询属性并确保其他内容保持不变且语义自然
- 示例3
- 用户最新问题:"第2个"
- 最新对话历史的用户问题"宏源电力公司第三项目部今天有多少项作业计划"
- 最新对话历史的AI回答您说的实施组织名可能是,第1个安徽宏源电力建设有限公司(线路)第2个安徽宏源电力建设有限公司(变电),请选择哪一个
- 则最终提问应为
"安徽宏源电力建设有限公司(变电)第三项目部今天有多少项作业计划"
处理步骤
1. 删除文本中的数量类词语例如有多少多少几个几条
2. 识别原句中的核心查询属性词判断其与新查询属性是否属于相同类别如均为对象地点组织等
3. 将原有核心查询词替换为新查询属性保留句中其余上下文结构不变
4. 保持句子语气自然避免引入是什么有多少等疑问表达
- 示例4
- 用户最新问题:"第2个"
- 最新对话历史的用户问题"打开中心变摄像头"
- 最新对话历史的AI回答您说的工程名可能是,第1个锦绣-常青π入中心变电站220kV架空线路工程(PROJ-2024-1206)第2个合肥中心变B包(PROJ-2024-0176),请选择哪一个
- 则最终提问应为
"打开合肥中心变B包(PROJ-2024-0176)摄像头"
返回
返回替换后的文本语义清晰语气自然
- 示例5
- 最新对话历史的用户问题今天循环园工程有多少项作业计划
- 最新对话历史的AI回答您说的工程名可能是第1个... 第2个... 第3个黄山巷联-水西220kV线路工程(PROJ-2024-0220)请确认您要选择哪一个
- 用户最新问题第3个
- 错误输出不要这样打开黄山巷联-水西220kV线路工程(PROJ-2024-0220)今天有多少项作业计划
- 正确输出你应该输出黄山巷联-水西220kV线路工程(PROJ-2024-0220)今天有多少项作业计划
示例
替换查询属性("今天送一分公司有多少作业计划", "作业内容")
"今天送一分公司的作业内容"
第三步输出最终问题
- 直接输出最终问题无解释无多余前缀或后缀
- 保持句式自然清晰
替换查询属性("今天送一分公司的班组详情", "送二分公司")
"今天送二分公司的班组详情"
---
替换查询属性("今天送一分公司的班组详情", "明天呢")
"明天送二分公司的班组详情"
最老对话历史:
{oldest_chat_history}
最新对话历史
{last_chat_history}
函数 有完整的句意(新问题):
如果新问题里有主体同时有操作对象或查询对象则返回TRUE
其他情况均返回FALSE
用户最新问题
{latest_user_question}
对话历史如下
{chat_history}
请输出最终问题'''
请你仅输出还原后的完整问题不要输出任何变量中间步骤或解释说明确保结果自然通顺语义完整
'''
message = [
{"role": "user", "content": prompt}
]
# logger.info(f"*********messages:{prompt}")
response = client.chat.completions.create(
messages=message,
model=model_name,

View File

@ -3,7 +3,7 @@ from paddlenlp.transformers import ErnieForTokenClassification, ErnieTokenizer
from globalData import GlobalData
from utils import standardize_name_only_high_score, clean_useless_company_name
from constants import SUBCONTRACTOR, CONSTRUCTION_UNIT, IMPLEMENTATION_ORG, PAGE, PROGRAM_NAVIGATION
from constants import SUBCONTRACTOR, CONSTRUCTION_UNIT, IMPLEMENTATION_ORG, PAGE, PROGRAM_NAVIGATION, PROJECT_DEPARTMENT
import paddle.nn.functional as F
@ -148,8 +148,15 @@ class SlotRecognition:
prob_updates = {}
for key, value in entities.items():
value = value.replace('#', '')
if "宏源" in value or "宏远" in value:
if key == SUBCONTRACTOR or key == CONSTRUCTION_UNIT:
updates[IMPLEMENTATION_ORG] = value
prob_updates[IMPLEMENTATION_ORG] = slot_probabilities[key]
else:
updates[key] = value
prob_updates[key] = slot_probabilities[key]
# 暂时不支持分包商和监管单位的查询
if key == SUBCONTRACTOR or key == CONSTRUCTION_UNIT or key == IMPLEMENTATION_ORG:
elif key == SUBCONTRACTOR or key == CONSTRUCTION_UNIT or key == IMPLEMENTATION_ORG:
# print(f"recognize_probability- key:{key}value:{value}")
match_results = standardize_name_only_high_score(value,clean_useless_company_name, GlobalData.simply_to_standard_company_name_map, GlobalData.pinyin_simply_to_standard_company_name_map, 90)
if match_results:
@ -177,10 +184,18 @@ class SlotRecognition:
else:
updates[key] = value
prob_updates[key] = slot_probabilities[key]
else:
# 先不处理 PROJECT_DEPARTMENT后续单独处理
elif key != PROJECT_DEPARTMENT:
updates[key] = value
prob_updates[key] = slot_probabilities[key]
# 最后单独处理 PROJECT_DEPARTMENT
if PROJECT_DEPARTMENT in entities:
value = entities[PROJECT_DEPARTMENT].replace('#', '')
updates[PROJECT_DEPARTMENT] = value
prob_updates[PROJECT_DEPARTMENT] = slot_probabilities[PROJECT_DEPARTMENT]
entities.clear()
slot_probabilities.clear()
entities.update(updates)

View File

@ -12,7 +12,8 @@ import re
from globalData import GlobalData
from constants import USELESS_COMPANY_WORDS, USELESS_PROJECT_WORDS, CONSTRUCTION_UNIT, IMPLEMENTATION_ORG, \
SUBCONTRACTOR, PROJECT_NAME, PROJECT_DEPARTMENT, RISK_LEVEL, TEAM_NAME, USELESS_PROGRAM_DEPARTMENT_WORDS
SUBCONTRACTOR, PROJECT_NAME, PROJECT_DEPARTMENT, RISK_LEVEL, TEAM_NAME, USELESS_PROGRAM_DEPARTMENT_WORDS, \
SLOT_KEYWORDS
from logger_util import setup_logger
@ -397,7 +398,7 @@ def generate_project_prompt_with_key(matched_projects, original_name="", slot_ke
if slot_key == CONSTRUCTION_UNIT:
type = "建管单位名"
elif slot_key == IMPLEMENTATION_ORG:
type = "实施组织"
type = "分公司"
elif slot_key == SUBCONTRACTOR:
type = "分包单位名"
elif slot_key == PROJECT_NAME:
@ -657,4 +658,15 @@ def check_standard_name_slot_probability(int_res, slot) -> tuple:
return CheckResult.NEEDS_MORE_ROUNDS, "您查询的风险等级在系统中未找到,请确认风险等级后再次提问"
return CheckResult.NO_MATCH, ""
#
def process_msg_content(content):
if not any(keyword in content for keyword in SLOT_KEYWORDS):
match = re.search(r"^.*?[。!?.!?:]", content)
if match:
first_sentence = match.group(0).rstrip("。!?.!?:") # 去掉末尾的标点符号
return first_sentence.strip() # 同时去掉前后空白字符
else:
return content.strip()
else:
return content

View File

@ -40,6 +40,7 @@ BASE_DATA = {
"九号工程",
"合州变电站",
"合州换流站",
"合州换流站文都",
"金牛变电站建筑",
"金牛变电站建筑部分",
"合州换流站线路",
@ -1050,6 +1051,11 @@ TEMPLATE_CONFIG = {
("{date}{implementation_organization}班组有哪些?", ["date", "implementation_organization"]),
("{date}{implementation_organization}{project_department}现场具体有哪些班组?",
["date", "implementation_organization", "project_department"]),
("{date}{project_name}有2项作业计划班组详情", ["date", "project_name"]),
("{date}{implementation_organization}{project_department}有1项作业计划班组详情呢",
["date", "implementation_organization", "project_department"]),
("{date}{project_department}现场班组详情是什么?", ["date", "project_department"]),
("{date}{project_manager}现场班组情况?", ["date", "project_manager"]),
("{date}分包单位为{subcontractor}具体班组情况是什么?", ["date", "subcontractor"]),
@ -1146,7 +1152,9 @@ TEMPLATE_CONFIG = {
"date": ["今天","最近"],
"templates": [
("{person_name}在哪个{person_query_type}", ["person_name","person_query_type"]),
("{person_name}在那个{person_query_type}", ["person_name","person_query_type"]),
("请帮我查一下{person_name}在哪个{person_query_type}", ["person_name","person_query_type"]),
("请帮我查一下{person_name}在那个{person_query_type}", ["person_name","person_query_type"]),
]
},