05 - Auto-prompt optimization — DSPy e além

TL;DR

Auto-prompt optimization transforma prompt de “string artesanal” em “programa compilado”: você declara o que quer (Signatures de input/output em DSPy), declara a métrica de sucesso (eval function), o compilador otimiza a versão final do prompt contra exemplos. DSPy (Stanford, Khattab et al., 2023) é o framework de referência em 2026: Signatures, Modules, Compilers (BootstrapFewShot, MIPRO, COPRO). Vizinhos: APE (Zhou et al., 2023) gera candidatos via LLM-meta-prompter, OPRO (Yang et al., 2023) trata LLM como otimizador iterativo, promptbreeder (Fernando et al., 2023) usa evolução. Quando auto-prompt vence: pipeline com várias chamadas LM, eval claro, iteração repetida. Quando manual vence: one-shot alto-risco, edge-case que exige judgment, ausência de eval confiável. Estado 2026: real e crescente em produção, mas longe de “default” — muito uso ainda é experimental ou em pipeline acadêmico.

Por que existe

Prompt engineering manual é caro (tempo do humano), inconsistente (cada engenheiro com seu estilo) e mal escala (10 prompts × 5 modelos × 3 versões = matriz que explode). Auto-prompt parte da premissa: se você consegue definir uma métrica de sucesso, dá pra otimizar o prompt em vez de artesanar.

Analogias úteis (com cautela):

  • Compilador de software: você escreve em linguagem de alto nível (Signature), compilador gera código de baixo nível (prompt) otimizado pra a target.
  • AutoML: em vez de tunar hiperparâmetros à mão, você define busca + métrica + dataset, e algoritmo encontra a melhor combinação.

Diferença pro caso de prompt: a “linguagem de alto nível” ainda inclui muita ambiguidade (descrição em PT/EN), e o “compilador” ainda depende de muitos shots da LM. Não é compilação clássica — é otimização orientada por exemplos e eval function.

DSPy — Signatures, Modules, Compilers

DSPy (Khattab et al., 2023) é o framework de referência em 2026. Três abstrações centrais:

Signature — o “tipo” do prompt

Declaração de input/output do passo LM, sem dizer como prompted:

import dspy
 
class SummarizeArticle(dspy.Signature):
    """Resuma um artigo em 3 bullets factuais."""
    article: str = dspy.InputField(desc="texto do artigo")
    summary: list[str] = dspy.OutputField(desc="exatamente 3 bullets factuais")

Signature é spec, não prompt. O prompt vai ser gerado/otimizado depois.

Module — a chamada LM com a signature

Module instancia a Signature numa chamada concreta:

class Summarizer(dspy.Module):
    def __init__(self):
        super().__init__()
        self.generate = dspy.ChainOfThought(SummarizeArticle)
 
    def forward(self, article: str):
        return self.generate(article=article)
 
summarizer = Summarizer()
result = summarizer(article="...")
# result.summary = ["bullet 1", "bullet 2", "bullet 3"]

O dspy.ChainOfThought é um padrão de module que adiciona reasoning antes do output. Outros: dspy.Predict (direto), dspy.ReAct (com tool use).

Compiler — otimiza o prompt contra exemplos + eval function

# 1. Dataset com exemplos rotulados (ou só inputs, dependendo do compiler)
trainset = [
    dspy.Example(article="...", summary=["...", "...", "..."]).with_inputs("article"),
    # ... mais exemplos
]
 
# 2. Eval function — diz quão boa é uma resposta
def evaluate_summary(example, pred, trace=None) -> bool:
    # Pred é o output do module; example é o ground truth
    if len(pred.summary) != 3:
        return False
    # Critério qualquer: cobertura, faithfulness, factualidade...
    return judge_with_llm(article=example.article, summary=pred.summary)
 
# 3. Compila — testa few-shot variations, escolhe a melhor
from dspy.teleprompt import BootstrapFewShot
 
teleprompter = BootstrapFewShot(metric=evaluate_summary, max_bootstrapped_demos=4)
compiled_summarizer = teleprompter.compile(summarizer, trainset=trainset)
 
# 4. Usa compiled como qualquer module — agora com few-shot otimizado
result = compiled_summarizer(article="...")

