# Motor de decisión

Decisiones estructuradas por criterios, en un nivel de velocidad y precisión que tú eliges.

URL base: `https://ai.inovacc.dev`

## Descripción general

El motor de decisiones responde preguntas estructuradas sobre un contenido con veredictos estructurados. Envías un **estado** (aquello que se va a juzgar: un texto, un registro, un objeto JSON) y un conjunto de **preguntas**, cada una de un tipo conocido; recibes una respuesta por pregunta, con la etiqueta o puntuación elegida, las probabilidades detrás de ella y una confianza. El endpoint es `POST /v1/run` en `https://ai.inovacc.dev`, invocado en una ruta de decisión de tu organización.

El problema que resuelve es convertir un juicio en datos sobre los que un programa pueda actuar. Preguntarle a un modelo de chat "¿esto es bueno?" devuelve prosa que luego tienes que analizar y que no puedes comparar de una llamada a otra. El motor de decisiones devuelve siempre la misma forma, de modo que puedes almacenarla, aplicarle umbrales y auditarla.

Úsalo para clasificación, puntuación, triaje y verificaciones frente a criterios: si este ticket necesita a una persona, cuál de cinco categorías encaja, qué tan bien cumple esta respuesta con el estándar. Cuando necesites prosa generada, usa el [Chat con IA](/es/products/ai.chat/); cuando el veredicto deba apoyarse en tus propios documentos, recupera primero los pasajes con [Conocimiento y búsqueda vectorial](/es/products/knowledge/) y colócalos en el estado.

## Conceptos

**Rutas de decisión.** Como en toda la IA de Inovacc, llamas a una ruta, nunca a un modelo. `GET /v1/models` lista tus rutas; una entrada cuyo `endpoint` es `/v1/run` es una ruta de ejecución, y las que Inovacc configuró como rutas de decisión responden con la forma que se describe más abajo.

**Preguntas y sus tipos.** `questions` es un objeto de 1 a 64 entradas, con claves que son tus propios ids (letras, dígitos, `_`, `.` o `-`, hasta 100 caracteres). Cada pregunta tiene un `type`, normalmente `instructions`, y `criteria` cuya forma queda fijada por el tipo:

| Tipo | Pide | `criteria` |
|---|---|---|
| `noul` | sí o no | un objeto `{"true": "...", "false": "..."}` |
| `choice` | una etiqueta entre varias | un objeto `{"label": "description", ...}` |
| `score` | un nivel en una escala | un arreglo de niveles, del más bajo al más alto |

Las formas son estrictas: una cadena de criterios como `"1-5"` o `"yes|no"` se rechaza.

**Respuestas.** Cada respuesta lleva lo que produjo su tipo: el `choice` o el `score` con `probabilities`, una `legend` y una `confidence`. Una respuesta `noul` lleva su probabilidad `p` en lugar de una confianza.

**Niveles.** Una ruta de decisión sirve uno de tres niveles: `fast`, `standard` o `precise`. Todos responden con una sola forma, y el `engine.tier` de la respuesta indica cuál respondió. **La confianza no es comparable entre niveles**: cada nivel reporta en su propia escala, así que configura un umbral por nivel.

**Escalamiento.** Con `escalate`, las preguntas respondidas por debajo de un umbral de confianza se vuelven a hacer, solas, en el siguiente nivel más fuerte (`fast`, luego `standard`, luego `precise`), y la respuesta más fuerte reemplaza a la más débil. Los valores predeterminados son `fast` 0.40, `standard` 0.80 y `precise` 0.55; puedes pasar `min_confidence` (un número, o uno por nivel) y `max_tier`. La confianza de una respuesta `noul` es la distancia de `p` a un lanzamiento de moneda, `|2p - 1|`.

## Cómo funciona

Toda llamada lleva `Authorization: Bearer <key>` y un `X-Operation-Id`. Tu organización se obtiene de la clave. El cuerpo toma solo `model`, `input` y `escalate`. En una ruta de decisión `input` es `{"state", "questions", "images"?}`: `state` es cualquier valor JSON no nulo, `images` tiene como máximo 16 entradas. Cualquier otro campo dentro de `input` se rechaza con `invalid_decision_input`, y la respuesta nunca lo repite.

La respuesta es:

`{"model": "<route id>", "result": {"answers": {...}, "usage": {"input_tokens", "output_tokens"}}, "state": "Completed", "engine": {"tier", "latency_ms"}}`

y sus encabezados llevan `x-route-id`, `x-usage-input-tokens` y `x-usage-output-tokens`.

Con escalamiento, cada respuesta también indica qué `tier` la respondió y, cuando fue escalada, `escalated_from` lista los niveles inferiores y la confianza que dio cada uno. Un objeto `escalation` reporta cada intento, cuántas preguntas se hicieron y cuántas volvieron por debajo del umbral, y cuántas respuestas dio cada nivel al final. Cada intento es su propia llamada, reservada contra tus cuotas y medida en su propio nivel. Si un intento falla, el escalamiento se detiene, se conservan las respuestas anteriores, y la solicitud sigue siendo `200`.

No hay streaming ni un tiempo límite de 60 segundos en este endpoint. Una respuesta exitosa se almacena durante 24 horas bajo su id de operación: el mismo id devuelve la misma respuesta combinada con `x-idempotent-replay: true` y no llama a nada. Lo que se mide son los tokens, al precio de cada nivel que respondió.

## Primeros pasos

