07 · Serving and scale

LLM serving and RAG

10 min read

A large language model answers by writing one small piece of text at a time. Serving it is therefore a matter of time per piece and memory per conversation rather than one prediction per request. Retrieval-augmented generation, usually shortened to RAG, adds a search step so the model answers from documents it is shown instead of from memory alone. The design decisions are how many conversations one machine can hold at once, how to keep the first words appearing quickly, and how to tell a search failure from a writing failure.

An everyday example

A reference librarian asked a hard question does three things in order. She finds the right pages, which can take a minute, she reads them, which takes another minute, and then she writes the answer out by hand, one sentence at a time, while you watch. You judge her on how soon she starts writing and on how fast the sentences come, and you forgive a long answer if the first line arrives quickly.

An assistant built on a language model does the same three things, and they are called retrieval, prefill, and decoding. Memory is where the librarian and the model part ways, because she reads the pages once and remembers them while she writes, at no cost to anyone else. The model must hold its working notes for every conversation it is in the middle of at the same time, and that memory on the machine is the resource that runs out first.

Separate prefill from decoding

A token is the unit of text the model works in, roughly a short word or part of a longer one, so 750 words is about 1,000 tokens. Prefill is the step where the model reads the whole prompt, which includes the retrieved passages, the instructions, and the question, and it happens once per request. Decoding is the step where the model writes the answer one token at a time, and each new token takes a fresh pass through the model, so a 300 token answer is 300 passes.

During prefill the model stores working notes about every token it has read, and that store is called the key-value cache, or KV cache. Decoding reuses the notes instead of rereading the prompt, which is what makes each step quick, but the notes stay in memory until the answer is finished. A long prompt therefore costs time at the start and a long answer costs time at the end, and both cost memory for as long as the request is active.

Where an assistant spends request timeRetrieve, Allowed passages. Prefill, Read the prompt. Decode, A token at a time. Respond, Cited answer. Time to first token is the first three boxes added together.Where an assistant spends request timeRetrieveAllowed passagesPrefillRead the promptDecodeA token at a timeRespondCited answer
Time to first token is the first three boxes added together.On a narrow screen, scroll the figure sideways to see every step.

Where the time and the memory go

Assume decoding produces a token every 20 milliseconds, which is a plausible figure for a mid-sized model on one accelerator, the chip built for model arithmetic, serving several requests at once. A 300 token answer then takes six seconds to finish, and no user waits six seconds looking at a blank box. The design answer is to stream, meaning send each token to the screen as it is produced, so the user sees the first words within about half a second and reads along while the rest arrives. Time to first token and tokens per second are therefore two separate targets, and a service can be fine on one and poor on the other.

Memory decides how many conversations one machine can hold. Assume the model weights take 16 gigabytes and the working notes take half a megabyte per token, both labelled assumptions for this exercise. A request with a 3,000 token prompt and a 1,000 token answer holds 4,000 tokens of notes, or about 2 gigabytes, at its peak. On an accelerator with 80 gigabytes of memory, 64 are left after the weights, which is room for about 32 such requests at once, and the thirty-third waits in a queue. Admission, the decision of whether to accept a new request now, has to count tokens, because a count of requests alone says nothing about memory.

One assistant request, time before and after the first tokenRetrieve, Search the index, 20 ms. Queue, Wait for memory to free, 10 ms. Prefill, Read the prompt, 25 ms. Decode, Write the answer, 45 ms. The last bar is the longest, but the user reads along while it runs.One assistant request, time before and after the first tokenRetrieve20 msSearch the indexQueue10 msWait for memory to freePrefill25 msRead the promptDecode45 msWrite the answer
The last bar is the longest, but the user reads along while it runs.On a narrow screen, scroll the figure sideways to see every step.
300 tokens × 20 ms per token = 6,000 ms = 6 s to finish the answer
4,000 tokens × 0.5 MB per token = 2 GB of notes per request (assumption)
80 GB memory minus 16 GB of weights = 64 GB for notes
64 GB / 2 GB ≈ 32 requests held at once

Use batching and caching with clear limits

Because requests finish at different times, the server does not wait for a whole batch to end before admitting new work. Continuous batching lets a finished request leave and a waiting one join at every decoding step, so the accelerator stays busy. Paged memory management hands out the working notes in small blocks instead of one large reserved slab per request, which stops a short answer from wasting space reserved for a long one. Both are efficiency tools, and a service still needs hard limits on prompt length, answer length, queued requests, and requests per user.

Prefix caching saves the working notes for a prompt beginning that repeats, such as the same long instruction block at the start of every request, so prefill for the repeated part is skipped. It saves time at the start and nothing during decoding, since the answer is still written fresh. It is also a different thing from a response cache, which stores whole answers and can only be reused when the same question is asked with the same permissions and the same documents. Each cache therefore has its own rule about who may share an entry.

Capacity is measured with the mix of prompt and answer lengths the product will see. A benchmark of short questions with short answers can get through ten times as many requests a second as document questions with long answers on the same machine. The test also includes the queue, the retrieval step, the safety checks, and users who close the window halfway through an answer.

Give RAG a complete data path

