Skip to content

Reasoning Tools

Turn reasoning into explicit, bounded, inspectable tool calls.

Reasoning strategies (PromptBuilder(reasoning_strategy="react")) nudge the model to think a certain way, but the thinking stays hidden inside the model's output. Reasoning tools make each reasoning step a think / analyze tool call instead — so the chain shows up as structured steps in the trace and is bounded by explicit min_steps / max_steps. The two compose: a strategy shapes how the model reasons; the tools make that reasoning visible and bounded.

Marked @beta. Lives in selectools.toolbox.reasoning_tools.

Quick start

from selectools import Agent
from selectools.toolbox.reasoning_tools import make_reasoning_tools

agent = Agent(
    tools=[*my_tools, *make_reasoning_tools(min_steps=1, max_steps=8)],
    provider=provider,
)
agent.run("Plan and execute the migration.")

The agent now has a think tool (a scratchpad for one reasoning step) and an analyze tool (evaluate a result and decide the next step). Both are plain tools that return an acknowledgement — they call no external system; their value is that the reasoning becomes part of the conversation as discrete, inspectable steps.

Inspecting the chain

Hold a ReasoningTools instance to read the recorded steps after a run:

from selectools.toolbox.reasoning_tools import ReasoningTools

reasoning = ReasoningTools(min_steps=2, max_steps=6)
agent = Agent(tools=[*my_tools, *reasoning.tools], provider=provider)
agent.run("...")

for step in reasoning.steps:        # list[ReasoningStep]
    print(step.index, step.kind, step.content)

reasoning.reset()                   # reuse the instance for another run

Both think and analyze count against the same budget, so reasoning.count is the total number of reasoning steps taken.

Bounds

Bound Behavior
max_steps (default 10) Enforced. Once reached, further think/analyze calls are not recorded and return a message telling the agent to stop reasoning and answer — a real guard against reasoning loops. Pass None for unbounded.
min_steps (default 1) Guidance. Advertised in the tool descriptions; each call reports how many more steps are expected. A model cannot be forced to call a tool, so the floor is a nudge, not a hard gate.
make_reasoning_tools(min_steps=0, max_steps=None)  # optional, unbounded
make_reasoning_tools(min_steps=3, max_steps=12)     # think hard, but cap it

Invalid bounds (min_steps < 0, max_steps < 1, max_steps < min_steps) raise ValueError at construction.

API

Symbol Description
make_reasoning_tools(min_steps=1, max_steps=10) -> list[Tool] Fresh think + analyze tools backed by a new bounded log.
ReasoningTools(min_steps=1, max_steps=10) Holds the log; .tools, .think_tool(), .analyze_tool(), .steps, .count, .reset().
ReasoningStep index (1-based), kind ("think"/"analyze"), content.

When to use which

  • Reasoning strategy (prompt) — lightweight, zero tool overhead; good default.
  • Reasoning tools — when you want the chain recorded (for evals, debugging, audit), or want a hard cap on reasoning effort. Use both together for shaped, visible, bounded reasoning.