O BootstrapFewShot é o compiler mais simples: roda o module sem few-shot no trainset, mantém os casos que passam no eval, usa eles como few-shot. Outros compilers mais sofisticados:

  • MIPRO / MIPROv2 — otimiza instruções e few-shot demos via busca bayesiana sobre o espaço de prompts; um dos mais usados em 2026
  • COPRO — otimiza só a instrução (texto do system) via meta-prompter
  • BootstrapFinetune — combina prompt otimizado com fine-tune

Diferença prática:

CompilerOtimizaCusto de compilaçãoQuando
BootstrapFewShotApenas few-shot demosBaixoStart; dataset pequeno
MIPRO/MIPROv2Instruções + few-shotMédio-altoQuando vale investir mais; melhor performance
COPROApenas instruçãoMédioQuando dataset não tem ground truth pra few-shot

Vizinhos do DSPy

APE — Automatic Prompt Engineer

Zhou et al., 2023 — APE usa um LLM-meta-prompter pra gerar candidatos de prompt a partir de input/output examples, depois ranqueia os candidatos por eval. Mais simples conceitualmente que DSPy: gera prompt → testa → escolhe melhor.

Pesquisa fundadora; em 2026 a maior parte da implementação prática migrou pra DSPy ou framework similar com primitives mais expressivas.

OPRO — LLMs as Optimizers

Yang et al., 2023 — OPRO trata o LLM como otimizador iterativo: o meta-prompt mostra “aqui estão prompts já tentados e seus scores, sugira o próximo”. O modelo aprende a partir do histórico do próprio experimento.

Forte em domínios onde o gradient de “o que melhora” é capturável em natural language. Menos usado em produção (custo de iteração + dependência do meta-modelo).

Promptbreeder

Fernando et al., 2023 — usa evolução genética sobre população de prompts: mutações (LLM reescreve um prompt), seleção (eval ranqueia), próximo geração. Pesquisa interessante, adoção em produção tímida.

Quando auto-prompt vence

Cenários onde o trabalho de configurar auto-prompt paga:

CenárioPor quê
Pipeline com várias chamadas LMOtimizar 5 prompts à mão = 5x o esforço; DSPy otimiza tudo junto contra eval end-to-end
Eval function clara e barataBootstrapFewShot precisa rodar eval várias vezes; eval caro = compilação cara
Otimização repetida (modelo novo)Mudou de Sonnet pra Opus? Recompila em vez de re-engenheirar 5 prompts à mão
Dataset com ground truth disponívelGround truth viabiliza few-shot selection automática
Tolerância pra prompt “feio”Compiler pode gerar prompts longos e estranhos; aceitar isso é parte do contrato

Quando manual vence

Cenários onde artesanato vence:

CenárioPor quê
One-shot alto-riscoInvestir em DSPy pra um prompt = overkill
Edge case que exige judgmentCompiler otimiza pro caso médio; edge precisa tuning explícito
Eval function frágil ou ausenteSem eval, compiler não sabe pra onde otimizar
Restrições de schema rígidasCompiler pode adicionar “ruído” no prompt que quebra schema em casos não cobertos pelo eval
Prompt safety-criticalCada token do prompt importa; compiler escolher por você é arriscado
Iteração rápida com produto/PMAuto-compila é assíncrono; conversa rápida é manual

Quando combinar — auto + manual

Em produção real, raramente é puro auto ou puro manual. Pattern recomendado:

1. Humano escreve Signature + base prompt + eval function (parte criativa, alto-nível)
2. DSPy compila few-shot e instruções contra eval (parte chata, alto-volume)
3. Humano revisa o prompt compilado, descarta partes problemáticas, ajusta
4. Recompila com a versão revisada como base
5. Promove se eval em produção confirma

A humano define o espaço (Signature, métrica), o compilador busca dentro do espaço.

Eval function — o ponto mais crítico do setup

Auto-prompt é só tão bom quanto sua eval function. Se eval mede a coisa errada, compiler vai gerar o prompt errado. Princípios:

  • Eval rápido — vai rodar centenas de vezes durante compilação; segundos importam
  • Eval estável — variância baixa entre runs
  • Eval cobre o objetivo final — não otimize só sintaxe; cubra semântica e edge cases
  • Eval bate com métrica de prod — eval que diverge de métrica de prod = ganha eval, perde prod

