Saltar al contenido principal
Las estrategias integradas — cheapest, quality, balanced, adaptive — eligen un modelo por precio y calidad. El Routing DSL es el nivel inferior a ese, para cuando el modelo correcto depende de lo que la solicitud realmente es: un turno largo de codificación agéntica, una llamada de clasificación barata, una solicitud de visión, un reintento tras una prueba fallida. Tú escribes reglas; la pasarela las evalúa por solicitud y enruta en consecuencia. Es la estrategia dsl de un enrutador nombrado — así que tu aplicación sigue llamando a orcarouter/{name} y la lógica de enrutamiento vive en el panel, versionada y editable sin un redespliegue.

Cuándo recurrir al DSL

Usa una estrategia integrada cuando “el modelo en vivo más barato” o “la mejor calidad” captura tu intención. Recurre al DSL cuando el enrutamiento depende del contenido o contexto de la solicitud:
  • Especialización por tarea — envía código a un modelo de codificación, visión a un modelo de visión, chat barato a un modelo barato.
  • Enrutamiento consciente de la dificultad — escala solo las solicitudes difíciles a un modelo caro; mantén las fáciles baratas.
  • Enrutamiento consciente del agente — enruta de forma distinta según el estado de la sesión (qué herramientas ha usado el agente, si las pruebas acaban de fallar, cuántos turnos lleva).
  • Reglas por tiempo / inquilino / cabecera — enrutamiento distinto por hora, grupo de usuarios o una cabecera de la solicitud.

Cómo habilitarlo

En el panel, bajo Routing, abre un enrutador y configura su Estrategia a DSL. Eso revela el editor de DSL para este enrutador. Todo lo demás del enrutador sigue aplicándose — el glob de Modelos permitidos, la red de seguridad de Modelo por defecto y la invocación orcarouter/{name}.

El editor

El editor está construido para llevarte de la intención a un conjunto de reglas funcional rápidamente:
  • Templates sembradas con los modelos reales de tu espacio de trabajo (vía un diálogo único de mapeo de niveles), para que nunca empieces desde un archivo en blanco ni choques con un muro de “modelo desconocido”.
  • Insert — inserta un Model, un Router (orcarouter/<name>) o un Pool desde el autocompletado en lugar de teclear identificadores a mano.
  • Generate — describe el enrutamiento que quieres en lenguaje sencillo y obtén de vuelta DSL compilado y limpio de lint, fundamentado en tus modelos reales.
  • Explain — una paráfrasis en español sencillo de lo que hace el conjunto de reglas actual.
  • Lint en línea — cada error reporta {line, column, message} y cada código de lint tiene un explicador ?. La precedencia (gana la primera coincidencia) y los patrones CEL comunes se muestran in situ.

Estructura del archivo

Un conjunto de reglas es YAML con tres claves de nivel superior:
version: 1              # required — currently must be 1
rules: [...]            # required — 1 to 30 rules, evaluated in order
default: {...}          # required — the effect when no rule matches
Una regla es una condición when: y un efecto use::
rules:
  - id: hard_code              # required: ^[a-z][a-z0-9_]{0,39}$, unique
    when: |                    # optional CEL boolean; absent ⇒ always matches
      task_class == "code" && difficulty > 0.6
    use:
      model: "anthropic/claude-sonnet-4-6"
default:
  delegate: balanced           # fall back to a built-in strategy
Las reglas se evalúan de arriba abajo; gana la primera regla cuyo when: sea verdadero. Si ninguna coincide, se aplica default:. Ordena tus reglas de la más específica primero — una regla amplia temprana eclipsa todo lo que está debajo.

when: — la condición

Las condiciones se escriben en CEL (Common Expression Language): seguro por diseño — sin bucles, sin E/S, evaluación en microsegundos, solo regex RE2. Estos seis patrones cubren la gran mayoría de las reglas reales:
PatrónEjemplo
Acceso a campotask_class == "agent"
Comparación numéricadifficulty > 0.6 && request.input_tokens < 50000
Lógica booleanaagent_state.has_edited && !agent_state.has_run_tests
Pertenencia a lista"Edit" in agent_state.tools_used
Macro de regexsystem_prompt_matches("(?i)planning agent")
Macro de herramientatool_calls_present_any(["Edit","Write","apply_patch"])

Variables

