How to Configure Needle for Multi‑Turn Agent Loops: A Complete Guide
Configure Needle's multi‑turn agent loops by setting max_steps and max_new_tokens in the run() method, supplying tools via the tools parameter, and optionally customizing the system prompt for persistent behavior across iterations.
Needle is a lightweight, open‑source inference engine from cactus‑compute/needle that supports agentic workflows—conversations where the model repeatedly calls tools, observes results, and decides on subsequent actions. This article explains how to configure and control these multi‑turn loops using the Needle.run() method and its parameters.
Understanding the Multi‑Turn Loop Architecture
The core mechanism lives in needle/__init__.py. When you invoke run(), Needle executes the following sequence:
- Initial completion — generates a response to your query.
- Function call detection — checks
response.get("function_calls")andresponse.get("type") == "call". - Tool execution — resolves each call against
self._functions, executes the matching function, and collects results. - Result feedback — serializes results to JSON and feeds them back to the model.
- Iteration — repeats until no more calls are requested or
max_stepsis reached.
Here is the actual implementation from the source:
def run(self, query, max_steps=8, max_new_tokens=256):
response = self.complete(query, max_new_tokens)
executed = []
for _ in range(max_steps):
calls = response.get("function_calls") or []
if response.get("type") != "call" or not calls:
break
results = []
for call in calls:
fn = self._functions.get(call.get("name"))
# ... validation and execution ...
results.append(fn(**(call.get("arguments") or {})))
executed.extend(results)
response = self.complete(json.dumps(results, default=_jsonable), max_new_tokens)
response["results"] = executed
return response
The loop requires no manual state management—the engine handles re‑binding and context continuation automatically via self._bind() inside complete.
Configuring Loop Behavior: Key Parameters
Control multi‑turn depth and response characteristics through these run() parameters:
| Parameter | Purpose | Default | When to Adjust |
|---|---|---|---|
max_steps |
Hard limit on tool‑calling rounds | 8 |
Increase for complex reasoning chains; decrease to limit API costs |
max_new_tokens |
Token budget per generation | 256 |
Raise for verbose explanations or structured outputs |
system |
Persistent system prompt | "" |
Set personality, constraints, or output formats |
tools |
Callable capabilities | None |
Define what functions the agent can invoke |
These parameters apply per run() invocation, allowing dynamic adjustment without re‑instantiating the agent.
Practical Configuration Examples
Basic Multi‑Turn Setup
Create an agent with a single tool and run a conversation requiring follow‑up reasoning:
from needle import Needle, tool
@tool
def get_weather(city: str):
"""Retrieve weather information for planning decisions."""
return {"temp_c": 22, "condition": "sunny"}
agent = Needle(tools=[get_weather])
result = agent.run(
"What's the weather in Lisbon? Should I pack a jacket?",
max_steps=3 # sufficient for query → tool call → final answer
)
print(result["type"]) # "text" when loop terminates
print(result["results"]) # [{"temp_c": 22, "condition": "sunny"}]
This pattern—demonstrated in tests/test_inference.py lines 33–45—shows the minimal configuration for functional agent loops.
Extended Reasoning with Custom Limits
For tasks requiring multiple sequential tool calls, increase both step and token budgets:
@tool
def search_flights(origin: str, destination: str, date: str):
"""Find available flights."""
return [{"flight": "AB123", "price": 299}]
@tool
def book_flight(flight_id: str):
"""Confirm a reservation."""
return {"status": "confirmed", "booking_ref": "XYZ789"}
travel_agent = Needle(
tools=[search_flights, book_flight],
system="You are a meticulous travel assistant. Always confirm prices before booking."
)
itinerary = travel_agent.run(
"Find me a flight from NYC to London on March 15, then book the cheapest option.",
max_steps=10, # allow search → comparison → booking sequence
max_new_tokens=512 # longer responses for fare explanations
)
The system prompt persists across all turns, ensuring consistent behavior without repetition.
Controlling Loop Termination
The loop exits automatically when either condition becomes true:
response.get("type") != "call"— the model returns a final text response instead of tool requests.max_stepsexhausted — safety limit reached, returning accumulated results.
This design prevents infinite loops while preserving partial progress. Check result["type"] to determine exit reason:
result = agent.run("Complex multi-step query", max_steps=5)
if result["type"] == "call":
print("Warning: step limit reached before completion")
# result["results"] contains intermediate tool outputs
Tool Definition and Registration
Tools must be registered for the loop to resolve function calls. Needle supports two patterns, both processed by needle/agent/tools.py:
Decorator pattern (recommended):
from needle import tool
@tool
def calculate_distance(lat1: float, lon1: float, lat2: float, lon2: float):
"""Calculate great-circle distance between coordinates."""
# implementation...
return {"km": distance}
Pydantic model pattern — for complex validation, though the decorator handles most use cases.
Tools are bound to self._functions during Needle initialization and remain available for unlimited invocations across all turns.
Performance and Resource Considerations
- Each turn incurs a full inference call — factor
max_stepsinto latency and cost estimates. - Token accumulation — conversation history grows with each iteration; monitor context window limits in underlying engines.
max_stepsas circuit breaker — set conservatively for production deployments to prevent runaway loops.
The needle/agent/fetch.py module handles engine library loading; customize paths only if running non‑standard deployments.
Summary
Needle.run()implements the multi‑turn loop inneedle/__init__.pywith automatic tool resolution and result feedback.- Control depth via
max_steps(default 8) and response length viamax_new_tokens(default 256). - Supply capabilities through
toolsand persistent behavior throughsystem. - The loop terminates naturally when the model stops calling tools or when safety limits trigger.
- No additional configuration is required—Needle handles state management and engine binding internally.
Frequently Asked Questions
What happens if the model keeps calling tools indefinitely?
Needle enforces the max_steps parameter as a hard ceiling. Once reached, the loop exits and returns accumulated results in response["results"], with response["type"] still "call" if more calls were pending. Increase max_steps for deeper reasoning, or add logic in tools to signal completion.
Can I modify the system prompt between turns?
The system parameter is set at Needle initialization and persists across all turns in a single run() call. For different behavior in subsequent calls, create a new agent instance or implement turn‑specific instructions within your tools' return values.
How do I add custom validation or logging to the loop?
The run() method does not expose hooks, but you can subclass Needle and override run() or complete() to inject middleware. The source structure in needle/__init__.py makes this straightforward—the loop logic is self‑contained and tool resolution occurs through self._functions.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →