Observed since 20 Oct, 22:01 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
Runtime control plane for AI agents: live cost metering and circuit breakers
Stop your agent before it burns $500 in a retry loop.
AgentFuse is a runtime control plane for AI agents: a reverse proxy that sits between your agents and the Anthropic API, meters every call in real time, detects dangerous patterns — retry loops, cost spirals, stalls — and trips a circuit breaker before the damage is done.
Agent observability tools read yesterday's exhaust. They ingest trace files after the
run is over and tell you, in the morning report, that your researcher agent called
search with the same arguments forty times overnight and spent $500 doing it.
AgentFuse sits in the request path and intervenes while it is happening:
| Axis | Trace-triage tools | AgentFuse |
|---|---|---|
| When | After the run (batch) | During the run (real time) |
| Role | Observe and rank | Meter and intervene |
| Integration | Upload trace files | One-line base_url change |
| Output | Morning report | Tripped breaker + live dashboard |
pip install agentfuse1
fuse demo
fuse demo starts the proxy against a built-in fake upstream and plays two acts.
Act one: a scripted "researcher" agent walks into a retry loop — spend ticks up,
the loop breaker warns, trips, the agent reads the guidance, pivots, and finishes
the job. Act two: a "coder" agent burns expensive calls until the budget breaker
warns at 80% and cuts it off at 100%. In CI, fuse demo --headless runs both arcs
and exits nonzero if either breaker fails to trip.
Run the proxy, then point your existing agent at it — no SDK, no code changes beyond
base_url:
fuse serve # proxy on :9000, dashboard at http://127.0.0.1:9000/
from anthropic import Anthropic
client = Anthropic(base_url="http://localhost:9000/anthropic")
Identity comes from two optional headers (any framework or language that can set a header is supported):
X-Fuse-Agent — logical agent name (default "default")X-Fuse-Run — run/session id (default: one generated run per proxy process)Your API key passes through to the real upstream untouched; AgentFuse never stores it.
Streaming ("stream": true) is fully supported: response chunks are relayed to the
agent the moment they arrive, while AgentFuse assembles its own copy of the stream to
meter tokens and feed the breakers — the agent notices nothing.
OpenAI-based agents get the same protection: point them at
base_url="http://localhost:9000/openai/v1" and calls to chat/completions are
metered and breaker-checked too (OpenAI streaming passes through unmetered for now).
| Breaker | Watches for | Default | Escalation |
|---|---|---|---|
| Loop | same tool called with near-identical arguments N times in a row (volatile keys like timestamps and request ids are ignored, so a loop can't disguise itself) | N = 4 | warn at 4, block at 6 |
| Budget | per-run and per-agent-daily dollar spend | $5 / run, $50 / agent / day | warn at 80%, block at 100% |
| Stall | K consecutive tool_result errors | K = 5 | warn at 5, block at 8 |
| Rate | LLM calls per minute per agent | 30 / min | block at cap, with a Retry-After header saying exactly how long to wait |
A blocked call returns HTTP 429 with a model-readable error body — written for the agent, not just the operator:
{
"type": "error",
"error": {
"type": "agentfuse_blocked",
"message": "AgentFuse blocked this call: you have called `search` with the same arguments 6 times in a row. Try a different approach, different arguments, or a different tool."
}
}
There is also a kill switch: POST /api/runs/{run}/kill (or the dashboard button)
blocks everything for a run until you reset it.
The block message is a feature, not just a stop sign. From the demo transcript:
calls ok=8 blocked=1
[WARN] loop: `search` called with identical arguments 4 times in a row.
[WARN] loop: `search` called with identical arguments 5 times in a row.
[BLOCK] loop: AgentFuse blocked this call: you have called `search` with the
same arguments 6 times in a row. Try a different approach, different
arguments, or a different tool.
demo OK: breaker tripped and agent recovered
The agent reads the guidance, feeds it back into its own context, switches from
search to fetch_docs, and completes the run. A block also resets the streak, so a
healed run keeps working — and an agent that ignores the guidance and keeps looping
re-trips the breaker.
fuse serve (and fuse demo) serve a live dashboard at / — a single HTML file,
no build step, updated over Server-Sent Events:
All sections optional; sensible defaults throughout. See fuse.example.toml:
[upstream]
anthropic = "https://api.anthropic.com"
[budget]
per_run = 5.00 # dollars; omit to disable
per_agent_daily = 50.00
[policies.loop]
threshold = 4
# keys ignored when comparing tool arguments
volatile_keys = ["timestamp", "ts", "request_id", "nonce", "trace_id", "idempotency_key"]
[policies.stall]
threshold = 5
[policies.rate]
calls_per_minute = 30
[alerting]
webhook_url = "" # Slack-compatible
cooldown_seconds = 600
[storage]
db_path = "./fuse.db" # ":memory:" for ephemeral
retention_days = 0 # 0 = keep events forever; >0 prunes older rows
[server]
api_token = "" # if set, /api/* requires Bearer token (dashboard: /?token=...)
Run with fuse serve --config fuse.toml. Inspect the active policies with
fuse policies; check a running proxy with fuse status; dump all metered
events as JSON lines with fuse export (for billing or analysis).
Thresholds and budgets can be changed on a running proxy without a restart:
GET /api/config shows the live values, POST /api/config with a JSON subset
(e.g. {"budget_per_run": 2.5}) hot-swaps the policies.
tool_use blocks in responses, tool_result blocks in requests. Any framework,
any language, zero agent-code changes.The proxy position is the platform: every agent action already flows through it.
pip install -e ".[dev]"
ruff check src tests && mypy && pytest --cov=agentfuse
MIT licensed.
Download counts and popularity will appear here as the collector receives them.
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 0.4.0 | 7 Jul 2026 | 9 |
This describes our collected data; it is not a security assessment.
Observed since 20 Oct, 22:01 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:db4713ff463b37999e0a18fc372cd466c63cce2ad4234286f87da42aeac6f342