Forma de la solicitud
VariableTipo
modelstring
request.input_tokensint
request.output_max_tokensint
request.streambool
request.visionbool
request.message_countint
request.has_system_promptbool
request.has_toolsbool
Clasificación (calculada por la pasarela por solicitud)
VariableTipoSignificado
task_classstringchat / code / agent / vision / audio / rag / creative
difficultydouble0.01.0
code_keyword_densitydouble0.01.0
reasoning_cue_countintseñales de razonamiento detectadas en el prompt
tool_countintdefiniciones de herramientas distintas en la solicitud
Sesión del agente (agent_state.*, persistido a lo largo de una conversación)
VariableTipo
agent_state.turnint
agent_state.tools_usedlist<string>
agent_state.files_readlist<string>
agent_state.has_editedbool
agent_state.has_run_testsbool
agent_state.last_test_failedbool
agent_state.consecutive_errorsint
agent_state.elapsed_secondsint
agent_state.models_triedlist<string>
Contexto
VariableTipo
headers["x-foo"]string
user.id / user.groupint / string
token.id / token.nameint / string
time.hour / time.weekdayint (UTC)
workspace.idint

Macros

Funciones CEL registradas para las comprobaciones comunes de “mirar dentro de la solicitud”:
MacroDevuelve
system_prompt_matches(regex)RE2 sobre los mensajes de sistema unidos
user_message_matches(regex)RE2 sobre el último mensaje de usuario
tool_definitions_include(name)una herramienta está declarada en la solicitud
tool_calls_present_any(list)la solicitud lleva alguna de estas llamadas a herramientas
tool_results_from_any(list)la solicitud tiene mensajes con rol de herramienta de alguno
header_matches(name, regex)RE2 sobre el valor de una cabecera

use: — el efecto

Un bloque use: nombra un destino (exactamente uno) y cualquier número de perillas opcionales por llamada.

Destino

use:
  model:    "anthropic/claude-sonnet-4-6"   # one upstream model
  models:   ["openai/gpt-4o-mini", "..."]   # load-balance across a list
  pool:     "@pool:<name>"                   # an admin-curated pool
  delegate: balanced                         # hand off to a built-in
                                             #   strategy: cheapest |
                                             #   quality | balanced |
                                             #   linucb | gated_adaptive
delegate: dsl se rechaza (haría recursión). Fijar a canales específicos (channels: / @channel:) no está disponible actualmente y el lint lo marca como no soportado — enruta por model, models o pool en su lugar.

Perillas por llamada

Combínalas con cualquier destino para dar forma a la llamada upstream:
use:
  reasoning_effort:       low | medium | high     # OpenAI o-series, Gemini
  thinking_budget_tokens: 1024..64000             # Claude / Gemini thinking
  samples:                1..16                    # the n parameter
  temperature:            0.0..2.0
  param_override:         { ... }                  # merged into upstream params
  header_override:        { ... }                  # merged into upstream headers
  reason_tag:             "<[a-z0-9_]+>"           # shows up in logs/telemetry
  affinity_ttl:           "5m"                      # channel stickiness window
  model_rewrite:          "<upstream-model>"       # send under a different name
param_override y header_override aplican una lista de denegación — no puedes sobrescribir model, messages, stream, tools, cabeceras de autenticación, etc. (eso subvertiría la facturación, la auditoría o el estado del agente).

Cascadas de confianza y ensembles (avanzado)

Dos efectos avanzados permiten que una regla reaccione a una primera respuesta débil o se despliegue en abanico entre varios modelos. Se escriben de la misma forma que cualquier regla. Cascade — reintenta ante una señal de baja confianza con un efecto más fuerte:
rules:
  - id: code_with_repair
    when: task_class == "code"
    use:
      model: "openai/gpt-4o-mini"
    on_low_confidence:
      signals: [patch_invalid, self_doubt, next_turn_test_failed]
      use:
        model: "anthropic/claude-sonnet-4-6"   # repair attempt
Ensemble — emite varias ramas en paralelo y deja que un árbitro elija:
use:
  parallel:
    - { model: "anthropic/claude-sonnet-4-6" }
    - { model: "openai/gpt-4o-mini", samples: 2 }
  arbiter:
    strategy: best_of_n        # or majority | first | tests_pass
    model:    "anthropic/claude-sonnet-4-6"   # judge (best_of_n only)
  max_latency_ms: 120000
El runtime de ensemble / cascade está restringido y desactivado por defecto. Como cada rama paralela y cada reparación en cascada se factura como su propia llamada, el runtime de despliegue en abanico está detrás de un flag de servidor mientras se valida la facturación por rama. Con él desactivado, una regla parallel: sirve solo la primera rama y una cascade registra su señal pero no vuelve a despachar — el conjunto de reglas igualmente pasa el lint, se guarda y enruta su efecto principal con normalidad. Contáctanos para habilitar el runtime de ensemble en tu espacio de trabajo.

Despliegue seguro

