Skip to content

Model Switching Per Iteration

Import: from selectools import AgentConfig Stability: beta

model_switching_demo.py
from selectools import Agent, AgentConfig, LocalProvider, tool

@tool(description="Search for information")
def search(query: str) -> str:
    return f"Results for: {query}"

agent = Agent(
    tools=[search],
    provider=LocalProvider(),
    config=AgentConfig(
        model="gpt-4o-mini",
        model_selector=lambda iteration, tool_calls, usage: (
            "gpt-4o" if iteration > 2 else "gpt-4o-mini"
        ),
    ),
)
result = agent.run("Research AI agents and write a summary")
print(result.content)

See Also

Added in: v0.17.4 File: src/selectools/agent/config.py, src/selectools/agent/core.py

Overview

Model switching allows different LLM models on different iterations of the agent loop. Use cheap models for tool selection and expensive models for final synthesis — cutting costs without sacrificing output quality.

Quick Start

from selectools import Agent, AgentConfig

config = AgentConfig(
    model="claude-haiku-4-5",  # default (cheap)
    model_selector=lambda iteration, tool_calls, usage: (
        "claude-sonnet-4-6" if iteration > 3 else "claude-haiku-4-5"
    ),
)

agent = Agent(tools=[...], provider=provider, config=config)
result = agent.run("Research and write a report on AI agents")
# Iterations 1-3: Haiku (tool calls, data gathering)
# Iterations 4+: Sonnet (synthesis, writing)

model_selector Callback

def my_selector(
    iteration: int,           # current iteration (1-based)
    tool_calls: List[ToolCall],  # all tool calls so far
    usage: AgentUsage,        # cumulative token/cost usage
) -> str:
    """Return the model name to use for this iteration."""
    return "gpt-4o" if usage.total_cost_usd > 0.05 else "gpt-4o-mini"

The callback receives full context about the run so far, enabling strategies like:

  • Iteration-based: cheap model early, expensive model late
  • Cost-based: switch to cheap model when budget is getting low
  • Tool-based: use a specific model after certain tools are called

Observer Event

from selectools import AgentObserver

class MyObserver(AgentObserver):
    def on_model_switch(self, run_id: str, iteration: int, old_model: str, new_model: str):
        print(f"Iteration {iteration}: {old_model}{new_model}")

The event only fires when the model actually changes — returning the same model doesn't trigger it.

Trace Verification

Each LLM_CALL trace step records the effective model:

for step in result.trace.steps:
    if step.type == "llm_call":
        print(f"{step.model}: {step.prompt_tokens} prompt, ${step.cost_usd:.4f}")

Reset Between Runs

The effective model resets to config.model at the start of each run via _prepare_run(). The selector is re-evaluated fresh for every run.

See Also

# Script Description
48 48_model_switching.py Per-iteration model switching
25 25_provider_fallback.py Provider fallback with circuit breaker
05 05_cost_tracking.py Cost tracking across models