> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ndi.nace.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Drex (Decision Model)

> Drex, NDI's decision model, answers typed questions about a state in one System One call

**Drex** is NDI's decision model. **System One** is the topology it runs in:
one synchronous call in which Drex answers every question about a state, with no
agent loop, job or workspace. The endpoint, SDK resource, CLI command and MCP
tool are named `systemone` after it.

`systemone.evaluate` answers typed questions about a state — text, or any JSON
value — in one synchronous call. There is no workspace and no job: the answers
are the response. Each question is keyed by a name you choose, and its answer
comes back under the same name.

| Question | Answer |
| - | - |
| `NoulQuestion` | `NoulAnswer.noul`: the probability that the question is true |
| `ChoiceQuestion` | `ChoiceAnswer.choice`: the likeliest option, with `confidence` and every option's `probabilities` |
| `ScoreQuestion` | `ScoreAnswer.score`: the expected level index on your scale (`0` is the lowest), with `legend`, `probabilities` per level index, and `confidence` |

***

## Evaluate a state

```python theme={"dark"}
from ndi_sdk import NdiClient
from ndi_sdk.models.systemone import (
    ChoiceAnswer,
    ChoiceQuestion,
    NoulAnswer,
    NoulCriteria,
    NoulQuestion,
    ScoreAnswer,
    ScoreQuestion,
)

with NdiClient() as client:
    result = client.systemone.evaluate(
        {"vendor": "Acme Freight", "amount": 1200, "purchase_order": "PO-4411"},
        {
            "approve": NoulQuestion(
                criteria=NoulCriteria(
                    true="The invoice matches an open purchase order",
                    false="No purchase order covers it",
                ),
            ),
            "risk": ChoiceQuestion(
                instructions="How risky is paying this invoice?",
                criteria={"low": None, "high": "Unusual vendor, amount, or terms"},
            ),
            "urgency": ScoreQuestion(criteria=["not urgent", "this week", "today"]),
        },
    )
    for name, answer in result.answers.items():
        if isinstance(answer, NoulAnswer):
            print(name, answer.noul)
        elif isinstance(answer, ChoiceAnswer):
            print(name, answer.choice, answer.probabilities)
        elif isinstance(answer, ScoreAnswer):
            print(name, answer.legend[round(answer.score)], answer.probabilities)
        else:
            print(name, answer.type, answer.data)  # UnknownAnswer: a type newer than this SDK
```

A `NoulQuestion` needs `instructions` or at least one criterion. A
`ChoiceQuestion` lists its options as `criteria` keys; a `None` value means the
option name says enough. A `ScoreQuestion` lists its levels lowest first.

`confidence` is Drex's average confidence in the answer. It tracks
the highest probability but is a different number; do not derive one from the
other. `legend[round(score)]` names the level nearest the expected score, which
is not always the most likely level; for that, take the highest entry in
`probabilities`. Limits and input rules are on [Drex (Decision Model)](/api-reference/systemone#limits-and-input-rules).

***

## Method signature

```text theme={"dark"}
def evaluate(
    state: JsonValue,
    questions: Mapping[str, ChoiceQuestion | NoulQuestion | ScoreQuestion],
) -> SystemOneResponse
```

### Parameters

| Parameter | Type | Required | Description |
| - | - | - | - |
| `state` | `JsonValue` | Yes | The situation to decide about: text, or any JSON value |
| `questions` | `Mapping[str, ChoiceQuestion \| NoulQuestion \| ScoreQuestion]` | Yes | Questions keyed by a name of your choice |

### Returns

A `SystemOneResponse`: `answers` keyed as the questions were, the `model` that
answered, token `usage`, `evaluation_time_ms`, and `request_id`. An answer type
newer than this SDK arrives as an `UnknownAnswer` carrying the raw answer in
`data`.

***

## Error handling

| Exception | Code | When |
| - | - | - |
| `pydantic.ValidationError` | none; nothing is sent | A question has an unknown `type`, or a `ChoiceQuestion` or `ScoreQuestion` has no `criteria`. The SDK checks questions against its models before sending, plain dicts included |
| `InvalidRequestError` | `invalid_request` (422) | The server refused the request: for example an empty `noul`, empty `criteria`, or a limit. `exc.body.detail["issues"]` lists each problem by path, such as `questions.risk.criteria` |
| `AuthenticationError` | `unauthorized` (401) | The API key is missing or invalid |
| `NotImplementedByServerError` | `not_implemented` (501) | The deployment has no decision model |
| `RateLimitError` | `rate_limited` (429) | Too many requests; retry after `Retry-After` |
| `ServerError` | `upstream_unavailable` (503) | The decision model is unavailable or did not answer within 45 seconds; retry |

The SDK does not retry `evaluate` on its own. Nothing is stored, so retrying a
`429` or `503` yourself is safe.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.