<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Christian Gonzales Komiya</title>
    <description>The latest articles on DEV Community by Christian Gonzales Komiya (@ckomiya).</description>
    <link>https://dev.to/ckomiya</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3976243%2F7ae6966c-9d81-4d32-ad97-3215224dc563.png</url>
      <title>DEV Community: Christian Gonzales Komiya</title>
      <link>https://dev.to/ckomiya</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ckomiya"/>
    <language>en</language>
    <item>
      <title>[Spanish] La IA no reemplaza las metodologías ágiles: cambia la forma en que las integramos para transformar los negocios</title>
      <dc:creator>Christian Gonzales Komiya</dc:creator>
      <pubDate>Mon, 02 Nov 2026 23:44:56 +0000</pubDate>
      <link>https://dev.to/ckomiya/la-ia-no-reemplaza-las-metodologias-agiles-cambia-la-forma-en-que-las-integramos-para-transformar-2pem</link>
      <guid>https://dev.to/ckomiya/la-ia-no-reemplaza-las-metodologias-agiles-cambia-la-forma-en-que-las-integramos-para-transformar-2pem</guid>
      <description>&lt;p&gt;Con la llegada de la inteligencia artificial, es natural preguntarnos si metodologías como &lt;em&gt;Design Thinking&lt;/em&gt;, &lt;em&gt;Lean Startup&lt;/em&gt;, &lt;em&gt;Agile&lt;/em&gt; y &lt;em&gt;Change Management&lt;/em&gt; necesitan reinventarse.&lt;/p&gt;

&lt;p&gt;Mi impresión es que sus fundamentos siguen vigentes, pero su aplicación está evolucionando.&lt;/p&gt;

&lt;p&gt;No creo que necesitemos un nuevo &lt;em&gt;Design Thinking&lt;/em&gt; o un nuevo &lt;em&gt;Agile&lt;/em&gt;. Sin embargo, sí debemos revisar cómo aprovechamos estas metodologías en un entorno donde la IA está cambiando la velocidad, el costo y las posibilidades de hacer las cosas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ejemplos de evolución metodológica
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Design Thinking: descubrir problemas sigue siendo fundamental
&lt;/h3&gt;

&lt;p&gt;La IA permite analizar entrevistas, encuestas y comentarios de clientes con mayor rapidez. También facilita generar ideas y construir prototipos.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lo que no cambia:&lt;/strong&gt; Necesitamos comprender a las personas y validar que estamos resolviendo problemas reales.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;El cuidado:&lt;/strong&gt; La IA puede ayudarnos a interpretar información, pero no debemos confundir sus respuestas con evidencia de los usuarios.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Lean Startup: experimentar nunca había sido tan accesible
&lt;/h3&gt;

&lt;p&gt;Crear un MVP, probar una hipótesis o explorar diferentes alternativas puede ser mucho más rápido y económico gracias a la IA.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ciclo Build-Measure-Learn:&lt;/strong&gt; Sigue teniendo sentido. Lo que cambia es nuestra capacidad para recorrerlo más veces, con menor inversión y aprendiendo más rápido.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La regla de oro:&lt;/strong&gt; Construir un prototipo en minutos no significa validar un negocio en minutos. La evidencia sigue siendo indispensable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Agile: los principios se mantienen, pero la ejecución cambia
&lt;/h3&gt;

&lt;p&gt;La entrega incremental, la colaboración, el feedback y la adaptación siguen siendo relevantes.&lt;/p&gt;

&lt;p&gt;La IA puede acelerar la programación, las pruebas, la documentación y otras actividades del desarrollo de software. Esto obliga a repensar algunas prácticas de los equipos, pero no elimina la necesidad de inspeccionar, adaptar y garantizar la calidad.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;El reto:&lt;/strong&gt; No es solamente desarrollar más rápido, sino asegurarnos de que estamos construyendo algo correcto, seguro y valioso.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  4. Change Management: la transformación va más allá de adoptar una nueva herramienta
&lt;/h3&gt;

&lt;p&gt;La IA no solo introduce nuevas tecnologías. También puede modificar procesos, responsabilidades, competencias y formas de trabajar.&lt;/p&gt;

&lt;p&gt;Por eso, la gestión del cambio adquiere una dimensión adicional: ayudar a las personas y organizaciones a trabajar de una manera diferente, en colaboración con sistemas y agentes de IA.&lt;/p&gt;




&lt;h2&gt;
  
  
  El verdadero cambio es transversal
&lt;/h2&gt;

&lt;p&gt;Quizás el cambio más importante no está dentro de cada metodología, sino en la manera en que las integramos. Podemos pasar de un enfoque tradicional a uno orientado a resultados de negocio.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Enfoque Tradicional&lt;/th&gt;
&lt;th&gt;Nuevo Enfoque con IA&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;¿Qué sistema necesitamos desarrollar?&lt;/td&gt;
&lt;td&gt;¿Qué resultado de negocio queremos mejorar y qué nos impide conseguirlo?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A partir de ahí, combinamos diferentes capacidades potenciadas de forma transversal por la IA:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Design Thinking:&lt;/strong&gt; Comprender a las personas y descubrir oportunidades.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lean Startup:&lt;/strong&gt; Formular hipótesis y experimentar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agile:&lt;/strong&gt; Desarrollar y evolucionar soluciones.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Change Management:&lt;/strong&gt; Transformar procesos y facilitar la adopción.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Una reflexión final
&lt;/h2&gt;

&lt;p&gt;Quizás el futuro de la agilidad no consiste en aplicar Scrum con herramientas de IA o en incorporar agentes a nuestros equipos.&lt;/p&gt;

&lt;p&gt;Consiste en ampliar nuestra capacidad para ayudar a las organizaciones a &lt;strong&gt;descubrir qué necesitan transformar, experimentar con nuevas posibilidades y conseguir resultados de negocio mediante tecnología.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;La IA no hace obsoletas nuestras metodologías; nos obliga a revisar si las estamos utilizando para el propósito correcto. Tal vez esa sea una de las evoluciones más interesantes del &lt;em&gt;Agile Coach&lt;/em&gt;: pasar de ayudar a las organizaciones a desarrollar productos de manera ágil, a ayudarlas a descubrir y ejecutar su transformación.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;¿Qué opinas?&lt;/strong&gt; ¿Estamos ante una evolución de las metodologías actuales o necesitamos empezar a pensar en nuevos modelos de trabajo?&lt;/p&gt;

</description>
      <category>agile</category>
      <category>ai</category>
      <category>management</category>
    </item>
    <item>
      <title>[Spanish] De desarrollar software a transformar negocios: el verdadero valor de la tecnología en la era de la IA</title>
      <dc:creator>Christian Gonzales Komiya</dc:creator>
      <pubDate>Mon, 02 Nov 2026 21:26:23 +0000</pubDate>
      <link>https://dev.to/ckomiya/spanish-de-desarrollar-software-a-transformar-negocios-el-verdadero-valor-de-la-tecnologia-en-la-583l</link>
      <guid>https://dev.to/ckomiya/spanish-de-desarrollar-software-a-transformar-negocios-el-verdadero-valor-de-la-tecnologia-en-la-583l</guid>
      <description>&lt;p&gt;La inteligencia artificial está cambiando la forma en que se construye&lt;br&gt;
software. Hoy, una persona o un equipo pequeño puede desarrollar&lt;br&gt;
aplicaciones, dashboards, automatizaciones y agentes de IA en mucho&lt;br&gt;
menos tiempo y con menores costos que hace unos años.&lt;/p&gt;

&lt;p&gt;Esto abre oportunidades enormes, pero también plantea una pregunta: &lt;strong&gt;si&lt;br&gt;
cada vez más personas pueden construir soluciones tecnológicas, ¿qué&lt;br&gt;
hará que una empresa o un profesional se diferencie?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  El software como ventaja competitiva está cambiando
&lt;/h2&gt;

&lt;p&gt;Durante mucho tiempo, desarrollar software exigía conocimientos&lt;br&gt;
especializados, equipos, tiempo y presupuesto. Esas barreras limitaban&lt;br&gt;
la cantidad de competidores y hacían que la capacidad de construir una&lt;br&gt;
solución fuera, por sí misma, una ventaja.&lt;/p&gt;

&lt;p&gt;La IA está reduciendo esas barreras. Cada vez es más sencillo crear&lt;br&gt;
productos que resuelven problemas similares: sistemas de reservas, CRM,&lt;br&gt;
dashboards, aplicaciones administrativas, tiendas virtuales, chatbots y&lt;br&gt;
automatizaciones.&lt;/p&gt;

&lt;p&gt;Eso no significa que todo el software vaya a convertirse en una&lt;br&gt;
mercancía indiferenciada. La calidad, la seguridad, la arquitectura, la&lt;br&gt;
integración y el mantenimiento siguen importando. Pero sí significa que&lt;br&gt;
&lt;strong&gt;la capacidad de programar, por sí sola, puede dejar de ser un&lt;br&gt;
diferenciador suficiente&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Además, que sea más fácil construir una aplicación no significa que sea&lt;br&gt;
fácil conseguir clientes, comprender sus necesidades, generar confianza&lt;br&gt;
o lograr que una solución se utilice y produzca resultados.&lt;/p&gt;

&lt;h2&gt;
  
  
  El valor se desplaza hacia el conocimiento del negocio
&lt;/h2&gt;

&lt;p&gt;Quizás el cambio más importante sea pasar de preguntar:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;¿Qué software necesita el cliente?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A preguntar:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;¿Qué necesita mejorar el negocio y cómo puede ayudar la tecnología a&lt;br&gt;
conseguirlo?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Un proveedor tradicional puede ofrecer una aplicación de ventas. Un&lt;br&gt;
socio con visión de negocio empieza por entender cómo se consiguen los&lt;br&gt;
clientes, dónde se pierden oportunidades, cuánto tarda el seguimiento&lt;br&gt;
comercial y qué información necesita la dirección para tomar decisiones.&lt;/p&gt;

&lt;p&gt;Después de ese diagnóstico, puede proponer una combinación de&lt;br&gt;
herramientas: una aplicación, un dashboard, una integración, un agente&lt;br&gt;
de IA, una automatización o incluso un cambio en el proceso.&lt;/p&gt;

&lt;p&gt;La tecnología deja de ser el objetivo y se convierte en un medio para&lt;br&gt;
conseguir una mejora concreta.&lt;/p&gt;

&lt;h2&gt;
  
  
  De proveedor de software a socio estratégico
&lt;/h2&gt;

&lt;p&gt;Podemos distinguir dos enfoques:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Enfoque centrado en el software&lt;/th&gt;
&lt;th&gt;Enfoque centrado en el negocio&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Vender una aplicación, un dashboard o un chatbot.&lt;/td&gt;
&lt;td&gt;Resolver un problema operativo o comercial.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Competir por funcionalidades, precio y tiempo de entrega.&lt;/td&gt;
&lt;td&gt;Diferenciarse por la comprensión del negocio y los resultados.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Entregar el producto y cerrar el proyecto.&lt;/td&gt;
&lt;td&gt;Acompañar la adopción, medir resultados y mejorar la solución.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;El software es el producto principal.&lt;/td&gt;
&lt;td&gt;La tecnología es una parte de una solución más amplia.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;El segundo enfoque requiere más que conocimientos técnicos. Exige&lt;br&gt;
entender procesos, costos, clientes, indicadores, prioridades y&lt;br&gt;
objetivos. También exige comunicar, negociar, facilitar cambios y&lt;br&gt;
conseguir que las personas adopten las soluciones.&lt;/p&gt;

&lt;h2&gt;
  
  
  La consultoría también tendrá que diferenciarse
&lt;/h2&gt;

&lt;p&gt;No basta con cambiar el rótulo de desarrollador por el de consultor de&lt;br&gt;
IA o transformación digital. Si todos ofrecen el mismo discurso, la&lt;br&gt;
consultoría también puede saturarse.&lt;/p&gt;

&lt;p&gt;La diferenciación puede surgir del conocimiento profundo de determinados&lt;br&gt;
problemas empresariales, de una metodología propia, de la capacidad de&lt;br&gt;
ejecutar y de la experiencia acumulada en proyectos reales.&lt;/p&gt;

&lt;p&gt;Una consultora pequeña podría combinar conocimiento especializado,&lt;br&gt;
herramientas de IA, agentes, componentes reutilizables y una red de&lt;br&gt;
colaboradores. Así podría ofrecer capacidades que antes requerían&lt;br&gt;
equipos mucho más grandes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Un ciclo de trabajo orientado a resultados
&lt;/h2&gt;

&lt;p&gt;Una relación de este tipo podría organizarse en cinco etapas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Diagnosticar:&lt;/strong&gt; comprender el negocio, sus procesos, dificultades,
costos y oportunidades.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Priorizar:&lt;/strong&gt; identificar qué problemas conviene resolver primero y
qué impacto se espera.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Diseñar e implementar:&lt;/strong&gt; seleccionar y construir la combinación
adecuada de aplicaciones, datos, dashboards, integraciones,
automatizaciones y agentes de IA.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Acompañar la adopción:&lt;/strong&gt; ajustar procesos, capacitar a las
personas y facilitar el cambio.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Medir y mejorar:&lt;/strong&gt; comprobar si hubo ahorro, productividad, más
ventas, menos errores o una mejor experiencia del cliente.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Desarrollar software es importante, pero es solo una parte del&lt;br&gt;
recorrido.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Cuándo ocurrirá este cambio?
&lt;/h2&gt;

&lt;p&gt;La transición ya está en marcha, aunque no todas las empresas avanzan al&lt;br&gt;
mismo ritmo. Algunas están experimentando con herramientas de IA; otras&lt;br&gt;
están automatizando tareas; y muchas todavía necesitan ayuda para&lt;br&gt;
integrar esas capacidades de manera segura y útil en sus operaciones.&lt;/p&gt;

&lt;p&gt;Es razonable esperar una evolución gradual: primero más experimentación,&lt;br&gt;
después mayor presión por demostrar retorno de inversión y,&lt;br&gt;
progresivamente, más demanda de integración, acompañamiento y&lt;br&gt;
especialización.&lt;/p&gt;

&lt;p&gt;No se trata de afirmar que desaparecerán los proveedores de software.&lt;br&gt;
Las empresas seguirán necesitando productos, plataformas y&lt;br&gt;
especialistas. La hipótesis es que una parte creciente del valor estará&lt;br&gt;
en conectar esas herramientas con objetivos empresariales concretos.&lt;/p&gt;

&lt;h2&gt;
  
  
  La oportunidad
&lt;/h2&gt;

&lt;p&gt;La IA puede permitir que profesionales y equipos pequeños ejecuten&lt;br&gt;
proyectos que antes exigían más recursos. Pero la oportunidad no&lt;br&gt;
consiste solamente en producir más software en menos tiempo.&lt;/p&gt;

&lt;p&gt;Consiste en utilizar esa capacidad para comprender mejor los negocios,&lt;br&gt;
detectar oportunidades y convertirlas en mejoras reales.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;La ventaja competitiva no estará necesariamente en quién puede&lt;br&gt;
desarrollar más software, sino en quién puede identificar mejores&lt;br&gt;
oportunidades de negocio y convertirlas en resultados utilizando&lt;br&gt;
tecnología.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>softwaredevelopment</category>
      <category>softwareengineering</category>
      <category>startup</category>
    </item>
    <item>
      <title>[Spanish] Entrenando un kantoku artificial — Capítulo 3: Nana-Ban, el juego donde vive el kantoku</title>
      <dc:creator>Christian Gonzales Komiya</dc:creator>
      <pubDate>Thu, 29 Oct 2026 17:00:17 +0000</pubDate>
      <link>https://dev.to/ckomiya/spanish-entrenando-un-kantoku-artificial-capitulo-3-nana-ban-el-juego-donde-vive-el-kantoku-7cd</link>
      <guid>https://dev.to/ckomiya/spanish-entrenando-un-kantoku-artificial-capitulo-3-nana-ban-el-juego-donde-vive-el-kantoku-7cd</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Esta serie tiene tres capítulos:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://dev.to/ckomiya/spanish-entrenando-un-kantoku-artificial-capitulo-1-por-que-no-use-una-red-neuronal-1p2p"&gt;Por qué no usé una red neuronal&lt;/a&gt;: qué es el gateball, qué es un kantoku y cómo pensé el kantoku artificial.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/ckomiya/spanish-entrenando-un-kantoku-artificial-capitulo-2-saihai-el-agente-que-aprendio-y-5e6c"&gt;Saihai, el agente&lt;/a&gt;: cómo se entrenó, qué aprendió, qué desaprendió y cómo terminó siendo un proyecto propio.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://dev.to/ckomiya/spanish-entrenando-un-kantoku-artificial-capitulo-3-nana-ban-el-juego-donde-vive-el-kantoku-7cd"&gt;Nana-Ban, el juego&lt;/a&gt;&lt;/strong&gt;: el videojuego donde vive el kantoku, la demo y cómo lo construí con Claude Code.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6vk4k423wgd7sdunmfo1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6vk4k423wgd7sdunmfo1.gif" alt="Nana-Ban: touch, spark y golpe continuo" width="640" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Todo este proyecto empezó por aquí: por las ganas de tener un videojuego de gateball. El kantoku artificial nació después, cuando me di cuenta de que la CPU acertaba pero no pensaba. En este último capítulo cuento cómo es el juego, cómo está hecho y dónde se puede jugar.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Qué es Nana-Ban?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Nana-Ban&lt;/strong&gt; (七番, «el número siete») es un juego de gateball en 2D, con estética de 16 bits, en español, que se juega en el navegador. Está inspirado en los videojuegos de gateball de los años 80, pero todo lo que contiene es propio: el código, los gráficos, los sonidos y los textos.&lt;/p&gt;

&lt;p&gt;El logo es una bola roja con el número 7. En el gateball, la bola 7 es roja, porque las rojas son las impares.&lt;/p&gt;

&lt;p&gt;Hoy el juego tiene:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Partidos completos&lt;/strong&gt; de 10 bolas, cinco contra cinco, con las reglas del gateball: las tres puertas, el poste, el touch, el spark, el golpe continuo, las faltas y el reloj de 30 minutos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cinco niveles de CPU&lt;/strong&gt;: cuatro con una CPU sencilla (del «Torneo del barrio» al «Mundial») y el nivel &lt;strong&gt;Kantoku IA&lt;/strong&gt;, donde juega Saihai.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sonido sintetizado&lt;/strong&gt; en el navegador, sin archivos de audio.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Una demo&lt;/strong&gt; que empieza directo en el partido, también en el celular.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9yqq1fj2ocwhm0md0bb9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9yqq1fj2ocwhm0md0bb9.png" alt="Pantalla de título de Nana-Ban" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué Phaser
&lt;/h2&gt;

&lt;p&gt;El juego está hecho con &lt;strong&gt;Phaser 3&lt;/strong&gt;, un motor de juegos 2D para el navegador, con &lt;strong&gt;TypeScript&lt;/strong&gt; y &lt;strong&gt;Vite&lt;/strong&gt;. La razón principal fue práctica: &lt;strong&gt;en Phaser todo es código en texto&lt;/strong&gt;. No hay un editor visual donde arrastrar objetos, como en Unity o Godot. Eso es justo lo que necesita Claude Code, que trabaja leyendo y escribiendo archivos.&lt;/p&gt;

