<?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: lu1tr0n</title>
    <description>The latest articles on DEV Community by lu1tr0n (@lu1tr0n).</description>
    <link>https://dev.to/lu1tr0n</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%2F806044%2F070ecfe6-05a6-44e1-95e3-81211fc13ab4.png</url>
      <title>DEV Community: lu1tr0n</title>
      <link>https://dev.to/lu1tr0n</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lu1tr0n"/>
    <language>en</language>
    <item>
      <title>Qwen3-4B deslop corta las muletillas de IA de 121 a 12,5 por 1000 palabras</title>
      <dc:creator>lu1tr0n</dc:creator>
      <pubDate>Mon, 19 Oct 2026 14:31:14 +0000</pubDate>
      <link>https://dev.to/lu1tr0n/qwen3-4b-deslop-corta-las-muletillas-de-ia-de-121-a-125-por-1000-palabras-13ik</link>
      <guid>https://dev.to/lu1tr0n/qwen3-4b-deslop-corta-las-muletillas-de-ia-de-121-a-125-por-1000-palabras-13ik</guid>
      <description>&lt;p&gt;Un ingeniero entrenó a &lt;strong&gt;Qwen3-4B&lt;/strong&gt; para que dejara de escribir como una IA: el puntaje de muletillas de IA bajó de 121 a 12,5 por cada 1.000 palabras, y los guiones largos retóricos pasaron de 20,5 por 1.000 palabras a cero, según &lt;a href="https://100toolkit.com/blog/deslop-model/" rel="noopener noreferrer"&gt;documenta el propio autor&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;El resultado se llama qwen3-4b-deslop y pesa 2,5 GB en su versión cuantizada. Cualquiera puede descargarlo con Ollama y comparar, prompt por prompt, cómo responde frente al modelo original. No es un experimento de laboratorio cerrado: es un modelo público que corre en una laptop.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;El puntaje de muletillas de IA de Qwen3-4B-deslop cae de 121 a 12,5 por 1.000 palabras tras el fine-tuning.- Los guiones largos (em dash) desaparecen por completo: de 20,5 por 1.000 palabras a cero.- RLHF premia el tono que suena 'servicial', y ese sesgo es el origen de los tics de escritura de la IA.- Un LoRA sobre 161 respuestas filtradas quita las muletillas sin tocar el conocimiento: acierta 30/30 preguntas.- Ollama corre el modelo cuantizado (2,5 GB) en tu laptop para comparar el antes y el después en tus propios prompts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ¿Qué son las muletillas de IA?
&lt;/h2&gt;

&lt;p&gt;Las muletillas de IA son fragmentos de estilo que un modelo de lenguaje repite con tanta frecuencia que delatan su origen artificial: el guion largo como pausa dramática, los tríos de adjetivos, la fórmula "no es X, sino Y" y las preguntas que el propio texto responde de inmediato.&lt;/p&gt;

&lt;p&gt;Es parecido a lo que pasa con un vendedor que aprende, a fuerza de repetir el mismo discurso, qué frases hacen asentir a la mayoría de la gente aunque no digan nada nuevo. El modelo no entiende que esas frases son vacías: solo sabe que, en el entrenamiento, subieron su calificación.&lt;/p&gt;

&lt;p&gt;No son errores aleatorios. Vienen del mismo proceso de entrenamiento, y por eso aparecen con la misma forma en modelos de laboratorios distintos. Entender de dónde salen es el primer paso para poder quitarlas sin dañar lo que el modelo sabe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué aparecen: el sesgo de RLHF hacia la aprobación humana
&lt;/h2&gt;

&lt;p&gt;El afinado que le da a un modelo su "voz de asistente" se llama &lt;a href="https://en.wikipedia.org/wiki/Reinforcement_learning_from_human_feedback" rel="noopener noreferrer"&gt;aprendizaje por refuerzo con retroalimentación humana (RLHF)&lt;/a&gt;. Un grupo de personas califica pares de respuestas, un modelo de recompensa aprende a predecir esas calificaciones y el modelo final se ajusta para maximizar esa puntuación.&lt;/p&gt;

&lt;p&gt;El problema es que los calificadores humanos no leen con la misma atención que un editor. Premian respuestas que "se sienten" completas y seguras, y ciertos adornos superficiales generan esa sensación sin aportar información real. Un trío de adjetivos suena exhaustivo. Una pregunta respondida en la misma frase suena didáctico. Un guion largo en mitad de la oración suena a pausa reflexiva. Nada de eso añade un dato, pero sí sube la calificación promedio.&lt;/p&gt;

&lt;p&gt;Por eso los tics de escritura de la IA no son un capricho de un modelo en particular, sino el subproducto estadístico de optimizar para el gusto promedio de miles de calificadores. Y como el proceso se repite en cada laboratorio con metodologías parecidas, los mismos hábitos aparecen en modelos que nunca compartieron código ni datos de entrenamiento.&lt;br&gt;
El modelo original promediaba 121 puntos de muletillas por 1.000 palabras.&lt;/p&gt;
&lt;h2&gt;
  
  
  Cómo funciona el fine-tuning dirigido para quitarlas
&lt;/h2&gt;

&lt;p&gt;Antes de tocar los pesos del modelo, el autor probó dos atajos que no requieren reentrenar nada. El primero fue el "activation steering": restar, durante la generación, la dirección interna que corresponde a la "voz de asistente", combinada con un prompt de sistema que pide un estilo plano. Funciona, y bajó el puntaje hasta cerca de 15, pero necesita ese prompt y ese código corriendo en cada respuesta, y además desvía las predicciones del modelo sobre texto normal que no tiene nada que ver con el estilo.&lt;/p&gt;

&lt;p&gt;El segundo atajo fue más quirúrgico: revisar neuronas una por una hasta encontrar cuatro cuya activación, si se invierte, elimina el 70% de los casos de "no es X, sino Y" casi sin costo. Pero ese parche solo funciona para esa muletilla puntual y no generaliza al resto de los hábitos.&lt;/p&gt;

&lt;p&gt;Para resolverlo de forma permanente, dentro de los pesos y sin hooks en tiempo de ejecución, el autor usó destilación. Un modelo "profesor" (construido con lo mejor que había funcionado hasta ese punto) genera respuestas a un conjunto de prompts de entrenamiento. Las respuestas que pasan los filtros de calidad alimentan un adaptador LoRA pequeño, que aprende a producir ese mismo estilo a partir del prompt original, sin el profesor ni el código extra.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dot"&gt;&lt;code&gt;&lt;span class="nv"&gt;flowchart&lt;/span&gt; &lt;span class="nv"&gt;TD&lt;/span&gt;
&lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Profesor genera 3 respuestas por prompt"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;B&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Filtro: puntaje de muletillas bajo 12"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;B&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;C&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Filtro: sin daño (deriva y quiz de 30 preguntas)"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;C&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;D&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Filtro: la respuesta cumple la tarea pedida"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;D&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;E&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"LoRA aprende del subconjunto aprobado"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;E&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;F&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Modelo estudiante Qwen3-4B-deslop"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La primera ronda de este proceso, sin gancho de estilo ni esteroides de prompt, ya bajó el puntaje a cerca de 20. Fue la prueba de que destilar funciona mejor que forzar el estilo en tiempo de ejecución.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dot"&gt;&lt;code&gt;&lt;span class="nv"&gt;flowchart&lt;/span&gt; &lt;span class="nv"&gt;LR&lt;/span&gt;
&lt;span class="nv"&gt;R1&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Ronda 1: puntaje cercano a 20"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;R2&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Ronda 2: puntaje 9.9, pero falla el ajuste a la tarea"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;R2&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;R3&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Ronda 3: puntaje 12.5, ajuste a la tarea en 80%"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  El detector de tics de escritura: cómo se mide el puntaje
&lt;/h3&gt;

&lt;p&gt;Para saber si el modelo mejoró, hace falta medir, no solo mirar. El detector que usa el autor mide patrones de escritura de IA con un conjunto de reglas con pesos. Cuenta guiones largos, apariciones de "no es X, sino Y", tríos de tres elementos, preguntas retóricas respondidas en la misma frase, muletillas de transición como "aquí está la clave" y palabras como "delve" o "seamless" en inglés (sus equivalentes en español serían "profundizar en" o "sin fisuras" usados como relleno). Cada hábito pesa distinto, y la suma se normaliza por cada 1.000 palabras.&lt;/p&gt;

&lt;p&gt;Un detector basado en reglas no distingue por sí solo buena de mala escritura, así que el autor también revisó manualmente una muestra de respuestas en cada ronda. Un regex puede contar guiones largos, pero no puede decir si el párrafo que los rodea tiene sentido.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dot"&gt;&lt;code&gt;&lt;span class="nv"&gt;flowchart&lt;/span&gt; &lt;span class="nv"&gt;LR&lt;/span&gt;
&lt;span class="nv"&gt;T&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Texto generado"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;H1&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Cuenta guiones largos"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;T&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;H2a&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Cuenta 'no es X, sino Y'"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;T&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;H3a&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Cuenta preguntas que se responden solas"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;H1&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;S&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Suma ponderada"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;H2a&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;S&lt;/span&gt;
&lt;span class="nv"&gt;H3a&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;S&lt;/span&gt;
&lt;span class="nv"&gt;S&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;P&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Puntaje por 1.000 palabras"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para ilustrar la idea, este es un detector simplificado en Python que cualquiera puede correr sobre un párrafo propio:&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;slop_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;em_dashes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;not_x_but_y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;no es .*?, sino&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IGNORECASE&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;rhetorical&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;\?\s*[A-ZÁÉÍÓÚ]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;em_dashes&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;not_x_but_y&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;rhetorical&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;words&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&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;span class="n"&gt;texto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;El framework no es solo rápido, es, ante todo, elegante. ¿Por qué importa? Porque resuelve tres problemas: velocidad, memoria y legibilidad.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;slop_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;texto&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con ese párrafo de ejemplo (que acumula un guion largo y una pregunta respondida en la misma frase), la función devuelve &lt;code&gt;227.3&lt;/code&gt;. Es un juguete de diez líneas, no el detector real del autor, pero muestra el principio: contar patrones, no juzgar el tema.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ejemplos prácticos: antes y después
&lt;/h2&gt;

&lt;p&gt;El autor probó el modelo con 40 prompts que nunca vio en entrenamiento, repartidos en diez temas y cuatro géneros: reseña de producto, publicación personal, pieza de opinión y correo. En el modelo original, una pieza de opinión sobre el nuevo CRM de una empresa (ver &lt;a href="https://100toolkit.com/blog/deslop-model/" rel="noopener noreferrer"&gt;el detalle en la publicación original&lt;/a&gt;) nunca tomaba una postura: leía como un reporte de incidente, lleno de guiones largos y tríos de beneficios.&lt;/p&gt;

&lt;p&gt;Una versión intermedia del modelo, entrenada antes de ajustar bien los filtros, se fue al otro extremo: bajó el puntaje de muletillas a 9,9, pero empezó a inventar datos. Citó un estudio de una universidad que no existe, y encadenó casi todas las oraciones con ", which" (el equivalente sería encadenar todo con ", lo cual" o ", así que"), 28 veces más seguido que el modelo original. Ese hábito nuevo venía del propio prompt que el autor le dio al profesor, que pedía unir oraciones "con porque, así que, pero, lo cual", y dos rondas de imitación lo habían amplificado sin querer.&lt;/p&gt;

&lt;p&gt;El filtro de ajuste a la tarea que añadió la tercera ronda no es un detector de estilo, sino un conjunto de reglas específicas por género. Un correo tiene que abrir con un saludo. Una reflexión personal tiene que estar en primera persona. Una pieza de opinión tiene que tomar postura temprano. Y la longitud tiene que acercarse a lo que pidió el prompt. Esas reglas, sumadas a contar las uniones con ", lo cual" y los estudios inventados, fueron las que detectaron el problema de la versión intermedia.&lt;/p&gt;

&lt;p&gt;La versión final, entrenada con ese filtro adicional, subió apenas de 9,9 a 12,5 en el puntaje de muletillas, pero recuperó el ajuste a la tarea: del 60% de la versión intermedia al 80% del modelo original. Las uniones con ", lo cual" bajaron de 1,4 a 0,3 por cada 100 palabras.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Ojo:&lt;/strong&gt; optimizar solo por el puntaje de muletillas, sin medir si la respuesta cumple la tarea, produce un modelo que escribe plano pero inventa datos y evita tomar postura.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Cómo empezar: correr Qwen3-4B-deslop en Ollama
&lt;/h2&gt;

&lt;p&gt;Para probarlo hace falta tener &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt; instalado. En Linux y macOS, el comando es:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://ollama.com/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En Windows, el instalador se descarga directamente desde &lt;a href="https://ollama.com/download" rel="noopener noreferrer"&gt;ollama.com/download&lt;/a&gt;. Con Ollama corriendo, el modelo cuantizado (2,5 GB) se descarga y arranca con un solo comando:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama run hf.co/erikkannike/qwen3-4b-deslop-v4-GGUF:Q4_K_M
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La primera vez, Ollama descarga el manifiesto y las capas del modelo, verifica su integridad y las guarda en disco. Cuando termina, abre un prompt interactivo donde se puede escribir directamente. Para comparar contra el modelo original, basta correr en otra terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama run qwen3:4b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y hacerle la misma pregunta a los dos. La diferencia de estilo entre ambas respuestas, el original lleno de guiones largos y tríos, el deslop más plano y directo, es el experimento que el propio autor invita a repetir.&lt;/p&gt;

&lt;p&gt;Para confirmar qué se descargó, &lt;code&gt;ollama list&lt;/code&gt; muestra el nombre del modelo, su tamaño y la fecha de modificación en una tabla de texto. Y &lt;code&gt;ollama show hf.co/erikkannike/qwen3-4b-deslop-v4-GGUF:Q4_K_M&lt;/code&gt; imprime la cuantización, el tamaño de contexto y la familia del modelo.&lt;br&gt;
Qwen3-4B-deslop pesa 2,5 GB cuantizado y corre con un comando de Ollama.&lt;/p&gt;

&lt;h2&gt;
  
  
  Casos de uso reales
&lt;/h2&gt;

&lt;p&gt;El caso de uso más directo es el control de calidad editorial: pasar un borrador generado por IA por un detector de hábitos de redacción sintética antes de publicarlo, igual que se corre un linter sobre código antes de un commit. Un medio o un canal técnico puede usarlo como filtro automático antes de que un editor humano revise el texto.&lt;/p&gt;

&lt;p&gt;También sirve como base para fine-tuning propio. El LoRA que produce qwen3-4b-deslop es chico, y el método (profesor que genera, filtros que aprueban, adaptador que aprende) se puede aplicar a cualquier estilo que se quiera reforzar o eliminar: no solo tics de escritura, también jerga corporativa, un tono de marca específico o un idioma de salida constante.&lt;/p&gt;

&lt;p&gt;Y hay un caso de uso educativo, que es el que motiva este artículo: correr el modelo original y el deslop lado a lado deja ver, en texto real y no en una tabla de benchmarks, qué efecto tiene exactamente el fine-tuning sobre el estilo de una IA.&lt;/p&gt;

&lt;h2&gt;
  
  
  Errores comunes y buenas prácticas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Medir solo el estilo&lt;/strong&gt;: un detector de muletillas no avisa si el modelo inventó un dato o evitó responder lo que se le pidió. Hace falta un chequeo de ajuste a la tarea en paralelo, no después.- &lt;strong&gt;Confiar en el prompt del profesor sin auditarlo&lt;/strong&gt;: el hábito de unir cada oración con ", lo cual" salió del propio prompt que pedía conectores variados. Revisar las respuestas del profesor antes de usarlas para entrenar evita amplificar un sesgo nuevo.- &lt;strong&gt;Usar un solo filtro de daño&lt;/strong&gt;: un quiz de conocimiento no detecta que el modelo dejó de tomar postura en una opinión. Hacen falta chequeos específicos por género de texto.- &lt;strong&gt;Tratar el puntaje como verdad absoluta&lt;/strong&gt;: un regex cuenta patrones, no calidad. Revisar manualmente una muestra en cada ronda de entrenamiento sigue siendo necesario.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Comparativa: tres formas de quitar los manierismos del modelo
&lt;/h2&gt;

&lt;p&gt;MétodoCuándo usarloVentajaLimitaciónPrompt de sistemaPruebas rápidas, sin acceso a los pesosNo requiere entrenar nadaEl efecto es parcial y se pierde si el prompt se recortaActivation steeringCuando se puede ejecutar código propio en la inferenciaBajó el puntaje a cerca de 15 sin tocar los pesosNecesita el hook corriendo siempre y desvía las predicciones en texto normalFine-tuning con LoRA (destilación)Cuando se quiere un modelo que se comporte así por defectoQueda dentro de los pesos, sin prompt ni código extra, con un tercio de la deriva del steeringRequiere generar y filtrar cientos de respuestas de entrenamiento&lt;/p&gt;

&lt;h2&gt;
  
  
  Profundizando: la dirección oculta dentro de los pesos
&lt;/h2&gt;

&lt;p&gt;Un hallazgo previo del mismo autor, en un experimento anterior sobre este modelo, encontró algo más estructural que una simple preferencia de estilo: la "voz de asistente" existe como una dirección medible dentro del espacio de activaciones del modelo. Restarla matemáticamente durante la generación cambia el estilo de la respuesta, de la misma forma en que mover un objeto en un plano de coordenadas cambia su posición sin cambiar su forma.&lt;/p&gt;

&lt;p&gt;El parche de cuatro neuronas es un caso más puntual de la misma idea: para el hábito "no es X, sino Y" específicamente, basta invertir el signo de activación de cuatro neuronas para eliminar el 70% de los casos. Pero esa solución es frágil porque está atada a esa única muletilla y no se generaliza a los guiones largos ni a los tríos.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💭 Clave:&lt;/strong&gt; que exista una dirección interna para el estilo no significa que todas las muletillas comparten esa misma dirección. Cada hábito puede vivir en una parte distinta de la red.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Por eso la destilación resulta más robusta que localizar neuronas o direcciones. No necesita encontrar la ubicación exacta de cada hábito dentro de la red: alcanza con mostrarle al modelo, con ejemplos reales, cómo se ve el resultado que se quiere.&lt;/p&gt;

&lt;p&gt;Tu próximo paso: descargá &lt;code&gt;ollama run hf.co/erikkannike/qwen3-4b-deslop-v4-GGUF:Q4_K_M&lt;/code&gt;, hacele la misma pregunta de opinión a ese modelo y a &lt;code&gt;ollama run qwen3:4b&lt;/code&gt;, y contá a mano cuántos guiones largos aparecen en cada respuesta.&lt;/p&gt;

&lt;p&gt;📖 Resumen en Telegram: &lt;a href="https://telegra.ph/Qwen3-4B-deslop-corta-las-muletillas-de-IA-de-121-a-125-por-1000-palabras-10-19" rel="noopener noreferrer"&gt;Ver resumen&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿Qué significa que un modelo tenga muletillas de IA?
&lt;/h3&gt;

&lt;p&gt;Significa que repite, con más frecuencia de la que usaría un humano al escribir con cuidado, un conjunto reducido de recursos de estilo: guiones largos, tríos de elementos, la fórmula "no es X, sino Y" y preguntas que responde en la misma frase. Un detector automático puede contarlos y dar un puntaje por cada 1.000 palabras.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Cómo se entrena Qwen3-4B-deslop para evitar estos tics?
&lt;/h3&gt;

&lt;p&gt;Con destilación en tres rondas: un modelo profesor genera respuestas, un conjunto de filtros descarta las que tienen muletillas, inventan datos o no cumplen la tarea pedida, y un adaptador LoRA chico aprende a imitar las respuestas que quedan. El resultado se mete en los pesos del modelo, sin prompt ni código extra en tiempo de ejecución.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Funciona igual que el modelo original en preguntas factuales?
&lt;/h3&gt;

&lt;p&gt;Según el autor, responde correctamente las 30 de 30 preguntas de un quiz de conocimiento, el mismo número que el modelo original sin afinar.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Es gratis correr Qwen3-4B-deslop en Ollama?
&lt;/h3&gt;

&lt;p&gt;Sí. El modelo está publicado en Hugging Face bajo un repositorio público y la versión cuantizada pesa 2,5 GB; solo hace falta tener Ollama instalado y suficiente espacio en disco.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿El fine-tuning de este tipo sirve para modelos más grandes?
&lt;/h3&gt;

&lt;p&gt;El autor entrenó este experimento en una MacBook Air, con un modelo de 4.000 millones de parámetros y 161 respuestas de entrenamiento. La receta no depende del tamaño del modelo, aunque probarla en modelos más grandes queda como trabajo pendiente.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Un detector automático puede distinguir buena de mala escritura?
&lt;/h3&gt;

&lt;p&gt;No por sí solo. Un detector basado en reglas cuenta patrones, no juzga sentido ni coherencia, y por eso el autor complementó cada ronda de entrenamiento con una revisión manual de respuestas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://100toolkit.com/blog/deslop-model/" rel="noopener noreferrer"&gt;100toolkit.com&lt;/a&gt;: publicación original con la metodología completa, los puntajes de cada ronda y los enlaces a los pesos del modelo.- &lt;a href="https://huggingface.co/erikkannike/qwen3-4b-deslop-v4-GGUF" rel="noopener noreferrer"&gt;Hugging Face&lt;/a&gt;: repositorio con los pesos cuantizados de Qwen3-4B-deslop en formato GGUF.- &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;: documentación oficial para instalar y correr modelos de lenguaje en local.- &lt;a href="https://en.wikipedia.org/wiki/Reinforcement_learning_from_human_feedback" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;: explicación general del aprendizaje por refuerzo con retroalimentación humana (RLHF).- &lt;a href="https://github.com/QwenLM/Qwen3" rel="noopener noreferrer"&gt;GitHub, QwenLM/Qwen3&lt;/a&gt;: repositorio oficial de la familia de modelos Qwen3 sobre la que se construyó este fine-tuning.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📱 &lt;strong&gt;¿Te gusta este contenido?&lt;/strong&gt; Únete a nuestro canal de Telegram &lt;a href="https://t.me/programacion" rel="noopener noreferrer"&gt;@programacion&lt;/a&gt; donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Superlite: delegar un spec sin aprobar cada paso</title>
      <dc:creator>lu1tr0n</dc:creator>
      <pubDate>Mon, 19 Oct 2026 08:47:08 +0000</pubDate>
      <link>https://dev.to/lu1tr0n/superlite-delegar-un-spec-sin-aprobar-cada-paso-1eah</link>
      <guid>https://dev.to/lu1tr0n/superlite-delegar-un-spec-sin-aprobar-cada-paso-1eah</guid>
      <description>&lt;p&gt;Claude Code se detiene a pedir aprobación cada pocos minutos, el costo habitual de los agentes de IA autónomos cuando el flujo de trabajo prioriza la supervisión sobre la velocidad. Lo resume bien el post &lt;a href="https://contextfirst.blog/superlite-hand-an-agent-a-spec/" rel="noopener noreferrer"&gt;"Superlite: hand an agent a spec, come back to tested work"&lt;/a&gt;, publicado en contextfirst.blog: su autor pidió un spec, se hizo un sándwich y volvió a encontrar al agente detenido en la sección 1, esperando un "sí" que llevaba veinte minutos ahí.&lt;/p&gt;

&lt;p&gt;Este artículo explica por qué pasa eso, qué mecanismos (permisos, hooks y revisión automática de tareas) permiten delegar un spec completo sin supervisión constante, y cómo Superlite, el plugin de Claude Code que nació de esa frustración, los combina en la práctica.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Los agentes se frenan porque cada herramienta sensible pide confirmación sin una regla de permisos previa.- Los hooks PreToolUse y PostToolUse interceptan cada acción y la aprueban o bloquean con código, sin preguntarte.- Superlite cambia 8 a 12 aprobaciones por feature por una sola, y mueve el resto de las reglas al código.- Un plan dividido en tareas sin archivos compartidos deja correr varios agentes a la vez en lugar de uno por uno.- La revisión automática por código reemplaza la aprobación humana, salvo en cambios destructivos o de seguridad.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ¿Qué son los agentes de IA autónomos?
&lt;/h2&gt;

&lt;p&gt;Los agentes de IA autónomos son programas que ejecutan tareas de desarrollo (planificar, escribir código, correr pruebas, corregir errores) encadenando llamadas a un modelo de lenguaje y herramientas del sistema, sin que un humano confirme cada paso intermedio: solo intervienen las decisiones destructivas, irreversibles o de seguridad.&lt;/p&gt;

&lt;p&gt;Herramientas como Claude Code, Cursor o el modo agente de GitHub Copilot funcionan así por defecto. Reciben una instrucción, la descomponen en pasos y ejecutan cada herramienta (leer un archivo, correr un comando, editar código) a través de un bucle de planificar, actuar y observar el resultado.&lt;/p&gt;

&lt;p&gt;Por diseño, la mayoría de esos agentes es conservadora. Antes de correr algo irreversible (borrar un archivo, hacer push, instalar una dependencia), se detienen y piden confirmación humana. Esa cautela evita desastres, pero también multiplica las pausas cuando el usuario ya sabe qué quiere construir.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué los agentes de IA autónomos se detienen a cada paso
&lt;/h2&gt;

&lt;p&gt;El post de contextfirst.blog profiló su propio flujo de trabajo (basado en el plugin "superpowers" para Claude Code) y encontró cinco cuellos de botella. El primero: &lt;a href="https://contextfirst.blog/superlite-hand-an-agent-a-spec/" rel="noopener noreferrer"&gt;entre 8 y 12 gates de aprobación humana por feature&lt;/a&gt;, uno por cada sección del diseño, más revisiones separadas del spec, del plan y del método de ejecución.&lt;/p&gt;

&lt;p&gt;El segundo es la ejecución estrictamente secuencial: un implementador, después un revisor, después la siguiente tarea. En el primer benchmark del autor eso se tradujo en una &lt;a href="https://contextfirst.blog/superlite-hand-an-agent-a-spec/" rel="noopener noreferrer"&gt;cola de 21 despachos en un solo archivo&lt;/a&gt;, como un banco con una sola ventanilla y un almuerzo muy largo.&lt;/p&gt;

&lt;p&gt;El tercero es la revisión después de cada tarea, con reintentos cuando la revisión encuentra algo: en un build de 34 minutos, &lt;a href="https://contextfirst.blog/superlite-hand-an-agent-a-spec/" rel="noopener noreferrer"&gt;12 minutos se fueron solo en esas revisiones&lt;/a&gt;. El cuarto es correr la batería completa de tests varias veces, una por cada skill que quiere asegurarse de que nada cambió. El quinto es usar el modelo más potente (Opus) en el camino crítico: según el post, &lt;a href="https://contextfirst.blog/superlite-hand-an-agent-a-spec/" rel="noopener noreferrer"&gt;escribe código a la mitad de velocidad que Sonnet&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ninguno de estos frenos es un error de diseño. Cada uno es una red de seguridad razonable cuando el objetivo es que un humano revise cada decisión. El problema aparece cuando el usuario ya sabe exactamente qué quiere construir: ahí la misma red de seguridad se convierte en una reunión de cuatro horas que se programó sola.&lt;br&gt;
Un build de 34 minutos perdió 12 en revisiones repetidas, según contextfirst.blog.&lt;/p&gt;
&lt;h2&gt;
  
  
  Cómo funciona el modelo de permisos en Claude Code
&lt;/h2&gt;

&lt;p&gt;Claude Code resuelve la primera capa de control con un sistema de permisos declarado en &lt;code&gt;settings.json&lt;/code&gt;. Cada acción (correr un comando de shell, editar un archivo, llamar una herramienta MCP) se evalúa contra tres listas: &lt;strong&gt;allow&lt;/strong&gt; (corre sin preguntar), &lt;strong&gt;ask&lt;/strong&gt; (pide confirmación interactiva) y &lt;strong&gt;deny&lt;/strong&gt; (nunca corre, ni siquiera se pregunta).&lt;/p&gt;

&lt;p&gt;Las reglas usan patrones por herramienta y argumento, no solo por nombre de comando. &lt;code&gt;Bash(git commit:*)&lt;/code&gt; autoriza cualquier commit, pero no un push; &lt;code&gt;Bash(rm -rf:*)&lt;/code&gt; se puede poner directamente en deny para que ningún agente, por más que lo pida con buenos modales, borre un directorio completo.&lt;/p&gt;

&lt;p&gt;El otro eje es el modo de permisos de la sesión: el modo por defecto pregunta ante cualquier acción no cubierta por una regla; &lt;strong&gt;acceptEdits&lt;/strong&gt; aprueba ediciones de archivo automáticamente pero sigue preguntando por comandos de shell; &lt;strong&gt;plan&lt;/strong&gt; no ejecuta nada hasta que el usuario aprueba el plan completo; y &lt;strong&gt;bypassPermissions&lt;/strong&gt; corre todo salvo lo que esté explícitamente en deny. Superlite y proyectos similares dependen de este último modo combinado con reglas de deny bien pensadas, no de confiar en que el agente se porte bien.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dot"&gt;&lt;code&gt;&lt;span class="nv"&gt;flowchart&lt;/span&gt; &lt;span class="nv"&gt;TD&lt;/span&gt;
&lt;span class="nv"&gt;T&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Agente llama una herramienta"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;C&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"Regla en permissions"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;C&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;|&lt;/span&gt;&lt;span class="s2"&gt;"allow"&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;E&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Ejecuta sin preguntar"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;C&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;|&lt;/span&gt;&lt;span class="s2"&gt;"ask"&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;H&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Hook o prompt interactivo"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;C&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;|&lt;/span&gt;&lt;span class="s2"&gt;"deny"&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;D&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Bloqueada, nunca corre"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;H&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;F&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"Hook decide"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;F&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;|&lt;/span&gt;&lt;span class="s2"&gt;"approve"&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;E&lt;/span&gt;
&lt;span class="nv"&gt;F&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;|&lt;/span&gt;&lt;span class="s2"&gt;"block"&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;D&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Hooks: la otra capa de control
&lt;/h2&gt;

