How to Use the Needle 2 Agent for Tool Calling: Complete Developer Guide
The Needle 2 agent enables language models to invoke Python functions by decorating them with @tool, automatically generating JSON schemas from type hints, and handling the full execution loop from LLM request to function call and response.
The Needle 2 agent, part of the cactus-compute/needle repository, provides a lightweight framework that transforms regular Python functions into LLM-invokable tools. By leveraging Python type hints and docstrings, the agent automatically constructs JSON Schema descriptions that allow language models to understand when and how to call your code. This approach eliminates manual schema writing while maintaining strict type validation between the model and your functions.
Core Architecture of the Needle 2 Agent
The @tool Decorator and Schema Generation
At the heart of the system lies the @tool decorator defined in needle/agent/tools.py. When applied to any Python callable, this decorator introspects the function's signature, extracts type hints and docstrings, and builds a complete JSON-Schema description. The decorator stores this metadata on the function object itself via the fn._needle_tool attribute, making the schema available to the agent at runtime.
The underlying build_schema function performs the heavy lifting of type translation. It maps Python annotations to their JSON Schema equivalents, ensuring that the language model receives accurate parameter specifications in the function-calling payload.
Supported Type Annotations
The schema generator in needle/agent/tools.py handles complex type hierarchies without requiring manual intervention:
- Simple types (
str,int,float,bool,list,dict) map directly to JSON Schema primitive types - enum.Enum classes convert to string schemas with constrained
enumvalues - Pydantic models generate full object schemas via
model_json_schema() - Optional types (
typing.Optional) mark parameters as non-required in the schema - Field constraints from Pydantic's
Field()class propagate validation rules such as min/max values, regex patterns, and descriptions
Step-by-Step Tool Calling Workflow
The Needle 2 agent implements a complete execution loop that bridges LLM reasoning and Python execution:
- Tool Declaration: Developers decorate Python functions with
@tool, which analyzes signatures and attaches JSON schemas - Tool Registration: Pass decorated callables to the
NeedleAgentconstructor or the CLI via--toolarguments - LLM Decision: The agent bundles tool schemas into the request payload, allowing the model to decide when invocation is appropriate
- Execution and Validation: When the model returns a function call payload, the agent looks up the matching Python function, validates arguments against the schema, and executes the call
- Response Integration: The function's return value feeds back into the conversation context as the tool's output, enabling the LLM to generate a final response
Creating Custom Tools with Python Type Hints
Define tools using standard Python syntax with type hints and docstrings. The decorator handles all schema generation automatically, including validation constraints via Pydantic's Field class:
from needle.agent.tools import tool, Field
@tool
def summarize(text: str, max_length: int = Field(default=150, description="Maximum characters")) -> str:
"""
Summarize a piece of text.
"""
# Simple placeholder implementation
return text[:max_length] + ("…" if len(text) > max_length else "")
This decorator captures the text parameter as a required string and max_length as an optional integer with validation metadata, all without writing JSON Schema manually.
Running the Agent: CLI vs. Programmatic API
Command-Line Interface
The needle/cli.py module provides a command-line interface that loads tools and starts interactive chat sessions. Register your decorated functions using the --tool flag:
# Register the tool and start an interactive session
needle chat --tool summarize
During the session, when you input a request like "Summarize this article: [text]", the model emits a function call payload such as:
{
"name": "summarize",
"arguments": {"text": "...", "max_length": 150}
}
The agent validates the arguments against the schema, executes summarize(...), and returns the result to the model for final response generation.
Programmatic Usage
For embedded applications, import the NeedleAgent class from needle/agent/__init__.py and pass a list of tool functions to the constructor:
from needle.agent import NeedleAgent
from needle.agent.tools import tool
@tool
def multiply(a: int, b: int) -> int:
"""Return the product of two integers."""
return a * b
agent = NeedleAgent(tools=[multiply])
response = agent.run("What is 7 times 8?")
print(response) # → "The result is 56."
This programmatic approach gives you full control over the conversation loop while the agent handles tool dispatching and schema validation internally.
Using Built-in Tools for External Data
The needle/agent/fetch.py module demonstrates real-world tool integration by providing a pre-built fetch utility. This tool performs HTTP GET requests and returns response bodies, showing how to wrap side-effect operations safely:
from needle.agent.fetch import fetch # already decorated with @tool
# Start the agent with both custom and built-in tools
needle chat --tool summarize --tool fetch
With both tools registered, you can request operations like "Retrieve and summarize the latest headlines from https://example.com/news." The LLM will first call fetch() to retrieve the content, then invoke summarize() to process the results, executing the full pipeline automatically.
Summary
- Automatic schema generation: The
@tooldecorator inneedle/agent/tools.pycreates JSON schemas from Python type hints, storing metadata infn._needle_tool - Comprehensive type support: Handles primitives, Enums, Pydantic models, Optional types, and Field validation constraints without manual configuration
- Dual interfaces: Use the CLI (
needle chat --tool) for quick testing orNeedleAgentprogrammatically for production integrations - Built-in examples: The
fetchtool inneedle/agent/fetch.pydemonstrates wrapping external APIs as safe, declarative functions - Validation loop: The agent validates LLM-provided arguments against schemas before execution, ensuring type safety between model outputs and Python functions
Frequently Asked Questions
How does Needle 2 generate JSON schemas from Python functions?
According to the cactus-compute/needle source code, the build_schema function in needle/agent/tools.py introspects function signatures using Python's inspect module and typing annotations. It maps simple types to JSON Schema primitives, handles enum.Enum as constrained strings, converts Pydantic models via model_json_schema(), and processes typing.Optional to mark parameters as non-required. The resulting schema attaches to the function object as fn._needle_tool when you apply the @tool decorator.
What parameter types does the Needle 2 agent support?
The Needle 2 agent supports str, int, float, bool, list, and dict as primitive types. It also handles enum.Enum for categorical values, Pydantic BaseModel subclasses for complex objects, and typing.Optional for nullable parameters. Additionally, you can use Pydantic's Field() class to define constraints like minimum/maximum values, regex patterns, and default values, all of which translate into corresponding JSON Schema validation rules.
How do I register multiple tools with the NeedleAgent class?
Pass a list of decorated functions to the tools parameter when instantiating NeedleAgent from needle/agent/__init__.py. For example: agent = NeedleAgent(tools=[multiply, summarize, fetch]). The agent bundles all provided schemas into the LLM request payload, allowing the model to choose between available functions based on the conversation context. Each tool remains independently validated according to its specific schema during execution.
Can I use the Needle 2 agent with existing Python libraries?
Yes. Because the @tool decorator works on any Python callable, you can wrap functions from existing libraries by importing them and applying the decorator. The schema builder handles standard type hints, so functions using datetime, pathlib.Path, or custom objects work provided you define Pydantic models for complex return types or use type annotations the builder recognizes. The tests/test_tools.py file in the repository contains validation examples confirming correct schema generation for various real-world signatures.
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 →