Saltar al contenido principal
Desarrollo con IA15 min de lectura

Tool Calling con Spring AI: cómo conectar un LLM con tu backend Java

Un LLM que responde no es un LLM que actúa. Cómo funciona Tool Calling en Spring AI 2.0 —@Tool, ToolCallingAdvisor, ToolContext, límites y observabilidad—, qué ve realmente el modelo y por qué la autorización sigue viviendo en Java.

Cristian Osorio · DatIACode

«¿Cuál es el estado del pedido 48392?». Sin una herramienta que consulte el ERP, el modelo no tiene forma fiable de saberlo: puede razonar sobre lo que hay en el contexto, pero no consultar un dato operativo que cambia en tiempo real.

Con Tool Calling, el modelo puede decidir solicitar una herramienta cuando necesita información o una acción externa: pide que se ejecute getOrderStatus con el identificador 48392, la aplicación Java consulta el ERP, devuelve SHIPPED y el modelo redacta la respuesta con un dato real.

Ahora cambia la frase por «cancela el pedido 48392». La mecánica es idéntica y el riesgo no tiene nada que ver. Consultar y modificar no son el mismo tipo de herramienta, y esa distinción recorre el artículo entero.

Este artículo explica cómo funciona Tool Calling en Spring AI 2.0 (la versión estable es la 2.0.1): cómo se define una herramienta, qué ve realmente el modelo, quién ejecuta el método Java y dónde siguen viviendo la autorización, los límites y la auditoría.

Tool Calling en Spring AI, en 30 segundos

  • Defines un método Java como herramienta; Spring AI genera su nombre, su descripción y el JSON Schema de los argumentos.
  • Esa definición viaja al modelo con el prompt; el modelo decide si quiere usarla y devuelve nombre y argumentos.
  • Spring AI ejecuta el método y devuelve el resultado al modelo, que responde o pide otra herramienta.
  • El modelo nunca recibe una referencia al método ni acceso a lo que hay detrás: solo el contrato.
  • Autenticación, autorización, validación, límites y auditoría siguen siendo cosa del código Java.

Qué es Tool Calling en Spring AI

Tool Calling en Spring AI permite exponer funciones de una aplicación Java a un modelo de lenguaje mediante un nombre, una descripción y un JSON Schema de argumentos. El modelo puede solicitar la ejecución de una herramienta, pero la lógica de ejecución pertenece a la aplicación: el modelo nunca recibe acceso directo a las APIs que hay detrás.

Cubren dos propósitos que conviene no mezclar: recuperar información (consultar un pedido, buscar un cliente, llamar a una API) y ejecutar acciones (abrir un ticket, enviar una notificación, cancelar una operación). La segunda categoría necesita bastante más control, y casi todo lo que sigue trata de eso.

Tool Calling y function calling: ¿son lo mismo?

En la práctica describen el mismo patrón: el modelo devuelve el nombre de una capacidad y unos argumentos estructurados para que la aplicación la ejecute. «Function calling» es el término que popularizaron los proveedores de modelos; «tool calling» es el que usa hoy Spring AI.

La diferencia es de API, no de concepto. Spring AI unificó la terminología en torno a tools y a ToolCallback, una abstracción más general que la antigua FunctionCallback: da igual que la herramienta venga de un método anotado, de un callback programático o de un servidor externo, todas recorren el mismo bucle. Si buscas function calling en Java con Spring AI, la documentación actual lo trata como tool calling.

El bucle de tool calling: quién ejecuta realmente la herramienta

En Spring AI 2.0 el bucle de herramientas es un componente de la cadena de advisors del ChatClient. DefaultChatClient auto-registra un ToolCallingAdvisor salvo que se desactive o se aporte otro ToolAdvisor, y ese advisor dirige el ciclo apoyándose en un ToolCallingManager, que localiza el ToolCallback y lo ejecuta.

  1. Usuario

    pregunta o instrucción

  2. Spring AI

    envía el prompt y las definiciones de las herramientas

  3. LLM

    decide si necesita una herramienta y devuelve nombre y argumentos

  4. ToolCallingAdvisor

    recibe la solicitud y la pasa al ToolCallingManager

  5. Java

    ejecuta el método con sus permisos y sus reglas

  6. LLM

    recibe el resultado y responde o pide otra herramienta