&lt;p&gt;Los permisos deciden si una acción corre; los hooks deciden qué pasa antes y después de que corra, con código real en vez de una regla declarativa. Claude Code dispara eventos como &lt;code&gt;PreToolUse&lt;/code&gt; (antes de ejecutar una herramienta), &lt;code&gt;PostToolUse&lt;/code&gt; (después) y &lt;code&gt;SessionStart&lt;/code&gt; (al abrir la sesión), y cada uno puede atarse a un script que recibe contexto en JSON por stdin y devuelve una decisión.&lt;/p&gt;

&lt;p&gt;Un hook de &lt;code&gt;PreToolUse&lt;/code&gt; atado al matcher &lt;code&gt;Bash&lt;/code&gt; ve el comando exacto que el agente está a punto de correr antes de que corra. Si el comando intenta un &lt;code&gt;git push&lt;/code&gt; a &lt;code&gt;main&lt;/code&gt;, el hook puede devolver un bloqueo con una razón, sin que el modelo tenga que entender ni respetar una instrucción en texto plano.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nv"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$input&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.tool_input.command // empty'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$cmd&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="s2"&gt;"push"&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$cmd&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'{"decision":"block","reason":"push a main requiere revision humana"}'&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="k"&gt;fi
&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'{"decision":"approve"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si el agente intenta &lt;code&gt;git push origin main&lt;/code&gt;, el hook imprime &lt;code&gt;{"decision":"block","reason":"push a main requiere revision humana"}&lt;/code&gt; y Claude Code cancela el comando sin ejecutarlo; para cualquier otro comando, imprime &lt;code&gt;{"decision":"approve"}&lt;/code&gt; y la ejecución sigue normal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sequenceDiagram
participant Agente
participant Hook as Hook PreToolUse
participant Repo as Repositorio
Agente-&amp;gt;&amp;gt;Hook: intenta "git push origin main"
Hook--&amp;gt;&amp;gt;Agente: decision block, razon
Agente-&amp;gt;&amp;gt;Repo: push cancelado
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💭 Clave:&lt;/strong&gt; &lt;a href="https://contextfirst.blog/superlite-hand-an-agent-a-spec/" rel="noopener noreferrer"&gt;El post original&lt;/a&gt; lo resume de forma directa: un agente lee una regla escrita en texto y después hace lo que quiere. Por eso las reglas que importan van en código (permisos y hooks), no en una instrucción de párrafo dentro del spec.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Superlite: un caso concreto de agente autónomo de código
&lt;/h2&gt;

&lt;p&gt;Superlite es el plugin de Claude Code que contextfirst.blog construyó para resolver justamente esos cinco cuellos de botella. En vez de pedir aprobación por sección, agrupa las preguntas en un solo lote de hasta cuatro, según el tamaño del pedido: un typo se corrige directo, sin documento de diseño; un cambio a un flujo existente recibe un diseño corto en el chat; un proyecto nuevo recibe un documento único de diseño y plan que el usuario aprueba una sola vez.&lt;/p&gt;

&lt;p&gt;Después de esa aprobación única, Superlite solo vuelve a preguntar ante algo destructivo, visible hacia afuera o sensible en seguridad: no va a hacer push a &lt;code&gt;main&lt;/code&gt; mientras el usuario está almorzando, pero sí va a hacer todo lo demás sin interrupción.&lt;/p&gt;

&lt;p&gt;La pieza central es el plan: en vez de una lista de pasos, Superlite genera un grafo de tareas. Cada tarea declara qué archivos posee, qué interfaces consume y produce, y qué tests tiene que pasar. Las tareas que no comparten archivos ni dependen entre sí corren al mismo tiempo; el ancho del grafo decide cuántos agentes trabajan en paralelo en un momento dado.&lt;/p&gt;

&lt;p&gt;Cada decisión que normalmente generaría una pregunta queda registrada como una resolución (&lt;em&gt;ruling&lt;/em&gt;): qué se decidió, por qué y qué pasa si estuvo mal. Eso es lo que permite que los agentes de IA autónomos avancen sin supervisión constante sin perder trazabilidad: el usuario vuelve al final y sabe exactamente qué decisión revisar primero, en vez de reconstruir el razonamiento desde cero.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dot"&gt;&lt;code&gt;&lt;span class="nv"&gt;flowchart&lt;/span&gt; &lt;span class="nv"&gt;TD&lt;/span&gt;
&lt;span class="k"&gt;subgraph&lt;/span&gt; &lt;span class="nv"&gt;Secuencial&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Un agente, un paso a la vez"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;A1&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Tarea 1"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;P1&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Aprobacion humana"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;P1&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;A2&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Tarea 2"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;A2&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;P2&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Aprobacion humana"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;P2&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;A3&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Tarea 3"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;subgraph&lt;/span&gt; &lt;span class="nv"&gt;Paralelo&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Varios agentes a la vez"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;B1&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Tarea 1"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;R&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Revision automatica"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;B2&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Tarea 2"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;R&lt;/span&gt;
&lt;span class="nv"&gt;B3&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Tarea 3"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;R&lt;/span&gt;
&lt;span class="nv"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El grafo de tareas de Superlite decide cuántos agentes corren a la vez, según contextfirst.blog.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cómo empezar: configurar permisos y hooks en Claude Code
&lt;/h2&gt;

&lt;p&gt;Dependencias antes de empezar: Claude Code instalado, un proyecto con carpeta &lt;code&gt;.claude/&lt;/code&gt; en la raíz, y &lt;code&gt;jq&lt;/code&gt; disponible para parsear el JSON dentro del hook (en Linux y macOS casi siempre ya está o se instala con el gestor de paquetes del sistema; en Windows, sin WSL, el mismo hook se escribe en PowerShell con &lt;code&gt;ConvertFrom-Json&lt;/code&gt; en una línea).&lt;/p&gt;

&lt;p&gt;Primero, crear o editar &lt;code&gt;.claude/settings.json&lt;/code&gt; en la raíz del proyecto con reglas de permisos:&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="nl"&gt;"permissions"&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="nl"&gt;"allow"&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="s2"&gt;"Bash(npm test:*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash(git commit:*)"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ask"&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="s2"&gt;"Bash(git push:*)"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deny"&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="s2"&gt;"Bash(rm -rf:*)"&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="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con esto, Claude Code corre tests y commits sin preguntar, pide confirmación antes de cualquier push y nunca va a ejecutar un &lt;code&gt;rm -rf&lt;/code&gt;, sin importar cómo se lo pidan.&lt;/p&gt;

&lt;p&gt;Segundo, crear el hook en &lt;code&gt;.claude/hooks/guard-push.sh&lt;/code&gt; con el script mostrado en la sección anterior, y darle permiso de ejecución con &lt;code&gt;chmod +x .claude/hooks/guard-push.sh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Tercero, registrar el hook en el mismo &lt;code&gt;settings.json&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&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="nl"&gt;"PreToolUse"&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="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".claude/hooks/guard-push.sh"&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="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="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para confirmar que la regla de permisos está activa, corré &lt;code&gt;/permissions&lt;/code&gt; dentro de una sesión de Claude Code: lista cada patrón (allow, ask o deny) junto con la herramienta a la que aplica. Para el hook, el chequeo real es pedirle al agente que intente &lt;code&gt;git push origin main&lt;/code&gt;: si la configuración está bien, el comando se cancela y aparece la razón &lt;code&gt;push a main requiere revision humana&lt;/code&gt; en la salida, en vez de ejecutarse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Casos de uso reales
&lt;/h2&gt;

&lt;p&gt;Una feature nueva y acotada (un endpoint, un formulario, una migración de datos) es el caso de uso más directo: el usuario aprueba un plan una sola vez y vuelve horas después a código probado, en vez de haber aprobado veinte decisiones chicas una por una.&lt;/p&gt;

&lt;p&gt;Un refactor grande que toca muchos archivos se beneficia del grafo de tareas: si el refactor se puede partir en módulos que no se pisan entre sí (por ejemplo, migrar cada ruta de una API de un framework a otro), varios agentes trabajan en paralelo en vez de uno detrás del otro.&lt;/p&gt;

&lt;p&gt;Una corrección de bug puntual sigue el camino corto: Superlite detecta que es un cambio chico y lo corrige directo, sin generar un documento de diseño para un typo. El tamaño de la respuesta se ajusta al tamaño del pedido, no al revés.&lt;/p&gt;

&lt;p&gt;El caso donde este enfoque no conviene es el opuesto: cuando el usuario todavía no sabe bien qué quiere construir y necesita iterar el diseño en vivo, sección por sección. Ahí el flujo colaborativo (el de Superpowers, con un humano cerca de cada paso) sigue siendo la herramienta correcta.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparativa con alternativas
&lt;/h2&gt;

&lt;p&gt;Ninguno de los tres enfoques siguientes es universalmente mejor: la decisión depende de si el usuario ya tiene el diseño resuelto en la cabeza o todavía lo está construyendo junto con el agente.&lt;br&gt;
OpciónCuándo usarlaVentajaLimitaciónAprobación manual en cada pasoDiseño todavía incierto, se decide sobre la marchaControl total, cero sorpresas8 a 12 pausas por feature, no escala a tareas grandesSuperpowers (flujo colaborativo)El usuario quiere revisar spec, plan y ejecución por separadoMáxima trazabilidad y seguridadEjecución secuencial, lenta para specs ya cerradosSuperlite (spec a ejecución autónoma)El spec ya está definido y solo falta construirloUna aprobación, tareas en paralelo, registro de decisionesExige reglas de permisos y hooks bien escritas de antemano&lt;/p&gt;

&lt;h2&gt;
  
  
  Errores comunes y buenas prácticas
&lt;/h2&gt;

&lt;p&gt;El error más frecuente es activar &lt;code&gt;bypassPermissions&lt;/code&gt; sin escribir reglas de &lt;code&gt;deny&lt;/code&gt;. El modo asume que todo lo que no está prohibido está permitido, así que un agente con ese modo y una lista de deny vacía puede ejecutar cualquier comando, incluido uno destructivo.&lt;/p&gt;

&lt;p&gt;El segundo es confundir una instrucción en el spec ("nunca borres archivos de producción") con una regla real. Esa frase vive en el contexto que el modelo lee. Un cambio de contexto, una instrucción contradictoria más adelante o simplemente una mala interpretación puede hacer que el agente la pase por alto. La regla necesita vivir en código: en &lt;code&gt;settings.json&lt;/code&gt; o en un hook, donde se evalúa siempre, sin depender de que el modelo la recuerde.&lt;/p&gt;

&lt;p&gt;El tercero, documentado en el propio post de Superlite, es correr Superpowers y Superlite en la misma sesión: ambos inyectan contexto al iniciar la sesión y comparten nombres de skill, así que terminan contradiciéndose. Se elige uno por sesión, no los dos.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Ojo:&lt;/strong&gt; un hook que siempre responde &lt;code&gt;{"decision":"approve"}&lt;/code&gt; sin leer el comando no protege nada. Es una ilusión de control. El hook tiene que inspeccionar el &lt;code&gt;tool_input&lt;/code&gt; real antes de decidir.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;La práctica que mejor funciona es separar las reglas en dos capas: permisos para lo previsible (qué comandos son siempre seguros o siempre peligrosos) y hooks para lo que depende del contenido exacto del comando (a qué rama se hace push, qué archivo se borra). Las dos capas se prueban por separado, antes de confiar en ellas con un spec grande.&lt;/p&gt;

&lt;h2&gt;
  
  
  Profundizando: el grafo de tareas y el registro de decisiones
&lt;/h2&gt;

&lt;p&gt;Por dentro, el planificador de Superlite no reparte tareas al azar: construye un grafo dirigido donde cada nodo es una tarea con tres propiedades explícitas: los archivos que posee, las interfaces que consume de otras tareas y los tests que tiene que pasar para considerarse terminada. Dos tareas sin superposición de archivos ni dependencias de interfaz quedan en la misma capa del grafo y se disparan juntas.&lt;/p&gt;

&lt;p&gt;Esto es distinto de simplemente decirle a varios agentes que trabajen en paralelo sin más: sin la restricción de archivos, dos agentes que tocan el mismo módulo generan conflictos de merge que nadie revisó. El grafo evita el conflicto antes de que ocurra, no lo arregla después.&lt;/p&gt;

&lt;p&gt;La revisión automática funciona igual para cada tarea terminada: un segundo agente (no necesariamente el mismo modelo) corre los tests declarados y compara el resultado contra la interfaz prometida. Si falla, se genera una tarea de corrección que vuelve a pasar por el mismo chequeo, sin despertar al usuario, salvo que el chequeo falle repetidamente.&lt;/p&gt;

&lt;p&gt;Y cuando el proceso sí necesita al usuario, no lo hace con una pregunta suelta: le entrega la lista completa de resoluciones tomadas durante la ejecución, cada una con su justificación y su costo si resultó ser la decisión equivocada. Así es como los agentes de IA autónomos logran avanzar horas sin intervención y, al mismo tiempo, dejan un rastro auditable.&lt;/p&gt;

&lt;p&gt;📖 Resumen en Telegram: &lt;a href="https://telegra.ph/Superlite-delegar-un-spec-sin-aprobar-cada-paso-10-19" rel="noopener noreferrer"&gt;Ver resumen&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tu próximo paso: cloná un repo de prueba, agregá el hook &lt;code&gt;guard-push.sh&lt;/code&gt; de este artículo a &lt;code&gt;.claude/hooks/&lt;/code&gt; y pedile a Claude Code que intente un &lt;code&gt;git push origin main&lt;/code&gt; para ver el bloqueo en acción.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿Superlite reemplaza a Superpowers?
&lt;/h3&gt;

&lt;p&gt;No. Superpowers prioriza mantener a un humano cerca de cada paso y Superlite prioriza la velocidad cuando el spec ya está cerrado. Comparten base de código, pero contextfirst.blog recomienda no activar los dos en la misma sesión porque sus skills se pisan.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Los hooks pueden bloquear cualquier acción de un agente autónomo de código?
&lt;/h3&gt;

&lt;p&gt;Sí, cualquier acción que pase por un evento con hook registrado, como &lt;code&gt;PreToolUse&lt;/code&gt; para herramientas o &lt;code&gt;SessionStart&lt;/code&gt; al abrir la sesión. Lo que el hook no intercepta (un evento sin matcher configurado) sigue las reglas normales de permisos.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué pasa si el agente intenta hacer push a main con la configuración del artículo?
&lt;/h3&gt;

&lt;p&gt;El hook &lt;code&gt;guard-push.sh&lt;/code&gt; detecta "push" y "main" en el comando, devuelve &lt;code&gt;{"decision":"block"}&lt;/code&gt; con la razón, y Claude Code cancela el comando sin ejecutarlo en el repositorio.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Cuántas aprobaciones humanas necesita un build con Superlite?
&lt;/h3&gt;

&lt;p&gt;Una, al aprobar el diseño y el plan. Después de eso, según el post original, solo vuelve a preguntar ante acciones destructivas, visibles hacia afuera o sensibles en seguridad.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Los permisos de Claude Code se pueden combinar con hooks?
&lt;/h3&gt;

&lt;p&gt;Sí, y es la combinación recomendada: los permisos cubren reglas previsibles por patrón de comando, y los hooks cubren decisiones que dependen del contenido exacto de ese comando, como a qué rama apunta un push.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿La IA agéntica autónoma funciona con modelos distintos a Claude?
&lt;/h3&gt;

&lt;p&gt;El mecanismo (permisos declarativos más hooks en código) es específico de Claude Code. Otros entornos agénticos resuelven el mismo problema con sus propios sistemas de políticas, pero el principio de reglas en código, no en texto del prompt, aplica igual.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://contextfirst.blog/superlite-hand-an-agent-a-spec/" rel="noopener noreferrer"&gt;Contextfirst.blog&lt;/a&gt;: el post original donde se presenta Superlite, sus benchmarks y el diseño del plugin.- &lt;a href="https://github.com/anthropics/claude-code" rel="noopener noreferrer"&gt;GitHub: anthropics/claude-code&lt;/a&gt;: repositorio oficial de Claude Code, con la documentación de hooks y permisos.- &lt;a href="https://docs.claude.com" rel="noopener noreferrer"&gt;Claude Docs&lt;/a&gt;: documentación oficial de Anthropic sobre Claude Code y su configuración.- &lt;a href="https://en.wikipedia.org/wiki/Software_agent" rel="noopener noreferrer"&gt;Wikipedia: Software agent&lt;/a&gt;: definición general del concepto de agente de software.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📱 &lt;strong&gt;¿Te gusta este contenido?&lt;/strong&gt; Únete a nuestro canal de Telegram &lt;a href="https://t.me/programacion" rel="noopener noreferrer"&gt;@programacion&lt;/a&gt; donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.&lt;/p&gt;

</description>
      <category>technology</category>
      <category>science</category>
      <category>programming</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Razonamiento latente: la IA que piensa en vectores</title>
      <dc:creator>lu1tr0n</dc:creator>
      <pubDate>Mon, 19 Oct 2026 02:31:31 +0000</pubDate>
      <link>https://dev.to/lu1tr0n/razonamiento-latente-la-ia-que-piensa-en-vectores-431a</link>
      <guid>https://dev.to/lu1tr0n/razonamiento-latente-la-ia-que-piensa-en-vectores-431a</guid>
      <description>&lt;p&gt;Un agente de IA que resuelve un problema matemático puede escribir miles de palabras de razonamiento en texto antes de dar la respuesta final, palabra por palabra, como si pensara en voz alta. Un paper publicado el 13 de octubre de 2026 por investigadores de Stanford y Google propone que, en vez de eso, el agente practique razonamiento latente: que piense en vectores matemáticos continuos sin pasar por el lenguaje humano en cada paso intermedio.&lt;/p&gt;

&lt;p&gt;El paper se llama &lt;a href="https://arxiv.org/abs/2610.18861" rel="noopener noreferrer"&gt;Hybrid Reasoning Agents&lt;/a&gt; y presenta una arquitectura concreta llamada HybridAgents, pero la idea de fondo es mucho más amplia que esa implementación puntual. Es un cambio en cómo se diseña la mente interna de un agente de IA.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;El razonamiento latente encadena vectores continuos en vez de palabras entre los pasos de pensamiento de un agente.- HybridAgents, el modelo que lo combina con texto, mejora 10,4% el desempeño frente a agentes solo-texto o solo-latentes.- Esa combinación recorta entre 35,5% y 54,7% los tokens generados por tarea, según el paper de Stanford de octubre de 2026.- La inferencia corre de 1,8x a 2,6x más rápido y el costo baja hasta 3,7 veces en los 11 benchmarks evaluados.- Inspeccionar el pensamiento se complica: un vector no se lee palabra por palabra como un razonamiento en texto.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ¿Qué es el razonamiento latente?
&lt;/h2&gt;

&lt;p&gt;El razonamiento latente es la técnica que permite a un modelo de lenguaje procesar los pasos intermedios de su pensamiento como vectores continuos en su espacio interno, en vez de convertir cada paso en palabras legibles antes de seguir pensando.&lt;/p&gt;

&lt;p&gt;La diferencia con una cadena de pensamiento tradicional es fácil de explicar con una analogía. Cuando una persona resuelve un problema mentalmente, no narra cada subpaso en voz alta: piensa en intuiciones y asociaciones difusas, y solo las pone en palabras cuando necesita explicarlas a otra persona. El razonamiento basado en texto obliga al modelo a narrar cada subpaso, aunque nadie lo necesite todavía.&lt;/p&gt;

&lt;p&gt;El pensamiento vectorial, en cambio, deja que el modelo mantenga esas asociaciones en su forma nativa, números de alta dimensión, sin pasar por el cuello de botella del vocabulario. Esto no es exclusivo de un paper: distintos equipos de investigación vienen explorando variantes de esta idea desde hace un par de años, bajo nombres como \"cadena de pensamiento continua\".&lt;br&gt;
HybridAgents se evaluó en 11 benchmarks y 3 escalas de modelo.&lt;/p&gt;
&lt;h2&gt;
  
  
  Por qué importa el razonamiento latente
&lt;/h2&gt;

&lt;p&gt;Todo modelo que razona en texto tiene que traducir cada paso intermedio de pensamiento a palabras, aunque esas palabras nunca las lea un humano. Esa traducción cuesta cómputo: generar un token de texto implica pasar por toda la capa de salida del modelo, el vocabulario completo, aunque el contenido real del pensamiento viva en un espacio vectorial mucho más rico que cualquier palabra individual.&lt;/p&gt;

&lt;p&gt;El paper &lt;a href="https://arxiv.org/abs/2610.18861" rel="noopener noreferrer"&gt;Hybrid Reasoning Agents&lt;/a&gt; mide ese costo de forma directa. Un agente que alterna entre texto y vectores logra hasta &lt;a href="https://arxiv.org/abs/2610.18861" rel="noopener noreferrer"&gt;54,7% de reducción de tokens generados&lt;/a&gt; frente a agentes que razonan solo en texto, y corre &lt;a href="https://arxiv.org/abs/2610.18861" rel="noopener noreferrer"&gt;de 1,8x a 2,6x más rápido&lt;/a&gt;. Eso se traduce en un ahorro de costo de &lt;a href="https://arxiv.org/abs/2610.18861" rel="noopener noreferrer"&gt;2,7x a 3,7x&lt;/a&gt; según el tamaño del modelo evaluado.&lt;/p&gt;

&lt;p&gt;Pero el ahorro no es gratis. Un agente que piense solo en vectores, sin pasar nunca por texto, pierde la capacidad de llamar herramientas externas y de mostrar su razonamiento a un humano. Por eso el diseño híbrido importa más que la pura eficiencia: busca quedarse con lo mejor de los dos mundos sin sacrificar ninguno por completo.&lt;/p&gt;
&lt;h2&gt;
  
  
  Cómo funciona el modo latente dentro de un agente
&lt;/h2&gt;

&lt;p&gt;Para entender cómo funciona el razonamiento latente en la práctica, conviene mirar el mecanismo que usa HybridAgents para decidir, en cada paso de la generación, si conviene seguir pensando en vectores o volver a texto.&lt;/p&gt;

&lt;p&gt;La diferencia técnica central es esta: un modelo de lenguaje estándar solo puede pensar emitiendo tokens discretos de un vocabulario fijo, uno a la vez. Un agente con modo latente puede, en cambio, emitir directamente el vector de activaciones que normalmente alimentaría la siguiente capa, sin forzarlo primero a colapsar en la palabra más probable del vocabulario. Esa diferencia parece pequeña, pero evita una pérdida de información que se acumula paso a paso a lo largo de un razonamiento largo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dot"&gt;&lt;code&gt;&lt;span class="nv"&gt;flowchart&lt;/span&gt; &lt;span class="nv"&gt;TD&lt;/span&gt;
    &lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Entrada del usuario"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;B&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"Modo de razonamiento"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nv"&gt;B&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;|&lt;/span&gt;&lt;span class="s2"&gt;"Texto"&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;C&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Cadena de pensamiento en palabras"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="nv"&gt;B&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;|&lt;/span&gt;&lt;span class="s2"&gt;"Latente"&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;D&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Vector continuo via HybridLink"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="nv"&gt;C&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;E&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Memoria de trabajo"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="nv"&gt;D&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;E&lt;/span&gt;
    &lt;span class="nv"&gt;E&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;F&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Accion: respuesta o llamada a herramienta"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  HybridLink: el puente entre dos modos de pensar
&lt;/h3&gt;

&lt;p&gt;El paper describe un módulo liviano llamado &lt;a href="https://arxiv.org/abs/2610.18861" rel="noopener noreferrer"&gt;HybridLink&lt;/a&gt; que habilita transiciones fluidas entre modo texto y modo latente dentro de un mismo proceso de generación. En la práctica, es la pieza que decide, token por token, si lo que sigue debe representarse como una palabra del vocabulario o como un vector sin pasar por esa capa de salida.&lt;/p&gt;

&lt;p&gt;Que HybridLink sea liviano importa por una razón práctica: no hace falta reentrenar el modelo entero desde cero para agregarle esta capacidad. El backbone conserva casi todo su conocimiento previo, y HybridLink se encarga solamente de la transición entre modos, lo que simplifica tanto el entrenamiento como el mantenimiento posterior del sistema.&lt;/p&gt;

&lt;h3&gt;
  
  
  Memoria de trabajo continua: no perder el hilo
&lt;/h3&gt;

&lt;p&gt;El segundo componente es una memoria de trabajo continua que preserva el contexto híbrido, texto y vectores mezclados, sin pérdida a lo largo de varios turnos de conversación o de uso de herramientas. Sin esta pieza, cada cambio de modo implicaría reconstruir el contexto desde cero, perdiendo precisión en el camino.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dot"&gt;&lt;code&gt;&lt;span class="nv"&gt;sequenceDiagram&lt;/span&gt;
    &lt;span class="nv"&gt;participant&lt;/span&gt; &lt;span class="nv"&gt;U&lt;/span&gt; &lt;span class="nv"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;Usuario&lt;/span&gt;
    &lt;span class="nv"&gt;participant&lt;/span&gt; &lt;span class="nv"&gt;A&lt;/span&gt; &lt;span class="nv"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;Agente&lt;/span&gt;
    &lt;span class="nv"&gt;participant&lt;/span&gt; &lt;span class="nv"&gt;H&lt;/span&gt; &lt;span class="nv"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;Herramienta&lt;/span&gt;
    &lt;span class="nv"&gt;U&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;envia&lt;/span&gt; &lt;span class="nv"&gt;la&lt;/span&gt; &lt;span class="nv"&gt;pregunta&lt;/span&gt;
    &lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;piensa&lt;/span&gt; &lt;span class="nv"&gt;en&lt;/span&gt; &lt;span class="nv"&gt;vectores&lt;/span&gt; &lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;modo&lt;/span&gt; &lt;span class="nv"&gt;latente&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
    &lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;H&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;llama&lt;/span&gt; &lt;span class="nv"&gt;a&lt;/span&gt; &lt;span class="nv"&gt;una&lt;/span&gt; &lt;span class="nv"&gt;herramienta&lt;/span&gt; &lt;span class="nv"&gt;en&lt;/span&gt; &lt;span class="nv"&gt;modo&lt;/span&gt; &lt;span class="nv"&gt;texto&lt;/span&gt;
    &lt;span class="nv"&gt;H&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;devuelve&lt;/span&gt; &lt;span class="nv"&gt;el&lt;/span&gt; &lt;span class="nv"&gt;resultado&lt;/span&gt;
    &lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;U&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;responde&lt;/span&gt; &lt;span class="nv"&gt;en&lt;/span&gt; &lt;span class="nv"&gt;lenguaje&lt;/span&gt; &lt;span class="nv"&gt;natural&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;HybridSFT entrena la coordinacion; HybridRL decide el modo de pensar.&lt;/p&gt;

&lt;h2&gt;
  
  
  El caso de estudio: cómo Stanford entrenó a un agente para elegir su propio modo de pensar
&lt;/h2&gt;

&lt;p&gt;Enseñarle a un modelo a usar HybridLink no es automático. El paper divide el entrenamiento en dos etapas con nombres propios.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HybridSFT&lt;/strong&gt;: estabiliza la generación híbrida con herramientas integradas, enseñándole al backbone a coordinarse con HybridLink antes de que el modelo tenga que decidir nada por su cuenta.- &lt;strong&gt;HybridRL&lt;/strong&gt;: una segunda etapa de aprendizaje por refuerzo que le enseña al modelo cuándo conviene pensar en espacio latente y cuándo conviene actuar a través de texto, en vez de seguir una regla fija.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Con ese entrenamiento completo, los autores evaluaron &lt;a href="https://arxiv.org/abs/2610.18861" rel="noopener noreferrer"&gt;HybridAgents en 3 escalas de modelo distintas, sobre 11 benchmarks&lt;/a&gt; que cubren matemáticas, ciencia, medicina, búsqueda y razonamiento agéntico. Comparado con agentes de frontera y frameworks agénticos que cubren tanto métodos basados en texto como en vectores, reportan una mejora consistente de &lt;a href="https://arxiv.org/abs/2610.18861" rel="noopener noreferrer"&gt;10,4%&lt;/a&gt; en desempeño.&lt;/p&gt;

&lt;p&gt;La comparación no fue solo contra otros agentes híbridos. El paper incluye frameworks agénticos de frontera que razonan puramente en texto y modelos que razonan puramente en espacio latente, lo que permite aislar qué parte de la ganancia viene de combinar ambos modos y cuál vendría de todas formas con un modelo más grande.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dot"&gt;&lt;code&gt;&lt;span class="nv"&gt;stateDiagram&lt;/span&gt;
    &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;ModoTexto&lt;/span&gt;
    &lt;span class="nv"&gt;ModoTexto&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;ModoLatente&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;HybridLink&lt;/span&gt; &lt;span class="nv"&gt;activa&lt;/span&gt; &lt;span class="nv"&gt;el&lt;/span&gt; &lt;span class="nv"&gt;cambio&lt;/span&gt;
    &lt;span class="nv"&gt;ModoLatente&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;ModoTexto&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;necesita&lt;/span&gt; &lt;span class="nv"&gt;usar&lt;/span&gt; &lt;span class="nv"&gt;una&lt;/span&gt; &lt;span class="nv"&gt;herramienta&lt;/span&gt;
    &lt;span class="nv"&gt;ModoTexto&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;entrega&lt;/span&gt; &lt;span class="nv"&gt;la&lt;/span&gt; &lt;span class="nv"&gt;respuesta&lt;/span&gt; &lt;span class="nv"&gt;final&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Ejemplos prácticos: qué significa pensar en vectores
