I run a RAG system off a 2017 Intel NUC to answer questions about my D&D campaign. It's built on session notes and NPC write-ups that were never meant for a machine to read, just for me and three other players to find things in Obsidian six months later. So when Google Cloud published the Open Knowledge Format in June, a spec for structuring exactly that kind of markdown-and-links vault, I had an odd realisation: mine already looked like one, by accident. The question I actually wanted answered wasn't "should you adopt this spec," which is the take every hot take on it has gone with. It was narrower. I already had a working system and a list of queries it couldn't answer. Would OKF's structure fix any of them?

Short answer: one, cleanly. The rest is a more useful story than a yes or no.

The setup

The stack is deliberately cheap. Embeddings run locally with BGE-M3 on the NUC itself, an i3-7100U with 8GB of RAM, no GPU. Generation goes out to AWS Bedrock's Converse API, which lets me swap models by changing one string. I started with 87 notes, chunked into 769 pieces for the vector index; the vault has grown a fair bit since. The notes live in Obsidian: [[wikilinks]] between anything that references something else, YAML frontmatter on every file, folders that already sort roughly into types: People, Places, Sessions, Bad Guys, Organisations, Monsters. None of that was designed for retrieval. It's just what two years of actually using Obsidian looks like.

To know whether any of this worked, I built a 24-query golden set early on, split into three tiers: lookup (one fact, one note), synthesis (the answer needs combining more than one note), and temporal (how something changed over a few sessions). Every experiment below gets scored against the same 24 queries, which is the only reason the results are comparable at all.

Where plain vector search breaks

Vanilla retrieval (embed the query, pull back the top five chunks by similarity) does fine on lookup questions. It falls over on synthesis and temporal ones, and not for one reason. Running the same queries at k=5, 10, and 15 split the failures into three distinct shapes:

Some were budget-hogging: one relevant note eats two or three of five available slots because its chunks all score similarly, starving anything else out. A bigger k fixed most of these outright: five of my eleven flagged queries went from partial to perfect just by asking for ten or fifteen chunks instead of five.

One was coincidental lexical overlap. A query about a cursed ward device kept pulling back five chunks that all shared surface phrasing with the question, "someone doing something, something reacting to it", none of them actually about the ward. More k didn't help. Fifteen chunks of the wrong thing is still the wrong thing.

Two, referred to from here as q12 and q13, just sat there. Recall stuck at 0.67 and 0.50 no matter how much context I gave the model. No obvious mechanism. Genuinely unexplained, and it stayed that way for a while.

Giving the model room to search again

The next move was agentic retrieval: instead of one fixed search, let the model call a search_knowledge_base tool up to five times per answer and decide for itself when it had enough. I ran this against both models I'd been comparing throughout, Amazon Nova Lite and Anthropic's Haiku 4.5.

It helped exactly one of them. Haiku's synthesis-tier recall went from 0.60 to 0.76 with the agent loop. Nova Lite's went from 0.60 to 0.54, slightly worse, because it mostly didn't bother using the extra searches. The cost of finding out: roughly 4.2x the per-query cost and 2.1x the latency for Haiku, 2.5x cost and 3.7x latency for Nova Lite, for a model that barely used the budget it was given.

The ward-device query, my coincidental-overlap case, is the clearest illustration of the gap between the two models. Nova Lite ran the same search three times with barely any variation, burned its whole budget, and returned nothing. Haiku tried eight genuinely different angles on the question and then said, honestly, that it couldn't find the answer. Recall for both stayed at zero: an agent can search harder, but it can't retrieve a note that isn't relevant, however many ways it phrases the search.

q12 didn't move. Neither did q13, mostly. Two rounds of fixes in, both were still open questions.

Enter Google's Open Knowledge Format

OKF, published 12 June 2026, is short enough to read in five minutes. A bundle is a directory of markdown files. Each file is a concept (a person, a place, a table, whatever) with a small YAML frontmatter block: typetitledescriptionresourcetagstimestamp. Only type is required. Concepts link to each other with plain markdown links, and those links form a graph, richer than a folder structure alone. That's the whole spec. Andrej Karpathy had already named the underlying pattern the ""LLM wiki"" back in April; Google's contribution is agreeing on field names so different tools can read the same bundle without a translation layer.

My vault already had most of it. Every note had title and tags, because that's what makes an Obsidian vault usable in the first place. The folders mapped cleanly onto concept types. The notes linked to each other constantly, over four thousand times across the vault, for the same reason people link anything: so they can find it again later. What was actually missing was small: no type field anywhere, and the links, real as they were, only ever got read by Obsidian's own renderer. My retrieval pipeline chunked notes and embedded them for vector search; it never once looked at a link between two notes. The wikilinks were decoration as far as retrieval was concerned. That gap, structure that exists but isn't used, was the whole experiment.

Building a producer

Converting the vault meant a script that walks every note, assigns a type from its folder, adds a mechanically-extracted description (the first sentence of the body, no LLM rewrite, and I'm not going to pretend that's more sophisticated than it is), stamps a timestamp, and rewrites [[wikilinks]] into the plain markdown links OKF actually specifies. The type mapping was a flat lookup: People → Person, Places → Place, Sessions → Session Log, Bad Guys → Antagonist, Organisations → Organisation, Monsters → Monster, and so on across 288 notes.

