Knowledge Graph¶
anchor.graph, anchor.models.graph, the graph stores and extractors.
Models (anchor.models.graph)¶
normalize_key(text) -> str¶
Canonical key for node ids and relation labels: NFKC + casefold, then every run of whitespace, hyphens and underscores becomes one _ (auth-service, Auth Service and AUTH_SERVICE are one node; C++/C#/.NET stay distinct). Raises ValueError on empty input.
GraphNode¶
| Field | Type | Default | Description |
|---|---|---|---|
id | str | (required) | Canonical key (normalized on construction). |
label | str | the raw id | Display name. |
aliases | tuple[str, ...] | () | Other spellings; the mention matcher uses them. |
metadata | dict[str, Any] | {} | Free metadata (kind, type, ...). |
GraphEdge¶
| Field | Type | Default | Description |
|---|---|---|---|
id | str | uuid4 | |
source, target | str | (required) | Node ids (normalized). |
relation | str | (required) | Free label, normalized (works_on). |
fact | str \| None | None | Evidence sentence for explain(). |
provenance | "extracted" \| "inferred" \| "ambiguous" | "extracted" | Where it came from. |
confidence | float | 1.0 | [0, 1]. |
evidence | tuple[str, ...] | () | ContextItem ids supporting the edge (deduplicated). |
valid_from, valid_to | datetime \| None | None | World time. |
created_at, invalidated_at | datetime / datetime \| None | now / None | Transaction time. |
metadata | dict[str, Any] | {} |
weight (property) is max(1, len(evidence)); is_live(as_of=None) is invalidated_at is None and valid_from <= as_of < valid_to.
Subgraph¶
nodes: dict[str, GraphNode], edges: list[GraphEdge], items: dict[str, tuple[str, ...]] — everything visible under a scope at a time; what the algorithms consume.
KnowledgeGraph (anchor.graph)¶
Every read takes scope: RetrievalScope | None and, where time matters, as_of: datetime | None (default now). Names are normalized on entry.
| Method | Returns | Description |
|---|---|---|
add_node(name, *, label=None, aliases=(), metadata=None) | GraphNode | Upsert; aliases/metadata merge, first label wins. |
add_edge(source, relation, target, *, evidence=(), fact=None, provenance="extracted", confidence=1.0, valid_from=None, valid_to=None, metadata=None) | GraphEdge | Creates endpoints; reinforces a live edge with the same triple. Evidence must be linked first. |
link_item(name, item_id, namespace="/") | None | The item evidences the node under its namespace. KeyError for unknown node. |
unlink_item(item_id) | int | Forget the item; edges left without evidence are invalidated. |
invalidate_edge(edge_id, *, at=None) | bool | Mark obsolete (kept in history). |
remove_node(name) | bool | Drop the node, invalidate its edges. |
node(name) / nodes(*, scope) | GraphNode \| None / list[str] | |
items(name, *, scope) | list[str] | Visible evidence of a node. |
edges(name, *, direction="both", scope, as_of) | list[GraphEdge] | Outgoing first, then incoming. |
backlinks(name, *, scope, as_of) | list[GraphEdge] | Edges pointing into the node. |
neighbors(name, *, max_depth=1, scope, as_of) | list[str] | BFS both ways, start excluded. |
related_items(name, *, max_depth=2, scope, as_of) | list[str] | Items of the node and its neighbourhood. |
path(a, b, *, scope, as_of) | list[str] \| None | Fewest hops; never through a hidden node. |
explain(a, b, *, scope, as_of) | list[GraphEdge] | The edges along path. |
mentions(text, *, scope, max_words=4, subgraph=None) | list[str] | Nodes whose id/alias appears in the text. |
query(seeds, *, item_seeds=(), top_k=10, scope, as_of, damping=0.5, subgraph=None) | list[tuple[str, float]] | Items ranked by personalized PageRank. |
hubs(*, k=10, scope, as_of) | list[tuple[str, int]] | Degree ranking. |
communities(*, scope, as_of) | dict[str, int] | Partition, cached on store.version; Leiden with [graph], else Louvain. |
Algorithms (anchor.graph.algorithms)¶
Pure Python: adjacency(pairs), bfs(adj, start, *, max_depth), shortest_path(adj, a, b), degree(adj), personalized_pagerank(adj, seeds, *, damping=0.5, max_iter=100, tol=1e-6), louvain(adj, *, resolution=1.0, max_levels=10), modularity(adj, communities).
Stores¶
GraphStore / AsyncGraphStore protocols (see Protocols) with InMemoryGraphStore(vault=...), SqliteGraphStore(conn_manager, vault=...) and the async PostgresGraphStore(conn_manager, vault=...). All are vault-bound at construction; version increments on every write.
Extraction (anchor.ingestion)¶
| Name | Description |
|---|---|
GraphExtractor (protocol) | extract(item) -> Extraction |
Extraction | nodes (linked to the item as evidence) and edges (evidence defaults to the item). |
StructureExtractor() | The chunk's note as a node; frontmatter aliases/tags. |
WikilinkExtractor() | [[target]] → links_to. Helpers: wikilinks(text), parse_wikilink(inner), Wikilink. |
LLMGraphExtractor(llm, *, relations=DEFAULT_RELATIONS) | Entities + typed relations in one model call; fail-soft. |
GraphIndexer(graph, extractors=None) | index(items) -> IndexStats, index_entries(entries); default extractors = structure + wikilinks. |
GraphIndexingEntryStore(inner, indexer) | A MemoryEntryStore wrapper: add indexes (re-extracts on changed content), delete/clear unlink; everything else forwards. What MemoryManager(graph=...) wraps the store in. |
Retrieval¶
GraphRetriever(graph, context_store, entity_extractor=None, *, vector_store=None, embeddings=None, seed_k=5, damping=0.5, tokenizer=None) — see Retrieval. graph_retrieval_step(graph, store, entity_extractor, ..., scope=None) — see Pipeline.