&lt;/h2&gt;

&lt;p&gt;No hay todavía una librería instalable que deje experimentar con la arquitectura exacta de HybridAgents fuera del paper, así que el mejor ejemplo práctico es uno conceptual: mostrar por qué dos pensamientos parecidos quedan cerca en un espacio vectorial, que es la propiedad que el modo latente explota.&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;

&lt;span class="n"&gt;pensamiento_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;0.12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.98&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;  &lt;span class="c1"&gt;# \"quiero resolver esta ecuacion\"
&lt;/span&gt;&lt;span class="n"&gt;pensamiento_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;0.15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.95&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.02&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;  &lt;span class="c1"&gt;# \"necesito despejar x\"
&lt;/span&gt;
&lt;span class="n"&gt;similitud&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pensamiento_a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pensamiento_b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linalg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;norm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pensamiento_a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linalg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;norm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pensamiento_b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;\&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Similitud coseno: {similitud:.4f}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La salida de este script es &lt;code&gt;Similitud coseno: 0.9987&lt;/code&gt;. Esos dos vectores representan ideas distintas en palabras (resolver una ecuación y despejar x), pero casi idénticas en el espacio vectorial. Un agente que razona en modo latente puede moverse entre ideas así de cercanas sin tener que materializar ninguna de las dos como texto, y solo convertir el resultado final a palabras cuando le toca responder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Casos de uso reales
&lt;/h2&gt;

&lt;p&gt;El paper no se queda en matemáticas de juguete. Evalúa la cognición vectorial de los agentes en cinco dominios distintos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Matemáticas&lt;/strong&gt;: resolución de problemas donde cada paso intermedio es una operación simbólica (despejar, factorizar, sustituir) que no necesita pasar por una frase completa en español o inglés.- &lt;strong&gt;Ciencia&lt;/strong&gt;: preguntas que requieren encadenar varios hechos verificables antes de llegar a una conclusión, un escenario donde un paso mal razonado arrastra error a todos los siguientes.- &lt;strong&gt;Medicina&lt;/strong&gt;: tareas de razonamiento clínico, donde la cantidad de pasos intermedios suele ser alta y el costo de cada token adicional se multiplica rápido.- &lt;strong&gt;Búsqueda&lt;/strong&gt;: tareas donde el agente necesita decidir qué buscar, evaluar resultados parciales y recién después construir la respuesta completa.- &lt;strong&gt;Razonamiento agéntico&lt;/strong&gt;: tareas que combinan pensar con usar herramientas externas, el escenario donde el modo híbrido se vuelve indispensable porque el modo puramente latente no puede ejecutar una llamada de herramienta.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Errores comunes y buenas prácticas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Asumir que cualquier modelo ya razona en latente&lt;/strong&gt;: no es así. Tratar al razonamiento latente como una función que viene gratis en cualquier backbone es el error más común; hace falta el módulo HybridLink y las dos etapas de post-entrenamiento, HybridSFT y HybridRL, para que el modelo aprenda a coordinarse.- &lt;strong&gt;Tratar al modo latente como una caja negra total&lt;/strong&gt;: parte del valor del paper es justamente que el razonamiento híbrido sigue siendo parcialmente inspeccionable en sus tramos de texto, a diferencia de un agente puramente latente sin HybridLink.- &lt;strong&gt;Ignorar el costo de entrenamiento&lt;/strong&gt;: pasar de un modelo solo-texto a uno híbrido no es gratis, implica las dos etapas de entrenamiento adicionales antes de ver las ganancias de eficiencia.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Ojo:&lt;/strong&gt; si no podés leer el razonamiento paso a paso de tu agente, tampoco podés auditarlo con la misma facilidad que un chain-of-thought en texto. Eso importa en dominios regulados como medicina o finanzas.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Comparativa con alternativas
&lt;/h2&gt;

&lt;p&gt;EnfoqueCómo representa el pensamientoVentajaLimitaciónCadena de pensamiento en textoPalabras generadas token por tokenTotalmente legible e inspeccionableCada paso intermedio cuesta tokens y cómputoRazonamiento puramente latenteSolo vectores continuos, sin pasar por textoMás eficiente en tokens y cómputoPierde acceso nativo a herramientas y transparenciaRazonamiento híbrido (HybridAgents)Alterna vectores y texto según la tareaReduce tokens hasta &lt;a href="https://arxiv.org/abs/2610.18861" rel="noopener noreferrer"&gt;54,7%&lt;/a&gt; sin perder el uso de herramientasRequiere entrenamiento adicional (HybridSFT y HybridRL)&lt;/p&gt;

&lt;h2&gt;
  
  
  Profundizando: qué arriesga un agente que piensa en vectores
&lt;/h2&gt;

&lt;p&gt;El texto es, en el fondo, una compresión con pérdida del pensamiento real de un modelo. Cuando un modelo genera una cadena de razonamiento en palabras, está forzando una representación de alta dimensión a pasar por un vocabulario finito, perdiendo matices en el camino. El razonamiento en espacio latente evita esa compresión.&lt;/p&gt;

&lt;p&gt;Esa ganancia tiene un costo que todavía preocupa a buena parte de la comunidad de seguridad en IA: el chain-of-thought en texto, con todos sus defectos, se volvió una herramienta de supervisión. Si un agente razona en vectores que nadie puede leer directamente, esa ventana de inspección se angosta, aunque el agente siga siendo técnicamente más eficiente.&lt;/p&gt;

&lt;p&gt;Esa tensión no es nueva en la investigación de seguridad en IA: ya antes de este paper, varios laboratorios discutían si optimizar demasiado la eficiencia de un chain-of-thought en texto terminaba empujando a los modelos a comprimir su razonamiento en formas cada vez menos legibles para un humano. El razonamiento en espacio latente lleva esa discusión un paso más allá, porque ya no se trata de un texto comprimido sino de una representación que nunca pasó por el lenguaje.&lt;/p&gt;

&lt;p&gt;Por eso el diseño de HybridAgents no apuesta todo al modo latente. Mantiene el texto disponible para los momentos donde la transparencia o el uso de una herramienta importan más que el ahorro de tokens, y usa HybridRL precisamente para aprender ese criterio en vez de fijarlo a mano.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💭 Clave:&lt;/strong&gt; el texto es una compresión con pérdida del pensamiento real del modelo. El razonamiento en espacio latente evita esa compresión, pero a cambio pierde la propiedad que hizo útil al chain-of-thought como herramienta de supervisión: que un humano pueda leerlo.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;📖 Resumen en Telegram: &lt;a href="https://telegra.ph/Razonamiento-latente-la-IA-que-piensa-en-vectores-10-19" rel="noopener noreferrer"&gt;Ver resumen&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tu próximo paso: tomá dos frases con significado parecido, generá sus embeddings con cualquier modelo de sentence-transformers y calculá la similitud coseno como en el ejemplo de este artículo, para ver con tus propios datos qué tan cerca quedan en el espacio vectorial.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿HybridAgents reemplaza a los modelos que razonan solo en texto?
&lt;/h3&gt;

&lt;p&gt;No. HybridAgents es una forma de combinar ambos modos dentro del mismo modelo, no de eliminar el modo texto. El texto sigue siendo necesario para llamar herramientas y para comunicar la respuesta final.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿El pensamiento vectorial funciona con cualquier modelo de lenguaje?
&lt;/h3&gt;

&lt;p&gt;No de forma automática. Requiere agregar el módulo HybridLink y pasar por las dos etapas de post-entrenamiento que describe el paper, HybridSFT y luego HybridRL. Sin ese proceso, el modelo no sabe cuándo conviene cambiar de modo ni cómo interpretar un vector que viene del modo contrario.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Por qué el razonamiento en espacio latente es más difícil de auditar?
&lt;/h3&gt;

&lt;p&gt;Porque un vector continuo no tiene una traducción directa y única a palabras. Un chain-of-thought en texto se puede leer línea por línea; un vector solo se puede analizar con técnicas indirectas, como comparar su similitud con otros vectores conocidos.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿HybridAgents puede usar herramientas externas mientras piensa en vectores?
&lt;/h3&gt;

&lt;p&gt;Sí, y es justamente el punto del diseño. Cuando el agente necesita llamar una herramienta, HybridLink lo devuelve al modo texto para esa llamada, y vuelve a modo latente después si conviene.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Dónde se probó la cognición vectorial de los agentes?
&lt;/h3&gt;

&lt;p&gt;El paper evaluó HybridAgents en 3 escalas de modelo distintas sobre 11 benchmarks de matemáticas, ciencia, medicina, búsqueda y razonamiento agéntico, comparando siempre contra agentes de frontera que razonan solo en texto o solo en vectores.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿El código de HybridAgents es público?
&lt;/h3&gt;

&lt;p&gt;El paper indica que el código y los datos están disponibles, aunque el resumen público no detalla todavía la URL exacta del repositorio.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://arxiv.org/abs/2610.18861" rel="noopener noreferrer"&gt;Hybrid Reasoning Agents (arXiv:2610.18861)&lt;/a&gt;: paper original de Stanford, Google y colaboradores, publicado el 13 de octubre de 2026.- &lt;a href="https://en.wikipedia.org/wiki/Chain-of-thought_prompting" rel="noopener noreferrer"&gt;Chain-of-thought prompting, Wikipedia&lt;/a&gt;: contexto sobre el razonamiento en texto que el razonamiento latente busca complementar.- &lt;a href="https://en.wikipedia.org/wiki/Large_language_model" rel="noopener noreferrer"&gt;Large language model, Wikipedia&lt;/a&gt;: fundamentos generales sobre cómo funcionan los modelos de lenguaje.- &lt;a href="https://numpy.org/doc/stable/" rel="noopener noreferrer"&gt;NumPy, documentación oficial&lt;/a&gt;: referencia de las funciones usadas en el ejemplo de código de este artículo.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📱 &lt;strong&gt;¿Te gusta este contenido?&lt;/strong&gt; Únete a nuestro canal de Telegram &lt;a href="https://t.me/programacion" rel="noopener noreferrer"&gt;@programacion&lt;/a&gt; donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Intel 8087: cómo el microcódigo sumaba y restaba en hardware</title>
      <dc:creator>lu1tr0n</dc:creator>
      <pubDate>Sun, 18 Oct 2026 20:28:50 +0000</pubDate>
      <link>https://dev.to/lu1tr0n/intel-8087-como-el-microcodigo-sumaba-y-restaba-en-hardware-4aff</link>
      <guid>https://dev.to/lu1tr0n/intel-8087-como-el-microcodigo-sumaba-y-restaba-en-hardware-4aff</guid>
      <description>&lt;p&gt;El microcódigo del 8087 multiplicó casi 100 veces la velocidad de la suma en punto flotante frente al 8086, usando apenas 1.648 microinstrucciones grabadas en su ROM. Ese salto convirtió a la PC de IBM en una máquina capaz de recalcular hojas de cálculo y planos de ingeniería sin esperar minutos por cada operación.&lt;/p&gt;

&lt;p&gt;Un análisis reciente del die del 8087, hecho con un chip real decapado y fotografiado al microscopio, reconstruyó paso a paso cómo su circuitería ejecuta una suma o una resta en punto flotante, con todos los casos límite que involucran infinitos y ceros con signo.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;El coprocesador matemático 8087 ejecuta suma y resta en punto flotante de 80 bits con 1.648 microinstrucciones.- Alinear los significandos antes de sumar es el mismo truco que alinear comas decimales, pero en binario.- El shifter del 8087 ocupa una porción enorme del die porque desplaza una cantidad arbitraria de bits en un solo paso.- Restar dos números cercanos puede exigir normalizar con un desplazamiento de varios bits a la izquierda.- Python con math.frexp y math.ldexp reproduce el mismo algoritmo de alineación y normalización del 8087.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ¿Qué es el microcódigo del 8087?
&lt;/h2&gt;

&lt;p&gt;El microcódigo del 8087 es la secuencia de 1.648 microinstrucciones grabadas en la ROM central del primer coprocesador matemático de Intel, lanzado en 1980, que controla paso a paso cómo el sumador, el shifter y el conversor de exponente ejecutan una suma o resta en punto flotante de 80 bits.&lt;/p&gt;

&lt;p&gt;Intel lo vendió como chip aparte para el 8086 y el 8088, el procesador que IBM eligió para su PC original. Sin coprocesador, esas CPU resolvían los decimales a pura instrucción entera, con rutinas de software que tardaban cientos de ciclos por cada operación.&lt;/p&gt;

&lt;p&gt;Con el 8087 instalado, el mismo cálculo se ejecutaba en circuitería dedicada: un sumador entero, un shifter capaz de mover bits en una sola pasada y un conversor de exponente que detecta desbordes antes de que arruinen el resultado.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué importa la aritmética en hardware
&lt;/h2&gt;

&lt;p&gt;Antes del 8087, un programa que sumara dos decimales en el 8086 debía descomponer la operación en instrucciones enteras: extraer el exponente, alinear bits a mano y recomponer el resultado. Cada paso costaba ciclos de reloj, y una sola suma en punto flotante podía tardar lo mismo que decenas de sumas enteras.&lt;/p&gt;

&lt;p&gt;El 8087 resolvió ese cuello de botella metiendo la lógica de alineación y normalización directamente en silicio. Según el análisis del die publicado en &lt;a href="https://www.righto.com/2026/10/8087-addition-subtraction-microcode.html" rel="noopener noreferrer"&gt;el blog de Ken Shirriff&lt;/a&gt;, la mejora de velocidad en la suma llegó a casi 100 veces frente al 8086 solo, y las operaciones más complejas, como raíces o logaritmos, ganaron todavía más.&lt;/p&gt;

&lt;p&gt;Para 1980 eso significaba que una hoja de cálculo con miles de celdas numéricas, o un programa de diseño asistido, podía recalcular en segundos en lugar de minutos. Ese fue el argumento de venta del 8087 en estaciones de ingeniería y en las primeras versiones de software como Lotus 1-2-3.&lt;br&gt;
El 8087 salió al mercado en 1980 como coprocesador opcional para el 8086 y el 8088.&lt;/p&gt;
&lt;h2&gt;
  
  
  El formato de 80 bits: así guarda los números el 8087
&lt;/h2&gt;

&lt;p&gt;Internamente, el 8087 no trabaja con el float de 32 bits ni el double de 64 bits que usan los lenguajes modernos. Todas sus operaciones ocurren en un formato propio de 80 bits que Intel llamó temporary real, con más bits de significando que cualquier otro formato que el chip exponía hacia el programador.&lt;/p&gt;

&lt;p&gt;Ese formato extendido absorbe el error de redondeo de cálculos intermedios antes de que el resultado final se convierta a 32 o 64 bits. Por eso una secuencia larga de sumas y restas en el 8087 pierde menos precisión que la misma secuencia hecha directamente en formato corto, ya que cada paso intermedio conserva bits de sobra que solo se descartan al final de la cadena.&lt;br&gt;
FormatoBits totalesBits de significandoRango aproximadoShort real3224&lt;a href="https://www.righto.com/2026/10/8087-addition-subtraction-microcode.html" rel="noopener noreferrer"&gt;supera 10⁻³⁶ a 10³⁸&lt;/a&gt;Long real6453mucho mayor que el short realTemporary real8064el más amplio de los tres, solo interno al chip&lt;/p&gt;
&lt;h2&gt;
  
  
  Cómo el 8087 alinea y suma dos números en punto flotante
&lt;/h2&gt;

&lt;p&gt;Un número en punto flotante se guarda como signo por significando por 2 elevado al exponente. El significando sigue la forma 1.bbb…, un 1 inicial seguido del punto binario y el resto de los bits. Así, el binario 1.1 representa 1 + 1/2, es decir 1.5 en decimal, y 1.0011 representa 1 + 1/8 + 1/16, o 1.1875.&lt;/p&gt;

&lt;p&gt;Para sumar dos números hace falta igualar sus exponentes antes de sumar los significandos, igual que alinear las comas decimales de 13.5 y 3.5 antes de sumarlas a mano. El 8087 expresa 13.5 como 1.6875 por 2 al cubo y 3.5 como 1.75 por 2 a la uno. La diferencia de exponentes es 2, así que el chip desplaza el significando de 3.5 dos posiciones a la derecha antes de sumar.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dot"&gt;&lt;code&gt;&lt;span class="nv"&gt;flowchart&lt;/span&gt; &lt;span class="nv"&gt;TD&lt;/span&gt;
    &lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Tomar los dos operandos"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;B&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Comparar exponentes"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="nv"&gt;B&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;C&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Desplazar el significando menor"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="nv"&gt;C&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;D&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Sumar los significandos alineados"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="nv"&gt;D&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;E&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"Hubo acarreo extra?"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nv"&gt;E&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;|&lt;/span&gt;&lt;span class="s2"&gt;"sí"&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;F&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Desplazar 1 bit a la derecha y sumar 1 al exponente"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="nv"&gt;E&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;|&lt;/span&gt;&lt;span class="s2"&gt;"no"&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;G&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Resultado ya normalizado"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="nv"&gt;F&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;G&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La suma de los significandos ya alineados da, en este caso, un resultado que desborda un bit hacia la izquierda. El 8087 normaliza desplazando ese resultado una posición a la derecha y sumando 1 al exponente, hasta que el bit más significativo vuelve a ser un 1. El resultado final es 17.0, igual que sumar 13.5 más 3.5 a mano.&lt;/p&gt;

&lt;h2&gt;
  
  
  Código: simulando la alineación y la suma como el 8087
&lt;/h2&gt;

&lt;p&gt;El mismo algoritmo de alinear, sumar y normalizar se reproduce en pocas líneas de Python usando math.frexp, que separa un número en significando y exponente, y math.ldexp, que los recompone.&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;align_and_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="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;ma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ea&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;frexp&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="n"&gt;mb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;eb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;frexp&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ea&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;eb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;mb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mb&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ea&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;eb&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;exp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ea&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;ma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ma&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eb&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;ea&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;exp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;eb&lt;/span&gt;
    &lt;span class="n"&gt;mantissa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ma&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;mb&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mantissa&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;mantissa&lt;/span&gt; &lt;span class="o"&gt;/=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
        &lt;span class="n"&gt;exp&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ldexp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mantissa&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;align_and_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;13.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;3.5&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La salida es &lt;code&gt;17.0&lt;/code&gt;, el mismo resultado que calculó el 8087 en el ejemplo de alineación de más arriba.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cómo funciona la resta: cancelación y normalización a la izquierda
&lt;/h2&gt;

&lt;p&gt;Restar es más delicado que sumar. El resultado puede ser positivo, negativo o exactamente cero, y cuando dos números son parecidos, el resultado queda mucho más chico que ambos operandos. El 8087 alinea los exponentes igual que en la suma, pero después de restar los significandos, el bit más significativo casi nunca queda en la posición correcta, así que la normalización suele exigir desplazar varios bits a la izquierda.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dot"&gt;&lt;code&gt;&lt;span class="nv"&gt;flowchart&lt;/span&gt; &lt;span class="nv"&gt;TD&lt;/span&gt;
    &lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Alinear exponentes"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;B&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Restar los significandos"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="nv"&gt;B&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;C&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"El resultado es cero?"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nv"&gt;C&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;|&lt;/span&gt;&lt;span class="s2"&gt;"sí"&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;D&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Devolver +0 o -0 según el signo"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="nv"&gt;C&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;|&lt;/span&gt;&lt;span class="s2"&gt;"no"&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;E&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"El significando es negativo?"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nv"&gt;E&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;|&lt;/span&gt;&lt;span class="s2"&gt;"sí"&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;F&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Hacerlo positivo e invertir el signo del resultado"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="nv"&gt;E&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;|&lt;/span&gt;&lt;span class="s2"&gt;"no"&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;G&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Normalizar desplazando a la izquierda"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="nv"&gt;F&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;G&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tomemos 8 menos 7.5. El chip expresa 8 como 1.0 por 2 al cubo y 7.5 como 1.875 por 2 al cuadrado; alineados, resta 1.0 menos 0.9375 y obtiene 0.0625. Para normalizar ese resultado hasta que el bit líder sea 1, el 8087 desplaza cuatro posiciones a la izquierda y resta 4 al exponente, llegando a 1.0 por 2 a la menos uno, o sea 0.5.&lt;/p&gt;

&lt;p&gt;Si el significando de la resta queda negativo, el 8087 lo convierte a positivo e invierte el signo del resultado. Y si la resta da exactamente cero, no hay nada que normalizar: el chip devuelve directamente +0 o -0 según el signo que corresponda.&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;align_and_sub&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="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;ma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ea&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;frexp&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="n"&gt;mb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;eb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;frexp&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ea&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;eb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;mb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mb&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ea&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;eb&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;exp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ea&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;ma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ma&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eb&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;ea&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;exp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;eb&lt;/span&gt;
    &lt;span class="n"&gt;mantissa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ma&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;mb&lt;/span&gt;
    &lt;span class="n"&gt;shifts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;mantissa&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mantissa&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sumador entero&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Registros temporales o pila&lt;/span&gt;&lt;span class="sh"&gt;"&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;C&lt;/span&gt;
    &lt;span class="n"&gt;C&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Registro de suma&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;E&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Shifter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;F&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;G&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Normalización&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Contador de ceros a la izquierda&lt;/span&gt;&lt;span class="sh"&gt;"&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;F&lt;/span&gt;
    &lt;span class="n"&gt;I&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Conversor de exponente&lt;/span&gt;&lt;span class="sh"&gt;"&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;G&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El datapath del 8087 ocupa la mitad inferior del die y agrupa los bloques que hacen el trabajo pesado. El sumador entero, en el centro, recibe un operando del registro B y otro de los registros temporales o de la pila de ocho registros del chip; su salida va al registro de suma.&lt;/p&gt;

&lt;p&gt;El shifter es, según el análisis del die, el bloque que más espacio ocupa dentro de esa mitad del chip, porque su circuitería permite mover un valor una cantidad arbitraria de posiciones en un solo paso, en lugar de desplazar bit por bit. La operación de alinear significandos, descrita más arriba, depende enteramente de este componente.&lt;br&gt;
El shifter del 8087 puede desplazar un valor una cantidad arbitraria de bits en un único paso.&lt;br&gt;
Junto al shifter trabaja un contador de ceros a la izquierda, que cuenta cuántos bits o bytes sobran del lado izquierdo de un valor para saber cuánto hay que desplazarlo durante la normalización. El conversor de exponente, por su parte, procesa los exponentes de ambos operandos, detecta cuándo un resultado se desborda o se queda sin rango, y ajusta el exponente final.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💭 Clave:&lt;/strong&gt; El 8087 no gana velocidad solo por tener más transistores: gana porque reemplaza un bucle de software, desplazar bit por bit, por un circuito que resuelve el desplazamiento completo en un solo ciclo.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Casos especiales: infinitos, ceros y signos
&lt;/h2&gt;

&lt;p&gt;Más allá de la aritmética normal, el microcódigo del 8087 dedica buena parte de sus 1.648 microinstrucciones a resolver casos límite. Sumar infinito más un número finito da infinito; sumar infinito positivo e infinito negativo no tiene un resultado numérico válido, así que el chip devuelve un valor especial llamado NaN, de not a number.&lt;/p&gt;

&lt;p&gt;Restar dos infinitos iguales en signo también es indeterminado, y el resultado también es NaN. Estos casos no se resuelven con el sumador: el chip los detecta antes, comparando los bits de exponente y significando que identifican a un infinito o un NaN, y salta directo al resultado correcto sin pasar por la lógica de alineación.&lt;/p&gt;

&lt;p&gt;El caso del cero con signo es más sutil. Si una resta da exactamente cero, el 8087 no puede normalizar un valor vacío, así que devuelve +0 o -0 según el signo que hubiera dado el cálculo. Esa distinción importa en operaciones posteriores, como dividir por el resultado: 1 dividido por +0 da +infinito, pero 1 dividido por -0 da -infinito.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparativa: del software al 8087 y al x87 moderno
&lt;/h2&gt;

&lt;p&gt;La forma de resolver una suma en punto flotante cambió varias veces desde 1980. Esta tabla resume las opciones principales y cuándo tenía sentido cada una.&lt;br&gt;
OpciónCuándo se usabaVentajaLimitaciónSoftware en el 8086Sistemas sin coprocesador, código solo con enterosNo necesita hardware adicionalCada suma en punto flotante cuesta decenas o cientos de ciclosCoprocesador 8087PC de IBM y clones con zócalo para el chip aparteAlineación y normalización en circuitería dedicadaChip extra, zócalo propio y mayor consumox87 integrado desde el 486DXCPU x86 de los 90 y 2000 con registros de 80 bitsMismo set de instrucciones, sin chip apartePila de registros rígida, difícil de optimizar por el compiladorSSE2 y AVXCódigo x86-64 moderno con precisión simple o dobleRegistros planos, vectoriza varias operaciones a la vezNo ofrece la precisión extendida de 80 bits del x87 original&lt;/p&gt;

&lt;h2&gt;
  
  
  Casos de uso reales
&lt;/h2&gt;

&lt;p&gt;Lotus 1-2-3 y las primeras hojas de cálculo para IBM PC recomendaban el 8087 para recalcular miles de celdas con decimales sin trabar la máquina. Los programas de diseño asistido por computadora de la época, que multiplicaban matrices para proyectar superficies en 3D, dependían todavía más del coprocesador: cada vista en perspectiva implicaba miles de sumas y multiplicaciones en punto flotante.&lt;/p&gt;

&lt;p&gt;Las estaciones de ingeniería y los primeros paquetes de simulación numérica también vendían el 8087 como upgrade casi obligatorio. Para 1985, el sucesor 80287 venía integrado de fábrica en varias placas basadas en 80286, señal de que el mercado ya daba por sentado tener un coprocesador matemático a bordo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Errores comunes y gotchas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Suponer que restar es simétrico a sumar&lt;/strong&gt;: la resta debe manejar signo negativo y el caso de resultado cero, algo que la suma nunca enfrenta.- &lt;strong&gt;Ignorar la cancelación catastrófica&lt;/strong&gt;: restar dos números muy parecidos deja pocos bits significativos en el resultado, aunque la operación parezca trivial.- &lt;strong&gt;Pensar que el x87 sigue siendo el camino rápido&lt;/strong&gt;: los compiladores modernos para x86-64 prefieren SSE2 o AVX, y reservan el x87 casi solo para el tipo long double de C.- &lt;strong&gt;Olvidar los casos de infinito y NaN al validar resultados&lt;/strong&gt;: una suma con un operando infinito no dispara una excepción, devuelve otro infinito o un NaN según el caso.- &lt;strong&gt;Asumir que el desplazamiento es gratis&lt;/strong&gt;: el shifter del 8087 lo resuelve en un solo paso, pero el software que lo emula bit por bit paga un costo que crece con el tamaño del desplazamiento.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Profundizando: por qué el shifter ocupa tanto espacio en el die
&lt;/h2&gt;

&lt;p&gt;Un shifter bit a bit, que mueve una posición por ciclo, es barato en transistores pero lento cuando hay que desplazar los 64 bits del significando completos. El 8087 resuelve esto con un shifter tipo barril: varias capas de multiplexores donde cada capa decide si desplaza el valor una cantidad fija de posiciones (1, 2, 4, 8…), y la combinación de capas permite cualquier desplazamiento en un solo paso.&lt;/p&gt;

&lt;p&gt;Ese diseño cuesta área de silicio, porque cada capa de multiplexores necesita tantas celdas como bits tiene el dato, multiplicadas por el número de capas. Ese costo explica por qué, dentro del datapath, el bloque del shifter resulta más grande que el del sumador entero, aunque el sumador haga aritmética en apariencia más compleja.&lt;/p&gt;

&lt;p&gt;El contador de ceros a la izquierda sigue una lógica parecida: en vez de revisar bit por bit desde la izquierda hasta encontrar el primer 1, usa un árbol de comparadores que reduce la búsqueda a unos pocos niveles lógicos. Sin ese circuito, cada normalización de una resta con cancelación numérica tardaría en proporción al tamaño del desplazamiento, algo que el chip no podía permitirse si quería mantener su ventaja de velocidad frente al 8086.&lt;/p&gt;

&lt;p&gt;📖 Resumen en Telegram: &lt;a href="https://telegra.ph/Intel-8087-c%C3%B3mo-el-microc%C3%B3digo-sumaba-y-restaba-en-hardware-10-18" rel="noopener noreferrer"&gt;Ver resumen&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tu próximo paso: corré align_and_sub con otro par de números cercanos, como 100.25 y 99.75, y confirmá a mano cuántos bits de desplazamiento predice la normalización antes de ejecutar el código.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿Qué hace distinto al microcódigo del 8087 frente al software?
&lt;/h3&gt;

&lt;p&gt;El microcódigo del 8087 corre dentro del chip y controla circuitería dedicada (sumador, shifter, conversor de exponente) en paralelo. Una rutina de software en el 8086 ejecuta las mismas alineaciones y normalizaciones con instrucciones enteras genéricas, paso por paso.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Por qué el coprocesador matemático 8087 necesita un shifter tan grande?
&lt;/h3&gt;

&lt;p&gt;Porque alinear exponentes y normalizar resultados exige desplazar significandos de hasta 64 bits. Hacerlo en un solo ciclo, en vez de bit por bit, es lo que le da al chip su ventaja de velocidad frente a emular la operación en software.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué pasa si una resta en el 8087 da como resultado cero?
&lt;/h3&gt;

&lt;p&gt;El chip no tiene nada que normalizar, así que devuelve directamente el valor especial +0 o -0, según el signo que hubiera correspondido al resultado antes de simplificarlo.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿El FPU del 8087 maneja infinitos y valores NaN?
&lt;/h3&gt;

