All articles

// Knowledge.log — 技術記事

Coding agent with a cost cap: token budget and circuit breaker per task

Set a monthly cap, stop long tasks, and bring tokens and cost per PR into CI without exposing prompts or waiting for the bill.

A coding agent is assigned to fix a test. It reads half the repository, tries a refactor, undoes the refactor, compacts the context, and keeps going. The PR does not exist yet, but the task has already consumed what should have covered several small fixes. An agent without guardrails has a peculiar talent for eating the month's budget before finding the wrong if.

The result we want is verifiable: every task ends within a turn and time limit, records tokens and cost in a usage.json, and CI rejects an artifact that exceeds the repository policy. Above that, the provider maintains a monthly financial cap to prevent one key or project from compromising the entire organization.

These are three different layers. Mixing them up leaves a gap exactly where the team expected protection:

  1. Provider financial backstop: limits monthly damage when the provider offers this feature.
  2. Task kill switch: ends a run before it becomes the entire budget.
  3. Observable spending: attributes tokens and dollars to the task or PR and enables a CI gate.

If the repository has not yet defined permissions, review, and agent isolation, it is worth putting the gates before the first PR in place first. Cost control does not make up for broad credentials.

Layer 1: the monthly cap does not replace the task cap

OpenAI provides monthly alerts and hard spend limits at the organization and project levels. An alert sends email but keeps traffic flowing. The hard limit starts returning 429 with organization_spend_limit_exceeded or project_spend_limit_exceeded. The spend limits documentation warns that enforcement is not instantaneous, so a small overage can occur.

A project-level limit isolates the agent's environment; the organization limit is the account's final backstop. With an Admin key, an organization cap of US$100 per month can be configured like this—threshold_amount uses cents:

curl -X POST https://api.openai.com/v1/organization/spend_limit \
  -H "Authorization: Bearer ***" \
  -H "Content-Type: application/json" \
  -d '{"threshold_amount":10000,"currency":"USD","interval":"month"}'

The concrete fix is to create a dedicated project for the agent, use keys from that project, and apply limits at both the project and organization levels. In production, monitor project_spend_limit_exceeded and organization_spend_limit_exceeded errors as well as the usage dashboard. Do not treat x-ratelimit-remaining-tokens as a balance: that header measures request capacity, not the bill. It is an understandable mistake; the header has numbers, and so does the bill.

The situation changes with other providers. xAI documents RPS and TPM by tier, but not a dollar-denominated financial kill switch equivalent to OpenAI's hard limit. MiniMax offers a balance alert in the console; it notifies you when the balance falls below the threshold but does not terminate a task. Therefore:

  • OpenAI: monthly hard limits per project and organization, plus alerts.
  • MiniMax: balance alert; the harness still needs to enforce the task limit.
  • xAI: RPS/TPM control throughput, not dollars; the harness is responsible for the financial cap per run.

The safe observation method is to keep the project isolated and compare daily provider aggregates with task artifacts. RPM and TPM are useful for stability, but they are not a budget.

Layer 2: circuit breaker per task

The monthly limit may let one run consume the whole month before it acts. The task needs independent local limits: turns, duration, and accumulated cost.

In the OpenAI Agents SDK, max_turns ends the loop with MaxTurnsExceeded. In xAI's agentic tools, max_turns counts assistant turns and tool calls; parallel tools belong to the same turn. In Hermes, the configuration can combine turn and time limits:

agent:
  max_turns: 40
  run_budget_seconds: 900
compression:
  enabled: true
  threshold: 0.50
  threshold_tokens: 120000

These values are an initial policy, not a universal standard. Calibrate them with small, known tasks, then adjust them by class of work. When a limit trips, preserve the state, stop reason, and last test run. That is the production observation: a budget termination must be distinguishable from a model failure, an external timeout, and a failing test.

Compaction also deserves an accurate label. It summarizes or reduces context so the run can continue; it is not a dollar circuit breaker. Codex provides compaction and model_auto_compact_token_limit, while codex exec --json emits usage events. Compacting without limiting turns may simply let the agent spend for longer, now with a tidy desk.

A local policy can also abort after three identical patches or test failures by comparing hashes in the harness. This is not an official feature of OpenAI, xAI, MiniMax, Codex, or Hermes; it is the team's own rule. Record the hash and reason in the log without retaining prompts or sensitive content.

Layer 3: a usage.json that CI can understand

GitHub Actions does not see LLM tokens natively and does not calculate “spend per file” from the diff. The agent or its harness needs to sum the usage from each response and produce one artifact per task or PR.

With Codex in non-interactive mode, the JSONL provides counters for each turn:

codex exec --json "implemente apenas o teste que está falhando" > /tmp/codex.jsonl

