The Bill That Surprises Teams
You shipped an agent that processes customer tickets. It works. Users are happy. Then your monthly invoice arrives and it's three times what you projected.
The issue isn't the model's price. It's the agent loop — the hidden cost of planning, replanning, and retrying that runs beneath every agent turn. Understanding where tokens actually go is the difference between a proof-of-concept and a platform you can afford to scale.
What You'll Learn
- How to measure token consumption per agent action
- The three places agents leak tokens unexpectedly
- A simple cost model you can plug into your existing pipeline
- Failure modes that drain budgets silently
Measure First, Optimize Second
Before you can cut costs, you need to see where tokens flow. Most teams estimate based on input and output size, but agents add layers: tool calls, intermediate reasoning, retry overhead, and context accumulation.
Here's a lightweight token counter you can wrap around any agent call:
import tiktoken
from functools import wraps
from typing import Callable
class TokenTracker:
def __init__(self, model: str = "gpt-4o"):
self.encoding = tiktoken.encoding_for_model(model)
self.total_tokens = 0
self.call_count = 0
def count(self, text: str) -> int:
return len(self.encoding.encode(text))
def track(self, func: Callable) -> Callable:
@wraps(func)
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
if isinstance(result, dict) and "content" in result:
tokens = self.count(str(result["content"]))
self.total_tokens += tokens
self.call_count += 1
print(f"[TokenTracker] Call #{self.call_count}: {tokens} tokens")
return result
return wrapper
tracker = TokenTracker()
This wrapper logs every agent call and its token cost. Drop it into your pipeline before you optimize anything. The numbers will likely shock you.
The Three Hidden Cost Centers
After instrumenting several production agents, I found three patterns that consistently inflate costs:
Verbose System Prompts That Don't Help
System prompts set context but also consume tokens on every turn. A 2,000-token system prompt in a 128,000-token context window sounds fine—until you're paying for it 50 times per conversation.
Planning Steps That Exceed the Problem
Agents that plan before acting use tokens on reasoning traces that get discarded. A five-step plan for a two-step problem wastes three steps of expensive inference.
Retry Loops Without Budget
When a tool fails, naive agents retry immediately with the same context. Without exponential backoff or a retry budget, a single failure cascades into five identical expensive calls.
A Comparison: Agent Architectures by Cost Profile
| Approach | Token Efficiency | Complexity | When to Use |
|---|---|---|---|
| Single-turn agent | High | Low | One-step tasks, clear inputs |
| Loop with memory | Medium | Medium | Multi-step tasks needing context |
| Loop with full replay | Low | Low | Debugging, audit trails |
| Loop with selective memory | Medium-High | High | Long-running tasks, limited budget |
The "loop with selective memory" pattern—keeping only recent turns and summarizing older ones—offers the best trade-off for production workloads. Here's a minimal implementation:
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class Message:
role: str
content: str
token_count: int
class SelectiveMemory:
MAX_TOKENS = 8000
def __init__(self, encoding):
self.encoding = encoding
self.history: list[Message] = []
self.summaries: list[Message] = []
def add(self, role: str, content: str):
tokens = len(self.encoding.encode(content))
self.history.append(Message(role, content, tokens))
self._prune()
def _prune(self):
total = sum(m.token_count for m in self.history + self.summaries)
while total > self.MAX_TOKENS and len(self.history) > 2:
oldest = self.history.pop(0)
total -= oldest.token_count
# In production, call an LLM to summarize here
self.summaries.append(oldest)
def get_context(self) -> list[dict]:
ctx = [{"role": m.role, "content": m.content}
for m in self.summaries + self.history]
return ctx
This keeps context bounded while preserving the most recent turns verbatim.
Failure Modes That Drain Budgets
These patterns will silently increase your costs:
Context poisoning: Accumulated turns cause the model to reference stale information, triggering extra clarification turns.
Tool loop traps: A tool that returns ambiguous errors causes the agent to call it repeatedly. Always return structured failure states.
Memory bloat in parallel agents: If you run multiple agents sharing a memory store, each one prunes independently. You lose summarization efficiency and pay full price per agent.
Silent token accumulation in tool responses: Tool outputs often include verbose logs. Strip them before returning to the agent.
Putting It Together
A cost-conscious agent pipeline starts with measurement, adds bounded memory, and enforces retry budgets. Here's the skeleton:
class CostAwareAgent:
def __init__(self, tracker: TokenTracker, memory: SelectiveMemory):
self.tracker = tracker
self.memory = memory
self.max_retries = 3
@tracker.track
def step(self, user_input: str) -> dict:
self.memory.add("user", user_input)
context = self.memory.get_context()
plan = self.planner.run(context) # Token-heavy step
for attempt in range(self.max_retries):
result = self.executor.run(plan)
if result["status"] == "success":
self.memory.add("assistant", str(result))
return result
# Check if retry is worth it
if attempt > 0 and result.get("error") == "auth":
break # Don't retry auth failures
return {"status": "failed", "error": "max_retries_exceeded"}
Notice the retry logic doesn't blindly retry everything. Auth failures and validation errors are terminal; network timeouts and rate limits are retriable.
Key Takeaways
- Wrap agent calls with token tracking before optimizing. You can't cut what you can't see.
- Bounded memory is the single biggest lever for long-running agents. Unbounded context grows costs linearly with conversation length.
- Retry budgets must distinguish terminal errors from transient ones. Retrying auth failures is wasteful and dangerous.
- System prompts are cheap in isolation but expensive at scale. Audit them like any other API call.
- Measure per-turn token cost, not just total cost. A high per-turn cost means your agent is doing work it shouldn't be doing.
Source
The economics of agent scale: tokens, ROI, and building platforms for AI-first teams (Part 2) — The original interview covers the platform perspective on agent economics. This article adds working code for measuring and reducing token costs in your own pipeline.
Top comments (0)