1.24 后处理提取带星要求添加了脚本提取的逻辑

This commit is contained in:
zy123 2025-01-24 10:48:59 +08:00
parent 4c4b83c2f4
commit 5818ff28c7
6 changed files with 132 additions and 37 deletions

View File

@ -127,6 +127,40 @@ def extract_common_header(pdf_path):
return '\n'.join(common_headers) return '\n'.join(common_headers)
# 执行策略1和策略2先执行策略1中间3页或根据页数调整然后执行策略2前三页或根据页数调整
# 获取两个策略的公共前缀:分别获取两个策略提取的公共页眉。
# 选择最长的公共前缀如果两个策略的前缀之间存在包含关系选择最长的那个。如果没有包含关系则返回策略1提取的前缀。
# headers_results = []
#
# for idx, (start, count) in enumerate(strategies):
# headers = get_headers(pdf_document, start, count, is_pypdf2)
# if len(headers) < 2:
# continue # 需要至少2页来比较
#
# current_common = find_common_headers(headers)
# if current_common:
# headers_results.append(current_common)
# # 不再中断,继续执行下一个策略
# # 如果没有找到,继续下一个策略
#
# if not headers_results:
# return "" # 没有找到任何公共页眉
# # 现在有两个(或一个)策略的公共页眉,选择最长的公共前缀
# # 首先,取出所有策略的公共页眉,并将其转换为单个字符串
# headers_strategies = ['\n'.join(headers) for headers in headers_results]
# # 找到最长的公共前缀
# longest_common = max(headers_strategies, key=lambda s: len(s))
#
# # 检查其他策略的前缀是否是最长前缀的子集
# is_contained = all(longest_common.startswith(other) for other in headers_strategies)
#
# if is_contained:
# # 如果所有策略的前缀都是最长前缀的子集,返回最长前缀
# return longest_common
# else:
# # 如果没有包含关系返回策略1的前缀即第一个策略的结果
# return headers_strategies[0]
except Exception as e: except Exception as e:
print(f"Error in extract_common_header: {e}") print(f"Error in extract_common_header: {e}")
return "" # 根据需求调整返回值 return "" # 根据需求调整返回值

View File

