A knowledge base lives on your server. The phone sends a question and shows an answer.
To add a knowledge base to an AI app you do four things. Split your documents into chunks. Turn each chunk into a vector with an embedding model. Store those vectors in a database column, next to the text they came from. Then, on every question, turn the question into a vector with the same model and ask the database for the nearest rows. Those rows go into the prompt and the model writes its answer from them. Almost all of that runs on a server. The phone sends a string of text and renders what comes back.
The abstract name for this is retrieval-augmented generation, and reading about it rarely tells you the thing you need, which is which machine each piece runs on. So that is what this page does. Where the embeddings get made, where the index sits, what the handset is allowed to know, and what the arrangement costs once real people are asking it questions. Every figure below comes from OpenAI's and Supabase's own documentation, read on 10 October 2026.
Start hereThe short version
Two jobs. One runs once, the other runs on every question.
Building the knowledge base is a batch job. You read your documents, cut them up, call an embedding model once per chunk and write the results into a table. It happens on a server or on your laptop, it takes minutes, and you only repeat it when the documents change. Answering a question is a request path. Embed the question, run one nearest row query, paste the winning chunks into a prompt, call the model that writes the reply. That has to be fast, and it has to happen somewhere you control.
The phone does neither. It owns the text box and the answer bubble. Everything between those two is your backend, for reasons that have nothing to do with how fast phones are. Even a small server can hold the whole corpus, keep one index over it, and keep the API key that none of this works without. A handset can do exactly one of those three things well.
Where do the embeddings and the vector index actually live?
In a column, in your database, in the same row as the text. An embedding, in OpenAI's own words, is a vector or list of floating point numbers, and the distance between two vectors measures their relatedness. That is the whole trick. Nothing clever happens at question time. The clever part already happened when you embedded the documents, and what is left is arithmetic over numbers you stored.
Supabase's guide shows the shape in Postgres. You enable the extension, then declare the column, which in its example reads embedding extensions.vector(384). The number in the brackets is not decoration. Supabase tells you to change it to match the dimensions your embedding model produces, and the models do not agree with each other. OpenAI's table lists 1536 dimensions for text-embedding-3-small and 3072 for text-embedding-3-large. Pick the model before you create the table, because the column is typed to the model.
Searching is a sort, not a lookup. Supabase documents a Postgres function called match_documents that takes a query embedding, a match threshold and a match count, and returns the rows that clear the threshold, closest first. Three distance measures are available on a vector column: cosine distance, Euclidean distance and negative inner product. Supabase notes that dot product tends to be the fastest if your vectors are normalised. Once the table starts to grow you add a vector index so the query stops reading every row. All of that is a database object, which is the plainest answer to where the index lives: wherever your Postgres lives.
None of this is special to one vendor. Any Postgres with pgvector does it, and dedicated vector databases are the same arrangement with different names for the pieces. For an app-sized corpus, keeping your documents, your vectors and your user accounts in one Postgres is one less system to operate and one less place for data to drift, which is the practical case for Supabase as a backend and for the managed Postgres that comes with most app platforms.
Supabase, storing embeddings in Postgres with pgvector, read 10 October 2026
Why is this a server job and not something the phone can do?
Three reasons, and the first one is the key. The embeddings endpoint is authenticated: OpenAI's own examples send an authorization header carrying your API key with every request. An app binary is a file anybody can download and unpack, so a key compiled into it is a key you have published. Obfuscating it moves the problem by about a week. The phone therefore calls an endpoint you own, that endpoint holds the key, and the same goes for the second call, the one that writes the answer.
The second reason is that the query vector and the stored vectors have to sit in the same space. Distances only mean anything between vectors from the same model. Change the model and every number moves. Even one model can hand back a shorter vector, because OpenAI documents a dimensions parameter that shortens an embedding by dropping numbers from the end of the sequence. If the document vectors were built on a server last month and the query vector comes from whatever small model happens to be on the handset, the two sets of numbers are not comparable, and nothing in the system will tell you so. The results just quietly get worse.
The third reason is the corpus. A nearest row search ranks every row, so whichever machine runs the search needs every row. Putting the knowledge base on the device means shipping your documents to everyone who installs the app, and fixing one sentence in one document means shipping an app update. Keep it on the server and an edit is one re-embed of one chunk, live for everybody in seconds. This is the same boundary you would draw for adding search, and for the same reason: the index belongs next to the data.
What the phone does get is worth saying out loud, because it is not nothing. It holds the conversation on screen. It caches the last answers, so a repeated question costs you nothing. It shows which documents an answer came from, which is the single feature that makes people trust the thing. And it can tell someone honestly that this screen needs a connection. An app that pretends to answer offline is worse than one that admits it cannot.
What does one question cost in practice?
Less than people expect on the retrieval side, and the bill is somewhere else. OpenAI prices its embedding models with a pages per dollar figure and notes the assumption of roughly 800 tokens to a page. Read on 10 October 2026, that table gives 62,500 pages per dollar for text-embedding-3-small, 12,500 for text-embedding-ada-002 and 9,615 for text-embedding-3-large. Work it backwards and a page costs about one six-hundredth of a cent with the small model.
So the one-off build is cheap. Ten thousand pages of documentation embedded once with the small model is about sixteen cents, and about a dollar with the large one. The question itself is cheaper still. A one-line question is maybe twenty tokens, a fortieth of a page, so embedding it rounds to nothing. You would be answering millions of questions before that line on the invoice was worth reading.
The cost that scales with traffic is the second call, the one that reads the retrieved chunks and writes the answer. You pay for everything you paste into that prompt. Ask for the ten nearest chunks at 500 tokens each and every question carries 5,000 tokens of input before the person has finished typing. That is the dial to tune. Dropping the match count from ten to four cuts it by more than half and often makes answers better, because the model stops being handed six paragraphs that were merely nearby. Prices for answer models move often enough that a number here would be stale, so take it from the model page you are actually calling.
The real trap is not per-question cents. It is re-embedding. Change your embedding model and the whole corpus has to be rebuilt, because the old vectors describe the old space. The same is true of chunk size: change it and you rebuild. That makes both choices stickier than a price list suggests, so settle them while you are still sketching the backend and database, then leave them alone.
Do the arithmetic
What embedding your documents costs once
Pick the size of your knowledge base. The figures come from the pages per dollar table in OpenAI's embeddings guide, which assumes roughly 800 tokens to a page.
Roughly how many tokens that is
400,000 tokens
One-off build, text-embedding-3-small
under a cent
One-off build, text-embedding-3-large
$0.05
This is the one-off cost, and you pay it again from scratch every time you change the embedding model or the chunk size.
What goes wrong first, and how do you catch it?
Chunking, almost always. People embed a whole handbook as one vector and wonder why retrieval feels vague. One vector for forty pages is an average of forty pages: close to everything, useful for nothing. There is a hard ceiling as well. OpenAI lists 8,192 tokens of maximum input for its current embedding models, so a long document cannot go in as a single call anyway. Cut on headings, keep each chunk readable on its own, and keep the heading inside the chunk text so a retrieved snippet still says what it is about.
Then dimension mismatches. A column declared at 384 and a model returning 1536 gives you an error, which is the good outcome. The bad outcome is two models that both return 1536. Nothing fails, half your rows sit in one space and half in another, and the only symptom is answers that are subtly wrong. Store the model name and a chunking version on every row, so you can always tell which rows are stale and re-embed just those.
After that, a missing floor. The match threshold exists because the nearest row is always some row. Ask a knowledge base with no refund policy about refunds and it hands back whatever was least unrelated, and the answer model writes a confident paragraph from it. With a threshold the query comes back empty instead, and an empty result is what lets your app say it does not know. Return the title and the row id with every match and show them under the answer, so a person can check it without trusting you.
The rest is ordinary product work. A knowledge base is one tool bolted onto a chat screen, so everything in adding an AI chatbot still applies: streaming, retries, a visible failure state, a cap on what one user can spend. Retrieval removes none of it. It only decides what the model is allowed to read before it answers.
Supabase, the match_documents function and its threshold argument, read 10 October 2026
Which piece runs where
| The piece | On the phone | On your server | Why there |
|---|---|---|---|
| The question somebody types | Yes | No | It is text in a text box, nothing more |
| Turning that question into a vector | No | Yes | The call is authenticated with your API key |
| The document chunks and their vectors | No | Yes | A nearest row search has to see every row |
| The vector index | No | Yes | It is a database object, built over the whole table |
| The model call that writes the answer | No | Yes | Same key, same reason |
| The answer, the sources and the history on screen | Yes | No | Cache it so a repeated question is free |
Where this lands if a builder generated your app
Newly is an AI app builder. You describe an app in plain English and it writes a real React Native and Expo project that you own. It runs the app on cloud simulators while it builds, uploads iOS builds to TestFlight, publishes to Google Play internal testing and can build a standalone Android APK, with two-way GitHub sync so the code is never trapped. It is $25 a month and there is no free tier. It does not submit to the App Store on your behalf, because Apple requires the owner of the content to submit.
For a knowledge base the useful thing to know is that the screen is the easy half. Describe a chat screen with a sources list under each answer and you have something to look at quickly. The other half is backend work: a table with a vector column, an endpoint that holds the API key, an embedding job you can re-run, and a retrieval function with a threshold. Ask for that half in those words. Asking for a chatbot and hoping gets you a screen with nothing behind it.
Owning the project is what makes the rest survivable. The retrieval function is code in your own repository, so you can change the chunking, swap the embedding model and rebuild the index without asking anyone for permission. The honest limit is that no builder can decide what your documents mean, where to cut them, or when an answer is good enough to show somebody. That judgement stays yours whoever typed the code.
Questions people ask about knowledge bases in apps
Four steps. Split your documents into chunks. Call an embedding model once per chunk. Store each vector in a database column next to the text it came from. Then, on every question, embed the question with the same model and ask the database for the nearest rows, which you paste into the prompt for the model that writes the answer. The first three steps are a batch job you re-run when the documents change. The fourth is a request path that runs on your server, not on the handset.
Describe the screen, then build the half that holds the documents
Start with the chat screen and the sources list, because you can look at those and judge them. Then add the table with the vector column and the endpoint that keeps your key off the handset. In that order the expensive decisions, the model and the chunk size, get made while rebuilding is still cheap.
Start building