# Извлечение полей

> Модель, которая не пишет текст, а заполняет поля: номер, сумму, дату, список, оценку — и для каждого говорит, насколько уверена.

Extractions — соседний с [решениями](/docs/guides/overview/decisions/overview) класс моделей. Decisions отвечают на вопросы с заранее известными вариантами, extractions достают из текста значения: номер счёта, сумму, дату, список контактов. Вы описываете поля и их типы, модель возвращает готовые значения. Парсить JSON из текста не нужно, и выдуманного формата не будет: число приходит числом, дата датой.

В RouterAI такие модели доступны через эндпоинт [`/api/v1/extractions`](https://routerai.ru/docs/reference#tag/extractions) (для клиентов, которые строят путь как OpenRouter, работает и `/api/alpha/extractions`). Сейчас это [`cloudflare/clairic`](https://routerai.ru/models/cloudflare/clairic). Работает обычный ключ RouterAI.

| Decisions (`/api/v1/decisions`)                   | Extractions (`/api/v1/extractions`)                        |
| ------------------------------------------------- | ---------------------------------------------------------- |
| `questions`: да/нет, выбор варианта, оценка       | `fields`: текст, число, дата, список, оценка, прогноз      |
| Ответ — вероятности по вашим вариантам            | Ответ — значение, `confidence` и `p_null`                  |
| Jev, `liquid/d1`, `cloudflare/clef*` и другие     | `cloudflare/clairic`                                       |

## Первый запрос

```python
import requests

response = requests.post(
    "https://routerai.ru/api/v1/extractions",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={
        "model": "cloudflare/clairic",
        "state": "Счёт № СЧ-2291 от 02.10.2026. Поставщик: ООО «Ромашка», ИНН 7701234567. "
                 "Оплата в течение 30 календарных дней. Итого к оплате: 108 000,00 руб.",
        "reference_date": "2026-11-09",
        "fields": {
            "invoice_number": {"type": "text", "instructions": "Номер счёта"},
            "supplier_inn": {"type": "text", "instructions": "ИНН поставщика"},
            "total": {"type": "number", "unit": "RUB", "decimals": 2, "instructions": "Итоговая сумма к оплате"},
            "payment_term": {"type": "duration", "unit": "days", "instructions": "Срок оплаты"},
            "contract_number": {"type": "text", "instructions": "Номер договора"}
        }
    },
)
answers = response.json()["answers"]
print(answers["total"]["value"])            # 108000
print(answers["contract_number"]["value"])  # None — договора в тексте нет
```

Ответ — объект `answers` с теми же именами полей:

```json
{
  "invoice_number": {"type": "text", "value": "СЧ-2291", "span": [7, 14], "confidence": 0.9998, "p_null": 0.00008},
  "total": {"type": "number", "value": 108000, "confidence": 0.9995, "p_null": 0.00003},
  "payment_term": {"type": "duration", "value": 30, "unit": "days", "confidence": 0.9997},
  "contract_number": {"type": "text", "value": null, "guess": "СЧ-2291", "p_null": 0.87}
}
```

- `value` — значение поля. `null`, если поля в тексте, скорее всего, нет.
- `p_null` — вероятность, что поля в тексте нет. Если она выше порога (по умолчанию 0.5, меняется параметром `null_threshold` у поля), `value` становится `null`.
- `guess` — лучшая догадка, даже когда `value` = `null`. В примере выше модель предлагает номер счёта как номер договора, но сама считает это маловероятным.
- `confidence` — уверенность в найденном значении.
- `span` — у текстовых полей: позиция `[начало, конец)` в `state`, в символах Unicode.

## Типы полей

| Тип        | Что вернёт                                  | Параметры                                                   |
| ---------- | ------------------------------------------- | ----------------------------------------------------------- |
| `text`     | Фрагмент текста и его `span`                | —                                                           |
| `number`   | Число                                       | `unit`, `decimals`, `integer`                               |
| `date`     | Дата `YYYY-MM-DD`                           | —                                                           |
| `time`     | Время суток                                 | —                                                           |
| `datetime` | Дата и время                                | —                                                           |
| `duration` | Длительность                                | `unit`: seconds, minutes, hours, days, weeks, months, years |
| `list`     | Список значений                             | `items`: text, number или date (обязателен)                 |
| `estimate` | Оценка величины: `value`, `mean`, `median`, `p10`, `p90` | `min`, `max` (обязательны), `step`, `scale`, `unit` |
| `forecast` | Прогноз ряда на несколько шагов             | `min`, `max`, `horizon`, `interval` (обязательны)           |

У любого поля есть `instructions` — что в нём должно быть — и `null_threshold`.

`estimate` и `forecast` отвечают, даже когда прямого ответа в тексте нет: модель оценивает величину по контексту и возвращает распределение. Например, поле «через сколько дней клиент фактически оплатит счёт» с `min: 0, max: 120` для счёта выше дало `value: 32`, `p10: 25`, `p90: 38`.

## Как не ошибиться

- **Проверяйте `confidence`.** Явные значения (номер, ИНН, сумма) приходят с уверенностью 0.999+. Вычисленные — заметно ниже. «Дата счёта плюс 30 дней» в наших прогонах давала то 2026-11-01, то 2026-11-02 с `confidence` 0.5–0.65. Такие поля сверяйте в коде или отдавайте человеку.
- **Списки чистите по порогу.** В списке контактов модель однажды вернула лишний элемент «Р» рядом с двумя ФИО, и `confidence` всего списка упал до 0.69. Низкая уверенность — повод перепроверить.
- **Передавайте `reference_date`.** Без неё модель не знает, к какому году отнести «2 октября» и от чего отсчитывать «через неделю».
- **Не просите вычислять в модели то, что можно посчитать в коде.** Извлеките дату счёта и срок оплаты отдельными полями и сложите их сами: результат будет точным.

## Картинки и PDF

Файлы не кладутся в `state`. Они передаются один раз в `assets` под ключом, а ключ упоминается в `state` или в `instructions` поля там, где файл нужен:

```json
{
  "model": "cloudflare/clairic",
  "state": "Чек покупки: receipt_1",
  "assets": {
    "receipt_1": {"type": "image", "format": "jpeg", "data_base64": "/9j/4AAQSkZJRgABAQ..."}
  },
  "fields": {
    "total": {"type": "number", "unit": "RUB", "instructions": "Сумма чека"}
  }
}
```

- `data_base64` — файл в base64 без префикса `data:`. Форматы — `png`, `jpg`, `jpeg`, `pdf`.
- Каждый файл из `assets` должен быть упомянут хотя бы раз.
- PDF читают только document-модели. Модель без изображений на входе на картинку ответит `400`.
- У значения, прочитанного с картинки, может прийти `source: "image"` и `spell_confidence` — уверенность в написании.

## Стоимость

Тарифицируются только входные токены: текст `state`, описание полей и картинки. Выход не тарифицируется. Счёт выше с девятью полями — около 1 200 входных токенов, это примерно одна сотая рубля. Цена конкретной модели — на её странице в [каталоге](https://routerai.ru/models).

## Частые вопросы

**Можно ли вызвать clairic через `/api/v1/decisions`?**
Нет. `cloudflare/clairic` обслуживается только на `/api/v1/extractions` с `fields`. На `/decisions` RouterAI ответит `400` с подсказкой правильного пути.

**Работают ли `temperature`, `response_format`, стриминг?**
Нет. Тело запроса — `model`, `state`, `fields` и, по необходимости, `assets`, `reference_date`, `provider`, `session_id`, `user`. Ответ приходит целиком.

**Что значит `truncated: true`?**
Модель прочитала только начало `state`: текст длиннее её контекста. Разбейте документ на части или передайте только нужный фрагмент.

## Что дальше

- [Решения вместо текста](/docs/guides/overview/decisions/overview) — когда нужен не набор полей, а ответ «да/нет», выбор варианта или оценка.
- [Справочник API](https://routerai.ru/docs/reference#tag/extractions) — параметры запроса, формат ответа, ошибки.