The retrieval half of RAG has its own pipeline that runs before any question arrives. Documents are parsed and cleaned, cut into chunks of a few hundred words each, and tagged with where they came from and who is allowed to see them. Each chunk is turned into an embedding, a list of numbers chosen so passages about the same thing end up close together, and the embeddings are loaded into an index that finds the nearest chunks to a question quickly. When a document is deleted or its permissions change, the index has to change the same day, and that cannot wait for the model to be retrained, because the model was never where the document lived.

When a question arrives, the service checks who is asking and searches the index for the chunks that person is allowed to see. It may reorder them with a small model, then it fits the best few into the prompt under a token limit and asks the language model to answer with references to the chunks it used. Chunk size is the main tuning decision. Chunks of 100 words lose the sentence that explains a fact, and chunks of 2,000 words fill the prompt with text the question did not need. The size is chosen by testing on questions whose supporting passages are known rather than copied from an example.

Preparing documents for retrievalDocuments, Parse and clean. Chunks, Few hundred words. Embeddings, One per chunk. Index, Search by meaning. The request path only touches the last box.Preparing documents for retrievalDocumentsParse and cleanChunksFew hundred wordsEmbeddingsOne per chunkIndexSearch by meaning
The request path only touches the last box.On a narrow screen, scroll the figure sideways to see every step.

Evaluate retrieval and generation separately

A wrong answer has two possible causes and they are fixed in different places. If the passage that holds the answer was never retrieved, the problem is the index, the chunking, or the embeddings, and no change to the model will help. If the passage was retrieved and the model still answered wrongly, the problem is in the prompt or the model. The evaluation set therefore needs both measurements, how often the right passage was in the prompt and how often the model answered correctly when it was.

The generation half is graded on whether the answer is correct, whether it is supported by the passages it cites, whether the citations point to the right places, and whether it declines when the evidence is not there. A citation is only a claim of support until a checker has read the passage, and an answer that cites an existing document for a sentence that document does not contain is the failure users notice most. Retrieved text can also carry instructions aimed at the model or content the asker is not allowed to see, so the test set includes both cases.

With the serving paths in place, the remaining question is how to know a week from now that any of them still works, which is where the monitoring section begins.

Two causes of a wrong answerWrong answer, Check the prompt sent. Passage missing, Retrieval failure. Passage present, Generation failure. The branch decides whether the fix is in the index or in the model.Two causes of a wrong answerWrong answerCheck the prompt sentPassage missingRetrieval failurePassage presentGeneration failure
The branch decides whether the fix is in the index or in the model.On a narrow screen, scroll the figure sideways to see every step.

Where people get confused

A language model service gets measured on requests per second. The useful numbers are time to first token, tokens per second during decoding, and how many requests the memory can hold at once, since a long document question and a yes-or-no question are both one request and differ in cost by a factor of fifty.

The two caches get confused with each other. Prefix caching saves the reading of a repeated prompt beginning and still writes a fresh answer, while a response cache returns a whole stored answer.

A response cache then gets keyed on the question alone, when it must be keyed on the asker’s permissions and the document versions as well, and a design that skips that returns one user’s answer to another.

Replacing the vector database, the store that holds the embeddings, is the reflex when the assistant answers wrongly. The fix depends on whether the right passage was in the prompt, and a team that does not log the prompt it sent cannot know.

In the interview

In a RAG or assistant question this topic takes five to ten minutes and the serving numbers are what separate a strong answer from a vague one. Name the two time targets, first token within half a second and a steady stream after that, and give the memory limit on conversations held at once with one worked number. Then say that retrieval and generation are evaluated separately and offer the chunking and caching details for the deep dive.

Interview questions

The assistant cites a relevant document but gives the wrong answer. Should you replace the vector database?

I would not replace it yet, because I do not know which half failed. I would pull the exact prompt that was sent and check whether the passage that holds the answer was in it. The document may have been retrieved while the one paragraph that mattered was in a different chunk, was cut off, or was contradicted by an older version of the same document.

If the passage was present and clear, the problem is generation, and I would work on the instructions, test a check that each cited sentence is supported by its passage, and evaluate on questions with and without good evidence. If the passage was missing, I would look at chunk size and the embeddings before the database, since swapping the store changes nothing about what was indexed.

How many users can one accelerator serve for this assistant?

It depends on memory for the working notes more than on compute. If the weights take 16 gigabytes of an 80 gigabyte card and a typical request holds 4,000 tokens of notes at half a megabyte each, that is 2 gigabytes per request and room for about 32 active requests. Those are assumptions I would confirm with the actual model and prompt lengths.

Since a request lasts a few seconds and most users read for a minute between questions, 32 active requests might support several hundred users chatting, and I would size by active requests and queue the rest with a short wait. Admitting more requests than memory allows does not degrade gracefully, so the limit is firm and the queue is where the slack lives.

Why stream tokens to the user instead of returning the finished answer?

A 300 token answer at 20 milliseconds a token takes six seconds, and a user who stares at a blank box for six seconds assumes the service is broken. If I stream, the first words appear after retrieval and prefill, about half a second, and the user reads along while the rest arrives.

Streaming costs an open connection per request and makes cancellation matter, since a user who closes the window should free the memory at once. I would track time to first token and tokens per second as separate targets, because a service can start quickly and then crawl, and the two problems have different fixes.

What to practice next

These problems let you cut documents into chunks, build a keyword retriever, and write the mask that makes a language model produce one token at a time, which are the preparation and generation pieces of this topic.