&lt;p&gt;Sí: el 8087 detecta estos casos comparando los bits de exponente y significando antes de entrar a la lógica de suma o resta, y devuelve directamente infinito o NaN sin pasar por el sumador ni el shifter.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Sigue usándose el conjunto de instrucciones heredado del 8087 en CPUs actuales?
&lt;/h3&gt;

&lt;p&gt;El set x87 sigue presente en los procesadores x86-64 por compatibilidad, pero los compiladores modernos prefieren SSE2 o AVX para punto flotante y reservan el x87 casi solo para el tipo long double de C.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.righto.com/2026/10/8087-addition-subtraction-microcode.html" rel="noopener noreferrer"&gt;Righto.com&lt;/a&gt;: análisis del die del Intel 8087 y su microcódigo de suma y resta en punto flotante.- &lt;a href="https://en.wikipedia.org/wiki/Intel_8087" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;: historia y especificaciones del coprocesador matemático Intel 8087.- &lt;a href="https://en.wikipedia.org/wiki/IEEE_754" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;: estándar IEEE 754 para la representación de números en punto flotante.- &lt;a href="https://en.wikipedia.org/wiki/Extended_precision" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;: formato extendido de 80 bits usado por el x87, heredado del 8087.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📱 &lt;strong&gt;¿Te gusta este contenido?&lt;/strong&gt; Únete a nuestro canal de Telegram &lt;a href="https://t.me/programacion" rel="noopener noreferrer"&gt;@programacion&lt;/a&gt; donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.&lt;/p&gt;

</description>
      <category>hardware</category>
      <category>technology</category>
      <category>programming</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Parser combinators en TypeScript, paso a paso</title>
      <dc:creator>lu1tr0n</dc:creator>
      <pubDate>Sun, 18 Oct 2026 14:31:38 +0000</pubDate>
      <link>https://dev.to/lu1tr0n/parser-combinators-en-typescript-paso-a-paso-oak</link>
      <guid>https://dev.to/lu1tr0n/parser-combinators-en-typescript-paso-a-paso-oak</guid>
      <description>&lt;p&gt;Una regex para separar fecha, hora y zona horaria funciona hasta que aparece un formato que no previste, y ahí hay que reescribir la expresión completa desde cero. Los parser combinators resuelven ese problema de otra manera: en vez de una expresión gigante, componés funciones chicas que se van pasando el resto del texto entre sí, como piezas que encajan.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Un parser es una función que recibe texto y devuelve el valor extraído junto con lo que falta por leer.- pSeq, pMap y pOr arman analizadores complejos sin escribir una gramática BNF por separado.- TypeScript tipa cada parser como Parser y el compilador avisa si mezclás tipos distintos.- Parsimmon aplica esta técnica en JavaScript para parsear JSON, fechas o configuraciones propias.- pBind deja que un valor ya leído decida cómo parsear lo que sigue, útil para campos de longitud variable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ¿Qué es parser combinators?
&lt;/h2&gt;

&lt;p&gt;Parser combinators es el nombre de una técnica de programación funcional para construir analizadores sintácticos combinando funciones pequeñas y puras. Cada función, llamada parser, recibe una cadena de texto y devuelve el valor extraído junto con el resto sin consumir. Combinarlos permite armar parsers complejos sin escribir una gramática formal aparte.&lt;/p&gt;

&lt;p&gt;Pensalo como una línea de ensamblaje: cada estación toma la pieza que le pasó la anterior, hace su trabajo puntual y entrega el resto a la siguiente estación. Ninguna estación necesita saber qué hace el resto de la línea, así que podés reordenar, quitar o agregar estaciones sin tocar las demás.&lt;br&gt;
La librería Parsec, publicada para Haskell en los años 90, popularizó el patrón de combinar funciones de parseo.&lt;/p&gt;
&lt;h2&gt;
  
  
  Por qué importan los combinadores de parsers
&lt;/h2&gt;

&lt;p&gt;Entre una regex de una línea y un generador de parsers como &lt;a href="https://www.antlr.org/" rel="noopener noreferrer"&gt;ANTLR&lt;/a&gt; hay un vacío enorme. El &lt;a href="https://t3.at/blog/2026-10-17-parser-combinators/" rel="noopener noreferrer"&gt;blog de T3&lt;/a&gt; lo plantea con dos extremos. Una regex se vuelve ilegible apenas el formato tiene estructura anidada, y &lt;a href="https://tree-sitter.github.io/" rel="noopener noreferrer"&gt;tree-sitter&lt;/a&gt;, la librería que impulsa el resaltado de sintaxis de muchos editores de código, es demasiado pesada para un parser que vas a usar una sola vez. Los combinadores de parsers ocupan ese punto medio.&lt;/p&gt;

&lt;p&gt;La ventaja aparece en el trabajo diario de un backend: parsear una cabecera HTTP propia, un formato de log, un mini lenguaje de configuración o un protocolo binario simple. Ninguno de esos casos justifica instalar un generador de gramáticas, pero todos se vuelven frágiles con una sola regex gigante que nadie quiere tocar después.&lt;/p&gt;
&lt;h2&gt;
  
  
  Cómo funcionan los parsers combinables
&lt;/h2&gt;

&lt;p&gt;Así es como los parser combinators consumen la cadena de entrada paso a paso: cada parser recibe el string completo que falta procesar, se queda con la porción que entiende y devuelve el resto para el siguiente. El tipo que describe esa idea en TypeScript es compacto.&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="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Un &lt;code&gt;Parser&amp;lt;T&amp;gt;&lt;/code&gt; es una función de un string a una tupla con el valor &lt;code&gt;T&lt;/code&gt; y el string restante. No hay clase, no hay estado compartido, no hay configuración previa que inicializar.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dot"&gt;&lt;code&gt;&lt;span class="nv"&gt;flowchart&lt;/span&gt; &lt;span class="nv"&gt;LR&lt;/span&gt;
&lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Texto de entrada"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;B&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Parser 1 consume una parte"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;B&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;C&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Parser 2 consume la siguiente parte"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;C&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;D&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Parser 3 consume el resto"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;D&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;E&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Resultado final y string vacio"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  El parser más chico posible
&lt;/h3&gt;

&lt;p&gt;El parser más simple reconoce dígitos y los convierte en número.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="sr"&gt;+/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&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="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;se esperaba un numero&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;digits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;match&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;digits&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;digits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)];&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="nf"&gt;pNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;42 manzanas&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La salida es &lt;code&gt;[ 42, ' manzanas' ]&lt;/code&gt;. El parser cortó los dígitos iniciales y devolvió el número junto con todo lo que todavía no se procesó.&lt;/p&gt;

&lt;h3&gt;
  
  
  Secuencias con pSeq
&lt;/h3&gt;

&lt;p&gt;Un solo parser no alcanza para nada real. &lt;strong&gt;pSeq&lt;/strong&gt; encadena varios parsers pasando el resto de uno como entrada del siguiente.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sequenceDiagram
participant P as pSeq
participant A as pVerbo
participant B as pEspacio
participant C as pNumber
P-&amp;gt;&amp;gt;A: entrada completa
A--&amp;gt;&amp;gt;P: devuelve verbo y resto
P-&amp;gt;&amp;gt;B: resto despues del verbo
B--&amp;gt;&amp;gt;P: devuelve espacio y resto
P-&amp;gt;&amp;gt;C: resto despues del espacio
C--&amp;gt;&amp;gt;P: devuelve numero y resto final
Note over P: combina los tres resultados en una tupla
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pLiteral&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;texto&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Parser&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;texto&lt;/span&gt;&lt;span class="p"&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="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`se esperaba "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;texto&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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="nx"&gt;texto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;texto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;pSeq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;parsers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;Parser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Parser&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="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;resultados&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;resto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for &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;parser&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;parsers&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;valor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nuevoResto&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resto&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;resultados&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;valor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;resto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nuevoResto&lt;/span&gt;&lt;span class="p"&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="nx"&gt;resultados&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resto&lt;/span&gt;&lt;span class="p"&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;pMetodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pLiteral&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&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;pEspacio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pLiteral&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &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;pLinea&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pSeq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pMetodo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pEspacio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pNumber&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="nf"&gt;pLinea&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET 200 restante&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La salida es &lt;code&gt;[ [ 'GET', ' ', 200 ], ' restante' ]&lt;/code&gt;. Cada parser de la secuencia se quedó con su porción y el último dejó el resto sin tocar.&lt;/p&gt;

&lt;h3&gt;
  
  
  Transformar resultados con pMap y elegir con pOr
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;pMap&lt;/strong&gt; aplica una función al valor ya extraído sin tocar el resto del string, y pOr prueba varias alternativas en orden hasta encontrar la primera que no lance error.&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;pMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Parser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;valor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Parser&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="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;valor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resto&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&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="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;valor&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;resto&lt;/span&gt;&lt;span class="p"&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;function&lt;/span&gt; &lt;span class="nf"&gt;pOr&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;parsers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Parser&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="nx"&gt;Parser&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="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for &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;parser&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;parsers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&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="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ninguna alternativa coincidio&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="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pVerboHttp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pOr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;pLiteral&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;pLiteral&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;pLiteral&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DELETE&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;pEntradaLog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;pSeq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pVerboHttp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pEspacio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pNumber&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;verbo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;verbo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="p"&gt;})&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="nf"&gt;pEntradaLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST 201 ok&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La salida es &lt;code&gt;[ { verbo: 'POST', status: 201 }, ' ok' ]&lt;/code&gt;. El parser combinado ya devuelve un objeto útil, no una tupla posicional que hay que recordar cómo leer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dot"&gt;&lt;code&gt;&lt;span class="nv"&gt;flowchart&lt;/span&gt; &lt;span class="nv"&gt;TD&lt;/span&gt;
&lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Parsers basicos: pNumber, pLiteral"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;B&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Combinadores de secuencia: pSeq"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;A&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;C&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Combinadores de eleccion: pOr"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;B&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;D&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Combinadores de transformacion: pMap"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;C&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;D&lt;/span&gt;
&lt;span class="nv"&gt;D&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;E&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Combinadores de repeticion: pSepBy"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;E&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;F&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Combinadores dependientes: pBind"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Ejemplos prácticos y cómo empezar
&lt;/h2&gt;

&lt;p&gt;El combinador que falta para casos reales es el de repetición. &lt;strong&gt;pSepBy&lt;/strong&gt; parsea una lista de ítems separados por un delimitador, algo que una regex puede hacer para casos chicos pero que se vuelve ilegible con separadores anidados.&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;pSepBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Parser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;separador&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Parser&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Parser&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="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;resto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;valor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nuevoResto&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resto&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;resto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nuevoResto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;valor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[,&lt;/span&gt; &lt;span class="nx"&gt;despuesDeSeparador&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;separador&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resto&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;resto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;despuesDeSeparador&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&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="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resto&lt;/span&gt;&lt;span class="p"&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;pListaNumeros&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pSepBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;pLiteral&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&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="nf"&gt;pListaNumeros&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;10,20,30 fin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La salida es &lt;code&gt;[ [ 10, 20, 30 ], ' fin' ]&lt;/code&gt;. El bucle usa &lt;code&gt;while&lt;/code&gt; en lugar de recursión a propósito: la recursión en JavaScript y TypeScript no hace tail-call optimization, así que una lista de miles de ítems resuelta de forma recursiva puede desbordar la pila.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cómo empezar
&lt;/h3&gt;

&lt;p&gt;Para correr estos ejemplos tal cual, necesitás Node.js instalado (versión 18 o superior) y nada más. Los comandos son los mismos en Linux, macOS y Windows con PowerShell, porque son comandos de npm y no del shell del sistema.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;parser-combinators-demo &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;parser-combinators-demo
npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; typescript ts-node @types/node
npx tsc &lt;span class="nt"&gt;--init&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con el proyecto creado, guardá el código de los ejemplos anteriores en un archivo &lt;code&gt;parser.ts&lt;/code&gt; y corrélo con:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx ts-node parser.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para confirmar que el tipado funciona de verdad, cambiá &lt;code&gt;pNumber&lt;/code&gt; por un parser que devuelva &lt;code&gt;string&lt;/code&gt; dentro de &lt;code&gt;pSeq(pMetodo, pEspacio, pNumber)&lt;/code&gt; y corré &lt;code&gt;npx tsc --noEmit&lt;/code&gt;. El compilador tiene que fallar con un error de tipos en esa línea, antes de ejecutar nada.&lt;/p&gt;

&lt;h2&gt;
  
  
  Casos de uso reales
&lt;/h2&gt;

&lt;p&gt;Los parsers combinables aparecen en el código de producción con más frecuencia de la que parece. Un equipo de backend los usa para leer líneas de log con un formato propio, donde cada campo tiene un tipo distinto y una regex terminaría con grupos de captura imposibles de mantener.&lt;/p&gt;

&lt;p&gt;También sirven para lenguajes de configuración caseros, el tipo de sintaxis que un equipo inventa para su propia herramienta interna y que no justifica una gramática formal versionada aparte. Y aparecen en protocolos de red simples: formatos donde un campo indica la longitud del que sigue, como se ve más adelante en la sección de combinadores dependientes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/jneen/parsimmon" rel="noopener noreferrer"&gt;Parsimmon&lt;/a&gt; es la librería de parsing combinatorio más usada en el ecosistema de JavaScript y aplica exactamente esta idea para parsear JSON, fechas con formatos no estándar o lenguajes de plantillas propios, sin depender de un generador externo.&lt;br&gt;
El protocolo RESP de Redis y el formato bencode de BitTorrent usan el mismo patrón: longitud seguida de contenido.&lt;/p&gt;
&lt;h2&gt;
  
  
  Errores comunes y buenas prácticas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Orden en pOr&lt;/strong&gt;: la primera alternativa que coincide gana, aunque no sea la más específica. Si &lt;code&gt;pLiteral("GET")&lt;/code&gt; y &lt;code&gt;pLiteral("GETX")&lt;/code&gt; compiten, poné primero la más larga o vas a cortar mal el texto.- &lt;strong&gt;Espacios olvidados&lt;/strong&gt;: si el formato permite espacios entre tokens, hay que consumirlos explícitamente en cada punto donde pueden aparecer, no solo al principio de la cadena completa.- &lt;strong&gt;Recursión sin límite&lt;/strong&gt;: un combinador de repetición escrito con recursión en vez de un bucle puede desbordar la pila con entradas largas, por eso &lt;code&gt;pSepBy&lt;/code&gt; usa &lt;code&gt;while&lt;/code&gt; en el ejemplo anterior.- &lt;strong&gt;Excepciones como control de flujo&lt;/strong&gt;: usar &lt;code&gt;throw&lt;/code&gt;/&lt;code&gt;catch&lt;/code&gt; para señalar que un parser falló es simple de leer, pero mezcla manejo de errores con lógica de parseo. Para producción conviene migrar a un tipo &lt;code&gt;Result&lt;/code&gt; explícito que distinga éxito de fallo sin lanzar excepciones.- &lt;strong&gt;Mensajes de error sin posición&lt;/strong&gt;: un parser que solo dice "no coincide" es inútil para debuggear un archivo de miles de líneas. Hay que envolver cada parser para que reporte también el índice donde falló.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Comparativa con alternativas
&lt;/h2&gt;

&lt;p&gt;Ninguna de estas técnicas es universalmente mejor; cada una resuelve un rango distinto de formatos.&lt;br&gt;
TécnicaCuándo usarlaVentajaLimitaciónRegexPatrones simples, sin anidamientoNativa en casi todo lenguaje, rápida de escribirIlegible y rígida ante estructura anidadaCombinadores de parsersFormatos a medida con estructura moderada: logs, configs, DSL chicosComposable, tipado, se prueba parser por parserRequiere pensar en funciones puras y backtracking manualGenerador de parsers (ANTLR)Lenguajes grandes con gramática formal estableGenera un parser óptimo desde una gramática declarativaAgrega un paso de build y una gramática separada del códigoParser manual (descenso recursivo)Rendimiento crítico o control total sobre los erroresSin dependencias, control absolutoMás código repetido y más difícil de mantener&lt;/p&gt;
&lt;h2&gt;
  
  
  Profundizando: de dónde viene esta idea
&lt;/h2&gt;

