Cómo construir un agente de IA con NestJS + Claude API
Tiempo estimado de lectura: 4 min
- Separar cliente LLM, registro de herramientas y loop de agente para modularidad y pruebas.
- Herramientas como contratos JSON-schema y validación antes de ejecución.
- Agent loop controlado: límites de iteraciones, métricas y manejo de errores normalizado.
- Producción: no bloquear peticiones HTTP, usar SSE/WebSockets y proteger PII y costes.
- Observabilidad y testing: trazas por sesión, mocks para provider y canary releases.
Introducción
Saber cómo construir un agente de IA con NestJS + Claude API es lo que separa una demo interesante de una pieza de infraestructura que puedas mantener en producción. En este artículo encontrarás la arquitectura, decisiones técnicas y patrones que realmente importan cuando implementas tool-calling de Claude dentro de un backend modular y tipado como NestJS.
Un agente no es un chatbot: es un bucle de decisión. Recibe objetivo, decide herramientas, ejecuta, observa resultados y vuelve a razonar hasta resolver la tarea o agotar límites.
Resumen rápido (lectores con prisa)
Qué: Un agente es un bucle de decisión que orquesta llamadas a herramientas externas desde un LLM.
Cuándo: Cuando necesitas que un LLM interactúe con sistemas externos (DB, APIs, logs) de forma controlada.
Por qué importa: Permite trazabilidad, testing y control de costes frente a soluciones ad-hoc.
Cómo: Separando un provider LLM, un registro de herramientas con schemas JSON y un agente que controla el loop y límites.
Cómo construir un agente de IA con NestJS + Claude API: arquitectura y flujo
Ese bucle —Agent Loop— obliga a diseñar responsabilidades claras. El agente recibe un objetivo, decide qué herramienta usar, ejecuta esa herramienta, observa el resultado y repite hasta resolver la tarea o agotar límites.
Arquitectura mínima recomendada:
- Proveedor del cliente LLM (Anthropic) como
Providerde NestJS. - Registro dinámico de herramientas (
ToolRegistryService) que mapea nombres/JSON-schema a métodos de servicios. - Motor de ejecución (
AgentService) que orquesta el loop y gestiona historial, stop reasons y seguridad.
Referencias: NestJS Docs y Anthropic Tool Use
Capa 1 — AnthropicProvider: el cliente como dependencia inyectable
Nunca newees el cliente de Anthropic en controladores o servicios. Crea un provider que se inyecte y centralice la lógica del cliente.
- Lee
ANTHROPIC_API_KEYmedianteConfigService. - Envuelve lógica de retries, backoff y métricas (tokens usados, latencia).
- Permite mockear en tests unitarios.
Ejemplo conceptual: el provider expone sendMessage(payload) que encapsula anthropic.messages.create() y normaliza la respuesta (stop_reason, content, tool_call).
Beneficio: centralizas control de coste y modelo (p. ej. cambiar de Claude 3.5 a otro modelo sin tocar el resto).
Capa 2 — ToolRegistry: herramientas como contratos JSON y servicios
Claude espera herramientas descritas por JSON-schema. En el backend conviene modelarlas y validar antes de ejecutar.
- Definir cada herramienta con nombre, descripción y schema (tipado TypeScript).
- Mapear cada herramienta a un método de servicio inyectable (p. ej.
UsersService.getById,LogsService.append). - Implementar granularidad: una herramienta = una responsabilidad.
Diseño práctico:
ToolRegistryServicemantiene un mapa {toolName -> { schema, executor }}.executor(args)valida los args contra el schema y ejecuta el método correspondiente en try/catch.
Así el agent loop no necesita switches gigantes; resuelve métodos dinámicamente.
Capa 3 — AgentService: el bucle, stop_reasons y límites
El AgentService orquesta el loop de decisión, mantiene el historial y aplica límites y políticas de seguridad.
Patrón del Agent Loop
- Construir
messages+tools(esquemas) y llamar a Anthropic. - Leer
stop_reason:end_turn: devolver respuesta final.tool_use: extraertool_nameyarguments.
- Ejecutar herramienta vía
ToolRegistryServicey añadirtool_resultal historial. - Repetir hasta
end_turno alcanzar un límite de iteraciones.
Normas prácticas
- Límite de iteraciones (p. ej. max 5) para evitar bucles y costes excesivos.
- Cada iteración registra métricas: tokens, latencia, herramienta invocada.
- Normaliza errores: si la herramienta falla, devuelve
{ error: 'timeout' }a Claude, no throws.
Producción: latencia, UX y seguridad
Al pasar a producción hay que priorizar experiencia de usuario, seguridad y observabilidad.
Latencia y UX
- No bloquees la petición HTTP principal. Emite progreso con SSE o WebSockets: “Pensando…”, “Consultando DB…”.
- Opcional: respuesta rápida + notificación cuando el resultado final esté listo (webhook / push).
Seguridad y validación
- Valida argumentos de herramientas con
class-validator/schema JSON antes de ejecutar. - Logs sensibles: evita enviar secrets o PII a la API de Claude.
- Rate limits y circuit breakers en el provider para proteger tu backend y controlar facturación.
Observabilidad
Traza cada sesión como un árbol de spans: requests al LLM, ejecuciones de herramientas, errores.
Integra herramientas de visualización y trazabilidad como Langfuse o LangSmith para visualizar flujos y coste por sesión.
Testing y despliegue
- Mockea el
AnthropicProvidery elToolRegistryServiceen tests unitarios. - Tests de integración: entorno con Claude sandbox o replay de respuestas deterministas.
- Canary releases: habilita el agente en subset de usuarios antes de producir a toda la base.
Coste y gobernanza
- Mide tokens por iteración; extrapola a coste por sesión.
- Define políticas: cuándo permitir tool-calling (p. ej. solo usuarios verificados) y límites diarios.
Qué evitar (errores comunes)
- Herramientas “dios” que hacen todo: dificultan autorización y testing.
- Dejar excepciones sin capturar: provoca loops rotos y mala UX.
- No auditar llamadas: sin trazabilidad no sabrás por qué el agente falló o costó tanto.
Cierre práctico
Construir un agente con NestJS + Claude API no es magia, es disciplina arquitectónica. Si abstraes el cliente, modelas herramientas como contratos y controlas el bucle con límites, obtienes un sistema escalable, testeable y seguro.
En el siguiente artículo veremos ejemplos concretos de ToolRegistryService y patrones para emitir progreso en SSE desde NestJS para mejorar la experiencia del usuario.
Dominicode Labs
Para continuidad en temas de automatización y agentes, consulta recursos adicionales en Dominicode Labs. Es una fuente útil para patrones, ejemplos y plantillas prácticas que complementan esta arquitectura.
FAQ
Respuesta: Un agente es un bucle de decisión que recibe un objetivo, decide qué herramienta invocar, ejecuta esa herramienta, observa el resultado y repite hasta completar la tarea o agotar límites.
Respuesta: Un provider centraliza la configuración del cliente (API key, retries, métricas), facilita el mock en tests y permite cambiar de modelo o proveedor sin modificar la lógica de negocio.
Respuesta: Las herramientas se describen con nombre, descripción y un JSON-schema (tipado TypeScript). Se valida la entrada antes de ejecutar y se mapea a funciones/executors inyectables.
Respuesta: stop_reason indica la acción del LLM: end_turn para respuesta final o tool_use para invocar una herramienta. El agente interpreta y actúa según ese valor.
Respuesta: No bloquear la petición HTTP principal; usar SSE o WebSockets para emitir progreso. También considerar respuestas rápidas con notificación posterior y aplicar límites de iteraciones para controlar latencia y coste.
Respuesta: Mockear el provider y el registry en tests unitarios; usar sandbox o replays deterministas en integración; ejecutar canary releases antes de un despliegue completo.
