From ba6e089f97842c5b77f6f19a9ed9ef91bb33974f Mon Sep 17 00:00:00 2001 From: weiweiw <14335254+weiweiw22@user.noreply.gitee.com> Date: Mon, 13 Jan 2025 09:55:22 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0docx=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chatchat_data/model_settings.yaml | 2 +- .../file_rag/document_loaders/__init__.py | 2 + .../file_rag/document_loaders/customercore.py | 61 +++++++++++++ .../file_rag/document_loaders/mywordload.py | 90 +++++++++++++++++++ ...ustomer_chinese_recursive_text_splitter.py | 76 +++++++++++----- .../chatchat/server/knowledge_base/utils.py | 12 ++- 6 files changed, 216 insertions(+), 27 deletions(-) create mode 100644 libs/chatchat-server/chatchat/server/file_rag/document_loaders/customercore.py create mode 100644 libs/chatchat-server/chatchat/server/file_rag/document_loaders/mywordload.py diff --git a/chatchat_data/model_settings.yaml b/chatchat_data/model_settings.yaml index ccdbcbd..edadeb0 100644 --- a/chatchat_data/model_settings.yaml +++ b/chatchat_data/model_settings.yaml @@ -121,7 +121,7 @@ MODEL_PLATFORMS: embed_models: [] text2image_models: [] image2text_models: [] - rerank_models: [] + rerank_models: [bge-reranker-large] speech2text_models: [] text2speech_models: [] - platform_name: ollama diff --git a/libs/chatchat-server/chatchat/server/file_rag/document_loaders/__init__.py b/libs/chatchat-server/chatchat/server/file_rag/document_loaders/__init__.py index eb99c82..7f58f49 100644 --- a/libs/chatchat-server/chatchat/server/file_rag/document_loaders/__init__.py +++ b/libs/chatchat-server/chatchat/server/file_rag/document_loaders/__init__.py @@ -2,3 +2,5 @@ from .mydocloader import RapidOCRDocLoader from .myimgloader import RapidOCRLoader from .mypdfloader import RapidOCRPDFLoader from .mypptloader import RapidOCRPPTLoader +from .mywordload import RapidWordLoader +from .customercore import custom_group_broken_paragraphs diff --git a/libs/chatchat-server/chatchat/server/file_rag/document_loaders/customercore.py b/libs/chatchat-server/chatchat/server/file_rag/document_loaders/customercore.py new file mode 100644 index 0000000..de5bdba --- /dev/null +++ b/libs/chatchat-server/chatchat/server/file_rag/document_loaders/customercore.py @@ -0,0 +1,61 @@ +import re + +from unstructured.nlp.patterns import ( + DOUBLE_PARAGRAPH_PATTERN_RE, + E_BULLET_PATTERN, + PARAGRAPH_PATTERN, + PARAGRAPH_PATTERN_RE, + UNICODE_BULLETS_RE, +) +from unstructured.cleaners.core import group_bullet_paragraph + +def custom_group_broken_paragraphs( + text: str, + line_split: re.Pattern = PARAGRAPH_PATTERN_RE, + paragraph_split: re.Pattern = DOUBLE_PARAGRAPH_PATTERN_RE, +) -> str: + """Groups paragraphs that have line breaks for visual/formatting purposes. + For example: + + '''The big red fox + is walking down the lane. + + At the end of the lane + the fox met a bear.''' + + Gets converted to + + '''The big red fox is walking down the lane. + At the end of the land the fox met a bear.''' + """ + paragraphs = paragraph_split.split(text) + clean_paragraphs = [] + for paragraph in paragraphs: + if not paragraph.strip(): + continue + # NOTE(robinson) - This block is to account for lines like the following that shouldn't be + # grouped together, but aren't separated by a double line break. + # Apache License + # Version 2.0, January 2004 + # http://www.apache.org/licenses/ + + #para_split = line_split.split(paragraph) + + # pytesseract converts some bullet points to standalone "e" characters + if UNICODE_BULLETS_RE.match(paragraph.strip()) or E_BULLET_PATTERN.match(paragraph.strip()): + tempList = group_bullet_paragraph(paragraph) + clean_paragraphs.extend(tempList) + #print(f"new 11111:{tempList}") + else: + tempList = re.sub(PARAGRAPH_PATTERN, " ", paragraph) + clean_paragraphs.append(tempList) + #print(f"new 333333:{tempList}") + + return "\n\n".join(clean_paragraphs) + + +# str1 = "手工分段**绝缘装置(10) 工作斗在额定载荷下起升至最大平台高度,制动后15 min, 工作斗下沉量应不超过该工况最大 平台高度的0.3%。" +# str2 = "手工分段**操控系统(12) 电气系统的要求如下:" +# custom_group_broken_paragraphs(str1) +# custom_group_broken_paragraphs(str2) + diff --git a/libs/chatchat-server/chatchat/server/file_rag/document_loaders/mywordload.py b/libs/chatchat-server/chatchat/server/file_rag/document_loaders/mywordload.py new file mode 100644 index 0000000..5fecdfa --- /dev/null +++ b/libs/chatchat-server/chatchat/server/file_rag/document_loaders/mywordload.py @@ -0,0 +1,90 @@ +from typing import List +from langchain.document_loaders.unstructured import UnstructuredFileLoader +from docx import Document as docxDocument +from docx.document import Document as _Document +from docx.table import _Cell +from docx.oxml.text.paragraph import CT_P +from docx.oxml.table import CT_Tbl +#from docx.oxml.table import CT_TblGrid +from docx.table import _Cell, Table +from docx.text.paragraph import Paragraph +from unstructured.partition.text import partition_text +import unstructured.cleaners.core +from .customercore import custom_group_broken_paragraphs + +unstructured.cleaners.core.group_broken_paragraphs = custom_group_broken_paragraphs + +class RapidWordLoader(UnstructuredFileLoader): + def _get_elements(self) -> List: + def iter_block_items(parent): + """ + Yield each paragraph and table child within *parent*, in document order. + Each returned value is an instance of either Table or Paragraph. + """ + #Document + if isinstance(parent, _Document): + parent_elm = parent._element.body + elif isinstance(parent, _Cell): + parent_elm = parent._element + else: + raise ValueError("something's not right") + + for child in parent_elm.iterchildren(): + if isinstance(child, CT_P): + yield Paragraph(child, parent) + elif isinstance(child, CT_Tbl): + yield Table(child, parent) + # elif isinstance(child, CT_TblGrid): + # yield Table(child, parent) + # else: + # print(f"都不属于") + + def read_table(table): + # 获取表格列标题 + headers = [cell.text.strip() for cell in table.rows[0].cells] + # 存储表格数据的字符串 + table_string = "" + + # 遍历表格行 + for row_index, row in enumerate(table.rows[1:], 2): # 从第二行开始遍历,因为第一行是标题 + row_data = [] + + # 遍历行中的单元格 + for cell_index, cell in enumerate(row.cells, 1): + cell_text = cell.text.strip() + row_data.append(f'"{headers[cell_index - 1]}": "{cell_text}"') + + # 将每一行的数据连接为字符串,用逗号分隔 + row_string = ", ".join(row_data) + # 将每一行的字符串添加到总的表格字符串中 + table_string += f"{{{row_string}}}\n" + + return table_string + + def word2text(filepath): + resp = "" + try: + doc = docxDocument(filepath) + for block in iter_block_items(doc): + if isinstance(block,Paragraph): + #print(f"Paragraph:{block.text}") + resp += (block.text + "\n\n") + elif isinstance(block, Table): + resp += read_table(block) + "\n" + except ValueError: + print(f"Error:input invalid parameter") + except Exception as e: + print(f"word2text error:{e}") + return resp + + text = word2text(self.file_path) + listText = partition_text(text=text, **self.unstructured_kwargs) + return listText + +if __name__ == "__main__": + loader = RapidWordLoader(file_path="/Users/wangvivi/Desktop/Work/新技术部/送变电大模型/知识库文档/操佳慧/国网(基建∕3)115-2021\ \ 国家电网有限公司输变电工 程初步设计审批管理办法.docx") + #loader = Docx2txtLoader(file_path="/Users/wangvivi/Desktop/Work/思极GPT/数字化部/设备类all/sb389/10kV带电作业用绝缘斗臂车.docx") + #loader = RapidWordLoader(file_path="/Users/wangvivi/Desktop/MySelf/AI/Test/这是一个测试文档_副本2.docx") + docs = loader.load() + print(docs) + diff --git a/libs/chatchat-server/chatchat/server/file_rag/text_splitter/customer_chinese_recursive_text_splitter.py b/libs/chatchat-server/chatchat/server/file_rag/text_splitter/customer_chinese_recursive_text_splitter.py index 054c746..1d8f188 100644 --- a/libs/chatchat-server/chatchat/server/file_rag/text_splitter/customer_chinese_recursive_text_splitter.py +++ b/libs/chatchat-server/chatchat/server/file_rag/text_splitter/customer_chinese_recursive_text_splitter.py @@ -156,32 +156,60 @@ class CustomerChineseRecursiveTextSplitter(RecursiveCharacterTextSplitter): if __name__ == "__main__": text_splitter = CustomerChineseRecursiveTextSplitter( - keep_separator=True, is_separator_regex=True, chunk_size=50, chunk_overlap=0 + keep_separator=True, is_separator_regex=True, chunk_size=500, chunk_overlap=0 ) - # ls = [ - # """中国对外贸易形势报告(75页)。前 10 个月,一般贸易进出口 19.5 万亿元,增长 25.1%, 比整体进出口增速高出 2.9 个百分点,占进出口总额的 61.7%,较去年同期提升 1.6 个百分点。其中,一般贸易出口 10.6 万亿元,增长 25.3%,占出口总额的 60.9%,提升 1.5 个百分点;进口8.9万亿元,增长24.9%,占进口总额的62.7%, 提升 1.8 个百分点。加工贸易进出口 6.8 万亿元,增长 11.8%, 占进出口总额的 21.5%,减少 2.0 个百分点。其中,出口增 长 10.4%,占出口总额的 24.3%,减少 2.6 个百分点;进口增 长 14.2%,占进口总额的 18.0%,减少 1.2 个百分点。此外, 以保税物流方式进出口 3.96 万亿元,增长 27.9%。其中,出 口 1.47 万亿元,增长 38.9%;进口 2.49 万亿元,增长 22.2%。前三季度,中国服务贸易继续保持快速增长态势。服务 进出口总额 37834.3 亿元,增长 11.6%;其中服务出口 17820.9 亿元,增长 27.3%;进口 20013.4 亿元,增长 0.5%,进口增 速实现了疫情以来的首次转正。服务出口增幅大于进口 26.8 个百分点,带动服务贸易逆差下降 62.9%至 2192.5 亿元。服 务贸易结构持续优化,知识密集型服务进出口 16917.7 亿元, 增长 13.3%,占服务进出口总额的比重达到 44.7%,提升 0.7 个百分点。 二、中国对外贸易发展环境分析和展望 全球疫情起伏反复,经济复苏分化加剧,大宗商品价格 上涨、能源紧缺、运力紧张及发达经济体政策调整外溢等风 险交织叠加。同时也要看到,我国经济长期向好的趋势没有 改变,外贸企业韧性和活力不断增强,新业态新模式加快发 展,创新转型步伐提速。产业链供应链面临挑战。美欧等加快出台制造业回迁计 划,加速产业链供应链本土布局,跨国公司调整产业链供应 链,全球双链面临新一轮重构,区域化、近岸化、本土化、 短链化趋势凸显。疫苗供应不足,制造业“缺芯”、物流受限、 运价高企,全球产业链供应链面临压力。 全球通胀持续高位运行。能源价格上涨加大主要经济体 的通胀压力,增加全球经济复苏的不确定性。世界银行今年 10 月发布《大宗商品市场展望》指出,能源价格在 2021 年 大涨逾 80%,并且仍将在 2022 年小幅上涨。IMF 指出,全 球通胀上行风险加剧,通胀前景存在巨大不确定性。""", - # ] - # # text = """""" - # for inum, text in enumerate(ls): - # print(inum) - # chunks = text_splitter.split_text(text) - # for chunk in chunks: - # print(chunk) +# ls = [""" +# 5 技术要求 +# 5.1 一般要求 +# 5.1.1 智能安全帽应符合GB 2811 中的基本性能要求。 +# 5.1.2 智能模块与安全帽本体之间的连接应牢固可靠,且不得影响安全帽佩戴的稳定性及 正常防护功能。 +# 5.1.3 智能模块的外壳防护等级应符合 GB/T 4208 中 IP54 的要求。 +# 5.1.4 智能模块重量基础型不宜超过300g。 +# 5.1.5 智能模块应能存储不低于8h 的采集回传的位置信息文件。 +# 5.1.6 智能模块应具有低电量报警功能,电量低于20%应能给出清晰的报警提示。 +# 5.1.7 电池应符合GB 31241中的相关要求。支持智能模块持续工作时间不得小于8h。 +# 5.1.8 无线通信应符合 GB 21288 中电磁辐射局部暴露限值的规定。 +# 5.1.9 智能安全帽应配合管理系统使用。管理系统的功能应符合附录B的要求。 +# """] + ls = ["""国家电网有限公司输变电工程初步设计 审批管理办法 +第一章 总 则 +第一条 为加强公司输变电工程初步设计审批管理工作 , 规范工作流程,提高质量和效率,加强工程造价和设计质量控 制,依据《 国家电网公司基建技经管理规定》和有关规章制度, 特制定本办法。 +第二条 本办法规定了输变电工程初步设计审批阶段各有 关单位和部门的工作职责、工作流程、计划管理、评审管理、 批复管理、评价考核、信息反馈等全过程管理内容。 +第三条 本办法适用于公司系统投资 35 千伏(含新建变电 站同期配套 10 千伏送出线路工程)至 750 千伏交流输变电工程 初步设计的审批管理。EPC 等包含设计的总承包模式,工程初步 设计审批管理按照有关法律法规要求和合同约定执行。 +第四条 初步设计审批的总体要求是依据项目核准、可行 性研究方案和投资估算,开展输变电工程建设方案的技术经济 分析和评价,确定安全可靠、技术先进、造价合理、控制精准 的设计方案。 +第五条 初步设计审批应严格遵循“严肃性、精准性、科 学性、规范性”原则。在初步设计评审各环节,统一发展、财 - ls=[""" -5 技术要求 -5.1 一般要求 -5.1.1 智能安全帽应符合GB 2811 中的基本性能要求。 -5.1.2 智能模块与安全帽本体之间的连接应牢固可靠,且不得影响安全帽佩戴的稳定性及 正常防护功能。 -5.1.3 智能模块的外壳防护等级应符合 GB/T 4208 中 IP54 的要求。 -5.1.4 智能模块重量基础型不宜超过300g。 -5.1.5 智能模块应能存储不低于8h 的采集回传的位置信息文件。 -5.1.6 智能模块应具有低电量报警功能,电量低于20%应能给出清晰的报警提示。 -5.1.7 电池应符合GB 31241中的相关要求。支持智能模块持续工作时间不得小于8h。 -5.1.8 无线通信应符合 GB 21288 中电磁辐射局部暴露限值的规定。 -5.1.9 智能安全帽应配合管理系统使用。管理系统的功能应符合附录B的要求。 -"""] - # +务、安监、设备、科技、调控等各专业部门意见,严格评审标 准 ,保证评审深度,确保评审质量。 + + +第二章 职责分工 +第六条 国网基建部管理职责: +( 一)负责公司系统输变电工程初步设计审批工作归口管 理,建立初步设计评审和批复管理体系,制定管理制度; +(二)负责公司系统 750 千伏、500 千伏、330 千伏和 220 千伏规模以上工程及 110(66)千伏地下变电站工程初步设计评 审计划管理; +( 三)负责涉及主网架结构调整的 750 千伏、500 千伏工程 及 330 千伏、220 千伏、110(66)千伏地下变电站工程初步设 计批复; +( 四)负责初步设计评审工作的监督、考核和质量评价工 作; +(五)负责推动初步设计评审工作标准化、信息化管理。 +第七条 省公司(包括省、 自治 区、直辖市电力公司, 以 +下同)基建管理部门管理职责: +( 一)贯彻落实公司初步设计审批工作有关要求,负责所 辖业务范围内输变电工程初步设计审批工作管理; +(二)负责 220 千伏规模以下及 110(66)千伏非地下变电 站、35 千伏工程初步设计评审计划管理; + +( 三)负责 750 千伏改扩建、500 千伏改扩建及 330 千伏、 +220 千伏、110(66)千伏非地下变电站工程初步设计批复。可 根据实际情况自行确定将部分工程批复权限下放至地市公司; +( 四)负责依法依规对评审单位进行监督与评价; +(五)配合公司开展输变电工程初步设计审批。 +第八条 地市公司组织或配合所辖范围内输变电工程初步 设计评审和批复工作。 +第九条 公司各级单位发展、财务、安监、设备、科技、 调控等部门职责: +( 一)发展部门负责审核初步设计执行可研情况,重点对 接入系统方案、变电站主接线型式及总平面布置、建设规模、 线路路径和站址等情况提出专业意见; +(二)设备部门负责对初步设计执行技术标准、主要电气 设备材料选择、安全可靠性保证、反事故措施等开展技术监督 工作,提出专业意见; +( 三)调控部门负责对系统接线方式、停电过渡方案、继 电保护、通信、监控和 自动化等提出专业意见; +( 四)财务、安监、科技等部门负责对工程提出专业意见。 第十条 建设管理单位参与负责建设管理的输变电工程初 +步设计评审工作。 +第十一条 评审单位负责对工程初步设计文件的主要技术 方案和概算投资开展评审工作。 + +第三章 评审计划 +第十二条 为保证公司年度基建计划的顺利完成,工程初 步设计评审工作实施计划管理,在年度预安排指导下,按月度 计划开展工作。 +第十三条 输变电工程初步设计评审采用公司总部和省公 司两级集中管理模式。"""] for inum, temptext in enumerate(ls): print(f"**************分段:{inum}") diff --git a/libs/chatchat-server/chatchat/server/knowledge_base/utils.py b/libs/chatchat-server/chatchat/server/knowledge_base/utils.py index 3d88022..21acee7 100644 --- a/libs/chatchat-server/chatchat/server/knowledge_base/utils.py +++ b/libs/chatchat-server/chatchat/server/knowledge_base/utils.py @@ -104,7 +104,7 @@ LOADER_DICT = { "CSVLoader": [".csv"], # "FilteredCSVLoader": [".csv"], 如果使用自定义分割csv "RapidOCRPDFLoader": [".pdf"], - "RapidOCRDocLoader": [".docx"], + # "RapidOCRDocLoader": [".docx"], "RapidOCRPPTLoader": [ ".ppt", ".pptx", @@ -132,10 +132,12 @@ LOADER_DICT = { "SRTLoader": [".srt"], "TomlLoader": [".toml"], "UnstructuredTSVLoader": [".tsv"], - "UnstructuredWordDocumentLoader": [".docx"], + # "UnstructuredWordDocumentLoader": [".docx"], "UnstructuredXMLLoader": [".xml"], "UnstructuredPowerPointLoader": [".ppt", ".pptx"], "EverNoteLoader": [".enex"], + "UnstructuredWordDocumentLoader":['.doc'], + "RapidWordLoader":['.docx'], } SUPPORTED_EXTS = [ext for sublist in LOADER_DICT.values() for ext in sublist] @@ -178,6 +180,7 @@ def get_loader(loader_name: str, file_path: str, loader_kwargs: Dict = None): "FilteredCSVLoader", "RapidOCRDocLoader", "RapidOCRPPTLoader", + "RapidWordLoader", ]: document_loaders_module = importlib.import_module( "chatchat.server.file_rag.document_loaders" @@ -237,11 +240,13 @@ def make_text_splitter(splitter_name, chunk_size, chunk_overlap): try: # 优先使用用户自定义的text_splitter text_splitter_module = importlib.import_module("chatchat.server.file_rag.text_splitter") TextSplitter = getattr(text_splitter_module, splitter_name) + logger.info(f"****1111splitter_name:{splitter_name}") except: # 否则使用langchain的text_splitter text_splitter_module = importlib.import_module( "langchain.text_splitter" ) TextSplitter = getattr(text_splitter_module, splitter_name) + logger.info(f"****2222splitter_name:{splitter_name}") if ( Settings.kb_settings.text_splitter_dict[splitter_name]["source"] == "tiktoken" @@ -255,6 +260,7 @@ def make_text_splitter(splitter_name, chunk_size, chunk_overlap): chunk_size=chunk_size, chunk_overlap=chunk_overlap, ) + logger.info(f"****333333splitter_name:{splitter_name}") except: text_splitter = TextSplitter.from_tiktoken_encoder( encoding_name=Settings.kb_settings.text_splitter_dict[splitter_name][ @@ -263,6 +269,7 @@ def make_text_splitter(splitter_name, chunk_size, chunk_overlap): chunk_size=chunk_size, chunk_overlap=chunk_overlap, ) + logger.info(f"****44444splitter_name:{splitter_name}") elif ( Settings.kb_settings.text_splitter_dict[splitter_name]["source"] == "huggingface" ): # 从huggingface加载 @@ -302,6 +309,7 @@ def make_text_splitter(splitter_name, chunk_size, chunk_overlap): text_splitter_module = importlib.import_module("langchain.text_splitter") TextSplitter = getattr(text_splitter_module, "RecursiveCharacterTextSplitter") text_splitter = TextSplitter(chunk_size=chunk_size, chunk_overlap=chunk_overlap) + logger.info(f"****55555splitter_name:RecursiveCharacterTextSplitter") # If you use SpacyTextSplitter you can use GPU to do split likes Issue #1287 # text_splitter._tokenizer.max_length = 37016792