<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="es"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://engineering.proportione.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://engineering.proportione.com/" rel="alternate" type="text/html" hreflang="es" /><updated>2026-04-25T21:18:20+01:00</updated><id>https://engineering.proportione.com/feed.xml</id><title type="html">Proportione Engineering</title><subtitle>Post-mortems, decisiones de arquitectura y material reutilizable de una consultoría ibérica AI-first.</subtitle><author><name>Proportione</name></author><entry><title type="html">Capataz y Obrero: por qué Claude Code delega a un LLM local en lugar de hacerlo él</title><link href="https://engineering.proportione.com/architecture/ia-local/2026/04/23/capataz-y-obrero-claude-code-llm-local.html" rel="alternate" type="text/html" title="Capataz y Obrero: por qué Claude Code delega a un LLM local en lugar de hacerlo él" /><published>2026-04-23T14:00:00+01:00</published><updated>2026-04-23T14:00:00+01:00</updated><id>https://engineering.proportione.com/architecture/ia-local/2026/04/23/capataz-y-obrero-claude-code-llm-local</id><content type="html" xml:base="https://engineering.proportione.com/architecture/ia-local/2026/04/23/capataz-y-obrero-claude-code-llm-local.html"><![CDATA[<p>Quien lleva un mes usando Claude Code en serio nota dos cosas. La primera, que es asombrosamente útil para tareas complejas — refactor, diseño, depuración fina. La segunda, que <strong>no toda tarea merece un Opus</strong>. Pedirle al modelo más capaz del mercado que renombre 40 variables o que clasifique 100 logs por categoría es como contratar a un cirujano para que cambie una bombilla. Funciona. Cuesta una barbaridad.</p>

<p>La trampa simétrica es montarse un modelo local creyendo que sustituye al frontier. No sustituye. Para razonamiento profundo, contexto largo, código nuevo desde cero, <strong>el frontier sigue ganando por mucho</strong>. Quien pretende reemplazarlo entero se encontrará rehaciendo a mano lo que el frontier hubiera resuelto en una iteración.</p>

<p>Entre los dos extremos hay un patrón que sí funciona: que el modelo frontier dirija y un modelo local ejecute la rutina. Lo llamamos <strong>Capataz y Obrero</strong>, y va a ser la primera carga real del servidor Aveiro <a href="/architecture/infraestructura/2026/04/23/servidor-aveiro-cuatro-opciones-dos-trampas.html">del que hablamos en el post anterior</a>.</p>

<h2 id="el-patrón">El patrón</h2>

<p>La división es asimétrica por diseño.</p>

<p><img src="/assets/img/2026-04-23-capataz-obrero/exhibit-1-patron.svg" alt="Diagrama del patrón Capataz y Obrero: Claude Code en el portátil decide qué hacer, descompone, revisa y cierra; un modelo LLM local ejecuta tareas rutinarias delegadas; ambos se comunican vía MCP sobre la red privada Tailscale." />
<em>Exhibit 1 — Capataz piensa, Obrero ejecuta. La línea entre ambos es MCP.</em></p>

<p>El <strong>Capataz</strong> (Claude Code en el portátil) hace lo que requiere razonamiento amplio: leer el repo, entender qué se pide, descomponer en pasos concretos, decidir cuáles delega y cuáles ejecuta él mismo, revisar el resultado y cerrar el bucle. Es caro por minuto, pero su tiempo se compensa con creces en las decisiones que toma.</p>

<p>El <strong>Obrero</strong> (un LLM local — en nuestro caso, Qwen 2.5 Coder 14B cuantizado a 4 bits, corriendo en Ollama sobre el servidor de Aveiro) recibe instrucciones acotadas y devuelve resultados acotados. No decide qué hacer; hace lo que se le pide. Cuesta cero por petición, está siempre disponible, y su latencia es predecible — del orden de un par de segundos por respuesta corta.</p>

<p>El conector es <strong>MCP</strong> (Model Context Protocol). MCP estandariza cómo un cliente como Claude Code llama a herramientas externas. Un servidor MCP que envuelve Ollama aparece ante Claude Code como un conjunto de funciones invocables: clasifica esto, resume aquello, busca el patrón en este texto. Claude decide cuándo llamarlas, espera el resultado y sigue su trabajo.</p>

<h2 id="qué-tarea-va-a-quién">Qué tarea va a quién</h2>

<p>La regla de reparto no es “tareas fáciles al obrero”. Es más fina: <strong>cualquier tarea con criterio de éxito objetivo y formato de salida estable es candidata a ir al obrero</strong>. Cualquier tarea que requiere juicio sobre el qué, no sobre el cómo, se queda con el capataz.</p>

<p><img src="/assets/img/2026-04-23-capataz-obrero/exhibit-2-matriz.svg" alt="Matriz de tipos de tarea con responsable correcto: clasificación con taxonomía cerrada, extracción estructurada de texto y resúmenes cortos van al obrero; arquitectura, depuración, diseño desde cero y revisión final se quedan con el capataz; tareas mixtas se ejecutan en pipeline." />
<em>Exhibit 2 — La regla de reparto. La columna del medio es donde está el dinero.</em></p>

<p>Algunos ejemplos concretos del flujo real de Proportione:</p>

<ul>
  <li><strong>Clasificar tickets entrantes</strong> por área y departamento. Taxonomía cerrada, salida estructurada, repetitivo. Al obrero.</li>
  <li><strong>Extraer entidades</strong> (nombre, empresa, asunto) de un correo libre. Formato estable, criterio objetivo. Al obrero.</li>
  <li><strong>Decidir si un PR introduce regresión arquitectónica.</strong> Requiere entender el repo, el contexto del cambio, la intención del autor. Al capataz.</li>
  <li><strong>Generar 30 variantes de copy para un A/B test</strong> con tono y restricciones dadas. Al obrero, con plantilla del capataz.</li>
  <li><strong>Diseñar el módulo nuevo de SLA</strong>. Al capataz, sin discusión.</li>
