How to Handle Multiple Needle Agents in the Same Process: Complete Guide
You can safely create unlimited independent Needle Agent instances within a single Python process, where each agent maintains its own configuration, tools, and state while sharing a thread-safe HTTP client.
Handling multiple Needle agents in the same process is straightforward because the Agent abstraction is intentionally lightweight. According to the cactus-compute/needle source code, every Agent encapsulates its own runtime parameters—model name, temperature, max tokens, and tool registry—without requiring heavy per-instance resources. This design makes it practical to run specialized agents side-by-side, whether you need different personalities, distinct tool sets, or concurrent execution for performance.
How Multiple Agents Work Internally
The Needle architecture separates agent-level configuration from shared infrastructure. Understanding this helps you use multiple agents effectively without unexpected coupling.
Agent Lifecycle and Model Runners
In needle/agent/__init__.py, each Agent instance holds a reference to a model runner (needle.model.run.Runner). The runner is created lazily on first use, and multiple agents can reference the same underlying model class while retaining independent runtime parameters. This means temperature, max_tokens, and similar settings are isolated per agent without duplicating model code.
Per-Instance Tool Registration
Tools are bound to specific agents, not shared globally. The @agent.tool decorator in needle/agent/tools.py generates JSON schemas and stores them in the calling agent's private _tool_registry. This ensures that when agent_math has an add function and agent_chat has an echo function, neither agent can accidentally invoke the other's tools.
Shared, Thread-Safe HTTP Client
The httpx.AsyncClient used for LLM API calls is instantiated once per process. This client is safe for concurrent async calls from multiple threads or asyncio tasks, so you never need manual locking when agents run in parallel.
Synchronous Usage: Independent Agents Side-by-Side
The simplest pattern creates agents inline and invokes them sequentially. Each agent maintains completely separate tool sets and conversational state.
from needle.agent import Agent
# Create two independent agents with different configurations
agent_a = Agent(name="alice", model="gpt-4o-mini", temperature=0.7)
agent_b = Agent(name="bob", model="gpt-4o-mini", temperature=0.2)
# Register tools to specific agents only
@agent_a.tool
def greet(name: str) -> str:
"""Return a friendly greeting."""
return f"Hello, {name}!"
@agent_b.tool
def calculate(a: int, b: int) -> int:
"""Add two numbers."""
return a + b
# Invoke independently—agent_a cannot see `calculate`, agent_b cannot see `greet`
resp_a = agent_a.run("Introduce yourself and greet the user named 'Sam'.")
resp_b = agent_b.run("What is 3 + 4?")
print(resp_a)
print(resp_b) # Returns 7 via the `calculate` tool
The temperature difference means agent_a produces more creative output while agent_b stays more deterministic, even with the same underlying model.
Asynchronous Usage: Concurrent Agent Execution
For I/O-bound workloads, run multiple Needle agents concurrently using asyncio. The shared HTTP client handles parallel requests efficiently without blocking.
import asyncio
from needle.agent import Agent
async def main():
agent_x = Agent(name="x", model="gpt-4")
agent_y = Agent(name="y", model="gpt-4")
# Launch both agents concurrently
task1 = asyncio.create_task(agent_x.run_async("Explain recursion."))
task2 = asyncio.create_task(agent_y.run_async("Summarize the plot of *Inception*."))
result_x, result_y = await asyncio.gather(task1, task2)
print("X:", result_x)
print("Y:", result_y)
asyncio.run(main())
Use .run_async() for asyncio contexts and .run() for synchronous code. Both methods are safe to call from the same process because the underlying httpx.AsyncClient manages connection pooling automatically.
Context Managers for Clean Resource Management
When agents are short-lived, use them as context managers to ensure proper cleanup of async resources.
from needle.agent import Agent
with Agent(name="temp1") as a1, Agent(name="temp2") as a2:
r1 = a1.run("What time is it in Tokyo?")
r2 = a2.run("Translate 'good night' to French.")
print(r1, r2)
# Exiting the `with` block triggers automatic teardown
This pattern is ideal for request-scoped agents in web applications or batch job workers.
Sharing Models While Isolating Tools
A common pattern reuses the same LLM across specialized agents with distinct capabilities. This reduces memory overhead while maintaining clear separation of concerns.
from needle.agent import Agent
shared_model = "gpt-4o-mini"
agent_math = Agent(name="math", model=shared_model)
agent_chat = Agent(name="chat", model=shared_model)
@agent_math.tool
def add(a: int, b: int) -> int:
"""Add two integers."""
return a + b
@agent_chat.tool
def echo(message: str) -> str:
"""Echo the input unchanged."""
return message
# Each agent only sees its own registered tools
print(agent_math.run("Add 5 and 7.")) # Calls `add`, returns 12
print(agent_chat.run("Repeat: hello!")) # Calls `echo`, returns "hello!"
Even with identical model arguments, the agents operate independently. The tool registries are strictly per-instance as implemented in needle/agent/tools.py.
Configuration Checklist for Multi-Agent Programs
| Configuration | Where to Set | Why It Matters |
|---|---|---|
| Model name | Agent(model="...") constructor |
Different agents can use different LLM versions or providers. |
| Tool set | @agent.tool decorator after agent creation |
Ensures tools attach to exactly one agent. |
| API style | .run_async() or .run() |
Match your program's concurrency model; both are safe. |
| Mutable state | Avoid sharing globals across agents | Each agent's message history and caches are isolated by design. |
| Authentication | Environment variables (OPENAI_API_KEY, etc.) |
Set once globally; the shared HTTP client reads them automatically. |
Source Files Reference
These files define the multi-agent behavior described above:
needle/agent/__init__.py—Agentclass definition, model runner integration, and async helper methods.needle/agent/tools.py—@tooldecorator implementation and per-agent_tool_registrymanagement.needle/cli.py— Minimal single-agent setup useful as a reference implementation.
Summary
- Create freely:
Agentinstances are lightweight—no significant per-agent overhead. - Isolate deliberately: Tools, temperature, and message history are per-agent by design.
- Concurrency safe: The shared
httpx.AsyncClientsupports async and threaded usage without locks. - Model sharing: Multiple agents can use the same
modelstring with different configurations. - Clean teardown: Use context managers for temporary agents in server or worker contexts.
Frequently Asked Questions
How many agents can I create in one process?
There is no hard limit. Each Agent stores only configuration, a tool registry, and message history—typically kilobytes of memory. The shared HTTP client handles connection pooling, so you can scale to hundreds or thousands of agents limited only by your application's memory and the API provider's rate limits.
Can two agents share the same tools?
Not directly. Tools are registered per-agent in _tool_registry. To share tool logic, define the function once and register it to multiple agents with separate @agent.tool decorators. Each agent receives its own JSON schema copy, so modifications to one agent's tools never affect another.
What happens if I call the same agent from multiple threads?
The Agent class itself is not thread-safe for concurrent modification. Multiple threads can safely call the same agent's methods only if you serialize access (e.g., with locks). For actual parallelism, create separate Agent instances per thread or use asyncio.gather() with distinct agents, which the HTTP client handles correctly.
Do I need separate API keys for each agent?
No. The shared httpx.AsyncClient reads environment variables like OPENAI_API_KEY once at process startup. All agents automatically use these credentials. If you need per-agent authentication (uncommon), you would need to modify the runner initialization in needle/model/run.py.
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 →