Skip to main content

Reasoning Agent

Routes queries to either a fast LLM or a reasoning-capable LLM based on query complexity. Simple queries use the fast path; complex queries (math, multi-step reasoning, architecture, debugging) are sent to the reasoning LLM with a token budget. Falls back to the fast LLM on timeout or budget exhaustion.

Import:

from synapsekit.agents import ReasoningAgent, ReasoningAgentConfig

No extra dependency beyond the reasoning LLM providers you are already using.


ReasoningAgentConfig

from synapsekit.agents import ReasoningAgentConfig
from dataclasses import dataclass

@dataclass
class ReasoningAgentConfig:
fast_llm: BaseLLM
reasoning_llm: ReasoningLLM
tools: list[BaseTool]
agent_type: Literal["react", "function_calling"] = "react"
max_iterations: int = 10
system_prompt: str = "You are a helpful AI assistant."
classifier_llm: BaseLLM | None = None
classifier_prompt: str | None = None
complexity_threshold: int = 2
thinking_budget_tokens: int = 15000
timeout_seconds: float | None = 30.0
fallback_on_error: bool = True
ParameterTypeDefaultDescription
fast_llmBaseLLMrequiredLLM for simple queries
reasoning_llmReasoningLLMrequiredReasoning-capable LLM for complex queries
toolslist[BaseTool]requiredTools available to both executors
agent_typestr"react"Agent loop type: "react" or "function_calling"
max_iterationsint10Maximum agent loop iterations
system_promptstr"You are a helpful AI assistant."System prompt for both executors
classifier_llmBaseLLM | NoneNoneLLM for query classification; falls back to heuristics if not set
classifier_promptstr | NoneNoneCustom classifier prompt template with {query} placeholder
complexity_thresholdint2Minimum heuristic score to classify a query as complex
thinking_budget_tokensint15000Maximum thinking tokens allowed per reasoning call
timeout_secondsfloat | None30.0Timeout for the reasoning path in seconds; None = no timeout
fallback_on_errorboolTrueFall back to fast_llm on timeout or budget exceeded

function_calling agent type is only supported for OpenAI and Anthropic reasoning models.


ReasoningAgent

from synapsekit.agents import ReasoningAgent

agent = ReasoningAgent(config: ReasoningAgentConfig)

Methods

  • async run(query: str) -> str — classify the query, run the appropriate executor, return the answer string
  • async stream(query: str) — async generator; runs run() and yields tokens

Properties

  • last_decision: ReasoningDecision | None — classification result from the most recent call
  • last_usage: ReasoningUsage | None — token usage from the reasoning LLM (if used)
  • last_fallback_reason: str | None — reason for the fast-path fallback if one occurred

ComplexityClassifier

Classifies a query as "simple" or "complex". Used internally by ReasoningAgent but available standalone.

from synapsekit.agents.reasoning_agent import ComplexityClassifier

classifier = ComplexityClassifier(
llm: BaseLLM | None = None,
prompt: str | None = None,
threshold: int = 2,
)
ParameterTypeDefaultDescription
llmBaseLLM | NoneNoneOptional LLM for classification; falls back to heuristics
promptstr | NoneNoneCustom prompt template; must contain {query}
thresholdint2Heuristic score threshold for "complex"

Methods

  • async classify(query: str) -> ReasoningDecision — classify the query; uses LLM if configured, heuristics otherwise

Heuristic signals: long queries (30+ words), multiple questions, math symbols/keywords (theorem, proof, , matrix), reasoning keywords (derive, optimize, architecture, complexity).


ReasoningDecision

from dataclasses import dataclass

@dataclass
class ReasoningDecision:
complexity: Literal["simple", "complex"]
reason: str
score: float | None = None
FieldTypeDescription
complexity"simple" | "complex"Classification result
reasonstrHuman-readable explanation of the classification
scorefloat | NoneHeuristic score if heuristics were used

Example

import asyncio
from synapsekit import OpenAILLM, LLMConfig
from synapsekit.llm import ReasoningLLM
from synapsekit.agents import ReasoningAgent, ReasoningAgentConfig
from synapsekit.agents.tools import CalculatorTool, WebSearchTool

async def main():
# Fast LLM for simple queries
fast_llm = OpenAILLM(LLMConfig(model="gpt-4o-mini", api_key="sk-..."))

# Reasoning LLM for complex queries
reasoning_llm = ReasoningLLM(
provider="openai",
model="o1-mini",
api_key="sk-...",
)

config = ReasoningAgentConfig(
fast_llm=fast_llm,
reasoning_llm=reasoning_llm,
tools=[CalculatorTool(), WebSearchTool()],
thinking_budget_tokens=10_000,
timeout_seconds=60.0,
fallback_on_error=True,
)

agent = ReasoningAgent(config)

# Simple query — routed to fast_llm
answer = await agent.run("What is the capital of France?")
print(f"[{agent.last_decision.complexity}] {answer}")
# > [simple] Paris

# Complex query — routed to reasoning_llm
answer = await agent.run(
"Derive the time complexity of Dijkstra's algorithm with a Fibonacci heap "
"and explain why it is optimal for sparse graphs."
)
print(f"[{agent.last_decision.complexity}] {answer[:120]}...")
print(f"Thinking tokens used: {agent.last_usage.thinking_tokens if agent.last_usage else 'n/a'}")

# Inspect classification
decision = agent.last_decision
print(f"Classification reason: {decision.reason}")

asyncio.run(main())

See also