AI Engineering Learning Hub

Latency & Cost Control for LLM Apps

Practical ways to make LLM features fast and cheap: streaming, model choice, token budgets, caching, timeouts, fallbacks and measuring per stage.

What it is

Every LLM call costs money per token and takes hundreds of milliseconds to seconds. Latency and cost control is the set of decisions that keep both inside a budget: which model, how many tokens in and out, what to cache, what to stream, when to give up and what to do when the provider is slow or down.

The two are linked. Fewer tokens in the prompt means less cost and faster first token. A smaller model is cheaper and faster. Streaming does not reduce cost but makes the same latency feel much shorter. Caching removes both for repeated questions.

On this site, the choices were: a small model, five short chunks instead of the whole knowledge base, a bounded history, a fixed reply for the contact intent, lead extraction only after a cheap regex pre-filter and only in a background task, and a keyword fallback when OpenAI fails. The voice agent adds streaming at every stage and a hard session timer.

Why it's needed

Users leave slow products and businesses cancel expensive ones. A chat feature that costs more per conversation than the revenue it protects will be switched off. A voice agent that pauses three seconds before every reply will never be used twice.

Cost is also the question every client asks second, right after 'does it work'. Being able to say 'about this much per conversation, here is how I measured it and here is the lever if it needs to be lower' is what separates an engineer from a demo builder.

How it works

  • Measure first: log tokens in, tokens out, latency to first token and total latency for every call, tagged by feature. You cannot optimise what you have not measured.
  • Pick the smallest model that passes your evaluation set. Try the cheap model first and upgrade only where the eval shows it fails.
  • Budget the prompt: cap retrieved context, trim history, keep the system prompt lean. Input tokens are usually the biggest cost in RAG.
  • Cap the output with max_tokens and ask for short replies in the prompt. Output tokens cost more and take longer.
  • Stream responses to the UI so the user reads while the model writes. For voice, stream text to speech sentence by sentence.
  • Cache: identical questions can return a stored answer; provider-side prompt caching cuts the cost of a long, stable system prompt. Set timeouts and a fallback path so a slow provider degrades the experience instead of breaking it.

Code example

import time, hashlib, functools
from openai import OpenAI

client = OpenAI(timeout=15.0, max_retries=1)      # never hang a worker

@functools.lru_cache(maxsize=512)
def _cached(key: str, prompt: str) -> str:
    r = client.chat.completions.create(
        model="gpt-4o-mini", max_tokens=300,       # cap the expensive side
        messages=[{"role": "user", "content": prompt}])
    return r.choices[0].message.content

def answer(question: str, context: str, fallback) -> dict:
    prompt = f"Context:\n{context}\n\nAnswer briefly: {question}"
    key = hashlib.sha256(prompt.encode()).hexdigest()
    t0 = time.perf_counter()
    try:
        text, source = _cached(key, prompt), "llm"
    except Exception as e:                        # timeout, outage, quota
        text, source = fallback(question), f"fallback:{type(e).__name__}"
    ms = round((time.perf_counter() - t0) * 1000)
    print({"source": source, "latency_ms": ms, "prompt_chars": len(prompt)})
    return {"text": text, "source": source, "latency_ms": ms}

# Streaming for the UI: yield tokens as they arrive
def stream(prompt: str):
    with client.chat.completions.stream(model="gpt-4o-mini",
            messages=[{"role": "user", "content": prompt}]) as s:
        for event in s:
            if event.type == "content.delta":
                yield event.delta

Where I used this

This site uses gpt-4o-mini, five short chunks instead of the whole knowledge base, a bounded history, a fixed reply for contact requests, lead extraction behind a regex pre-filter in a background task, and a keyword fallback when OpenAI is unavailable. The voice agent streams every stage and ends calls at a hard session limit.

Use cases

  • Chat products with a per-conversation cost target
  • Voice agents where every stage has a latency budget
  • High-volume classification and extraction pipelines
  • Public demos that must survive traffic spikes without a surprise bill
  • Any feature that needs a graceful answer when the provider is down

Common mistakes

  • Defaulting to the biggest model. Most product tasks pass on a small model; the eval set, not instinct, should decide.
  • Stuffing the whole document into the prompt 'to be safe'. It multiplies cost and makes answers worse.
  • No timeout, so one slow upstream call blocks a worker and the queue behind it.
  • Measuring average latency only. Users feel the p95; report percentiles and time to first token.
  • Caching without a key that includes the context. A cached answer from an old knowledge base is a stale answer.

Interview questions

What is the single biggest cost lever in a RAG app?
Input tokens, meaning the retrieved context and history sent on every call. Retrieving five tight chunks instead of twenty, and trimming history to the last few turns, usually cuts cost by more than switching models. Output tokens matter next; cap them and ask for short replies.
Does streaming make the model faster?
No, total time is the same. It makes the product feel faster because the user sees the first words within a few hundred milliseconds instead of waiting for the whole reply. In voice it is essential: speech starts on the first sentence while the rest is still being generated.
How do you choose between gpt-4o-mini and a larger model?
Run the golden set on both. If the small model passes, use it; it is many times cheaper and faster. Where it fails, check whether a better prompt or better retrieval fixes it before paying for the larger model. Route by task if needed: small model for classification and chat, larger for the rare complex question.
What should happen when the LLM provider is down?
The product should still answer something useful. This site falls back to a keyword-based responder over the same real data, so a visitor gets correct facts in plainer prose instead of an error. Timeouts, limited retries and a fallback path are the minimum for anything public.
How does prompt caching work?
Providers can cache the processed prefix of a prompt, so a long, stable system prompt and reference text are billed at a lower rate and processed faster on subsequent calls. Structure prompts so the stable part comes first and the variable part last, and the cache hits often.
How do you measure cost per conversation?
Log tokens in and out per call with the model name, multiply by the price per token, and sum per session id. Report the distribution, not just the mean, because a few long conversations dominate. Then set an alert on daily spend so a bug or an attack shows up the same day.
Ask the AI assistant about this topic

Get new topics by email

One short, practical AI-engineering lesson whenever a new topic is published. No spam, unsubscribe any time.

Need this built for your product?

I build production RAG pipelines, LLM automations and voice agents. Open to freelance projects and full-time roles.