Pidoku

Cost, TCO and ROI

Intermediate 1h Difficulty 3/5 Lesson 05 of 06

Prerequisites Requirements and Success Metrics, Choosing a Model

The idea in one minute#

An AI business case rests on three numbers. Unit cost is what one task costs to run — one claim, one answer, one resolved ticket. Total cost of ownership (TCO) is everything the solution costs over three years, including the people who build and keep it. Return on investment (ROI) compares that total with the benefit, and payback says how many months until the money comes back.

Two things make AI cost different from ordinary software. It is variable: every request is billed by the token, so cost scales with use rather than with servers. And the model bill is the small part: for most enterprise solutions, people, integration, evaluation and change management are several times the token spend.

An architect who can build this model in a spreadsheet — and say which three assumptions it is most sensitive to — is the person finance wants in the room. This lesson gives you the structure, the levers and a calculator.

A picture#

flowchart TB
  subgraph UNIT["Unit cost — per task"]
    direction LR
    U1[":i-coins: Tokens in × price"] --> U3[":i-gauge: <b>Cost per task</b>"]
    U2[":i-coins: Tokens out × price"] --> U3
    U4[":i-recycle: Caching, batch, routing"] -.->|"reduce"| U3
  end
  subgraph TCO["Total cost of ownership — three years"]
    direction LR
    T1[":i-code: Build<br/><small>one-off</small>"]
    T2[":i-users: Run team"]
    T3[":i-database: Platform<br/><small>index, hosting, monitoring</small>"]
    T4[":i-flask-conical: Evaluation and upgrades"]
    T5[":i-hand: Human review time"]
    T6[":i-book-open: Training and change"]
  end
  UNIT -->|"× volume"| TCO
  BEN[":i-building-2: <b>Benefit</b><br/><small>× realisation factor</small>"]
  TCO --> ROI[":i-scale: <b>ROI and payback</b>"]
  BEN --> ROI
  ROI --> SENS[":i-triangle-alert: <b>Sensitivity</b><br/><small>which assumption breaks it?</small>"]
  class U1,U2,U3 compute
  class U4 queue
  class T1,T2,T3,T4,T5,T6 memory
  class BEN neutral
  class ROI io
  class SENS warn

How it really works#

Unit cost#

cost per task = (input tokens × input price + output tokens × output price) ÷ 1,000,000
              × number of model calls per task

Estimate tokens from discovery, not from guesses. For a document task: pages × about 500–800 tokens per page of text, more for pages sent as images. For chat: instructions, plus retrieved passages, plus the history, on every turn. For an agent: steps × the growing context — see Workflows and Agents.

Then check the estimate. Run twenty real cases through a prototype and read the token counts from the response. First estimates are commonly wrong by a factor of two to five, almost always too low, because of content nobody counted: tool descriptions, retries, long documents.

Six levers on unit cost#

In rough order of effect. Each is an architecture decision, and each should be re-tested against the evaluation set, because cheaper must not mean worse.

LeverTypical savingThe catch
Smaller model for easy cases (routing)50–90% on the routed shareNeeds a reliable way to tell easy from hard
Prompt cachingUp to about 90% on the repeated prefixThe prefix must be byte-for-byte stable; put changing content last
Batch processingAbout 50%Results arrive within hours, not seconds
Send lessProportionalBetter retrieval, trimmed history, shorter tool results — quality must hold
Cap and shape outputOutput is ~5× the input priceStructured output and length limits; watch for truncated answers
Fine-tune a small modelLarge at very high volumeTraining, evaluation and upkeep; see The Pattern Ladder

Total cost of ownership#

The token invoice is one line. A TCO that leaves out the others will be wrong by a multiple.

BucketWhat is in itOne-off or recurring
BuildEngineers, design, integration, security review, initial evaluation setOne-off
Model usageTokens: production, plus testing, evaluation runs and retriesRecurring, scales with volume
PlatformSearch index, vector storage, hosting, gateway, logging and tracing, guardrail servicesRecurring
Run teamEngineers on call, prompt and model upgrades, incident handlingRecurring — usually the largest line
Evaluation upkeepRefreshing test sets, grading, re-testing on each model changeRecurring
Human reviewThe minutes people spend checking outputRecurring, scales with volume
Change managementTraining, communication, process redesign, support during rolloutMostly year one
GovernanceRisk assessments, audits, documentation for regulatorsRecurring
Exit and migrationMoving when a model is retired or a vendor changes termsOccasional, certain

