Voltar ao catálogo
Season 2 15 Episódios 55 min 2026

Learning DSPy (v3.1 - 2026 Edition)

Um currículo abrangente e passo a passo para aprender DSPy, passando de prompts frágeis baseados em strings para uma programação modular e estruturada, e otimização automatizada.

Frameworks de AI/ML Engenharia de Prompts
Learning DSPy (v3.1 - 2026 Edition)
A Reproduzir
Click play to start
0:00
0:00
1
Programação, Não Prompting
Este episódio aborda a filosofia fundamental do DSPy: o afastamento de prompts frágeis baseados em strings em direção a uma programação modular e estruturada. Os ouvintes vão aprender por que razão separar a arquitetura do sistema das instruções do modelo de linguagem cria aplicações de IA mais robustas.
3m 41s
2
Configurar Modelos de Linguagem
Aprende a configurar e gerir modelos de linguagem no DSPy. Este episódio cobre a definição de modelos predefinidos, a gestão de cache, a substituição de definições de geração e o acesso a diferentes fornecedores de modelos através do LiteLLM.
3m 57s
3
Prompting Declarativo com Signatures
Descobre como as Signatures do DSPy substituem o prompting tradicional. Este episódio explica como definir o comportamento de input e output de um módulo de forma declarativa, utilizando tanto strings inline como definições baseadas em classes com tipagem estrita.
3m 53s
4
Blocos de Construção com Modules
Explora os Modules do DSPy, os blocos de construção centrais para programas de modelos de linguagem. Este episódio aborda o dspy.Predict, o dspy.ChainOfThought e como compor múltiplos módulos numa pipeline maior e coesa.
3m 43s
5
Ligar Modelos com Adapters
Compreende o papel dos Adapters no DSPy. Este episódio explica como o ChatAdapter e o JSONAdapter fazem a ponte entre as signatures abstratas do DSPy e as mensagens multi-turn reais enviadas para as APIs dos modelos de linguagem.
3m 57s
6
Gerir Dados com Examples
Aprende como o DSPy lida com datasets para machine learning. Este episódio aborda o objeto dspy.Example, a distinção entre input keys e labels, e a preparação de dados para avaliação e otimização.
3m 26s
7
Definir o Sucesso com Metrics
Descobre como avaliar programas DSPy utilizando metrics. Este episódio ensina-te a escrever funções Python personalizadas para pontuar outputs, a usar o argumento trace e até a tirar partido de AI-as-a-judge para avaliações de formato longo.
3m 55s
8
Uma Introdução aos Optimizers
Entra na magia central do DSPy: os Optimizers. Este episódio fornece uma visão geral do que os optimizers fazem, o ciclo de otimização iterativo e a invulgar estratégia de divisão de dados 20/80 para otimização de prompts.
3m 26s
9
Few-Shot Learning Automático
Aprende como o DSPy automatiza o few-shot prompting. Este episódio foca-se no BootstrapFewShot e no BootstrapFewShotWithRandomSearch, explicando como sintetizam, filtram e injetam exemplos de alta qualidade nos teus prompts.
3m 40s
10
Otimização de Instruções com MIPROv2
Mergulha no instruction tuning automático. Este episódio explora o MIPROv2 e o COPRO, mostrando como o DSPy utiliza Bayesian Optimization e coordinate ascent para descobrir instruções de prompt superiores e contraintuitivas.
4m 02s
11
Finetuning com BootstrapFinetune
Descobre como destilar modelos de linguagem massivos em modelos mais pequenos e eficientes. Este episódio aborda o BootstrapFinetune, explicando como converter um programa DSPy baseado em prompts num modelo personalizado e com atualização de pesos.
3m 35s
12
Uso Automatizado de Tools com ReAct
Aprende a dar aos modelos de linguagem acesso a tools externas. Este episódio aborda o módulo dspy.ReAct, demonstrando como construir agentes autónomos que raciocinam e interagem com APIs de forma dinâmica.
3m 37s
13
Gestão Manual de Tools para Controlo
Assume o controlo total sobre a execução de tools. Este episódio aborda a gestão manual de tools no DSPy utilizando dspy.Tool, dspy.ToolCalls e native function calling para aplicações sensíveis à latência.
3m 38s
14
Integrar Tools com MCP
Liga os teus agentes a servidores de tools universais. Este episódio explica como utilizar o Model Context Protocol (MCP) no DSPy para tirar partido de tools padronizadas em diferentes frameworks com uma configuração mínima.
3m 45s
15
Ensembles e Meta-Otimização
Leva o DSPy ao limite. O episódio final aborda transformações de programas através do dspy.Ensemble e do meta-optimizer experimental BetterTogether, que combina prompt tuning com weight finetuning para o máximo desempenho.
3m 39s

Episódios

1

Programação, Não Prompting

3m 41s

Este episódio aborda a filosofia fundamental do DSPy: o afastamento de prompts frágeis baseados em strings em direção a uma programação modular e estruturada. Os ouvintes vão aprender por que razão separar a arquitetura do sistema das instruções do modelo de linguagem cria aplicações de IA mais robustas.

Download
Olá, daqui fala o Alex da DEV STORIES DOT EU. A aprender DSPy, episódio 1 de 15. Passas três horas a criar um prompt perfeito para que o teu modelo de linguagem gere os outputs corretos. Depois, um provider lança um novo modelo, trocas a API key, e todo o teu pipeline vai abaixo. Ficas preso a manter strings frágeis em vez de construir software. Este é o problema resolvido por uma filosofia chamada Programming, Not Prompting. As pessoas ouvem muitas vezes falar do DSPy e assumem que é apenas mais uma ferramenta de prompt templating para inserir variáveis em blocos de texto. Mas não é. O DSPy é uma framework para compilar e otimizar o control flow. Imagina um setup standard para um sistema que lê documentos e gera um relatório com citações. Numa abordagem tradicional, escreves um bloco de texto enorme. Explicas a tarefa, injetas os documentos, adicionas instruções manuais como pensa passo a passo, e especificas exatamente como as citações devem ficar. Esta abordagem acopla fortemente a arquitetura do teu sistema a escolhas incidentais. A tua arquitetura é a lógica central. Isso significa extrair factos, redigir um resumo e adicionar citações. As escolhas incidentais são as palavras específicas que usaste para convencer um determinado modelo de linguagem a obedecer-te. Essas palavras exatas não vão funcionar de forma ideal num modelo diferente, ou mesmo numa versão diferente do mesmo modelo. Quando os dados mudam ligeiramente, o prompt quebra. O DSPy separa a arquitetura do teu sistema dessas escolhas incidentais de prompt. Paras de escrever strings de instruções longas. Em vez disso, defines a tua tarefa puramente em termos de inputs e outputs. Para o gerador de relatórios, declaras que o input é uma lista de trechos de texto, e o output é um texto redigido e um conjunto de referências de citação. Uma vez definidos os inputs e outputs, ligas tudo usando código standard. Crias um componente de extração, passas-lhe os documentos e recolhes os factos. Depois, passas esses factos para um componente de redação. Finalmente, podes usar um loop simples para verificar o rascunho em relação aos factos originais. Não há strings manuais a implorar ao modelo para formatar o seu output corretamente. Há apenas lógica estruturada. A ideia principal é esta. Como a tua arquitetura é definida como código modular, um compilador pode traduzir automaticamente essa estrutura nos prompts reais exigidos por qualquer modelo de linguagem que estejas a usar. O DSPy trata as instruções do modelo, os passos de raciocínio e os exemplos few-shot como parâmetros internos. Estas são variáveis para serem otimizadas pela framework, em vez de texto estático que digitas à mão. Constróis o pipeline, defines os formatos dos dados e escreves os passos de execução em Python standard. A framework lida com a tarefa imprevisível de descobrir a melhor maneira de pedir ao modelo de linguagem para executar esses passos de forma fiável. Isto muda fundamentalmente a experiência do developer. Passas o teu tempo a fazer debug da lógica da tua aplicação, e não a tentar adivinhar qual adjetivo fará o modelo prestar atenção ao final de uma frase. A arquitetura do teu sistema deve sobreviver às peculiaridades de qualquer modelo de linguagem para o qual estejas a encaminhar requests hoje em dia. Obrigado por ouvirem, e boas programações a todos!
2

