Skip to content

GraphRAG for Research

v1.5.0's second pillar — Pillar B: GraphRAG — gave FCC a retrieval surface designed for research corpora that are both large and cross-disciplinary. Classic vector-RAG works well when the relevant chunk is semantically close to the query. Research rarely works that way: the relevant chunk is often three semantic hops away, sitting in a neighbouring discipline that shares an ontology but not a vocabulary. GraphRAG addresses that head-on by combining semantic retrieval, a knowledge-graph walk, a Zachman-cell filter, and optional LYRA harmonic-expansion into a single reproducible pipeline.

This page is the scientist-facing reference for GraphRAG in FCC v1.5.x. It covers corpus setup, the Zachman filter for cross-disciplinary work, LYRA augmentation, reproducibility anchors, and citation formatting for academic output.


The GraphRAG pipeline in one paragraph

A GraphRAG query threads four stages: (1) the semantic retriever surfaces seed nodes from the corpus; (2) a breadth-first graph walk expands those seeds out to max_hops neighbours along the knowledge graph's typed edges; (3) a ZachmanCell filter narrows the expanded subgraph to the relevant enterprise perspective (e.g., only ARCHITECT/WHAT cells for a data-model question); (4) the LYRA bridge (when available) augments the filtered result with cross-namespace harmonic concepts via SKOS-proxy edges. The output is a frozen GraphRAGResult containing the surviving nodes and edges, a ranked list of (node_id, score) pairs, and a pre-assembled LLM-ready context block. Every stage is deterministic; a replay with the same seed corpus, same query, same max_hops, and same filter produces byte-identical output.

Source code: src/fcc/rag/graphrag.py. See also ADR-011 — GraphRAG + Zachman filtering for the architectural rationale.


Setting up a research corpus

GraphRAG's input is a KnowledgeGraph populated from your corpus. FCC ships five corpus builder functions under src/fcc/knowledge/builders.py; for custom corpora, the contract is simple.

Step 1 — ingest documents

Use DocumentChunker from fcc.rag.chunking. Six chunking strategies ship: fixed-size, sentence, paragraph, section-heading, sliding-window, and semantic-boundary. For literature reviews, section-heading usually works best because academic papers have predictable internal structure.

from fcc.api import DocumentChunker

chunker = DocumentChunker(strategy="section-heading")
chunks = chunker.chunk_directory("corpus/papers/")

Step 2 — build the knowledge graph

A minimal research KG has three node types: Document, Concept, Author. Edges include cites, discusses, authored_by. For a richer graph, add Dataset, Method, Finding, and contradicts / replicates edges.

from fcc.api import KnowledgeGraph
from fcc.knowledge.models import KnowledgeNode, KnowledgeEdge, NodeType

kg = KnowledgeGraph(namespace="my_research_project")
# Populate nodes from chunks, edges from metadata.
# See the Pool D-maintained evolution guide for richer patterns.

Step 3 — add a semantic retriever

from fcc.api import SemanticRetriever, MockEmbeddingProvider

retriever = SemanticRetriever(
    embedding_provider=MockEmbeddingProvider(dim=384),
    chunks=chunks,
)

Any EmbeddingProvider works. For production research use, swap MockEmbeddingProvider for a sentence-transformers-backed provider. The MockEmbeddingProvider is deterministic — excellent for reproducibility tests but not for semantic quality.

Step 4 — assemble the GraphRAG instance

from fcc.rag.graphrag import GraphRAG

graphrag = GraphRAG(
    kg=kg,
    retriever=retriever,
    lyra_bridge=None,  # lazy-resolved via get_lyra_bridge() if omitted
)

You now have a research-grade GraphRAG instance.


Zachman-cell filtering for cross-disciplinary queries

The Zachman 6×6 cross-cut (introduced in v1.4.1) classifies every entity in the FCC ecosystem — personas, actions, workflow nodes, and selected knowledge-graph nodes — into exactly one of 36 cells defined by six perspectives (PLANNER, OWNER, ARCHITECT, ENGINEER, SUBCONTRACTOR, USER) and six abstractions (WHAT, HOW, WHERE, WHO, WHEN, WHY).

Research questions are often implicitly Zachman-typed. A question about experimental methodology lives in ENGINEER/HOW; a question about study motivation lives in PLANNER/WHY; a question about subject- recruitment protocol lives in USER/WHO. GraphRAG's Zachman filter lets you make that typing explicit.