The FinOps Foundation’s guidance for AI makes the same point from the finance side: track cost per inference, cost per token and resource utilisation, but report against a business unit — cost per query, per user, per workflow — because providers do not label spend by use case for you. You need a gateway or proxy that tags every call.

Three ways to pay, and when each wins#

ModePrice shapeWins when
On-demandPer tokenVolume is low, spiky or unknown
Provisioned throughputFixed fee for reserved capacityLoad is steady enough to keep the reservation busy — break-even is typically somewhere above half utilisation
Self-hostedFixed GPU and people costVolume is very large and steady, or data cannot leave

The trap in both fixed-fee modes is utilisation. Reserved capacity that sits idle at night still costs money. Compute break-even at your real daily curve, not at peak.

Benefit, honestly#

Benefit comes in the five types from Qualifying and Prioritising Use Cases. Apply three corrections before it goes in the model.

  1. Realisation factor. Of the time saved, how much turns into something the business can count — avoided hires, more cases handled, faster service? 30–60% is typical; 100% is a red flag.
  2. Adoption curve. Nobody is at full use in month one. Model a ramp — say 20%, 50%, 80% over the first three quarters.
  3. Quality cost. Subtract the expected cost of AI errors, from the oversight calculation in Requirements and Success Metrics.

The four numbers finance asks for#

Net benefit per year  =  realised benefit − recurring cost
Payback (months)      =  one-off cost ÷ (net benefit per year ÷ 12)
ROI over 3 years      =  (total benefit − total cost) ÷ total cost
Unit economics        =  benefit per task  vs  fully loaded cost per task

Larger organisations will also ask for net present value — the same sum with future years discounted. Finance will do that part; your job is to supply defensible inputs and state which are measured and which are assumed.

Sensitivity: the part that earns trust#

Every business case has two or three assumptions that decide it. Find them, show them, and say what would have to be true for the case to fail. The usual suspects:

  • Adoption — will people actually use it?
  • Realisation — does saved time turn into value?
  • Volume — is the task as frequent as the sponsor believes?
  • Review time — does checking the output take two minutes or ten?

Token price is rarely on the list. Present a low, expected and high case, and name the measurement in the pilot that will replace each assumption with a fact.

Common mistakes#

MistakeEffect
Counting only tokensCost understated several-fold
Counting saved time at 100%Benefit overstated two-fold or more
Using average tokens for agentsMisses the expensive tail that sets the budget
Forgetting test and evaluation usageDevelopment can outspend production for months
Assuming prices only fallNewer, more capable models are often priced higher; plan on today’s price
No owner for the billSpend grows unnoticed until someone asks

Code#

A complete business case in sixty lines: unit cost before and after the levers, the TCO buckets, payback, three-year ROI, and a sensitivity table. Numbers continue the claims example.

Go
// case.go — unit cost, TCO, payback and sensitivity for one AI solution.
package main

import "fmt"

