Skip to content

Your First Retrieval

This guide runs the full Odin pipeline once and then walks through every field of the result, so you know exactly what you are getting back and where to reach for each piece of information.

Run a retrieval

from arango import ArangoClient
from odin import OdinEngine

client = ArangoClient(hosts="http://localhost:8529")
db = client.db("my_graph", username="root", password="")

engine = OdinEngine(db=db, community_id="global")

result = engine.retrieve(
    seeds=["entity/claim_123"],
    max_paths=50,
    hop_limit=3,
    beam_width=64,
)

That single call runs PPR, then beam search, then NPLL scoring, then aggregation, and returns everything as one dictionary. Four parameters shape it:

Parameter Type Default Meaning
seeds list[str] required Entity IDs to start from
max_paths int 50 Maximum paths to return
hop_limit int 3 Maximum path length
beam_width int 64 Paths kept per hop during beam search

How they interact is covered in Tuning Retrieval; for now the defaults are fine.

What comes back

The result dictionary has ten keys, and it helps to see them grouped by purpose:

result.keys()
# dict_keys(['topk_ppr', 'paths', 'evidence_strength',
#            'community_relevance', 'insight_score', 'aggregates',
#            'triage', 'ics', 'used_budget', 'trace'])

The paths are the primary output, returned best-first. Each carries a combined score and a list of edges; there is no separate nodes field, so the node sequence is derived from the edges:

for p in result["paths"][:5]:
    edges = p["edges"]
    nodes = [edges[0]["u"], *(e["v"] for e in edges)] if edges else []
    print(f"[{p['score']:.2f}]", " -> ".join(str(n) for n in nodes))

The triage score is the single number most agent loops gate on, along with the breakdown that produced it (see Triage & Insight Scoring):

result["triage"]["score"]         # 0-100 prioritization number
result["triage"]["components"]    # per-component breakdown

The aggregates turn those paths into patterns, and topk_ppr exposes the anchors the walk started from:

result["topk_ppr"]                      # structurally important anchor nodes
result["aggregates"]["motifs"]          # recurring patterns
result["aggregates"]["relation_share"]  # relation-type breakdown
result["aggregates"]["summary"]         # provenance, recency, coverage, ...

The quality signals are there when you want finer-grained observability than the triage score alone:

result["insight_score"]         # 0.0-1.0 overall quality
result["evidence_strength"]     # 0.0-1.0
result["community_relevance"]   # 0.0-1.0
result["ics"]                   # decomposition of insight_score

The trace and budget are for operability, safe to log or ignore:

result["used_budget"]   # how much exploration budget was consumed
result["trace"]         # ppr/beam traces, params, timings_ms

Every field is documented exhaustively in the Result Schema.

Putting it together

In practice a first look at a retrieval reads like this:

result = engine.retrieve(seeds=["entity/claim_123"], max_paths=25)

print(f"Triage: {result['triage']['score']}/100   "
      f"Insight: {result['insight_score']:.2f}   "
      f"Paths: {len(result['paths'])}")

print("\nTop motifs:")
for m in result["aggregates"]["motifs"][:5]:
    print("  ", m)

print("\nTop paths:")
for p in result["paths"][:5]:
    edges = p["edges"]
    nodes = [edges[0]["u"], *(e["v"] for e in edges)] if edges else []
    print(f"  [{p['score']:.2f}]", " -> ".join(str(n) for n in nodes))

Next

From here, Scoring Edges covers the single-edge counterpart to retrieval, Tuning Retrieval explains the parameters, and AI Agent Integration wires the result into an agent loop.