Build the three-way cost model every AI architecture decision eventually needs — hosted API vs provisioned throughput vs self-hosted GPU — as a structured worksheet where every price is a placeholder you fill from live calculators, then run sensitivity on context length and cache hit rate and write a recommendation memo with explicit breakpoints.
Anita's orbit-docs-copilot is approaching GA and the CFO wants a number. Industry surveys keep finding that most AI projects blow their budgets, and the root cause is rarely arithmetic — it is unverified inputs and missing sensitivity. One rule governs this whole lab: verify today's price — do not trust any number in this lab or from the model. LLM pricing recall is unreliable by construction; prices change faster than training data.
~90 minutes hands-on · Core tier (maps to module M7). Cost: ₹0 — a spreadsheet or Python, public pricing calculators, and a chat assistant.
Create rag-tco/ with prices.md (empty — your evidence file) and the model script below as tco_rag.py. The workload numbers are synthetic Orbit GA estimates; the prices are deliberately None and the script refuses to run until you fill them from live sources.
# tco_rag.py — three-way TCO model. EVERY price is a PLACEHOLDER.
# Fill each None from the provider's own calculator/price page TODAY,
# and record source URL + date in prices.md before running.
PRICES = {
"api_input_per_1m": None, # hosted API: $ / 1M input tokens
"api_output_per_1m": None, # hosted API: $ / 1M output tokens
"api_cached_in_per_1m": None, # $ / 1M cached (cache-read) input tokens
"pt_unit_per_hour": None, # provisioned throughput: $ / unit-hour
"pt_unit_tokens_per_min": None, # vendor-stated tokens/min per unit
"gpu_instance_per_hour": None, # self-hosted: cloud GPU $ / hour
"gpu_tokens_per_sec": None, # benchmarked serving throughput
}
W = {
"requests_per_day": 40_000, # synthetic Orbit GA estimate
"input_tokens": 6_000, # system + question + retrieved context
"output_tokens": 400,
"cache_hit_rate": 0.40, # fraction of input served from cache
"peak_to_avg": 3.0,
}
missing = [k for k, v in PRICES.items() if v is None]
assert not missing, f"Fill from live calculators first: {missing}"
def hosted_api(w, p):
din = w["requests_per_day"] * w["input_tokens"]
cached, fresh = din * w["cache_hit_rate"], din * (1 - w["cache_hit_rate"])
dout = w["requests_per_day"] * w["output_tokens"]
return (fresh * p["api_input_per_1m"] + cached * p["api_cached_in_per_1m"]
+ dout * p["api_output_per_1m"]) / 1e6 * 30
def provisioned(w, p):
peak_tpm = (w["requests_per_day"] / 86_400) * w["peak_to_avg"] * 60 \
* (w["input_tokens"] + w["output_tokens"])
units = max(1, -(-peak_tpm // p["pt_unit_tokens_per_min"]))
return units * p["pt_unit_per_hour"] * 24 * 30
def self_hosted(w, p):
peak_tps = (w["requests_per_day"] / 86_400) * w["peak_to_avg"] \
* (w["input_tokens"] + w["output_tokens"])
gpus = max(1, -(-peak_tps // p["gpu_tokens_per_sec"]))
return gpus * p["gpu_instance_per_hour"] * 24 * 30
for name, fn in [("hosted API", hosted_api), ("provisioned PT", provisioned),
("self-hosted GPU", self_hosted)]:
print(f"{name:16s} ≈ ${fn(W, PRICES):,.0f}/month")
Before touching prices, interrogate the synthetic workload with your assistant: which assumption moves cost the most? Sketch the arithmetic: daily input tokens dwarf output tokens in RAG (6,000 vs 400 here) because retrieved context rides along on every request — which is why context length and caching, not model choice alone, dominate RAG economics. Write your workload assumptions and their sources ("Orbit GA estimate — synthetic") at the top of prices.md. Adjust peak_to_avg if your judgment says B2B docs traffic is spikier than 3x.
✅ Checkpoint: prices.md opens with the workload table and a one-line justification per assumption.
Open the actual pricing pages/calculators for one hosted API provider, that provider's (or a hyperscaler's) provisioned-throughput offering, and one cloud GPU instance family suitable for serving an open-weights model. Fill all seven PRICES values. For each, record in prices.md: value, exact source URL, and today's date. For gpu_tokens_per_sec, use a published serving benchmark for your chosen model/GPU pair and record that source too — never let the assistant "estimate" it.
Then run the audit that motivates this lab: ask your AI assistant what these prices are, and log its answers next to the verified ones. Any delta — and there will be deltas — goes in a short "AI price audit" section.
✅ Checkpoint: script runs (assertion passes); prices.md has 7 sourced prices, each dated, plus the AI-vs-calculator audit table.
Run tco_rag.py and copy the three monthly numbers into a comparison table with two extra rows you compute by hand: cost per request and cost per 1M tokens served (a unit-economics number finance can benchmark). Sanity-check the shape, not just the values: provisioned throughput bills for capacity whether used or not, so at modest utilization it should look expensive; self-hosted carries the GPU 24×7 plus the ops burden the script deliberately omits — add a memo line acknowledging the unmodeled engineer-time cost.
✅ Checkpoint: comparison table with monthly, per-request, and per-1M-token rows for all three options.
Re-run the model across a grid: input_tokens ∈ {2,000, 6,000, 12,000} × cache_hit_rate ∈ {0.0, 0.4, 0.7} (a small loop over W, or nine spreadsheet cells). Record the hosted-API cost for each cell and note where the ranking of the three options changes, not just the totals. Two findings should emerge from the arithmetic: retrieval design (how much context you stuff) is a first-order cost decision, and cache hit rate behaves like a discount on your dominant cost driver — which makes the caching architecture worth designing before scale, not after.
✅ Checkpoint: a 3×3 sensitivity table with the option ranking marked in every cell.
Write memo.md, one page, CFO register: recommendation for Orbit's GA volume, and — the part most memos omit — the breakpoints: at what requests/day does provisioned throughput's utilization math beat pay-per-token (solve for the crossover using your verified prices)? At what sustained token volume does self-hosted break even before counting ops headcount, and what does adding a realistic ops cost do to that threshold? Close with a review trigger: "re-run this model when prices change or volume passes X" — pricing moves often enough that a TCO model has a shelf life, and saying so in the memo is what makes it credible.
✅ Checkpoint: memo.md with a recommendation, two computed breakpoints, the ops-cost caveat, and a review trigger.
A TCO model is an argument, not a number. Its credibility rests on three things this lab enforces: every price traced to a live source with a date, sensitivity shown on the drivers that actually move RAG cost (context length, cache hit rate), and breakpoints that tell the reader when the recommendation flips.
The rag-tco/ folder: tco_rag.py (filled), prices.md, the comparison and sensitivity tables, memo.md. Self-grading rubric (pass = 7/9):
Cost models rarely contain client-confidential data — the workload numbers are the sensitive part. Under a strict MSA: keep real volumes in a local spreadsheet, run the AI-assisted structuring with rounded or indexed volumes (e.g., "N requests/day"), and never paste client contract pricing (negotiated discounts are confidential even when list prices are public). The memo format is a client deliverable; your prices.md evidence file is what makes it defensible in a commercial review.
None remains — gpu_tokens_per_sec is the one everyone forgets to source.