An AI chat app that can answer from your own documents, stream its responses, and cite sources the reader can click to check can be built and deployed in a single Vercel project.
Each of those capabilities has a mechanism behind it:
- Streaming: Vercel Functions on Fluid compute hold a response open for the length of an answer, and Active CPU billing pauses whenever the code awaits I/O, whether a database read or the token stream, so waiting bills no CPU.
- Answering from your documents: retrieval-augmented generation (RAG) runs inside the chat request, a vector lookup over a database provisioned alongside the app, plus a reranking pass through AI Gateway, the same endpoint that serves the model.
- Citations the reader can check: the model cites
[id]tags that resolve against the exact sources retrieval returned, and those sources stream to the client with the message that cites them. - Staying current: the chunking, embedding, and indexing behind the corpus runs on Vercel Workflows as durable steps that retry and survive redeploys, never inside a request.
The examples use Next.js, but nothing here requires it. The AI SDK has bindings for React, Svelte, and Vue, and the rest of the stack works from any framework Vercel supports.
Production architecture for a RAG chatbot on Vercel covers the decision side of the same stack, including when a container platform is still the right call.
Copy link to headingOverview
In this guide, you'll learn how to:
- Map the four layers of a production RAG chat app to the Vercel platform primitives
- Set up a Next.js chat route with AI SDK streaming and clickable source citations
- Retrieve context from a vector database and rerank results through the AI Gateway
- Run document chunking, embedding, and indexing with Vercel Workflows
- Compare the Vercel architecture with a container-based build
- Apply production best practices for timeouts, billing, security, retrieval quality, and ingestion
Copy link to headingArchitecture at a glance
A typical production RAG chat app has four layers, and each maps to a platform primitive.
- UI and chat transport: a Next.js App Router app with the AI SDK
useChathook. Streaming happens over the standard HTTP response, no WebSocket infrastructure required. - Model access and retrieval quality: AI Gateway, one endpoint across OpenAI, Anthropic, Google, and open-weight models, which also exposes reranking as a modality, a request type alongside chat and embeddings.
- Vector storage and metadata: a vector database connected to the project. Neon with pgvector and Upstash Vector provision natively through the Vercel Marketplace and inject their connection credentials as environment variables. External stores like Pinecone connect with an API key.
- Ingestion and background work: Vercel Workflows for chunk, embed, and index pipelines, built on Vercel Queues underneath.
Copy link to headingPrerequisites
- A Vercel account and a project in the framework of your choice. The examples use Next.js on the App Router.
- The packages the guide uses:
npm install ai @ai-sdk/react @ai-sdk/gateway workflow zod. - A vector database: Neon or Upstash Vector provisioned through the Vercel Marketplace, or an external store reachable with an API key. Create a
lib/vector.tsmodule that wraps your store's query, upsert, and delete-by-prefix calls, since every snippet below imports it. - Comfort reading TypeScript.
Working with a coding agent? Paste this prompt to have it prepare the project:
Set up a Next.js App Router project for the guide at vercel.com/kb/guide/building-ai-chat-app-with-rag-and-citations-on-vercel. Install ai, @ai-sdk/react, @ai-sdk/gateway, workflow, and zod. Add a lib/vector.ts wrapper for my vector database with query, upsert, and delete-by-prefix functions, and confirm AI Gateway access works with a one-line generateText call.
Copy link to headingStream grounded answers from the chat route
The chat route does three things in order. It retrieves context for the user's question, tells the model to answer only from that context and cite it, and streams the result. The stream construction follows the AI SDK streaming data reference.
The citation contract lives in the system prompt, so it comes first:
The system prompt: answer only from the sources, cite by [id].
The [id] tags the prompt demands are the same ids the route streams as source parts, which is what makes rendering a lookup later. The route itself retrieves, writes the source parts, and merges the model's stream into the same response:
The chat route: retrieve, write source parts, stream the model into the same response.
- Timeout ceilings: the default
maxDurationon Fluid compute is already 300 seconds, 800 seconds is generally available on Pro and Enterprise, 1800 seconds is in beta, and work with no ceiling belongs in Vercel Workflows. Pair long ceilings with an upstream abort so a stalled provider stream does not hold the request open. See configuring duration. - Billing: Active CPU is the default billing model for I/O-bound functions. CPU bills while code runs and pauses while it awaits a database, an upstream API, or a token stream. Provisioned memory bills for the instance's lifetime, and concurrent chats share one instance.
- Security: this endpoint is public, and every request triggers embedding, reranking, and generation. Protect it before launch with your auth layer, rate limiting, or Vercel Firewall rules.
Routing the model call through AI Gateway adds automatic provider retries and fallbacks, spend monitoring, and a one-line model change that never touches retrieval code. You can swap the model string for any model in the catalog.
Copy link to headingRetrieve and rerank the context
Vector search returns chunks by embedding similarity, which is a fast approximation of relevance. A reranker scores each query-document pair directly, so it catches matches that similarity misses. The pattern is to over-fetch and prune. Pull 20 candidates from the vector index, let the reranker keep the best 5, and send only those to the model. The call shapes follow the AI Gateway reranking reference.
Embed the query, over-fetch candidates, rerank to the top five.
- Latency: the rerank call is a serial hop between retrieval and generation, so it adds to time to first token on every request. It helps most on large or noisy corpora and on top of cheap embedding models. Skip it, or rerank only long candidate lists, when your corpus is small and curated.
- Multi-turn queries: the raw last message is not always the query. In a longer conversation, "what about the enterprise plan?" retrieves nothing useful on its own, so production apps often condense the conversation into a standalone query with a small model call before embedding. Start without it, add it when multi-turn quality drops.
Copy link to headingResolve citations against retrieved sources
The route streamed the sources as parts, and the system prompt made the model cite by [id]. On the client, the [id] tags in the text resolve against the source-url parts on the same message, so rendering is a lookup, and a citation can only point at a source retrieval returned. Hook shapes follow the AI SDK chatbot reference.
The lookup itself is a small helper. An [id] with a matching source becomes a link, and an [id] the model invents matches nothing and stays plain text, visibly broken instead of clickable:
An [id] tag becomes a link only when a matching source exists.
The page builds each message's source map from that message's own parts, then renders its text through the helper:
Each message resolves its own [id] tags against its own source parts.
Each message carries its own sources, so turn five's citations can never resolve against turn one's documents.
Copy link to headingRun ingestion on Vercel Workflows
Chunking a large PDF, embedding hundreds of chunks, and writing them to the index is work that outlives any request, so it runs as a workflow instead. Functions marked 'use workflow' orchestrate. Each helper marked 'use step' becomes a durable, retried unit that survives redeploys, and by default, a failed step retries up to three times without redoing the steps before it.
The workflow reads as the pipeline it is: fetch, chunk, embed, index.
The steps carry the work. Each one is a durable, retriable unit:
Embed in one batch, then delete and re-upsert the document's chunks.
Start a run from the upload endpoint or a cron. Directive syntax follows the Workflow SDK docs.
The upload handler returns immediately and the workflow does the heavy work. fetchDocument and chunkDocument carry the step directive in their own module.
- Batched embeddings:
embedManyembeds all chunks in one step, so a provider rate limit retries one batch call instead of replaying a per-chunk loop from the start. Shard into multiple steps for very large documents so retry granularity matches failure granularity. - Deterministic IDs, delete before upsert: IDs like
documentId:indexmake retried writes idempotent, which durable delivery requires, and deleting the document's chunks before upserting keeps a re-ingested, shorter document from stranding orphans that retrieval would keep returning. - A thin upload handler: parsing PDFs in the upload request blocks the user and hits request limits. The handler's only job is to enqueue.
Copy link to headingHow the stack compares to a container build
The same four needs exist on any platform. The difference is how much arrives assembled.
| What the workload needs | Assembled on a container platform | In one Vercel project |
|---|---|---|
| Streaming chat requests | Container service, load balancer, autoscaling policy | Vercel Functions on Fluid compute, billed on Active CPU |
| Background ingestion | Worker service, a queue (SQS or Redis-backed), retry logic | Vercel Workflows with durable, retried steps |
| Model access and reranking | Per-provider SDKs or a self-hosted proxy | AI Gateway, one endpoint with fallbacks and spend monitoring |
| Deploys and previews | CI pipelines and environments you maintain | Git push, preview deployments |
The container column is a real architecture that works. Every row of it is also a component to build, secure, and pay for before the first chat ships.
A container that streams answers is allocated and billed for the life of the instance, whether tokens are flowing or not, and platforms that bill per request still allocate CPU for the full request duration. On Fluid compute, Active CPU bills while code executes and pauses while the function awaits the token stream, and one instance serves many concurrent chats. A chat request spends most of its wall-clock time awaiting tokens, which is exactly the case this billing model favors.
Portability cuts both ways. The chat route, retrieval, and UI in this guide are plain AI SDK and Next.js code that run anywhere Node.js runs. The Workflows pipeline is Vercel-specific, and its container equivalent is the worker service, queue, and retry logic in the table. Vercel is not a replacement for every containerized workload. What it removes for this one is the worker service, the queue backing store, and the streaming plumbing.
Copy link to headingBest practices
- Keep the retrieval prompt strict: cite by
[id], refuse to answer outside the sources. Reranking only helps if the model actually uses the top chunks. - Route resilience through AI Gateway: provider fallbacks and retries keep a provider outage from taking the chat down, and spend monitoring catches runaway usage.
- Chunk with overlap: 600 to 1000 tokens with 10 to 15 percent overlap is a solid starting point for documentation and support content. Tune against your corpus.
- Match ingest and query embedding models: a mismatch degrades retrieval silently. Change both together, and re-embed when you change either.
- Rebuild, don't accumulate: re-ingest changed documents with delete-before-upsert so stale chunks leave the index when the source shrinks.
Copy link to headingNext steps
- AI SDK docs: the
useChathook, UI message streams, and source parts used throughout this guide - AI Gateway reranking: the
rerank()call, supported models, and the Cohere-compatible REST endpoints - Cost-aware model routing with AI Gateway: picking and switching models without overpaying for easy requests
- Vercel Workflows: durable steps and observability for the ingestion pipeline
- Understanding vector databases for AI apps: how to choose and operate the store this stack retrieves from
- Production architecture for a RAG chatbot on Vercel: the decision-level view of this stack, including when containers still win
- The Agent Stack: the assembled pieces this guide draws on, with guides for agent-shaped workloads
- What is retrieval-augmented generation (RAG): the concepts behind the pipeline you just built
Copy link to headingFAQ
Do long streaming answers hit function timeouts?
The Fluid compute default is 300 seconds of maxDuration, 800 seconds is generally available on Pro and Enterprise, and 1800 seconds is in beta. Work with no ceiling belongs in Vercel Workflows.
Does a chat request cost compute while it waits on the model?
Active CPU pauses during I/O waits, including token streams, so no CPU bills while a response waits. Provisioned memory bills for the instance, which concurrent chats share.
Which vector database should I use?
Neon (pgvector) and Upstash Vector are provisioned natively through the Vercel Marketplace. External stores like Pinecone connect with an API key. Pick one in the same region as your functions.
Can ingestion really run without a container or worker service?
Yes. Vercel Workflows runs multi-step pipelines as durable, retried steps with no ceiling on run duration, built on Vercel Queues.