Agentes LLM robustos: arquitectura hexagonal en Python

En pocas palabras: Un asistente de investigación de mercado robusto separa el dominio de negocio de las APIs externas y los modelos de inferencia usando el patrón Ports and Adapters (arquitectura hexagonal). Cada proveedor —Oanda, TwelveData, Claude, Ollama— es un adaptador intercambiable, y Pydantic valida cada dato: el LLM razona, nunca calcula.

Si alguna vez armaste un agente LLM que consultaba una API de mercado y se te cayó entero porque el proveedor cambió un campo, ya conocés el problema. El artículo “Building a Robust Market Research Assistant with Claude”, publicado el 27 de agosto de 2026 en dev.to, propone una solución concreta: arquitectura hexagonal, validación estricta con Pydantic y enrutamiento de inferencia desacoplado. La idea central es simple. El LLM razona, pero no calcula.

Un asistente de investigación de mercado robusto es un sistema que separa el núcleo de negocio de las APIs externas y de los modelos de inferencia, usando el patrón Ports and Adapters. El dominio queda aislado: los datos financieros entran por un puerto, el razonamiento sale por otro, y cada proveedor concreto (Oanda, TwelveData, Claude, Ollama) es un adaptador intercambiable que respeta el mismo contrato.

En 30 segundos

  • Dos modos de fallo: acoplamiento ajustado (una API rota tira todo el pipeline) y outputs frágiles (el LLM alucina cifras).
  • Arquitectura hexagonal: núcleo de dominio aislado detrás de puertos (MarketDataPort, LLMInferencePort); los proveedores son adaptadores reemplazables.
  • Regla de oro: RSI, MACD y medias móviles se calculan en Python puro; al modelo solo le pasás métricas ya validadas.
  • Failover real: si el proveedor primario tira HTTP 429, el orquestador cambia a un adaptador de respaldo con el mismo contrato.
  • Pydantic: valida cada respuesta del modelo con esquemas inmutables para que no te llegue un campo faltante o un tipo cambiado.

Claude es un modelo de lenguaje grande desarrollado por Anthropic que genera texto, responde preguntas y asiste en análisis de datos. Disponible en versiones para diferentes casos de uso, desde investigación de mercados hasta programación.

¿Por qué fallan los agentes LLM en producción?

Fallan por dos razones, y las dos son de diseño. La primera es el acoplamiento ajustado: cuando la lógica de orquestación del LLM queda pegada directo a las APIs de datos, cualquier cambio del proveedor rompe todo el pipeline. La segunda es el output frágil: si dejás que el modelo genere números “deterministas” a mano, tarde o temprano te devuelve una cifra inventada.

Ponele que tu agente pide el RSI de un par de divisas. El proveedor renombra un campo en el JSON, tu parser explota, y el orquestador se lleva puesto todo lo que venía atrás. ¿Y qué pasa cuando eso ocurre a las tres de la mañana en producción? Exacto, nadie se entera hasta que un cliente reclama. Relacionado: entender las capacidades de Claude.

El otro caso es más traicionero. El modelo no se cae: te miente con seguridad. Le pedís que analice métricas y de paso “calcule” un promedio, y te devuelve un 68,4 que no existe en ningún lado. Según el artículo original, confiar en la generación de texto crudo para indicadores deterministas produce figuras alucinadas y caídas río abajo. La diferencia entre los dos fallos importa: uno es del proveedor, el otro es tuyo por dejar que el LLM haga aritmética.

¿Qué es la arquitectura hexagonal en agentes LLM?

La arquitectura hexagonal, o Ports and Adapters, es un patrón donde el núcleo de dominio no sabe nada del mundo exterior. Define puertos (contratos abstractos) y deja que los adaptadores concretos implementen esos contratos. En un agente robusto de investigación con Claude, esto significa que tu lógica nunca toca un cliente HTTP ni un motor de inferencia específico.

El esquema tiene capas claras. Arriba, la interfaz (CLI, API). Abajo, la capa de aplicación con el ResearchCoordinator y el AnalysisOrchestrator. Y en el medio, dos puertos que actúan de frontera: MarketDataPort para los datos y LLMInferencePort para el razonamiento. Cada puerto tiene sus adaptadores. Del lado de datos: OandaAdapter, TwelveDataAdapter y MockDataAdapter. Del lado de inferencia: OllamaAdapter, OpenRouterAdapter y ClaudeAdapter.

Los beneficios son tres y son reales. Testing sin costo, porque el MockDataAdapter te deja correr tests de integración completos sin consumir rate limits ni créditos pagos. Failover resiliente, porque si el proveedor primario tira un 429, el orquestador salta a un adaptador de respaldo que implementa el mismo contrato. Y migraciones sin downtime, porque cambiar de proveedor es cambiar un adaptador, no reescribir el core.

