M10 ยท The Hybrid Context Stack
Learning Objectives
SKILL LEVEL: ADVANCED
- Assign each context layer its owned territory: structural graph = what the code is; OKF = what engineers know; vector RAG = the unstructured remainder.
- Apply the two failure triggers โ multi-hop reasoning failure and tribal-knowledge sprawl โ to decide which layer (if any) a system needs next.
- Trace one query through the three-stage pipeline (narrative orientation โ structural traversal โ semantic search) and account for its token budget.
- Build a router that classifies queries as canonical, structural, or exploratory, and dispatches them to the right layer with fallbacks.
- Cite the production evidence โ the ERPNext case study โ and explain why accuracy gains are a better signal than token-reduction multiples.
Three Layers, Not Three Competitors
The viral framing of 2026 โ "Graphify kills RAG," "OKF kills vector databases" โ is backwards, and you have spent nine modules accumulating the evidence to see why. Graphify's own benchmarks (M04) showed it tying dense vector RAG on QA accuracy and slightly losing on recall. OKF's own spec (M06) ships no search layer at all. Neither of these things is trying to replace retrieval. They are specialized layers that sit alongside a vector index, each justified only by a specific failure mode.
BEFORE: Think about how you'd research an unfamiliar city before moving there. You'd use the street map to understand how districts connect. You'd read a local's guide for the things no map shows โ which neighborhoods are quiet, why the old town floods in spring. And you'd search the city's newspaper archive for anything else that comes up.
PAIN: Now imagine being told to choose ONE. Navigate with only the newspaper archive and you'll piece together street layouts from decade-old traffic reports. Use only the map and you'll never learn the flood story. Each source answers a class of question the others structurally cannot.
MAPPING: The structural graph is the street map โ precise, verifiable connections. The OKF bundle is the local's guide โ curated, narrative, explains why. Vector RAG is the newspaper archive โ vast, unstructured, searchable by theme. The 2026 architecture question was never "which one wins?" It is "which layer owns which context?"
A hybrid context stack is an agent architecture in which three retrieval mechanisms coexist, each owning a distinct knowledge class: (1) a vector RAG index over unstructured text โ docs, tickets, Slack threads, the long tail; (2) a structural graph โ deterministic, AST-extracted call graphs, class hierarchies, imports and exports; (3) a narrative layer (OKF) โ curated Markdown concepts capturing architecture, schemas, playbooks, and intent. In one sentence: Graphify extracts what the code is, OKF curates what engineers know, and vector RAG searches the unstructured remainder.
context assembly
docs ยท tickets ยท Slack
long-tail text
AST call graphs
imports ยท hierarchies
architecture ยท schemas
playbooks ยท intent
The capability breakdown
| Vector RAG | Structural graph | OKF narrative | |
|---|---|---|---|
| Owns | Unstructured long tail | What the code IS | What engineers KNOW |
| Retrieval | Probabilistic (nearest-neighbor) | Deterministic (edge traversal) | Deterministic (exact file) |
| Best question | "Themes in churn interviews?" | "What calls verify_token?" | "Why does billing route through a webhook?" |
| Fails at | Multi-hop structure, canonical truth | Business intent, the why | Uncurated bulk, thematic search |
| Ingest cost | Embedding + re-indexing | Zero-LLM AST parse | Curation labor (M08's pipeline) |
"The graph replaces my vector index." โ On head-to-head QA accuracy, graph retrieval tied dense RAG (76% vs 76% on LongMemEval-S) and slightly lost on recall. Its wins are determinism and ingest cost, not retrieval quality. Evaluate it as a RAG replacement and you'll maintain graphs for problems you don't have.
"OKF is a smarter RAG." โ OKF has no retrieval at all. It's a format. At scale something must still search it โ often the same retrieval machinery RAG already provides, now pointed at something worth retrieving from.
"More layers = better agent." โ Every layer is maintenance: hooks, lint, freshness gates (M11). A layer with no failure mode justifying it is pure overhead.
Failure-Driven Adoption: The Two Triggers
Here is the discipline that separates architecture from hype-chasing: you add a layer when โ and only when โ you have observed a concrete failure mode that layer addresses. Not because a benchmark impressed you, not because a launch post went viral, and not because of vague dissatisfaction with your current RAG setup.
That last one deserves numbers. Industry surveys found that 72% of enterprise RAG deployments underdelivered or failed outright in year one โ but the dominant causes were poor chunking strategies, outdated vector indices, and inappropriate embedding models, not the absence of a graph. Adding a knowledge graph to a flawed retrieval pipeline gives you a flawed retrieval pipeline with higher operational overhead. Fix the pipeline first; as Atlan's guidance puts it: "RAG first, graph when depth becomes a bottleneck."
dependencies & calls
across team silos
(AST edges: Graphify / CodeGraph)
(curated narrative spec)
Trigger 1 โ multi-hop reasoning failures โ add the structural graph
This failure appears when an answer requires linking facts across multiple files that are never co-located in one text chunk. The symptom sounds like: your agent cannot answer "What breaks in the API layer if I alter the schema in db/migrations/04_users.sql?" It retrieves the migration file (keyword-rich, semantically close) but misses the foreign-key references, interface implementations, and cross-file calls that make up the actual answer.
Why can't better prompting fix it? Because vector RAG embeds text blocks independently. The relationship between the migration and the API handler was never captured in vector space โ there is nothing for a better prompt or a bigger context window to recover. M02 called this the structural blind spot; the fix is the layer whose entire job is relationships: the AST graph you built in Track 2.
Trigger 2 โ tribal-knowledge sprawl โ add OKF
This failure appears when critical institutional context is fragmented across Slack channels, outdated wikis, and unwritten developer memory. The symptom is subtler and nastier: the agent generates syntactically valid code that violates internal architectural guidelines, calls deprecated internal APIs, or ignores domain-specific business logic. Nothing crashes. Reviews catch it โ sometimes.
Why can't the structural graph fix it? Because AST parsers inspect source code, and business intent isn't in the source. No parse of webhooks.py reveals that the team decided (ADR-001) never to make billing settlement block on notifications. That knowledge lives in prose โ which is exactly what OKF's concept files are for, in a shape agents reliably parse.
The triggers keep you honest in both directions. orderflow's 15 files will never produce a multi-hop failure a single context window can't absorb โ so a structural graph there is ceremony (below the ~500-file floor from M04). A 3,000-file monorepo with three teams and a rotating on-call absolutely will โ and there the graph pays for itself weekly. Same tool, opposite verdicts, decided by observed failure, not fashion.
The Staged Query Pipeline
When a system has earned all three layers, how do they compose at query time? Not as alternatives the agent picks between at random โ as stages, ordered from cheapest-and-most-curated to broadest-and-noisiest. Follow one real query through: "What breaks if I modify UserService.py?"
Walk the stages in prose, because the ordering logic is the lesson:
- Stage 1 โ narrative orientation (~2K tokens). The agent reads the OKF catalog's entry for the target: what UserService is, who owns it, which modules relate. This is progressive disclosure from M09 โ a curated 2K-token orientation instead of a 50-file exploration. Output: the specific nodes worth investigating.
- Stage 2 โ structural traversal (zero LLM cost). With targets identified, the agent queries the graph: callers, implementers, transitive dependents. Deterministic answers from the M09 server; the LLM spends nothing acquiring them and everything reasoning about them.
- Stage 3 โ the unstructured remainder. Whatever the curated layers can't answer โ "did we have an incident last time someone touched this?" โ goes to vector/sparse search over Slack logs, GitHub issues, PR comments. RAG does what RAG is actually for: the long tail.
Each stage narrows the next stage's work. The narrative layer told us where to look; the graph told us exactly what's connected; search only had to cover what neither curated layer could know. Reverse the order and stage 3 runs first over everything โ which is the M00 exploration problem all over again, with extra infrastructure.
The Router Pattern
The staged pipeline suits deep investigations. But plenty of agent traffic is single questions, and those deserve a cheaper dispatch: a router that classifies each query and sends it straight to the layer that owns it. A canonical question โ "What is the SLA for Severity-1 incidents?" โ routes to a deterministic OKF read. A structural one โ "who calls this?" โ routes to the graph server. An exploratory one โ "what are common themes in churn interviews?" โ falls back to vector search.
Let's build the skeleton, in three chunks. Chunk 1 โ classification. What: decide the query class. Why: the class determines cost and failure characteristics โ a canonical query answered probabilistically is how the stale-WAU disaster of M02 happened. Gotcha: keyword rules look naive but are transparent and debuggable; teams often run rules first with an LLM-classifier fallback, not the reverse.
STRUCTURAL_CUES = ("who calls", "what calls", "depends on", "call graph",
"imports", "blast radius", "what breaks", "cycle")
CANONICAL_CUES = ("definition of", "canonical", "sla", "policy",
"runbook", "how do we compute", "what is our")
def classify(query: str) -> str:
q = query.lower()
if any(cue in q for cue in STRUCTURAL_CUES):
return "structural"
if any(cue in q for cue in CANONICAL_CUES):
return "canonical"
return "exploratory" # default: the broad, noisy layerconst STRUCTURAL_CUES = ["who calls", "what calls", "depends on", "call graph",
"imports", "blast radius", "what breaks", "cycle"];
const CANONICAL_CUES = ["definition of", "canonical", "sla", "policy",
"runbook", "how do we compute", "what is our"];
function classify(query) {
const q = query.toLowerCase();
if (STRUCTURAL_CUES.some(c => q.includes(c))) return "structural";
if (CANONICAL_CUES.some(c => q.includes(c))) return "canonical";
return "exploratory"; // default: the broad, noisy layer
}Chunk 2 โ dispatch with fallbacks. What: send each class to its layer, and fall through when a layer misses. Why: the M09 server returns a graceful miss ("not in graph โ try grep") precisely so a router can catch it and degrade to search instead of failing. Gotcha: log every route decision โ when the router misroutes (it will), the log is how you tune the cues.
import logging
log = logging.getLogger("router")
def route(query: str) -> dict:
kind = classify(query)
log.info("route=%s query=%r", kind, query)
try:
if kind == "structural":
answer = graph_query(query) # M09 MCP tools
if "error" in answer: # graceful miss -> degrade
log.warning("graph miss, falling back to search")
return {"layer": "vector", "answer": vector_search(query)}
return {"layer": "graph", "answer": answer}
if kind == "canonical":
return {"layer": "okf", "answer": read_concept(query)} # exact file
return {"layer": "vector", "answer": vector_search(query)}
except Exception as exc:
log.error("layer failed: %s โ degrading to vector search", exc)
return {"layer": "vector", "answer": vector_search(query)}async function route(query) {
const kind = classify(query);
console.error(`route=${kind} query=${JSON.stringify(query)}`);
try {
if (kind === "structural") {
const answer = await graphQuery(query); // M09 MCP tools
if (answer.error) { // graceful miss -> degrade
console.error("graph miss, falling back to search");
return { layer: "vector", answer: await vectorSearch(query) };
}
return { layer: "graph", answer };
}
if (kind === "canonical")
return { layer: "okf", answer: await readConcept(query) }; // exact file
return { layer: "vector", answer: await vectorSearch(query) };
} catch (exc) {
console.error(`layer failed: ${exc} โ degrading to vector search`);
return { layer: "vector", answer: await vectorSearch(query) };
}
}Chunk 3 โ what the leaf functions are. Nothing new: graph_query wraps the M09 MCP tools; read_concept is a python-frontmatter read of the matching bundle file (M06); vector_search is whatever retrieval you already had. The router adds no storage of its own โ it is pure traffic control over layers you built in earlier modules.
You turned three independent layers into one system with ~40 lines of dispatch. Note the asymmetry of the fallbacks: everything degrades toward vector search (broad, probabilistic, always available) and never toward OKF (exact answers only โ a fuzzy fallback pretending to be canonical is precisely the M02 disaster).
The Evidence
Does the assembled stack actually help? Two data points frame the answer โ one for the upside, one for the cost of not having it.
The ERPNext case study
The best production-shaped benchmark in the corpus ran on ERPNext, an open-source enterprise repository of over 1,000,000 lines of Python and JavaScript โ real scale, not a 52-file demo. Evaluations used Claude Opus 4.8, capped at 14 turns per task:
- Baseline agent (vector RAG / grep exploration): 70.8% key-fact coverage.
- Graphify + OKF hybrid pipeline: 82.0% key-fact coverage, averaging ~140,000 tokens per query session.
Sit with what an 11.2-percentage-point improvement means on a million-line codebase: roughly one in nine facts the baseline agent missed or got wrong, the hybrid agent got right. Missed facts are wrong refactors, broken callers, violated conventions. That accuracy delta is a far more reliable indicator of production value than any synthetic token-reduction multiple โ token savings tell you the agent was cheaper; fact coverage tells you it was right.
The cost of retrieval-only
On the other side of the ledger: research into legal-domain RAG systems โ a field with high-stakes, multi-document questions much like large codebases โ found standard retrieval tools hallucinate in 17% to 33% of complex queries, because retrieval alone fails to model multi-document logic. That is the gap the structural and narrative layers exist to close: not making search better, but removing whole question classes from search's jurisdiction.
Notice which number this module leads with: an accuracy gain measured on a 1M-LOC repo with a stated model and turn cap โ not a 71.5ร token multiple from a favorable 52-file corpus. When you evaluate this architecture for your own team, replicate the ERPNext shape: your repo, a fixed task set, fact-coverage scoring, before and after. M04 taught you why: token multiples are corpus-topology-dependent; correctness deltas are what your users experience.
The Decision Matrix
Locate your codebase's shape and your observed failure mode; the cell tells you what to add next. (No observed failure โ the answer is always "nothing yet.")
| Environment | Multi-hop failures observed | Tribal-knowledge failures observed | No concrete failures yet |
|---|---|---|---|
| Monolith (<500 files) | Usually fits in context โ verify before adding a graph (below the payoff floor) | Add a small OKF bundle; curation cost is low at this size | RAG-only is fine; fix chunking first |
| Microservices | Add the structural graph โ cross-service call chains are exactly what chunks never co-locate | Add OKF โ ownership boundaries and contracts are narrative knowledge | Keep RAG; add contract concepts opportunistically |
| Monorepo (1000s of files) | Structural graph, with M11's freshness ops from day one | OKF with lint + governance (type drift arrives with team count) | Instrument agent failures first; adopt from data |
The matrix encodes this module's whole argument: the environment sets the cost of each layer, the failure mode sets the value, and you adopt when value exceeds cost โ never on hype.
Walk it, step by step
One question sent to each layer in turn, including the layers that answer it badly. The routing rule at the end is what the whole module is for.
MCP vs RAG vs OKF: One Metric, Three Roles
A final source of confusion to dissolve: MCP, RAG, and OKF get name-dropped together so often that they blur. They occupy different positions in the timeline of a question. OKF is knowledge written down in advance. MCP is the live connection to systems at question time. RAG is on-the-spot retrieval over what nobody curated.
orderflow's WAU metric shows all three in one story. The definition of weekly active users โ 7-day window, completed orders, testers excluded โ lives in an OKF concept file, written once, versioned in git (M06). When an agent must compute this week's WAU, the definition doesn't run queries: the agent calls a database tool over MCP to execute the SQL against the live warehouse. And when someone asks "have we ever debated changing the WAU window?" โ that discussion lives in old Slack threads and PR comments, which is RAG's territory.
One more composition worth knowing: OKF bundles can themselves be RAG targets. Once a bundle grows past what an agent browses by hand, a search layer indexes it โ and clean, structured Markdown with explicit links is far better material to index than PDFs chopped into arbitrary chunks. The honest picture isn't "OKF instead of RAG"; it's "RAG, pointed at something worth retrieving from."
Hands-On: Route 10 Queries
What you'll do: act as the router. For each orderflow query, pick the layer that should own it. Immediate feedback per pick โ this exercise is embedded (no lab folder). Time: 15 min.
Q1. "Who calls verify_token, directly and transitively?"
Q2. "What is the canonical definition of weekly active users?"
Q3. "What were the recurring complaints in last quarter's support tickets?"
Q4. "What breaks if orders_fact gains a column?"
Q5. "Why is the event bus in-process instead of a broker?"
Q6. "Is there a dependency cycle anywhere in services/?"
Q7. "What's our runbook for rotating the payments signing key?"
Q8. "Did anyone discuss rate-limiting webhooks before? What was decided?"
Q9. "Which functions does advance_order reach in two hops?"
Q10. "Summarize how our architecture docs describe the notifications service."
8+ correct: you route like the architecture intends. The pattern to internalize โ structure โ graph, canon โ bundle, everything undocumented โ search โ plus one refinement: deep investigations (Q4) legitimately use stages in sequence, not a single layer.
Knowledge Check
1. What is the correct one-sentence division of labor across the three layers?
2. Your agent writes syntactically valid code that keeps using a deprecated internal API. Which trigger fired, and what do you add?
3. Why does the staged pipeline put OKF orientation BEFORE graph traversal?
4. In the ERPNext study, why is the 70.8% โ 82.0% key-fact result a better adoption signal than a 71.5ร token multiple?
5. In the router, why do all failures degrade toward vector search and never toward OKF?
6. "72% of enterprise RAG deployments underdelivered in year one." What does this module conclude from that?
Module Summary
Layers, not competitors
Graph = what the code is. OKF = what engineers know. RAG = the unstructured remainder. Graph retrieval TIED dense RAG on accuracy โ its wins are determinism and ingest cost.
Failure-driven adoption
Multi-hop failures โ structural graph. Tribal-knowledge sprawl โ OKF. No observed failure โ add nothing. 72% of RAG failures are pipeline quality, not missing graphs.
The staged pipeline
OKF orientation (~2K tokens) โ graph traversal (zero LLM cost) โ vector search for the remainder. Each stage narrows the next.
The evidence
ERPNext, 1M+ LOC: 70.8% โ 82.0% key-fact coverage (~140K tokens/session). Accuracy deltas beat token multiples as adoption signals. Retrieval-only hallucinates 17โ33% on complex multi-document queries.
What we built on orderflow: a router dispatching canonical โ bundle, structural โ graph server, exploratory โ search โ with all failures degrading toward the honestly-probabilistic layer.
Next module preview: the stack works โ on the day you built it. M11 is about every day after: silent hook failures, artifact desync, the scale wall, and the observability that separates a production system from a demo that hasn't failed yet.
References
- "Graphify, OKF, or Both? Beyond RAG for Codebases" โ the three-layer framing and decision matrix (Towards AI, Jul 2026)
- Graphify BENCHMARKS.md โ LongMemEval-S / LOCOMO head-to-head results
- Atlan โ "Knowledge Graphs vs RAG" (2026): "RAG first, graph when depth becomes a bottleneck"
- Prism Labs โ "Beyond Vector Embeddings in 2026" (chunk co-location)
- ERPNext hybrid-pipeline evaluation (1M+ LOC case study)
- Course modules: M02 (RAG limits), M06โM08 (OKF), M09 (serving)