DEV Community

Odd_Background_328
Odd_Background_328

Posted on

Reject Agent Tool Rounds Before Free Capacity Inverts Unit Cost

You get the cost alert at 02:14 UTC tonight.
Queue depth still looks healthy on your Grafana dashboard.
Completed jobs stay flat while token spend climbs.

Which operational action follows from that split in evidence?
You pause new agent sessions before you scale anything.
You measure cost per completed job, not started calls.

The contradiction you should not ignore

Started requests keep succeeding at the edge proxy tonight.
Downstream tool rounds never return a finished answer.
You pay for context, tools, and retries that never land.

This pattern is unit-cost inversion, not simple queue backup.
Free capacity hides it until the finance export arrives.
You need a ledger that splits started work from finished work.

Topology you can reproduce on a laptop

Run one admission sidecar in front of a local fake model.
The sidecar counts expected tokens times planned tool rounds.
A worker loop calls tools until a deadline or reject.

Declared pieces for the local drill:

  • ledger.py writes one JSON line per job event
  • admit.py rejects work above a unit-cost ceiling
  • worker.py injects extra tool rounds on command
  • cleanup.sh deletes the ledger and stops the loop

Do not treat this laptop loop as production evidence.
Label every number below as expected output from the script.

Declared test conditions

Use these conditions and do not silently change them.

  • Workload: 40 agent jobs, max four tool rounds each
  • Fake tokens: 800 completion plus 200 prompt per round
  • Deadline slack: 12 seconds wall time per healthy job
  • Reject if projected tokens exceed 4,000 per completion
  • Reject if three starts land with zero completions
  • Local only, no remote model, no network dependency

You are measuring inversion, not remote model quality.
Keep the fake server single-threaded so queue age stays visible.

Build a per-job token ledger

Create ledger.py as a JSONL append-only local event file.
It records start, round, complete, cancel, and reject events.

#!/usr/bin/env python3
"""Local job ledger. Expected output only. Not production telemetry."""
import json, time, threading, os
from pathlib import Path

LEDGER = Path(os.environ.get("LEDGER_PATH", "/tmp/agent_ledger.jsonl"))
_lock = threading.Lock()

def emit(event, job_id, **fields):
    rec = {"ts": time.time(), "event": event, "job_id": job_id}
    rec.update(fields)
    with _lock:
        with LEDGER.open("a") as f:
            f.write(json.dumps(rec) + "\n")

def summarize():
    started = completed = cancelled = rejected = 0
    tokens = 0
    if not LEDGER.exists():
        return {
            "started": 0,
            "completed": 0,
            "cancelled": 0,
            "rejected": 0,
            "tokens_spent": 0,
            "unit_cost": None,
            "start_complete_ratio": None,
        }
    for line in LEDGER.read_text().splitlines():
        r = json.loads(line)
        ev = r["event"]
        if ev == "start":
            started += 1
        elif ev == "complete":
            completed += 1
            tokens += r.get("tokens", 0)
        elif ev == "cancel":
            cancelled += 1
            tokens += r.get("tokens", 0)
        elif ev == "reject":
            rejected += 1
    unit = (tokens / completed) if completed else None
    ratio = (started / completed) if completed else None
    return {
        "started": started,
        "completed": completed,
        "cancelled": cancelled,
        "rejected": rejected,
        "tokens_spent": tokens,
        "unit_cost": unit,
        "start_complete_ratio": ratio,
    }
Enter fullscreen mode Exit fullscreen mode

You now have started, completed, cancelled, and spent tokens.
Unit cost uses completed jobs as the only denominator.
Cancelled work still adds tokens and exposes the inversion.

Admission rule you can explain at 03:00

Create admit.py as the 03:00 gate you can explain.
The gate uses two independent thresholds, not one blended score.

#!/usr/bin/env python3
"""Admit or reject agent jobs from ledger summaries."""
from ledger import summarize, emit

MAX_TOKENS_PER_COMPLETION = 4000
MAX_START_COMPLETE_RATIO = 1.8
MAX_ROUNDS = 4
TOKENS_PER_ROUND = 1000  # declared: 800 completion + 200 prompt
ZERO_COMPLETION_STARTS = 3

def projected_tokens(planned_rounds):
    return min(planned_rounds, MAX_ROUNDS) * TOKENS_PER_ROUND

def admit(job_id, planned_rounds):
    stats = summarize()
    proj = projected_tokens(planned_rounds)
    reasons = []
    if proj > MAX_TOKENS_PER_COMPLETION:
        reasons.append("projected_tokens")
    ratio = stats["start_complete_ratio"]
    if ratio is not None and ratio > MAX_START_COMPLETE_RATIO:
        reasons.append("start_complete_ratio")
    unit = stats["unit_cost"]
    if unit is not None and unit > MAX_TOKENS_PER_COMPLETION:
        reasons.append("observed_unit_cost")
    if stats["started"] >= ZERO_COMPLETION_STARTS and stats["completed"] == 0:
        reasons.append("zero_completions")
    if reasons:
        emit("reject", job_id, reasons=reasons, projected=proj)
        return False, reasons
    emit("start", job_id, planned_rounds=planned_rounds, projected=proj)
    return True, []
Enter fullscreen mode Exit fullscreen mode

Ask for one operational threshold and write down the rationale.
Queue age is not the same signal as utilization percent.
Deadline slack is not the same signal as token headroom.

Pick the threshold from the evidence in front of you:

  • Pick unit cost first when completions stall under spend
  • Pick queue age first when workers stay busy and finish
  • Pick deadline slack first when the caller still waits
  • Pick zero-completion starts when cancels wipe the denominator

