Embeddings Are Cheap Enough for Personal Wikis Now
My Obsidian vault is my main second brain: around 1,800 Markdown notes. Lately I’ve been less interested in what to put in it and more interested in a different question: what useful tools could I build on top of it?
A few ideas came to mind. Could I do better than Obsidian’s built-in search for the times I remember the idea but not the exact words I used? Could I eventually wrap the whole thing in my own little plugin? Both sound like fun future projects. But the one I wanted to tackle first was better backlinking: getting the system to find related notes and suggest links, so the graph helps maintain its own structure.
So I did what any reasonable person would do. I turned it into a small experiment.
The task was deliberately concrete: given an ordinary note, suggest the topic notes it should link up to. In Obsidian terms, that means adding a frontmatter field like this:
---
up:
- "[[TypeScript]]"
- "[[Agentic Harness Landscape]]"
---
Call them parent backlinks, MOC links, or knowledge graph edges. The label matters less than the shape: each note can have zero, one, or several parents, and the system should never force every note into exactly one folder.
The Setup
My vault had about 1,800 notes, 27 topic hubs (the pages other notes already linked to, like “TypeScript” or “Tailwind CSS”), and a hand-labeled gold set of 38 notes. No vector database. No pre-existing up: links.
Every approach had to emit the same report schema, so I could compare methods mechanically instead of eyeballing a few good examples. And nothing wrote to the vault by default. Applying links was a separate, explicit step. That “report first, apply later” rule is the difference between a useful automation and a spooky one.
The Baseline: Full-Text Search
I started with classic keyword retrieval: index the topic notes, turn each child note into a query, score with Postgres full-text search plus trigram similarity. It’s a good baseline because it’s deterministic, cheap, and explainable.
But it has an obvious weakness in a personal wiki. A note can be about “serverless container hosting” without ever saying “Azure Container Apps.” A note about pyenv and virtual environments might belong under “Python” without naming it. FTS is great when the words line up. Personal notes often don’t.
The Embeddings Version
The embeddings approach was simple: build a text representation for every topic note and every child note, embed both with Google’s Gemini Embedding 2 model, normalize, take the cosine similarity, and keep the top matches above a threshold.
The text prep mattered more than anything else. For child notes I used the title plus opening body. For topic notes, the title, aliases, summary, and first section, stripping wiki-link syntax. MOC pages are often long lists of [[Some Note]] links, and that’s worse input than a short prose description of what the topic actually means.
I did not need vector infrastructure. Vectors were stored as plain arrays in Postgres, cosine similarity computed in Python. With 27 topic vectors and 1,800 child notes, brute force ran in about 6 seconds, and embedding the whole vault plus every test run cost about 30 cents. At this scale, architecture matters less than good text prep.
The Numbers
On the 38-note gold set:
| Approach | Recall@1 | Recall@3 | MRR | F1 |
|---|---|---|---|---|
| Full-text search | 0.519 | 0.615 | 0.609 | 0.424 |
| Embeddings | 0.923 | 0.962 | 1.000 | 0.622 |
Of the 30 intended links across the gold set, embeddings found 28 and missed 2. Full-text search found 18 and missed 12.
The precision wasn’t amazing, and that’s fine. The goal of the first pass is high-recall suggestion, not automatic writing. It produces candidates for review. A later LLM reranker or a human can tighten precision.
One detail surprised me. I tested two query framings: one worded as “search result,” one as “semantic similarity.” Ranking was identical, but the similarity framing had worse precision because it surfaced loosely related matches. For “which topic should this note be filed under?”, the task behaves more like search than duplicate detection. Semantic search and semantic similarity are not the same product requirement.
Thresholds and the Gold Set
My first threshold was too low. Everything looked related to everything. Raising cosine similarity to around 0.70 cut the output to a reviewable set: of 1,805 notes scanned, 682 got at least one suggestion, averaging about 2.4 parents each. The rest got nothing, which is correct. Plenty of notes are fragments, logs, and one-offs. A good backlink suggester should be comfortable saying nothing.
You don’t find that threshold on a model card. You find it on your own notes.
Which brings me to the most valuable artifact in the whole project: the gold set. It wasn’t the embedding index. It was 38 hand-labeled notes, including negative examples with no expected links. Without it, I’d have judged by vibes, and that’s dangerous with embeddings because the good examples look magical. You need the misses in the same report or you’ll overestimate the system. Thirty to fifty labeled examples is enough to expose bad assumptions.
What I’d Actually Do
If I were adding this to another wiki, the order would be:
- Pick 20 to 100 topic notes explicitly, by folder, tag, or inbound link count. Don’t make every note a possible parent.
- Label 30 to 50 examples, negatives included.
- Build an FTS baseline first. If embeddings can’t beat it on your labeled data, your text prep is wrong.
- Build the embedding baseline and compare.
- Tune text prep and threshold.
- Emit a report before any writes.
- Only then consider LLM reranking or a real vector database.
That order keeps you honest. You’ll know whether embeddings help your wiki before you spend time getting Qdrant setup.
Personal wikis are small enough that we can stop treating semantic search as an enterprise architecture problem. The interesting work isn’t the vector database. It’s deciding what “related” means in your own graph, measuring it against your own notes, and building the tool so it suggests, explains, and writes only when asked. Use semantic search to propose structure. Use reports and gold sets to keep it honest. Let the human knowledge base stay human.
I’ll probably keep poking at it.
I’d appreciate a follow. You can subscribe with your email below. The emails go out once a week, or you can find me on Mastodon at @[email protected].