Skip to content

Set an agent's budget

View as Markdown

This guide caps the model calls, the spend and the time of each agent turn, and handles a turn that runs out.

A budget caps each turn of an agent: how many model calls it makes, what they cost, and how long it takes. A turn that runs out fails, and the run says which limit it hit.

  • An agent, in a pack on a runtime such as kindgi dev, with Kindgi 0.1's CLI. This guide uses acme.order-desk, from Write an agent.
  • A model registered with prices, for the cost budget. A model with a price of zero never uses it.
  • For the app's half: Node 22.12 or later, Python 3.11 or later, or Java 17 or later, with Kindgi 0.1's client, and KINDGI_API_URL and KINDGI_API_TOKEN set.

Ask your coding agent: "Limit what each turn of my agent can spend, and how long it can take." Kindgi's skills in your pack tell it how (How it knows Kindgi). For reference, it adds budget to the agent's definition:

// in agents/order-desk/index.ts
budget: { maxSteps: 6, maxCostUsd: 0.05, maxWallMs: 60_000 },
Excerpt
Field Caps Default
maxSteps Model calls in the turn. 8
maxCostUsd The turn's model spend, in US dollars. none
maxWallMs The turn's time, in milliseconds. 120000

Each turn of the agent now stops at the first limit it reaches.

A step is one model call. The tool calls the model asks for run between steps, so a turn that looks an order up and then answers takes two. Repairing a typed answer takes a step too.

Cost is what the models' registered prices make of the tokens each call used, as Models describes. A model registered with a price of zero never uses the cost budget. Cost is an estimate from the vendor's published prices: your provider's invoice is what you pay.

Each limit fails the turn with its own error. kindgi runs start prints it and exits 1. The run, on stdout, has "status": "failed" and the error in its failureMessage. These are from acme.order-desk with one limit set low at a time, on a local llama3.1 registered with a price.

Steps. After a model call that asks for more tool calls, with no steps left:

Error [budget-exceeded]: Agent turn steps budget exceeded (limit 1, observed 1)

Cost. Checked after each model call, against the turn's total so far:

Error [budget-exceeded]: Agent turn cost budget exceeded (limit 0.0001, observed 0.0001084)

Time. A timer starts with the turn. When it fires, the model call in flight is cancelled:

Error [agent-turn-aborted]: Agent turn aborted: Request was aborted.

kindgi runs journal <run-id> shows how far the run got, where <run-id> is the failed run's id.

Leave room for real models: one call to a hosted model takes a second or more, and a model on your own machine can take ten. A turn with a tool call makes two calls.

Your app can tell a turn that ran out from other failures. The failed turn comes back as an error, with the limit in its fields. Ask your coding agent: "I want my app to tell when a turn ran out of budget, and which limit it hit." For reference, the code looks like this:

scripts/budget.ts
import { KindgiApiError, createClient } from '@kindgi/sdk/client';
const kindgi = createClient(); // KINDGI_API_URL, KINDGI_API_TOKEN, or the running kindgi dev
try {
await kindgi.runs.start({
agent: 'acme.order-desk',
input: { userMessage: 'Where is my order A-1001?' },
});
} catch (err) {
if (!(err instanceof KindgiApiError)) throw err;
const e = err.error;
if (e.code === 'server' && e.serverCode === 'budget-exceeded') {
console.log(e.fields?.kind, e.fields?.limit, e.fields?.observed, e.fields?.runId);
} else {
throw err;
}
}
steps 1 1 2cc56724-92e7-42e8-aefd-14ad5c294d1f

It prints which limit the turn hit, the limit, what the turn used, and the run's id. A turn that runs out of time has serverCode agent-turn-aborted instead, with reason timeout.

An agent step whose turn runs out fails, and the flow run fails with it. The run carries the reason:

Terminal window
pnpm exec kindgi runs start --flow=acme.notify-customer --input='{"orderId":"A-1001"}'

--flow is the flow to run, and --input its input. It prints the failed run:

{
…
"flowId": "acme.notify-customer",
…
"status": "failed",
…
"failureMessage": "budget-exceeded: Agent turn cost budget exceeded (limit 0.00001, observed 0.00031)",
"output": {},
…
}
Excerpt

Here the step's agent, acme.order-update from Give an agent its input, had budget: { maxCostUsd: 0.00001 }.