Ir al contenido principal
Ingeniería

Diseñado para agentes: El caso de las canalizaciones declarativas de Spark

Evaluamos 528 canalizaciones de datos creadas por agentes; la creación declarativa demostró ser más económica, rápida y ligera que el código procedimental.

por Lisa Cao y Sudhanva Huruli

  • Las canalizaciones procedimentales consumieron aproximadamente 34 veces más tiempo de cómputo que las canalizaciones declarativas, principalmente porque los errores en las canalizaciones declarativas estructuralmente inválidas se detectan mediante una "prueba en seco" económica que no las ejecuta por completo.
  • Las canalizaciones declarativas contienen un 51% menos de código para la misma canalización, por lo que hay mucho menos que leer y revisar.
  • Juntas, estas eficiencias hacen que los marcos declarativos como SDP sean la opción arquitectónica clara para los agentes autónomos que crean canalizaciones de producción.

Los agentes de IA ahora escriben una porción cada vez mayor de los pipelines de datos de producción (más del 70 % de los pipelines de SDP en Databricks ahora son creados por agentes), lo que eleva las apuestas en dos áreas: (1) cada intento que hace un agente cuesta capacidad de cómputo y tiempo, por lo que las iteraciones deben ser económicas y eficientes de ejecutar; y (2) la revisión de código se convierte en un filtro crítico, por lo que el código debe ser sucinto, legible y correcto. Apache Spark® ofrece dos modelos de programación para crear tus pipelines, procedural y declarativo, y difieren en lo que escribes, en lo que Spark puede verificar antes de ejecutar y en cuánto cuesta un error. Evaluamos ambos con el mismo agente en los mismos trabajos, y el enfoque declarativo resultó ser más seguro, económico y eficiente.

Un pipeline procedural es código que le indica a Spark cómo construir cada conjunto de datos, paso a paso. La única manera de saber si funciona es ejecutarlo.

Un pipeline declarativo describe los conjuntos de datos que deseas y deja que Spark decida cómo construirlos. Esto es similar a cuando le das un prompt a un LLM y este descubre cómo hacerlo (utilizando las habilidades y herramientas adecuadas). Esto se ilustra mejor en el código para ver cómo un simple error tipográfico fallará:

ProceduralDeclarativo
bronze = spark.read.parquet(RAW)
bronze.write.saveAsTable("bronze")
silver = 
bronze.withColumnRenamed("revenue", 
"revenues")  # typo
silver.write.saveAsTable("silver")
gold = 
spark.read.table("silver").groupBy("revenue")
.sum("revenue")
gold.write.saveAsTable("gold")  
# runtime error
@dp.materialized_view(name="gold")
def gold():
    return 
spark.read.table("silver").groupBy("revenue")
.sum("revenue")

1. Los pipelines declarativos se pueden validar antes de ejecutarse

Los errores vienen en dos presentaciones. Los estructurales se pueden detectar leyendo el pipeline: una referencia de columna no resuelta, una tabla que aún no existe, un cambio de configuración que no está permitido. Los silenciosos solo aparecen en la salida cuando finaliza la ejecución y la respuesta es incorrecta.

Un pipeline declarativo se puede verificar en busca de errores estructurales antes de ejecutarse, de manera similar a cómo se puede verificar el flujo de datos en un lenguaje tipado como Rust en tiempo de compilación. Debido a que un pipeline declarativo es una descripción en lugar de una ejecución, Spark puede responder tres tipos de preguntas leyéndolo:

  • Cada referencia de columna se resuelve con el esquema del conjunto de datos del que lee, por lo que una columna que no existe es un rechazo en lugar de un fallo en tiempo de ejecución.
  • Cada dependencia entre conjuntos de datos se empareja con un productor en el gráfico, por lo que una referencia a una tabla que nada declara se detecta antes de que comience el pipeline.
  • Se verifica la integridad y el orden del gráfico, por lo que el motor determina qué se ejecuta y cuándo, en lugar de confiar en el orden en que el modelo escribió las cosas por casualidad.

2. Los pipelines declarativos descargan las tareas repetitivas en Spark

En PySpark procedural, el agente de codificación de IA escribe las operaciones de datos, como filtrar filas no válidas y unir tablas. También escribe código para ejecutar los pasos del pipeline en el orden correcto y guardar sus resultados. Este código de soporte repite gran parte de la misma estructura repetitiva en los pipelines.

En los pipelines declarativos, el agente de IA define cada conjunto de datos y la consulta que lo produce. Spark maneja el orden de ejecución y guarda los resultados. Con Spark encargándose de esa estructura repetitiva, el agente escribe menos código y el equipo tiene menos que revisar y mantener.