The frontmatter took an hour. The links took the rest of the afternoon. Of roughly four thousand wikilinks in the vault, ninety percent resolved cleanly to a real note on the first pass. The other ten percent split into two separate problems, neither caused by the conversion itself. Some links were already dead: a character referred to by a nickname nine times in her own note, with no note or alias ever created to catch it, broken in Obsidian long before I touched anything, Obsidian just never tells you because grey unclickable text doesn't stand out. Others were ambiguous: a handful of notable NPCs existed as two separate files, once under People and once under Bad Guys, almost certainly a villain reclassified at some point without the original getting deleted. Converting to OKF forces a single answer for every link, so both problems got written to a report instead of silently guessed at.

One real bug turned up here too: a handful of image embeds and a note transclusion use a different bracket syntax, meant to insert content rather than link to it. The first version of the script didn't know the difference and quietly turned a working embed into a broken image link. Fixed by leaving that syntax alone.

Building a consumer, and the rule that keeps the test honest

None of this replaces the vector search. The same BGE-M3/Chroma setup still does the actual matching, finding the chunks closest to the query embedding. What OKF adds sits after that: vector search finds the five closest notes, then graph-walk follows whatever those five link out to, one hop further, on the theory that a missing note is often sitting one click from something that was already found.

The lazy way to test this is to compare graph-walk against plain search at the same starting budget, five notes versus five-plus-neighbours, and call it a win when the bigger set does better. That's not a result. The k-experiment already proved more context helps on its own. So the actual comparison has to be graph-walk against vector search at k=10 or k=15, roughly matched context sizes, two different ways of choosing what's in them.

Two things broke before I got anywhere near real numbers, both found by running the thing against the real vault rather than assuming it would behave the way I expected. First: a link only runs one way as written. A graveyard's own note links out to nearby places and NPCs, never to the two session logs that mention it, because a session note records where the party went, not the other way round. Following links outward from the graveyard alone never reaches the sessions. Fixed by treating every link as bidirectional, which is what Obsidian's own backlinks pane already does without you noticing.

Second, the moment that was fixed: some notes get mentioned constantly, across sessions that have nothing to do with each other. That same graveyard note is one of them. Once links ran both ways, walking out from just two starting notes reached 267 of the vault's 288 notes: not a missing note turning up nearby, just most of the vault, relabelled. So the walk gets capped at roughly the same size as the vector-only comparison, so neither method wins just by having more material on the table.

What actually happened

Same 24 queries, same three tiers, same recall metric (the fraction of notes a question actually needs that a method actually found), run against vector search at k=5, 10, and 15, and against graph-walk built from k=5 plus one capped hop:

| Tier | Vector k=5 | Vector k=10 | Vector k=15 | Graph-walk (capped) |
|---|---|---|---|---|
| Lookup | 1.00 | 1.00 | 1.00 | 1.00 |
| Synthesis | 0.60 | 0.85 | 0.85 | 0.81 |
| Temporal | 0.62 | 0.81 | 0.88 | 0.81 |

Graph-walk beats the k=5 set it's built from. It never catches k=15, and it does this while spending almost its entire context budget on nearly every query; k=15 gets a slightly better result on less material. That's the honest headline, and it's not the one I expected going in: the graph doesn't beat deeper vector search, and where it loses, it loses at an equal or larger cost.

The per-query breakdown is where it earns its keep. One query (the two notes that genuinely reference each other) never got above 0.50 recall at any k I tried, up to fifteen. Graph-walk got it every time, because those two notes link to each other directly, and no depth of semantic similarity search ever put them in the same top-fifteen together. That's a real structural relationship embeddings genuinely can't see, and a link genuinely can.

Three queries went the other way: deeper vector search solved them, graph-walk didn't move at all, because the missing note apparently wasn't one hop from anything k=5 had already found. Neither method subsumes the other; they catch different misses.

q12, the query that stayed flat through the k-experiment and the agent, stayed flat here too. Still no explanation, across three separate fixes now. And the coincidental-overlap ward query, my control case, stayed at exactly zero, as predicted: a graph edge can't invent a connection that was never in the source material. Structure fixes structural misses. It was never going to fix a ranking problem.

Was it worth building

Yes, on cost alone. The producer and consumer together are a few hundred lines and an afternoon apiece, on top of frontmatter and links a reasonably well-kept vault already has for other reasons. For that, it caught one real relationship that three separate rounds of fixing (bigger k, a second model, an agent free to re-search) never found. A link found it instead.

The cases where deeper vector search wins aren't really a cost of adding the graph. They're a reminder it's a second way of finding notes, not a replacement for the first. Nothing stops running both and keeping the union: vector search at whatever k you'd use anyway, plus a capped hop layered on top, only asked to add what vector search missed rather than beat it outright. On that basis it's hard to find a reason not to add it, once the frontmatter's already sitting there for other reasons.

The two queries nothing has fixed are the honest limit, not the cost. A cross-link graph fixes one specific kind of miss: two things genuinely connected but never phrased alike enough for an embedding to notice. It was never going to fix the ones that aren't that.

What I'd take from this if you're weighing the same thing

If your data already has structure that only humans read (a wiki, a set of internal docs with cross-references, anything with links nobody built for a machine), the gap between "looks like a graph" and "is a graph your retrieval code uses" is worth checking. It cost me an afternoon to find out, converting the vault surfaced a few hundred already-broken links I didn't know about as a free bonus, and it fixed exactly the kind of failure that more context, a better model, and letting the model search again all failed to touch. It's not a replacement for any of those. It's one more tool that catches a specific kind of miss, cheap enough that there's not much argument against having it.

I write this project up in more depth, code included, as a running series at jamielab.uk. This piece condenses three separate posts' worth of experiments into one. Next on my list is revisiting whether a small local model can take generation back from the cloud now, a year after I decided the NUC couldn't carry it. I already have a guess about how that one turns out. I've been wrong about that kind of guess three times running this year, so we'll see.