Qué cambió en Tool Calling con Spring AI 2.0

En Spring AI 1.x el patrón estándar era declarar un @Bean de tipo java.util.function.Function<Request, Response> y referenciarlo por su nombre: el framework lo resolvía dinámicamente en el ApplicationContext —a través de SpringBeanToolCallbackResolver— y derivaba la definición del tipo de entrada. En 2.0 ese modelo desaparece: se eliminan tanto el resolutor por nombre como la API de FunctionCallback.

El sustituto es el registro explícito: una herramienta se declara con @Tool sobre un método de servicio o se construye como ToolCallback, la abstracción única bajo la que conviven los métodos anotados, MethodToolCallback, FunctionToolCallback y las herramientas remotas. La consecuencia práctica es que el modelo ve las herramientas que adjuntas a la petición, no las que el contenedor encuentre por nombre.

La otra mitad del cambio es el bucle. En 1.x cada implementación de ChatModel ejecutaba las tool calls internamente; en 2.0 esa responsabilidad está estrictamente en el ToolCallingAdvisor de la cadena del ChatClient. Llamar a ChatModel directamente con herramientas devuelve la solicitud sin ejecutarla, y el bucle lo conduces tú con ToolCallingManager.

Cambios de Tool Calling entre Spring AI 1.x y 2.0
Spring AI 1.xSpring AI 2.0Qué implica
FunctionCallback (deprecado)ToolCallbackLa sustitución venía de antes; 2.0 elimina los tipos heredados
Bucle dentro de cada ChatModelToolCallingAdvisorChatModel ya no ejecuta herramientas por su cuenta
functions() y defaultFunctions()tools() y defaultTools()«Tools» en toda la API
toolNames() sobre beans FunctionToolCallback explícitoSpringBeanToolCallbackResolver eliminado
Sin límite de llamadasLímites por defecto40 por herramienta y 150 en total por turno

Un cambio relevante para las facturas: el advisor acumula el uso de tokens de todas las llamadas del bucle, así que el ChatResponse final refleja el coste completo. El detalle de cada cambio está en las notas de migración oficiales.

Primer ejemplo: una herramienta con @Tool

Una herramienta es un método normal de un servicio Spring: la anotación solo añade el contrato que verá el modelo.

Herramienta de consultajava
@Component
class OrderTools {

    private final OrderService orderService;

    OrderTools(OrderService orderService) {
        this.orderService = orderService;
    }

    @Tool(name = "getOrderStatus",
          description = "Obtiene el estado actual de un pedido existente. Úsala cuando el usuario "
                      + "pregunte por el estado, el seguimiento o la entrega de un pedido.")
    OrderStatus getOrderStatus(
            @ToolParam(description = "Identificador interno del pedido, por ejemplo 48392")
            Long orderId) {
        return orderService.getStatus(orderId);
    }
}
Registro por peticiónjava
String respuesta = chatClient.prompt()
        .user("¿Cuál es el estado del pedido 48392?")
        .tools(orderTools)
        .call()
        .content();

Entre ambas llamadas: Spring AI envía la definición al modelo, el modelo devuelve una solicitud con el argumento orderId, el advisor ejecuta el método y el resultado vuelve al modelo, que redacta la respuesta final.

@Tool no es magia: nombre, descripción y schema

El modelo no ve tu implementación: recibe nombre, descripción y JSON Schema de los parámetros, y con eso decide qué herramienta usar y con qué argumentos.

Antes y después de la misma herramientajava
// Pobre: el modelo tiene que adivinar cuándo usarla y qué es "id"
@Tool
Order find(Long id) { ... }

// Mejor: nombre semántico, cuándo usarla y qué significa cada argumento
@Tool(name = "getOrderStatus",
      description = "Obtiene el estado actual de un pedido existente. Úsala cuando el usuario "
                  + "pregunte por el estado o el seguimiento de un pedido. No sirve para "
                  + "buscar pedidos por cliente ni para modificarlos.")
OrderStatus getOrderStatus(
        @ToolParam(description = "Identificador interno del pedido") Long orderId) { ... }

@ToolParam: argumentos obligatorios y opcionales