Lo que probamos

Probamos 22 tareas de ingeniería de datos. Cada tarea parecía un ticket real: describía un problema y especificaba la tabla, los nombres de las columnas y los tipos de datos que el pipeline tenía que producir. El agente podía elegir cualquier implementación siempre que la salida cumpliera con esos requisitos.

Para cada tarea, generamos 12 conjuntos de datos de entrada diferentes con los tipos de valores desordenados que se encuentran en los datos de producción. El agente escribió un nuevo pipeline para cada conjunto de datos usando PySpark procedural y otro usando Spark Declarative Pipelines. Eso produjo 264 compilaciones por enfoque y 528 en total: 22 tareas × 12 conjuntos de datos × 2 enfoques. Todas las compilaciones usaron Opus 4.8.

Ejecutamos cada pipeline y verificamos su salida con la tabla requerida. El evaluador automatizado no sabía qué enfoque había producido el resultado. Los pipelines declarativos pasaron primero por la ejecución de prueba (dry run) de Spark. Si una compilación fallaba, el agente podía revisarla y volver a intentarlo, hasta un límite fijo.

Puedes volver a ejecutar todo tú mismo desde el repositorio público: la configuración, los prompts, los conjuntos de datos, el evaluador y los resultados detrás de cada número.

Resultados

Ambos métodos completaron casi todas las tareas, con 255 de 264 completadas de forma procedural y 258 de 264 completadas de forma declarativa, por lo que cualquiera de los dos te lleva a un pipeline funcional. Lo que difiere es todo lo que lo rodea: cuándo surgieron los errores, cuánto código tomó solucionar y cuán consistente fue la salida. Cubrimos las tres ventajas de los pipelines declarativos a continuación, seguidas de dos advertencias.

1. Validar antes de la ejecución hizo que los pipelines declarativos tomaran ~34 veces menos tiempo

Los errores de un pipeline surgen antes de la ejecución, durante una ejecución o nunca. Los pipelines procedurales no detuvieron ninguno antes de la ejecución porque no tienen una etapa previa a la misma. Para encontrar un error estructural, Spark tuvo que ejecutar el pipeline en un clúster hasta llegar al paso roto.

En todas las compilaciones que medimos, las ejecuciones procedurales usaron 596 segundos de ejecutor, en comparación con 17 para las ejecuciones declarativas. La validación de ejecución de prueba del código declarativo detectó errores estructurales temprano y redujo el cómputo en 34X.

Pipelines procedurales

2. El código de los pipelines declarativos tuvo la mitad del tamaño de los procedurales

Entre las compilaciones completadas, los pipelines declarativos tuvieron aproximadamente la mitad del tamaño, con una mediana de 64 líneas en comparación con 131 para los pipelines procedurales.

Las implementaciones procedurales también variaron más. Para el mismo trabajo, algunas usaron transformaciones de Spark mientras que otras cargaron los datos en la memoria y los procesaron fila por fila. Ambas pasaron en los datos de prueba, pero la versión en memoria puede fallar cuando los datos crecen. En la prueba comparativa, 19 compilaciones procedurales usaron este atajo. Ninguna de las compilaciones declarativas lo hizo.

Un código más corto y consistente le da al equipo menos cosas que revisar y un solo patrón que mantener.

Código de pipeline declarativo

3. En cuanto a errores lógicos, ambos quedaron empatados

Esas son las dos ventajas. Al ejecutar las pruebas, también identificamos dos advertencias.

Primero, una ejecución de prueba solo puede detectar errores estructurales. No puede detectar uno silencioso: un pipeline que se ejecuta limpiamente y devuelve la respuesta incorrecta, que es el tipo más peligroso. En ese aspecto, ambos quedaron empatados: sus tasas de errores silenciosos estuvieron dentro de aproximadamente un 15 % entre sí, una brecha demasiado pequeña para ser una diferencia real. Lo declarativo hace que los errores estructurales sean económicos de detectar; no hace nada por la lógica, que aún necesita pruebas y revisión.

4. Lo declarativo gasta más al principio para ahorrar más después

Lo declarativo gasta más en un solo lugar. El agente itera en la ejecución de prueba, reintentando hasta que el pipeline sea sólido, por lo que hace una mayor parte de su razonamiento en tokens por adelantado en lugar de descubrir problemas en un clúster más tarde. Los tokens son un gasto fijo de una sola vez, que se paga mientras se escribe el pipeline; la cantidad no cambia según el tamaño de tus datos o la frecuencia con la que se ejecuta el pipeline. El cómputo desperdiciado en el clúster es lo opuesto: se repite en cada ejecución fallida y crece con los datos, por lo que cuanto más procesas, más cuesta una mala compilación. Pagar un costo fijo por adelantado para evitar uno que sigue creciendo solo se ve mejor a medida que tus pipelines y datos crecen.

