---
title: "Prompting an LLM for JSON vs Using an Extraction Model"
description: "Prompting an LLM for JSON vs a dedicated extraction model: null handling, confidence, spans, per-request billing."
canonical: "https://system-one.dev/en/blog/clairic-vs-llm-extraction"
language: "en"
---

# Prompting an LLM for JSON vs Using an Extraction Model

**Key points**\n\n1. An LLM asked for JSON generates text that looks like JSON; Clairic returns one typed value per field and does not generate text.
2. When the input doesn't support a value, Clairic returns null (pNull above the threshold) instead of a plausible guess, and text answers can point back to their span in the source.
3. Use Clairic to get the values, Jev or Clef to decide, and an LLM only where you need written text, such as a reply.

Prompting an LLM for JSON works, but you're on the hook for missing values, types and auditability. An extraction model like Clairic builds those into what it returns.

![Prompted LLM JSON output compared with Clairic typed fields](TODO-IMG: Cover, split layout. Left: "Prompt → text that looks like JSON". Right: "Fields → typed values + null". Designed.)

## The common approach: prompt + JSON schema

The usual setup: list the fields and a JSON schema in the prompt, then parse what comes back. It's quick to build, and plenty of teams ship it.

What's left to you: checking that each value has the right type, checking that a value actually appears in the source, and deciding what to do when something is missing.

## Invented values, and how Clairic handles them

A generative model is built to produce a plausible answer. Clairic instead returns `pNull` alongside scalar values. When `pNull` is above the null threshold (0.5 by default), `value` is `null`, though `guess` may still hold something. You can change the threshold for the whole request with `options.nullThreshold`, or per field with `nullThreshold`.

In the complaint-email example from the official blog, the email has no tracking number, so `tracking_number` comes back `null` instead of an invented one.

![Clairic returns null for a tracking number missing from the email](TODO-IMG: Illustrative card: tracking_number: null, with a label "pNull > threshold". Designed.)

## Types and spans make it auditable

Clairic has 9 field types: text, number, date, time, datetime, duration, list, estimate and forecast. A number field can set `decimals`. A text answer can include a `span`, the start and end character positions of the value in `state`, so a reviewer can see it highlighted in the source.

Lists can include `spans` too. In the official complaint-email example, each damage item, "cracked screen" and "dented corner", comes with its location in the source text.

![Damage items highlighted in the source email using Clairic spans](TODO-IMG: Complaint email text with "cracked screen" and "dented corner" highlighted, built from span data. Designed.)

This table compares interfaces and output shape only, not speed, accuracy or price.

| | Prompted LLM | Clairic |
|---|---|---|
| What comes back | Generated text you parse into JSON | One typed value per field |
| Missing values | Depends on your prompt and your checks | `null` when pNull is above the threshold |
| Confidence signal | Design it yourself | Scalar fields can carry confidence and pNull |
| Pointing back to the source | Build it yourself | Text can carry a span, lists can carry spans |
| Types | Validate them yourself | 9 built-in types, with TypeScript types in the SDK |
| Billing | Depends on the service you use | Once per request, by input size, not per field |
| Generates text? | Yes | No |

Here's the complaint-email task as code:

```ts
import {
  createSystemOne, defineFields, textField, numberField, dateField, durationField, listField,
} from '@system-one-ai/core';
import { systemOneAdapter } from '@system-one-ai/adapter-system-one';
import { createFetchTransport } from '@system-one-ai/transport-fetch';

const apiKey = process.env.SYSTEM_ONE_API_KEY;
if (!apiKey) throw new Error('Set SYSTEM_ONE_API_KEY.');

const client = createSystemOne({
  adapter: systemOneAdapter,
  transport: createFetchTransport(),
  apiKey,
  timeoutMs: 30_000,
  maxRetries: 0,
});

const email = `(Fictional sample for illustration.)
Hi Example Shop team,
Order ORD-10482 was delivered on 2026-09-30. I'm writing on 2026-10-11.
The item was $649 plus $52.10 tax. The screen is cracked and the case is dented.
Please help. - Alex Sample`;

const fields = defineFields({
  order_number: textField({ instructions: 'The order number in the email.' }),
  delivery_date: dateField({ instructions: 'The date the order was delivered.' }),
  total_paid: numberField({ decimals: 2, instructions: 'Item price plus tax actually paid.' }),
  days_since_delivery: durationField({ unit: 'days', instructions: 'Days from delivery to the date of the email.' }),
  damage: listField({ items: 'text' }),
  tracking_number: textField({ instructions: 'The shipment tracking number, if the email gives one.' }),
});

const result = await client.extract({ model: 'clairic', state: email, fields });

const { tracking_number, damage } = result.answers;
console.log({
  trackingNumber: tracking_number.value, // null when the email has no tracking number
  trackingPNull: tracking_number.pNull,
  damage: damage.value,
  damageSpans: damage.spans,
});
```

Install with `npm install @system-one-ai/core@0.8.0 @system-one-ai/adapter-system-one@0.7.0 @system-one-ai/transport-fetch@0.8.0`, set `SYSTEM_ONE_API_KEY` in `.env`, and run with `node --env-file=.env`. Every run creates a new billable request.

Illustrative output, using only the values from the official example:

```json
{
  "total_paid": 701.10,
  "days_since_delivery": 11,
  "damage": ["cracked screen", "dented corner"],
  "tracking_number": null
}
```

Over HTTP, the endpoint is `POST https://system-one.dev/v1/systemone`, and camelCase SDK names become snake_case (for example `referenceDate` → `reference_date`).

[Read the Clairic SDK docs](https://docs.system-one.dev/en/docs/sdk/clairic) *(CTA button)*

## Computed values

Some values aren't written in the input. In the official example, the email never states the total paid; Clairic adds 649 and 52.10 in tax to get 701.10. The 11 days from delivery to the email are worked out from two dates too.

There are limits. The official blog says the preview isn't yet reliable at long chains of calculations or multi-row aggregation, and well-formed output isn't necessarily correct. Test on your own samples before production, and set thresholds on confidence and pNull.

You can see null handling in the [invoice extraction demo](https://system-one.dev/en/demo/invoice-extraction) (sign-in required): when the total due says "to be confirmed", the amount comes back null and the invoice number is still extracted.

## Cost model

Clairic is billed once per request based on input size, and the cost isn't multiplied by the number of fields. Rates are on the [Clairic model page](https://system-one.dev/en/models/clairic) and the [pricing page](https://system-one.dev/en/pricing).

New accounts get 100 free credits on sign-up.

[See how Clairic is billed](https://system-one.dev/en/models/clairic) *(CTA button)*

## When an LLM is still the right tool

When you need written text, such as a reply or a summary, use an LLM. Clairic doesn't generate text.

The official three-step claim example: Clairic extracts values from the email, Clef checks them against the policy and decides between refund and repair, and an LLM writes the reply. The decision step sees structured values rather than the raw email, so it's less easily swayed by the email's wording. More in [extract, then decide](https://system-one.dev/en/blog/clairic-vs-jev-clef); if your input is an image, see [image input](https://system-one.dev/en/use-cases/screenshot-to-json).

![Extract with Clairic, decide with Clef, write the reply with an LLM](TODO-IMG: Flow: Clairic extracts values → Clef decides refund/repair → LLM writes reply. Designed.)

## Related reading

- [What is Clairic? Extraction models explained](https://system-one.dev/en/blog/what-is-clairic)
- [Clairic vs Jev and Clef: extract, then decide](https://system-one.dev/en/blog/clairic-vs-jev-clef)
- [Screenshot to JSON for AI agents](https://system-one.dev/en/use-cases/screenshot-to-json)
- [Clairic model page](https://system-one.dev/en/models/clairic)

[See how Clairic is billed](https://system-one.dev/en/models/clairic) *(CTA button, end of page)*

Last verified: 2026-10-22

## Frequently asked questions

### Is Clairic a large language model?

No. It doesn't generate text; it returns typed values for the fields you define.

### Can I use Clairic together with an LLM?

Yes. A common chain is Clairic to extract values, Jev or Clef to decide, and an LLM to write the reply if one is needed.

### Does it cost more if I extract more fields?

No. Clairic is billed once per request based on input size, not multiplied by field count.

### What happens when the text doesn't contain a value?

The value is null when pNull is above the null threshold (0.5 by default); guess may still hold a value.

### Is Clairic always correct?

No. The preview isn't yet reliable at long calculations or multi-row aggregation, and well-formed output isn't necessarily correct. Test on your own samples and set thresholds on confidence and pNull.
