diff --git a/flask_app/general/clean_pdf.py b/flask_app/general/clean_pdf.py index 25beba0..a3860f7 100644 --- a/flask_app/general/clean_pdf.py +++ b/flask_app/general/clean_pdf.py @@ -127,6 +127,40 @@ def extract_common_header(pdf_path): 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: print(f"Error in extract_common_header: {e}") return "" # 根据需求调整返回值 diff --git a/flask_app/general/post_processing.py b/flask_app/general/post_processing.py index 0e23667..eede9a0 100644 --- a/flask_app/general/post_processing.py +++ b/flask_app/general/post_processing.py @@ -268,6 +268,7 @@ def outer_post_processing(combined_data, includes, good_list): busi_requirements_info="" tech_deviation={} busi_requirements={} + tech_requirements = {} # 检查 '基础信息' 是否在 includes 中 if "基础信息" in includes: 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) 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_deviation_info=tech_deviation_info, + tech_requirements_dict=tech_requirements, busi_requirements_dict=busi_requirements, zige_info=zige_info, fuhe_info=fuhe_info, diff --git a/flask_app/general/读取文件/按页读取pdf.py b/flask_app/general/读取文件/按页读取pdf.py index 8d356d3..ccd390d 100644 --- a/flask_app/general/读取文件/按页读取pdf.py +++ b/flask_app/general/读取文件/按页读取pdf.py @@ -117,7 +117,7 @@ def save_extracted_text_to_txt(pdf_path, txt_path): if __name__ == '__main__': # 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 = 'C:\\Users\\Administrator\\Desktop\\货物标\\截取test\\交警支队机动车查验监管系统项目采购_tobidders_notice_part1.pdf' # file_path = "C:\\Users\\Administrator\\Desktop\\招标文件\\招标test文件夹\\zbtest8.pdf" diff --git a/flask_app/routes/偏离表main.py b/flask_app/routes/偏离表main.py index b88513e..0c56305 100644 --- a/flask_app/routes/偏离表main.py +++ b/flask_app/routes/偏离表main.py @@ -1,5 +1,6 @@ import json import os +import re import time from copy import deepcopy @@ -418,37 +419,90 @@ def extract_business_deviation(busi_requirements_dict): return business_req_deviation, business_star_req_deviation -def get_tech_star_deviation(tech_string): - if not tech_string: - return {} - prompt_template = """以下输入文本包含采购货物的技术参数要求或采购要求。请从每个键对应的字符串列表中提取带有星★或三角▲的要求项。返回结果仅为符合要求的 JSON 格式对象,每个键名保持不变,键值为包含对应货物、系统或功能模块的带星或带三角要求项的字符串列表。 + +def get_tech_star_deviation(tech_requirements_dict): + def get_tech_star_deviation_directly(tech_dict): + """ + 清理要求项开头的序号并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. 如果某个货物、系统或功能模块下没有带星★或带三角▲的要求项,则不返回该键值对。 -2. 每个带星★或带三角▲的要求项应作为单独的字符串。 -3. 如果输入文本中所有设备、系统或功能模块中都没有带星★或带三角▲的要求项,则直接返回空字典 `{{}}`,无需返回其他说明性描述。 +1. 仅保留符合条件的键值对:如果某键名下没有任何以星号(★)、三角符号(▲)或其他特殊符号开头的要求项,则该键名及其对应内容不包含在输出结果中。 +2. 逐条提取:每个以星号(★)、三角符号(▲)或其他特殊符号开头的要求项,均作为独立的字符串保存在对应键的值列表中。 +3. 忽略序号:如果星号(★)、三角符号(▲)或其他特殊符号前带有序号(例如:1.、(1) 等),提取时需忽略这些序号,仅保留星号(★)、三角符号(▲)或特殊符号及其后内容。 +4. 返回空字典:如果输入文本中所有键名下均无符合条件的要求项,则直接返回一个空字典 {{}},无需返回其他任何说明性文本。 ### 示例输入1如下: {{ "控制键盘": [ "普通要求xx", - "★带星要求xx" - ] - "摄像机"[ + "#特殊符号要求xx" + ], + "摄像机": [ "★带星要求xx", "▲带三角要求xx", "普通要求xx" - ] - "交换机":[ + ], + "交换机": [ "普通要求xx", "普通要求xxx" ] }} ### 对应的输出如下: {{ - "摄像机控制键盘": [ - "★带星要求xx" - ] - "摄像机"[ + "控制键盘": [ + "#特殊符号要求xx" + ], + "摄像机": [ "★带星要求xx", "▲带三角要求xx" ] @@ -459,23 +513,29 @@ def get_tech_star_deviation(tech_string): "控制键盘": [ "普通要求xx", "普通要求xxx" - ] - "摄像机"[ + ], + "摄像机": [ "普通要求xx" ] }} ### 对应的输出如下: -{{}} +{{}} 输入文本内容:{full_text} """ - user_query = prompt_template.format(full_text=tech_string) - # print(user_query) - model_res = qianwen_plus(user_query) - # print(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 + user_query = prompt_template.format(full_text=tech_string) + # 调用模型接口,假设qianwen_plus是已定义的模型调用函数 + model_res = qianwen_plus(user_query) + # 假设clean_json_string是已定义的JSON清理函数 + 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 + + 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): prompt_template = """以下文本是从招标文件中摘取的资格审查、采购需求、商务条款、技术评分相关内容。请根据这些内容,提取并列出投标人需要提交的证明材料。 @@ -509,11 +569,11 @@ def get_proof_materials(all_data_info): proof_materials = clean_json_string(model_res) 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 = [ - ("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,)), ("zigefuhe_deviation", extract_zige_deviation_table, (zige_info, fuhe_info, zigefuhe_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]) 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, zige_info=zige_info, fuhe_info=fuhe_info, diff --git a/flask_app/routes/货物标解析main.py b/flask_app/routes/货物标解析main.py index 5f0cf82..d413cbf 100644 --- a/flask_app/routes/货物标解析main.py +++ b/flask_app/routes/货物标解析main.py @@ -265,11 +265,11 @@ def goods_bid_main(output_folder, file_path, file_type, unique_id): #TODO:小解析考虑提速:1:直接pdf转文本,再切分。后期考虑。 #TODO: -# 解决禅道 测试的bug +# 解决禅道 测试的bu # 货物标和工程标的资格审查整合 ##TODO:陕西省公安厅交通警察总队高速公路交通安全智能感知巡查系统项目(1)_tobidders_notice_part2.pdf 唐山市公安交通警察支队机动车查验机构视频存储回放系统竞争性谈判-招标文件正文(1)_tobidders_notice_part1.pdf 不好搞 # 无法判断用户上传的是否为乱码文件,可以考虑并行调用大模型,如果为乱码文件直接return None -# 目前偏离表.py这块提取带星要求是通过大模型,若采购需求非常长且带星要求非常多,可能会超最大输出字数限制。 +# 国道107 在提取成json文件时,有'湖北众恒永业工程项目管理有限公司广水分公司编'干扰,尝试清除 #截取json文件有些问题:C:\Users\Administrator\Desktop\新建文件夹 (3)\test keywords和special... if __name__ == "__main__": diff --git a/flask_app/货物标/商务服务其他要求提取.py b/flask_app/货物标/商务服务其他要求提取.py index 888fa7c..bdd9f05 100644 --- a/flask_app/货物标/商务服务其他要求提取.py +++ b/flask_app/货物标/商务服务其他要求提取.py @@ -239,11 +239,11 @@ def generate_template(required_keys,full_text, type=1): 如果章节开头位置或采购清单中,除了列出货物名称,还描述了如工期要求、进度要求、品牌要求等商务要求,需提取这些内容 若文档标题包含“工期要求”、“进度要求”等商务要求相关的关键字,应提取对应内容。 -商务要求的组织形式: - 嵌套键值对形式:添加至 '商务要求' 的键值部分,嵌套键名为对应的子标题,保留 三角▲、五角星★ 等符号(若有)。若不存在这些内容,无需额外添加,避免返回空的嵌套键值对'工期要求':[] + 嵌套键值对形式:添加至 '商务要求' 的键值部分,嵌套键名为对应的子标题,保留 三角▲、五角星★ 等特殊符号(若有)。若不存在这些内容,无需额外添加,避免返回空的嵌套键值对'工期要求':[] 直接添加具体内容:不采用嵌套键值对形式,将具体内容直接作为字符串列表的一部分添加到 '商务要求' 的键值部分。 7. 补充要求(服务要求提取): -在提取'服务要求'的时候,若原文(包含正文和表格)中存在'安装要求'、'售后要求'、'维护要求'、'培训要求、质量要求'等服务相关的标题及内容,不要遗漏这部分的'服务要求': - 嵌套键值对形式:添加至 '服务要求' 的键值部分,嵌套键名为对应的子标题,保留 三角▲、五角星★ 等符号(若有)。若不存在这些内容,无需额外添加,避免返回空的嵌套键值对'安装要求':[] + 嵌套键值对形式:添加至 '服务要求' 的键值部分,嵌套键名为对应的子标题,保留 三角▲、五角星★ 等特殊符号(若有)。若不存在这些内容,无需额外添加,避免返回空的嵌套键值对'安装要求':[] 直接添加具体内容:不采用嵌套键值对形式,将具体内容直接作为字符串列表的一部分添加到 '服务要求' 的键值部分。 8. 避免重复提取: 若正文某部分已被提取,则无需再次重复提取。例如:提取'服务要求'时得到了'售后要求'相关内容,那么在提取'商务要求'时无需再提取该内容。如果文档中未明确列出某类要求,直接返回空列表 []。 @@ -258,7 +258,7 @@ def generate_template(required_keys,full_text, type=1): -在提取技术要求或技术、服务要求时,你无需从采购清单或表格中提取具体设备、采购标的的技术要求以及参数要求,你仅需定位到原文中包含'技术要求'或'技术、服务要求'关键字的标题,并提取该标题下的整体技术要求内容; 7. 补充要求(技术要求提取): -在提取{outer_keys_str}的时候,若原文中存在如“总体要求”、“建设要求”等子标题,不要遗漏这部分的'技术要求': - 嵌套键值对形式:添加至 {outer_keys_str}的键值部分,嵌套键名为对应的子标题,保留 三角▲、五角星★ 等符号(若有)。若不存在这些内容,无需额外添加,避免返回空的嵌套键值对'安装要求':[] + 嵌套键值对形式:添加至 {outer_keys_str}的键值部分,嵌套键名为对应的子标题,保留 三角▲、五角星★ 等特殊符号(若有)。若不存在这些内容,无需额外添加,避免返回空的嵌套键值对'安装要求':[] 直接添加具体内容:不采用嵌套键值对形式,将具体内容直接作为字符串列表的一部分添加到 {outer_keys_str}的键值部分。 8. 在提取'技术要求'时,注意不要提取有关'安装、售后、维护、运维、培训、质保、工期、进度'等要求,它们不属于'技术要求'。 **限制内容**: @@ -278,7 +278,7 @@ def generate_template(required_keys,full_text, type=1): -也可以将它们作为该要求下的嵌套键名,但字符串列表中只提取实际的具体要求。 3. **内容提取规则**: -**保留原始格式和符号**:字符串列表中的每个字符串内容需与原文内容保持一致,保留前面的三角▲、五角星★或其他特殊符号和序号(如果有)。不得擅自添加、删减这些符号。 - -如果文档中有明确的标题或子标题,其前面带有三角▲、五角星★,则键名中应完整保留这些符号,与原文保持一致。 + -如果文档中有明确的标题或子标题,其前面带有三角▲、五角星★等特殊符号,则键名中应完整保留这些符号,与原文保持一致。 -表格形式处理: -注意请不要返回Markdown表格语法,可以使用冒号':'将相关信息拼接在一起,如"交付期:合同签订之日起30天内。";或将其组织为嵌套键值对形式,最多允许一层嵌套。 -表格中出现的特殊符号如▲★需要添加至相应键值中。