Necesitas una clave de API y una ruta de decisión habilitada para tu organización ([Autenticación](/es/guides/authentication/)).

1. **Encuentra tu ruta de decisión** con `GET /v1/models`: una entrada cuyo `endpoint` sea `/v1/run`.
2. **Haz una pregunta `noul`.** `POST /v1/run` con tu ruta como `model`, un texto corto como `state` y una pregunta `{"type":"noul","instructions":"...","criteria":{"true":"...","false":"..."}}` (consulta [los ejemplos](#example)). La respuesta tiene el id de tu pregunta bajo `result.answers`, con su probabilidad `p`.
3. **Agrega una pregunta `score`** con tres niveles a la misma llamada. Ambas respuestas regresan en una sola respuesta; `engine.tier` nombra el nivel.
4. **Escala.** Envía la llamada de nuevo con un nuevo id de operación y `"escalate": true`. La respuesta ahora lleva `tier` por respuesta y un objeto `escalation` que lista los intentos.

## Casos de uso

**Triaje de tickets.** Cada ticket de soporte entrante es el `state`; una pregunta `choice` elige el equipo, una pregunta `noul` pregunta si debe responder una persona, una pregunta `score` califica la urgencia. Las respuestas se almacenan con el ticket, y un ticket cuya confianza esté por debajo de tu umbral pasa a una persona.

**Verificaciones de calidad sobre texto generado.** Antes de enviar el borrador de un asistente, una llamada de decisión lo puntúa frente a tu estándar ("Por debajo del estándar", "En el estándar", "Por encima del estándar") y verifica algunas reglas `noul`. Con `escalate`, solo los borradores sobre los que el nivel rápido tuvo dudas pagan por el nivel preciso.

**Moderación de contenido con imágenes.** El texto de una publicación y hasta 16 imágenes se juzgan frente a las preguntas de tu política; la confianza por pregunta decide entre publicar, rechazar y enviar a revisión.

## Límites y precios

| Límite | Valor |
|---|---|
| Preguntas por llamada | 1 a 64 |
| Id de pregunta | 1 a 100 caracteres: letras, dígitos, `_`, `.`, `-` |
| Imágenes por llamada | 16 |
| Cuerpo de la solicitud | 1 MiB por defecto; tu organización puede configurarse entre 1 KiB y 20 MiB |
| Tokens de entrada por solicitud | el tope de tu organización |
| Respuesta almacenada para una repetición | 24 horas |
| Solicitudes, tokens, costo, concurrencia, presupuestos diarios | según lo configurado para tu organización |

**Precios:** a consultar. La unidad de precio son los **tokens**, al precio del nivel que respondió.

## Errores

| Estado | Código | Qué significa y qué hacer |
|---|---|---|
| 400 | `invalid_input` | Falta `input` o no es un objeto JSON. |
| 400 | `invalid_decision_input` | `input` no es `{state, questions, images?}` o una pregunta está mal formada; revisa los tipos y los ids. |
| 400 | `invalid_escalate` | `escalate` tiene una clave o un valor que no admite, o la ruta no es una ruta de decisión. |
| 400 | `unsupported_field` | Un campo de nivel superior distinto de `model`, `input`, `escalate`. |
| 400 | `model_not_allowed`, `input_too_large` | Usa un id de ruta válido; acorta el estado. |
| 400 | `missing_operation_id`, `invalid_operation_id` | Envía un `X-Operation-Id` válido. |
| 401 | `missing_credentials`, `invalid_credentials` | Envía una clave válida. |
| 403 | `route_forbidden` | La ruta no es tuya, o no es una ruta de ejecución. |
| 409 | `operation_in_progress` | Ese id de operación sigue en ejecución. |
| 413 | `request_too_large` | El cuerpo supera tu tope de tamaño. |
| 429 | `rate_limited`, `quota_exceeded`, `concurrency_limited`, `budget_exhausted` | Consulta [Errores](/es/guides/errors/); espera `Retry-After` cuando se indique. |
| 502 | `upstream_error` | Falló el nivel, o se rechazó la forma de los criterios; revisa los criterios y luego reintenta. |
| 503 | `service_unavailable` | No se puede acceder a las decisiones; reintenta más tarde. |

## Buenas prácticas

- **Escribe los criterios en las formas estrictas**: un objeto para `noul` y `choice`, un arreglo para `score`.
- **Mantén umbrales por nivel**, nunca un solo número para todos los niveles.
- **Usa `escalate` con `max_tier`** para limitar el costo: la mayoría de las preguntas se detienen en el nivel más barato.
- **Coloca en `state` todo lo que el juicio necesita**, incluidos los pasajes recuperados; el motor no ve nada más.
- **Usa ids de pregunta estables**, para que las respuestas puedan compararse entre llamadas y almacenarse por id.
- **Deriva el id de operación del elemento juzgado** para que un lote reintentado repita la respuesta en lugar de pagar dos veces.

## Relacionados

- [Chat con IA](/es/products/ai.chat/), [Conocimiento y búsqueda vectorial](/es/products/knowledge/)
- [Autenticación](/es/guides/authentication/), [Errores](/es/guides/errors/), [Límites](/es/guides/limits/)

## Endpoints

- POST /v1/run

## Ejemplo

```sh
curl -X POST "https://ai.inovacc.dev/v1/run" -H "Authorization: Bearer $INOVACC_API_KEY" -H "X-Operation-Id: $(uuidgen)" -H "Content-Type: application/json" -d '{}'
```