Worker that can waste tokens on purpose

Create worker.py as a fault injector, not a model client.
Extra rounds burn tokens without producing a completed job.

#!/usr/bin/env python3
"""Inject extra tool rounds. Local expected behavior only."""
import argparse, time, uuid
from admit import admit, TOKENS_PER_ROUND
from ledger import emit, summarize

def run_job(extra_rounds, deadline_s):
    job_id = str(uuid.uuid4())[:8]
    planned = 1 + extra_rounds
    ok, reasons = admit(job_id, planned)
    if not ok:
        return {"job_id": job_id, "status": "rejected", "reasons": reasons}

    spent = 0
    t0 = time.time()
    for rnd in range(planned):
        time.sleep(0.15)  # declared fake round latency
        spent += TOKENS_PER_ROUND
        emit("round", job_id, round=rnd, tokens=spent)
        if time.time() - t0 > deadline_s:
            emit("cancel", job_id, tokens=spent, reason="deadline")
            return {"job_id": job_id, "status": "cancelled", "tokens": spent}
    emit("complete", job_id, tokens=spent)
    return {"job_id": job_id, "status": "complete", "tokens": spent}

if __name__ == "__main__":
    p = argparse.ArgumentParser()
    p.add_argument("--jobs", type=int, default=40)
    p.add_argument("--extra-rounds", type=int, default=0)
    p.add_argument("--deadline", type=float, default=12.0)
    args = p.parse_args()
    for _ in range(args.jobs):
        print(run_job(args.extra_rounds, args.deadline))
    print("SUMMARY", summarize())
Enter fullscreen mode Exit fullscreen mode

Run the healthy baseline before you inject extra rounds.

export LEDGER_PATH=/tmp/agent_ledger.jsonl
rm -f "$LEDGER_PATH"
python3 worker.py --jobs 40 --extra-rounds 0 --deadline 12
Enter fullscreen mode Exit fullscreen mode

Treat the next block as expected output, not production proof.

  • Forty complete events in the JSONL ledger
  • About 40,000 tokens spent across finished jobs
  • Unit cost near 1,000 tokens per completion
  • Start/complete ratio near 1.0 with no rejects

Now inject extra tool rounds against a tight deadline.

rm -f "$LEDGER_PATH"
python3 worker.py --jobs 40 --extra-rounds 2 --deadline 0.2
Enter fullscreen mode Exit fullscreen mode

Expect this declared fault to invert unit cost quickly.

  • Early jobs cancel after the first 0.15s fake round
  • Tokens still land on cancel events with no completion
  • After three starts, zero_completions rejects new work
  • Unit cost stays undefined while spend is already real

That reject is the operational action from the evidence.
You stop starting work that cannot finish on time.

When free capacity is the wrong bet

Free model access looks cheap at the request edge.
Agent loops multiply that cost by every tool round.
A cancelled round still consumed context window and time.

Use free capacity as a lab, not as a production SLO.
Promote only after the ledger stays free of inversion.

Disclosure: This article was prepared as part of MonkeyCode's product outreach.
MonkeyCode offers free model access and a free server option you can point this local ledger at during lab drills. Do not treat that path as unbounded production headroom.

Free capacity is the wrong bet under these five conditions:

  • Tool rounds per job exceed your completion token budget
  • Cancel rate stays high while spend keeps rising anyway
  • Queue looks empty because work dies inside the worker
  • Restarts replay full agent traces, not a single tool
  • Deadline slack is smaller than one extra tool round

Paid burst capacity can be cheaper per completed job.
You pay more per hour and less per finished answer.
That inversion is the decision, not a vendor slogan.

Failure handling and the rollback path

Watch these fields on every reject and every cancel.

  • event: start, round, complete, cancel, reject
  • tokens: cumulative spend on that job_id
  • reasons: projected_tokens, start_complete_ratio, observed_unit_cost, zero_completions
  • start_complete_ratio: started divided by completed, when defined
  • unit_cost: spent tokens divided by completed jobs only

If the ledger file is missing, you must fail closed.
Do not admit work you cannot account for tonight.
If summarize returns no completions, reject every new start.

Follow this rollback path after a false reject storm:

  1. Set --extra-rounds 0 and rerun the healthy baseline.
  2. Drain in-flight jobs for one full deadline window.
  3. Restore admit thresholds only after unit cost drops.
  4. Keep LEDGER_PATH on a tmpfs so leftover files cannot linger.

Do not raise token ceilings to silence the admission gate.
Fix the fan-out pattern or the deadline first.

Cleanup

Stop the injector and delete the local ledger file.

pkill -f worker.py || true
rm -f /tmp/agent_ledger.jsonl
unset LEDGER_PATH
ps aux | grep -v grep | grep worker.py || echo "no worker"
ls /tmp/agent_ledger.jsonl 2>/dev/null || echo "ledger gone"
Enter fullscreen mode Exit fullscreen mode

Confirm no leftover python workers with a process listing.
Confirm the ledger path is gone before you leave.

Who should not use this

Skip this gate if you run single-shot completions only.
Skip it if every round lacks a stable job identifier.
Skip it if policy forbids even local request event logs.

This ledger is not billing-grade and not multi-tenant isolation.
You also should not use free capacity for launch-path traffic.
Lab inversion drills belong on a throwaway local server.

Production needs a budget, a drain, and a named owner.
A free lab path does not replace those three controls.

What you do at 02:14

You do not scale replica count on a token spike.
You compare started jobs against completed jobs first.
You reject new agent rounds until unit cost falls.

Then you rerun the local injector with extra rounds.
You keep the reject threshold tied to finished completions.
Run that drill before the next agent loop ships.

Top comments (0)