Routing DSL: componga un panel de modelos que piensa como Fable 5

Routing DSL: componga un panel de modelos que piensa como Fable 5

Fecha de publicación

Volver a todas las publicaciones

Durante dos años, el manual para "más inteligencia" ha sido "esperar al próximo modelo". Creemos que esa es la unidad de progreso equivocada. La frontera no es un único punto de control — es un panel. Da a tres buenos modelos el mismo problema difícil, déjalos discrepar y arbitra entre las respuestas, y el panel supera a cualquiera de sus miembros. A menudo supera al próximo modelo en la tabla de precios.

El Routing DSL es cómo se construye ese panel. Es una estrategia de enrutamiento programable — YAML + CEL — que convierte tu endpoint de OrcaRouter en un grafo de inferencia: enruta por dificultad, enruta por tarea, distribuye a varios modelos a la vez, juzga o vota sus salidas, retrocede cuando la confianza es baja, y ajusta todo para costo, latencia o calidad. Escribes reglas; el gateway las compila y ejecuta en cada solicitud en ~5 ms.

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


El primer resultado

Dos benchmarks ilustrativos. (Los números son ilustrativos — pretenden mostrar la forma del efecto, no para ser citados como puntuaciones oficiales.)

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


Paneles Fusion vs. modelos solo — puntuado en 93 de 100 tareas (de OpenRouter):


Tres cosas que vale la pena mirar:

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 solo (~58.5%) como a GPT-5.5 solo (~60%) por 7–9 puntos. El desacuerdo es señal; el arbitraje lo cosecha.

Fusion alcanza el siguiente nivel. Tres paneles diferentes cruzan Fable 5 solo (~65.5%) usando solo los modelos debajo de él.

No necesitas membresías caras.Opus + Opus self-fusion (~65.5%) iguala a Fable 5 con un modelo y un muestreador. Un panel de baratos modelos — Gemini 3 Flash + Kimi K2.6 + DeepSeek V4 Pro (~64.5%) — se sitúa ligeramente por debajo de Fable 5 a una fracción del costo por token. Esa es toda la tesis: compra inteligencia con topología, no con el siguiente nivel de precio.

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


La gramática en 30 segundos

Un conjunto de reglas es una versión, una lista de reglas y un valor predeterminado requerido. Las reglas se evalúan de arriba a abajo; la primera when: que sea verdadera gana. No 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 — sandboxed, regex solo RE2, sin bucles, sin E/S, evaluación en microsegundos, con un único plazo de 5 ms compartido en todo el conjunto de reglas. El use: es el efecto: a dónde va la solicitud y cómo se ajusta. Los límites son deliberadamente pequeños (≤30 reglas, ≤16 KiB de fuente, ≤200 caracteres por when:) para que un conjunto de reglas sea auditable.


Primitiva 1 — ruta por dificultad y tarea

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

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 los documentos):

Ejemplos de grupo

Forma de 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 controles por llamada, traducidos a los parámetros nativos de cada proveedor por el adaptador de retransmisión: reasoning_effort (bajo/medio/alto), thinking_budget_tokens (1024–64000), samples (1–16), temperature (0.0–2.0), además de param_override / header_override protegidos por listas de denegación. Eso ya es suficiente para construir el endpoint con enrutamiento por dificultad de Table A: modelo barato en la cola fácil, Opus con un presupuesto de pensamiento en la difícil.


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

De aquí proviene el aumento del benchmark. Un efecto paralelo: envía la solicitud a 2–5 ramas simultáneamente, luego un árbitro decide lo que realmente ve el cliente:

- 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 diferente a "¿cuál salida gana?":

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

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

best_of_n — un LLM judge lee todos los candidatos y los clasifica. Esta es la configuración Opus + GPT-5.5 → judge de la Tabla B. Optimiza calidad en trabajos de final abierto; recurre a first-successful si el judge falla.

pruebas_pasanbasado en ejecución: sirve al candidato cuyo parche realmente hace pasar el conjunto de pruebas. Sin adivinación del juez — el arnés decide. Este es el árbitro más fuerte para el trabajo de código/agente. El verificador reside fuera de la puerta de enlace (conectado a través de un VerifierProvider); sin ninguno conectado, degrada al primero exitoso.