jq -s '
  reduce (.[] | select(.type == "turn.completed") | .usage) as $u
    ({input_tokens: 0, cached_tokens: 0, output_tokens: 0};
     .input_tokens += ($u.input_tokens // 0) |
     .cached_tokens += ($u.cached_input_tokens // 0) |
     .output_tokens += ($u.output_tokens // 0))
' /tmp/codex.jsonl > usage.json

For xAI, the official cost tracking includes input_tokens, output_tokens, total_tokens, and cost_in_usd_ticks in every response; US$1 corresponds to 10^10 ticks. Sum all responses in the conversation—the field is per request. For streaming, enable stream_options: {"include_usage": true} to receive usage in the final chunk. For MiniMax, persist prompt_tokens, completion_tokens, and prompt_tokens_details.cached_tokens from each response.

A useful internal contract, which is not any vendor's schema, looks like this:

{
  "task_id": "pr-184",
  "provider": "xai",
  "model": "grok-4.6",
  "input_tokens": 23000,
  "cached_tokens": 8000,
  "output_tokens": 4100,
  "usd": 0.0586
}

These values are only an example of the format. The real file must be produced from responses, never filled in manually. Do not include the prompt, completion, diff, or tool output. The task and PR identifier already makes it possible to aggregate cost without sending code to the dashboard.

The gate uses the repository policy and the artifact, not some GitHub telepathy:

- name: Validar orçamento da tarefa
  shell: bash
  run: |
    jq -e '
      (.input_tokens + .output_tokens) <= 120000 and
      (.usd == null or .usd <= 1.50)
    ' usage.json

- name: Publicar medição
  uses: actions/upload-artifact@v4
  with:
    name: agent-usage
    path: usage.json

GitHub Actions artifact uploads preserve the evidence behind the decision. The remediation for an exceeded gate is to reduce the scope and open a new task with its own budget, not raise the limit in the same job. For production observation, aggregate task_id, provider, model, tokens, and usd in a dashboard; never expose an Admin key to a workflow that executes untrusted PR code.

For OpenAI, the dashboard can be reconciled without prompts through the Usage and Costs Admin APIs. GET /v1/organization/usage/completions aggregates tokens and requests by project, key, or model; GET /v1/organization/costs returns amounts in USD. Keep the Admin key in a collector separate from the PR runner.

What each token costs on August 29, 2026

The official pages publish prices per 1 million tokens. The table below divides those amounts by one thousand; it does not mix credits from ChatGPT or Codex plans with API billing.

Model and tierInput / 1kCache / 1kOutput / 1k
gpt-5.6-sol, standard shortUS$ 0,00400US$ 0,00040US$ 0,02000
grok-4.6, prompt below 200kUS$ 0,00200US$ 0,00050US$ 0,00600
MiniMax-M3, PAYG standard up to 512k billedUS$ 0,00030US$ 0,00006 cache readUS$ 0,00120

For gpt-5.6-sol, the official table also charges US$5 per million for cache writes; the stated promotional price is valid at least through November 21, 2026. With Grok 4.6, requests with prompts of 200k or more double every price for the request. In MiniMax M3 PAYG, the figures above are the billed prices with the permanent 50% discount stated on that date; above 512k, rates double.

List price helps define the budget, but the gate should prefer billed cost when the provider supplies it. Caching, server-side tools, and service tiers make naive multiplication insufficient.

Verify before trusting the cap

Run a cheap execution and confirm the entire path:

  1. Check that every response contains usage; for xAI, confirm that the sum of cost_in_usd_ticks / 10^10 matches the dashboard within rounding.
  2. Compare the daily OpenAI Usage API bucket with the dashboard and with the sum of the same project's usage.json files.
  3. In a disposable project, configure a small hard limit and confirm the 429 with project_spend_limit_exceeded. Do not use the main organization for this test.
  4. Force max_turns and run_budget_seconds in a controlled task and confirm the stop reason, artifact, and log.
  5. Verify that the workflow fails with a usage.json above the limit and publishes the artifact when it remains below.

DevDojo would adopt this design when agents can run autonomous tasks in CI or open PRs billed through an API. The team would fall back to manual execution and smaller scope if the provider does not expose reliable usage per response, if the harness cannot stop the task, or if reconciliation between artifacts and the bill diverges repeatedly.

The next step is simple: choose a short task, apply turn and time limits, generate the first usage.json, and make CI reject it on purpose. A circuit breaker only deserves the name after someone proves that it opens.

ai-agentsgithubdevex

// Continue.training — 次のステップ

Knowledge only counts when it becomes practice.

Go back to the article, run the examples, and share what you learned.

Explore more articles