&lt;p&gt;TypeScript atrapa errores antes de que lleguen al juego, y yo lo leo con comodidad por mi experiencia en desarrollo web. Vite hace que cada cambio se vea al instante en el navegador. Y al final, todo el juego se empaqueta en &lt;strong&gt;un solo archivo HTML&lt;/strong&gt;, que se publica en cualquier lado.&lt;/p&gt;

&lt;h3&gt;
  
  
  La física es propia
&lt;/h3&gt;

&lt;p&gt;Lo que no usé de Phaser fue su motor de física. Escribí una física propia y sencilla, porque el gateball necesita algo muy específico: &lt;strong&gt;el pasto&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;La primera versión se sentía como billar: la bola rodaba perfecta, como sobre una mesa. Esa fue mi primera crítica como jugador, y de ahí salió un pasto con tres efectos: un freno fijo, un freno que crece con la velocidad y un pequeño desvío aleatorio. Un tiro perfectamente recto se desvía unos 5 cm a 4 metros y unos 23 cm a 14 metros. Así se siente el pasto de verdad: de cerca casi no importa; de lejos, sí.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dos barras para cada golpe
&lt;/h3&gt;

&lt;p&gt;Cada golpe se decide en tres pasos: apuntas, fijas la &lt;strong&gt;fuerza&lt;/strong&gt; con una barra que se mueve y fijas la &lt;strong&gt;precisión&lt;/strong&gt; con otra. Cada barra tiene una marca: si la detienes ahí, el tiro sale como lo pensaste. Cuanto más lejos el tiro, más rápido se mueve la barra de precisión. Es simple de entender y difícil de dominar, como el gateball.&lt;/p&gt;

&lt;h2&gt;
  
  
  El juego y el agente
&lt;/h2&gt;

&lt;p&gt;La decisión técnica más importante del proyecto fue &lt;strong&gt;separar las reglas del dibujo&lt;/strong&gt;. Al principio, las reglas del gateball vivían dentro de la escena de Phaser, mezcladas con los gráficos. Las saqué a un motor aparte, sin Phaser, que solo sabe de bolas, puertas, turnos y puntos.&lt;/p&gt;

&lt;p&gt;Esa separación lo hizo posible todo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Entrenar al kantoku sin pantalla.&lt;/strong&gt; Un partido de CPU contra CPU tarda menos de un segundo en simularse. Así se jugaron los miles de partidos de cada entrenamiento.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Que Saihai piense con las reglas reales.&lt;/strong&gt; Cuando simula un tiro, usa el mismo motor que el juego, no una aproximación.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separar a Saihai en su propio proyecto.&lt;/strong&gt; Hoy el motor y el agente viven juntos en el paquete &lt;code&gt;saihai-gateball&lt;/code&gt;, y el juego lo usa como cualquier otra librería.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Así quedó el reparto: &lt;strong&gt;Saihai decide y el motor arbitra; el juego dibuja, anima y suena.&lt;/strong&gt; La escena principal del juego pasó de más de mil líneas a menos de seiscientas.&lt;/p&gt;

&lt;h2&gt;
  
  
  La demo
&lt;/h2&gt;

&lt;p&gt;Para compartir el juego con amigos, hice una &lt;strong&gt;demo&lt;/strong&gt;: sin menús, empieza directo en el partido, tú con las rojas contra Kantoku IA con las blancas. La tecla R reinicia el partido, y los controles están dibujados en pantalla.&lt;/p&gt;

&lt;p&gt;Después la adapté al &lt;strong&gt;celular&lt;/strong&gt;. Se juega en horizontal (si lo pones vertical, te pide girarlo), tocando la cancha para ubicar la bola o mover la mira, con zoom de dos dedos y botones A y B en los costados, como en una consola portátil.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fohi4tn1k8svw8c97vhwv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fohi4tn1k8svw8c97vhwv.png" alt="La demo en el celular, en horizontal" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Un detalle que aprendí: en el iPhone, el navegador no permite pantalla completa, así que el botón explica cómo agregar el juego a la pantalla de inicio. La versión táctil la probé en mi Android. Si la juegas en un iPhone, me encantaría saber cómo te va.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dónde se puede jugar
&lt;/h2&gt;

&lt;p&gt;La demo está publicada en tres lugares:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;itch.io&lt;/strong&gt;, la casa natural de los juegos indie: &lt;a href="https://ckomiya.itch.io/nana-ban" rel="noopener noreferrer"&gt;https://ckomiya.itch.io/nana-ban&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hugging Face&lt;/strong&gt;, junto al agente: &lt;a href="https://huggingface.co/spaces/ckomiya/saihai-gateball-demo" rel="noopener noreferrer"&gt;https://huggingface.co/spaces/ckomiya/saihai-gateball-demo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Pages&lt;/strong&gt;: &lt;a href="https://ckomiya.github.io/gateball-retro/demo/" rel="noopener noreferrer"&gt;https://ckomiya.github.io/gateball-retro/demo/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La publicación es automática: cada vez que subo un cambio al repositorio, GitHub Actions construye el juego y la demo y los publica.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cómo lo construí con Claude Code
&lt;/h2&gt;

&lt;p&gt;Todo el código de Nana-Ban y de Saihai lo escribí con &lt;strong&gt;Claude Code&lt;/strong&gt;. Pero «escribir con IA» no significa pedirle un juego y esperar. La forma de trabajo fue la parte que más aprendí.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Iteraciones de una sola cosa.&lt;/strong&gt; Cada iteración tenía un solo objetivo y terminaba en algo jugable. El reparto era claro: &lt;strong&gt;yo juzgaba la sensación de juego; Claude Code verificaba que funcionara&lt;/strong&gt; (que compilara, sin errores, con la mecánica nueva probada). Las fases 1 y 2 del juego, desde el primer tiro hasta las 10 bolas con touch y spark, salieron en unas pocas horas del primer día.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mi criterio, traducido a código.&lt;/strong&gt; Mis comentarios eran de jugador, no de programador: «se siente como billar», «me resultó muy fácil ganar», «juega bien, pero una vez se ofreció». Claude Code los convertía en cambios concretos: un pasto más rugoso, un rival más certero, un script que reproducía el turno exacto donde la CPU se ofreció.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memoria entre sesiones.&lt;/strong&gt; Claude Code no recuerda las conversaciones anteriores, así que el proyecto tiene sus propios documentos: un &lt;code&gt;CLAUDE.md&lt;/code&gt; con las reglas del proyecto, un &lt;code&gt;BACKLOG.md&lt;/code&gt; con lo que viene y un &lt;code&gt;HISTORIAL.md&lt;/code&gt; con lo que se hizo. Cada sesión empieza leyendo el BACKLOG y confirmando el objetivo antes de escribir código.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Las pruebas como red de seguridad.&lt;/strong&gt; Hay decenas de escenarios de reglas que se ejecutan con el motor y también con la escena real del juego en el navegador, y deben dar exactamente lo mismo. Hay pruebas que juegan un partido completo con el teclado. Y hay una prueba legal que revisa que ningún texto nombre al juego que me inspiró ni copie nada de él.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trabajo largo, con punto de parada.&lt;/strong&gt; La separación de Saihai fue una tarea de varias horas. La planteé como una lista de pasos numerados, con un diario de decisiones y un punto donde detenerse: el último paso, el que publica, era mío. Claude Code hizo los pasos uno a uno, sin supervisión, se detuvo donde debía y me dejó un documento con todo lo que tenía que revisar. Hasta anotó un error propio, un comando que ejecutó por descuido y que corrigió en el acto.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Medir antes de afirmar.&lt;/strong&gt; Esta frase terminó siendo la regla del proyecto. Cada versión del kantoku se midió y se anotó con honestidad, incluso cuando el resultado era «corrige el caso, no mejora la fuerza».&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que viene, sin prisa
&lt;/h2&gt;

&lt;p&gt;Tengo muchas ideas para Nana-Ban, varias inspiradas en esos juegos de los 80:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Una vista de acción&lt;/strong&gt;, con una cámara en perspectiva que sigue a la bola.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Doce jugadores con atributos propios&lt;/strong&gt;: unos certeros de cerca, otros de lejos, como en un equipo real. Con eso, Saihai tendrá que aprender a elegir quién tira cada golpe.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modos de juego y un tutorial&lt;/strong&gt;, para que cualquiera aprenda gateball jugando.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Arte final y música chiptune.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pero no tengo prisa. Para mí, por ahora, lo importante era el kantoku: un rival con criterio. El juego completo irá creciendo de a poco, cuando tenga tiempo y ganas, y cada versión nueva de Saihai llegará también a la demo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gracias por leer
&lt;/h2&gt;

&lt;p&gt;Esta serie empezó con una pregunta de jugador: &lt;strong&gt;¿puede una máquina dirigir un partido de gateball como un kantoku?&lt;/strong&gt; Después de 11 días, la respuesta es: todavía no como yo, pero ya me ganó un partido, y cada error suyo me enseña algo sobre mi propia forma de dirigir.&lt;/p&gt;

&lt;p&gt;Si juegas la demo, me encantaría saber qué te pareció. Y si eres desarrollador y quieres hacer tu propio juego de gateball, Saihai es libre y está esperando un nuevo partido.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enlaces
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Juega la demo en itch.io:&lt;/strong&gt; &lt;a href="https://ckomiya.itch.io/nana-ban" rel="noopener noreferrer"&gt;https://ckomiya.itch.io/nana-ban&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Juega contra Saihai en Hugging Face:&lt;/strong&gt; &lt;a href="https://huggingface.co/spaces/ckomiya/saihai-gateball-demo" rel="noopener noreferrer"&gt;https://huggingface.co/spaces/ckomiya/saihai-gateball-demo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Demo en GitHub Pages:&lt;/strong&gt; &lt;a href="https://ckomiya.github.io/gateball-retro/demo/" rel="noopener noreferrer"&gt;https://ckomiya.github.io/gateball-retro/demo/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Saihai, el agente (código, MIT):&lt;/strong&gt; &lt;a href="https://github.com/ckomiya/saihai-gateball" rel="noopener noreferrer"&gt;https://github.com/ckomiya/saihai-gateball&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Saihai, el modelo:&lt;/strong&gt; &lt;a href="https://huggingface.co/ckomiya/saihai-gateball" rel="noopener noreferrer"&gt;https://huggingface.co/ckomiya/saihai-gateball&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>[Spanish] Entrenando un kantoku artificial — Capítulo 2: Saihai, el agente que aprendió (y desaprendió) a dirigir</title>
      <dc:creator>Christian Gonzales Komiya</dc:creator>
      <pubDate>Thu, 29 Oct 2026 16:56:55 +0000</pubDate>
      <link>https://dev.to/ckomiya/spanish-entrenando-un-kantoku-artificial-capitulo-2-saihai-el-agente-que-aprendio-y-5e6c</link>
      <guid>https://dev.to/ckomiya/spanish-entrenando-un-kantoku-artificial-capitulo-2-saihai-el-agente-que-aprendio-y-5e6c</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Esta serie tiene tres capítulos:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://dev.to/ckomiya/spanish-entrenando-un-kantoku-artificial-capitulo-1-por-que-no-use-una-red-neuronal-1p2p"&gt;Por qué no usé una red neuronal&lt;/a&gt;:&lt;/strong&gt; qué es el gateball, qué es un kantoku y cómo pensé el kantoku artificial.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://dev.to/ckomiya/spanish-entrenando-un-kantoku-artificial-capitulo-2-saihai-el-agente-que-aprendio-y-5e6c"&gt;Saihai, el agente&lt;/a&gt;&lt;/strong&gt; : cómo se entrenó, qué aprendió, qué desaprendió y cómo terminó siendo un proyecto propio.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://dev.to/ckomiya/spanish-entrenando-un-kantoku-artificial-capitulo-3-nana-ban-el-juego-donde-vive-el-kantoku-7cd"&gt;Nana-Ban, el juego:&lt;/a&gt;&lt;/strong&gt; el videojuego donde vive el kantoku, la demo y cómo lo construí con Claude Code.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;En el capítulo 1 conté por qué mi kantoku artificial no es una red neuronal, y cómo el primer campeón salió &lt;strong&gt;hiperagresivo&lt;/strong&gt;: atacaba sin parar porque había entrenado contra rivales que nunca atacaban. Ese fue el primer entrenamiento. Este capítulo cuenta todo lo que vino después.&lt;/p&gt;

&lt;h2&gt;
  
  
  Primero, ¿qué es Saihai?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Saihai&lt;/strong&gt; (采配) es el nombre del kantoku artificial. En japonés, &lt;em&gt;saihai&lt;/em&gt; es la forma de dirigir de un kantoku: sus decisiones tácticas durante el partido.&lt;/p&gt;

&lt;p&gt;Técnicamente, Saihai es un &lt;strong&gt;agente de IA&lt;/strong&gt;. Cumple las tres cosas que definen a un agente:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Percibe:&lt;/strong&gt; lee el estado del partido (dónde está cada bola, a quién le toca, el marcador y el reloj).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decide:&lt;/strong&gt; genera decenas de golpes posibles, simula cada uno con la física real del juego (incluido el error del jugador que tira) y evalúa cómo queda la cancha.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actúa:&lt;/strong&gt; entrega el tiro (dirección, fuerza y precisión) y el motor lo ejecuta.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Dentro lleva un &lt;strong&gt;modelo&lt;/strong&gt;: los pesos aprendidos por autojuego. Es como un kantoku y su experiencia: el kantoku es el agente; su experiencia es el modelo.&lt;/p&gt;

&lt;p&gt;Y una aclaración que hago siempre: &lt;strong&gt;no es un modelo de lenguaje ni una red neuronal&lt;/strong&gt;. Es IA clásica de juegos, de la misma familia que los motores de ajedrez o las IA de billar: búsqueda, simulación y una función de evaluación con parámetros aprendidos, más reglas de experto.&lt;/p&gt;

&lt;h2&gt;
  
  
  El segundo entrenamiento: menos exposición
&lt;/h2&gt;

&lt;p&gt;Después del campeón hiperagresivo, el objetivo del segundo entrenamiento (el &lt;strong&gt;v2&lt;/strong&gt;) fue que el kantoku dejara de «ofrecerse». Lo había visto en varias partidas: al reentrar a la cancha o al salir de la puerta 1, dejaba su bola justo donde yo podía tocarla.&lt;/p&gt;

&lt;p&gt;Cambiamos el entrenamiento por completo. En vez de jugar solo contra los pesos escritos a mano, cada candidato jugaba una &lt;strong&gt;liga&lt;/strong&gt;: contra los pesos a mano, contra el campeón anterior, contra un «salón de la fama» de campeones pasados y contra los otros candidatos. Y le pusimos un &lt;strong&gt;piso al riesgo&lt;/strong&gt;: por más que el optimizador quisiera, exponerse nunca podía valer cero.&lt;/p&gt;

&lt;p&gt;Fueron 30 generaciones en 94 minutos. El v2 le ganó el &lt;strong&gt;60 %&lt;/strong&gt; de los partidos al v1, y las veces que su bola era tocada o sacada al reentrar bajaron del 83 % al 44 %.&lt;/p&gt;

&lt;p&gt;Un detalle que me gustó: el peso del riesgo terminó &lt;strong&gt;exactamente en el piso&lt;/strong&gt; que habíamos puesto. Es decir, la máquina no lo descubrió sola: la restricción la pusimos nosotros. Fue la primera señal de algo que se repetiría muchas veces: &lt;strong&gt;el criterio humano y el aprendizaje se complementan&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Una bola que se ofreció
&lt;/h3&gt;

&lt;p&gt;Jugando contra el v2 vi una bola de la CPU reentrar y quedar justo al lado de la mía. ¿Por qué, si ahora valoraba el riesgo? Claude Code reprodujo el turno con un script y encontró la causa: &lt;strong&gt;el cálculo del riesgo solo miraba a 3 de las 5 rivales&lt;/strong&gt;. Las que jugaban más tarde no existían para él.&lt;/p&gt;

&lt;p&gt;Se corrigió y se continuó el entrenamiento desde donde había quedado, 20 generaciones más. Esa versión le ganó el &lt;strong&gt;69 %&lt;/strong&gt; al v2.&lt;/p&gt;

&lt;h2&gt;
  
  
  El efecto techo: cuando ya no se ve la mejora
&lt;/h2&gt;

&lt;p&gt;Después agregué reglas que conozco como jugador: aprovechar la línea de la cancha, cuándo juntarse con una compañera, cómo recoger bolas. Las medí y el resultado fue desconcertante: &lt;strong&gt;51 %&lt;/strong&gt;, un empate técnico contra el campeón sin esas reglas.&lt;/p&gt;

&lt;p&gt;¿No servían? Sí servían, pero no se notaba. El campeón ya les ganaba casi el 98 % de los partidos a los pesos escritos a mano, y al primer rival «tipo humano» que probamos, el 99 %. Cuando ganas casi todo, no queda margen para mostrar que mejoraste. Es el &lt;strong&gt;efecto techo&lt;/strong&gt;: como medir la altura de alguien en una habitación de techo bajo.&lt;/p&gt;

&lt;p&gt;La solución fue construir un &lt;strong&gt;rival tipo humano&lt;/strong&gt; cada vez más exigente: una CPU con el error de un buen jugador de gateball (mi referencia: la mitad de los touches a 6 m, uno de cada cinco a 13 m) y que se pasa de fuerza como lo hace una persona. Desde entonces, cada versión se mide también contra ese rival.&lt;/p&gt;

&lt;h2&gt;
  
  
  El tercer entrenamiento: la máquina tenía razón
&lt;/h2&gt;

&lt;p&gt;Con el &lt;strong&gt;v3&lt;/strong&gt; pasó algo curioso. El criterio que yo había puesto para adoptarlo decía que el kantoku tenía que pasar la puerta 1 en su primer intento casi siempre. El v3 no lo cumplía: a veces &lt;strong&gt;se quedaba esperando&lt;/strong&gt; en la salida.&lt;/p&gt;

&lt;p&gt;Iba a descartarlo, hasta que me di cuenta de que esperar en la salida &lt;strong&gt;es una táctica real del gateball&lt;/strong&gt;: si la cancha está vacía, a veces conviene no entrar todavía. La máquina había descubierto sola una táctica que yo conozco como jugador, y mi criterio de evaluación estaba mal planteado.&lt;/p&gt;

&lt;p&gt;Cambiamos el criterio a «espera con sentido» y el v3 pasó a ser el campeón.&lt;/p&gt;

&lt;p&gt;La lección: &lt;strong&gt;antes de culpar al modelo, revisa la pregunta que le estás haciendo&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  El entrenamiento que aprendió a no atacar
&lt;/h2&gt;

&lt;p&gt;El cuarto entrenamiento (le llamamos &lt;strong&gt;F&lt;/strong&gt;) fue el más ambicioso hasta ese momento: unas cinco horas, nueve parámetros nuevos de reglas que yo había agregado jugando, y el rival tipo humano dentro de la evaluación.&lt;/p&gt;