¿Cómo desacoplar los datos del razonamiento del LLM?

Separás el cálculo determinista de la síntesis semántica, y no dejás que se mezclen nunca. Los indicadores técnicos (RSI, medias móviles, MACD) se computan directo en Python dentro de los adaptadores de datos. Recién ahí, con las métricas ya calculadas y validadas, le pasás el snapshot al modelo. El LLM sintetiza y perfila riesgo. No genera números. Ya lo cubrimos antes en elegir el modelo Claude correcto.

El flujo es lineal: traés los datos con el adaptador (por ejemplo OandaAdapter para históricos OHLCV), calculás los indicadores en código puro, y armás un prompt que le da al modelo las cifras verificadas. En el artículo original, el OllamaResearchAdapter corre local en http://localhost:11434 y su función generate_executive_summary incluye una instrucción explícita: “Do not generate or modify numerical metrics”. Es decir, sintetizá el riesgo, pero no toques los números.

¿Por qué tanto cuidado? Porque los LLM son buenísimos para redactar un párrafo de contexto y pésimos para garantizar que 2 más 2 dé 4 todas las veces. Dejá que Python haga la matemática (que para eso es determinista) y que el modelo haga lo que sabe: leer patrones y explicarlos en lenguaje natural.

¿Cómo validar outputs de LLM con Pydantic?

Con Pydantic definís un esquema inmutable y forzás a que cada respuesta del modelo lo cumpla antes de seguir. Si falta un campo, si el tipo no coincide o si un valor cae fuera de rango, la validación falla ahí mismo, en la frontera, y no diez capas más abajo cuando ya es imposible rastrear el origen del bug.

Pensá un modelo ResearchSummary con un RSI: float validado, un MACD_Signal como enum acotado y un Risk_Level tipo Literal["low", "medium", "high"]. Si el modelo devuelve “riesgo alto-ish”, Pydantic lo rechaza. Sin sorpresas silenciosas. El propio artículo original insiste en el mismo punto: forzar JSON estructurado y validar contra el esquema evita los errores de parseo que rompen todo lo que viene después.

Eso sí: la validación no es magia. Si el prompt pide algo ambiguo, el modelo va a fallar y Pydantic te va a marcar el fallo, pero no te lo va a arreglar. La validación es tu red, no tu piloto automático. Esto se conecta con lo que analizamos en configurar Claude Code en tu proyecto.

¿Qué es el enrutamiento inteligente de herramientas?

El enrutamiento (tool routing) es la lógica que decide qué adaptador usar según la tarea. Si el agente necesita históricos de forex, va al adaptador de datos de mercado. Si necesita razonar sobre esas métricas, va al adaptador de inferencia. La decisión puede ser por reglas fijas, por clasificación del propio LLM, o por costo. Como todos respetan el mismo contrato de puerto, cambiar de ruta no rompe nada.

TareaPuertoAdaptador sugeridoPor qué
Datos históricos OHLCVMarketDataPortOandaAdapterFuente primaria de precios de mercado
Indicadores pre-calculadosMarketDataPortTwelveDataAdapterTrae snapshots técnicos listos
Tests de integraciónMarketDataPortMockDataAdapterCero consumo de rate limits ni créditos
Razonamiento local / privadoLLMInferencePortOllamaAdapter (localhost:11434)Inferencia local, sin salir a la nube
Síntesis de calidad altaLLMInferencePortClaudeAdapterPerfilado de riesgo y resumen ejecutivo
Fallback ante 429LLMInferencePortOpenRouterAdapterRespaldo con el mismo contrato de puerto
claude building robust diagrama explicativo

¿Cómo se arman ResearchCoordinator y AnalysisOrchestrator?

El ResearchCoordinator orquesta el flujo completo y no conoce implementaciones concretas: recibe los puertos por inyección de dependencias. Pide datos al MarketDataPort, dispara el análisis, manda la síntesis al LLMInferencePort, valida el esquema con Pydantic y devuelve el resultado. El AnalysisOrchestrator encapsula la lógica de dominio (los cálculos), separada de la coordinación.

El pseudocódigo del flujo es más o menos así: recibís el pedido de investigación, traés los históricos por el puerto de datos, calculás los ratios e indicadores en Python, armás el snapshot verificado, se lo pasás al modelo para el perfil de riesgo, validás la respuesta contra el esquema, y devolvés un output estructurado. Si algo falla en el medio (una API que no responde, un modelo que no cumple el esquema), el error queda contenido en su capa. Sigue un ciclo tipo ReAct: pensar, actuar (traer datos), observar (validar), sintetizar. La inyección de dependencias es lo que hace todo esto testeable, porque en los tests inyectás los mocks y listo.

