Skip to content

Architecture

Odin is a single library assembled from a few well-separated parts. You only ever touch one of them directly: OdinEngine, the public entry point. Understanding how it fits together makes the parameters, the result shape, and the performance characteristics all make sense.

The pipeline

A call to retrieve() runs four stages in order, each feeding the next:

┌─────────────┐    ┌──────────────────────────────────────┐    ┌─────────────────┐
│ Seed        │ -> │ PPR → Beam Search → NPLL → Aggregate │ -> │ Ranked Paths +  │
│ Entities    │    │            (Odin Engine)             │    │ Motifs + Scores │
└─────────────┘    └──────────────────────────────────────┘    └─────────────────┘

PPR goes first, scoring nodes by structural importance relative to your seeds and handing back the anchors worth exploring. Beam search grows multi-hop paths outward from those anchors, keeping only the top-K at each hop so the search stays bounded. As it goes, NPLL scores each candidate edge for plausibility and prunes the nonsense. Finally, aggregation folds the survivors into motifs, relation shares, and a triage score. Each stage exists to make the next one tractable: PPR shrinks where beam search looks, NPLL keeps the beam clean, and aggregation turns raw paths into something an agent can act on.

The pieces that do the work

Behind OdinEngine, the RetrievalOrchestrator runs that pipeline and assembles the result. Everything else is a component it coordinates:

┌─────────────────────────────────────────────────────────────────────────┐
│                              OdinEngine                                   │
│  ┌─────────────────────────────────────────────────────────────────────┐ │
│  │                    RetrievalOrchestrator                             │ │
│  │  Coordinates PPR → Beam → NPLL → Aggregation and builds the result  │ │
│  └─────────────────────────────────────────────────────────────────────┘ │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  │
│  │ Cached Graph │  │ APPRAnchors  │  │  NPLLModel   │  │ Aggregators  │  │
│  │  Accessor    │  │   (PPR)      │  │ (confidence) │  │  (motifs)    │  │
│  └──────────────┘  └──────────────┘  └──────────────┘  └──────────────┘  │
└─────────────────────────────────────────────────────────────────────────┘
Component Responsibility Source
OdinEngine Public API; wiring and lifecycle odin/engine.py
RetrievalOrchestrator Runs the pipeline, assembles the result retrieval/orchestrator.py
ArangoCommunityAccessor Reads nodes/edges from ArangoDB retrieval/adapters_arango.py
CachedGraphAccessor LRU cache over graph access retrieval/cache.py
APPRAnchors Personalized PageRank anchors retrieval/ppr/
NPLLConfidence / ConstantConfidence Edge plausibility scoring retrieval/confidence.py
NPLLModel / KnowledgeBootstrapper Model and its lifecycle npll/
aggregators Motifs, relation shares, triage retrieval/aggregators.py

What happens when you build an engine

Constructing an OdinEngine wires those parts together in one shot. It opens an ArangoCommunityAccessor for your community_id and wraps it in a CachedGraphAccessor; it initializes intelligence by loading or training the NPLL model (unless auto_train=False), yielding either NPLLConfidence or the ConstantConfidence fallback; and it hands both to a fresh RetrievalOrchestrator and APPRAnchors engine. From then on the engine is ready to serve retrievals.

engine = OdinEngine(
    db=db,
    community_id="global",   # scope of exploration
    cache_size=5000,          # graph-accessor LRU size
    auto_train=True,          # train NPLL if no model exists
    community_mode="none",    # "none" = global, "mapping" = scoped
)

Every one of these parameters is documented in the OdinEngine API.

One deliberate boundary

The design keeps graph intelligence and language reasoning strictly apart. Odin returns structured, scored evidence (nodes, edges, motifs, numbers) and stops there; the agent, an LLM, interprets that evidence and decides what to do. Drawing the line here buys two things: the graph layer stays explainable and testable, and hallucination is kept out of it entirely. Every path Odin returns genuinely exists in your data, because Odin never invents relationships; it only ranks the ones that are already there.


From here, follow the pipeline in order, Data Model then PPR, or skip to the Result Schema for the exact shape retrieve() hands back.