Files
wehub-resource-sync 1443d3fdf9
Ruff Format Check / Ruff Format & Lint (push) Failing after 7m39s
Deploy VitePress site to Pages / build (push) Failing after 9m11s
Deploy VitePress site to Pages / Deploy (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:32:26 +08:00

151 lines
4.9 KiB
Python

"""图谱构建相关的纯函数工具集。
将数据变换逻辑从 MilvusGraphService 中抽离,
使 service 类专注于 I/O 和业务编排。
"""
from __future__ import annotations
from typing import Any
from yuxi.utils import hashstr
def normalize_entity_name(text: str) -> str:
"""统一实体名称:去首尾空白、小写化、压缩内部连续空白。"""
return " ".join(text.strip().lower().split())
def compute_entity_id(kb_id: str, normalized_name: str, label: str) -> str:
return hashstr(f"{kb_id}:{normalized_name}:{label}", length=32)
def compute_triple_id(
kb_id: str,
source_normalized_name: str,
source_label: str,
relation_type: str,
target_normalized_name: str,
target_label: str,
) -> str:
return hashstr(
f"{kb_id}:{source_normalized_name}:{source_label}:{relation_type}:{target_normalized_name}:{target_label}",
length=32,
)
def graph_entity_collection_name(kb_id: str) -> str:
return f"{kb_id}_entity"
def graph_triple_collection_name(kb_id: str) -> str:
return f"{kb_id}_triple"
def build_graph_payload(normalized_result: dict[str, Any]) -> dict[str, Any]:
"""将抽取器产出的标准化结果转换为 Neo4j 写入所需的图结构。
返回的 entities 已完成去重合并:同名同 label 的实体只保留一份,
属性(attributes)取并集。
"""
entities: list[dict[str, Any]] = []
entity_by_key: dict[tuple[str, str], dict[str, Any]] = {}
def add_entity(entity: dict[str, Any]) -> str:
key = (normalize_entity_name(entity["text"]), entity.get("label") or "Entity")
existing = entity_by_key.get(key)
if existing is not None:
known_attributes = {(attr["text"], attr["label"]) for attr in existing.get("attributes") or []}
for attribute in entity.get("attributes") or []:
attribute_key = (attribute["text"], attribute["label"])
if attribute_key not in known_attributes:
existing.setdefault("attributes", []).append(attribute)
known_attributes.add(attribute_key)
return existing["id"]
graph_entity = {
"id": f"e{len(entities) + 1}",
"text": entity["text"],
"label": entity.get("label") or "Entity",
"attributes": list(entity.get("attributes") or []),
}
entities.append(graph_entity)
entity_by_key[key] = graph_entity
return graph_entity["id"]
for entity in normalized_result["entities"]:
add_entity(entity)
relations = []
for relation in normalized_result["relations"]:
relations.append(
{
"source": add_entity(relation["source"]),
"target": add_entity(relation["target"]),
"text": relation["text"],
"label": relation.get("label") or "RELATED_TO",
}
)
return {"entities": entities, "relations": relations, "metadata": normalized_result["metadata"]}
# ─── Cypher 模板 ────────────────────────────────────────────────
# 将大段 Cypher 字符串集中管理,提升 write_chunk_graph 的可读性。
def cypher_merge_chunk(db_label: str) -> str:
"""MERGE Chunk 节点并写入元数据。"""
return f"""
MERGE (c:Chunk:MilvusKB:`{db_label}` {{chunk_id: $chunk_id}})
SET c.file_id = $file_id,
c.kb_id = $kb_id,
c.chunk_index = $chunk_index,
c.content_preview = $content_preview,
c.start_char_pos = $start_char_pos,
c.end_char_pos = $end_char_pos
"""
def cypher_merge_entity_mention(db_label: str) -> str:
"""MERGE Entity 节点并创建 Chunk → Entity 的 MENTIONS 关系。"""
return f"""
MATCH (c:Chunk:MilvusKB:`{db_label}` {{chunk_id: $chunk_id}})
MERGE (e:Entity:MilvusKB:`{db_label}` {{
kb_id: $kb_id,
normalized_name: $normalized_name,
label: $entity_label
}})
SET e.entity_id = $entity_id,
e.name = $name,
e.attributes = $attributes
MERGE (c)-[m:MENTIONS {{chunk_id: $chunk_id, file_id: $file_id, kb_id: $kb_id}}]->(e)
"""
def cypher_merge_relation(db_label: str) -> str:
"""MERGE 两个 Entity 之间的 RELATION 边。"""
return f"""
MATCH (source:Entity:MilvusKB:`{db_label}` {{
kb_id: $kb_id,
normalized_name: $source_name,
label: $source_label
}})
MATCH (target:Entity:MilvusKB:`{db_label}` {{
kb_id: $kb_id,
normalized_name: $target_name,
label: $target_label
}})
MERGE (source)-[r:RELATION {{
kb_id: $kb_id,
chunk_id: $chunk_id,
source_name: $source_name,
target_name: $target_name,
type: $relation_type
}}]->(target)
SET r.triple_id = $triple_id,
r.text = $text,
r.file_id = $file_id,
r.extractor_type = $extractor_type
"""