Build1 publisher3 min readPublished
Dropping the vector database hands chunk boundaries to Google's whitespace rule
Maneshwar's Go build answers questions over 1.4 million tokens of internal docs through a hosted Gemini File Search store, paying for embeddings once at indexing and giving up every retrieval knob except the markdown.
The Engineer · Build desk

What happened
- Maneshwar published a Go tool that answers questions over about 1.4 million tokens of internal books, essays and postmortems using Gemini File Search, with no vector database, chunker or reranker.
- Google charges once at indexing time at embedding prices, keeps storage and query-time embeddings free, and bills the chunks the model retrieves as ordinary context tokens on the same call.
- The implementation is one Go binary with SQLite through modernc.org/sqlite, a hand-rolled REST client so request and response bodies can be logged verbatim, and two model calls per question.
- Of three PDF converters tried, pymupdf4llm found forty-four real headings in one essay, where markitdown glued words to their neighbours and pdftotext discarded headings entirely.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- decision Chunk quality is now decided in the document converter, which moves retrieval tuning out of query-time code and into a make target that nobody reviews.
- cost Teams that keep spare keys for headroom have to budget ingest as an N-times operation, so a corpus that changes weekly makes re-indexing the expensive part of the design.
- constraint The design holds only while the corpus stays under the store cap and the question rate stays under the per-minute quota, and neither limit can be relieved by adding capacity you control.
- capability For a corpus this size a team can get grounded, cited answers without ever choosing an embedding model, so building a vector store turns into a question about corpus size and request rate.
The chunking decision moved in this build. File Search chunks uploaded documents on whitespace with a token budget [7]. That rule belongs to Google. The one input still under the builder's control is the whitespace in the markdown he uploads, and that is where an evening went [8].
markitdown gave him headings of a sort, and it also gave him this out of a PDF: `Theubiquityoffrustrating,unhelpfulsoftwareinterfaceshasmotivateddecadesofresearch` [8]. Maneshwar wrote that the embedding model does not know what `Theubiquityoffrustrating` is, and neither does the retrieval [9]. pdftotext fixed the spacing and threw away every heading, so a 300-page book became one undifferentiated scroll [10]. pymupdf4llm reads font sizes to decide what is a heading, keeps bold and italics, and handled the spacing on the same PDF [11]. On one essay it found 44 real headings where the others found none [12]. "A chunk that starts at a heading is a chunk that means something on its own," he wrote [13]. The conversion now sits behind `make process-data`, with EPUBs, PDFs and a sync of the blog repos landing in one markdown tree [14].
That count measures the converter. Forty-four headings tell you the chunk boundaries fell on section starts; whether the right chunk comes back for a given question is a separate measurement.
The second thing I would check before adopting this is where the store lives. A File Search store sits inside the Google Cloud project behind the API key that created it [15]. Create a store with key A, search it with key B from a different project, and the error tells you nothing useful: as far as key B is concerned, the store is simply not there [15]. Free-tier per-minute caps are real, and the usual fix is rotating requests across several keys, which a tool-attached call cannot do [16]. So the keys got roles: `keys/store.txt` lists keys that each own a complete copy of the corpus in their own store, ingest uploads to all of them, and the search call tries store 0 with key 0, falling through to store 1 with key 1 on a quota or auth failure [17].
Indexing is the one step you pay for [4]. A fallback chain of N keys pays that one-time embedding bill N times, and every corpus update re-indexes N stores [19].
The storage cap has slack at this size. A 1 GB free-tier store against 5.3 MB of markdown leaves room for roughly 190 copies of the whole shelf [18]. The per-minute rate is the limit the key chain exists to work around [16].
For this to transfer, your corpus has to fit under the cap, change rarely enough that re-ingesting every store stays cheap, sit inside the per-minute quota at your question volume, and survive being cut on whitespace against someone else's token budget. Retrieval is free at query time; the chunks the model pulls in are billed as ordinary context tokens on the call you were already making [4].
What to watch
- Whether Google exposes chunking parameters or lets one store be shared across projects. Either change removes the copy-per-key workaround.
- Paid-tier quota and store limits: the build is described entirely on the free tier, so cost per question at volume is untested here.
- A retrieval accuracy comparison on the same corpus against a self-hosted pgvector baseline. That comparison would price what the hosted chunker costs in answer quality.