Quando ground truth não existe, LLM-as-judge (04 - LLM-as-judge — quando e como) viabiliza eval automatizado pra DSPy. Cuidado: judge enviesado vira prompt enviesado.

Como DSPy encaixa no ciclo eval → diff → ship

DSPy não substitui o ciclo — ele automatiza um passo do ciclo:

Ciclo canônico:         Com DSPy:
1. Observability  →     (sem mudança: humano monitora)
2. Eval mede      →     (sem mudança: golden set + judge)
3. Diff (hipótese →     DSPy faz o diff automaticamente:
   vira mudança)        compiler gera candidato vs baseline
4. A/B test       →     (sem mudança: canary ainda é necessário)
5. Ship           →     (sem mudança: champion-challenger decide)

O passo 3 era “humano escreve prompt candidato baseado em hipótese”. Com DSPy, o passo 3 vira “humano define Signature + trainset, compiler gera candidato”. O candidato gerado ainda precisa passar pelo A/B (passo 4) e pelo champion-challenger (passo 5).

O ganho real: em vez de um único candidato por ciclo (custo humano de escrever prompt), DSPy pode gerar e avaliar dezenas de candidatos em paralelo, escolhendo o melhor antes do A/B. Mais candidatos testados = maior chance de encontrar o ótimo no espaço de prompts.

Estado 2026

Realidade honesta:

  • DSPy é estável e amplamente conhecido em comunidade de IA, com adoção crescente em pipeline acadêmico e em algumas equipes de produção. Não é “tooling de produção default” como um framework web maduro.
  • Ferramentas competidoras / complementares estão surgindo (TextGrad, frameworks proprietários de labs); o espaço ainda está se consolidando.
  • Auto-prompt em produção costuma cobrir partes do pipeline (e.g., compilar few-shot pra uma signature crítica) em vez de todo o sistema.
  • Tradeoff conhecido — prompts gerados por compiler costumam ser mais longos e menos legíveis que prompts artesanais. Tolerar isso é parte do custo.

A escolha “DSPy ou não” em 2026 é típica de adoção de tecnologia em transição: time pequeno em produto early, manual basta; time que vai recompilar contra modelos novos a cada trimestre, vale aprender DSPy mesmo com overhead inicial.

DSPy integra com o ecossistema existente:

  • Langfuse: instrumentação de trace + versionamento de prompt compilado
  • Weaviate, Qdrant, Pinecone: retrieval DSPy via dspy.Retrieve
  • MLflow / Weights & Biases: experimentos de compilação logados pra comparação

Custo real de compilação: Compilar com BootstrapFewShot em dataset de 50 exemplos + modelo Sonnet ≈ ~100-200 chamadas de API, custo estimado $2-10 dependendo do modelo e tamanho dos exemplos. MIPRO com bayesian search pode ser 5-10x isso. Compensa calcular o custo de compilação antes de incluir num pipeline frequente.

Anti-padrões

  • DSPy sem eval function — compiler precisa de métrica; sem ela, gera lixo otimizado
  • Eval function que mede a coisa errada — compiler diverge da métrica de produção
  • Acoplar DSPy ao código sem fallback pra prompt artesanal — quando compilação quebra ou modelo muda, sem fallback você fica parado
  • Compilar uma vez e esquecer — modelo muda, distribuição muda, recompila periodicamente
  • DSPy como prata bala em problema mal definido — se o objetivo é difícil de medir, DSPy não resolve, agrava
  • Confiar 100% no prompt compilado em prod safety-critical — humano deve auditar antes de promover
  • Ignorar custo de compilação — MIPRO com dataset grande + modelo caro = compilação cara

Armadilhas comuns

Usar DSPy sem eval function — compilar sem métrica é otimizar em direção desconhecida

DSPy sem eval function é como compilar código sem saber o que o programa deve fazer. O BootstrapFewShot sem metric vai usar um fallback genérico ou simplesmente não filtrar os few-shots — e você vai promover um prompt “compilado” que ninguém avaliou. A eval function não precisa ser perfeita pra começar; ela precisa ser melhor que nada. Um LLM-as-judge simples que responde “pass/fail” com critério claro já é suficiente pra primeira compilação. A evolução natural é: eval simples (boolean) → eval com score (float 0-1) → eval por dimensão (faithfulness, completeness, format). Não espere a eval perfeita pra começar — compile com a que você tem, mede em prod, calibra.