max_latency_ms (1000–600000, por defecto 120000) limita la expansión (fan-out) para que una rama lenta no pueda bloquear la respuesta — los rezagados se descartan. Anidar parallel dentro de parallel se rechaza en tiempo de análisis (lint); el panel tiene intencionadamente un solo nivel de profundidad.

Nota de disponibilidad: el tiempo de ejecución de fan-out de N vías está controlado por la bandera del servidor ROUTING_DSL_ENSEMBLE_RUNTIME mientras que la facturación por tramo está reforzada en staging — por eso fusion está en preview, no GA. Con la bandera desactivada, una regla parallel: sirve limpiamente su primer tramo, por lo que puede crear y sombrear sus paneles hoy y activarlos cuando fusion llegue a su región.


Primitiva 3 — alternativas y cascadas de confianza

Fan-out gasta N× por adelantado. Un cascada gasta extra solo cuando la primera respuesta parece incorrecta. Después de la respuesta, on_low_confidence: evalúa 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 regex de frases de duda), low_logprob (logprob medio de 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 en el reintento — 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:

Costo — delegado: el más barato, mantener el modelo barato en la cola fácil, y reservar el fan-out para dificultad > 0.7. El panel barato de la Tabla B (~64.5% ≈ Fable 5 solo) es la prueba de existencia: una fusión de modelos pequeños puede reemplazar un modelo fronterizo a una fracción del costo por token. Sin embargo, sea realista — la fusión utiliza el "bill every leg" modelo: un panel best_of_n de 3 piernas factura tres candidatos más el juez. La economía funciona porque (a) solo usas fan-out en la minoría difícil de las solicitudes y (b) fusionas más baratos miembros que el modelo fronterizo que estás reemplazando.

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

Calidad — best_of_n para trabajos abiertos, tests_pass cuando hay un conjunto de pruebas para basarse. samples y thinking_budget_tokens compran más dentro de una sola etapa.


Operarlo sin romper prod

Los cambios de enrutamiento dan miedo, por lo que el DSL viene con las medidas de seguridad que un SRE espera:

Lint en cada guardado — esquema, verificación de tipos CEL (todo when: debe evaluarse a bool), resolución de referencias, rangos de perillas, listas denegadas de encabezados/params. Los errores se devuelven como {línea, columna, mensaje, regla} y se muestran como marcas en el margen en el editor.

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

Modo sombra — durante 24 h después del primer guardado, el DSL se evalúa pero no se utiliza; un registro de sombra registra las selecciones potenciales y la consola muestra una diferencia (porcentaje de rutas cambiadas, delta de costo diario proyectado, recuentos de activaciones por regla).

Canary — un control deslizante de tráfico de 0 a 100. Aumente progresivamente de 5 a 25, luego a 50, luego a 100 mientras observa las métricas por segmento; vuelva atrás deslizando a 0.

Auditoría + reversión — cada guardado/reversión escribe una fila de auditoría en la misma transacción; las ediciones concurrentes reciben un 409 con la versión actual para que reintentes con el estado actualizado.

Casos de prueba, reproducción de trazas y una vista de IA "explicar este conjunto de reglas" lo completan. Lo encuentra en el panel bajo enrutamiento → estrategia → DSL.


Un conjunto de reglas completo

Barato en fácil, medio en medio, un panel de fusión juzgado en la cola agentiva difícil, con una cascada de confianza 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 se encuentra en la parte superior de la Tabla A — no porque encontró un mejor modelo, sino porque asigna el modelo correcto a la solicitud correcta y fusiona un panel exactamente donde el panel gana.


Empezar a redactar

El próximo salto en capacidad no tiene que esperar al próximo punto de control. Es un gráfico que puedes escribir esta tarde: enrutar por dificultad, abanico en la cola dura, juzgar o probar las salidas, en cascada cuando la confianza baja.

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

Interfaz de usuario: enrutamiento → Crear router -> Estrategia de enrutamiento → DSL (experto)

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