教學:如何在 Agent 無人值守執行前展示 Token 上限
Tutorial: Show the Token Ceiling Before the Agent Runs Unattended
這篇教學示範如何在 Agent 無人值守執行前,量度單次請求的 Token 用量、設定執行上限,並以停止閘門和可見的恢復介面處理超出預算的情況。示例建議每次執行最多使用已測得免費額度的 12%,另保留 25% 作復原緩衝,並標明 12% 是設計假設。
Last month a two-person team demoed an agent to a room of twelve people.
At minute nine the run stopped dead. Half a draft hung on the projector.
Nobody knew the token ceiling, so nobody could explain the stop.
Who owned that decision? The PM did. She clicked "run twice" ninety minutes earlier.
That click was the point of reversibility, and nobody marked it.
So let me ask you something. Would you ship a form that silently discards input at submit?
Then why are you shipping an agent that silently dies at minute nine?
This tutorial builds the missing evidence. You will produce a capacity rehearsal card.
You will gate the run on a measured ceiling, not a guess.
Every stage ends with a check you can actually run.
I use MonkeyCode's free model access and free server option as the staging surface here.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
Treat the free allowance as an operator-stated plan detail and re-check current terms first.
Stage 1: Measure one call before you automate ten
Start with one request. Never with a batch.
I want a token delta, a latency, and an exit code.
# example harness, not executed in this post
curl -sS -o out.json -w '%{http_code}\n' \
-X POST "$BASE_URL/chat/completions" \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d @fixtures/short_prompt.json
Then read usage from the response body, not from your own counter.
Your counter is a hypothesis. The provider's number is evidence.
import json, pathlib
usage = json.loads(pathlib.Path("out.json").read_text())["usage"]
total = usage["prompt_tokens"] + usage["completion_tokens"]
print("one_call_total", total)
Check: run it twice with the same fixture. If the two totals differ by more than a rounding step, your ceiling math is already unsafe.
Stage 2: Turn the allowance into a per-run budget
A free allowance is a pool, not a promise. Pools get shared and pools get spent.
So convert the pool into a per-run number you can defend.
Here is the ratio I use for a demo run: one run may spend at most 12% of the measured allowance.
That leaves room for a retry, a bad day, and a second teammate.
Is 12% magic? No. It is a stated design hypothesis you must label as one.
ALLOWANCE_FRACTION = 0.12 # design hypothesis, not a finding
RESERVE_FRACTION = 0.25 # untouchable floor for recovery
DEMO_RUNS = 3
def ceiling(allowance_total: int) -> int:
return int(allowance_total * (1 - RESERVE_FRACTION) * ALLOWANCE_FRACTION)
Check: write the number on the rehearsal card next to the assumption it came from.
A ceiling without its source is a rumour with a decimal point.
Stage 3: Build the gate that refuses the run
Now the interesting part. The gate must say no.
An agent that can only start is not controlled. It is merely fast.
class StopRun(Exception):
def __init__(self, code, shown, next_step):
self.code, self.shown, self.next_step = code, shown, next_step
def guarded_run(run, budget, allowance_left, dry_run=False):
if allowance_left < budget + int(budget * 4): # reserve check
raise StopRun("E_ALLOWANCE_FLOOR",
"This run needs more room than the free allowance has left.",
"Shorten the prompt, or wait for the allowance to reset.")
if dry_run:
return {"status": "rehearsed", "budget": budget}
return run(budget=budget)
Every stop carries three fields. A code, a sentence a human reads, and a next step.
Drop any one of those and you have built a dead end, not a guardrail.
Check: call guarded_run with allowance_left=budget. It must raise, not warn.
A warning in a log is not a stop condition. It is a hope.
Stage 4: Make the stop legible, not decorative
Here is where most agent UIs fail quietly. The stop appears as a toast. It vanishes.
A screen reader user hears nothing, and the run history shows a gap.
So render the stop as a persistent card with text, not colour.
<section role="status" aria-live="polite" aria-atomic="true" id="stop-card" hidden>
<h2>Run paused: allowance floor reached</h2>
<p id="stop-reason"></p>
<p><strong>Next step:</strong> <span id="stop-next"></span></p>
<button type="button">Review the last complete draft</button>
</section>
Move focus to that heading. Keep the previous draft reachable in one click.
An error without a recovery path is just an apology with better typography.
Check: tab through the paused state with the mouse untouched. If focus stays on the send button, you have work left.
Stage 5: Rehearse the failure before the demo
Now break it on purpose. Kill the run at 80% of its budget.
Then ask a colleague who did not write the code to recover the draft.
If they hesitate for more than ten seconds, your recovery story is fiction.
Write down what they said out loud. That sentence becomes your hand-back copy.
Check: the dry run returns status: rehearsed, the live run returns a real result, and both leave the same audit line.
Stage 6: Decide with a stop table, not a vibe
| Evidence on the card | Approve unattended run? | Why |
|---|---|---|
| Measured one-call tokens, two runs agree | Yes | Ceiling has a source |
| Ceiling set, reserve floor intact | Yes | Recovery is funded |
| Stop card focus-tested, next step present | Yes | Failure is legible |
| Tokens inferred from character count | No | Arithmetic dressed as measurement |
| Allowance undocumented at run time | No | Unknown ceiling is no ceiling |
| Recovery requires reading a log file | No | Users cannot recover |
That last row matters most to me. Logs are for maintainers, not for the person whose draft just froze.
What this method cannot tell you
It cannot promise the free allowance stays constant. Plans move, caps change, servers migrate.
It cannot tell you whether a longer prompt is worth its cost. That is a product judgement.
And it cannot replace a real load test if you plan concurrent runs.
Skip this workflow if you are prototyping alone with no second user, or if your provider gives you hard caps at the API layer. The gate is redundant there.
Is it redundant for you? Check before you build it.
Where MonkeyCode fits, honestly
The free model access let me rehearse on a separate surface without touching paid quota.
The free server option meant the gate ran next to the model, not on my laptop.
That separation is the point. Rehearsal should never borrow the demo's keys.
Start with Stage 1 and one fixture. Measure, divide, gate, then break it.
If the free tier is still open when you read this, that is more than enough room to begin.
And when a run stops in front of twelve people, you will have the number ready.
Not an excuse. A ceiling, a card, and a way back.
來源:DEV Community · dev.to