GraphRAG + Zachman Filter¶
This diagram traces a single :class:GraphRAG query from free-form input
text through to a fully-populated :class:GraphRAGResult. The entry point
is GraphRAG.query(text, k, persona_id, zachman_cell, max_hops) at
src/fcc/rag/graphrag.py:166, which sequences seed retrieval, BFS graph
expansion, Zachman-cell filtering, persona-aware scoring, LYRA
augmentation, and context assembly into one deterministic pipeline.
GraphRAG is v1.5.0 Pillar B; this is the request-path developers reach
for when they need "find every relevant node, but keep only the ones
that matter to THIS persona in THIS Zachman cell." ADR-011 captures the
decision to couple the Zachman classifier with the graph walk.
The sequence below shows one query end to end, including the graceful
fallback to the in-process :class:LyraMockBridge when the live LYRA
adapter is unavailable.
sequenceDiagram
participant Caller
participant GraphRAG
participant SemanticRetriever
participant KnowledgeGraph
participant PersonaRegistry
participant LyraBridge
Caller->>GraphRAG: query(text, k, persona_id, zachman_cell, max_hops)
alt retriever configured
GraphRAG->>SemanticRetriever: retrieve(text, k)
SemanticRetriever-->>GraphRAG: top_k RetrievalResult
else no retriever
GraphRAG->>KnowledgeGraph: match labels(text)
KnowledgeGraph-->>GraphRAG: seed_ids
end
GraphRAG->>KnowledgeGraph: all_edges, all_nodes
KnowledgeGraph-->>GraphRAG: edges, nodes
Note over GraphRAG: BFS expand from seeds up to max_hops
opt zachman_cell is not None
GraphRAG->>GraphRAG: _apply_zachman_filter(visited, cell)
Note over GraphRAG: drop nodes whose<br/>zachman_cell does<br/>not match coord<br/>or perspective
end
GraphRAG->>PersonaRegistry: resolve persona_id (optional)
PersonaRegistry-->>GraphRAG: match anchors for scoring
Note over GraphRAG: _score_nodes: +0.5 label hit,<br/>+0.4 persona match, +0.1 baseline
GraphRAG->>LyraBridge: graph_of_thought_query(text, depth=max_hops)
alt lyra.api importable
LyraBridge-->>GraphRAG: {nodes, edges, paths}
else fallback
Note over LyraBridge: LyraMockBridge walks<br/>seed KG when<br/>lyra.api absent
LyraBridge-->>GraphRAG: {nodes, edges, paths} (mock)
end
opt zachman_cell not None
GraphRAG->>GraphRAG: filter LYRA extras by Zachman
end
GraphRAG->>GraphRAG: assemble_context(result, max_tokens=2000)
GraphRAG-->>Caller: GraphRAGResult(nodes, edges, paths, scored, context_text)
Three failure modes are worth noting. An empty knowledge graph short-circuits
the query and returns an empty GraphRAGResult without any further work —
GraphRAG.query checks node_count == 0 up front. A missing
zachman_cell skips both the pre-LYRA and post-LYRA filter steps without
any exception. A LYRA bridge that raises is caught inside
_apply_lyra_augment and treated as an empty augmentation — the query
continues with KG-only results. This is the "graceful degradation" the
module docstring guarantees.
Persona awareness is a scoring signal, not a filter. Even when persona_id
is supplied, nodes that do not mention the persona still appear in the
result — they just score lower. Combined with the Zachman filter (which
IS a hard gate), this lets callers say "anything in this cell, ranked by
this persona" instead of "only this-persona nodes."
Steps in detail¶
- Caller to GraphRAG: query — Free-form text plus optional filters arrive at the single entry point.
- Seed retrieval — Either via the configured
SemanticRetriever(mapping chunk IDs back to KG node IDs) or via a case-insensitive substring match over node labels when no retriever is configured. - BFS expansion —
_bfs_expand(seed_ids, max_hops)walks up tomax_hopslayers out from each seed, recording traversed edges and frontier-shortest paths. - Zachman filter (pre-LYRA) —
_apply_zachman_filterdrops visited nodes whoseproperties["zachman_cell"]does not match the target cell'scoordinateor perspective. - Persona-aware scoring —
_score_nodesassigns each surviving node a deterministic score in [0, 1]: +0.5 for a query-text hit, +0.4 for persona alignment, +0.1 baseline, clamped at 1.0. - LYRA augmentation —
_apply_lyra_augmentcallsLyraBridge.graph_of_thought_query(text, depth=max_hops)and folds distinct nodes / edges / paths back in. The bridge resolves lazily viaget_lyra_bridge()and falls back toLyraMockBridgewhenlyra.apiis not importable. - Zachman filter (post-LYRA) — Extras go through the same cell match so the filter contract holds end to end.
- Context assembly —
assemble_context(result, max_tokens=2000)rolls the top-scored nodes into a bullet list truncated at a 4-chars-per-token budget. - Return — A frozen :class:
GraphRAGResultwithnodes,edges,paths,scored, andcontext_text.
See also¶
- Entry point:
src/fcc/rag/graphrag.py:166(GraphRAG.query) - LYRA bridge:
src/fcc/knowledge/lyra_bridge.py,src/fcc/plugins/v1_5_preview/lyra_bridge.py - Zachman classifier:
src/fcc/classification/zachman.py(ZachmanCell,Perspective) - Related class diagram:
../class-diagrams/lyra-bridge.md,../class-diagrams/six-pillars-overview.md - ADR:
docs/decisions/ADR-011_*.md(GraphRAG+Zachman), ADR-012 (CRDT) for neighbouring context - Pillar overview:
../class-diagrams/six-pillars-overview.md