from openai import OpenAI from typing import List, Dict import json from langchain_text_splitters import MarkdownHeaderTextSplitter, CharacterTextSplitter import requests import json from modelsAPI.model_api import OpenaiAPI def _split_markdown(text, chunk_size=2048, chunk_overlap=50): """ 根据 max_level 动态生成 headers_to_split_on 配置 规则: - max_level=1 → 只按 # 分割 - max_level=2 → 按 # 和 ## 分割 - 以此类推 """ # 动态生成 headers_to_split_on 配置(目前固定为 Header_1) headers_to_split_on = [("#", "Header_1")] # 初始化分割器 splitter = MarkdownHeaderTextSplitter( headers_to_split_on=headers_to_split_on, strip_headers=True ) chunks = splitter.split_text(text) char_splitter = CharacterTextSplitter( chunk_size=chunk_size, chunk_overlap=chunk_overlap, length_function=len, separator="\n\n" # 更合理的分隔符 ) final_chunks = [] start_chars = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "第", "十", "一", "二", "三", "四", "五", "六", "七", "八", "九"," 故障"] new_header = [] # 提取所有 Header_1 并筛选出“有效标题” for chunk in chunks: header_1 = chunk.metadata.get('Header_1', '') if header_1 and header_1[0] in start_chars: new_header.append(header_1) new_header_set = set(new_header) # 构建带标题前缀的 final_chunks for chunk in chunks: header_1 = chunk.metadata.get('Header_1', '') content = chunk.page_content if len(content) <= 20000: chunk_with_header = f"{header_1}\n{content}" if header_1 else content final_chunks.append(chunk_with_header) else: split_texts = char_splitter.split_documents([chunk]) for split_chunk in split_texts: chunk_with_header = f"{header_1}\n{split_chunk.page_content}" if header_1 else split_chunk.page_content final_chunks.append(chunk_with_header) # ===== 重新分组,并记录每个组的 header ===== rechunked = [] rechunked_headers = [] # 新增:每个 rechunked 块对应的 header current = "" current_header = "" # 当前正在构建的块所属的标题 for chunk in final_chunks: is_new_section = False matched_header = "" # 检查是否以某个 new_header 开头 for h in new_header_set: if chunk.startswith(h + "\n"): is_new_section = True matched_header = h break if is_new_section: # 保存上一段 if current: rechunked.append(current) rechunked_headers.append(current_header) # 开启新段 current = chunk current_header = matched_header else: # 合并到当前段 if current: current += "\n" + chunk else: current = chunk current_header = "" # 如果开头无标题,则 header 为空 # 添加最后一段 if current: rechunked.append(current) rechunked_headers.append(current_header) return rechunked, rechunked_headers Ontology = ["舰艇", "系统", "设备", "零部件", "故障", "修理", "维修项目","操作项目","保养"] Relationships = ["包含", "组成", "解决", "用于", "使用","具有"] # 节点类型及其属性定义 NODE_TYPES = { "舰艇": ["名称","型号", "弦号", "设计单位", "总装工厂", "所属部队"], "系统": ["名称", "技术责任单位", "生产单位", "级别"], "设备": ["名称", "型号", "技术责任单位", "生产单位", "技术状态", "生产日期", "启用时间","运行时间","运行条件"], "零部件": ["名称", "型号", "技术责任单位", "生产单位"], "故障": ["故障现象名称", "故障原因", "故障影响", "故障类型", "发生时间", "处置情况", "修复情况"], "修理": ["修理方案","修理人员", "修理时间", "修理工具", "备品备件", "修理类型", "维修项目"], "维修项目": ["名称","维修项目编号", "组成编码", "维修设备名称", "维修间隔期", "维修级别", "页码", "安全防护要求", "安全注意事项","工具","备品备件","消耗材料","人力人员","修理步骤"], "操作项目": ["名称", "操作项目编号", "安全防护", "注意事项","保障设施、保障设备(工具)", "人力人员", "操作步骤"], "保养" : ["保养人员","保养时间","保养工具","保养频率"] } Ontology_DESCRIPTION = { "舰艇": ["代表一艘完整海上作战或保障平台的实体,是所有系统、设备和零部件的最高层级载体。区别于“系统”或“设备”,舰艇具有独立的型号、弦号和部队归属,不可作为其他舰艇的组成部分。"], "系统": ["舰艇中实现某一类功能(如动力、导航、通信)的集成化功能单元,由多个设备协同组成。与“设备”不同,系统本身不直接对应物理装置,而是逻辑/功能层面的集合;与“舰艇”不同,系统不具备独立航行能力或部队归属。"], "设备": ["安装在舰艇上、具有独立型号和运行状态的物理装置,可被启停、监控运行时间,并可能产生故障。区别于“零部件”,设备是可独立更换或维修的功能单元;区别于“系统”,设备是具体硬件而非功能集合。"], "零部件": ["构成设备的不可再拆分的基础物理元件(如轴承、电路板、密封圈),通常无独立运行状态或启用时间。与“设备”关键区别在于:零部件不能单独运行,也不记录运行时间或技术状态变更。"], "故障": ["设备或系统在运行中出现的异常状态或失效事件,必须包含具体现象(如“无法启动”)、原因(如“电源短路”)及影响。区别于“修理”或“保养”,故障是问题本身,而非应对措施;不包含操作步骤或人员信息。"], "修理": ["针对已发生故障所执行的一次性修复行为,包含具体时间、人员、工具、方案和所关联的维修项目。区别于“保养”,修理是响应式、非周期性的;区别于“维修项目”,修理是实际发生的实例,而非标准化规程。"], "维修项目": ["标准化、可重复执行的维修任务规程,规定了某类设备或系统在特定条件下应如何维修,包含编号、步骤、材料、安全要求等。区别于“修理”,维修项目是模板或规范,而非具体事件;不包含实际发生时间或人员。"], "操作项目": ["舰艇正常运行中需执行的标准操作流程(如启动主机、校准雷达),强调操作步骤与安全保障。区别于“维修项目”,操作使用项目用于日常使用而非维护"], "保养" : ["按计划周期性执行的预防性维护活动(如润滑、清洁、检测),目的是延缓性能退化。"] } RELATIONSHIP_TYPES = { "包含": ["舰艇包含系统", "系统包含设备","设备包含零部件","修理包含维修项目"], "解决": ["修理解决故障", "维修项目解决故障"], "使用": ["设备使用操作项目", "设备使用保养","设备使用维修项目"], "具有": ["设备具有故障","零部件具有故障"] } def create_extraction_prompt( text: str, ontology: List[str], Ontology_DESCRIPTION: Dict[str, List[str]], relationships: List[str], node_types: Dict[str, List[str]], relationship_types: Dict[str, List[str]] ) -> str: # 构建已有本体的定义(用于上下文) ontology_def_descriptions = [] for etype in ontology: clean_etype = etype.strip() if clean_etype in Ontology_DESCRIPTION: desc = ";".join(Ontology_DESCRIPTION[clean_etype]) ontology_def_descriptions.append(f"- {clean_etype}:{desc}") else: ontology_def_descriptions.append(f"- {clean_etype}:无明确定义") # 构建关系示例 rel_examples = [] for rtype, examples in relationship_types.items(): for ex in examples[:1]: rel_examples.append(f" - {ex}") prompt = f"""你是一个专业的舰艇维修领域工程师。请基于给出的本体和属性等信息,抽取出实体、关系以及相关属性值。 > ⚠️ 重要说明: > - **本体(Ontology Class)是抽象概念类别**,是对特定领域中概念体系的形式化、结构化规范,用于明确定义该领域内的实体类型(类)。 ### 已有本体类型: {chr(10).join(ontology_def_descriptions)} ### 已有关系类型: - {"、".join(relationships)} 典型用法示例: {chr(10).join(rel_examples)} ### 待分析文本: {text.strip()} ### 你的任务: 1、根据给出的本体、关系以及相应的属性,从文本中抽取出所有可能的实体、关系以及相关属性值。 2、不要随意添加无效属性,抽取的实体属于本体范围内,关系属于关系范围内。、 ### 输出格式: 请输出json格式内容,不要输出其他格式和额外内容 {{ "entities": [ {{ "type": "设备", "name": "1 号主机冷却系统", "properties": {{ "名称": "1 号主机冷却系统", "型号": "", "技术责任单位": "XX 舰艇保障大队", "生产单位": "", "技术状态": "", "生产日期": "", "启用时间": "", "运行时间": "", "运行条件": "" }} }}, {{ "type": "故障", "name": "1 号主机冷却系统温度异常升高", "properties": {{ "故障现象": "1 号主机冷却系统温度异常升高", "故障原因": "根据 A 设备发生的温度异常升高现象,结合相关技术手册与历史故障记录,分析如下: (一)冷却液循环泵可能存在异常,导致流量不足; (二)热交换器内部堵塞或外部散热片污损,影响散热效率; (三)温度传感器或控制系统信号异常,导致误报警。 ", "故障影响": "", "故障类型": "", "发生时间": "2013 年 05 月 7 日 9 时 23 分 37 秒", "处置情况": " ", "修复情况": "", “” }} }} ], "relationships": [ {{ "type": "具有", "from_entity": "1 号主机冷却系统", "to_entity": "1 号主机冷却系统温度异常升高" }} ] }} 注意: - 不要生成一些关系,如果没有就不要推荐 - 如果文本没有符合的关系和实体,不要进行强行抽取 """ return prompt async def extract_text_node_relationship( text: str, ontology: List[str], Ontology_DESCRIPTION: Dict[str, List[str]], relationships: List[str], node_types: Dict[str, List[str]], relationship_types: Dict[str, List[str]] ): final_prompt = create_extraction_prompt( text=text, ontology=ontology, Ontology_DESCRIPTION=Ontology_DESCRIPTION, relationships=relationships, relationship_types=relationship_types, node_types=node_types ) res_str = await OpenaiAPI.openai_chat_aysnc(final_prompt,timeout=180) # 处理 API 调用失败 if res_str is None: print("⚠️ 跳过当前 chunk:API 调用失败") # 尝试解析 JSON try: res = json.loads(res_str) except json.JSONDecodeError as e: print(f"❌ JSON 解析失败: {e}") print(f"原始响应内容: {repr(res_str)}") # 安全访问字段 new_entities = res.get("entities", []) new_relationships = res.get("relationships", []) # 可选:最后汇总输出 # print("\n=== 最终结果 ===") # print("生成文本本体:", new_entities_result) # print("生成文本关系:", new_relationships_result) return new_entities,new_relationships # if __name__ == "__main__": # text = """ # # 1)螺旋桨本体 # 功能与特性:能量转换的核心。它将主机的旋转动能转化为水流的反作用力(推力)。其特性由材料(如镍铝青铜、锰青铜)、桨叶数(3-6叶)、盘面比、螺距比等决定,直接影响效率、空泡和振动性能。 # 相互关系:直接安装在尾轴(螺旋桨轴) 末端,其扭矩由尾轴传递,其推力通过推力轴承传递给船体。 # 2)轴系 # 功能与特性:动力传递的桥梁。包括: # s # 推力轴及推力轴承:承受螺旋桨产生的巨大轴向推力,并将其传递给船体结构,是决定船舶能否前进、后退的关键。 # 中间轴:连接主机输出端与推力轴/尾轴。 # 尾轴(螺旋桨轴):穿过尾轴管,末端安装螺旋桨,工作环境恶劣(浸没海水中),通常需包覆铜套防腐。 # 相互关系:将主机的旋转输出与螺旋桨本体刚性连接,形成一个完整的动力传递链。推力轴承是轴系与船体结构之间的关键受力界面。 # 3)尾轴管与密封系统功能与特性: # 尾轴管:固定于船体尾端,支撑尾轴并形成从船内(机舱)到船外(海水)的通道。 # 密封系统(首尾密封):绝对关键的安全设备。防止海水沿尾轴流入机舱(尾密封),同时防止润滑油泄漏(首尾密封均有此功能)。通常采用唇式密封或面式密封,材料耐磨、耐腐蚀。 # 相互关系:为尾轴提供支撑和润滑(油润滑或水润滑),其密封系统直接保护尾轴并确保机舱安全,与轴系和船体结构紧密集成。 # 4)毂帽与导流罩功能与特性: # 毂帽:安装在螺旋桨毂后端,呈流线型,用于减少桨毂后部产生的涡流阻力,提高效率。 # 导流罩(如桨毂涡流消除器):安装在桨毂前方,改善流入桨叶的水流状态,尤其适用于肥大船型,能提升效率、减少振动。 # 相互关系:直接安装在螺旋桨本体的毂上,是其水动力性能的优化附件。 # 系统相互关系总结: # 主机驱动轴系旋转,轴系末端的尾轴带动螺旋桨本体旋转。螺旋桨产生的推力通过尾轴传递至推力轴承,最终作用在船体上使其运动。尾轴管及密封系统为这一旋转运动提供支撑、润滑和至关重要的水密保障。毂帽和导流罩则作为螺旋桨本体的附件,优化其水动力性能。所有子设备协同工作的核心目标,是确保主机功率被高效、平稳、可靠地转化为推进船舶前进的有效推力。 任何一环失效(如密封泄漏、轴承损坏)都将导致整个推进系统瘫痪。 # """ # content = [] # content.append(text) # extract_text_node_relationship(content,ontology=Ontology,Ontology_DESCRIPTION=Ontology_DESCRIPTION,relationships=Relationships,relationship_types=RELATIONSHIP_TYPES,node_types=NODE_TYPES)