JamJet

Inicio Rápido de Java

Construye tu primer agente de IA y flujo de trabajo en Java con JamJet — herramientas, estrategias, ejecución duradera y compilación IR.

Guía Rápida de Java

Esta guía te lleva paso a paso en la construcción de un agente de IA y un flujo de trabajo duradero en Java. Al finalizar comprenderás cómo se integran las herramientas de JamJet, las estrategias de agente, la compilación de IR y el estado de flujo de trabajo tipado — y por qué cada decisión de diseño importa para sistemas de agentes en producción.


Requisitos previos

Antes de comenzar, asegúrate de tener:

  • Java 21+ — JamJet utiliza hilos virtuales (Thread.ofVirtual) para I/O no bloqueante sin maraña de callbacks. Los hilos virtuales son una característica final (no preview) en Java 21 (JEP 444).
  • Maven 3.9+ o Gradle 8+ — cualquier herramienta de construcción que pueda obtener dependencias desde Maven Central.
  • Un runtime de JamJet en ejecución — para ejecución en producción. Durante el desarrollo puedes ejecutar todo en el mismo proceso (sin servidor requerido), o iniciar el runtime local con:
    jamjet dev
  • Una clave de API de LLM — configura OPENAI_API_KEY o ANTHROPIC_API_KEY en tu entorno. Para desarrollo exclusivamente local, Ollama funciona sin ninguna clave.

consejo: Puedes seguir toda esta guía sin un runtime en ejecución. El ejecutor en proceso te permite compilar, validar y ejecutar flujos de trabajo localmente. Agrega el runtime cuando necesites recuperación ante fallos y estado duradero.


Agregar la dependencia

El SDK de Java está publicado en Maven Central como dev.jamjet:jamjet-sdk.

Maven

<dependency>
    <groupId>dev.jamjet</groupId>
    <artifactId>jamjet-sdk</artifactId>
    <version>0.4.0</version>
</dependency>

Gradle (Kotlin DSL)

implementation("dev.jamjet:jamjet-sdk:0.4.0")

Gradle (Groovy DSL)

implementation 'dev.jamjet:jamjet-sdk:0.4.0'

Asegúrate de que tu proyecto esté configurado para Java 21 o posterior. En Maven:

<properties>
    <maven.compiler.source>21</maven.compiler.source>
    <maven.compiler.target>21</maven.compiler.target>
</properties>

Definir una herramienta

Las herramientas son el puente entre tu agente y el mundo exterior — búsqueda web, consultas de base de datos, llamadas a APIs, I/O de archivos. En JamJet, una herramienta es un record de Java anotado con @Tool que implementa ToolCall<T>.

import dev.jamjet.tool.Tool;
import dev.jamjet.tool.ToolCall;

@Tool(description = "Buscar en la web información sobre un tema")
record WebSearch(String query) implements ToolCall<String> {
    public String execute() {
        // En producción, llama a tu API de búsqueda aquí
        return "Resultados para '" + query + "': JamJet es un runtime y framework "
                + "nativo para agentes de IA, centrado en rendimiento.";
    }
}

¿Por qué records?

Este diseño es intencional. Los records de Java te brindan tres propiedades que importan para las herramientas de agentes:

  1. Inmutabilidad — los parámetros de una llamada a herramienta nunca cambian después de la construcción. Esto hace que las llamadas a herramientas sean seguras para serializar, reproducir y auditar. Cuando JamJet reproduce un flujo de trabajo fallido, vuelve a invocar la misma llamada a herramienta con exactamente los mismos parámetros.

  2. Derivación automática de JSON Schema — el SDK inspecciona los componentes del record (String query arriba) y genera el JSON Schema que el LLM necesita para invocar la herramienta. Sin escritura manual de esquemas, sin sobrecarga de anotaciones, sin desincronización entre tu código y tu esquema.

  3. Igualdad estructural — dos instancias de WebSearch("jamjet") son iguales. Esto permite la deduplicación y el almacenamiento en caché de llamadas a herramientas entre reintentos.

La anotación @Tool proporciona la description que el LLM ve al decidir qué herramienta usar. Escríbela como si explicaras la herramienta a un colega — clara, específica, orientada a la acción.

nota: Para una cobertura más profunda de patrones de diseño de herramientas, estrategias de agentes y cuándo usar cada una — consulta Patrones de IA Agéntica.


Construye un agente

Un agente combina un modelo, herramientas, instrucciones y una estrategia de razonamiento. La estrategia determina cómo piensa el agente — no solo qué hace.

import dev.jamjet.agent.Agent;

var agent = Agent.builder("researcher")
        .model("claude-haiku-4-5-20251001")
        .tools(WebSearch.class)
        .instructions("Eres un asistente de investigación útil. "
                + "Siempre busca primero, luego proporciona un resumen exhaustivo.")
        .strategy("react")
        .maxIterations(5)
        .build();

Desglosemos cada parte.

La estrategia react

Cuando estableces .strategy("react"), le estás indicando a JamJet que use el bucle ReAct (Razonamiento + Actuación):

  1. Pensamiento — el modelo razona sobre qué hacer a continuación
  2. Acción — el modelo llama a una herramienta
  3. Observación — el resultado de la herramienta se retroalimenta al modelo
  4. Repetir hasta que el modelo produzca una respuesta final o alcance el límite de iteraciones

Esta es la estrategia de agente más común porque es flexible: el modelo decide dinámicamente qué herramientas llamar y en qué orden. Funciona bien para tareas abiertas donde no puedes predecir la secuencia exacta de pasos.

JamJet admite tres estrategias integradas:

EstrategiaCuándo usarCómo funciona
reactTareas abiertas, investigación exploratoriaBucle pensamiento-acción-observación
plan-and-executeTareas estructuradas que se benefician de planificación previaGenera plan, luego ejecuta cada paso secuencialmente
criticTareas que requieren control de calidadBucle borrador, crítica, revisión

consejo: ¿No estás seguro qué estrategia elegir? Comienza con react. Actualiza a plan-and-execute cuando veas que el agente divaga, o a critic cuando la calidad del resultado importe más que la velocidad. Consulta comparaciones de estrategias en jamjet.dev/research para ver benchmarks.

Barreras de protección: costo, tiempo e iteraciones

Los agentes en producción necesitan límites estrictos. Sin ellos, un modelo confundido puede quemar tu presupuesto de API en un bucle:

var agent = Agent.builder("investment-researcher")
        .model("gpt-4o")
        .tools(WebSearch.class, FetchUrl.class, StoreNote.class)
        .instructions("""
                Eres un analista profesional de investigación de inversiones.
                Busca noticias recientes y datos financieros, luego produce
                un memorándum de inversión estructurado.
                """)
        .strategy("plan-and-execute")
        .maxIterations(6)
        .maxCostUsd(0.50)
        .timeoutSeconds(120)
        .build();
  • maxIterations(6) — el agente se detiene después de 6 pasos de razonamiento, incluso si no ha terminado. Esto previene bucles infinitos.
  • maxCostUsd(0.50) — el runtime rastrea los costos de tokens en tiempo real y detiene al agente si el gasto excede los 50 centavos.
  • timeoutSeconds(120) — timeout de tiempo real. Si el agente no ha completado en 2 minutos, la ejecución se aborta.

Estos no son sugerencias — son límites estrictos aplicados por el runtime. El runtime de JamJet los verifica entre cada paso, no solo al final.


Ejecútalo

Con el agente construido, puedes ejecutarlo e inspeccionar los resultados:

public static void main(String[] args) {
    var agent = Agent.builder("researcher")
            .model("claude-haiku-4-5-20251001")
            .tools(WebSearch.class)
            .instructions("Eres un asistente de investigación útil. "
                    + "Siempre busca primero, luego proporciona un resumen exhaustivo.")
            .strategy("react")
            .maxIterations(5)
            .build();

    // Ejecutar el agente
    var result = agent.run("¿Qué es JamJet?");

    System.out.println(result.output());
    System.out.printf("Duración: %.2f ms%n", result.durationUs() / 1000.0);
    System.out.printf("Llamadas a herramientas: %d%n", result.toolCalls().size());
}
export OPENAI_API_KEY=sk-...
mvn compile exec:java -Dexec.mainClass=com.example.MyAgent

Compilación IR: qué ocurre bajo el capó

Antes de que tu agente se ejecute, JamJet lo compila a una representación intermedia (IR) — un formato de grafo canónico compartido entre el SDK de Java, el SDK de Python y los flujos de trabajo YAML. Puedes inspeccionar el IR directamente:

var ir = agent.compile();

System.out.println("workflow_id: " + ir.id());
System.out.println("start_node:  " + ir.startNode());
System.out.println("nodes:       " + ir.nodes().size());
System.out.println("edges:       " + ir.edges().size());

Esto imprime algo como:

workflow_id: researcher
start_node:  react_start
nodes:       3
edges:       4

¿Por qué importa esto? Porque el IR es lo que el runtime de JamJet realmente ejecuta. Ya sea que escribas tu agente en Java, Python o YAML, se compila al mismo formato de grafo. Esto significa:

  • Portabilidad — un agente escrito en Java puede desplegarse en cualquier runtime de JamJet
  • Inspección — puedes validar y visualizar el grafo de ejecución antes de ejecutarlo
  • Durabilidad — el runtime crea checkpoints en los límites de nodos, por lo que puede reanudarse después de un fallo

También puedes validar el IR antes del envío:

import dev.jamjet.ir.IrValidator;

IrValidator.validateOrThrow(ir);

Esto detecta problemas estructurales (nodos desconectados, aristas faltantes, esquemas de estado inválidos) en tiempo de compilación en lugar de en tiempo de ejecución.


Construye un flujo de trabajo

Los agentes son ideales para tareas abiertas donde el modelo decide qué hacer. Pero muchos sistemas del mundo real necesitan pipelines determinísticos de múltiples pasos — enriquecimiento de datos, RAG, cadenas de aprobación, ETL. Para estos casos, usa un Workflow.

La diferencia clave: en un agente, el LLM decide la ruta de ejecución. En un flujo de trabajo, decides la ruta de ejecución y el LLM es solo un paso en el pipeline.

Aquí hay un flujo de trabajo RAG (Generación Aumentada por Recuperación) de dos pasos:

import dev.jamjet.workflow.Workflow;
import java.util.List;

// Estado tipado — un record de Java
record RagState(
        String query,
        List<String> retrievedDocs,
        String answer) {}

var workflow = Workflow.<RagState>builder("rag-assistant")
        .version("1.0.0")
        .state(RagState.class)
        // Paso 1: Recuperar documentos relevantes
        .step("retrieve", state -> {
            var docs = searchKnowledgeBase(state.query());
            return new RagState(state.query(), docs, null);
        })
        // Paso 2: Sintetizar una respuesta a partir del contexto
        .step("synthesize", state -> {
            var context = String.join("\n\n", state.retrievedDocs());
            var answer = callLlm(state.query(), context);
            return new RagState(state.query(), state.retrievedDocs(), answer);
        })
        .build();

Cómo fluye el estado a través de un flujo de trabajo

Cada paso recibe el RagState actual y devuelve un nuevo RagState. El estado es siempre inmutable — nunca mutas el record existente, construyes uno nuevo. Esto es lo que hace que los flujos de trabajo sean durables: si el runtime falla entre "retrieve" y "synthesize", se reanuda desde el último checkpoint completado con el estado exacto que fue persistido.

Así es como se ejecuta este flujo de trabajo:

graph LR
    A["Start"] --> B["retrieve"]
    B --> C["synthesize"]
    C --> D["End"]

El paso "retrieve" llena retrievedDocs. El paso "synthesize" los lee y produce la answer final. Cada paso tiene un checkpoint — si el proceso falla después de que "retrieve" se complete, el runtime se reanuda en "synthesize" sin volver a ejecutar la recuperación.

Ejecutando el flujo de trabajo

import dev.jamjet.workflow.ExecutionResult;
import java.util.ArrayList;

var initialState = new RagState(
        "How does JamJet handle concurrent tool calls?",
        new ArrayList<>(),
        null);

ExecutionResult<RagState> result = workflow.run(initialState);

System.out.println(result.state().answer());
System.out.printf("Ran %d steps in %.2f ms%n",
        result.stepsExecuted(), result.totalDurationUs() / 1000.0);

Agent vs Workflow: cuándo usar cada uno

AgentWorkflow
Flujo de controlEl LLM decideTú decides
Ideal paraTareas abiertas, investigación, chatPipelines, RAG, cadenas de aprobación, ETL
DeterminismoNo determinista (guiado por modelo)Determinista (guiado por código)
DurabilidadCheckpoints en límites de estrategiaCheckpoints en cada paso
HerramientasEl modelo elige qué herramientas llamarLos pasos llaman herramientas explícitamente

Puedes combinar ambos: usa un workflow como orquestador externo e integra agents dentro de pasos individuales. Esto te da pipelines deterministas con subpasos inteligentes.


Próximos pasos

Ahora tienes un agent y un workflow funcionando. Aquí tienes dónde profundizar:

consejo: ¿Ya usas Spring Boot? Avanza directamente a la Guía del Spring Boot Starter — envuelve todo lo de este inicio rápido en autoconfiguración con health checks, métricas y registros de auditoría.

On this page