Repository navigation
Description
Summary
AgentLoopMiddleware has one budget dimension today: max_iterations (default 10). This issue proposes adding token budget and wall-clock time budget as first-class parameters, integrated into the loop's existing stop evaluation, in both the Python and .NET implementations.
We intend to take on implementation.
User need
When a user delegates a task to an autonomous agent, they are accepting a cost in tokens and time. Two failure modes need to be bounded:
- Token runaway — an agent in a confused or stuck loop continues issuing LLM calls. Without a token cap, this continues until something external intervenes.
- Time runaway — an agent waiting on a slow or hung external system doesn't burn tokens, but holds a session open indefinitely. Token budgets alone don't catch this.
Operators deploying autonomous agents need explicit, enforced limits — not defaults that permit unbounded runs.
Developer need
There is no standard token or wall-clock budget primitive in MAF. Every team building autonomous loops implements cost controls from scratch, with different semantics for what happens when limits are hit, and no shared warning-threshold mechanism. Wall-clock limits in particular are rarely implemented correctly.
Additional gaps:
- Hierarchical chargeback: when an agent spawns sub-agents via
BackgroundAgentsProvider, sub-agent token spend is invisible to the parent's budget. - Warning thresholds: teams find out a budget was exceeded after the fact, not at 80% when the agent could still wrap up cleanly.
What's already there
- Python:
AgentLoopMiddleware(python/packages/core/agent_framework/_harness/_loop.py) — hasmax_iterationschecked in_evaluate_stopbeforeshould_continue. This is the correct integration point for additional budget dimensions.ContextWindowCompactionStrategyhandles context window tokens for compaction, not cost control. - .NET:
AgentLoopMiddlewareequivalent indotnet/src/Microsoft.Agents.AI/Harness/Loop/anddotnet/src/Microsoft.Agents.AI.Harness/.BackgroundAgentsProviderexists in both languages. - Neither implementation has
max_tokens,max_duration_seconds, warning thresholds, or hierarchical chargeback.
Proposal
An AgentBudget type with three independently-configured dimensions, added as an optional parameter to AgentLoopMiddleware:
| Dimension | Field | Default |
|---|---|---|
| Token budget | max_tokens |
No default — operator-required |
| Iteration cap | max_iterations |
Already exists |
| Wall-clock time | max_duration_seconds |
No default — operator-required |
max_tokens and max_duration_seconds have no framework defaults intentionally — they are too workload-specific. For autonomous sessions, the framework should require these to be set explicitly.
Integration into stop evaluation: budget checks run before should_continue, consistent with how max_iterations is already handled. Each dimension that fires produces a typed exit reason (see related issue #8860 on typed exit reasons).
Wall-clock enforcement: a background async task started when the loop begins. On expiry it cancels the loop.
Warning events at 80%: when any budget dimension reaches 80% of its limit, emit a warning into the agent context or session state so the agent can wrap up before the hard cap fires.
Hierarchical chargeback: child agent token spend (via BackgroundAgentsProvider) reduces the parent's remaining max_tokens. Wall-clock budget does not propagate — each agent in a hierarchy has its own time limit.
Scope
- Both Python and .NET implementations are in scope. The Python harness (
python/packages/core/agent_framework/_harness/) and .NET harness (dotnet/src/Microsoft.Agents.AI/Harness/anddotnet/src/Microsoft.Agents.AI.Harness/) maintain feature parity; changes to one require equivalent changes in the other. AgentBudgettype: new in core (Python and .NET), experimental-flagged- Integration into
AgentLoopMiddlewarestop evaluation in both languages - Background async task for wall-clock enforcement
- Chargeback hook into
BackgroundAgentsProvider(may require design coordination)
Open questions
- Where should warning events surface — agent context injection, session state, a new event type, or all three?
- The chargeback integration with
BackgroundAgentsProviderneeds design review — specifically, whether chargeback should be opt-in or automatic when a budget is configured.
Related
- Add signal-based completion detection and typed exit reasons to AgentLoopMiddleware #8860 — typed exit reasons (budget firing produces a typed exit reason)
- Add AsyncRequestBroker utility and shell execution audit log #8861 —
AsyncRequestBroker(independent, but part of the same autonomous agent primitive set)
Metadata
Metadata
Labels
Type
Projects
- StatusShow more project fieldsNo status
Activity
[-]Add token and wall-clock budget enforcement to AgentLoopMiddleware[/-][+]Python: Add token and wall-clock budget enforcement to AgentLoopMiddleware[/+][-]Python: Add token and wall-clock budget enforcement to AgentLoopMiddleware[/-][+]Add token and wall-clock budget enforcement to AgentLoopMiddleware[/+]marklicata commentedon Oct 1, 2026
Hello Eduard van Valkenburg (@eavanvalkenburg), would love to hear your thoughts on this work. And we would be taking on the implementation, so should only need review/oversight from the MAF team.
eavanvalkenburg commentedon Nov 2, 2026
Mark Licata (@marklicata) The overall direction here makes sense to me. We can extend the existing loop types with budget limits. I would, however, keep sub-agent accounting out of the first framework implementation and demonstrate it in a sample instead.
We plan to remove
agent.as_toolin favor of samples (tracked in #8950) showing explicit agent-as-tool composition. There are several issues with that abstraction, including the accounting problems exposed here, so I do not want this work to deepen our dependency on it. This is not a request to remove the existing API in this PR.There is also a more fundamental accounting question: a sub-agent can use a different model from its parent. Adding their token counts does not necessarily describe the cost we want to limit. Different models have different input/output prices, and a local model might have no per-token charge at all. For a spend budget, those tokens need to become model-specific costs under an application-owned policy, rather than being treated as interchangeable tokens by the framework.
For this first Python version, please:
as_tooland background-agent rollups and their supporting telemetry changes. Document that child-agent usage is excluded from the built-in budget.agent.as_tool. Keep usage attributable to the relevant model and make the aggregation or cost policy explicit, so the example does not imply that tokens from different models always have the same cost.This gives us a useful first budget implementation and starts the sample-based replacement for
agent.as_toolat the same time. It also lets applications choose the accounting policy that fits their models without committing core to a premature cost abstraction. The other feedback on loop-local duration enforcement and exit-reason handling still applies. We can revisit built-in hierarchical accounting once we have experience with the sample and a clearer contract for mixed-model costs.Python: remove as_tool budget rollup; fix max_duration in-flight enfo…
marklicata commentedon Nov 3, 2026
Thanks Eduard van Valkenburg (@eavanvalkenburg). Updated commit pushed to #9164.
Agreed and done:
agent.as_toolrollup (as_toolis deprecated, Python: RemoveAgent.as_tool()in vNext #8950)max_duration: now wraps each iteration inasyncio.wait_forso in-flight calls are actually cancelled, not just skipped after the factOne point of disagreement on sub-agent accounting:
We read
max_tokensas a thinking budget — a bound on total inference work — not a cost proxy. An agent that routes around its reasoning cap by spawning background sub-agents has effectively bypassed the budget; rolling their tokens in is what makes the bound meaningful.If cost were the goal,
max_tokenswould already be the wrong abstraction regardless of rollup — you'd need model-specific pricing. I've updated the docstring to make the thinking-budget framing explicit.BackgroundAgentsProviderrollup stays.Happy to discuss if you see it differently.