&lt;p&gt;Los parser combinators heredan esta idea directamente de Parsec, la librería de Haskell de los años 90 que popularizó el patrón de componer parsers con operadores en lugar de generar código desde una gramática. La clave teórica es que cada parser implementa lo que en Haskell se llama una mónada, una estructura que define cómo encadenar operaciones que pueden fallar o depender del resultado anterior.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💭 Clave:&lt;/strong&gt; pSeq y pMap alcanzan para combinar parsers independientes entre sí, pero no sirven cuando el segundo parser depende del valor que devolvió el primero, no solo de su tipo. Para eso hace falta un combinador más, pBind.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Un caso real de esa dependencia es un campo con longitud variable: primero se lee un número, y ese número dice cuántos caracteres leer después. Es el mismo patrón que usan el protocolo RESP de Redis y el formato bencode de BitTorrent.&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;pBind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Parser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;valor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Parser&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Parser&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="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;valor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resto&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;valor&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;resto&lt;/span&gt;&lt;span class="p"&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;pTomar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Parser&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&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;n&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&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;pCampoConLongitud&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pBind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;longitud&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nf"&gt;pMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;pSeq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;pLiteral&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;pTomar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;longitud&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="p"&gt;([,&lt;/span&gt; &lt;span class="nx"&gt;texto&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;texto&lt;/span&gt;&lt;span class="p"&gt;)&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="nf"&gt;pCampoConLongitud&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;5:holamundo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La salida es &lt;code&gt;[ 'holam', 'undo' ]&lt;/code&gt;. &lt;code&gt;pNumber&lt;/code&gt; lee el 5, y ese valor decide que &lt;code&gt;pTomar&lt;/code&gt; debe cortar exactamente cinco caracteres después de los dos puntos. &lt;code&gt;pSeq&lt;/code&gt; no podría expresar esto porque no conoce el valor del parser anterior, solo encadena su resto.&lt;/p&gt;

&lt;p&gt;La otra pieza avanzada es el rendimiento del backtracking: cuando &lt;code&gt;pOr&lt;/code&gt; prueba varias alternativas sobre la misma posición del texto, puede repetir trabajo si esas alternativas comparten un prefijo largo. La técnica clásica para evitarlo es el &lt;em&gt;packrat parsing&lt;/em&gt;, que cachea el resultado de cada parser en cada posición de entrada para no volver a calcularlo.&lt;/p&gt;

&lt;p&gt;📖 Resumen en Telegram: &lt;a href="https://telegra.ph/Parser-combinators-en-TypeScript-paso-a-paso-10-18" rel="noopener noreferrer"&gt;Ver resumen&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tu próximo paso: tomá el ejemplo de &lt;code&gt;pSepBy&lt;/code&gt; de esta guía, modificalo para que ignore espacios opcionales antes y después de la coma usando un parser de espacios en blanco entre &lt;code&gt;item&lt;/code&gt; y &lt;code&gt;separador&lt;/code&gt;, y probalo contra &lt;code&gt;"10, 20 ,30"&lt;/code&gt; para ver dónde falla.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿Los parser combinators reemplazan una gramática formal como BNF?
&lt;/h3&gt;

&lt;p&gt;No necesariamente. Para lenguajes grandes y estables, una gramática declarativa que alimenta un generador como ANTLR sigue siendo más mantenible. Los combinadores de parsers brillan en formatos chicos o cambiantes donde versionar una gramática aparte no se justifica.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué diferencia hay entre parsers combinables y expresiones regulares?
&lt;/h3&gt;

&lt;p&gt;Una regex describe un patrón plano con backtracking interno oculto. Un parser combinable es una función explícita que se puede testear, componer y tipar por separado; el costo es más código para resolver el mismo patrón simple.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿El parsing combinatorio es más lento que un parser generado?
&lt;/h3&gt;

&lt;p&gt;Puede serlo si el backtracking recorre la misma porción del texto varias veces. La solución clásica es el packrat parsing, que cachea el resultado de cada parser en cada posición para no repetir trabajo.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Necesito saber programación funcional para usar combinadores de parsers?
&lt;/h3&gt;

&lt;p&gt;Ayuda entender funciones de orden superior, pero no hace falta Haskell ni mónadas para empezar. pSeq, pMap y pOr se leen y se escriben como cualquier función de TypeScript o JavaScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué librerías de parsing combinatorio existen en JavaScript o TypeScript?
&lt;/h3&gt;

&lt;p&gt;Parsimmon es la más usada en el ecosistema de Node.js. También hay variantes pensadas para TypeScript puro, todas con la misma idea de parsers chicos combinados con funciones de orden superior.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Los parsers combinables sirven para compiladores de lenguajes completos?
&lt;/h3&gt;

&lt;p&gt;Sí, con matices. Proyectos reales los usan para el frontend de lenguajes de tamaño moderado, pero cuando la gramática crece mucho el costo de mantenimiento suele empujar hacia un generador de parsers o un parser de descenso recursivo escrito a mano.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://t3.at/blog/2026-10-17-parser-combinators/" rel="noopener noreferrer"&gt;T3: Parser Combinators&lt;/a&gt;: el artículo original que inspira esta guía, con el tipo Parser y los ejemplos de pSeq y pHello.- &lt;a href="https://en.wikipedia.org/wiki/Parser_combinator" rel="noopener noreferrer"&gt;Wikipedia: Parser combinator&lt;/a&gt;: definición y contexto histórico de la técnica.- &lt;a href="https://github.com/jneen/parsimmon" rel="noopener noreferrer"&gt;Parsimmon en GitHub&lt;/a&gt;: librería de parsing combinatorio para JavaScript usada en producción.- &lt;a href="https://www.antlr.org/" rel="noopener noreferrer"&gt;ANTLR&lt;/a&gt;: generador de parsers de referencia para gramáticas grandes y formales.- &lt;a href="https://tree-sitter.github.io/" rel="noopener noreferrer"&gt;tree-sitter&lt;/a&gt;: librería de parsing incremental que impulsa el resaltado de sintaxis de editores modernos.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📱 &lt;strong&gt;¿Te gusta este contenido?&lt;/strong&gt; Únete a nuestro canal de Telegram &lt;a href="https://t.me/programacion" rel="noopener noreferrer"&gt;@programacion&lt;/a&gt; donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.&lt;/p&gt;

</description>
      <category>technology</category>
      <category>science</category>
      <category>programming</category>
      <category>discuss</category>
    </item>
    <item>
      <title>DeepSeek lanza Search API y Fetch API para agentes y RAG</title>
      <dc:creator>lu1tr0n</dc:creator>
      <pubDate>Sun, 18 Oct 2026 08:27:55 +0000</pubDate>
      <link>https://dev.to/lu1tr0n/deepseek-lanza-search-api-y-fetch-api-para-agentes-y-rag-2laj</link>
      <guid>https://dev.to/lu1tr0n/deepseek-lanza-search-api-y-fetch-api-para-agentes-y-rag-2laj</guid>
      <description>&lt;p&gt;DeepSeek acaba de completar su API con dos piezas que le faltaban: búsqueda y lectura de páginas web en vivo. La Search API de DeepSeek y su compañera, la Fetch API, llegan bajo el mismo token Bearer que ya usan los desarrolladores para chat completions, sobre el motor de búsqueda que la empresa opera de forma interna, sin pasar por Google, Bing ni un proveedor externo como SerpAPI o Tavily.&lt;/p&gt;

&lt;p&gt;La guía oficial describe dos endpoints REST: uno busca en la web y devuelve resultados estructurados, el otro toma una URL puntual y la convierte en Markdown. Juntos resuelven lo que antes exigía ensamblar herramientas de terceros.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;DeepSeek lanza la Search API y la Fetch API, dos endpoints REST para que agentes busquen y lean la web.- La Search API usa el motor de búsqueda propio de DeepSeek y acepta operadores como site:, lang:, after: y before:.- Cada resultado trae un rerank_score de 0 a 3 que mide relevancia, autoridad y frescura combinadas.- Fetch API convierte una sola página web en Markdown listo para que el modelo la lea sin parsear HTML.- Ambos endpoints se autentican con el mismo Bearer token que usa el resto de la API de DeepSeek.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Qué es la Search API de DeepSeek
&lt;/h2&gt;

&lt;p&gt;La Search API de DeepSeek es un endpoint REST que devuelve resultados de búsqueda web estructurados (título, URL, resumen y una puntuación de relevancia) a partir de una consulta de texto, usando el motor de búsqueda que la compañía opera internamente.&lt;/p&gt;

&lt;p&gt;Es, en palabras de la propia &lt;a href="https://api-docs.deepseek.com/guides/search/" rel="noopener noreferrer"&gt;documentación oficial&lt;/a&gt;, una API JSON cruda: no hay capa de chat ni function calling automático de por medio. El desarrollador decide cuándo llamarla, qué hacer con los resultados y en qué momento pasarlos al modelo como contexto, igual que con cualquier integración de RAG armada a mano.&lt;br&gt;
DeepSeek expone ambos endpoints bajo la misma base api.deepseek.com, sin dominio aparte.&lt;/p&gt;
&lt;h2&gt;
  
  
  Qué pasó
&lt;/h2&gt;

&lt;p&gt;La documentación publicada por DeepSeek detalla dos rutas nuevas bajo la misma base que el resto de la plataforma: &lt;code&gt;POST https://api.deepseek.com/v1/tools/search&lt;/code&gt; para buscar, y &lt;code&gt;POST https://api.deepseek.com/v1/tools/fetch&lt;/code&gt; para leer una página puntual. Ambas usan la cabecera &lt;code&gt;Authorization: Bearer &amp;lt;DeepSeek API Key&amp;gt;&lt;/code&gt; y &lt;code&gt;Content-Type: application/json&lt;/code&gt;, exactamente como las llamadas de chat completions.&lt;/p&gt;

&lt;p&gt;No es un modelo nuevo ni una actualización de V3 o R1: es infraestructura de soporte, pensada para que agentes y aplicaciones de recuperación aumentada (RAG) consulten la web vigente sin salir del ecosistema de DeepSeek ni sumar un proveedor de búsqueda aparte.&lt;/p&gt;
&lt;h2&gt;
  
  
  Contexto e historia
&lt;/h2&gt;

&lt;p&gt;DeepSeek construyó su reputación con modelos de pesos abiertos (V3, R1 y sus variantes) ofrecidos a precios muy por debajo de la competencia estadounidense. Pero un modelo, por sí solo, no resuelve que la información cambia todos los días: para responder sobre algo de esta semana, un agente necesita buscar en la web en el momento de la consulta, no solo recordar lo que vio durante el entrenamiento.&lt;/p&gt;

&lt;p&gt;Otros laboratorios ya resolvieron esa pieza para sus propios modelos: OpenAI integró navegación web en su API de Responses, Anthropic ofrece una herramienta de búsqueda web nativa para Claude, y Google conecta Gemini con su propio índice mediante grounding. Hasta ahora, un desarrollador que quisiera lo mismo con DeepSeek tenía que contratar aparte una API de búsqueda como Bing, SerpAPI, Tavily o Exa, y después unir esos resultados al prompt a mano. El buscador propio de DeepSeek cierra esa brecha sin que el desarrollador salga de la cuenta que ya tiene.&lt;/p&gt;
&lt;h2&gt;
  
  
  Detalles técnicos: cómo funciona el buscador propio de DeepSeek
&lt;/h2&gt;

&lt;p&gt;La consulta (&lt;code&gt;query&lt;/code&gt;) acepta entre 1 y 128 caracteres Unicode y se reenvía casi intacta al índice interno, lo que significa que los operadores de búsqueda no los procesa DeepSeek: los interpreta el motor que hay detrás. Eso incluye &lt;code&gt;site:&lt;/code&gt; y &lt;code&gt;-site:&lt;/code&gt; para incluir o excluir dominios, comillas para frases exactas o su negación con &lt;code&gt;-\"frase\"&lt;/code&gt;, &lt;code&gt;lang:&lt;/code&gt; para restringir idioma, y &lt;code&gt;after:&lt;/code&gt;/&lt;code&gt;before:&lt;/code&gt; para acotar por fecha. Todos se combinan en una sola cadena, por ejemplo &lt;code&gt;DeepSeek site:github.com OR site:huggingface.co lang:en after:2025&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Tip:&lt;/strong&gt; combinar after: con lang:es es la forma más directa de filtrar ruido cuando el agente solo necesita noticias recientes en español.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cada resultado trae un campo &lt;code&gt;rerank_score&lt;/code&gt; de 0 a 3 que combina señales de relevancia, autoridad y frescura; según la &lt;a href="https://api-docs.deepseek.com/guides/search/" rel="noopener noreferrer"&gt;documentación oficial&lt;/a&gt;, un puntaje de 1.5 o más ya se considera de alta calidad. El campo &lt;code&gt;snippet&lt;/code&gt; es un resumen corto (entre 100 y 200 caracteres) pensado para una interfaz, mientras que &lt;code&gt;summary&lt;/code&gt; es más largo (entre 600 y 800 caracteres) y está pensado para que lo lea el modelo directamente.&lt;/p&gt;

&lt;p&gt;El parámetro que más cambia el costo de la llamada es &lt;code&gt;need_content&lt;/code&gt;. En &lt;code&gt;false&lt;/code&gt; (el valor por defecto), el campo &lt;code&gt;content&lt;/code&gt; siempre llega vacío: la API solo devuelve metadatos y resúmenes. En &lt;code&gt;true&lt;/code&gt;, DeepSeek además descarga la página completa y la entrega en el mismo array de resultados, lo que suma latencia y tokens de contexto si el agente va a pasarle ese contenido al modelo. La Fetch API resuelve el caso en que ya se conoce la URL exacta: en vez de buscar, pide directamente esa página y la devuelve convertida a Markdown, sin el resto de los campos de un resultado de búsqueda.&lt;br&gt;
EndpointCuándo usarloVentajaLimitaciónSearch API (&lt;code&gt;/v1/tools/search&lt;/code&gt;)No se conoce la URL, hay que descubrir qué página responde la consultaDevuelve varios candidatos con rerank_score para elegirSin need_content no trae el texto completoFetch API (&lt;code&gt;/v1/tools/fetch&lt;/code&gt;)Ya se tiene la URL (de un resultado de Search o de una cita del usuario)Devuelve la página entera en Markdown, lista para el modeloUna sola página por llamada, no busca nada&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sequenceDiagram
participant A as Agente o app
participant S as Search API
participant M as Motor interno de DeepSeek
participant F as Fetch API
A-&amp;gt;&amp;gt;S: POST /v1/tools/search (query, count)
S-&amp;gt;&amp;gt;M: ejecuta la consulta con operadores
M--&amp;gt;&amp;gt;S: resultados con rerank_score
S--&amp;gt;&amp;gt;A: results en JSON
A-&amp;gt;&amp;gt;F: POST /v1/tools/fetch (url)
F--&amp;gt;&amp;gt;A: pagina convertida a Markdown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://api.deepseek.com/v1/tools/search &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$DEEPSEEK_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "query": "DeepSeek Search API site:github.com after:2026",
    "count": 5,
    "need_content": false
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La documentación ilustra la forma de la respuesta con un ejemplo (no son datos de producción, solo el esquema): un array &lt;code&gt;results&lt;/code&gt; donde cada elemento trae &lt;code&gt;rerank_score&lt;/code&gt;, &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;url&lt;/code&gt;, &lt;code&gt;snippet&lt;/code&gt;, &lt;code&gt;summary&lt;/code&gt;, &lt;code&gt;site_name&lt;/code&gt; y &lt;code&gt;date&lt;/code&gt;. Una búsqueda sin coincidencias no da error: devuelve &lt;code&gt;results: []&lt;/code&gt; con HTTP 200, que es también la forma más simple de confirmar que una cuenta tiene el acceso habilitado. Un &lt;code&gt;401&lt;/code&gt; en esa misma llamada indica que el Bearer token no tiene el scope de búsqueda activo.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;📌 Nota:&lt;/strong&gt; al ser una API JSON cruda y no una herramienta de function calling, el modelo de DeepSeek no decide solo cuándo buscar: es el código del agente el que llama a /v1/tools/search y después arma el prompt con lo que recibe.&lt;br&gt;
Con need_content en false, el campo content de cada resultado llega siempre vacío.&lt;/p&gt;
&lt;h2&gt;
  
  
  Impacto y análisis
&lt;/h2&gt;
&lt;/blockquote&gt;

&lt;p&gt;Para un desarrollador que ya paga por los modelos de DeepSeek, la ganancia inmediata es operativa: un proveedor menos que facturar, un token menos que rotar, y una API con el mismo formato de autenticación que el resto de la plataforma. Eso simplifica el stack de un agente que antes necesitaba la clave de DeepSeek para generar texto y la clave de otro proveedor solo para buscar.&lt;/p&gt;

&lt;p&gt;La contrapartida es que la calidad de los resultados depende por completo del índice propio de DeepSeek, que no tiene el historial ni la cobertura documentada públicamente de Google o Bing. La guía tampoco especifica un precio separado para estos endpoints más allá de lo que ya cuesta la cuenta de API: hasta la fecha de este artículo no hay un apartado de pricing propio para Search y Fetch en la documentación pública, así que conviene revisar la sección de precios antes de meter el buscador propio de DeepSeek en un flujo con volumen alto de llamadas.&lt;/p&gt;

&lt;p&gt;También hay una arista de privacidad poco comentada: el parámetro &lt;code&gt;localization&lt;/code&gt; acepta una IP para ajustar resultados por geografía, lo que implica que, si un agente reenvía la IP real del usuario final, esa dirección queda en la solicitud a DeepSeek igual que pasaría con cualquier buscador que geolocalice consultas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué sigue
&lt;/h2&gt;

&lt;p&gt;Lo que falta por confirmar es si DeepSeek va a exponer estos dos endpoints como function calling nativo dentro de chat completions, para que el modelo decida solo cuándo buscar en vez de que lo orqueste el código del agente, y si va a publicar límites de tasa y una tabla de precios dedicada. También queda por verse si otros laboratorios de pesos abiertos con enfoque similar en costo responden empaquetando su propio buscador en lugar de dejarlo en manos de integraciones de terceros.&lt;/p&gt;

&lt;p&gt;📖 Resumen en Telegram: &lt;a href="https://telegra.ph/DeepSeek-lanza-Search-API-y-Fetch-API-para-agentes-y-RAG-10-18" rel="noopener noreferrer"&gt;Ver resumen&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Probalo vos: hacé un POST a &lt;a href="https://api.deepseek.com/v1/tools/search" rel="noopener noreferrer"&gt;https://api.deepseek.com/v1/tools/search&lt;/a&gt; con tu API key y una consulta con site: para ver el array de results antes de conectarlo a un agente en producción.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿Qué hace la Search API de DeepSeek que no haga una búsqueda normal en Google?
&lt;/h3&gt;

&lt;p&gt;Responde en JSON estructurado pensado para que lo lea un modelo, no una persona: cada resultado trae rerank_score, summary y, si se pide, el contenido completo de la página. Usa los mismos operadores de búsqueda (site:, lang:, after:) pero bajo la cuenta y la facturación que ya tiene el desarrollador con DeepSeek.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Cómo se autentica la Fetch API de DeepSeek?
&lt;/h3&gt;

&lt;p&gt;Con el mismo esquema que el resto de la plataforma: una cabecera Authorization: Bearer  y Content-Type: application/json. No existe una clave separada para búsqueda o fetch.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué significa el rerank_score que devuelve el buscador propio de DeepSeek?
&lt;/h3&gt;

&lt;p&gt;Es un número de 0 a 3 que combina relevancia, autoridad y frescura de cada página. Según la documentación oficial, 1.5 o más indica un resultado de alta calidad; valores más bajos no significan que la página esté rota, solo que el sistema la considera menos relevante para esa consulta puntual.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿El buscador propio de DeepSeek reemplaza un pipeline de RAG completo?
&lt;/h3&gt;

&lt;p&gt;No. Cubre la parte de recuperación de contenido público de la web vigente, pero no reemplaza embeddings, bases vectoriales ni el chunking de documentos privados. Para datos propios de una empresa, sigue siendo complementario a un pipeline de RAG tradicional, no un sustituto.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué pasa si need_content queda en false al llamar al buscador propio de DeepSeek?
&lt;/h3&gt;

&lt;p&gt;El campo content de cada resultado llega vacío; solo se completa cuando need_content se manda en true, lo que además suma latencia porque DeepSeek tiene que descargar la página completa antes de responder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://api-docs.deepseek.com/guides/search/" rel="noopener noreferrer"&gt;DeepSeek API Docs&lt;/a&gt;: guía oficial de Search y Fetch API, con esquemas de request y response.- &lt;a href="https://api-docs.deepseek.com/" rel="noopener noreferrer"&gt;DeepSeek API Docs&lt;/a&gt;: catálogo completo de endpoints de la plataforma, incluida la autenticación Bearer.- &lt;a href="https://www.deepseek.com/" rel="noopener noreferrer"&gt;DeepSeek&lt;/a&gt;: sitio oficial de la compañía y sus modelos.- &lt;a href="https://en.wikipedia.org/wiki/DeepSeek" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;: historia y contexto de DeepSeek como laboratorio de IA.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📱 &lt;strong&gt;¿Te gusta este contenido?&lt;/strong&gt; Únete a nuestro canal de Telegram &lt;a href="https://t.me/programacion" rel="noopener noreferrer"&gt;@programacion&lt;/a&gt; donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.&lt;/p&gt;

</description>
      <category>technology</category>
      <category>science</category>
      <category>programming</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Furifont: cómo OpenType incrusta la furigana en la fuente</title>
      <dc:creator>lu1tr0n</dc:creator>
      <pubDate>Sun, 18 Oct 2026 02:47:23 +0000</pubDate>
      <link>https://dev.to/lu1tr0n/furifont-como-opentype-incrusta-la-furigana-en-la-fuente-25fa</link>
      <guid>https://dev.to/lu1tr0n/furifont-como-opentype-incrusta-la-furigana-en-la-fuente-25fa</guid>
      <description>&lt;p&gt;Furifont incrusta la furigana en la fuente misma: no hace falta el tag &lt;code&gt;&amp;lt;ruby&amp;gt;&lt;/code&gt; ni una sola línea de CSS para que la lectura en kana aparezca arriba del kanji correcto.&lt;/p&gt;

&lt;p&gt;El proyecto, publicado por el desarrollador Anthony Fu en &lt;a href="https://furifont.antfu.me/" rel="noopener noreferrer"&gt;furifont.antfu.me&lt;/a&gt;, usa sustituciones contextuales y variantes estilísticas de OpenType (las mismas funciones que combinan letras en una ligadura o cambian el estilo de un número) para resolver un problema tipográfico que hasta ahora dependía de markup externo.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Furifont dibuja la furigana dentro del glifo del kanji usando sustituciones contextuales de OpenType, sin ruby ni CSS.- GSUB, la tabla de sustitución de glifos, elige la lectura correcta según el kanji vecino sin una línea de JavaScript.- Elegir la lectura manualmente es posible con variantes estilísticas cuando un kanji admite varias pronunciaciones.- Sin soporte de esas funciones de OpenType, el navegador muestra el kanji solo, sin romper el resto del layout.- La misma técnica sirve para pinyin, transliteración árabe o cualquier anotación que hoy depende de HTML extra.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ¿Qué es Furifont?
&lt;/h2&gt;

&lt;p&gt;Furifont es una fuente OpenType experimental que incrusta la furigana en la fuente mediante sustituciones de glifos: cada kanji con su lectura se reemplaza por un glifo compuesto que ya incluye los caracteres kana en miniatura, sin marcado adicional en el documento.&lt;/p&gt;

&lt;p&gt;La furigana en sí no es nueva: los diccionarios y los libros infantiles japoneses la usan desde hace más de un siglo para anotar la pronunciación de un kanji complejo arriba o al costado del carácter principal. Internet resolvió el problema con el elemento &lt;code&gt;&amp;lt;ruby&amp;gt;&lt;/code&gt; de HTML, estandarizado por el W3C, pero ese marcado depende de que cada plataforma (un lector de EPUB, un generador de PDF, un editor de texto plano) lo implemente correctamente.&lt;/p&gt;

&lt;p&gt;Furifont ataca el problema desde otro ángulo, pidiéndole a la fuente que haga el trabajo en lugar de depender de una etiqueta nueva. Cualquier aplicación que ya sabe renderizar texto con OpenType, que es prácticamente todo el software moderno, muestra la furigana sin ningún cambio de código.&lt;/p&gt;

&lt;p&gt;Para alguien que recién empieza a leer japonés, la furigana cumple la misma función que un pequeño cartel de pronunciación al lado de una palabra difícil: no reemplaza aprender el kanji, pero permite leer el texto completo desde el primer día sin frenar cada tres palabras a buscar en un diccionario.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué importa la furigana embebida en glifos
&lt;/h2&gt;

&lt;p&gt;El soporte de &lt;code&gt;&amp;lt;ruby&amp;gt;&lt;/code&gt; en la web es parejo, pero fuera del navegador es irregular. Generadores de PDF, motores de slides y varios lectores de EPUB todavía no posicionan la furigana donde corresponde, o simplemente la ignoran. Para un material educativo que necesita verse igual en una app, un PDF impreso y una página web, cada renderer que falla es un dolor de cabeza distinto.&lt;/p&gt;

&lt;p&gt;Al mover la anotación a la fuente, Furifont saca ese problema de la ecuación. Si el software sabe mostrar texto en japonés con una tipografía OpenType moderna, algo que cubre a casi todo el software actual, ya sabe mostrar la furigana. No hay que parchear el lector de EPUB ni el generador de PDF.&lt;/p&gt;

&lt;p&gt;La inconsistencia no es teórica. Algunos lectores de EPUB posicionan la furigana demasiado cerca de la línea anterior cuando el texto tiene mucho kanji consecutivo, y ciertos generadores de PDF basados en motores de layout más viejos ni siquiera reconocen bien el markup ruby, mostrando la lectura como texto suelto fuera de lugar.&lt;/p&gt;

&lt;p&gt;El valor del proyecto no está solo en resolver furigana. Fu lo plantea como demostración de una técnica más amplia de font engineering: cualquier anotación secundaria que hoy dependa de markup (el pinyin sobre un carácter chino, la vocalización sobre una consonante árabe, una transcripción fonética sobre una palabra) se puede empujar hacia la capa de la fuente con las mismas herramientas de OpenType.&lt;br&gt;
La furigana acompaña al kanji en diccionarios japoneses desde hace más de un siglo.&lt;/p&gt;
&lt;h2&gt;
  
  
  Cómo funciona la furigana en la fuente
&lt;/h2&gt;

&lt;p&gt;Todo pasa en la etapa de shaping, el paso donde un motor de texto convierte una cadena Unicode en una secuencia de glifos con posición. Un navegador, un editor o un sistema operativo no dibuja letras, dibuja los glifos que la fuente le indica, siguiendo las reglas que trae la fuente misma en sus tablas internas.&lt;/p&gt;
&lt;h3&gt;
  
  
  El pipeline de shaping: de texto a glifo compuesto
&lt;/h3&gt;

&lt;p&gt;Motores de shaping como HarfBuzz (usado por Chrome, Firefox y la mayoría de Linux) o CoreText en macOS reciben la cadena de kanji y consultan la tabla GSUB (Glyph Substitution) de la fuente. Esa tabla define reglas: si aparece tal secuencia de caracteres en tal contexto, sustituí el glifo simple por otro. Furifont define, para cada kanji que soporta, una regla que apunta a un glifo compuesto, y ese glifo nuevo no es solo el trazo del kanji: incluye además los caracteres kana de la lectura dibujados en miniatura, ya posicionados y escalados dentro del mismo contorno.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dot"&gt;&lt;code&gt;&lt;span class="nv"&gt;flowchart&lt;/span&gt; &lt;span class="nv"&gt;TD&lt;/span&gt;
&lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Texto: kanji y contexto"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;B&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Motor de shaping (HarfBuzz)"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;B&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;C&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Tabla GSUB: lookup calt"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;C&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;D&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Existe regla contextual"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;D&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;|&lt;/span&gt;&lt;span class="s2"&gt;"Si"&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;E&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Sustituye por glifo compuesto"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;D&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;|&lt;/span&gt;&lt;span class="s2"&gt;"No"&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;F&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Muestra el kanji sin furigana"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;E&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;G&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Glifo con kana en miniatura"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Sustituciones contextuales (GSUB) para elegir la lectura correcta
&lt;/h3&gt;

&lt;p&gt;La función &lt;code&gt;calt&lt;/code&gt; (contextual alternates) es la pieza clave: decide qué glifo mostrar según los caracteres vecinos, no solo según el carácter aislado. Esto importa porque un mismo kanji puede leerse de maneras distintas según la palabra que forma. Una regla &lt;code&gt;calt&lt;/code&gt; bien escrita mira el kanji siguiente o anterior y elige el glifo compuesto con la lectura correcta para ese contexto específico.&lt;/p&gt;

&lt;h3&gt;
  
  
  Variantes estilísticas para las excepciones
&lt;/h3&gt;

&lt;p&gt;El contexto no siempre alcanza para desambiguar. Cuando un kanji admite varias lecturas válidas en el mismo contexto, algo común en nombres propios japoneses, Furifont recurre a las variantes estilísticas, la familia de funciones &lt;code&gt;ss01&lt;/code&gt; a &lt;code&gt;ss20&lt;/code&gt; que el estándar OpenType reserva para que un diseñador o el propio usuario elija manualmente entre glifos alternativos. En lugar de que la fuente adivine, el documento activa una variante estilística específica para forzar la lectura correcta en ese kanji puntual.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cómo se construye el glifo compuesto
&lt;/h3&gt;

&lt;p&gt;Diseñar cada glifo compuesto es trabajo de tipografía, no de programación. En una herramienta como FontForge o Glyphs, el diseñador toma el contorno del kanji, agrega los contornos de los caracteres kana reducidos a una fracción del tamaño y los posiciona en el espacio superior del glifo, ajustando el avance horizontal para que el conjunto no choque con el glifo vecino. Ese glifo nuevo se guarda con su propio nombre dentro de la tabla de glifos de la fuente (&lt;code&gt;glyf&lt;/code&gt; en TrueType o &lt;code&gt;CFF&lt;/code&gt; en fuentes OpenType-CFF), y la tabla GSUB simplemente indica que, cuando aparezca el glifo original de ese kanji en ese contexto, hay que usar este otro glifo en su lugar. Herramientas como &lt;code&gt;fonttools&lt;/code&gt;, la librería en Python que lee y escribe binarios de fuentes, permiten automatizar buena parte de ese mapeo generando reglas GSUB a partir de una lista de pares kanji-lectura en vez de escribirlas a mano una por una.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ejemplos prácticos y cómo empezar
&lt;/h2&gt;

&lt;p&gt;Probar la anotación furigana incrustada no requiere compilar nada: alcanza con cargar la fuente vía &lt;code&gt;@font-face&lt;/code&gt; y activar las funciones de OpenType correctas desde CSS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Activar las funciones por CSS
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@font-face&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"Furifont"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url("Furifont.ttf")&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;"truetype"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.furigana&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"Furifont"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-feature-settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"calt"&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este bloque carga la fuente y activa &lt;code&gt;calt&lt;/code&gt;, la función que dispara las sustituciones contextuales. Con esto, un &lt;code&gt;&amp;lt;span class="furigana"&amp;gt;&lt;/code&gt; que contenga un kanji soportado por Furifont ya debería mostrar la lectura en miniatura arriba, sin ningún otro cambio en el HTML.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.furigana-manual&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"Furifont"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-feature-settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"calt"&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;"ss03"&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Acá se suma &lt;code&gt;ss03&lt;/code&gt; para forzar una variante estilística puntual, útil cuando el kanji tiene una lectura ambigua que el contexto solo no puede resolver, por ejemplo un nombre propio. El número de variante depende de cómo Furifont mapeó cada lectura alternativa en su propia tabla GSUB.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verificar que la sustitución está activa
&lt;/h3&gt;

&lt;p&gt;Firefox trae un inspector de fuentes dentro de las herramientas de desarrollo, en la pestaña Fuentes del panel de Inspector, que lista para el texto seleccionado qué funciones OpenType trae la fuente y cuáles están activas. Si &lt;code&gt;calt&lt;/code&gt; aparece marcado y el kanji en pantalla muestra la furigana, la sustitución está funcionando. Si el kanji se ve solo, sin la lectura arriba, lo primero a revisar es que &lt;code&gt;font-feature-settings&lt;/code&gt; llegó al elemento y no fue sobrescrito por otra regla con mayor especificidad.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dot"&gt;&lt;code&gt;&lt;span class="nv"&gt;flowchart&lt;/span&gt; &lt;span class="nv"&gt;LR&lt;/span&gt;
&lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Fuente Furifont cargada"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;B&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Renderizador soporta GSUB contextual"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;B&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;|&lt;/span&gt;&lt;span class="s2"&gt;"Si"&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;C&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Muestra kanji con furigana incrustada"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;B&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;|&lt;/span&gt;&lt;span class="s2"&gt;"No"&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;D&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Muestra solo el kanji base"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;C&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;E&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Texto copiado conserva solo el kanji"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;D&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;E&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Casos de uso reales
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Libros electrónicos&lt;/strong&gt;: el soporte de &lt;code&gt;&amp;lt;ruby&amp;gt;&lt;/code&gt; varía entre lectores de EPUB, así que incrustar la furigana en la fuente evita que la lectura desaparezca en un dispositivo que ignora el markup.- &lt;strong&gt;Material de aprendizaje de japonés&lt;/strong&gt;: apps y hojas de práctica que solo necesitan tipear el kanji y dejar que la fuente muestre la lectura.- &lt;strong&gt;Documentos y PDFs generados por sistemas que no controlan HTML&lt;/strong&gt;: reportes, facturas o formularios donde agregar &lt;code&gt;&amp;lt;ruby&amp;gt;&lt;/code&gt; no es una opción.- &lt;strong&gt;Subtitulado y señalética digital&lt;/strong&gt;: pantallas que renderizan texto plano sin motor de ruby, donde forzar una variante estilística puntual es más simple que integrar un parser de markup nuevo.- &lt;strong&gt;Font engineering general&lt;/strong&gt;: la misma idea de sustitución contextual sirve para incrustar pinyin sobre caracteres chinos o vocalización sobre consonantes árabes.&lt;br&gt;
OpenType reserva hasta 20 variantes estilísticas, ss01 a ss20, por fuente.&lt;/p&gt;
&lt;h2&gt;
  
  
  Errores comunes y buenas prácticas
&lt;/h2&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Copiar y pegar pierde la lectura&lt;/strong&gt;: el texto subyacente sigue siendo solo el kanji, la furigana es puramente visual.- &lt;strong&gt;Explosión de glifos&lt;/strong&gt;: cada combinación de kanji con lectura necesita su propio glifo compuesto, así que una fuente completa no puede cubrir todas las lecturas posibles.- &lt;strong&gt;Motores de render antiguos&lt;/strong&gt;: si el software no implementa GSUB contextual o ignora &lt;code&gt;calt&lt;/code&gt; por defecto, el kanji se muestra solo.- &lt;strong&gt;Altura de línea&lt;/strong&gt;: el glifo compuesto ocupa más alto que un kanji normal, así que hay que ajustar el line-height a mano para que la furigana no se corte.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💭 Clave:&lt;/strong&gt; Furifont resuelve la presentación de la furigana, no su accesibilidad: para un lector de pantalla o un buscador, el texto sigue siendo solo el kanji.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Comparativa con alternativas
&lt;/h2&gt;

&lt;p&gt;La tabla siguiente compara la furigana en la fuente con las alternativas más usadas para anotar lecturas en texto japonés, en la web y fuera de ella.&lt;br&gt;
OpciónCuándo usarlaVentajaLimitación/ HTMLPáginas web donde controlás el markupEstándar, accesible, el texto real incluye la lecturaRequiere marcar cada palabra a mano y soporte del rendererCSS generado (content/attr)Cuando no podés tocar el HTML pero sí el CSSNo duplica el kanji en el DOMNo es accesible para lectores de pantalla ni copiableFurifont (furigana en la fuente)Documentos, PDFs o apps sin control sobre el motor de renderCero marcado adicional, funciona en cualquier texto planoEl archivo de fuente crece y el texto copiado pierde la lecturaImagen o SVG prerenderizadoSeñalética o materiales impresos fijosControl visual totalNo es texto real: no se puede buscar, copiar ni reflow&lt;/p&gt;

&lt;h2&gt;
  
  
  Profundizando: dentro de la tabla GSUB
&lt;/h2&gt;

&lt;p&gt;La especificación de OpenType define varios tipos de lookup dentro de GSUB, numerados del 1 al 8. Una ligadura simple, como combinar dos letras en un solo glifo, usa el lookup tipo 4. Lo que hace Furifont se acerca más al tipo 6, sustitución contextual encadenada, que permite mirar glifos antes y después del objetivo antes de decidir el reemplazo. Esa es la maquinaria que &lt;code&gt;calt&lt;/code&gt; expone al diseñador de la fuente sin que el autor del documento tenga que escribir una sola regla.&lt;/p&gt;

&lt;p&gt;El costo de este enfoque es combinatorio. Cada combinación de kanji más lectura que la fuente quiera soportar necesita su propio glifo compuesto, dibujado a mano o generado por script, con la kana en miniatura ya posicionada dentro del contorno. Un kanji con cinco lecturas comunes implica cinco glifos distintos en la fuente, no uno. Esto pone un techo práctico a cuántas combinaciones puede cubrir una sola fuente antes de que el archivo crezca demasiado, así que proyectos como este suelen enfocarse en un vocabulario acotado en lugar de cubrir los miles de kanji de uso común con todas sus lecturas.&lt;/p&gt;

&lt;p&gt;Una alternativa técnica que la fuente también podría explotar es GPOS, la tabla de posicionamiento de glifos que usa el mismo mecanismo que acentos y diacríticos combinables en escrituras como el árabe o el hebreo. En vez de reemplazar el kanji por un glifo compuesto, la fuente podría adjuntar los caracteres kana como marcas ancladas sobre el kanji base, reutilizando el mismo glifo del kanji en otros contextos. Esa ruta reduce la explosión de glifos, pero complica el control fino sobre el tamaño y la posición exacta de cada kana, que es justamente lo que un glifo compuesto resuelve de una sola vez.&lt;/p&gt;

&lt;p&gt;El shaping ocurre una sola vez por cada corrida de texto y el motor de layout cachea el resultado, así que no hay costo de rendimiento en tiempo de ejecución más allá del que ya paga cualquier texto con ligaduras o kerning. La limitación real es otra: el texto subyacente, lo que queda en el portapapeles, lo que indexa un buscador, lo que lee un lector de pantalla, sigue siendo solo el kanji. La furigana vive en la capa visual, no en los datos, algo que el elemento &lt;code&gt;&amp;lt;ruby&amp;gt;&lt;/code&gt; de HTML sí resuelve porque la lectura es texto real dentro del documento.&lt;/p&gt;

&lt;p&gt;La idea de resolver anotaciones tipográficas dentro de la fuente, en lugar de en el documento, no es exclusiva de Furifont. Los sistemas de tipografía asiática llevan décadas lidiando con el problema de dónde vive la información de lectura, y el propio ruby de HTML llegó tarde: recién se volvió ampliamente interoperable en navegadores bien entrada la década de 2010. Lo que aporta Furifont es demostrar la técnica completa, de punta a punta, para construir una tipografía con lectura fonética incrustada usando solo funciones estándar de OpenType que cualquier fuente puede adoptar sin esperar un cambio en ningún navegador.&lt;/p&gt;

&lt;p&gt;📖 Resumen en Telegram: &lt;a href="https://telegra.ph/Furifont-c%C3%B3mo-OpenType-incrusta-la-furigana-en-la-fuente-10-18" rel="noopener noreferrer"&gt;Ver resumen&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tu próximo paso: descargá la fuente de prueba desde furifont.antfu.me, armá un &lt;code&gt;span&lt;/code&gt; con &lt;code&gt;font-feature-settings: "calt" 1;&lt;/code&gt; y confirmá en el inspector de fuentes de Firefox que la sustitución contextual se disparó.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿Furifont reemplaza al elemento HTML ruby?
&lt;/h3&gt;

&lt;p&gt;No lo reemplaza en todos los casos: &lt;code&gt;&amp;lt;ruby&amp;gt;&lt;/code&gt; sigue siendo la opción correcta cuando necesitás que la lectura exista como texto real, accesible y buscable. Furifont tiene sentido cuando el canal de salida no te deja controlar el markup, como un PDF generado automáticamente o un lector de EPUB que ignora &lt;code&gt;&amp;lt;ruby&amp;gt;&lt;/code&gt;. Para contenido nuevo donde controlás el pipeline completo, &lt;code&gt;&amp;lt;ruby&amp;gt;&lt;/code&gt; sigue siendo la opción más robusta a largo plazo.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué pasa si el navegador no soporta las funciones OpenType que usa Furifont?
&lt;/h3&gt;

&lt;p&gt;Si el motor de shaping no aplica &lt;code&gt;calt&lt;/code&gt; ni las variantes estilísticas, simplemente muestra el glifo base del kanji, sin la furigana. No rompe el layout ni genera un carácter faltante, pero el lector pierde la anotación.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Se puede copiar el texto y conservar la lectura en kana?
&lt;/h3&gt;

&lt;p&gt;No. La sustitución ocurre solo en la capa visual del glifo: el texto Unicode subyacente sigue siendo el kanji original, así que copiar y pegar, buscar en la página o pasarlo por un lector de pantalla no recupera la lectura en kana.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Furifont funciona con cualquier tipografía japonesa?
&lt;/h3&gt;

&lt;p&gt;No: la anotación furigana tiene que estar diseñada dentro de esa fuente específica, glifo por glifo. No es una función que se le pueda sumar a cualquier tipografía japonesa existente sin rediseñar sus tablas GSUB y agregar los glifos compuestos correspondientes. Migrar una tipografía existente implica, en la práctica, generar una fuente derivada solo para este propósito.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿La anotación furigana incrustada sirve para otros idiomas además del japonés?
&lt;/h3&gt;

&lt;p&gt;La técnica sí: cualquier escritura que combine un carácter principal con una anotación fonética secundaria, como el pinyin sobre caracteres chinos o la vocalización sobre consonantes árabes o hebreas, puede usar el mismo mecanismo de sustituciones contextuales y variantes estilísticas de OpenType.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Cuánto crece el archivo de la fuente al incrustar furigana en los glifos?
&lt;/h3&gt;

&lt;p&gt;Crece en proporción directa a cuántas combinaciones de kanji y lectura soporte: cada combinación nueva es un glifo compuesto adicional en la tabla de glifos. Por eso ninguna fuente de este tipo intenta cubrir los miles de kanji de uso común con todas sus lecturas posibles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://furifont.antfu.me/" rel="noopener noreferrer"&gt;Furifont&lt;/a&gt;: sitio oficial del proyecto de Anthony Fu, con la demo de furigana incrustada en la fuente.- &lt;a href="https://en.wikipedia.org/wiki/Furigana" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;: historia y uso de la furigana en la tipografía japonesa.- &lt;a href="https://learn.microsoft.com/en-us/typography/opentype/spec/" rel="noopener noreferrer"&gt;Microsoft Typography&lt;/a&gt;: especificación oficial de OpenType, incluida la tabla GSUB y sus funciones de layout.- &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/font-feature-settings" rel="noopener noreferrer"&gt;MDN&lt;/a&gt;: documentación de la propiedad CSS &lt;code&gt;font-feature-settings&lt;/code&gt; para activar funciones OpenType.- &lt;a href="https://en.wikipedia.org/wiki/OpenType" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;: antecedentes técnicos del formato de fuentes OpenType.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📱 &lt;strong&gt;¿Te gusta este contenido?&lt;/strong&gt; Únete a nuestro canal de Telegram &lt;a href="https://t.me/programacion" rel="noopener noreferrer"&gt;@programacion&lt;/a&gt; donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.&lt;/p&gt;

</description>
      <category>technology</category>
      <category>science</category>
      <category>programming</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Pele: la librería que renderiza Mermaid a SVG puro</title>
      <dc:creator>lu1tr0n</dc:creator>
      <pubDate>Sat, 17 Oct 2026 20:31:08 +0000</pubDate>
      <link>https://dev.to/lu1tr0n/pele-la-libreria-que-renderiza-mermaid-a-svg-puro-3b9n</link>
      <guid>https://dev.to/lu1tr0n/pele-la-libreria-que-renderiza-mermaid-a-svg-puro-3b9n</guid>
      <description>&lt;p&gt;Un sitio de documentación carga hoy 1,6 MB de JavaScript solo para convertir texto en diagramas Mermaid en SVG. &lt;a href="https://pele.run/" rel="noopener noreferrer"&gt;Pele&lt;/a&gt; logra lo mismo con 230 kB comprimidos y sin ninguna dependencia declarada.&lt;/p&gt;

&lt;p&gt;La librería acepta la misma sintaxis que ya usás en Mermaid.js, pero cambia cómo se pintan los colores: en vez de pasar un objeto de tema y volver a ejecutar el renderizador, lee variables CSS directamente del navegador.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Pele renderiza sintaxis Mermaid a SVG en un bundle de 230 kB gzip, frente a 1,6 MB de Mermaid.js.- render() devuelve el SVG en 0,05 a 0,3 ms, contra 6 a 82 ms de Mermaid.js.- Las variables --pele-bg, --pele-fg y --pele-line recolorean el diagrama sin volver a renderizarlo.- Cero dependencias directas reducen la superficie de auditoría frente a las 23 de Mermaid.js.- npm install pele habilita render() en cualquier proyecto de JavaScript o TypeScript.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ¿Qué es Pele?
&lt;/h2&gt;

&lt;p&gt;Pele es una librería de JavaScript de código abierto que renderiza diagramas Mermaid en SVG sin depender de un motor de layout externo ni de un framework de UI. Pesa 230 kB comprimidos, no declara dependencias directas y expone sus colores como &lt;strong&gt;variables CSS&lt;/strong&gt; que cualquier hoja de estilos puede sobrescribir.&lt;/p&gt;

&lt;p&gt;El proyecto vive en &lt;a href="https://pele.run/" rel="noopener noreferrer"&gt;pele.run&lt;/a&gt;, donde el sitio oficial funciona también como editor interactivo: se escribe sintaxis Mermaid a la izquierda y el SVG resultante aparece a la derecha, con un panel aparte para probar las variables de color en vivo.&lt;/p&gt;

&lt;p&gt;La propuesta de valor no es agregar funciones nuevas sobre Mermaid.js, sino resolver un problema puntual: renderizar los mismos diagramas con menos peso y menos superficie de dependencias.&lt;br&gt;
El bundle de Mermaid.js pesa 1,6 MB comprimido frente a los 230 kB de Pele.&lt;/p&gt;
&lt;h2&gt;
  
  
  Por qué importan los diagramas Mermaid en SVG en documentación técnica
&lt;/h2&gt;

&lt;p&gt;Cada diagrama en una página de documentación suma al JavaScript que el navegador tiene que descargar, parsear y ejecutar antes de mostrar nada. Con Mermaid.js, &lt;a href="https://pele.run/" rel="noopener noreferrer"&gt;esa factura ronda 1,6 MB comprimidos y 23 dependencias directas&lt;/a&gt;, aunque la página solo muestre tres cajas y dos flechas.&lt;/p&gt;

&lt;p&gt;Ese costo pesa distinto según el proyecto. Un blog técnico con un diagrama por artículo lo nota poco. Una herramienta interna que renderiza decenas de diagramas por sesión (un visor de arquitectura, un editor de runbooks, un generador de documentación a partir de código) sí lo nota, porque cada render repite el costo de parseo y layout.&lt;/p&gt;

&lt;p&gt;Pele ataca justamente ese caso. Su &lt;a href="https://pele.run/" rel="noopener noreferrer"&gt;bundle gzip pesa 230 kB y su tiempo de render va de 0,05 a 0,3 ms&lt;/a&gt;, frente a 6 a 82 ms de Mermaid.js. La diferencia se nota más cuanto más diagramas hay en una misma página, porque cada diagrama paga su propio tiempo de render.&lt;/p&gt;
&lt;h2&gt;
  
  
  Cómo funciona Pele por dentro
&lt;/h2&gt;

&lt;p&gt;La función central de la librería es &lt;code&gt;render()&lt;/code&gt;: recibe un string con sintaxis Mermaid y devuelve un objeto con una propiedad &lt;code&gt;svg&lt;/code&gt;, que es el markup SVG completo listo para insertar en el DOM. No hay paso intermedio de configuración ni un objeto de tema que pasar como segundo argumento.&lt;/p&gt;

&lt;p&gt;La parte que distingue a Pele está en cómo ese SVG define sus colores. En vez de escribir atributos con valores fijos calculados en el momento del render, los elementos del SVG referencian variables CSS, por ejemplo &lt;code&gt;fill="var(--pele-fg)"&lt;/code&gt;. El navegador resuelve esa variable al pintar, no la librería al generar el SVG.&lt;/p&gt;

&lt;p&gt;Esa diferencia es la que permite cambiar de tema sin volver a renderizar. Si el color estuviera fijo en el atributo, cambiar de claro a oscuro obligaría a llamar &lt;code&gt;render()&lt;/code&gt; de nuevo y reemplazar el SVG completo en el DOM; con variables CSS alcanza con cambiar el valor de &lt;code&gt;--pele-bg&lt;/code&gt; en una regla CSS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dot"&gt;&lt;code&gt;&lt;span class="nv"&gt;flowchart&lt;/span&gt; &lt;span class="nv"&gt;LR&lt;/span&gt;
 &lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Sintaxis Mermaid"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;B&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Parser interno"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
 &lt;span class="nv"&gt;B&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;C&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Calculo de layout"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
 &lt;span class="nv"&gt;C&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;D&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"SVG con var(--pele-fg)"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
 &lt;span class="nv"&gt;D&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;E&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Navegador"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El mismo mecanismo explica por qué &lt;a href="https://pele.run/" rel="noopener noreferrer"&gt;la demo oficial permite cambiar colores sin que el diagrama se vuelva a dibujar&lt;/a&gt;: el paso "SVG con var(--pele-fg)" del diagrama de arriba ya ocurrió una sola vez, y de ahí en adelante el navegador se encarga del repintado.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ejemplos prácticos con código progresivo
&lt;/h2&gt;

&lt;p&gt;El primer ejemplo es el equivalente a un hola mundo: tomar un diagrama de flujo simple y mostrarlo en pantalla.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&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;render&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;pele&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;svg&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`flowchart LR
  A[Build] --&amp;gt; B[Test]
  B --&amp;gt; C[Deploy]`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#diagrama&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;svg&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;svg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; {
  contenedor.classList.toggle(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;oscuro&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;);
});
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sequenceDiagram
    participant U as Usuario
    participant J as JS de la pagina
    participant C as CSS
    participant S as SVG renderizado
    U-&amp;gt;&amp;gt;J: clic en boton modo oscuro
    J-&amp;gt;&amp;gt;C: alterna la clase oscuro
    C-&amp;gt;&amp;gt;S: el navegador repinta var(--pele-bg)
    Note over S: no se llama render de nuevo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para confirmar que el theming está realmente activo hay que inspeccionar el elemento en las devtools del navegador: si el atributo &lt;code&gt;fill&lt;/code&gt; de un nodo muestra &lt;code&gt;var(--pele-fg)&lt;/code&gt; en lugar de un valor hexadecimal fijo, la variable está conectada. Si en cambio aparece un color fijo, algo en el build (un minificador de CSS agresivo, por ejemplo) resolvió la variable antes de tiempo.&lt;br&gt;