&lt;p&gt;El resultado parecía excelente: el candidato le ganaba el &lt;strong&gt;67 %&lt;/strong&gt; al campeón. Pero una prueba de comportamiento encendió la alarma. En una situación muy clara del gateball (pasas la puerta, ganas un golpe extra y tienes una rival cerca), un kantoku ataca. El candidato &lt;strong&gt;no atacaba nunca: 0 de 60 veces&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Había aprendido que podía ganar igual sin atacar en esa situación, así que simplemente dejó de hacerlo. Como estrategia contra las CPU funcionaba; como kantoku, era absurdo.&lt;/p&gt;

&lt;p&gt;Lo descartamos y de ahí salió una regla de diseño que sigo usando: &lt;strong&gt;lo que un kantoku nunca debe dejar de hacer se programa como regla fija, no como algo que se aprende&lt;/strong&gt;. El optimizador aprende cuánto vale cada cosa; las convicciones del kantoku no se negocian.&lt;/p&gt;

&lt;h2&gt;
  
  
  El v4: aprender con examen incluido
&lt;/h2&gt;

&lt;p&gt;Para el siguiente entrenamiento (el &lt;strong&gt;v4&lt;/strong&gt;) cambiamos la forma de evaluar. Además de ganar partidos, cada candidato tenía que rendir una &lt;strong&gt;batería de comportamiento&lt;/strong&gt;: tableros reales de mis partidas, donde se sabe qué haría un kantoku. Así, ganar haciendo absurdos ya no daba puntos.&lt;/p&gt;

&lt;p&gt;Fueron 24 generaciones y unas cinco horas. El v4 aprendió algo interesante: &lt;strong&gt;cuando va perdiendo, los puntos pesan más&lt;/strong&gt;. Pero no aprendió que el reloj importa: ese parámetro quedó en cero.&lt;/p&gt;

&lt;p&gt;Según el criterio de adopción que habíamos escrito antes de entrenar, el v4 no alcanzaba: tenía que ganarle al menos el 57 % al campeón y llegó al 55,5 %. Contra el rival tipo humano ganaba el 80,5 %, frente al 81,8 % del v3. Pero esas diferencias estaban &lt;strong&gt;dentro del ruido&lt;/strong&gt; de la medición. Lo adopté igual, porque era el primero entrenado con las pruebas de comportamiento dentro de la evaluación, y eso valía más que un punto que podía ser azar.&lt;/p&gt;

&lt;p&gt;Desde entonces, &lt;strong&gt;Saihai no se ha vuelto a entrenar&lt;/strong&gt;. Todo lo que mejoró después fueron reglas.&lt;/p&gt;

&lt;h2&gt;
  
  
  De la máquina al criterio: cinco versiones en un día
&lt;/h2&gt;

&lt;p&gt;El 27 de octubre jugué varias partidas contra el v4 y anoté turno por turno lo que un kantoku habría hecho distinto. Cada observación se convirtió en una regla, y ese día salieron &lt;strong&gt;cinco versiones&lt;/strong&gt;, de la v1.0 a la v1.4.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Versión&lt;/th&gt;
&lt;th&gt;Qué aprendió (en simple)&lt;/th&gt;
&lt;th&gt;De dónde salió&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;v1.0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Calcula cuántos turnos le quedan a cada bola según cómo está la cancha, y valora mejor el doble ángulo «bola y puerta»&lt;/td&gt;
&lt;td&gt;El parámetro del reloj que el v4 no aprendió&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;v1.1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reentradas más cuidadosas: prevé que la rival toque a su compañera y la mande junto a su bola&lt;/td&gt;
&lt;td&gt;Siete reentradas de una misma partida mía&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;v1.2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;«Casi siempre se reentra»: quedarse fuera bajó del 10,7 % al 0,4 %&lt;/td&gt;
&lt;td&gt;Mi criterio de kantoku&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;v1.3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;En el último turno busca la cadena de golpes que suma más puntos&lt;/td&gt;
&lt;td&gt;Un último turno donde solo pasó una puerta&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;v1.4&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;El spark ya no saca bolas propias; no arriesga quedar junto a una rival por avanzar si probablemente no vuelve a jugar&lt;/td&gt;
&lt;td&gt;Un spark que sacó a su propia compañera&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;v1.5&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Con el partido ya ganado, la última bola intenta el touch lejano para sumar&lt;/td&gt;
&lt;td&gt;Una última bola que no intentó nada&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Lo honesto: casi ninguna de estas versiones &lt;strong&gt;ganó más&lt;/strong&gt; que la anterior. Corregían el caso que yo había visto, sin empeorar nada. Para mí eso era suficiente, porque el objetivo no era un kantoku más fuerte, sino uno &lt;strong&gt;más parecido a un kantoku real&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Y hubo una idea que descarté a propósito: entrenar a Saihai para &lt;strong&gt;imitar&lt;/strong&gt; mis partidas. Cada kantoku tiene su forma de dirigir, y no quería una copia de la mía. Prefiero que descubra su propio estilo a partir de principios, y que yo le corrija los errores de criterio.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que Saihai todavía no entiende
&lt;/h2&gt;

&lt;p&gt;No todas mis observaciones se pudieron resolver, y creo que esa parte es la más interesante.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;El puente que no quiso hacer.&lt;/strong&gt; En un turno, yo habría puesto la bola 2 junto a la 4 para hacerle de puente (&lt;em&gt;tsunagi&lt;/em&gt;): facilitar que la 4 pasara su puerta, en un lugar donde la rival que juega antes no tuviera doble ángulo. Saihai la mandó a un sitio tapado. ¿Por qué? Porque veía tres rivales a 13 metros, cada una con un 20 % de acertar, y sumadas le parecían un peligro serio. Para mí, como kantoku, un touch a 13 metros es muy difícil y no condiciona la jugada.&lt;/p&gt;

&lt;p&gt;Probamos varias correcciones y ninguna lo resolvió sin trampas. La diferencia de fondo es &lt;strong&gt;cuánto pesa una amenaza lejana&lt;/strong&gt;, y eso no se debe ajustar a mano para que pase una prueba: se va a calibrar con datos de partidas reales en el próximo entrenamiento.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;El peor riesgo tapa a los demás.&lt;/strong&gt; En otra partida, una bola blanca quedó frente a la puerta 2 con un triple ángulo para una rival. Saihai calcula el riesgo de cada rival como &lt;strong&gt;su peor amenaza&lt;/strong&gt;, no como la suma de todas. Como esa rival tenía una amenaza aún mayor en otro lado, mover la bola «no cambiaba nada» para él. Arreglarlo exige reentrenar, porque los pesos aprendieron con esa forma de medir.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;El tiro que no existía.&lt;/strong&gt; Otra vez yo habría dejado la bola pegada a la línea para dominar la zona de la puerta 2. Saihai midió que tenía razón: ahí era más segura. Pero desde donde estaba, &lt;strong&gt;los postes de la puerta tapaban ese tiro&lt;/strong&gt;. No era un error de criterio, sino de opciones.&lt;/p&gt;

&lt;p&gt;Todo esto está documentado para la próxima versión grande, la &lt;strong&gt;v2.0&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Medir sin engañarse
&lt;/h2&gt;

&lt;p&gt;Si algo aprendí en este proyecto es a desconfiar de los números bonitos.&lt;/p&gt;

&lt;p&gt;Con 200 partidos, cada cifra tiene un margen de ±5 a 7 puntos. Una vez, un candidato parecía ganar el 63,5 %, y al medirlo bien bajó a 60 %: era el mejor de 11 mediciones con ruido. Otra vez, un 81,5 % no se pudo reproducir: dio 76,8 %.&lt;/p&gt;

&lt;p&gt;La lección más clara llegó con la última versión. Comparé la v1.5 contra la v1.4 y dio &lt;strong&gt;47,8 %&lt;/strong&gt;, por debajo del 48 % que había puesto como meta. Antes de concluir que era peor, medimos un &lt;strong&gt;control&lt;/strong&gt;: la v1.4 contra sí misma, con las mismas semillas. Dio &lt;strong&gt;exactamente 47,8 %&lt;/strong&gt;. La meta era imposible incluso para dos copias idénticas.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;El gráfico mezcla condiciones distintas (certeza, rivales y hasta el grosor del poste), así que no es una serie continua. Lo que muestra es otra cosa: cuánto se mueven los números por razones que no son «mejorar».&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  De parte del juego a proyecto propio
&lt;/h2&gt;

&lt;p&gt;Al principio, el kantoku vivía dentro del juego. Pero un kantoku artificial de gateball podía servirle a otros: a quien quiera hacer su propio juego de gateball, en otro motor o con otra estética. Así que decidí &lt;strong&gt;separarlo&lt;/strong&gt; en su propio repositorio, sin interfaz, como una librería.&lt;/p&gt;

&lt;p&gt;El kantoku «piensa» simulando tiros con las reglas y la física, así que no se podía ir solo: se llevó también &lt;strong&gt;el motor de gateball&lt;/strong&gt;. Hoy el juego usa ese mismo paquete para las reglas y para la IA. Una sola fuente de verdad.&lt;/p&gt;

&lt;p&gt;La condición que puse fue estricta: &lt;strong&gt;después de separarlo, tenía que jugar idéntico&lt;/strong&gt;. No «parecido»: idéntico. Se midió todo antes y después con las mismas semillas, y los resultados coincidieron partido por partido, hasta los archivos de referencia byte a byte.&lt;/p&gt;

&lt;p&gt;Esa comparación encontró algo que nadie había visto: algunas pruebas leían partidas guardadas solo en mi laptop. En una copia limpia del repositorio, esas pruebas veían la mitad de los datos y pasaban igual, sin avisar. Las 23 partidas quedaron versionadas como parte de las pruebas.&lt;/p&gt;

&lt;p&gt;Usar Saihai se ve así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createMatch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Saihai&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;playShot&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;saihai-gateball&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createMatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;            &lt;span class="c1"&gt;// partido con azar reproducible&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;saihai&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Saihai&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;opponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;human&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;   &lt;span class="c1"&gt;// juzga a su rival como a una persona&lt;/span&gt;

&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stage&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;done&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;saihai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chooseShot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;            &lt;span class="c1"&gt;// Saihai decide el tiro&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;playShot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shot&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                &lt;span class="c1"&gt;// el motor lo ejecuta&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                          &lt;span class="c1"&gt;// puertas, touches, sparks, faltas…&lt;/span&gt;
  &lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;proceed&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;                                  &lt;span class="c1"&gt;// siguiente golpe o turno&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;teamScore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;teamScore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sin Phaser, sin navegador: corre en Node o en cualquier juego web. La misma semilla reproduce exactamente el mismo partido, y Saihai puede explicar cada decisión, en español o en inglés.&lt;/p&gt;

&lt;p&gt;Para comprobar que de verdad servía a otros, hicimos una prueba de «desarrollador externo»: una carpeta vacía, instalar Saihai desde GitHub y jugar un partido usando solo lo que dice el README. Funcionó, y de esa prueba salieron once mejoras a la documentación.&lt;/p&gt;

&lt;h2&gt;
  
  
  Saihai en Hugging Face
&lt;/h2&gt;

&lt;p&gt;El último paso fue publicarlo donde la comunidad de IA busca modelos y agentes: &lt;strong&gt;Hugging Face&lt;/strong&gt;. Ahí están los pesos (31 valores aprendidos por autojuego y 14 fijos), una ficha que explica qué es y qué no es, y un &lt;strong&gt;Space&lt;/strong&gt; donde cualquiera puede jugar contra Saihai desde el navegador.&lt;/p&gt;

&lt;p&gt;Hugging Face no tiene una categoría de «agentes», así que Saihai aparece como modelo. Es lo habitual con los agentes de juegos: el repositorio de modelo guarda lo que el agente aprendió, y el Space lo muestra funcionando.&lt;/p&gt;

&lt;h2&gt;
  
  
  Las cifras de hoy
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Medida&lt;/th&gt;
&lt;th&gt;Saihai v1.5&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Contra la versión anterior (200 partidos)&lt;/td&gt;
&lt;td&gt;47,8 %, igual que el control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contra el rival tipo humano (200 partidos)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;84,5 %&lt;/strong&gt;, con 7 puntos de diferencia por partido&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pruebas de comportamiento (tableros de mis partidas)&lt;/td&gt;
&lt;td&gt;98 %&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tiempo para decidir un golpe&lt;/td&gt;
&lt;td&gt;unos 53 ms en mi laptop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Valores aprendidos&lt;/td&gt;
&lt;td&gt;31 (más 14 fijos)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Una advertencia importante: todas son cifras &lt;strong&gt;contra rivales simulados&lt;/strong&gt;. Contra personas reales, por ahora, solo están mis partidas. Y ya me ganó un partido, bien jugado.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que viene
&lt;/h2&gt;

&lt;p&gt;El próximo paso es un nuevo entrenamiento, la &lt;strong&gt;v2.0&lt;/strong&gt;, con una diferencia clave: &lt;strong&gt;calibrar el rival humano con mis partidas reales&lt;/strong&gt;. Mis partidas guardadas registran cada tiro, así que se puede medir cuánto acierto yo de verdad a 6, 9 o 13 metros, en vez de suponerlo. Si a 13 metros acierto menos de lo que Saihai cree, aprenderá solo que esas amenazas pesan poco. Que es justo lo que yo, como kantoku, le habría dicho.&lt;/p&gt;

&lt;p&gt;Mientras tanto, sigo jugando y anotando turnos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enlaces
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Saihai en GitHub&lt;/strong&gt; (MIT): &lt;a href="https://github.com/ckomiya/saihai-gateball" rel="noopener noreferrer"&gt;https://github.com/ckomiya/saihai-gateball&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Saihai en Hugging Face:&lt;/strong&gt; &lt;a href="https://huggingface.co/ckomiya/saihai-gateball" rel="noopener noreferrer"&gt;https://huggingface.co/ckomiya/saihai-gateball&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Juega contra Saihai&lt;/strong&gt; (Space): &lt;a href="https://huggingface.co/spaces/ckomiya/saihai-gateball-demo" rel="noopener noreferrer"&gt;https://huggingface.co/spaces/ckomiya/saihai-gateball-demo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Historial completo del kantoku:&lt;/strong&gt; &lt;a href="https://github.com/ckomiya/saihai-gateball/blob/main/HISTORIAL.md" rel="noopener noreferrer"&gt;https://github.com/ckomiya/saihai-gateball/blob/main/HISTORIAL.md&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Próximo capítulo
&lt;/h2&gt;

&lt;p&gt;En el &lt;strong&gt;capítulo 3&lt;/strong&gt; hablo del juego donde vive Saihai: &lt;strong&gt;Nana-Ban&lt;/strong&gt;. De qué consta, por qué lo hice con Phaser, cómo es la demo (también en el celular), dónde se puede jugar y cómo lo construí con Claude Code, iteración por iteración.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>machinelearning</category>
      <category>spanish</category>
    </item>
    <item>
      <title>[Spanish] Entrenando un kantoku artificial — Capítulo 1: por qué no usé una red neuronal</title>
      <dc:creator>Christian Gonzales Komiya</dc:creator>
      <pubDate>Thu, 22 Oct 2026 01:53:50 +0000</pubDate>
      <link>https://dev.to/ckomiya/spanish-entrenando-un-kantoku-artificial-capitulo-1-por-que-no-use-una-red-neuronal-1p2p</link>
      <guid>https://dev.to/ckomiya/spanish-entrenando-un-kantoku-artificial-capitulo-1-por-que-no-use-una-red-neuronal-1p2p</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Esta serie tiene tres capítulos:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://dev.to/ckomiya/spanish-entrenando-un-kantoku-artificial-capitulo-1-por-que-no-use-una-red-neuronal-1p2p"&gt;Por qué no usé una red neuronal&lt;/a&gt;&lt;/strong&gt; : qué es el gateball, qué es un kantoku y cómo pensé el kantoku artificial.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://dev.to/ckomiya/spanish-entrenando-un-kantoku-artificial-capitulo-2-saihai-el-agente-que-aprendio-y-5e6c"&gt;Saihai, el agente&lt;/a&gt;:&lt;/strong&gt; : cómo se entrenó, qué aprendió, qué desaprendió y cómo terminó siendo un proyecto propio.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://dev.to/ckomiya/spanish-entrenando-un-kantoku-artificial-capitulo-3-nana-ban-el-juego-donde-vive-el-kantoku-7cd"&gt;Nana-Ban, el juego&lt;/a&gt;:&lt;/strong&gt; : el videojuego donde vive el kantoku, la demo y cómo lo construí con Claude Code.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Primero, ¿qué es el gateball?
&lt;/h2&gt;

&lt;p&gt;El gateball es un deporte de equipo que nació en Japón en 1947, en Hokkaido. Se juega en una cancha de pasto de unos 20 × 15 metros, con tres puertas (arcos) y un poste en el centro. Hay dos equipos de cinco bolas, rojas y blancas, y se juega por turnos en orden fijo: bola 1 roja, bola 2 blanca, bola 3 roja, y así hasta la 10.&lt;/p&gt;

&lt;p&gt;Cada bola tiene que pasar las tres puertas en orden y luego tocar el poste. Cada puerta da un punto y el poste da dos. Pero lo interesante está en otra parte: si mi bola golpea a otra (un &lt;strong&gt;touch&lt;/strong&gt;), puedo colocar las dos juntas y, con un golpe a la mía, mandar a la otra lejos. Eso es el &lt;strong&gt;spark&lt;/strong&gt;. Con él puedo ayudar a una compañera a avanzar o sacar de la cancha a una bola rival. Un partido dura 30 minutos, y gana el equipo con más puntos.&lt;/p&gt;

&lt;p&gt;A simple vista parece un juego de puntería. En realidad es un juego de estrategia.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmthssj2yee75tdc1a8g9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmthssj2yee75tdc1a8g9.png" alt=" " width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Y qué es el kantoku?
&lt;/h2&gt;

&lt;p&gt;El &lt;strong&gt;kantoku&lt;/strong&gt; (監督) es quien dirige la estrategia del equipo. Decide qué tiene que hacer cada bola en cada turno: avanzar a su puerta, atacar a un rival, acomodarse para ayudar a una compañera o protegerse.&lt;/p&gt;

&lt;p&gt;Un buen kantoku no piensa solo en el golpe de ahora. Piensa en &lt;strong&gt;qué le deja al rival que juega después&lt;/strong&gt;, en &lt;strong&gt;quién de su equipo es certero de cerca y quién de lejos&lt;/strong&gt;, en &lt;strong&gt;qué zonas de la cancha conviene dominar&lt;/strong&gt; y en &lt;strong&gt;cuánto tiempo queda&lt;/strong&gt;, porque la forma de dirigir cambia mucho en los últimos minutos.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2zjvi9xzlcnriopxo3hy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2zjvi9xzlcnriopxo3hy.png" alt=" " width="800" height="1067"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  De dónde salió este proyecto
&lt;/h2&gt;

&lt;p&gt;Todo empezó con un videojuego. Estoy desarrollando &lt;strong&gt;Nana-Ban&lt;/strong&gt; (七番, «el número siete»), un juego de gateball en 2D con estética retro, inspirado en los videojuegos de gateball de los años 80. Lo construyo con Claude Code, en iteraciones cortas.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2yot8jvm981klryivejf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2yot8jvm981klryivejf.png" alt=" " width="800" height="627"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Llegó un momento en que el juego ya tenía de todo: las 10 bolas, los turnos, el touch, el spark, las faltas y el reloj. La CPU jugaba un partido completo. Pero al jugar contra ella me di cuenta de algo: &lt;strong&gt;hacía jugadas sin criterio&lt;/strong&gt;. Apuntaba a su puerta y poco más. A veces dejaba su bola justo donde yo podía tocarla y sacarla. Otras, volvía a entrar a la cancha poniéndose a mi merced. Acertaba, pero no pensaba.&lt;/p&gt;

