# Needle JSONL Training Data Format: How to Structure Queries, Tools, and Answers

> Learn the Needle JSONL training data format for structuring queries, tools, and answers. Understand how to define tool schemas and tool calls for effective model training.

- Repository: [Cactus Compute, Inc./needle](https://github.com/cactus-compute/needle)
- Tags: how-to-guide
- Published: 2026-08-14

---

**Needle uses a JSON Lines (JSONL) format where each line is a JSON object containing a `query` string, optional `tools` array with JSON Schema definitions, and an `answers` array with tool calls that fulfill the request.**

The Needle framework from cactus-compute expects training examples in a strict JSONL structure. This format enables fine-tuning of small language models to call tools accurately, with explicit schemas that constrain generation. Each training example represents one turn of user interaction and the correct tool invocation sequence.

## Core JSONL Schema for Needle Training Data

Every line in your training file must be a valid JSON object with these fields:

| Field | Required | Description |
|-------|----------|-------------|
| `query` | **Yes** | Natural language request or passage to process |
| `tools` | No | Array of JSON Schema objects describing available tools |
| `answers` | **Yes** | Array of tool call objects with `name` and `arguments` |
| `reasoning` | No | Human-readable explanation of argument derivation |

A minimal valid example:

```json
{
  "query": "dim the kitchen to 10",
  "answers": [
    {"name": "set_lights", "arguments": {"room": "kitchen", "brightness": 10}}
  ]
}

```

A complete example with all optional fields:

```json
{
  "query": "dim the kitchen to 10",
  "tools": [
    {
      "name": "set_lights",
      "parameters": {
        "type": "object",
        "properties": {
          "room": {"type": "string"},
          "brightness": {"type": "integer"}
        },
        "required": ["room", "brightness"]
      },
      "description": "Set the brightness of a room's lights."
    }
  ],
  "answers": [
    {"name": "set_lights", "arguments": {"room": "kitchen", "brightness": 10}}
  ],
  "reasoning": "'kitchen' → room; 'dim to 10' → brightness 10"
}

```

## Understanding Each Field in the Needle JSONL Format

### The `query` Field

The `query` contains the raw user input. Its content varies by tool type:

- **Action tools**: Short commands like `"send email to alice"` or `"what's the weather in Tokyo?"`
- **Extraction tools**: Longer passages containing data to extract, such as product descriptions or log files

In [`needle/model/finetune.py`](https://github.com/cactus-compute/needle/blob/main/needle/model/finetune.py), the `load_jsonl` function silently skips any line missing the `query` key.

### The `tools` Array

The `tools` field holds JSON Schema objects describing each function the model may call. These schemas are **auto-generated** from Python functions decorated with `@needle.tool` in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py).

The `tool` decorator uses `build_schema` to introspect type hints and docstrings, producing schemas like:

```json
{
  "name": "function_name",
  "parameters": {
    "type": "object",
    "properties": {...},
    "required": [...]
  },
  "description": "Function docstring"
}

```

Including `tools` in training examples teaches the model the available function signatures and argument constraints.

### The `answers` Array

The `answers` field contains the **ground-truth tool calls** that solve the query. Each object requires:

- **`name`**: Must exactly match a tool name in the `tools` list
- **`arguments`**: Object with keys matching the schema's `properties`, values of correct types

Multiple tool calls are supported for multi-step reasoning:

```json
{
  "query": "Turn off the lights and lock the front door",
  "answers": [
    {"name": "set_lights", "arguments": {"room": "all", "state": "off"}},
    {"name": "lock_door", "arguments": {"location": "front"}}
  ]
}

```

### The `reasoning` Field

The optional `reasoning` string provides a human-readable trace linking query segments to arguments. The training pipeline ignores this field, but it aids debugging and dataset inspection. Example: `"user mentioned 'tomorrow' → date param is 2024-01-15"`.

## Defining Tools and Auto-Generating Schemas

The `@needle.tool` decorator in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py) eliminates manual schema writing. Define your function with Python type hints:

```python
import needle

@needle.tool
def set_lights(room: str, brightness: int):
    """Set the brightness of a room's lights."""
    return {"room": room, "brightness": brightness}

@needle.tool
def get_weather(city: str, units: str = "metric"):
    """Fetch current weather for a city."""
    return {"city": city, "units": units}

```

The decorator registers each function and builds its schema automatically. The generated `tools` JSON matches OpenAI's function-calling format, ensuring compatibility with standard tool-use datasets.

## Loading JSONL Data for Training

