250 lines
11 KiB
Python
250 lines
11 KiB
Python
import json
|
||
import threading
|
||
import time
|
||
|
||
from flask_app.main.json_utils import clean_json_string, nest_json_under_key,rename_outer_key
|
||
from flask_app.main.投标人须知正文提取指定内容 import extract_from_notice
|
||
from flask_app.main.判断是否分包等 import judge_whether_main, read_questions_from_judge, merge_json_to_list
|
||
from flask_app.main.多线程提问 import read_questions_from_file, multi_threading
|
||
from flask_app.main.通义千问long import upload_file
|
||
|
||
|
||
def aggregate_basic_info_engineering(baseinfo_list):
|
||
"""
|
||
将基础信息列表中的数据进行合并和分类。
|
||
|
||
参数:
|
||
- baseinfo_list (list): 包含多个基础信息的列表。
|
||
|
||
返回:
|
||
- dict: 合并和分类后的基础信息字典。
|
||
"""
|
||
key_groups = {
|
||
"招标人/代理信息": ["招标人", "招标人联系方式", "招标代理机构", "招标代理机构联系方式"],
|
||
"项目信息": ["项目名称", "招标编号", "项目概况", "招标范围", "招标控制价", "投标竞争下浮率"],
|
||
"关键时间/内容": [
|
||
"投标文件递交截止日期",
|
||
"投标文件递交方式",
|
||
"开标时间",
|
||
"开标地点",
|
||
"投标人要求澄清招标文件的截止时间",
|
||
"投标有效期",
|
||
"评标结果公示媒介"
|
||
],
|
||
"保证金相关": ["质量保证金", "退还投标保证金"],
|
||
"其他信息": [
|
||
"重新招标、不再招标和终止招标",
|
||
"投标费用承担",
|
||
"招标代理服务费",
|
||
"是否退还投标文件",
|
||
]
|
||
}
|
||
|
||
combined_data = {}
|
||
relevant_keys_detected = set()
|
||
|
||
# 合并所有基础信息并收集相关键
|
||
for baseinfo in baseinfo_list:
|
||
combined_data.update(baseinfo)
|
||
relevant_keys_detected.update(baseinfo.keys())
|
||
|
||
# 动态调整键组
|
||
dynamic_key_handling(key_groups, relevant_keys_detected)
|
||
|
||
# 创建一个副本以存储未分类的项目
|
||
unclassified_items = {k: v for k, v in combined_data.items() if k not in [item for sublist in key_groups.values() for item in sublist]}
|
||
|
||
# 按键组分类并嵌套
|
||
for group_name, keys in key_groups.items():
|
||
group_data = {key: combined_data.get(key, "未提供") for key in keys}
|
||
combined_data[group_name] = group_data
|
||
# 从 unclassified_items 中移除已分类的键
|
||
for key in keys:
|
||
unclassified_items.pop(key, None)
|
||
|
||
# 将剩余未分类的键值对添加到 "其他信息" 组
|
||
combined_data["其他信息"].update(unclassified_items)
|
||
|
||
# 移除顶层的未分类键值对
|
||
for key in list(combined_data.keys()):
|
||
if key not in key_groups:
|
||
del combined_data[key]
|
||
|
||
return combined_data
|
||
|
||
def dynamic_key_handling(key_groups, detected_keys):
|
||
# 检查和调整键组配置
|
||
for key in detected_keys:
|
||
# 处理“保证金相关”组,插到"质量保证金"前
|
||
if "保证金" in key:
|
||
group = key_groups["保证金相关"]
|
||
insert_before = "质量保证金"
|
||
if insert_before in group:
|
||
index = group.index(insert_before)
|
||
if key not in group: # 避免重复插入
|
||
group.insert(index, key)
|
||
else:
|
||
group.append(key) # 如果没有找到特定键,则追加到末尾
|
||
elif "联合体" in key:
|
||
key_groups["项目信息"].append(key)
|
||
elif "分包" in key:
|
||
key_groups["项目信息"].append(key)
|
||
elif "踏勘现场" in key:
|
||
key_groups["其他信息"].append(key)
|
||
elif "投标预备会" in key:
|
||
key_groups["其他信息"].append(key)
|
||
elif "偏离" in key:
|
||
key_groups["其他信息"].append(key)
|
||
|
||
|
||
def judge_consortium_bidding(baseinfo_list):
|
||
updated_list = []
|
||
accept_bidding = False
|
||
for baseinfo in baseinfo_list:
|
||
# 检查 "是否接受联合体投标" 键是否存在且其值为 "是"
|
||
if "是否接受联合体投标" in baseinfo and baseinfo["是否接受联合体投标"] == "是":
|
||
accept_bidding = True
|
||
# 从字典中移除特定键值对
|
||
baseinfo.pop("是否接受联合体投标", None)
|
||
# # 将修改后的 json 数据转换回 JSON 字符串(如果需要)
|
||
# updated_info = json.dumps(json_data)
|
||
updated_list.append(baseinfo)
|
||
# 更新原始列表,如果你想保留修改
|
||
baseinfo_list[:] = updated_list
|
||
return accept_bidding
|
||
|
||
def generate_query(baseinfo_list):
|
||
questions_list = []
|
||
for item in baseinfo_list:
|
||
# print(json.dumps(item, ensure_ascii=False, indent=4))
|
||
for key, value in item.items():
|
||
if value == "未知" or (isinstance(value, dict) and all(v == "未知" for v in value.values())):
|
||
question = (
|
||
f"根据该招标文件中的信息,{key}的内容是怎样的?"
|
||
f"请按json格式给我提供信息,键名是'{key}',"
|
||
f"若存在未知信息,在对应的键值中填'未知'。"
|
||
)
|
||
questions_list.append(question)
|
||
return questions_list
|
||
|
||
def update_baseinfo_lists(baseinfo_list1, baseinfo_list2):
|
||
# 创建一个字典,用于存储 baseinfo_list1 中的所有键值对
|
||
combined_dict = {}
|
||
for item in baseinfo_list1:
|
||
combined_dict.update(item)
|
||
|
||
# 使用 baseinfo_list2 中的信息更新 combined_dict
|
||
for item in baseinfo_list2:
|
||
for key, value in item.items():
|
||
if key in combined_dict:
|
||
combined_dict[key] = value
|
||
|
||
# 重新构建 baseinfo_list1,保持原有的结构
|
||
updated_list = []
|
||
for item in baseinfo_list1:
|
||
updated_item = {}
|
||
for key in item:
|
||
updated_item[key] = combined_dict[key]
|
||
updated_list.append(updated_item)
|
||
|
||
return updated_list
|
||
|
||
def process_judge_questions(judge_file_path, chosen_numbers, truncate0, baseinfo_list1):
|
||
judge_questions = read_questions_from_judge(judge_file_path, chosen_numbers)
|
||
judge_consortium = judge_consortium_bidding(baseinfo_list1)
|
||
if judge_consortium:
|
||
judge_consortium_question = (
|
||
"该招标文件对于联合体投标的要求是怎样的,请按json格式给我提供信息,"
|
||
"外层键名为'联合体投标要求',其中有一个嵌套键值对为:\"是否接受联合体投标\":\"是\""
|
||
)
|
||
judge_questions.append(judge_consortium_question)
|
||
file_id3 = upload_file(truncate0)
|
||
res2 = multi_threading(judge_questions, "", file_id3, 2)
|
||
|
||
if not res2:
|
||
print("基础信息整合: multi_threading error!")
|
||
else:
|
||
for question, response in res2:
|
||
baseinfo_list1.append(clean_json_string(response))
|
||
|
||
def process_questions_list(questions_list, truncate2):
|
||
if questions_list:
|
||
file_id = upload_file(truncate2)
|
||
baseinfo_results = multi_threading(questions_list, "", file_id, 2)
|
||
return [clean_json_string(res) for _, res in baseinfo_results] if baseinfo_results else []
|
||
else:
|
||
return []
|
||
|
||
def combine_basic_info(merged_baseinfo_path,truncate0,truncate2, clause_path):
|
||
"""
|
||
综合和处理基础信息,生成最终的基础信息字典。
|
||
|
||
参数:
|
||
- knowledge_name (str): 知识名称。
|
||
- truncate0 (str): 文件路径。
|
||
- output_folder (str): 输出文件夹路径。
|
||
- clause_path (str): 条款路径。
|
||
|
||
返回:
|
||
- dict: 综合后的基础信息。
|
||
"""
|
||
# baseinfo_prompt_file_path='flask_app/static/提示词/基本信息工程标qianwen-long.txt'
|
||
file_id1 = upload_file(merged_baseinfo_path)
|
||
baseinfo_prompt_file_path = 'D:\\flask_project\\flask_app\\static\\提示词\\基本信息工程标qianwen-long.txt'
|
||
questions = read_questions_from_file(baseinfo_prompt_file_path)
|
||
# 判断是否分包、是否需要递交投标保证金等
|
||
more_query = "请你根据招标文件信息,回答以下问题:是否组织踏勘现场?是否召开投标预备会?是否允许偏离?是否退还投标文件?是否允许分包? 是否需要递交投标保证金?是否需要提交履约保证金(履约担保)?是否有招标代理服务费?请按json格式给我提供信息,键名分别为'是否组织踏勘现场','是否召开投标预备会','是否允许偏离','是否退还投标文件',是否允许分包','是否递交投标保证金','是否提交履约保证金','是否有招标代理服务费',键值仅限于'是','否','未知',若存在矛盾信息,请回答'未知'。"
|
||
questions.append(more_query)
|
||
baseinfo_results = multi_threading(questions, "", file_id1, 2)
|
||
# 清理 JSON 字符串
|
||
baseinfo_list1 = [clean_json_string(res) for _, res in baseinfo_results] if baseinfo_results else []
|
||
|
||
chosen_numbers, merged = merge_json_to_list(baseinfo_list1.pop())
|
||
baseinfo_list1.append(merged)
|
||
|
||
questions_list=generate_query(baseinfo_list1)
|
||
|
||
# judge_file_path = 'flask_app/static/提示词/是否相关问题qianwen-long.txt'
|
||
judge_file_path = 'D:\\flask_project\\flask_app\\static\\提示词\\是否相关问题qianwen-long.txt'
|
||
# 创建两个线程
|
||
thread1 = threading.Thread(target=process_judge_questions,
|
||
args=(judge_file_path, chosen_numbers, truncate0, baseinfo_list1))
|
||
thread2 = threading.Thread(target=process_questions_list, args=(questions_list, truncate2))
|
||
|
||
# 启动线程
|
||
thread1.start()
|
||
thread2.start()
|
||
|
||
# 等待两个线程完成
|
||
thread1.join()
|
||
thread2.join()
|
||
|
||
# 处理结果
|
||
# baseinfo_list1 已经在 process_judge_questions 中被更新
|
||
baseinfo_list2 = process_questions_list(questions_list, truncate2)
|
||
|
||
updated_list=update_baseinfo_lists(baseinfo_list1,baseinfo_list2)
|
||
|
||
rebidding_situation = extract_from_notice(clause_path, 3) # "重新招标, 不再招标和终止招标"需从投标人须知正文提取
|
||
update_json = rename_outer_key(rebidding_situation, "重新招标、不再招标和终止招标")
|
||
updated_list.append(update_json)
|
||
aggregated_baseinfo = aggregate_basic_info_engineering(updated_list) # 现在是一个字典
|
||
return {"基础信息": aggregated_baseinfo}
|
||
|
||
#TODO:先不带投标人须知正文,如果是未知,再直接问正文,
|
||
if __name__ == "__main__":
|
||
start_time=time.time()
|
||
merged_baseinfo_path="C:\\Users\\Administrator\\Desktop\\招标文件\\special_output\\zbtest2_merged_baseinfo.pdf"
|
||
# output_folder="C:\\Users\\Administrator\\Desktop\\招标文件\\special_output"
|
||
truncate0="C:\\Users\\Administrator\\Desktop\\招标文件\\special_output\\zbtest2_tobidders_notice_table.pdf"
|
||
truncate2="C:\\Users\\Administrator\\Desktop\\招标文件\\special_output\\zbtest2_tobidders_notice.pdf"
|
||
clause_path="C:\\Users\\Administrator\\Desktop\\招标文件\\special_output\\clause1.json"
|
||
res=combine_basic_info(merged_baseinfo_path,truncate0,truncate2,clause_path)
|
||
print(json.dumps(res,ensure_ascii=False,indent=4))
|
||
end_time=time.time()
|
||
print("elapsed_time:"+str(end_time-start_time))
|
||
|
||
|
||
|
||
|