&lt;p&gt;Ahí entendí que lo que le faltaba no era puntería: le faltaba un kantoku.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Es como un jugador de ajedrez?
&lt;/h2&gt;

&lt;p&gt;Mi primera intuición fue esa: un kantoku es como un ajedrecista, alguien que lee el tablero y anticipa al rival. Se parecen en eso, pero hay tres diferencias importantes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Las jugadas son continuas.&lt;/strong&gt; En ajedrez hay unas 30 jugadas posibles por turno. En gateball, el ángulo y la fuerza son infinitos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hay azar en la ejecución.&lt;/strong&gt; Puedes elegir el tiro perfecto y fallarlo. El pasto desvía la bola.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quién juega después importa muchísimo.&lt;/strong&gt; El orden de turnos es fijo, así que la pregunta clave es: ¿qué le estoy dejando a la bola rival que viene?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Por eso el modelo más parecido no es el ajedrez, sino la inteligencia artificial del billar o del curling: &lt;strong&gt;pensar varias opciones, simular cada una y evaluar cómo queda la cancha&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cómo piensa el kantoku artificial
&lt;/h2&gt;

&lt;p&gt;Hoy ese kantoku tiene nombre: &lt;strong&gt;Saihai&lt;/strong&gt; (采配), que en japonés es la forma de dirigir de un kantoku, sus decisiones tácticas durante el partido. En cada turno, Saihai hace tres cosas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Piensa opciones:&lt;/strong&gt; pasar su puerta, hacer touch a un rival, acercarse a una compañera, ir al poste o acomodarse.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Las prueba en su cabeza:&lt;/strong&gt; simula cada tiro con la misma física del juego, varias veces y con el error propio del jugador que tira, porque no es lo mismo un tirador certero que uno inseguro.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evalúa cómo queda la cancha:&lt;/strong&gt; le pone un puntaje a cada situación posible y elige la mejor.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Esa evaluación es el corazón del kantoku. Es una fórmula que suma cosas como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;los puntos ganados,&lt;/li&gt;
&lt;li&gt;qué tan bien quedan mis bolas para su siguiente puerta,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;cuál es la mejor opción que le dejo al rival que juega después&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;si controlo la zona frente a las puertas 2 y 3,&lt;/li&gt;
&lt;li&gt;si mis bolas quedan cerca entre sí para encadenar touch y spark.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cada término tiene un &lt;strong&gt;peso&lt;/strong&gt;: cuánto vale. ¿Vale más sacar una bola rival o asegurar un punto? ¿Cuánto cuesta dejar mi bola expuesta? Ahí está la estrategia.&lt;/p&gt;

&lt;h2&gt;
  
  
  Que la máquina aprenda los pesos
&lt;/h2&gt;

&lt;p&gt;Al principio, esos pesos los escribí a mano, según mi criterio. Funcionaba, pero eran estimaciones. Entonces me pregunté: ¿y si un &lt;strong&gt;kantoku artificial&lt;/strong&gt; los descubre solo?&lt;/p&gt;

&lt;p&gt;La idea es simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Se crean 24 kantokus con pesos un poco distintos.&lt;/li&gt;
&lt;li&gt;Juegan partidos entre ellos, miles, sin dibujar nada, solo simulando.&lt;/li&gt;
&lt;li&gt;El sistema se queda con lo que mejor funciona y genera una nueva generación cerca de los ganadores.&lt;/li&gt;
&lt;li&gt;Se repite durante decenas de generaciones.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Al final queda un &lt;strong&gt;campeón&lt;/strong&gt;: un archivo con unos 30 números. Ese es el modelo. Lo que se aprende no es la fórmula, sino &lt;strong&gt;cuánto vale cada cosa&lt;/strong&gt; dentro de ella. Es como una receta: el código dice «lleva harina, azúcar y huevos», y el entrenamiento descubre las cantidades.&lt;/p&gt;

&lt;p&gt;Técnicamente es optimización por autojuego con un algoritmo llamado CMA-ES. Corre en mi laptop: el primer entrenamiento tardó 80 minutos, y los más completos, unas cinco horas.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Por qué no una red neuronal?
&lt;/h2&gt;

&lt;p&gt;Es la pregunta obvia. Las redes neuronales son la base de la inteligencia artificial moderna, y AlphaZero aprendió a jugar ajedrez y Go mejor que cualquier humano. ¿Por qué no usar una?&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Pesos + fórmula (lo que elegí)&lt;/th&gt;
&lt;th&gt;Red neuronal&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Partidos para aprender&lt;/td&gt;
&lt;td&gt;Miles&lt;/td&gt;
&lt;td&gt;Cientos de miles o millones&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Equipo&lt;/td&gt;
&lt;td&gt;Mi laptop, en horas&lt;/td&gt;
&lt;td&gt;GPU y días o semanas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;¿Se puede leer?&lt;/td&gt;
&lt;td&gt;Sí: «touch al rival vale 4,6»&lt;/td&gt;
&lt;td&gt;No: millones de números sin significado claro&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Corregir un defecto&lt;/td&gt;
&lt;td&gt;Directo&lt;/td&gt;
&lt;td&gt;Reentrenar y esperar&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mi conocimiento de kantoku&lt;/td&gt;
&lt;td&gt;Entra como términos&lt;/td&gt;
&lt;td&gt;La red tiene que descubrirlo sola&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tamaño en el juego&lt;/td&gt;
&lt;td&gt;~30 números&lt;/td&gt;
&lt;td&gt;Megas de datos en el navegador&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;La razón más importante para mí fue la última fila. Cuando jugué contra la CPU y dije «hay que dominar la zona frente a la puerta 2 sin darle ángulo de tiro al rival», esa idea se convirtió en un término de la fórmula, y el kantoku empezó a valorarla en el siguiente entrenamiento. Con una red neuronal, esa intuición tendría que descubrirla sola, jugando millones de partidos, sin garantía de lograrlo.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Y si le ponemos una GPU?
&lt;/h3&gt;

&lt;p&gt;Pensé que con una GPU todo sería más rápido. No en este caso. Una GPU es excelente cuando el trabajo se puede partir en millones de operaciones simples e iguales, como pintar una cerca de mil tablas con mil personas a la vez. Así son las redes neuronales.&lt;/p&gt;

&lt;p&gt;Pero un partido de gateball es una &lt;strong&gt;carrera de postas&lt;/strong&gt;: el choque produce un touch, el touch habilita un spark, el spark define el siguiente golpe. Cada paso depende del anterior. Eso lo hace mejor un procesador normal (CPU), y lo que acelera el entrenamiento es &lt;strong&gt;tener más núcleos&lt;/strong&gt;: cada núcleo juega un partido distinto, como un campeonato con varias canchas.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Entonces la red neuronal es peor?
&lt;/h3&gt;

&lt;p&gt;No. En techo, una red neuronal puede llegar más lejos, porque no está limitada a los términos que yo definí y puede descubrir conceptos que a nadie se le ocurrieron. Pero en un videojuego el objetivo no es el kantoku más fuerte del mundo, sino &lt;strong&gt;un buen rival, con criterio&lt;/strong&gt;. Para eso, el enfoque elegido es más rápido, más barato, entendible y fácil de corregir.&lt;/p&gt;

&lt;p&gt;Si algún día hace falta más, el siguiente paso no sería una red desde cero, sino una red pequeña que aprenda a partir de los mismos términos que ya definimos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que aprendí en el primer entrenamiento
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuofjr8lpjpmddui5yc6v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuofjr8lpjpmddui5yc6v.png" alt=" " width="799" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;El primer campeón ganó el &lt;strong&gt;93 %&lt;/strong&gt; de sus partidos contra los pesos escritos a mano. Parecía un éxito. Pero cuando jugué contra él, le gané fácil.&lt;/p&gt;

&lt;p&gt;¿Qué pasó? El kantoku había aprendido a ser &lt;strong&gt;hiperagresivo&lt;/strong&gt;: hacía touches y sacaba bolas sin parar, y dejó en &lt;strong&gt;cero&lt;/strong&gt; el peso de la exposición. No le importaba dejar sus bolas a mi alcance. La razón: entrenó contra rivales pasivos, que casi nunca atacaban. Contra alguien que no ataca, exponerse no cuesta nada, y eso fue exactamente lo que aprendió.&lt;/p&gt;

&lt;p&gt;Es como un equipo que solo juega amistosos contra rivales que nunca tiran al arco: aprende que defender no sirve.&lt;/p&gt;

&lt;p&gt;La lección: &lt;strong&gt;una máquina aprende el mundo que le das&lt;/strong&gt;, no el mundo real. Para el segundo entrenamiento cambiamos cuatro cosas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rivales más duros&lt;/strong&gt;, incluido el propio campeón.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Un piso al riesgo&lt;/strong&gt;, porque en el gateball exponerse siempre cuesta algo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;El desvío real del pasto&lt;/strong&gt;: el kantoku creía acertar el 98 % de los tiros, y a 9 m se acierta cerca del 60 %.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mi referencia de jugador&lt;/strong&gt;: un buen jugador acierta un touch la mitad de las veces a unos 6 m, y a 13 m quizás una de cada cinco.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ese es el valor de este enfoque: cada observación que hago jugando se puede convertir en algo que el kantoku entiende.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7f1tutepzsfoj7idm2ia.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7f1tutepzsfoj7idm2ia.png" alt=" " width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Próximo capítulo
&lt;/h2&gt;

&lt;p&gt;Este fue el capítulo 1: por qué el kantoku artificial es como es. En el &lt;strong&gt;capítulo 2&lt;/strong&gt; cuento la historia de &lt;strong&gt;Saihai&lt;/strong&gt;: los entrenamientos que siguieron, la vez que aprendió a ganar sin atacar, las reglas de kantoku que salieron de mis propias partidas, lo que todavía no logra entender, y cómo terminó convertido en un proyecto propio, abierto y publicado en Hugging Face.&lt;/p&gt;

&lt;p&gt;En el &lt;strong&gt;capítulo 3&lt;/strong&gt; hablaré del juego, &lt;strong&gt;Nana-Ban&lt;/strong&gt;: de qué consta, por qué lo hice con Phaser, cómo usa a Saihai y dónde se puede jugar.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>[Spanish] Un marketplace en producción de costo cero: la arquitectura detrás</title>
      <dc:creator>Christian Gonzales Komiya</dc:creator>
      <pubDate>Sun, 18 Oct 2026 17:21:36 +0000</pubDate>
      <link>https://dev.to/ckomiya/un-marketplace-en-produccion-de-costo-cero-la-arquitectura-detras-5f6g</link>
      <guid>https://dev.to/ckomiya/un-marketplace-en-produccion-de-costo-cero-la-arquitectura-detras-5f6g</guid>
      <description>&lt;p&gt;&lt;em&gt;Cómo armé, yo solo y con Claude Code, un sitio de clasificados real usando herramientas gratuitas, y por qué elegí cada pieza.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Hace unos meses me planteé un reto: construir un marketplace de verdad, en producción, con usuarios reales, &lt;strong&gt;sin presupuesto&lt;/strong&gt;. Nada de «prototipo» ni «demo»: registro, login, publicar con fotos, catálogo con filtros, mensajes, moderación, SEO, textos legales y un panel de administración.&lt;/p&gt;

&lt;p&gt;El resultado es &lt;a href="https://rejugado.com" rel="noopener noreferrer"&gt;Rejugado&lt;/a&gt;, un clasificado de juegos de mesa usados para Perú. No hay pagos entre usuarios (compradores y vendedores se ponen de acuerdo por su cuenta), lo cual simplifica mucho el negocio y también la arquitectura. Y el único gasto fijo es el dominio.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftn380enzi5n2juse3x0w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftn380enzi5n2juse3x0w.png" alt=" " width="800" height="589"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En este post te cuento &lt;strong&gt;qué herramientas usé, por qué las elegí&lt;/strong&gt; (incluido el famoso «¿por qué Supabase y no Firebase?») y &lt;strong&gt;cómo Claude Code me ayudó&lt;/strong&gt; a llegar hasta aquí siendo una sola persona.&lt;/p&gt;

&lt;h2&gt;
  
  
  El mapa general
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 ┌─────────────────────────────────────────┐
  Navegador ───▶ │  Cloudflare Workers                     │
  (móvil/PC)     │  Astro (SSR) + islas React              │
                 │  Turnstile · DNS · Email Routing        │
                 └───────────────┬─────────────────────────┘
                                 │
                 ┌───────────────▼─────────────────────────┐
                 │  Supabase (región São Paulo)            │
                 │  Postgres + RLS · Auth · Storage        │
                 └───────────────┬─────────────────────────┘
                                 │
                       Correo (SMTP / Resend)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cuatro piezas, todas con plan gratuito. Vamos una por una.&lt;/p&gt;

&lt;h2&gt;
  
  
  Astro + islas de React: velocidad y SEO sin pagar de más
&lt;/h2&gt;

&lt;p&gt;Un clasificado vive de que Google lo encuentre y de que cargue rápido en un celular con datos limitados. Por eso las páginas públicas (catálogo, detalle de cada aviso, tienda del vendedor) se renderizan &lt;strong&gt;en el servidor&lt;/strong&gt; con &lt;a href="https://astro.build" rel="noopener noreferrer"&gt;Astro&lt;/a&gt;: el navegador recibe HTML listo, sin esperar a que se ejecute un montón de JavaScript.&lt;/p&gt;

&lt;p&gt;Las partes que sí necesitan interacción (formulario de publicar, bandeja de mensajes, el panel de cuenta) son &lt;strong&gt;islas de React&lt;/strong&gt;: componentes que se hidratan solo donde hacen falta. El resto de la página no paga ese costo.&lt;/p&gt;

&lt;p&gt;El resultado práctico: el catálogo saca &lt;strong&gt;100 en Lighthouse móvil&lt;/strong&gt; y el detalle ronda los 95. Eso no es vanidad: una página liviana consume menos ancho de banda y menos ejecución en el servidor, y cuando trabajas con límites gratuitos, cada recurso cuenta.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cloudflare Workers: el servidor que no administras
&lt;/h2&gt;

&lt;p&gt;La aplicación corre en &lt;strong&gt;Cloudflare Workers&lt;/strong&gt;. No hay máquina que mantener, ni parches de sistema operativo, ni escalado que configurar: el código se despliega en la red de Cloudflare y se ejecuta cerca del usuario.&lt;/p&gt;

&lt;p&gt;Lo que más valoro es que &lt;strong&gt;todo vive en el mismo panel&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Despliegue&lt;/strong&gt; del sitio.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DNS y dominio&lt;/strong&gt;, incluida la redirección de &lt;code&gt;www&lt;/code&gt; al dominio principal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email Routing&lt;/strong&gt;: la dirección de contacto del sitio reenvía a mi Gmail personal, sin pagar un servicio de correo corporativo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Turnstile&lt;/strong&gt;: un captcha moderno y sin fricción para proteger registro, login, recuperación de contraseña y formularios de contacto. Los usuarios casi nunca ven un desafío, y los bots sí se topan con la pared.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tener todo junto reduce la cantidad de cuentas, claves y paneles que debo vigilar. Para un proyecto de una persona, esa simplicidad vale oro.&lt;/p&gt;

&lt;h2&gt;
  
  
  Supabase: el corazón del sistema
&lt;/h2&gt;

&lt;p&gt;Aquí viene la decisión más importante. Supabase me da tres cosas en un solo paquete:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Postgres&lt;/strong&gt;: la base de datos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auth&lt;/strong&gt;: registro, login, recuperación de contraseña y sesiones por cookie.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage&lt;/strong&gt;: almacenamiento de las fotos de los juegos.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ¿Por qué Supabase y no Firebase?
&lt;/h3&gt;

&lt;p&gt;Firebase es excelente y lo he visto funcionar muy bien en muchos proyectos. Pero para &lt;strong&gt;este&lt;/strong&gt; proyecto, Supabase encajaba mejor por varias razones concretas:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Mis datos son relacionales.&lt;/strong&gt;&lt;br&gt;
Un catálogo con filtros (precio, condición, departamento, idioma, tipo de juego), vendedores con sus avisos, listas de «lo que busco» para intercambios, reportes y moderación. Todo eso son tablas que se relacionan entre sí. En SQL, un filtro combinado o una vista es una consulta natural. En una base documental como Firestore habría tenido que &lt;strong&gt;duplicar y desnormalizar datos&lt;/strong&gt;, y mantener esas copias sincronizadas se vuelve una fuente constante de errores.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. La seguridad vive en la base de datos (RLS).&lt;/strong&gt;&lt;br&gt;
Con Postgres uso &lt;em&gt;Row Level Security&lt;/em&gt;: reglas que dicen, tabla por tabla, quién puede leer, insertar o modificar cada fila. Aunque alguien encuentre mi clave pública de la API, &lt;strong&gt;la base se defiende sola&lt;/strong&gt;. Y como esas reglas son SQL, pude escribir pruebas automáticas para ellas: hoy tengo &lt;strong&gt;343 casos de prueba con pgTAP&lt;/strong&gt; que verifican que cada política hace lo que debe (por ejemplo, que un usuario no pueda editar el aviso de otro o leer campos privados).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Migraciones versionadas en Git.&lt;/strong&gt;&lt;br&gt;
Cada cambio al esquema es un archivo SQL en el repositorio. Mi regla es estricta: &lt;strong&gt;nada de cambios manuales en producción&lt;/strong&gt;. Si algo se rompe, sé exactamente qué cambió y cuándo; y puedo recrear la base desde cero.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Sin encierro tecnológico (lock-in).&lt;/strong&gt;&lt;br&gt;
Supabase es Postgres estándar y de código abierto. Si mañana quisiera irme, puedo llevarme mi base a cualquier otro proveedor. Es más: en desarrollo, mi entorno de Supabase &lt;strong&gt;corre en una Raspberry Pi 5&lt;/strong&gt; en mi casa. Costo: cero, y con los mismos servicios que en producción.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Costos más predecibles.&lt;/strong&gt;&lt;br&gt;
Firebase cobra por operaciones (lecturas, escrituras, borrados). Con un catálogo que se consulta mucho, una mala consulta o un bot puede dispararte la cuenta. En Supabase el modelo de precios se parece más a «recursos que usas» (espacio, ancho de banda, usuarios activos), lo que me resulta más fácil de prever.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Latencia para mi público.&lt;/strong&gt;&lt;br&gt;
Creé el proyecto en la región de &lt;strong&gt;São Paulo&lt;/strong&gt;, la más cercana a Perú entre las disponibles, con lo que las consultas responden rápido.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dónde Firebase gana (seamos honestos)
&lt;/h3&gt;