Spring AI genera el JSON Schema con su JsonSchemaGenerator, el mismo que usa la salida estructurada. Por defecto todos los parámetros son obligatorios; se marcan como opcionales con @ToolParam(required = false), @JsonProperty(required = false), @Schema(required = false) o @Nullable, en ese orden de precedencia.

La documentación avisa: si marcas un parámetro como obligatorio y el modelo no puede conocer su valor, se lo inventará.

Argumentos bien tipadosjava
@Tool(description = "Busca los pedidos de un cliente en un rango de fechas")
List<OrderSummary> findCustomerOrders(
        @ToolParam(description = "Identificador del cliente") Long customerId,
        @ToolParam(description = "Fecha inicial en formato ISO-8601", required = false)
        LocalDate desde,
        @ToolParam(description = "Estado por el que filtrar", required = false)
        OrderStatus estado) {
    return orderService.search(customerId, desde, estado);
}

Dos reglas: pocos argumentos bien descritos y tipos cerrados —enums, records, LocalDate— en lugar de String para todo. Un enum convierte una alucinación en un error detectable.

Tres formas de definir herramientas en Spring AI 2.0

Las tres producen instancias de ToolCallback y circulan por el mismo bucle, así que se pueden mezclar en la misma llamada.

FunctionToolCallback como beanjava
@Bean
ToolCallback catalogSearch(CatalogService catalogService) {
    return FunctionToolCallback.builder("searchCatalog", catalogService::search)
            .description("Busca productos del catálogo por texto libre")
            .inputType(CatalogQuery.class)
            .build();
}
Formas de definir una herramienta en Spring AI 2.0
OpciónCuándo usarla
@Tool sobre un métodoEl método es tuyo y quieres la mínima ceremonia
MethodToolCallbackNecesitas control programático sobre un método concreto
FunctionToolCallbackQuieres exponer una Function, Supplier, Consumer o una referencia a método

Si vienes de 1.x: los beans Function referenciados con toolNames() ya no se resuelven; hay que declarar un ToolCallback explícito.

tools() frente a defaultTools(): una decisión de seguridad

.tools(...) añade herramientas a una petición concreta; .defaultTools(...) las deja disponibles en todas las peticiones de ese ChatClient. Ambos aceptan objetos con @Tool, instancias de ToolCallback y ToolCallbackProvider.

Un matiz importante: en ChatClient las herramientas por petición se suman a las de por defecto, no las sustituyen, así que lo que dejas en los defaults está siempre sobre la mesa. La documentación es explícita: los defaults pueden ser peligrosos si se usan sin cuidado, y las herramientas destructivas deberían añadirse por llamada.

Qué herramientas conviene dejar como defaults y cuáles no
Herramienta¿Default razonable?Motivo
Consultar catálogo o documentaciónSíLectura, sin efectos y con datos públicos internos
Consultar un pedido del tenant actualCon cuidadoLectura, pero necesita aislamiento por tenant
Abrir un ticket o guardar un borradorMejor por peticiónEscritura reversible, pero genera ruido
Cancelar un pedido o emitir un reembolsoNoAcción sensible: se habilita en el flujo que la necesita

Garantía adicional: solo se ejecutan las herramientas adjuntas a la petición. La resolución por nombre está desactivada salvo que actives spring.ai.tools.resolution.fallback.enabled, que vuelve ejecutable cualquier herramienta conocida por el resolver en cuanto el modelo la nombre.

ToolContext: los datos que el modelo no debe controlar

ToolContext permite pasar a una herramienta datos que no son del modelo: tenant, usuario autenticado, ámbito de la petición o identificadores de traza. El punto que cambia el diseño: esos datos no se envían al modelo, llegan a la herramienta en el momento de la invocación.

Es la diferencia entre un agente multi-tenant seguro y uno que confía en que el modelo rellene bien el tenant.

Identidad fuera del alcance del modelojava
@Tool(description = "Obtiene un pedido del cliente autenticado")
Order getOrder(
        @ToolParam(description = "Identificador interno del pedido") Long orderId,
        ToolContext toolContext) {

    String tenantId = (String) toolContext.getContext().get("tenantId");
    return orderService.getOrder(tenantId, orderId);
}
El contexto se inyecta en la peticiónjava
chatClient.prompt()
        .user(pregunta)
        .tools(orderTools)
        .toolContext(Map.of(
                "tenantId", sesion.tenantId(),
                "userId", sesion.userId()))
        .call()
        .content();

