FinOps for LLMs — Unit Economics and Multi-Tenant Attribution
Traditional FinOps breaks on LLM spend. Costs are token transactions, not resource uptime, and tags don't map because an API call is a transaction, not an asset. Prompt design, context window, and output length are financial decisions. Instrument three attribution dimensions on day one: per-user (`user_id`) for seat pricing and expansion, per-task (`task_id` + `route`) for product surface cost, per-tenant (`tenant_id`) for unit economics and renewal. Split spend into four token layers (prompt, tool, memory, response), because one bucket hides where it goes. For multi-tenant products, enforce in three steps: a rate limit per tenant (2-3x expected peak, a clear 429 with retry-after), a daily spend cap (1.5-3x the contracted ceiling, which tightens the rate limit and alerts), and a kill switch on spend z-score > 4 (auto-pause and page on-call). Attribution patterns range from tag-and-aggregate to the telemetry joiner (trace ID to billing, the most accurate), sampling and extrapolation, model-based allocation, event-sourced, and real-time streaming. Measure cost per resolved query or per generated artifact, not $/M tokens. Retroactive tagging misses edge cases, so stamp the request when you create it.
Learning Objectives
- Explain why traditional FinOps (tags + tiers) breaks on LLM spend and name the three new attribution dimensions.
- Enumerate the four token layers (prompt, tool, memory, response) and why single-bucket billing hides cost.
- Design an enforcement ladder (rate → spend cap → kill switch) for a multi-tenant product.
- Pick a unit metric (cost per resolved query / artifact) instead of $/M tokens.
The Problem
Your bill says $40,000. You don't know:
- Which tenant spent it.
- Which product feature drove it.
- Whether any individual user was abusive.
- Whether prompt bloat, tool calls, or memory amplification was the culprit.
Tag-and-aggregate on the provider side works for cloud resources (EC2, S3), where tags propagate to line items. LLM API calls don't auto-tag. You have to stamp user, task, and tenant at the call site and carry them through. Attribution added after the fact always misses edge cases.
The Concept
Three attribution dimensions
Per-user (user_id): who is costing what. Drives seat pricing, expansion conversations, identifies power users.
Per-task (task_id + route): which product surface costs what. Drives feature prioritization, kill-expensive-features decisions.
Per-tenant (tenant_id): which customer is profitable. Drives unit economics, renewal pricing, tier thresholds.
Instrument all three at the call site on day one. Doing it later costs more and misses more.
Four token layers
| Layer | Example | Typical % of total |
|---|---|---|
| Prompt | system + user input | 40-60% |
| Tool | tool-call results fed back | 20-40% (agent workloads) |
| Memory | prior conversation / retrieved docs | 10-30% |
| Response | model output | 10-30% |
Lump all four into one number and you can't tell which one to optimize. Break them out in your attribution schema.
Enforcement ladder
-
Rate limit per tenant. 2-3x expected peak. Return 429 with
Retry-After. The tenant sees friction instead of a surprise bill. -
Daily spend cap per tenant. 1.5-3x contracted ceiling. Trigger: tighten rate limit + alert customer-success.
-
Kill switch on spend z-score > 4 relative to tenant baseline. Auto-pause tenant; page on-call; escalate to ops + CS.
Attribution patterns
- Tag-and-aggregate: stamp metadata headers and aggregate later. Simple, but rough.
- Telemetry joiner: join traces to billing via trace IDs. The most accurate option, and what mature teams do.
- Sampling + extrapolation: sample 5-10%, multiply. Cheap for a rough number, but it misses the tails.
- Model-based allocation: regression to infer cost driver. Use it for legacy data without tags.
- Event-sourced: cost as events in a stream (Kafka / Kinesis), so you get it in real time.
- Real-time streaming: the dashboard updates in under a second.
Cost per X is the unit metric
$/M tokens is how vendors price things. Product teams need a different unit:
- Cost per resolved support ticket.
- Cost per generated article.
- Cost per successful agent task.
- Cost per user-session-minute.
Tie cost to a product outcome, or you have nothing to anchor optimization to.
Cost attribution trace shape
trace_id: abc123
user_id: u_42
tenant_id: t_7
task_id: task_classify_doc
route: model_haiku
layers:
prompt_tokens: 1800
tool_tokens: 600
memory_tokens: 400
response_tokens: 150
cost_usd: 0.0135
cached_input: true
batch: false
Emit it on each call, store it in a data lake, and aggregate per dimension. It lives in the Phase 17 · 13 observability stack.
The compounded-savings stack
Four levers stack: cache, batch, route, and gateway. With all four:
- Cache L2 (Phase 17 · 14): ~10x cheaper input.
- Batch (Phase 17 · 15): 50% off.
- Route to cheap model (Phase 17 · 16): 60% cost reduction.
- Gateway efficiency (Phase 17 · 19): redundancy + retries.
Best case, the stack lands at ~5-10% of the naive baseline. Most teams have 2-3 levers engaged, and few stack all four.
Numbers you should remember
- Attribution dimensions: per-user, per-task, per-tenant.
- Four token layers: prompt, tool, memory, response.
- Kill switch: spend z-score > 4.
- Unit metric: cost per resolved query, not $/M tokens.
- Stacked optimizations: ~5-10% of baseline possible.
Use It
main.py simulates a multi-tenant LLM service with three tenants over 10 days: a normal one, a growing one, and one with 25x the traffic multiplier. It models the daily spend cap (a printed alert) and the z-score kill switch. The per-tenant rate_limit_per_min is defined on the policy but nothing enforces it, so the first rung of the ladder is only a field here.
Ship It
This lesson produces outputs/skill-finops-plan.md. Given product and scale, designs the attribution schema and enforcement ladder.
I'd run main.py before trusting the ladder, because the output surprised me. The kill switch fires on tenant_A_normal on day 8, at z=4.80, and the abusive tenant keeps running. Spend runs from about $0.50 a day for the normal tenant to $9-$22 a day for the abusive one, against contracts of $20 to $100. The cap is 2x the contract, so no tenant ever crosses it and the cap check never prints a breach either.
The reason is in how the z-score works. The baseline is the tenant's own five-plus days of history, and a steady tenant has a tiny standard deviation (about $0.08 around $0.60 for the normal one). A small, ordinary uptick then looks like a huge outlier. A tenant that is consistently abusive has a baseline that already includes the abuse, so nothing deviates from it. That is why the body says the threshold is relative to the tenant's own baseline, and why the cap and the rate limit exist as separate rungs.
So my bet is that the z-score kill switch needs a floor on absolute spend or a minimum standard deviation before it can page anyone, and the cap should be measured against the contract, not just history. Exercise 1 is the place to try that. The rate limit isn't enforced in the code at all, so treat the simulator as a sketch of the cap and kill-switch logic, not a working gateway.
Exercises
- Run
main.py. Which tenant does the kill switch pause, at what z-score, and why? How do you pick the threshold? - Design a per-tenant, per-task cost dashboard. What are the 5 views you build first?
- Your largest tenant is unit-economics-negative. Propose three interventions ordered by customer impact.
- Compute cost per resolved ticket for a support product: 3M tokens/ticket, ~800 tickets/day, GPT-5 cached rate.
- Argue whether retroactive tagging can ever work. When is it acceptable?
Key Terms
| Term | What people say | What it actually means |
|---|---|---|
| Per-user attribution | "user-level cost" | user_id stamped on every call |
| Per-task attribution | "feature cost" | task_id + route identify product surface |
| Per-tenant attribution | "customer cost" | tenant_id; drives unit economics |
| Four token layers | "cost layers" | prompt + tool + memory + response |
| Rate limit | "429 guard" | Per-tenant ceiling enforced at gateway |
| Daily spend cap | "daily ceiling" | Tenant-scoped budget with alert |
| Kill switch | "auto-pause" | Spend z-score > 4 triggers auto-suspension |
| Cost per resolved | "product unit metric" | Cost tied to product outcome, not tokens |
| Telemetry joiner | "trace-to-billing" | Highest-accuracy attribution pattern |
| Stacked optimization | "cache+batch+route+gateway" | Compounding savings to ~5-10% baseline |
Code
- main.py (python, 83 lines)