Como o DSPy otimiza um prompt?
Compilar, no DSPy, é procurar a melhor combinação de texto de instrução e exemplos para cada módulo do seu programa. O compilador propõe candidatos, roda o programa inteiro no seu conjunto de casos, pontua cada saída pela sua métrica e fica com o que pontuou melhor. Nenhum peso é alterado: o que muda é o texto que chega ao modelo.
A palavra “compilar” é uma analogia e cobra atenção. Um compilador de linguagem faz uma tradução determinística; este faz uma busca guiada por medição, e o resultado depende da amostra. Rodar duas vezes pode dar dois prompts diferentes, e os dois podem estar certos.
Este artigo é a mecânica por dentro do que o DSPy chama de compilação. Tudo aqui pressupõe um conjunto de avaliação com métrica automática, e a família mais recente de otimizadores, da qual o GEPA é o exemplo de 2025, muda a forma de propor candidatos sem mudar essa dependência.
O laço
O laço tem quatro passos e nenhum deles é sofisticado.
Propor. O otimizador monta candidatos. Um candidato é um par: o texto da instrução de um módulo e o conjunto de demonstrações que acompanha esse módulo. Em um programa de três módulos, o candidato é uma escolha por módulo, e o espaço cresce multiplicativamente.
Rodar. O programa inteiro é executado nos casos do conjunto com aquele candidato aplicado.
Pontuar. A sua função de métrica recebe o exemplo e a saída e devolve um número, ou um booleano. Ela é o único juízo de valor no processo.
Manter. Os candidatos com nota melhor sobrevivem e alimentam a proposta seguinte.
A seta de retorno é o que separa isso de amostragem aleatória. Nos otimizadores que propõem instrução, o modelo que gera a próxima proposta recebe as tentativas anteriores com as notas correspondentes. Essa ideia foi formulada em 2023 como usar o próprio modelo de linguagem como otimizador: você alimenta o modelo com as soluções já avaliadas e suas pontuações, e pede as próximas1.
De onde vêm os exemplos
O nome que aparece na documentação e nos tutoriais é bootstrap, e a mecânica explica o nome.
O otimizador roda o programa nos casos de treino, com a instrução inicial e sem demonstração nenhuma. Para cada caso, ele guarda o traço da execução: a entrada e a saída de cada módulo naquela passagem. Depois aplica a sua métrica ao resultado final e descarta os traços dos casos em que ela não passou.
O que sobra é um estoque de execuções que deram certo. Cada uma delas é uma demonstração pronta para qualquer módulo do programa, com entrada e saída no formato exato daquele módulo. É essa a peça que o paper de 2023 descreve quando diz que os módulos são parametrizados no sentido de aprenderem criando e colhendo demonstrações2.
Repare no que isso resolve. Escrever demonstração à mão para o módulo do meio de um pipeline é chato porque você precisa inventar a entrada intermediária, que nem existe até o programa rodar. O bootstrap não inventa nada: ele colhe o que apareceu.
E repare no que isso não resolve. Se a instrução inicial for ruim demais, quase nenhum caso passa, e o estoque de traços sai vazio. É o modo de falha mais comum de uma primeira compilação, e o sintoma é uma compilação rápida demais com resultado igual ao da linha de base.
De onde vem o texto da instrução
Colher exemplo é a metade barata. Propor instrução é a metade difícil, porque o espaço é aberto.
O trabalho de 2024 que definiu o padrão atual quebra o problema em duas partes que podem ser otimizadas separadamente, instrução e demonstração, e propõe três estratégias que valem entender3.
A primeira é propor instrução com consciência do programa e dos dados. Em vez de pedir a um modelo “melhore este prompt”, o proponente recebe o código do programa, uma amostra do conjunto e o histórico de tentativas, e escreve uma instrução com base nisso. Uma instrução ancorada na tarefa vence uma reescrita genérica.
A segunda é avaliar em minilote. Rodar o conjunto inteiro em cada candidato é caro, então o algoritmo avalia amostras e usa essas avaliações para aprender um modelo substituto do objetivo, que estima quais candidatos merecem uma avaliação completa. É a mesma lógica de otimização bayesiana aplicada a um espaço discreto de textos.
A terceira é meta-otimização: refinar ao longo do tempo a forma como o modelo constrói as propostas, e não só as propostas em si.
Junto com isso vem a atribuição de crédito. Você fornece uma métrica no fim do programa e não rótulo por módulo, então o algoritmo precisa decidir qual etapa merece a mudança. Os autores relatam até 13 pontos de exatidão acima das linhas de base em cinco de sete programas testados com Llama-3-8B3.
O tamanho do espaço de busca
Vale fazer a conta uma vez, porque ela explica por que o algoritmo é o que é.
Suponha um programa de três módulos. Para cada módulo, o otimizador tem dez instruções candidatas e oito conjuntos de demonstração candidatos. Isso dá oitenta combinações por módulo e, como a escolha é independente entre eles, 512 mil combinações no programa inteiro. Avaliar todas em 100 casos seriam 51,2 milhões de execuções do programa.
Nenhuma delas é avaliada. O algoritmo amostra um punhado de combinações, mede em minilote, ajusta a estimativa de onde vale gastar e concentra a avaliação completa nas poucas candidatas promissoras. Uma sessão típica avalia dezenas de combinações, não centenas de milhares.
Isso tem uma consequência que muda a expectativa: o resultado de uma compilação não é o ótimo, é uma amostra boa encontrada com orçamento limitado. Rodar de novo com outra semente pode devolver outro vencedor com nota parecida, e isso é esperado, não sinal de bug.
A métrica precisa ser barata
Uma decisão de projeto tem efeito desproporcional no custo, e é a escolha da métrica.
A métrica roda uma vez por caso, por candidato avaliado. Numa sessão com 30 candidatos e 100 casos, ela é chamada milhares de vezes. Se ela for uma verificação programática, o parse do JSON, a comparação de um campo, o teste que passa, o custo dela é zero e some da conta. Se ela for uma chamada a um modelo juiz, ela dobra o número de chamadas da sessão e passa a dominar o gasto.
Há um segundo efeito, menos óbvio e mais caro. Um juiz por modelo tem variância: o mesmo par de entrada e saída pode receber notas diferentes em execuções distintas. O otimizador não distingue variação da métrica de variação do candidato, então parte da busca vai perseguir ruído do juiz. Com métrica determinística esse problema desaparece.
Daí a regra prática: reduza a saída ao que puder ser verificado por código, e use o juiz só na parte que sobrar. Uma métrica binária por caso, passou ou não passou, costuma ser mais estável e mais barata que uma nota de zero a dez, e é o suficiente para ordenar candidatos.
Antes e depois
Vale imprimir o prompt final na primeira vez que você compilar, porque a comparação com o que você escreveu é instrutiva e às vezes desconfortável.
O prompt escrito à mão costuma ser uma instrução em prosa, com o formato descrito em texto e nenhum exemplo. O compilado costuma ser uma instrução mais seca, seguida de três a oito demonstrações completas, com os campos rotulados de forma rígida e repetitiva.
Ele não é mais bonito. Frequentemente é pior de ler. A única coisa que ele tem a mais é ter sido medido contra dezenas de alternativas no seu conjunto, e é essa a propriedade que você está comprando.
Sobre a magnitude do ganho, os números públicos vêm com escopo. No paper de 2023, programas compilados superaram few-shot padrão em geralmente mais de 25% com GPT-3.5 e mais de 65% com llama2-13b-chat, e superaram pipelines com demonstrações escritas por especialistas em faixas de 5% a 46% e 16% a 40%2. São dois estudos de caso, com aqueles modelos, naquelas tarefas. O que se transporta para você é o método, não a porcentagem.
As famílias de otimizador
Elas se diferenciam por como propõem candidatos, e a escolha muda bastante o custo.
Bootstrap de demonstrações. Só colhe traços que passaram e monta conjuntos de exemplos. Barato, sem proposta de instrução, e resolve mais casos do que parece. É por onde começar.
Proposta de instrução com busca. A família do trabalho de 2024: propõe instrução, seleciona demonstração, avalia em minilote e usa modelo substituto para decidir onde gastar avaliação completa3. É o padrão em julho de 2026.
Evolução reflexiva. Em vez de tratar a nota como um escalar, o otimizador lê os traços de execução em linguagem natural, diagnostica o que deu errado e propõe a correção, mantendo uma fronteira de Pareto das próprias tentativas. O GEPA, de 2025, reporta ficar mais de 10% acima do MIPROv2 e superar o GRPO em 6% na média, com até 35 vezes menos execuções4. A economia de execuções é o número que muda o cálculo de quando compilar vale a pena.
Otimização conjunta de peso e prompt. O compilador também gera dados de fine-tuning a partir das execuções. Um trabalho de 2024 mostra que alternar entre otimizar peso e otimizar prompt supera fazer só uma das duas, por até 60% e 6% em média, respectivamente, nos modelos e tarefas testados5.
Onde falha
Sobreajuste ao conjunto. Trinta candidatos avaliados nos mesmos cinquenta casos produzem um vencedor parcialmente sortudo. É o modo de falha mais provável e o mais fácil de evitar: separe o holdout antes de compilar e confirme nele depois.
A métrica vira o alvo. O otimizador vai explorar qualquer atalho que a sua métrica premie. Se ela compara texto por sobreposição de palavra, o vencedor vai ser verboso. Vale ler as saídas vencedoras, não só a nota.
O bootstrap não decola. Instrução inicial fraca produz poucos traços aprovados, e sem traços não há demonstração. A correção é melhorar a instrução inicial à mão antes de compilar, o que soa contraditório e não é: o compilador precisa de um ponto de partida que funcione às vezes.
O prompt cresce. Demonstrações ocupam contexto, e o custo por requisição em produção sobe junto. Um ganho de dois pontos de exatidão que dobra o prompt pode não valer a conta, e a decisão é de produto.
O artefato não se revisa em pull request. O prompt compilado é um blob gerado, e comparar a diferença entre duas compilações não diz nada útil a quem revisa. O que se revisa é o conjunto, a métrica e o número final. Tratar o artefato como código revisável gasta o tempo de quem lê e não pega erro nenhum.
O resultado não transfere. O artefato é ajustado ao modelo e à versão medidos. Guarde o conjunto, a métrica e a semente, porque recompilar é a operação que você vai repetir a cada troca de modelo.
Como conduzir uma compilação
- Meça a linha de base sem compilar. O programa com instrução simples e nenhum exemplo. Sem esse número, o resto não tem contra o que comparar.
- Separe o holdout antes de qualquer coisa. Vinte por cento, intocado até o fim.
- Comece pelo bootstrap. Barato e frequentemente suficiente. Só passe para proposta de instrução se o ganho parar.
- Limite o número de candidatos na primeira rodada. Poucos candidatos com conjunto honesto valem mais que muitos com conjunto pequeno.
- Imprima o prompt vencedor e leia. É o único jeito de perceber que a métrica premiou a coisa errada.
- Meça o tamanho do prompt junto com a exatidão. As duas grandezas decidem juntas.
- Versione o artefato compilado com o conjunto e a versão do modelo. Sem os três, você não consegue reproduzir nem explicar o resultado depois.
Quanto custa
Ordem de grandeza. O custo de uma compilação é aproximadamente o número de candidatos multiplicado pelo tamanho do conjunto e pelo custo de uma execução do programa. Com 30 candidatos, 100 casos e um programa de dois módulos, são cerca de 6 mil execuções de módulo por sessão, o que nos preços praticados em julho de 2026 fica na casa de algumas dezenas de dólares. A avaliação em minilote reduz isso, porque nem todo candidato chega à avaliação completa.
Vale medir uma sessão pequena antes de disparar a grande: dez candidatos em vinte casos dão o custo por execução real e evitam a surpresa de uma conta trinta vezes maior do que a estimada.
Se a métrica usa um modelo como juiz, aproximadamente dobre o número de chamadas. E some o custo que não aparece na sessão: um prompt compilado maior paga a diferença em toda requisição de produção, todo mês, enquanto ele estiver no ar.
Footnotes
-
Yang et al. (2023) formularam o uso do próprio modelo como otimizador, alimentado com as soluções já avaliadas e suas pontuações. ↩
-
Khattab et al. (2023) descrevem o compilador que cria e colhe demonstrações, com programas compilados superando few-shot padrão em geralmente mais de 25% com GPT-3.5 e mais de 65% com llama2-13b-chat. ↩ ↩2
-
Opsahl-Ong et al. (2024) separaram proposta de instrução de seleção de demonstração, com avaliação estocástica em minilote e modelo substituto do objetivo, chegando a 13 pontos acima das linhas de base em cinco de sete programas com Llama-3-8B. ↩ ↩2 ↩3
-
Agrawal et al. (2025) leem traços de execução em linguagem natural para diagnosticar e corrigir, ficando mais de 10% acima do MIPROv2 e 6% acima do GRPO na média, com até 35 vezes menos execuções. ↩
-
Soylu et al. (2024) alternaram otimização de pesos e de prompts no mesmo pipeline, superando otimizar só pesos em até 60% e só prompts em até 6%, na média entre modelos e tarefas. ↩
Perguntas frequentes
- Como o DSPy otimiza um prompt?
- Ele propõe candidatos, avalia cada um no seu conjunto de casos usando a sua métrica e fica com os que pontuaram melhor. Um candidato é uma combinação de texto de instrução e conjunto de exemplos, escolhida por módulo do programa.
- Como funciona o bootstrap de exemplos no DSPy?
- O otimizador roda o próprio programa nos casos de treino e guarda os traços de execução em que a métrica passou. Esses traços viram demonstrações para os módulos. É por isso que a técnica se chama bootstrap: o exemplo é gerado pelo sistema, não escrito por você.
- Como o DSPy escolhe quais exemplos entram no prompt?
- Por medição. Ele monta conjuntos candidatos a partir dos traços que passaram, avalia cada combinação no conjunto e mantém a que pontuou melhor. Não há critério de similaridade nem heurística de diversidade decidindo sozinha: quem decide é a nota.
- O que é um teleprompter no DSPy?
- É o nome que o paper de 2023 deu ao componente que compila o programa. A documentação passou a chamá-lo de optimizer, e o caminho de importação com o nome antigo continuou existindo. Teleprompter e optimizer são o mesmo objeto, em vocabulários de épocas diferentes.
- O compilador reescreve a minha instrução?
- Reescreve, nos otimizadores que propõem instrução. Eles usam um modelo para gerar variações a partir do seu programa, de amostras dos seus dados e das notas já obtidas. A docstring da signature entra como instrução inicial e pode não sobreviver à compilação.
- Quantos casos o DSPy precisa para compilar?
- Dezenas resolvem o começo. Poucos exemplos bastam para o bootstrap colher demonstrações, mas ordenar candidatos parecidos exige mais: com 50 casos, uma diferença de três pontos percentuais fica dentro do ruído. Separe também um holdout que não participa da busca.
- Preciso de rótulo em cada etapa do pipeline?
- Não. Os otimizadores de programas de vários módulos foram desenhados para funcionar sem rótulo intermediário: você fornece a métrica no fim e o algoritmo distribui o crédito entre as etapas. É justamente o problema que o trabalho de 2024 sobre o MIPRO ataca.
Referências
- Khattab, O. et al.. DSPy: Compiling Declarative Language Model Calls into Self-Improving Pipelines (2023)arXiv:2310.03714
- Opsahl-Ong, K. et al.. Optimizing Instructions and Demonstrations for Multi-Stage Language Model Programs (2024)arXiv:2406.11695
- Yang, C. et al.. Large Language Models as Optimizers (2023)arXiv:2309.03409
- Agrawal, L. A. et al.. GEPA: Reflective Prompt Evolution Can Outperform Reinforcement Learning (2025)arXiv:2507.19457
- Soylu, D. et al.. Fine-Tuning and Prompt Optimization: Two Great Steps that Work Better Together (2024)arXiv:2407.10930