In [`needle/model/finetune.py`](https://github.com/cactus-compute/needle/blob/main/needle/model/finetune.py), the `load_jsonl` function converts your JSONL file into NumPy arrays for JAX training:

```python
from needle.model.finetune import load_jsonl
from needle.model.tokenizer import get_tokenizer

tokenizer = get_tokenizer()

# Returns padded/truncated token IDs and attention masks

sequences, attention_masks = load_jsonl(
    "training_data.jsonl",
    tokenizer,
    max_len=1024
)

print(sequences.shape)      # (num_examples, 1024)

print(attention_masks.shape)  # (num_examples, 1024)

```

The function handles:

- Tokenization of queries, tools, and answers into the model's prompt format
- Padding or truncation to `max_len`
- Generation of attention masks for variable-length sequences

Lines without a `query` key are skipped without error.

## Generating Synthetic Training Data

Needle provides `needle generate-data` to synthesize examples from tool schemas using the OpenRouter API. This augments small hand-written datasets.

**Step 1**: Export your tool schemas to JSON:

```python
from needle.agent.tools import export_schemas

export_schemas([set_lights, get_weather], "my_tools.json")

```

**Step 2**: Generate synthetic examples:

```bash
export OPENROUTER_API_KEY=sk-...
needle generate-data \
    --tools my_tools.json \
    --num-samples 500 \
    --output data.jsonl

```

The `generate_examples` and `generate_dataset` functions in [`needle/model/finetune.py`](https://github.com/cactus-compute/needle/blob/main/needle/model/finetune.py) handle API calls, schema validation, and JSONL formatting.

## Augmenting Existing Datasets

To add generated examples to an existing training file, use `augment_jsonl` from [`needle/model/finetune.py`](https://github.com/cactus-compute/needle/blob/main/needle/model/finetune.py):

```python
from needle.model.finetune import augment_jsonl

augment_jsonl(
    original_path="existing_data.jsonl",
    new_examples=generated_examples,
    output_path="combined_data.jsonl"
)

```

This appends new rows without parsing the entire file into memory, supporting incremental dataset growth.

## Complete Workflow Example

```python

# 1. Define tools with auto-generated schemas

import needle

@needle.tool
def search_products(query: str, max_results: int = 10):
    """Search product catalog by query string."""
    pass

# 2. Export schemas for data generation

from needle.agent.tools import export_schemas
export_schemas([search_products], "schemas.json")

# 3. Generate synthetic training data (CLI)

# needle generate-data --tools schemas.json --num-samples 1000 --output train.jsonl

# 4. Load for fine-tuning

from needle.model.finetune import load_jsonl
from needle.model.tokenizer import get_tokenizer

tokenizer = get_tokenizer()
seqs, masks = load_jsonl("train.jsonl", tokenizer, max_len=512)

# 5. Start training via CLI

# needle finetune --data train.jsonl --model gpt2 --epochs 3

```

## Summary

- Needle training data uses **JSONL format** with one JSON object per line
- Required fields: **`query`** (user input) and **`answers`** (tool calls)
- Optional fields: **`tools`** (JSON Schemas) and **`reasoning`** (debugging notes)
- Tool schemas are **auto-generated** via `@needle.tool` decorator in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py)
- **`load_jsonl`** in [`needle/model/finetune.py`](https://github.com/cactus-compute/needle/blob/main/needle/model/finetune.py) converts JSONL to NumPy arrays for training
- Use **`needle generate-data`** to synthesize examples from schemas via OpenRouter API

## Frequently Asked Questions

### What happens if a JSONL line is missing the query field?

Lines without a `query` key are silently skipped by `load_jsonl` in [`needle/model/finetune.py`](https://github.com/cactus-compute/needle/blob/main/needle/model/finetune.py). No error is raised, and processing continues with valid lines. This allows mixed files with metadata headers or incomplete examples.

### Can I include multiple tool calls in one training example?

Yes. The `answers` array supports multiple objects when a query requires sequential or parallel tool execution. Each object needs a `name` matching a tool in the `tools` array and valid `arguments` per that tool's schema.

### How do I validate my JSONL file before training?

Needle does not include a standalone validator, but you can verify structure by attempting to load with `load_jsonl`. For schema validation, check that tool `name` values in `answers` exist in the `tools` list and that `arguments` match the declared `parameters` structure.

### Is the reasoning field used during model training?

No. The `reasoning` field is ignored by the training pipeline in [`needle/model/finetune.py`](https://github.com/cactus-compute/needle/blob/main/needle/model/finetune.py). It exists for human dataset curation and debugging. The model learns only from the structured relationship between `query`, `tools`, and `answers`.