NexusCoder / nexus /skills /knowledge_graph.py
AdminReal's picture
Import NexusCoder from github.com/mhieuhonda/NexusCoder
eca5751 verified
Raw History Blame Contribute Delete
8.12 kB
"""Knowledge Graph Skill - Neo4j Cypher schema + queries + RDF mapping.
Sinh đồ thị tri thức trong Neo4j: schema constraints, indexed labels/relationships,
ingestion queries (MERGE), traversal queries (1-hop / multi-hop / shortest path),
page-rank style analytics, và mapping sang RDF (n10s / neosemantics).
Author: Hieu Louis (2026)
"""
from __future__ import annotations
from typing import Dict, List
from .base import Skill, SkillCategory, SkillContext, SkillPriority, SkillResult
NEO4J_SCHEMA = """// Neo4j schema for a knowledge graph / Schema đồ thị tri thức
// ============================================================
// Constraints & indexes (run once)
CREATE CONSTRAINT person_id IF NOT EXISTS
FOR (n:Person) REQUIRE n.id IS UNIQUE;
CREATE CONSTRAINT org_id IF NOT EXISTS
FOR (n:Organization) REQUIRE n.id IS UNIQUE;
CREATE CONSTRAINT concept_id IF NOT EXISTS
FOR (n:Concept) REQUIRE n.id IS UNIQUE;
CREATE CONSTRAINT article_id IF NOT EXISTS
FOR (n:Article) REQUIRE n.doi IS UNIQUE;
CREATE INDEX person_name IF NOT EXISTS FOR (n:Person) ON (n.name);
CREATE INDEX org_name IF NOT EXISTS FOR (n:Organization) ON (n.name);
CREATE INDEX article_year IF NOT EXISTS FOR (n:Article) ON (n.year);
// Full-text index for fuzzy search / Index full-text để tìm mờ
CREATE FULLTEXT INDEX entity_fulltext IF NOT EXISTS
FOR (n:Person|Organization|Concept) ON EACH [n.name, n.description];
// Labels: Person, Organization, Concept, Article, Location, Event
// Relationships:
// (Person)-[:WORKS_AT]->(Organization)
// (Person)-[:AUTHORED]->(Article)
// (Article)-[:MENTIONS]->(Concept)
// (Person)-[:KNOWS {since: date}]->(Person)
// (Organization)-[:LOCATED_IN]->(Location)
// (Concept)-[:SUBCLASS_OF]->(Concept)
"""
INGEST_QUERIES = """// Ingest with MERGE (idempotent) / Nhập liệu bằng MERGE
// ---- People & organizations ----
UNWIND $people AS p
MERGE (person:Person {id: p.id})
SET person.name = p.name, person.bio = p.bio, person.updated_at = datetime()
MERGE (org:Organization {id: p.org_id})
SET org.name = p.org_name
MERGE (person)-[:WORKS_AT]->(org);
// ---- Articles & concepts ----
UNWIND $articles AS a
MERGE (art:Article {doi: a.doi})
SET art.title = a.title, art.year = a.year, art.abstract = a.abstract
WITH art, a
UNWIND a.author_ids AS aid
MATCH (au:Person {id: aid})
MERGE (au)-[:AUTHORED]->(art)
WITH art, a
UNWIND a.concepts AS c
MERGE (con:Concept {id: c.id}) SET con.name = c.name
MERGE (art)-[:MENTIONS]->(con);
// ---- Concept taxonomy (subclass-of) ----
UNWIND $edges AS e
MATCH (c1:Concept {id: e.from}), (c2:Concept {id: e.to})
MERGE (c1)-[:SUBCLASS_OF]->(c2);
"""
TRAVERSAL_QUERIES = """// Common traversal & analytics queries / Truy vấn phổ biến
// 1. Co-authors (1-hop) / Đồng tác giả
MATCH (p:Person {id: $person_id})-[:AUTHORED]->(:Article)<-[:AUTHORED]-(co)
RETURN co.name AS coauthor, count(*) AS joint_papers
ORDER BY joint_papers DESC LIMIT 10;
// 2. Shortest path between two people / Đường đi ngắn nhất
MATCH path = shortestPath(
(p1:Person {id: $from})-[:KNOWS|AUTHORED*..6]-(p2:Person {id: $to})
)
RETURN [n IN nodes(path) | coalesce(n.name, n.title)] AS hops;
// 3. Top influential concepts (degree centrality) / Khái niệm quan trọng
MATCH (c:Concept)<-[:MENTIONS]-(:Article)
RETURN c.name AS concept, count(*) AS mentions
ORDER BY mentions DESC LIMIT 20;
// 4. PageRank via GDS (Graph Data Science library)
CALL gds.pageRank.stream('conceptGraph')
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS concept, score
ORDER BY score DESC LIMIT 25;
// 5. Community detection (Louvain) / Phát hiện cộng đồng
CALL gds.louvain.write('entityGraph', { writeProperty: 'community' })
YIELD communityCount, modularity;
// 6. Find experts on a topic (with hop limit) / Tìm chuyên gia
MATCH (c:Concept {name: $topic})<-[:MENTIONS]-(a:Article)<-[:AUTHORED]-(p:Person)
WITH p, count(a) AS papers, collect(a.year) AS years
RETURN p.name AS expert, papers, years
ORDER BY papers DESC LIMIT 10;
// 7. Org collaboration network / Mạng hợp tác tổ chức
MATCH (o1:Organization)<-[:WORKS_AT]-(p1)-[:AUTHORED]->(a)<-[:AUTHORED]-(p2)-[:WORKS_AT]->(o2)
WHERE id(o1) < id(o2)
RETURN o1.name, o2.name, count(DISTINCT a) AS joint_papers
ORDER BY joint_papers DESC LIMIT 10;
"""
RDF_MAPPING = """
RDF Export / Mapping (neosemantics / n10s)
==========================================
1. Enable RDF in Neo4j:
CREATE CONSTRAINT n10s_unique_uri IF NOT EXISTS
FOR (r:Resource) REQUIRE r.uri IS UNIQUE;
CALL n10s.graphconfig.init({handleVocabUris: "MAP"});
CALL n10s.nsprefixes.add("schema", "https://schema.org/");
CALL n10s.nsprefixes.add("ex", "https://example.org/kg/");
2. Export as Turtle:
:Person_123 a schema:Person ;
schema:name "Hieu Louis" ;
schema:worksFor :Org_42 .
:Org_42 a schema:Organization ;
schema:name "ACME" .
:Article_doi a schema:ScholarlyArticle ;
schema:author :Person_123 ;
schema:about :Concept_ML .
3. SPARQL federated query (on exported RDF):
SELECT ?expert ?paper WHERE {
?paper schema:about/schema:name "Machine Learning" ;
schema:author ?expert .
}
"""
class KnowledgeGraphSkill(Skill):
"""Sinh Neo4j Cypher schema, queries, và RDF mapping cho knowledge graph."""
category = SkillCategory.DATA
priority = SkillPriority.LOW
keywords: List[str] = [
"knowledge graph", "neo4j", "cypher", "graph database",
"entity relation", "entity-relationship", "rdf", "owl",
"sparql", "n10s", "neosemantics", "kg",
]
examples = [
"Tạo knowledge graph schema trên Neo4j",
"Sinh Cypher queries cho co-author network",
"Map Neo4j entities to RDF / OWL",
]
@property
def name(self) -> str:
return "knowledge_graph"
@property
def description(self) -> str:
return (
"Sinh Neo4j Cypher schema (constraints + indexes), ingestion (MERGE), "
"traversal + analytics queries (PageRank, Louvain), và RDF mapping "
"via neosemantics."
)
def can_handle(self, prompt: str, context: SkillContext = None) -> float:
prompt_lower = prompt.lower()
score = 0.0
for kw in self.keywords:
if kw in prompt_lower:
score += 0.16
return min(1.0, score)
def execute(self, context: SkillContext) -> SkillResult:
artifacts: List[Dict[str, str]] = [
{"name": "schema.cypher", "language": "cypher", "content": NEO4J_SCHEMA},
{"name": "ingest.cypher", "language": "cypher", "content": INGEST_QUERIES},
{"name": "queries.cypher", "language": "cypher", "content": TRAVERSAL_QUERIES},
{"name": "RDF_MAPPING.md", "language": "markdown", "content": RDF_MAPPING},
]
return SkillResult(
success=True,
output=(
"[knowledge_graph] Generated Neo4j Cypher: schema (constraints+indexes), "
"ingestion (MERGE idempotent), 7 traversal/analytics queries "
"(PageRank, Louvain, shortest path) + RDF export guide."
),
artifacts=artifacts,
suggestions=[
"Install APOC + Graph Data Science (GDS) plugin for PageRank/Louvain",
"Use EXPLAIN / PROFILE to verify query plans use indexes",
"Batch ingest with `:auto` + periodic.commit for >10k nodes",
"Add schema validation (SHACL) when exporting to RDF",
"Consider Stardog / GraphDB if SPARQL reasoning (OWL) is required",
],
metadata={
"skill": self.name,
"labels": ["Person", "Organization", "Concept", "Article", "Location", "Event"],
"relationships": ["WORKS_AT", "AUTHORED", "MENTIONS", "KNOWS", "SUBCLASS_OF"],
"algorithms": ["PageRank", "Louvain", "shortestPath", "degree centrality"],
"version": self.version,
"author": self.author,
},
)