Ir para o conteúdo principal
Engenharia

Desenvolvido para Agentes: O Caso dos Spark Declarative Pipelines

Testamos 528 data pipelines criados por agentes - a criação declarativa se mostrou mais barata, rápida e enxuta do que o código procedural.

por Lisa Cao e Sudhanva Huruli

  • Pipelines procedurais gastaram cerca de 34 vezes mais tempo de computação do que pipelines declarativos, principalmente porque erros em pipelines declarativos estruturalmente inválidos são detectados por um "teste a seco" ("dry run") barato que não os executa totalmente.
  • Pipelines declarativos contêm 51% menos código para o mesmo pipeline, portanto há muito menos para ler e revisar.
  • Juntas, essas eficiências tornam os frameworks declarativos como SDP a escolha arquitetônica óbvia para agentes autônomos que constroem pipelines de produção.

Os agentes de IA agora escrevem uma parcela crescente de data pipelines de produção (mais de 70% dos pipelines SDP no Databricks agora são criados por agentes), o que aumenta a importância em duas áreas: (1) cada tentativa de um agente consome computação e tempo, então as iterações precisam ser baratas e eficientes para executar; e (2) a revisão de código se torna uma barreira crítica, portanto o código precisa ser conciso, legível e correto. O Apache Spark® oferece dois modelos de programação para criar seus pipelines, procedural e declarativo, e eles diferem no que você escreve, no que o Spark pode verificar antes de executar e em quanto custa um erro. Fizemos benchmarks de ambos com o mesmo agente nos mesmos jobs, e o declarativo se mostrou mais seguro, mais barato e mais enxuto.

Um pipeline procedural é um código que diz ao Spark como construir cada dataset, passo a passo. A única maneira de saber se funciona é executando-o.

Um pipeline declarativo descreve os datasets que você deseja e deixa que o Spark decida como construí-los. Isso é semelhante a quando você faz um prompt para um LLM, e ele descobre como fazer isso (usando as habilidades e ferramentas certas). Isso é melhor ilustrado no código para ver como um simples erro de digitação falhará:

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. Pipelines declarativos podem ser validados antes de serem executados

Os bugs vêm em dois tipos. Os estruturais você pode detectar lendo o pipeline: uma referência de coluna não resolvida, uma tabela que ainda não existe, uma alteração de configuração que não é permitida. Os silenciosos aparecem apenas na saída quando a execução termina e a resposta está incorreta.

Um pipeline declarativo pode ser verificado quanto a bugs estruturais antes de ser executado, de forma semelhante a como o fluxo de dados em uma linguagem com tipagem como Rust pode ser verificado em tempo de compilação. Como um pipeline declarativo é uma descrição e não uma execução, o Spark pode responder a três tipos de perguntas ao lê-lo:

  • Cada referência de coluna é resolvida com base no esquema do dataset de onde faz a leitura, portanto, uma coluna que não existe resulta em uma rejeição em vez de uma falha em tempo de execução.
  • Cada dependência entre datasets é correspondida a um produtor no grafo, portanto, uma referência a uma tabela que nada declara é detectada antes que o pipeline inicie.
  • O grafo é verificado quanto à integridade e à ordenação, permitindo que o mecanismo determine o que é executado e quando, em vez de confiar na ordem em que o modelo por acaso escreveu as coisas.

2. Pipelines declarativos descarregam tarefas repetitivas para o Spark

No PySpark procedural, o agente de codificação de IA escreve as operações de dados, como filtrar linhas inválidas e fazer join de tabelas. Ele também escreve código para executar as etapas do pipeline na ordem correta e salvar seus resultados. Esse código de suporte repete grande parte da mesma estrutura padrão em vários pipelines.

Em pipelines declarativos, o agente de IA define cada dataset e a query que o produz. O Spark gerencia a ordem de execução e salva os resultados. Com o Spark lidando com essa rotina, o agente escreve menos código, e a equipe tem menos trabalho para revisar e manter.

O que testamos

Testamos 22 tarefas de engenharia de dados. Cada tarefa parecia um ticket real: descrevia um problema e especificava a tabela, os nomes das colunas e os tipos de dados que o pipeline precisava produzir. O agente podia escolher qualquer implementação, desde que a saída atendesse a esses requisitos.

