diff --git a/lightrag/operate.py b/lightrag/operate.py index 02d9c85e..ceec7b61 100644 --- a/lightrag/operate.py +++ b/lightrag/operate.py @@ -16,6 +16,7 @@ from .utils import ( encode_string_by_tiktoken, is_float_regex, list_of_list_to_csv, + normalize_extracted_info, pack_user_ass_to_openai_messages, split_string_by_multi_markers, truncate_list_by_token_size, @@ -163,6 +164,9 @@ async def _handle_single_entity_extraction( ) return None + # Normalize entity name + entity_name = normalize_extracted_info(entity_name) + # Clean and validate entity type entity_type = clean_str(record_attributes[2]).strip('"') if not entity_type.strip() or entity_type.startswith('("'): @@ -173,6 +177,8 @@ async def _handle_single_entity_extraction( # Clean and validate description entity_description = clean_str(record_attributes[3]).strip('"') + entity_description = normalize_extracted_info(entity_description) + if not entity_description.strip(): logger.warning( f"Entity extraction error: empty description for entity '{entity_name}' of type '{entity_type}'" @@ -198,7 +204,14 @@ async def _handle_single_relationship_extraction( # add this record as edge source = clean_str(record_attributes[1]).strip('"') target = clean_str(record_attributes[2]).strip('"') + + # Normalize source and target entity names + source = normalize_extracted_info(source) + target = normalize_extracted_info(target) + edge_description = clean_str(record_attributes[3]).strip('"') + edge_description = normalize_extracted_info(edge_description) + edge_keywords = clean_str(record_attributes[4]).strip('"') edge_source_id = chunk_key weight = ( diff --git a/lightrag/utils.py b/lightrag/utils.py index fd188498..6b9b07fa 100644 --- a/lightrag/utils.py +++ b/lightrag/utils.py @@ -1006,6 +1006,43 @@ def get_content_summary(content: str, max_length: int = 250) -> str: return content[:max_length] + "..." +def normalize_extracted_info(name: str) -> str: + """Normalize entity/relation names and description with the following rules: + 1. Remove spaces between Chinese characters + 2. Remove spaces between Chinese characters and English letters/numbers + 3. Preserve spaces within English text and numbers + 4. Replace Chinese parentheses with English parentheses + 5. Replace Chinese dash with English dash + + Args: + name: Entity name to normalize + + Returns: + Normalized entity name + """ + # Replace Chinese parentheses with English parentheses + name = name.replace("(", "(").replace(")", ")") + + # Replace Chinese dash with English dash + name = name.replace("—", "-").replace("-", "-") + + # Use regex to remove spaces between Chinese characters + # Regex explanation: + # (?<=[\u4e00-\u9fa5]): Positive lookbehind for Chinese character + # \s+: One or more whitespace characters + # (?=[\u4e00-\u9fa5]): Positive lookahead for Chinese character + name = re.sub(r"(?<=[\u4e00-\u9fa5])\s+(?=[\u4e00-\u9fa5])", "", name) + + # Remove spaces between Chinese and English/numbers + name = re.sub(r"(?<=[\u4e00-\u9fa5])\s+(?=[a-zA-Z0-9])", "", name) + name = re.sub(r"(?<=[a-zA-Z0-9])\s+(?=[\u4e00-\u9fa5])", "", name) + + # Remove English quotation marks from the beginning and end + name = name.strip('"').strip("'") + + return name + + def clean_text(text: str) -> str: """Clean text by removing null bytes (0x00) and whitespace