cd /news/artificial-intelligence/how-i-built-a-multi-tenant-rag-knowl… · home topics artificial-intelligence article
[ARTICLE · art-97802] src=dev.to ↗ pub= topic=artificial-intelligence verified=true sentiment=↑ positive

How I Built a Multi-Tenant RAG Knowledge Base with Source-Cited Answers — Pipeline, Multi-Tenancy, and Lessons

KnowBase AI, a multi-tenant SaaS knowledge base built by a developer, enables businesses to upload documents and receive AI-generated answers grounded in their own content, complete with clickable source citations. The system implements a RAG pipeline with chunking, workspace-scoped retrieval for tenant isolation, and a provider-agnostic design supporting OpenAI, Gemini, and Claude, plus a demo mode for keyless testing. The developer highlights that the key differentiator is source-cited answers, which separates a gimmick from a trusted support tool.

read4 min views1 publishedAug 15, 2026

Every "build a RAG chatbot" tutorial ends the same way: embed a few paragraphs, call similaritySearch

, print the answer. That gets you a demo, not a product. The gap between a RAG demo and a RAG product you'd trust with a company's documents is where all the real engineering lives.

I built 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. This post covers the RAG pipeline, how multi-tenancy changes the design, and the decisions I'd repeat.

Live demo: knowbase-ai.netlify.app — no login needed, fully functional (it runs in demo mode with mock responses).

RAG sounds simple: retrieve relevant context, feed it to the LLM, get a grounded answer. In production it means:

Each step is a small product on its own. Here's the pipeline.

Documents arrive as files, URLs, or manual entries. The key decision is chunking — too big and retrieval is fuzzy, too small and you lose context. The pipeline chunks text with overlap so no meaning falls through the gaps:

export function chunkText(text: string, size = 800, overlap = 200): string[] {
  const chunks: string[] = [];
  let i = 0;
  while (i < text.length) {
    chunks.push(text.slice(i, i + size));
    i += size - overlap;
  }
  return chunks;
}

Each chunk becomes a DocumentChunk

tied to its source document, so retrieval can always trace back to where the information came from.

A single-user RAG app and a multi-tenant SaaS share almost no code after the demo stage. Every query, chunk, and conversation must be scoped to a workspace:

// Every AI retrieval is scoped by workspaceId — a tenant can never
// retrieve another tenant's chunks, even if the embedding matches.
export async function retrieve(workspaceId: string, query: string) {
  return prisma.documentChunk.findMany({
    where: {
      document: { source: { workspaceId } },
      text: { contains: query },
    },
    take: 5,
  });
}

The data model enforces isolation at the schema level:

Workspace

— tenant containerWorkspaceMember

— roles: Owner / Admin / Member (RBAC via NextAuth.js v5)KnowledgeSource

  • Document

  • DocumentChunk

— the RAG layer, always under a workspaceConversation

  • Message

— chat sessions, scoped per workspaceApiUsage

— token tracking per workspaceOne of the best decisions: never hard-code a model. A thin provider interface means the product runs on OpenAI, Google Gemini, or Anthropic Claude by configuration, and it made the demo mode trivial:

export interface AIProvider {
  chat(messages: Message[]): AsyncIterable<string>;
}

export const providers = {
  openai: OpenAIProvider,
  gemini: GeminiProvider,
  claude: ClaudeProvider,
};

// No API keys configured? Run a fully functional mock.
export function getProvider(): AIProvider {
  const configured = Object.entries(providers)
    .find(([, P]) => new P().isConfigured());
  return configured ? new configured[1]() : new MockProvider();
}

Demo mode was the reason I could publish a real demo without leaking keys or asking visitors to sign up.

An AI answer with no receipts is just a guess. KnowBase streams responses over SSE and attaches the chunks that informed each answer, so users can click through to the source document:

// Simplified — stream tokens, then emit the citations that grounded them
export async function chat(conversationId: string, content: string) {
  const chunks = await retrieve(workspaceId, content);
  const stream = await provider.chat([
    { role: "system", content: buildRagPrompt(chunks) },
    ...history,
    { role: "user", content },
  ]);

  return new Response(sse(stream, chunks), {
    headers: { "Content-Type": "text/event-stream" },
  });
}

This single feature separates a gimmick from a support tool. Customer-support teams don't trust "trust me" — they trust a cited answer they can verify in two clicks.

Layer Technology
Framework Next.js 16 (App Router, Turbopack)
Language TypeScript (strict)
Database SQLite + Prisma 7 (@prisma/adapter-libsql )
Auth NextAuth.js v5 (Auth.js) + JWT
AI OpenAI / Gemini / Claude (provider abstraction)
Styling Tailwind CSS v4 + shadcn/ui
Charts / Markdown Recharts + React Markdown (remark-gfm)
Forms React Hook Form + Zod
Deployment Netlify

I'm a full-stack developer at RA Technologies, where we build SaaS and AI products with exactly this architecture — one codebase, strict types, a database that needs no server, and a demo anyone can click.

@prisma/adapter-libsql

gives you a real production-ish setup with zero config. Swap to PostgreSQL later only if you actually need it — most knowledge-base products don't.workspaceId

at the query layer, never rely on the UI to filter.If you've built RAG in production, what did I get wrong? I'd love to hear it in the comments.

── more in #artificial-intelligence 4 stories · sorted by recency
── more on @knowbase ai 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/how-i-built-a-multi-…] indexed:0 read:4min 2026-08-15 ·