</ul>

<p>El patrón gana cuando una sesión real combina las dos cosas. El capataz lee la conversación, identifica que hay 50 tickets que clasificar, los manda en lote al obrero, recibe el resultado y sigue con el problema arquitectónico que le ocupaba. La clasificación cuesta cero, ocurre en segundo plano, y libera al capataz para lo que solo él puede hacer.</p>

<h2 id="stack-técnico">Stack técnico</h2>

<p>El montaje físico es deliberadamente simple, en parte para que se pueda replicar en otra organización sin equipo de plataforma.</p>

<p><img src="/assets/img/2026-04-23-capataz-obrero/exhibit-3-stack.svg" alt="Diagrama del stack: portátil con Claude Code y servidor MCP local, conexión por red privada Tailscale al servidor Aveiro, donde Ollama sirve el modelo Qwen 2.5 Coder 14B sobre la GPU; opcionalmente apps móviles Enchanted o Reins consumen el mismo endpoint." />
<em>Exhibit 3 — Cuatro componentes, cero dependencias externas tras la instalación.</em></p>

<p>Las decisiones que conviene explicar:</p>

<p><strong>Por qué Ollama y no llama.cpp directo.</strong> Ollama envuelve a llama.cpp con una API estable y un sistema de modelos versionados. Para una organización que va a cambiar de modelo cada pocos meses (porque la frontera abierta se mueve rápido), el coste de mantener invocaciones a llama.cpp directas no compensa.</p>

<p><strong>Por qué Qwen 2.5 Coder 14B Q4 y no algo más grande.</strong> Es la combinación que cabe holgada en 16 GB de VRAM dejando margen para contexto largo (32 K tokens) sin descargar pesos a CPU. La cuantización a 4 bits degrada la calidad por debajo del umbral perceptible para tareas rutinarias. Cuando entre la GPU de Fase 2, sustituiremos por Qwen 32B o Llama 70B sin tocar el resto del stack.</p>

<p><strong>Por qué Tailscale y no abrir el puerto 11434.</strong> Tailscale crea una red privada entre los equipos sin exponer servicios a internet. El servidor Aveiro no tiene IP pública, no aparece en escáneres, y solo los dispositivos del <em>tailnet</em> corporativo pueden hablarle. Reduce la superficie de ataque a cero sin la fricción habitual de una VPN tradicional.</p>

<p><strong>Por qué un servidor MCP propio en lugar de uno genérico.</strong> Los servidores MCP genéricos para Ollama existen pero no aplican prompts específicos de Proportione (taxonomías de tickets, formato de extracción, restricciones de copy). Mantener un servidor MCP local de 200 líneas que sí los aplica es trivial y permite versionar los prompts junto al resto del código.</p>

<h2 id="tradeoffs-honestos">Tradeoffs honestos</h2>

<p>Tres cosas que no funcionan tan bien como una presentación dejaría creer:</p>

<p><strong>La calidad del obrero degrada con prompts mal diseñados.</strong> Un modelo de 14B cuantizado tolera muchísimo peor un prompt ambiguo que Claude. Si la tarea no se puede explicar en cinco líneas con ejemplos, probablemente no es candidata a delegación. Lo aprendimos con un primer intento de delegar generación de mensajes de commit: el resultado era inconsistente porque el prompt era impreciso. Reescribirlo más estricto resolvió el problema, pero exige tiempo del capataz que hay que contar como inversión.</p>

<p><strong>Latencia de red al delegar mata el ahorro en lotes pequeños.</strong> Para una sola petición, llamar al servidor MCP, ir hasta Aveiro por Tailscale y volver añade unos 200 ms de overhead. Si la tarea individual cuesta 800 ms al obrero, hablamos del 25 % de la ejecución total. Para una sola tarea no compensa. Para un lote de 50 sí: el overhead se diluye.</p>

<p><strong>Cuando el obrero falla, el capataz no se entera bien.</strong> Un modelo local que devuelve basura formateada como JSON válido pasa los validadores básicos. Necesitamos validadores semánticos sencillos — ¿el área devuelta está en la taxonomía? ¿el resumen es más corto que el original? — y un fallback explícito al capataz cuando saltan. Todavía afinando estos validadores.</p>

<h2 id="cuándo-este-patrón-gana">Cuándo este patrón gana</h2>

<p>No siempre compensa. Tres condiciones que conviene verificar antes de montarlo:</p>

<ol>
  <li><strong>Volumen suficiente para amortizar el setup.</strong> Si la previsión es delegar menos de unos cientos de tareas al mes, el ahorro económico es marginal frente al coste de mantener el stack. Mejor pagar al frontier y olvidarse.</li>
  <li><strong>Tareas con formato cerrado.</strong> Si el formato de salida varía mucho entre llamadas, el modelo local sufre y la consistencia cae. El patrón requiere disciplina en la definición de las herramientas MCP.</li>
  <li><strong>Capacidad de mantener un nodo de cómputo propio.</strong> Si el servidor exige más atención operativa de la que la organización puede sostener, lo barato sale caro. El patrón presupone que la decisión de tener nodo propio ya está tomada y validada por otros motivos — en nuestro caso, los del <a href="/architecture/infraestructura/2026/04/23/servidor-aveiro-cuatro-opciones-dos-trampas.html">post anterior</a>.</li>
</ol>

<p>Cuando las tres se cumplen, el patrón es genuinamente diferencial. No por el ahorro económico — que existe pero rara vez es el motor — sino por algo más importante: <strong>libera la atención del capataz para lo que solo el capataz puede hacer</strong>. Y esa atención, en un equipo pequeño, es el recurso más escaso.</p>

<hr />

