D242 — Graph retrieval tools: graph_context, link_suggest, graph_path¶
Decision. Three read-only tools run graph algorithms on the link graph.
graph_context seeds from a question's top search hits and from given
concepts, ranks the rest by personalized PageRank (restart 0.25, forward-push
threshold 1e-4) over the undirected projection, and explains each result with
its hop count and the concept it was reached through. link_suggest proposes
links by resource allocation with at least two shared neighbours.
graph_path returns the shortest chain between two concepts and is advanced.
All three run on the subgraph induced by the concepts the caller may see. The
algorithms live in a new pure package, internal/graphalgo, over the int-indexed
projection kb.LinkGraph(include).
Why. An agent needing the context around a topic chained search,
concept_read and graph_neighbors one hop and one call at a time, with
unranked sets, and graph_neighbors beyond one hop is refused to narrowed
tokens. PPR (as in HippoRAG) ranks multi-hop context continuously and
discounts hubs smoothly, where a BFS cut at "distance ≤ k" needs hub blocking
(as Graphify does) and ranks nothing within a ring. A restart of 0.25 lets 2–3
hop context score on graphs with a mean undirected degree of 6–9; HippoRAG's
0.5 is tuned for much denser phrase graphs. Resource allocation penalises
hub-mediated evidence more than Adamic–Adar, and a single shared neighbour is
too weak to act on. Removing hidden concepts before computing — rather than
filtering results — is what makes the tools safe for a narrowed token without
a whole-KB restriction: scores, distances and paths computed on the whole
graph would carry what the caller cannot see. The cost is one projection per
call: 3–5 ms on the synthetic 1,000-concept KB of D241 (graph_context warm
3.2 ms, link_suggest 5.3 ms, graph_path 5.3 ms, most of it the cache
validation).
Alternatives rejected.
- gonum: a dependency for a few short algorithms at ≤ 5k nodes, with less
control over determinism than ascending-order loops.
- One tool with a mode switch: search dropped its modes for coherence
(D135); each tool has one job.
- Filtering results by visibility after computing on the whole KB: leaks
hidden concepts through the numbers.
- Raw search scores as seed weights: FTS5 and the in-memory index score on
different scales; the reciprocal rank does not depend on the backend.
- The plan's PPR test bound (L1 ≤ 1e-3 at eps 1e-5): forward push only
guarantees eps·Σdeg, a few 1e-3 on the test graphs. The test checks that
bound at 1e-5 and ≤ 1e-3 at 1e-6.
Consequences. Every output is deterministic: loops run in ascending node
order, ties break by id. A missing and a hidden concept give the same
not found text. kb.LinkGraph took the name the D241 accessor had; that one
is now kb.Links. Later graph work builds on internal/graphalgo and
kb.LinkGraph.