You shipped an AI support bot. Deflection rates look great. Then finance forwards you the OpenAI invoice and asks a very reasonable question: why did it triple last month while ticket volume stayed flat?
Welcome to token freeloading. It's the slow leak that turns a profitable chatbot into a cost center nobody wants to own.
What token freeloading actually is
Token freeloading is any token spend that produces zero business value. Your bot burns compute, the meter runs, and the customer gets nothing useful in return - or worse, the traffic isn't even a customer.
The usual suspects:
- Context bloat - stuffing the entire knowledge base into every prompt because retrieval was never tuned.
- Retry storms - a flaky tool call fails, the agent retries three times, each retry re-sends the full context.
- Bots talking to bots - scrapers, competitors, and curious engineers running your endpoint for fun.
- Endless loops - a poorly bounded agent that keeps "thinking" until it hits the max token ceiling.
- Whale users - the 2% of accounts generating 40% of token spend on questions your docs already answer.
None of these show up in a deflection dashboard. They only show up on the invoice.
Why it destroys ROI quietly
The math is deceptively simple. If a resolved ticket saves you $6 of human agent time and costs $0.40 in tokens, you're winning. But token cost isn't fixed - it scales with prompt size and conversation length, both of which drift upward over time.
Context creeps as you add more docs. Conversations get longer as you add "helpful" follow-up prompts. Six months later that $0.40 conversation costs $2.10, and nobody noticed because it happened one token at a time.
Step one: price every conversation
You can't control what you don't measure per unit. Aggregate monthly spend is useless. You need cost-per-conversation, tagged by user, intent, and outcome.
Wrap your LLM calls with a cost tracker:
PRICING = {
"gpt-4o": {"input": 2.50 / 1_000_000, "output": 10.00 / 1_000_000},
"gpt-4o-mini": {"input": 0.15 / 1_000_000, "output": 0.60 / 1_000_000},
}
def log_cost(model, usage, session_id, user_id, resolved):
rate = PRICING[model]
cost = (usage.prompt_tokens * rate["input"] +
usage.completion_tokens * rate["output"])
metrics.emit({
"session_id": session_id,
"user_id": user_id,
"model": model,
"prompt_tokens": usage.prompt_tokens,
"completion_tokens": usage.completion_tokens,
"cost_usd": round(cost, 5),
"resolved": resolved,
})
return cost
Now you can build the one report that matters: cost per resolved conversation. Unresolved conversations that still cost money are your freeloading signal.
Step two: cap the runaway cases
Most of your budget bleed comes from a small number of pathological sessions. Put hard limits in place before you optimize anything clever.
MAX_TOKENS_PER_SESSION = 25_000
MAX_TURNS = 12
class SessionBudget:
def __init__(self, session_id):
self.session_id = session_id
self.tokens_used = 0
self.turns = 0
def check(self):
if self.tokens_used > MAX_TOKENS_PER_SESSION:
raise BudgetExceeded("handoff_to_human")
if self.turns > MAX_TURNS:
raise BudgetExceeded("handoff_to_human")
def record(self, cost_tokens):
self.tokens_used += cost_tokens
self.turns += 1
self.check()
When a session hits its cap, don't just cut it off - route to a human. That's a feature. If someone needs 25,000 tokens of AI thrashing, they needed a person 20,000 tokens ago.
Step three: fix the structural leaks
Once you have measurement and caps, go after the root causes.
Right-size the model per intent
A "where's my order" query does not need your flagship model. Route by classified intent. Cheap models handle 70% of support traffic at a fraction of the cost; reserve the expensive model for genuine reasoning.
Trim the context
Stop sending your whole knowledge base. A tuned retrieval step that returns the top 3 relevant chunks instead of top 20 can cut input tokens by 60%+ with no drop in answer quality.
Cache the obvious
A huge share of support questions are near-identical. Semantic caching - matching new queries against previously answered ones - lets you serve a response for zero LLM tokens.
def get_response(query):
hit = semantic_cache.lookup(query, threshold=0.92)
if hit:
metrics.emit({"cache_hit": True, "cost_usd": 0})
return hit.response
response = llm_answer(query)
semantic_cache.store(query, response)
return response
Step four: govern the traffic
Rate-limit per authenticated user. Block unauthenticated bot traffic at the edge. Add per-account monthly token budgets for your API-tier customers so a single integration can't run up your bill.
Governance sounds bureaucratic, but it's just the same discipline you'd apply to any expensive downstream dependency. Your LLM is a metered API. Treat it like one.
The bottom line
Chatbot ROI isn't a launch-day number - it's a maintenance job. The deflection rate tells you the bot works. The cost-per-resolved-conversation tells you whether it's worth running.
Instrument first, cap the outliers, then optimize the structure. Do that and your bot stays in the black. Skip it, and token freeloading will quietly eat the savings you built the thing to capture.
If you're building AI support systems and want the cost governance baked in from the start rather than retrofitted after a scary invoice, that's exactly the kind of thing we build at Michael AI.
Originally published at getmichaelai.com
Top comments (0)