Skip to content

Build1 publisher3 min readPublished

A RAG demo becomes a product at the tenant boundary, not the retriever

A dev.to build log for KnowBase AI publishes the load-bearing parts: retrieval scoped by workspaceId, chunks that keep a pointer home, and citations you can click. The retriever is the swappable half.

The Engineer · Build desk

Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction

Illustration accompanying A RAG demo becomes a product at the tenant boundary, not the retriever
Generated illustration

What happened

  • A dev.to writeup describes KnowBase AI, a multi-tenant SaaS knowledge base where businesses upload documents and an AI assistant answers questions grounded in their own content, with source citations you can click.
  • The author writes that every "build a RAG chatbot" tutorial ends the same way: embed a few paragraphs, call similaritySearch, print the answer, and that this gets you a demo, not a product.
  • The post says RAG in production means four steps: ingestion (upload, chunk intelligently, store), retrieval (find chunks that answer the question), grounding (build a prompt from retrieved context, no hallucinated answers), and trust (show source citations); each step is a small product on its own.
  • The chunkText function defaults to size = 800 characters and overlap = 200 characters, slicing text with overlap so no meaning falls through the gaps.
  • With size 800 and overlap 200, the chunker advances 600 characters per chunk.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

A developer has published the build log for KnowBase AI, a multi-tenant SaaS knowledge base where businesses upload documents and an assistant answers from their own content with clickable source citations [1]. The useful part is not the retrieval trick; it is that the two things holding the product up, tenant scoping and chunk provenance, are both decided at schema and ingestion time.

The author's framing is blunt: the standard tutorial embeds a few paragraphs, calls similaritySearch, prints an answer, and that gets you a demo rather than a product [2]. In production the same idea splits into four separate small products: ingestion, retrieval, grounding, and trust [3]. The writeup argues that a single-user RAG app and a multi-tenant SaaS share almost no code after the demo stage, because every query, chunk, and conversation has to be scoped to a workspace [12].

The published retrieve function takes a workspaceId and a query, pulls document chunks whose document.source.workspaceId matches, and takes five [8]. It also filters chunk text with a contains match, which is a substring scan rather than vector search [9], while the comment directly above it talks about a tenant never retrieving another tenant's chunks even if the embedding matches [10]. That gap between comment and code is the clearest evidence for the angle: the ranking function is the replaceable half of retrieval, and the workspace predicate travelling through document to source is a property of the schema, not of the query. The data model carries it: Workspace as tenant container, WorkspaceMember with Owner, Admin, and Member roles via NextAuth.js v5, KnowledgeSource plus Document plus DocumentChunk always under a workspace, Conversation and Message scoped per workspace, and ApiUsage tracking tokens per workspace [11].

Ingestion is where citations are won or lost. Documents arrive as files, URLs, or manual entries [19], and the chunker slices text at 800 characters with 200 characters of overlap [4], which means a stride of 600 [5] and roughly 1.33 characters stored and embedded for every character of source [6]. Each chunk is tied to its source document so retrieval can always trace back to where the information came from [7]. Keep that pointer and citations are a rendering problem later; drop it and you re-ingest the corpus.

The answer path streams tokens over SSE and attaches the chunks that grounded them, so a user can click through to the source document [16]. The author's claim is that this single feature separates a gimmick from a support tool, because support teams do not trust "trust me", they trust an answer they can verify in two clicks [17].

The genuinely deferrable decisions are the ones the writeup treats lightly. A thin AIProvider interface lets the app run on OpenAI, Gemini, or Claude by configuration and fall back to a MockProvider when no keys are set [13], which is how a public demo shipped with no signup and no leaked keys [14][15]. SQLite via @prisma/adapter-libsql was chosen first, with PostgreSQL held in reserve [18].

Three things to check if you copy this. The take of five caps each answer's evidence at about 4,000 characters of retrieved text [21], which is a grounding limit, not a retrieval one. The live demo returns mock responses [15], so answer quality is not observable from it. And ApiUsage per workspace [11] is the seam where metering becomes billing.

Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories