DSL de enrutamiento: compón un panel de modelos que piense como Fable 5

DSL de enrutamiento: compón un panel de modelos que piense como Fable 5

Fecha de publicación

Últimos modelos · 20Ver todos los modelos →
Benchmarks: Artificial Analysis · actualizado a diario
Volver a todas las publicaciones

Durante dos años, la fórmula para "más inteligencia" ha sido "esperar al siguiente modelo". Creemos que esa es la unidad de progreso equivocada. La frontera no es un único checkpoint, sino un panel. Dale a tres buenos modelos el mismo problema difícil, deja que discrepen y arbitra entre las respuestas, y el panel supera a cualquiera de sus miembros. A menudo supera al siguiente modelo en la escala de precios.

El Routing DSL es la forma de construir ese panel. Se trata de una estrategia de enrutamiento programable — YAML + CEL — que convierte tu endpoint de OrcaRouter en un grafo de inferencia: enruta según la dificultad, enruta según la tarea, distribuye la carga entre varios modelos a la vez, evalúa o vota sobre sus resultados, recurre a una alternativa cuando la confianza es baja y ajusta todo el conjunto en función del coste, la latencia o la calidad. Tú escribes las reglas; la pasarela las compila y las ejecuta en cada solicitud en ~5 ms.

Esta publicación es el recorrido de ingeniería: la gramática, las variables sobre las que puedes ramificar, los cuatro árbitros, la cascada y, al final, un conjunto de reglas de producción completo.


El resultado primero

Dos puntos de referencia ilustrativos. (Las cifras son ilustrativas — su propósito es mostrar la forma del efecto, no ser citadas como puntuaciones oficiales.)

Comparación de la frontera — un endpoint DSL enrutado por dificultad frente a la frontera en solitario:


Paneles Fusion vs. modelos individuales — puntuados en 93 de 100 tareas (de OpenRouter):


Tres cosas que vale la pena mirar fijamente:

Todo panel de fusión supera a cada uno de sus propios miembros. Opus 4.8 + GPT-5.5 (~67,5 %) supera tanto a Opus en solitario (~58,5 %) como a GPT-5.5 en solitario (~60 %) por 7–9 puntos. El desacuerdo es señal; el arbitraje lo aprovecha.

Fusion alcanza el siguiente nivel. Tres paneles distintos superan a Fable 5 en solitario (~65.5%) usando solo los modelos inferiores a él.

No necesitas miembros caros. Opus + autofusión de Opus (~65,5 %) iguala a Fable 5 con un solo modelo y un sampler. Un panel de modelos baratos — Gemini 3 Flash + Kimi K2.6 + DeepSeek V4 Pro (~64,5 %) — se queda a un pelo por debajo de Fable 5 por una fracción del coste por token. Esa es toda la tesis: compra inteligencia con topología, no con el siguiente nivel de precio.

El Routing DSL es la superficie de control que te permite invertir esa topología solo donde vale la pena: modelos baratos en el 80% fácil, un panel de fusión en la cola difícil.


La gramática en 30 segundos

Un conjunto de reglas es una versión, una lista de reglas y un valor predeterminado obligatorio. Las reglas se evalúan de arriba a abajo; la primera when: que sea verdadera gana. Sin when: significa "siempre coincide".

versión: 1

rules:
  - id: only_rule
    use: { model: "claude-sonnet-4-6" }
default:
  delegate: balanced


El when: es una CEL expresión booleana — en sandbox, regex solo de RE2, sin bucles, sin E/S, evaluación en microsegundos, con un único plazo de 5 ms compartido por todo el conjunto de reglas. El use: es el effect: adónde va la solicitud y cómo se ajusta. Los límites son deliberadamente pequeños (≤30 reglas, ≤16 KiB de código fuente, ≤200 caracteres por when:) para que un conjunto de reglas siga siendo auditable.


{{1}}Primitivo 1 — enrutar por dificultad y tarea{{/1}}

El distribuidor clasifica cada solicitud antes de enrutar y expone las características a CEL. Te ramificas en ellas directamente:

versión: 1

rules:
  - id: hard_reasoning
    when: difficulty > 0.8
    use:
      model: "claude-opus-4-8"
      reasoning_effort: "high"
      thinking_budget_tokens: 32000


  - id: code_path
    when: task_class == "code" && code_keyword_density > 0.5
    use: { model: "gpt-5.5" }


  - id: cheap_chat
    when: difficulty < 0.3
    use: { model: "gemini-3-flash" }


default:
  delegate: balanced


Las variables que puedes leer en when: (abreviado — consulta la referencia completa en la documentación):

GrupoEjemplos

Forma de la solicitud

request.input_tokens, request.output_max_tokens, request.stream, request.vision, request.message_count, request.has_tools


Clasificación