@ -268,6 +268,7 @@ def outer_post_processing(combined_data, includes, good_list):
busi_requirements_info="" busi_requirements_info=""
tech_deviation={} tech_deviation={}
busi_requirements={} busi_requirements={}
tech_requirements = {}
# 检查 '基础信息' 是否在 includes 中 # 检查 '基础信息' 是否在 includes 中
if "基础信息" in includes: if "基础信息" in includes:
base_info = combined_data.get("基础信息", {}) base_info = combined_data.get("基础信息", {})
@ -298,7 +299,7 @@ def outer_post_processing(combined_data, includes, good_list):
busi_eval_info=json.dumps(busi_eval,ensure_ascii=False,indent=4) busi_eval_info=json.dumps(busi_eval,ensure_ascii=False,indent=4)
all_data_info = '\n'.join([zige_info, fuhe_info, zigefuhe_info, tech_deviation_info,busi_requirements_info, tech_eval_info,busi_eval_info]) all_data_info = '\n'.join([zige_info, fuhe_info, zigefuhe_info, tech_deviation_info,busi_requirements_info, tech_eval_info,busi_eval_info])
tech_star_deviation, business_deviation, business_star_deviation, zigefuhe_deviation,proof_materials = process_functions_in_parallel( tech_star_deviation, business_deviation, business_star_deviation, zigefuhe_deviation,proof_materials = process_functions_in_parallel(
tech_deviation_info=tech_deviation_info, tech_requirements_dict=tech_requirements,
busi_requirements_dict=busi_requirements, busi_requirements_dict=busi_requirements,
zige_info=zige_info, zige_info=zige_info,
fuhe_info=fuhe_info, fuhe_info=fuhe_info,

View File

@ -117,7 +117,7 @@ def save_extracted_text_to_txt(pdf_path, txt_path):
if __name__ == '__main__': if __name__ == '__main__':
# file_path='D:\\flask_project\\flask_app\\static\\output\\output1\\648e094b-e677-47ce-9073-09e0c82af210\\ztbfile_tobidders_notice_part2.pdf' # file_path='D:\\flask_project\\flask_app\\static\\output\\output1\\648e094b-e677-47ce-9073-09e0c82af210\\ztbfile_tobidders_notice_part2.pdf'
pdf_path=r"D:\flask_project\flask_app\static\output\output1\139ecc93-bc85-4007-a4c5-1fb158a88719\ztbfile_invalid.pdf" pdf_path=r"C:\Users\Administrator\Downloads\bid_format (1).pdf"
# file_path = r"C:\Users\Administrator\Desktop\招标文件\招标test文件夹\zbtest8.pdf" # file_path = r"C:\Users\Administrator\Desktop\招标文件\招标test文件夹\zbtest8.pdf"
# file_path = 'C:\\Users\\Administrator\\Desktop\\货物标\\截取test\\交警支队机动车查验监管系统项目采购_tobidders_notice_part1.pdf' # file_path = 'C:\\Users\\Administrator\\Desktop\\货物标\\截取test\\交警支队机动车查验监管系统项目采购_tobidders_notice_part1.pdf'
# file_path = "C:\\Users\\Administrator\\Desktop\\招标文件\\招标test文件夹\\zbtest8.pdf" # file_path = "C:\\Users\\Administrator\\Desktop\\招标文件\\招标test文件夹\\zbtest8.pdf"

View File

@ -1,5 +1,6 @@
import json import json
import os import os
import re
import time import time
from copy import deepcopy from copy import deepcopy
@ -418,37 +419,90 @@ def extract_business_deviation(busi_requirements_dict):
return business_req_deviation, business_star_req_deviation return business_req_deviation, business_star_req_deviation
def get_tech_star_deviation(tech_string):
if not tech_string: def get_tech_star_deviation(tech_requirements_dict):
return {} def get_tech_star_deviation_directly(tech_dict):
prompt_template = """以下输入文本包含采购货物的技术参数要求或采购要求。请从每个键对应的字符串列表中提取带有星★或三角▲的要求项。返回结果仅为符合要求的 JSON 格式对象,每个键名保持不变,键值为包含对应货物、系统或功能模块的带星或带三角要求项的字符串列表。 """
清理要求项开头的序号并python代码提取以特殊符号开头的要求项
参数:
tech_dict (dict): 输入的技术要求字典
返回:
dict: 返回清理后的字典仅包含以特殊符号开头的要求项
"""
# 定义用于清理序号的正则表达式
clean_pattern = (r'^\s*(?:[(]\s*\d+\s*[))]|'
r'[A-Za-z]?\d+(?:\.\s*\d+)+[\s*\.、.)]?|' # 12.1
r'[一二三四五六七八九十]+、|'
r'[A-Za-z]\s*[)\.、.]?\s*)|'
r'\d+\s*[)\.、.]?\s*|'
r'[\s*\.、.)]?')
# 定义特殊符号的正则模式
special_symbols_pattern = re.compile(r'^[#★▲●■◆☆△◇○□]')
# 初始化结果字典
result = {}
for key, requirements in tech_dict.items():
cleaned_requirements = []
# 清理每个要求项的开头序号
for req in requirements:
cleaned_req = re.sub(clean_pattern, '', req).strip()
cleaned_requirements.append(cleaned_req)
# 过滤以特殊符号开头的要求项
special_requirements = [req for req in cleaned_requirements if special_symbols_pattern.match(req)]
if special_requirements:
result[key] = special_requirements
return result
def get_tech_star_deviation_model(tech_dict):
"""
使用大模型提取带有特殊符号开头的要求项
参数:
tech_requirements_dict (dict): 输入的技术要求字典
返回:
dict: 返回提取后的技术要求字典
"""
if not tech_dict:
return {}
tech_string = json.dumps(tech_dict, ensure_ascii=False, indent=4)
prompt_template = """以下输入文本包含采购标的的技术参数要求或采购要求。请从每个键对应的字符串列表中提取带有星★或三角▲或其他特殊符号开头的的要求项。返回结果仅为符合要求的 JSON 格式对象,每个键名保持不变,键值为包含该键名下的带星或带三角或以其他特殊符合开头的要求项的字符串列表。
要求与指南 要求与指南
1. 如果某个货物系统或功能模块下没有带星或带三角的要求项则不返回该键值对 1. 仅保留符合条件的键值对如果某键名下没有任何以星号三角符号或其他特殊符号开头的要求项则该键名及其对应内容不包含在输出结果中
2. 每个带星或带三角的要求项应作为单独的字符串 2. 逐条提取每个以星号三角符号或其他特殊符号开头的要求项均作为独立的字符串保存在对应键的值列表中
3. 如果输入文本中所有设备系统或功能模块中都没有带星或带三角的要求项则直接返回空字典 `{{}}`无需返回其他说明性描述 3. 忽略序号如果星号三角符号或其他特殊符号前带有序号例如1.(1) 提取时需忽略这些序号仅保留星号三角符号或特殊符号及其后内容
4. 返回空字典如果输入文本中所有键名下均无符合条件的要求项则直接返回一个空字典 {{}}无需返回其他任何说明性文本
### 示例输入1如下 ### 示例输入1如下
{{ {{
"控制键盘": [ "控制键盘": [
"普通要求xx", "普通要求xx",
"★带星要求xx" "#特殊符号要求xx"
] ],
"摄像机"[ "摄像机": [
"★带星要求xx", "★带星要求xx",
"▲带三角要求xx", "▲带三角要求xx",
"普通要求xx" "普通要求xx"
] ],
"交换机":[ "交换机": [
"普通要求xx", "普通要求xx",
"普通要求xxx" "普通要求xxx"
] ]
}} }}
### 对应的输出如下: ### 对应的输出如下:
{{ {{
"摄像机控制键盘": [ "控制键盘": [
"★带星要求xx" "#特殊符号要求xx"
] ],
"摄像机"[ "摄像机": [
"★带星要求xx", "★带星要求xx",
"▲带三角要求xx" "▲带三角要求xx"
] ]
@ -459,23 +513,29 @@ def get_tech_star_deviation(tech_string):
"控制键盘": [ "控制键盘": [
"普通要求xx", "普通要求xx",
"普通要求xxx" "普通要求xxx"
] ],
"摄像机"[ "摄像机": [
"普通要求xx" "普通要求xx"
] ]
}} }}
### 对应的输出如下: ### 对应的输出如下:
{{}} {{}}
输入文本内容{full_text} 输入文本内容{full_text}
""" """
user_query = prompt_template.format(full_text=tech_string) user_query = prompt_template.format(full_text=tech_string)
# print(user_query) # 调用模型接口假设qianwen_plus是已定义的模型调用函数
model_res = qianwen_plus(user_query) model_res = qianwen_plus(user_query)
# print(model_res) # 假设clean_json_string是已定义的JSON清理函数
tech_star_deviation = clean_json_string(model_res) tech_star_deviation = clean_json_string(model_res)
filtered_dict = {key: value for key, value in tech_star_deviation.items() if value} #过滤键值为空列表,二重保险。 # 过滤键值为空的键
return filtered_dict filtered_dict = {key: value for key, value in tech_star_deviation.items() if value}
return filtered_dict
if len(tech_requirements_dict) > 30: #暂时设置如果采购货物>30 就脚本提取带星要求
return get_tech_star_deviation_directly(tech_requirements_dict)
else:
return get_tech_star_deviation_model(tech_requirements_dict)
def get_proof_materials(all_data_info): def get_proof_materials(all_data_info):
prompt_template = """以下文本是从招标文件中摘取的资格审查、采购需求、商务条款、技术评分相关内容。请根据这些内容,提取并列出投标人需要提交的证明材料。 prompt_template = """以下文本是从招标文件中摘取的资格审查、采购需求、商务条款、技术评分相关内容。请根据这些内容,提取并列出投标人需要提交的证明材料。
@ -509,11 +569,11 @@ def get_proof_materials(all_data_info):
proof_materials = clean_json_string(model_res) proof_materials = clean_json_string(model_res)
return proof_materials return proof_materials
def process_functions_in_parallel(tech_deviation_info, busi_requirements_dict, zige_info, fuhe_info, zigefuhe_info,all_data_info): def process_functions_in_parallel(tech_requirements_dict, busi_requirements_dict, zige_info, fuhe_info, zigefuhe_info,all_data_info):
# 准备输入参数 # 准备输入参数
# 定义任务和对应参数 # 定义任务和对应参数
tasks = [ tasks = [
("tech_star_deviation", get_tech_star_deviation, (tech_deviation_info,)), ("tech_star_deviation", get_tech_star_deviation, (tech_requirements_dict,)),
("business_deviation_and_star", extract_business_deviation, (busi_requirements_dict,)), ("business_deviation_and_star", extract_business_deviation, (busi_requirements_dict,)),
("zigefuhe_deviation", extract_zige_deviation_table, (zige_info, fuhe_info, zigefuhe_info)), ("zigefuhe_deviation", extract_zige_deviation_table, (zige_info, fuhe_info, zigefuhe_info)),
("proof_materials", get_proof_materials, (all_data_info,)) ("proof_materials", get_proof_materials, (all_data_info,))
@ -658,7 +718,7 @@ def get_tech_and_business_deviation(file_path,file_type,unique_id,output_folder,
all_data_info = '\n'.join([zige_info, fuhe_info, zigefuhe_info, tech_deviation_info,busi_requirements_info, evaluation_info]) all_data_info = '\n'.join([zige_info, fuhe_info, zigefuhe_info, tech_deviation_info,busi_requirements_info, evaluation_info])
tech_star_deviation, business_deviation, business_star_deviation, zigefuhe_deviation, proof_materials= process_functions_in_parallel( tech_star_deviation, business_deviation, business_star_deviation, zigefuhe_deviation, proof_materials= process_functions_in_parallel(
tech_deviation_info=tech_deviation_info, tech_requirements_dict=tech_requirements,
busi_requirements_dict=busi_requirements, busi_requirements_dict=busi_requirements,
zige_info=zige_info, zige_info=zige_info,
fuhe_info=fuhe_info, fuhe_info=fuhe_info,

View File

@ -265,11 +265,11 @@ def goods_bid_main(output_folder, file_path, file_type, unique_id):
#TODO:小解析考虑提速1直接pdf转文本再切分。后期考虑。 #TODO:小解析考虑提速1直接pdf转文本再切分。后期考虑。
#TODO: #TODO:
# 解决禅道 测试的bug # 解决禅道 测试的bu
# 货物标和工程标的资格审查整合 # 货物标和工程标的资格审查整合
##TODO:陕西省公安厅交通警察总队高速公路交通安全智能感知巡查系统项目(1)_tobidders_notice_part2.pdf 唐山市公安交通警察支队机动车查验机构视频存储回放系统竞争性谈判-招标文件正文(1)_tobidders_notice_part1.pdf 不好搞 ##TODO:陕西省公安厅交通警察总队高速公路交通安全智能感知巡查系统项目(1)_tobidders_notice_part2.pdf 唐山市公安交通警察支队机动车查验机构视频存储回放系统竞争性谈判-招标文件正文(1)_tobidders_notice_part1.pdf 不好搞
# 无法判断用户上传的是否为乱码文件,可以考虑并行调用大模型,如果为乱码文件直接return None # 无法判断用户上传的是否为乱码文件,可以考虑并行调用大模型,如果为乱码文件直接return None
# 目前偏离表.py这块提取带星要求是通过大模型若采购需求非常长且带星要求非常多可能会超最大输出字数限制。 # 国道107 在提取成json文件时有'湖北众恒永业工程项目管理有限公司广水分公司编'干扰,尝试清除
#截取json文件有些问题C:\Users\Administrator\Desktop\新建文件夹 (3)\test keywords和special... #截取json文件有些问题C:\Users\Administrator\Desktop\新建文件夹 (3)\test keywords和special...
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -239,11 +239,11 @@ def generate_template(required_keys,full_text, type=1):
如果章节开头位置或采购清单中除了列出货物名称还描述了如工期要求进度要求品牌要求等商务要求需提取这些内容 如果章节开头位置或采购清单中除了列出货物名称还描述了如工期要求进度要求品牌要求等商务要求需提取这些内容
若文档标题包含工期要求进度要求等商务要求相关的关键字应提取对应内容 若文档标题包含工期要求进度要求等商务要求相关的关键字应提取对应内容
-商务要求的组织形式 -商务要求的组织形式
嵌套键值对形式添加至 '商务要求' 的键值部分嵌套键名为对应的子标题保留 三角五角星 符号若有若不存在这些内容无需额外添加避免返回空的嵌套键值对'工期要求':[] 嵌套键值对形式添加至 '商务要求' 的键值部分嵌套键名为对应的子标题保留 三角五角星 特殊符号若有若不存在这些内容无需额外添加避免返回空的嵌套键值对'工期要求':[]
直接添加具体内容不采用嵌套键值对形式将具体内容直接作为字符串列表的一部分添加到 '商务要求' 的键值部分 直接添加具体内容不采用嵌套键值对形式将具体内容直接作为字符串列表的一部分添加到 '商务要求' 的键值部分
7. 补充要求服务要求提取 7. 补充要求服务要求提取
-在提取'服务要求'的时候若原文包含正文和表格中存在'安装要求''售后要求''维护要求''培训要求、质量要求'等服务相关的标题及内容不要遗漏这部分的'服务要求' -在提取'服务要求'的时候若原文包含正文和表格中存在'安装要求''售后要求''维护要求''培训要求、质量要求'等服务相关的标题及内容不要遗漏这部分的'服务要求'
嵌套键值对形式添加至 '服务要求' 的键值部分嵌套键名为对应的子标题保留 三角五角星 符号若有若不存在这些内容无需额外添加避免返回空的嵌套键值对'安装要求':[] 嵌套键值对形式添加至 '服务要求' 的键值部分嵌套键名为对应的子标题保留 三角五角星 特殊符号若有若不存在这些内容无需额外添加避免返回空的嵌套键值对'安装要求':[]
直接添加具体内容不采用嵌套键值对形式将具体内容直接作为字符串列表的一部分添加到 '服务要求' 的键值部分 直接添加具体内容不采用嵌套键值对形式将具体内容直接作为字符串列表的一部分添加到 '服务要求' 的键值部分
8. 避免重复提取 8. 避免重复提取
若正文某部分已被提取则无需再次重复提取例如提取'服务要求'时得到了'售后要求'相关内容那么在提取'商务要求'时无需再提取该内容如果文档中未明确列出某类要求直接返回空列表 [] 若正文某部分已被提取则无需再次重复提取例如提取'服务要求'时得到了'售后要求'相关内容那么在提取'商务要求'时无需再提取该内容如果文档中未明确列出某类要求直接返回空列表 []
@ -258,7 +258,7 @@ def generate_template(required_keys,full_text, type=1):
-在提取技术要求或技术服务要求时你无需从采购清单或表格中提取具体设备采购标的的技术要求以及参数要求你仅需定位到原文中包含'技术要求''技术、服务要求'关键字的标题并提取该标题下的整体技术要求内容 -在提取技术要求或技术服务要求时你无需从采购清单或表格中提取具体设备采购标的的技术要求以及参数要求你仅需定位到原文中包含'技术要求''技术、服务要求'关键字的标题并提取该标题下的整体技术要求内容
7. 补充要求技术要求提取 7. 补充要求技术要求提取
-在提取{outer_keys_str}的时候若原文中存在如总体要求建设要求等子标题不要遗漏这部分的'技术要求': -在提取{outer_keys_str}的时候若原文中存在如总体要求建设要求等子标题不要遗漏这部分的'技术要求':
嵌套键值对形式添加至 {outer_keys_str}的键值部分嵌套键名为对应的子标题保留 三角五角星 符号若有若不存在这些内容无需额外添加避免返回空的嵌套键值对'安装要求':[] 嵌套键值对形式添加至 {outer_keys_str}的键值部分嵌套键名为对应的子标题保留 三角五角星 特殊符号若有若不存在这些内容无需额外添加避免返回空的嵌套键值对'安装要求':[]
直接添加具体内容不采用嵌套键值对形式将具体内容直接作为字符串列表的一部分添加到 {outer_keys_str}的键值部分 直接添加具体内容不采用嵌套键值对形式将具体内容直接作为字符串列表的一部分添加到 {outer_keys_str}的键值部分
8. 在提取'技术要求'注意不要提取有关'安装、售后、维护、运维、培训、质保、工期、进度'等要求它们不属于'技术要求' 8. 在提取'技术要求'注意不要提取有关'安装、售后、维护、运维、培训、质保、工期、进度'等要求它们不属于'技术要求'
**限制内容** **限制内容**
@ -278,7 +278,7 @@ def generate_template(required_keys,full_text, type=1):
-也可以将它们作为该要求下的嵌套键名,但字符串列表中只提取实际的具体要求 -也可以将它们作为该要求下的嵌套键名,但字符串列表中只提取实际的具体要求
3. **内容提取规则** 3. **内容提取规则**
-**保留原始格式和符号**字符串列表中的每个字符串内容需与原文内容保持一致保留前面的三角五角星或其他特殊符号和序号如果有不得擅自添加删减这些符号 -**保留原始格式和符号**字符串列表中的每个字符串内容需与原文内容保持一致保留前面的三角五角星或其他特殊符号和序号如果有不得擅自添加删减这些符号
-如果文档中有明确的标题或子标题其前面带有三角五角星则键名中应完整保留这些符号与原文保持一致 -如果文档中有明确的标题或子标题其前面带有三角五角星等特殊符号则键名中应完整保留这些符号与原文保持一致
-表格形式处理 -表格形式处理
-注意请不要返回Markdown表格语法可以使用冒号':'将相关信息拼接在一起"交付期:合同签订之日起30天内。"或将其组织为嵌套键值对形式最多允许一层嵌套 -注意请不要返回Markdown表格语法可以使用冒号':'将相关信息拼接在一起"交付期:合同签订之日起30天内。"或将其组织为嵌套键值对形式最多允许一层嵌套
-表格中出现的特殊符号如需要添加至相应键值中 -表格中出现的特殊符号如需要添加至相应键值中