&lt;p&gt;No quiero venderte humo. Firebase es mejor si:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Haces una &lt;strong&gt;app móvil nativa&lt;/strong&gt; y quieres SDKs muy pulidos.&lt;/li&gt;
&lt;li&gt;Necesitas &lt;strong&gt;modo offline&lt;/strong&gt; y sincronización en tiempo real como pilar del producto.&lt;/li&gt;
&lt;li&gt;Ya vives en el ecosistema de Google (Analytics, Crashlytics, Cloud Messaging…).&lt;/li&gt;
&lt;li&gt;Tus datos son simples, tipo documento, y casi no tienes relaciones.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Y sobre Supabase, ojo con las &lt;strong&gt;limitaciones del plan gratuito&lt;/strong&gt;: los proyectos inactivos pueden pausarse tras un tiempo y hay topes de almacenamiento y de ancho de banda. &lt;em&gt;(Revisa siempre las cifras vigentes en la página de precios; cambian con el tiempo.)&lt;/em&gt; Para un proyecto con tráfico que empieza, es más que suficiente; si crece, tocará pasar a un plan de pago, y eso es una buena señal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Detalles pequeños que ahorran dinero
&lt;/h2&gt;

&lt;p&gt;El costo cero no es solo elegir servicios gratuitos: también es &lt;strong&gt;diseñar para gastar poco&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fotos en WebP, comprimidas en el navegador.&lt;/strong&gt; Cada aviso admite hasta 3 fotos. Antes de subirlas, el navegador del usuario las convierte a WebP (~150 KB) y genera una miniatura (~20 KB) para el catálogo. El servidor nunca procesa imágenes pesadas, y el almacenamiento rinde muchísimo más.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Correo diferido.&lt;/strong&gt; Los correos de autenticación salen por SMTP de Gmail durante la beta. Los avisos de «tienes un mensaje nuevo» están apagados hasta que haya volumen que justifique un servicio como Resend. Decidí &lt;strong&gt;no pagar por algo que todavía no necesito&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Límites de uso.&lt;/strong&gt; Mensajes y reportes tienen un tope por hora (por correo, cuenta e IP) y pasan por Turnstile. Menos spam significa menos consumo y menos moderación.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ¿Cuánto cuesta realmente?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pieza&lt;/th&gt;
&lt;th&gt;Para qué&lt;/th&gt;
&lt;th&gt;Costo en esta etapa&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cloudflare Workers&lt;/td&gt;
&lt;td&gt;Hospedaje de la aplicación&lt;/td&gt;
&lt;td&gt;Plan gratuito&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloudflare DNS + Email Routing&lt;/td&gt;
&lt;td&gt;Dominio y correo de contacto&lt;/td&gt;
&lt;td&gt;Gratis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloudflare Turnstile&lt;/td&gt;
&lt;td&gt;Antibots / captcha&lt;/td&gt;
&lt;td&gt;Gratis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Supabase&lt;/td&gt;
&lt;td&gt;Base de datos, Auth, Storage&lt;/td&gt;
&lt;td&gt;Plan gratuito&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gmail SMTP&lt;/td&gt;
&lt;td&gt;Correos de autenticación (beta)&lt;/td&gt;
&lt;td&gt;Gratis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Raspberry Pi 5&lt;/td&gt;
&lt;td&gt;Entorno de desarrollo de Supabase&lt;/td&gt;
&lt;td&gt;Ya la tenía&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dominio&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Nombre del sitio&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Único gasto fijo&lt;/strong&gt; (una vez al año)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;¿Qué me haría pagar primero? Probablemente el &lt;strong&gt;almacenamiento de fotos&lt;/strong&gt; o el ancho de banda de Supabase si el sitio crece, y un servicio de correo como Resend cuando quiera activar las notificaciones. Son costos que llegan &lt;em&gt;después&lt;/em&gt; de tener usuarios, no antes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dónde ayudó Claude Code
&lt;/h2&gt;

&lt;p&gt;Ahora, la parte que más preguntas me genera: &lt;strong&gt;¿cómo se construye todo esto entre una sola persona?&lt;/strong&gt; Una gran parte de la respuesta es &lt;a href="https://claude.com/claude-code" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt;, el asistente de programación que trabaja directamente en el repositorio y la terminal. Esto fue lo que más valor me dio:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Un &lt;code&gt;CLAUDE.md&lt;/code&gt; como memoria del proyecto.&lt;/strong&gt;&lt;br&gt;
En la raíz del repositorio mantengo un archivo con el estado del proyecto, el stack y las &lt;strong&gt;reglas que no se rompen&lt;/strong&gt;: RLS activado en todas las tablas, permisos explícitos por tabla, claves secretas solo en el servidor, CSP estricta, etc. Claude Code lo lee al empezar cada sesión, así que no tengo que repetir el contexto ni temer que «olvide» una regla de seguridad.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Migraciones y pruebas escritas juntas.&lt;/strong&gt;&lt;br&gt;
Cuando le pido una funcionalidad nueva (por ejemplo, el sistema de intercambio de juegos), no escribe solo la migración SQL: escribe también las &lt;strong&gt;políticas RLS y sus pruebas pgTAP&lt;/strong&gt;. Esa disciplina de «ninguna política sin su test» es difícil de sostener a mano cuando estás solo y cansado; con el asistente se vuelve parte del proceso.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pruebas de integración y revisión de seguridad.&lt;/strong&gt;&lt;br&gt;
Tengo unos 80 casos de prueba de la API (mensajes, reportes, moderación, SEO, intercambio). Claude Code los escribió, los ejecuta, y cuando algo falla ayuda a diagnosticar la causa raíz en lugar de parchar el síntoma. También lo usé para revisar cabeceras de seguridad, validaciones y casos límite.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trabajo «aburrido pero crítico».&lt;/strong&gt;&lt;br&gt;
Documentación técnica, manual de usuario, guía de pruebas manuales, borradores de textos legales, scripts de despliegue. Todo lo que en un proyecto personal suele quedar para «después» y nunca se hace, aquí quedó hecho.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Un despliegue más seguro.&lt;/strong&gt;&lt;br&gt;
Después de ver el riesgo de compilar con la configuración equivocada, creamos un script de despliegue que aparta el archivo de desarrollo, compila con la configuración de producción y &lt;strong&gt;revisa el resultado antes de publicar&lt;/strong&gt;. Un error humano clásico, convertido en un paso automático.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lo que Claude Code no hace por ti
&lt;/h3&gt;

&lt;p&gt;Para ser justo, también hay límites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Necesita tu criterio.&lt;/strong&gt; Las decisiones de producto, de negocio y, sobre todo, las &lt;strong&gt;legales&lt;/strong&gt; (términos, protección de datos, inscripción en la autoridad) son tuyas. El asistente ayuda a redactar y estructurar, pero tú respondes por el contenido.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hay que revisar lo que genera.&lt;/strong&gt; Es como trabajar con un colega muy rápido: puede equivocarse o malinterpretar. Las pruebas automáticas y la revisión de cambios (&lt;code&gt;git diff&lt;/code&gt;) son tu red de seguridad.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Funciona tan bien como el contexto que le das.&lt;/strong&gt; Un buen &lt;code&gt;CLAUDE.md&lt;/code&gt; marca la diferencia entre un asistente genérico y uno que conoce &lt;em&gt;tu&lt;/em&gt; proyecto.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lecciones que me llevo
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Elige herramientas por el problema, no por la moda.&lt;/strong&gt; Mis datos eran relacionales; por eso Postgres. Si los tuyos no lo fueran, quizá la respuesta sería otra.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pon la seguridad lo más cerca posible de los datos.&lt;/strong&gt; RLS con pruebas automáticas me da tranquilidad que ninguna validación de formulario puede darme.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Posterga lo que no necesitas hoy.&lt;/strong&gt; Cobros, notificaciones por correo y funciones «de fase 2» pueden esperar. Cada cosa pospuesta es dinero y tiempo ahorrados.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatiza los errores que ya te dolieron.&lt;/strong&gt; Un script de despliegue vale más que una nota que dice «acuérdate de…».&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Un asistente de IA multiplica, no reemplaza.&lt;/strong&gt; Te permite hacer el trabajo de un equipo pequeño, pero sigues siendo quien decide el rumbo.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Para cerrar
&lt;/h2&gt;

&lt;p&gt;Lanzar un proyecto web propio ya no requiere servidores caros, equipos grandes ni meses de trabajo. Con &lt;strong&gt;Astro, Cloudflare, Supabase&lt;/strong&gt; y un buen asistente como &lt;strong&gt;Claude Code&lt;/strong&gt;, una sola persona puede tener un producto serio en producción, con seguridad, pruebas y SEO, gastando prácticamente solo en el dominio.&lt;/p&gt;

&lt;p&gt;¿Y tú? ¿Qué stack usas para tus proyectos y por qué? Me encantaría leerte en los comentarios, sobre todo si elegiste Firebase y tienes argumentos que yo no consideré.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Nota: las condiciones de los planes gratuitos (límites, pausas, precios) cambian con frecuencia. Verifica siempre la información vigente en la página oficial de cada proveedor antes de basar tu proyecto en ella.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>startup</category>
      <category>webdev</category>
    </item>
    <item>
      <title>[Spanish] ¿Cómo deshacer una operación en microservicios? El patrón Saga explicado con Java</title>
      <dc:creator>Christian Gonzales Komiya</dc:creator>
      <pubDate>Mon, 12 Oct 2026 12:37:27 +0000</pubDate>
      <link>https://dev.to/ckomiya/spanish-como-deshacer-una-operacion-en-microservicios-el-patron-saga-explicado-con-java-274o</link>
      <guid>https://dev.to/ckomiya/spanish-como-deshacer-una-operacion-en-microservicios-el-patron-saga-explicado-con-java-274o</guid>
      <description>&lt;p&gt;Imagina que compras una laptop por internet. La tienda crea el pedido, reserva una unidad en el inventario e intenta cobrarte. Pero tu tarjeta está vencida.&lt;/p&gt;

&lt;p&gt;El cobro falla. ¿Qué pasa con el pedido que ya se creó? ¿Y con la laptop que quedó reservada?&lt;/p&gt;

&lt;p&gt;En una aplicación pequeña, quizá todas esas operaciones ocurran dentro de una misma transacción de base de datos. Si algo sale mal, un &lt;code&gt;ROLLBACK&lt;/code&gt; puede deshacer los cambios.&lt;/p&gt;

&lt;p&gt;En una arquitectura de microservicios, en cambio, cada servicio puede tener su propia base de datos y confirmar sus cambios de forma independiente. El &lt;code&gt;ROLLBACK&lt;/code&gt; de un servicio no puede deshacer automáticamente lo que otro ya guardó.&lt;/p&gt;

&lt;p&gt;Para resolver este tipo de situaciones existe un patrón conocido como &lt;strong&gt;Saga&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. El problema: una operación de negocio, varios servicios
&lt;/h2&gt;

&lt;p&gt;Supongamos que nuestra tienda tiene tres microservicios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;OrderService&lt;/code&gt;: registra y administra pedidos.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;InventoryService&lt;/code&gt;: reserva y libera productos.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PaymentService&lt;/code&gt;: procesa los pagos.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;El cliente ve una sola acción —comprar—, pero por detrás ocurren varias operaciones.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    A["Cliente confirma la compra"] --&amp;gt; B["OrderService&amp;lt;br/&amp;gt;Crea pedido PENDING"]
    B --&amp;gt; C["InventoryService&amp;lt;br/&amp;gt;Reserva la laptop"]
    C --&amp;gt; D["PaymentService&amp;lt;br/&amp;gt;Intenta cobrar"]
    D --&amp;gt;|Pago aprobado| E["OrderService&amp;lt;br/&amp;gt;Confirma pedido"]
    D --&amp;gt;|Pago rechazado| F["Cancelar pedido"]
    F --&amp;gt; G["Liberar reserva"]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;El camino feliz es sencillo: se reserva el producto, el pago se aprueba y el pedido queda confirmado.&lt;/p&gt;

&lt;p&gt;El problema aparece cuando una operación falla después de que otras ya se completaron. Por ejemplo, si el pago es rechazado, no queremos dejar un pedido aparentemente válido ni mantener una unidad reservada indefinidamente.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Por qué no basta con hacer &lt;code&gt;ROLLBACK&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Una transacción ACID permite agrupar operaciones de base de datos para que se confirmen como una unidad o se reviertan. Pero ese alcance tiene un límite: normalmente es la transacción gestionada por una base de datos.&lt;/p&gt;

&lt;p&gt;Si &lt;code&gt;OrderService&lt;/code&gt; y &lt;code&gt;InventoryService&lt;/code&gt; tienen bases de datos independientes, el &lt;code&gt;ROLLBACK&lt;/code&gt; de una no deshace los cambios de la otra.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Paso&lt;/th&gt;
&lt;th&gt;Servicio&lt;/th&gt;
&lt;th&gt;Resultado&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;OrderService&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pedido &lt;code&gt;ORD-123&lt;/code&gt; creado como &lt;code&gt;PENDING&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;code&gt;InventoryService&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Una laptop queda reservada&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PaymentService&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;El pago es rechazado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Hay que cancelar el pedido y liberar la reserva&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;La clave es esta: &lt;strong&gt;no existe un &lt;code&gt;ROLLBACK&lt;/code&gt; local que revierta automáticamente todas las operaciones ya confirmadas en servicios distintos&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. La idea de Saga: compensar, no viajar al pasado
&lt;/h2&gt;

&lt;p&gt;Una Saga convierte una operación de negocio en una secuencia de pasos locales. Cada paso confirma sus propios cambios. Si uno falla, se ejecutan acciones compensatorias para contrarrestar los pasos anteriores.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    A["Crear pedido"] --&amp;gt; B["Reservar inventario"]
    B --&amp;gt; C["Procesar pago"]
    C --&amp;gt;|Éxito| D["Confirmar pedido"]
    C --&amp;gt;|Fallo| E["Compensar"]
    E --&amp;gt; F["Cancelar pedido"]
    F --&amp;gt; G["Liberar inventario"]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Una compensación no es necesariamente borrar datos. De hecho, conservar el historial suele ser importante para auditoría y soporte.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operación original&lt;/th&gt;
&lt;th&gt;Posible compensación&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Crear un pedido&lt;/td&gt;
&lt;td&gt;Cancelar el pedido&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reservar una unidad&lt;/td&gt;
&lt;td&gt;Liberar la reserva&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cobrar al cliente&lt;/td&gt;
&lt;td&gt;Reembolsar el importe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reservar una habitación&lt;/td&gt;
&lt;td&gt;Liberar la habitación&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;La compensación debe reflejar las reglas del negocio. Por ejemplo, si el pago se rechazó antes de cobrar, no corresponde hacer un reembolso; basta con cancelar el pedido y liberar la reserva.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Un ejemplo con Java y Spring Boot
&lt;/h2&gt;

&lt;p&gt;Para entender el mecanismo, imaginemos un servicio que coordina los pasos del checkout. Los clientes HTTP que aparecen en el ejemplo representan llamadas a los otros microservicios.&lt;/p&gt;

&lt;p&gt;El código es ilustrativo: se centra en el flujo de la Saga, no en toda la configuración de Spring Boot.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CheckoutService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;OrderClient&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;InventoryClient&lt;/span&gt; &lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;PaymentClient&lt;/span&gt; &lt;span class="n"&gt;payments&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CheckoutService&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="nc"&gt;OrderClient&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
            &lt;span class="nc"&gt;InventoryClient&lt;/span&gt; &lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
            &lt;span class="nc"&gt;PaymentClient&lt;/span&gt; &lt;span class="n"&gt;payments&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;inventory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;payments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;payments&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;checkout&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// El pedido ya existe con estado PENDING.&lt;/span&gt;
        &lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;reserve&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;payments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;charge&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;confirm&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PaymentRejectedException&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Acciones compensatorias.&lt;/span&gt;
            &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cancel&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;release&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El flujo se puede leer así:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;El pedido ya fue creado con estado &lt;code&gt;PENDING&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Se reserva el producto.&lt;/li&gt;
&lt;li&gt;Se intenta procesar el pago.&lt;/li&gt;
&lt;li&gt;Si el pago se aprueba, se confirma el pedido.&lt;/li&gt;
&lt;li&gt;Si el pago se rechaza, se cancela el pedido y se libera la reserva.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;En un sistema real, &lt;code&gt;OrderClient&lt;/code&gt;, &lt;code&gt;InventoryClient&lt;/code&gt; y &lt;code&gt;PaymentClient&lt;/code&gt; podrían implementarse con &lt;code&gt;WebClient&lt;/code&gt;, HTTP Interfaces de Spring o un cliente de mensajería.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Cómo sería la compensación en &lt;code&gt;OrderService&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;Un endpoint puede cambiar el estado del pedido sin borrar el registro:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@PostMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/{orderId}/cancel"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;cancel&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@PathVariable&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orElseThrow&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getStatus&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nc"&gt;OrderStatus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CANCELLED&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Repetir la petición no vuelve a cancelar.&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getStatus&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nc"&gt;OrderStatus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PENDING&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IllegalStateException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"El pedido no se puede cancelar"&lt;/span&gt;
        &lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setStatus&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderStatus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CANCELLED&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este ejemplo incorpora una comprobación de idempotencia básica: si el pedido ya está cancelado, repetir la petición no vuelve a modificarlo. En producción, las reglas de concurrencia y las transiciones de estado deben protegerse también a nivel de persistencia.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. ¿Qué pasa si falla la compensación?
&lt;/h2&gt;

&lt;p&gt;Aquí es donde el diseño se vuelve interesante.&lt;/p&gt;

&lt;p&gt;Supongamos que el pago es rechazado, pero &lt;code&gt;InventoryService&lt;/code&gt; está temporalmente fuera de servicio. El pedido puede cancelarse, pero la reserva sigue activa.&lt;/p&gt;

&lt;p&gt;No conviene confiar en que una llamada HTTP tendrá éxito siempre. Una implementación robusta debe contemplar, al menos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reintentos controlados:&lt;/strong&gt; volver a intentar la compensación ante errores transitorios.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idempotencia:&lt;/strong&gt; repetir una solicitud no debe liberar dos veces el mismo stock ni duplicar un reembolso.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistencia del progreso:&lt;/strong&gt; registrar qué pasos se completaron y qué compensaciones siguen pendientes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observabilidad:&lt;/strong&gt; contar con logs, métricas y alertas para detectar Sagas atascadas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timeouts y recuperación:&lt;/strong&gt; decidir qué hacer si un servicio no responde o el resultado de una llamada es incierto.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;También hay una diferencia importante entre un rechazo de negocio y un error técnico. Una tarjeta vencida es un rechazo claro. Un timeout, en cambio, no demuestra que el cobro haya fallado: el proveedor podría haber cobrado y haberse perdido la respuesta. Antes de reembolsar o repetir el cobro, hay que consultar el estado de la operación o utilizar una clave de idempotencia.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Orquestación y coreografía
&lt;/h2&gt;

&lt;p&gt;Hay dos formas habituales de coordinar una Saga.&lt;/p&gt;

&lt;h3&gt;
  
  
  Orquestación
&lt;/h3&gt;

&lt;p&gt;Un componente coordinador decide qué paso ejecutar y qué compensaciones iniciar.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    O["Checkout Orchestrator"] --&amp;gt; A["OrderService"]
    O --&amp;gt; B["InventoryService"]
    O --&amp;gt; C["PaymentService"]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;La ventaja es que el flujo se puede leer en un lugar. El reto es que el orquestador necesita guardar su progreso y recuperarse después de un fallo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Coreografía
&lt;/h3&gt;

