Skip to documentation

Atlas

Relation explainability

Click any edge in the entity graph and eli.ai answers three questions a consultant or analyst always asks: how strong is this link, really?which way does it point?why do you say so? The answer is a fully deterministic edge insight: zero LLM calls, computed on the fly from tables the graph already has (mentions, relations, relation_evidence, canonical_relations). Same inputs, same numbers, every time.

One edge in, one EdgeInsight out: evidence quotes, the composite strength, direction by relation type, and a deterministic explanation sentence — no model calls anywhere.

Why raw mention counts aren't the strength

The co-occurrence layer accumulates a mentionCount per edge — every document where two entities appear together bumps it. That is a fine thickness heuristic and a bad strengthheuristic: a ubiquitous entity ("Finance", "the platform") co-occurs with everything, so six shared documents with a hub mean far less than three with a rare entity. This is the classic degree bias from the collocation and link-prediction literature, and eli.ai corrects for it with two standard, bounded measures over the document universe:

  • NPMI (normalized pointwise mutual information, Bouma 2009) — compares how often the pair actually shares documents against what chance predicts, normalized into [-1, 1]: 0 means statistically independent, 1 means they always appear together, negative means less than chance. Two popular entities that overlap only at the chance rate land near 0; a rare-but-tight pairing lands near 1.
  • Jaccard — of all documents mentioning either entity, the share mentioning both ([0, 1]). Always defined, so it is the fallback when NPMI is degenerate: fewer than two documents in the universe, or the pair never shares a document (then npmi is reported as null).

The probability space is documents, not word windows: p(x) is the fraction of entity-mentioning documents that mention x's canonical cluster, p(x,y) the fraction mentioning both.

The composite score

Association is only half the story. An edge is strong if it is eitherdirectly asserted in the text ("Dana works at Northwind") or strongly associated statistically — a single hard works_at statement is a strong edge even with weak co-occurrence. So the score combines two orthogonal terms with a probabilistic OR:

the composite (docs/research/relation-explainability.md §4, mirrored verbatim in computeStrength())text
E = s / (s + 2)          # assertion term: s = Σ mentionCount over assertional
                         #   relation types (everything except related_to / mentions).
                         #   Saturating: 1 → 0.33, 2 → 0.50, 4 → 0.67.
A = clamp(NPMI, 0, 1)    # association term; falls back to clamp(Jaccard, 0, 1)
                         #   when NPMI is null (degenerate).

score = 1 − (1 − E) · (1 − A)     # probabilistic OR, always in [0, 1]
  • Saturating assertions— the first and second direct statements move the needle a lot, the twentieth barely at all, matching how a reader trusts "two independent sources said so" far more than one but "twenty" not much more than "ten". Only assertional types count here, so co-occurrence weight isn't double-counted (it already drives A).
  • Clamped association — independent or negatively-associated pairs contribute 0 to strength; they are not evidence of a real link.
  • Soft OR — symmetric, bounded, monotonic in both terms, and there are no tuning weights to justify.

score, jaccard, and npmi are rounded to 4 decimal places at the API boundary, so displays are stable and tests can assert equality. Raw mentionCount still exists — as edge thickness in the graph view, never as the strength number.

Direction semantics

Direction is a property of the relation type, not of the individual edge — the RDF/OWL split between directed predicates and owl:SymmetricProperty. The classification is deterministic:

  • Symmetric typesrelated_to and mentions: storage order is an extraction artefact, so they render without a preferred direction (direction: "symmetric").
  • Everything else is directedworks_at, part_of, reports_to, owns, feeds, uses, manages, and any future or unknown type — preserving the stored src → dst order (the safe default: the extractor writes triples subject-first). Because a stored row may sit either way around relative to the pair you queried, the insight resolves it to "src->dst" or "dst->src" for your orientation.

The workspace ontology describes relation types; this classification is intentionally independent of it, so strength and direction stay deterministic even in workspaces whose ontology is sparse.

Evidence, best first

A weighted edge is not yet a readable claim — the "why" is the actual quotes. The insight separates two kinds:

  • Assertional evidence— quotes backing typed relations ("Dana reports to the CFO"), each deep-linking to its source document and chunk. These state the relationship and are ordered first.
  • Associational context — the documents where the two entities merely co-occur. These situate the relationship and come after the real statements.

Finally, a fixed template assembles the explanation sentence — no LLM, so it is reproducible and free: the top assertional relation rendered in its resolved direction with a per-type verb phrase (works_at→ "works at"), the direct-statement count, the co-occurrence count, and a bucketed qualifier for the association ("strong" ≥ 0.5, "moderate" ≥ 0.25, "weak" > 0, else "chance-level"):

a deterministic explanationtext
Dana Whitfield works at Northwind Trading Co (3 direct statements);
they also co-occur in 5 documents (strong association, NPMI 0.62).

Where it surfaces

One shared card renders the insight everywhere — explanation sentence, strength bar with the three measures behind it (plain-word tooltips), every relation with its direction glyph, evidence quotes with document deep-links, and co-mention chips:

  • Graph explorer — clicking an edge (not a node) opens the edge side panel with the full insight. The graph view also uses the bulk per-edge scores for edge thickness and direction arrows.
  • Entity detail — every relation row in an entity's neighborhood panel carries a Why? expander that loads the insight for that pair (cached per pair, so collapsing and re-expanding never refetches).
  • Document view — the entities rail beside the editor shows In this document (type-colored chips for every entity the document mentions) and Related (entities the document does not mention but that connect through the graph), each related row with its via path, a mini strength bar, and the same Why? expander. See Documents & editing.

The backing endpoints are workspace-scoped and member-gated: GET /api/w/:workspaceId/graph/edge?src&dst for one full insight, GET …/graph/edge-strengths for the bulk per-edge scores, and GET …/documents/:docId/entities for the document rail.

Merged entities resolve to their canonical head

All of the math runs over canonical clusters: mentions of merged duplicates roll up to the head entity, so merging two aliases strengthens their shared edges instead of splitting the signal. See Entity review & merge.

The research behind the formulas

The measure selection — why NPMI over raw PMI, why Dunning's G² was considered and deferred, why the composite is a probabilistic OR — is documented with sources in docs/research/relation-explainability.md in the repository. The formulas there are mirrored verbatim in the implementation.