Todo lo que medimos, lado a lado

El conjunto completo de diferencias medidas se muestra a continuación.

DimensiónPySpark proceduralDeclarativo (SDP)
Lo que produce el agenteCódigo que se ejecutaUna descripción que se verifica
Errores estructuralesSurgen a mitad de la ejecución, después de escrituras parciales o nunca; ninguno se detiene antes de la salidaSe detectan en la ejecución de prueba; 55 de 264 se detienen antes de cualquier salida
Costo

Tiempo de clúster: ~34x mayor (hasta ~521 segundos de ejecutor en una ejecución fallida)

Tokens de LLM: base

Tiempo de clúster: base (~0.5 segundos de ejecutor en una ejecución fallida)

Tokens de LLM: ~2.3x (compilación mediana ≈ $0.50 frente a $0.26, tarifas de lista de Opus 4.8)

Tamaño del códigobase

Líneas de código: ~51% menos (mediana)

Tamaño del AST: ~44% más pequeño

Bugs silenciosos (se ejecutaron limpios, respuesta incorrecta)73 de 264 (27.7%)85 de 264 (32.2%)

Conclusión

A escala de producción, el enfoque declarativo resulta ser la mejor opción. Cuesta aproximadamente 2.3 veces más en tokens de LLM, y a cambio, nunca gasta tiempo de clúster en una canalización que no iba a funcionar. A escala de producción, el enfoque declarativo demuestra ser ventajoso al cambiar costos ligeramente mayores de tokens de LLM por la prevención garantizada de tiempo de clúster desperdiciado en canalizaciones fallidas. Esto se vuelve cada vez más rentable a medida que los volúmenes de datos aumentan, ya que el desperdicio de cómputo crece proporcionalmente mientras que los costos de los tokens se mantienn constantes.

Si estás evaluando agentes para el trabajo de canalizaciones, toma tan en serio la forma en que escribe el agente como el agente que eliges. Un mejor agente comete menos errores; una canalización declarativa hace que los errores sean baratos y los detecta antes de que cuesten algo.

El repositorio documenta el diseño, los datos y la procedencia completa. Para empezar con tus agentes usando Spark Declarative Pipelines, consulta la guía de programación oficial y usa la habilidad de SDP con PySpark.

FAQ

¿Tengo que confiar en el agente de IA para que esto funcione?

No, y no tener que hacerlo es la clave. El agente declarativo solo escribe código de canalización declarativa —una descripción de las tablas que quiere— y el propio Spark verifica esa descripción antes de que se ejecute cualquier cosa. El verificador es parte de la plataforma: estás confiando en la evaluación de la canalización por parte de Spark, no en el modelo. El agente nunca ejecuta código ni lee tus datos por sí mismo.

¿Esto es solo sobre Spark Declarative Pipelines, o se aplica de manera más general?

Se aplica de manera más general a los enfoques declarativos: en cualquier lugar donde puedas separar "esto es lo que quiero" de "ve y constrúyelo", para que la primera parte se pueda verificar por sí sola. Ves la misma estructura en SQL, en herramientas de infraestructura como código y en APIs basadas en esquemas. Spark Declarative Pipelines es la que probamos en nuestros benchmarks, pero el razonamiento se aplica a cualquier herramienta construida de esa manera.

El agente declarativo usa 2.3 veces más tokens. ¿No es eso un problema insalvable?

No con tamaños de canalización realistas. El factor de 2.3 se traduce en aproximadamente $0.50 en tokens por compilación declarativa mediana, frente a $0.26 para la procedimental, según las tarifas de lista de Opus 4.8. Una ejecución procedimental fallida consumió unos 521 executor-seconds, mientras que una declarativa rechazada costó aproximadamente medio segundo; por lo que evitar unas cuantas ejecuciones malas compensa muchos tokens adicionales, y la brecha solo se amplía a medida que crecen los volúmenes de datos. Los precios de los tokens también varían según el servicio de modelos que uses, mientras que los costos de cómputo son más estables y fáciles de planificar.

(Esta entrada del blog ha sido traducida utilizando herramientas basadas en inteligencia artificial) Publicación original

Etiquetas relacionadas:

Ingeniería de datos y Código abierto

Recibe las últimas publicaciones en tu bandeja de entrada

Suscríbete a nuestro blog y recibe las últimas publicaciones directamente en tu bandeja de entrada.