Seguridad: el LLM no es tu capa de autorización

El modelo decide qué quiere solicitar; qué está permitido hacer lo decide el backend. Confundirlo es el fallo de diseño más caro de esta parte del stack.

El método anotado con @Tool se comporta como cualquier endpoint expuesto: permisos, validación, reglas de dominio, aislamiento por tenant y rastro. Una instrucción del tipo «llama a esta herramienta solo si el usuario tiene permiso» ayuda al modelo a elegir, pero no es un control de acceso.

Autorización y reglas antes de actuarjava
@Tool(description = "Cancela un pedido que todavía admite cancelación")
CancellationResult cancelOrder(
        @ToolParam(description = "Identificador interno del pedido") Long orderId,
        ToolContext toolContext) {

    String tenantId = (String) toolContext.getContext().get("tenantId");
    String userId = (String) toolContext.getContext().get("userId");

    if (!authorizationService.canCancel(userId, tenantId, orderId)) {
        return CancellationResult.denied("NOT_ALLOWED",
                "El usuario no tiene permiso para cancelar este pedido");
    }

    Order order = orderService.get(tenantId, orderId);
    if (!order.isCancellable()) {
        return CancellationResult.denied("ORDER_NOT_CANCELLABLE",
                "El pedido ya ha salido del almacén");
    }

    return orderService.cancel(order, userId);
}

La herramienta no implementa la cancelación: la delega en el servicio de dominio, con sus transacciones, su idempotencia y sus tests. Una tool con doscientas líneas de lógica es una tool que nadie puede probar sin un LLM delante.

Cuatro niveles de riesgo en las herramientas

No todas las herramientas necesitan el mismo control. Clasificarlas antes de escribirlas evita discusiones más tarde y ordena las revisiones de seguridad.

  1. Lectura

    consultar un pedido o buscar documentación. Riesgo: exposición de datos. Control: filtrado por tenant y permisos de lectura.

  2. Escritura reversible

    abrir un ticket o guardar un borrador. Riesgo: ruido operativo. Control: validación y trazabilidad.

  3. Acción sensible

    cancelar una operación, enviar una comunicación, cambiar permisos. Riesgo: efectos visibles fuera. Control: autorización estricta y aprobación cuando proceda.

  4. Acción crítica

    pagos, borrados, cambios en infraestructura. Riesgo: irreversible. Control: aprobación humana explícita, idempotencia y auditoría completa.

Que el modelo solicite una herramienta destructiva no significa que deba ejecutarse de inmediato. Spring AI 2.0 permite desactivar el auto-registro del advisor en una petición, con AdvisorParams.toolCallingAdvisorAutoRegister(false), y conducir el bucle a mano con ToolCallingManager: detectas las tool calls, decides si se ejecutan —aprobación humana, cola o regla de negocio— y devuelves el resultado al modelo. Para casos menos radicales suele bastar con un advisor propio dentro del bucle, que conserva observabilidad y composición.

returnDirect: cuándo evitar otra llamada al modelo

Por defecto el resultado de una herramienta vuelve al modelo para que redacte la respuesta final. Con returnDirect = true se devuelve directamente a quien hizo la llamada: útil cuando la salida ya es la respuesta y reformularla solo añade latencia y tokens.

Un matiz antes de usarlo: si el modelo solicita varias herramientas en la misma ronda, returnDirect solo se respeta si todas lo tienen activado; si no, los resultados vuelven al modelo.

Errores en las herramientas: el happy path no basta

Cuando una herramienta lanza una excepción, Spring AI la envuelve en ToolExecutionException y se la pasa al ToolExecutionExceptionProcessor: por defecto devuelve al modelo el mensaje de las RuntimeException y relanza las comprobadas y los Error. Con spring.ai.tools.throw-exception-on-error en true, todos se lanzan para que los gestione el caller.

El riesgo es evidente: ese mensaje acaba en el contexto del modelo. Nada de stack traces, SQL ni rutas internas. Para los fallos previsibles —pedido inexistente, operación no permitida— suele ser mejor un resultado explícito que una excepción.

Resultado explícito en lugar de excepciónjava
record CancellationResult(boolean success, String code, String message) {

    static CancellationResult denied(String code, String message) {
        return new CancellationResult(false, code, message);
    }
}

// Lo que ve el modelo:
// {"success": false, "code": "ORDER_NOT_CANCELLABLE",
//  "message": "El pedido ya ha salido del almacén"}

Con un código estable y un mensaje apto para el usuario, el modelo explica lo ocurrido sin que la aplicación filtre nada de su interior.

Límites: evitar bucles y abuso de herramientas

Spring AI 2.0 pone límites por defecto donde antes no había ninguno. DefaultToolCallingManager cuenta las llamadas del turno actual —solo desde el último mensaje del usuario— y corta en 40 por herramienta y 150 en total. Al superarse lanza ToolCallLimitExceededException, que el advisor convierte en la respuesta final; con RETURN_ERROR_RESPONSE se informa al modelo y la conversación continúa.

Son la diferencia entre un fallo acotado y una factura sorpresa: más tool calls significan más llamadas externas, más latencia, más tokens, más coste y más superficie de fallo. Al estimar cuánto cuesta la IA en producción, el bucle de herramientas es la partida que más se subestima; los límites de tool calls son el freno que trae la propia librería.

Límites de tool calls configurables en Spring AI 2.0
PropiedadQué controlaPor defecto
spring.ai.tools.limits.max-calls-per-tool-defaultLlamadas máximas a una herramienta por turno40
spring.ai.tools.limits.max-calls-per-tool.<nombre>Límite específico para una herramienta—
spring.ai.tools.limits.max-total-tool-callsLlamadas totales por turno150
spring.ai.tools.limits.on-limit-exceededTHROW o RETURN_ERROR_RESPONSETHROW

Observabilidad: qué herramientas usa el agente y con qué resultado

Spring AI emite observaciones de Micrometer por cada llamada bajo spring.ai.tool: nombre y tipo de la herramienta, duración, identificador de la tool call y contexto de trazas cuando hay un Tracer.

Los argumentos y el resultado no se exportan por defecto, porque pueden contener datos sensibles; se activan con spring.ai.tools.observations.include-content, y conviene decidir antes qué se registra y con qué retención.

Sin esas señales no sabes qué herramientas se usan, cuáles fallan ni cuántas vueltas da el bucle. Es el criterio de observabilidad de agentes de IA en producción.

¿Funciona Tool Calling con streaming?

Sí. El bucle funciona con .call() y con .stream(): ToolCallingAdvisor implementa ambos caminos. Streaming no significa que cada paso intermedio llegue al usuario: el advisor filtra los fragmentos de las tool calls, así que se emite la respuesta del modelo mientras la orquestación ocurre por debajo.

Para emitir progreso a una interfaz, la vía recomendada es un advisor propio dentro del bucle. Es una diferencia con Structured Output, donde entity() solo existe en el camino bloqueante.

Qué ocurre cuando tienes cincuenta herramientas

El advisor por defecto envía todas las definiciones en cada petición. Con cinco herramientas da igual; con treinta o más —o varios servidores MCP agregando catálogos— aparecen tres problemas: contexto inflado, peor selección y coste por token en cada llamada.

Para eso existe ToolSearchToolCallingAdvisor, que implementa divulgación progresiva: indexa el catálogo por sesión, envía solo una herramienta de búsqueda y añade las definiciones que el modelo descubre en lenguaje natural. Se activa con spring.ai.chat.client.tool-search-advisor.enabled.

  1. Catálogo completo

    decenas o cientos de herramientas indexadas por sesión

  2. Primera petición

    el modelo solo ve la herramienta de búsqueda

  3. Descubrimiento

    el modelo consulta en lenguaje natural qué necesita

  4. Ejecución

    solo las herramientas encontradas viajan en las siguientes peticiones

La documentación sitúa el punto de interés a partir de diez herramientas o cuando las definiciones superan los 10K tokens por petición, y cita reducciones del 34–64 % en sus propios benchmarks con OpenAI, Anthropic y Gemini. Son sus mediciones, no una garantía universal.

Tool Calling y Structured Output no resuelven el mismo problema

Los dos usan JSON Schema y por eso se confunden. Structured Output responde a «cómo convierto la salida del modelo en un contrato Java»; Tool Calling, a «cómo permito que el modelo solicite información o acciones».

La documentación deja clara la frontera: el StructuredOutputConverter no se usa en tool calling, porque esa función ya produce argumentos estructurados. En una aplicación real conviven.

Structured Output y Tool Calling: dos usos distintos del mismo JSON Schema
DimensiónStructured OutputTool Calling
ObjetivoDar forma a la respuestaSolicitar información o una acción
El schema describeLa salida del modeloLos argumentos de la herramienta
ResultadoUn objeto JavaLa ejecución de un método Java
Papel del modeloGenera datosElige herramienta y argumentos
Papel del backendConsume el resultadoAutoriza y ejecuta la acción

La otra mitad, en Structured Output con Spring AI.

¿Tool Calling convierte un chatbot en un agente?

No. Tool Calling es un mecanismo: permite invocar herramientas y recibir su resultado. Un agente es un patrón de ejecución orientado a objetivos, en el que el modelo decide iterativamente qué pasos dar, normalmente con memoria, planificación y criterios de parada.

Las herramientas aportan capacidad de actuar; el comportamiento agéntico aparece cuando esa capacidad se organiza con memoria, control de flujo y evaluación. La arquitectura completa la desarrollamos en Spring AI, RAG y agentes, y cómo comprobar que funciona, en evals para agentes de IA.

Tool Calling frente a comportamiento agéntico
Tool CallingAgente
Mecanismo para invocar herramientasPatrón de ejecución orientado a objetivos
Puede resolverse en una sola llamadaEncadena varios pasos y decisiones
No implica autonomíaImplica cierto grado de autonomía
Es una capacidadEs una arquitectura y un comportamiento

Tool Calling y MCP: mecanismo frente a protocolo

Tool Calling es el mecanismo por el que un modelo solicita usar una capacidad y aporta los argumentos para ejecutarla. MCP es un protocolo para descubrir, exponer y consumir capacidades entre aplicaciones y servidores compatibles. No se diferencian por dónde vive el sistema al que se llama: una herramienta local de Spring AI puede consultar perfectamente un ERP, un CRM o una API remota.

Detalle de diseño: los proveedores MCP no se registran solos en el ChatClient —listar herramientas al arrancar obligaría a una llamada de red a cada servidor—, se inyectan y se pasan de forma explícita.

Diferencias entre Tool Calling y MCP
Tool CallingMCP
Mecanismo de invocaciónProtocolo de integración
Define cómo el modelo solicita una herramientaDefine cómo se exponen y descubren capacidades entre sistemas
Usa herramientas implementadas en la aplicaciónPermite consumir capacidades publicadas por servidores MCP
No establece un protocolo de interoperabilidadEstandariza esa interoperabilidad

La duda previa —recuperar conocimiento o ejecutar acciones— la tratamos en MCP vs RAG.

Caso práctico: un asistente de pedidos con herramientas de distinto riesgo

El asistente responde preguntas sobre pedidos y a veces cancela. Cuatro herramientas: getOrderStatus y getCustomerOrders leen; prepareCancellation comprueba si el pedido es cancelable; cancelOrder ejecuta la cancelación.

La decisión de arquitectura no está en el prompt, sino en qué herramientas recibe cada conversación: las de lectura viajan siempre; la cancelación, solo cuando el flujo lo requiere y el usuario tiene permiso.

Herramientas según el contexto de la conversaciónjava
@Service
class OrderAssistant {

    private final ChatClient chatClient;
    private final OrderReadTools readTools;      // getOrderStatus, getCustomerOrders
    private final OrderCancellationTools cancellationTools; // prepareCancellation, cancelOrder

    OrderAssistant(ChatClient.Builder builder,
                   OrderReadTools readTools,
                   OrderCancellationTools cancellationTools) {
        this.chatClient = builder
                .defaultSystem("""
                        Eres el asistente de pedidos de una tienda B2B.
                        Consulta siempre el estado real antes de responder.
                        No prometas cancelaciones: propón y confirma con la herramienta.
                        """)
                .defaultTools(readTools)   // lectura: siempre disponible
                .build();
        this.readTools = readTools;
        this.cancellationTools = cancellationTools;
    }

    String responder(Sesion sesion, String mensaje) {
        var peticion = chatClient.prompt()
                .user(mensaje)
                .toolContext(Map.of(
                        "tenantId", sesion.tenantId(),
                        "userId", sesion.userId()));

        // La capacidad de cancelar se habilita por petición, no como default
        if (sesion.puedeCancelar()) {
            peticion = peticion.tools(cancellationTools);
        }

        return peticion.call().content();
    }
}
  1. Usuario

    «cancela el pedido 48392»

  2. LLM

    solicita prepareCancellation

  3. Java

    comprueba permisos y reglas y devuelve el resultado

  4. LLM

    confirma con el usuario y solicita cancelOrder

  5. Java

    ejecuta la cancelación con idempotencia y auditoría

Arquitectura recomendada: una frontera de ejecución

Tool Calling es una frontera entre un componente probabilístico que decide qué quiere hacer y un código determinista que decide qué puede hacerse. Lo que la cruza es siempre lo mismo: un nombre de herramienta y unos argumentos que cumplen un schema.

Por debajo no cambia nada: autenticación, autorización, validación, reglas de dominio, idempotencia, auditoría y límites. Lo que cambia es que quien propone la acción es un modelo, y eso obliga a que esos controles sean explícitos en el método.

Si la empresa ya trabaja con Java y Spring Boot, esa frontera encaja en la arquitectura existente sin montar un stack paralelo. Es el enfoque con el que trabajamos en desarrollo de soluciones con Spring AI.

Diagrama de la frontera de ejecución en Tool Calling: arriba, la zona probabilística con prompt y herramientas, el LLM y la tool call; en medio, el contrato con nombre y JSON Schema; abajo, la zona determinista con Spring AI, autorización y el método Java, que llega a base de datos, API, ERP y CRM
El modelo propone una acción; la aplicación decide si la ejecuta y bajo qué condiciones.

Errores habituales al poner herramientas en producción

Casi ninguno es un problema de la API: son decisiones de diseño que se pagan después.

  • Dar todas las herramientas siempre: más contexto, peor selección y capacidades activas sin motivo.
  • Descripciones pobres: el modelo elige por nombre y descripción; si son ambiguos, elige mal.
  • Herramientas genéricas: un executeAction(String accion, Map args) devuelve el problema al prompt.
  • String para todo: los enums y tipos propios hacen detectable el error del modelo.
  • Exponer permisos como argumento: cancelOrder(Long id, boolean isAdmin) hace del modelo la capa de autorización.
  • No distinguir lectura de escritura: un lookup y un borrado no merecen los mismos controles.
  • Herramientas destructivas en los defaults: la comodidad de configuración se vuelve capacidad permanente.
  • Devolver excepciones internas al modelo: stack traces y SQL acaban en el contexto.
  • No observar nada: sin métricas por herramienta, el agente es opaco.

Cuándo usar Tool Calling y cuándo no

Regla práctica: si hace falta un dato que solo existe en tus sistemas o hay que ejecutar algo, Tool Calling encaja; si el modelo ya tiene el material en el contexto, sobra.

Tabla de decisión: cuándo aplicar Tool Calling
Caso¿Tool Calling?Motivo
Consultar datos actuales de un sistema propioSíEl modelo no tiene ese dato y lo inventaría
Operar sobre CRM, ERP o un workflowSí, con controlHay efectos reales: permisos, validación y auditoría
Clasificar o extraer campos de un textoNo suele hacer faltaStructured Output resuelve el contrato de salida
Responder con documentación propiaNoRAG cubre el caso con menos superficie de riesgo
Ejecutar una acción crítica sin supervisiónNo directamenteNecesita aprobación explícita y auditoría

Conclusión

Tool Calling no consiste en dejar que un LLM ejecute código: consiste en ofrecerle un conjunto explícito de capacidades cuyo contrato, ejecución y seguridad siguen perteneciendo a la aplicación. Spring AI 2.0 pone el bucle en un advisor, fija límites por defecto y deja a mano lo necesario para producción: ToolContext, control manual del bucle y observabilidad.

El modelo puede decidir qué herramienta quiere utilizar; el backend sigue decidiendo qué está permitido hacer. Esa frase resume casi todas las decisiones de diseño de este artículo.

Structured Output hace que Java reciba una salida estructurada y tipada bajo un contrato verificable —que la forma sea correcta no significa que el contenido sea cierto—; Tool Calling le da al modelo una vía controlada para pedir información y solicitar acciones. Son las dos piezas básicas de una aplicación Java con IA que hace algo más que conversar.

Fuentes técnicas

Preguntas frecuentes

¿Qué es Tool Calling en Spring AI?

Es el mecanismo para exponer funciones de una aplicación Java a un modelo mediante un nombre, una descripción y un JSON Schema de argumentos. El modelo puede solicitar la ejecución de una herramienta; Spring AI y la aplicación son quienes la resuelven y la ejecutan.

¿El LLM ejecuta directamente el método Java?

No. El modelo devuelve el nombre de la herramienta y los argumentos que propone; ToolCallingAdvisor y ToolCallingManager localizan el ToolCallback y ejecutan el método dentro de la aplicación. El modelo nunca recibe acceso a la base de datos ni a las APIs.

¿Cómo funciona @Tool en Spring AI?

Anotas un método con @Tool y sus parámetros con @ToolParam. Spring AI genera la definición —nombre, descripción y JSON Schema— y la envía al modelo con el prompt. La anotación admite name, description, returnDirect y resultConverter; por defecto todos los parámetros son obligatorios.

¿Qué diferencia hay entre @Tool y ToolCallback?

ToolCallback es la abstracción que modela una herramienta: la definición que ve el modelo y la lógica que se ejecuta. @Tool es la vía declarativa para producir uno desde un método; MethodToolCallback y FunctionToolCallback son las programáticas. Las tres circulan por el mismo bucle.

¿Qué diferencia hay entre Tool Calling y Structured Output?

Structured Output da forma a la respuesta final y la convierte en un objeto Java. En Tool Calling el schema describe los argumentos de la herramienta que el modelo quiere ejecutar, no la salida. La documentación aclara que el StructuredOutputConverter no se usa en tool calling.

¿Tool Calling convierte un chatbot en un agente?

No necesariamente. Tool Calling es la capacidad de invocar herramientas; el comportamiento agéntico aparece cuando el modelo encadena pasos hacia un objetivo, con memoria, criterios de parada y control de flujo.

¿Qué diferencia hay entre Tool Calling y MCP?

Tool Calling define cómo el modelo usa herramientas dentro de una aplicación; MCP es un protocolo para exponerlas y consumirlas entre sistemas. Spring AI puede consumir herramientas de servidores MCP y exponer las suyas, y el bucle que las ejecuta es el mismo.

¿Qué es ToolContext y para qué sirve?

Es el mecanismo para pasar a una herramienta datos que no debe controlar el modelo: tenant, usuario autenticado, ámbito de la petición o identificadores de traza. No se envían al modelo: llegan al método en el momento de la invocación, así que la identidad no depende de un argumento que el LLM pueda inventar.

¿Funciona Tool Calling con streaming en Spring AI?

Sí. El bucle funciona con .call() y con .stream(), porque ToolCallingAdvisor conduce ambos modos. Streaming no significa que cada resultado intermedio llegue al usuario: se emite la respuesta del modelo mientras la orquestación sigue por debajo.

¿Cómo se limita el número de llamadas a herramientas?

Spring AI 2.0 aplica límites por turno: 40 llamadas por herramienta y 150 en total. Se ajustan con las propiedades spring.ai.tools.limits.* o con el builder de ToolCallingManager, y al superarlos puede lanzarse una excepción o devolverse un error al modelo para que continúe.

  • Leer artículo
    Desarrollo con IA12 min

    Structured Output con Spring AI: de texto a objetos Java fiables

  • Leer artículo
    Desarrollo con IA18 min

    Spring AI vs LangChain4j: qué framework elegir para IA en Java

  • Leer artículo
    Desarrollo con IA13 min

    Spring AI, RAG y agentes: cómo construir aplicaciones Java con inteligencia artificial real

Ver todos los artículos