Building a multi-agent research system that actually works in production means solving three hard problems: durability (what happens when the orchestrator crashes mid-research?), tool integration (how do agents use search, browse, and analysis tools without drowning in glue code?), and self-improvement (how does the system get better at researching over time?). The Dapr Deep Research experiment from the lab-experiments repo tackles all three by combining three technologies that aren’t often seen together: Dapr for crash-recoverable workflows, DSPy 3.2+ for programmatic LM optimization, and MCP (Model Context Protocol) for standardized tool access. The result is a five-agent research platform that checkpoints its progress to Redis, survives process failures, and optionally distills expensive teacher models into cheaper students via DSPy compilation.
Construir un sistema de investigación multi-agente que realmente funcione en producción significa resolver tres problemas difíciles: la durabilidad (¿qué sucede cuando el orquestador falla a mitad de una investigación?), la integración de herramientas (¿cómo usan los agentes herramientas de búsqueda, navegación y análisis sin ahogarse en código repetitivo?), y la auto-mejora (¿cómo mejora el sistema investigando con el tiempo?). El experimento Dapr Deep Research del repositorio lab-experiments aborda los tres combinando tres tecnologías que no suelen verse juntas: Dapr para workflows recuperables ante caídas, DSPy 3.2+ para optimización programática de LMs, y MCP (Model Context Protocol) para acceso estandarizado a herramientas. El resultado es una plataforma de investigación de cinco agentes que checkpointea su progreso en Redis, sobrevive fallos de proceso y, opcionalmente, destila modelos teacher costosos en estudiantes más baratos mediante compilación DSPy.
Architecture: Hub-and-Spoke with Five Agents
Arquitectura: Hub-and-Spoke con Cinco Agentes
The system uses a hub-and-spoke architecture where a central ResearchWorkflow orchestrator dispatches specialized research agents via Dapr’s cross-app invocation (call_agent()). Each agent is a DurableAgent subclass wrapped in @workflow_entry, giving them automatic retry and state persistence. The five agents are:
El sistema usa una arquitectura hub-and-spoke donde un orquestador central ResearchWorkflow despacha agentes especializados de investigación mediante la invocación cruzada de Dapr (call_agent()). Cada agente es una subclase de DurableAgent envuelta en @workflow_entry, lo que les proporciona reintento automático y persistencia de estado. Los cinco agentes son:
- ExplorerAgent — Generates diverse research directions using dspy.RLM for discovery and dspy.BestOfN for top-3 selection. It produces FoundDirection[] objects with topics, relevance scores, and seed queries, then selects the most promising directions via a ChainOfThought ranker.
- ExplorerAgent — Genera direcciones diversas de investigación usando dspy.RLM para descubrimiento y dspy.BestOfN para selección top-3. Produce objetos FoundDirection[] con temas, puntuaciones de relevancia y consultas semilla, luego selecciona las direcciones más prometedoras mediante un ranker ChainOfThought.
- DeepReaderAgent — Reads content from URLs and extracts structured findings using dspy.RLM for content extraction and dspy.ChainOfThought for cross-validation. Its CrossValidateFindings signature checks consistency across sources before recording results.
- DeepReaderAgent — Lee contenido de URLs y extrae hallazgos estructurados usando dspy.RLM para extracción de contenido y dspy.ChainOfThought para validación cruzada. Su firma CrossValidateFindings verifica la consistencia entre fuentes antes de registrar resultados.
- SynthesizerAgent — Takes findings across multiple sources and produces a SynthesisReport with insights, contradictions, and knowledge gaps. Uses dspy.RLM for synthesis and dspy.ChainOfThought(SynthesizeAcrossSources) for cross-source reasoning.
- SynthesizerAgent — Toma hallazgos de múltiples fuentes y produce un SynthesisReport con insights, contradicciones y brechas de conocimiento. Usa dspy.RLM para síntesis y dspy.ChainOfThought(SynthesizeAcrossSources) para razonamiento entre fuentes.
- CriticAgent — The most sophisticated agent. Runs a two-pass dspy.RLM critique with dspy.MultiChainComparison across three independent chains, then uses dspy.Refine for iterative improvement. Its CritiqueReasoning signature identifies strengths, weaknesses, and follow-up questions.
- CriticAgent — El agente más sofisticado. Ejecuta una crítica dspy.RLM de dos pasos con dspy.MultiChainComparison a través de tres cadenas independientes, luego usa dspy.Refine para mejora iterativa. Su firma CritiqueReasoning identifica fortalezas, debilidades y preguntas de seguimiento.
- ResearchWorkflow (Orchestrator) — The central coordinator. Seeds the research frontier with an initial query, then loops up to max_iterations iterations. In each iteration it selects an agent via ChainOfThought(SelectAgent), dispatches the agent, computes a confidence delta via ChainOfThought(ComputeConfidenceDelta), and absorbs findings back into the frontier. Checkpoints progress to Redis every 3 iterations.
- ResearchWorkflow (Orquestador) — El coordinador central. Siembra el frente de investigación con una consulta inicial, luego itera hasta max_iterations iteraciones. En cada iteración selecciona un agente mediante ChainOfThought(SelectAgent), despacha el agente, computa un delta de confianza mediante ChainOfThought(ComputeConfidenceDelta) y absorbe los hallazgos de vuelta al frente. Checkpointea el progreso en Redis cada 3 iteraciones.
Dual-Mode Architecture
Arquitectura de Modo Dual
One of the experiment’s practical design decisions is the dual-mode architecture. The system can run in two configurations:
Una de las decisiones prácticas de diseño del experimento es la arquitectura de modo dual. El sistema puede ejecutarse en dos configuraciones:
- Full distributed — Uses
DaprFrontier(Redis-backed viaStateStoreService) and requires the Dapr sidecar. All state survives crashes. Thedapr-multi-app-run.yamllaunches all 5 agents on ports 8000-8004 with gRPC protocol. - No-infrastructure — Uses
InMemoryFrontier+NoopStore(an in-memoryStateStoreServicedrop-in). No Dapr sidecar needed. Ideal for development, testing, and quick experiments.
- Distribuido completo — Usa
DaprFrontier(respaldado por Redis viaStateStoreService) y requiere el sidecar de Dapr. Todo el estado sobrevive caídas. Eldapr-multi-app-run.yamllanza los 5 agentes en los puertos 8000-8004 con protocolo gRPC. - Sin infraestructura — Usa
InMemoryFrontier+NoopStore(unStateStoreServiceen memoria). Sin necesidad de sidecar de Dapr. Ideal para desarrollo, pruebas y experimentos rápidos.
The NoopStore is a minimal 492-byte subclass that implements StateStoreService methods as no-ops. The InMemoryFrontier implements the same UCB scoring algorithm as DaprFrontier but without persistence. This means you can iterate on agent logic locally without running Redis or Dapr, then deploy with full durability by swapping two lines of config.
El NoopStore es una subclase mínima de 492 bytes que implementa los métodos de StateStoreService como no-ops. El InMemoryFrontier implementa el mismo algoritmo de puntuación UCB que DaprFrontier pero sin persistencia. Esto significa que puedes iterar sobre la lógica de agentes localmente sin ejecutar Redis o Dapr, luego desplegar con durabilidad completa intercambiando dos líneas de configuración.
Dapr + DSPy Integration Matrix
Matriz de Integración Dapr + DSPy
The experiment maps DSPy’s programmatic LM modules onto Dapr’s durable infrastructure in a clean way:
El experimento mapea los módulos programáticos de DSPy sobre la infraestructura durable de Dapr de una manera limpia:
| Component | DSPy Implementation | Dapr Role |
|---|---|---|
| Agent dispatch | ChainOfThought(SelectAgent) | call_agent() cross-app invocation |
| Agent reasoning | RLM + CoT + BestOfN + Refine + MultiChainComparison | DurableAgent shell + @workflow_entry |
| Frontier saturation | ChainOfThought(AssessBatchSaturation) — single batch call | DaprFrontier via StateStoreService |
| Quality evaluation | ChainOfThought(QualityEvaluation) + BootstrapFewShot | State persisted in Redis |
| Pattern extraction | ChainOfThought(ExtractPatterns) + BootstrapFewShot | State persisted in Redis |
| Confidence deltas | ChainOfThought(ComputeConfidenceDelta) per agent result | — |
| Agent optimization | BootstrapFewShot.compile() on all agents | DaprFrontier persistent state |
| Componente | Implementación DSPy | Rol de Dapr |
|---|---|---|
| Despacho de agentes | ChainOfThought(SelectAgent) | Invocación cruzada call_agent() |
| Razonamiento de agentes | RLM + CoT + BestOfN + Refine + MultiChainComparison | Shell DurableAgent + @workflow_entry |
| Saturación del frente | ChainOfThought(AssessBatchSaturation) — llamada batch única | DaprFrontier via StateStoreService |
| Evaluación de calidad | ChainOfThought(QualityEvaluation) + BootstrapFewShot | Estado persistido en Redis |
| Extracción de patrones | ChainOfThought(ExtractPatterns) + BootstrapFewShot | Estado persistido en Redis |
| Deltas de confianza | ChainOfThought(ComputeConfidenceDelta) por resultado de agente | — |
| Optimización de agentes | BootstrapFewShot.compile() en todos los agentes | Estado persistente DaprFrontier |
Key Engineering Decisions
Decisiones Clave de Ingeniería
Several design choices in the experiment are worth highlighting for anyone building similar systems:
Varias decisiones de diseño en el experimento merecen destacarse para cualquiera que construya sistemas similares:
Batch saturation assessment. The DaprFrontier uses AssessBatchSaturation to replace N+1 per-direction LLM calls with a single batch DSPy call. Instead of asking “is this direction saturated?” for each direction individually, it passes all directions as JSON in one call. The batch result is cached in _saturated_indices and invalidated only on mutation — not on every next_action(). This is a significant optimization for research workflows with many active directions.
Evaluación de saturación por lotes. El DaprFrontier usa AssessBatchSaturation para reemplazar las N+1 llamadas LLM por dirección con una sola llamada batch DSPy. En lugar de preguntar “¿esta dirección está saturada?” para cada dirección individualmente, pasa todas las direcciones como JSON en una sola llamada. El resultado del batch se cachea en _saturated_indices y se invalida solo en mutación — no en cada next_action(). Esta es una optimización significativa para flujos de investigación con muchas direcciones activas.
Dual-format MCP bridge. The MCPBridge in mcp/bridge.py produces tools in both DSPy RLM format (for dspy.RLM(tools=...)) and dapr-agents AgentTool format. This bridge is what makes DSPy + RFL + tool-use work together — a non-trivial integration that the experiment solves with clean adapter code.
Puente MCP de doble formato. El MCPBridge en mcp/bridge.py produce herramientas tanto en formato DSPy RLM (para dspy.RLM(tools=...)) como en formato AgentTool de dapr-agents. Este puente es lo que hace que DSPy + RFL + uso de herramientas funcionen juntos — una integración no trivial que el experimento resuelve con código adaptador limpio.
Crash resilience via workflow checkpointing. The orchestrator persists heartbeat_frontier and heartbeat_findings_count to Redis every 3 iterations via Dapr workflow checkpointing. If the process dies mid-research, the workflow resumes from the last checkpoint. This is the difference between a demo and a system that could run unattended for hours.
Resiliencia ante caídas mediante checkpointing de workflows. El orquestador persiste heartbeat_frontier y heartbeat_findings_count en Redis cada 3 iteraciones mediante el checkpointing de workflows de Dapr. Si el proceso muere a mitad de una investigación, el workflow se reanuda desde el último checkpoint. Esta es la diferencia entre un demo y un sistema que podría ejecutarse sin supervisión durante horas.
Meta-Optimization: LSE and Trace2Skill
Meta-Optimización: LSE y Trace2Skill
Beyond the core research loop, the experiment includes two meta-optimization modules inspired by recent research:
Más allá del bucle de investigación central, el experimento incluye dos módulos de meta-optimización inspirados en investigación reciente:
LSE (Learning to Self-Evolve) — The LSEOptimizer tracks improvement trends across research runs. It computes the quality delta r = quality(c1) - quality(c0) between consecutive checkpoints and uses this signal to learn which agent configurations work best for which types of queries. This is implemented as a ChainOfThought(QualityEvaluation) DSPy program compiled with BootstrapFewShot.
LSE (Learning to Self-Evolve) — El LSEOptimizer rastrea tendencias de mejora a través de ejecuciones de investigación. Computa el delta de calidad r = quality(c1) - quality(c0) entre checkpoints consecutivos y usa esta señal para aprender qué configuraciones de agentes funcionan mejor para qué tipos de consultas. Esto se implementa como un programa DSPy ChainOfThought(QualityEvaluation) compilado con BootstrapFewShot.
Trace2Skill consolidation — The SkillConsolidator uses ExtractPatterns DSPy signatures to mine execution trajectories for reusable skill patterns. Agents don’t just execute tasks — they produce structured traces that encode how they approached the problem. The consolidator identifies generalizable patterns and stores them as candidate skills for future runs.
Consolidación Trace2Skill — El SkillConsolidator usa firmas DSPy ExtractPatterns para minar trayectorias de ejecución en busca de patrones de habilidad reutilizables. Los agentes no solo ejecutan tareas — producen trazas estructuradas que codifican cómo abordaron el problema. El consolidador identifica patrones generalizables y los almacena como habilidades candidatas para ejecuciones futuras.
The experiment also supports teacher→student distillation via the distill CLI command. All agent DSPy programs can be compiled from an expensive teacher model (DeepSeek v4 Flash) to a cheaper student (Gemma 4 via Ollama), enabling local inference for development without losing the optimized behavior.
El experimento también soporta destilación teacher→student mediante el comando CLI distill. Todos los programas DSPy de agentes pueden compilarse desde un modelo teacher costoso (DeepSeek v4 Flash) a un estudiante más barato (Gemma 4 via Ollama), permitiendo inferencia local para desarrollo sin perder el comportamiento optimizado.
Running the Experiment
Ejecutando el Experimento
The full experiment is open source in the lab-experiments repository. To run it:
El experimento completo es de código abierto en el repositorio lab-experiments. Para ejecutarlo:
- Clone the repo:
git clone https://github.com/OctAg0nO/lab-experiments - Install dependencies:
pip install -e .(requires Python 3.12+) - Launch the infrastructure:
docker compose up(Crawl4AI) +dapr run -f dapr-multi-app-run.yaml - Run a research mission:
python -m lab.10_dapr_deep_research mission --query "your question" --iterations 10
- Clona el repo:
git clone https://github.com/OctAg0nO/lab-experiments - Instala dependencias:
pip install -e .(requiere Python 3.12+) - Lanza la infraestructura:
docker compose up(Crawl4AI) +dapr run -f dapr-multi-app-run.yaml - Ejecuta una misión de investigación:
python -m lab.10_dapr_deep_research mission --query "tu pregunta" --iterations 10
For development without infrastructure, use the run CLI command which starts all agents in-process with in-memory storage — no Docker, no Dapr sidecar needed.
Para desarrollo sin infraestructura, usa el comando CLI run que inicia todos los agentes en el mismo proceso con almacenamiento en memoria — sin Docker, sin sidecar de Dapr.
Why This Matters
Por Qué Esto Importa
The Dapr Deep Research experiment demonstrates a practical architecture for production-grade agentic research. The combination of Dapr workflows (durability), DSPy optimization (programmatic LM improvement), and MCP tools (standardized tool access) forms a stack that addresses the three hard problems of agentic research systems head-on. It’s not a polished product — it’s an engineering prototype that shows what’s possible when you combine these technologies intentionally. The lab-experiments repo contains the full source, and the pattern is adaptable to different LLMs, tool sets, and research domains.
El experimento Dapr Deep Research demuestra una arquitectura práctica para investigación agéntica de grado de producción. La combinación de workflows de Dapr (durabilidad), optimización DSPy (mejora programática de LMs) y herramientas MCP (acceso estandarizado a herramientas) forma un stack que aborda los tres problemas difíciles de los sistemas de investigación agénticos de frente. No es un producto pulido — es un prototipo de ingeniería que muestra lo que es posible cuando combinas estas tecnologías intencionalmente. El repositorio lab-experiments contiene el código fuente completo, y el patrón es adaptable a diferentes LLMs, conjuntos de herramientas y dominios de investigación.
References
Referencias
- Dapr Deep Research — Lab Experiments Repository. github.com/OctAg0nO/lab-experiments
- Dapr Deep Research — Repositorio de Experimentos. github.com/OctAg0nO/lab-experiments
- Chen, Z. et al. (2026). Learning to Self-Evolve: Adaptive Optimization for LLM Agents. arXiv:2603.18620
- Chen, Z. et al. (2026). Learning to Self-Evolve: Optimización Adaptativa para Agentes LLM. arXiv:2603.18620
- Ni, Z. et al. (2026). Trace2Skill: Distill Trajectory-Local Lessons into Transferable Agent Skills. arXiv:2603.25158
- Ni, Z. et al. (2026). Trace2Skill: Destilando Lecciones Locales de Trayectorias en Habilidades Transferibles de Agentes. arXiv:2603.25158
- Dapr Agents Documentation. dapr-agents.readthedocs.io
- Documentación de Dapr Agents. dapr-agents.readthedocs.io
- DSPy: Compiling Declarative Language Model Calls into Self-Improving Pipelines. ICLR 2024 (Spotlight). github.com/stanfordnlp/dspy
- DSPy: Compilando Llamadas Declarativas de Modelos de Lenguaje en Pipelines Auto-Mejorables. ICLR 2024 (Spotlight). github.com/stanfordnlp/dspy