&lt;p&gt;Cada servicio publica eventos y otros servicios reaccionan a ellos. Por ejemplo, &lt;code&gt;OrderCreated&lt;/code&gt; puede iniciar la reserva; &lt;code&gt;PaymentRejected&lt;/code&gt; puede provocar la cancelación del pedido y la liberación del inventario.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart LR
    A["OrderService"] --&amp;gt;|"OrderCreated"| B["InventoryService"]
    B --&amp;gt;|"InventoryReserved"| C["PaymentService"]
    C --&amp;gt;|"PaymentRejected"| D["OrderService"]
    D --&amp;gt;|"OrderCancelled"| B&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;La coreografía evita concentrar toda la coordinación en un único componente, pero puede ser más difícil seguir el flujo completo cuando hay muchos servicios y eventos.&lt;/p&gt;

&lt;p&gt;Ningún enfoque elimina la necesidad de gestionar fallos, duplicados y reintentos. La elección depende de la complejidad del proceso y de cómo el equipo quiera mantenerlo.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Un detalle práctico: guardar el cambio y publicar el evento
&lt;/h2&gt;

&lt;p&gt;En una Saga basada en eventos aparece otro posible fallo: el servicio guarda un cambio en su base de datos, pero se cae antes de publicar el evento que informa a los demás.&lt;/p&gt;

&lt;p&gt;El patrón &lt;strong&gt;Transactional Outbox&lt;/strong&gt; ayuda a resolverlo. El servicio guarda el cambio de negocio y el evento pendiente en la misma transacción local. Otro proceso publica los eventos pendientes y los marca como enviados.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    A["Una transacción local"] --&amp;gt; B["Actualizar pedido"]
    A --&amp;gt; C["Guardar evento en Outbox"]
    C --&amp;gt; D["Publicador de eventos"]
    D --&amp;gt; E["Broker de mensajes"]
    E --&amp;gt; F["Servicios consumidores"]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;El publicador puede enviar un evento más de una vez, así que los consumidores deben ser idempotentes. Outbox ayuda a no perder la publicación entre la escritura local y el envío; no convierte toda la Saga en una transacción ACID global.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. ¿Cuándo tiene sentido usar Saga?
&lt;/h2&gt;

&lt;p&gt;Saga resulta útil cuando una operación de negocio atraviesa varios servicios independientes y no es viable —o no conviene— compartir una transacción de base de datos entre todos ellos.&lt;/p&gt;

&lt;p&gt;No hace falta aplicar el patrón a cada llamada entre microservicios. Si una operación solo consulta información o si un servicio puede completar su trabajo de forma independiente, quizá no necesite una Saga.&lt;/p&gt;

&lt;p&gt;Antes de implementarla, conviene responder estas preguntas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;¿Cuáles son los pasos del proceso?&lt;/li&gt;
&lt;li&gt;¿Qué puede fallar en cada paso?&lt;/li&gt;
&lt;li&gt;¿Qué acción compensa cada paso que ya se completó?&lt;/li&gt;
&lt;li&gt;¿Cómo se reintenta una compensación sin duplicar efectos?&lt;/li&gt;
&lt;li&gt;¿Cómo se recupera el proceso tras reiniciar un servicio?&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusión
&lt;/h2&gt;

&lt;p&gt;En un sistema distribuido, fallar a mitad de una operación es algo que hay que diseñar, no una excepción que se pueda ignorar.&lt;/p&gt;

&lt;p&gt;El patrón Saga organiza los pasos de un proceso y define cómo compensar los que ya se completaron si algo sale mal. A cambio, exige pensar en estados intermedios, reintentos, idempotencia y recuperación.&lt;/p&gt;

&lt;p&gt;La idea no es que los microservicios carezcan de transacciones. Cada servicio puede usar transacciones ACID en su propia base de datos. Lo que no se obtiene automáticamente es una única transacción ACID que abarque todos los servicios independientes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;La pregunta útil no es solo «¿qué pasa si todo sale bien?», sino también «¿cómo dejamos el negocio en un estado coherente si falla el paso número tres?».&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>java</category>
      <category>microservices</category>
    </item>
    <item>
      <title>[Spanish] Un sitio de 1000 páginas sin servidor ni base de datos: Astro + Claude Code</title>
      <dc:creator>Christian Gonzales Komiya</dc:creator>
      <pubDate>Tue, 06 Oct 2026 02:20:04 +0000</pubDate>
      <link>https://dev.to/ckomiya/un-sitio-de-996-paginas-sin-servidor-ni-base-de-datos-astro-claude-code-15om</link>
      <guid>https://dev.to/ckomiya/un-sitio-de-996-paginas-sin-servidor-ni-base-de-datos-astro-claude-code-15om</guid>
      <description>&lt;p&gt;Hace unos meses empecé a ordenar los resultados de gateball en el Perú. Los datos estaban repartidos en más de diez años de entradas del blog Kantokeando: torneos, equipos, jugadores y puestos. Los fui pasando a un Excel a mano y me pregunté si se podía convertir en algo útil para la comunidad, que busca saber quién ganó, cuántos títulos tiene un equipo o cómo le fue a un jugador.&lt;/p&gt;

&lt;p&gt;Así nació &lt;strong&gt;&lt;a href="//gatedatos.org.pe"&gt;Gate-Datos&lt;/a&gt;&lt;/strong&gt;: un archivo de consulta con 189 torneos, 203 equipos, 538 jugadores y más de mil resultados, de 2014 a 2026. Es solo de lectura, sin login, sin formularios y sin base de datos en el camino.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgmi826q42r1o4v7vmfgq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgmi826q42r1o4v7vmfgq.png" alt="Portada de Gate-Datos: buscador, último torneo y tabla de posiciones en curso" width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;La portada: un buscador y el último torneo. Todo es HTML generado de antemano.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  La idea: generar todo antes, servir solo archivos
&lt;/h2&gt;

&lt;p&gt;Los datos cambian poco. Se agregan unos 4 a 8 registros cada dos semanas, así que no hace falta consultar nada en tiempo real. Eso me llevó a esta arquitectura:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;El &lt;strong&gt;Excel&lt;/strong&gt; es la única fuente de verdad.&lt;/li&gt;
&lt;li&gt;Un pipeline en &lt;strong&gt;Python&lt;/strong&gt; (pandas, rapidfuzz) lo valida, normaliza los nombres y exporta JSON.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Astro&lt;/strong&gt; lee esos JSON y genera el sitio entero como HTML estático.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloudflare&lt;/strong&gt; sirve los archivos.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No hay servidor de aplicación, API ni consultas en cada visita. Cada página ya está construida antes de que alguien la pida.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe5609py36ll60f8tcb3p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe5609py36ll60f8tcb3p.png" alt="Podios de un torneo con campeón, segundo y tercer puesto por categoría" width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cada torneo tiene su página con los podios por categoría. Una página más de las 996 que genera el build.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué Astro
&lt;/h2&gt;

&lt;p&gt;Con los datos actuales, un solo build genera &lt;strong&gt;996 páginas&lt;/strong&gt; (una por torneo, jugador, equipo, año, tabla de posiciones y más) en unos &lt;strong&gt;11 a 13 segundos&lt;/strong&gt;. El sitio completo pesa unos 51 MB. Reconstruirlo entero en cada actualización me sale gratis en la práctica, y por eso lo hago a propósito: la normalización de nombres es global, y un registro nuevo puede cambiar datos que ya existían. Un build completo siempre es coherente.&lt;/p&gt;

&lt;p&gt;Lo que le ganamos al usuario:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Velocidad:&lt;/strong&gt; cada página es HTML listo, sin esperar a un servidor ni a que un framework "arme" la pantalla en el navegador.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Poco JavaScript:&lt;/strong&gt; Astro no envía JS que no pidas. En mi caso solo viaja un índice de búsqueda de unos 90 KB. Los JSON grandes (el de jugadores pesa unos 2 MB) los usa únicamente el build y nunca llegan al navegador.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hosting barato y robusto:&lt;/strong&gt; son archivos estáticos. No hay nada que se caiga por exceso de consultas.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  La arquitectura de islas, explicada simple
&lt;/h2&gt;

&lt;p&gt;Una página web tradicional con framework suele cargar JavaScript para toda la pantalla, aunque la mayor parte sea texto que nunca cambia. La &lt;strong&gt;arquitectura de islas&lt;/strong&gt; parte de lo contrario: la página es HTML estático (el "mar"), y solo las partes que realmente necesitan interactividad son "islas" con su propio JavaScript. Un botón de tema oscuro o un buscador pueden ser islas, y el resto de la página no paga ese costo.&lt;/p&gt;

&lt;p&gt;Un punto de honestidad: en este proyecto casi no usé islas. No metí React ni otro framework de UI. Mi interactividad es mínima: un buscador con sugerencias y un botón de tema claro/oscuro, ambos con scripts pequeños. Con eso, casi todo el sitio es HTML puro. Astro me permitió no pensar en hidratación hasta que de verdad la necesitara, y esa es la gracia: lo estático es el punto de partida y lo interactivo se agrega solo donde hace falta.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fld8sn530aveadbsu8tsb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fld8sn530aveadbsu8tsb.png" alt="Ficha de un equipo con sus títulos y la vitrina de medallas por año" width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;La ficha de un equipo: casi todo es HTML estático; el buscador y el botón de tema son las únicas partes con JavaScript.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Cómo lo publico
&lt;/h2&gt;

&lt;p&gt;El sitio vive en &lt;strong&gt;Cloudflare Workers con archivos estáticos&lt;/strong&gt;, en el plan gratuito. Está conectado a mi repositorio de GitHub: cada &lt;code&gt;git push&lt;/code&gt; a la rama principal dispara el build y el despliegue en uno o dos minutos. El dominio propio se asocia al mismo proyecto. Hasta ahora he gastado 8 de los 3.000 minutos de compilación gratuitos al mes, y los archivos estáticos no cuentan contra el límite de peticiones.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm6834joeejd7vkskq23o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm6834joeejd7vkskq23o.png" alt="Versión móvil de la portada" width="390" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;En el celular: el mismo sitio, sin app aparte.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;También agregué sitemap y robots.txt, y envié el sitio a Google Search Console.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cómo me ayudó Claude Code
&lt;/h2&gt;

&lt;p&gt;Trabajé con Claude Code como compañero de programación durante todo el proyecto, y esto es lo que más me sirvió:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;El pipeline de datos.&lt;/strong&gt; Los nombres en las entradas del blog vienen con variantes: tildes, orden Nombre/Apellido invertido, apodos, tipeos. Claude escribió los scripts que &lt;em&gt;proponen&lt;/em&gt; qué nombres son la misma persona, pero la decisión siempre fue mía, y las reglas confirmadas quedaron en archivos de alias. Esa separación (la máquina propone, el humano decide) fue clave para no mezclar a dos personas distintas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Convertir un diseño en Astro.&lt;/strong&gt; Partí de un diseño exportado y Claude lo reescribió en componentes de Astro con CSS propio, tokens de color claro y oscuro y sin librerías de UI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Un archivo de memoria del proyecto.&lt;/strong&gt; Mantengo un &lt;code&gt;CLAUDE.md&lt;/code&gt; con las decisiones: qué se muestra, qué no, cómo se actualiza. Cada sesión nueva parte con ese contexto y no tengo que explicar todo de nuevo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Revisión visual.&lt;/strong&gt; Para cambios de diseño, Claude levanta el sitio, saca capturas en escritorio y móvil, y yo veo el resultado antes de dar el visto bueno. Por ejemplo, me di cuenta de que las tarjetas con forma de arco parecían lápidas para un público mayor, y en una sola conversación se rediseñaron en cuatro páginas y se publicaron.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentación y despliegue.&lt;/strong&gt; Los pasos de publicación, los riesgos y las decisiones quedaron escritos en el repositorio, listos para retomar meses después.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Algo que aprendí: la IA rinde mucho más cuando el proyecto tiene reglas claras escritas (la fuente de verdad, qué nunca se toca, cómo se valida). Con eso, en vez de adivinar, trabaja dentro de tus límites.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que me llevo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Para datos que cambian de vez en cuando, &lt;strong&gt;generar estático&lt;/strong&gt; es más simple, más rápido y más barato que un backend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Astro&lt;/strong&gt; hace natural ese enfoque: poco JavaScript, builds rápidos y una forma de crecer hacia la interactividad cuando la necesites.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code&lt;/strong&gt; acelera mucho, siempre que tú decidas lo que importa: los datos, las reglas y el diseño final.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si te interesa ver el resultado, está en &lt;strong&gt;gatedatos.org.pe&lt;/strong&gt;, y el código es público en &lt;a href="https://github.com/ckomiya/kantokeando-data" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>astro</category>
      <category>claude</category>
      <category>webdev</category>
    </item>
    <item>
      <title>[Spanish] Por qué un agente de IA no calcula probabilidades (aunque debería, en teoría)</title>
      <dc:creator>Christian Gonzales Komiya</dc:creator>
      <pubDate>Sun, 30 Aug 2026 01:27:58 +0000</pubDate>
      <link>https://dev.to/ckomiya/spanish-por-que-un-agente-de-ia-no-calcula-probabilidades-aunque-deberia-en-teoria-3ge1</link>
      <guid>https://dev.to/ckomiya/spanish-por-que-un-agente-de-ia-no-calcula-probabilidades-aunque-deberia-en-teoria-3ge1</guid>
      <description>&lt;p&gt;Cada vez que alguien explica cómo "piensan" los agentes de IA basados en LLM, tarde o temprano aparecen tres palabras que suenan intimidantes: &lt;strong&gt;POMDP&lt;/strong&gt;, &lt;strong&gt;belief state&lt;/strong&gt; y &lt;strong&gt;regla de Bayes&lt;/strong&gt;. La buena noticia es que, detrás de la notación, la idea es bastante simple. Vamos a desarmarla con una sola analogía.&lt;/p&gt;

&lt;h2&gt;
  
  
  La analogía: un detective que nunca ve la escena completa
&lt;/h2&gt;

&lt;p&gt;Imagina un detective investigando un caso. Nunca puede ver la escena del crimen completa de una sola vez — solo va recibiendo pistas sueltas, una por una: un testimonio hoy, una huella mañana, una grabación borrosa la próxima semana. Nunca sabe con certeza absoluta quién es el culpable. Solo puede ir &lt;strong&gt;ajustando sus sospechas&lt;/strong&gt; a medida que llegan nuevas pistas.&lt;/p&gt;

&lt;p&gt;Ese es, casi exactamente, el problema que enfrenta cualquier agente de IA.&lt;/p&gt;

&lt;h2&gt;
  
  
  El problema de fondo: el mundo real está oculto
&lt;/h2&gt;

&lt;p&gt;En la jerga formal, el &lt;strong&gt;estado real del mundo&lt;/strong&gt; (llamémoslo &lt;em&gt;s&lt;/em&gt;) — quién es el culpable, en nuestra analogía — está &lt;strong&gt;oculto&lt;/strong&gt;. El agente nunca lo ve directamente. Lo único que recibe son &lt;strong&gt;observaciones parciales&lt;/strong&gt; (&lt;em&gt;o&lt;/em&gt;) — las pistas sueltas, incompletas y a veces ruidosas.&lt;/p&gt;

&lt;p&gt;Este problema tiene nombre en teoría de decisiones: se llama &lt;strong&gt;POMDP&lt;/strong&gt; (&lt;em&gt;Partially Observable Markov Decision Process&lt;/em&gt;, o proceso de decisión de Markov parcialmente observable). Es, simplemente, el marco matemático formal para "tomar decisiones cuando no ves el mundo real, solo pistas parciales".&lt;/p&gt;

