sipi.bot integration
sipi.bot + Microsoft AutoGen
AutoGen's multi-agent conversations can rack up spend quickly. sipi.bot enforces per-agent, per-conversation, and total-run spend ceilings.
The cost of a conversation grows faster than the conversation
AutoGen's premise is that agents talk to each other. Its cost profile follows directly: every round, each participant is sent the transcript so far. Round twenty is not the same price as round two, because round twenty carries nineteen rounds of history in its input.
That single fact explains most AutoGen bill shocks. A run that felt fine at five rounds during development becomes dramatically more expensive at forty in production, and the change in cost is not proportional to the change in rounds.
The characteristic runaway is politeness. Two agents that keep acknowledging each other, refining a plan, or deferring to one another will converse until something stops them. Nothing in the conversation itself signals a problem — each individual message is perfectly reasonable.
max_round counts turns, not money
Setting a round limit is correct and it is not a budget. It bounds the number of exchanges; it says nothing about what an exchange costs, and — because of the transcript growth above — the last rounds under the limit are the most expensive ones you will pay for. A round cap and a spend ceiling answer different questions and you want both.
Give every agent its own identity
The most useful thing you can do on an AutoGen integration is stop treating the
crew as one spender. Register each ConversableAgent separately and send
its own key, and the audit trail tells you which participant is burning the budget —
usually not the one you would have guessed.
import requests
from autogen import ConversableAgent
def spend_guard(agent_name, sipi_key):
def check(amount, description):
d = requests.post(
"https://sipi.bot/v1/transactions/evaluate",
headers={"Authorization": f"Bearer {sipi_key}"},
json={"amount": amount, "merchant": "llm-provider",
"category": "conversation",
"description": f"{agent_name}: {description}"},
timeout=5,
).json()
if d["decision"] != "APPROVED":
raise RuntimeError(f"{agent_name} {d['decision']}: {d['reason']}")
return check
researcher = ConversableAgent("researcher", llm_config=cfg)
guard = spend_guard("researcher", RESEARCHER_KEY)
Per-agent keys also mean a per-agent ceiling. One participant hitting its limit stops that participant rather than the whole run — often the difference between a degraded result and no result.
Rules for multi-agent runs
| Rule type | What it does on this integration |
|---|---|
velocity | The primary control. A conversational loop shows up as many calls in a short window long before it shows up as a large total. |
daily_total | Bounds the aggregate across every conversation the crew runs. |
approval_threshold | Escalates a long-running deliberation to a human instead of killing work that may be close to converging. |
per_transaction | Catches the late-round call carrying an enormous transcript. |
What it will not do for you
- Judge whether the conversation is productive. Two agents converging usefully and two agents stuck in a loop generate identical spend signals.
- Summarise or truncate the transcript. Controlling context growth is a design decision in your agent configuration, not something a firewall can retrofit.
- See spend from a human participant in the loop, or from any call made outside the guarded path.
Questions
Where in the round should the check go?
Before the model call the agent is about to make, not after the round completes. By the time a round has finished, its cost is already incurred.
Should a blocked agent end the whole run?
Usually not. Catch the block in that agent's path and let it return a message saying it has reached its budget. The group chat can often finish without it, and a graceful degradation beats a stack trace.