I sat down to write the pgvector section of this post: the HNSW index DDL, the reranker batching, the metadata filter shapes. Then I realized I kept reaching for the wrong file. The query I was proud of wasn't the vector search. It was dedupeRagChunks.
So the post changed shape. I'd been treating retrieval as a storage problem (pick the right index, tune the right parameters) when the engineering that actually broke and got fixed lives above it: how queries get constructed, how overlapping chunks get collapsed, how the final context gets formatted before a downstream model ever sees it.
This is a post about the parts of RAG that don't make it into architecture diagrams. Drawn from real call sites and commit history.
RAG is a chain of small, testable transforms
The non-obvious part of production retrieval isn't embeddings. The middle of the chain is where the work is:
- how you choose what to search for,
- how you dedupe overlapping chunks,
- how you format the final context so downstream steps don't drown.
That middle section is where most retrieval systems break or succeed.
In the index module, Stage 1 is described as:
"Deep Research, scans repos, fetches contexts, runs RAG queries, then persists research_context and enqueues the 'draft' stage."
And the imports tell you what the research stage considers core:
ragQuerydedupeRagChunksformatRagResults
Those names are the shape of the system. Even without the internals, the design intent is easy to infer: retrieval reads as a pipeline producing a clean, bounded "research context" artifact, rather than a single SQL statement.
Panning for gold is the closest analogy I've got. Embeddings get you to the right riverbed. Dedupe and formatting are the sieve that stops you dumping wet gravel into the next stage.
Stage 1 research orchestrates the retrieval utilities
The research stage is a Supabase Edge Function that chains multiple steps. Here's the import surface, and it matters, because it puts the retrieval utilities inside the production pipeline instead of a README promise.
// supabase/functions/blog-stage-research/index.ts
// STAGE 1 + 1b: Deep Research — scans repos, fetches contexts, runs RAG queries,
// then persists research_context and enqueues the 'draft' stage.
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.38.4';
import {
corsHeaders, SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY,
REPOS, STOPWORDS,
setActiveGeneration, broadcastProgress, isCancelled, updateRun,
fetchBlocklist, ragQuery, dedupeRagChunks, formatRagResults,
fetchHighlights, fetchPreviousPosts, buildDiversityContext,
THEME_COOLDOWN_DAYS,
} from '../_shared/blog-utils.ts';
import { fetchRepoContext, RepoContext } from '../_shared/blog-github.ts';
That surface forces separation of concerns, which is the thing I like about it. Stage 1 stays an orchestrator. Retrieval logic lives in the blog utils module, which makes it easier to harden retrieval without turning the stage handler into a god-function.
The tradeoff is real too. If the blog utils module turns into a junk drawer, you get implicit coupling. The fact that retrieval is broken into ragQuery / dedupeRagChunks / formatRagResults is a good sign: it's already modular.
The data flow
The full pipeline is: embed → index → ANN search → candidate fetch → cross-encoder rerank → final context assembly. Here's what the codebase makes explicit:
- a retrieval step (
ragQuery) - a dedupe step (
dedupeRagChunks) - a formatting step (
formatRagResults) - a final persistence into
research_context(described in the comment)
Repo context can also come from GitHub or from an "embeddings fallback" mode, via fetchRepoContextFromEmbeddings.
Here's the architecture diagram.
The pgvector HNSW query and the BGE reranker sit beneath ragQuery. That's the storage layer, and this post skips it, because the pipeline sitting on top is where the more interesting engineering happened.
Embeddings fallback (and why it matters for retrieval)
One of the more concrete changes in the commit history:
feat: embeddings fallback when repo not on GitHub (a CRM integration)- and later fixes around that fallback, including prioritizing "feature files over infrastructure"
The logging line alone tells me something real: the system can construct a RepoContext from an embeddings-backed store, and it tracks:
- file paths
- dependencies
- synthetic commits
Here's the log line from that change:
console.log(`[fetchRepoContext] Embeddings fallback for ${repoFilter}: ${allFilePaths.length} files, ${dependencies.length} deps, ${syntheticCommits.length} synthetic commits`);
Not fluff. If repo context can't be fetched from GitHub reliably, the retrieval system turns brittle, and the best ranking in the world doesn't help if the corpus disappears. The fallback is an availability feature, not a relevance one.
Query construction pulls search terms out of code identifiers
Query construction lives in the blog utils module, and that's where I changed how search queries get extracted.
The commit message says:
- "extract code identifiers for RAG queries"
The diff shows the older heuristic ("first technical term from a code block") getting replaced. The key pieces:
- there is a function
extractSearchQueries(title: string, tags: string[], content: str...) - it used to look for the first line of a code block using this regex:
const codeMatch = content.match(/
```(?:python|typescript|javascript|go|rust)?\n(.+)/);
…and then keywords got derived from that first line.
Two things I learned the hard way building retrieval systems like this:
- naive keyword extraction from code fences is fragile, since code blocks often open with imports or boilerplate,
- identifiers are a better thing to search on, if you can extract them deterministically.
The retrieval pipeline utilities
Those Stage 1 imports (ragQuery, dedupeRagChunks, formatRagResults) are the retrieval API surface. The implementations live in the blog utils module.
Function signatures matter less here than the split itself: three named steps, query and dedupe and format, instead of one opaque call. Each step is independently testable. When retrieval quality degrades, you know which step to instrument first.
Retrieval as a stage, not a query
Treating "RAG" as a synonym for vector search is tempting, but in this codebase the more interesting engineering choice is that retrieval is a stage with explicit artifacts.
- Stage 1 "persists
research_context" (comment) - Stage 1 then "enqueues the 'draft' stage" (comment)
That's a production posture. Retrieval output isn't ephemeral. It's stored, inspectable, and can be replayed.
The limitation arrives in the same box. If the stored context is wrong, downstream stages will be wrong consistently. Which is why dedupe and formatting are first-class steps. They're the choke points where garbage can be cut early.
Retrieval fallbacks and prioritization both had to be fixed
The diff history shows multiple fixes around the embeddings fallback:
- "embeddings fallback when repo not on GitHub"
- "embeddings fallback prioritizes feature files over infrastructure"
- "filePaths → allFilePaths reference in embeddings fallback log line"
The story is familiar. Fallbacks tend to start as "just make it work." Then you notice the fallback corpus is dominated by the wrong material, infrastructure and boilerplate, and you have to bias it toward feature files. The prioritization work is captured in the commit history: the heuristic changed, and the pipeline got better.
What I shipped
I built and shipped a retrieval stage that's explicitly wired into my blog research pipeline. ragQuery feeds dedupeRagChunks, which feeds formatRagResults, and the result is persisted as research_context before drafting even begins. I am not reproducing the pgvector HNSW SQL or the BGE reranking machinery here.