from fcc.classification.zachman import ZachmanCell, Perspective

result = graphrag.query(
    query="How do hospital-based BCI studies handle informed consent?",
    max_hops=2,
    zachman_filter=ZachmanCell(
        perspective=Perspective.USER,
        abstraction="WHO",
    ),
)

The filter prunes the expanded subgraph to nodes whose own Zachman cell matches (or is compatible with — the filter's compatibility relation is documented in src/fcc/classification/zachman.py). Cross- disciplinary queries benefit especially: ask about methodology across medicine, finance, and energy verticals filtered on ENGINEER/HOW, and the filter keeps the three-discipline results coherently scoped.

Filter tips for cross-disciplinary work

  1. Start broad, then narrow. Run once without the filter, inspect the top-20 scored nodes, decide which cell actually contains the relevant evidence, then re-run with the filter.
  2. Use perspective without abstraction for exploratory queries. Passing only Perspective.ARCHITECT (without an abstraction) widens the filter to the whole architect row — useful when the abstraction isn't yet obvious.
  3. Layer filters across runs for triangulation. Run the same query against ENGINEER/HOW and ARCHITECT/HOW, then compare the two result sets. Disagreement between the two is itself an interesting finding.

LYRA augmentation for cross-namespace queries

LYRA — the Graph-of-Thought bridge promoted to stable in v1.5.0 — expands GraphRAG results across namespace boundaries using SKOS-proxy edges and broad-match chains. The metaphor of the Greek lyre's harmonic expansion is taken seriously: LYRA adds notes in neighbouring keys, not arbitrary distant notes.

from fcc.api import get_lyra_bridge

graphrag = GraphRAG(
    kg=kg,
    retriever=retriever,
    lyra_bridge=get_lyra_bridge(),  # picks mock fallback if live is unavailable
)

result = graphrag.query(
    query="What retention policies apply to brain-imaging data?",
    max_hops=2,
    lyra_expand=True,
)

When lyra_expand=True, the LYRA bridge receives the filtered subgraph and returns a list of cross-namespace candidate concepts. Typical candidates for a neuroscience query might include DICOM retention policy, HIPAA PHI retention, and institutional IRB data management plans. GraphRAG merges these into the final ranked list with a clear provenance marker so the scientist can distinguish direct-hit nodes from LYRA-augmented nodes.

LYRA currently has a mock bridge as the stable default — the live LYRA backend is incubating and will land in a future release. The mock bridge walks a 7-node / 6-edge seed graph; it is useful for demonstration and deterministic testing but not for production research. When the live backend lands, the import path does not change — only the availability sentinel flips.

For the bridge origin reasoning, see the Codename Decoder entry on LYRA.


Reproducibility — the three anchors

Research retrieval is only useful if it's reproducible. GraphRAG pins reproducibility on three anchors:

Anchor 1 — deterministic BFS and ranking

The breadth-first walk is deterministic given a stable node-iteration order. FCC's KnowledgeGraph stores nodes in insertion order; the walk visits neighbours in the same order. Ranking (the persona boost and Zachman filter) is also deterministic. Identical inputs → identical outputs, every run.

Anchor 2 — frozen results

GraphRAGResult is a @dataclass(frozen=True) of immutable tuples. You can serialize it to JSON (to_dict()), archive it to POLARIS (see the LTAS walk-through in Long-Term Archive Stewardship), and replay it at any future date. The result structure will be unchanged across v1.x because of the SemVer contract enforced by KGSE.

Anchor 3 — FAIR + Open Science audit

Every GraphRAG query can emit an OPEN-SCI-009 Agent Transparency Card via the compliance subsystem. The card captures the query, the seed nodes, the filter parameters, the LYRA augmentation flag, the result summary, and the embedding-provider hash. For published research, the card is the citable artifact — the thing you link to from your "Methods: Retrieval Pipeline" section.

from fcc.compliance.pipeline import CompliancePipeline

pipeline = CompliancePipeline()
card = pipeline.emit_transparency_card(
    subject=graphrag,
    query_spec={"query": "...", "max_hops": 2, "zachman_filter": "USER/WHO"},
    result=result,
)

Store the card alongside the paper. When a reviewer asks "how did you retrieve the supporting evidence?", you point at the card.


Citation formatting

A GraphRAG-supported finding has two layers of citation.

Layer 1 — traditional citations

The documents retrieved are normal academic sources. Cite them as you would any other: author, year, title, venue. GraphRAG's result.nodes includes a KnowledgeNode per document with a metadata dict that carries the canonical citation.

Layer 2 — methodological citation

Add a Methods subsection to the paper that cites the pipeline itself. A minimal citation skeleton:

Evidence retrieval was performed with FCC GraphRAG (v1.5.3). Seed nodes were surfaced via a SemanticRetriever over 384-dimensional embeddings; the subgraph was expanded to max_hops=2 and filtered on the ZachmanCell {perspective}/{abstraction}. LYRA harmonic expansion was {enabled|disabled}. The query specification and result summary are recorded in Open-Science Transparency Card OPEN-SCI-009-{card_id}, archived under POLARIS retention class cold as {archive_id}.

Every bracketed token is filled from the card and the archive metadata. Reviewers with access to the same FCC version, the same corpus, and the same query can reproduce the result bit-identically.


Worked example — a cross-disciplinary BCI study

A scientist is drafting a paper on brain-computer-interface patient recruitment. The corpus spans three disciplines: neuroscience, clinical trial methodology, and bioethics.

Step 1: Formulate the question. "What are the consent protocols for BCI clinical trials with patients who have motor neuron disease?"

Step 2: Type the question. This is a USER/WHO question (patient perspective, consent protocol). The Zachman filter is Perspective.USER, abstraction="WHO".

Step 3: Run GraphRAG.

result = graphrag.query(
    query="consent protocols for BCI clinical trials with MND patients",
    max_hops=2,
    zachman_filter=ZachmanCell(perspective=Perspective.USER, abstraction="WHO"),
    lyra_expand=True,
)

Step 4: Triangulate with a second run. Re-run filtered on ENGINEER/HOW to catch methodology-framed passages the first run may have missed.

Step 5: Emit the transparency card. Record query spec + result summary on the card; archive to POLARIS.

Step 6: Draft the Methods subsection. Cite the card, cite the archive_id, cite the FCC version. The retrieval is now reproducible.

This sequence — formulate, type, run, triangulate, archive, cite — is the recommended GraphRAG research pattern across disciplines.


Interplay with FAIR

GraphRAG reinforces the FAIR principles that are core to the FCC scientist tier:

  • Findable. The knowledge graph gives every document a stable node_id. The transparency card is findable via the Open-Science index.
  • Accessible. Archived queries are pullable from POLARIS at any retention tier. The card carries the access policy.
  • Interoperable. The knowledge graph can be serialized to OWL / RDF / SKOS / JSON-LD via fcc.knowledge.serializers. The LYRA bridge adds cross-namespace interoperability.
  • Reusable. The frozen result structure means that any downstream researcher can replay the query on the same corpus and get the same result.

See FAIR Workflow for the FCC-wide FAIR-compliance framing.


Limitations and honest caveats

GraphRAG is not a search engine replacement. Some honest caveats:

  1. Corpus quality dominates. A noisy graph produces noisy results no matter how clever the filter.
  2. Mock embeddings are deterministic but not semantic. For production research, use a real embedding provider.
  3. LYRA is currently mocked. Cross-namespace augmentation is limited until the live LYRA backend ships.
  4. Zachman classification is only as good as the classifier. If your custom nodes lack a zachman_cell attribute, the filter silently drops them — verify with fcc audit knowledge --strict.
  5. Persona-boosting can over-concentrate. If you pass a persona_id hint, the walk over-weights nodes the persona has historically interacted with. This is a feature (personalised retrieval) but also a reproducibility hazard — always record the persona_id in the transparency card.


Next steps

  1. Walk docs/tutorials/advanced-capabilities/graphrag-deep-dive.md (Pool B) for a hands-on GraphRAG tutorial.
  2. Run notebook 46_graphrag_advanced_patterns.ipynb (Pool F) — the v1.5.3 notebook that makes the pipeline interactive.
  3. Skim notebook 44_lyra_graphrag_preview.ipynb for the v1.5.0 LYRA preview that shipped alongside GraphRAG.
  4. Read ADR-011 — GraphRAG + Zachman filtering for the architectural rationale.
  5. Consult the Codename Decoder for LYRA origin reasoning and canonical status.
  6. Read the Decision Archaeology page if you need to reconstruct the rationale for a retrieval pipeline choice retrospectively.
  7. Browse docs/tutorials/sample-prompts/pillar-graphrag-prompts.md (Pool C) for ready-to-run prompts.