task_class (chat/code/agent/vision/audio/rag/creative), difficulty (0.0–1.0), code_keyword_density, reasoning_cue_count, log_prompt_tokens, tool_count


Sesión

agent_state.turn, agent_state.tools_used, agent_state.has_edited, agent_state.last_test_failed, agent_state.consecutive_errors, agent_state.models_tried


Contexto

headers["x-…"], user.group, token.name, time.hour, workspace.id
…plus six macros for the things regex-over-payload is good at: system_prompt_matches(re), user_message_matches(re), tool_definitions_include(name), tool_calls_present_any([…]), tool_results_from_any([…]), header_matches(name, re).


Cualquier destino puede llevar ajustes por llamada, traducidos a los parámetros nativos de cada proveedor por el adaptador de relay: reasoning_effort (low/medium/high), thinking_budget_tokens (1024–64000), samples (1–16), temperature (0.0–2.0), además de param_override / header_override protegidos por lista de denegación. Eso ya es suficiente para construir el endpoint enrutado por dificultad de la Tabla A: modelo barato en la cola fácil, Opus con un presupuesto de razonamiento en la difícil.


Primitiva 2 — distribuir a un panel (fusión)

Aquí es de donde viene la mejora en el benchmark. Un paralelismo: effect envía la solicitud a 2–5 legs de forma concurrente, y luego un árbitro decide lo que el cliente realmente ve:

- id: hard_tail_panel
  when: difficulty > 0.7 && task_class == "agent"
  use:
    parallel:
      - { model: "anthropic/claude-opus-4-8", reasoning_effort: "high" }
      - { model: "openai/gpt-5.5", thinking_budget_tokens: 16000 }
      - { model: "google/gemini-3.1-pro", temperature: 0.3 }
    arbiter:
      strategy: best_of_n
      model: "anthropic/claude-sonnet-4-6"      # the judge
      template: judge_code
    max_latency_ms: 120000
    on_disagreement:                  # majority-only escape hatch
      model: "anthropic/claude-opus-4-8"
      reasoning_effort: "high"


Cuatro estrategias de árbitro, cada una una respuesta distinta a «¿la salida de quién gana?»:

primero — compite entre las ramas, sirve el primer éxito, cancela los perdedores. Optimiza la latencia (obtienes la más rápida de N).

mayoría — voto estructurado entre las salidas de las ramas, sin una llamada adicional al modelo. Cuando las ramas se dividen sin una mayoría estricta, la rama opcional on_disagreement: vuelve a despachar un intento nuevo y más fuerte en lugar de resolver con un desempate. Optimiza la robustez en tareas con una respuesta canónica.

best_of_n — un juez LLM lee todos los candidatos y los clasifica. Esta es la configuración Opus + GPT-5.5 → juez de la Tabla B. Optimiza la calidad en trabajos abiertos; recurre al primer resultado exitoso si el juez falla.

tests_pass — execution-grounded: entrega el candidato cuyo parche realmente hace pasar la suite de tests. Sin conjeturas del juez: el arnés decide. Es el árbitro más sólido para el trabajo de código/agentes. El verificador vive fuera del gateway (conectado mediante un VerifierProvider); si no hay ninguno conectado, se degrada a first-successful.

max_latency_ms (1000–600000, predeterminado 120000) limita el fan-out para que una sola rama lenta no pueda bloquear la respuesta — los rezagados se descartan. Anidar parallel dentro de parallel se rechaza en el lint; el panel es intencionalmente de un solo nivel de profundidad.

Nota de disponibilidad: el runtime de fan-out en N vías está condicionado por el flag de servidor ROUTING_DSL_ENSEMBLE_RUNTIME mientras que la facturación por tramo se está reforzando en staging — por eso la fusión está en versión preliminar, no en disponibilidad general. Con el flag desactivado, una regla parallel: sirve su primer tramo sin problemas, así que puedes crear y poner en sombra tus paneles hoy y activarlos cuando la fusión llegue a tu región.


Primitivo 3 — respaldos y cascadas de confianza

Fan-out gasta N× por adelantado. Una cascada gasta extra solo cuando la primera respuesta parece incorrecta. Después de la respuesta, on_low_confidence: evalúa las señales y, si una se activa, reenvía a un destino más fuerte:

- id: agent_with_safety_net
  when: task_class == "agent"
  use:
    pool: "@pool:fast"
  on_low_confidence:
    signals: [patch_invalid, self_doubt, next_turn_test_failed]
    threshold: { low_logprob: -1.5 }
    use:
      model: "claude-opus-4-8"
      reasoning_effort: "high"


