adagio¶
Earned-capacity pacing for agentic data discovery.
Cheap, informative calls earn tickets. Expensive calls spend them. An agent that has not explored cheaply cannot run expensive operations, so exploration proceeds from light to heavy — enforced at the call boundary rather than requested in a prompt.
It is a token bucket whose refill is event-driven rather than time-driven.
import adagio
ledger = adagio.Ledger()
@ledger.gate(size="xs", subject=lambda table: table)
def describe_table(table: str) -> dict:
"""Metadata only. Costs nothing, earns 1."""
...
@ledger.gate(estimator=adagio.estimators.Function(dry_run))
def run_query(sql: str) -> list[dict]:
"""Priced per call from what the query would actually read."""
...
with ledger.scope("session-123"):
run_query("select * from order_items") # refused — and told why
The refusal is not a bare error. It tells the agent what to do instead:
{
"status": "insufficient_capacity",
"required": 10, "available": 0, "shortfall": 10,
"tier": "heavy", "estimated_bytes": 12884901888,
"suggestions": [
{"callable": "describe_table", "cost": 0, "yield": 1, "net": 1,
"reason": "not yet called for order_items"}
]
}
Why not just a rate limit¶
Rate limits, dollar caps and maximum_bytes_billed are all caps. They stop runaway spend
but do nothing about the order of exploration — an agent well under every limit can still
open with a 12 GB scan against a table it knows nothing about. Adagio makes the expensive call
unavailable until the cheap ones have happened.
| Control | Bounds spend | Shapes order |
|---|---|---|
| Requests per minute | yes | no |
| Token / dollar budget | yes | no |
maximum_bytes_billed |
per query | no |
| Prompt instruction ("explore first") | no | not enforced |
| Adagio | via the cap | yes |
You don't write the cost table¶
Balancing costs and yields by hand is the part that goes wrong. Pick a tempo instead:
| Tempo | Alias | Novel cheap calls before one heavy call |
|---|---|---|
adagio |
strict |
~10 |
andante |
balanced |
~5 |
allegro |
lenient |
~3 |
Everything else is derived and stays internally consistent. See Configuration.
Where to go next¶
- Quickstart — gate your first callable
- Configuration — tempos, tiers, and
adagio.yaml - MCP Server — expose gated tools to a real agent
- How Pacing Works — the call lifecycle, stage by stage
- Concepts — why earned capacity, and how it resists gaming
Status¶
M1. The mechanism is built and tested; the behavioral claim it rests on is not yet measured. See Concepts.