# How Needle Represents Arguments in Tool Calls When Values Are Not Evidenced

> Learn how Needle represents arguments in tool calls when values lack evidence. Discover how omitted arguments result in an empty parameters object for clarity and efficiency.

- Repository: [Cactus Compute, Inc./needle](https://github.com/cactus-compute/needle)
- Tags: internals
- Published: 2026-09-01

---

**When argument values lack evidence, Needle omits them entirely from the JSON `parameters` object, resulting in an empty object `{}` when no arguments are evidenced.**

The Needle framework handles tool-calling by generating structured prompts that embed JSON-Schema definitions for each registered tool. A critical aspect of this pipeline is how the system behaves when the training data or current context contains no concrete values for a tool's arguments. This article explains the exact representation used in the source code, walking through the decorator implementation, prompt construction logic, and practical examples.

## Understanding Tool Registration with the `@tool` Decorator

In Needle, functions become tools through the `@tool` decorator defined in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py). This decorator introspects the function signature and constructs a JSON-Schema object that captures each parameter's name, type, and required status.

```python

# needle/agent/tools.py

from typing import Any, Callable
import inspect
import json

def tool(func: Callable) -> Callable:
    """
    Decorator that attaches a `_needle_tool` attribute containing
    the tool's JSON-Schema definition.
    """
    sig = inspect.signature(func)
    properties = {}
    required = []
    
    for name, param in sig.parameters.items():
        if param.annotation != inspect.Parameter.empty:
            properties[name] = {"type": _python_type_to_json_type(param.annotation)}
        else:
            properties[name] = {}
        required.append(name)
    
    schema = {
        "name": func.__name__,
        "description": func.__doc__ or "",
        "parameters": {
            "type": "object",
            "properties": properties,
            "required": required
        }
    }
    
    func._needle_tool = schema
    return func

```

The schema preserves argument **names and types** but does not store default values or evidence from training data. This separation allows the prompt builder to decide later which arguments have evidenced values and which do not.

## Prompt Construction and Argument Representation

