Skip to content

feat(session): rethink tool budget with per-call counting and graceful wind-down#280

Merged
Aaronontheweb merged 1 commit into
devfrom
feat/tool-budget-rethink
Mar 18, 2026
Merged

feat(session): rethink tool budget with per-call counting and graceful wind-down#280
Aaronontheweb merged 1 commit into
devfrom
feat/tool-budget-rethink

Conversation

@Aaronontheweb

Copy link
Copy Markdown
Collaborator

Summary

Continues #277 — replaces the iteration-based tool circuit breaker with a smarter three-phase approach inspired by OpenCode (tool stripping + summary prompt) and LangGraph (budget awareness nudge).

Problem: The old circuit breaker counted iteration rounds (default 10), then hard-stripped tools. Models (especially Qwen) often hallucinated tool calls after tools were removed, causing a turn_force_no_tools_violation with the misleading "I didn't produce a reply" message.

Solution: Three-phase budget with per-call counting:

  1. Per-call counting (MaxToolCallsPerTurn=30): Each tool call counts individually. 3 parallel calls in one batch = 3 against the budget. More accurate than counting rounds.

  2. Budget nudge at ~75%: Injects a system message: "You have used X of Y tool calls. Start wrapping up." Gives the model awareness to self-regulate.

  3. Graceful wind-down at 100%: Strips tools AND injects "Summarize your work and answer the user's question." The model gets one turn to produce a proper summary instead of being abruptly cut off.

  4. Specific violation message: If the model still hallucinate tool calls, the error says "I used all available tool calls" instead of the generic fallback.

Files changed

File Change
SessionConfig.cs MaxToolIterationsPerTurnMaxToolCallsPerTurn (default 30)
LlmSessionActor.cs Per-call counting, budget nudge, summary prompt, specific violation message
MaxToolIterationTests.cs Updated for new config name and violation message
configuration.md Updated config docs

Test plan

  • Tool_iteration_limit_forces_text_response — budget triggers at correct call count
  • Tool_iteration_limit_fails_turn_when_model_keeps_emitting_tool_calls_without_tools — violation uses specific message
  • Tool_iteration_counter_resets_between_turns — counters reset correctly
  • Normal_tool_use_within_limit_works_unchanged — normal flow unaffected
  • All 913 tests pass with zero regressions

…ge, and graceful wind-down (#277)

Replace the iteration-based tool circuit breaker (MaxToolIterationsPerTurn=10)
with per-call counting (MaxToolCallsPerTurn=30). Each tool call in a batch
counts individually, giving a more accurate budget.

Three-phase approach inspired by OpenCode and LangGraph:
1. At ~75% budget: inject a system nudge so the model can self-regulate
2. At 100% budget: strip tools AND inject a "summarize your work" prompt
3. If model still hallucinate tool calls: fail with a specific message
   ("I used all available tool calls") instead of generic "I didn't produce
   a reply"

The key insight: give the model awareness and agency to wrap up gracefully,
rather than just pulling the rug out.
@Aaronontheweb Aaronontheweb merged commit 8a0b685 into dev Mar 18, 2026
3 checks passed
@Aaronontheweb Aaronontheweb deleted the feat/tool-budget-rethink branch March 18, 2026 04:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant