How to Set Up a Continuous Agent Loop with Needle 2's `run()` Method
Needle 2's run() method implements a lightweight agent loop that automatically calls user-defined tools up to max_steps times, returning a final response only when the model produces no more function calls or reaches a terminal state.
The continuous agent loop in Needle 2 allows large language models to interact with external tools iteratively until a task completes. According to the cactus-compute/needle source code, this behavior is encapsulated entirely within the Needle.run() method found in needle/__init__.py (lines 39-60). By configuring tool definitions and loop parameters, you can create anything from single-query agents to perpetual autonomous systems.
Understanding the Agent Loop Architecture
The core agent logic in Needle 2 follows a deterministic execution pattern orchestrated by the run() method. When you invoke run(query, max_steps, max_new_tokens), the implementation performs the following sequence:
-
Initial LLM call — The method invokes
_completeto generate a response, which may contain afunction_callsarray indicating tool requests. -
Iterative execution — For each step up to
max_steps(default 8), the agent looks up requested tools in the internalself._functionsregistry, executes them, and encodes results as JSON for the next prompt. -
Termination conditions — The loop stops early when the model returns a non-
calltype or when no tool calls are present in the response. -
Result aggregation — The method returns a dictionary containing the final LLM response text and a
resultsfield aggregating all tool outputs.
This architecture separates the orchestration logic in needle/__init__.py from the underlying generation engine in needle/model/run.py and tool schema handling in needle/agent/tools.py.
Step-by-Step Implementation
Define Tools with the @tool Decorator
Register functions that the agent can invoke using the @tool decorator. Each tool becomes available in the self._functions registry during loop execution.
from needle import tool, Field
@tool
def search_web(query: str = Field(description="Search query")) -> str:
"""Return a short fake web-search result for demonstration."""
return f"Found result for '{query}'"
Instantiate the Needle Agent
Pass your tool functions to the Needle constructor to populate the agent's internal registry. The system prompt defines the agent's behavior throughout the loop.
from needle import Needle
agent = Needle(
tools=[search_web],
system="You are a helpful assistant with access to web search."
)
Execute Single-Step Runs with Automatic Iteration
Invoke run() to trigger the automatic multi-step loop. The method handles all iterations internally, up to the max_steps limit.
response = agent.run(
query="What's the weather in Paris today?",
max_steps=5, # Maximum tool-calling iterations per query
max_new_tokens=256 # LLM token budget per generation round
)
print(response["results"]) # List of all tool execution results
print(response["text"]) # Final aggregated answer from the LLM
Build a Continuous Interactive Loop
For conversational applications, wrap run() in a while loop that processes sequential user inputs. Each call to run() maintains the agent's state through the conversation history managed by the Needle class.
while True:
user_input = input("User: ")
if user_input.lower() in {"exit", "quit"}:
break
resp = agent.run(user_input) # Uses default max_steps=8
print("Assistant:", resp.get("text"))
Create Persistent Autonomous Loops
For self-driving agents that continue processing without human input, feed previous results back as the next query. This creates a perpetual reasoning chain until you implement a custom termination condition.
state = ""
while True:
# Combine prior results with continuation prompt
query = f"{state}\nContinue processing."
resp = agent.run(query, max_steps=8)
state = resp.get("text", "")
print(state)
# Custom break condition
if "DONE" in state:
break
Key Configuration Parameters
The run() method accepts several parameters that control the continuous loop behavior:
-
max_steps(int, default 8) — Hard limit on the number of tool-calling iterations. Prevents infinite loops when the model repeatedly requests tools. -
max_new_tokens(int) — Token generation budget for each individual LLM call within the loop. The native engine inneedle/model/run.pyenforces this limit during the_completeinvocation. -
query(str) — The initial user prompt that seeds the agent loop.
Because the loop is entirely contained within run(), you control continuity by managing how often you invoke the method and how you structure the input queries.
Summary
- Needle 2's agent loop is implemented in
needle/__init__.py(lines 39-60) within therun()method, which automatically iterates up tomax_stepstimes. - Tool registration occurs via the
@tooldecorator or manual function passing, populating theself._functionsregistry used during execution. - Continuous operation requires wrapping
run()in external loops—either conversationalwhile Trueblocks for interactive use or persistent feedback loops for autonomous agents. - Termination happens automatically when no function calls remain or when
max_stepsis reached, returning aggregated results and final text. - Underlying generation is handled by the native engine in
needle/model/run.py, while tool schemas are managed inneedle/agent/tools.py.
Frequently Asked Questions
What happens if the agent exceeds max_steps?
When the iteration count reaches the max_steps parameter (default 8), the run() method terminates the loop immediately and returns the current state, even if additional tool calls remain pending. The returned dictionary includes all results collected up to that point, allowing you to inspect partial progress or re-invoke run() with the remaining work.
How does Needle 2 handle tool execution errors?
According to the implementation in needle/__init__.py, the agent catches exceptions during tool execution and encodes error objects into the JSON results sent back to the model. This allows the LLM to receive feedback about failed operations and potentially request corrective actions in subsequent iterations within the same run() call.
Can I use custom Pydantic models with Needle tools?
Yes, the @tool decorator in needle/agent/tools.py supports Pydantic models for complex argument schemas. You can define fields using Field objects with descriptions, and the schema builder automatically converts these to JSON Schema format for the LLM's function-calling interface.
Where does the actual LLM generation occur?
The actual token generation and buffer management occur in needle/model/run.py, which provides the generate and batch_generate functions called by the Needle._complete method. This separation allows the run() loop to focus on orchestration while the model file handles low-level inference.
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 →