Para cada tarefa, geramos 12 datasets de entrada diferentes com os tipos de valores confusos encontrados em dados de produção. O agente escreveu um novo pipeline para cada dataset usando PySpark procedural e outro usando Spark Declarative Pipelines. Isso resultou em 264 builds por abordagem e 528 no total: 22 tarefas × 12 datasets × 2 abordagens. Todos os builds usaram o Opus 4.8.

Executamos cada pipeline e verificamos sua saída em relação à tabela necessária. O avaliador automatizado não sabia qual abordagem havia produzido o resultado. Os pipelines declarativos passaram primeiro pela execução de teste do Spark. Se um build falhasse, o agente podia revisá-lo e tentar novamente, até um limite fixo.

Você mesmo pode executar tudo novamente a partir do repositório público: a configuração, os prompts, os datasets, o avaliador e os resultados por trás de cada número.

Resultados

Ambas as formas concluíram quase todas as tarefas, com 255 de 264 concluídas proceduralmente e 258 de 264 concluídas de forma declarativa, portanto, qualquer uma delas leva a um pipeline funcional. O que muda é tudo ao redor disso: quando os erros surgiram, quanto código foi necessário para consertar e quão consistente foi a saída. Abordamos as três vantagens dos pipelines declarativos abaixo, seguidas de duas ressalvas.

1. Validar antes da execução tornou os pipelines declarativos cerca de 34 vezes mais rápidos

Os erros de um pipeline surgem antes da execução, durante a execução ou nem surgem. Os pipelines procedural não impediram nenhum erro antes da execução porque não possuem um estágio pré-execução. Para encontrar um erro estrutural, o Spark teve que executar o pipeline em um cluster até atingir a etapa com falha.

Nos builds que medimos, as execuções procedurais usaram 596 executor-seconds, em comparação com 17 para execuções declarativas. A validação por dry-run de código declarativo detectou erros estruturais antecipadamente e reduziu a computação em 34 vezes.

Pipelines Procedurais

2. O código do pipeline declarativo teve metade do tamanho dos pipelines procedurais

Entre os builds concluídos, os pipelines declarativos tiveram cerca de metade do tamanho, com uma mediana de 64 linhas em comparação com 131 para os pipelines procedurais.

As implementações procedurais também variaram mais. Para o mesmo job, algumas usaram transformações do Spark, enquanto outras carregaram os dados na memória e os processaram linha por linha. Ambas passaram nos dados de teste, mas a versão em memória pode falhar quando os dados crescem. Em todo o benchmark, 19 builds procedurais usaram esse atalho. Nenhum dos builds declarativos usou.

Um código mais curto e consistente dá à equipe menos itens para revisar e apenas um padrão para manter.

Código de Pipeline Declarativo

3. Em erros de lógica, as duas abordagens empataram

Essas são as duas vantagens. Ao executar os testes, também identificamos duas ressalvas.

Primeiro, um dry-run só consegue detectar erros estruturais. Ele não consegue detectar um erro silencioso: um pipeline que é executado sem erros e retorna a resposta errada, que é o tipo mais perigoso. Nesse ponto, as duas empataram: suas taxas de erros silenciosos ficaram dentro de aproximadamente 15% uma da outra, uma diferença muito pequena para ser considerada real. O formato declarativo torna os erros estruturais baratos de detectar; ele não faz nada em relação à lógica, que ainda precisa de testes e revisão.

4. O declarativo gasta mais no início para economizar mais depois

O declarativo gasta mais em um aspecto. O agente faz iterações no dry-run, tentando novamente até que o pipeline esteja correto, portanto, ele gasta mais de seu raciocínio em tokens antecipadamente em vez de descobrir problemas em um cluster mais tarde. Os tokens são um gasto fixo, único, pago enquanto o pipeline está sendo escrito; o valor não muda com base no tamanho dos dados ou na frequência com que o pipeline é executado. A computação desperdiçada no cluster é o oposto: ela se repete a cada execução com falha e cresce com os dados, portanto, quanto mais você processa, mais custa um build com falha. Pagar um custo fixo antecipadamente para evitar um que continua crescendo só parece melhor à medida que seus pipelines e dados aumentam.

