Reasoning Agents (GPT-5.6 Sol, high to max effort)
On this page
Updated November 2, 2026 — models.
o3,o3-pro, and legacy GPT-5 snapshots will be removed from the API on December 11, 2026, ando4-minihas long been superseded. For new agents, start with the GPT-6 family: GPT-6.1 Sol for balance, GPT-6 Astra for the most demanding tasks.
Reasoning Agents (GPT-5.6, high to max effort)
With the GPT-5.6 family (available since July 2026), reasoning is no longer handled by separate models as in the previous generation (o3-pro, o4-mini): it is now a per-request setting (none, low, medium, high, xhigh, max) applicable to every model. An agent that truly needs to think—exploring multiple paths before answering—will typically use GPT-5.6 Sol with high or xhigh effort. Integrated into your agents, this reasoning capability unlocks tasks that demand genuine deliberation.
When to enable reasoning in an agent
Deep reasoning shines in specific situations, and experience shows they all share a common trait: comparing possible paths before making a decision. This applies to complex planning, where you break a task into optimal steps, and mathematical analysis—calculations, statistics, optimization—where one wrong step invalidates the rest. It also applies to debugging, which involves tracing a symptom back to its root cause, and decision-making, where you must evaluate several options and choose the best one. Code review falls into the same category: finding a subtle bug requires mentally simulating executions that linear reading won’t reveal.
For simple tasks (answering a question, formatting text), GPT-5.6 Terra or Luna with low effort (or even none) is faster and cheaper. Paying for reasoning to rephrase a paragraph is like idling a race car in traffic.
Sol + high/xhigh effort: deep reasoning
GPT-5.6 Sol with high reasoning effort replaces what o3-pro used to do. The agent takes more time but produces deeper analyses. The example below is typical: a strategic question with three options, where the right answer depends on trade-offs between risk, cost, and time horizon. Note that the instructions explicitly define the dimensions to consider—reasoning always benefits from clear constraints.
from agents import Agent, Runner, ModelSettings
from openai.types.shared import Reasoning
strategic_advisor = Agent(
name="Strategic Advisor",
instructions="""You are a business strategy consultant.
Analyze deeply before recommending.
Always consider: risks, opportunities,
costs, and alternatives.""",
model="gpt-5.6-sol",
model_settings=ModelSettings(reasoning=Reasoning(effort="high")),
)
result = Runner.run_sync(
strategic_advisor,
"""Our SaaS startup has 500 clients, €2M MRR, and is growing 15% per month.
We are debating:
A) Raise a Series B (€20M) and accelerate in the US
B) Stay bootstrapped and consolidate in Europe
C) Seek a strategic acquirer
Which strategy do you recommend and why?"""
)
print(result.final_output)
For the most critical analyses, ramp up to xhigh or max. There is also an ultra setting (announced in July 2026) that coordinates 4 agents in parallel by default (up to 16 on certain evaluations) for the heaviest tasks—powerful, but with significantly higher token consumption: reserve it for problems that justify the cost.
Terra + medium effort: fast reasoning
Between no reasoning and deep analysis lies a sweet spot widely used in production. GPT-5.6 Terra with medium effort offers a good compromise between reasoning capability and speed—the equivalent of the former o4-mini. Ideal for agents that need to reason quickly, like this diagnostic agent that reads server metrics and must link memory saturation to a spike in 5xx errors within seconds, while the site is experiencing an incident.
from agents import Agent, Runner, ModelSettings, function_tool
from openai.types.shared import Reasoning
@function_tool
def get_server_metrics(server: str) -> str:
"""Retrieves metrics for a server."""
import json
metrics = {
"cpu": 87.5,
"ram": 92.1,
"disk": 65.3,
"requests_per_second": 1250,
"response_time_p99": 450,
"errors_5xx": 23,
}
return json.dumps(metrics)
diagnostic_agent = Agent(
name="Diagnostic Agent",
instructions="""You diagnose infrastructure problems.
Analyze the metrics and identify the probable cause.
Propose immediate and long-term corrective actions.""",
model="gpt-5.6-terra",
model_settings=ModelSettings(reasoning=Reasoning(effort="medium")),
tools=[get_server_metrics],
)
result = Runner.run_sync(
diagnostic_agent,
"The site has been slow for 30 minutes. Diagnose server web-prod-01."
)
Pattern: configuration router
You don’t have to pick one configuration forever. Use a lightweight configuration for routing and the appropriate one for execution: a first agent reads the task, returns a typed object indicating which model and effort to use, then you instantiate the execution agent with that decision. The reason field isn’t decorative—it lets you, when reviewing your logs, verify that the router isn’t systematically over-provisioning.
from pydantic import BaseModel
from typing import Literal
class ConfigDecision(BaseModel):
model: Literal["gpt-5.6-luna", "gpt-5.6-terra", "gpt-5.6-sol"]
effort: Literal["none", "low", "medium", "high", "xhigh"]
reason: str
router_agent = Agent(
name="Smart Router",
instructions="""Analyze the task and decide which configuration to use:
- gpt-5.6-luna, effort none/low: simple tasks (summaries, rephrasing, factual answers)
- gpt-5.6-terra, effort medium: fast reasoning (classification, diagnostics)
- gpt-5.6-sol, effort high/xhigh: complex tasks (strategic planning, deep analysis, math)""",
model="gpt-5.6-terra",
model_settings=ModelSettings(reasoning=Reasoning(effort="medium")),
output_type=ConfigDecision,
)
async def process_with_optimal_config(task: str):
# Step 1: choose the configuration
decision = await Runner.run(router_agent, task)
config = decision.final_output
print(f"Configuration: {config.model} / effort {config.effort} ({config.reason})")
# Step 2: execute with the right configuration
execution_agent = Agent(
name="Execution Agent",
instructions="Accomplish the requested task with precision.",
model=config.model,
model_settings=ModelSettings(reasoning=Reasoning(effort=config.effort)),
)
result = await Runner.run(execution_agent, task)
return result.final_output
Streaming behavior with reasoning enabled
When reasoning is enabled, streaming has a quirk: the model’s internal thought process is not streamed in plain text. Your interface therefore receives tokens for the final answer, but nothing during the reasoning phase.
agent = Agent(
name="Analyst",
instructions="Analyze the data deeply.",
model="gpt-5.6-sol",
model_settings=ModelSettings(reasoning=Reasoning(effort="high")),
)
# Streaming delivers the final answer, not the intermediate reasoning
result = Runner.run_streamed(agent, "Analyze this complex problem.")
async for event in result.stream_events():
if event.type == "raw_response_event":
if hasattr(event.data, "delta") and event.data.delta:
print(event.data.delta, end="", flush=True)
Account for this latency in your UX: with high effort or higher, the first tokens may arrive after a significant thinking time. An explicit waiting indicator prevents users from thinking the app has frozen and refreshing the page at the worst moment.
Optimizing costs
Reasoning consumes additional tokens (internal thinking is billed). Two levers help contain costs: lower the default effort and only ramp up when needed, then separate decision from execution—that is, reason to decide but execute without reasoning. The hybrid workflow below applies the second principle—Terra at medium establishes the approach, Luna carries it out with tools.
# Pattern: hybrid reasoning
# Terra + medium for decision, Luna without reasoning for execution
decision_agent = Agent(
name="Decider",
instructions="Decide on the optimal approach.",
model="gpt-5.6-terra",
model_settings=ModelSettings(reasoning=Reasoning(effort="medium")),
)
executor_agent = Agent(
name="Executor",
instructions="Execute the decided plan.",
model="gpt-5.6-luna", # Cheaper for execution
tools=[search_crm, generate_report],
)
async def hybrid_workflow(task: str):
plan = await Runner.run(decision_agent, f"How to handle: {task}")
result = await Runner.run(executor_agent, plan.final_output)
return result.final_output
Key takeaways
- Reasoning is a per-request setting on every GPT-5.6 model (none → max), no longer separate models like o3-pro/o4-mini
- GPT-5.6 Sol with
high/xhigheffort: strategic planning, complex analysis, mathematics - GPT-5.6 Terra with
mediumeffort: fast reasoning for diagnostics and routing - The
ultrasetting coordinates multiple agents in parallel for the heaviest tasks—significantly higher token cost - The router pattern dynamically selects the model + effort combination and optimizes costs
- Reason to decide, execute without reasoning: the best quality-to-price ratio