Las señales: patch_invalid (el diff falla en git apply --check), self_doubt (un conjunto de expresiones regulares de frases dubitativas), low_logprob (logprob medio de los tokens por debajo del umbral, cuando el proveedor lo expone) y next_turn_test_failed (un latch entre turnos: el prompt de este turno lleva la forma de las pruebas fallidas del turno anterior). Las cascadas son de profundidad 1 por diseño. Combínalas con agent_state.models_tried para obtener diversidad al reintentar — nunca envíes la reparación al modelo que acaba de fallar.


Ajustando el dial: costo, latencia, calidad

El mismo DSL expresa los tres objetivos; tú eliges por regla:

Coste — delegar: lo más barato, mantén el modelo barato en la cola fácil y reserva el fan-out para dificultad > 0,7. El panel barato de la tabla B (~64,5 % ≈ Fable 5 en solitario) es la prueba de su existencia: una fusión de modelos pequeños puede sustituir a un modelo de frontera a una fracción del coste por token. No obstante, conviene ser realista: la fusión usa el "bill every leg" modelo: un panel best_of_n de 3 patas factura tres candidatos más el juez. La economía funciona porque (a) solo haces fan-out en la minoría difícil de solicitudes y (b) fusionas miembros más baratos que el modelo de frontera al que reemplazas.

Latencia — árbitro: { strategy: first } más un max_latency_ms ajustado te da el más rápido de N con un techo estricto.

Calidad — best_of_n para trabajo abierto, tests_pass cuando hay una suite en la que basarse. samples y thinking_budget_tokens compran más dentro de un solo tramo.


Operarlo sin romper producción

Los cambios de enrutamiento dan miedo, así que el DSL viene con las barreras de seguridad que un SRE espera:

Lint en cada guardado — esquema, verificación de tipos CEL (cada when: debe evaluar a bool), resolución de ref, rangos de los controles, listas de denegación de encabezados/parámetros. Los errores se devuelven como {line, column, message, rule} y se muestran como chips en el margen del editor.

Simulación — Envía una solicitud sintética mediante POST (task_class, difficulty, agent_state, …) y recibe la regla coincidente, el efecto resuelto y el tiempo de evaluación antes de que se despliegue nada.

Modo sombra — durante 24 h tras el primer guardado, el DSL se evalúa pero no se usa; un registro en sombra recoge las selecciones que se habrían hecho y la consola muestra un diff (porcentaje de rutas modificadas, delta de coste diario proyectado, recuento de activaciones por regla).

Canary — un control deslizante de tráfico de 0 a 100. Sube de 5 → 25 → 50 → 100 observando las métricas por segmento; revierte deslizando hasta 0.

Auditoría + reversión — cada guardado/reversión escribe una fila de auditoría en la misma transacción; las ediciones simultáneas reciben un 409 con la versión actual, de modo que reintentas con un estado actualizado.

Los casos de prueba, la reproducción de trazas y una vista de IA para «explicar este conjunto de reglas» lo completan. Lo encontrarás en el panel, en enrutamiento → estrategia → DSL.


Un conjunto de reglas completo

Barato en los fáciles, medio en los medios, un panel de fusión evaluado por jueces en la cola agéntica difícil, con una cascada de confianza por debajo:

versión: 1

rules:
  - id: trivial
    when: difficulty < 0.3 && !has_tools
    use: { model: "gemini-3-flash" }


  - id: standard
    when: difficulty < 0.7
    use:
      model: "gpt-5.5"
    on_low_confidence:
      signals: [self_doubt, low_logprob]
      use: { model: "claude-opus-4-8", reasoning_effort: "high" }


  - id: hard_agent_panel
    when: difficulty >= 0.7 && task_class == "agent"
    use:
      parallel:
        - { model: "anthropic/claude-opus-4-8", reasoning_effort: "high" }
        - { model: "openai/gpt-5.5", thinking_budget_tokens: 16000 }
        - { model: "google/gemini-3.1-pro" }
      arbiter:
        strategy: tests_pass        # execution-grounded; judged fallback if no harness
      max_latency_ms: 180000
      on_disagreement:
        model: "claude-opus-4-8"
        reasoning_effort: "high"


default:
  delegate: balanced


Ese endpoint es el que ocupa el primer puesto de la Tabla A — no porque haya encontrado un modelo mejor, sino porque emplea el modelo adecuado en la solicitud adecuada y fusiona un panel exactamente donde el panel gana.


Empezar a redactar

El siguiente salto de capacidad no tiene que esperar al siguiente checkpoint. Es un grafo que puedes escribir esta tarde: enruta según la dificultad, abre en abanico sobre la cola difícil, juzga o prueba las salidas, y aplica cascada cuando la confianza baje.

Documentación: https://docs.orcarouter.ai/routing/routing-dsl

UI: enrutamiento → Crear enrutador -> Estrategia de enrutamiento → DSL (experto)

La frontera es un panel. Ve y construye el tuyo.