# How to Use Needle for Structured Data Extraction: A Complete Guide

> Learn to use Needle for structured data extraction. Transform unstructured text into typed Python objects or dictionaries with this powerful helper.

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

---

**Needle provides a high-level `extract()` helper that transforms unstructured text into typed Python objects or dictionaries by constraining the model's output generation through a user-defined schema.**

Needle is an open-source inference engine from `cactus-compute/needle` designed for deterministic, schema-guided text generation. Its `extract()` function enables developers to perform **structured data extraction** from raw text by compiling schemas into byte-level grammars that constrain token generation, ensuring type-safe outputs that respect field definitions and enumerations.

## How Needle's Extraction Engine Works

The `extract()` function in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) implements a four-stage pipeline that converts free-form text into structured data:

1. **Single-tool agent creation** – The function instantiates a temporary `Needle` agent using the supplied schema as the sole available tool (`Needle(tools=[schema], …)`). This occurs at lines 66-71 of [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py), where the schema is wrapped in a list to restrict the model's tool-calling scope.

2. **Engine binding** – The agent binds to the native inference engine, loading requested weights (or the base model) and initializing the engine with the JSON representation of the schema. This initialization logic is found at lines 55-63 in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py).

3. **Prompt execution and function calling** – Calling `agent.complete(text, max_new_tokens)` executes the model on the raw input. The model returns a JSON envelope containing `function_calls` that specify which schema to invoke and the inferred argument values (lines 11-19 of [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py)).

4. **Argument extraction and instantiation** – The function extracts the `arguments` dictionary from the first function call. If the schema is a Pydantic model, it instantiates the class directly (`schema(**arguments)`); otherwise, it returns the raw dictionary. This final transformation occurs at lines 74-78 of [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py).

Because the schema is compiled into a byte-level grammar that constrains the model's token generation, the extraction is **deterministic** and respects field types, enumerations, and value ranges defined in the schema.

## Extracting Data with Pydantic Models

The most common approach for **structured data extraction** uses Pydantic models to define strict type constraints. The `extract()` function automatically instantiates the model when provided with a `BaseModel` subclass.

```python
import needle
from pydantic import BaseModel

class Invoice(BaseModel):
    vendor: str
    total: float
    due_date: str

text = "Invoice from Acme Corp, $1,200.00, due 2026-09-01"
invoice = needle.extract(text, Invoice)

print(invoice.vendor)   # → Acme Corp

print(invoice.total)   # → 1200.0

```

This approach loads the schema, executes the model, and returns a fully-typed `Invoice` instance with validated fields. The schema definition in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py) handles the underlying `Field` implementations and schema building required for this conversion.

## Extracting Data with Raw JSON Schemas

For scenarios where Pydantic dependencies are unnecessary, `extract()` accepts plain dictionaries conforming to JSON Schema specifications. When using raw schemas, the function returns a standard Python `dict` rather than a typed object.

```python
import needle

schema = {
    "name": "Contact",
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "email": {"type": "string", "format": "email"}
    },
    "required": ["name", "email"]
}

text = "John Doe can be reached at john@doe.com"
result = needle.extract(text, schema)

print(result)  # → {'name': 'John Doe', 'email': 'john@doe.com'}

```

The `Needle` class processes this schema through the same grammar compilation pipeline defined in [`doc/apis.md`](https://github.com/cactus-compute/needle/blob/main/doc/apis.md), ensuring that the model outputs valid JSON objects matching the specified properties and required fields.

## Using Custom Weights for Domain-Specific Extraction

You can perform **structured data extraction** using fine-tuned models by instantiating a persistent `Needle` agent with custom weights. This approach reuses the tuned parameters across multiple extraction operations while maintaining identical schema-binding behavior.

```python

# Assume you have a tuned .cact file from a LoRA fine-tune

agent = needle.Needle(weights="my_needle.cact")
product = agent.extract(
    "Product: UltraWidget, price $99.99, in stock",
    schema={"name": "ProductInfo", "type": "object",
            "properties": {"name": {"type": "string"},
                           "price": {"type": "number"},
                           "stock": {"type": "boolean"}},
            "required": ["name", "price"]})
print(product)

```

The `weights` parameter loads the specified `.cact` file during engine initialization, allowing the extraction pipeline to leverage domain-specific knowledge while maintaining deterministic output constraints.

## Summary

- **Single-function API**: The `extract()` helper in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) provides a one-line interface for converting text to structured data using either Pydantic models or JSON schemas.
- **Deterministic generation**: Schema compilation into byte-level grammars ensures outputs strictly conform to type definitions, enumerations, and required fields.
- **Flexible input formats**: Supports both `BaseModel` subclasses for typed objects and raw dictionaries for lightweight extraction.
- **Custom model support**: The `Needle` class accepts custom `.cact` weights for domain-specific extraction tasks while using the same underlying `complete()` method.

## Frequently Asked Questions

### What is the difference between using a Pydantic model and a raw dictionary schema?

When you pass a Pydantic `BaseModel` subclass to `extract()`, the function instantiates the model class with the extracted arguments (`schema(**arguments)`), returning a typed object with validation. When using a raw dictionary schema, the function returns a plain Python `dict` containing the extracted values. Both approaches use the same grammar-constrained generation pipeline defined in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py).

### How does Needle ensure deterministic output during extraction?

Needle compiles the provided schema into a **byte-level grammar** that constrains the model's token generation at the inference level. This means the model can only produce tokens that form valid JSON matching the schema structure, field types, and constraints (such as enums or regex patterns), eliminating hallucinated fields or type mismatches.

### Can I use fine-tuned models with the extract() function?

Yes. While the standalone `needle.extract()` helper uses the base model, you can create a persistent `Needle` instance with custom weights via `needle.Needle(weights="path/to/model.cact")`. This instance provides an `extract()` method that follows the same schema-binding logic while utilizing your fine-tuned parameters for domain-specific **structured data extraction**.

### Where is the extraction logic implemented in the Needle codebase?

The core extraction pipeline resides in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py), specifically lines 11-19 (output parsing), 55-63 (engine binding), 66-71 (agent initialization), and 74-78 (argument instantiation). Schema definitions and tool handling are implemented in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py), while detailed API documentation covering grammar compilation is available in [`doc/apis.md`](https://github.com/cactus-compute/needle/blob/main/doc/apis.md).