How to Initialize the Needle Agent with Custom Tools and System Information
Pass a list of callables or Pydantic models to the tools parameter and a string to the system parameter when instantiating the Needle class from needle/__init__.py.
The Needle SDK provides a lightweight Python interface to Cactus Compute's native inference engine. You customize an agent's capabilities and behavior by defining external functions the model can invoke and by conditioning its responses through a system prompt. This article shows how to configure both, with references to the actual source implementation in the cactus-compute/needle repository.
Overview of the Needle Constructor
The Needle class in [needle/__init__.py](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) accepts several parameters that control initialization:
| Parameter | Type | Purpose |
|---|---|---|
tools |
list[Callable | BaseModel | dict] |
Functions, Pydantic models, or raw JSON schemas describing available tools |
system |
str |
System prompt encoding the agent's persona and constraints |
weights |
str | Path | None |
Path to a fine-tuned .cact file for custom model weights |
tool_index_path |
str | None |
Pre-built tool index for large tool collections |
buffer_size |
int |
Response buffer size in bytes (default: 32768) |
The constructor performs three core operations: tool resolution, encoding, and engine binding.
Step 1: Define Custom Tools
Tools are Python functions decorated with @tool or Pydantic models that generate JSON schemas. The @tool decorator is implemented in [needle/agent/tools.py](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py).
Using the @tool Decorator
from needle import tool, Field
@tool
def translate(text: str, target_lang: str = "es") -> str:
"""Translate *text* into *target_lang* (default Spanish)."""
return f"[{target_lang}] {text}"
The @tool decorator calls build_schema to generate a JSON schema from the function signature and attaches it as _needle_tool.
Using Field for Rich Metadata
def summarize(
text: str,
max_sentences: int = Field(default=3, description="Maximum number of sentences")
):
"""Return a short summary of *text*."""
return " … ".join(text.split(".")[:max_sentences])
Field objects enrich parameter metadata with defaults, descriptions, and constraints that appear in the generated schema.
Raw Pydantic Models or Dictionaries
You can also pass:
- Pydantic models — converted via
pydantic_schemaintools.py - Raw dictionaries — used verbatim as JSON schemas
Step 2: Create a System Prompt
The system parameter conditions the model's behavior. It is UTF-8 encoded and passed directly to the native engine:
system_prompt = """
You are a precise technical assistant. When calling tools, explain your reasoning
before invoking the function. Keep responses concise and factual.
"""
In [needle/__init__.py](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py), this becomes:
self._system = (system or "").encode("utf-8")
If omitted, the system prompt defaults to an empty string "".
Step 3: Initialize the Needle Agent
Combine tools and system prompt in the constructor:
from needle import Needle
agent = Needle(
tools=[translate, summarize],
system=system_prompt,
)
Internal Processing Flow
-
_resolveiteratestools— Each entry is classified:- Callables: wrapped by
@tool, schema built viabuild_schema - Pydantic models: schema generated via
pydantic_schema - Dictionaries: stored as-is
Resolved schemas populate
self._functions(callable storage) andself._tools_json(serialized JSON). - Callables: wrapped by
-
Byte encoding —
systemand tools JSON are converted to UTF-8. -
Engine initialization —
_bindloads the native library and callsneedle_initwith:self._system(encoded system prompt)self._tools_json(tools schema)- Optional
tool_index_path
Complete Working Example
# example.py
from needle import Needle, tool, Field
# Define tools
@tool
def translate(text: str, target_lang: str = "es") -> str:
"""Translate *text* into *target_lang*."""
return f"[{target_lang}] {text}"
def summarize(
text: str,
max_sentences: int = Field(default=3, description="Maximum sentences")
):
"""Summarize *text* to *max_sentences* sentences."""
return " … ".join(text.split(".")[:max_sentences])
# System prompt with persona
system_prompt = """You are a helpful assistant that explains tool usage."""
# Initialize agent
agent = Needle(
tools=[translate, summarize],
system=system_prompt,
)
# Execute query that may trigger tools
result = agent.run(
query="Translate and summarize: AI transforms industries."
)
print(result)
The run method drives the completion loop: it calls needle_complete, executes any returned tool calls, and iterates until the engine produces a final response.
Advanced: Fine-Tuned Weights and Tool Indexing
Custom Weights
Pass a .cact file to use fine-tuned parameters instead of the default engine:
agent = Needle(
tools=[my_tool],
weights="./models/custom.cact",
)
This triggers FineTuneWorker from [needle/_worker.py](https://github.com/cactus-compute/needle/blob/main/needle/_worker.py) rather than loading the standard native library.
Pre-Built Tool Index
For large tool collections, provide a pre-computed index:
agent = Needle(
tools=extensive_tool_list,
tool_index_path="./indices/tools.idx",
buffer_size=65536, # Larger buffer for verbose tools
)
Summary
- Tool definitions go to the
toolsparameter as decorated functions, Pydantic models, or raw schemas — processed by_resolveand stored inself._functions. - System prompts shape model behavior via the
systemparameter, UTF-8 encoded and forwarded toneedle_init. - Engine binding happens automatically in
_bind, selecting between standard library loading orFineTuneWorkerfor custom weights. - Tool execution is handled by
agent.run(), which manages the completion loop and callback invocation.
Frequently Asked Questions
Can I pass regular Python functions without the @tool decorator?
Yes, but they must have type annotations. The Needle constructor's _resolve method will wrap un-decorated callables with @tool automatically. However, explicit decoration is recommended for clarity and to leverage Field metadata.
What happens if I omit the system parameter?
The agent initializes with an empty system prompt (""). According to the source in needle/__init__.py, this is encoded as UTF-8 bytes and passed to needle_init without modification. The model will use default behavior without persona-conditioning.
How are tool schemas generated from Pydantic models?
The _resolve method detects BaseModel subclasses and calls pydantic_schema from needle/agent/tools.py. This extracts field types, defaults, and descriptions into JSON Schema format, stored alongside the model class in 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 →