Configurar Modelos de Linguagem

3m 57s

Aprende a configurar e gerir modelos de linguagem no DSPy. Este episódio cobre a definição de modelos predefinidos, a gestão de cache, a substituição de definições de geração e o acesso a diferentes fornecedores de modelos através do LiteLLM.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. Aprender DSPy, episódio 2 de 15. Fazer uma única chamada de API para um language model é fácil. Mas gerir vários providers, fazer cache de respostas e monitorizar históricos de prompts normalmente obriga-te a criar e manter um wrapper personalizado. Configurar Language Models no DSPy elimina esse trabalho repetitivo. Podes achar que precisas do SDK da OpenAI para modelos GPT, do SDK da Anthropic para o Claude e de uma library separada para modelos locais. Mas não precisas. O DSPy lida com isto de forma uniforme através de uma única classe LM, alimentada pelo LiteLLM nos bastidores. Para usares um modelo, instancias um objeto LM do DSPy e passas uma string com o nome do provider, uma barra e o nome do modelo. Por exemplo, passas open ai barra gpt-4o-mini. Ao criares este objeto, também podes passar parâmetros standard como temperature ou max tokens. Graças ao backend unificado, os nomes destes parâmetros mantêm-se consistentes, independentemente do provider que estiveres a chamar. Podes interagir diretamente com este objeto LM. Chama-o como uma função Python normal, passando uma simples string de texto ou uma lista de dicionários de chat. Ele processa o input e devolve uma lista de strings geradas. Por default, retorna uma única string nessa lista, mas podes pedir várias completions. Esta utilização direta é simples, mas passar manualmente um objeto de modelo para cada função numa codebase grande torna-se confuso rapidamente. Para resolver isto, o DSPy usa um sistema de configuração global. Defines o teu language model default uma única vez chamando dspy ponto configure, atribuindo o teu objeto LM instanciado como o target. Todas as operações subsequentes do DSPy são encaminhadas automaticamente por esse modelo. Mas e se quiseres comparar outputs entre providers? Imagina que queres testar como o Claude 3.5 Sonnet lida com uma função específica em comparação com o teu modelo GPT default. Em vez de substituíres o estado global, usas o dspy ponto context. Isto cria um scope temporário. Abres um bloco with de Python usando dspy ponto context, atribuis o Claude como o default local e executas o teu código. Quando o bloco termina, o DSPy reverte automaticamente para o teu modelo GPT-4o-mini global. Isto cobre o encaminhamento de requests. E quanto à performance? Por default, o DSPy faz cache de cada geração do modelo para poupar tempo e custos de API. Se executares exatamente o mesmo prompt com exatamente os mesmos parâmetros, ele serve a resposta em cache instantaneamente. Aqui está o ponto chave. Às vezes, precisas de uma nova geração sem alterar o teu prompt ou ajustar a temperature. Para fazeres isto, o DSPy usa um parâmetro chamado rollout id. Quando passas um novo rollout id, como um integer único, o DSPy trata-o como um request distinto e ignora a cache. Isto força o modelo a gerar uma nova sequência, dando-te controlo sobre a diversidade da geração, enquanto mantém os core inputs estáticos. Finalmente, ao fazeres experiências, precisas de ver exatamente o que passou na rede. Cada objeto LM mantém o seu próprio log de interação. Podes aceder aos raw data através do atributo history no objeto do modelo. Para um resumo human readable, chamas o método inspect history. Ele faz print do prompt exato enviado ao provider e da resposta exata recebida. O verdadeiro valor desta camada de configuração é que separa completamente a lógica da tua aplicação das peculiaridades do provider, transformando a seleção de modelos e o caching em simples switches declarativos. Obrigado por ouvirem, happy coding a todos!
3

Prompting Declarativo com Signatures

3m 53s

Descobre como as Signatures do DSPy substituem o prompting tradicional. Este episódio explica como definir o comportamento de input e output de um módulo de forma declarativa, utilizando tanto strings inline como definições baseadas em classes com tipagem estrita.

Download
Olá, daqui fala o Alex da DEV STORIES DOT EU. A aprender DSPy, episódio 3 de 15. As signatures de funções standard de Python dizem ao teu código que tipos de dados esperar. Mas e se uma signature pudesse ditar a própria lógica da função, sem teres de escrever as instruções explicitamente? Essa é a premissa do Declarative Prompting with Signatures. Podes naturalmente confundir uma signature de DSPy com uma signature de função standard de Python. Parecem semelhantes, mas os seus papéis são fundamentalmente diferentes. Uma signature standard de Python define uma interface de dados estrita. Uma signature de DSPy, na verdade, declara e inicializa o comportamento do language model. Não estás a escrever um prompt. Estás a escrever uma especificação declarativa do que precisa de acontecer. A framework pega nesta especificação, olha para as expectativas de input e output, e constrói o prompt subjacente para ti. A maneira mais simples de definir uma signature é inline, usando uma string curta. Especificas as tuas variáveis de input, escreves uma seta direcional, e especificas as tuas variáveis de output. Por exemplo, a string "question arrow answer" diz ao DSPy que o modelo vai receber uma pergunta e tem de gerar uma resposta. Podes passar múltiplos inputs, como "context comma question arrow answer". É aqui que a coisa fica interessante. Os nomes das variáveis que escolhes carregam um peso semântico real. O DSPy usa esses nomes exatos da string para atribuir papéis no prompt. Se nomeares um input como "context", o modelo interpreta-o como informação de base. Não faças over-engineering destes nomes, nem tentes manipular as keywords com truques de prompt engenhosos. Usa palavras claras e descritivas em inglês para definir os papéis. Quando as strings inline não são expressivas o suficiente, passas para signatures baseadas em classes. Crias uma nova classe que herda da base class Signature do DSPy. Dentro desta classe, defines os teus inputs e outputs como atributos. Atribuis estes atributos usando as funções Input Field e Output Field fornecidas pelo DSPy. Esta abordagem dá-te um controlo minucioso sobre o comportamento do modelo. A docstring da própria classe torna-se a instrução principal para o language model, definindo a tarefa geral. Considera um cenário de classificação de imagens multimodal. Queres passar uma imagem e uma pergunta em texto para um vision model, e extrair a raça específica de um cão. Crias uma classe chamada ClassifyDogBreed. No topo da classe, escreves uma docstring a dizer: Identifica a raça do cão com base na imagem e na pergunta fornecidas. A seguir, defines os teus inputs. Crias um atributo chamado "image" e atribuis-lhe um Input Field. Crias um segundo atributo chamado "question" e atribuis-lhe um Input Field. Finalmente, defines um atributo chamado "breed" e atribuis-lhe um Output Field. Dentro desse Output Field, podes passar um argumento description a dizer: O nome exato da raça do cão, sem texto extra. As signatures baseadas em classes também lidam com a resolução de tipos. Podes especificar type hints standard de Python para os teus fields. Se deres type-hint ao teu output field como um boolean, o DSPy entende que o modelo tem de retornar um valor true ou false. A framework processa estas type annotations e injeta automaticamente constraints na estrutura do prompt, guiando o modelo para o formato de output correto. A estrutura dos teus dados e os nomes das tuas variáveis são as instruções propriamente ditas. Um field com um nome claro e uma docstring precisa numa signature declarativa vão ditar o comportamento do modelo de forma muito mais fiável do que parágrafo após parágrafo de prompt engineering feito à mão. Obrigado por ouvirem, happy coding a todos!
4

