How GraphRAG Works in ReportEngine for Knowledge Graph Construction
GraphRAG in ReportEngine constructs a structured knowledge graph from engine state JSONs, persists it to graphrag.json, and enables LLM-driven multi-round queries to enrich report chapters with cross-engine insights.
The bettafish repository (666ghj/bettafish) implements GraphRAG as an optional module within its Report Engine, transforming parsed states from Insight, Media, and Query engines into a traversable knowledge structure. This system allows large language models to perform targeted retrieval across interconnected data sources before generating report content.
Enabling GraphRAG in ReportEngine
GraphRAG functionality is controlled through environment variables defined in ReportEngine/utils/config.py (lines 70-76). The system checks GRAPHRAG_ENABLED (defaulting to false) and GRAPHRAG_MAX_QUERIES to determine whether to initialize the graph construction pipeline and how many query rounds to execute per chapter.
When enabled, the ReportAgent instantiates the graph builder and executes the GraphRAGQueryNode during chapter generation. Configure these settings before initializing the engine:
# ReportEngine/utils/config.py
settings = Settings()
settings.GRAPHRAG_ENABLED = True # Activate the module
settings.GRAPHRAG_MAX_QUERIES = 3 # Limit to 3 retrieval rounds per chapter
Constructing the Knowledge Graph
The GraphBuilder class in ReportEngine/graphrag/graph_builder.py (lines 40-70) orchestrates graph construction by consuming the report topic and a dictionary of parsed engine states (ParsedState). It generates a heterogeneous graph representing the relationships between analytical components.
Node and Edge Architecture
The builder creates five distinct node types:
- topic: The root node representing the user's original query
- engine: Nodes for insight, media, query, and optional host engines
- section: Child nodes under each engine representing content sections
- search_query: Deduped per section, storing executed search strings
- source: URL-hashed nodes representing discovered references
Edges encode four semantic relationships: analyzed_by, contains, searched, and found. When forum logs are present, the builder additionally creates a host engine node with summary sections.
from ReportEngine.graphrag.graph_builder import GraphBuilder
# engine_states contains ParsedState objects from each engine
graph = GraphBuilder().build(
topic="疫情舆情分析",
states=engine_states,
forum_entries=forum_logs # Optional
)
Persisting the Graph to Disk
Once constructed, the Graph object passes to GraphStorage.save() in ReportEngine/graphrag/graph_storage.py (lines 40-65). This serializes the entire graph structure—including node metadata, edge relationships, and identifiers—to a portable JSON file named graphrag.json stored adjacent to the chapter output directory.
The persistence layer stores task_id and report_id within the JSON structure, enabling later retrieval and inspection via the optional Flask API.
from pathlib import Path
from ReportEngine.graphrag.graph_storage import GraphStorage
graph_path = GraphStorage().save(
graph=graph,
task_id="run_20240223",
run_dir=Path("final_reports/chapters/run_20240223")
)
Querying the Knowledge Graph
The QueryEngine class in ReportEngine/graphrag/query_engine.py (lines 15-33) provides the retrieval interface for the LLM. It operates on the in-memory graph structure without requiring external graph databases.
Retrieval Capabilities
The query engine supports four filtering mechanisms:
- Keyword matching: Searches node attributes including
name,title,query_text, andsummary - Node-type filtering: Restricts results to specific types (
section,search_query,source) - Engine filtering: Limits scope to specific engines (
insight,media,query,host) - Depth expansion: After initial keyword matches, traverses neighbors up to
depthhops to capture contextual relationships
To prevent token overflow in downstream prompts, the engine applies hard caps: max_sections, max_queries, and max_sources.
LLM-Driven Multi-Round Retrieval
The GraphRAGQueryNode in ReportEngine/nodes/graphrag_query_node.py (lines 95-140) orchestrates the retrieval workflow, executing up to GRAPHRAG_MAX_QUERIES rounds (default 3) per chapter.
Decision-Making Architecture
Each round begins by constructing a decision prompt using templates from ReportEngine/graphrag/prompts.py (lines 9-62): GRAPHRAG_QUERY_DECISION_SYSTEM and GRAPHRAG_QUERY_DECISION_USER. This prompt includes:
- Chapter metadata and graph statistics from
query_engine.get_node_summary() - Section titles per engine
- Sample search queries
- Full query history from previous rounds
The LLM must return a JSON object specifying should_query, keywords, node_types, engine_filter, depth, and reasoning fields.
History-Aware Iteration
The node maintains a QueryHistory object that feeds prior rounds back into subsequent prompts (lines 70-85), preventing redundant queries while encouraging novel retrieval angles. The execution loop follows this sequence:
- Build decision prompt with current context
- Parse LLM response into
QueryParams - Execute
QueryEngine.query()with specified parameters - Log results via
knowledge_logger - Append round details to
QueryHistory
from ReportEngine.nodes.graphrag_query_node import GraphRAGQueryNode
from ReportEngine.llms.base import LLMClient
llm = LLMClient()
graph_query_node = GraphRAGQueryNode(llm)
results = graph_query_node.run(
section=current_chapter,
context=report_context,
graph=graph,
max_queries=3
)
Merging Results and Generating Insights
After completing all query rounds, the _merge_results() method (lines 290-340 of graphrag_query_node.py) deduplicates sections, queries, and sources across the entire retrieval history. It calculates total_nodes retrieved and generates cross-engine insights—metacognitive annotations indicating when multiple engines contributed to the context or when searches span diverse analytical domains.
Injecting Graph Context into Chapter Prompts
The merged results flow through format_graph_results_for_prompt() defined in ReportEngine/graphrag/prompts.py (lines 72-88), which applies the USER_PROMPT_GRAPH_RESULTS_TEMPLATE to structure the retrieval data.
The ChapterGenerationNode prepends two components to the final LLM prompt:
SYSTEM_PROMPT_CHAPTER_GRAPH_ENHANCEMENT: Instructions for incorporating graph-derived context- The formatted graph results containing deduplicated sources, queries, and cross-engine insights
This injection enables the chapter generation model to reference specific sources discovered by the Query engine while maintaining awareness of analytical sections generated by the Insight engine.
from ReportEngine.graphrag.prompts import format_graph_results_for_prompt
enhanced_prompt = format_graph_results_for_prompt(results)
# System prompt SYSTEM_PROMPT_CHAPTER_GRAPH_ENHANCEMENT is prepended automatically
End-to-End Orchestration
The ReportAgent class in ReportEngine/agent.py (lines 600-710) coordinates the complete GraphRAG pipeline:
- Loads input files and parsed engine states
- Builds the knowledge graph if
GRAPHRAG_ENABLEDisTrue - Executes
GraphRAGQueryNodefor each chapter section - Injects
graph_enhancement_promptinto the generation context - Produces final chapter text with cross-engine citations
The persisted graphrag.json remains available for post-hoc analysis or external API consumption after report generation completes.
Summary
GraphRAG in ReportEngine implements a complete retrieval-augmented generation pipeline through these key mechanisms:
- Pure-Python graph model: Uses lightweight dataclasses (
Node,Edge,Graph) requiring no external graph database - Deterministic persistence: Serializes complete graph state to
graphrag.jsonviaGraphStorage - LLM-driven query planning: Delegates semantic judgment to the language model while keeping graph traversal lightweight
- History-aware retrieval: Prevents redundant queries through
QueryHistorytracking across multiple rounds - Token-safe limits: Enforces caps on returned nodes via
QueryEngineto protect downstream prompts - Cross-engine integration: Merges insights from Insight, Media, Query, and Forum engines into unified chapter context
Frequently Asked Questions
What file format does ReportEngine use to store the GraphRAG knowledge graph?
ReportEngine persists the knowledge graph as a JSON file named graphrag.json using the GraphStorage.save() method in ReportEngine/graphrag/graph_storage.py. This file contains serialized node metadata, edge relationships, and identifiers including task_id and report_id, stored adjacent to the chapter output directory for portability and later inspection.
How does the GraphRAGQueryNode prevent redundant queries across multiple rounds?
The GraphRAGQueryNode maintains a QueryHistory object that records all previous retrieval rounds. According to lines 70-85 of ReportEngine/nodes/graphrag_query_node.py, this history feeds back into subsequent decision prompts, allowing the LLM to recognize previously queried keywords and node types. This design encourages novel retrieval angles while avoiding duplicate graph traversals within the same chapter generation cycle.
What types of nodes are created during GraphRAG knowledge graph construction?
The GraphBuilder in ReportEngine/graphrag/graph_builder.py creates five node types: topic (root), engine (insight, media, query, host), section (content divisions), search_query (deduped search strings per section), and source (URL-hashed references). Edges encode relationships including analyzed_by, contains, searched, and found to link these entities semantically.
How does the QueryEngine protect against token overflow when retrieving graph data?
The QueryEngine in ReportEngine/graphrag/query_engine.py applies explicit caps through parameters including max_sections, max_queries, and max_sources. These limits constrain the total nodes returned per type during keyword matching, depth expansion, and filtering operations, ensuring that formatted graph results remain within token budgets for downstream LLM prompts.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →