Un plan de estudios completo y paso a paso para aprender DSPy, pasando de los frágiles prompts basados en cadenas de texto a una programación modular y estructurada, y a la optimización automatizada.
Este episodio cubre la filosofía fundamental de DSPy: alejarse de los frágiles prompts basados en cadenas de texto hacia una programación modular y estructurada. Los oyentes aprenderán por qué separar la arquitectura del sistema de las instrucciones del modelo de lenguaje crea aplicaciones de IA más robustas.
3m 37s
2
Configuración de modelos de lenguaje
Aprende a configurar y gestionar modelos de lenguaje en DSPy. Este episodio cubre cómo establecer modelos predeterminados, manejar la caché, anular los ajustes de generación y acceder a diferentes proveedores de modelos a través de LiteLLM.
4m 34s
3
Prompting declarativo con Signatures
Descubre cómo las Signatures de DSPy reemplazan el prompting tradicional. Este episodio explica cómo definir el comportamiento de entrada y salida de un módulo de forma declarativa, utilizando tanto cadenas en línea como definiciones basadas en clases con tipado estricto.
4m 18s
4
Bloques de construcción con Modules
Explora los Modules de DSPy, los bloques de construcción centrales para los programas de modelos de lenguaje. Este episodio cubre dspy.Predict, dspy.ChainOfThought y cómo componer múltiples módulos en un pipeline más grande y cohesivo.
4m 18s
5
Conectando modelos con Adapters
Comprende el papel de los Adapters en DSPy. Este episodio explica cómo ChatAdapter y JSONAdapter cierran la brecha entre las firmas abstractas de DSPy y los mensajes reales de múltiples turnos enviados a las APIs de los modelos de lenguaje.
4m 40s
6
Gestión de datos con Examples
Aprende cómo DSPy maneja los datasets para machine learning. Este episodio cubre el objeto dspy.Example, distinguiendo entre claves de entrada y etiquetas, y preparando datos para evaluación y optimización.
4m 06s
7
Definiendo el éxito con métricas
Descubre cómo evaluar programas de DSPy utilizando métricas. Este episodio te enseña a escribir funciones personalizadas en Python para puntuar salidas, usar el argumento trace e incluso aprovechar la IA como juez para evaluaciones de formato largo.
4m 12s
8
Una introducción a los Optimizers
Adéntrate en la magia central de DSPy: los Optimizers. Este episodio ofrece una visión general de lo que hacen los optimizadores, el ciclo de optimización iterativo y la inusual estrategia de división de datos 20/80 para la optimización de prompts.
3m 54s
9
Few-Shot Learning automático
Aprende cómo DSPy automatiza el prompting few-shot. Este episodio se centra en BootstrapFewShot y BootstrapFewShotWithRandomSearch, explicando cómo sintetizan, filtran e inyectan ejemplos de alta calidad en tus prompts.
3m 56s
10
Optimización de instrucciones con MIPROv2
Sumérgete en el ajuste automático de instrucciones. Este episodio explora MIPROv2 y COPRO, mostrando cómo DSPy utiliza la optimización bayesiana y el ascenso de coordenadas para descubrir instrucciones de prompt superiores y contraintuitivas.
4m 08s
11
Finetuning con BootstrapFinetune
Descubre cómo destilar modelos de lenguaje masivos en otros más pequeños y eficientes. Este episodio cubre BootstrapFinetune, explicando cómo convertir un programa de DSPy basado en prompts en un modelo personalizado con actualización de pesos.
3m 44s
12
Uso automatizado de herramientas con ReAct
Aprende a dar a los modelos de lenguaje acceso a herramientas externas. Este episodio cubre el módulo dspy.ReAct, demostrando cómo construir agentes autónomos que razonan e interactúan con APIs de forma dinámica.
4m 42s
13
Manejo manual de herramientas para mayor control
Toma el control total sobre la ejecución de herramientas. Este episodio cubre el manejo manual de herramientas en DSPy utilizando dspy.Tool, dspy.ToolCalls y native function calling para aplicaciones sensibles a la latencia.
4m 16s
14
Integración de herramientas con MCP
Conecta tus agentes a servidores de herramientas universales. Este episodio explica cómo usar el Model Context Protocol (MCP) en DSPy para aprovechar herramientas estandarizadas en diferentes frameworks con una configuración mínima.
4m 06s
15
Ensembles y metaoptimización
Lleva DSPy al límite. El episodio final cubre las transformaciones de programas a través de dspy.Ensemble y el metaoptimizador experimental BetterTogether, que combina el ajuste de prompts con el finetuning de pesos para obtener el máximo rendimiento.
4m 14s
Episodios
1
Programación, no prompting
3m 37s
Este episodio cubre la filosofía fundamental de DSPy: alejarse de los frágiles prompts basados en cadenas de texto hacia una programación modular y estructurada. Los oyentes aprenderán por qué separar la arquitectura del sistema de las instrucciones del modelo de lenguaje crea aplicaciones de IA más robustas.
Hola, soy Alex de DEV STORIES DOT EU. Aprendiendo DSPy, episodio 1 de 15.
Pasas tres horas perfeccionando un prompt para que tu modelo de lenguaje genere los outputs correctos. Luego, un proveedor lanza un nuevo modelo, cambias la API key y todo tu pipeline se desmorona. Te quedas atascado manteniendo strings frágiles en lugar de desarrollar software. Ese es el problema que resuelve la filosofía de Programming, Not Prompting.
La gente suele oír hablar de DSPy y asume que es solo otra herramienta de prompt templating para insertar variables en bloques de texto. No lo es. DSPy es un framework para compilar y optimizar el flujo de control.
Imagina un setup estándar para un sistema que lee documentos y genera un informe con citas. En un enfoque tradicional, escribes un enorme bloque de texto. Explicas la tarea, inyectas los documentos, añades instrucciones manuales como piensa paso a paso, y especificas exactamente cómo deben verse las citas. Este enfoque acopla estrechamente la arquitectura de tu sistema con decisiones secundarias. Tu arquitectura es la lógica central. Esto implica extraer datos, redactar un resumen y añadir citas. Las decisiones secundarias son las palabras específicas que usaste para convencer a un modelo de lenguaje en particular de que te obedezca. Esas mismas palabras no funcionarán de forma óptima en un modelo diferente, ni siquiera en una versión distinta del mismo modelo. Cuando los datos cambian ligeramente, el prompt se rompe.
DSPy separa la arquitectura de tu sistema de esas decisiones secundarias del prompt. Dejas de escribir strings de instrucciones larguísimos. En su lugar, defines tu tarea puramente en términos de inputs y outputs. Para el generador de informes, declaras que el input es una lista de fragmentos de texto, y el output es un texto redactado y un conjunto de referencias para las citas.
Una vez definidos los inputs y outputs, los conectas usando código estándar. Creas un componente de extracción, le pasas los documentos y recoges los datos. Luego, pasas esos datos a un componente de redacción. Finalmente, puedes usar un bucle sencillo para comprobar el borrador con los datos originales. No hay strings manuales rogándole al modelo que formatee su output correctamente. Solo hay lógica estructurada.
Aquí reside la clave. Como tu arquitectura está definida como código modular, un compilador puede traducir automáticamente esa estructura a los prompts reales que necesite el modelo de lenguaje que estés usando. DSPy trata las instrucciones del modelo, los pasos de razonamiento y los ejemplos few-shot como parámetros internos. Estas son variables que el framework debe optimizar, en lugar de texto estático que escribes a mano. Construyes el pipeline, defines las estructuras de datos y escribes los pasos de ejecución en Python estándar. El framework se encarga de la impredecible tarea de descubrir la mejor manera de pedirle al modelo de lenguaje que ejecute esos pasos de forma fiable. Esto transforma radicalmente la experiencia del desarrollador. Dedicas tu tiempo a depurar la lógica de tu aplicación, no a adivinar qué adjetivo hará que el modelo preste atención al final de una frase.
La arquitectura de tu sistema debería sobrevivir a las peculiaridades del modelo de lenguaje al que le estés enrutando las requests hoy.
¡Gracias por escuchar, feliz programación a todos!
2
Configuración de modelos de lenguaje
4m 34s
Aprende a configurar y gestionar modelos de lenguaje en DSPy. Este episodio cubre cómo establecer modelos predeterminados, manejar la caché, anular los ajustes de generación y acceder a diferentes proveedores de modelos a través de LiteLLM.
Hola, soy Alex de DEV STORIES DOT EU. Aprendiendo DSPy, episodio 2 de 15. Hacer una sola llamada a la API de un modelo de lenguaje es sencillo. Sin embargo, gestionar múltiples proveedores, cachear las respuestas y hacer un seguimiento del historial de los prompts suele obligarte a crear y mantener un wrapper personalizado. Configurar modelos de lenguaje en DSPy elimina este trabajo tedioso.
Podrías pensar que necesitas el SDK de OpenAI para los modelos GPT, el SDK de Anthropic para Claude y una librería aparte para los modelos locales. Pues no. DSPy lo gestiona de forma uniforme mediante una única clase LM que usa LiteLLM por debajo. Para usar un modelo, instancias un objeto LM de DSPy y le pasas un string que contiene el nombre del proveedor, un slash y el nombre del modelo. Por ejemplo, le pasas open ai slash gpt-4o-mini. Al crear este objeto, también puedes pasar parámetros estándar como temperature o max tokens. Gracias al backend unificado, estos nombres de parámetros se mantienen consistentes independientemente del proveedor al que llames realmente.
Puedes interactuar directamente con este objeto LM. Llámalo como a una función normal de Python, pasándole un simple string de texto o una lista de diccionarios de chat. Procesa el input y devuelve una lista de strings generados. Por defecto, devuelve un solo string en esa lista, pero puedes pedir múltiples completions. Este uso directo es sencillo, pero ir pasando manualmente un objeto de modelo a cada función en un codebase grande se vuelve un lío muy rápido.
Para solucionar esto, DSPy utiliza un sistema de configuración global. Defines tu modelo de lenguaje por defecto una sola vez llamando a dspy punto configure, asignando tu objeto LM instanciado como target. Cada operación posterior de DSPy hace el routing automáticamente a través de ese modelo. Pero, ¿qué pasa si quieres comparar outputs entre proveedores? Imagina que quieres probar cómo Claude 3.5 Sonnet maneja una función específica comparado con tu modelo GPT por defecto. En lugar de sobrescribir el estado global, usas dspy punto context. Esto crea un scope temporal. Abres un bloque with de Python usando dspy punto context, asignas a Claude como modelo por defecto local y ejecutas tu código. Cuando termina el bloque, DSPy vuelve automáticamente a tu modelo global GPT-4o-mini.
Eso cubre el routing de requests. ¿Y el rendimiento? DSPy cachea cada generación del modelo por defecto para ahorrar tiempo y costes de API. Si ejecutas exactamente el mismo prompt con exactamente los mismos parámetros, te sirve la respuesta cacheada al instante. Aquí está la clave. A veces necesitas una generación nueva sin cambiar tu prompt ni ajustar el temperature. Para hacer esto, DSPy usa un parámetro llamado rollout id. Cuando pasas un nuevo rollout id, como un número entero único, DSPy lo trata como una request distinta y se salta la caché. Esto fuerza al modelo a generar una nueva secuencia, dándote control sobre la diversidad de generación mientras mantienes los inputs principales estáticos.
Por último, al experimentar, necesitas ver exactamente qué viajó por la red. Cada objeto LM mantiene su propio log de interacción. Puedes acceder a los datos raw a través del atributo history en el objeto del modelo. Para un resumen legible por humanos, llamas al método inspect history. Este imprime el prompt exacto enviado al proveedor y la respuesta exacta recibida.
El verdadero valor de esta capa de configuración es que desvincula por completo la lógica de tu aplicación de las peculiaridades del proveedor, convirtiendo la selección de modelos y el caching en simples interruptores declarativos.
¡Gracias por escuchar, feliz programación a todos!
3
Prompting declarativo con Signatures
4m 18s
Descubre cómo las Signatures de DSPy reemplazan el prompting tradicional. Este episodio explica cómo definir el comportamiento de entrada y salida de un módulo de forma declarativa, utilizando tanto cadenas en línea como definiciones basadas en clases con tipado estricto.
Hola, soy Alex de DEV STORIES DOT EU. Aprendiendo DSPy, episodio 3 de 15. Las signatures de funciones estándar de Python le indican a tu código qué tipos de datos esperar. Pero, ¿qué pasaría si una signature pudiera dictar la lógica de la función en sí, sin que tengas que escribir las instrucciones explícitamente? Esa es la premisa del Prompting Declarativo con Signatures.
Es normal que confundas una signature de DSPy con una signature de función estándar de Python. Se parecen, pero sus roles son fundamentalmente diferentes. Una signature estándar de Python define una interfaz de datos estricta. Una signature de DSPy realmente declara e inicializa el comportamiento del modelo de lenguaje. No estás escribiendo un prompt. Estás escribiendo una especificación declarativa de lo que tiene que pasar. El framework coge esta especificación, analiza las expectativas de entrada y salida, y construye el prompt subyacente por ti.
La forma más sencilla de definir una signature es inline, usando un string corto. Especificas tus variables de entrada, escribes una flecha direccional y especificas tus variables de salida. Por ejemplo, el string "question flecha answer" le indica a DSPy que el modelo recibirá una pregunta y debe generar una respuesta. Puedes pasar múltiples entradas, como "context coma question flecha answer". Aquí es donde la cosa se pone interesante. Los nombres de las variables que elijas tienen un peso semántico real. DSPy utiliza esos nombres de string exactos para asignar roles en el prompt. Si llamas a una entrada "context", el modelo lo interpreta como información de contexto. No te compliques demasiado con estos nombres ni intentes hackear las keywords con trucos de prompt ingeniosos. Usa palabras en inglés claras y descriptivas para definir los roles.
Cuando los strings inline no son lo suficientemente expresivos, pasas a las signatures basadas en clases. Creas una nueva clase que hereda de la clase base Signature de DSPy. Dentro de esta clase, defines tus entradas y salidas como atributos. Asignas estos atributos usando las funciones InputField y OutputField que proporciona DSPy. Este enfoque te da un control muy preciso sobre el comportamiento del modelo. El docstring de la propia clase se convierte en la instrucción principal para el modelo de lenguaje, definiendo la tarea general.
Considera un escenario de clasificación de imágenes multimodal. Quieres pasar una imagen y una pregunta de texto a un modelo de visión, y extraer la raza específica de un perro. Creas una clase llamada ClassifyDogBreed. Al principio de la clase, escribes un docstring que diga: Identifica la raza del perro basándote en la imagen y la pregunta proporcionadas. A continuación, defines tus entradas. Creas un atributo llamado "image" y lo asignas como un InputField. Creas un segundo atributo llamado "question" y lo asignas como un InputField. Finalmente, defines un atributo llamado "breed" y lo asignas como un OutputField. Dentro de ese OutputField, puedes pasar un argumento de descripción que diga: El nombre exacto de la raza del perro, sin texto adicional.
Las signatures basadas en clases también gestionan la resolución de tipos. Puedes especificar type hints estándar de Python para tus campos. Si defines el type hint de tu campo de salida como boolean, DSPy entiende que el modelo debe devolver un valor verdadero o falso. El framework procesa estas anotaciones de tipo e inyecta automáticamente restricciones en la estructura del prompt, guiando al modelo hacia el formato de salida correcto.
La estructura de tus datos y los nombres de tus variables son las instrucciones reales. Un campo con un nombre claro y un docstring preciso en una signature declarativa dictarán el comportamiento del modelo de forma mucho más fiable que párrafo tras párrafo de prompt engineering hecho a mano.
Gracias por escuchar, ¡feliz programación a todos!
4
Bloques de construcción con Modules
4m 18s
Explora los Modules de DSPy, los bloques de construcción centrales para los programas de modelos de lenguaje. Este episodio cubre dspy.Predict, dspy.ChainOfThought y cómo componer múltiples módulos en un pipeline más grande y cohesivo.
Hola, soy Alex de DEV STORIES DOT EU. Aprendiendo DSPy, episodio 4 de 15. Normalmente, cuando quieres que un modelo de lenguaje razone sobre un problema, recurres a hacer apaños juntando strings, añadiendo a lo loco frases como think step by step a tu prompt. Enredas la lógica de tu aplicación con instrucciones de texto frágiles. En DSPy, las técnicas de prompting no son strings. Son componentes estructurados e intercambiables llamados módulos.
Es fácil confundir los módulos con las signatures. Piensa en una signature como el contrato. Define el qué, mapeando campos de entrada específicos a campos de salida específicos. Un módulo define el cómo. Es un objeto parametrizado y callable que toma tu signature y aplica una estrategia de prompting específica para cumplir ese contrato.
El módulo más básico es el módulo Predict. Lo inicializas pasándole tu signature como argumento. Si tu signature pide convertir un documento en un resumen, el módulo Predict se encarga del formateo del prompt y llama al modelo de lenguaje. Pero quizás la tarea sea compleja y requiera lógica intermedia. Puedes cambiar fácilmente el módulo Predict por el módulo Chain of Thought. No cambias tu signature. Simplemente se la pasas al módulo Chain of Thought. Por debajo, este módulo modifica automáticamente la arquitectura del prompt. Le indica al modelo de lenguaje que genere una traza de razonamiento paso a paso antes de producir los campos de salida finales que definiste.
Cuando llamas al módulo Chain of Thought con tus datos de entrada, devuelve un objeto que contiene las salidas solicitadas. Como usaste Chain of Thought, ese objeto también incluye un nuevo campo con el razonamiento del modelo. Puedes inspeccionar exactamente cómo el modelo de lenguaje llegó a su respuesta, totalmente separado del valor final extraído.
Aquí está la clave. Puedes anidar estos módulos built-in para crear programas complejos, de forma muy parecida a como apilarías capas de redes neuronales en PyTorch. Podemos construir un pipeline de retrieval multi-hop para ver esto en acción. Empiezas definiendo una clase personalizada. En su fase de inicialización, declaras los módulos más pequeños que vas a necesitar. Para una arquitectura multi-hop, podrías declarar un módulo generador de queries usando Chain of Thought, y un módulo de síntesis de respuestas usando el Predict estándar. Luego, defines un método forward para enrutar los datos entre ellos.
El método forward recibe una pregunta inicial del usuario. Le pasa esa pregunta a tu módulo generador de queries, que devuelve una query de búsqueda. Ejecutas esa búsqueda en tu base de datos para recuperar un documento. Si necesitas un segundo hop, vuelves a pasar el documento recuperado y la pregunta original al módulo generador de queries para producir una query de búsqueda más refinada. Finalmente, pasas todos los documentos recuperados y la pregunta del usuario al módulo de síntesis de respuestas para generar la respuesta final. Acabas de crear un grafo ejecutable personalizado a partir de componentes modulares.
Cuando encadenas múltiples llamadas de esta manera, es crucial ver exactamente qué se le está enviando al modelo por debajo. DSPy hace un seguimiento del uso de tu modelo de lenguaje a nivel global. Puedes llamar al comando inspect history en tu objeto del modelo de lenguaje para imprimir las interacciones más recientes. Esto renderiza el string exacto que recibió el modelo y el string exacto que generó, asegurando que tu pipeline compuesto esté ensamblando el contexto correctamente.
Al separar la definición de tu tarea en signatures y tu estrategia de ejecución en módulos parametrizados, transformas el prompting de una tarea de edición de texto a una decisión arquitectónica, lo que te permite mejorar la capacidad de razonamiento de tu pipeline simplemente cambiando el nombre de una clase.
¡Gracias por escuchar, happy coding a todos!
5
Conectando modelos con Adapters
4m 40s
Comprende el papel de los Adapters en DSPy. Este episodio explica cómo ChatAdapter y JSONAdapter cierran la brecha entre las firmas abstractas de DSPy y los mensajes reales de múltiples turnos enviados a las APIs de los modelos de lenguaje.
Hola, soy Alex de DEV STORIES DOT EU. Aprendiendo DSPy, episodio 5 de 15. Escribes una signature limpia y fuertemente tipada en tu código, se la pasas a un modelo de lenguaje y, de alguna manera, te devuelve un objeto Python perfectamente estructurado. Entre tu código limpio y la API de raw text del modelo hay una capa de traducción caótica. Esa capa oculta es el concepto de conectar modelos con Adapters.
Antes de ver cómo funcionan, vamos a aclarar una confusión muy común. Puede que confundas los Adapters con los Modules. Los Modules gestionan la estrategia de razonamiento. Deciden si el modelo debe usar Chain of Thought o depender de herramientas externas. A los Adapters les da igual la estrategia. Un Adapter es puramente la capa de traducción. Se encarga de la raw string y la serialización JSON que realmente se envía por la red a la API del modelo.
Los modelos de lenguaje no entienden las signatures declarativas. Esperan arrays de mensajes multi-turno que contengan roles y bloques de texto específicos. El Adapter hace de puente. La herramienta por defecto para esto en DSPy es el ChatAdapter. Cuando invocas un Module, el ChatAdapter intercepta tu signature y la formatea como un historial de chat estándar. La instrucción principal de la signature se mapea directamente en el system prompt. Tus campos de input se recopilan y se colocan en el mensaje del usuario.
Aquí está la clave. El ChatAdapter utiliza marcadores de texto específicos para mantener tus inputs estrictamente organizados. Envuelve cada nombre de campo entre dobles corchetes y dobles almohadillas. Si tu campo de input se llama context, el modelo de lenguaje ve un marcador con corchetes y almohadillas rodeando la palabra context, seguido inmediatamente por los datos reales de context. Este límite visual evita que el modelo confunda sin querer tus instrucciones del sistema con el texto del input del usuario. Repite este patrón para los campos de output esperados, indicando al modelo en el prompt que genere exactamente esos mismos marcadores en su respuesta.
Imagina un escenario en el que estás extrayendo noticias de ciencia. Tu input es un artículo en raw text, y tu output tiene que coincidir con una clase de Pydantic con campos específicos para el titular y el descubrimiento científico principal. Cuando pasas este requisito por el ChatAdapter, este inspecciona tu clase Pydantic, genera un JSON schema completo y lo inyecta directamente en el system prompt. Le dice explícitamente al modelo de lenguaje cómo formatear su respuesta de texto. Cuando el modelo por fin responde, el ChatAdapter captura la raw text string. Busca los marcadores de output esperados, extrae el bloque de texto que hay entre ellos, y parsea esos datos para convertirlos en los objetos Python exactos que requiere tu aplicación.
Eso cubre los inputs y el parsing para las interacciones basadas en texto. Pero los modelos de lenguaje modernos a menudo tienen soporte nativo para structured output. Aquí es donde entra en juego el JSONAdapter. En lugar de modificar a fondo el system prompt y depender de marcadores de texto, el JSONAdapter toma una ruta más directa. Delega las restricciones de formato al JSON mode nativo o a la structured outputs API del proveedor del modelo. El modelo se ve obligado a nivel de protocolo a devolver un objeto JSON válido que contenga todos los campos de output que has pedido. Como la API del modelo gestiona la estructura de forma nativa, evita que el Adapter tenga que buscar marcadores de string en el raw text. Si tu modelo de destino soporta esta capacidad, cambiar tu pipeline para usar el JSONAdapter suele resultar en una menor latencia y un parsing significativamente más fiable.
El Adapter es la frontera rígida entre la lógica determinista de tu aplicación y la generación de texto no estructurado del modelo de lenguaje. Al controlar exactamente cómo se serializan los inputs y se parsean los outputs, los Adapters aseguran que tu pipeline nunca se rompa por culpa de una string mal formateada.
¡Gracias por escuchar, feliz programación a todos!
6
Gestión de datos con Examples
4m 06s
Aprende cómo DSPy maneja los datasets para machine learning. Este episodio cubre el objeto dspy.Example, distinguiendo entre claves de entrada y etiquetas, y preparando datos para evaluación y optimización.
Hola, soy Alex de DEV STORIES DOT EU. Aprendiendo DSPy, episodio 6 de 15. Ejecutas un optimizador para mejorar el pipeline de tu modelo de lenguaje, pero obtiene una puntuación perfecta en el primer intento. Al revisarlo con más detalle, te das cuenta de que accidentalmente introdujiste las respuestas objetivo directamente en el prompt. Para tratar los modelos de lenguaje como componentes tradicionales de machine learning, necesitas una forma infalible de gestionar tus sets de training, dev y test sin que se filtren las respuestas. La gestión de datos con Examples en DSPy resuelve precisamente esto.
En DSPy, la estructura de datos fundamental es el objeto Example. Lo utilizas para construir todos tus datasets. A simple vista, actúa de forma muy similar a un diccionario estándar de Python. Creas uno pasando pares clave-valor que representan tus datos. Tomemos una tarea de resumen. Creas un nuevo objeto Example y le das dos campos. Asignas un string largo a un campo llamado report, y un string corto a un campo llamado summary. Puedes leer estos valores en cualquier momento usando la dot notation estándar, pidiendo el campo report o el campo summary directamente del objeto.
Es común tratar el objeto Example como un simple wrapper de diccionario, pero usar un diccionario normal romperá el proceso de compilación. Cuando pasas un dataset a un optimizador de DSPy, el compilador necesita separar lo que entra en el pipeline de lo que se usa para evaluar el pipeline. Requiere límites explícitos entre los datos de entrada y las respuestas esperadas.
Aquí es donde la cosa se pone interesante. El objeto Example controla estos límites usando un método específico llamado with_inputs. Cuando instancias tu Example que contiene el report y el summary, haces chain del método with_inputs justo al final. Le pasas el string report. Esto etiqueta explícitamente el campo report como los datos de entrada. Cualquier campo que no especifiques en este método se convierte automáticamente en un label. El optimizador ahora sabe que solo debe enviar el report a tu pipeline. El summary permanece completamente oculto durante la inferencia.
Una vez que tienes un solo Example configurado, agrupas múltiples Examples en listas estándar de Python para formar tus sets de training, dev y test. Como DSPy plantea el prompt engineering como un problema de optimización de machine learning, tener estos datasets claramente particionados es un requisito estricto. Cuando el optimizador ejecuta tu pipeline sobre el training set, procesa un Example a la vez. Extrae los labels, pasa los inputs hacia adelante, captura el output generado y luego evalúa el resultado.
Cuando escribas métricas de evaluación personalizadas, necesitarás acceder a estos campos separados. El objeto Example proporciona dos métodos para esta extracción. Llamar al método inputs devuelve un diccionario que contiene solo los datos que marcaste como inputs. Llamar al método labels devuelve un diccionario que contiene los datos objetivo ocultos. Tu función de evaluación llama al método labels para recuperar el summary objetivo, lo compara con el texto generado y asigna una puntuación basada en lo bien que coinciden.
Configurar correctamente tus objetos Example garantiza que tu pipeline realmente aprenda a mapear inputs a outputs. La estricta separación de inputs y labels evita la fuga de datos durante la optimización, asegurando que tu sistema mejore en lugar de simplemente memorizar las respuestas proporcionadas.
Gracias por escuchar, ¡feliz programación a todos!
7
Definiendo el éxito con métricas
4m 12s
Descubre cómo evaluar programas de DSPy utilizando métricas. Este episodio te enseña a escribir funciones personalizadas en Python para puntuar salidas, usar el argumento trace e incluso aprovechar la IA como juez para evaluaciones de formato largo.
Hola, soy Alex de DEV STORIES DOT EU. Aprendiendo DSPy, episodio 7 de 15. No puedes mejorar de forma consistente lo que no puedes medir, y al trabajar con modelos de lenguaje, confiar en la intuición humana para medir la calidad del output simplemente no escala. Para reescribir automáticamente tus prompts o ajustar tu sistema, el compilador necesita una guía matemática, y eso es exactamente lo que aporta definir el éxito con métricas.
En DSPy, una métrica es una función estándar de Python. Recibe dos argumentos principales. El primero es un ejemplo, que representa el input de referencia y el output esperado de tu dataset. El segundo es una predicción, que es la respuesta real generada por tu programa DSPy. La función de la métrica compara la predicción con el ejemplo y devuelve un score. Este score suele ser un float, un integer, o un simple valor boolean como true o false.
Para tareas básicas de clasificación, tu métrica podría ser lógica directa de Python. Podrías escribir una función de coincidencia exacta que compruebe si el string predicho es perfectamente igual al string esperado. Para ejecutar esta medición sistemáticamente en tus datos, DSPy proporciona una utilidad integrada llamada Evaluate. A esta utilidad le pasas tu dataset de desarrollo, tu función de métrica y parámetros de ejecución como el número de threads en paralelo. La utilidad Evaluate ejecuta tu métrica sobre cada predicción, agrega los resultados y devuelve un único score numérico que representa el rendimiento general de tu sistema.
Sin embargo, la coincidencia exacta es casi siempre demasiado rígida para las tareas de lenguaje generativo. Aquí es donde pasas de simples comprobaciones de strings a usar feedback de IA, un patrón comúnmente conocido como LLM-as-a-judge. Como los módulos de DSPy son simplemente código Python, tu función de métrica puede instanciar y llamar a un programa DSPy independiente y más pequeño para evaluar outputs semánticos complejos.
Considera un escenario concreto. Estás construyendo un sistema que genera un tuit promocional para responder a la pregunta de un usuario. Una métrica de coincidencia exacta falla por completo en este caso, ya que un buen tuit puede formularse de innumerables maneras válidas. En su lugar, tu función de métrica debería evaluar múltiples dimensiones del output. Primero, utiliza una comprobación básica de longitud de Python para asegurar que el texto generado tenga menos de doscientos ochenta caracteres. Segundo, comprueba si el texto contiene la respuesta factual requerida por el ejemplo. Finalmente, pasa el texto generado a una signature especializada de DSPy que le pide a un modelo de lenguaje más pequeño que evalúe si el tuit es atractivo. Tu función de métrica combina entonces la comprobación de longitud, la verificación de hechos y el score de engagement del modelo de lenguaje en un único valor matemático final.
Cuando finalmente comiences a compilar y optimizar estos programas, tu función de métrica debe aceptar un tercer argumento opcional. Se llama trace. Los oyentes suelen confundir el argumento trace con un log de debugging que imprime errores por consola o el historial de ejecución. Eso es exactamente lo que no es. El argumento trace es un objeto específico que utiliza el compilador de DSPy durante la optimización para validar los pasos de razonamiento intermedios. Si tu programa encadena múltiples llamadas a modelos de lenguaje, el trace contiene la ruta de razonamiento específica que tomó el modelo para llegar al final. Al acceder al trace dentro de tu métrica, tu función puede verificar no solo que el tuit final fue bueno, sino que los pasos intermedios utilizados para redactarlo fueron lógicamente sólidos.
Esta es la parte que importa. Tu métrica define estrictamente cómo se ve el éxito, y el compilador de DSPy optimizará implacablemente tu sistema para maximizar ese score específico. Si tu métrica tiene fallos, tu programa compilado tendrá fallos exactamente de la misma manera. ¡Gracias por escuchar, happy coding a todos!
8
Una introducción a los Optimizers
3m 54s
Adéntrate en la magia central de DSPy: los Optimizers. Este episodio ofrece una visión general de lo que hacen los optimizadores, el ciclo de optimización iterativo y la inusual estrategia de división de datos 20/80 para la optimización de prompts.
Hola, soy Alex de DEV STORIES DOT EU. Aprendiendo DSPy, episodio 8 de 15. Imagina que escribes un programa y, en lugar de ajustar manualmente su lógica interna para que pase tus tests, un compilador reescribe automáticamente las instrucciones para que el programa funcione mejor por sí solo. No tocas el código. Solo proporcionas el test set. Eso es exactamente lo que hace DSPy mediante un concepto llamado Optimizers.
Los Optimizers, que antes se llamaban teleprompters en versiones anteriores del framework, son algoritmos que ajustan los parámetros de tu programa. En el machine learning tradicional, los parámetros son los pesos de la red neuronal. En DSPy, los parámetros son principalmente los prompt strings y las instrucciones que se envían al modelo de lenguaje, aunque también pueden incluir pesos. La función del optimizer es ajustar estos parámetros para maximizar una métrica que ya has definido. Este proceso ocurre antes de hacer deploy de tu aplicación. Es puro compute en tiempo de pre-inferencia. Inviertes potencia de procesamiento por adelantado para encontrar las mejores instrucciones, de modo que tu aplicación funcione con precisión más adelante.
Cuando escuchas la palabra optimizer, podrías pensar que necesitas datasets enormes, como harías para el fine-tuning de un modelo tradicional. Pues no. Los prompt optimizers son muy eficientes. Normalmente solo requieren entre treinta y trescientos ejemplos. Como el dataset es tan pequeño, DSPy recomienda un enfoque inusual para dividir tus datos. En lugar del split estándar ochenta-veinte, donde la mayor parte de los datos va para training, le das la vuelta. Usas el veinte por ciento para training y el ochenta por ciento para validación. Si tienes cincuenta ejemplos, le das diez al optimizer para construir los prompts y usas los cuarenta restantes para evaluar si esos prompts realmente generalizan. Este split inverso evita que el optimizer haga overfitting de los prompts generados en un set muy pequeño de inputs.
Aquí está la clave. El ciclo de desarrollo iterativo en DSPy gira en torno a ejecutar este loop de optimización. Veamos un escenario concreto. Estás creando un bot básico de question answering. Primero, defines tu programa DSPy y tu métrica. Luego, recopilas tu dataset de cincuenta preguntas sin etiquetar. Haces un split de estos datos, pasando la pequeña porción de training a un objeto optimizer. Le dices al optimizer que compile tu programa usando tus datos de training y tu métrica. El optimizer se ejecuta, experimentando internamente con diferentes estructuras de prompts. Comprueba los outputs contra tu métrica, aprende qué funciona y refina los prompts.
Cuando el optimizer termina, devuelve una nueva versión compilada de tu programa. Este programa compilado contiene los parámetros recién ajustados. No necesitas ejecutar este paso de optimización cada vez que se inicia tu aplicación. En su lugar, llamas al método save del programa compilado, proporcionando una ruta de archivo. Esto escribe todos los prompts y configuraciones optimizados en un archivo JSON estándar. Cuando haces deploy de tu aplicación a producción, tu código simplemente instancia el programa base y llama al método load, apuntando a ese mismo archivo JSON. Tu bot está listo de inmediato para responder preguntas usando las instrucciones optimizadas. El verdadero poder de los optimizers de DSPy es que desacoplan la lógica de tu aplicación del fraseo exacto de tus prompts, dejando que el compute encuentre las mejores palabras por ti. ¡Gracias por escuchar, happy coding a todos!
9
Few-Shot Learning automático
3m 56s
Aprende cómo DSPy automatiza el prompting few-shot. Este episodio se centra en BootstrapFewShot y BootstrapFewShotWithRandomSearch, explicando cómo sintetizan, filtran e inyectan ejemplos de alta calidad en tus prompts.
Hola, soy Alex de DEV STORIES DOT EU. Aprendiendo DSPy, episodio 9 de 15. Seleccionar manualmente los mejores ejemplos para un prompt es tedioso y propenso a sesgos. Adivinas qué ejemplos importan, los hardcodeas en tus strings y esperas que el modelo preste atención. DSPy sintetiza, prueba y selecciona activamente las demostraciones perfectas para ti. Este proceso es few-shot learning automático, y DSPy lo gestiona mediante tres optimizadores específicos.
El enfoque más sencillo es LabeledFewShot. Proporcionas un conjunto de ejemplos de entrenamiento etiquetados. El optimizador selecciona aleatoriamente un subconjunto de estos pares de input y output y los inserta directamente en tus prompts como demostraciones. Esto le da al modelo un patrón básico a seguir. Funciona bien si tus datos de entrenamiento coinciden exactamente con los pasos intermedios que necesita tu programa. Normalmente, no es así.
Esto nos lleva a BootstrapFewShot. Un error común es pensar que BootstrapFewShot simplemente elige ejemplos al azar de tu training set. No lo hace. Genera activamente pasos de razonamiento intermedios que nunca existieron en tu raw data.
Así es como fluye el proceso de bootstrapping. El optimizador requiere un programa teacher. Por defecto, esta es simplemente la versión sin optimizar y zero-shot de tu propio programa. El teacher recorre tus ejemplos de entrenamiento. Para cada ejemplo, intenta generar una respuesta. Luego, DSPy pasa esa respuesta a tu métrica de evaluación. Si la métrica dice que el output es correcto, DSPy guarda el trace completo de esa ejecución exitosa. Este trace incluye el input, el output y, lo que es crucial, cualquier trabajo intermedio que el programa haya hecho para llegar hasta ahí.
Imagina un clasificador de sentimientos. Tu raw dataset contiene solo reseñas de clientes y una etiqueta positiva o negativa. Tu programa DSPy le pide al modelo de lenguaje que use razonamiento chain-of-thought antes de devolver el sentimiento. Durante el bootstrapping, el teacher lee una reseña y escribe un párrafo de razonamiento antes de adivinar el sentimiento. Si la predicción final coincide con la etiqueta verdadera, ese razonamiento generado se considera exitoso. El optimizador recopila estos traces exitosos. Toma cuatro de ellos y los inyecta en futuros prompts. Tu clasificador zero-shot es ahora un clasificador four-shot experto, completo con pasos de razonamiento sintetizados.
BootstrapFewShot se detiene una vez que encuentra suficientes traces exitosos. Pero los primeros traces exitosos no siempre son los mejores.
Aquí está la clave. BootstrapFewShotWithRandomSearch resuelve esto ejecutando todo el proceso de bootstrap varias veces. En cada ejecución, extrae una submuestra aleatoria de tus datos de entrenamiento. Esto crea varios conjuntos candidatos diferentes de demostraciones few-shot. Luego, el optimizador toma todos estos conjuntos candidatos y los evalúa contra tus datos de validación. Prueba qué combinación específica de demostraciones produce la puntuación general más alta. Descarta los conjuntos débiles y conserva el ganador matemático.
El verdadero poder del few-shot learning automático no es solo ahorrarte tiempo escribiendo prompts, sino descubrir rutas de razonamiento intermedias exitosas que tu dataset nunca contuvo explícitamente.
¡Gracias por escuchar, feliz programación a todos!
10
Optimización de instrucciones con MIPROv2
4m 08s
Sumérgete en el ajuste automático de instrucciones. Este episodio explora MIPROv2 y COPRO, mostrando cómo DSPy utiliza la optimización bayesiana y el ascenso de coordenadas para descubrir instrucciones de prompt superiores y contraintuitivas.
Hola, soy Alex de DEV STORIES DOT EU. Aprendiendo DSPy, episodio 10 de 15. A veces, la instrucción más efectiva para un modelo de lenguaje resulta incomprensible para un humano. Pasas horas elaborando meticulosamente la frase perfecta, solo para descubrir que un prompt automatizado y ligeramente inconexo supera con creces tu trabajo. Por eso, deberías dejar que los algoritmos escriban tus prompts. Vamos a ver la optimización de instrucciones con MIPROv2.
Primero, olvídate del prompt templating. No se trata de intercambiar variables en un string estático. La optimización de instrucciones en realidad reescribe las instrucciones del sistema que rigen tu pipeline. Algoritmos anteriores como COPRO y SIMBA abordaban esto generando variaciones paso a paso de los prompts y refinándolas con el tiempo. MIPROv2 lleva este concepto mucho más allá al tratar las instrucciones y los ejemplos few-shot como un espacio de búsqueda unificado.
MIPROv2 opera en tres etapas distintas. La primera etapa es el bootstrapping. El optimizador ejecuta tu programa sin optimizar sobre tus datos de entrenamiento para crear un conjunto de traces de ejecución. Estos traces contienen los inputs reales, los pasos intermedios y los outputs que fluyen a través de tu sistema.
La segunda etapa es la propuesta fundamentada. El optimizador no adivina nuevas instrucciones a ciegas. Utiliza un modelo de lenguaje independiente, llamado prompter, para analizar esos traces generados. Al analizar dónde tuvo éxito y dónde falló tu pipeline, el prompter elabora un conjunto de nuevas instrucciones candidatas. Estos candidatos se basan directamente en el comportamiento real de tu programa, no en templates genéricos.
La tercera etapa es la búsqueda discreta. MIPROv2 evalúa las nuevas instrucciones junto con diferentes combinaciones de traces few-shot. Para hacerlo de manera eficiente, se basa en la optimización bayesiana. En lugar de probar por fuerza bruta todas las combinaciones posibles, MIPROv2 construye un surrogate model. Este surrogate model actúa como un proxy ligero. Predice qué combinaciones de instrucciones y traces producirán la puntuación más alta en tu métrica de evaluación específica.
La optimización bayesiana permite que el surrogate model mapee el espacio de prompts y demostraciones. Calcula la mejora esperada al probar una nueva combinación. Esto equilibra sistemáticamente la exploración de instrucciones no probadas con el aprovechamiento de las combinaciones que ya obtienen buenos resultados. El optimizador se centra en la configuración óptima sin ejecutar miles de llamadas de red redundantes.
Imagina un escenario concreto. Creas un agente ReAct para responder consultas complejas. Inicialmente, su precisión es del 24 %. Pasas este agente a MIPROv2, lo configuras para que se ejecute en modo ligero y le proporcionas un dataset de 500 preguntas. El optimizador hace el bootstrapping de los traces, propone instrucciones fundamentadas y busca en el espacio utilizando el surrogate model. Al finalizar, la precisión de tu agente salta del 24 % al 51 %. Es probable que el prompt final que impulsa este salto de rendimiento contenga instrucciones y selecciones de traces que un humano jamás habría ideado.
Aquí está la clave. MIPROv2 elimina el cuello de botella de la intuición humana. Trata tus instrucciones en lenguaje natural exactamente igual que pesos ajustables en un modelo matemático, transformando la creación de prompts de un arte impredecible a un problema de optimización determinista. ¡Gracias por escuchar, feliz programación a todos!
11
Finetuning con BootstrapFinetune
3m 44s
Descubre cómo destilar modelos de lenguaje masivos en otros más pequeños y eficientes. Este episodio cubre BootstrapFinetune, explicando cómo convertir un programa de DSPy basado en prompts en un modelo personalizado con actualización de pesos.
Hola, soy Alex de DEV STORIES DOT EU. Aprendiendo DSPy, episodio 11 de 15. El prompting de modelos masivos es ideal para arrancar un prototipo. Pero cuando esa lógica llega a producción, la latencia y el coste de un modelo de setenta mil millones de parámetros se convierten rápidamente en un problema. Necesitas la potencia de razonamiento del modelo grande, pero la velocidad y el precio de un modelo de ocho mil millones de parámetros. Eso es exactamente lo que gestiona BootstrapFinetune.
BootstrapFinetune compila tu programa DSPy en un modelo fine-tuned. Sirve como la optimización definitiva para la eficiencia. Actualiza los pesos reales de un modelo objetivo más pequeño para imitar el comportamiento exacto de tu pipeline pesado.
Un error común es pensar que para hacer fine-tuning a un modelo, tienes que recopilar manualmente miles de ejemplos, formatearlos en archivos JSONL tediosos y supervisar un training loop. BootstrapFinetune automatiza esto por completo. Gestiona la generación del dataset, el formateo y las actualizaciones de pesos totalmente a través de las traces de ejecución de tu programa.
Imagina un escenario concreto con un clasificador de intenciones bancarias. El programa recibe un mensaje desordenado del cliente y lo categoriza. Inicialmente, creas un módulo DSPy usando un modelo muy capaz como GPT-4o-mini, configurado para usar razonamiento chain-of-thought. El modelo piensa paso a paso en la frase del cliente antes de devolver el output de la intención. Obtiene las respuestas correctas, pero es demasiado lento y caro para un chat en tiempo real.
Para optimizar esto, inicializas BootstrapFinetune. Le pasas tu métrica de evaluación para medir el éxito, y especificas el modelo objetivo más pequeño y barato al que quieres hacer deploy. Luego, compilas el programa.
Cuando le das a compilar, DSPy ejecuta tu programa sin optimizar sobre tus datos de entrenamiento. Utiliza el modelo teacher pesado para generar los outputs. El optimizador observa esta ejecución. Cada vez que el modelo teacher obtiene la respuesta correcta según tu métrica, BootstrapFinetune captura la trace. Registra los inputs, el razonamiento paso a paso y el output final. Mapea la lógica interna del modelo masivo a un formato que el modelo objetivo pequeño pueda procesar.
Una vez que se recopilan suficientes traces exitosas, BootstrapFinetune las estructura automáticamente en un dataset de entrenamiento. Luego, activa el proceso de fine-tuning en tu modelo objetivo. El modelo pequeño se entrena directamente sobre las rutas de razonamiento de alta calidad generadas por el modelo grande.
Aquí está la clave. El modelo más pequeño aprende la distribución específica de la tarea y el estilo de razonamiento necesario para resolverla sin ejecutar el pesado chain-of-thought en tiempo de inferencia. En nuestro ejemplo del clasificador bancario, un modelo pequeño estándar podría alcanzar solo un sesenta y seis por ciento de precisión out of the box. Pero después de compilar con BootstrapFinetune, ese mismo modelo pequeño salta a un ochenta y siete por ciento de precisión.
El fine-tuning ya no es un proyecto de infraestructura independiente; es simplemente otro paso de compilación que convierte un pipeline de razonamiento caro en un recurso de producción rápido y barato.
¡Gracias por escuchar, feliz programación a todos!
12
Uso automatizado de herramientas con ReAct
4m 42s
Aprende a dar a los modelos de lenguaje acceso a herramientas externas. Este episodio cubre el módulo dspy.ReAct, demostrando cómo construir agentes autónomos que razonan e interactúan con APIs de forma dinámica.
Hola, soy Alex de DEV STORIES DOT EU. Aprendiendo DSPy, episodio 12 de 15. Darle a un LLM acceso a APIs externas lo hace increíblemente capaz, pero escribir el loop para gestionar su razonamiento, parsear los outputs y recuperarse de los errores de ejecución es un gran quebradero de cabeza. La solución es el tool use totalmente automatizado con el módulo DSPy ReAct.
La gente a menudo confunde ReAct con el function calling básico. El function calling es simplemente el mecanismo de la API que permite a un modelo de lenguaje formatear su output como una request de datos estructurados. ReAct es un paradigma de comportamiento específico. Viene de Reason y Act. Es un loop de ejecución donde el modelo pasa por tres pasos distintos: un Thought, una Action y una Observation.
El módulo DSPy ReAct gestiona completamente esta orquestación por ti. Tú no escribes el loop de ejecución. No haces catch manualmente de las excepciones de la API. ReAct envuelve una signature de DSPy y una lista de tools, transformando un prompt estático en un agente autónomo.
Para usarlo, primero defines tus tools. En DSPy, las tools son simplemente funciones estándar de Python. Escribes una función, defines sus parámetros de entrada y proporcionas un docstring claro. Ese docstring es crítico. DSPy extrae el nombre de la función y el docstring, pasándoselos al modelo de lenguaje para que sepa exactamente qué hace la tool y cuándo hacerle deploy.
Considera un escenario donde creas un agente básico de búsqueda y del tiempo. Escribes una función de Python llamada get weather que acepta el nombre de una ciudad como un string y consulta una API para devolver la temperatura actual. Instancias el módulo dspy dot ReAct, pasándole una signature estándar de pregunta y respuesta junto con una lista que contiene tu función get weather.
Cuando le preguntas al módulo qué tiempo hace en Tokio, comienza el loop de ReAct. Primero, el modelo genera un Thought. Razona que necesita datos meteorológicos actuales para Tokio. A continuación, genera una Action. Decide llamar a tu tool get weather, pasándole Tokio como argumento.
Aquí está la clave. Tú no ejecutas esa función. El módulo DSPy ReAct intercepta la Action del modelo, ejecuta tu función de Python en segundo plano y captura el output. Si la función tiene éxito, DSPy le devuelve los datos de temperatura al modelo como una Observation. Si el modelo alucina un parámetro o la función lanza un error de Python, DSPy hace catch de ese error y devuelve el mensaje de error como la Observation. El modelo lee el error, genera un nuevo Thought para corregir su error e intenta una nueva Action.
Una vez que el modelo observa los datos de temperatura correctos, reconoce que su objetivo se ha cumplido. Sale del loop y formatea la respuesta final para el usuario.
Para evitar ejecuciones descontroladas, este ciclo está estrictamente limitado por un parámetro llamado max iters, que viene de maximum iterations. Este parámetro dicta cuántos ciclos de Thought, Action y Observation se le permite realizar al módulo. Si el modelo tiene dificultades para encontrar los datos correctos y alcanza el límite de iteraciones, ReAct le obliga a detener la búsqueda y generar una respuesta final utilizando únicamente la información que ha recopilado con éxito.
El verdadero poder de este módulo es que abstrae el control flow frágil y propenso a errores de los loops de agentes, dejándote tratar el razonamiento complejo aumentado por tools como un componente predecible más en tu pipeline.
¡Gracias por escuchar, feliz programación a todos!
13
Manejo manual de herramientas para mayor control
4m 16s
Toma el control total sobre la ejecución de herramientas. Este episodio cubre el manejo manual de herramientas en DSPy utilizando dspy.Tool, dspy.ToolCalls y native function calling para aplicaciones sensibles a la latencia.
Hola, soy Alex de DEV STORIES DOT EU. Aprendiendo DSPy, episodio 13 de 15. Los agentes automatizados son geniales cuando tienes una tarea flexible y abierta. Pero cuando necesitas un control absoluto y determinista sobre exactamente cómo, cuándo y si se ejecuta una función externa, darle el volante por completo al modelo de lenguaje es demasiado arriesgado. Tienes que abrir el capó y gestionar la ejecución tú mismo. Ahí es exactamente donde entra en juego el manejo manual de tools para tener el control.
Usar un loop de agente automatizado abstrae la capa de ejecución, lo que puede causar latencia impredecible u ocultar errores de runtime. El manejo manual es la alternativa para los power users. Te devuelve el control sobre la recuperación de errores, los límites de timeout y el orden exacto de ejecución. Para montar esto en DSPy, empiezas envolviendo una función estándar de Python usando la clase dspy.Tool. Imagina que tienes una función de Python que hace de calculadora para multiplicar dos números. Le pasas esta función a dspy.Tool. Si tu función maneja queries de base de datos o peticiones de red, también puedes envolver funciones asíncronas, y la clase tool gestionará la ejecución async de forma nativa.
Una vez que tu tool de calculadora esté lista, tienes que exponerla al modelo de lenguaje. Esto lo haces pasándole una lista que contenga tu tool directamente a un módulo Predict de DSPy. Defines un parámetro llamado tools en tu llamada a Predict. Cuando el modelo procesa el input, evalúa el prompt y decide si necesita la calculadora para generar la respuesta final.
Cuando el modelo decide usar tu tool, se apoya en un mecanismo subyacente llamado Adapter. Por defecto, DSPy usa un JSONAdapter. Este adapter traduce automáticamente tu tool de Python al formato nativo de function calling que requiere la API del modelo de lenguaje específico que estés usando. Esto asegura que el modelo devuelva un JSON estructurado y fiable cuando solicita una tool. Presta atención a esta parte. Es fácil asumir que usar tool calling nativo produce automáticamente outputs de mayor calidad. La documentación de DSPy advierte explícitamente que esto es un error. El tool calling nativo da mayor fiabilidad para la sintaxis de la request, pero no garantiza una mejor calidad de razonamiento que el parsing estándar basado en texto. El modelo no se vuelve más inteligente de repente solo por formatear JSON.
Como estás gestionando este proceso manualmente, el modelo en realidad no ejecuta la calculadora. Se para y devuelve una respuesta que contiene su intención. Accedes a esta intención inspeccionando response punto outputs punto tool calls. Esta propiedad devuelve un objeto dspy.ToolCalls, que se comporta como una lista de instrucciones. Cada elemento de esta lista especifica qué tool quiere usar el modelo y los argumentos exactos que ha generado, como cinco y diez para la calculadora.
A continuación, escribes un loop estándar de Python para iterar sobre estas tool calls solicitadas. Para cada call, invocas manualmente su método execute. Invocar execute lanza tu código Python real usando los argumentos generados por el modelo y devuelve el resultado. Si los argumentos no son válidos, o si la calculadora lanza un error, tu loop de Python lo captura. Gestionas el fallo bajo tus propios términos, en lugar de esperar que un loop automatizado se recupere por sí solo.
El manejo manual de tools separa limpiamente la decisión del modelo de pedir una acción de la ejecución física de esa acción, dándole a tu aplicación la fiabilidad determinista que requieren los entornos de producción. ¡Gracias por escuchar, happy coding a todos!
14
Integración de herramientas con MCP
4m 06s
Conecta tus agentes a servidores de herramientas universales. Este episodio explica cómo usar el Model Context Protocol (MCP) en DSPy para aprovechar herramientas estandarizadas en diferentes frameworks con una configuración mínima.
Hola, soy Alex de DEV STORIES DOT EU. Aprendiendo DSPy, episodio 14 de 15. Cada vez que adoptas un nuevo framework de IA, normalmente terminas reescribiendo los mismos wrappers personalizados de Python para tus consultas de bases de datos, búsquedas web y lectores de archivos. En lugar de mantener un sinfín de API wrappers duplicados, ¿qué pasaría si tus agentes pudieran conectarse instantáneamente a tool servers universales y estandarizados? Esa es la promesa del Model Context Protocol, y hoy veremos cómo integrar herramientas con MCP.
El Model Context Protocol es un estándar abierto introducido por Anthropic. Proporciona una forma universal de conectar modelos de IA a fuentes de datos y herramientas externas. Al adoptar este estándar, los desarrolladores escriben una herramienta una sola vez, la alojan en un servidor MCP y la utilizan en cualquier framework compatible. DSPy lo soporta de forma nativa. No necesitas escribir adapter classes complejas para incorporar estas herramientas externas a tus programas DSPy.
Un error común es pensar que DSPy gestiona por sí mismo las conexiones subyacentes del servidor para estas herramientas. No es así. DSPy depende completamente del paquete oficial de Python mcp para gestionar la red y establecer la conexión. DSPy solo interviene al final para convertir el tool object activo de MCP a un formato de herramienta nativo de DSPy.
Para ver cómo funciona, vamos a repasar cómo conectar un servidor de herramientas local a un agente DSPy. Primero, necesitas un servidor MCP en ejecución. En tu script de Python, importas la clase de parámetros del servidor desde el paquete MCP. Si ejecutas un proceso local, defines los parámetros del servidor standard IO y lo apuntas a tu ejecutable del servidor. Por otro lado, si tus herramientas viven en un servidor remoto, configuras una conexión HTTP client. Ambos métodos establecen cómo se comunicará tu aplicación con el proveedor de herramientas.
A continuación, utilizas la librería MCP para abrir una client session. Dentro de este session context, inicializas la conexión. En este punto, DSPy sigue estando completamente al margen. Le pides a la sesión activa de MCP que liste sus herramientas disponibles. El servidor responde con una lista de tool objects. Cada objeto contiene el nombre de la herramienta, una descripción de lo que hace y los input arguments esperados.
Ahora conectas ambas partes. Aquí es donde se pone interesante. Por cada herramienta que devuelve el servidor, llamas al método from mcp tool en la clase base DSPy Tool. Le pasas a este método dos argumentos específicos: el raw tool object y la client session activa. Este único comando lee el schema proporcionado por el servidor MCP y lo envuelve instantáneamente en una interfaz compatible. Ahora tienes una lista de herramientas nativas de DSPy listas para usar.
Finalmente, le pasas esa lista de herramientas recién convertida a un agente. Inicializas un módulo ReAct y le pasas tu array de herramientas de DSPy. Cuando ejecutas el agente, ahora puede llamar sin problemas a las herramientas externas de MCP. Los argumentos fluyen desde el módulo ReAct, a través del wrapper convertido de DSPy, por la client session de MCP hasta el servidor, y el resultado vuelve para informar el siguiente paso de razonamiento.
El verdadero poder de esta integración es el desacoplamiento. Tus módulos DSPy pueden acceder de forma segura a bases de datos empresariales o file systems locales con cero custom wrapper code, asegurando a la vez que exactamente el mismo servidor de herramientas siga siendo totalmente utilizable por frameworks completamente diferentes.
¡Gracias por escuchar, feliz programación a todos!
15
Ensembles y metaoptimización
4m 14s
Lleva DSPy al límite. El episodio final cubre las transformaciones de programas a través de dspy.Ensemble y el metaoptimizador experimental BetterTogether, que combina el ajuste de prompts con el finetuning de pesos para obtener el máximo rendimiento.
Hola, soy Alex de DEV STORIES DOT EU. Aprendiendo DSPy, episodio 15 de 15. ¿Qué pasa cuando combinas la optimización bayesiana de prompts con el fine-tuning de pesos en deep learning? Dejas de tratar el prompt engineering y el entrenamiento de modelos como pasos aislados, y empiezas a tratarlos como un pipeline continuo. Aquí es donde alcanzas la vanguardia de la ingeniería de IA automatizada, basándote en Ensembles y Meta-Optimization.
Tenemos que aclarar algo de inmediato. Cuando escuchas la palabra ensemble, probablemente piensas en consultar cinco foundation models diferentes a la vez. En DSPy, un ensemble es algo completamente distinto. Aquí, un ensemble significa ejecutar múltiples programas optimizados sobre el mismo modelo de lenguaje subyacente. Combina diferentes estructuras de prompts y diferentes reasoning traces para agregar sus outputs.
La lógica aquí es muy sencilla. Durante una ejecución de optimización profunda, diferentes configuraciones suelen descubrir reasoning paths distintos e igualmente válidos para llegar a la respuesta correcta. Digamos que acabas de ejecutar el optimizador MIPROv2. Evalúa cientos de configuraciones y guarda un historial de las que mejor rinden. En lugar de quedarte solo con el programa de mayor puntuación y descartar el resto, extraes los cinco mejores programas candidatos. Se los pasas a la transformación Ensemble de DSPy. Cuando llega un nuevo input, el ensemble ejecuta los cinco programas. Agrega sus outputs, normalmente mediante votación por mayoría, y devuelve una respuesta final muy robusta. Básicamente, estás escalando tu compute en tiempo de inference para garantizar un resultado de mayor calidad.
Ejecutar un ensemble de cinco programas en un foundation model masivo te da una precisión increíble, pero es caro y lento. Aquí es donde entran en juego los meta-optimizers. Un meta-optimizer gestiona la ejecución de otros optimizadores, secuenciándolos para multiplicar sus beneficios. El mejor ejemplo en DSPy es BetterTogether.
BetterTogether superpone mejoras de forma sistemática. Te permite coger la enorme capacidad de razonamiento de tu ensemble y destilarla en un modelo rápido y con fine-tuning. Primero, configuras BetterTogether para usar la optimización de prompts y generar reasoning traces de altísima calidad a partir de tu ensemble pesado. Después, pasa automáticamente esos traces a un optimizador de pesos. El optimizador de pesos usa esos datos para hacer fine-tuning a los parámetros de un student model mucho más pequeño y barato. Finalmente, BetterTogether puede ejecutar una segunda ronda de optimización de prompts, esta vez adaptando las instrucciones específicamente a los pesos recién actualizados del student model.
Pasas de la optimización de prompts, a la optimización de pesos, y de vuelta a la optimización de prompts. El output es un modelo rápido y muy especializado que ha capturado los diversos reasoning paths del ensemble original, sin el enorme coste de inference. Superponer técnicas de optimización secuencialmente es la forma de cerrar la brecha entre un razonamiento pesado y caro, y una inference rápida y lista para producción.
Y con esto llegamos al final de la serie. Te animo muchísimo a explorar la documentación oficial de DSPy, a probar a construir estos pipelines de forma práctica, o a visitar devstories dot eu para sugerir temas que quieras que cubramos a continuación. ¡Gracias por escuchar, y feliz programación a todos!
Tap to start playing
Browsers block autoplay
Share this episode
Episode
—
Copy this episode in another language:
Este sitio web no utiliza cookies. Nuestro proveedor de alojamiento puede registrar tu dirección IP con fines analíticos. Saber más.