Qué está confirmado y qué no

  • Confirmado: el artículo de dev.to (27 de agosto de 2026) documenta la arquitectura Ports and Adapters, los nombres de los adaptadores y la instrucción anti-aritmética en el prompt de Ollama.
  • Confirmado: el failover ante HTTP 429 y el testing con MockDataAdapter son parte del diseño descripto.
  • No confirmado: el código completo, los benchmarks de latencia o el costo por consulta no aparecen en la fuente. El post muestra el esquema, no un repositorio auditable con números de performance.
  • No confirmado: qué tan bien escala el patrón con decenas de adaptadores simultáneos. Habría que probarlo, tomalo con pinzas.

Errores comunes al diseñar agentes LLM

  • Dejar que el LLM calcule números: es el error más caro. Calculá los indicadores en Python y pasale al modelo solo el snapshot validado. La síntesis es del LLM; la aritmética, no.
  • Pegar la orquestación a la API concreta: si tu código llama directo al cliente de Oanda, cada cambio del proveedor te rompe todo. Meté un puerto en el medio y dejá el proveedor detrás de un adaptador.
  • No validar la salida del modelo: confiar en el texto crudo es pedir campos faltantes y tipos rotos. Un esquema Pydantic en la frontera te ahorra horas de debugging a ciegas.
  • Tareas demasiado amplias: un agente que “hace todo” es imposible de testear. Acotá cada componente a una responsabilidad clara.
  • Saltar directo a un framework sin base: si no entendés por qué necesitás puertos, ningún framework te va a salvar. El patrón primero, la herramienta después.

Qué significa para equipos en Latinoamérica

Para un equipo chico que corre esto en producción, el desacople es plata directa. Podés arrancar con inferencia local vía Ollama para no pagar tokens en desarrollo, y recién en producción rutear a un modelo más pesado sin tocar el core. Y cuando toque desplegar la API que expone el agente, tenerla sobre infraestructura confiable importa: para hosting y servidores en Argentina, donweb.com te resuelve el dónde sin complicarte. El patrón te deja migrar de proveedor de datos o de modelo sin downtime, que para un equipo sin guardia 24/7 es la diferencia entre dormir tranquilo y no.

Preguntas Frecuentes

¿Qué es la arquitectura hexagonal en Python?

Es un patrón de diseño donde el núcleo de negocio se aísla del exterior mediante puertos (interfaces abstractas) y adaptadores (implementaciones concretas). En Python se implementa con clases base abstractas para los puertos y clases concretas para cada proveedor. El dominio no sabe si los datos vienen de Oanda o de un mock. Tema relacionado: integrar Claude con herramientas externas.

¿Cómo evito que el LLM invente datos numéricos?

Calculás las métricas fuera del modelo, en código determinista, y le pasás solo los valores ya verificados con una instrucción explícita de no modificarlos. El artículo original usa la línea “Do not generate or modify numerical metrics” en el prompt. Sumale validación Pydantic sobre la respuesta y cerrás el círculo.

¿Para qué sirve Pydantic en agentes LLM?

Pydantic valida que la salida del modelo cumpla un esquema definido: tipos correctos, campos presentes y valores dentro de rango. Si el modelo devuelve algo mal formado, la validación falla en la frontera y no propaga el error. Es la red de seguridad entre el texto del LLM y tu lógica de negocio.

¿Qué pasa si el proveedor de datos tira error 429?

El orquestador cambia a un adaptador de respaldo que implementa el mismo contrato de puerto, sin tocar el núcleo. Como todos los adaptadores respetan la misma interfaz, el failover es transparente para el resto del sistema. Por eso el patrón Ports and Adapters resiste caídas y límites de rate.

¿Conviene usar Claude u Ollama para el razonamiento?

Depende del caso: Ollama corre local (en localhost:11434), ideal para desarrollo y datos privados sin costo por token; Claude sirve para síntesis de mayor calidad en producción. Con la arquitectura hexagonal no tenés que elegir uno para siempre: cambiás de ClaudeAdapter a OllamaAdapter sin reescribir el core.

Conclusión

Lo que cambia con este enfoque no es la magia del modelo, es la ingeniería alrededor. Separar el cálculo determinista de la síntesis semántica, meter puertos entre tu dominio y el mundo, y validar cada salida con Pydantic transforma un agente frágil en uno que aguanta producción. Si vas a construir un agente LLM que toque datos reales, arrancá por el patrón: definí tus dos puertos (datos e inferencia), escribí primero el MockDataAdapter para testear sin costo, y no le dejes al modelo ni una sola operación aritmética. El resto son detalles.

Fuentes

Desplazarse hacia arriba