Un nuevo conjunto de reglas no se hace cargo de tu tráfico en el momento en que lo guardas:
  • Modo shadow — durante una ventana tras el primer guardado, el DSL se evalúa pero no se usa: tu estrategia anterior sigue sirviendo el tráfico mientras la pasarela registra lo que el DSL habría hecho. El panel muestra un informe de diferencias — porcentaje de rutas divergentes, delta de coste proyectado, conteos de disparo por regla y con qué frecuencia cayó hasta default:. Léelo antes de confiar en las reglas.
  • Canary — sube gradualmente el DSL a un porcentaje del tráfico en vivo (5 → 25 → 50 → 100), observando las métricas por porción, y revierte al instante deslizando el porcentaje a 0.
También puedes hacer un dry-run de un conjunto de reglas contra una solicitud sintética (clase de tarea, dificultad, estado del agente, forma de la solicitud) directamente en el editor y ver la traza y la regla coincidente — sin tráfico, nada persistido.

Límites y validación

Cada guardado ejecuta un lint estricto; los conjuntos de reglas inválidos se rechazan con {line, column, message, rule}:
  • Esquema — claves requeridas, tipos/enums correctos, sin campos desconocidos.
  • Tamaño — ≤ 30 reglas, ≤ 16 KiB de YAML, ≤ 200 caracteres por when:.
  • CEL — parsea, verifica tipos contra el entorno de variables, sin identificadores desconocidos, y when: debe evaluar a un bool.
  • Efecto — exactamente un destino por bloque use:; todas las referencias model / models / @pool: deben resolverse en tu espacio de trabajo.
  • Rangos de perillasthinking_budget_tokens ∈ [1024, 64000], temperature ∈ [0, 2], samples ∈ [1, 16].
  • Reservado — los ids de regla que empiezan con _ están reservados; default como id de regla se rechaza (usa el bloque default: de nivel superior).
Cada guardado y reversión escribe una fila de auditoría; las ediciones concurrentes se detectan y se le pide al segundo guardado que reintente contra el estado fresco.

Un ejemplo completo

version: 1
rules:
  - id: vision
    when: request.vision
    use: { model: "openai/gpt-4o" }

  - id: cheap_chat
    when: task_class == "chat" && difficulty < 0.3
    use: { delegate: cheapest }

  - id: hard_code
    when: task_class == "code" && difficulty > 0.6
    use:
      model: "anthropic/claude-sonnet-4-6"
      thinking_budget_tokens: 8000
      reason_tag: hard_code

  - id: agent_after_failed_test
    when: agent_state.last_test_failed && agent_state.consecutive_errors >= 2
    use:
      model: "anthropic/claude-sonnet-4-6"
      reason_tag: repair

default:
  delegate: balanced
Para confirmar a qué modelo se resolvió una solicitud, lee las cabeceras de respuesta X-Orca-Router y X-Orca-Resolved-Model response headers.

Referencia de la API

El DSL se gestiona por enrutador; las escrituras requieren Developer+.
Método y rutaRolPropósito
GET /api/user/routers/:id/dslMemberFuente + versión + estado shadow/canary.
PUT /api/user/routers/:id/dslDeveloper+Lint + guardar (nueva versión, auditada).
POST /api/user/routers/:id/dsl/lintMemberHacer lint de un borrador → {errors:[…]}.
POST /api/user/routers/dsl/lintMemberLint sin estado (sin id de enrutador).
POST /api/user/routers/:id/dsl/dryrunMemberEvaluar una solicitud sintética → traza + regla coincidente.
GET /api/user/routers/:id/dsl/historyMemberHistorial de versiones, la más nueva primero.
POST /api/user/routers/:id/dsl/rollback/:versionDeveloper+Volver a hacer lint y restaurar una versión anterior.

Preguntas frecuentes

Es una estrategia — la opción dsl junto a cheapest / quality / balanced / adaptive. Las demás eligen por precio y calidad; el DSL elige según reglas que tú escribes sobre la forma de la solicitud, la clasificación y el estado del agente. Aun así puedes hacer delegate: a una estrategia integrada como efecto de una regla o como el default.
Se aplica el efecto default: de nivel superior. Es requerido, así que siempre hay un resultado definido — comúnmente delegate: balanced o un modelo de red de seguridad específico.
Sí. CEL se ejecuta en un sandbox solo con funciones de la biblioteca estándar, una fecha límite de evaluación de unos pocos milisegundos, regex RE2 (tiempo lineal, sin ReDoS) y sin acceso a la base de datos, la red o el sistema de archivos. El entorno de variables es un conjunto fijo de escalares y listas.
Tres formas: hazle un dry-run contra una solicitud sintética en el editor, déjalo en modo shadow y lee el informe de diferencias, y luego haz un canary sobre un pequeño porcentaje del tráfico en vivo antes de subirlo al 100%.