Knowledge Graphs¶
Duration: 90 minutes
Level: Advanced
Module: fcc.knowledge
This tutorial teaches you how to build, query, traverse, and export knowledge graphs that represent the FCC persona ecosystem. You will work with the KnowledgeGraph class, its 9 node types and 9 edge types, the 5 builder functions, 4 serialization formats, the FCCOntology schema, and federated knowledge graphs spanning multiple namespaces.
Prerequisites¶
- Completed beginner/intermediate tutorials
- Familiarity with
PersonaRegistry,CrossReferenceMatrix,WorkflowActionRegistry - Understanding of graph concepts (nodes, edges, adjacency)
Core Concepts¶
The FCC knowledge graph represents the complete persona ecosystem as a typed, directed graph. Every persona, workflow action, category, constitution, and dimension becomes a node, connected by semantically meaningful edges.
Node Types (9)¶
| NodeType | Description |
|---|---|
PERSONA |
An FCC persona (e.g., RC, BC, DE) |
WORKFLOW |
A workflow graph definition |
ACTION |
A workflow action (scaffold, refactor, debug, test, compare, document) |
CONCEPT |
An abstract domain concept |
DELIVERABLE |
A produced output artifact |
CATEGORY |
A persona category (e.g., core, integration, governance) |
DIMENSION |
A persona dimension attribute |
CONSTITUTION |
A governance constitution |
ECOSYSTEM_PROJECT |
An ecosystem project reference |
Edge Types (9)¶
| EdgeType | Description |
|---|---|
INTERACTS_WITH |
Peer interaction between personas |
DEPENDS_ON |
Dependency relationship |
ORCHESTRATES |
Champion orchestrating team members |
CHAMPIONS |
Champion-of relationship |
PRODUCES |
Persona producing an action |
BELONGS_TO |
Persona belonging to a category |
MAPS_TO |
Vocabulary mapping |
GOVERNS |
Constitution governing a persona |
FEDERATION_LINK |
Cross-namespace federation edge |
Building a Knowledge Graph¶
Manual Construction¶
Create nodes and edges directly using the KnowledgeGraph class:
from fcc.knowledge.graph import KnowledgeGraph
from fcc.knowledge.models import (
KnowledgeNode, KnowledgeEdge, NodeType, EdgeType
)
graph = KnowledgeGraph()
# Add persona nodes
rc_node = KnowledgeNode(
node_id="RC",
node_type=NodeType.PERSONA,
label="Research Crafter",
properties={"category": "core", "fcc_phase": "find"},
)
bc_node = KnowledgeNode(
node_id="BC",
node_type=NodeType.PERSONA,
label="Blueprint Crafter",
properties={"category": "core", "fcc_phase": "create"},
)
graph.add_node(rc_node)
graph.add_node(bc_node)
# Add a category node
core_cat = KnowledgeNode(
node_id="cat_core",
node_type=NodeType.CATEGORY,
label="core",
)
graph.add_node(core_cat)
# Add edges
graph.add_edge(KnowledgeEdge(
source_id="RC", target_id="cat_core",
edge_type=EdgeType.BELONGS_TO,
))
graph.add_edge(KnowledgeEdge(
source_id="RC", target_id="BC",
edge_type=EdgeType.INTERACTS_WITH,
properties={"interaction": "handoff", "strength": 0.9},
))
print(f"Nodes: {graph.node_count}, Edges: {graph.edge_count}")
Using Builder Functions¶
The fcc.knowledge.builders module provides 5 builder functions that construct graphs from FCC registries:
from fcc.personas.registry import PersonaRegistry
from fcc.personas.cross_reference import CrossReferenceMatrix
from fcc.workflow.actions import WorkflowActionRegistry
from fcc.governance.constitution_registry import ConstitutionRegistry
from fcc.knowledge.builders import (
build_persona_graph,
build_crossref_graph,
build_workflow_graph,
build_constitution_graph,
build_full_fcc_graph,
)
# Load registries
registry = PersonaRegistry.from_yaml_directory("src/fcc/data/personas")
# Build individual sub-graphs
persona_graph = build_persona_graph(registry)
print(f"Persona graph: {persona_graph.node_count} nodes, "
f"{persona_graph.edge_count} edges")
# Build the complete composite graph from all registries
full_graph = build_full_fcc_graph(
persona_registry=registry,
# crossref_matrix=matrix, # optional
# action_registry=actions, # optional
# constitution_registry=consts, # optional
)
print(f"Full graph: {full_graph.node_count} nodes, "
f"{full_graph.edge_count} edges")
The build_persona_graph function creates:
- A PERSONA node for each persona
- A CATEGORY node for each unique category
- BELONGS_TO edges from persona to category
- ORCHESTRATES edges where the persona orchestrates team members
- CHAMPIONS edges where the persona champions another
Querying and Traversing¶
Node and Edge Queries¶
# Get all persona nodes
personas = full_graph.nodes_by_type(NodeType.PERSONA)
print(f"Persona nodes: {len(personas)}")
# Get all category nodes
categories = full_graph.nodes_by_type(NodeType.CATEGORY)
print(f"Categories: {[c.label for c in categories]}")
# Get all ORCHESTRATES edges
orchestrations = full_graph.edges_by_type(EdgeType.ORCHESTRATES)
print(f"Orchestration edges: {len(orchestrations)}")
# Look up a specific node
rc = full_graph.get_node("RC")
if rc:
print(f"Found: {rc.label} ({rc.node_type.value})")
Neighbor Traversal¶
# Find all neighbors of a persona
neighbors = full_graph.neighbors("RC")
for neighbor in neighbors:
print(f" {neighbor.node_id}: {neighbor.label} ({neighbor.node_type.value})")
Subgraph Extraction¶
Extract a focused subgraph containing only specific nodes:
# Extract just the core personas and their relationships
core_ids = {p.node_id for p in personas if p.properties.get("category") == "core"}
core_ids.add("cat_core") # Include the category node
core_subgraph = full_graph.subgraph(core_ids)
print(f"Core subgraph: {core_subgraph.node_count} nodes, "
f"{core_subgraph.edge_count} edges")
Graph Merging¶
Combine two graphs, with nodes from the second overwriting duplicates:
graph_a = build_persona_graph(registry)
# graph_b = build_crossref_graph(matrix)
# merged = graph_a.merge(graph_b)
# print(f"Merged: {merged.node_count} nodes")
Graph Statistics¶
stats = full_graph.stats()
for key, value in sorted(stats.items()):
print(f" {key}: {value}")
# node_count: 122
# edge_count: 315
# nodes_persona: 102
# nodes_category: 20
# edges_belongs_to: 102
# edges_orchestrates: 15
# ...
Serialization and Export¶
Turtle (OWL/RDF)¶
from fcc.knowledge.serializers import serialize_turtle
turtle_str = serialize_turtle(full_graph)
print(turtle_str[:500])
# Save to file
with open("fcc_knowledge.ttl", "w") as f:
f.write(turtle_str)
JSON-LD¶
from fcc.knowledge.serializers import serialize_jsonld
jsonld_str = serialize_jsonld(full_graph)
print(jsonld_str[:500])
# Save to file
with open("fcc_knowledge.jsonld", "w") as f:
f.write(jsonld_str)
N-Triples¶
from fcc.knowledge.serializers import serialize_ntriples
ntriples_str = serialize_ntriples(full_graph)
print(f"Triples: {ntriples_str.count(chr(10))}")
SKOS (Concept Scheme)¶
The SKOS serializer maps categories to skos:Concept instances with skos:broader/skos:narrower hierarchies:
from fcc.knowledge.serializers import serialize_skos
skos_str = serialize_skos(full_graph)
print(skos_str[:500])
Using an Ontology for Prefix Control¶
Pass an FCCOntology to serializers for custom namespace prefixes and base URIs:
from fcc.knowledge.schema import FCCOntology, default_ontology
ontology = default_ontology()
print(f"Namespace: {ontology.namespace}")
print(f"Version: {ontology.version}")
print(f"Base URI: {ontology.base_uri}")
print(f"Node types defined: {len(ontology.node_type_definitions)}")
print(f"Edge types defined: {len(ontology.edge_type_definitions)}")
# Serialize with ontology-controlled prefixes
turtle_with_ontology = serialize_turtle(full_graph, ontology=ontology)
FCCOntology Schema¶
The FCCOntology is a frozen dataclass defining the knowledge graph schema. It specifies valid node types, edge types, and namespace prefix mappings:
from fcc.knowledge.schema import FCCOntology
# Create a custom ontology
custom_ontology = FCCOntology(
namespace="myproject",
version="1.0.0",
base_uri="https://myproject.example.org/ontology/",
node_type_definitions={
"persona": {"description": "An agent persona"},
"category": {"description": "A persona category"},
},
edge_type_definitions={
"interacts_with": {"description": "Peer interaction"},
"belongs_to": {"description": "Category membership"},
},
prefix_map={
"myproject": "https://myproject.example.org/ontology/",
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
"rdfs": "http://www.w3.org/2000/01/rdf-schema#",
},
)
Federated Knowledge Graphs¶
The FederatedKnowledgeGraph class enables cross-project knowledge graph operations by maintaining a local FCC graph alongside registered remote project graphs:
from fcc.knowledge.federation import FederatedKnowledgeGraph
from fcc.knowledge.models import EdgeType
# Create a federated graph
fkg = FederatedKnowledgeGraph(local_namespace="fcc")
# Register the local FCC graph
fkg.register_local(full_graph)
# Create and register a remote project graph
remote_graph = KnowledgeGraph()
remote_graph.add_node(KnowledgeNode(
node_id="ext_persona_1",
node_type=NodeType.PERSONA,
label="External Analyst",
namespace="partner_project",
))
fkg.register_remote("partner_project", remote_graph)
# Add cross-namespace edges
fkg.add_cross_edge(
source_namespace="fcc",
source_id="RC",
target_namespace="partner_project",
target_id="ext_persona_1",
edge_type=EdgeType.FEDERATION_LINK,
)
# Check registered namespaces
print(f"Namespaces: {fkg.namespaces()}")
# ["fcc", "partner_project"]
# Merge everything into a single graph
merged = fkg.merge_federated()
print(f"Merged: {merged.node_count} nodes, {merged.edge_count} edges")
# Resolve entities across namespaces
resolved = fkg.resolve_entity("RC", "fcc", "partner_project")
if resolved:
print(f"Resolved: {resolved.label}")
# Get per-namespace statistics
stats = fkg.stats()
print(f"FCC nodes: {stats.get('fcc_nodes', 0)}")
print(f"Partner nodes: {stats.get('partner_project_nodes', 0)}")
print(f"Cross edges: {stats['cross_edges']}")
Serialization and Persistence¶
Graphs can be serialized to and from plain dictionaries for JSON persistence:
import json
# Serialize to dict
data = full_graph.to_dict()
with open("graph.json", "w") as f:
json.dump(data, f, indent=2)
# Deserialize from dict
with open("graph.json") as f:
loaded_data = json.load(f)
restored_graph = KnowledgeGraph.from_dict(loaded_data)
print(f"Restored: {restored_graph.node_count} nodes")
Summary¶
In this tutorial you learned how to:
- Create knowledge graphs with 9 node types and 9 edge types
- Use 5 builder functions to construct graphs from FCC registries
- Query nodes and edges by type, traverse neighbors, extract subgraphs
- Export to OWL/RDF (Turtle), JSON-LD, N-Triples, and SKOS formats
- Define and use the FCCOntology schema for namespace control
- Build federated knowledge graphs spanning multiple project namespaces
Next Steps¶
- RAG Pipeline -- Build retrieval-augmented generation pipelines
- Semantic Search -- Search personas with natural language
- Federation -- Cross-project entity resolution and change tracking