How to Configure Multi‑Step Reasoning with `max_steps` in Needle

Set the max_steps parameter in agent.run() to control how many reasoning iterations Needle performs—default is 8, increase for complex tool chains, decrease to limit depth, or use 0 to disable looping entirely.

Needle's agentic execution loop lets language models call tools, observe results, and reason across multiple turns. The max_steps argument in the run() method determines precisely how many of these iterations are allowed before the loop terminates. This article explains how to configure multi-step reasoning with max_steps in Needle, with practical examples from the source code.

Understanding the Agentic Loop in Needle

The run() method in needle/__init__.py implements Needle's core reasoning cycle. Each iteration allows the model to emit tool calls, executes the corresponding Python functions, and feeds results back for subsequent reasoning.

def run(self, query: str, max_steps: int = 8, max_new_tokens: int = 256) -> dict:
    # ...implementation in needle/__init__.py

The loop continues until one of three conditions is met:

  • The max_steps limit is reached
  • The model returns a response type other than "call"
  • No function calls are present in the model's output

All intermediate tool results accumulate in the final response under the "results" key.

max_steps Configuration Options

Default Behavior: 8 Steps

If you omit max_steps, Needle defaults to 8 reasoning steps. This suffices for most single-tool or shallow multi-tool tasks.

from needle import Needle, tool

@tool
def get_weather(city: str):
    """Get the weather for a city."""
    return {"temp_c": 20}

agent = Needle(tools=[get_weather])
response = agent.run("What's the weather in Paris?")
print(response["type"])        # → "respond" (final answer)

print(response["results"])     # → [{'temp_c': 20}]

Extended Reasoning: Higher Values

Increase max_steps when tasks require deeper tool chaining or sequential dependencies.

@tool
def get_location():
    """Return a location that the next tool can use."""
    return {"city": "Paris"}

@tool
def get_weather(city: str):
    """Get the weather for a city."""
    return {"temp_c": 20}

agent = Needle(tools=[get_location, get_weather])

# Allow up to 15 reasoning steps for multi-hop queries

response = agent.run(
    "Tell me the current temperature where I am.",
    max_steps=15,
    max_new_tokens=256,
)
print(response["results"])

# → [{'city': 'Paris'}, {'temp_c': 20}]

Limited or Disabled Looping

Use smaller values to constrain reasoning depth. Set max_steps=0 to disable the loop entirely—only the initial completion is returned, with no tool execution.

Manual Loop Control with complete()

For fine-grained control over multi-step reasoning, implement the loop manually using complete(). This replicates run()'s behavior while letting you decide termination conditions.

import json
from needle import Needle, tool

@tool
def get_weather(city: str):
    """Get the weather for a city."""
    return {"temp_c": 20}

agent = Needle(tools=[get_weather])

# First turn: model emits tool call

turn = agent.complete("Weather in Tokyo?")
if turn["type"] == "call":
    # Execute the suggested call manually

    args = turn["function_calls"][0]["arguments"]
    result = get_weather(**args)

    # Feed result back and continue (you control when to stop)

    turn = agent.complete(json.dumps(result))
    print(turn)   # Final response after custom iteration

This approach is useful when you need custom retry logic, conditional branching, or integration with external state management.

Key Source Files

File Purpose
needle/__init__.py Implements Needle.run() with max_steps parameter
doc/apis.md Documents agent.run(query, max_steps=…, max_new_tokens=…)
tests/test_inference.py Validates default run() behavior

Summary

  • max_steps controls iteration count in Needle.run()—default 8, configurable per query
  • Increase for complex multi-hop reasoning; decrease to limit depth; zero to disable looping
  • Loop terminates early on non-"call" responses or empty function calls
  • Use complete() for manual loop implementation with custom control logic
  • Results from all steps collect in response["results"]

Frequently Asked Questions

What happens if max_steps is reached before the model finishes reasoning?

The loop stops immediately when max_steps iterations complete. The final response contains whatever results accumulated, with response["type"] reflecting the last model output—potentially another "call" if the model still wanted to continue. Check response["results"] to see executed tool outputs.

Can I change max_steps dynamically between queries on the same agent?

Yes. The max_steps parameter is passed per-call to run(), not set at agent initialization. Each query can specify a different limit based on expected complexity.

agent = Needle(tools=[my_tools])
simple_response = agent.run("Simple question", max_steps=3)
complex_response = agent.run("Complex research task", max_steps=20)

What's the difference between max_steps and max_new_tokens?

max_steps limits reasoning iterations (how many tool-use cycles), while max_new_tokens limits token generation per LLM call (how long each response can be). They operate independently—deep reasoning with max_steps=50 remains efficient if each step uses few tokens.

Does max_steps=0 prevent all tool execution?

Correct. With max_steps=0, run() skips the agentic loop entirely. The model receives the query and returns its direct response without checking for or executing any tool calls. Use this for pure text completion without tool augmentation.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →