RAG vs. GraphRAG Comparison Guide
Document: RAG_VS_GRAPHRAG_COMPARISON_GUIDE.md Version: 2.0.0 Last Updated: January 30, 2026 Scope: Retrieval Architecture, Knowledge Graph Integration, Hallucination Reduction, MIZ OKI Enhancement Roadmap
Executive Summary
This guide provides practical decision criteria for choosing between standard Vector RAG and GraphRAG retrieval strategies, with deep integration into MIZ OKI's existing dual-store retrieval architecture. It documents the current implementation, identifies improvement opportunities, and provides concrete enhancement blueprints.
| Approach | Retrieval Method | Best For | MIZ OKI Component |
|---|---|---|---|
| Vector RAG | Semantic similarity (nearest chunks) | Single-hop lookups, definitions, FAQ-style Q&A | DualStoreRetriever.VECTOR_ONLY |
| GraphRAG | Relationship/path traversal in KG | Multi-hop reasoning, causal chains, entity relationships | StepwiseKGReasoner + CausalQueryRouter |
| Hybrid | Vector + KG fusion | Complex queries requiring both semantic and structural context | DualStoreRetriever.DUAL / ADAPTIVE |
Part 1: Core Concepts
Vector RAG
Retrieves by semantic similarity—finding the nearest embedding chunks to the query.
Strengths: - Fast and simple to implement - Excellent for "find the passage" tasks - Works well when answers live in a single chunk - Lower infrastructure complexity
Limitations: - Cannot reason across entities or time - Misses causal relationships - Struggles with multi-step inference
GraphRAG
Retrieves by relationships and paths in a Knowledge Graph, enabling who-did-what-when-why reasoning.
Strengths: - Multi-hop reasoning across entities - Causal and temporal awareness - Explainable retrieval paths - Handles complex entity relationships
Limitations: - Higher infrastructure complexity - Requires well-structured KG schema - More expensive to maintain
Part 2: Decision Rules
Rule 1: Choose Vector RAG When...
The question is a "find the passage" task where one hop of context suffices.
Examples: - Definitions and glossary lookups - Policy and procedure retrieval - Code/API documentation Q&A - FAQ-style support queries - Simple fact retrieval
Signal: Answers mostly live in a single chunk; no entity joins required.
Rule 2: Choose GraphRAG When...
The question is about links across entities, time, or causality.
Examples: - "Which campaigns drove uplift via audience X after event Y?" - "What led from A → B → C?" - "Who influences whom under condition Z?" - "Why did metric M change after action A?" - "Trace the attribution path from click to conversion"
Signals: - Multi-step paths required - Entity ↔ Event ↔ Metric joins needed - Explanations require "because..." reasoning - Temporal ordering matters
Rule 3: Choose Hybrid/Adaptive When...
The question requires both semantic context and structural relationships.
Examples: - "Find documents about campaign optimization that mention the audience segment we created last week" - "What's the best practice for bidding on keywords similar to our top performers?" - Complex analytical queries with both content and relationship aspects
Part 3: MIZ OKI Current Architecture
3.1 DualStoreRetriever (dual_store_retriever.py)
The DualStoreRetriever is MIZ OKI's core hybrid retrieval engine combining vector search and KG traversal.
Location: miz-oki-adk-agents/boss/dual_store_retriever.py (960 lines)
Retrieval Modes
class RetrievalMode(str, Enum):
VECTOR_ONLY = "vector_only" # Pure semantic search
KG_ONLY = "kg_only" # Pure graph traversal
DUAL = "dual" # Both with fusion
ADAPTIVE = "adaptive" # Auto-select based on query
| Mode | When Used | Query Types |
|---|---|---|
VECTOR_ONLY |
Simple lookups, factual Q&A | "What is X?", definitions |
KG_ONLY |
Relationship queries, path finding | "How are X and Y connected?" |
DUAL |
Complex queries needing both | Multi-faceted analysis |
ADAPTIVE |
Unknown query complexity | Default for general queries |
Fusion Strategies
class FusionStrategy(str, Enum):
RECIPROCAL_RANK = "reciprocal_rank" # RRF fusion (default)
WEIGHTED_AVERAGE = "weighted_average" # Fixed weight blend
MAX_SCORE = "max_score" # Take highest score per result
LEARNED = "learned" # ML-based fusion (future)
Current Implementation (Reciprocal Rank Fusion):
# From dual_store_retriever.py lines 420-445
def _fuse_results_rrf(self, vector_results: List, kg_results: List, k: int = 60) -> List:
"""
Reciprocal Rank Fusion: score = Σ 1/(k + rank_i) across sources
"""
scores = defaultdict(float)
for rank, result in enumerate(vector_results):
scores[result.id] += 1.0 / (k + rank + 1)
for rank, result in enumerate(kg_results):
scores[result.id] += 1.0 / (k + rank + 1)
# Sort by fused score
return sorted(scores.items(), key=lambda x: x[1], reverse=True)
Graph-Aware Caching
The GraphAwareCache provides intelligent caching with edge-based invalidation:
class GraphAwareCache:
"""Cache with KG-aware invalidation.
When an edge is updated in the KG, any cached paths
traversing that edge are automatically invalidated.
"""
def __init__(self, max_size: int = 10000, ttl_seconds: int = 3600):
self.cache = {}
self.edge_to_paths = defaultdict(set) # Track which paths use which edges
def invalidate_edge(self, edge_id: str):
"""Invalidate all cached paths containing this edge."""
affected_paths = self.edge_to_paths.get(edge_id, set())
for path_key in affected_paths:
self.cache.pop(path_key, None)
3.2 StepwiseKGReasoner (stepwise_kg_reasoner.py)
Multi-paradigm KG reasoning with Chain-of-Thought (CoT), Tree-of-Thought (ToT), and Graph-of-Thought (GoT).
Location: miz-oki-adk-agents/boss/stepwise_kg_reasoner.py (1133 lines)
Reasoning Paradigms
class ReasoningParadigm(str, Enum):
COT = "chain_of_thought" # Linear step-by-step reasoning
TOT = "tree_of_thought" # Branching exploration with pruning
GOT = "graph_of_thought" # Full graph traversal with cycles
| Paradigm | Complexity | Best For |
|---|---|---|
| CoT | O(n) | Simple causal chains, linear attribution |
| ToT | O(b^d) | Multi-path exploration, decision trees |
| GoT | O(V+E) | Complex interconnected reasoning, cycles |
Node and Edge Types
class NodeType(str, Enum):
ENTITY = "entity" # Concrete entities (campaigns, products)
CONCEPT = "concept" # Abstract concepts
EVENT = "event" # Time-bounded occurrences
METRIC = "metric" # Measurable values
DOCUMENT = "document" # Text sources
FACT = "fact" # Verified statements
class EdgeType(str, Enum):
IS_A = "is_a" # Taxonomy
CAUSES = "causes" # Causal relationship
RELATED_TO = "related_to" # General association
CONTAINS = "contains" # Composition
PRECEDES = "precedes" # Temporal ordering
ATTRIBUTED_TO = "attributed_to" # Attribution
GraphRAG Extraction
The GraphRAGExtractor implements Microsoft-style community detection and summarization:
class GraphRAGExtractor:
"""Extract community summaries for hierarchical retrieval."""
async def extract_communities(self, subgraph: KnowledgeGraph) -> List[Community]:
"""
1. Run Leiden clustering on subgraph
2. Generate summary for each community
3. Create hierarchical index for retrieval
"""
communities = self._leiden_cluster(subgraph)
for community in communities:
community.summary = await self._generate_summary(community.nodes)
return communities
3.3 CausalQueryRouter (causal_query_router.py)
Intelligent routing of causal queries to specialized expert cells.
Location: miz-oki-adk-agents/boss/causal_query_router.py (905 lines)
10 Causal Query Types
class CausalQueryType(str, Enum):
CAUSE_ATTRIBUTION = "cause_attribution" # "What caused X?"
COUNTERFACTUAL = "counterfactual" # "What if X hadn't happened?"
INTERVENTION = "intervention" # "What happens if we do X?"
MEDIATION = "mediation" # "Does X affect Y through Z?"
EFFECT_PREDICTION = "effect_prediction" # "What will happen if X?"
CAUSAL_DISCOVERY = "causal_discovery" # "What are the causal relationships?"
ATE_ESTIMATION = "ate_estimation" # Average Treatment Effect
CATE_ESTIMATION = "cate_estimation" # Conditional ATE
MECHANISM_EXPLANATION = "mechanism_explanation" # "How does X cause Y?"
TEMPORAL_CAUSATION = "temporal_causation" # Time-ordered causality
Dual-Stage Classification
class QueryClassifier:
"""Two-stage query classification for routing."""
async def classify(self, query: str) -> ClassificationResult:
# Stage 1: Pattern matching (fast)
pattern_match = self._pattern_classify(query)
if pattern_match.confidence > 0.85:
return pattern_match
# Stage 2: Embedding similarity (accurate)
query_embedding = await self._embed_query(query)
return self._embedding_classify(query_embedding)
Pattern Keywords by Query Type:
| Query Type | Keywords |
|---|---|
CAUSE_ATTRIBUTION |
"caused", "led to", "resulted in", "because" |
COUNTERFACTUAL |
"what if", "had not", "would have", "without" |
INTERVENTION |
"if we", "change", "set", "intervene" |
EFFECT_PREDICTION |
"will happen", "expect", "predict", "forecast" |
Expert Cell Routing
class ExpertCell(str, Enum):
CELL_03 = "cell03" # KG Brain - primary knowledge graph
CELL_05 = "cell05" # Causal inference specialist
CELL_13 = "cell13" # Causal API service
CELL_23 = "cell23" # Causal discovery
BOSS_AGENT = "boss_agent" # Fallback orchestrator
# Routing matrix: query_type -> [primary, secondary, fallback]
ROUTING_MATRIX = {
CausalQueryType.CAUSE_ATTRIBUTION: [ExpertCell.CELL_05, ExpertCell.CELL_03],
CausalQueryType.COUNTERFACTUAL: [ExpertCell.CELL_05, ExpertCell.CELL_13],
CausalQueryType.INTERVENTION: [ExpertCell.CELL_13, ExpertCell.CELL_05],
CausalQueryType.CAUSAL_DISCOVERY: [ExpertCell.CELL_23, ExpertCell.CELL_03],
# ...
}
Part 4: Retrieval Hygiene for Hallucination Reduction
These practices apply to both RAG approaches and significantly reduce hallucinations.
1. Tight Scopes
Filter by org/project/time/window before embedding search.
# MIZ OKI Implementation in DualStoreRetriever
async def search(self, query: str, filters: Optional[Dict] = None) -> List[Result]:
# Apply pre-filters before any retrieval
effective_filters = {
"org_id": self.current_org,
"time_window": filters.get("time_window", self.default_window),
"entity_types": filters.get("entity_types", None),
**filters
}
return await self._execute_search(query, effective_filters)
2. Typed Metadata
| Field | Purpose | MIZ OKI Location |
|---|---|---|
entity_type |
Filter by node type | NodeType enum in stepwise_kg_reasoner.py |
time_start/end |
Temporal scoping | Edge properties in KG |
source |
Attribution and trust | source_id on all nodes |
confidence |
Quality weighting | edge_confidence in graph |
version |
Document versioning | version field on documents |
3. De-duplication and Chunking
# Current deduplication in DualStoreRetriever
def _deduplicate_results(self, results: List[Result]) -> List[Result]:
seen_ids = set()
deduped = []
for result in results:
content_hash = hashlib.md5(result.content.encode()).hexdigest()
if content_hash not in seen_ids:
seen_ids.add(content_hash)
deduped.append(result)
return deduped
4. Attribution-On Policy
Always return source IDs and snippets alongside the context:
# Required response format for all retrieval operations
@dataclass
class RetrievalResponse:
answer: str
sources: List[Source]
confidence: float
reasoning_path: Optional[List[str]] = None # For GraphRAG
@dataclass
class Source:
doc_id: str
snippet: str
confidence: float
node_id: Optional[str] = None # KG node reference
edge_path: Optional[List[str]] = None # For multi-hop
5. Evidence-Based Ranking
# Current ranking formula in DualStoreRetriever
def compute_final_score(self, result: Result) -> float:
return (
result.similarity_score * 0.4 +
result.recency_weight * 0.2 +
result.authority_score * 0.2 +
result.confidence * 0.2
)
6. Causal Guardrails (GraphRAG-specific)
Only traverse edges with proper metadata:
| Edge Requirement | Purpose | Implementation |
|---|---|---|
direction |
Prevent reverse causality | EdgeType.CAUSES is directional |
timestamp |
Maintain temporal ordering | EdgeType.PRECEDES enforced |
edge_confidence |
Filter low-quality relationships | Threshold 0.5 default |
Part 5: Improvement Proposals
Based on analysis of the current implementation, these are high-priority enhancements:
5.1 Adaptive Weight Learning (HIGH PRIORITY)
Gap: Current fusion uses fixed weights (0.5 vector, 0.5 KG). Weights should adapt based on query type and historical performance.
Current State:
# dual_store_retriever.py - Fixed weights
self.vector_weight = 0.5
self.kg_weight = 0.5
Proposed Enhancement:
class AdaptiveWeightLearner:
"""Learn optimal fusion weights per query type."""
def __init__(self):
self.weights_by_query_type: Dict[str, Tuple[float, float]] = {}
self.outcome_buffer: List[QueryOutcome] = []
async def get_weights(self, query: str, query_type: CausalQueryType) -> Tuple[float, float]:
"""Get learned weights for this query type."""
if query_type in self.weights_by_query_type:
return self.weights_by_query_type[query_type]
# Default weights by query type
defaults = {
CausalQueryType.CAUSE_ATTRIBUTION: (0.3, 0.7), # Favor KG
CausalQueryType.COUNTERFACTUAL: (0.2, 0.8), # Strong KG
"factual": (0.7, 0.3), # Favor vector
"mixed": (0.5, 0.5), # Balanced
}
return defaults.get(query_type, (0.5, 0.5))
async def record_outcome(self, query: str, query_type: str,
vector_contributed: float, kg_contributed: float,
user_feedback: float):
"""Record retrieval outcome for learning."""
self.outcome_buffer.append(QueryOutcome(
query_type=query_type,
vector_contribution=vector_contributed,
kg_contribution=kg_contributed,
feedback_score=user_feedback
))
# Batch update weights periodically
if len(self.outcome_buffer) >= 100:
await self._update_weights()
async def _update_weights(self):
"""Update weights using gradient descent on feedback."""
for query_type in set(o.query_type for o in self.outcome_buffer):
outcomes = [o for o in self.outcome_buffer if o.query_type == query_type]
# Simple gradient: increase weight for source that correlated with positive feedback
vector_gradient = sum(o.vector_contribution * o.feedback_score for o in outcomes)
kg_gradient = sum(o.kg_contribution * o.feedback_score for o in outcomes)
total = vector_gradient + kg_gradient
if total > 0:
self.weights_by_query_type[query_type] = (
vector_gradient / total,
kg_gradient / total
)
self.outcome_buffer = []
MCP Tool:
# New MCP tool for adaptive weights
async def retriever_set_adaptive_weights(
query_type: str,
vector_weight: float,
kg_weight: float
) -> Dict:
"""Configure adaptive fusion weights for a query type."""
5.2 Cross-Store Reranking (HIGH PRIORITY)
Gap: Current RRF fusion treats both sources equally. Need reranking that considers semantic coherence between vector and KG results.
Proposed Enhancement:
class CrossStoreReranker:
"""Rerank fused results considering cross-store coherence."""
def __init__(self, embedding_model: str = "text-embedding-3-large"):
self.embedder = EmbeddingModel(embedding_model)
async def rerank(
self,
query: str,
vector_results: List[Result],
kg_results: List[Result],
top_k: int = 10
) -> List[RankedResult]:
"""
Rerank considering:
1. Query-result similarity
2. Cross-source agreement (results found in both get boost)
3. Path coherence for KG results
4. Novelty penalty for redundant results
"""
query_embedding = await self.embedder.embed(query)
all_results = []
seen_content = set()
for result in vector_results + kg_results:
# Skip near-duplicates
content_sig = self._signature(result.content)
if content_sig in seen_content:
continue
seen_content.add(content_sig)
# Compute rerank score
score = await self._compute_rerank_score(
query_embedding,
result,
vector_results,
kg_results
)
all_results.append(RankedResult(result=result, score=score))
return sorted(all_results, key=lambda x: x.score, reverse=True)[:top_k]
async def _compute_rerank_score(
self,
query_emb: np.ndarray,
result: Result,
vector_results: List[Result],
kg_results: List[Result]
) -> float:
# Base similarity
result_emb = await self.embedder.embed(result.content)
similarity = cosine_similarity(query_emb, result_emb)
# Cross-source agreement bonus
in_both = (
any(r.id == result.id for r in vector_results) and
any(r.id == result.id for r in kg_results)
)
agreement_bonus = 0.15 if in_both else 0.0
# Path coherence for KG results
path_coherence = 0.0
if hasattr(result, 'path') and result.path:
path_coherence = self._compute_path_coherence(result.path)
return similarity * 0.6 + agreement_bonus + path_coherence * 0.25
5.3 Query-Expert Affinity Matrix (HIGH PRIORITY)
Gap: Static routing matrix doesn't learn from outcomes. Need dynamic affinity learning.
Current State:
# causal_query_router.py - Static routing
ROUTING_MATRIX = {
CausalQueryType.CAUSE_ATTRIBUTION: [ExpertCell.CELL_05, ExpertCell.CELL_03],
# ...
}
Proposed Enhancement:
class QueryExpertAffinityMatrix:
"""Learn and maintain query-expert affinity scores."""
def __init__(self):
# Initialize with prior beliefs
self.affinity: Dict[Tuple[CausalQueryType, ExpertCell], float] = {}
self.success_counts: Dict[Tuple[CausalQueryType, ExpertCell], int] = defaultdict(int)
self.total_counts: Dict[Tuple[CausalQueryType, ExpertCell], int] = defaultdict(int)
def get_routing(self, query_type: CausalQueryType, top_k: int = 3) -> List[ExpertCell]:
"""Get top-k experts for query type, ranked by affinity."""
affinities = []
for expert in ExpertCell:
key = (query_type, expert)
# Thompson sampling: sample from Beta distribution
alpha = self.success_counts[key] + 1
beta = self.total_counts[key] - self.success_counts[key] + 1
sample = np.random.beta(alpha, beta)
affinities.append((expert, sample))
affinities.sort(key=lambda x: x[1], reverse=True)
return [e for e, _ in affinities[:top_k]]
def record_outcome(
self,
query_type: CausalQueryType,
expert: ExpertCell,
success: bool,
response_quality: float # 0-1 score
):
"""Update affinity based on outcome."""
key = (query_type, expert)
self.total_counts[key] += 1
if success and response_quality > 0.7:
self.success_counts[key] += 1
5.4 Intelligent Path Pruning (MEDIUM PRIORITY)
Gap: Current graph traversal explores all paths up to max_hops. Need intelligent pruning based on relevance.
Proposed Enhancement:
class IntelligentPathPruner:
"""Prune low-relevance paths during graph traversal."""
def __init__(self, relevance_threshold: float = 0.3):
self.threshold = relevance_threshold
self.pruning_stats = {"pruned": 0, "kept": 0}
async def should_expand(
self,
current_path: List[str],
next_edge: Edge,
query_embedding: np.ndarray
) -> bool:
"""Decide whether to expand this path."""
# Check edge confidence
if next_edge.confidence < self.threshold:
self.pruning_stats["pruned"] += 1
return False
# Check semantic relevance of path so far
path_text = self._path_to_text(current_path + [next_edge])
path_embedding = await self._embed(path_text)
relevance = cosine_similarity(query_embedding, path_embedding)
if relevance < self.threshold:
self.pruning_stats["pruned"] += 1
return False
self.pruning_stats["kept"] += 1
return True
def _path_to_text(self, path: List) -> str:
"""Convert path to natural language for embedding."""
parts = []
for i, item in enumerate(path):
if isinstance(item, Node):
parts.append(item.name)
elif isinstance(item, Edge):
parts.append(f"--[{item.type}]-->")
return " ".join(parts)
5.5 Temporal Reasoning Enhancement (MEDIUM PRIORITY)
Gap: Limited support for "before/after" and time-bounded queries.
Proposed Enhancement:
class TemporalReasoner:
"""Enhanced temporal reasoning for time-bounded queries."""
TEMPORAL_PATTERNS = [
(r"before (\d{4}-\d{2}-\d{2})", "before"),
(r"after (\d{4}-\d{2}-\d{2})", "after"),
(r"between (.+) and (.+)", "between"),
(r"in the last (\d+) (days?|weeks?|months?)", "relative"),
(r"since (.+)", "since"),
]
async def extract_temporal_constraints(self, query: str) -> TemporalConstraint:
"""Extract temporal constraints from query."""
for pattern, constraint_type in self.TEMPORAL_PATTERNS:
match = re.search(pattern, query, re.IGNORECASE)
if match:
return self._parse_constraint(constraint_type, match)
return TemporalConstraint(type="none")
async def filter_by_temporal(
self,
results: List[Result],
constraint: TemporalConstraint
) -> List[Result]:
"""Filter results by temporal constraint."""
if constraint.type == "none":
return results
filtered = []
for result in results:
if self._satisfies_temporal(result, constraint):
filtered.append(result)
return filtered
async def order_by_causality(
self,
path: List[Node],
edges: List[Edge]
) -> List[Tuple[Node, Edge]]:
"""Order path elements by causal/temporal sequence."""
# Sort by timestamp, respecting PRECEDES edges
timestamped = []
for i, node in enumerate(path):
ts = self._get_timestamp(node, edges[i] if i < len(edges) else None)
timestamped.append((node, edges[i] if i < len(edges) else None, ts))
return sorted(timestamped, key=lambda x: x[2] or datetime.max)
5.6 Pattern Mining Pipeline (LOW PRIORITY)
Gap: No automated discovery of common query patterns for optimization.
Proposed Enhancement:
class QueryPatternMiner:
"""Mine common query patterns for optimization."""
def __init__(self, min_support: float = 0.05):
self.min_support = min_support
self.patterns: List[QueryPattern] = []
self.query_log: List[QueryLogEntry] = []
async def mine_patterns(self, lookback_days: int = 30) -> List[QueryPattern]:
"""Mine frequent patterns from query log."""
# Load recent queries
queries = await self._load_query_log(lookback_days)
# Extract features
feature_sequences = [self._extract_features(q) for q in queries]
# Run frequent pattern mining
patterns = self._apriori(feature_sequences, self.min_support)
# Rank by support and performance correlation
ranked = []
for pattern in patterns:
support = self._compute_support(pattern, feature_sequences)
perf_correlation = await self._compute_performance_correlation(pattern)
ranked.append(QueryPattern(
features=pattern,
support=support,
performance_impact=perf_correlation
))
return sorted(ranked, key=lambda x: x.support * x.performance_impact, reverse=True)
def _extract_features(self, query: QueryLogEntry) -> List[str]:
"""Extract feature set from query."""
features = []
features.append(f"type:{query.query_type}")
features.append(f"mode:{query.retrieval_mode}")
features.append(f"hops:{query.max_hops}")
if query.entity_types:
for et in query.entity_types:
features.append(f"entity:{et}")
return features
Part 6: Implementation Roadmap
Phase 1: Foundation Enhancements (Weeks 1-2)
| Enhancement | File | Effort | Impact |
|---|---|---|---|
| Adaptive Weight Learning | dual_store_retriever.py |
Medium | High |
| Cross-Store Reranking | dual_store_retriever.py |
Medium | High |
| Query-Expert Affinity | causal_query_router.py |
Low | Medium |
Phase 2: Advanced Retrieval (Weeks 3-4)
| Enhancement | File | Effort | Impact |
|---|---|---|---|
| Intelligent Path Pruning | stepwise_kg_reasoner.py |
Medium | Medium |
| Temporal Reasoning | New module | High | Medium |
| Query Rewriting | causal_query_router.py |
Medium | Medium |
Phase 3: Learning & Optimization (Weeks 5-6)
| Enhancement | File | Effort | Impact |
|---|---|---|---|
| Pattern Mining | New module | High | Low |
| Distributed Reasoning | stepwise_kg_reasoner.py |
High | Medium |
| Unified Orchestrator | New module | High | High |
Part 7: MCP Tools for Retrieval
Existing Tools
| Tool | Description | Location |
|---|---|---|
kg_brain_reason |
Unified neuro-symbolic reasoning | knowledge_graph_brain_integration.py |
kg_search_dual_track |
Search KG with optimal backend selection | dual_store_retriever.py |
kg_reason_auto |
Automatic paradigm selection | stepwise_kg_reasoner.py |
kg_reason_cot |
Chain-of-Thought reasoning | stepwise_kg_reasoner.py |
kg_reason_tot |
Tree-of-Thought reasoning | stepwise_kg_reasoner.py |
kg_reason_got |
Graph-of-Thought reasoning | stepwise_kg_reasoner.py |
retriever_search |
Dual-store hybrid search | dual_store_retriever.py |
router_route_query |
Route query to expert cell | causal_query_router.py |
Proposed New Tools
| Tool | Description | Enhancement |
|---|---|---|
retriever_set_adaptive_weights |
Configure adaptive fusion weights | 5.1 |
retriever_record_outcome |
Record retrieval outcome for learning | 5.1 |
retriever_rerank_cross_store |
Cross-store reranking | 5.2 |
router_get_affinity |
Get query-expert affinity scores | 5.3 |
router_record_routing_outcome |
Record routing outcome | 5.3 |
kg_prune_paths |
Configure path pruning | 5.4 |
kg_temporal_filter |
Filter by temporal constraints | 5.5 |
Part 8: Best Practices Summary
| Practice | Vector RAG | GraphRAG | MIZ OKI Implementation |
|---|---|---|---|
| Pre-filtering | Required | Required | DualStoreRetriever.search(filters=...) |
| Top-k selection | 5-20 chunks | 3-5 paths | Configurable via top_k param |
| Reranking | Cross-encoder | Evidence + path confidence | CrossStoreReranker (proposed) |
| Max hops | N/A | 3 (default) | StepwiseKGReasoner.max_hops |
| Edge filtering | N/A | direction, time, confidence | Edge metadata on all traversals |
| Citation | Always | Always + path explanation | Source.edge_path for GraphRAG |
| Fallback | "I don't know" | "I don't know" | Confidence threshold checks |
| Weight adaptation | N/A | Learn from feedback | AdaptiveWeightLearner (proposed) |
Part 9: Virtuoso Routing Integration
The Boss Agent's Virtuoso routing leverages this guide:
# In boss_agent_core.py ACTION_KEYWORDS
ACTION_KEYWORDS = {
# Existing keywords...
"rag_retrieval": ["find", "lookup", "search for", "what is", "definition"],
"graphrag_retrieval": ["trace", "path", "caused", "led to", "why", "how connected"],
"hybrid_retrieval": ["analyze", "investigate", "comprehensive", "full picture"],
"causal_query": ["because", "resulted in", "influenced", "what if", "intervene"],
}
Routing Decision Tree:
Query arrives
│
├─ Contains causal keywords? ──────► CausalQueryRouter.classify()
│ │ │
│ │ ├─ CAUSE_ATTRIBUTION ─► Cell 05
│ │ ├─ COUNTERFACTUAL ────► Cell 05
│ │ ├─ INTERVENTION ──────► Cell 13
│ │ └─ other ─────────────► KG Brain
│ │
├─ Simple factual query? ──────────► DualStoreRetriever.VECTOR_ONLY
│
├─ Relationship query? ────────────► StepwiseKGReasoner (auto-paradigm)
│
└─ Complex/unknown? ───────────────► DualStoreRetriever.ADAPTIVE
References
Internal Documentation
- MIZ OKI Knowledge Graph Framework:
docs/KNOWLEDGE_GRAPH_FRAMEWORK_REPORT.md - Boss Agent Architecture:
docs/BOSS_AGENT_ARCHITECTURE_V5.md - Causal Reasoning Systems:
docs/archive/2025-12-24-session.md
Implementation Files
| File | Lines | Purpose |
|---|---|---|
dual_store_retriever.py |
960 | Hybrid vector + KG retrieval |
stepwise_kg_reasoner.py |
1133 | Multi-paradigm KG reasoning |
causal_query_router.py |
905 | Causal query classification and routing |
knowledge_graph_brain_integration.py |
~1500 | Unified neuro-symbolic substrate |
External Research
- Microsoft GraphRAG: Community-based summarization for hierarchical retrieval
- Reciprocal Rank Fusion: Cormack et al., SIGIR 2009
- Thompson Sampling for Multi-Armed Bandits: Russo et al., 2018
Version History
| Version | Date | Changes |
|---|---|---|
| 2.0.0 | 2026-01-30 | Deep MIZ OKI integration, improvement proposals, implementation blueprints |
| 1.0.0 | 2026-01-30 | Initial document |