func main() {
	// Volume, from discovery.
	claimsPerDay, workdays := 900.0, 250.0
	claims := claimsPerDay * workdays

	// Unit cost: one extraction call per claim, documents sent as pages.
	inTok, outTok := 30_000.0, 1_200.0
	midIn, midOut := 2.0, 10.0
	smallIn, smallOut := 0.10, 0.50
	naive := (inTok*midIn + outTok*midOut) / 1e6

	// Levers: 60% of claims are simple and go to the small model; a 4,000-token
	// instruction prefix is cached at a tenth of the input price; nightly backlog
	// (25% of volume) runs in batch at half price.
	cachedPrefix := 4_000.0
	mid := ((inTok-cachedPrefix)*midIn + cachedPrefix*midIn*0.1 + outTok*midOut) / 1e6
	small := ((inTok-cachedPrefix)*smallIn + cachedPrefix*smallIn*0.1 + outTok*smallOut) / 1e6
	blended := 0.6*small + 0.4*mid
	tuned := blended * (0.75 + 0.25*0.5)

	fmt.Printf("unit cost  naive $%.4f   after routing, caching, batch $%.4f  (%.0f%% lower)\n\n",
		naive, tuned, 100*(1-tuned/naive))

	// Total cost of ownership.
	build := 5 * 0.5 * 180_000.0 // five people for six months
	change := 120_000.0          // training and rollout, year one
	recurring := map[string]float64{
		"model usage (prod)":   claims * tuned,
		"model usage (eval)":   0.15 * claims * tuned,
		"platform":             90_000,
		"run team (2 people)":  360_000,
		"evaluation upkeep":    60_000,
		"governance and audit": 40_000,
	}
	run := 0.0
	for _, k := range []string{"model usage (prod)", "model usage (eval)", "platform",
		"run team (2 people)", "evaluation upkeep", "governance and audit"} {
		fmt.Printf("  %-22s %10.0f / year\n", k, recurring[k])
		run += recurring[k]
	}
	fmt.Printf("  %-22s %10.0f / year\n", "recurring total", run)
	fmt.Printf("  %-22s %10.0f once\n\n", "build + change", build+change)

	// Benefit: reading time falls from 25 to 6 minutes per claim.
	hourly, minutesSaved := 45.0, 19.0
	gross := claims * minutesSaved / 60 * hourly

	report := func(label string, realisation, adoption float64) {
		benefit := gross * realisation * adoption
		net := benefit - run
		payback := (build + change) / (net / 12)
		tco := build + change + 3*run
		roi := (3*benefit - tco) / tco
		fmt.Printf("  %-26s benefit %9.0f  net %9.0f  payback %5.1f mo  3y ROI %4.0f%%\n",
			label, benefit, net, payback, roi*100)
	}
	fmt.Printf("gross time saved is worth $%.0f a year; what matters is how much is real:\n", gross)
	report("expected (50% × 80%)", 0.5, 0.8)
	report("low (30% × 60%)", 0.3, 0.6)
	report("high (60% × 90%)", 0.6, 0.9)

	once := build + change
	fmt.Printf("\nrealisation × adoption needed just to cover running cost: %.0f%%\n", 100*run/gross)
	fmt.Printf("needed to pay back within 24 months:                      %.0f%%\n", 100*(run+once/2)/gross)

	fmt.Printf("\nfully loaded cost per claim: $%.2f   (model tokens alone: $%.3f)\n",
		(run+(build+change)/3)/claims, tuned)
}

Read the last line of the output first. The fully loaded cost per claim is far above the token cost per claim, and that ratio is the single most useful thing to show a sponsor who has been told that “AI costs a few cents”. Then look at the low case: with weak adoption the project never pays back its build cost, whatever the model costs. That is the risk to manage in the pilot.

Remember this#

  • Three numbers: unit cost, three-year TCO, payback. Know all three for every proposal.
  • Tokens are usually the small line. People are the large one.
  • Six levers: route to smaller models, cache, batch, send less, cap output, fine-tune.
  • Discount benefits for realisation and adoption. 100% of time saved is never real.
  • Show sensitivity. Name the two assumptions that decide the case and how the pilot will test them.

Try it#

  1. Run case.go. Which has more effect on payback: halving the model price, or raising adoption from 80% to 90%? Change the code to check.
  2. Remove the routing lever (send everything to the mid model). How much does the recurring total change? What does that tell you about where to spend design effort for this case?
  3. Build the same model in a spreadsheet for a use case at your work, with a low, expected and high column. Mark each input as measured or assumed.

Check yourself#

  1. What is the difference between unit cost and TCO?
  2. Why is the token bill rarely the largest line?
  3. What is a realisation factor, and why is 100% a red flag?
  4. Provisioned capacity is cheaper per token than on-demand. When is it still the more expensive choice?

Sources#

↑↓ navigate↵ openesc close

drag to pan · scroll to zoom