Mermaid.js declara 23 dependencias directas; Pele no declara ninguna.&lt;/p&gt;
&lt;h2&gt;
  
  
  Cómo empezar: instalación y primer render
&lt;/h2&gt;

&lt;p&gt;Lo único que necesitás antes de instalar Pele es tener Node.js y un gestor de paquetes en la máquina; el comando es idéntico en Windows, macOS y Linux.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;pele
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si tu proyecto usa otro gestor, el paquete se instala igual con &lt;code&gt;pnpm add pele&lt;/code&gt;, &lt;code&gt;yarn add pele&lt;/code&gt; o &lt;code&gt;bun add pele&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Con el paquete instalado, el primer render completo en una página HTML queda así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&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;render&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;pele&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;diagrama&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`flowchart TD
  A[Peticion HTTP] --&amp;gt; B{Cache valido?}
  B -- Si --&amp;gt; C[Responder desde cache]
  B -- No --&amp;gt; D[Consultar origen]
  D --&amp;gt; E[Guardar en cache]
  E --&amp;gt; C`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;svg&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;diagrama&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#diagrama&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No hay paso de inicialización ni un objeto de configuración que crear antes de llamar a &lt;code&gt;render()&lt;/code&gt;. Eso es consistente con la cifra de &lt;a href="https://pele.run/" rel="noopener noreferrer"&gt;cero dependencias directas&lt;/a&gt;: no hay un motor de plugins que configurar primero.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Tip:&lt;/strong&gt; combinar las variables CSS de Pele con &lt;code&gt;prefers-color-scheme&lt;/code&gt; evita escribir JavaScript para el modo oscuro: una media query que redefina &lt;code&gt;--pele-bg&lt;/code&gt; y &lt;code&gt;--pele-fg&lt;/code&gt; alcanza para que el diagrama respete la preferencia del sistema operativo sin un listener de clic.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Casos de uso reales
&lt;/h2&gt;

&lt;p&gt;El caso más directo es cualquier sitio estático que ya escribe diagramas en sintaxis Mermaid dentro de Markdown: generadores de documentación, wikis internas, blogs técnicos. En vez de cargar el bundle completo de Mermaid.js en cada página, un paso de build puede recorrer los bloques de código marcados como Mermaid y llamar a &lt;code&gt;render()&lt;/code&gt; una sola vez por diagrama, dejando el SVG ya resuelto en el HTML final.&lt;/p&gt;

&lt;p&gt;Otro caso es el de herramientas internas que generan diagramas dinámicamente a partir de datos: un visor de dependencias entre microservicios, un editor de runbooks de incidentes, un panel que dibuja el estado de una pipeline de CI en tiempo real. Ahí el tiempo de render por diagrama se nota en la latencia percibida de la interfaz.&lt;/p&gt;

&lt;p&gt;Un tercer caso es el de editores o extensiones que muestran una vista previa del diagrama mientras el usuario escribe: ahí el costo de re-renderizar en cada tecla es lo primero que se siente si la librería es pesada.&lt;/p&gt;

&lt;h2&gt;
  
  
  Errores comunes y buenas prácticas
&lt;/h2&gt;

&lt;p&gt;El error más común es olvidar la clase &lt;code&gt;pele&lt;/code&gt; (o el equivalente que definas) en un ancestro del SVG. Las variables CSS de Pele no son globales por defecto: si las declarás en una clase que nunca envuelve al contenedor del diagrama, el SVG usa sus valores por defecto y el cambio de color simplemente no aparece.&lt;/p&gt;

&lt;p&gt;El segundo error es confundir qué cambios necesitan volver a llamar &lt;code&gt;render()&lt;/code&gt; y cuáles no. Cambiar un color definido como variable CSS no lo necesita, el navegador repinta solo. Pero cambiar el &lt;strong&gt;contenido&lt;/strong&gt; del diagrama sí requiere ejecutar &lt;code&gt;render()&lt;/code&gt; de nuevo con el string actualizado: las variables CSS resuelven el theming, no la estructura.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Ojo:&lt;/strong&gt; si el texto que le pasás a &lt;code&gt;render()&lt;/code&gt; viene de un usuario final y no de tu propio código, tratalo como entrada no confiable antes de insertar el resultado con &lt;code&gt;innerHTML&lt;/code&gt;. Un SVG puede incluir elementos como &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; o manejadores de evento embebidos; sanitizar el SVG resultante (por ejemplo con DOMPurify configurado para SVG) es una precaución razonable en cualquier flujo donde el diagrama no lo escribiste vos.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Por último, antes de asumir que Pele corre igual en un script de build de Node que en el navegador, probalo en ese entorno concreto: la demo de &lt;a href="https://pele.run/" rel="noopener noreferrer"&gt;pele.run&lt;/a&gt; muestra el uso en el navegador, y confirmar que &lt;code&gt;render()&lt;/code&gt; no depende de ninguna API específica del DOM es algo que conviene verificar en tu propio proyecto antes de depender de ello en producción.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparativa: Pele vs Mermaid.js
&lt;/h2&gt;

&lt;p&gt;La tabla resume las cifras que &lt;a href="https://pele.run/" rel="noopener noreferrer"&gt;pele.run&lt;/a&gt; publica comparando ambas librerías sobre el mismo tipo de diagrama.&lt;br&gt;
MétricaPeleMermaid.jsBundle (gzip)230 kB1,6 MBTiempo de renderizado0,05 a 0,3 ms6 a 82 msDependencias directas023ThemingVariables CSS, sin re-renderObjeto de configuración de temaPantallas pequeñasAdapta el layoutEscala el diagrama completo&lt;br&gt;
La fila de pantallas pequeñas importa más de lo que parece: adaptar el layout significa reorganizar nodos para que entren en el ancho disponible, mientras que escalar el diagrama completo achica también el texto, que puede quedar ilegible en una pantalla de teléfono.&lt;/p&gt;
&lt;h2&gt;
  
  
  Profundizando: theming, accesibilidad y límites de Pele
&lt;/h2&gt;

&lt;p&gt;El modelo de variables CSS tiene una ventaja de accesibilidad que no es obvia a primera vista: como el texto del diagrama sigue siendo texto SVG real, no una imagen rasterizada, un lector de pantalla puede leerlo y un usuario puede seleccionarlo y copiarlo. Esto no es exclusivo de Pele, Mermaid.js también emite SVG con texto real, pero es una propiedad que se pierde si en algún punto del pipeline el SVG se convierte a PNG para incrustarlo en un PDF.&lt;/p&gt;

&lt;p&gt;La elección entre adaptar el layout o escalar el diagrama completo en pantallas chicas es una decisión de diseño con compromisos en ambas direcciones: reorganizar nodos mantiene el texto legible pero puede alargar mucho el diagrama en vertical, mientras que escalar todo preserva las proporciones originales pero sacrifica legibilidad. Una jerarquía angosta se adapta mejor a reorganizarse; un diagrama ancho con muchos nodos en el mismo nivel sufre más con cualquiera de las dos estrategias.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dot"&gt;&lt;code&gt;&lt;span class="nv"&gt;flowchart&lt;/span&gt; &lt;span class="nv"&gt;TD&lt;/span&gt;
 &lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Necesitas diagramas Mermaid"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;B&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"Bundle size y dependencias importan?"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="nv"&gt;B&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"Si"&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;C&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Pele"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
 &lt;span class="nv"&gt;B&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"No, ya usas Mermaid.js"&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;D&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Mermaid.js"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
 &lt;span class="nv"&gt;C&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;E&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"SVG final"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
 &lt;span class="nv"&gt;D&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;E&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esa decisión también depende de cuánta cobertura de sintaxis necesitás. Mermaid.js acumuló años de tipos de diagrama (Gantt, estados, clases, entidad-relación, mapas mentales, entre otros) y una comunidad grande alrededor de esa sintaxis extendida. Antes de migrar un proyecto completo de Mermaid.js a Pele conviene revisar, diagrama por diagrama, si el tipo que usás está soportado en la documentación de &lt;a href="https://pele.run/" rel="noopener noreferrer"&gt;pele.run&lt;/a&gt;, no asumirlo porque ambas acepten sintaxis Mermaid.&lt;/p&gt;

&lt;p&gt;📖 Resumen en Telegram: &lt;a href="https://telegra.ph/Pele-la-librer%C3%ADa-que-renderiza-Mermaid-a-SVG-puro-10-17" rel="noopener noreferrer"&gt;Ver resumen&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tu próximo paso: abrí el editor interactivo de &lt;a href="https://pele.run/" rel="noopener noreferrer"&gt;pele.run&lt;/a&gt;, cambiá el valor de &lt;code&gt;--pele-bg&lt;/code&gt; desde las devtools del navegador y confirmá en la pestaña Elements que el &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; sigue mostrando &lt;code&gt;var(--pele-bg)&lt;/code&gt; en vez de un color fijo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿Pele reemplaza completamente a Mermaid.js?
&lt;/h3&gt;

&lt;p&gt;No necesariamente. Pele cubre el caso de renderizar sintaxis Mermaid a SVG con menos peso y un theming más simple, pero Mermaid.js tiene más tipos de diagrama acumulados a lo largo de los años. Conviene revisar si los tipos que usás están soportados antes de migrar un proyecto entero.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Los diagramas Mermaid en SVG generados por Pele funcionan sin conexión a internet?
&lt;/h3&gt;

&lt;p&gt;Sí, una vez instalado el paquete, &lt;code&gt;render()&lt;/code&gt; corre localmente: no hace ninguna petición de red para convertir la sintaxis en SVG. El resultado tampoco depende de recursos externos para mostrarse, salvo que vos mismo agregues imágenes o fuentes externas dentro del diagrama.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Pele soporta todos los tipos de diagrama de Mermaid.js?
&lt;/h3&gt;

&lt;p&gt;El sitio oficial muestra flowcharts en su demo principal; para confirmar la cobertura de otros tipos (secuencia, clases, Gantt, estados) hay que revisar la documentación del proyecto en &lt;a href="https://pele.run/" rel="noopener noreferrer"&gt;pele.run&lt;/a&gt; en lugar de asumir paridad total con Mermaid.js.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Puedo usar Pele con React, Vue o Svelte?
&lt;/h3&gt;

&lt;p&gt;Sí, porque &lt;code&gt;render()&lt;/code&gt; devuelve un string de SVG, no un componente atado a un framework. En React se puede insertar con &lt;code&gt;dangerouslySetInnerHTML&lt;/code&gt;, en Vue con &lt;code&gt;v-html&lt;/code&gt; y en Svelte con &lt;code&gt;{@html}&lt;/code&gt;, siempre con las mismas precauciones de sanitización si el contenido viene de un usuario.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué pasa si el SVG generado por Pele no se ve bien en pantallas chicas?
&lt;/h3&gt;

&lt;p&gt;Pele adapta el layout en pantallas pequeñas en lugar de escalar el diagrama completo, lo que mantiene el texto legible a costa de reorganizar los nodos. Si un diagrama específico sigue viéndose mal, revisar si tiene muchos nodos en el mismo nivel ayuda a diagnosticar el problema.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Pele funciona en Node.js sin navegador?
&lt;/h3&gt;

&lt;p&gt;No hay confirmación directa de esto en la documentación pública del proyecto. Antes de depender de un uso en build time o en el servidor conviene probarlo en ese entorno concreto, en vez de asumir que la ausencia de dependencias implica ausencia de APIs del navegador.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://pele.run/" rel="noopener noreferrer"&gt;Pele&lt;/a&gt;: sitio oficial del proyecto, con editor interactivo, API de instalación y la comparativa de rendimiento contra Mermaid.js.- &lt;a href="https://mermaid.js.org/" rel="noopener noreferrer"&gt;Mermaid.js&lt;/a&gt;: documentación oficial de la librería que define la sintaxis que ambos proyectos comparten.- &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties" rel="noopener noreferrer"&gt;MDN: uso de propiedades personalizadas de CSS&lt;/a&gt;: referencia sobre cómo funcionan las variables CSS que usa Pele para el theming.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📱 &lt;strong&gt;¿Te gusta este contenido?&lt;/strong&gt; Únete a nuestro canal de Telegram &lt;a href="https://t.me/programacion" rel="noopener noreferrer"&gt;@programacion&lt;/a&gt; donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.&lt;/p&gt;

</description>
      <category>technology</category>
      <category>science</category>
      <category>programming</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Un ensayo en Nature cuestiona el uso de 'superinteligencia'</title>
      <dc:creator>lu1tr0n</dc:creator>
      <pubDate>Sat, 17 Oct 2026 16:27:37 +0000</pubDate>
      <link>https://dev.to/lu1tr0n/un-ensayo-en-nature-cuestiona-el-uso-de-superinteligencia-4jc8</link>
      <guid>https://dev.to/lu1tr0n/un-ensayo-en-nature-cuestiona-el-uso-de-superinteligencia-4jc8</guid>
      <description>&lt;p&gt;Nature publicó en octubre de 2026 un ensayo que hace una pregunta incómoda para toda la industria de la inteligencia artificial: si la &lt;strong&gt;superinteligencia artificial&lt;/strong&gt; sigue siendo un término con base científica o ya es solo una etiqueta de marketing. La revista lo plantea justo cuando varios laboratorios afirman en público estar cerca de superar la inteligencia humana en tareas generales.&lt;/p&gt;

&lt;p&gt;El ensayo, titulado &lt;em&gt;The science of superintelligence and why AI is still a useful word&lt;/em&gt;, no niega que la IA avance. Cuestiona si existe una definición medible de 'superinteligencia' que sirva para algo más que un titular.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Nature publicó en octubre de 2026 un ensayo que cuestiona si la superinteligencia tiene base científica sólida.- DeepMind propuso en 2023 seis niveles de capacidad de IA, de 'sin IA' a 'sobrehumana', en el paper Levels of AGI.- Anthropic ata su Responsible Scaling Policy a niveles ASL medibles, no a la palabra 'superinteligencia'.- La Ley de IA de la UE fija el umbral de riesgo sistémico en 10^25 FLOPs de entrenamiento, no en una etiqueta.- El ensayo no niega el avance de la IA: defiende que 'inteligencia artificial' sigue siendo un término útil.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Qué es la superinteligencia artificial
&lt;/h2&gt;

&lt;p&gt;Superinteligencia artificial es la capacidad hipotética de un sistema de IA para superar a los humanos en prácticamente cualquier tarea cognitiva relevante, incluyendo las que exigen juicio, creatividad y planificación a largo plazo, y no solo en pruebas aisladas de memoria o cálculo.&lt;/p&gt;

&lt;p&gt;El término se popularizó dentro de la filosofía de la IA mucho antes de que existieran los modelos de lenguaje actuales, pero carece de una métrica estándar aceptada por toda la comunidad científica. Por eso Nature usa la pregunta como punto de partida: hace falta distinguir entre la ambición declarada por las empresas y lo que de verdad se puede medir en un laboratorio.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué pasó
&lt;/h2&gt;

&lt;p&gt;El ensayo repasa cómo el lenguaje de la industria se movió, en apenas unos años, de hablar de modelos de lenguaje a hablar de &lt;a href="https://www.nature.com/articles/d41586-026-03198-6" rel="noopener noreferrer"&gt;inteligencia artificial general y superinteligencia&lt;/a&gt; casi como sinónimos. El autor sostiene que esa deriva no vino de un avance medible, sino de la ausencia de un criterio compartido: cada laboratorio define la etiqueta a su manera, y esa definición cambia según convenga para una ronda de financiamiento, un anuncio de producto o un debate regulatorio.&lt;/p&gt;

&lt;p&gt;El texto no pide abandonar el vocabulario de la IA. Al contrario, defiende que 'inteligencia artificial' como término sigue siendo útil para la ciencia, aunque sea impreciso, porque agrupa un conjunto real de técnicas (aprendizaje profundo, aprendizaje por refuerzo, modelos de lenguaje) que sí se pueden estudiar y comparar. El problema aparece, según el ensayo, cuando se salta directo a 'superinteligencia' sin pasar por una escala intermedia que alguien pueda verificar.&lt;/p&gt;

&lt;p&gt;La publicación llega además en un momento en que medios generalistas, no solo especializados, repiten la palabra en titulares sin matizarla. Para una revista científica, esa repetición sin respaldo empírico es justo el tipo de problema que un comité editorial no puede ignorar: cuando un término se vuelve sinónimo de progreso sin que nadie pueda medirlo, deja de servir como categoría científica y pasa a ser una consigna.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contexto e historia
&lt;/h2&gt;

&lt;p&gt;La idea de medir la IA por niveles, no por una etiqueta binaria, no es nueva. En 2023, investigadores de Google DeepMind publicaron &lt;a href="https://arxiv.org/abs/2311.02462" rel="noopener noreferrer"&gt;Levels of AGI for Operationalizing Progress on the Path to AGI&lt;/a&gt;, un paper que propuso seis niveles de capacidad, de Nivel 0 (sin IA) a Nivel 5 (sobrehumana), cruzados con una segunda escala de generalidad, de tareas estrechas a tareas completamente generales. La propuesta buscaba exactamente lo que Nature reclama ahora: una vara común para medir progreso sin depender de un anuncio de prensa.&lt;/p&gt;

&lt;p&gt;Ese marco convive con posturas opuestas dentro de la propia industria. OpenAI nació con la misión declarada de construir una AGI segura y beneficiosa, pero nunca fijó un umbral técnico público para decidir cuándo se alcanza ese punto. Anthropic tomó el camino contrario: en su ensayo &lt;a href="https://darioamodei.com/machines-of-loving-grace" rel="noopener noreferrer"&gt;Machines of Loving Grace&lt;/a&gt;, su director ejecutivo Dario Amodei evita deliberadamente las palabras AGI y superinteligencia, y propone hablar en cambio de 'IA potente', definida por lo que el sistema puede hacer en tareas concretas, no por una comparación vaga con la inteligencia humana en general.&lt;/p&gt;

&lt;p&gt;La propia etiqueta 'inteligencia artificial' nació en 1955, cuando John McCarthy decidió organizar el &lt;a href="https://en.wikipedia.org/wiki/Dartmouth_workshop" rel="noopener noreferrer"&gt;taller de Dartmouth&lt;/a&gt; y eligió ese nombre para el nuevo campo, y desde entonces atravesó más de un 'invierno de la IA' en el que buena parte de la comunidad científica dejó de usar el término por el desprestigio que había acumulado. La discusión actual sobre la superinteligencia repite, a otra escala, ese mismo patrón: una palabra que promete demasiado termina por perder credibilidad incluso cuando la tecnología detrás sigue avanzando.&lt;br&gt;
La escala de DeepMind de 2023 define seis niveles de capacidad, de 'sin IA' a 'sobrehumana'.&lt;/p&gt;
&lt;h2&gt;
  
  
  Detalles técnicos
&lt;/h2&gt;

&lt;p&gt;La disputa sobre el término no es solo semántica: en al menos un caso, una definición de capacidad dispara compromisos de seguridad que una empresa está obligada a cumplir. La &lt;a href="https://www.anthropic.com/news/anthropics-responsible-scaling-policy" rel="noopener noreferrer"&gt;Responsible Scaling Policy&lt;/a&gt; de Anthropic ata cada nivel de seguridad (ASL, por AI Safety Level) a evaluaciones concretas de capacidades peligrosas: cuánto ayuda un modelo a diseñar un arma biológica, a replicarse de forma autónoma en infraestructura ajena o a ejecutar un ciberataque complejo sin supervisión humana. Si un modelo supera el umbral de una evaluación, la política obliga a reforzar la seguridad del despliegue antes de publicarlo, sin importar cómo se llame esa capacidad hacia afuera.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dot"&gt;&lt;code&gt;&lt;span class="nv"&gt;flowchart&lt;/span&gt; &lt;span class="nv"&gt;LR&lt;/span&gt;
&lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Nivel 0: Sin IA"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;B&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Nivel 1: Emergente"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;B&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;C&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Nivel 2: Competente"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;C&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;D&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Nivel 3: Experta"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;D&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;E&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Nivel 4: Virtuosa"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;E&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;F&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Nivel 5: Sobrehumana"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NivelQué habilitaCapacidad evaluadaCompromiso de seguridadASL-2Modelos de uso general ya desplegadosRiesgo bajo de asistencia en armas o ciberataquesPruebas estándar de seguridad y filtrado de contenidoASL-3Modelos con capacidad real de asistencia peligrosaAsistencia significativa en bioseguridad o ciberofensivaControles de acceso reforzados y auditoría externaASL-4Modelos hipotéticos de mayor autonomíaRiesgo de pérdida de control o replicación autónomaContención técnica adicional, aún no activada en 2026&lt;br&gt;
La escala de DeepMind no mide solo profundidad de la capacidad: también mide generalidad. Un modelo puede ser virtuoso jugando ajedrez (nivel alto) pero estrecho en generalidad, mientras que un asistente conversacional moderno es más general pero rara vez pasa del nivel competente en tareas específicas exigentes. Esa doble vara explica por qué dos laboratorios pueden mirar el mismo modelo y llegar a conclusiones opuestas sobre si ya es superinteligente.&lt;/p&gt;

&lt;p&gt;No hay una API ni un comando que devuelva automáticamente en qué nivel está un modelo. Ubicarlo en la escala de DeepMind es una evaluación cualitativa: cada equipo de investigación compara los resultados de sus propios benchmarks contra la descripción de las seis categorías del paper, y dos equipos pueden discrepar en el resultado. Eso es, precisamente, lo que Nature señala como punto débil.&lt;/p&gt;