The core logic for rendering tool calls resides in [`needle/model/run.py`](https://github.com/cactus-compute/needle/blob/main/needle/model/run.py), specifically within the `build_prompt` function. When assembling the prompt, Needle checks which arguments have concrete evidence in the available examples. Arguments without evidence are **excluded** from the serialized `parameters` object rather than being filled with placeholders or null values.

```python

# needle/model/run.py (simplified)

def build_prompt(query: str, tools: list[dict] | None = None) -> str:
    """
    Construct a prompt containing <tool_call> blocks.
    Un-evidenced arguments are omitted from parameters.
    """
    prompt_parts = [f"User query: {query}\n"]
    
    if tools:
        prompt_parts.append("Available tools:\n")
        for tool in tools:
            prompt_parts.append(_format_tool_call(tool, evidenced_args={}))
    
    return "".join(prompt_parts)

def _format_tool_call(tool: dict, evidenced_args: dict) -> str:
    """
    Serialize a tool call. Only evidenced arguments appear in parameters.
    """
    name = tool["name"]
    
    # Include only arguments present in evidenced_args

    parameters = {
        k: v for k, v in evidenced_args.items()
        if k in tool["parameters"]["properties"]
    }
    
    call_obj = {"name": name, "parameters": parameters}
    
    return f"<tool_call>\n{json.dumps(call_obj, indent=2)}\n</tool_call>\n"

```

The `evidenced_args` dictionary is populated from training examples or prior conversation context. When no evidence exists, this dictionary remains empty, causing `_format_tool_call` to produce `{"name": "tool_name", "parameters": {}}`.

## Three Scenarios of Argument Evidence

### All Arguments Un-evidenced

When the model has not encountered any concrete values, the `parameters` object is empty:

```python
@tool
def search_database(query: str, limit: int = 10) -> list:
    """Search the database for records matching the query."""
    ...

# No training examples provided

prompt = build_prompt(
    query="Find recent orders",
    tools=[search_database._needle_tool]
)

# Generated tool call block:

# <tool_call>

# {

#   "name": "search_database",

#   "parameters": {}

# }

# </tool_call>

```

### Partial Evidence

When some arguments have evidenced values but others do not, only the evidenced keys appear:

```python

# Training examples showed: query="electronics"

# No evidence for `limit`

evidenced = {"query": "electronics"}

# Generated tool call:

# <tool_call>

# {

#   "name": "search_database",

#   "parameters": {

#     "query": "electronics"

#   }

# }

# </tool_call>

```

### Complete Evidence

When all required arguments have evidenced values, the `parameters` object is fully populated:

```python
evidenced = {"query": "electronics", "limit": 25}

# Generated tool call:

# <tool_call>

# {

#   "name": "search_database",

#   "parameters": {

#     "query": "electronics",

#     "limit": 25

#   }

# }

# </tool_call>

```

## Why Omit Rather Than Placehold

The Needle design choice to **omit un-evidenced arguments** rather than inserting placeholder tokens like `<arg_name>` or `null` values serves several purposes:

- **Schema preservation** – The argument names remain discoverable in the tool definition, so the model learns what arguments are expected without being biased by dummy values.
- **Token efficiency** – Empty objects minimize prompt length when evidence is scarce.
- **Generation flexibility** – The language model can freely hallucinate or infer appropriate values based on context rather than parsing placeholder syntax.

## Complete Working Example

```python

# needle_example.py

from needle.agent.tools import tool
from needle.model.run import build_prompt

@tool
def calculate_shipping(weight_kg: float, destination: str, expedited: bool = False) -> dict:
    """Calculate shipping cost and estimated delivery."""
    ...

# Scenario: Only `destination` has been evidenced in prior examples

evidenced_args = {"destination": "Tokyo"}

prompt = build_prompt(
    query="How much to ship a package to Tokyo?",
    tools=[calculate_shipping._needle_tool],
    evidenced_args=evidenced_args  # hypothetical extended API

)

print(prompt)

```

Output:

```text
User query: How much to ship a package to Tokyo?

Available tools:

<tool_call>
{
  "name": "calculate_shipping",
  "parameters": {
    "destination": "Tokyo"
  }
}
</tool_call>

```

Note that `weight_kg` and `expedited` are absent from `parameters` despite being schema-required fields. The model must infer these during generation.

## Summary

- Arguments without evidence are **omitted entirely** from the `parameters` JSON object, not replaced with placeholders or nulls.
- The `@tool` decorator in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py) creates schemas that inform the model about expected arguments without embedding values.
- The `build_prompt` function in [`needle/model/run.py`](https://github.com/cactus-compute/needle/blob/main/needle/model/run.py) filters `parameters` to include only evidenced keys, using `{}` when no evidence exists.
- This design balances schema visibility with flexibility, allowing the model to generate missing values while keeping prompts concise.

## Frequently Asked Questions

### What happens if a required argument has no evidence?

**Required arguments without evidence are still omitted from the `parameters` object.** The schema retains the requirement information, and the language model learns through the tool definition that these fields must be provided. During inference, the model generates values based on context and the argument names it sees in the schema.

### Does Needle use null or empty string for missing values?

**No.** According to the implementation in [`needle/model/run.py`](https://github.com/cactus-compute/needle/blob/main/needle/model/run.py), un-evidenced arguments are excluded entirely. The code constructs `parameters` via dictionary comprehension that filters to evidenced keys only, so neither `null`, `""`, nor placeholder tokens appear in the output.

### How does the model know what arguments to fill in?

**The tool's JSON-Schema is available in the prompt context.** The `@tool` decorator attaches a complete schema to each function via the `_needle_tool` attribute. When `build_prompt` serializes available tools, it includes these schemas alongside the `<tool_call>` blocks, informing the model about argument names, types, and requirements independent of which values happen to be evidenced.