Blocos de Construção com Modules

3m 43s

Explora os Modules do DSPy, os blocos de construção centrais para programas de modelos de linguagem. Este episódio aborda o dspy.Predict, o dspy.ChainOfThought e como compor múltiplos módulos numa pipeline maior e coesa.

Download
Olá, daqui fala o Alex da DEV STORIES DOT EU. A aprender DSPy, episódio 4 de 15. Normalmente, quando queres que um modelo de linguagem raciocine sobre um problema, recorres a juntar strings à pressa, adicionando frases como think step by step ao teu prompt de forma atabalhoada. Emaranhas a lógica da tua aplicação com instruções de texto frágeis. No DSPy, as técnicas de prompting não são strings. São componentes estruturados e intermutáveis chamados Modules. É fácil confundir Modules com Signatures. Pensa numa Signature como o contrato. Define o quê, mapeando campos de input específicos para campos de output específicos. Um Module define o como. É um objeto parametrizado e callable que recebe a tua Signature e aplica uma estratégia de prompting específica para cumprir esse contrato. O Module mais básico é o Module Predict. Inicializas este Module passando a tua Signature como argumento. Se a tua Signature pedir para transformar um documento num resumo, o Module Predict trata da formatação do prompt e chama o modelo de linguagem. Mas talvez a tarefa seja complexa e exija lógica intermédia. Podes facilmente trocar o Predict pelo Module Chain of Thought. Não mudas a tua Signature. Em vez disso, basta passá-la para o Module Chain of Thought. Internamente, este Module modifica automaticamente a arquitetura do prompt. Ele instrui o modelo de linguagem a gerar um trace de raciocínio passo a passo antes de produzir os campos de output finais que definiste. Quando chamas o Module Chain of Thought com os teus dados de input, ele devolve um objeto que contém os outputs solicitados. Como usaste o Chain of Thought, esse objeto também inclui um novo campo com o rationale do modelo. Podes inspecionar exatamente como o modelo de linguagem chegou à resposta, completamente separado do valor final extraído. Aqui está a ideia principal. Podes aninhar estes Modules built-in para criar programas complexos, de forma muito semelhante a empilhar layers de redes neuronais no PyTorch. Podemos construir uma pipeline de multi-hop retrieval para ver isto em ação. Começas por definir uma classe custom. Na sua fase de inicialização, declaras os Modules mais pequenos de que vais precisar. Para uma arquitetura multi-hop, podes declarar um Module gerador de queries usando o Chain of Thought, e um Module de síntese de respostas usando o Predict standard. A seguir, defines um método forward para encaminhar os dados entre eles. O método forward recebe uma pergunta inicial do utilizador. Passa essa pergunta para o teu Module gerador de queries, que devolve uma search query. Executas essa pesquisa na tua base de dados para obter um documento. Se precisares de um segundo hop, passas o documento obtido e a pergunta original de volta para o Module gerador de queries para produzir uma search query mais refinada. Finalmente, passas todos os documentos obtidos e a pergunta do utilizador para o Module de síntese de respostas para gerar a resposta final. Acabaste de construir um graph executável e custom a partir de componentes modulares. Quando fazes chain de várias calls desta forma, é crucial ver exatamente o que está a ser enviado para o modelo nos bastidores. O DSPy faz track da utilização do teu modelo de linguagem globalmente. Podes chamar o comando inspect history no objeto do teu modelo de linguagem para imprimir as interações mais recentes. Isto renderiza a string exata que o modelo recebeu e a string exata que gerou, garantindo que a tua pipeline composta está a montar o contexto corretamente. Ao separar a definição da tua tarefa em Signatures e a tua estratégia de execução em Modules parametrizados, transformas o prompting de uma tarefa de edição de texto numa decisão arquitetural, permitindo-te fazer upgrade à capacidade de raciocínio da tua pipeline simplesmente trocando o nome de uma classe. Obrigado por ouvirem, happy coding a todos!
5

Ligar Modelos com Adapters

3m 57s

Compreende o papel dos Adapters no DSPy. Este episódio explica como o ChatAdapter e o JSONAdapter fazem a ponte entre as signatures abstratas do DSPy e as mensagens multi-turn reais enviadas para as APIs dos modelos de linguagem.

Download
Olá, daqui é o Alex do DEV STORIES DOT EU. A aprender DSPy, episódio 5 de 15. Escreves uma signature limpa e fortemente tipada no teu código, passas isso para um language model, e de alguma forma recebes de volta um objeto Python perfeitamente estruturado. Entre o teu código limpo e a API de raw text do modelo, existe uma camada de tradução caótica. Essa camada oculta é o conceito de ligar modelos com Adapters. Antes de vermos como funcionam, vamos esclarecer uma confusão comum. Podes confundir Adapters com Modules. Os Modules gerem a estratégia de raciocínio. Eles decidem se o modelo deve usar Chain of Thought ou depender de external tools. Os Adapters não querem saber da estratégia. Um adapter é puramente a camada de tradução. Ele lida com a raw string e com a serialização JSON que é efetivamente enviada pela rede para a API do modelo. Os language models não entendem signatures declarativas. Eles esperam arrays de mensagens multi-turn, contendo roles específicos e blocos de texto. O adapter preenche esta lacuna. A ferramenta padrão para isto no DSPy é o ChatAdapter. Quando invocas um module, o ChatAdapter interceta a tua signature e formata-a num chat history padrão. A instrução principal da signature é mapeada diretamente para o system prompt. Os teus input fields são recolhidos e colocados na user message. Aqui está o ponto chave. O ChatAdapter usa marcadores de texto específicos para manter os teus inputs estritamente organizados. Ele envolve cada nome de campo entre parênteses retos duplos e cardinais duplos. Se o teu input field se chamar context, o language model vê um marcador com parênteses retos e cardinais à volta da palavra context, imediatamente seguido pelos dados reais do context. Esta fronteira visual impede que o modelo confunda acidentalmente as tuas system instructions com o texto de input do utilizador. Ele repete este padrão para os output fields esperados, dando um prompt ao modelo para gerar exatamente os mesmos marcadores na sua resposta. Considera um cenário em que estás a extrair notícias de ciência. O teu input é um artigo em raw text, e o teu output precisa de corresponder a uma classe Pydantic com campos específicos para o título e para a principal descoberta científica. Quando passas este requisito pelo ChatAdapter, ele inspeciona a tua classe Pydantic, gera um JSON schema completo, e injeta esse schema diretamente no system prompt. Ele diz explicitamente ao language model como formatar a sua resposta de texto. Quando o modelo finalmente responde, o ChatAdapter captura a raw text string. Ele procura os marcadores de output esperados, extrai o bloco de texto entre eles, e faz o parse desses dados de volta para os objetos Python exatos que a tua aplicação requer. Isto cobre os inputs e o parsing para interações baseadas em texto. Mas os language models modernos têm frequentemente suporte nativo para structured output. É aqui que o JSONAdapter entra em ação. Em vez de modificar extensivamente o system prompt e depender de marcadores de texto, o JSONAdapter adota uma abordagem mais direta. Ele delega as restrições de formatação ao JSON mode nativo do provider do modelo ou à API de structured outputs. O modelo é forçado, ao nível do protocolo, a devolver um objeto JSON válido que contenha todos os teus output fields solicitados. Como a API do modelo lida com a estrutura nativamente, evita a necessidade de o adapter procurar marcadores de string no raw text. Se o teu modelo de destino suporta esta capacidade, mudar o teu pipeline para usar o JSONAdapter resulta geralmente em menor latência e num parsing significativamente mais fiável. O adapter é a fronteira rígida entre a lógica determinística da tua aplicação e a geração de texto não estruturado do language model. Ao controlar exatamente como os inputs são serializados e como é feito o parse dos outputs, os adapters garantem que o teu pipeline nunca quebra devido a uma string mal formatada. Obrigado por ouvirem, bons códigos a todos!
6

Gerir Dados com Examples

3m 26s

Aprende como o DSPy lida com datasets para machine learning. Este episódio aborda o objeto dspy.Example, a distinção entre input keys e labels, e a preparação de dados para avaliação e otimização.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. A aprender DSPy, episódio 6 de 15. Corres um optimizer para melhorar o teu pipeline de language models, mas ele atinge um score perfeito logo na primeira tentativa. Ao analisares mais de perto, percebes que acidentalmente passaste as respostas target diretamente para o prompt. Para tratares language models como componentes tradicionais de machine learning, precisas de uma forma infalível de gerir os teus datasets de training, dev e test sem deixares vazar as respostas. A gestão de dados com Examples no DSPy resolve exatamente isso. No DSPy, a estrutura de dados fundamental é o objeto Example. Usas este objeto para construir todos os teus datasets. Superficialmente, comporta-se de forma muito semelhante a um dicionário standard de Python. Crias um passando pares key-value que representam os teus dados. Pensa numa task de sumarização. Crias um novo objeto Example e dás-lhe dois campos. Atribuis uma string longa a um campo chamado report, e uma string curta a um campo chamado summary. Podes ler estes valores a qualquer momento usando a dot notation standard, pedindo o campo report ou o campo summary diretamente do objeto. É comum tratar o objeto Example apenas como um wrapper de um dicionário, mas usar um dicionário simples vai quebrar o processo de compilação. Quando passas um dataset para um optimizer do DSPy, o compiler precisa de separar o que entra no pipeline daquilo que é usado para calcular o score do pipeline. Isto exige limites explícitos entre os dados de input e as respostas esperadas. É aqui que a coisa fica interessante. O objeto Example controla estes limites usando um método específico chamado with_inputs. Ao instanciares o teu Example contendo o report e o summary, fazes chain do método with_inputs no final. Passas-lhe a string "report". Isto marca explicitamente o campo report como os dados de input. Qualquer campo que não especifiques neste método torna-se automaticamente numa label. O optimizer agora sabe que deve enviar apenas o report para o teu pipeline. O summary permanece totalmente oculto durante a inference. Depois de configurares um único Example, agrupas vários Examples em listas standard de Python para formar os teus datasets de training, dev e test. Como o DSPy enquadra a prompt engineering como um problema de otimização de machine learning, ter estes datasets claramente particionados é um requisito estrito. Quando o optimizer corre o teu pipeline sobre o training set, processa um Example de cada vez. Ele remove as labels, faz o feed forward dos inputs, captura o output gerado e, em seguida, avalia o resultado. Quando escreveres métricas de avaliação custom, vais precisar de aceder a estes campos separados. O objeto Example fornece dois métodos para esta extração. Chamar o método inputs retorna um dicionário contendo apenas os dados que marcaste como inputs. Chamar o método labels retorna um dicionário contendo os dados target ocultos. A tua função de avaliação chama o método labels para recuperar o target summary, compara-o com o texto gerado e atribui um score com base no quão bem eles correspondem. Configurar corretamente os teus objetos Example garante que o teu pipeline aprende realmente a mapear inputs para outputs. A separação estrita de inputs e labels evita o data leakage durante a otimização, garantindo que o teu sistema melhore em vez de apenas memorizar as respostas fornecidas. Obrigado por ouvirem, e happy coding a todos!
7

Definir o Sucesso com Metrics

3m 55s

Descobre como avaliar programas DSPy utilizando metrics. Este episódio ensina-te a escrever funções Python personalizadas para pontuar outputs, a usar o argumento trace e até a tirar partido de AI-as-a-judge para avaliações de formato longo.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. A aprender DSPy, episódio 7 de 15. Não consegues melhorar consistentemente o que não consegues medir, e quando lidamos com language models, confiar na intuição humana para medir a qualidade do output simplesmente não escala. Para reescreveres automaticamente os teus prompts ou afinares o teu sistema, o compiler precisa de uma estrela-guia matemática, e é exatamente isso que Defining Success with Metrics te dá. No DSPy, uma métrica é uma função standard de Python. Ela recebe dois argumentos principais. O primeiro é um example, que representa o input gold standard e o output esperado do teu dataset. O segundo é uma prediction, que é a resposta real gerada pelo teu programa DSPy. A função da métrica compara a prediction com o example e devolve um score. Este score é geralmente um float, um integer, ou um simples valor boolean como true ou false. Para tarefas básicas de classificação, a tua métrica pode ser lógica Python simples. Podes escrever uma função de exact match que verifica se a string da prediction é perfeitamente igual à string esperada. Para executares esta medição sistematicamente em todos os teus dados, o DSPy fornece um utilitário built-in chamado Evaluate. Passas a este utilitário o teu dataset de desenvolvimento, a tua função de métrica e parâmetros de execução, como o número de threads em paralelo. O utilitário Evaluate corre a tua métrica em cada prediction, agrega os resultados e devolve um único score numérico que representa a performance geral do teu sistema. No entanto, o exact matching é quase sempre demasiado rígido para tarefas de linguagem generativa. É aqui que transitas de simples verificações de strings para o uso de feedback de IA, um pattern vulgarmente conhecido como LLM-as-a-judge. Como os módulos do DSPy são apenas código Python, a tua função de métrica pode instanciar e chamar um programa DSPy mais pequeno e separado para avaliar outputs semânticos complexos. Considera um cenário concreto. Estás a construir um sistema que gera um tweet promocional para responder a uma pergunta do utilizador. Uma métrica de exact match falha completamente aqui porque um bom tweet pode ser formulado de inúmeras maneiras válidas. Em vez disso, a tua função de métrica deve avaliar múltiplas dimensões do output. Primeiro, usa um length check básico de Python para garantir que o texto gerado tem menos de duzentos e oitenta caracteres. Segundo, verifica se o texto contém a resposta factual exigida pelo example. Por fim, passa o texto gerado para uma signature DSPy especializada que pede a um language model mais pequeno para avaliar se o tweet é envolvente. A tua função de métrica combina então o length check, o fact check e o engagement score do language model num valor matemático final. Quando eventualmente começares a compilar e a otimizar estes programas, a tua função de métrica tem de aceitar um terceiro argumento opcional. Chama-se trace. Os ouvintes muitas vezes confundem o argumento trace com um debugging log que imprime erros na consola ou o histórico de execução. Não é nada disso. O argumento trace é um objeto específico usado pelo compiler do DSPy durante a otimização para validar os passos intermédios de reasoning. Se o teu programa encadeia várias calls de language models, o trace contém o reasoning path específico que o modelo seguiu para chegar ao fim. Ao acederes ao trace dentro da tua métrica, a tua função pode verificar não apenas se o tweet final ficou bom, mas também se os passos intermédios usados para o redigir faziam sentido lógico. Esta é a parte que importa. A tua métrica define estritamente o que é o sucesso, e o compiler do DSPy vai otimizar impiedosamente o teu sistema para maximizar esse score específico. Se a tua métrica tiver falhas, o teu programa compilado terá falhas exatamente da mesma forma. Obrigado por ouvirem, happy coding a todos!
8

Uma Introdução aos Optimizers

3m 26s

Entra na magia central do DSPy: os Optimizers. Este episódio fornece uma visão geral do que os optimizers fazem, o ciclo de otimização iterativo e a invulgar estratégia de divisão de dados 20/80 para otimização de prompts.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. Aprender DSPy, episódio 8 de 15. Imagina que escreves um software e, em vez de ajustares manualmente a sua lógica interna para passar nos teus testes, um compilador reescreve automaticamente as instruções para que o programa tenha um melhor desempenho por si só. Não tocas no código. Apenas forneces o test set. É exatamente isso que o DSPy faz através de um conceito chamado Optimizers. Os Optimizers, que antes eram chamados de teleprompters em versões mais antigas da framework, são algoritmos que ajustam os parâmetros do teu programa. No machine learning tradicional, parâmetros significam pesos de redes neuronais. No DSPy, parâmetros significam principalmente as próprias strings de prompt e as instruções enviadas ao modelo de linguagem, embora também possam incluir pesos. O trabalho do optimizer é ajustar estes parâmetros para maximizar uma métrica que já definiste. Este processo acontece antes de fazeres o deploy da tua aplicação. É puro compute em tempo de pré-inferência. Gastas poder de processamento antecipadamente para encontrar as melhores instruções, para que a tua aplicação corra com precisão mais tarde. Quando ouves a palavra optimizer, podes assumir que precisas de datasets enormes, como precisarias para fazer o fine-tuning de um modelo tradicional. Mas não precisas. Os optimizers de prompt são altamente eficientes. Normalmente, exigem apenas de trinta a trezentos exemplos. Como o dataset é tão pequeno, o DSPy recomenda uma abordagem invulgar para dividir os teus dados. Em vez do split padrão de oitenta-vinte, onde a maior parte dos dados vai para treino, tu invertes a proporção. Usas vinte por cento para treino e oitenta por cento para validação. Se tiveres cinquenta exemplos, dás dez ao optimizer para construir os prompts e usas os quarenta restantes para avaliar se esses prompts realmente generalizam. Este split inverso impede que o optimizer faça overfitting dos prompts gerados a um conjunto muito pequeno de inputs. Aqui está o ponto-chave. O ciclo de desenvolvimento iterativo no DSPy gira em torno da execução deste loop de otimização. Vamos analisar um cenário concreto. Estás a construir um bot básico de perguntas e respostas. Primeiro, defines o teu programa DSPy e a tua métrica. A seguir, reúnes o teu dataset de cinquenta perguntas sem labels. Fazes o split destes dados, passando a pequena porção de treino para um objeto optimizer. Dizes ao optimizer para compilar o teu programa usando os teus dados de treino e a tua métrica. O optimizer corre, a experimentar diferentes estruturas de prompt nos bastidores. Ele verifica os outputs contra a tua métrica, aprende o que funciona e refina os prompts. Quando o optimizer termina, devolve uma nova versão compilada do teu programa. Este programa compilado contém os parâmetros recém-ajustados. Não precisas de correr este passo de otimização sempre que a tua aplicação arranca. Em vez disso, chamas o método save no programa compilado, fornecendo um file path. Isto grava todos os prompts e configurações otimizados num ficheiro JSON standard. Quando fazes o deploy da tua aplicação para produção, o teu código simplesmente instancia o programa base e chama o método load, apontando-o para esse exato ficheiro JSON. O teu bot fica imediatamente pronto para responder a perguntas usando as instruções otimizadas. O verdadeiro poder dos optimizers do DSPy é que eles desacoplam a lógica da tua aplicação da formulação exata dos teus prompts, permitindo que a computação encontre as melhores palavras para ti. Obrigado por ouvirem, bom código a todos!
9

Few-Shot Learning Automático

3m 40s

Aprende como o DSPy automatiza o few-shot prompting. Este episódio foca-se no BootstrapFewShot e no BootstrapFewShotWithRandomSearch, explicando como sintetizam, filtram e injetam exemplos de alta qualidade nos teus prompts.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. Aprender DSPy, episódio 9 de 15. Escolher manualmente os melhores exemplos para colocar num prompt é tedioso e propenso a vieses. Tu tentas adivinhar que exemplos importam, fazes hardcode deles nas tuas strings, e esperas que o modelo preste atenção. O DSPy sintetiza, testa e seleciona ativamente as demonstrações perfeitas para ti. Este processo é few-shot learning automático, e o DSPy gere isso através de três optimizers específicos. A abordagem mais simples é o LabeledFewShot. Tu forneces um conjunto de exemplos de treino rotulados. O optimizer seleciona aleatoriamente um subconjunto destes pares de input e output e insere-os diretamente nos teus prompts como demonstrações. Isto dá ao modelo um padrão básico a seguir. Isto funciona bem se os teus dados de treino corresponderem exatamente aos passos intermédios de que o teu programa precisa. Normalmente, não correspondem. Isto leva-nos ao BootstrapFewShot. Um equívoco comum é achar que o BootstrapFewShot escolhe exemplos aleatoriamente do teu training set. Não é o caso. Ele gera ativamente passos de raciocínio intermédios que nunca existiram nos teus raw data. Eis como o processo de bootstrapping flui. O optimizer requer um programa teacher. Por default, esta é apenas a versão não otimizada e zero-shot do teu próprio programa. O teacher percorre os teus exemplos de treino. Para cada exemplo, tenta gerar uma resposta. O DSPy depois passa essa resposta para a tua métrica de avaliação. Se a métrica disser que o output está correto, o DSPy guarda o trace inteiro dessa execução bem-sucedida. Este trace inclui o input, o output e, crucialmente, qualquer trabalho intermédio que o programa tenha feito para lá chegar. Considera um classificador de sentimentos. O teu raw dataset contém apenas reviews de clientes e uma label positiva ou negativa. O teu programa DSPy pede ao language model para usar raciocínio chain-of-thought antes de fazer output do sentimento. Durante o bootstrapping, o teacher lê uma review e escreve um parágrafo de raciocínio antes de adivinhar o sentimento. Se o palpite final corresponder à label verdadeira, esse raciocínio gerado é considerado bem-sucedido. O optimizer recolhe estes traces bem-sucedidos. Ele pega em quatro deles e injeta-os em futuros prompts. O teu classificador zero-shot é agora um classificador four-shot especialista, completo com passos de raciocínio sintetizados. O BootstrapFewShot para assim que encontra traces bem-sucedidos suficientes. Mas os primeiros traces bem-sucedidos nem sempre são os melhores. Aqui está a ideia principal. O BootstrapFewShotWithRandomSearch resolve isto executando todo o processo de bootstrap várias vezes. De cada vez, ele tira uma subamostra aleatória dos teus dados de treino. Isto cria vários conjuntos candidatos diferentes de demonstrações few-shot. O optimizer depois pega em todos estes conjuntos candidatos e avalia-os contra os teus dados de validação. Ele testa que combinação específica de demonstrações produz a pontuação geral mais alta. Descarta os conjuntos fracos e guarda o vencedor matemático. O verdadeiro poder do few-shot learning automático não é apenas poupar-te tempo a escrever prompts, mas descobrir caminhos de raciocínio intermédios bem-sucedidos que o teu dataset nunca conteve explicitamente. Obrigado por ouvires, happy coding a todos!
10

