Definition & Overview
A semantic link is a typed, directed connection between two concepts in a knowledge graph that expresses a meaningful relationship beyond simple association. Unlike hyperlinks (which merely connect documents) or foreign keys (which enforce referential integrity), semantic links carry relationship type information, metadata, and provenance that enable reasoning about the connection itself. A semantic link from "OSINT Signal Synthesis" to "Social Media OSINT" with type prerequisite and weight 0.9 expresses not just that these topics are connected, but how they are connected, how strongly, and why.
Semantic links form the edges of knowledge graphs used for ontology representation, recommendation systems, and intelligent navigation. In educational platforms, semantic links between topics enable learning path computation, prerequisite validation, and "related topics" recommendations. In intelligence platforms, semantic links between entities enable relationship mapping, network analysis, and pattern discovery.
The Prismatic Platform implements semantic links in the Academy's InterconnectionEngine, where they connect topics based on shared categories, skills, keywords, and explicitly declared prerequisites. The DD pipeline uses semantic links to represent relationships between entities (business ownership, political association, organizational hierarchy). The glossary itself is a network of semantic links, where each cross-reference represents a typed relationship between terms.
Technical Deep Dive
Semantic links in the Prismatic Platform are represented as typed structs with source, target, relationship type, weight, and metadata. The InterconnectionEngine builds and maintains the link graph using Erlang's :digraph module.
defmodule PrismaticAcademy.SemanticLink do
@moduledoc """
Typed semantic connection between Academy topics
expressing relationships with weight and metadata.
"""
@type link_type ::
:prerequisite
| :related
| :extends
| :complements
| :applies_to
| :derived_from
| :same_category
| :shared_skill
@type t :: %__MODULE__{
source: String.t(),
target: String.t(),
type: link_type(),
weight: float(),
metadata: map(),
created_at: DateTime.t()
}
defstruct [:source, :target, :type, :weight, :metadata, :created_at]
@spec new(String.t(), String.t(), link_type(), float(), map()) :: t()
def new(source, target, type, weight \\ 1.0, metadata \\ %{}) do
%__MODULE__{
source: source,
target: target,
type: type,
weight: weight,
metadata: metadata,
created_at: DateTime.utc_now()
}
end
@spec bidirectional?(link_type()) :: boolean()
def bidirectional?(:related), do: true
def bidirectional?(:complements), do: true
def bidirectional?(:same_category), do: true
def bidirectional?(:shared_skill), do: true
def bidirectional?(_), do: false
end
The link graph supports traversal queries that find related topics, compute similarity scores, and discover connection paths between distant concepts.
defmodule PrismaticAcademy.LinkGraph do
@moduledoc """
Graph operations on semantic links supporting traversal,
similarity computation, and path discovery.
"""
@type graph :: :digraph.graph()
@spec build([PrismaticAcademy.SemanticLink.t()]) :: graph()
def build(links) do
graph = :digraph.new()
vertices = links |> Enum.flat_map(fn l -> [l.source, l.target] end) |> Enum.uniq()
Enum.each(vertices, &:digraph.add_vertex(graph, &1))
Enum.each(links, fn link ->
:digraph.add_edge(graph, link.source, link.target, link)
if PrismaticAcademy.SemanticLink.bidirectional?(link.type) do
reverse = %{link | source: link.target, target: link.source}
:digraph.add_edge(graph, link.target, link.source, reverse)
end
end)
graph
end
@spec related(graph(), String.t(), keyword()) :: [{String.t(), float()}]
def related(graph, topic_slug, opts \\ []) do
max_depth = Keyword.get(opts, :depth, 2)
link_types = Keyword.get(opts, :types, :all)
neighbors = find_reachable(graph, topic_slug, max_depth, link_types)
neighbors
|> Enum.reject(fn {slug, _} -> slug == topic_slug end)
|> Enum.sort_by(fn {_, weight} -> weight end, :desc)
end
@spec shortest_path(graph(), String.t(), String.t()) :: [String.t()] | nil
def shortest_path(graph, source, target) do
:digraph.get_short_path(graph, source, target)
end
@spec similarity(graph(), String.t(), String.t()) :: float()
def similarity(graph, slug_a, slug_b) do
neighbors_a = :digraph.out_neighbours(graph, slug_a) |> MapSet.new()
neighbors_b = :digraph.out_neighbours(graph, slug_b) |> MapSet.new()
intersection = MapSet.intersection(neighbors_a, neighbors_b) |> MapSet.size()
union = MapSet.union(neighbors_a, neighbors_b) |> MapSet.size()
if union > 0, do: intersection / union, else: 0.0
end
defp find_reachable(graph, start, max_depth, link_types) do
do_bfs(graph, [{start, 0, 1.0}], %{}, max_depth, link_types)
end
defp do_bfs(_graph, [], visited, _max_depth, _types), do: Map.to_list(visited)
defp do_bfs(graph, [{current, depth, weight} | rest], visited, max_depth, types) do
if depth >= max_depth or Map.has_key?(visited, current) do
do_bfs(graph, rest, visited, max_depth, types)
else
visited = Map.put(visited, current, weight)
next =
:digraph.out_edges(graph, current)
|> Enum.map(fn edge ->
{_, _, target, label} = :digraph.edge(graph, edge)
{target, depth + 1, weight * label.weight * 0.5}
end)
|> Enum.reject(fn {slug, _, _} -> Map.has_key?(visited, slug) end)
do_bfs(graph, rest ++ next, visited, max_depth, types)
end
end
endArchitecture & Implementation
The semantic link architecture in the Prismatic Platform uses a dual representation: in-memory :digraph for fast traversal queries and PostgreSQL storage for persistence. The InterconnectionEngine GenServer maintains the in-memory graph and rebuilds it from the database at startup or when links are modified.
Semantic links are generated both explicitly (prerequisite declarations in topic registration) and automatically (shared category, shared skills, keyword overlap). The automatic link generation runs as a batch process after any topic registration change, computing similarity metrics and creating links above a configurable threshold.
Usage in Prismatic Platform
Semantic links drive the Academy's recommendation engine and the DD pipeline's entity relationship mapping. The Academy dashboard displays related topics based on semantic link traversal.
defmodule PrismaticWeb.Academy.RelatedTopicsComponent do
use Phoenix.Component
attr :topic_slug, :string, required: true
def related_topics(assigns) do
graph = PrismaticAcademy.InterconnectionEngine.get_graph()
related = PrismaticAcademy.LinkGraph.related(graph, assigns.topic_slug, depth: 2)
assigns = assign(assigns, :related, Enum.take(related, 5))
~H"""
<div class="space-y-2">
<h4 class="text-sm font-medium text-gray-400">Related Topics</h4>
<%= for {slug, weight} <- @related do %>
<a href={ class="block text-indigo-400 hover:text-indigo-300">
<%= slug %> <span class="text-gray-500">(<%= Float.round(weight, 2) %>)</span>
</a>
<% end %>
</div>
"""
end
endCross-References
- Semantic Linking - Engine that generates and maintains semantic links
- Prerequisite - Specific semantic link type encoding learning dependencies
- Progress - Completion tracking using semantic link paths
- Provenance - Origin tracing for automatically generated semantic links
- Self-Registration - Source of topic metadata used for automatic link generation
Created by Tomas Korcak (korczis) | GitHub