Every Token Is a Line Item: A Ledger Workflow for Agent Spend
A developer asks a simple question: how much does the agent cost per run? Nobody answers. The dashboard shows a monthly total. The total hides the details. One task type may burn most of the budget. Retries add a silent surcharge. Context grows across steps. The answer lives in a ledger, not a dashboard.
This article builds that ledger. The worked example uses MonkeyCode's free tier. MonkeyCode is an open source project. It offers 10 million free tokens and a free server option. Both claims are operator-supplied. No model names, hardware, or quotas are assumed here. The workflow itself is the artifact. Run it against any OpenAI-compatible endpoint.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
The question nobody can answer
Most teams track latency. Few track token spend per task. Latency is visible in every request log. Token spend is buried in usage fields. Aggregated dashboards smooth out the spikes. A weekly total cannot show which task caused the spike. The fix is bookkeeping. Record every run. Group by task type. Reconcile weekly.
A ledger, not a dashboard
A ledger stores one row per run. Each row records the task type, the token counts, and the retry count. This schema is small enough to start in SQLite. It is portable enough to move to Postgres later.
CREATE TABLE token_ledger (
id INTEGER PRIMARY KEY,
ts TEXT NOT NULL,
task_type TEXT NOT NULL,
run_id TEXT NOT NULL,
model TEXT NOT NULL,
prompt_tokens INTEGER NOT NULL,
completion_tokens INTEGER NOT NULL,
total_tokens INTEGER NOT NULL,
retries INTEGER NOT NULL DEFAULT 0,
ok INTEGER NOT NULL DEFAULT 1
);
Run_id links retries to the original attempt. Task_type links spend to a feature. The model column keeps the ledger honest during model swaps.
Logging every run
The logging step is a thin wrapper around the client call. It writes one row per attempt. It writes failures too. Failed runs consume tokens. They belong in the ledger.
import sqlite3
from datetime import datetime, timezone
def log_run(conn, task_type, run_id, model, usage, retries=0, ok=True):
conn.execute(
"INSERT INTO token_ledger "
"(ts, task_type, run_id, model, prompt_tokens, "
"completion_tokens, total_tokens, retries, ok) "
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
(
datetime.now(timezone.utc).isoformat(),
task_type,
run_id,
model,
usage.prompt_tokens,
usage.completion_tokens,
usage.total_tokens,
retries,
int(ok),
),
)
conn.commit()
The wrapper is deliberately dumb. No aggregation. No filtering. Raw rows only. Aggregation belongs in queries.
Three queries that explain spend
Query 1: cost per task type
SELECT task_type,
COUNT(*) AS runs,
ROUND(AVG(total_tokens)) AS avg_tokens,
SUM(total_tokens) AS total_burn
FROM token_ledger
WHERE ok = 1
GROUP BY task_type
ORDER BY total_burn DESC;
This query ranks workloads by burn. The top row is the first place to optimize. A single task type often dominates. The average can be distorted by one long run. Check the distribution before trusting the mean.
Query 2: retry cost
SELECT run_id,
COUNT(*) AS attempts,
SUM(total_tokens) AS run_cost
FROM token_ledger
GROUP BY run_id
HAVING COUNT(*) > 1
ORDER BY run_cost DESC;
Every retry re-sends the full context. The prompt tokens are paid twice. This query groups attempts by run_id. It shows the true cost of a flaky call. A 10 percent retry rate adds roughly 10 percent to the burn.
Query 3: the reconciliation check
SELECT SUM(total_tokens) AS burn_so_far,
10000000 - SUM(total_tokens) AS remaining
FROM token_ledger;
The 10,000,000 in this query is the operator-supplied free tier figure. Replace it with your own budget. Run this check weekly. Compare the burn rate to the calendar. If the week used more than one-seventh of the budget, the month will not fit.
The weekly reconciliation
Once a week, run the three queries. That is the moment to act. Reduce retries. Shorten context. Move heavy tasks to a different lane. Reconciliation is a habit, not a script. The queries take seconds. The discipline takes a calendar reminder.
What the ledger typically reveals
Three patterns show up in most ledgers:
- One task type dominates. The fix is targeted: shorten the prompt or split the task.
- Retries are a hidden tax. The fix is a timeout budget and a backoff policy.
- Multi-step runs grow context. Each tool result feeds the next call. The ledger shows the growth as a row-by-row cost.
None of these patterns require a benchmark. They require bookkeeping.
Where the free server fits
The ledger also informs server choice. Treat the free server as a single lane until measured otherwise. It suits bursty, low-concurrency workloads. A nightly job fits. A CI smoke test fits. A steady stream of parallel requests does not. The ledger shows the request pattern. Let the data decide.
Limitations
This workflow records what the client reports. It does not measure server-side capacity or uptime. The 10 million token figure is operator-supplied. Verify the current terms before relying on them. The ledger is a cost tool, not a performance tool. Latency and correctness need separate measurements.
Who should skip this
Teams with strict latency SLAs should not point production at a free server. High-concurrency workloads need a load-tested lane. Long-context agents may exhaust the budget before the week ends. The ledger will tell them. It will not save them.
Start the ledger before you start the agent. The first row is the cheapest lesson.
MonkeyCode provides free models that can run this workflow.
Top comments (0)