Otimização de Instruções com MIPROv2

4m 02s

Mergulha no instruction tuning automático. Este episódio explora o MIPROv2 e o COPRO, mostrando como o DSPy utiliza Bayesian Optimization e coordinate ascent para descobrir instruções de prompt superiores e contraintuitivas.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. Aprender DSPy, episódio 10 de 15. Às vezes, a instrução mais eficaz para um language model parece um disparate para um humano. Passas horas a elaborar meticulosamente a frase perfeita, só para descobrir que um prompt automatizado e ligeiramente desconexo supera completamente o teu trabalho. É por isso que deves deixar os algoritmos escreverem os teus prompts. Estamos a analisar a Instruction Optimization com o MIPROv2. Primeiro, tira da cabeça a ideia de prompt templating. Não se trata de trocar variáveis numa string estática. A Instruction Optimization, na verdade, reescreve as instruções sistémicas que governam o teu pipeline. Algoritmos anteriores, como o COPRO e o SIMBA, lidavam com isto a gerar variações passo a passo de prompts e a refiná-los ao longo do tempo. O MIPROv2 leva este conceito muito mais longe, ao tratar instruções e exemplos few-shot como um search space unificado. O MIPROv2 opera em três fases distintas. A primeira fase é o bootstrapping. O optimizer executa o teu programa não otimizado sobre os teus training data para construir uma pool de execution traces. Estas traces contêm os inputs reais, os passos intermédios e os outputs que fluem pelo teu sistema. A segunda fase é a grounded proposal. O optimizer não adivinha novas instruções às cegas. Ele usa um language model separado, chamado prompter, para analisar essas traces geradas. Ao analisar onde o teu pipeline teve sucesso e onde falhou, o prompter elabora um conjunto de novas instruções candidatas. Estas candidatas são diretamente grounded no comportamento real do teu programa, e não em templates genéricos. A terceira fase é a discrete search. O MIPROv2 avalia as novas instruções juntamente com diferentes combinações de few-shot traces. Para fazer isto de forma eficiente, ele utiliza Bayesian Optimization. Em vez de fazer brute-force a todas as combinações possíveis, o MIPROv2 constrói um surrogate model. Este surrogate model atua como um proxy leve. Ele prevê quais combinações de instruções e traces vão produzir a maior pontuação na tua métrica de avaliação específica. A Bayesian Optimization permite que o surrogate model mapeie o espaço de prompts e demonstrações. Ela calcula a melhoria esperada ao testar uma nova combinação. Isto equilibra sistematicamente a exploration de instruções não testadas com a exploitation das combinações que já têm uma boa pontuação. O optimizer foca-se na configuração ideal sem executar milhares de network calls redundantes. Considera um cenário concreto. Tu crias um agente ReAct para responder a queries complexas. Inicialmente, a sua accuracy está nos 24 por cento. Passas este agente para o MIPROv2, configuras para correr em light mode e forneces um dataset de 500 perguntas. O optimizer faz o bootstrap das traces, propõe grounded instructions e pesquisa o espaço usando o surrogate model. Quando termina, a accuracy do teu agente salta de 24 por cento para 51 por cento. O prompt final que impulsiona esse salto de performance provavelmente vai conter instruções e seleções de traces que um humano nunca teria elaborado. Aqui está a ideia principal. O MIPROv2 elimina o bottleneck da intuição humana. Ele trata as tuas instruções em linguagem natural exatamente como tunable weights num modelo matemático, transformando a criação de prompts de uma forma de arte imprevisível num problema de otimização determinístico. Obrigado por ouvirem, happy coding a todos!
11

Finetuning com BootstrapFinetune

3m 35s

Descobre como destilar modelos de linguagem massivos em modelos mais pequenos e eficientes. Este episódio aborda o BootstrapFinetune, explicando como converter um programa DSPy baseado em prompts num modelo personalizado e com atualização de pesos.

Download
Olá, daqui fala o Alex da DEV STORIES DOT EU. A aprender DSPy, episódio 11 de 15. Fazer prompting a modelos massivos é ótimo para arrancar com um protótipo. Mas quando essa lógica chega a produção, a latência e o custo de um modelo de setenta mil milhões de parâmetros tornam-se rapidamente num problema. Precisas do poder de raciocínio do modelo grande, mas da velocidade e do preço de um modelo de oito mil milhões de parâmetros. É exatamente disso que o BootstrapFinetune trata. O BootstrapFinetune compila o teu programa DSPy num modelo fine-tuned. Serve como a otimização derradeira para a eficiência. Atualiza os weights reais de um modelo target mais pequeno para imitar o comportamento exato do teu pipeline pesado. Um equívoco comum é que, para fazer fine-tune a um modelo, tens de recolher manualmente milhares de exemplos, formatá-los em ficheiros JSONL aborrecidos e tomar conta de um training loop. O BootstrapFinetune automatiza isto por completo. Trata da geração do dataset, da formatação e das atualizações de weights inteiramente através dos execution traces do teu programa. Pega num cenário concreto que envolve um classificador de intenções bancárias. O programa recebe uma mensagem confusa de um cliente e categoriza-a. Inicialmente, constróis um módulo DSPy usando um modelo altamente capaz, como o GPT-4o-mini, configurado para usar raciocínio chain-of-thought. O modelo pensa passo a passo na frase do cliente antes de fazer output da intenção. Obtém as respostas certas, mas é demasiado lento e caro para um chat em tempo real. Para otimizar isto, inicializas o BootstrapFinetune. Dás-lhe a tua métrica de avaliação para medir o sucesso, e especificas o modelo target mais pequeno e mais barato que queres fazer deploy. Depois, compilas o programa. Quando clicas em compilar, o DSPy corre o teu programa não otimizado sobre os teus dados de treino. Usa o modelo teacher pesado para gerar outputs. O optimizer observa esta execução. Sempre que o modelo teacher obtém a resposta certa de acordo com a tua métrica, o BootstrapFinetune captura o trace. Regista os inputs, o raciocínio passo a passo e o output final. Mapeia a lógica interna do modelo massivo para um formato que o modelo target mais pequeno consiga consumir. Assim que forem recolhidos traces bem-sucedidos suficientes, o BootstrapFinetune estrutura-os automaticamente num training dataset. Depois, desencadeia o processo de fine-tuning no teu modelo target. O modelo mais pequeno é treinado diretamente nos caminhos de raciocínio de alta qualidade gerados pelo modelo grande. Aqui está o ponto-chave. O modelo mais pequeno aprende a distribuição específica da task e o estilo de raciocínio necessário para a resolver sem correr o chain-of-thought pesado em tempo de inferência. No nosso exemplo do classificador bancário, um modelo pequeno standard pode atingir apenas sessenta e seis por cento de accuracy logo à partida. Mas depois de compilar com o BootstrapFinetune, esse mesmo modelo pequeno salta para oitenta e sete por cento de accuracy. O fine-tuning já não é um projeto de infraestrutura em separado; é simplesmente mais um passo de compilação que transforma um pipeline de raciocínio caro num asset de produção rápido e barato. Obrigado por ouvirem, e happy coding a todos!
12

