Skip to content

Post cover

How to Attribute LLM Cost to Agents, Users, and Workflows

Most teams know their LLM spend for last month to the dollar. Few can say which agents, customers or workflows produced it. Provider dashboards report spend by API key and model, and nobody runs a business by API key.

LLM cost is hard to explain after the fact because nobody provisions it. A cloud bill maps to resources someone chose to create. An LLM bill is the sum of many small decisions made inside conversations and agent runs: a longer system prompt, a growing context window, a large tool result carried forward, a retry, a step routed to a bigger model. That is why spend often grows faster than usage, and why the gap doesn't show up on the provider dashboard as anything but a larger number.

The fix is on the telemetry side, not the billing side: record cost on each call, inside the trace, while the context still exists.

Record cost where the context is

flowchart LR
    CALL["LLM call"]
    CALL -- "billing export" --> BILL["monthly total<br/>by API key<br/><i>no context</i>"]
    CALL -- "span attributes" --> SPAN["tokens + cost<br/>inside the trace"]
    SPAN --> DIM["by agent · user ·<br/>workflow · team · outcome"]
    DIM --> UE["<b>unit economics</b><br/>useful spend vs waste"]
    style BILL stroke:#8e9aaa,stroke-dasharray:4 4
    style SPAN stroke:#f4ae30,stroke-width:2px
    style UE stroke:#59ca6f,stroke-width:2px

On every model call, record:

What Example attribute Notes
Model and provider gen_ai.request.model Record the model that actually answered, if routing can change it
Input and output tokens gen_ai.usage.input_tokens, gen_ai.usage.output_tokens OpenTelemetry GenAI names; most agent SDKs emit them
Cached and reasoning tokens provider-specific Priced differently from normal input and output
Cost cost_usd Compute at write time from a price table you version
Who and what user_id, account_id, workflow, agent, team The dimensions you'll report on
Change context release, prompt_version To tie spend changes to deployments

Two details save a lot of confusion later:

  • Version the price table. Prices change. Store the computed cost on the span rather than recomputing history with today's prices.
  • Attribute your own overhead. Evaluation judges, embeddings, summarization jobs and retries are real spend. Give them their own workflow value (for example eval or indexing) instead of letting them disappear into a shared bucket.

The dimensions worth having

Dimension What it shows
Agent or workflow Which product area and owner the spend belongs to
User, account or tenant Heavy users, abuse, and per-customer unit economics
Model and route Whether expensive models are used where they make a difference
Prompt and release version Prompt growth and spend changes after a deployment
Tool and retry count Hidden loops and oversized tool results
Outcome Spend that produced a result versus spend on failed runs

Don't add dimensions you won't report on. Each one has to be set correctly by every service that makes model calls, and a dimension that's wrong 20% of the time is worse than none.

Report unit economics, not totals

Total spend can rise for good reasons, such as more users. Two ratios tell you more:

  • Cost per completed workflow: total cost of runs in a workflow divided by the runs that reached a successful end state.
  • Cost per successful outcome: the same, with "success" defined by the business (ticket resolved without escalation, invoice matched, lead qualified).

If either ratio rises, the system is getting less efficient at turning model calls into results, whatever the total is doing.

Check the expensive tail first

Runs sorted by cost, with the most expensive tenth highlighted

Sort runs by cost and look at the top few percent. A common finding is that a small share of runs accounts for a large share of spend, that those runs share a cause (often a retry loop or one oversized tool result in one workflow), and that they also complete less often. When that's the case, the fix is not a tradeoff: removing the loop lowers cost and raises completion at the same time.

What not to optimize first

Switching to a cheaper model, shortening prompts or capping conversation length are the most visible levers, and often premature. Cutting context the agent needed makes the bill smaller and the product worse. Downgrading a model in a workflow where failures go to human agents can lower the provider bill while raising the total cost of the workflow.

Start with spend that has no upside: retries, loops, oversized tool results, unused context, unnecessary escalation to larger models, and abandoned sessions that used expensive tokens before failing. Once those are under control, decisions about routing and prompt size are safer, because you can see the quality and outcome tradeoff. The cost reduction guide has a worked example.

Doing this with Anosys

The Anosys SDKs for OpenAI, Anthropic and the agent frameworks record tokens (including cached and reasoning tokens) on every span, and get_user_context attaches the user and session. OpenTelemetry sources and OpenRouter Broadcast carry the same fields, and coding tools such as Claude Code and Codex are covered by their own hooks. Your workflow, team and outcome fields are mapped once in Collecting → Field Mapping, so dashboards can show cost per workflow, account or outcome next to evaluation scores, and an alert can fire on cost per completed run rather than on the monthly total.

See Track AI and coding spend to get started.

Free plan, no credit card

See your LLM cost per agent, user and workflow

Connect your SDK or OpenTelemetry source and break spend down by agent, user, workflow and model, with the expensive tail one click away.

Start tracking cost for free Learn how to attribute and cut spend →