diff --git a/flask_app/general/file2markdown.py b/flask_app/general/file2markdown.py index 8501cf6..98de8a3 100644 --- a/flask_app/general/file2markdown.py +++ b/flask_app/general/file2markdown.py @@ -50,7 +50,7 @@ def convert_file_to_markdown(file_path): 'page_start': 0, 'page_count': 100, # 设置解析页数为50页 'table_flavor': 'html', # html 按html语法输出表格 - 'parse_mode': 'auto', # 设置解析模式为scan模式 + 'parse_mode': 'scan', # 设置解析模式为scan模式 'page_details': 0, # 不包含页面细节 'markdown_details': 1, 'apply_document_tree': 1, @@ -75,6 +75,6 @@ def convert_file_to_markdown(file_path): if __name__ == "__main__": # file_path=r"C:\Users\Administrator\Desktop\fsdownload\e702f1e6-095d-443d-bb7d-ef2e42037cb1\ztbfile_procurement.pdf" - file_path=r"C:\Users\Administrator\Desktop\new招标文件\output5\HBDL-2024-0514-001-招标文件_procurement\HBDL-2024-0514-001-招标文件_procurement_1.pdf" + file_path=r"C:\Users\Administrator\Desktop\文件解析问题\文件解析问题\baada43d-24f6-459d-8a81-219d130f20da\ztbfile_procurement.pdf" res=convert_file_to_markdown(file_path) print(res) \ No newline at end of file diff --git a/flask_app/general/投标人须知正文条款提取成json文件.py b/flask_app/general/投标人须知正文条款提取成json文件.py index be4c041..6767629 100644 --- a/flask_app/general/投标人须知正文条款提取成json文件.py +++ b/flask_app/general/投标人须知正文条款提取成json文件.py @@ -4,10 +4,26 @@ from PyPDF2 import PdfReader from flask_app.货物标.截取pdf货物标版 import clean_page_content,extract_common_header def compare_headings(current, new): + """ + 比较两个标题的层次关系,并确保新标题比当前标题大且最高位数字差值不超过5。 + + 参数: + current (str): 当前标题,例如 "1.2.3" + new (str): 新标题,例如 "1.3" + + 返回: + bool: 如果新标题大于当前标题且最高位数字差值不超过3,则返回 True,否则返回 False + """ # 使用过滤来确保只处理非空且为数字的部分 current_nums = [int(num) for num in current.split('.') if num.isdigit()] new_nums = [int(num) for num in new.split('.') if num.isdigit()] + # 确保新标题的最高位数字不超过当前标题的最高位数字 + 3 + if new_nums: + if len(new_nums) > 0 and len(current_nums) > 0: + if new_nums[0] > current_nums[0] + 3: + return False + # 比较数字序列以确定标题的层次关系 for c, n in zip(current_nums, new_nums): if n > c: diff --git a/flask_app/工程标/截取pdf工程标版.py b/flask_app/工程标/截取pdf工程标版.py index 9c3e9f6..617f668 100644 --- a/flask_app/工程标/截取pdf工程标版.py +++ b/flask_app/工程标/截取pdf工程标版.py @@ -66,7 +66,7 @@ def extract_pages_tobidders_notice(pdf_path, output_folder, begin_pattern, begin # 定义基础的 mid_pattern base_mid_pattern = r'^\s*(?:[((]\s*[一二12]?\s*[))]\s*[、..]*|' \ r'[一二12][、..]+|[、..]+)\s*(说\s*明|总\s*则|名\s*词\s*解\s*释)' \ - r'|(?max_tokens: -# questions_to_continue.append((question, message)) -# else: -# temp_final.update(parsed) -# # 第二步:多线程处理需要调用 `continue_answer` 的问题 -# if questions_to_continue: -# continued_results = process_continue_answers(questions_to_continue, model_type, file_id) -# temp_final.update(continued_results) -# -# """根据所有键是否已添加处理技术要求""" -# # 更新原始采购需求字典 -# final_res=combine_and_update_results(modified_data, temp_final) -# ffinal_res=all_postprocess(final_res) -# ffinal_res["货物列表"] = good_list -# # 输出最终的 JSON 字符串 -# return {"采购需求":ffinal_res} + key_paths, grouped_paths, good_list, data_copy= generate_key_paths(processed_data) # 提取需要采购的货物清单 key_list:交通监控视频子系统.高清视频抓拍像机 ... grouped_paths是同一系统下同时有'交换机-1'和'交换机-2',提取'交换机' ,输出eg:{'交通标志.标志牌铝板', '交通信号灯.交换机'} + modified_data=rename_keys(data_copy) + user_query_template = """请根据货物标中采购要求部分的内容,告诉我\"{}\"的技术参数或采购要求是什么。请以 JSON 格式返回结果,键名为\"{}\",键值为一个列表,列表中包含若干描述\"{}\"的技术参数或采购要求或功能说明的字符串,请按原文内容回答,保留三角▲、五角★和序号,不可擅自增删内容,尤其是不可擅自添加序号。 +**重要限制**: +- **仅提取技术参数或采购要求,不包括任何商务要求**。商务要求通常涉及供应商资格、报价条款、交货时间、质保等内容,是整体的要求;而技术参数或采购要求则具体描述产品的技术规格、功能、性能指标等。 +- **商务要求的关键词示例**(仅供参考,不限于此):报价、交货、合同、资质、认证、服务、保修期等。如果内容包含上述关键词,请仔细甄别是否属于商务要求。 + +要求与指南: +1. 你的键值应该全面,不要遗漏。 + -a.若技术参数或采购要求在表格中,那么单元格内的内容基本都要涵盖 + -对于单元格内以序号分隔的各条参数要求,应逐条提取,并分别作为键值中的字符串列表项。 + -对于无序号标明且在同一单元格内的参数要求或功能说明,也要根据语义分别添加进键值中。 + -b.若技术参数或采购要求在正文部分,应准确定位到与目标货物(设备、系统、功能模块)相关的内容,将其后的技术参数或采购要求或功能说明完整提取,逐一添加到键值的字符串列表中,不得擅自添加或修改序号。 +2. 如果存在嵌套结构,且原文为Markdown 的表格语法,如'摄像机|有效像素|≥900W像素', 请不要返回该Markdown语法,而是使用冒号':'将相关信息拼接在一起,生成一条完整且清晰的技术参数(或采购要求)描述,作为列表中的一个字符串。如"摄像机:有效像素:≥900W像素"。 +3. 字符串中的内容为具体的技术参数要求或采购要求,请不要返回诸如'(1)高清录像功能'这种标题性质且不能体现要求的内容。 +4. 如果该货物没有相关采购要求或技术参数要求,键值应为空列表[]。 + +### 示例输出1如下: +{{ + "摄像机控制键盘": [ + "1、▲支持串行 RS232/RS422 和 IP 混合控制,允许在一个控制器上使用 RS232/RS422/IP 控制单个系统中的摄像机;", + "2、支持 2 组 RS422 串口 VISCA 协议菊花链控制 2x7 台摄像机。", + "★能够自动对焦,提供检测报告" + ] +}} + +### 示例输出2如下(包含嵌套结构): +{{ + "摄像机": [ + "摄像机:有效像素:≥900W像素", + "摄像机:最低照度:彩色≤0.001lx", + "协议:routes 接口开放:具备;▲支持标准 ONVIF 协议与第三方厂家设备进行互联;支持 GB/T28181;应提供 SDK" + ] +}} + +{} +""" + user_query_template_two="""请根据货物标中采购要求部分的内容,告诉我\"{}\"的技术参数或采购要求是什么。由于该货物存在 {} 种不同的采购要求或技术参数,请逐一列出,并以 JSON 格式返回结果。请以'货物名-编号'区分多种型号,编号为从 1 开始的自然数,依次递增,即第一个键名为\"{}-1\";键值为一个列表,列表中包含若干描述\"{}\"的技术参数或采购要求或功能说明的字符串,请按原文内容回答,保留三角▲、五角★和序号(若有),不可擅自增删内容,尤其是不可擅自添加序号。 + +要求与指南: +1. 你的键值应该全面,不要遗漏。 + -a.若技术参数或采购要求在表格中,那么单元格内的内容基本都要涵盖 + -对于单元格内以序号分隔的各条参数要求,应逐条提取,并分别作为键值中的字符串列表项。 + -对于无序号标明且在同一单元格内的参数要求或功能说明,也要根据语义分别添加进键值中。 + -b.若技术参数或采购要求在正文部分,应准确定位到与目标货物(设备、系统、功能模块)相关的内容,将其后的技术参数或采购要求或功能说明完整提取,逐一添加到键值的字符串列表中,不得擅自添加或修改序号。 +2. 如果存在嵌套结构,且原文为Markdown 的表格语法,如'摄像机|有效像素|≥900W像素', 请不要返回该Markdown语法,而是使用冒号':'将相关信息拼接在一起,生成一条完整且清晰的技术参数(或采购要求)描述,作为列表中的一个字符串。如"摄像机:有效像素:≥900W像素"。 +3. 字符串中的内容为具体的技术参数要求或采购要求,请不要返回诸如'(1)高清录像功能'这种标题性质且不能体现要求的内容。 +4. 如果该货物没有相关采购要求或技术参数要求,键值应为空列表[]。 + +### 示例输出1如下: +{{ + "交换机-1": [ + "★1、支持固化千兆电口≥8 个,固化千兆光口≥2 个,桌面型设备;", + "2、支持静态链路聚合" + ], + "交换机-2": [ + "1、交换容量≥52Gbps,包转发率≥38.69Mpps,", + "2、提供国家强制性产品认证证书及测试报告(3C)", + "★能实现信号控制独立传输" + ] +}} + +### 示例输出2如下(包含嵌套结构): +{{ + "摄像机-1": [ + "摄像机:有效像素:≥900W像素", + "摄像机:最低照度:彩色≤0.001lx", + "协议:routes 接口开放:具备;▲支持标准 ONVIF 协议与第三方厂家设备进行互联;支持 GB/T28181;应提供 SDK" + ], + "摄像机-2": [ + "支持夜视", "支持云存储" + ] +}} + +{} + """ + queries = [] + for key in key_paths: + # 将键中的 '.' 替换为 '下的' + modified_key = key.replace('.', '下的') + # 使用修改后的键填充第一个占位符,原始键填充第二个占位符 + if model_type: + new_query = user_query_template.format(modified_key, key, modified_key,f"文件内容:{full_text}") #转豆包后取消注释 + else: + new_query = user_query_template.format(modified_key, key, modified_key,"") + queries.append(new_query) + + # 处理 grouped_paths 中的项,应用 user_query_template_two + for grouped_dict in grouped_paths: + for grouped_key, grouped_key_cnt in grouped_dict.items(): + # 将键中的 '.' 替换为 '下的' + modified_grouped_key = grouped_key.replace('.', '下的') + if model_type: + new_query = user_query_template_two.format(modified_grouped_key, grouped_key_cnt, grouped_key, + modified_grouped_key, f"文件内容:{full_text}") + else: + new_query = user_query_template_two.format(modified_grouped_key, grouped_key_cnt, grouped_key, + modified_grouped_key, "") + queries.append(new_query) + if model_type: + results = multi_threading(queries, "", "", 3,True) # 豆包 + else: + results = multi_threading(queries, "", file_id, 2,True) # 豆包 + temp_final={} + if not results: + print("errror!未获得大模型的回答!") + else: + # 第一步:收集需要调用 `continue_answer` 的问题和解析结果 + questions_to_continue = [] # 存储需要调用 continue_answer 的 (question, parsed) + max_tokens=3900 if model_type==1 else 5900 + for question, response in results: + message=response[0] + parsed = clean_json_string(message) + total_tokens=response[1] + if not parsed and total_tokens>max_tokens: + questions_to_continue.append((question, message)) + else: + temp_final.update(parsed) + # 第二步:多线程处理需要调用 `continue_answer` 的问题 + if questions_to_continue: + continued_results = process_continue_answers(questions_to_continue, model_type, file_id) + temp_final.update(continued_results) + + """根据所有键是否已添加处理技术要求""" + # 更新原始采购需求字典 + final_res=combine_and_update_results(modified_data, temp_final) + ffinal_res=all_postprocess(final_res) + ffinal_res["货物列表"] = good_list + # 输出最终的 JSON 字符串 + return {"采购需求":ffinal_res} def test_all_files_in_folder(input_folder, output_folder): # 确保输出文件夹存在 @@ -664,7 +663,7 @@ if __name__ == "__main__": invalid_path=r"D:\flask_project\flask_app\static\output\output1\000aac0d-4aa4-4bc3-a9f9-76ff82ec2470\invalid_added.docx" # file_id=upload_file(truncate_file) # processed_filepath = convert_file_to_markdown(truncate_file) - processed_filepath=r"D:\flask_project\flask_app\static\output\output1\000aac0d-4aa4-4bc3-a9f9-76ff82ec2470\extract1.txt" + processed_filepath=r"C:\Users\Administrator\Desktop\文件解析问题\文件解析问题\baada43d-24f6-459d-8a81-219d130f20da\extract1.txt" res=get_technical_requirements(invalid_path,processed_filepath) json_string = json.dumps(res, ensure_ascii=False, indent=4) print(json_string) diff --git a/flask_app/货物标/提取json货物标版.py b/flask_app/货物标/提取json货物标版.py index 46a2458..00197d4 100644 --- a/flask_app/货物标/提取json货物标版.py +++ b/flask_app/货物标/提取json货物标版.py @@ -177,13 +177,12 @@ def process_folder(input_folder, output_folder): #TODO:招标文件111_tobidders_notice_part2.pdf 陕西省公安厅交通警察总队高速公路交通安全智能感知巡查系统项目(1)_tobidders_notice_part2.pdf 唐山市公安交通警察支队机动车查验机构视频存储回放系统竞争性谈判-招标文件正文(1)_tobidders_notice_part1.pdf #TODO:2024-陕西-陕西省某单位2024年执勤化妆服采购项目.pdf -#TODO: .不予受理的情形 ,‘.后面必须跟中文或者空格’ if __name__ == "__main__": - file_path = r'C:\Users\Administrator\Desktop\fsdownload\91399aa4-1ee8-447d-a05b-03cd8d15ced5\tmp\_2020年广水市中小学教师办公电脑系统及多媒体“班班通”设备采购安装项目_加水印3333_tobidders_notice_part2.pdf' + file_path = r'C:\Users\Administrator\Desktop\文件解析问题\文件解析问题\3496bb36-c476-42f0-947e-3e39c295f8bc\ztbfile_tobidders_notice_part2.pdf' # file_path=r'C:\Users\Administrator\Desktop\招标文件-采购类\all\2024-陕西-陕西省某单位2024年执勤化妆服采购项目_tobidders_notice_part2.pdf' # file_path=r'C:\Users\Administrator\Desktop\货物标\output4\磋商文件_tobidders_notice_part2.pdf' # file_path = 'C:\\Users\\Administrator\\Desktop\\货物标\\output4\\6.2定版视频会议磋商文件_tobidders_notice_part2.pdf' - output_folder = r'C:\Users\Administrator\Desktop\fsdownload\91399aa4-1ee8-447d-a05b-03cd8d15ced5\tmp' + output_folder = r'C:\Users\Administrator\Desktop\文件解析问题\文件解析问题\3496bb36-c476-42f0-947e-3e39c295f8bc\tmp' try: output_path = convert_clause_to_json(file_path,output_folder,1) print(f"Final JSON result saved to: {output_path}")