&lt;p&gt;Formalmente, el mundo se comporta así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;s_{t+1} ~ T(s' | s_t, a_t)
o_{t+1} ~ O(o' | s_{t+1}, a_t)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Traducido: el mundo cambia de un estado oculto a otro (&lt;em&gt;T&lt;/em&gt;, el "kernel de transición") como respuesta a la acción del agente, y &lt;strong&gt;luego&lt;/strong&gt; genera una observación (&lt;em&gt;O&lt;/em&gt;, el "kernel de observación") a partir de ese nuevo estado — nunca al revés, y nunca de forma perfecta. El símbolo &lt;code&gt;~&lt;/code&gt; significa "se muestrea de", es decir, es un proceso con algo de azar, no una fórmula que siempre da el mismo resultado. En la analogía: el crimen ya ocurrió y dejó cierta escena (&lt;em&gt;s&lt;/em&gt;), pero la pista que le llega al detective (&lt;em&gt;o&lt;/em&gt;) es apenas un reflejo parcial y ruidoso de esa escena — nunca la escena completa.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bayes: la herramienta para actualizar sospechas
&lt;/h2&gt;

&lt;p&gt;Si el detective no puede ver la verdad directamente, ¿qué puede hacer? Mantener una &lt;strong&gt;sospecha&lt;/strong&gt; — una estimación de qué tan probable es cada posible culpable — y ajustarla cada vez que llega una pista nueva. Eso es exactamente lo que hace la &lt;strong&gt;regla de Bayes&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Mi nueva sospecha es mi sospecha anterior, reajustada según qué tan bien la pista nueva encaja con cada sospechoso posible.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;En la teoría del POMDP, esta sospecha se llama &lt;strong&gt;belief state&lt;/strong&gt; (estado de creencia, &lt;em&gt;b&lt;/em&gt;), y esa frase en español es, literalmente, esta fórmula:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;b_{t+1}(s') ∝ O(o_{t+1} | s', a_t) · Σ_s T(s' | s, a_t) · b_t(s)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se lee de derecha a izquierda: parte de la sospecha anterior (&lt;em&gt;b_t&lt;/em&gt;), predice a dónde pudo haber llevado la última acción (la suma sobre &lt;em&gt;T&lt;/em&gt;, recorriendo todos los sospechosos posibles), y luego reajusta esa predicción según qué tan bien la encaja la nueva pista (el factor &lt;em&gt;O&lt;/em&gt;). Es la regla de Bayes, aplicada un paso a la vez, cada vez que llega una observación nueva.&lt;/p&gt;

&lt;p&gt;Y una vez que el detective tiene una sospecha, todavía necesita decidir &lt;em&gt;qué hacer con ella&lt;/em&gt; — interrogar al mayordomo, pedir una orden de cateo, esperar una pista más. Eso también tiene una fórmula, la ecuación de valor óptimo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;V*(b) = max_{a} [ Σ_s b(s)·R(s,a) + γ · Σ_o P(o|b,a)·V*(b') ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En español: el valor de tener cierta sospecha (&lt;em&gt;b&lt;/em&gt;) es el de la mejor acción disponible — el pago inmediato esperado de esa acción, más el valor de a dónde te lleve después (descontado por &lt;em&gt;γ&lt;/em&gt;, porque el futuro vale un poco menos que el presente). Es una fórmula preciosa... y es exactamente la que hace que resolver un POMDP sea &lt;strong&gt;computacionalmente brutal&lt;/strong&gt;: el espacio de posibles sospechas es continuo e infinito, y calcular esta ecuación de forma exacta es tan costoso que se vuelve inviable para cualquier problema del mundo real.&lt;/p&gt;

&lt;h2&gt;
  
  
  El giro: así NO funciona un agente LLM
&lt;/h2&gt;

&lt;p&gt;Aquí está la parte que sorprende: un agente construido sobre un LLM &lt;strong&gt;no hace nada de esto&lt;/strong&gt;. No calcula sospechas con fórmulas de Bayes. No mantiene una distribución de probabilidad formal sobre el estado del mundo.&lt;/p&gt;

&lt;p&gt;En cambio, hace algo mucho más simple — y sorprendentemente efectivo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a_t = π(h_t)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Traducido: la siguiente acción del agente (&lt;em&gt;a_t&lt;/em&gt;) es simplemente el resultado de meterle &lt;strong&gt;todo el historial de mensajes&lt;/strong&gt; (&lt;em&gt;h_t&lt;/em&gt;) a la política (&lt;em&gt;π&lt;/em&gt;, que en este caso es el LLM).&lt;/p&gt;

&lt;p&gt;En otras palabras, el agente &lt;strong&gt;no calcula nada&lt;/strong&gt; — simplemente guarda todas las pistas en un cuaderno de notas completo (el historial de la conversación) y le muestra ese cuaderno entero al LLM cada vez que necesita decidir algo. Confía en que el modelo, al leer todo de una vez, "entienda" la situación razonablemente bien, sin necesidad de un cálculo formal de probabilidades.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué se usa el vocabulario del POMDP de todos modos
&lt;/h2&gt;

&lt;p&gt;Si el agente LLM no ejecuta las matemáticas del POMDP, ¿por qué los libros y artículos técnicos siguen usando palabras como "creencia" u "observación parcial" para describirlo?&lt;/p&gt;

&lt;p&gt;Porque son un &lt;strong&gt;préstamo útil de vocabulario&lt;/strong&gt;, no una afirmación de que se estén haciendo los cálculos reales. Es parecido a decir "voy a hacer un backup mental de esto" — tomamos prestada una palabra técnica (backup) para describir algo mucho más simple (memorizar), sin ejecutar ningún proceso técnico real.&lt;/p&gt;

&lt;p&gt;De la misma forma, decir que el historial de mensajes (&lt;em&gt;h_t&lt;/em&gt;) "aproxima" el belief state (&lt;em&gt;b_t&lt;/em&gt;) es una forma útil de explicar &lt;strong&gt;por qué&lt;/strong&gt; guardar y mostrar todo el historial es una buena estrategia — sin necesidad de afirmar que el LLM está calculando probabilidades exactas por debajo.&lt;/p&gt;

&lt;h2&gt;
  
  
  El mapa completo, en una tabla
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concepto&lt;/th&gt;
&lt;th&gt;Qué es&lt;/th&gt;
&lt;th&gt;En la analogía del detective&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;s&lt;/strong&gt; (estado real)&lt;/td&gt;
&lt;td&gt;La verdad oculta del mundo&lt;/td&gt;
&lt;td&gt;Quién es realmente el culpable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;o&lt;/strong&gt; (observación)&lt;/td&gt;
&lt;td&gt;Lo que el agente sí percibe&lt;/td&gt;
&lt;td&gt;Cada pista suelta&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;POMDP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;El problema formal completo&lt;/td&gt;
&lt;td&gt;"Resolver el caso solo con pistas parciales"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;b&lt;/strong&gt; (belief state)&lt;/td&gt;
&lt;td&gt;La sospecha calculada con Bayes&lt;/td&gt;
&lt;td&gt;"70% creo que fue el mayordomo" (cálculo exacto)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;h&lt;/strong&gt; (historia)&lt;/td&gt;
&lt;td&gt;Todas las pistas guardadas en crudo&lt;/td&gt;
&lt;td&gt;El cuaderno de notas completo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;π&lt;/strong&gt; (política = LLM)&lt;/td&gt;
&lt;td&gt;Quien decide la siguiente acción&lt;/td&gt;
&lt;td&gt;El detective decidiendo qué hacer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;a&lt;/strong&gt; (acción)&lt;/td&gt;
&lt;td&gt;Lo que el agente hace&lt;/td&gt;
&lt;td&gt;"Voy a interrogar al mayordomo"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  La idea para llevarse
&lt;/h2&gt;

&lt;p&gt;El POMDP y la regla de Bayes describen la forma &lt;strong&gt;ideal y matemáticamente perfecta&lt;/strong&gt; de decidir bajo incertidumbre. Pero es tan costosa de calcular que ningún agente real la ejecuta tal cual.&lt;/p&gt;

&lt;p&gt;Un agente LLM toma un atajo pragmático: en vez de calcular una creencia formal, simplemente &lt;strong&gt;lee todo el historial de golpe&lt;/strong&gt; y confía en la capacidad del modelo de lenguaje para captar, de forma implícita, algo parecido a esa creencia — sin necesidad de una sola fórmula de probabilidad.&lt;/p&gt;

&lt;p&gt;El vocabulario del POMDP no describe lo que el agente &lt;em&gt;hace&lt;/em&gt;. Describe el problema que el agente &lt;em&gt;está tratando de resolver&lt;/em&gt;, y por qué la estrategia de "meter todo el contexto al modelo" no es arbitraria — es, en el fondo, una aproximación honesta a un problema que sabemos, matemáticamente, que no se puede resolver de forma exacta.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>llm</category>
    </item>
    <item>
      <title>(Spanish) El nuevo cuello de botella no es escribir código, es confiar en él</title>
      <dc:creator>Christian Gonzales Komiya</dc:creator>
      <pubDate>Sat, 25 Jul 2026 19:25:58 +0000</pubDate>
      <link>https://dev.to/ckomiya/spanish-el-nuevo-cuello-de-botella-no-es-escribir-codigo-es-confiar-en-el-50k1</link>
      <guid>https://dev.to/ckomiya/spanish-el-nuevo-cuello-de-botella-no-es-escribir-codigo-es-confiar-en-el-50k1</guid>
      <description>&lt;p&gt;&lt;em&gt;Por qué Scrum no muere con la llegada de los agentes de IA — pero tampoco sobrevive intacto&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  El punto de partida
&lt;/h2&gt;

&lt;p&gt;Todo el mundo asume que si los agentes de IA escriben el código, los sprints se acortan, los equipos entregan más rápido, y Scrum tal como lo conocemos se vuelve obsoleto por lento. Es una conclusión intuitiva. También es, en mi opinión, parcialmente equivocada.&lt;/p&gt;

&lt;p&gt;La generación de código escala con cómputo. La capacidad de un humano para revisar ese código con criterio — para decidir si debe llegar a producción — no escala igual. Si un agente te entrega 500 líneas en dos minutos y te toma diez revisarlas bien, el límite real del sistema nunca fue la velocidad de generación. Es cuántas revisiones de calidad puede hacer un humano por día.&lt;/p&gt;

&lt;p&gt;Eso cambia todo el análisis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que se mantiene: el esqueleto de valores
&lt;/h2&gt;

&lt;p&gt;Scrum, en el fondo, es un marco de tres cosas: transparencia (todos ven el mismo estado real), inspección (revisas seguido, no al final) y adaptación (cambias de rumbo rápido). Nada de esto depende de si quien "hace" el trabajo es humano o agente. Si acaso, la necesidad de iterar corto y revisar seguido se vuelve &lt;em&gt;más&lt;/em&gt; importante cuando el volumen de output se dispara.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que muta: las ceremonias se redefinen alrededor del review, no de la escritura
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Sprint Planning&lt;/strong&gt; deja de ser "¿qué historias entran esta iteración?" y pasa a ser "¿qué specs están lo suficientemente claras para que un agente no alucine?". Esto es trabajo nuevo: antes, la ambigüedad la resolvía el dev sobre la marcha mientras codeaba. Ahora tiene que estar resuelta &lt;em&gt;antes&lt;/em&gt;, por escrito, o el agente rellena los huecos con suposiciones.&lt;/p&gt;

&lt;p&gt;Una spec "lista para agente" necesita, como mínimo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Un contrato de entrada/salida explícito — no "el endpoint de login", sino el shape exacto del request/response y qué pasa en cada caso límite.&lt;/li&gt;
&lt;li&gt;Restricciones de arquitectura ya resueltas: qué patrón usar, qué no tocar. Hoy esto vive en la cabeza del dev senior; con agentes tiene que estar escrito.&lt;/li&gt;
&lt;li&gt;Criterios de aceptación como casos de test concretos, no como prosa ("debe validar bien los datos" no sirve).&lt;/li&gt;
&lt;li&gt;Contraejemplos explícitos — qué NO hacer — porque los agentes repiten antipatrones si nadie se los marca.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La consecuencia incómoda: el planning probablemente se &lt;strong&gt;alarga&lt;/strong&gt;, no se acorta. El trabajo de pensar se mueve del "durante" (mientras codeas) al "antes" (mientras especificas). Es el mismo total de esfuerzo cognitivo, solo que concentrado al inicio.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Daily&lt;/strong&gt; se vuelve menos "¿qué hiciste ayer?" y más "¿dónde está atorado el agente, y dónde estoy atorado yo revisando?". El reporte de bloqueo cambia de "trabajo pendiente" a "contexto faltante" o "cola de revisión".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sprint Review&lt;/strong&gt; gana peso relativo — es el único punto donde un humano de negocio valida que lo generado realmente sirve, no solo que pasa los tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retro&lt;/strong&gt; se parte en dos preguntas que hoy están mezcladas: qué mejoramos del proceso humano, y qué mejoramos del contexto/prompts/guardrails del agente. La segunda se parece más a MLOps que a retro tradicional.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que cambia de fondo: la unidad de trabajo
&lt;/h2&gt;

&lt;p&gt;Hoy la unidad es la historia de usuario, dimensionada en story points para que quepa en el sprint de una persona. Con agentes, el tamaño de la tarea deja de estar limitado por cuánto puede teclear un humano en dos semanas. El límite pasa a ser cuánto contexto no ambiguo le puedes dar al agente de una vez.&lt;/p&gt;

&lt;p&gt;La consecuencia es contraintuitiva: agentes más capaces no significa tareas más grandes, sino specs más atómicas — porque la ambigüedad se paga cara a escala.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué reemplaza a story points
&lt;/h2&gt;

&lt;p&gt;Story points miden esfuerzo relativo de un humano &lt;em&gt;escribiendo&lt;/em&gt; código. Si eso deja de ser el cuello de botella, medirlo pierde sentido. Candidatos más coherentes con este nuevo mundo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tiempo de revisión estimado&lt;/strong&gt;, no de generación. Una tarea "grande" ya no es la que tarda en codearse, sino la que tarda en verificar — por superficie de cambio, criticidad, o contexto de negocio que exige.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Densidad de ambigüedad de la spec&lt;/strong&gt;: cuántas decisiones quedaron sin resolver antes de mandarle la tarea al agente.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blast radius / criticidad&lt;/strong&gt;: no es lo mismo tocar una página estática que el módulo de pagos. Esto se parece más a cómo se estima riesgo en seguridad (impacto × probabilidad) que a Scrum clásico.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tasa de aceptación en primera pasada&lt;/strong&gt;: qué porcentaje del output del agente pasa review sin cambios mayores. Mide la calidad del sistema (contexto, prompts), no del trabajo puntual — sirve para mejorar el proceso, no para planear el sprint.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;El punto de fondo: estimar deja de ser "cuánto esfuerzo humano toma escribir esto" y pasa a ser "cuánto esfuerzo humano toma confiar en que esto está bien". Es una pregunta de naturaleza distinta, y nadie ha formalizado todavía el "planning poker" de esto.&lt;/p&gt;

&lt;h2&gt;
  
  
  Una nota de cautela sobre las métricas nuevas
&lt;/h2&gt;

&lt;p&gt;Cuidado con métricas tipo "cuántos PRs generó el agente y cuántos aceptó el humano" (agent throughput). En cuanto una métrica se vuelve la meta, la gente optimiza para que suba el número, no para que mejore la calidad — ya lo vimos con "líneas de código" en los 90s. Una métrica más honesta apunta a defectos escapados a producción por sprint, o tiempo de review ponderado por complejidad: mide la calidad del filtro, no el volumen que pasa por él.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué esto no será parejo
&lt;/h2&gt;

&lt;p&gt;CRUD, dashboards, integraciones estándar, proyectos greenfield: ahí la transformación se ve rápido y clara. Pero en legacy sin tests, dominios con reglas de negocio implícitas y nunca documentadas, o contextos regulatorios (fintech, salud, gobierno), el cuello de botella sigue siendo explicarle al agente años de decisiones tácitas que nadie escribió — y eso no lo resuelve el mejor RAG del mercado. Ahí el sprint de dos semanas probablemente sobrevive más tiempo del que la narrativa optimista sugiere, y no por resistencia al cambio sino por razones regulatorias y de auditoría que no son técnicas.&lt;/p&gt;

&lt;h2&gt;
  
  
  La apuesta
&lt;/h2&gt;

&lt;p&gt;Scrum no muere. Pero dentro de tres a cinco años, lo que hoy llamamos "Scrum" probablemente se parezca más a un &lt;strong&gt;framework de gobernanza de agentes con vocabulario ágil prestado&lt;/strong&gt;, que al Scrum de 2015 enfocado en coordinar humanos escribiendo código a mano.&lt;/p&gt;

&lt;p&gt;El dev, mientras tanto, no desaparece — se convierte en el filtro más importante del sistema. Y ese filtro, a diferencia del código que revisa, no se puede generar en dos minutos.&lt;/p&gt;

</description>
      <category>agile</category>
      <category>ai</category>
      <category>softwareengineering</category>
      <category>scrum</category>
    </item>
    <item>
      <title>(Spanish) Las 8 expectativas de un Arquitecto de Software</title>
      <dc:creator>Christian Gonzales Komiya</dc:creator>
      <pubDate>Tue, 14 Jul 2026 22:32:20 +0000</pubDate>
      <link>https://dev.to/ckomiya/spanish-las-8-expectativas-de-un-arquitecto-de-software-1529</link>
      <guid>https://dev.to/ckomiya/spanish-las-8-expectativas-de-un-arquitecto-de-software-1529</guid>
      <description>&lt;p&gt;La figura del &lt;strong&gt;Arquitecto de Software&lt;/strong&gt; suele asociarse con alguien que toma decisiones técnicas importantes o diseña grandes sistemas. Sin embargo, el rol va mucho más allá de elegir tecnologías o dibujar diagramas.&lt;/p&gt;

&lt;p&gt;Más que preguntarnos &lt;strong&gt;qué es un arquitecto de software&lt;/strong&gt;, una mejor pregunta sería:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;¿Qué se espera realmente de un arquitecto de software?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A continuación comparto ocho expectativas fundamentales que todo arquitecto debería cumplir. Estas ideas están inspiradas en &lt;em&gt;Fundamentals of Software Architecture (2nd Edition)&lt;/em&gt; de Mark Richards y Neal Ford, y representan una excelente guía para quienes desean crecer profesionalmente hacia este rol.&lt;/p&gt;




&lt;h1&gt;
  
  
  1. 🏗️ Tomar decisiones arquitectónicas
&lt;/h1&gt;

&lt;p&gt;Un arquitecto debe definir las &lt;strong&gt;decisiones arquitectónicas&lt;/strong&gt; y los &lt;strong&gt;principios de diseño&lt;/strong&gt; que orientarán las decisiones tecnológicas del equipo, del departamento o incluso de toda la organización.&lt;/p&gt;

&lt;p&gt;La palabra clave aquí es &lt;strong&gt;orientar&lt;/strong&gt;, no &lt;strong&gt;imponer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Por ejemplo, decidir utilizar &lt;strong&gt;React&lt;/strong&gt; en un proyecto es una decisión técnica. En cambio, una decisión arquitectónica consiste en establecer que el frontend debe desarrollarse utilizando un &lt;strong&gt;framework reactivo&lt;/strong&gt;, permitiendo que el equipo elija entre Angular, React, Vue u otra alternativa adecuada.&lt;/p&gt;

&lt;p&gt;El trabajo del arquitecto consiste en proporcionar el marco de decisión para que los equipos puedan tomar buenas decisiones técnicas.&lt;/p&gt;

&lt;p&gt;Naturalmente, existen excepciones. Cuando una característica arquitectónica como la &lt;strong&gt;escalabilidad&lt;/strong&gt;, el &lt;strong&gt;rendimiento&lt;/strong&gt; o la &lt;strong&gt;disponibilidad&lt;/strong&gt; está en riesgo, el arquitecto puede necesitar imponer una tecnología o una restricción específica.&lt;/p&gt;




&lt;h1&gt;
  
  
  2. 🔍 Analizar continuamente la arquitectura
&lt;/h1&gt;

&lt;p&gt;Una arquitectura nunca está terminada.&lt;/p&gt;

&lt;p&gt;Con el tiempo cambian las necesidades del negocio, aparecen nuevas tecnologías y el software evoluciona. Una buena arquitectura debe evolucionar junto con él.&lt;/p&gt;

&lt;p&gt;También existe un fenómeno conocido como &lt;strong&gt;deterioro estructural&lt;/strong&gt; (&lt;em&gt;structural decay&lt;/em&gt;): pequeñas modificaciones realizadas por distintos desarrolladores terminan afectando características importantes como el rendimiento, la disponibilidad o la escalabilidad.&lt;/p&gt;

&lt;p&gt;El arquitecto debe evaluar continuamente si la arquitectura sigue siendo saludable y detectar oportunidades de mejora antes de que los problemas se vuelvan críticos.&lt;/p&gt;




&lt;h1&gt;
  
  
  3. 🚀 Mantenerse actualizado con las últimas tendencias
&lt;/h1&gt;

&lt;p&gt;Las decisiones arquitectónicas suelen tener impacto durante muchos años.&lt;/p&gt;

&lt;p&gt;Por ello, un arquitecto necesita mantenerse actualizado sobre nuevas tecnologías, herramientas y tendencias de la industria.&lt;/p&gt;

&lt;p&gt;Esto no significa perseguir todas las modas tecnológicas.&lt;/p&gt;

&lt;p&gt;Significa comprender cuáles realmente aportan valor y cuáles simplemente representan una tendencia pasajera.&lt;/p&gt;




&lt;h1&gt;
  
  
  4. ✅ Garantizar el cumplimiento de las decisiones arquitectónicas
&lt;/h1&gt;

&lt;p&gt;Diseñar una buena arquitectura no basta.&lt;/p&gt;

&lt;p&gt;También es necesario asegurarse de que los equipos realmente la implementen.&lt;/p&gt;

&lt;p&gt;Si cada desarrollador comienza a ignorar las reglas arquitectónicas, la arquitectura pierde coherencia y deja de ofrecer las características para las que fue diseñada.&lt;/p&gt;

&lt;p&gt;La arquitectura requiere &lt;strong&gt;gobernanza&lt;/strong&gt;, además de diseño.&lt;/p&gt;




&lt;h1&gt;
  
  
  5. 🌐 Comprender tecnologías diversas
&lt;/h1&gt;

&lt;p&gt;No se espera que un arquitecto sea experto en todas las tecnologías.&lt;/p&gt;

&lt;p&gt;Lo que sí se espera es que posea una visión amplia del ecosistema tecnológico.&lt;/p&gt;

&lt;p&gt;Debe conocer distintos lenguajes de programación, frameworks, plataformas, bases de datos, herramientas de integración y soluciones en la nube para poder seleccionar la alternativa más adecuada para cada contexto.&lt;/p&gt;

&lt;p&gt;En arquitectura suele ser más importante la &lt;strong&gt;amplitud del conocimiento&lt;/strong&gt; que la especialización extrema en una única tecnología.&lt;/p&gt;




&lt;h1&gt;
  
  
  6. 💼 Conocer el dominio del negocio
&lt;/h1&gt;

&lt;p&gt;La mejor solución técnica puede fracasar si no resuelve un problema de negocio.&lt;/p&gt;

&lt;p&gt;Un arquitecto necesita comprender:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;los objetivos del negocio;&lt;/li&gt;
&lt;li&gt;los procesos que soporta el sistema;&lt;/li&gt;
&lt;li&gt;las necesidades de los usuarios;&lt;/li&gt;
&lt;li&gt;las restricciones de la organización.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Su credibilidad no depende únicamente de su conocimiento técnico, sino también de su capacidad para hablar el lenguaje del negocio.&lt;/p&gt;




&lt;h1&gt;
  
  
  7. 🤝 Poseer habilidades interpersonales
&lt;/h1&gt;

&lt;p&gt;Una gran parte del trabajo de un arquitecto consiste en trabajar con personas.&lt;/p&gt;

&lt;p&gt;Debe:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;liderar equipos;&lt;/li&gt;
&lt;li&gt;facilitar acuerdos;&lt;/li&gt;
&lt;li&gt;comunicar ideas complejas;&lt;/li&gt;
&lt;li&gt;resolver conflictos;&lt;/li&gt;
&lt;li&gt;formar y mentorizar desarrolladores.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Como decía Gerald Weinberg:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"No matter what they tell you, it's always a people problem."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;("No importa lo que te digan, al final siempre es un problema de personas").&lt;/p&gt;

&lt;p&gt;Las habilidades técnicas son indispensables, pero el liderazgo y la comunicación son igual de importantes.&lt;/p&gt;




&lt;h1&gt;
  
  
  8. 🧭 Comprender y desenvolverse en la política organizacional
&lt;/h1&gt;

&lt;p&gt;Las decisiones arquitectónicas casi siempre generan resistencia.&lt;/p&gt;

&lt;p&gt;Cada equipo tiene objetivos distintos.&lt;/p&gt;

&lt;p&gt;Cada área posee prioridades diferentes.&lt;/p&gt;

&lt;p&gt;Y cada decisión importante afecta a varias personas.&lt;/p&gt;

&lt;p&gt;Por ello, el arquitecto necesita desarrollar habilidades de:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;negociación;&lt;/li&gt;
&lt;li&gt;influencia;&lt;/li&gt;
&lt;li&gt;comunicación;&lt;/li&gt;
&lt;li&gt;construcción de consensos.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Programar implica tomar decisiones sobre el código.&lt;/p&gt;

&lt;p&gt;Ser arquitecto implica tomar decisiones que afectan a toda una organización.&lt;/p&gt;

&lt;p&gt;Y eso requiere mucho más que conocimientos técnicos.&lt;/p&gt;




&lt;h1&gt;
  
  
  🎯 Reflexión final
&lt;/h1&gt;

&lt;p&gt;Muchas personas creen que convertirse en arquitecto significa dejar de programar.&lt;/p&gt;

&lt;p&gt;En realidad, significa ampliar el alcance de las decisiones que uno toma.&lt;/p&gt;

&lt;p&gt;Un arquitecto exitoso combina:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;conocimiento técnico;&lt;/li&gt;
&lt;li&gt;visión de negocio;&lt;/li&gt;
&lt;li&gt;liderazgo;&lt;/li&gt;
&lt;li&gt;comunicación;&lt;/li&gt;
&lt;li&gt;capacidad de negociación;&lt;/li&gt;
&lt;li&gt;aprendizaje continuo.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La arquitectura de software no consiste únicamente en diseñar sistemas.&lt;/p&gt;

&lt;p&gt;Consiste en ayudar a que personas, equipos y tecnología trabajen juntos para construir soluciones que sigan siendo valiosas con el paso del tiempo.&lt;/p&gt;




&lt;h2&gt;
  
  
  📚 Referencia
&lt;/h2&gt;

&lt;p&gt;Este artículo está inspirado en los conceptos presentados en la siguiente obra:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Richards, M., &amp;amp; Ford, N. (2025). &lt;em&gt;Fundamentals of Software Architecture: A Modern Engineering Approach&lt;/em&gt; (2nd ed.). O'Reilly Media. ISBN: &lt;strong&gt;978-1-098-17551-1&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;El contenido de este artículo corresponde a una síntesis, interpretación y adaptación realizada por el autor con fines de aprendizaje y divulgación técnica. Para una explicación completa y en profundidad, se recomienda la lectura del libro.&lt;/p&gt;




&lt;h2&gt;
  
  
  ✍️ Sobre este artículo
&lt;/h2&gt;

&lt;p&gt;Este artículo forma parte de mis notas de estudio sobre &lt;strong&gt;Arquitectura de Software&lt;/strong&gt;, donde comparto conceptos, aprendizajes y reflexiones obtenidas durante la lectura de libros especializados y mi experiencia profesional.&lt;/p&gt;

&lt;p&gt;Mi objetivo es convertir estas notas en contenido práctico que pueda servir tanto a otros ingenieros de software como a mí mismo como material de consulta en el futuro.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>career</category>
      <category>softwareengineering</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>(Spanish) Cómo piensa un agente ReAct: por qué el LLM no resuelve todo en una sola llamada</title>
      <dc:creator>Christian Gonzales Komiya</dc:creator>
      <pubDate>Mon, 13 Jul 2026 13:32:35 +0000</pubDate>
      <link>https://dev.to/ckomiya/spanish-como-piensa-un-agente-react-por-que-el-llm-no-resuelve-todo-en-una-sola-llamada-14fm</link>
      <guid>https://dev.to/ckomiya/spanish-como-piensa-un-agente-react-por-que-el-llm-no-resuelve-todo-en-una-sola-llamada-14fm</guid>
      <description>&lt;p&gt;Cuando empecé a jugar con LangGraph y un agente con tools (&lt;code&gt;add&lt;/code&gt;, &lt;code&gt;subtract&lt;/code&gt;, &lt;code&gt;multiply&lt;/code&gt;), me chocó algo que parecía contraintuitivo: si le pido al LLM &lt;code&gt;5 + 8/12&lt;/code&gt;, el modelo &lt;em&gt;no&lt;/em&gt; responde el resultado final de una. Hace una llamada, pide una tool, espera, hace otra llamada, pide otra tool, espera, y recién en una tercera llamada da la respuesta.&lt;/p&gt;

&lt;p&gt;Mi primera reacción fue pensar que el modelo era "limitado" o que solo podía resolver la parte para la que tenía una tool a la mano. La explicación real es más simple y más interesante: &lt;strong&gt;el modelo no puede escribir una tool call cuyo argumento sea un resultado que todavía no existe.&lt;/strong&gt; Este post explica esa idea con el ejemplo concreto que usé para entenderla.&lt;/p&gt;

&lt;h2&gt;
  
  
  El código base
&lt;/h2&gt;

&lt;p&gt;Un agente ReAct minimalista en LangGraph, con tres tools aritméticas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Herramienta de suma que agrega dos números.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;

&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;multiply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Función de multiplicación&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;

&lt;span class="n"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subtract&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;multiply&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChatDeepSeek&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;deepseek-chat&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;bind_tools&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;model_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AgentState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;AgentState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;system_prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SystemMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Eres mi asistente de IA...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;should_continue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AgentState&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;last_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;last_message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_calls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;end&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;continue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El grafo conecta &lt;code&gt;our_agent&lt;/code&gt; → &lt;code&gt;tools&lt;/code&gt; → &lt;code&gt;our_agent&lt;/code&gt; en bucle, hasta que &lt;code&gt;should_continue&lt;/code&gt; ya no encuentra &lt;code&gt;tool_calls&lt;/code&gt; en la última respuesta.&lt;/p&gt;

&lt;h2&gt;
  
  
  La confusión inicial: "resuelve solo una parte porque solo tiene esa tool"
&lt;/h2&gt;

&lt;p&gt;Con el input &lt;code&gt;5 + 8/12&lt;/code&gt;, uno esperaría que el modelo "vea" la expresión completa y la resuelva de un tiro. En cambio, pasa esto:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Llamada 1 → pide &lt;code&gt;divide(8, 12)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Llamada 2 → pide &lt;code&gt;add(5, 0.667)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Llamada 3 → responde en texto&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Da la impresión de que el modelo "hizo lo que pudo con la tool que tenía a mano, y no siguió". Pero esa lectura es engañosa. El límite no es la disponibilidad de tools — es la &lt;strong&gt;dependencia de datos&lt;/strong&gt; entre los dos pasos.&lt;/p&gt;

&lt;h2&gt;
  
  
  La razón real: no se puede prometer un resultado futuro
&lt;/h2&gt;

&lt;p&gt;Una tool call necesita valores concretos, no referencias simbólicas. El modelo no puede generar algo como:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"add"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"resultado_de_divide"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Eso no es un valor válido en el esquema de tool calling. Solo puede escribir &lt;code&gt;add(5, 0.667)&lt;/code&gt; si &lt;code&gt;0.667&lt;/code&gt; ya existe como número real en el historial. Y ese número solo existe &lt;strong&gt;después&lt;/strong&gt; de que la tool &lt;code&gt;divide&lt;/code&gt; se ejecutó y su resultado volvió como respuesta.&lt;/p&gt;

&lt;p&gt;Por eso, en la primera llamada, lo único que el modelo puede pedir es la operación que &lt;strong&gt;ya tiene todos sus datos disponibles&lt;/strong&gt;: la división. No es que "decida" resolver solo esa parte — es la única parte que puede ejecutar con la información que tiene en ese instante.&lt;/p&gt;

&lt;h3&gt;
  
  
  Analogía: mandarle mensajes a un amigo con calculadora
&lt;/h3&gt;

&lt;p&gt;Imagina que sabes exactamente qué cuenta quieres hacer (5 + 8/12), pero no tienes calculadora — solo puedes escribirle a un amigo que sí tiene una, y esperar su respuesta antes de seguir.&lt;/p&gt;

&lt;p&gt;Le escribes: &lt;em&gt;"divide 8 entre 12"&lt;/em&gt;. Ya sabes que después vas a sumar 5 al resultado — pero no puedes escribir eso en el mismo mensaje, porque tu amigo no interpreta instrucciones encadenadas en lenguaje natural: solo ejecuta funciones con números concretos. Así que lo único que puedes mandar es la operación cuyos números ya tienes: la división. La suma queda para el siguiente mensaje, una vez que tengas la respuesta.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prueba de que no es "una tool por llamada"
&lt;/h2&gt;

&lt;p&gt;Si el límite fuera "el modelo solo puede usar una tool por llamada", entonces con un input como:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Suma 4+2 y suma 7+8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Debería necesitar dos llamadas. Pero no es así — como ninguna de las dos sumas depende de la otra, el modelo pide &lt;strong&gt;ambas tool calls en la misma respuesta&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"add"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"add"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El &lt;code&gt;ToolNode&lt;/code&gt; las ejecuta ambas sin volver a llamar al LLM entre medio. Total: &lt;strong&gt;2 llamadas al LLM&lt;/strong&gt;, no 3. Esto confirma que la regla real es &lt;em&gt;"no puedo pedir algo cuyo argumento depende de un resultado que aún no existe"&lt;/em&gt; — no una limitación arbitraria de cantidad de tools por turno.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué pasa cuando falta una tool
&lt;/h2&gt;

&lt;p&gt;Con &lt;code&gt;5 + 8/12&lt;/code&gt;, si el modelo tiene tanto &lt;code&gt;add&lt;/code&gt; como &lt;code&gt;divide&lt;/code&gt;, ambas operaciones las hace código Python con precisión garantizada, en 3 llamadas. Pero:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Si solo existe la tool &lt;code&gt;add&lt;/code&gt;&lt;/strong&gt; (no hay &lt;code&gt;divide&lt;/code&gt;): el modelo calcula la división mentalmente (como cualquier LLM sin tools, con su propio "cálculo interno"), y usa la tool solo para la suma. Total: 2 llamadas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Si solo existe la tool &lt;code&gt;divide&lt;/code&gt;&lt;/strong&gt; (no hay &lt;code&gt;add&lt;/code&gt;): el modelo pide la división como tool call, y luego calcula la suma mentalmente con el resultado. Total: 2 llamadas.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;El patrón se mantiene: el número de llamadas depende de cuántos pasos secuenciales &lt;em&gt;con dependencia de datos&lt;/em&gt; hay — no de cuántas tools existan en total. Cuando falta una tool para un paso, el modelo simplemente resuelve ese paso con su propio razonamiento en lugar de con código, y sigue adelante.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que el LLM sabe y lo que no sabe desde la primera llamada
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Sí sabe, desde el inicio:&lt;/strong&gt; qué tools tiene disponibles y sus esquemas (nombre, descripción, parámetros). Eso se manda en &lt;strong&gt;cada&lt;/strong&gt; llamada — es literalmente lo que hace &lt;code&gt;bind_tools(tools)&lt;/code&gt;: adjuntar esa lista de forma fija a cada invocación.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No sabe, ni falta que sepa:&lt;/strong&gt; cuántas llamadas totales se van a necesitar. El modelo no planea "esto me va a tomar 3 vueltas" — es reactivo, no planificador de antemano. En cada llamada individual mira el historial actual y decide un solo paso siguiente: pedir tool(s) o responder en texto. Quien determina que &lt;em&gt;habrá&lt;/em&gt; otra llamada es LangGraph (&lt;code&gt;should_continue&lt;/code&gt; + el edge &lt;code&gt;tools → our_agent&lt;/code&gt;), no el modelo.&lt;/p&gt;

&lt;h2&gt;
  
  
  El input y output real de cada llamada
&lt;/h2&gt;

&lt;p&gt;Un punto clave: &lt;strong&gt;el LLM nunca ve clases de Python&lt;/strong&gt; como &lt;code&gt;AIMessage&lt;/code&gt; o &lt;code&gt;ToolMessage&lt;/code&gt;. Esas son abstracciones de LangChain. Lo que realmente viaja a la API es JSON con un campo &lt;code&gt;role&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Clase de LangChain&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;role&lt;/code&gt; real&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SystemMessage&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;system&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;HumanMessage&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;user&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AIMessage&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;assistant&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ToolMessage&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tool&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Así se ve la secuencia completa para &lt;code&gt;5 + 8/12&lt;/code&gt; (con tools &lt;code&gt;add&lt;/code&gt; y &lt;code&gt;divide&lt;/code&gt;):&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Llamada 1 — input:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"system"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Eres mi asistente de IA..."&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"5 + 8/12"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Llamada 1 — output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assistant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tool_calls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call_001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"function"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"divide"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 8, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;b&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 12}"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Llamada 2 — input&lt;/strong&gt; (todo lo anterior + el resultado de la tool):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"system"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Eres mi asistente de IA..."&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"5 + 8/12"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assistant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tool_calls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call_001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"function"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"divide"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 8, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;b&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 12}"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tool_call_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call_001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.6666666666666666"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Llamada 2 — output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assistant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tool_calls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call_002"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"function"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"add"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 5, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;b&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 0.6666666666666666}"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Llamada 3 — input&lt;/strong&gt; (historial completo hasta ahora):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"system"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Eres mi asistente de IA..."&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"5 + 8/12"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assistant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tool_calls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call_001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"function"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"divide"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 8, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;b&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 12}"&lt;/span&gt;&lt;span class="p"&gt;}}]},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tool_call_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call_001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.6666666666666666"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assistant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tool_calls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call_002"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"function"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"add"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 5, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;b&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 0.6666666666666666}"&lt;/span&gt;&lt;span class="p"&gt;}}]},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tool_call_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call_002"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"5.666666666666667"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Llamada 3 — output&lt;/strong&gt; (ya sin &lt;code&gt;tool_calls&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assistant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"5 + 8/12 = 5.666666666666667 (aproximadamente 5.67)."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tool_calls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nota clave: el &lt;code&gt;tool_call_id&lt;/code&gt; (&lt;code&gt;call_001&lt;/code&gt;, &lt;code&gt;call_002&lt;/code&gt;) es el hilo que conecta cada tool call con su respuesta correspondiente — así el modelo sabe cuál &lt;code&gt;ToolMessage&lt;/code&gt; responde a cuál petición suya.&lt;/p&gt;

