Artículo

Guía de orquestación de subagentes en Spring AI

Guía de orquestación de subagentes en Spring AI

1. Visión general

A medida que los sistemas de IA se vuelven más capaces, un único agente “generalista” suele resultar ineficiente. Intenta manejar todo en una sola ventana de contexto, lo que provoca prompts ruidosos, respuestas más lentas y una calidad de salida degradada. Un enfoque mejor es dividir las responsabilidades entre agentes especializados y permitir que un orquestador central las coordine.

Esto es exactamente lo que Subagent Orchestration habilita en Spring AI. Usando la Task herramienta del spring-ai-agent-utils, podemos construir sistemas jerárquicos de agentes donde cada subagente trabaja en un contexto aislado y devuelve solo resultados esenciales.

En este tutorial implementaremos este patrón de principio a fin utilizando APIs reales del ecosistema Spring. Aprenderemos cómo funciona la orquestación, cómo configurar subagentes y cómo construir un sistema funcional que delegue tareas de manera dinámica.

2. Entendiendo la Orquestación de Subagentes

La orquestación de subagentes es un patrón donde un agente AI primario delega trabajo a agentes más pequeños y especializados. Cada subagente está diseñado para una responsabilidad específica y opera en su propia ventana de contexto. Este aislamiento garantiza que los prompts permanezcan enfocados y evita que la información innecesaria contamine el proceso de razonamiento.

A diferencia de la orquestación de servicios tradicional, la decisión de delegación no está codificada. El agente principal utiliza un LLM para decidir cuándo una tarea debe ser delegada. Esta decisión se basa en descripciones en lenguaje natural proporcionadas para cada subagente, lo que hace que el sistema sea flexible y adaptable. Este enfoque mejora la separación de responsabilidades, la claridad de los prompts, el mantenimiento, la extensibilidad y la especialización del modelo.

2.1. ¿Por qué usar Subagentes Especializados?

Los subagentes especializados nos ayudan a crear sistemas AI más modulares. Por ejemplo, en un asistente de viajes AI, un subagente puede buscar vuelos, otro puede recomendar hoteles y otro puede construir itinerarios personalizados basados en las preferencias del usuario. Esto permite que cada subagente se enfoque en una responsabilidad especializada en lugar de depender de un único prompt grande.

Cada subagente recibe una responsabilidad enfocada en lugar de competir por contexto dentro de un flujo de trabajo compartido. Este patrón se vuelve especialmente útil en sistemas empresariales donde las aplicaciones AI siguen creciendo en complejidad con el tiempo.

2.2. Cómo Spring AI Apoya la Orquestación

Spring AI Community Agent Utils ofrece utilidades de orquestación a través de TaskTool y ClaudeSubagentReferences. Estos componentes nos ayudan a registrar subagentes, cargar definiciones de subagentes dinámicamente, delegar tareas automáticamente y orquestar múltiples flujos de trabajo AI.

Uno de los aspectos más interesantes de este enfoque es que los subagentes pueden definirse en archivos markdown en lugar de clases Java.

3. Configuración del Proyecto

Antes de implementar la Orquestación de Subagentes, configuraremos una aplicación sencilla de Spring Boot usando Spring Initializr con el Spring AI starter OpenAI, starter-test y Spring AI Community Agent Utils. Primero añadiremos las dependencias requeridas y luego configuraremos el modelo OpenAI y las definiciones de subagentes basadas en markdown.

En esta aplicación construiremos un sistema de orquestación impulsado por IA que delegue trabajo a múltiples subagentes especializados. Un subagente revisará la calidad del código de una aplicación Spring Boot, mientras que otro generará documentación técnica concisa. Un orquestador central analizará la solicitud del usuario y coordinará estos subagentes para producir una respuesta combinada.

3.1. Añadiendo Dependencias Maven

Primero, configuramos las dependencias requeridas:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-model-openai</artifactId>
        <version>2.0.0-M5</version>
        <scope>compile</scope>
    </dependency>

    <dependency>
        <groupId>org.springaicommunity</groupId>
        <artifactId>spring-ai-agent-utils</artifactId>
        <version>0.4.2</version>
    </dependency>
</dependencies>

Esta configuración añade las dependencias básicas necesarias para nuestra aplicación. Estas dependencias proporcionan soporte para aplicaciones Spring Boot, integración de OpenAI a través de Spring AI, utilidades de orquestación para subagentes y soporte de pruebas. Después de añadir las dependencias, configuraremos la clave API de OpenAI.

3.2. Configurando el Acceso a OpenAI

A continuación configuraremos la clave API de OpenAI, el modelo LLM predeterminado y la ubicación de las definiciones de subagentes basadas en markdown dentro de application.properties:

spring.ai.openai.api-key=${OPENAI_API_KEY}
spring.ai.openai.chat.options.model=gpt-4.1-mini
spring.application.name=spring-ai-subagent
agent.tasks.paths=classpath:/agents/*.md

Debemos reemplazar OPENAI_API_KEY por una clave válida de OpenAI o exponerla a través de una variable de entorno para evitar almacenar credenciales sensibles directamente en el código fuente.

La propiedad agent.tasks.paths apunta a las definiciones de subagentes basadas en markdown ubicadas bajo src/main/resources/agents/. Spring AI utiliza estos archivos para cargar y registrar dinámicamente los subagentes especializados durante el arranque de la aplicación.

4. Creando Subagentes Especializados

Este proyecto utiliza archivos markdown para definir subagentes especializados. Sus ubicaciones se declaran como recursos de clase. Este enfoque mantiene el comportamiento del agente externo y fácil de mantener. Primero crearemos un directorio agents bajo la ruta src/main/resources/.

4.1. Creando el Subagente Revisor de Código

Crearemos un subagente responsable de revisar la calidad del código. El cuerpo markdown actúa como el prompt del sistema del subagente y define su comportamiento especializado.

Añadiremos un archivo code-reviewer.md bajo la ruta src/main/resources/agents/ con las siguientes instrucciones:

---

name: code-reviewer
description: >
  Expert code reviewer. Use proactively after writing or modifying code
  to surface quality, security, and readability issues.
tools: Read, Grep, Glob
disallowedTools: Edit, Write
model: sonnet
---


You are a senior code reviewer with expertise in software quality.

**When Invoked:**

1. Run `git diff` to identify recent changes
2. Inspect the modified files and surrounding context
3. Check for issues in the areas listed below

**Review Checklist:**

- Code clarity and readability
- Proper naming conventions
- Error handling and edge cases
- Security vulnerabilities

**Output:** Clear, actionable feedback organized by file, with line references.

Este subagente se enfoca únicamente en responsabilidades de análisis de código. Externalizar el comportamiento en archivos markdown facilita la evolución de los prompts sin modificar clases Java.

4.2. Creando el Subagente Redactor de Documentación

A continuación, crearemos otro subagente especializado en documentación técnica. Añadiremos un archivo documentation-writer.md bajo src/main/resources/agents/ y definiremos las siguientes instrucciones:

---

name: documentation-writer
description: >
  Technical documentation specialist for architecture explanations,
  workflow summaries, and concise developer-facing docs.
model: default
---


You are a senior technical documentation specialist.

Your responsibilities:
- Generate concise technical documentation
- Explain Spring Boot and Java application architecture
- Summarize workflows clearly
- Produce developer-friendly explanations
- Keep documentation simple and technically accurate

Este subagente se enfoca completamente en generar documentación para desarrolladores. Ahora tenemos dos subagentes especializados llamados code-reviewer y documentation-writer.

5. Configurando el Agente Orquestador Principal

El orquestador es responsable de cargar subagentes, registrar herramientas de orquestación y ejecutar flujos de trabajo delegados. Este agente principal es el punto de entrada con el que un usuario interactúa directamente. Su modelo de lenguaje grande (LLM) tiene acceso al TaskTool, que expone un catálogo de subagentes disponibles. Cuando el agente principal decide que un especialista maneje mejor una parte de la solicitud del usuario, invoca el TaskTool, pasando el nombre del subagente y una descripción de la tarea.

5.1. Configurando Referencias de Subagente

Spring AI Community Agent Utils proporciona ClaudeSubagentReferences para cargar subagentes basados en markdown. La siguiente configuración carga dinámicamente subagentes desde el directorio de recursos markdown:

@Configuration
public class AgentConfig {

    @Value("${agent.tasks.paths}")
    private List<Resource> agentPaths;

    @Bean
    @Primary
    public ChatClient orchestratorChatClient(ChatClient.Builder chatClientBuilder) {

        SubagentType claudeType = ClaudeSubagentType.builder()
          .chatClientBuilder("default", chatClientBuilder.clone())
          .build();

        ToolCallback taskTool = TaskTool.builder()
          .subagentReferences(ClaudeSubagentReferences.fromResources(agentPaths))
          .subagentTypes(claudeType)
          .build();

        return chatClientBuilder.clone()
          .defaultToolCallbacks(taskTool)
          .build();
    }
}

Las dos clases clave que se enlazan son TaskTool (la herramienta que llama el LLM del orquestador) y ClaudeSubagentType (que indica a la herramienta cómo generar un subagente y qué ChatClient.Builder usar). Observe las llamadas .clone() en chatClientBuilder. Esto es importante porque el orquestador y cada subagente necesitan su propia instancia independiente de ChatClient.Builder para no compartir opciones por defecto ni prompts de sistema.

5.2. Creando el Servicio Orquestador

Este servicio actúa como el punto de entrada para solicitudes AI orquestadas. El LLM del agente principal decide automáticamente si delega a un subagente según las descripciones de tarea en el Registro de Agentes:

@Service
public class OrchestratorService {

    private final ChatClient chatClient;

    public OrchestratorService(ChatClient chatClient) {
        this.chatClient = chatClient;
    }

    public String ask(String userMessage) {
        return chatClient.prompt(userMessage).call().content();
    }
}

El servicio en sí permanece muy pequeño porque la complejidad de la orquestación se maneja internamente a través de las herramientas de Spring AI. Observe que no hay código de invocación manual de subagente aquí. La delegación ocurre de forma transparente dentro del bucle de razonamiento del LLM cuando el modelo selecciona la herramienta Task.

5.3. Ejecutando Flujos de Trabajo Orquestados

Ahora ejecutaremos flujos de trabajo orquestados. El siguiente método desencadena múltiples subagentes especializados:

public class SpringAiSubagentApplication {

    private static final Logger logger = LoggerFactory.getLogger(SpringAiSubagentApplication.class);

    public static void main(String[] args) {
        SpringApplication.run(SpringAiSubagentApplication.class, args);
    }

    @Bean
    CommandLineRunner demo(OrchestratorService orchestratorService) {

        return args -> {
            String response = orchestratorService.ask(
              """
              Perform the following tasks:
              - Review the code quality.
              - Generate concise technical documentation like user guide.
              """
            );
            logger.info("{}", response);
        };
    }
}

Este prompt permite al orquestador delegar responsabilidades entre varios subagentes. Cuando ejecutamos la aplicación, Spring AI carga los subagentes basados en markdown, enruta la solicitud a través de la capa de orquestación y genera una respuesta combinada de los agentes especializados.

La siguiente captura de pantalla muestra la salida generada por el flujo de trabajo de orquestación multi-subagente: Spring AI SubAgent Output scaled

De la respuesta generada podemos ver claramente cómo cada subagente maneja su propia responsabilidad especializada. El subagente code-reviewer se enfoca en analizar preocupaciones de calidad de código, mientras que el subagente documentation-writer genera documentación técnica concisa. Esta separación de responsabilidades es una de las principales ventajas de la orquestación de subagentes.

6. Probando la Orquestación de Subagentes

Los flujos de trabajo de orquestación AI aún deben probarse como cualquier otro componente de la aplicación. La aplicación incluye pruebas de integración para el comportamiento de orquestación.

6.1. Probando la Carga de Subagentes

La siguiente prueba verifica que Spring AI puede cargar las definiciones de subagente correctamente:

@Test
void givenSubagentDefinitions_whenLoadingSubagents_thenReferencesAreCreated() {

    var references = ClaudeSubagentReferences.fromResources(agentPaths);

    assertThat(references).isNotNull();
}

Esta prueba asegura que la capa de orquestación puede cargar con éxito subagentes basados en markdown.

6.2. Probando Respuestas Orquestadas

A continuación, verificaremos que el orquestador puede ejecutar solicitudes con éxito:

@Test
void givenPrompt_whenExecutingOrchestration_thenResponseIsGenerated() {

    List<Resource> agentResources = List.of(
      new ClassPathResource("agents/test-agent.md")
    );

    SubagentType claudeType = ClaudeSubagentType.builder()
      .chatClientBuilder("default", chatClientBuilder.clone())
      .build();

    ToolCallback taskTool = TaskTool.builder()
      .subagentReferences(ClaudeSubagentReferences.fromResources(agentResources))
      .subagentTypes(claudeType)
      .build();

    ChatClient chatClient = chatClientBuilder.clone()
      .defaultToolCallbacks(taskTool)
      .build();

    String result = chatClient.prompt("Explain how the authentication module works.")
      .call()
      .content();

    assertThat(result).isNotBlank();
    assertThat(result).contains("authentication");
}

Esta prueba de integración valida el flujo de orquestación. En este caso, la prueba unitaria debe simular la capa del modelo y verificar la salida de orquestación. Esta prueba valida el contrato del servicio y mantiene el comportamiento de orquestación verificable sin llamadas externas a la API.

7. Conclusión

En este artículo construimos un sistema de orquestación de subagentes usando Spring AI y Spring AI Community Agent Utils. Creamos subagentes especializados para revisión de código y documentación técnica, y luego los orquestamos a través de un flujo de trabajo AI centralizado. Este enfoque ayuda a mantener los sistemas AI modular, mantenibles y más fáciles de evolucionar.

También exploramos cómo Spring AI proporciona una forma limpia y declarativa de construir sistemas multienemigo jerárquicos. Al aislar cada subagente dentro de su propia ventana de contexto, podemos reducir la contaminación de contexto que a menudo afecta los flujos de trabajo de un solo agente. La configuración basada en markdown facilita definir y versionar subagentes sin introducir implementaciones Java adicionales. También vimos cómo TaskTool actúa como puente entre el orquestador y los subagentes especializados.

A medida que las aplicaciones AI continúan creciendo en complejidad, la orquestación de subagentes ofrece una forma práctica de distribuir responsabilidades entre agentes enfocados en lugar de depender de un único prompt grande.

Como siempre, el código de este ejemplo está disponible en GitHub.

Newsletter Semanal de Java

Cada viernes recibe lo más nuevo del ecosistema Java: frameworks, herramientas y mejores prácticas.

Sin spam. Cancela cuando quieras.

Compartir artículo