<p><em>Próximo post de la serie: cómo el patrón cambia el ticketing y la gestión de SLA cuando uno de los pasos es “esperar respuesta del cliente” y otro es “una persona del equipo está revisando”. Para hablar de este, abre una <a href="https://github.com/Proportione/proportione-plugins/discussions">Discussion</a> o escribe a <a href="mailto:info@proportione.com">info@proportione.com</a>.</em></p>]]></content><author><name>Proportione</name></author><category term="architecture" /><category term="ia-local" /><category term="claude-code" /><category term="mcp" /><category term="ollama" /><category term="qwen" /><category term="llm-local" /><category term="agente" /><summary type="html"><![CDATA[Claude Code para todo es caro y lento. Un modelo local para todo es limitado. La combinación correcta no es elegir uno: es dividir el trabajo. Va el patrón que vamos a montar sobre el nodo Aveiro.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://engineering.proportione.com/assets/img/heroes/post-4-capataz-obrero.svg" /><media:content medium="image" url="https://engineering.proportione.com/assets/img/heroes/post-4-capataz-obrero.svg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Servidor propio para una consultoría AI-first: cuatro opciones, dos trampas y una decisión</title><link href="https://engineering.proportione.com/architecture/infraestructura/2026/04/23/servidor-aveiro-cuatro-opciones-dos-trampas.html" rel="alternate" type="text/html" title="Servidor propio para una consultoría AI-first: cuatro opciones, dos trampas y una decisión" /><published>2026-04-23T14:00:00+01:00</published><updated>2026-04-23T14:00:00+01:00</updated><id>https://engineering.proportione.com/architecture/infraestructura/2026/04/23/servidor-aveiro-cuatro-opciones-dos-trampas</id><content type="html" xml:base="https://engineering.proportione.com/architecture/infraestructura/2026/04/23/servidor-aveiro-cuatro-opciones-dos-trampas.html"><![CDATA[<p>Una consultoría pequeña con foco en IA tiene tres caminos para su capa de cómputo: alquilarlo todo en la nube, encadenar Macs como puestos de trabajo, o montar un servidor propio. Cada camino esconde un coste distinto. Decidimos el tercero — un servidor x86 en nuestra sede de Aveiro — y este post cuenta cómo se llega ahí sin caer en la tentación del catálogo más caro ni del más barato.</p>

<p>Por el camino acumulamos dos lecciones que nos vinieron por sorpresa: una sobre el desfase entre la placa que eliges en hoja de cálculo y la que de verdad puedes comprar; otra sobre cómo los marketplaces europeos rompen los pedidos enterprise en pedazos.</p>

<h2 id="marco-de-decisión-cuatro-opciones-reales">Marco de decisión: cuatro opciones reales</h2>

<p>Antes de elegir hardware, listamos qué soluciones cumplían los criterios mínimos: cómputo suficiente para inferencia LLM local, almacenamiento ampliable, un canal de réplica al nodo Madrid y vida útil mayor de cinco años. Salieron cuatro candidatos.</p>

<p><img src="/assets/img/2026-04-23-aveiro/exhibit-1-opciones.svg" alt="Matriz comparativa de cuatro opciones para la capa de cómputo de una consultoría pequeña: nube hyperscaler, Mac Mini en estantería, NAS Synology con módulo de cálculo y servidor x86 propio, evaluadas en cinco dimensiones." />
<em>Exhibit 1 — Las cuatro opciones que evaluamos. La fila clave es la última.</em></p>

<p>La nube hyperscaler perdió por una razón concreta: nuestro perfil de carga es continuo y predecible (inferencia LLM más servicios internos), no a ráfagas. Pagar por hora un cómputo que necesitamos siempre es la peor combinación. Cuando hicimos números a tres años, cualquier instancia con GPU equivalente nos salía entre tres y cinco veces más cara que comprar el hardware una vez.</p>

<p>El Mac Mini quedó fuera por el techo de RAM (24 GB en el modelo base, 64 GB en el M2 Pro) y por la imposibilidad de instalar Proxmox o pasar GPU a un contenedor. Es un equipo excelente como puesto de trabajo. Como nodo de cómputo central, no.</p>

<p>El NAS con módulo de cálculo lo descartamos por un detalle que solo se ve cuando ya tienes uno: el cómputo va al ritmo del NAS, y el NAS se diseña para ser silencioso y ahorrador, no para mover modelos de lenguaje. Mantenemos el NAS Synology de Madrid como nodo de réplica fría y de Time Machine, que es exactamente para lo que es bueno.</p>

<p>Quedó el servidor x86 propio. La decisión no era qué comprar, sino con qué placa, qué procesador y qué GPU.</p>

<h2 id="la-bom-y-por-qué-cada-componente">La BOM y por qué cada componente</h2>

<p>Apuntamos a una arquitectura de cuatro fases. Fase 1 es funcional desde el día uno: hipervisor, almacenamiento, red interna y un modelo LLM local de tamaño medio. Las fases siguientes añaden GPU mayor, más RAM y redundancia.</p>

<p><img src="/assets/img/2026-04-23-aveiro/exhibit-2-bom.svg" alt="Tabla con la lista de componentes de la fase 1 del servidor de Aveiro: placa server-grade con IPMI, CPU EPYC con AVX-512, RAM ECC, almacenamiento mixto NVMe + SATA, GPU 16 GB, fuente Titanium dimensionada para upgrades futuros, caja silenciosa." />
<em>Exhibit 2 — Cada componente lleva una razón. La razón vale más que el modelo.</em></p>

<p>Tres principios marcaron las elecciones:</p>

<ol>
  <li><strong>Nada se desperdicia al escalar</strong>. La fuente está dimensionada para una GPU de gama alta futura. La caja admite el largo de tarjetas de la próxima generación. La RAM se añade en módulos, no se sustituye. La GPU actual pasa a tarjeta secundaria cuando entre la siguiente.</li>
  <li><strong>Server-grade siempre que el sobrecoste sea pequeño</strong>. Placa con IPMI fuera de banda, RAM ECC, fuente Titanium con garantía de diez años, discos enterprise. La diferencia frente a componentes consumer está entre el 10 % y el 30 %, y compra años de tranquilidad.</li>
  <li><strong>Comprable desde Aveiro</strong>. Todo el catálogo se podía pedir a Amazon.es, PCComponentes o Worten con envío a Portugal. Sin importaciones internacionales, sin segunda mano, sin “lo trae alguien que viaja”.</li>
</ol>

<h2 id="trampa-1-la-placa-que-eliges-no-es-la-placa-que-llegas-a-comprar">Trampa 1: la placa que eliges no es la placa que llegas a comprar</h2>

<p>Aquí ocurrió el episodio más educativo del proceso. La hoja de cálculo dijo claramente que la placa correcta era una Supermicro server-grade con IPMI maduro y dos NVMe nativos. La validación externa lo confirmó: una alternativa de ASRock Rack que también encajaba tenía dos bugs documentados en el foro de Proxmox — uno relacionado con passthrough de GPU, otro con la NIC integrada que desaparecía después de ciertos reinicios. Decisión clara: Supermicro.</p>

<p>Cuando fuimos a comprar, la Supermicro elegida no estaba disponible ni en Amazon, ni en PCComponentes, ni en Senetic, ni en los proveedores españoles habituales con envío a Portugal. La estimación más optimista era de seis a ocho semanas. La ASRock con los bugs sí estaba disponible, en stock, con envío inmediato.</p>

<p><img src="/assets/img/2026-04-23-aveiro/exhibit-3-trampa-stock.svg" alt="Diagrama de decisión flip-flop en cuatro pasos: especificación inicial elige A, validación externa cambia a B por bugs documentados, compra real vuelve a A por ruptura de stock de B, mitigación de bugs con BIOS y software open-source." />
<em>Exhibit 3 — La realidad pragmática del stock. Lo importante no es no cambiar de opinión: es saber por qué cambias.</em></p>

<p>Volvimos a la ASRock con los bugs documentados. Pero no como antes — esta vez con un plan de mitigación específico para cada bug:</p>

<ul>
  <li><strong>Bug del passthrough de GPU</strong>: actualización de BIOS a la última versión del fabricante (donde el bug está corregido) y BMC a la versión 6.01 o superior. Verificación durante el montaje, antes de poner el servidor en producción.</li>
  <li><strong>Bug de la NIC integrada</strong>: tener a mano la utilidad de recuperación del fabricante de la NIC y, como salvaguarda, una NIC PCIe de Intel para sustituirla si el problema persiste.</li>
</ul>

<p>La lección no es “Supermicro es mejor que ASRock” ni al contrario. Es que <strong>una decisión de hardware sin verificar disponibilidad real es solo una preferencia</strong>. Y que cambiar de opinión está bien siempre que el motivo del cambio se documente en algún sitio donde el yo del futuro pueda releerlo.</p>

<h2 id="trampa-2-comprar-hardware-enterprise-en-marketplaces-europeos">Trampa 2: comprar hardware enterprise en marketplaces europeos</h2>

<p>Esta nos sorprendió más. Los componentes enterprise — placa, CPU, fuente, NIC — viven en Amazon.es como vendedores marketplace, no como artículos de Amazon directo. Eso tiene tres consecuencias que conviene saber antes:</p>

<p><strong>Un pedido se convierte en varios.</strong> Confirmas siete artículos en una transacción y el sistema los fragmenta en cinco o seis subpedidos, uno por vendedor. Cada uno tiene su propia fecha de entrega, su propio código y su propia política de devolución. La gestión postventa se multiplica.</p>

<p><strong>Anti-fraude se activa con valor alto en poco tiempo.</strong> Confirmar dos pedidos con varios cientos de euros cada uno en menos de diez minutos disparó los algoritmos de Amazon. El pedido más grande quedó cancelado automáticamente, “por seguridad”. Resolverlo requirió enviar documentación de empresa por su canal de recurso. Aprendido para la siguiente vez: separar las compras grandes por al menos un día.</p>

<p><strong>Amazon Locker no funciona con marketplace.</strong> Probablemente el detalle más doloroso. Los puntos de recogida automática que tan bien funcionan para artículos directos están vetados para vendedores externos. Hay que recibir en dirección física durante ventana laboral, lo que en una sede pequeña requiere coordinación o dejar plantón a alguien.</p>

<p>Ninguna de las tres es un drama, pero las tres juntas alargaron el proceso de un día previsto a una semana real. Si vas a montar un servidor enterprise por catálogo, presupuestar tiempo de gestión de pedidos como si fuera una tarea aparte, no como un click final.</p>

<h2 id="plan-de-fases-y-por-qué-importa-contarlo">Plan de fases y por qué importa contarlo</h2>

<p>El servidor no nace adulto. Lo levantamos por fases para que cada euro tenga una utilidad inmediata, y para que el upgrade futuro a una GPU de gama alta no obligue a tirar nada.</p>

<p><img src="/assets/img/2026-04-23-aveiro/exhibit-4-fases.svg" alt="Línea temporal en cuatro fases: cimientos con hipervisor y modelo de lenguaje medio, IA potente con GPU mayor y más RAM, redundancia con segundo nodo y SAI, expansión de almacenamiento RAIDZ." />
<em>Exhibit 4 — Cuatro fases. Cada una entrega valor sin depender de la siguiente.</em></p>

<p>La Fase 1 entrega el grueso del valor: hipervisor con contenedores, almacenamiento espejado, réplica al nodo Madrid, modelo LLM local de tamaño medio funcional desde el primer día y <em>tailnet</em> con acceso desde portátil y móvil sin abrir puertos. El retorno frente al ahorro de la API externa es directo y medible — del orden de varias decenas de euros al mes desde la primera semana.</p>

<p>Las fases siguientes son optativas. Si Fase 1 cubre la carga real durante seis meses, no hay urgencia. Si el modelo medio se queda corto, la Fase 2 sustituye la GPU por una mayor — y la GPU original pasa a tarjeta secundaria para <em>embeddings</em>, voz a texto y generación de imagen, sin tirar nada.</p>

<h2 id="patrón-reutilizable">Patrón reutilizable</h2>

<p>Tres cosas nos llevamos para repetir en futuras decisiones de hardware:</p>

<ol>
  <li><strong>Empezar por el marco, no por el componente</strong>. La pregunta correcta no es “¿qué placa?” sino “¿qué carga real, qué horizonte temporal, qué presupuesto operativo a tres años?”. El componente cae solo cuando esas tres respuestas están claras.</li>
  <li><strong>Validar disponibilidad antes de cerrar la decisión</strong>. La especificación ideal sin stock es un mock-up, no una decisión. Reservar siempre una alternativa con plan B documentado.</li>
  <li><strong>Server-grade donde el sobrecoste sea moderado</strong>. ECC, IPMI, fuente Titanium y discos enterprise no son lujo: son tiempo que no gastarás depurando inestabilidades raras dentro de tres años.</li>
</ol>

<p>El servidor entra en servicio durante mayo. El siguiente post de esta serie contará el patrón “Capataz y Obrero” que monta encima — cómo Claude Code en el portátil delega tareas rutinarias a un modelo LLM local que vive en este hardware.</p>

<hr />

<p><em>Si estás evaluando algo parecido y quieres contraste honesto sobre alguna decisión concreta, abre una <a href="https://github.com/Proportione/proportione-plugins/discussions">Discussion</a> o escribe a <a href="mailto:info@proportione.com">info@proportione.com</a>.</em></p>]]></content><author><name>Proportione</name></author><category term="architecture" /><category term="infraestructura" /><category term="servidor" /><category term="aveiro" /><category term="proxmox" /><category term="zfs" /><category term="hardware" /><category term="ia-local" /><summary type="html"><![CDATA[Decidimos no migrar a un hyperscaler ni encadenar Macs. Va el marco con el que elegimos servidor propio, la BOM real, las dos trampas que casi rompen la decisión y la lección sobre comprar hardware enterprise en marketplaces europeos.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://engineering.proportione.com/assets/img/heroes/post-3-servidor-aveiro.svg" /><media:content medium="image" url="https://engineering.proportione.com/assets/img/heroes/post-3-servidor-aveiro.svg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Prospección B2B industrial sin email frío: cuatro herramientas y una regla</title><link href="https://engineering.proportione.com/architecture/prospeccion/2026/04/23/prospeccion-industrial-sin-email-frio.html" rel="alternate" type="text/html" title="Prospección B2B industrial sin email frío: cuatro herramientas y una regla" /><published>2026-04-23T13:00:00+01:00</published><updated>2026-04-23T13:00:00+01:00</updated><id>https://engineering.proportione.com/architecture/prospeccion/2026/04/23/prospeccion-industrial-sin-email-frio</id><content type="html" xml:base="https://engineering.proportione.com/architecture/prospeccion/2026/04/23/prospeccion-industrial-sin-email-frio.html"><![CDATA[<p>El email frío lleva tiempo sin rendir. Las tasas de respuesta por debajo del 1 % son habituales, los filtros de spam cada vez más duros y los compradores industriales — nuestra audiencia — reconocen el patrón desde la primera línea. La salida fácil es contratar una agencia que lance diez mil emails más. La salida lenta es ir uno a uno, a mano, como en 2008.</p>

<p>Ninguna de las dos nos servía. Levantamos un pipeline intermedio en cuatro fases y lo probamos primero en industria portuguesa. Va el detalle.</p>

<p><img src="/assets/img/2026-04-23-prospeccion/exhibit-1-pipeline.svg" alt="Pipeline en cuatro fases con la regla innegociable arriba: descubrimiento, enriquecimiento, outreach dual y conversión, con las herramientas asignadas a cada paso." />
<em>Exhibit 1 — Las cuatro fases y la regla que las gobierna.</em></p>

<h2 id="la-regla-antes-que-la-arquitectura">La regla, antes que la arquitectura</h2>

<p>Una regla innegociable antes de elegir herramienta: <strong>nunca contactar a un decisor sin contexto previo</strong>.</p>

<p>Contexto quiere decir dos cosas. Una, que la empresa del decisor ha visto contenido nuestro antes del primer mensaje — un artículo, un post, una mención en prensa sectorial. Dos, que el mensaje habla de algo concreto de su industria, no de “nuestras capacidades”. El email genérico al CEO pidiendo una reunión es lo que intentamos no enviar jamás.</p>

<p>Esa regla nos obligó a separar dos flujos que muchas pipelines de prospección funden: el descubrimiento de empresas y la conversación con decisores. Descubrir es rápido y masivo. Conversar es lento y selectivo. Mezclar los tiempos es la raíz de la mayoría de los emails fríos que todos detestamos.</p>

<h2 id="fase-1--descubrimiento">Fase 1 — Descubrimiento</h2>

<p>Fuente principal: Google Maps. No es glamoroso, pero es honesto — cada ficha en Maps corresponde a una empresa que existe, tiene dirección física verificable y declara un sector. Para industria portuguesa eso es suficiente.</p>

<p>El tramo automatizable lo cubre <a href="https://browseract.com">BrowserAct</a> con dos plantillas encadenadas: una que barre resultados de búsqueda (“metalurgia {ciudad}”, “moldes {municipio}”, “cerâmica {região}”) y otra que entra en cada ficha para sacar web, teléfono y código postal. El output llega a un JSON que procesamos con un script pequeño (<code class="language-plaintext highlighter-rouge">collect-browseract-results.mjs</code>) que descarta duplicados por dominio.</p>

<p>Sobre esos datos corre un filtro ICP en <code class="language-plaintext highlighter-rouge">enrich-and-qualify.mjs</code>. Elimina restaurantes, farmacias y servicios de proximidad que se cuelan por el barrido amplio, y asigna un score por tamaño, claridad de industria y presencia web profesional. El criterio de “web profesional” es una heurística tosca pero útil: empresas con sitio propio moderno suelen tener también capacidad para invertir en consultoría.</p>

<p>Orden de magnitud real tras un ciclo completo: <strong>500-700 empresas raw, 300-500 candidatas ICP, 20 targets seleccionados a mano</strong>. El recorte brutal no es casualidad. Preferimos 20 conversaciones posibles que 500 contactos imposibles.</p>

<p><img src="/assets/img/2026-04-23-prospeccion/exhibit-2-funnel.svg" alt="Embudo descendente de tres pasos: ~600 empresas raw filtradas a ~400 candidatas ICP y reducidas finalmente a 20 targets, con porcentajes de descarte y motivos." />
<em>Exhibit 2 — El embudo. El −95 % de la última fase es manual y a propósito.</em></p>

<h2 id="fase-2--enriquecimiento">Fase 2 — Enriquecimiento</h2>

<p>Aquí buscamos los decisores. No el genérico <code class="language-plaintext highlighter-rouge">info@empresa.pt</code>, que nunca llega a nadie: los nombres, los cargos y los correos personales. Cuatro fuentes en orden de rendimiento decreciente:</p>

<ol>
  <li><strong>Web de la empresa</strong> — página “Equipa” u “Órgãos Sociais”. Las grandes la tienen. Las medianas, rara vez.</li>
  <li><strong>Hunter.io Domain Search</strong> — devuelve emails con <code class="language-plaintext highlighter-rouge">emailStatus</code> (valid, accept_all, invalid). Criterio: aceptamos <code class="language-plaintext highlighter-rouge">valid</code> siempre, <code class="language-plaintext highlighter-rouge">accept_all</code> con precaución, <code class="language-plaintext highlighter-rouge">invalid</code> nunca.</li>
  <li><strong>Registros públicos portugueses</strong> — Racius devuelve gerentes de sociedades. Información pública, gratuita, fiable.</li>
  <li><strong>Social Media Finder en BrowserAct</strong> — para los casos en que la empresa no publica LinkedIn en su web.</li>
</ol>

<p>La tasa de éxito del Social Media Finder fue del 12 % en nuestra primera ronda. No es espectacular, pero entra a coste casi cero en el flujo. LinkedIn por Playwright lo probamos y lo descartamos: funciona, sí, pero es lento, frágil y los cambios del front de LinkedIn rompen cualquier scraper cada pocas semanas. Para LinkedIn hay herramientas mejores.</p>

<h2 id="decisión-1--firestore-como-crm">Decisión 1 — Firestore como CRM</h2>

<p>Aquí es donde mucha gente elige HubSpot, Pipedrive o Salesforce. Nosotros elegimos Firestore. El argumento:</p>

<ul>
  <li><strong>Nuestros datos, nuestra lógica</strong>. Un CRM SaaS te cobra por contacto, te mete en su embudo y convierte tu base comercial en un activo que no controlas. Firestore es almacenamiento ligero — los flujos los modelamos nosotros.</li>
  <li><strong>Integración nativa con el resto del stack</strong>. Las entidades viven en la misma base que ticketing y soporte, con el mismo <code class="language-plaintext highlighter-rouge">orgId</code>, las mismas convenciones de <code class="language-plaintext highlighter-rouge">createdAt</code>, las mismas reglas de auditoría.</li>
  <li><strong>Coste marginal</strong>. Unas decenas de miles de registros en Firestore cuestan céntimos al mes. Un CRM comercial, varios cientos de euros desde el primer día.</li>
</ul>

<p>El tradeoff honesto: Firestore no trae interfaz de CRM de caja. Hemos tenido que construir la vista de deals, la cadencia de follow-ups, los dashboards BI. Nos salió a cuenta porque lo necesitábamos también para ticketing — pero no es universalmente buena decisión. Si solo hicieras CRM, el SaaS probablemente gana.</p>

<p><img src="/assets/img/2026-04-23-prospeccion/exhibit-3-firestore-vs-saas.svg" alt="Tabla comparativa de cinco dimensiones entre Firestore y CRM SaaS: coste a 3 años, time-to-value, integración, propiedad del dato y cuándo gana cada uno." />
<em>Exhibit 3 — Cuándo gana cada uno. La fila clave es la última.</em></p>

<h2 id="decisión-2--canal-dual-no-solo-email">Decisión 2 — Canal dual, no solo email</h2>

<p>Aun con dominio verificado y calentado, solo el 30-40 % de los emails llegan a la bandeja de entrada. Eso deja fuera a seis de cada diez destinatarios. Para no perderlos, el canal secundario es LinkedIn.</p>

<p>El orquestador de LinkedIn es <a href="https://sendpilot.com">SendPilot</a>: gestiona conexiones, secuencias de mensajes y <em>warmup</em> automáticamente, y nos devuelve estado vía webhook. El integrador local (<code class="language-plaintext highlighter-rouge">sendpilot-routing.mjs</code>, <code class="language-plaintext highlighter-rouge">sendpilot-status-poll.mjs</code>, <code class="language-plaintext highlighter-rouge">sendpilot-warmup-queue.mjs</code>) consume esos eventos, los escribe en la misma entidad Firestore del contacto y deja a Brevo — nuestro canal email — funcionar en paralelo sobre el mismo decisor.</p>

<p>La regla operativa: <strong>primero Brevo, después SendPilot, nunca los dos a la vez</strong>. La cadencia típica es blog post enviado por newsletter → esperar 2-3 días → si no hay apertura, activar secuencia LinkedIn. Nunca empezamos por LinkedIn frío. Nunca mandamos email genérico sin blog detrás. El único mensaje que el decisor recibe de nosotros sin contenido previo es una conexión de LinkedIn, sin pitch.</p>

<p><img src="/assets/img/2026-04-23-prospeccion/exhibit-4-cadencia.svg" alt="Línea temporal de 21 días con cinco hitos: contenido publicado, newsletter, decisión de bifurcación según apertura, follow-up por email o conexión LinkedIn, mensaje LinkedIn final con contenido propio." />
<em>Exhibit 4 — La cadencia. La bifurcación ocurre solo si el email se ignora.</em></p>

<h2 id="lo-que-no-funcionó">Lo que no funcionó</h2>

<p>Merece la pena anotar los caminos cerrados:</p>

<ul>
  <li><strong>Scraping de LinkedIn con Playwright</strong>. Funcionaba el lunes, roto el viernes. Mantener scrapers caseros contra un target que se defiende activamente es un sumidero de horas.</li>
  <li><strong>Hunter.io Email Verifier como único filtro</strong>. Un <code class="language-plaintext highlighter-rouge">accept_all</code> parece un email válido, pero el 30-40 % de ellos rebotan. Combinar con otra señal (presencia en LinkedIn, dominio con MX razonable) lo hace fiable.</li>
  <li><strong>Primer mensaje LinkedIn pidiendo reunión</strong>. Tasa de aceptación de conexión al suelo. Cambiar a “conexión sin pitch + mensaje con contenido dos semanas después” subió la tasa al rango útil.</li>
</ul>

<h2 id="patrón-reutilizable">Patrón reutilizable</h2>

<p>Dos cosas que nos llevamos para replicar en otra región o sector:</p>

<ol>
  <li><strong>Separar descubrimiento de conversación por tiempo y por herramienta</strong>. Un scraper de ICP no es un CRM, y un CRM no es un sistema de outreach. Meterlos en la misma caja es la raíz de la prospección mal hecha.</li>
  <li><strong>Dos canales paralelos con una regla de precedencia clara</strong>. Email por delante, LinkedIn detrás, nunca los dos a la vez. Contenido antes que pitch, siempre.</li>
</ol>

<p>El pipeline completo corre desde el ordenador de una persona en un par de sesiones al mes por región. No hace falta un SDR a tiempo completo. No hay misterio: hay un orden, un filtro duro al principio y una regla innegociable en el último tramo.</p>

<hr />

<p><em>Si estás montando algo parecido y atascas en alguna parte concreta, abre una <a href="https://github.com/Proportione/proportione-plugins/discussions">Discussion</a> o escribe a <a href="mailto:info@proportione.com">info@proportione.com</a>. Compartimos las decisiones que tomamos, no las cifras que tomaron nuestros clientes.</em></p>]]></content><author><name>Proportione</name></author><category term="architecture" /><category term="prospeccion" /><category term="prospeccion" /><category term="browseract" /><category term="sendpilot" /><category term="firestore" /><category term="crm" /><category term="b2b" /><summary type="html"><![CDATA[El email frío ya no rinde. El uno-a-uno manual tampoco escala. Este es el pipeline que construimos para prospección industrial en Portugal — cuatro piezas, dos decisiones de arquitectura y una regla innegociable.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://engineering.proportione.com/assets/img/heroes/post-2-prospeccion.svg" /><media:content medium="image" url="https://engineering.proportione.com/assets/img/heroes/post-2-prospeccion.svg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Cuando el router inteligente desconecta tus IoT: el caso del Roaming Assistant</title><link href="https://engineering.proportione.com/post-mortem/redes/2026/03/24/roaming-assistant-desconexion-iot.html" rel="alternate" type="text/html" title="Cuando el router inteligente desconecta tus IoT: el caso del Roaming Assistant" /><published>2026-03-24T23:00:00+00:00</published><updated>2026-03-24T23:00:00+00:00</updated><id>https://engineering.proportione.com/post-mortem/redes/2026/03/24/roaming-assistant-desconexion-iot</id><content type="html" xml:base="https://engineering.proportione.com/post-mortem/redes/2026/03/24/roaming-assistant-desconexion-iot.html"><![CDATA[<p>Seis Alexas dejaron de responder a las 22:00 del 23 de marzo. Ninguna reconectó por sí sola. No había corte de luz, no había actualización pendiente, no había cambios en la red durante el día.</p>

<p>La causa no estaba en las Alexas ni en Amazon. Estaba en un parámetro del router que tiene sentido en una oficina densa y casi ninguno en una casa.</p>

<h2 id="síntoma">Síntoma</h2>

<ul>
  <li>Seis dispositivos Alexa (Echo Dot mezclados con Show 5) sin conectividad simultáneamente.</li>
  <li>Ninguno aparecía en la tabla de clientes DHCP del router.</li>
  <li>Reiniciar una Alexa la devolvía a la lista, pero solo durante unos minutos.</li>
  <li>Otros dispositivos 2.4 GHz (termostato, sensores Zigbee vía hub) funcionaban con normalidad.</li>
</ul>

<h2 id="sospechas-descartadas">Sospechas descartadas</h2>

<p>Una por una:</p>

<ul>
  <li><strong>Canal 2.4 GHz saturado</strong> — ya estaba fijado en canal 1 con 20 MHz desde el fix de marzo (un episodio anterior de ACSD cambiando canal por la noche). No era eso.</li>
  <li><strong>DHCP agotado</strong> — la tabla tenía espacio.</li>
  <li><strong>Interferencia bluetooth o microondas</strong> — las Alexas son robustas a esto; además no explicaba las seis a la vez.</li>
  <li><strong>Firmware del router obsoleto</strong> — ya era la última (3.0.0.4.388_24305 en el ASUS TUF-AX5400).</li>
</ul>

<h2 id="causa-raíz">Causa raíz</h2>

<p>En el <code class="language-plaintext highlighter-rouge">syslog</code> del router aparecían decenas de eventos en <code class="language-plaintext highlighter-rouge">eth5</code> (la interfaz 2.4 GHz):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Disassociated due to inactivity
</code></pre></div></div>

<p>Ese mensaje no significa “el dispositivo dejó de responder”. Significa “yo, el router, decidí expulsarlo”. Y lo decidió por una razón concreta: el <strong>Roaming Assistant</strong> con umbral de <code class="language-plaintext highlighter-rouge">-70 dBm</code>.</p>

<h2 id="qué-hace-el-roaming-assistant">Qué hace el Roaming Assistant</h2>

<p>El Roaming Assistant está pensado para entornos con varios puntos de acceso. Su trabajo es empujar a un cliente a reasociarse con otro AP cuando su señal cae por debajo de un umbral. Para conseguirlo, lo <em>desauthentica</em> activamente, confiando en que el cliente buscará un AP mejor a continuación.</p>

<p>La lógica tiene dos problemas en una casa:</p>

<ol>
  <li><strong>No hay otro AP</strong> al que migrar. El cliente queda desconectado y punto.</li>
  <li><strong>Los IoT baratos</strong> no implementan bien el reconnect tras una desasociación forzada. Algunos dejan de intentarlo hasta un ciclo de corriente.</li>
</ol>

<p>Con la señal de las Alexas fluctuando alrededor de <code class="language-plaintext highlighter-rouge">-70 dBm</code> (el salón no estaba justo al lado del router), el Roaming Assistant las iba expulsando una tras otra. Tras varios intentos fallidos de reasociarse, se rendían.</p>

<h2 id="fix-aplicado">Fix aplicado</h2>

<p>Cinco cambios en la configuración 2.4 GHz (NVRAM persistente):</p>

<table>
  <thead>
    <tr>
      <th>Parámetro</th>
      <th>Antes</th>
      <th>Después</th>
      <th>Motivo</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Roaming Assistant (<code class="language-plaintext highlighter-rouge">wl0_user_rssi</code>)</td>
      <td><code class="language-plaintext highlighter-rouge">-70 dBm</code></td>
      <td><code class="language-plaintext highlighter-rouge">0</code> (OFF)</td>
      <td>Causa directa. No hay AP alternativo.</td>
    </tr>
    <tr>
      <td>TX Beamforming (<code class="language-plaintext highlighter-rouge">wl0_txbf</code>)</td>
      <td>ON</td>
      <td>OFF</td>
      <td>Inestabilidad observada con IoT baratos.</td>
    </tr>
    <tr>
      <td>Implicit Beamforming (<code class="language-plaintext highlighter-rouge">wl0_itxbf</code>)</td>
      <td>ON</td>
      <td>OFF</td>
      <td>Ídem.</td>
    </tr>
    <tr>
      <td>WPS (<code class="language-plaintext highlighter-rouge">wps_enable</code>)</td>
      <td>ON</td>
      <td>OFF</td>
      <td>Vector de ataque + reconexiones espontáneas.</td>
    </tr>
    <tr>
      <td>Turbo QAM 256 (<code class="language-plaintext highlighter-rouge">wl0_turbo_qam</code>)</td>
      <td>ON</td>
      <td>OFF</td>
      <td>Modulación no soportada por algunos IoT.</td>
    </tr>
  </tbody>
</table>

<p>Configuración final 2.4 GHz verificada: canal 1 fijo, 20 MHz, WiFi 6 desactivado, WPA2-Personal/AES, MU-MIMO y OFDMA desactivados, Smart Connect desactivado. Un router “tonto” en la banda 2.4, que es exactamente lo que los IoT necesitan.</p>

<h2 id="lecciones">Lecciones</h2>

<ol>
  <li>
    <p><strong>El router “inteligente” no es siempre tu amigo.</strong> Las optimizaciones pensadas para entornos empresariales densos pueden romper un setup doméstico con IoT heterogéneo.</p>
  </li>
  <li>
    <p><strong>El <code class="language-plaintext highlighter-rouge">syslog</code> del router es infravalorado.</strong> El mensaje <code class="language-plaintext highlighter-rouge">Disassociated due to inactivity</code> era la pista completa. Sin acceso al log, habríamos seguido culpando a las Alexas.</p>
  </li>
  <li>
    <p><strong>2.4 GHz merece configuración separada de 5 GHz.</strong> Los IoT viven en 2.4 GHz y no necesitan nada de lo que hace atractivo a 5 GHz. Mezclarlos con “Smart Connect” empeora las dos bandas.</p>
  </li>
  <li>
    <p><strong>Documentar el fix en el punto donde alguien lo leerá.</strong> En nuestro caso, <a href="https://github.com/Proportione"><code class="language-plaintext highlighter-rouge">router_credentials.md</code></a> con el “Known issue” anotado. El próximo episodio — y lo habrá — empezará por ahí.</p>
  </li>
</ol>

<h2 id="acción-manual-restante">Acción manual restante</h2>

<p>Para cerrar el incidente hubo que desenchufar y reenchufar las seis Alexas (una por una, 10-15 segundos entre cada una). El ciclo de corriente es la única forma fiable de que un Echo que lleva rato “rendido” vuelva a intentar asociarse.</p>

<hr />

<p><em>Si ves algo que está mal o tienes un caso parecido, abre un <a href="https://github.com/Proportione/proportione-plugins/issues">Issue</a> o comparte en <a href="https://www.linkedin.com/company/proportione">LinkedIn</a>.</em></p>]]></content><author><name>Proportione</name></author><category term="post-mortem" /><category term="redes" /><category term="iot" /><category term="wifi" /><category term="asus" /><category term="router" /><category term="alexa" /><category term="post-mortem" /><summary type="html"><![CDATA[Seis Alexas dejaron de responder a las 22:00 y no volvieron solas. La causa no estaba en las Alexas ni en Amazon — estaba en un parámetro del router que tiene sentido en una oficina y ninguno en casa.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://engineering.proportione.com/assets/img/heroes/post-1-roaming.svg" /><media:content medium="image" url="https://engineering.proportione.com/assets/img/heroes/post-1-roaming.svg" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>