&lt;h2&gt;
  
  
  Output del modelo vs. input acumulado: quién arma el historial
&lt;/h2&gt;

&lt;p&gt;Algo que vale la pena separar bien: &lt;strong&gt;el output del LLM es solo el mensaje nuevo&lt;/strong&gt;, no el historial repetido. &lt;code&gt;model.invoke(...)&lt;/code&gt; te devuelve únicamente el &lt;code&gt;assistant&lt;/code&gt; que acaba de generar.&lt;/p&gt;

&lt;p&gt;Quien acumula todo es LangGraph, gracias al reducer definido en el estado:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AgentState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TypedDict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;BaseMessage&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;add_messages&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cuando el nodo hace &lt;code&gt;return {"messages": [response]}&lt;/code&gt;, el reducer &lt;code&gt;add_messages&lt;/code&gt; no sobreescribe &lt;code&gt;state["messages"]&lt;/code&gt; — hace un &lt;strong&gt;append&lt;/strong&gt;. Así, para la siguiente llamada, &lt;code&gt;state["messages"]&lt;/code&gt; ya contiene todo lo acumulado hasta ese momento, y &lt;code&gt;model_call&lt;/code&gt; vuelve a mandar la lista completa (&lt;code&gt;[system_prompt] + state["messages"]&lt;/code&gt;) en cada invocación.&lt;/p&gt;

&lt;p&gt;Es la diferencia entre "el modelo te contesta una carta" y "tú guardas copia de cada carta en una carpeta, y le mandas la carpeta completa junto con la carta nueva la próxima vez".&lt;/p&gt;

&lt;h2&gt;
  
  
  Resumen: el patrón completo
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;El modelo recibe todo el historial acumulado hasta ese punto (nunca recuerda nada por sí mismo — no tiene memoria entre llamadas).&lt;/li&gt;
&lt;li&gt;Con esa información, decide &lt;strong&gt;un solo paso&lt;/strong&gt;: pedir tool(s) para las que ya tiene todos los datos concretos, o responder en texto si no falta nada.&lt;/li&gt;
&lt;li&gt;Cada tool call se ejecuta como código Python normal (&lt;code&gt;ToolNode&lt;/code&gt;), sin costo de LLM.&lt;/li&gt;
&lt;li&gt;LangGraph vuelve a invocar al modelo con el historial actualizado — y el ciclo se repite hasta que la respuesta ya no trae &lt;code&gt;tool_calls&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;El número de llamadas al LLM depende de cuántos pasos secuenciales con &lt;strong&gt;dependencia de datos&lt;/strong&gt; tiene la tarea — no de cuántas tools existan en total. Tools independientes se piden en paralelo, en una sola llamada.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Cada llamada al LLM es una petición HTTP real, facturable en tokens, y el costo de entrada crece en cada vuelta porque se reenvía todo el historial acumulado — algo a tener en cuenta al diseñar agentes con muchos pasos encadenados.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>llm</category>
      <category>langchain</category>
      <category>langgraph</category>
    </item>
  </channel>
</rss>