Tratar o prompt compilado como caixa-preta — e não entender o que foi gerado

Compiler pode gerar prompts longos, repetitivos, com few-shots questionáveis e linguagem que nenhum humano escolheria. A tentação é “mas funciona no eval” — e promover sem ler. O problema: prompts muito longos têm custo, latência e comportamento inesperado em distribuição diferente do trainset. Além disso, prompts gerados podem ter comportamento emergente (vieses do few-shot, instruções que conflitam) que só aparecem em edge cases. O protocolo mínimo: antes de promover um prompt compilado, ler o prompt gerado inteiramente, rodar manualmente em 5-10 casos variados, e confirmar que o comportamento é o esperado — não só que o score passou.

Compilar uma vez e nunca recompilar — prompt compilado degrada com o tempo

O prompt compilado foi otimizado contra um modelo específico (e.g., claude-sonnet-4-5) com uma distribuição específica de trainset. Quando o modelo do provider atualiza (mesmo minor snapshot), ou quando a distribuição de input muda significativamente, o prompt compilado pode regredir sem que ninguém perceba — porque não tem mecanismo de recompilação periódica. Recompilar não é “refazer do zero” — é rodar o compiler com o mesmo Signature e uma versão atualizada do trainset, que costuma ser rápido se a infrastructure já está montada. Cadência razoável: recompila quando troca de modelo, quando eval score em prod cai abaixo do threshold, ou trimestralmente como manutenção preventiva.

Como explicar em inglês

Interview quote: “Auto-prompt optimization treats prompts as compiled artifacts rather than handcrafted strings. You declare the task semantics via a Signature — input and output types with descriptions — and a success metric, and the compiler like DSPy’s BootstrapFewShot or MIPRO searches the space of few-shot combinations and instructions to find the version that maximizes your metric. The key insight is that if you can measure it, you can optimize it. It pays when you have multiple LM calls in a pipeline, a clear and cheap eval function, and repeated optimization needs — like recompiling against a new model.”

PortuguêsInglês
Otimização automática de promptAuto-prompt optimization
Compilador de promptsPrompt compiler
Assinatura de input/outputSignature (input/output declaration)
Módulo LMLanguage Model module
Função de avaliaçãoEval function / metric
Conjunto de treinamento de exemplosTraining set / trainset
Seleção automática de few-shotsFew-shot bootstrapping
Otimização de instrução (texto do system)Instruction optimization
Recompilação (modelo ou trainset novo)Recompilation
Prompt compilado vs artesanalCompiled prompt vs handcrafted prompt

O que vem a seguir

Auto-prompt cobre a otimização do diff. A nota 06 fecha um outro ângulo do loop: capturar o sinal de feedback do usuário — thumbs up/down, re-prompt rate, abandonment — e transformá-lo em sinal estruturado que alimenta o trainset, o golden set, e as decisões de qual área do pipeline otimizar.

Fontes

  • Khattab, Singhvi, Maheshwari et al.DSPy: Compiling Declarative Language Model Calls into Self-Improving Pipelines (arxiv:2310.03714). Paper original.
  • DSPydocumentação oficial e repositório.
  • Khattab et al.In-Context Learning for Extreme Multi-Label Classification (arxiv:2401.12178). Uso de DSPy em problema concreto.
  • Zhou et al.Large Language Models Are Human-Level Prompt Engineers (APE, arxiv:2211.01910).
  • Yang et al.Large Language Models as Optimizers (OPRO, arxiv:2309.03409).
  • Fernando et al.Promptbreeder: Self-Referential Self-Improvement Via Prompt Evolution (arxiv:2309.16797).
  • Opsahl-Ong et al.Optimizing Instructions and Demonstrations for Multi-Stage Language Model Programs (MIPROv2, arxiv:2406.11695).
  • Liang et al.HotpotQA: A Dataset for Diverse, Explainable Multi-hop Question Answering (arxiv:1809.09600). Benchmark usado pra avaliar DSPy no paper original.
  • LangfuseDSPy integration. Como logar traces de compilação DSPy no Langfuse.
  • Omar KhattabDSPy in production. Talk de 2024 sobre uso real de DSPy em produto.
  • TextGradTextGrad: Automatic Differentiation via Text. Abordagem rival/complementar pra auto-otimização de pipeline LLM.

Veja também