Uso Automatizado de Tools com ReAct

3m 37s

Aprende a dar aos modelos de linguagem acesso a tools externas. Este episódio aborda o módulo dspy.ReAct, demonstrando como construir agentes autónomos que raciocinam e interagem com APIs de forma dinâmica.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. Aprender DSPy, episódio 12 de 15. Dar acesso a APIs externas a um LLM torna-o incrivelmente capaz, mas escrever o loop para gerir o seu raciocínio, fazer o parse dos outputs e recuperar de erros de execução é uma dor de cabeça enorme. A solução é o uso totalmente automatizado de tools com o módulo ReAct do DSPy. As pessoas costumam confundir o ReAct com function calling básico. Function calling é simplesmente o mecanismo de API que permite a um language model formatar o seu output como um pedido de dados estruturado. O ReAct é um paradigma comportamental específico. Significa Reason and Act. É um execution loop onde o modelo percorre três passos distintos: um Thought, uma Action e uma Observation. O módulo ReAct do DSPy gere completamente esta orquestração por ti. Tu não escreves o execution loop. Não fazes o catch manual de exceções da API. O ReAct faz o wrap de uma signature do DSPy e de uma lista de tools, transformando um prompt estático num agente autónomo. Para o usares, primeiro defines as tuas tools. No DSPy, as tools são simplesmente funções Python standard. Escreves uma função, defines os seus parâmetros de input e forneces uma docstring clara. Essa docstring é crítica. O DSPy extrai o nome da função e a docstring, passando-os ao language model para que ele saiba exatamente o que a tool faz e quando fazer o seu deploy. Considera um cenário em que constróis um agente básico de meteorologia e pesquisa. Escreves uma função Python chamada get weather que aceita o nome de uma cidade como uma string e faz uma query a uma API para devolver a temperatura atual. Instancias o módulo dspy dot ReAct, passando-lhe uma signature standard de question and answer, juntamente com uma lista que contém a tua função get weather. Quando perguntas ao módulo como está o tempo em Tóquio, o loop do ReAct começa. Primeiro, o modelo gera um Thought. Ele raciocina que precisa de dados meteorológicos atuais para Tóquio. A seguir, gera uma Action. Decide chamar a tua tool get weather, passando Tóquio como argumento. Aqui está o ponto chave. Não és tu que executas essa função. O módulo ReAct do DSPy interceta a Action do modelo, executa a tua função Python nos bastidores e captura o output. Se a função for bem-sucedida, o DSPy devolve os dados de temperatura ao modelo como uma Observation. Se o modelo alucinar um parâmetro ou a função atirar um erro de Python, o DSPy faz o catch desse erro e devolve a mensagem de erro como a Observation. O modelo lê o erro, gera um novo Thought para corrigir o seu erro e tenta uma nova Action. Assim que o modelo observa os dados de temperatura corretos, reconhece que o seu objetivo foi atingido. Sai do loop e formata a resposta final para o utilizador. Para evitar execuções descontroladas, este ciclo é estritamente limitado por um parâmetro chamado max iters, que significa maximum iterations. Este parâmetro dita quantos ciclos de Thought, Action e Observation o módulo tem permissão para executar. Se o modelo tiver dificuldades em encontrar os dados corretos e atingir o limite de iterações, o ReAct força-o a parar a pesquisa e a gerar uma resposta final usando apenas a informação que recolheu com sucesso. O verdadeiro poder deste módulo é que ele abstrai o control flow frágil e propenso a erros dos loops de agentes, permitindo-te tratar o raciocínio complexo aumentado por tools como apenas mais um componente previsível no teu pipeline. Obrigado por ouvirem, happy coding a todos!
13

Gestão Manual de Tools para Controlo

3m 38s

Assume o controlo total sobre a execução de tools. Este episódio aborda a gestão manual de tools no DSPy utilizando dspy.Tool, dspy.ToolCalls e native function calling para aplicações sensíveis à latência.

Download
Daqui fala o Alex da DEV STORIES DOT EU. A aprender DSPy, episódio 13 de 15. Agentes automatizados são ótimos quando tens uma tarefa flexível e aberta. Mas quando precisas de controlo absoluto e determinístico sobre exatamente como, quando e se uma função externa é executada, entregar o volante totalmente ao modelo de linguagem é demasiado arriscado. Tens de abrir o capô e gerir a execução tu mesmo. É exatamente aí que entra o tool handling manual para controlo. Usar um loop de agente automatizado abstrai a camada de execução, o que pode causar latência imprevisível ou esconder runtime errors. O handling manual é a alternativa para power-users. Restaura o teu controlo sobre a recuperação de erros, limites de timeout e a ordem exata de execução. Para construir isto no DSPy, começas por fazer wrap de uma função Python standard usando a classe dspy.Tool. Imagina que tens uma função Python que atua como uma calculadora para multiplicar dois números. Passas esta função para dspy.Tool. Se a tua função lida com queries a bases de dados ou pedidos de rede, também podes fazer wrap de funções assíncronas, e a classe tool vai gerir a execução async de forma nativa. Assim que a tua tool de calculadora estiver pronta, tens de a expor ao modelo de linguagem. Fazes isto passando uma lista que contém a tua tool diretamente para um módulo Predict do DSPy. Defines um parâmetro chamado tools na tua chamada Predict. Quando o modelo processa o input, avalia o prompt e decide se precisa da calculadora para gerar a resposta final. Quando o modelo decide usar a tua tool, baseia-se num mecanismo subjacente chamado Adapter. Por defeito, o DSPy usa um JSONAdapter. Este adapter traduz automaticamente a tua tool Python para o formato de function calling nativo exigido pela API do modelo de linguagem específico que estás a usar. Isto garante que o modelo gera JSON estruturado e fiável ao pedir uma tool. Presta atenção a esta parte. É fácil assumir que usar tool calling nativo produz automaticamente outputs de maior qualidade. A documentação do DSPy avisa explicitamente que isto é um equívoco. O tool calling nativo oferece maior fiabilidade para a sintaxe do request, mas não garante uma qualidade de raciocínio superior ao parsing standard baseado em texto. O modelo não fica de repente mais inteligente só porque está a formatar JSON. Como estás a gerir este processo manualmente, na verdade o modelo não executa a calculadora. Ele para e devolve uma resposta com a sua intenção. Acedes a esta intenção inspecionando response outputs dot tool calls. Esta propriedade devolve um objeto dspy.ToolCalls, que se comporta como uma lista de instruções. Cada item nesta lista especifica qual tool o modelo quer usar e os argumentos exatos que gerou, como cinco e dez para a calculadora. A seguir, escreves um loop Python standard para iterar por estes tool calls pedidos. Para cada call, invocas manualmente o seu método execute. Invocar o execute dispara o teu código Python real usando os argumentos gerados pelo modelo e devolve o resultado. Se os argumentos forem inválidos, ou se a calculadora der um erro, o teu loop Python apanha-o. Lidas com a falha nos teus termos, em vez de esperar que um loop automatizado recupere sozinho. O tool handling manual separa claramente a decisão do modelo de pedir uma ação da execução física dessa ação, dando à tua aplicação a fiabilidade determinística que os ambientes de produção exigem. Obrigado por ouvirem, happy coding a todos!
14

Integrar Tools com MCP

3m 45s

Liga os teus agentes a servidores de tools universais. Este episódio explica como utilizar o Model Context Protocol (MCP) no DSPy para tirar partido de tools padronizadas em diferentes frameworks com uma configuração mínima.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. A aprender DSPy, episódio 14 de 15. Sempre que adotas uma nova framework de IA, normalmente acabas a reescrever os mesmos wrappers de Python customizados para as tuas database queries, web searches e file readers. Em vez de manteres infinitos wrappers de API duplicados, e se os teus agents se pudessem ligar instantaneamente a tool servers universais e standardizados? Essa é a promessa do Model Context Protocol, e hoje vamos olhar para a integração de tools com o MCP. O Model Context Protocol é um open standard introduzido pela Anthropic. Ele fornece uma forma universal de ligar modelos de IA a data sources e tools externas. Ao adotar este standard, os developers escrevem uma tool uma única vez, fazem o host num server MCP e usam-na em qualquer framework compatível. O DSPy suporta isto nativamente. Não precisas de escrever adapter classes complexas para trazer estas tools externas para os teus programas DSPy. Um equívoco comum é achar que o DSPy lida ele próprio com as conexões de server subjacentes para estas tools. Não lida. O DSPy depende inteiramente do package oficial mcp de Python para gerir o networking e estabelecer a conexão. O DSPy só entra em ação mesmo no fim, para converter o tool object ativo do MCP num formato de tool nativo do DSPy. Para veres como isto funciona, vamos passo a passo ligar um tool server local a um agent DSPy. Primeiro, precisas de um server MCP a correr. No teu script de Python, importas a server parameters class do package MCP. Se estiveres a correr um processo local, defines os standard IO server parameters e apontas para o teu executável do server. Alternativamente, se as tuas tools viverem num server remoto, configuras uma HTTP client connection. Ambos os métodos estabelecem como a tua aplicação vai falar com o tool provider. A seguir, usas a library MCP para abrir uma client session. Dentro deste session context, inicializas a conexão. Neste ponto, o DSPy ainda está completamente fora de cena. Pedes à session MCP ativa para listar as suas tools disponíveis. O server responde com uma lista de tool objects. Cada objeto contém o nome da tool, uma descrição do que ela faz e os input arguments esperados. Agora fazes a ponte. É aqui que a coisa fica interessante. Para cada tool retornada pelo server, chamas o method from mcp tool na base class DSPy Tool. Passas a este method dois arguments específicos: o raw tool object e a client session ativa. Este único comando lê o schema fornecido pelo server MCP e faz instantaneamente o wrap numa interface compatível. Agora tens uma lista pronta a usar de tools nativas do DSPy. Finalmente, entregas essa lista de tools recém-convertida a um agent. Inicializas um module ReAct e passas o teu array de tools DSPy. Quando corres o agent, ele agora pode chamar as tools externas do MCP sem problemas. Os arguments fluem do module ReAct, através do wrapper DSPy convertido, pela client session do MCP até ao server, e o resultado flui de volta para informar o próximo reasoning step. O verdadeiro poder desta integração é o decoupling. Os teus modules DSPy podem aceder com segurança a enterprise databases ou file systems locais com zero código de wrapper customizado, garantindo que o exato mesmo tool server permanece completamente utilizável por frameworks inteiramente diferentes. Obrigado por ouvirem, happy coding a todos!
15

Ensembles e Meta-Otimização

3m 39s

Leva o DSPy ao limite. O episódio final aborda transformações de programas através do dspy.Ensemble e do meta-optimizer experimental BetterTogether, que combina prompt tuning com weight finetuning para o máximo desempenho.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. Aprender DSPy, episódio 15 de 15. O que acontece quando combinas Bayesian prompt optimization com o fine-tuning de weights em deep learning? Deixas de tratar a prompt engineering e o treino de modelos como etapas isoladas, e passas a tratá-los como uma pipeline contínua. É aqui que chegas à vanguarda da engenharia de IA automatizada, recorrendo a Ensembles e Meta-Optimization. Precisamos de esclarecer uma coisa imediatamente. Quando ouves a palavra ensemble, provavelmente pensas em fazer queries a cinco foundation models diferentes em simultâneo. No DSPy, um ensemble é algo completamente diferente. Aqui, um ensemble significa correr vários programas otimizados no mesmo language model subjacente. Combina diferentes estruturas de prompts e diferentes reasoning traces para agregar os seus outputs. A lógica aqui é simples. Durante uma run de deep optimization, diferentes configurações descobrem frequentemente reasoning paths distintos e igualmente válidos para chegar à resposta correta. Imagina que acabaste de correr o optimizer MIPROv2. Ele avalia centenas de configurações e guarda um histórico das que tiveram melhor performance. Em vez de escolheres apenas o programa com a pontuação mais alta e descartares o resto, extrais os cinco melhores programas candidatos. Passas estes programas para a transformação Ensemble do DSPy. Quando chega um novo input, o ensemble corre todos os cinco programas. Agrega os seus outputs, geralmente através de majority voting, e devolve uma resposta final altamente robusta. Estás essencialmente a escalar o teu compute em tempo de inference para garantir um resultado de maior qualidade. Correr um ensemble de cinco programas num foundation model massivo dá-te uma accuracy incrível, mas é caro e lento. É aqui que entram os meta-optimizers. Um meta-optimizer gere a execução de outros optimizers, sequenciando-os para potenciar os seus benefícios. O principal exemplo no DSPy é o BetterTogether. O BetterTogether sobrepõe melhorias de forma sistemática. Permite-te pegar na enorme capacidade de reasoning do teu ensemble e destilá-la num modelo rápido e com fine-tuning. Primeiro, configuras o BetterTogether para usar prompt optimization para gerar reasoning traces de altíssima qualidade a partir do teu ensemble pesado. A seguir, ele passa automaticamente essas traces para um weight optimizer. O weight optimizer usa esses dados para fazer o fine-tuning dos parâmetros de um student model muito mais pequeno e barato. Por fim, o BetterTogether pode correr uma segunda ronda de prompt optimization, desta vez adaptando as instruções especificamente aos weights recém-atualizados do student model. Estás a passar de prompt optimization, para weight optimization, e de volta para prompt optimization. O output é um modelo rápido e altamente especializado que capturou os diversos reasoning paths do ensemble original, sem o custo massivo de inference. Sobrepor técnicas de otimização sequencialmente é como colmatas o fosso entre um reasoning pesado e caro, e uma inference rápida e production-ready. Isto leva-nos ao fim da série. Recomendo vivamente que explores a documentação oficial do DSPy, tentes construir estas pipelines hands-on, ou visites devstories dot eu para sugerires tópicos que queiras ver abordados a seguir. Obrigado por ouvirem, happy coding a todos!