Tudo o que medimos, lado a lado

O conjunto completo de diferenças medidas é mostrado abaixo.

DimensãoPySpark ProceduralDeclarativo (SDP)
O que o agente produzCódigo que é executadoUma descrição que é verificada
Erros estruturaisAparecem no meio da execução, após gravações parciais ou nunca; nenhum é interrompido antes da saídaDetectados no dry-run; 55 de 264 interrompidos antes de qualquer saída
Custo

Tempo de cluster: ~34x maior (até ~521 executor-seconds em uma execução com falha)

Tokens de LLM: linha de base

Tempo de cluster: linha de base (~0,5 executor-seconds em uma execução com falha)

Tokens de LLM: ~2,3x (mediana de build ≈ US$ 0,50 vs US$ 0,26, tarifas de tabela do Opus 4.8)

Tamanho do códigolinha de base

Linhas de código: ~51% a menos (mediana)

Tamanho da AST: ~44% menor

Bugs silenciosos (executados sem erros, resposta errada)73 de 264 (27,7%)85 de 264 (32,2%)

Conclusão

Em escala de produção, a abordagem declarativa leva vantagem. Ela custa cerca de 2,3 vezes mais em tokens de LLM e, em troca, nunca gasta tempo de cluster em um pipeline que não funcionaria. Em escala de produção, a abordagem declarativa se mostra vantajosa ao trocar custos ligeiramente maiores de tokens de LLM pela prevenção garantida de tempo de cluster desperdiçado em pipelines com falha. Isso se torna cada vez mais econômico conforme os volumes de dados aumentam, já que o desperdício de computação cresce proporcionalmente, enquanto os custos de tokens permanecem constantes.

Se você está avaliando agentes para o trabalho de pipeline, leve a forma como o agente escreve tão a sério quanto o agente que você escolhe. Um agente melhor comete menos erros; um pipeline declarativo torna os erros baratos e os detecta antes que custem alguma coisa.

O repositório documenta o design, os dados e a procedência completa. Para começar a usar seus agentes com Spark Declarative Pipelines, consulte o guia de programação oficial e use a habilidade SDP com PySpark.

FAQ

Eu preciso confiar no agente de IA para que isso funcione?

Não, e não precisar é o objetivo. O agente declarativo apenas escreve código de pipeline declarativo — uma descrição das tabelas que ele deseja —, e o próprio Spark verifica essa descrição antes que qualquer coisa seja executada. O verificador faz parte da plataforma: você está confiando na avaliação do pipeline feita pelo Spark, e não no modelo. O agente nunca executa código nem lê seus dados diretamente.

Isso se refere apenas aos Spark Declarative Pipelines, ou se aplica de forma mais ampla?

Isso se aplica de forma mais ampla a abordagens declarativas: em qualquer lugar onde você possa separar "aqui está o que eu quero" de "agora vá e construa", para que a primeira parte possa ser verificada por conta própria. Você vê o mesmo formato no próprio SQL, em ferramentas de infraestrutura como código e em APIs focadas em esquema. O Spark Declarative Pipelines é o que usamos no benchmark, mas o raciocínio é válido para qualquer ferramenta construída dessa forma.

O agente declarativo usa 2,3x mais tokens. Isso não é um problema eliminatório?

Não em tamanhos de pipeline realistas. Os 2,3x representam aproximadamente US$ 0,50 em tokens por build declarativo mediano, versus US$ 0,26 para o procedural, com as taxas de tabela do Opus 4.8. Uma execução procedural com falha consome cerca de 521 executor-seconds, enquanto uma declarativa rejeitada custou cerca de meio segundo — portanto, evitar algumas execuções ruins compensa muitos tokens extras, e a diferença só aumenta à medida que os volumes de dados crescem. Os preços dos tokens também mudam dependendo do serviço de modelo que você usa, enquanto os custos de computação são mais estáveis e fáceis de planejar.

(Esta publicação no blog foi traduzida utilizando ferramentas baseadas em inteligência artificial) Publicação original

Tags relacionadas:

Data Engineering e Código aberto

Receba os posts mais recentes na sua caixa de entrada

Assine nosso blog e receba os posts mais recentes diretamente na sua caixa de entrada.