&lt;p&gt;Ni la escala de DeepMind ni la política de Anthropic resuelven el problema de fondo: ambas dependen de evaluaciones internas, hechas por la misma empresa que después decide si lanza el modelo. Nature no pide externalizar esas pruebas, pero el ensayo sí nota que ninguna de las dos escalas tiene hoy una auditoría independiente obligatoria.&lt;/p&gt;

&lt;h2&gt;
  
  
  Impacto y análisis
&lt;/h2&gt;

&lt;p&gt;La falta de una definición común tiene consecuencias prácticas más allá del laboratorio. Gobiernos que destinan fondos públicos a IA necesitan un criterio para decidir qué proyectos califican como investigación de frontera y cuáles son solo aplicaciones convencionales etiquetadas como superinteligencia artificial para atraer presupuesto.&lt;/p&gt;

&lt;p&gt;Para los inversores, el problema es distinto pero relacionado: una ronda de financiamiento valorada en varios miles de millones de dólares puede apoyarse en la promesa de llegar a la AGI sin que exista una forma independiente de verificar cuánto falta para eso. El ensayo de Nature no acusa a ninguna empresa de mentir; argumenta que, sin una métrica común, ni siquiera la propia empresa puede saber con certeza en qué punto de la escala está.&lt;/p&gt;

&lt;p&gt;La Ley de IA de la Unión Europea evita el problema de lleno: en lugar de depender de una palabra, fija un umbral técnico medible, el entrenamiento con más de 10^25 FLOPs, para clasificar un modelo como de riesgo sistémico y exigirle evaluaciones adicionales. Es la misma lógica que defiende el ensayo de Nature: una cifra que cualquiera puede verificar pesa más que una etiqueta que cambia de significado según quién la use.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💭 Clave:&lt;/strong&gt; ninguna política de seguridad de la industria usa hoy la palabra 'superinteligencia' como umbral oficial; todas, incluida la de Anthropic, dependen de pruebas de capacidades concretas y verificables.&lt;br&gt;
La Ley de IA de la UE usa 10^25 FLOPs de entrenamiento como umbral de riesgo sistémico.&lt;/p&gt;
&lt;h2&gt;
  
  
  Qué sigue
&lt;/h2&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nature no es la única publicación científica presionando en esta dirección. En los próximos meses se esperan revisiones de los marcos de capacidades de OpenAI, Google DeepMind y Anthropic, que suelen actualizarse cuando aparece una nueva generación de modelos. Si la presión editorial se mantiene, es probable que los laboratorios empiecen a hablar menos de superinteligencia en sus anuncios y más de niveles concretos de capacidad, aunque sea solo para evitar que un ensayo como este los cite como ejemplo de lenguaje inflado.&lt;/p&gt;

&lt;p&gt;Lo que no va a pasar pronto es un acuerdo formal entre empresas sobre una sola escala. Cada laboratorio tiene incentivos distintos para definir el progreso a su manera, y ninguna autoridad científica tiene poder para imponer una terminología común.&lt;/p&gt;

&lt;p&gt;📖 Resumen en Telegram: &lt;a href="https://telegra.ph/Un-ensayo-en-Nature-cuestiona-el-uso-de-superinteligencia-10-17" rel="noopener noreferrer"&gt;Ver resumen&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Si te interesa el detalle técnico, leé el paper original &lt;a href="https://arxiv.org/abs/2311.02462" rel="noopener noreferrer"&gt;Levels of AGI&lt;/a&gt; de DeepMind: son pocas páginas y la tabla de niveles se entiende sin necesitar formación en IA.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿Qué dice exactamente el ensayo de Nature sobre la superinteligencia artificial?
&lt;/h3&gt;

&lt;p&gt;Argumenta que el término carece de una métrica científica aceptada, aunque defiende que 'inteligencia artificial' como categoría general sigue siendo útil para la investigación.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Quién definió los niveles de AGI que usa este artículo como referencia?
&lt;/h3&gt;

&lt;p&gt;Investigadores de Google DeepMind los propusieron en 2023 en el paper 'Levels of AGI for Operationalizing Progress on the Path to AGI'.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué es la Responsible Scaling Policy de Anthropic?
&lt;/h3&gt;

&lt;p&gt;Es el marco interno que ata cada nivel de riesgo (ASL) a evaluaciones concretas de capacidades peligrosas, como asistencia en bioseguridad o ciberataques autónomos, en lugar de a una etiqueta como superinteligencia.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿La IA actual ya alcanzó la inteligencia artificial general?
&lt;/h3&gt;

&lt;p&gt;No hay consenso científico al respecto. Distintos laboratorios usan criterios distintos, que es precisamente el problema que señala el ensayo de Nature.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Por qué importa este debate sobre la inteligencia artificial general para alguien que no trabaja en IA?
&lt;/h3&gt;

&lt;p&gt;Porque esa definición influye en decisiones de financiamiento público, en qué exige la regulación y en qué tan creíbles son los anuncios comerciales de las empresas de IA.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.nature.com/articles/d41586-026-03198-6" rel="noopener noreferrer"&gt;Nature&lt;/a&gt;: ensayo 'The science of superintelligence and why AI is still a useful word'.- &lt;a href="https://arxiv.org/abs/2311.02462" rel="noopener noreferrer"&gt;arXiv&lt;/a&gt;: 'Levels of AGI for Operationalizing Progress on the Path to AGI' (Google DeepMind, 2023).- &lt;a href="https://www.anthropic.com/news/anthropics-responsible-scaling-policy" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;: anuncio oficial de la Responsible Scaling Policy.- &lt;a href="https://darioamodei.com/machines-of-loving-grace" rel="noopener noreferrer"&gt;Dario Amodei&lt;/a&gt;: ensayo 'Machines of Loving Grace' sobre IA potente frente a AGI y superinteligencia.- &lt;a href="https://en.wikipedia.org/wiki/Artificial_general_intelligence" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;: contexto general sobre inteligencia artificial general.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📱 &lt;strong&gt;¿Te gusta este contenido?&lt;/strong&gt; Únete a nuestro canal de Telegram &lt;a href="https://t.me/programacion" rel="noopener noreferrer"&gt;@programacion&lt;/a&gt; donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Memoria asociativa: el estudio de 2020 que conecta a Hopfield con la atención</title>
      <dc:creator>lu1tr0n</dc:creator>
      <pubDate>Sat, 17 Oct 2026 08:41:32 +0000</pubDate>
      <link>https://dev.to/lu1tr0n/memoria-asociativa-el-estudio-de-2020-que-conecta-a-hopfield-con-la-atencion-5hlc</link>
      <guid>https://dev.to/lu1tr0n/memoria-asociativa-el-estudio-de-2020-que-conecta-a-hopfield-con-la-atencion-5hlc</guid>
      <description>&lt;p&gt;En 1982, el físico &lt;a href="https://en.wikipedia.org/wiki/Hopfield_network" rel="noopener noreferrer"&gt;John Hopfield publicó un modelo de red neuronal&lt;/a&gt; que, cuatro décadas después, resultó ser la base conceptual del mecanismo de atención que sostiene a GPT, Claude y Gemini. La idea central es la memoria asociativa: una red que guarda patrones completos y los recupera a partir de una versión incompleta o ruidosa, sin usar direcciones exactas como la memoria de una computadora.&lt;/p&gt;

&lt;p&gt;El investigador Alex Wang reconstruyó ese vínculo en un ensayo reciente: mostró, paso a paso, cómo partir solo de la idea de guardar y recuperar patrones alcanza para derivar la fórmula de atención sin memorizarla de un paper.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Una red de Hopfield recupera un patrón completo a partir de una versión incompleta o ruidosa de ese patrón.- La energía de la red baja en cada actualización hasta estabilizarse en el patrón memorizado más parecido.- El mecanismo de atención de un transformer calcula la misma recuperación por similitud entre vectores.- Las matrices Query, Key y Value convierten esa búsqueda asociativa en multiplicaciones paralelas en GPU.- Treinta y cinco años después, el paper de atención de 2017 formaliza esa equivalencia matemática.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ¿Qué es la memoria asociativa?
&lt;/h2&gt;

&lt;p&gt;La memoria asociativa es un modelo de almacenamiento donde un patrón completo se recupera a partir de una parte de sí mismo o de una versión con ruido, sin usar una dirección exacta como en la memoria de una computadora tradicional; las redes de Hopfield son su implementación clásica.&lt;/p&gt;

&lt;p&gt;En una memoria RAM, pedís el dato ubicado en la dirección 0x4F2A y la máquina te lo entrega sin importar qué tan parecido sea a lo que buscabas. La memoria direccionable por contenido invierte ese orden: entrás con un fragmento del dato, un patrón corrupto o incompleto, y el sistema te devuelve el patrón almacenado más parecido. Es la misma lógica que usa tu cerebro cuando reconocés una canción a partir de tres notas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué importa entender este modelo
&lt;/h2&gt;

&lt;p&gt;La red de Hopfield nació en biofísica, no en ingeniería de software: Hopfield buscaba explicar cómo el cerebro recupera recuerdos completos a partir de pistas parciales. Durante años quedó como una curiosidad de los cursos de redes neuronales, superada en la práctica por arquitecturas más flexibles.&lt;/p&gt;

&lt;p&gt;El giro llegó cuando varios investigadores notaron que la regla de actualización de una red de Hopfield continua se parece, término a término, a la fórmula de atención que popularizó el paper &lt;a href="https://arxiv.org/abs/1706.03762" rel="noopener noreferrer"&gt;Attention Is All You Need&lt;/a&gt; en 2017. Entender la recuperación asociativa deja de ser arqueología académica: explica por qué la atención a veces converge a un token equivocado, lo mismo que una red de Hopfield puede estabilizarse en un mínimo de energía incorrecto, y por qué ampliar el contexto cambia la capacidad efectiva de memoria del modelo.&lt;/p&gt;

&lt;p&gt;Para quien ajusta prompts o diseña agentes, esta lectura explica un fenómeno conocido: un dato repetido varias veces en el contexto se recupera con más fuerza que uno mencionado una sola vez. En términos de energía, un patrón reforzado varias veces crea un pozo más profundo y más fácil de encontrar para la red.&lt;br&gt;
Hopfield formuló su modelo en 1982, antes de que existieran las GPU modernas.&lt;/p&gt;
&lt;h2&gt;
  
  
  Cómo funciona una red de Hopfield
&lt;/h2&gt;

&lt;p&gt;Una red de Hopfield clásica tiene N neuronas binarias, cada una con valor +1 o -1. Para memorizar un patrón, la red no lo guarda en una celda: lo codifica en una matriz de pesos W usando la regla de Hebb, donde cada peso W_ij se calcula como el producto de los valores i y j del patrón, con la diagonal en cero para que ninguna neurona se refuerce a sí misma.&lt;/p&gt;

&lt;p&gt;La regla de Hebb funciona porque el producto p_i × p_j es positivo cuando dos neuronas coinciden en el patrón memorizado y negativo cuando difieren. Al sumar esos productos en la matriz W, cada par de neuronas aprende a reforzarse o inhibirse según qué tan seguido coincidieron en los patrones guardados: es la versión matemática de la plasticidad hebbiana que se resume desde los años cincuenta como "las neuronas que se disparan juntas, se conectan juntas".&lt;/p&gt;

&lt;p&gt;Para recuperar un patrón a partir de una entrada corrupta, la red actualiza cada neurona una por una: cada neurona suma las señales que recibe de las demás, ponderadas por los pesos, y toma el signo de esa suma como su nuevo valor. Repetir esta actualización empuja al sistema hacia abajo en una función de energía E = -0.5 × xᵗWx. Como los pesos son simétricos, cada actualización asíncrona garantiza que la energía nunca sube, así que el sistema converge a un mínimo estable: el patrón memorizado más cercano a la entrada.&lt;/p&gt;

&lt;p&gt;Hay dos formas de aplicar la actualización: asíncrona (una neurona a la vez, en orden aleatorio) o sincrónica (todas a la vez). La garantía de que la energía nunca sube solo vale para la actualización asíncrona con pesos simétricos; actualizar todas las neuronas al mismo tiempo puede hacer que el sistema oscile entre dos estados sin converger nunca, un detalle que casi siempre se omite en las explicaciones simplificadas del modelo.&lt;/p&gt;

&lt;p&gt;Ese mínimo estable es, por definición, el recuerdo recuperado. La memoria asociativa funciona porque la red no busca una dirección: cae en un pozo de energía que coincide con el patrón original.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dot"&gt;&lt;code&gt;&lt;span class="nv"&gt;flowchart&lt;/span&gt; &lt;span class="nv"&gt;TD&lt;/span&gt;
    &lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Patron de entrada con ruido"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;B&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Calcular energia de la red"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="nv"&gt;B&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;C&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Actualizar una neurona segun el signo de su entrada"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="nv"&gt;C&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;D&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"Bajo la energia?"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nv"&gt;D&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"Si"&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;C&lt;/span&gt;
    &lt;span class="nv"&gt;D&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"No, se estabilizo"&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;E&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Patron memorizado recuperado"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La capacidad de una red de Hopfield clásica es limitada: la teoría clásica de Amit, Gutfreund y Sompolinsky de 1985 ubica el límite práctico alrededor del 14% del número de neuronas antes de que los patrones memorizados empiecen a mezclarse entre sí, lo que se conoce como crosstalk. Con 100 neuronas, eso son apenas 14 patrones distintos antes de que la recuperación empiece a fallar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ejemplos prácticos: recuperación asociativa en cuatro líneas de Python
&lt;/h2&gt;

&lt;p&gt;El ejemplo más simple cabe en una red de 4 neuronas. Guardamos el patrón [1, -1, 1, -1] y luego le mostramos una versión con un bit invertido, para ver si la red lo corrige:&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;

&lt;span class="n"&gt;pattern&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&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;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="mi"&gt;1&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="n"&gt;W&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;outer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill_diagonal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;W&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="n"&gt;corrupted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&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;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="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="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="c1"&gt;# un bit invertido respecto al original
&lt;/span&gt;&lt;span class="n"&gt;recovered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;W&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;corrupted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;recovered&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La salida es exactamente el patrón original, aunque la entrada tenía un bit distinto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ 1 -1  1 -1]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La neurona corrupta, la tercera, que entró en -1 en vez de 1, recibe una señal neta de +3 desde las otras tres neuronas, así que su signo se corrige en una sola actualización. Con patrones más largos la corrección necesita varias rondas, pero el mecanismo es idéntico.&lt;/p&gt;

&lt;p&gt;El segundo ejemplo guarda dos patrones a la vez, [1, -1, 1, -1] y [1, 1, -1, -1], que son ortogonales entre sí:&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="n"&gt;patterns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&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;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="mi"&gt;1&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="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&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;span class="mi"&gt;1&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="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="n"&gt;W&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;outer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;patterns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill_diagonal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;W&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="n"&gt;corrupted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&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;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="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="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="n"&gt;recovered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;W&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;corrupted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;recovered&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La red sigue recuperando &lt;code&gt;[ 1 -1  1 -1]&lt;/code&gt; aunque ahora guarda dos patrones, porque al ser ortogonales no interfieren entre sí. Si agregás un tercer patrón correlacionado con alguno de los dos, el riesgo de crosstalk aparece mucho antes del límite del 14% mencionado arriba.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cómo empezar a experimentar con memoria asociativa
&lt;/h2&gt;

&lt;p&gt;Para correr los ejemplos anteriores en tu máquina solo necesitás Python 3 y NumPy. En Linux y macOS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;numpy
python3 hopfield_demo.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En Windows, el mismo comando usando &lt;code&gt;python&lt;/code&gt; en vez de &lt;code&gt;python3&lt;/code&gt; desde PowerShell o CMD.&lt;/p&gt;

&lt;p&gt;Guardá el código de los dos ejemplos en un archivo &lt;code&gt;hopfield_demo.py&lt;/code&gt; y ejecutalo: no hay API keys, ni dependencias externas, ni GPU necesaria para esta escala de red.&lt;/p&gt;

&lt;h2&gt;
  
  
  Casos de uso reales de la memoria asociativa
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Corrección de errores didáctica&lt;/strong&gt;: los cursos de redes neuronales usan redes de Hopfield para mostrar cómo un sistema distribuido corrige ruido sin un algoritmo de corrección explícito, por ejemplo recuperando una imagen binaria completa a partir de la mitad de sus píxeles.- &lt;strong&gt;Modelos basados en energía&lt;/strong&gt;: las máquinas de Boltzmann y los modelos de difusión comparten la misma lógica de minimizar una función de energía hasta estabilizarse en un patrón válido, generando un dato nuevo al dejar que el sistema caiga hacia un mínimo de esa superficie.- &lt;strong&gt;Capas de atención alternativas&lt;/strong&gt;: el paper &lt;a href="https://arxiv.org/abs/2008.02217" rel="noopener noreferrer"&gt;Hopfield Networks is All You Need&lt;/a&gt; (2020) propone capas de Hopfield continuo como reemplazo directo de las capas de atención en un transformer, con capacidad de almacenamiento exponencial en vez de lineal.- &lt;strong&gt;Interpretabilidad de modelos de lenguaje&lt;/strong&gt;: pensar la atención como recuperación asociativa permite tratar un fallo de memoria del modelo como una convergencia a un mínimo de energía equivocado, en vez de un misterio de caja negra sin explicación.
El paper "Hopfield Networks is All You Need" se publicó en 2020, treinta y ocho años después del modelo original.
## Errores comunes y buenas prácticas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;El error más frecuente es sobrecargar la red: guardar más patrones de los que su capacidad permite produce recuperaciones mezcladas, donde la red converge a un patrón espurio que no es ninguno de los memorizados. La solución clásica es reducir la cantidad de patrones o usar patrones poco correlacionados entre sí, como en el ejemplo ortogonal de arriba.&lt;/p&gt;

&lt;p&gt;Otro error es romper la simetría de la matriz de pesos W. La garantía de convergencia depende de que W sea simétrica (W_ij = W_ji); si entrenás la red con una regla distinta a Hebb sin verificar la simetría, perdés la garantía matemática de que la energía solo baja, y el sistema puede oscilar sin estabilizarse nunca.&lt;/p&gt;

&lt;p&gt;Por último, es común confundir la red de Hopfield clásica, binaria y con actualización iterativa, con su versión moderna continua, que recupera un patrón en un solo paso usando softmax. Son parientes matemáticos, pero tienen propiedades de capacidad y convergencia distintas; tratarlas como intercambiables lleva a conclusiones equivocadas sobre qué tan parecida es la atención real a la red original de 1982.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparativa: Hopfield clásica, moderna y atención de los transformers
&lt;/h2&gt;

&lt;p&gt;La tabla resume las tres versiones del mismo principio, de la más antigua a la más usada en producción hoy:&lt;br&gt;
ModeloRegla de actualizaciónCapacidadRelación con la atenciónHopfield clásica (1982)Signo de la suma ponderada, iterativa~0,14 × N patronesAncestro conceptual, actualización discretaHopfield moderna continua (2020)Softmax sobre similitudes, un solo pasoExponencial en NMatemáticamente equivalente a una capa de atenciónAtención de un transformerSoftmax(QKᵗ/√d)·VLimitada por la ventana de contextoCaso particular de recuperación asociativa continua&lt;/p&gt;
&lt;h2&gt;
  
  
  Profundizando: de la energía discreta al softmax continuo
&lt;/h2&gt;

&lt;p&gt;La versión moderna de la red de Hopfield, descrita en &lt;a href="https://arxiv.org/abs/2008.02217" rel="noopener noreferrer"&gt;Hopfield Networks is All You Need&lt;/a&gt;, cambia las neuronas binarias por vectores continuos y reemplaza la regla de actualización por una función de energía basada en log-sum-exp. El resultado de minimizar esa energía en un solo paso es, término a término, la fórmula de atención: softmax de la similitud entre una consulta y un conjunto de llaves, multiplicada por los valores asociados.&lt;/p&gt;

&lt;p&gt;En el lenguaje de un transformer, la consulta (Query) es el patrón corrupto o parcial que entra a la red; las llaves (Key) son los patrones memorizados contra los que se compara por similitud; y los valores (Value) son lo que efectivamente se recupera cuando una llave coincide. La diferencia frente a Hopfield clásica es que la recuperación no itera: el softmax calcula de una sola vez una combinación ponderada de todos los valores, pesada por qué tan parecida es cada llave a la consulta.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dot"&gt;&lt;code&gt;&lt;span class="nv"&gt;sequenceDiagram&lt;/span&gt;
    &lt;span class="nv"&gt;participant&lt;/span&gt; &lt;span class="nv"&gt;Q&lt;/span&gt; &lt;span class="nv"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;Consulta&lt;/span&gt; &lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Query&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
    &lt;span class="nv"&gt;participant&lt;/span&gt; &lt;span class="nv"&gt;K&lt;/span&gt; &lt;span class="nv"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;Llaves&lt;/span&gt; &lt;span class="nv"&gt;memorizadas&lt;/span&gt; &lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Key&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
    &lt;span class="nv"&gt;participant&lt;/span&gt; &lt;span class="nv"&gt;V&lt;/span&gt; &lt;span class="nv"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;Valores&lt;/span&gt; &lt;span class="nv"&gt;asociados&lt;/span&gt; &lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Value&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
    &lt;span class="nv"&gt;Q&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;K&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;compara&lt;/span&gt; &lt;span class="nv"&gt;similitud&lt;/span&gt; &lt;span class="nv"&gt;con&lt;/span&gt; &lt;span class="nv"&gt;cada&lt;/span&gt; &lt;span class="nv"&gt;llave&lt;/span&gt;
    &lt;span class="nv"&gt;K&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;Q&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;devuelve&lt;/span&gt; &lt;span class="nv"&gt;un&lt;/span&gt; &lt;span class="nv"&gt;peso&lt;/span&gt; &lt;span class="nv"&gt;de&lt;/span&gt; &lt;span class="nv"&gt;similitud&lt;/span&gt; &lt;span class="nv"&gt;por&lt;/span&gt; &lt;span class="nv"&gt;llave&lt;/span&gt;
    &lt;span class="nv"&gt;Q&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;V&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;combina&lt;/span&gt; &lt;span class="nv"&gt;los&lt;/span&gt; &lt;span class="nv"&gt;valores&lt;/span&gt; &lt;span class="nv"&gt;segun&lt;/span&gt; &lt;span class="nv"&gt;esos&lt;/span&gt; &lt;span class="nv"&gt;pesos&lt;/span&gt;
    &lt;span class="nv"&gt;V&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;Q&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;patron&lt;/span&gt; &lt;span class="nv"&gt;recuperado&lt;/span&gt; &lt;span class="nv"&gt;por&lt;/span&gt; &lt;span class="nv"&gt;asociacion&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dot"&gt;&lt;code&gt;&lt;span class="nv"&gt;flowchart&lt;/span&gt; &lt;span class="nv"&gt;LR&lt;/span&gt;
    &lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Embeddings de entrada"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;B&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Matriz Query"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="nv"&gt;A&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;C&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Matriz Key"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="nv"&gt;A&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;D&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Matriz Value"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="nv"&gt;B&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;E&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Similitud Query-Key"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="nv"&gt;C&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;E&lt;/span&gt;
    &lt;span class="nv"&gt;E&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;F&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Softmax sobre las similitudes"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="nv"&gt;F&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;G&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Suma ponderada de Values"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="nv"&gt;D&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;G&lt;/span&gt;
    &lt;span class="nv"&gt;G&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;H&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Salida de la capa de atencion"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La versión continua agrega un parámetro adicional, llamado beta (equivalente al factor 1/√d de la fórmula de atención), que controla qué tan dura es la recuperación. Con beta bajo, la red devuelve un promedio difuso de todos los patrones parecidos a la consulta, como una memoria borrosa; con beta alto, converge a un único patrón memorizado, igual que la red clásica de 1982. Los transformers modernos operan en un punto intermedio: ni promedian todo el contexto por igual ni recuperan un solo token, sino una combinación pesada donde pocos tokens dominan la salida.&lt;/p&gt;

&lt;p&gt;Esta lectura no dice que los ingenieros que diseñaron el &lt;a href="https://arxiv.org/abs/1706.03762" rel="noopener noreferrer"&gt;paper de atención de 2017&lt;/a&gt; partieron de Hopfield; muestra que dos caminos de investigación distintos convergen en la misma fórmula. Lo que sí permite es derivar la atención desde una intuición física, recuperar un recuerdo completo, en vez de memorizarla como una fórmula de álgebra lineal sin motivo aparente.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💭 Clave:&lt;/strong&gt; la capacidad exponencial de la Hopfield moderna es la razón matemática de que ampliar el contexto de un modelo de lenguaje no multiplique linealmente su capacidad de "recordar": la relación es mucho más favorable que en la red clásica de 1982.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;📖 Resumen en Telegram: &lt;a href="https://telegra.ph/Memoria-asociativa-el-estudio-de-2020-que-conecta-a-Hopfield-con-la-atenci%C3%B3n-10-17" rel="noopener noreferrer"&gt;Ver resumen&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tu próximo paso: abrí una terminal, pegá el código de los dos ejemplos en &lt;code&gt;hopfield_demo.py&lt;/code&gt; y probá qué pasa si agregás un tercer patrón correlacionado con los otros dos, hasta que la recuperación empiece a fallar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿Qué diferencia hay entre la memoria asociativa y la memoria RAM de una computadora?
&lt;/h3&gt;

&lt;p&gt;La RAM recupera un dato por su dirección exacta, sin importar su contenido. La memoria direccionable por contenido, en cambio, recupera un patrón completo a partir de una parte de él o de una versión con ruido, comparando similitud en vez de direcciones.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Las redes de Hopfield siguen usándose hoy?
&lt;/h3&gt;

&lt;p&gt;La versión clásica de 1982 se usa casi exclusivamente con fines didácticos, por su baja capacidad de almacenamiento. Su versión continua moderna, en cambio, es la base teórica de propuestas activas de investigación para rediseñar capas de atención.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Por qué se dice que la atención de los transformers es una forma de memoria asociativa?
&lt;/h3&gt;

&lt;p&gt;Porque la fórmula de atención, softmax de la similitud entre consulta y llaves multiplicada por valores, es matemáticamente equivalente a un paso de actualización de una red de Hopfield continua. Ambas resuelven el mismo problema: recuperar el patrón almacenado más parecido a una consulta parcial.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Cuántos patrones puede guardar una red de Hopfield?
&lt;/h3&gt;

&lt;p&gt;La red clásica binaria guarda, de forma confiable, alrededor del 14% del número de sus neuronas antes de que los patrones empiecen a mezclarse. La versión continua moderna eleva esa cifra a un crecimiento exponencial respecto al número de neuronas.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué es una red de Hopfield moderna o continua?
&lt;/h3&gt;

&lt;p&gt;Es la versión descrita en 2020 que reemplaza las neuronas binarias por vectores continuos y la regla de actualización iterativa por un softmax de un solo paso, logrando mayor capacidad y una conexión matemática directa con el mecanismo de atención.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Necesito entender cálculo para seguir este modelo?
&lt;/h3&gt;

&lt;p&gt;No para la intuición: alcanza con álgebra de vectores y la idea de que un sistema cae hacia un mínimo de energía, como una pelota que rueda por una superficie hasta el punto más bajo. El cálculo aparece recién cuando se quiere derivar formalmente la versión continua de 2020.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://alexwang.ai/posts/associative-memory/" rel="noopener noreferrer"&gt;Alex Wang, "You could have invented transformers from memory"&lt;/a&gt;: el ensayo que deriva la fórmula de atención desde la memoria asociativa.- &lt;a href="https://en.wikipedia.org/wiki/Hopfield_network" rel="noopener noreferrer"&gt;Wikipedia: Hopfield network&lt;/a&gt;: historia y formulación matemática del modelo original de 1982.- &lt;a href="https://arxiv.org/abs/2008.02217" rel="noopener noreferrer"&gt;Ramsauer et al., "Hopfield Networks is All You Need" (2020)&lt;/a&gt;: la versión continua y su equivalencia con la atención.- &lt;a href="https://arxiv.org/abs/1706.03762" rel="noopener noreferrer"&gt;Vaswani et al., "Attention Is All You Need" (2017)&lt;/a&gt;: el paper original que introdujo el mecanismo de atención de los transformers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📱 &lt;strong&gt;¿Te gusta este contenido?&lt;/strong&gt; Únete a nuestro canal de Telegram &lt;a href="https://t.me/programacion" rel="noopener noreferrer"&gt;@programacion&lt;/a&gt; donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>ECDSA: la sospecha de 26 años era un timestamp, no la NSA</title>
      <dc:creator>lu1tr0n</dc:creator>
      <pubDate>Sat, 17 Oct 2026 02:31:38 +0000</pubDate>
      <link>https://dev.to/lu1tr0n/ecdsa-la-sospecha-de-26-anos-era-un-timestamp-no-la-nsa-1lb6</link>
      <guid>https://dev.to/lu1tr0n/ecdsa-la-sospecha-de-26-anos-era-un-timestamp-no-la-nsa-1lb6</guid>
      <description>&lt;p&gt;Un misterio criptográfico de 26 años quedó resuelto en menos de 30 minutos: las semillas de curvas NIST que usa ECDSA no salieron de un hash secreto de la NSA, sino de un generador de números aleatorios de los años 90 sembrado con la hora del sistema. Así lo reveló Anthropic el 16 de octubre de 2026, después de pedirle a un modelo de investigación interno de Claude que rastreara el origen de esos parámetros.&lt;/p&gt;

&lt;p&gt;El resultado no es una falla ni un parche urgente. Es la confirmación, casi tres décadas después, de que esos parámetros se generaron sin manipulación oculta.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Claude resolvió en menos de 30 minutos el misterio de 26 años sobre las semillas de ECDSA.- Las semillas de P-192, P-224, P-256, P-384 y P-521 vienen de lrand48(), no de un hash secreto.- El cálculo de la semilla de P-256 necesitó 1.842 iteraciones de un generador sembrado con un timestamp Unix.- Anthropic descarta puertas traseras: el método usado ahora es reproducible por cualquiera.- Jerry Solinas, ingeniero de la NSA fallecido en 2023, dejó en 2015 una pista que resultó errónea.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Qué es ECDSA
&lt;/h2&gt;

&lt;p&gt;ECDSA es el algoritmo de firma digital basado en curvas elípticas que adoptó el NIST en el año 2000 dentro del estándar FIPS 186-2, y que depende de un conjunto de curvas estandarizadas cuyas semillas de curvas NIST permanecieron sin explicación pública durante 26 años.&lt;/p&gt;

&lt;p&gt;Hoy, ECDSA firma certificados TLS, protege transacciones de Bitcoin y autentica llaves SSH en millones de servidores. Su seguridad depende de que la curva elíptica subyacente, uno de los cinco pares que define el estándar FIPS 186-2 (P-192, P-224, P-256, P-384 y P-521), no tenga ninguna debilidad matemática escondida a propósito.&lt;/p&gt;

&lt;p&gt;Esa curva se define con una ecuación y una constante, llamada b, que a su vez se calcula aplicando una función hash a un número de 160 bits: la semilla. El NIST publicó las semillas junto con las curvas en el año 2000, pero nunca explicó de dónde salieron esos números.&lt;/p&gt;

&lt;p&gt;Las curvas elípticas se volvieron populares frente a RSA porque logran niveles de seguridad comparables con claves mucho más cortas, lo que las hizo más eficientes para dispositivos móviles y conexiones de alto volumen. Esa eficiencia explica en parte por qué ECDSA domina hoy buena parte de las conexiones TLS modernas y las firmas de varias criptomonedas basadas en curvas elípticas.&lt;br&gt;
La curva P-256 es una de las cinco curvas primas que define el estándar FIPS 186-2.&lt;/p&gt;
&lt;h2&gt;
  
  
  Qué pasó con las semillas de curvas NIST
&lt;/h2&gt;

&lt;p&gt;El 16 de octubre de 2026, Anthropic publicó un &lt;a href="https://www.anthropic.com/research/ecdsa-curve-origins" rel="noopener noreferrer"&gt;informe de investigación&lt;/a&gt; donde cuenta que un empleado le pidió a un modelo interno de Claude que averiguara, sin pistas previas, cómo se generaron esas semillas. El modelo tardó menos de media hora en encontrar el origen de los parámetros de P-192, P-224, P-256, P-384 y P-521.&lt;/p&gt;

&lt;p&gt;La respuesta, según el &lt;a href="https://www.anthropic.com/research/ecdsa-curve-origins" rel="noopener noreferrer"&gt;informe de Anthropic&lt;/a&gt;, es que cada semilla salió de &lt;code&gt;lrand48()&lt;/code&gt;, el generador de números pseudoaleatorios de las librerías C de Unix, inicializado con una marca de tiempo del sistema. El programa original tomaba 20 bytes por cada vuelta del generador como candidato a semilla; para producir la semilla publicada de P-256 hicieron falta 1.842 iteraciones.&lt;/p&gt;

&lt;p&gt;Anthropic lo resume sin dramatismo: “no hay ninguna acción que tomar ni nada que hacer de forma distinta”, dice el informe, que presenta el hallazgo como el capítulo final de una especulación que ya duraba más de dos décadas.&lt;/p&gt;
&lt;h2&gt;
  
  
  Contexto e historia
&lt;/h2&gt;

&lt;p&gt;La sospecha empezó en el propio texto del estándar. El NIST publicó la semilla de P-256 como esta cadena hexadecimal, sin ninguna nota al pie:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SEED = C49D3608 86E70493 6A6678E1 139D26B7 819F7E90
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Los matemáticos Neal Koblitz y Alfred Menezes documentaron la controversia en el ensayo “A Riddle Wrapped in an Enigma”, y durante años circuló una recompensa informal para quien lograra reconstruir el origen de esos números.&lt;/p&gt;

&lt;p&gt;La pista más citada venía de Jerry Solinas, el ingeniero de la NSA que trabajó en el diseño original. En 2015 dijo recordar que las semillas salieron de un hash aplicado a una frase en broma, algo como “Give Bob and Jerry a raise”, aunque admitió que ni él ni su colega Bob Reiter guardaban el código ni los detalles. Solinas murió en 2023 sin resolver la duda, y la pista de la frase resultó, según el hallazgo de Claude, completamente falsa.&lt;/p&gt;

&lt;h2&gt;
  
  
  Detalles técnicos
&lt;/h2&gt;

&lt;p&gt;Claude no intentó repetir la búsqueda de la frase en broma. En cambio, según el &lt;a href="https://www.anthropic.com/research/ecdsa-curve-origins" rel="noopener noreferrer"&gt;informe de Anthropic&lt;/a&gt;, planteó una hipótesis distinta: que el software usado en los 90 para generar las semillas se apoyaba en un generador pseudoaleatorio típico de esa época, y que el patrón de ese generador todavía podía reconocerse en los bytes publicados.&lt;/p&gt;

&lt;p&gt;Para probarlo, escribió un escáner que deslizaba una ventana de bits sobre cada semilla y comparaba bloques contiguos contra 34 combinaciones de generador y porción de salida. Ahí apareció la señal: en cuatro de diez semillas analizadas, las 20 palabras de 32 bits tenían el bit más alto en cero. Esa coincidencia, por azar, tiene una probabilidad de apenas 1 en 5.810.&lt;/p&gt;

&lt;p&gt;Ese patrón es la firma de &lt;code&gt;lrand48()&lt;/code&gt;, un &lt;a href="https://en.wikipedia.org/wiki/Linear_congruential_generator" rel="noopener noreferrer"&gt;generador congruencial lineal&lt;/a&gt; que solo devuelve los 31 bits superiores de un estado interno de 48 bits. Con esa pista, Claude probó por fuerza bruta los 2^17 valores posibles de los 17 bits ocultos y confirmó que las siguientes cuatro palabras de cada semilla encajaban exactamente.&lt;/p&gt;

&lt;p&gt;Las semillas de las cinco curvas primas del NIST (P-192, P-224, P-256, P-384 y P-521) y cinco más del estándar ANSI X9.62 conservaban aún menos bits por salida, así que la fuerza bruta ya no alcanzaba. Para esas diez, Claude resolvió un problema clásico de retículas con el algoritmo Lenstra-Lenstra-Lovász (LLL), probando cada posible ventana de bits hasta confirmar el mismo patrón en las diez: &lt;code&gt;lrand48() &amp;gt;&amp;gt; 23&lt;/code&gt;.&lt;br&gt;
lrand48() forma parte de las librerías C estándar desde los años 90.Técnica usada por ClaudeDónde se aplicóQué resolvióEscaneo bit a bit (34 combinaciones)Las 10 semillas publicadasIdentificar qué generador de los 90 produjo cada semillaFuerza bruta de 2^17 valores4 semillas con bit superior en ceroRecuperar los bits ocultos del estado de lrand48()Reducción de retícula LLL10 semillas (5 NIST primas + 5 ANSI X9.62)Recuperar el estado truncado de lrand48() &amp;gt;&amp;gt; 23&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dot"&gt;&lt;code&gt;&lt;span class="nv"&gt;flowchart&lt;/span&gt; &lt;span class="nv"&gt;TD&lt;/span&gt;
&lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Semilla publicada (ej. P-256)"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;B&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Escaneo bit a bit: 34 combinaciones de PRNG"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;B&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;C&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"¿Bit superior en cero en las 20 palabras?"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;C&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;|&lt;/span&gt;&lt;span class="s2"&gt;"Sí"&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;D&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Fuerza bruta de 2^17 valores ocultos"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;C&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;|&lt;/span&gt;&lt;span class="s2"&gt;"No"&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;E&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Reducción de retícula LLL"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;D&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;F&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Estado interno de lrand48 recuperado"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;E&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;F&lt;/span&gt;
&lt;span class="nv"&gt;F&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;G&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Iteraciones desde un timestamp Unix (1.842 para P-256)"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💭 Clave:&lt;/strong&gt; una probabilidad de 1 en 5.810 no es una demostración matemática, es una señal estadística. Lo que convierte el hallazgo en evidencia sólida es que, además de detectar el patrón, Claude reconstruyó bit por bit cada semilla publicada y las 10 coincidieron exactamente.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Impacto y análisis
&lt;/h2&gt;

&lt;p&gt;El resultado no corrige ninguna vulnerabilidad activa. ECDSA sigue siendo tan seguro o inseguro como lo era antes del 16 de octubre de 2026; lo que cambia es la certeza sobre cómo se eligieron sus parámetros.&lt;/p&gt;

&lt;p&gt;Durante 26 años, la falta de explicación alimentó la hipótesis de que la NSA pudo haber probado miles de semillas hasta encontrar una que escondiera una debilidad conocida solo por ella. Que el proceso completo dependa de un reloj y un generador público, reproducible por cualquiera con una terminal, reduce drásticamente el espacio para esa manipulación.&lt;/p&gt;

&lt;p&gt;La comparación con RSA es útil. El algoritmo RSA no depende de parámetros elegidos por un tercero: cualquiera puede generar su propio par de claves desde cero. ECDSA, en cambio, necesita una curva estándar compartida, y esa dependencia es precisamente lo que mantuvo viva la sospecha sobre el origen de sus parámetros durante más de dos décadas.&lt;/p&gt;

&lt;p&gt;Queda una duda honesta. El método muestra cómo se generó cada semilla, pero no cuántos intentos, es decir, cuántas marcas de tiempo distintas, probó la NSA antes de quedarse con el resultado publicado. Si alguien hubiera podido repetir el proceso con miles de timestamps y elegir la curva más conveniente, el reloj por sí solo no lo habría impedido. Anthropic no aborda ese escenario en su informe, y es la pieza que todavía depende de la confianza en el proceso original, no solo en las matemáticas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué sigue
&lt;/h2&gt;

&lt;p&gt;Anthropic no promete ninguna acción de seguimiento: el informe lo llama explícitamente un “capítulo de cierre divertido”. No hay parche, no hay nueva versión de ECDSA ni recomendación de migrar a otra curva por este motivo.&lt;/p&gt;

&lt;p&gt;Lo que sí abre es un precedente. Usar un modelo de lenguaje para reconstruir decisiones técnicas de hace 30 años, a partir solo de los bytes publicados, es un método que podría aplicarse a otros estándares con parámetros nunca explicados. La criptografía tiene varios más esperando ese mismo tipo de arqueología.&lt;/p&gt;

&lt;p&gt;📖 Resumen en Telegram: &lt;a href="https://telegra.ph/ECDSA-la-sospecha-de-26-a%C3%B1os-era-un-timestamp-no-la-NSA-10-17" rel="noopener noreferrer"&gt;Ver resumen&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Probalo vos: tomá el seed de P-256 (&lt;code&gt;C49D3608 86E70493 6A6678E1 139D26B7 819F7E90&lt;/code&gt;) y seguí el razonamiento completo en el informe original de Anthropic antes de decidir cuánto confiar en una curva de 26 años.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿Qué encontró Claude sobre las semillas de curvas NIST?
&lt;/h3&gt;

&lt;p&gt;Encontró que cada semilla, incluida la de P-256, es el resultado directo del generador lrand48() inicializado con una marca de tiempo Unix, y no de un hash de una frase secreta como se rumoraba desde 2015.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Por qué importaban los parámetros de ECDSA antes de este hallazgo?
&lt;/h3&gt;

&lt;p&gt;Porque la NSA entregó esas semillas al NIST en el año 2000 sin explicar su origen, y una curva elíptica con un parámetro elegido a propósito podría, en teoría, esconder una debilidad conocida solo por quien la eligió.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué es lrand48() y por qué importa para la curva P-256?
&lt;/h3&gt;

&lt;p&gt;Es un generador de números pseudoaleatorios incluido en las librerías C desde los años 90. La semilla de P-256 salió de 1.842 iteraciones de ese generador arrancado con un timestamp del sistema, y el mismo patrón apareció en otras nueve semillas de curvas distintas.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Este hallazgo significa que ECDSA tiene una puerta trasera?
&lt;/h3&gt;

&lt;p&gt;No. Es lo contrario: al mostrar que el proceso de generación fue simple, público y reproducible, el hallazgo reduce la probabilidad de que exista una puerta trasera deliberada en las curvas NIST, aunque no descarta por completo que se hayan probado muchas marcas de tiempo antes de elegir la publicada.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué pasó con la pista de Jerry Solinas sobre el estándar FIPS 186-2?
&lt;/h3&gt;

&lt;p&gt;Solinas, ingeniero de la NSA que ayudó a diseñar el estándar, dijo en 2015 recordar un hash de una frase en broma como origen de las semillas. Murió en 2023, y el hallazgo de Claude en 2026 confirmó que esa pista, repetida durante once años, era incorrecta.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.anthropic.com/research/ecdsa-curve-origins" rel="noopener noreferrer"&gt;Anthropic Research&lt;/a&gt;: el informe original donde Claude reconstruye el origen de las semillas de las curvas NIST.- &lt;a href="https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;: historia y especificación técnica de ECDSA.- &lt;a href="https://en.wikipedia.org/wiki/Linear_congruential_generator" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;: cómo funciona un generador congruencial lineal como lrand48().- &lt;a href="https://csrc.nist.gov" rel="noopener noreferrer"&gt;NIST Computer Security Resource Center&lt;/a&gt;: fuente oficial del estándar FIPS 186-2 y sus curvas elípticas.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📱 &lt;strong&gt;¿Te gusta este contenido?&lt;/strong&gt; Únete a nuestro canal de Telegram &lt;a href="https://t.me/programacion" rel="noopener noreferrer"&gt;@programacion&lt;/a&gt; donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Mercor: solo 208 de 923 ocupaciones con cobertura real de IA</title>
      <dc:creator>lu1tr0n</dc:creator>
      <pubDate>Fri, 16 Oct 2026 20:37:49 +0000</pubDate>
      <link>https://dev.to/lu1tr0n/mercor-solo-208-de-923-ocupaciones-con-cobertura-real-de-ia-10c8</link>
      <guid>https://dev.to/lu1tr0n/mercor-solo-208-de-923-ocupaciones-con-cobertura-real-de-ia-10c8</guid>
      <description>&lt;p&gt;Un chatbot resuelve casi cualquier pregunta de programación en segundos, pero nadie sabe si uno hace bien el trabajo real de un electricista, una enfermera o un inspector de seguridad. Según un informe de &lt;a href="https://www.mercor.com/research/labor-index/benchmark-map/" rel="noopener noreferrer"&gt;Mercor&lt;/a&gt;, apenas 208 de las 923 ocupaciones de EE.UU. tienen una tarea medida por benchmarks de IA con cinco pruebas o más.&lt;/p&gt;

&lt;p&gt;La cifra agregada es más contundente: el 90% de las horas de trabajo pagadas en el país caen en tareas que las pruebas de IA actuales apenas rozan.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Mercor halla que solo 208 de 923 ocupaciones de EE.UU. tienen benchmarks de IA con 5 o más tareas cubiertas.- El 90% de las horas laborales del país caen en tareas con poca o nula cobertura de pruebas de IA.- Veintinueve benchmarks mapean 18.797 tareas O*NET agrupadas en 22 familias laborales, según el estudio.- Seis de 22 familias de empleo, entre ellas salud y educación, no tienen ninguna ocupación bien cubierta.- La cobertura sigue el salario: Computación y Matemáticas llega a 29 de 36 ocupaciones medidas.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Qué es el Benchmark Map de Mercor
&lt;/h2&gt;

&lt;p&gt;El Benchmark Map de Mercor es un índice público que cruza las 923 ocupaciones y las 18.797 tareas de la taxonomía O*NET del Departamento de Trabajo de EE.UU. con 29 benchmarks de IA existentes, para medir qué porción del trabajo real del país tiene al menos una prueba que lo evalúe.&lt;/p&gt;

&lt;p&gt;Mercor es conocida en la industria por operar un marketplace que conecta expertos de distintas profesiones con laboratorios de inteligencia artificial para generar datos de entrenamiento y evaluación. Su propio benchmark, APEX, forma parte de los 29 incluidos en este índice, con la ventaja de que la empresa tiene acceso a prompts aún no publicados y puede mapearlos directamente a cada tarea O*NET.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué pasó
&lt;/h2&gt;

&lt;p&gt;El informe, publicado por Mercor y disponible como mapa interactivo, cruza la &lt;a href="https://www.onetcenter.org/" rel="noopener noreferrer"&gt;taxonomía O*NET&lt;/a&gt; con 29 benchmarks de evaluación de modelos de IA. &lt;a href="https://www.mercor.com/research/labor-index/benchmark-map/" rel="noopener noreferrer"&gt;Según el estudio&lt;/a&gt;, de las 923 ocupaciones que componen la fuerza laboral estadounidense, solo 208 tienen al menos una tarea con cinco o más prompts de algún benchmark mapeados a ella.&lt;/p&gt;

&lt;p&gt;La cifra agregada confirma el problema: nueve de cada diez horas de trabajo pagadas en el país corresponden a tareas sin ningún benchmark o con una cobertura mínima, según el mismo informe. Mercor agrupó las 923 ocupaciones en 22 familias laborales siguiendo la clasificación oficial de O*NET, y encontró que 6 de esas 22 familias, que incluyen salud y educación, no tienen una sola ocupación con cobertura real.&lt;/p&gt;

&lt;p&gt;El mapa divide esas 923 ocupaciones en 22 familias que siguen la clasificación estándar de O*NET, desde Computación y Matemáticas hasta Práctica de la Salud. Cada ocupación aparece como una celda que se colorea según el porcentaje de sus tareas con cobertura, lo que permite ver de un vistazo qué sectores concentran casi toda la atención de las pruebas de IA actuales y cuáles quedan prácticamente a oscuras.&lt;br&gt;
El informe cubre 18.797 tareas repartidas en 923 ocupaciones de EE.UU.&lt;/p&gt;
&lt;h2&gt;
  
  
  Contexto e historia
&lt;/h2&gt;

&lt;p&gt;Los benchmarks que miden qué tan bien razona o programa un modelo se multiplicaron en los últimos años, pero casi todos nacieron para medir capacidades académicas o de ingeniería de software, no el trabajo que sostiene el resto de la economía. Mercor dice que este Benchmark Map es apenas &lt;a href="https://www.mercor.com/research/labor-index/benchmark-map/" rel="noopener noreferrer"&gt;su primer paso&lt;/a&gt; hacia un recurso que planea actualizar de forma continua.&lt;/p&gt;

&lt;p&gt;La idea de medir qué tan expuesta está una ocupación a la IA no es nueva. En 2023, los investigadores Tyna Eloundou, Sam Manning, Pamela Mishkin y Daniel Rock publicaron &lt;a href="https://arxiv.org/abs/2303.10130" rel="noopener noreferrer"&gt;un estudio&lt;/a&gt; que etiquetó miles de tareas de O*NET según cuánto podía acelerarlas un modelo de lenguaje. Mercor retoma esas etiquetas binarias de exposición y las cruza con su propio mapa de cobertura para ver si las pruebas de IA realmente se concentran donde la exposición es mayor.&lt;/p&gt;

&lt;p&gt;El propio informe reconoce que la saturación de pruebas de IA en ingeniería de software no es casual: ese es el tipo de tarea donde es más fácil automatizar la evaluación, porque hay tests, métricas de éxito claras y repositorios públicos, y donde los laboratorios de IA tienen interés directo en medir el progreso de sus propios modelos. Por eso Mercor impuso un tope de benchmarks por familia laboral al armar su índice de 29 pruebas, para que ingeniería de software y investigación académica no dominaran el panorama como lo hacen en catálogos previos.&lt;/p&gt;
&lt;h2&gt;
  
  
  Cómo mide Mercor los benchmarks de IA
&lt;/h2&gt;

&lt;p&gt;El mecanismo detrás del mapa es un juez automático. Mercor usa un modelo de lenguaje para leer cada prompt de los 29 benchmarks evaluados y asignarle una o varias tareas de la lista de 18.797 que define O*NET; un mismo prompt puede mapear a varias tareas distintas si evalúa habilidades que se superponen. Una tarea O*NET cuenta como cubierta únicamente si el juez le asignó cinco o más prompts entre todos los benchmarks, no basta con una mención aislada.&lt;/p&gt;

&lt;p&gt;Para confirmar que el etiquetado automático no inventaba coincidencias, el equipo comparó las etiquetas del juez con las de expertos humanos sobre APEX, el propio banco de pruebas de Mercor, donde la empresa tiene acceso a prompts todavía no publicados. Esa validación es la que permite afirmar que la cobertura medida no es ruido estadístico, sino una aproximación razonable a qué mide cada prueba.&lt;/p&gt;

&lt;p&gt;Los 29 benchmarks del índice se eligieron con cinco criterios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Disponibilidad pública&lt;/strong&gt;: al menos 25 tareas públicas, o acceso directo vía APEX en el caso de Mercor.- &lt;strong&gt;Tareas agénticas&lt;/strong&gt;: pedirle al modelo que ejecute una tarea, no solo que responda preguntas sobre ella.- &lt;strong&gt;Realismo económico&lt;/strong&gt;: tareas parecidas a las que una empresa real le pide a un empleado, no acertijos académicos.- &lt;strong&gt;Diversidad económica&lt;/strong&gt;: un tope de benchmarks por familia laboral para que software e investigación no dominen el índice.- &lt;strong&gt;Dificultad balanceada&lt;/strong&gt;: benchmarks que los modelos de frontera todavía no saturan.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para entender por qué unas ocupaciones tienen más cobertura que otras, Mercor corrió una regresión logística a nivel de tarea: la probabilidad de cobertura se explica con la etiqueta de exposición a la IA de Eloundou et al. y el logaritmo de la masa salarial de la ocupación, calculada multiplicando el salario medio que reporta la Oficina de Estadísticas Laborales por la cantidad de trabajadores y una semana de 40 horas. Los errores estándar se agrupan por familia ocupacional y los intervalos de confianza del 95% usan el método delta.&lt;/p&gt;

&lt;p&gt;El resultado de esa regresión es una curva, no una tabla simple: a medida que sube la masa salarial de una ocupación, la probabilidad estimada de que una de sus tareas esté cubierta por algún benchmark sube de manera consistente, y la brecha entre tareas de alta y baja exposición a la IA se mantiene casi constante en todo el rango salarial. Ser una tarea automatizable según Eloundou et al. ayuda, pero ayuda mucho menos que pertenecer a una ocupación bien pagada.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dot"&gt;&lt;code&gt;&lt;span class="nv"&gt;flowchart&lt;/span&gt; &lt;span class="nv"&gt;TD&lt;/span&gt;
    &lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Taxonomia O*NET: 923 ocupaciones, 18797 tareas"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;B&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Juez LLM etiqueta cada prompt de benchmark con sus tareas O*NET"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="nv"&gt;B&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;C&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Validacion contra etiquetas humanas en APEX"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="nv"&gt;C&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;D&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"Tarea con 5 o mas prompts mapeados?"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nv"&gt;D&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;|&lt;/span&gt;&lt;span class="s2"&gt;"Si"&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;E&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Tarea cubierta"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="nv"&gt;D&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;|&lt;/span&gt;&lt;span class="s2"&gt;"No"&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;F&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Tarea sin cobertura"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Impacto y análisis
&lt;/h2&gt;

&lt;p&gt;Solo 29 benchmarks conforman el índice inicial del Benchmark Map.&lt;br&gt;
El hallazgo más incómodo del informe es el patrón detrás del número total de ocupaciones cubiertas, más que el número en sí. Los benchmarks de IA no se reparten al azar entre las 923 ocupaciones: la probabilidad de que una tarea tenga cobertura sube con la masa salarial de la ocupación a la que pertenece, y sube todavía más si esa tarea ya está etiquetada como expuesta a la IA. El ejemplo que da Mercor es contundente: las tareas de abogados, una ocupación de alta exposición y alto salario, están ampliamente cubiertas, pero tareas igual de expuestas en ocupaciones como secretarias legales o empleados de juzgados apenas aparecen en algún benchmark.&lt;br&gt;
Familia laboral (O*NET)Ocupaciones cubiertasCoberturaComputación y Matemáticas29 de 3680,6%Servicio comunitario10 de 1471,4%Legal5 de 771,4%Negocios y Finanzas33 de 4868,8%Administración y oficina21 de 5141,2%Gerencia21 de 5637,5%Arquitectura e Ingeniería19 de 5633,9%Artes y Medios13 de 4032,5%Ventas6 de 2227,3%Ciencia15 de 6025,0%Educación y Bibliotecas9 de 6214,5%Práctica de la Salud12 de 8913,5%&lt;br&gt;
La tabla muestra solo 12 de las 22 familias para no diluir el patrón, pero la tendencia se mantiene en el resto: cuanto más grande es la nómina agregada de una familia laboral, más probable es que algún benchmark la mida, incluso cuando ocupaciones de menor salario comparten el mismo nivel de exposición a la IA según la clasificación de Eloundou et al.&lt;/p&gt;

&lt;p&gt;El informe llama a esto la larga cola del trabajo de ingresos medios: ocupaciones como asistentes administrativos, técnicos de laboratorio o coordinadores de proyectos rara vez aparecen en un paper de benchmarks, aunque sumen millones de horas trabajadas al año. Sin una medición directa de esas tareas, cualquier afirmación sobre qué tan buena es la IA en el trabajo real descansa sobre una muestra sesgada hacia el código y las finanzas.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💭 Clave:&lt;/strong&gt; la exposición a la IA de una tarea importa menos que el salario de la ocupación a la hora de decidir si alguien se molestó en medirla.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Qué sigue
&lt;/h2&gt;

&lt;p&gt;Mercor dice que este mapa es un recurso vivo, no un informe cerrado: planea sumar más pruebas al índice a medida que aparezcan benchmarks con mayor realismo económico, y ampliar la cobertura hacia las familias laborales que hoy quedan en cero, como salud y educación. El objetivo declarado es que cualquiera pueda consultar, ocupación por ocupación, qué tan bien evaluada está la IA frente al trabajo que esa persona hace todos los días.&lt;/p&gt;

&lt;p&gt;📖 Resumen en Telegram: &lt;a href="https://telegra.ph/Mercor-solo-208-de-923-ocupaciones-con-cobertura-real-de-IA-10-16" rel="noopener noreferrer"&gt;Ver resumen&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Si evaluás modelos de IA o diseñás benchmarks, abrí el &lt;a href="https://www.mercor.com/research/labor-index/benchmark-map/" rel="noopener noreferrer"&gt;mapa interactivo de Mercor&lt;/a&gt; y filtrá la familia laboral de tu propia industria para ver cuántas de sus tareas tienen alguna prueba real detrás.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿Qué mide exactamente el Benchmark Map de Mercor?
&lt;/h3&gt;

&lt;p&gt;Mide qué porción de las 18.797 tareas que O*NET usa para describir el trabajo en Estados Unidos tiene al menos una prueba de modelos de IA que la cubra con cinco o más prompts, agrupando el resultado por las 923 ocupaciones y 22 familias laborales del país.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Cuántas de las 923 ocupaciones de EE.UU. están bien cubiertas?
&lt;/h3&gt;

&lt;p&gt;Solo 208 tienen una tarea con cinco o más prompts mapeados por el índice de Mercor; el resto, unas 715 ocupaciones, no alcanza ese umbral o no tiene ninguna prueba asociada.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Por qué las ocupaciones legales están mejor evaluadas que las administrativas?
&lt;/h3&gt;

&lt;p&gt;Porque la cobertura del Benchmark Map sigue más de cerca la masa salarial de la ocupación que su exposición real a la IA: abogados, con salarios altos, están bien cubiertos, mientras secretarias legales y empleados de juzgados, con exposición similar según Eloundou et al., quedan casi sin pruebas.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué es la exposición a la IA de Eloundou et al.?
&lt;/h3&gt;

&lt;p&gt;Es una etiqueta binaria que ese estudio de 2023 asignó a miles de tareas de O*NET según cuánto podía acelerarlas un modelo de lenguaje grande; Mercor la usa como variable de control para separar el efecto del salario del efecto de la exposición real.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Mercor seguirá actualizando este índice de benchmarks?
&lt;/h3&gt;

&lt;p&gt;Sí: la empresa lo describe como un primer paso hacia un recurso que planea actualizar de forma continua, sumando benchmarks y ocupaciones conforme aparezcan pruebas con suficiente realismo económico.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.mercor.com/research/labor-index/benchmark-map/" rel="noopener noreferrer"&gt;Mercor Benchmark Map&lt;/a&gt;: el informe original con el mapa interactivo de cobertura por ocupación.- &lt;a href="https://www.onetcenter.org/" rel="noopener noreferrer"&gt;O*NET Resource Center&lt;/a&gt;: la taxonomía oficial del Departamento de Trabajo de EE.UU. con las 923 ocupaciones y sus tareas.- &lt;a href="https://arxiv.org/abs/2303.10130" rel="noopener noreferrer"&gt;Eloundou, Manning, Mishkin y Rock (2023)&lt;/a&gt;: el estudio que etiquetó la exposición de cada tarea de O*NET a los modelos de lenguaje.- Bureau of Labor Statistics, Occupational Employment and Wage Statistics: la fuente de los salarios usados para calcular la masa salarial de cada ocupación.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📱 &lt;strong&gt;¿Te gusta este contenido?&lt;/strong&gt; Únete a nuestro canal de Telegram &lt;a href="https://t.me/programacion" rel="noopener noreferrer"&gt;@programacion&lt;/a&gt; donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
