<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://wec.wiline.com/docs/es/tutorials/</id>
    <title>WEC Docs Blog</title>
    <updated>2026-07-01T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://wec.wiline.com/docs/es/tutorials/"/>
    <subtitle>WEC Docs Blog</subtitle>
    <icon>https://wec.wiline.com/docs/es/img/Logo_icon_blue.svg</icon>
    <entry>
        <title type="html"><![CDATA[JSON confiable: valida el esquema de la salida estructurada de tu modelo]]></title>
        <id>https://wec.wiline.com/docs/es/tutorials/structured-output-evals-wiline-inference/</id>
        <link href="https://wec.wiline.com/docs/es/tutorials/structured-output-evals-wiline-inference/"/>
        <updated>2026-07-01T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Los LLM prometen JSON y entregan cercas de markdown y razonamiento. Construye una evaluación de Promptfoo que clasifique tickets de soporte en JSON validado por esquema en la API de Inferencia WEC — con transformaciones para recuperar salidas desordenadas, una matriz de fiabilidad del modelo y una puerta de CI. Cada comando y resultado es real.]]></summary>
        <content type="html"><![CDATA[<div class="tutorialHero"><img class="tutorialHero__brand" src="https://wec.wiline.com/docs/es/assets/images/promptfoo-avatar-31b32c657d72dac0a7edbd77c24ec717.jpg" alt="Promptfoo"><span class="tutorialHero__plus">+</span><svg xmlns="http://www.w3.org/2000/svg" width="242" height="255" fill="none" viewBox="0 0 242 255" class="tutorialHero__wiline"><path fill="#4E8EF7" d="M56.711 11.293A195 195 0 0 1 41.899 4.79c4.788 1.85 9.71 4.028 14.812 6.504M73.807 29.35c-1.302-1.446-2.666-3.233-4.02-5.38 1.326 1.44 2.642 3.24 4.02 5.38M69.761 23.354c-.832-.585-1.752-1.472-2.558-2.714.9.567 1.685 1.49 2.558 2.714M62.97 15.774a10.7 10.7 0 0 1-3.158-1.841c.993.299 1.986.956 3.159 1.841M59.715 13.346c-.818-.194-1.782-.674-2.829-1.494.84.175 1.761.691 2.83 1.494M39.317 3.782c-.524.192-1.413.302-2.455.14.594-.255 1.342-.238 2.455-.14M74.906 31.339c-.334-.153-.73-.61-1.117-1.398.358.143.706.619 1.117 1.398M66.024 18.878c-.239.1-.557.05-.752-.276.35-.201.532-.069.752.276M71.773 150.625c3.363 7.291 7.185 14.411 9.983 21.914 4.474 11.997 11.167 20.832 24.296 24.757 10.62 3.175 20.369 1.795 30.093-1.816 9.937-3.689 15.165-11.978 18.501-21.541 4.349-12.466 9.385-24.443 19.668-33.446 11.32-9.909 24.639-14.788 39.429-15.63 8.3-.473 16.649-.081 25.444.444.472 15.673.385 30.825.55 45.974.038 3.499-1.049 4.719-4.557 4.543-4.982-.25-9.987-.079-14.98.009-.209.004-.4.991-.6 1.521-23.106 1.695-37.691 15.22-47.961 34.651-3.387 6.408-5.913 13.38-10.013 19.273-4.368 6.277-10.134 11.582-15.853 17.196-6.439 1.421-12.244 3.342-18.202 4.416-12.627 2.274-24.661.457-35.764-6.356-1.028-.631-2.395-.709-3.604-1.043 0 0-.117-.11-.047-.495-.818-.973-1.706-1.562-2.593-2.15 0 0 .09-.044.05-.335-.513-.545-.988-.798-1.463-1.052 0 0-.07-.123.012-.492-.561-.894-1.205-1.421-1.848-1.947 0 0 .134-.068.102-.28-.321-.442-.61-.671-.898-.901 0 0 .086-.007.074-.336-2.05-3.429-4.089-6.529-6.127-9.629 0 0 .094-.017.123-.365-3.013-6.117-6.056-11.886-9.1-17.655 0 0 .096-.01.088-.342-1.978-4.073-3.89-7.848-5.997-11.51-.246-.428-1.572-.235-2.398-.329 0 0-.226-.202-.114-.613-.213-.499-.537-.587-.861-.674 0 0-.032-.101.02-.455-.62-.846-1.291-1.339-1.964-1.832 0 0 .133-.083.119-.372-2.476-4.624-5.645-8.048-10.914-8.777 0 0 .108-.031.01-.326-1.673-1.444-3.105-2.918-4.85-3.688-9.751-4.308-20.013-5.643-30.63-5.23-7.305.285-7.316.051-7.317-7.446q-.002-18.496-.002-36.992v-3.57h36.106s-.08.019.133.283c1.43.415 2.646.567 3.863.719 0 0-.077.022.113.296 1.804.591 3.417.908 5.03 1.225 0 0 .139-.037.152.369 1.592.962 3.17 1.517 4.748 2.073 0 0-.091.027.052.267.735.415 1.328.59 1.92.765 0 0-.076.033.053.246.477.325.825.438 1.173.551 0 0-.149.018-.07.246.418.239.757.251 1.096.263 0 0 .23.058.189.465 1.522 1.319 3.084 2.231 4.647 3.143 0 0-.105.033-.015.316 2.723 2.764 5.356 5.244 7.989 7.724 0 0-.104.062-.094.352 1.01 1.401 2.01 2.512 3.01 3.624"></path><path fill="#4E8EF7" d="M214.77 52.819c-3.343.846-6.723 1.574-10.024 2.562-11.388 3.408-20.567 10.142-27.166 19.865-5.097 7.511-9.621 15.483-13.685 23.608-6.809 13.613-15.725 24.809-31.343 28.198-24.707 5.362-45.22-.126-57.852-24.564C69.466 92.363 63.8 82.375 57.349 73c-3.369-4.896-8.866-8.326-13.679-13.041-1.11-1.409-1.81-2.472-2.794-2.937-10.697-5.056-21.87-7.3-33.686-5.344-3.877.641-5.72-.297-5.615-4.86.294-12.816.26-25.646.019-38.464C1.509 3.874 3.15 2.5 7.429 2.716c9.774.492 19.564.678 29.347.989 0 0-.076.026.082.257 1.046.122 1.935.012 2.825-.098.575.043 1.15.086 2.038.692 5.265 2.72 10.218 4.877 15.17 7.034 0 0-.108.03-.016.317 1.058.765 2.022 1.245 2.987 1.724 0 0-.113.048-.082.354 1.144.876 2.258 1.446 3.372 2.017 0 0 .18.145.097.506.223.404.53.447.837.49 0 0 .214.13.144.504.244.426.558.477.873.528 0 0 .171.124.108.485.256.412.574.464.893.515 0 0 .167.127.105.493.265.437.592.509.919.58 0 0 .078.108.019.5.861 1.28 1.782 2.166 2.702 3.053 0 0-.101.061-.087.368 1.379 2.093 2.743 3.88 4.107 5.666 0 0-.094.011-.092.297.4.742.795 1.2 1.19 1.656 0 0-.121.02-.165.337.685 1.432 1.413 2.549 2.14 3.665 0 0-.11.024-.129.299.326.776.67 1.277 1.014 1.78 0 0-.084-.005-.1.367.933 4.079.311 8.497 4.648 10.67 4.69 12.164 12.107 21.794 25.852 23.497 19.618 2.431 35.321.062 45.299-24.554 3.398-8.383 7.099-16.902 12.232-24.264 8.353-11.979 21.046-17.795 35.832-20.32 1.666.417 2.649.651 3.624.622 9.93-.298 19.865-.513 29.785-1.032 3.818-.2 4.825 1.198 4.746 4.853-.218 9.967.004 19.944-.109 29.915-.052 4.583 1.149 10.606-1.239 13.302-2.044 2.308-8.394.86-12.842.967-3.82.092-7.644.036-11.467.045.065.31.097.633.213.922.034.084.306.072.469.105M37.585 127.354c-11.585.344-23.368.344-35.907.344v3.57q0 18.496.002 36.992c0 7.497.012 7.731 7.318 7.446 10.616-.413 20.878.922 30.63 5.23 1.743.77 3.176 2.244 4.84 3.744-13.655-7.873-28.93-7.767-44.468-7.398v-52.503c12.516.308 25.063-1.222 37.585 2.575"></path><path fill="#4E8EF7" d="M36.619 3.401c-9.626-.007-19.415-.193-29.19-.685C3.151 2.5 1.51 3.874 1.594 8.354c.241 12.818.275 25.648-.018 38.464-.105 4.563 1.737 5.501 5.614 4.86 11.816-1.956 22.99.288 33.686 5.344.984.465 1.685 1.529 2.616 2.66-13.39-7.538-28.284-7.343-43.479-6.78V0C12.314 1.045 24.388 2.071 36.62 3.401M215.232 52.852c-.625-.066-.897-.054-.931-.138-.116-.29-.148-.612-.212-.922 3.822-.01 7.646.047 11.467-.045 4.448-.108 10.797 1.341 12.842-.967 2.387-2.696 1.186-8.719 1.238-13.302.114-9.971-.109-19.948.109-29.915.08-3.655-.928-5.052-4.746-4.853-9.919.519-19.855.734-29.785 1.032-.975.03-1.958-.205-3.228-.58C214.559 1.945 227.423.991 240.73.003v52.882c-8.276 0-16.656 0-25.498-.033M220.071 177.398c-.271-.573-.08-1.561.129-1.565 4.993-.088 9.998-.259 14.98-.009 3.508.176 4.595-1.044 4.557-4.543-.165-15.149-.078-30.301-.088-45.911.458.372 1.32 1.202 1.324 2.036.079 16.424.059 32.848.059 50.036-6.984 0-13.737 0-20.961-.044M88.39 245.741c1.022.084 2.39.162 3.417.793 11.102 6.813 23.137 8.63 35.763 6.356 5.959-1.073 11.764-2.995 17.871-4.34-5.612 2.132-11.343 5.166-17.325 5.79-13.898 1.451-27.803 1.213-39.725-8.599M66.556 210.212c2.975 5.411 6.018 11.18 9.02 17.344-3.011-5.4-5.982-11.193-9.02-17.344M58.341 197.934c.666-.167 1.992-.36 2.238.068 2.107 3.662 4.018 7.437 5.985 11.557-2.69-3.535-5.376-7.449-8.223-11.625M44.637 185.241c5.099.438 8.268 3.862 10.737 8.564-3.518-2.513-7.043-5.393-10.737-8.564M82.29 48.422c-4.252-1.834-3.63-6.252-4.548-10.37 1.508 3.069 2.986 6.55 4.548 10.37M68.757 146.326c-2.533-2.157-5.166-4.637-7.882-7.455 2.539 2.151 5.16 4.641 7.882 7.455M75.532 228.226a122 122 0 0 1 6.047 9.319c-1.993-2.742-3.986-5.854-6.047-9.319M71.695 150.312c-.922-.799-1.921-1.91-2.906-3.366.952.788 1.89 1.921 2.906 3.366M46.663 130.007c-1.353-.103-2.966-.42-4.767-1.051 1.377.07 2.941.454 4.767 1.051M51.647 132.369a13.1 13.1 0 0 1-4.501-1.77c1.48.227 2.902.852 4.5 1.77M60.724 138.321a17.7 17.7 0 0 1-4.395-2.781c1.501.609 2.869 1.551 4.395 2.781M76.889 35.31c-.674-.781-1.402-1.898-2.068-3.39.711.768 1.362 1.912 2.068 3.39M85.668 243.124c.782.31 1.67.899 2.423 1.825-.862-.29-1.59-.919-2.423-1.825M41.596 128.372c-1.033.176-2.25.024-3.68-.432 1.023-.169 2.26-.032 3.68.432M55.367 194.37c.568.222 1.24.715 1.808 1.548-.637-.199-1.17-.738-1.808-1.548M82.397 239.295c.56.251 1.204.778 1.71 1.628-.633-.235-1.13-.794-1.71-1.628M84.291 241.679c.334.034.809.287 1.298.875-.376.004-.766-.326-1.298-.875M53.64 133.426c-.438.094-1.03-.081-1.764-.543.441-.101 1.026.086 1.764.543M77.805 37.409c-.322-.188-.666-.69-.978-1.52.34.182.648.693.978 1.52M81.588 238.108c.218-.039.507.19.823.707-.233.045-.493-.196-.823-.707M55.906 134.832c-.198.157-.537.145-.92-.161.262-.29.537-.195.92.161M57.276 196.552c.254-.079.578.009.756.361-.377.171-.557.018-.756-.361M54.888 134.25c-.215.129-.563.017-1.038-.351.217-.133.56-.012 1.038.351M64.014 16.858a.545.545 0 0 1-.71-.247c.344-.19.51-.067.71.247M65.027 17.882a.574.574 0 0 1-.734-.272c.35-.193.525-.063.734.272M67.045 19.939c-.244.093-.57.021-.777-.329.358-.192.55-.044.777.329"></path></svg><span class="tutorialHero__plus">+</span><img class="tutorialHero__brand" src="data:image/png;base64,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" alt="JSON"></div>
<p>"Devuelve solo JSON" es una de las instrucciones más comunes en aplicaciones LLM en producción — y una de las menos fiables. Los modelos envuelven JSON en cercas de markdown, añaden una frase amigable, o (si son modelos de razonamiento) narran todo su proceso de pensamiento alrededor de ello. Cualquiera de esos casos rompe un estricto <code>JSON.parse</code>, y tu pipeline se cae.</p>
<p>En esta guía construimos una evaluación de <a href="https://www.promptfoo.dev/" target="_blank" rel="noopener noreferrer" class="">Promptfoo</a> que hace que los modelos de <strong>API de Inferencia WEC</strong> clasifiquen tickets de soporte en <strong>JSON validado por esquema</strong>, luego lo endurecemos contra el desorden del mundo real — y lo usamos para elegir un modelo en el que realmente puedes confiar. Esta es la parte 2 de la serie de evaluaciones (consulta <a class="" href="https://wec.wiline.com/docs/es/tutorials/eval-models-promptfoo-wiline-inference/">parte 1</a> para la configuración inicial). Todo aquí se ejecutó en vivo contra <code>https://inference.wiline.com</code>.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="cómo-funciona-la-evaluación">Cómo funciona la evaluación<a href="https://wec.wiline.com/docs/es/tutorials/structured-output-evals-wiline-inference/#c%C3%B3mo-funciona-la-evaluaci%C3%B3n" class="hash-link" aria-label="Enlace directo al Cómo funciona la evaluación" title="Enlace directo al Cómo funciona la evaluación" translate="no">​</a></h2>
<p>Cada test pasa por la misma tubería. El detalle más importante: el <strong><code>transform</code> se ejecuta
sobre la salida del modelo <em>antes</em> de que la aserción la vea</strong> — así es como recuperamos JSON
desordenado antes de validarlo.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="requisitos-previos">Requisitos previos<a href="https://wec.wiline.com/docs/es/tutorials/structured-output-evals-wiline-inference/#requisitos-previos" class="hash-link" aria-label="Enlace directo al Requisitos previos" title="Enlace directo al Requisitos previos" translate="no">​</a></h2>
<ul>
<li class="">
<p>Una <strong>clave de API de Inferencia WEC</strong> — consulta <a class="" href="https://wec.wiline.com/docs/es/cloud_portal/platform/inference/api_keys/">Inferencia → Claves de API</a>.</p>
</li>
<li class="">
<p>Los nombres de los modelos utilizados aquí (<code>GLM-5.2</code>, <code>gemma4</code>, <code>Qwen2.5-3B-Instruct</code>, <code>qwen3.5:9B</code>) provienen del <a class="" href="https://wec.wiline.com/docs/es/cloud_portal/platform/inference/models_hub/">Hub de Modelos</a> — intercambia por lo que hayas habilitado.</p>
</li>
<li class="">
<p><strong>Node.js 22+</strong> y tu clave exportada (Promptfoo se ejecuta a través de <code>npx</code>):</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">nvm use 24</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">export WEC_API_KEY='sk-your-key'</span><br></div></code></pre></div></div>
</li>
</ul>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>tip</div><div class="admonitionContent_BuS1"><p>Si tu primera ejecución da error con <code>Cannot convert argument to a ByteString</code>, tu clave pegada tiene un carácter invisible — consulta <a href="https://wec.wiline.com/docs/es/tutorials/structured-output-evals-wiline-inference/#ts-invisible-key" class="">Solución de problemas</a> al final.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-1--un-esquema-estricto-y-un-fallo-real">Paso 1 — Un esquema estricto y un fallo real<a href="https://wec.wiline.com/docs/es/tutorials/structured-output-evals-wiline-inference/#paso-1--un-esquema-estricto-y-un-fallo-real" class="hash-link" aria-label="Enlace directo al Paso 1 — Un esquema estricto y un fallo real" title="Enlace directo al Paso 1 — Un esquema estricto y un fallo real" translate="no">​</a></h2>
<p>Haremos algo más cercano a una carga de trabajo real que "capital de Francia": clasificar un ticket de soporte en <code>categoría</code>, <code>prioridad</code> y un breve <code>resumen</code>. La mejora clave sobre una verificación básica es el <strong>Esquema JSON</strong> — impone <code>enum</code>s, por lo que un modelo que inventa una categoría falla.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">cat &gt; promptfooconfig.yaml &lt;&lt;'EOF'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">description: "API de Inferencia WEC — salida estructurada (esquema JSON)"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">providers:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - id: openai:chat:zai-org/GLM-5.2</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    config:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      apiBaseUrl: https://inference.wiline.com/v1</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      apiKeyEnvar: WEC_API_KEY</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      temperature: 0</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">prompts:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - |</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Clasifica este ticket de soporte. Devuelve SOLO JSON con claves:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    "categoría" (una de: facturación, técnica, cuenta, otro),</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    "prioridad" (una de: baja, media, alta),</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    "resumen" (cadena, máximo 12 palabras).</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Ticket: {{ticket}}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">defaultTest:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  assert:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    - type: is-json</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      value:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        type: object</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        required: [categoría, prioridad, resumen]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        properties:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          categoría: { type: string, enum: [facturación, técnica, cuenta, otro] }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          prioridad: { type: string, enum: [baja, media, alta] }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          resumen: { type: string }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">tests:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - vars: { ticket: "Me cobraron dos veces por mi instancia este mes." }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    assert: [{ type: icontains, value: facturación }]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - vars: { ticket: "Mi VM no arranca después de la última restauración de instantánea." }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    assert: [{ type: icontains, value: técnica }]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npx -y promptfoo@latest eval -c promptfooconfig.yaml</span><br></div></code></pre></div></div>
<p>El resultado — <strong>50%</strong>, y el fallo es instructivo:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">Salida</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">ticket                                    [zai-org/GLM-5.2]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Me cobraron dos veces por mi instancia…      [FAIL] ```json</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                                          { "categoría": "facturación", "prioridad": "alta", … }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                                          ```</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Mi VM no arranca después de la última…        [PASS] { "categoría": "técnica", … }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">✓ 1 aprobado (50%)   ✗ 1 fallido (50%)</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="Paso 1 — un ticket falla porque el JSON está envuelto en cercas de markdown" src="https://wec.wiline.com/docs/es/assets/images/json-step1-schema-fail-dc2b9c12c0ee29b0086499d5c76f2fd6.png" width="964" height="756" class="img_ev3q">
<strong>Figura 1.</strong> Mismo modelo, mismo aviso, temperatura 0 — una respuesta es limpia, la otra está envuelta en <code>```json</code> cercas, por lo que <code>is-json</code> no puede analizarla.</p>
<p>El <em>contenido</em> es correcto; el <em>formato</em> no lo es. <code>is-json</code> valida tanto "¿es JSON analizable?" como "¿cumple con el esquema?" — y la salida cercada falla la primera prueba.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-2--recuperar-salida-cercada-con-un-transform">Paso 2 — Recuperar salida cercada con un <code>transform</code><a href="https://wec.wiline.com/docs/es/tutorials/structured-output-evals-wiline-inference/#paso-2--recuperar-salida-cercada-con-un-transform" class="hash-link" aria-label="Enlace directo al paso-2--recuperar-salida-cercada-con-un-transform" title="Enlace directo al paso-2--recuperar-salida-cercada-con-un-transform" translate="no">​</a></h2>
<p>Un <code>transform</code> se ejecuta en la salida del modelo <strong>antes</strong> de las afirmaciones. Aquí eliminamos las cercas. (Este bloque utiliza cuatro comillas invertidas porque la configuración en sí contiene tres comillas invertidas.)</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">cat &gt; promptfooconfig.yaml &lt;&lt;'EOF'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">description: "API de Inferencia WEC — transformación de eliminación de cercas"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">providers:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - id: openai:chat:zai-org/GLM-5.2</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    config:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      apiBaseUrl: https://inference.wiline.com/v1</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      apiKeyEnvar: WEC_API_KEY</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      temperature: 0</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">prompts:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - |</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Clasifica este ticket de soporte. Devuelve SOLO JSON con claves:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    "categoría" (una de: facturación, técnica, cuenta, otro),</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    "prioridad" (una de: baja, media, alta),</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    "resumen" (cadena, máximo 12 palabras).</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Ticket: {{ticket}}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">defaultTest:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  options:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    transform: |</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      return output.replace(/```json\n?|\n?```/g, '').trim();</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  assert:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    - type: is-json</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      value:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        type: object</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        required: [categoría, prioridad, resumen]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        properties:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          categoría: { type: string, enum: [facturación, técnica, cuenta, otro] }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          prioridad: { type: string, enum: [baja, media, alta] }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          resumen: { type: string }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">tests:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - vars: { ticket: "Me cobraron dos veces por mi instancia este mes." }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    assert: [{ type: icontains, value: facturación }]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - vars: { ticket: "Mi VM no arranca después de la última restauración de instantánea." }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    assert: [{ type: icontains, value: técnica }]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npx -y promptfoo@latest eval -c promptfooconfig.yaml --no-cache</span><br></div></code></pre></div></div>
<p>Ahora ambos pasan — <strong>2/2</strong>. Las cercas se eliminan antes de que <code>is-json</code> vea la salida.</p>
<p><img decoding="async" loading="lazy" alt="Paso 2 — con la transformación de eliminación de cercas, ambos tickets pasan" src="https://wec.wiline.com/docs/es/assets/images/json-step2-transform-pass-14cb388c6725d0aacffeee1bb320f553.png" width="966" height="735" class="img_ev3q">
<strong>Figura 2.</strong> El <code>transform</code> elimina las cercas de código, por lo que el ticket que falló anteriormente se analiza.</p>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span><code>transform</code> necesita un <code>return</code> explícito</div><div class="admonitionContent_BuS1"><p>Un <code>transform</code> de varias líneas es un cuerpo de función, no una expresión — omite <code>return</code> y obtienes <code>Transform function did not return a value</code>. Consulta <a href="https://wec.wiline.com/docs/es/tutorials/structured-output-evals-wiline-inference/#ts-transform-return" class="">Solución de problemas</a>.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-3--la-solución-más-limpia-modo-json">Paso 3 — La solución más limpia: modo JSON<a href="https://wec.wiline.com/docs/es/tutorials/structured-output-evals-wiline-inference/#paso-3--la-soluci%C3%B3n-m%C3%A1s-limpia-modo-json" class="hash-link" aria-label="Enlace directo al Paso 3 — La solución más limpia: modo JSON" title="Enlace directo al Paso 3 — La solución más limpia: modo JSON" translate="no">​</a></h2>
<p>En lugar de limpiar después del modelo, pide a la API que emita solo JSON válido a través de <code>response_format</code>. Sin <code>transform</code> esta vez:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">cat &gt; promptfooconfig.yaml &lt;&lt;'EOF'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">description: "API de Inferencia WEC — modo JSON (response_format)"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">providers:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - id: openai:chat:zai-org/GLM-5.2</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    config:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      apiBaseUrl: https://inference.wiline.com/v1</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      apiKeyEnvar: WEC_API_KEY</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      temperature: 0</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      response_format: { type: json_object }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">prompts:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - |</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Clasifica este ticket de soporte. Devuelve SOLO JSON con claves:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    "categoría" (una de: facturación, técnica, cuenta, otro),</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    "prioridad" (una de: baja, media, alta),</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    "resumen" (cadena, máximo 12 palabras).</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Ticket: {{ticket}}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">defaultTest:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  assert:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    - type: is-json</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      value:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        type: object</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        required: [categoría, prioridad, resumen]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        properties:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          categoría: { type: string, enum: [facturación, técnica, cuenta, otro] }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          prioridad: { type: string, enum: [baja, media, alta] }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          resumen: { type: string }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">tests:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - vars: { ticket: "Me cobraron dos veces por mi instancia este mes." }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    assert: [{ type: icontains, value: facturación }]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - vars: { ticket: "Mi VM no arranca después de la última restauración de instantánea." }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    assert: [{ type: icontains, value: técnica }]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npx -y promptfoo@latest eval -c promptfooconfig.yaml --no-cache</span><br></div></code></pre></div></div>
<p><strong>2/2 APROBADO</strong> sin transformación — parece que el modo JSON funciona.</p>
<p><img decoding="async" loading="lazy" alt="Paso 3 — el modo JSON pasa en GLM-5.2" src="https://wec.wiline.com/docs/es/assets/images/json-step3-jsonmode-6cabcc01d214aaa3b14b1a0842eeb720.png" width="962" height="718" class="img_ev3q">
<strong>Figura 3.</strong> Con <code>response_format: json_object</code>, GLM-5.2 devuelve JSON limpio, sin necesidad de transformación.</p>
<p>Pero antes de confiar en <code>response_format</code>, necesitamos probarlo en todos los modelos — porque un solo resultado verde puede ser suerte, no cumplimiento.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-4--matriz-de-fiabilidad-entre-modelos-wec">Paso 4 — Matriz de fiabilidad entre modelos WEC<a href="https://wec.wiline.com/docs/es/tutorials/structured-output-evals-wiline-inference/#paso-4--matriz-de-fiabilidad-entre-modelos-wec" class="hash-link" aria-label="Enlace directo al Paso 4 — Matriz de fiabilidad entre modelos WEC" title="Enlace directo al Paso 4 — Matriz de fiabilidad entre modelos WEC" translate="no">​</a></h2>
<p>Ejecuta la configuración en modo JSON contra los cuatro modelos de chat. Esta es la recompensa — una <strong>comparación de fiabilidad específica de WEC</strong> que no puedes obtener de una tabla de clasificación genérica.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">cat &gt; promptfooconfig.yaml &lt;&lt;'EOF'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">description: "API de Inferencia WEC — fiabilidad en modo JSON entre modelos"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">providers:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - id: openai:chat:zai-org/GLM-5.2</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    config: { apiBaseUrl: https://inference.wiline.com/v1, apiKeyEnvar: WEC_API_KEY, temperature: 0, response_format: { type: json_object } }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - id: openai:chat:gemma4</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    config: { apiBaseUrl: https://inference.wiline.com/v1, apiKeyEnvar: WEC_API_KEY, temperature: 0, response_format: { type: json_object } }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - id: openai:chat:Qwen2.5-3B-Instruct</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    config: { apiBaseUrl: https://inference.wiline.com/v1, apiKeyEnvar: WEC_API_KEY, temperature: 0, response_format: { type: json_object } }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - id: openai:chat:qwen3.5:9B</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    config: { apiBaseUrl: https://inference.wiline.com/v1, apiKeyEnvar: WEC_API_KEY, temperature: 0, response_format: { type: json_object } }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">prompts:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - |</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Clasifica este ticket de soporte. Devuelve SOLO JSON con claves:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    "categoría" (una de: facturación, técnica, cuenta, otro),</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    "prioridad" (una de: baja, media, alta),</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    "resumen" (cadena, máximo 12 palabras).</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Ticket: {{ticket}}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">defaultTest:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  assert:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    - type: is-json</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      value:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        type: object</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        required: [categoría, prioridad, resumen]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        properties:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          categoría: { type: string, enum: [facturación, técnica, cuenta, otro] }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          prioridad: { type: string, enum: [baja, media, alta] }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          resumen: { type: string }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">tests:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - vars: { ticket: "Me cobraron dos veces por mi instancia este mes." }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    assert: [{ type: icontains, value: facturación }]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - vars: { ticket: "Mi VM no arranca después de la última restauración de instantánea." }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    assert: [{ type: icontains, value: técnica }]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npx -y promptfoo@latest eval -c promptfooconfig.yaml --no-cache</span><br></div></code></pre></div></div>
<p><strong>4/8.</strong> GLM-5.2 y Qwen2.5-3B pasan limpiamente; <strong>gemma4 y qwen3.5:9B vierten su razonamiento</strong>
("Proceso de Pensamiento…") en lugar de JSON — a pesar de que <code>response_format</code> esté configurado.</p>
<p><img decoding="async" loading="lazy" alt="Paso 4 — con modo JSON, dos modelos pasan y dos emiten razonamiento" src="https://wec.wiline.com/docs/es/assets/images/json-matrix-responseformat-d227b3a1ca8bffbf85f89ca2da11eb87.png" width="826" height="983" class="img_ev3q">
<strong>Figura 4.</strong> <code>response_format</code> configurado en los cuatro — pero los dos modelos orientados al razonamiento lo ignoran.</p>
<table><thead><tr><th>Modelo</th><th>Resultado</th><th>Tokens (2 llamadas)</th></tr></thead><tbody><tr><td><code>zai-org/GLM-5.2</code></td><td>✅✅</td><td>202</td></tr><tr><td><code>Qwen2.5-3B-Instruct</code></td><td>✅✅</td><td>243</td></tr><tr><td><code>gemma4</code></td><td>❌❌ (razonamiento)</td><td>251</td></tr><tr><td><code>qwen3.5:9B</code></td><td>❌❌ (razonamiento)</td><td>1,757</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-5--verificar-está-response_format-haciendo-algo">Paso 5 — Verificar: ¿está <code>response_format</code> haciendo algo?<a href="https://wec.wiline.com/docs/es/tutorials/structured-output-evals-wiline-inference/#paso-5--verificar-est%C3%A1-response_format-haciendo-algo" class="hash-link" aria-label="Enlace directo al paso-5--verificar-está-response_format-haciendo-algo" title="Enlace directo al paso-5--verificar-está-response_format-haciendo-algo" translate="no">​</a></h2>
<p>No asumas que el razonamiento fue <em>causado</em> por el modo JSON. Vuelve a ejecutar la misma matriz <strong>sin</strong>
<code>response_format</code> (usando la transformación de eliminación de cercas en su lugar) y compara:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">cat &gt; promptfooconfig.yaml &lt;&lt;'EOF'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">description: "API de Inferencia WEC — matriz sin response_format"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">providers:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - id: openai:chat:zai-org/GLM-5.2</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    config: { apiBaseUrl: https://inference.wiline.com/v1, apiKeyEnvar: WEC_API_KEY, temperature: 0 }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - id: openai:chat:gemma4</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    config: { apiBaseUrl: https://inference.wiline.com/v1, apiKeyEnvar: WEC_API_KEY, temperature: 0 }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - id: openai:chat:Qwen2.5-3B-Instruct</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    config: { apiBaseUrl: https://inference.wiline.com/v1, apiKeyEnvar: WEC_API_KEY, temperature: 0 }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - id: openai:chat:qwen3.5:9B</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    config: { apiBaseUrl: https://inference.wiline.com/v1, apiKeyEnvar: WEC_API_KEY, temperature: 0 }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">prompts:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - |</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Clasifica este ticket de soporte. Devuelve SOLO JSON con claves:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    "categoría" (una de: facturación, técnica, cuenta, otro),</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    "prioridad" (una de: baja, media, alta),</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    "resumen" (cadena, máximo 12 palabras).</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Ticket: {{ticket}}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">defaultTest:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  options:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    transform: |</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      return output.replace(/```json\n?|\n?```/g, '').trim();</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  assert:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    - type: is-json</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      value:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        type: object</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        required: [categoría, prioridad, resumen]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        properties:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          categoría: { type: string, enum: [facturación, técnica, cuenta, otro] }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          prioridad: { type: string, enum: [baja, media, alta] }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          resumen: { type: string }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">tests:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - vars: { ticket: "Me cobraron dos veces por mi instancia este mes." }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    assert: [{ type: icontains, value: facturación }]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - vars: { ticket: "Mi VM no arranca después de la última restauración de instantánea." }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    assert: [{ type: icontains, value: técnica }]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npx -y promptfoo@latest eval -c promptfooconfig.yaml --no-cache</span><br></div></code></pre></div></div>
<p>Sigue siendo <strong>4/8</strong> — los mismos modelos fallan. Así que <code>response_format</code> <strong>no cambió nada</strong>: en WEC hoy es efectivamente un no-op (el backend no está haciendo cumplir el modo JSON). El comportamiento de razonamiento pertenece a los modelos, no a la bandera.</p>
<p><img decoding="async" loading="lazy" alt="Paso 5 — mismo 4/8 sin response_format, demostrando que no estaba haciendo nada" src="https://wec.wiline.com/docs/es/assets/images/json-matrix-transform-77179c366ad47c9eb5b00fe2b5d92381.png" width="824" height="983" class="img_ev3q">
<strong>Figura 5.</strong> Resultado idéntico sin modo JSON — la bandera no estaba haciendo nada.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>nota</div><div class="admonitionContent_BuS1"><p>Lección que merece su propio llamado: <strong>no atribuyas un resultado a un cambio hasta que hayas probado el cambio en aislamiento.</strong> Una ejecución adicional convirtió una conclusión incorrecta en la correcta.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-6--una-transformación-más-robusta-extraer-el-json">Paso 6 — Una transformación más robusta: extraer el JSON<a href="https://wec.wiline.com/docs/es/tutorials/structured-output-evals-wiline-inference/#paso-6--una-transformaci%C3%B3n-m%C3%A1s-robusta-extraer-el-json" class="hash-link" aria-label="Enlace directo al Paso 6 — Una transformación más robusta: extraer el JSON" title="Enlace directo al Paso 6 — Una transformación más robusta: extraer el JSON" translate="no">​</a></h2>
<p>Los modelos de razonamiento <em>sí</em> emiten JSON — simplemente está enterrado en prosa. En lugar de eliminar cercas,
<strong>extrae el primer bloque <code>{ … }</code></strong> de cualquier parte de la salida:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">cat &gt; promptfooconfig.yaml &lt;&lt;'EOF'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">description: "API de Inferencia WEC — transformación de extracción de JSON"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">providers:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - id: openai:chat:zai-org/GLM-5.2</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    config: { apiBaseUrl: https://inference.wiline.com/v1, apiKeyEnvar: WEC_API_KEY, temperature: 0 }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - id: openai:chat:gemma4</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    config: { apiBaseUrl: https://inference.wiline.com/v1, apiKeyEnvar: WEC_API_KEY, temperature: 0 }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - id: openai:chat:Qwen2.5-3B-Instruct</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    config: { apiBaseUrl: https://inference.wiline.com/v1, apiKeyEnvar: WEC_API_KEY, temperature: 0 }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - id: openai:chat:qwen3.5:9B</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    config: { apiBaseUrl: https://inference.wiline.com/v1, apiKeyEnvar: WEC_API_KEY, temperature: 0 }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">prompts:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - |</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Clasifica este ticket de soporte. Devuelve SOLO JSON con claves:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    "categoría" (una de: facturación, técnica, cuenta, otro),</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    "prioridad" (una de: baja, media, alta),</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    "resumen" (cadena, máximo 12 palabras).</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Ticket: {{ticket}}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">defaultTest:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  options:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    transform: |</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      const m = output.match(/\{[\s\S]*\}/);</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      return m ? m[0] : output;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  assert:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    - type: is-json</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      value:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        type: object</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        required: [categoría, prioridad, resumen]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        properties:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          categoría: { type: string, enum: [facturación, técnica, cuenta, otro] }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          prioridad: { type: string, enum: [baja, media, alta] }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          resumen: { type: string }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">tests:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - vars: { ticket: "Me cobraron dos veces por mi instancia este mes." }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    assert: [{ type: icontains, value: facturación }]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - vars: { ticket: "Mi VM no arranca después de la última restauración de instantánea." }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    assert: [{ type: icontains, value: técnica }]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npx -y promptfoo@latest eval -c promptfooconfig.yaml --no-cache</span><br></div></code></pre></div></div>
<p><strong>7/8</strong> — un gran salto. gemma4 y Qwen2.5 se recuperan completamente; qwen3.5:9B recupera uno de dos. El
único fallo restante es revelador: esa respuesta emitió JSON <strong>y luego siguió razonando</strong> (con
más llaves), por lo que la codificación codiciosa <code>{ … }</code> capturó en exceso y produjo JSON inválido.</p>
<p><img decoding="async" loading="lazy" alt="Paso 6 — la extracción de JSON rescata la mayoría de los modelos (7/8)" src="https://wec.wiline.com/docs/es/assets/images/json-matrix-extraction-3b34ee4a32f6990e447d9590d51d8911.png" width="827" height="901" class="img_ev3q">
<strong>Figura 6.</strong> Extraer el bloque JSON recupera los modelos habladores — pero es una heurística, no una garantía (nota el único fallo por captura excesiva).</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-7--verlo-en-el-navegador">Paso 7 — Verlo en el navegador<a href="https://wec.wiline.com/docs/es/tutorials/structured-output-evals-wiline-inference/#paso-7--verlo-en-el-navegador" class="hash-link" aria-label="Enlace directo al Paso 7 — Verlo en el navegador" title="Enlace directo al Paso 7 — Verlo en el navegador" translate="no">​</a></h2>
<p>La tabla del terminal está bien para una lectura rápida, pero el informe web es donde una matriz como esta cobra vida:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">npx -y promptfoo@latest view</span><br></div></code></pre></div></div>
<p>El panel en la parte superior te da la imagen de fiabilidad del modelo de un vistazo — las barras de <strong>Tasa de Aprobación (%)</strong> son las que debes observar aquí: tres modelos altos, <code>qwen3.5:9B</code> sentado en 50%. Abajo, cada proveedor es una columna que puedes expandir para ver la respuesta <strong>completa</strong> (así es como obtuvimos el fallo por captura excesiva en <a href="https://wec.wiline.com/docs/es/tutorials/structured-output-evals-wiline-inference/#ts-reasoning-dump" class="">Figura 10</a> — el terminal lo trunca).</p>
<p><img decoding="async" loading="lazy" alt="El informe de Promptfoo: barras de Tasa de Aprobación por modelo y salida expandible por caso" src="https://wec.wiline.com/docs/es/assets/images/json-view-report-3739e5abd8419ffc8ece8271ff559851.png" width="1896" height="1034" class="img_ev3q">
<strong>Figura 7.</strong> El panel del informe — las barras de Tasa de Aprobación convierten la matriz de modelos en un ranking de un vistazo, y cualquier celda se expande a la salida completa.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>nota</div><div class="admonitionContent_BuS1"><p>Los otros gráficos del informe (el dispersor de aviso contra aviso) brillan cuando estás <strong>realizando pruebas A/B de avisos</strong>, no de modelos — un buen tema para una publicación posterior. Para una ejecución de un solo aviso y múltiples modelos como esta, las barras de Tasa de Aprobación son el gráfico que importa.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-8--protegerlo-en-ci">Paso 8 — Protegerlo en CI<a href="https://wec.wiline.com/docs/es/tutorials/structured-output-evals-wiline-inference/#paso-8--protegerlo-en-ci" class="hash-link" aria-label="Enlace directo al Paso 8 — Protegerlo en CI" title="Enlace directo al Paso 8 — Protegerlo en CI" translate="no">​</a></h2>
<p>Promptfoo sale con un código no cero cuando las pruebas fallan, así que una línea es una puerta (ponla en un script o <code>Makefile</code>, nunca en tu shell interactivo):</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">npx -y promptfoo@latest eval -c promptfooconfig.yaml \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  &amp;&amp; echo "✅ aprobado" \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  || { echo "❌ evaluación fallida — bloqueando"; exit 1; }</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="La puerta bloqueando en una evaluación fallida" src="https://wec.wiline.com/docs/es/assets/images/json-ci-gate-71912b506157c96cfd7daa8e5c81f300.png" width="948" height="977" class="img_ev3q">
<strong>Figura 8.</strong> Un fallo real bloquea: 7/8 aprobados, pero la captura excesiva de <code>qwen3.5:9B</code> (del Paso 6) activa la puerta — el código de salida no cero es la señal que CI usa para bloquear un PR.</p>
<p>En GitLab:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">eval</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> node</span><span class="token punctuation" style="color:#393A34">:</span><span class="token number" style="color:#36acaa">22</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">script</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> npx </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">y promptfoo@latest eval </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">c promptfooconfig.yaml</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># WEC_API_KEY configurada como una variable CI/CD enmascarada — nunca comprometida.</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="lo-que-aprendimos">Lo que aprendimos<a href="https://wec.wiline.com/docs/es/tutorials/structured-output-evals-wiline-inference/#lo-que-aprendimos" class="hash-link" aria-label="Enlace directo al Lo que aprendimos" title="Enlace directo al Lo que aprendimos" translate="no">​</a></h2>
<ul>
<li class=""><strong>La elección del modelo domina.</strong> <code>GLM-5.2</code> y <code>Qwen2.5-3B</code> producen JSON válido según el esquema de manera fiable y son ~8× más baratos que los modelos de razonamiento. Para salida estructurada en WEC, comienza allí.</li>
<li class=""><strong>Los modelos de razonamiento</strong> (<code>qwen3.5:9B</code>, y <code>gemma4</code> el día que probamos) narran su pensamiento y cuestan mucho más — malas opciones para JSON estricto.</li>
<li class=""><strong>Un <code>transform</code> es tu red de seguridad</strong> — la eliminación de cercas recupera el caso común; la extracción de JSON recupera la mayoría de las salidas habladoras — pero la extracción es una heurística que puede capturar en exceso.</li>
<li class=""><strong><code>response_format</code> es un no-op en WEC hoy</strong> — verificado al aislarlo. No confíes en ello; valida en su lugar.</li>
<li class=""><strong>La evaluación es el punto.</strong> Cada una de estas sorpresas fue capturada automáticamente, antes de que llegara a producción.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="solución-de-problemas">Solución de problemas<a href="https://wec.wiline.com/docs/es/tutorials/structured-output-evals-wiline-inference/#soluci%C3%B3n-de-problemas" class="hash-link" aria-label="Enlace directo al Solución de problemas" title="Enlace directo al Solución de problemas" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="la-función-de-transformación-no-devolvió-un-valor"><a id="ts-transform-return"></a>"La función de transformación no devolvió un valor"<a href="https://wec.wiline.com/docs/es/tutorials/structured-output-evals-wiline-inference/#la-funci%C3%B3n-de-transformaci%C3%B3n-no-devolvi%C3%B3-un-valor" class="hash-link" aria-label="Enlace directo al la-función-de-transformación-no-devolvió-un-valor" title="Enlace directo al la-función-de-transformación-no-devolvió-un-valor" translate="no">​</a></h3>
<p>Un <code>transform</code> de varias líneas se compila como un <strong>cuerpo de función</strong>, por lo que necesita un <code>return</code> explícito. <code>output.replace(...)</code> por sí solo no devuelve nada.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">options</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">transform</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">|</span><span class="token scalar string" style="color:#e3116c"></span><br></div><div class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">    return output.replace(/.../g, '').trim();   # nota el `return`</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="El error de retorno de transformación" src="https://wec.wiline.com/docs/es/assets/images/json-ts-transform-return-764896c26bc05d65d37eac7808ee06fb.png" width="827" height="894" class="img_ev3q">
<strong>Figura 9.</strong> Sin <code>return</code>, Promptfoo da error en lugar de transformar.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="un-modelo-devuelve-razonamiento-alrededor-del-json"><a id="ts-reasoning-dump"></a>Un modelo devuelve razonamiento alrededor del JSON<a href="https://wec.wiline.com/docs/es/tutorials/structured-output-evals-wiline-inference/#un-modelo-devuelve-razonamiento-alrededor-del-json" class="hash-link" aria-label="Enlace directo al un-modelo-devuelve-razonamiento-alrededor-del-json" title="Enlace directo al un-modelo-devuelve-razonamiento-alrededor-del-json" translate="no">​</a></h3>
<p>Si <code>is-json</code> falla con <strong>"Se esperaba que la salida fuera JSON válido"</strong> y la respuesta contiene una narración de "Proceso de Pensamiento…" / "Revisión Final…", estás usando un modelo de razonamiento. A veces emite <em>solo</em> razonamiento; a veces — como a continuación — emite JSON válido y luego <strong>sigue hablando</strong>, por lo que incluso la extracción del Paso 6 captura en exceso y el análisis falla. Opciones: cambia a <code>GLM-5.2</code> / <code>Qwen2.5-3B</code>, o acepta que el transformador de extracción es un esfuerzo de buena fe, no una garantía.</p>
<p><img decoding="async" loading="lazy" alt="El caso fallido expandido en el informe — JSON seguido de razonamiento adicional" src="https://wec.wiline.com/docs/es/assets/images/json-ts-reasoning-dump-dbfebf9e8d4f5f89e6b2e14b8e0edf50.png" width="561" height="859" class="img_ev3q">
<strong>Figura 10.</strong> El caso fallido expandido en el informe: <code>qwen3.5:9B</code> devolvió JSON válido y <em>luego siguió razonando</em> ("7. Revisión Final…"), por lo que la extracción capturó en exceso y la afirmación informa "Se esperaba que la salida fuera JSON válido."</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="no-se-puede-convertir-el-argumento-a-un-bytestring"><a id="ts-invisible-key"></a>"No se puede convertir el argumento a un ByteString"<a href="https://wec.wiline.com/docs/es/tutorials/structured-output-evals-wiline-inference/#no-se-puede-convertir-el-argumento-a-un-bytestring" class="hash-link" aria-label="Enlace directo al no-se-puede-convertir-el-argumento-a-un-bytestring" title="Enlace directo al no-se-puede-convertir-el-argumento-a-un-bytestring" translate="no">​</a></h3>
<p>Tu clave de API pegada contiene un carácter Unicode invisible. Elimínalo:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">export WEC_API_KEY=$(printf '%s' "$WEC_API_KEY" | LC_ALL=C tr -cd '[:print:]')</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="El error de ByteString de un carácter clave invisible" src="https://wec.wiline.com/docs/es/assets/images/json-ts-bytestring-0bb4979ce94f0775f1fe314315b38e01.png" width="953" height="988" class="img_ev3q">
<strong>Figura 11.</strong> Un carácter errante en la clave activa el encabezado HTTP antes de que se envíe cualquier solicitud.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="qué-sigue">¿Qué sigue?<a href="https://wec.wiline.com/docs/es/tutorials/structured-output-evals-wiline-inference/#qu%C3%A9-sigue" class="hash-link" aria-label="Enlace directo al ¿Qué sigue?" title="Enlace directo al ¿Qué sigue?" translate="no">​</a></h2>
<ul>
<li class=""><strong>Conjuntos de regresión DeepEval</strong> — convierte estas verificaciones en una ejecución de regresión versionada y con puerta de CI.</li>
<li class=""><strong>Trazado</strong> — ve <em>por qué</em> una respuesta salió mal, no solo que falló.</li>
</ul>
<p>¿Tienes un caso de salida estructurada que aún se escapa? Esa es la próxima prueba que agregar.</p>]]></content>
        <author>
            <name>Rafael Fernandes</name>
            <uri>https://www.linkedin.com/in/rafaelmacariofernandes/</uri>
        </author>
        <category label="ai" term="ai"/>
        <category label="evals" term="evals"/>
        <category label="promptfoo" term="promptfoo"/>
        <category label="inference" term="inference"/>
        <category label="json" term="json"/>
        <category label="structured-output" term="structured-output"/>
        <category label="observability" term="observability"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Evalúa tus modelos con Promptfoo en la API de Inferencia WEC]]></title>
        <id>https://wec.wiline.com/docs/es/tutorials/eval-models-promptfoo-wiline-inference/</id>
        <link href="https://wec.wiline.com/docs/es/tutorials/eval-models-promptfoo-wiline-inference/"/>
        <updated>2026-06-30T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Deja de estimar la salida de LLM. Construye un verdadero arnés de evaluación con Promptfoo apuntando a la API de Inferencia WEC — afirmaciones, límites de latencia, verificaciones de esquema JSON, rúbricas calificadas por el modelo, una comparación de modelos todo-WEC y una puerta de CI. Cada comando y resultado es real.]]></summary>
        <content type="html"><![CDATA[
<div class="tutorialHero"><img class="tutorialHero__brand" src="https://wec.wiline.com/docs/es/assets/images/promptfoo-avatar-31b32c657d72dac0a7edbd77c24ec717.jpg" alt="Promptfoo"><span class="tutorialHero__plus">+</span><svg xmlns="http://www.w3.org/2000/svg" width="242" height="255" fill="none" viewBox="0 0 242 255" class="tutorialHero__wiline"><path fill="#4E8EF7" d="M56.711 11.293A195 195 0 0 1 41.899 4.79c4.788 1.85 9.71 4.028 14.812 6.504M73.807 29.35c-1.302-1.446-2.666-3.233-4.02-5.38 1.326 1.44 2.642 3.24 4.02 5.38M69.761 23.354c-.832-.585-1.752-1.472-2.558-2.714.9.567 1.685 1.49 2.558 2.714M62.97 15.774a10.7 10.7 0 0 1-3.158-1.841c.993.299 1.986.956 3.159 1.841M59.715 13.346c-.818-.194-1.782-.674-2.829-1.494.84.175 1.761.691 2.83 1.494M39.317 3.782c-.524.192-1.413.302-2.455.14.594-.255 1.342-.238 2.455-.14M74.906 31.339c-.334-.153-.73-.61-1.117-1.398.358.143.706.619 1.117 1.398M66.024 18.878c-.239.1-.557.05-.752-.276.35-.201.532-.069.752.276M71.773 150.625c3.363 7.291 7.185 14.411 9.983 21.914 4.474 11.997 11.167 20.832 24.296 24.757 10.62 3.175 20.369 1.795 30.093-1.816 9.937-3.689 15.165-11.978 18.501-21.541 4.349-12.466 9.385-24.443 19.668-33.446 11.32-9.909 24.639-14.788 39.429-15.63 8.3-.473 16.649-.081 25.444.444.472 15.673.385 30.825.55 45.974.038 3.499-1.049 4.719-4.557 4.543-4.982-.25-9.987-.079-14.98.009-.209.004-.4.991-.6 1.521-23.106 1.695-37.691 15.22-47.961 34.651-3.387 6.408-5.913 13.38-10.013 19.273-4.368 6.277-10.134 11.582-15.853 17.196-6.439 1.421-12.244 3.342-18.202 4.416-12.627 2.274-24.661.457-35.764-6.356-1.028-.631-2.395-.709-3.604-1.043 0 0-.117-.11-.047-.495-.818-.973-1.706-1.562-2.593-2.15 0 0 .09-.044.05-.335-.513-.545-.988-.798-1.463-1.052 0 0-.07-.123.012-.492-.561-.894-1.205-1.421-1.848-1.947 0 0 .134-.068.102-.28-.321-.442-.61-.671-.898-.901 0 0 .086-.007.074-.336-2.05-3.429-4.089-6.529-6.127-9.629 0 0 .094-.017.123-.365-3.013-6.117-6.056-11.886-9.1-17.655 0 0 .096-.01.088-.342-1.978-4.073-3.89-7.848-5.997-11.51-.246-.428-1.572-.235-2.398-.329 0 0-.226-.202-.114-.613-.213-.499-.537-.587-.861-.674 0 0-.032-.101.02-.455-.62-.846-1.291-1.339-1.964-1.832 0 0 .133-.083.119-.372-2.476-4.624-5.645-8.048-10.914-8.777 0 0 .108-.031.01-.326-1.673-1.444-3.105-2.918-4.85-3.688-9.751-4.308-20.013-5.643-30.63-5.23-7.305.285-7.316.051-7.317-7.446q-.002-18.496-.002-36.992v-3.57h36.106s-.08.019.133.283c1.43.415 2.646.567 3.863.719 0 0-.077.022.113.296 1.804.591 3.417.908 5.03 1.225 0 0 .139-.037.152.369 1.592.962 3.17 1.517 4.748 2.073 0 0-.091.027.052.267.735.415 1.328.59 1.92.765 0 0-.076.033.053.246.477.325.825.438 1.173.551 0 0-.149.018-.07.246.418.239.757.251 1.096.263 0 0 .23.058.189.465 1.522 1.319 3.084 2.231 4.647 3.143 0 0-.105.033-.015.316 2.723 2.764 5.356 5.244 7.989 7.724 0 0-.104.062-.094.352 1.01 1.401 2.01 2.512 3.01 3.624"></path><path fill="#4E8EF7" d="M214.77 52.819c-3.343.846-6.723 1.574-10.024 2.562-11.388 3.408-20.567 10.142-27.166 19.865-5.097 7.511-9.621 15.483-13.685 23.608-6.809 13.613-15.725 24.809-31.343 28.198-24.707 5.362-45.22-.126-57.852-24.564C69.466 92.363 63.8 82.375 57.349 73c-3.369-4.896-8.866-8.326-13.679-13.041-1.11-1.409-1.81-2.472-2.794-2.937-10.697-5.056-21.87-7.3-33.686-5.344-3.877.641-5.72-.297-5.615-4.86.294-12.816.26-25.646.019-38.464C1.509 3.874 3.15 2.5 7.429 2.716c9.774.492 19.564.678 29.347.989 0 0-.076.026.082.257 1.046.122 1.935.012 2.825-.098.575.043 1.15.086 2.038.692 5.265 2.72 10.218 4.877 15.17 7.034 0 0-.108.03-.016.317 1.058.765 2.022 1.245 2.987 1.724 0 0-.113.048-.082.354 1.144.876 2.258 1.446 3.372 2.017 0 0 .18.145.097.506.223.404.53.447.837.49 0 0 .214.13.144.504.244.426.558.477.873.528 0 0 .171.124.108.485.256.412.574.464.893.515 0 0 .167.127.105.493.265.437.592.509.919.58 0 0 .078.108.019.5.861 1.28 1.782 2.166 2.702 3.053 0 0-.101.061-.087.368 1.379 2.093 2.743 3.88 4.107 5.666 0 0-.094.011-.092.297.4.742.795 1.2 1.19 1.656 0 0-.121.02-.165.337.685 1.432 1.413 2.549 2.14 3.665 0 0-.11.024-.129.299.326.776.67 1.277 1.014 1.78 0 0-.084-.005-.1.367.933 4.079.311 8.497 4.648 10.67 4.69 12.164 12.107 21.794 25.852 23.497 19.618 2.431 35.321.062 45.299-24.554 3.398-8.383 7.099-16.902 12.232-24.264 8.353-11.979 21.046-17.795 35.832-20.32 1.666.417 2.649.651 3.624.622 9.93-.298 19.865-.513 29.785-1.032 3.818-.2 4.825 1.198 4.746 4.853-.218 9.967.004 19.944-.109 29.915-.052 4.583 1.149 10.606-1.239 13.302-2.044 2.308-8.394.86-12.842.967-3.82.092-7.644.036-11.467.045.065.31.097.633.213.922.034.084.306.072.469.105M37.585 127.354c-11.585.344-23.368.344-35.907.344v3.57q0 18.496.002 36.992c0 7.497.012 7.731 7.318 7.446 10.616-.413 20.878.922 30.63 5.23 1.743.77 3.176 2.244 4.84 3.744-13.655-7.873-28.93-7.767-44.468-7.398v-52.503c12.516.308 25.063-1.222 37.585 2.575"></path><path fill="#4E8EF7" d="M36.619 3.401c-9.626-.007-19.415-.193-29.19-.685C3.151 2.5 1.51 3.874 1.594 8.354c.241 12.818.275 25.648-.018 38.464-.105 4.563 1.737 5.501 5.614 4.86 11.816-1.956 22.99.288 33.686 5.344.984.465 1.685 1.529 2.616 2.66-13.39-7.538-28.284-7.343-43.479-6.78V0C12.314 1.045 24.388 2.071 36.62 3.401M215.232 52.852c-.625-.066-.897-.054-.931-.138-.116-.29-.148-.612-.212-.922 3.822-.01 7.646.047 11.467-.045 4.448-.108 10.797 1.341 12.842-.967 2.387-2.696 1.186-8.719 1.238-13.302.114-9.971-.109-19.948.109-29.915.08-3.655-.928-5.052-4.746-4.853-9.919.519-19.855.734-29.785 1.032-.975.03-1.958-.205-3.228-.58C214.559 1.945 227.423.991 240.73.003v52.882c-8.276 0-16.656 0-25.498-.033M220.071 177.398c-.271-.573-.08-1.561.129-1.565 4.993-.088 9.998-.259 14.98-.009 3.508.176 4.595-1.044 4.557-4.543-.165-15.149-.078-30.301-.088-45.911.458.372 1.32 1.202 1.324 2.036.079 16.424.059 32.848.059 50.036-6.984 0-13.737 0-20.961-.044M88.39 245.741c1.022.084 2.39.162 3.417.793 11.102 6.813 23.137 8.63 35.763 6.356 5.959-1.073 11.764-2.995 17.871-4.34-5.612 2.132-11.343 5.166-17.325 5.79-13.898 1.451-27.803 1.213-39.725-8.599M66.556 210.212c2.975 5.411 6.018 11.18 9.02 17.344-3.011-5.4-5.982-11.193-9.02-17.344M58.341 197.934c.666-.167 1.992-.36 2.238.068 2.107 3.662 4.018 7.437 5.985 11.557-2.69-3.535-5.376-7.449-8.223-11.625M44.637 185.241c5.099.438 8.268 3.862 10.737 8.564-3.518-2.513-7.043-5.393-10.737-8.564M82.29 48.422c-4.252-1.834-3.63-6.252-4.548-10.37 1.508 3.069 2.986 6.55 4.548 10.37M68.757 146.326c-2.533-2.157-5.166-4.637-7.882-7.455 2.539 2.151 5.16 4.641 7.882 7.455M75.532 228.226a122 122 0 0 1 6.047 9.319c-1.993-2.742-3.986-5.854-6.047-9.319M71.695 150.312c-.922-.799-1.921-1.91-2.906-3.366.952.788 1.89 1.921 2.906 3.366M46.663 130.007c-1.353-.103-2.966-.42-4.767-1.051 1.377.07 2.941.454 4.767 1.051M51.647 132.369a13.1 13.1 0 0 1-4.501-1.77c1.48.227 2.902.852 4.5 1.77M60.724 138.321a17.7 17.7 0 0 1-4.395-2.781c1.501.609 2.869 1.551 4.395 2.781M76.889 35.31c-.674-.781-1.402-1.898-2.068-3.39.711.768 1.362 1.912 2.068 3.39M85.668 243.124c.782.31 1.67.899 2.423 1.825-.862-.29-1.59-.919-2.423-1.825M41.596 128.372c-1.033.176-2.25.024-3.68-.432 1.023-.169 2.26-.032 3.68.432M55.367 194.37c.568.222 1.24.715 1.808 1.548-.637-.199-1.17-.738-1.808-1.548M82.397 239.295c.56.251 1.204.778 1.71 1.628-.633-.235-1.13-.794-1.71-1.628M84.291 241.679c.334.034.809.287 1.298.875-.376.004-.766-.326-1.298-.875M53.64 133.426c-.438.094-1.03-.081-1.764-.543.441-.101 1.026.086 1.764.543M77.805 37.409c-.322-.188-.666-.69-.978-1.52.34.182.648.693.978 1.52M81.588 238.108c.218-.039.507.19.823.707-.233.045-.493-.196-.823-.707M55.906 134.832c-.198.157-.537.145-.92-.161.262-.29.537-.195.92.161M57.276 196.552c.254-.079.578.009.756.361-.377.171-.557.018-.756-.361M54.888 134.25c-.215.129-.563.017-1.038-.351.217-.133.56-.012 1.038.351M64.014 16.858a.545.545 0 0 1-.71-.247c.344-.19.51-.067.71.247M65.027 17.882a.574.574 0 0 1-.734-.272c.35-.193.525-.063.734.272M67.045 19.939c-.244.093-.57.021-.777-.329.358-.192.55-.044.777.329"></path></svg></div>
<p>No enviarías código sin pruebas — pero la mayoría de los equipos envían características de LLM con "se ve bien para mí." <strong>Evaluaciones</strong> (evals) solucionan eso: defines casos de prueba y criterios de aprobación/rechazo, luego mides tu modelo de manera objetiva — detectando regresiones, comparando modelos y bloqueando despliegues.</p>
<p>En esta guía construirás un verdadero arnés de evaluación con <a href="https://www.promptfoo.dev/" target="_blank" rel="noopener noreferrer" class="">Promptfoo</a> apuntando a la <strong>API de Inferencia WEC</strong> — el mismo punto final compatible con OpenAI que llamas desde tus aplicaciones. Todo aquí se ejecutó en vivo contra <code>https://inference.wiline.com</code>; las salidas son reales.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="requisitos-previos">Requisitos previos<a href="https://wec.wiline.com/docs/es/tutorials/eval-models-promptfoo-wiline-inference/#requisitos-previos" class="hash-link" aria-label="Enlace directo al Requisitos previos" title="Enlace directo al Requisitos previos" translate="no">​</a></h2>
<ul>
<li class="">
<p>Una cuenta de <strong>WEC (WiLine Edge Cloud)</strong> y una <strong>clave de API de Inferencia</strong> — ver <a class="" href="https://wec.wiline.com/docs/es/cloud_portal/platform/inference/api_keys/">Inferencia → Claves API</a>.</p>
</li>
<li class="">
<p><strong>Node.js 22+</strong>. Promptfoo considera que Node 20 está al final de su vida útil; con <code>nvm</code>:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">nvm install 24      # 24 LTS preferido</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">nvm use 24</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">node -v             # v24.x</span><br></div></code></pre></div></div>
</li>
</ul>
<p>Ejecutaremos Promptfoo con <code>npx</code> — nada que instalar globalmente.</p>
<p><img decoding="async" loading="lazy" alt="La página de claves API del portal WiLine" src="https://wec.wiline.com/docs/es/assets/images/promptfoo-api-key-a1aca6bfffa7335651ab59ec1c07d8f4.png" width="1642" height="866" class="img_ev3q">
<strong>Figura 1.</strong> Crea una clave en Inferencia → Claves API, luego cópiala.</p>
<p>Pon tu clave en la terminal:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">export WEC_API_KEY='sk-your-key-here'</span><br></div></code></pre></div></div>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>tip</div><div class="admonitionContent_BuS1"><p>Si tu primera evaluación falla con <code>Cannot convert argument to a ByteString</code>, consulta <a href="https://wec.wiline.com/docs/es/tutorials/eval-models-promptfoo-wiline-inference/#1-byte-string-error-on-first-eval" class="">Solución de problemas #1</a> a continuación.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-1--ver-qué-modelos-tienes">Paso 1 — Ver qué modelos tienes<a href="https://wec.wiline.com/docs/es/tutorials/eval-models-promptfoo-wiline-inference/#paso-1--ver-qu%C3%A9-modelos-tienes" class="hash-link" aria-label="Enlace directo al Paso 1 — Ver qué modelos tienes" title="Enlace directo al Paso 1 — Ver qué modelos tienes" translate="no">​</a></h2>
<p>La API es compatible con OpenAI, por lo que la lista de modelos es un simple <code>GET /v1/models</code>:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl -s https://inference.wiline.com/v1/models \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  -H "Authorization: Bearer $WEC_API_KEY" | jq -r '.data[].id'</span><br></div></code></pre></div></div>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">Salida</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">zai-org/GLM-5.2</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">gemma4</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Qwen2.5-3B-Instruct</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">qwen3.5:9B</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">whisper-large-v3</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">whisper-medium</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">whisper-base</span><br></div></code></pre></div></div>
<p>Los modelos <code>whisper-*</code> son de voz a texto; el resto son modelos de chat. Evaluaremos los modelos de chat, comenzando con <code>zai-org/GLM-5.2</code>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-2--tu-primera-evaluación">Paso 2 — Tu primera evaluación<a href="https://wec.wiline.com/docs/es/tutorials/eval-models-promptfoo-wiline-inference/#paso-2--tu-primera-evaluaci%C3%B3n" class="hash-link" aria-label="Enlace directo al Paso 2 — Tu primera evaluación" title="Enlace directo al Paso 2 — Tu primera evaluación" translate="no">​</a></h2>
<p>Crea una configuración. <code>apiBaseUrl</code> apunta Promptfoo a WEC; <code>apiKeyEnvar</code> le dice qué variable de entorno contiene tu clave:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">cat &gt; promptfooconfig.yaml &lt;&lt;'EOF'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">description: "Evaluación de API de Inferencia WEC"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">providers:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - id: openai:chat:zai-org/GLM-5.2</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    config:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      apiBaseUrl: https://inference.wiline.com/v1</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      apiKeyEnvar: WEC_API_KEY</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      temperature: 0</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">prompts:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - "Responde en una sola palabra. ¿Cuál es la capital de {{country}}?"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">tests:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - vars: { country: France }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    assert: [{ type: icontains, value: Paris }]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - vars: { country: Japan }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    assert: [{ type: icontains, value: Tokyo }]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npx -y promptfoo@latest eval -c promptfooconfig.yaml</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="Terminal: primera evaluación, 2 casos ambos APROBADOS" src="https://wec.wiline.com/docs/es/assets/images/promptfoo-first-eval-42c0a9a515e6a1e5af7a6cf5c6a30118.png" width="954" height="501" class="img_ev3q">
<strong>Figura 4.</strong> Tu primera evaluación aprobada contra la API de Inferencia WEC.</p>
<p><code>icontains</code> es una afirmación <strong>determinista</strong> — una verificación de subcadena sin distinción de mayúsculas. Barata, rápida y perfecta para hechos con una respuesta conocida.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-3--el-informe-visual">Paso 3 — El informe visual<a href="https://wec.wiline.com/docs/es/tutorials/eval-models-promptfoo-wiline-inference/#paso-3--el-informe-visual" class="hash-link" aria-label="Enlace directo al Paso 3 — El informe visual" title="Enlace directo al Paso 3 — El informe visual" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">npx -y promptfoo@latest view</span><br></div></code></pre></div></div>
<p>Esto abre una interfaz web local con cada aviso, salida y aprobación/rechazo — mucho más fácil de escanear que la terminal una vez que tienes más de unos pocos casos.</p>
<p><img decoding="async" loading="lazy" alt="El informe web de Promptfoo" src="https://wec.wiline.com/docs/es/assets/images/promptfoo-view-report-b93aa9bcadc4c60e008df1bb5effcd3c.png" width="1904" height="1040" class="img_ev3q">
<strong>Figura 5.</strong> <code>promptfoo view</code> — los mismos resultados en el navegador.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-4--agregar-un-segundo-modelo">Paso 4 — Agregar un segundo modelo<a href="https://wec.wiline.com/docs/es/tutorials/eval-models-promptfoo-wiline-inference/#paso-4--agregar-un-segundo-modelo" class="hash-link" aria-label="Enlace directo al Paso 4 — Agregar un segundo modelo" title="Enlace directo al Paso 4 — Agregar un segundo modelo" translate="no">​</a></h2>
<p>Agregar un proveedor es un bloque extra. Aquí agregamos <code>Qwen2.5-3B-Instruct</code> junto a GLM-5.2 y ejecutamos los mismos dos casos de prueba contra ambos:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">cat &gt; promptfooconfig.yaml &lt;&lt;'EOF'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">description: "Evaluación de API de Inferencia WEC — comparación de modelos"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">providers:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - id: openai:chat:zai-org/GLM-5.2</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    config:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      apiBaseUrl: https://inference.wiline.com/v1</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      apiKeyEnvar: WEC_API_KEY</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      temperature: 0</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - id: openai:chat:Qwen2.5-3B-Instruct</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    config:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      apiBaseUrl: https://inference.wiline.com/v1</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      apiKeyEnvar: WEC_API_KEY</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      temperature: 0</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">prompts:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - "Responde en una sola palabra. ¿Cuál es la capital de {{country}}?"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">tests:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - vars: { country: France }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    assert: [{ type: icontains, value: Paris }]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - vars: { country: Japan }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    assert: [{ type: icontains, value: Tokyo }]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npx -y promptfoo@latest eval -c promptfooconfig.yaml</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="Terminal: comparación de 2 modelos, 4 casos todos APROBADOS" src="https://wec.wiline.com/docs/es/assets/images/promptfoo-two-model-3c6c51ea8839c03dd51d270c78648ec9.png" width="956" height="590" class="img_ev3q">
<strong>Figura 6.</strong> Ambos modelos aprueban. Los resultados de GLM-5.2 se sirven desde caché (0 nuevas solicitudes); solo Qwen2.5 realiza llamadas en vivo.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-5--califica-calidad-no-subcadenas-rúbrica-llm">Paso 5 — Califica calidad, no subcadenas (rúbrica LLM)<a href="https://wec.wiline.com/docs/es/tutorials/eval-models-promptfoo-wiline-inference/#paso-5--califica-calidad-no-subcadenas-r%C3%BAbrica-llm" class="hash-link" aria-label="Enlace directo al Paso 5 — Califica calidad, no subcadenas (rúbrica LLM)" title="Enlace directo al Paso 5 — Califica calidad, no subcadenas (rúbrica LLM)" translate="no">​</a></h2>
<p>Las verificaciones de subcadenas no pueden juzgar una respuesta abierta. Una afirmación <code>llm-rubric</code> <strong>calificada por el modelo</strong> utiliza un modelo para puntuar la salida contra criterios — y el calificador puede ejecutarse en WEC también, por lo que no necesitas un proveedor externo:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">cat &gt; promptfooconfig.yaml &lt;&lt;'EOF'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">description: "Evaluación de API de Inferencia WEC — calificación por modelo (rúbrica LLM)"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">providers:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - id: openai:chat:zai-org/GLM-5.2</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    config:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      apiBaseUrl: https://inference.wiline.com/v1</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      apiKeyEnvar: WEC_API_KEY</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      temperature: 0</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">defaultTest:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  options:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    provider:                       # el modelo calificador</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      id: openai:chat:zai-org/GLM-5.2</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      config:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        apiBaseUrl: https://inference.wiline.com/v1</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        apiKeyEnvar: WEC_API_KEY</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">prompts:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - "Explica qué es un {{topic}} en dos oraciones, para un principiante."</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">tests:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - vars: { topic: VPC }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    assert:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      - type: llm-rubric</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        value: "Describe una VPC como una red virtual privada e aislada en la nube, y es comprensible para un principiante."</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npx -y promptfoo@latest eval -c promptfooconfig.yaml</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="Terminal: evaluación de rúbrica LLM, 1 APROBADO, línea de tokens de calificación visible" src="https://wec.wiline.com/docs/es/assets/images/promptfoo-llm-rubric-f969ad98a43427eacf9f83f13f2e6dde.png" width="957" height="556" class="img_ev3q">
<strong>Figura 7.</strong> La ejecución muestra dos líneas de tokens — <code>Eval</code> (75) y <code>Grading</code> (350) — porque la verificación de rúbrica es en sí misma una llamada de modelo.</p>
<p>Las verificaciones calificadas por el modelo son poderosas, pero no son gratuitas.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-6--líneas-de-defensa-latencia">Paso 6 — Líneas de defensa: latencia<a href="https://wec.wiline.com/docs/es/tutorials/eval-models-promptfoo-wiline-inference/#paso-6--l%C3%ADneas-de-defensa-latencia" class="hash-link" aria-label="Enlace directo al Paso 6 — Líneas de defensa: latencia" title="Enlace directo al Paso 6 — Líneas de defensa: latencia" translate="no">​</a></h2>
<p>Una respuesta correcta que tarda 30 segundos sigue siendo un problema de producción. Agrega un límite de <strong>latencia</strong> a <code>defaultTest</code> (se aplica a cada caso). Siempre ejecuta con <code>--no-cache</code> — las respuestas en caché informan ~0 ms y pasarían la puerta gratis:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">cat &gt; promptfooconfig.yaml &lt;&lt;'EOF'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">description: "Evaluación de API de Inferencia WEC — límite de latencia"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">providers:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - id: openai:chat:zai-org/GLM-5.2</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    config:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      apiBaseUrl: https://inference.wiline.com/v1</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      apiKeyEnvar: WEC_API_KEY</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      temperature: 0</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">defaultTest:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  assert:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    - type: latency</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      threshold: 5000        # fallar cualquier cosa más lenta que 5 s</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">prompts:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - "Responde en una sola palabra. ¿Cuál es la capital de {{country}}?"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">tests:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - vars: { country: France }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    assert: [{ type: icontains, value: Paris }]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - vars: { country: Japan }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    assert: [{ type: icontains, value: Tokyo }]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npx -y promptfoo@latest eval -c promptfooconfig.yaml --no-cache</span><br></div></code></pre></div></div>
<p>Esto <strong>falló</strong> — las respuestas eran correctas (<code>Paris</code>, <code>Tokyo</code>) pero ambas llamadas tomaron ~33 s, activando la puerta de 5 s:</p>
<p><img decoding="async" loading="lazy" alt="Terminal: umbral de latencia 5000ms, 2 FALLIDOS, Duración: 33s" src="https://wec.wiline.com/docs/es/assets/images/promptfoo-latency-fail-ab40e8dc4386c6acd496942ee3a490aa.png" width="953" height="518" class="img_ev3q">
<strong>Figura 8.</strong> La línea de defensa se activa: respuestas correctas, latencia incorrecta. Duración: 33s.</p>
<p>Aumenta el umbral a algo realista y vuelve a ejecutar:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">sed -i 's/threshold: 5000/threshold: 60000/' promptfooconfig.yaml</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npx -y promptfoo@latest eval -c promptfooconfig.yaml --no-cache</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="Terminal: umbral de latencia 60000ms, 2 APROBADOS, Duración: 2s" src="https://wec.wiline.com/docs/es/assets/images/promptfoo-latency-pass-f3fa1a1c9cb0a11a87328b2a15ef1f31.png" width="957" height="538" class="img_ev3q">
<strong>Figura 9.</strong> Con un umbral de 60 s, las mismas llamadas pasan fácilmente — Duración: 2s.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>La latencia de inferencia varía — calibra antes de bloquear</div><div class="admonitionContent_BuS1"><p>Las llamadas consecutivas al mismo modelo variaron de ~2 s a ~33 s en el mismo día. Ejecuta algunos pases <code>--no-cache</code> para obtener una línea base realista antes de fijar tu umbral.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-7--líneas-de-defensa-latencia--límite-de-tokens">Paso 7 — Líneas de defensa: latencia + límite de tokens<a href="https://wec.wiline.com/docs/es/tutorials/eval-models-promptfoo-wiline-inference/#paso-7--l%C3%ADneas-de-defensa-latencia--l%C3%ADmite-de-tokens" class="hash-link" aria-label="Enlace directo al Paso 7 — Líneas de defensa: latencia + límite de tokens" title="Enlace directo al Paso 7 — Líneas de defensa: latencia + límite de tokens" translate="no">​</a></h2>
<p>La afirmación de <code>cost</code> no funciona contra WEC (ver <a href="https://wec.wiline.com/docs/es/tutorials/eval-models-promptfoo-wiline-inference/#2-cost-assertion-errors-immediately" class="">Solución de problemas #2</a>). Usa latencia + <code>max_tokens</code> juntos como tu línea de defensa de producción en su lugar:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">cat &gt; promptfooconfig.yaml &lt;&lt;'EOF'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">description: "Evaluación de API de Inferencia WEC — líneas de defensa (latencia + límite de tokens)"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">providers:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - id: openai:chat:zai-org/GLM-5.2</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    config:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      apiBaseUrl: https://inference.wiline.com/v1</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      apiKeyEnvar: WEC_API_KEY</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      temperature: 0</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      max_tokens: 16</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">defaultTest:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  assert:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    - type: latency</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      threshold: 30000</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">prompts:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - "Responde en una sola palabra. ¿Cuál es la capital de {{country}}?"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">tests:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - vars: { country: France }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    assert: [{ type: icontains, value: Paris }]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - vars: { country: Japan }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    assert: [{ type: icontains, value: Tokyo }]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npx -y promptfoo@latest eval -c promptfooconfig.yaml --no-cache</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="Terminal: latencia 30000ms + max_tokens:16, 2 APROBADOS" src="https://wec.wiline.com/docs/es/assets/images/promptfoo-latency-token-cap-968bf5b53dd3edf6d77129f011e4a66f.png" width="955" height="522" class="img_ev3q">
<strong>Figura 11.</strong> Con un umbral de 30 s y un límite de 16 tokens, ambos casos aprueban.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-8--validar-salida-estructurada-validación-de-esquema-json">Paso 8 — Validar salida estructurada (validación de esquema JSON)<a href="https://wec.wiline.com/docs/es/tutorials/eval-models-promptfoo-wiline-inference/#paso-8--validar-salida-estructurada-validaci%C3%B3n-de-esquema-json" class="hash-link" aria-label="Enlace directo al Paso 8 — Validar salida estructurada (validación de esquema JSON)" title="Enlace directo al Paso 8 — Validar salida estructurada (validación de esquema JSON)" translate="no">​</a></h2>
<p>Las aplicaciones reales quieren JSON, no prosa. <code>is-json</code> verifica que la salida sea un JSON válido <strong>y</strong> coincida con un esquema en una afirmación:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">cat &gt; promptfooconfig.yaml &lt;&lt;'EOF'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">description: "Evaluación de API de Inferencia WEC — validación de esquema JSON"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">providers:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - id: openai:chat:zai-org/GLM-5.2</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    config:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      apiBaseUrl: https://inference.wiline.com/v1</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      apiKeyEnvar: WEC_API_KEY</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      temperature: 0</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">prompts:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - 'Devuelve SOLAMENTE un objeto JSON (sin markdown, sin prosa) con claves "capital" (cadena) y "population_millions" (número) para el país {{country}}.'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">defaultTest:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  assert:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    - type: is-json</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      value:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        type: object</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        required: [capital, population_millions]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        properties:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          capital: { type: string }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          population_millions: { type: number }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">tests:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - vars: { country: France }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    assert: [{ type: icontains, value: Paris }]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - vars: { country: Japan }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    assert: [{ type: icontains, value: Tokyo }]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npx -y promptfoo@latest eval -c promptfooconfig.yaml</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="Terminal: evaluación de esquema JSON, Francia y Japón ambos APROBADOS con salida JSON limpia" src="https://wec.wiline.com/docs/es/assets/images/promptfoo-json-schema-82dcc59881029f9bc0134dcf5a5c5ea2.png" width="955" height="516" class="img_ev3q">
<strong>Figura 12.</strong> GLM-5.2 devuelve JSON compacto y válido según el esquema en el primer intento.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-9--la-recompensa-compara-todos-los-modelos-wec">Paso 9 — La recompensa: compara todos los modelos WEC<a href="https://wec.wiline.com/docs/es/tutorials/eval-models-promptfoo-wiline-inference/#paso-9--la-recompensa-compara-todos-los-modelos-wec" class="hash-link" aria-label="Enlace directo al Paso 9 — La recompensa: compara todos los modelos WEC" title="Enlace directo al Paso 9 — La recompensa: compara todos los modelos WEC" translate="no">​</a></h2>
<p>Aquí está lo que no puedes obtener en ningún otro lugar — la misma prueba estricta de JSON a través de <strong>todos</strong> los modelos de chat en tu cuenta:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">cat &gt; promptfooconfig.yaml &lt;&lt;'EOF'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">description: "Evaluación de API de Inferencia WEC — matriz de modelos"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">providers:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - id: openai:chat:zai-org/GLM-5.2</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    config: { apiBaseUrl: https://inference.wiline.com/v1, apiKeyEnvar: WEC_API_KEY, temperature: 0 }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - id: openai:chat:gemma4</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    config: { apiBaseUrl: https://inference.wiline.com/v1, apiKeyEnvar: WEC_API_KEY, temperature: 0 }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - id: openai:chat:Qwen2.5-3B-Instruct</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    config: { apiBaseUrl: https://inference.wiline.com/v1, apiKeyEnvar: WEC_API_KEY, temperature: 0 }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - id: openai:chat:qwen3.5:9B</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    config: { apiBaseUrl: https://inference.wiline.com/v1, apiKeyEnvar: WEC_API_KEY, temperature: 0 }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">prompts:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - 'Devuelve SOLAMENTE un objeto JSON (sin markdown, sin prosa) con claves "capital" (cadena) y "population_millions" (número) para el país {{country}}.'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">defaultTest:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  assert:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    - type: is-json</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      value:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        type: object</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        required: [capital, population_millions]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        properties:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          capital: { type: string }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          population_millions: { type: number }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">tests:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - vars: { country: France }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    assert: [{ type: icontains, value: Paris }]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - vars: { country: Japan }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    assert: [{ type: icontains, value: Tokyo }]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  - vars: { country: Brazil }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    assert: [{ type: icontains, value: Bras }]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npx -y promptfoo@latest eval -c promptfooconfig.yaml</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="Terminal: tabla de resultados de matriz de 4 modelos — GLM, gemma4, Qwen2.5 todos APROBADOS; qwen3.5:9B todos FALLIDOS con texto de razonamiento" src="https://wec.wiline.com/docs/es/assets/images/promptfoo-model-matrix-table-fcebe135ddd276ef881fc83a726c6b96.png" width="1001" height="972" class="img_ev3q">
<strong>Figura 13a.</strong> 12 casos de prueba a través de 4 modelos — 9 aprobados, 3 fallidos. <code>qwen3.5:9B</code> emite su cadena de pensamiento en lugar de JSON.</p>
<p><img decoding="async" loading="lazy" alt="Terminal: resumen de tokens de matriz de modelos — qwen3.5:9B consumió 2,650 tokens frente a ~200 para los demás" src="https://wec.wiline.com/docs/es/assets/images/promptfoo-model-matrix-summary-96e3ce6b439ae8d487aef486120dbe97.png" width="639" height="319" class="img_ev3q">
<strong>Figura 13b.</strong> El desglose de tokens cuenta la verdadera historia: el modelo de razonamiento costó 13× más para la misma tarea.</p>
<table><thead><tr><th>Modelo</th><th>¿JSON Estricto?</th><th>Notas</th><th>Tokens (3 llamadas)</th></tr></thead><tbody><tr><td><code>zai-org/GLM-5.2</code></td><td>✅ todos aprueban</td><td>JSON compacto</td><td>177</td></tr><tr><td><code>gemma4</code></td><td>✅ todos aprueban</td><td>poblaciones enteras</td><td>207</td></tr><tr><td><code>Qwen2.5-3B-Instruct</code></td><td>✅ todos aprueban</td><td>JSON bien formateado, preciso</td><td>262</td></tr><tr><td><code>qwen3.5:9B</code></td><td>❌ todos fallan</td><td><strong>modelo de razonamiento</strong> — emite su "pensamiento" en lugar de JSON crudo</td><td><strong>2,650</strong></td></tr></tbody></table>
<p>Dos lecciones se desprenden de inmediato:</p>
<ul>
<li class=""><strong><code>qwen3.5:9B</code> es un modelo de razonamiento.</strong> "Piensa en voz alta", por lo que rompe el JSON estricto a menos que elimines el razonamiento o uses una restricción de formato de respuesta. Elegirlo para salida estructurada fallaría silenciosamente.</li>
<li class=""><strong>El costo varía ~13×.</strong> El modelo de razonamiento consumió 2,650 tokens para la misma tarea que los demás hicieron en ~200. Para un trabajo de extracción de JSON, los modelos más pequeños son correctos <em>y</em> mucho más baratos.</li>
</ul>
<p>Esa es una decisión de selección de modelo que ahora puedes defender con datos — para <em>tus</em> cargas de trabajo, no una tabla de clasificación genérica.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-10--hazlo-una-puerta">Paso 10 — Hazlo una puerta<a href="https://wec.wiline.com/docs/es/tutorials/eval-models-promptfoo-wiline-inference/#paso-10--hazlo-una-puerta" class="hash-link" aria-label="Enlace directo al Paso 10 — Hazlo una puerta" title="Enlace directo al Paso 10 — Hazlo una puerta" translate="no">​</a></h2>
<p>Promptfoo sale <strong>no cero (<code>100</code>)</strong> cuando cualquier prueba falla. Verifica esto primero:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">npx -y promptfoo@latest eval -c promptfooconfig.yaml &gt; /dev/null 2&gt;&amp;1; echo "código de salida: $?"</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="Terminal: código de salida: 100" src="https://wec.wiline.com/docs/es/assets/images/promptfoo-exit-code-a3471bde65e7ebf24feaf0d964a3b7eb.png" width="652" height="70" class="img_ev3q">
<strong>Figura 14.</strong> El código de salida 100 confirma que Promptfoo señala fallos de manera confiable — exactamente lo que necesita CI.</p>
<p>Conéctalo como una puerta:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">npx -y promptfoo@latest eval -c promptfooconfig.yaml \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  &amp;&amp; echo "✅ evaluación aprobada" \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  || { echo "❌ evaluación fallida — bloqueando"; exit 1; }</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="Terminal: tabla de evaluación de puerta de CI — mismos resultados de 4 modelos, 9 aprobados / 3 fallidos" src="https://wec.wiline.com/docs/es/assets/images/promptfoo-ci-gate-table-7067b62a9b737ed79e3906b6da54ea2a.png" width="968" height="989" class="img_ev3q">
<strong>Figura 15a.</strong> La puerta vuelve a ejecutar la evaluación de matriz desde caché — mismo resultado 9/3.</p>
<p><img decoding="async" loading="lazy" alt="Terminal: ❌ evaluación fallida — bloqueando" src="https://wec.wiline.com/docs/es/assets/images/promptfoo-ci-gate-result-8765593b4f037c480e7fcef844ff6eb4.png" width="959" height="647" class="img_ev3q">
<strong>Figura 15b.</strong> Promptfoo sale no cero, se activa la rama <code>||</code>, y la puerta bloquea.</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>tip</div><div class="admonitionContent_BuS1"><p>Nunca ejecutes la variante <code>exit 1</code> en tu terminal interactiva — consulta <a href="https://wec.wiline.com/docs/es/tutorials/eval-models-promptfoo-wiline-inference/#3-exit-1-closes-your-ssh-session" class="">Solución de problemas #3</a>.</p></div></div>
<p>En CI, ni siquiera necesitas el <code>exit 1</code> — la salida no cero falla el trabajo. En GitLab:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">eval</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> node</span><span class="token punctuation" style="color:#393A34">:</span><span class="token number" style="color:#36acaa">22</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">script</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> npx </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">y promptfoo@latest eval </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">c promptfooconfig.yaml</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># WEC_API_KEY se establece como una variable CI/CD enmascarada — nunca la comités.</span><br></div></code></pre></div></div>
<p>Ahora un cambio de aviso que silenciosamente rompe la salida JSON, o un cambio de modelo que duplica la latencia, falla la tubería antes de que llegue a los usuarios.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="solución-de-problemas-errores-reales">Solución de problemas (errores reales)<a href="https://wec.wiline.com/docs/es/tutorials/eval-models-promptfoo-wiline-inference/#soluci%C3%B3n-de-problemas-errores-reales" class="hash-link" aria-label="Enlace directo al Solución de problemas (errores reales)" title="Enlace directo al Solución de problemas (errores reales)" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-error-de-bytestring-en-la-primera-evaluación">1. Error de ByteString en la primera evaluación<a href="https://wec.wiline.com/docs/es/tutorials/eval-models-promptfoo-wiline-inference/#1-error-de-bytestring-en-la-primera-evaluaci%C3%B3n" class="hash-link" aria-label="Enlace directo al 1. Error de ByteString en la primera evaluación" title="Enlace directo al 1. Error de ByteString en la primera evaluación" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">Salida</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">Error de llamada a la API: TypeError: Cannot convert argument to a ByteString because</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">the character at index 32 has a value of 8232 which is greater than 255.</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="Terminal: error de ByteString, ambos casos ERROR" src="https://wec.wiline.com/docs/es/assets/images/promptfoo-invisible-char-error-540ad4b2ab9242c120ae80870319df01.png" width="996" height="627" class="img_ev3q">
<strong>Figura 2.</strong> Cada llamada a la API falla antes de llegar al servidor — la clave ni siquiera sale de la máquina.</p>
<p><strong>Causa:</strong> La clave pegada contiene un carácter Unicode invisible — U+2028 (separador de línea). Algunos terminales y administradores de portapapeles lo insertan silenciosamente al copiar desde ciertas fuentes.</p>
<p><strong>Solución:</strong> Elimina los caracteres no imprimibles de la clave:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">export WEC_API_KEY=$(printf '%s' "$WEC_API_KEY" | LC_ALL=C tr -cd '[:print:]')</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">printf 'len=%s\n' "$(printf %s "$WEC_API_KEY" | wc -c)"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># len=25</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="Terminal: comando de eliminación de clave + confirmación de len=25" src="https://wec.wiline.com/docs/es/assets/images/promptfoo-key-strip-1a9c78d8c230c69db3e89eecfdfa24ca.png" width="712" height="70" class="img_ev3q">
<strong>Figura 3.</strong> Después de eliminar, la longitud de la clave confirma que no quedan caracteres extra. Vuelve a ejecutar la evaluación — pasa.</p>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-errores-de-afirmación-de-costo-inmediatamente">2. Errores de afirmación de costo inmediatamente<a href="https://wec.wiline.com/docs/es/tutorials/eval-models-promptfoo-wiline-inference/#2-errores-de-afirmaci%C3%B3n-de-costo-inmediatamente" class="hash-link" aria-label="Enlace directo al 2. Errores de afirmación de costo inmediatamente" title="Enlace directo al 2. Errores de afirmación de costo inmediatamente" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">Salida</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">Error: La afirmación de costo no admite proveedores que no devuelven costo</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="Terminal: afirmación de costo, 2 errores" src="https://wec.wiline.com/docs/es/assets/images/promptfoo-cost-error-1615b4a5c8b0973274d32c6d658a2e12.png" width="953" height="931" class="img_ev3q">
<strong>Figura 10.</strong> La API de Inferencia WEC no devuelve un campo de costo en sus respuestas.</p>
<p><strong>Causa:</strong> Promptfoo calcula el costo buscando un precio por token para el modelo en su lista de precios incorporada y multiplicando por los tokens utilizados. Tiene precios para los grandes modelos alojados, pero no para los nombres de modelos autoalojados de WEC, por lo que no tiene un precio que aplicar y la afirmación falla. (La mayoría de las API, incluida OpenAI, no devuelven un costo en dólares en la respuesta — las herramientas lo calculan).</p>
<p><strong>Solución:</strong> Omite la afirmación de <code>cost</code> por completo. Realiza un seguimiento del gasto desde la línea <code>Total Tokens</code> que Promptfoo imprime al final de cada ejecución, y usa <code>max_tokens</code> en la configuración del proveedor para limitar el uso de tokens por llamada.</p>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-exit-1-cierra-tu-sesión-ssh">3. <code>exit 1</code> cierra tu sesión SSH<a href="https://wec.wiline.com/docs/es/tutorials/eval-models-promptfoo-wiline-inference/#3-exit-1-cierra-tu-sesi%C3%B3n-ssh" class="hash-link" aria-label="Enlace directo al 3-exit-1-cierra-tu-sesión-ssh" title="Enlace directo al 3-exit-1-cierra-tu-sesión-ssh" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">Salida</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">❌ evaluación fallida — bloqueando</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">logout</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Conexión a 10.80.4.212 cerrada.</span><br></div></code></pre></div></div>
<p><strong>Causa:</strong> Ejecutar <code>|| { echo "❌ evaluación fallida — bloqueando"; exit 1; }</code> directamente en una terminal interactiva cierra el proceso de la terminal en sí — lo que, a través de SSH, cierra la conexión.</p>
<p><strong>Solución:</strong> Coloca la puerta en un contexto no interactivo: un objetivo de <code>Makefile</code>, un script de <code>npm</code>, un gancho <code>pre-commit</code> de git, o un trabajo de CI. En esos entornos, <code>exit 1</code> falla el trabajo sin afectar tu terminal. Si necesitas probar la puerta localmente, usa <code>|| echo "❌ evaluación fallida"</code> (sin <code>exit 1</code>) para ver la salida de manera segura.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="qué-sigue">¿Qué sigue?<a href="https://wec.wiline.com/docs/es/tutorials/eval-models-promptfoo-wiline-inference/#qu%C3%A9-sigue" class="hash-link" aria-label="Enlace directo al ¿Qué sigue?" title="Enlace directo al ¿Qué sigue?" translate="no">​</a></h2>
<p>Has pasado de "se ve bien para mí" a un verdadero arnés de evaluación automatizable en WEC — con verificaciones deterministas, líneas de defensa, validación de esquemas, rúbricas calificadas por modelos, una matriz de modelos y una puerta de CI.</p>
<p>Próximamente en esta serie:</p>
<ul>
<li class=""><strong><a class="" href="https://wec.wiline.com/docs/es/tutorials/structured-output-evals-wiline-inference/">JSON confiable: valida el esquema de la salida estructurada de tu modelo</a></strong>
— <strong>ahora disponible (parte 2):</strong> recupera la salida envuelta en cercas y razonamiento con transformaciones, compara modelos por confiabilidad de JSON, y bloquea todo en CI.</li>
<li class=""><strong>Evaluaciones calificadas por modelo bien hechas</strong> — calibrando <code>llm-rubric</code>, puntuación de factualidad y verificaciones de múltiples jueces para que tu calificador sea confiable.</li>
<li class=""><strong>Capturando alucinaciones</strong> — emparejando estas técnicas con la <a href="https://wec.wiline.com/resources/rag-hallucination-eval" target="_blank" rel="noopener noreferrer" class="">evaluación de alucinaciones RAG</a> de WEC.</li>
<li class=""><strong>Evaluando audio</strong> — puntuando la precisión de la transcripción de Whisper en la API de Inferencia WEC.</li>
</ul>]]></content>
        <author>
            <name>Rafael Fernandes</name>
            <uri>https://www.linkedin.com/in/rafaelmacariofernandes/</uri>
        </author>
        <category label="ai" term="ai"/>
        <category label="evals" term="evals"/>
        <category label="promptfoo" term="promptfoo"/>
        <category label="inference" term="inference"/>
        <category label="testing" term="testing"/>
        <category label="observability" term="observability"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Autoalojar el Agente Hermes con memoria persistente]]></title>
        <id>https://wec.wiline.com/docs/es/tutorials/self-host-hermes-agent/</id>
        <link href="https://wec.wiline.com/docs/es/tutorials/self-host-hermes-agent/"/>
        <updated>2026-06-25T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Despliega el Agente Hermes de código abierto de Nous Research en tu Instancia WEC — con memoria persistente respaldada por SQLite que sobrevive a un reinicio completo. Instalación real, configuración del modelo y una prueba de memoria que sobrevive al reinicio.]]></summary>
        <content type="html"><![CDATA[<div class="tutorialHero"><img class="tutorialHero__brand" src="https://wec.wiline.com/docs/es/assets/images/hermesagent-c76bbe81610ae2e70b0950e0f96bec8a.png" alt="Hermes Agent"><span class="tutorialHero__plus">+</span><img class="tutorialHero__brand" src="https://wec.wiline.com/docs/es/assets/images/SQLite370-e02c30e84f9e098787bb57701b0b7232.png" alt="SQLite"></div>
<p><a href="https://hermes-agent.nousresearch.com/" target="_blank" rel="noopener noreferrer" class="">Hermes Agent</a> es el agente de IA de código abierto
(MIT) de Nous Research — "el agente que crece contigo." Su característica destacada es
<strong>memoria persistente</strong>: aprende sobre tus proyectos y <strong>no olvida entre
reinicios</strong>. Esta guía lo despliega en la <strong>misma Instancia WEC</strong> que ya usas para
OpenClaw, lo apunta a un modelo y prueba que la memoria sobrevive a un reinicio completo.</p>
<!-- -->
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>Reproducibilidad</div><div class="admonitionContent_BuS1"><p>Funciona en la <strong>misma Instancia WEC</strong> de la serie OpenClaw — sin nueva VM. El instalador
descarga su propio Python (a través de <code>uv</code>), herramientas de navegador Node.js y un
Playwright Chromium automáticamente. El estado se guarda en <code>~/.hermes/</code>. La puerta de enlace de mensajería se ejecuta como un <strong>servicio de usuario systemd</strong>, por lo que coexiste con la pila Docker de OpenClaw. Capturado en vivo en Ubuntu
22.04 con <code>hermes</code> <strong>v0.17.0</strong>.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="por-qué-la-memoria-persistente-es-importante">Por qué la memoria persistente es importante<a href="https://wec.wiline.com/docs/es/tutorials/self-host-hermes-agent/#por-qu%C3%A9-la-memoria-persistente-es-importante" class="hash-link" aria-label="Enlace directo al Por qué la memoria persistente es importante" title="Enlace directo al Por qué la memoria persistente es importante" translate="no">​</a></h2>
<p>Un agente que olvida en cada reinicio es un juguete. Hermes mantiene un <strong>almacenamiento
duradero en disco</strong> bajo <code>~/.hermes/</code>:</p>
<ul>
<li class=""><strong><code>~/.hermes/memories/MEMORY.md</code></strong> — un archivo legible por humanos de hechos que el agente ha
decidido recordar.</li>
<li class=""><strong><code>~/.hermes/state.db</code></strong> — una base de datos <strong>SQLite</strong> que contiene cada sesión y mensaje,
con índices de <strong>búsqueda de texto completo FTS5</strong> superpuestos para que el agente pueda buscar su propia
historia.</li>
</ul>
<p>Un despliegue, un fallo o un reinicio no borran lo que aprendió. Esa es la diferencia entre
una demostración de chat y un agente en el que puedes confiar día a día — y es exactamente lo que probamos al
final.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="las-piezas">Las piezas<a href="https://wec.wiline.com/docs/es/tutorials/self-host-hermes-agent/#las-piezas" class="hash-link" aria-label="Enlace directo al Las piezas" title="Enlace directo al Las piezas" translate="no">​</a></h2>
<p>Todo corre en una sola WEC Instance. Hermes habla con un modelo a través de la WEC Inference
API, guarda su memoria en disco local y corre como servicio de usuario de systemd — así se
reinicia solo tras un reinicio y su memoria sigue ahí:</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="requisitos-previos">Requisitos previos<a href="https://wec.wiline.com/docs/es/tutorials/self-host-hermes-agent/#requisitos-previos" class="hash-link" aria-label="Enlace directo al Requisitos previos" title="Enlace directo al Requisitos previos" translate="no">​</a></h2>
<ul>
<li class="">Una <strong>Instancia WEC</strong> existente con acceso a la terminal (la caja de OpenClaw funciona).</li>
<li class="">Un modelo: ya sea un inicio de sesión en <strong>Nous Portal</strong>, o tu propia clave API de proveedor (usamos una
clave de OpenAI aquí; <strong>Modelos WEC</strong> con <code>glm-5.2</code> también funcionan).</li>
<li class="">Nada que preinstalar — el instalador maneja Python, Node.js y el motor
del navegador.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-1--instalar-hermes">Paso 1 — Instalar Hermes<a href="https://wec.wiline.com/docs/es/tutorials/self-host-hermes-agent/#paso-1--instalar-hermes" class="hash-link" aria-label="Enlace directo al Paso 1 — Instalar Hermes" title="Enlace directo al Paso 1 — Instalar Hermes" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="Ejecutando la línea de instalación — el instalador de Hermes comienza a resolver y descargar paquetes" src="https://wec.wiline.com/docs/es/assets/images/hermes-install-start-1f3d29fcdd9d66dd1e5c1bc28b3c9e5d.png" width="660" height="341" class="img_ev3q"></p>
<p>El instalador resuelve un entorno virtual de Python con <code>uv</code>, instala las herramientas de
navegador de Node.js, descarga un Playwright Chromium, sincroniza las habilidades empaquetadas y coloca un
lanzador <code>hermes</code> en <code>~/.local/bin</code>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">Salida</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">✓ Paquete principal instalado (verificado por hash a través de uv.lock)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">✓ Todas las dependencias instaladas</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">✓ Configuración del motor de navegador completa</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">✓ Lanzador hermes instalado → ~/.local/bin/hermes</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">✓ Directorio de configuración listo: ~/.hermes/</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">→ Iniciando el asistente de configuración...</span><br></div></code></pre></div></div>
<p>Luego lanza automáticamente el asistente de configuración. Recarga tu terminal después para que el
comando <code>hermes</code> esté en tu <code>PATH</code>:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">source ~/.bashrc</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-2--ejecutar-el-asistente-de-configuración">Paso 2 — Ejecutar el asistente de configuración<a href="https://wec.wiline.com/docs/es/tutorials/self-host-hermes-agent/#paso-2--ejecutar-el-asistente-de-configuraci%C3%B3n" class="hash-link" aria-label="Enlace directo al Paso 2 — Ejecutar el asistente de configuración" title="Enlace directo al Paso 2 — Ejecutar el asistente de configuración" translate="no">​</a></h2>
<p>El asistente guía a través del tipo de configuración, backend de terminal y mensajería. En una caja que
ya ejecutó OpenClaw, ofrece <strong>importar</strong> la configuración antigua primero — puedes previsualizar
lo que se migraría antes de que cambie algo.</p>
<p>La primera elección es <strong>cómo configurar Hermes</strong>:</p>
<ul>
<li class=""><strong>Configuración rápida (Nous Portal)</strong> — un inicio de sesión OAuth, sin claves API. El Portal agrupa un
modelo y una puerta de enlace de herramientas. <strong>Requiere una suscripción activa a Nous Portal.</strong></li>
<li class=""><strong>Configuración completa (trae tus propias claves)</strong> — configuras proveedores y pegas tus propias
claves.</li>
</ul>
<p><img decoding="async" loading="lazy" alt="El aviso &amp;quot;¿Cómo te gustaría configurar Hermes?&amp;quot; con opciones de Configuración rápida, Configuración completa y Pizarra en blanco" src="https://wec.wiline.com/docs/es/assets/images/hermes-setup-choice-0db3a3f64faec3d72e3477b075af1901.png" width="658" height="125" class="img_ev3q"></p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>Lo que realmente hicimos</div><div class="admonitionContent_BuS1"><p>Elegimos <strong>Configuración rápida</strong> primero. El inicio de sesión en el Portal mostró un código de dispositivo y se quedó en
<em>"Esperando aprobación (sondeando)"</em> — necesita una suscripción activa al Portal, que esta
cuenta no tenía. Presionamos <strong>Ctrl+C</strong> para cancelar y en su lugar <strong>trajimos nuestro propio
modelo en el Paso 3</strong> (a continuación) con una clave de OpenAI. Si <em>tienes</em> un plan de Portal, completa
el inicio de sesión aquí y puedes omitir el Paso 3.</p></div></div>
<p>Para el resto del asistente:</p>
<ul>
<li class=""><strong>Backend de terminal:</strong> <em>Local</em> — los comandos se ejecutan directamente en esta máquina.</li>
<li class=""><strong>Mensajería:</strong> <em>Omitir</em> por ahora (puedes conectar Telegram/Discord más tarde con
<code>hermes setup gateway</code>).</li>
</ul>
<p>El asistente aplica valores predeterminados sensatos para el agente (máximo de iteraciones, un umbral de compresión de contexto del 50%) y termina con un resumen de disponibilidad de herramientas.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-3--conectar-un-modelo">Paso 3 — Conectar un modelo<a href="https://wec.wiline.com/docs/es/tutorials/self-host-hermes-agent/#paso-3--conectar-un-modelo" class="hash-link" aria-label="Enlace directo al Paso 3 — Conectar un modelo" title="Enlace directo al Paso 3 — Conectar un modelo" translate="no">​</a></h2>
<p>Fuera de la caja, el modelo predeterminado se enruta a través del Nous Portal / OpenRouter y necesita
una clave. Para usar <strong>tu propio proveedor</strong> en su lugar, ejecuta:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">hermes setup model</span><br></div></code></pre></div></div>
<p>Luego selecciona <strong>OpenAI ▸ OpenAI API</strong>, pega tu clave API cuando se te pida (se escribe en
<code>~/.hermes/.env</code> — nunca se muestra), acepta la URL base predeterminada
<code>https://api.openai.com/v1</code>, y elige un modelo — usamos <strong><code>gpt-5.5</code></strong>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">Salida</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">Clave API guardada.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">URL base [https://api.openai.com/v1]:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Seleccionar modelo predeterminado:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> → (●) gpt-5.5</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="Eligiendo el modelo predeterminado en hermes setup model — gpt-5.5 seleccionado de la lista de OpenAI" src="https://wec.wiline.com/docs/es/assets/images/hermes-model-select-2b0c5452d2e217ff8692c022badd9b28.png" width="661" height="545" class="img_ev3q"></p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>Usa Modelos WEC en su lugar</div><div class="admonitionContent_BuS1"><p>Para ejecutar inferencias en el propio endpoint de WiLine, elige <strong>Endpoint personalizado</strong>, establece la URL base
en <code>https://inference.wiline.com/v1</code>, pega tu clave API de WEC y usa un modelo como
<code>glm-5.2</code>. Mismo flujo compatible con OpenAI.</p></div></div>
<p>Confirma que el modelo y la clave están saludables:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">hermes doctor</span><br></div></code></pre></div></div>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">Salida</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">◆ Archivos de configuración</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  ✓ El archivo ~/.hermes/.env existe</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  ✓ Clave API o endpoint personalizado configurado</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">...</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">◆ Proveedor de memoria</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  ✓ Memoria incorporada activa (no se configuró proveedor externo — esto está bien)</span><br></div></code></pre></div></div>
<p>Las líneas restantes <code>⚠</code> en <code>doctor</code> son integraciones de herramientas opcionales (búsqueda web,
Discord, generación de imágenes) — no necesarias para esta guía.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-4--chatear-y-guardar-una-memoria">Paso 4 — Chatear y guardar una memoria<a href="https://wec.wiline.com/docs/es/tutorials/self-host-hermes-agent/#paso-4--chatear-y-guardar-una-memoria" class="hash-link" aria-label="Enlace directo al Paso 4 — Chatear y guardar una memoria" title="Enlace directo al Paso 4 — Chatear y guardar una memoria" translate="no">​</a></h2>
<p>Inicia una sesión interactiva:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">hermes</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="La pantalla de bienvenida del Agente Hermes TUI — logo ASCII, herramientas y habilidades disponibles, modelo gpt-5.5, y el aviso de chat" src="https://wec.wiline.com/docs/es/assets/images/hermes-tui-welcome-842b22f5c733599e17b84cde75db4933.png" width="657" height="814" class="img_ev3q"></p>
<p>Envíale un hecho y pídele que lo recuerde — observa cómo se activa la herramienta <code>🧠 memory</code>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">En el TUI de Hermes</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">● Por favor guarda en tu memoria a largo plazo: mi nombre en clave del proyecto WEC es</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  Bluefin-7, ejecutándose en la caja de OpenClaw.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  ┊ 🧠 memory    ?  0.0s</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">╭─ ⚕ Hermes ─────────────────────────────────────────────────────────╮</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Guardado en la memoria a largo plazo: tu nombre en clave del proyecto WEC es Bluefin-7,</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    ejecutándose en la caja de OpenClaw.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">╰─────────────────────────────────────────────────────────────────────╯</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="Hermes activando la herramienta de memoria y confirmando que el hecho fue guardado en la memoria a largo plazo" src="https://wec.wiline.com/docs/es/assets/images/hermes-memory-save-458cefa8aaf46e0d1cc554418a3acc38.png" width="663" height="218" class="img_ev3q"></p>
<p>Esa escritura crea <code>~/.hermes/memories/MEMORY.md</code>. Sal con <code>/exit</code> o <code>Ctrl+D</code>.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>Problema al pegar</div><div class="admonitionContent_BuS1"><p>El TUI envía al presionar enter, por lo que pegar texto de varias líneas puede enviar un mensaje medio escrito.
Si un aviso solo captura un fragmento, <strong>escríbelo en una línea</strong> a mano.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-5--probar-que-la-memoria-sobrevive-a-un-reinicio">Paso 5 — Probar que la memoria sobrevive a un reinicio<a href="https://wec.wiline.com/docs/es/tutorials/self-host-hermes-agent/#paso-5--probar-que-la-memoria-sobrevive-a-un-reinicio" class="hash-link" aria-label="Enlace directo al Paso 5 — Probar que la memoria sobrevive a un reinicio" title="Enlace directo al Paso 5 — Probar que la memoria sobrevive a un reinicio" translate="no">​</a></h2>
<p>Este es el objetivo principal. La puerta de enlace de Hermes se instala como un <strong>servicio de usuario systemd</strong>
con <strong>linger habilitado</strong>, por lo que vuelve automáticamente después de un reinicio — sin necesidad de iniciar sesión.
Reinicia el servicio y luego reinicia toda la caja para hacer la prueba a prueba de fallos:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">systemctl --user restart hermes-gateway   # reinicia solo el servicio de puerta de enlace</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">sudo reboot                               # luego reinicia toda la caja</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="Reiniciando la puerta de enlace y reiniciando la caja — la conexión SSH se cierra mientras el host se apaga" src="https://wec.wiline.com/docs/es/assets/images/hermes-reboot-ae228254579427ae3018547803f83c62.png" width="616" height="74" class="img_ev3q"></p>
<p>Reconéctate una vez que la caja esté activa y confirma que el servicio se reinició por sí mismo:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">systemctl --user status hermes-gateway --no-pager</span><br></div></code></pre></div></div>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">Salida</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">● hermes-gateway.service - Hermes Agent Gateway - Integración de Plataforma de Mensajería</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">     Cargado: cargado (...; habilitado; ajuste de proveedor: habilitado)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">     Activo: activo (en ejecución) desde el jue 2026-06-25 21:21:54 UTC; hace 1min 36s</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="status de systemctl mostrando hermes-gateway activo y en ejecución desde el arranque, iniciado automáticamente por linger después del reinicio" src="https://wec.wiline.com/docs/es/assets/images/hermes-gateway-status-4da5472c10ec41fe81cfc64298589f10.png" width="1001" height="272" class="img_ev3q"></p>
<p>Ahora abre una sesión <strong>nueva</strong> y pregunta — sin contexto previo, ID de sesión completamente nuevo:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">hermes</span><br></div></code></pre></div></div>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">En el TUI de Hermes</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">● ¿Cuál es mi nombre en clave del proyecto WEC, y en qué caja se ejecuta?</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">╭─ ⚕ Hermes ─────────────────────────────────────────────────────────╮</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Tu nombre en clave del proyecto WEC es Bluefin-7, y se ejecuta en la</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    caja de OpenClaw.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">╰─────────────────────────────────────────────────────────────────────╯</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="Una nueva sesión de Hermes después del reinicio recordando correctamente el nombre en clave Bluefin-7 desde el disco" src="https://wec.wiline.com/docs/es/assets/images/hermes-reboot-recall-eac2b093a48d12569fa730340f5751d3.png" width="652" height="300" class="img_ev3q"></p>
<p>Recordó el hecho desde el disco después de un reinicio completo. Inspecciona dónde vive ese estado duradero:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">cat ~/.hermes/memories/MEMORY.md</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">file ~/.hermes/state.db &amp;&amp; du -h ~/.hermes/state.db</span><br></div></code></pre></div></div>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">Salida</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">El nombre en clave del proyecto WEC del usuario es Bluefin-7, ejecutándose en la caja de OpenClaw.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">/home/ubuntu/.hermes/state.db: base de datos SQLite 3.x, ...</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">176K	/home/ubuntu/.hermes/state.db</span><br></div></code></pre></div></div>
<p>Un <code>MEMORY.md</code> en texto plano que puedes leer y editar, respaldado por un <code>state.db</code> de SQLite para
sesiones. Esa es la memoria persistente — y acaba de sobrevivir a un reinicio.</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>Cómo se almacena la memoria — dos archivos, dos trabajos</div><div class="admonitionContent_BuS1"><p>Cuando le pides a Hermes que recuerde algo, escribe el hecho en
<strong><code>~/.hermes/memories/MEMORY.md</code></strong> — Markdown plano que puedes abrir, leer e incluso editar a
mano. Por separado, tu <strong>historial completo de conversación</strong> se mantiene en
<strong><code>~/.hermes/state.db</code></strong>, una base de datos <strong>SQLite</strong>. Ambos viven en disco, por lo que un
reinicio no los pierde.</p><p>La forma más sencilla de ver lo que sabe el agente es simplemente leer el Markdown:</p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">cat ~/.hermes/memories/MEMORY.md</span><br></div></code></pre></div></div><p>Para mirar dentro de la base de datos, ten en cuenta que el <em>motor</em> de SQLite está integrado en Python, por lo que Hermes
lo usa sin ninguna instalación adicional. La opcional herramienta de línea de comandos <code>sqlite3</code> — para
navegar por la base de datos a mano — no está instalada por defecto. Si la quieres:</p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">sudo apt install -y sqlite3</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">sqlite3 ~/.hermes/state.db '.tables'</span><br></div></code></pre></div></div><p><img decoding="async" loading="lazy" alt="Salida de .tables en state.db — tablas de sesiones y mensajes junto a la familia de mensajes_fts FTS5" src="https://wec.wiline.com/docs/es/assets/images/hermes-sqlite-tables-dbdc5016a3514137534038cac3f6298d.png" width="540" height="170" class="img_ev3q"></p><p>Verás tablas <code>sessions</code> y <code>messages</code> que contienen la conversación, además de una familia de
tablas <code>messages_fts*</code>. Esos son índices de texto completo <strong>FTS5</strong> de SQLite — incluyendo una
variante de trigramas para coincidencias difusas — que permiten a Hermes buscar sus propias conversaciones pasadas:</p><div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">Salida (de .schema)</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">CREATE</span><span class="token plain"> VIRTUAL </span><span class="token keyword" style="color:#00009f">TABLE</span><span class="token plain"> messages_fts </span><span class="token keyword" style="color:#00009f">USING</span><span class="token plain"> fts5</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">content</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">CREATE</span><span class="token plain"> VIRTUAL </span><span class="token keyword" style="color:#00009f">TABLE</span><span class="token plain"> messages_fts_trigram </span><span class="token keyword" style="color:#00009f">USING</span><span class="token plain"> fts5</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">content</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tokenize</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">'trigram'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div><p>Y cada sesión es una fila que puedes inspeccionar — aquí están las dos de esta guía, la
segunda creada <em>después</em> del reinicio:</p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">sqlite3 -header -column ~/.hermes/state.db \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  "SELECT substr(id,1,15) AS id, model, message_count FROM sessions ORDER BY started_at;"</span><br></div></code></pre></div></div><div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">Salida</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">id               model    message_count</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">---------------  -------  -------------</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">20260625_211227  gpt-5.5  8</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">20260625_212333  gpt-5.5  2</span><br></div></code></pre></div></div><p>Nunca <em>necesitas</em> la CLI para que Hermes funcione — es solo para inspeccionar la base de datos
tú mismo. Día a día, el <code>MEMORY.md</code> legible por humanos es todo lo que tienes que mirar.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="bonificación--leer-la-conversación-que-el-agente-almacenó">Bonificación — leer la conversación que el agente almacenó<a href="https://wec.wiline.com/docs/es/tutorials/self-host-hermes-agent/#bonificaci%C3%B3n--leer-la-conversaci%C3%B3n-que-el-agente-almacen%C3%B3" class="hash-link" aria-label="Enlace directo al Bonificación — leer la conversación que el agente almacenó" title="Enlace directo al Bonificación — leer la conversación que el agente almacenó" translate="no">​</a></h2>
<p>La tabla <code>messages</code> contiene el texto real de cada turno. Cada fila tiene un <code>role</code> —
<code>user</code> (lo que escribiste), <code>assistant</code> (la respuesta de Hermes), o <code>tool</code> (el resultado de una herramienta) — así que
puedes reproducir todo el intercambio:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">sqlite3 -header -column ~/.hermes/state.db \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  "SELECT substr(session_id,10,6) AS sess, role, substr(content,1,55) AS content</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   FROM messages ORDER BY timestamp;"</span><br></div></code></pre></div></div>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">Salida</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">sess    role       content</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">------  ---------  -------------------------------------------------------</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">211227  user       hello, can you hear me?</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">211227  assistant  Yes — I can hear you. How can I help?</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">211227  user       Please save to your long-term memory: my WEC project...</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">211227  tool       {"success": true, "done": true, "target": "memory", ...</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">211227  assistant  Saved to long-term memory: your WEC project codename...</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">212333  user       What's my WEC project codename, and which box does...</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">212333  assistant  Your WEC project codename is Bluefin-7, and it runs...</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="La tabla de mensajes en state.db mostrando la conversación completa — filas de usuario, asistente y herramienta a través de ambas sesiones" src="https://wec.wiline.com/docs/es/assets/images/hermes-sqlite-messages-12ab80eeb7db27fbf02d5c73b075d553.png" width="659" height="294" class="img_ev3q"></p>
<p>La parte interesante es <em>cómo</em> se escribió la memoria. El asistente no editó un archivo
directamente — emitió una <strong>llamada a la herramienta <code>memory</code></strong>, y esa herramienta es la que escribió
<code>MEMORY.md</code>. Puedes ver la llamada exacta registrada en el historial:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">sqlite3 ~/.hermes/state.db \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  "SELECT tool_calls FROM messages WHERE tool_calls LIKE '%memory%' LIMIT 1;"</span><br></div></code></pre></div></div>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">Salida</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token property" style="color:#36acaa">"function"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"memory"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"arguments"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  "</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">\"target\"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain">\"memory\"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">\"operations\"</span><span class="token operator" style="color:#393A34">:</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">     </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">\"action\"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain">\"add\"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      \"content\"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain">\"El nombre en clave del proyecto WEC del usuario es Bluefin</span><span class="token number" style="color:#36acaa">-7</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ejecutándose en la caja de OpenClaw.\"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain">"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="La llamada a la herramienta de memoria registrada en la tabla de mensajes — la operación de adición que escribió el hecho de Bluefin-7 en MEMORY.md" src="https://wec.wiline.com/docs/es/assets/images/hermes-sqlite-memory-toolcall-53bc3762b14f9ca8e30715118341b792.png" width="897" height="362" class="img_ev3q"></p>
<p>Así que la cadena completa es: <strong>tú escribes → el agente decide que vale la pena conservar → llama a la
herramienta <code>memory</code> con <code>action: add</code> → eso escribe el hecho en <code>MEMORY.md</code></strong>, mientras que
toda la conversación permanece en <code>state.db</code>, completamente buscable a través de FTS5. Dos almacenes, una
memoria duradera.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="qué-puedes-hacer-con-ello">¿Qué puedes hacer con ello?<a href="https://wec.wiline.com/docs/es/tutorials/self-host-hermes-agent/#qu%C3%A9-puedes-hacer-con-ello" class="hash-link" aria-label="Enlace directo al ¿Qué puedes hacer con ello?" title="Enlace directo al ¿Qué puedes hacer con ello?" translate="no">​</a></h2>
<p>Una memoria que sobrevive a los reinicios solo importa por lo que el agente <em>hace</em> con ella.
Con Hermes en funcionamiento, la misma instalación ofrece estas capacidades (las viste en la
lista de herramientas de <code>hermes doctor</code>) — aquí está lo que desbloquean día a día:</p>
<ul>
<li class=""><strong>Realiza trabajo real en la caja.</strong> Las herramientas <code>terminal</code> y <code>code_execution</code> le permiten actuar,
no solo chatear — por ejemplo, <em>"clona este repositorio, instala dependencias y ejecuta las pruebas,"</em> o <em>"verifica
por qué se está llenando el disco."</em> Lo hace en la Instancia WEC y reporta de vuelta.</li>
<li class=""><strong>Controla desde tu teléfono.</strong> Una puerta de enlace de mensajería (Telegram, Discord, Slack, y
más) significa que puedes darle una tarea desde cualquier lugar — sin SSH. <em>(Configuración en la Parte 2.)</em></li>
<li class=""><strong>Ponlo en un horario.</strong> La herramienta <code>cronjob</code> ejecuta trabajos recurrentes: un <strong>informe matutino</strong>,
un <strong>informe semanal de uso del disco</strong>, o <strong>vigila un servicio y avísame cuando falle</strong> — entregado a tu canal de mensajería. <em>(Parte 3.)</em></li>
<li class=""><strong>Navega y busca en la web.</strong> Con las herramientas de navegador y búsqueda web puede obtener información en vivo y actuar en páginas reales. <em>(Estas necesitan claves API de proveedor — consulta <code>hermes setup tools</code>.)</em></li>
<li class=""><strong>Desarrolla habilidades reutilizables y delega.</strong> Puede construir <strong>habilidades</strong> a partir de lo que aprende y
delegar subtareas a <strong>subagentes</strong> — la parte "agente que crece contigo".</li>
<li class=""><strong>Recuerda a través de todo ello.</strong> Todo lo anterior está respaldado por la memoria persistente que
acabas de configurar, por lo que el contexto se lleva de una tarea — y un día — al siguiente.</li>
</ul>
<p>El resto de esta serie convierte cada una de estas en una guía práctica (consulta <em>Qué sigue</em>).</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="solución-de-problemas-real">Solución de problemas (real)<a href="https://wec.wiline.com/docs/es/tutorials/self-host-hermes-agent/#soluci%C3%B3n-de-problemas-real" class="hash-link" aria-label="Enlace directo al Solución de problemas (real)" title="Enlace directo al Solución de problemas (real)" translate="no">​</a></h2>
<ul>
<li class="">
<p><strong>La Configuración rápida (Nous Portal) se queda atascada en "Esperando aprobación (sondeando)".</strong> El inicio de sesión en el Portal
necesita una suscripción/crédito activo. Si no tienes uno, presiona <code>Ctrl+C</code> y usa
<em>Configuración completa</em> con tu propia clave en su lugar (Paso 3).</p>
</li>
<li class="">
<p><strong><code>doctor</code> muestra "No se encontró clave API en ~/.hermes/.env".</strong> El modelo predeterminado apunta a
Portal/OpenRouter. Ejecuta <code>hermes setup model</code>, elige tu proveedor y pega la
clave — <code>doctor</code> luego informa <em>"Clave API o endpoint personalizado configurado."</em></p>
</li>
<li class="">
<p><strong>Advertencia de <code>.env</code> obsoleta al iniciar la puerta de enlace</strong> (por ejemplo, <code>MESSAGING_CWD</code>). Inofensiva;
sigue la sugerencia para mover la configuración a <code>config.yaml</code> cuando sea conveniente.</p>
</li>
<li class="">
<p><strong>Repetidos <code>WARNING [Telegram] …</code> en los registros de la puerta de enlace.</strong> Si seguiste las partes anteriores
de OpenClaw y agregaste un canal de Telegram, la configuración de Hermes detectó OpenClaw y
<strong>importó esas configuraciones de mensajería</strong> en <code>~/.hermes/.env</code>. Como omitimos
terminar la mensajería aquí, la puerta de enlace tiene un token pero no una configuración completa, por lo que sigue
reintentando y registrando esta advertencia. (Una caja nueva sin configuración de Telegram de OpenClaw no
lo verá.) Elimina las entradas sobrantes y reinicia:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">sed -i '/^TELEGRAM_ALLOWED_USERS=/d; /^TELEGRAM_BOT_TOKEN=/d' ~/.hermes/.env</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">systemctl --user restart hermes-gateway</span><br></div></code></pre></div></div>
<p>Si ese token alguna vez fue expuesto, revócalo en <strong>@BotFather</strong> de Telegram y emite uno nuevo.</p>
</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="qué-sigue">Qué sigue<a href="https://wec.wiline.com/docs/es/tutorials/self-host-hermes-agent/#qu%C3%A9-sigue" class="hash-link" aria-label="Enlace directo al Qué sigue" title="Enlace directo al Qué sigue" translate="no">​</a></h2>
<p>Ahora que Hermes está funcionando con memoria duradera, el resto de la serie convierte cada
capacidad anterior en una guía práctica:</p>
<ul>
<li class=""><strong>Parte 2 — Controla a Hermes desde Telegram.</strong> Termina de migrar el canal de Telegram que Hermes
ya importó de OpenClaw (<code>hermes setup gateway</code>, listas de permitidos, verifica que el bot
responda), para que puedas darle tareas desde tu teléfono.</li>
<li class=""><strong>Parte 3 — Ponlo en un horario.</strong> Usa la herramienta cron para un informe matutino, un informe semanal,
y una alerta de salud del servicio entregada a tu canal.</li>
<li class=""><strong>Parte 4 — Habilidades personalizadas y subagentes.</strong> Enséñale habilidades reutilizables y delega trabajo.</li>
</ul>
<p>Los enlaces aparecerán aquí a medida que cada parte se publique.</p>]]></content>
        <author>
            <name>Rafael Fernandes</name>
            <uri>https://www.linkedin.com/in/rafaelmacariofernandes/</uri>
        </author>
        <category label="ai" term="ai"/>
        <category label="autoalojamiento" term="autoalojamiento"/>
        <category label="hermes" term="hermes"/>
        <category label="agentes" term="agentes"/>
        <category label="memoria" term="memoria"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Haz OpenClaw privado con una VPN mesh de NetBird]]></title>
        <id>https://wec.wiline.com/docs/es/tutorials/netbird-mesh-vpn-openclaw/</id>
        <link href="https://wec.wiline.com/docs/es/tutorials/netbird-mesh-vpn-openclaw/"/>
        <updated>2026-06-24T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Pon OpenClaw en una mesh privada con NetBird, reapunta tu hostname a la IP de la mesh y cierra los puertos públicos — para que la misma URL de chat funcione solo para tus dispositivos. Comandos reales y trampas de una ejecución en vivo.]]></summary>
        <content type="html"><![CDATA[<div class="tutorialHero"><img class="tutorialHero__brand" src="https://wec.wiline.com/docs/es/assets/images/openclaw-wordmark-003352a1a7f02afc3bd877ae6f2dc175.png" alt="OpenClaw"><span class="tutorialHero__plus">+</span><img class="tutorialHero__docker" src="https://wec.wiline.com/docs/es/assets/images/netbird-dark-0d2ba9c783792904def84b7e687b8f95.png" alt="NetBird"></div>
<p>En el <a class="" href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/">Artículo 2</a> pusimos Caddy delante de
OpenClaw para HTTPS, y en el <a class="" href="https://wec.wiline.com/docs/es/tutorials/add-telegram-channel-openclaw/">Artículo 3</a>
añadimos un canal de Telegram. El gateway funciona — pero Caddy sigue escuchando en
<code>0.0.0.0</code>, alcanzable por cualquier cosa que pueda enrutar hasta la máquina. Esta es la
<em>chave de ouro</em> de la serie: unimos el servidor y tu laptop a una mesh de <strong>NetBird</strong>,
reapuntamos <code>openclaw.local</code> a la IP de la mesh y <strong>cerramos los puertos públicos</strong>. La misma
URL <code>https://openclaw.local/chat</code> sigue funcionando — pero solo para tus dispositivos.
Cada comando y error de abajo proviene de la ejecución real.</p>
<!-- -->
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>Reproducibilidad</div><div class="admonitionContent_BuS1"><p>Continúa desde los Artículos 1–3 en la misma Instancia WEC — <strong>Ubuntu</strong>, OpenClaw detrás de
<strong>Caddy 2</strong>. Usamos <strong>NetBird gestionado</strong> (el tier gratuito en
<a href="https://app.netbird.io/" target="_blank" rel="noopener noreferrer" class="">app.netbird.io</a>) con el agente <strong>0.73.2</strong> tanto en el
servidor (Linux, WireGuard de kernel) como en el cliente (macOS, userspace). NetBird está
basado en <a href="https://www.wireguard.com/" target="_blank" rel="noopener noreferrer" class="">WireGuard</a> y es open source; también puedes
autoalojar el control plane, que es un tema más grande para otro día.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="cómo-funciona-netbird-y-qué-vas-a-construir">Cómo funciona NetBird (y qué vas a construir)<a href="https://wec.wiline.com/docs/es/tutorials/netbird-mesh-vpn-openclaw/#c%C3%B3mo-funciona-netbird-y-qu%C3%A9-vas-a-construir" class="hash-link" aria-label="Enlace directo al Cómo funciona NetBird (y qué vas a construir)" title="Enlace directo al Cómo funciona NetBird (y qué vas a construir)" translate="no">​</a></h2>
<p>NetBird tiene dos partes, y la distinción es justamente el punto:</p>
<ol>
<li class=""><strong>El control plane</strong> (el servicio gestionado de <em>management</em> + <em>signal</em> de NetBird) — esto es lo único con presencia pública. Cada dispositivo que quiere unirse <strong>se autentica contra él</strong> (con tu setup key o login SSO), y él intermedia el intercambio de la clave pública <a href="https://www.wireguard.com/" target="_blank" rel="noopener noreferrer" class="">WireGuard</a> de cada peer.</li>
<li class=""><strong>El data plane</strong> — una vez autenticados, los peers se hablan <strong>directamente entre sí</strong>, cifrados de extremo a extremo sobre WireGuard. Tu tráfico real nunca pasa por un puerto público que tú gestiones; el control plane solo hizo las presentaciones.</li>
</ol>
<p>Así que estás construyendo una mesh donde el <strong>Peer A</strong> (tu laptop) alcanza al <strong>Peer B</strong> (la máquina de OpenClaw) por su dirección privada <code>100.x</code> — y <code>openclaw.local</code> apunta ahí, con Caddy ligado solo a esa interfaz. El lado público/LAN no tiene nada escuchando.</p>
<p>La idea clave para más adelante: <strong>la mesh no se limita a dos peers.</strong> Autentícate una vez, y cada nuevo servicio — Ollama, un VDI, WordPress — se une como otro peer con su propia dirección <code>100.x</code>, alcanzable de forma privada con <strong>ningún puerto público que abrir</strong>. Configuras el modelo de auth una vez; añades servicios para siempre.</p>
<!-- -->
<p>La internet pública solo alcanza al <strong>control plane de NetBird</strong> — y eso solo
<em>autentica</em> peers, nunca lleva el tráfico de tu app. Tus servicios no están
"bloqueados por firewall" tanto como <strong>invisibles</strong>: OpenClaw no tiene IP pública, así que
simplemente <strong>no hay ruta</strong> hacia él desde internet. La única forma de entrar es ser un
peer autenticado en la mesh.</p>
<p>En este tutorial conectamos los dos primeros peers — <strong>tu laptop</strong> y la
<strong>máquina de OpenClaw</strong>. Los demás servicios dejan claro el punto: una vez configurada la auth,
<code>Ollama</code>, un <code>VDI</code>, <code>WordPress</code> simplemente se unen a la misma mesh como otro peer
<code>100.x</code> — sin puertos públicos, sin exposición por servicio.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="levanta-un-control-plane-de-netbird">Levanta un control plane de NetBird<a href="https://wec.wiline.com/docs/es/tutorials/netbird-mesh-vpn-openclaw/#levanta-un-control-plane-de-netbird" class="hash-link" aria-label="Enlace directo al Levanta un control plane de NetBird" title="Enlace directo al Levanta un control plane de NetBird" translate="no">​</a></h2>
<p>Toda mesh de NetBird tiene dos roles: <strong>peers</strong> — tus máquinas, es decir la máquina
de OpenClaw de las Partes 1–3 y tu laptop — y un <strong>control plane</strong> que autentica esos
peers y emite sus claves de inscripción. Ya tienes el peer (tu máquina de OpenClaw);
ahora necesitas un control plane al que se una. Dos formas de conseguirlo:</p>
<ul>
<li class=""><strong>Opción A — autoalójalo en WiLine (recomendada).</strong> Despliega la <strong>plantilla NetBird del Marketplace de WEC</strong>: una <strong>Instancia WEC dedicada</strong> que corre tu <em>propio</em> control plane de NetBird — dashboard, certificado de confianza, sin shell. Así tu red privada <em>completa</em> vive en WiLine — la máquina de OpenClaw <strong>y</strong> su control plane — sin depender de un tercero.</li>
<li class=""><strong>Opción B — NetBird gestionado (sin máquina extra).</strong> ¿No quieres correr tu propio control plane? Usa el tier gratuito en <a href="https://app.netbird.io/" target="_blank" rel="noopener noreferrer" class="">app.netbird.io</a> — inicia sesión y listo; el control plane es la nube de NetBird. Luego salta a <a href="https://wec.wiline.com/docs/es/tutorials/netbird-mesh-vpn-openclaw/#ahora-inscribe-tus-m%C3%A1quinas" class="">Ahora inscribe tus máquinas</a>.</li>
</ul>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>Dos máquinas, a propósito — y tu máquina de OpenClaw queda intacta</div><div class="admonitionContent_BuS1"><p>Con la <strong>Opción A</strong> correrás <strong>dos instancias</strong>: tu <strong>máquina de OpenClaw</strong> (Partes
1–3, sin cambios) y una <strong>segunda máquina dedicada al control plane</strong> (esta plantilla).
Esa es la topología correcta — un control plane debe ser su propia máquina, nunca
compartiendo con una carga de trabajo. Con la <strong>Opción B</strong> solo tienes tu única
máquina de OpenClaw. <strong>En cualquier caso nunca reconstruyes la máquina de OpenClaw</strong>
— en <a href="https://wec.wiline.com/docs/es/tutorials/netbird-mesh-vpn-openclaw/#ahora-inscribe-tus-m%C3%A1quinas" class="">Ahora inscribe tus máquinas</a> solo instalas el
agente de NetBird en ella y se une a la mesh como peer.</p></div></div>
<p>Los pasos de abajo configuran la <strong>Opción A</strong>. WEC ejecuta la instalación; tú aportas un email y una
IP pública, y unos minutos después tienes tu propio dashboard de NetBird en
<code>https://&lt;your-ip&gt;.apps.wiline.cloud</code> con un certificado válido.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-obtén-una-ip-pública-elastic">1. Obtén una IP pública (Elastic)<a href="https://wec.wiline.com/docs/es/tutorials/netbird-mesh-vpn-openclaw/#1-obt%C3%A9n-una-ip-p%C3%BAblica-elastic" class="hash-link" aria-label="Enlace directo al 1. Obtén una IP pública (Elastic)" title="Enlace directo al 1. Obtén una IP pública (Elastic)" translate="no">​</a></h3>
<p>El dashboard necesita una dirección pública. En <strong>Networks → Elastic IPs</strong>, asigna una
IP nueva o elige una libre, y anota su valor (la nuestra: <code>108.60.112.165</code>). Debe estar
en la <strong>VPC y zona en la que vas a desplegar</strong>. Pasos completos:
<a class="" href="https://wec.wiline.com/docs/es/cloud_portal/platform/networks/elastic_ip/">Configurar Elastic IPs</a>.</p>
<p><img decoding="async" loading="lazy" alt="Una Elastic IP disponible en Networks → Elastic IPs" src="https://wec.wiline.com/docs/es/assets/images/netbird-eip-available-b4ec56b4c4946226b7c9e199bd540f61.png" width="2793" height="1305" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-despliega-una-vm-con-la-plantilla">2. Despliega una VM con la plantilla<a href="https://wec.wiline.com/docs/es/tutorials/netbird-mesh-vpn-openclaw/#2-despliega-una-vm-con-la-plantilla" class="hash-link" aria-label="Enlace directo al 2. Despliega una VM con la plantilla" title="Enlace directo al 2. Despliega una VM con la plantilla" translate="no">​</a></h3>
<p>Esta nueva instancia se convierte en tu <strong>control plane de NetBird</strong> — una máquina
aparte de tu instancia de OpenClaw, que queda intacta. Abre <strong>Deploy → Deploy a VM</strong> (recorrido completo:
<a class="" href="https://wec.wiline.com/docs/es/cloud_portal/platform/compute/instances/compute_instance/">Desplegar una máquina virtual</a>).
Las elecciones que importan aquí:</p>
<ul>
<li class=""><strong>Network</strong> — elige la red en la <strong>misma VPC/zona que tu Elastic IP</strong> (nosotros usamos <code>Local Subnet</code> en <code>Princeton-VPC</code>), para que la IP pueda adjuntarse después.</li>
<li class=""><strong>Template → Marketplace → Ubuntu 24.04 LTS Netbird.</strong></li>
<li class=""><strong>Template Configuration</strong> — tu <strong>email de Let's Encrypt</strong> y la <strong>Elastic IP del paso 1</strong> como dirección externa.</li>
</ul>
<p><img decoding="async" loading="lazy" alt="Template Configuration — email de Let&amp;#39;s Encrypt y la Elastic IP" src="https://wec.wiline.com/docs/es/assets/images/netbird-template-config-e03939727bf27b7f6ce4ceb4fc5acdf1.png" width="1968" height="1240" class="img_ev3q"></p>
<p>Termina el asistente y despliega.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-adjunta-la-elastic-ip-a-la-nueva-instancia">3. Adjunta la Elastic IP a la nueva instancia<a href="https://wec.wiline.com/docs/es/tutorials/netbird-mesh-vpn-openclaw/#3-adjunta-la-elastic-ip-a-la-nueva-instancia" class="hash-link" aria-label="Enlace directo al 3. Adjunta la Elastic IP a la nueva instancia" title="Enlace directo al 3. Adjunta la Elastic IP a la nueva instancia" translate="no">​</a></h3>
<p>La IP no queda ligada al momento de desplegar — la adjuntas una vez que la VM está arriba. Ve a
<strong>Networks → Elastic IPs → tu IP → ⋯ → Allocate</strong>, y selecciona tu nueva instancia
(<code>netbird-tutorial</code>). Solo aparecen en la lista las VMs de la <strong>misma VPC</strong>.</p>
<p><img decoding="async" loading="lazy" alt="Asignando la Elastic IP a la nueva VM" src="https://wec.wiline.com/docs/es/assets/images/netbird-eip-allocate-620e7b57084f55e9e8ca3323494cda6d.png" width="1834" height="1070" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-espera-al-primer-arranque-luego-verifica-que-está-arriba">4. Espera al primer arranque, luego verifica que está arriba<a href="https://wec.wiline.com/docs/es/tutorials/netbird-mesh-vpn-openclaw/#4-espera-al-primer-arranque-luego-verifica-que-est%C3%A1-arriba" class="hash-link" aria-label="Enlace directo al 4. Espera al primer arranque, luego verifica que está arriba" title="Enlace directo al 4. Espera al primer arranque, luego verifica que está arriba" translate="no">​</a></h3>
<p>La instancia muestra <strong>Running</strong> en <strong>Compute → Instances</strong> en menos de un minuto — pero la
plantilla sigue trabajando unos minutos más después de eso, descargando e iniciando los
contenedores de NetBird y levantando el dashboard. No esperes que la URL responda
de inmediato.</p>
<p>Comprueba desde tu <strong>laptop</strong> por HTTPS (no con <code>ping</code> — ICMP está bloqueado por firewall, así que un
ping fallido no significa que esté caído):</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl -skS -m 10 -o /dev/null -w "%{http_code}\n" https://&lt;your-ip&gt;.apps.wiline.cloud</span><br></div></code></pre></div></div>
<ul>
<li class=""><code>000</code> / connection refused → aún aprovisionando; espera un minuto y reintenta.</li>
<li class=""><code>301</code> luego <code>200</code> → está arriba (HTTP redirige a HTTPS).</li>
</ul>
<p>Para verlo directamente, entra por SSH (<code>ssh ubuntu@&lt;your-ip&gt;</code>) y confirma que los contenedores están
corriendo y que los puertos están ligados:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">cloud-init status</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">sudo docker ps</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">sudo ss -tlnp | grep -E ':(80|443)'</span><br></div></code></pre></div></div>
<p>Está listo una vez que <strong><code>netbird-dashboard</code>, <code>netbird-traefik</code> y <code>netbird-server</code></strong> muestran
<strong>Up</strong> y <strong>80/443</strong> están escuchando.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-abre-el-dashboard-y-crea-tu-cuenta">5. Abre el dashboard y crea tu cuenta<a href="https://wec.wiline.com/docs/es/tutorials/netbird-mesh-vpn-openclaw/#5-abre-el-dashboard-y-crea-tu-cuenta" class="hash-link" aria-label="Enlace directo al 5. Abre el dashboard y crea tu cuenta" title="Enlace directo al 5. Abre el dashboard y crea tu cuenta" translate="no">​</a></h3>
<p>Abre <strong><code>https://&lt;your-ip&gt;.apps.wiline.cloud</code></strong> — haz clic a través de la advertencia del
certificado si aparece (el mismo click-through de certificado autofirmado que el
<a class="" href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/">Artículo 2</a>).</p>
<p>Este es <strong>tu propio</strong> control plane de NetBird, corriendo en tu Instancia WEC. En la
primera visita te pide que <strong>crees la cuenta de admin</strong> — introduce un <strong>nombre, email
y contraseña</strong>, haz clic en <strong>Create</strong>, luego <strong>inicia sesión</strong> con esas mismas credenciales.
(Esa pantalla de configuración aparece solo una vez; después el dashboard muestra una
página normal de <strong>Sign in</strong>.)</p>
<p>Llegas al dashboard de NetBird — tu control plane privado en tu propio dominio:</p>
<p><img decoding="async" loading="lazy" alt="El dashboard de NetBird autoalojado" src="https://wec.wiline.com/docs/es/assets/images/netbird-dashboard-c98eca329c445df75fee98ca55f230e9.png" width="2850" height="1646" class="img_ev3q"></p>
<p>Haz clic en <strong>Add Peer</strong> y NetBird te entrega el comando de inscripción para <em>tu</em> servidor —
fíjate en el <code>--management-url</code> apuntando a tu propio dominio:</p>
<p><img decoding="async" loading="lazy" alt="Add Peer en el dashboard autoalojado — fíjate en el flag --management-url" src="https://wec.wiline.com/docs/es/assets/images/netbird-add-peer-09fd78007bcc0279cae91217c1786221.png" width="2827" height="1599" class="img_ev3q"></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl -fsSL https://pkgs.netbird.io/install.sh | sh</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">netbird up --management-url https://&lt;your-ip&gt;.apps.wiline.cloud</span><br></div></code></pre></div></div>
<p>(Ese segundo comando abre un login SSO en el navegador. Para un servidor <strong>headless</strong>, añade
<code>--setup-key &lt;KEY&gt;</code> desde la página <strong>Setup Keys</strong> de tu dashboard — igual que el
flujo gestionado, solo con <code>--management-url</code> añadido.)</p>
<p>Este es el dashboard donde creas las setup keys que inscriben máquinas — que es
exactamente lo que hacemos a continuación.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ahora-inscribe-tus-máquinas">Ahora inscribe tus máquinas<a href="https://wec.wiline.com/docs/es/tutorials/netbird-mesh-vpn-openclaw/#ahora-inscribe-tus-m%C3%A1quinas" class="hash-link" aria-label="Enlace directo al Ahora inscribe tus máquinas" title="Enlace directo al Ahora inscribe tus máquinas" translate="no">​</a></h2>
<p>Ya tienes un control plane (tu propio dashboard de la plantilla, o NetBird gestionado). Ahora pon
las dos máquinas en la mesh — la <strong>máquina de OpenClaw</strong> de las Partes 1–3 y tu
<strong>laptop</strong> — y luego cierra los puertos públicos de la máquina. Estos pasos son los mismos sin
importar qué control plane hayas elegido.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>De dónde viene tu setup key</div><div class="admonitionContent_BuS1"><p>Los comandos de abajo usan <strong>NetBird gestionado</strong> (<code>app.netbird.io</code>) como ejemplo. Si
desplegaste la <strong>plantilla de WEC</strong> en su lugar, es idéntico excepto que: creas la setup key
en <strong>tu propio</strong> dashboard (<code>https://&lt;your-ip&gt;.apps.wiline.cloud</code>) y añades
<strong><code>--management-url https://&lt;your-ip&gt;.apps.wiline.cloud</code></strong> a <code>netbird up</code>, para que el
agente apunte a <em>tu</em> servidor en lugar de a la nube gestionada.</p></div></div>
<p><strong>Requisitos previos:</strong> OpenClaw corriendo detrás de Caddy desde los
<a class="" href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/">Artículos 1–3</a> (alcanzable en
<code>https://openclaw.local</code>), un control plane del paso anterior, y acceso por shell a
la máquina además de admin en tu laptop.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-1--instala-el-agente-de-netbird-en-la-máquina-de-openclaw">Paso 1 — Instala el agente de NetBird en la máquina de OpenClaw<a href="https://wec.wiline.com/docs/es/tutorials/netbird-mesh-vpn-openclaw/#paso-1--instala-el-agente-de-netbird-en-la-m%C3%A1quina-de-openclaw" class="hash-link" aria-label="Enlace directo al Paso 1 — Instala el agente de NetBird en la máquina de OpenClaw" title="Enlace directo al Paso 1 — Instala el agente de NetBird en la máquina de OpenClaw" translate="no">​</a></h2>
<p>Esta es la máquina de las Partes 1–3 (una Instancia WEC) — la estamos inscribiendo como <strong>peer</strong>,
lo cual es independiente de cualquier servidor de control plane. NetBird trae un instalador de una línea:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl -fsSL https://pkgs.netbird.io/install.sh | sh</span><br></div></code></pre></div></div>
<p><strong>Trampa (real):</strong> en Ubuntu la instalación dispara el diálogo <code>needrestart</code> —
una caja azul de <em>"Which services should be restarted?"</em>. El <code>Tab</code> entre botones puede
ser tragado por algunas configuraciones de SSH/terminal; usa las <strong>flechas</strong> para moverte, <strong>Espacio</strong> para
alternar, <strong>Enter</strong> para confirmar (<code>&lt;Ok&gt;</code> es el predeterminado). El paquete ya está en
su lugar para cuando aparece esta caja, así que aunque salgas con <code>Ctrl+C</code>, el agente
queda instalado — volver a ejecutar el instalador solo te lo dice:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">NetBird seems to be installed already, please remove it before proceeding</span><br></div></code></pre></div></div>
<p>(Si vuelves a ejecutar el instalador después de este tutorial, verás en cambio
<code>NetBird service is running, please stop it before proceeding</code> — el agente ya
está arriba, así que no hay nada que reinstalar.)</p>
<p>Confirma la versión:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">netbird version</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># 0.73.2</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-2--crea-una-setup-key-y-une-la-máquina-de-openclaw">Paso 2 — Crea una setup key y une la máquina de OpenClaw<a href="https://wec.wiline.com/docs/es/tutorials/netbird-mesh-vpn-openclaw/#paso-2--crea-una-setup-key-y-une-la-m%C3%A1quina-de-openclaw" class="hash-link" aria-label="Enlace directo al Paso 2 — Crea una setup key y une la máquina de OpenClaw" title="Enlace directo al Paso 2 — Crea una setup key y une la máquina de OpenClaw" translate="no">​</a></h2>
<p>Una <strong>setup key</strong> es el token que inscribe un dispositivo en tu mesh — ideal para un
servidor headless (sin necesidad de SSO en navegador).</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>¿Primera vez en el dashboard? Evita los desvíos</div><div class="admonitionContent_BuS1"><p>Algunas cosas intentarán mandarte por el camino equivocado en una cuenta nueva — aquí está la
línea recta:</p><ul>
<li class=""><strong>¿Aún sin cuenta?</strong> Crea una primero (inicia sesión con Google/GitHub/email). No puedes generar una key hasta que estés dentro.</li>
<li class=""><strong>El asistente de onboarding</strong> ("Add your first resource" — <em>Single IP / Entire Subnet / Domain</em>) aparece de inmediato. <strong>Sáltalo.</strong> Los resources son para exponer subredes/dominios; nosotros solo necesitamos dos peers simples, así que no hace falta.</li>
<li class=""><strong>El botón naranja "Add Peer"</strong> abre el <strong>flujo de instalación SSO</strong> (descargar la app, registrarse con email, conectar desde la bandeja). Eso está pensado para un escritorio con navegador — <strong>no</strong> para un servidor headless. No lo sigas.</li>
<li class="">En su lugar, ve directo a la página <strong>Setup Keys</strong>: <strong><code>https://app.netbird.io/setup-keys</code></strong>.</li>
</ul></div></div>
<ol>
<li class="">Abre <strong><code>https://app.netbird.io/setup-keys</code></strong>.</li>
<li class=""><strong>Create Setup Key</strong> → nombre <code>openclaw-tutorial</code>, activa <strong>Reusable</strong> (para que sirva también para la laptop), por lo demás los valores predeterminados.</li>
<li class="">Copia la key.</li>
</ol>
<p><img decoding="async" loading="lazy" alt="Creando una setup key reutilizable en el dashboard de NetBird" src="https://wec.wiline.com/docs/es/assets/images/netbird-setup-key-ade9af748bded2998a18890a1ebde93b.png" width="1898" height="950" class="img_ev3q"></p>
<p>Conecta la VM (reemplaza con tu key):</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">sudo netbird up --setup-key &lt;YOUR_SETUP_KEY&gt;</span><br></div></code></pre></div></div>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>Trata la setup key como un secreto</div><div class="admonitionContent_BuS1"><p>Cualquiera con una key reutilizable puede añadir dispositivos a tu red. No la pegues en
chats ni commits, y <strong>elimínala</strong> del dashboard una vez que tus dispositivos estén
inscritos (los peers existentes siguen conectados).</p></div></div>
<p>Comprueba la conexión:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">netbird status</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="netbird status en la VM — Management &amp;amp; Signal Connected, IP de la mesh 100.87.239.229, Peers count 0/0" src="https://wec.wiline.com/docs/es/assets/images/netbird-vm-status-8e8088e406d7392203f1eb5da63b4258.png" width="731" height="374" class="img_ev3q"></p>
<p>Esa <strong><code>NetBird IP</code> (<code>100.87.239.229</code>)</strong> es la dirección a la que apunta todo lo de
abajo. <code>Peers count: 0/0</code> es lo esperado — nada más se ha unido todavía.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-3--une-tu-laptop">Paso 3 — Une tu laptop<a href="https://wec.wiline.com/docs/es/tutorials/netbird-mesh-vpn-openclaw/#paso-3--une-tu-laptop" class="hash-link" aria-label="Enlace directo al Paso 3 — Une tu laptop" title="Enlace directo al Paso 3 — Une tu laptop" translate="no">​</a></h2>
<p>Instala el cliente de NetBird en lo que estés usando, luego conéctate con la
<strong>misma setup key reutilizable</strong>. El comando de conexión (<code>netbird up --setup-key …</code>) es
idéntico en todos los SO — solo difiere la instalación.</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>Lo ejecutamos en macOS</div><div class="admonitionContent_BuS1"><p>La salida capturada de abajo es de un Mac. Las pestañas de Windows y Linux dan la
instalación equivalente; los pasos de <code>netbird up</code>, <code>netbird status</code> y ping son los
mismos en todas partes (Windows usa <code>ping -n</code> en lugar de <code>-c</code>).</p></div></div>
<div class="theme-tabs-container tabs-container tabList_zI5C"><ul role="tablist" aria-orientation="horizontal" class="tabs"><li role="tab" tabindex="0" aria-selected="true" class="tabs__item tabItem_yeRP tabs__item--active">macOS</li><li role="tab" tabindex="-1" aria-selected="false" class="tabs__item tabItem_yeRP">Windows</li><li role="tab" tabindex="-1" aria-selected="false" class="tabs__item tabItem_yeRP">Linux</li></ul><div class="margin-top--md"><div role="tabpanel" class="tabItem_yrzq"><p>Instala vía Homebrew, luego inicia el servicio y conéctate:</p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">brew install netbirdio/tap/netbird</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">sudo netbird service install</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">sudo netbird service start</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">netbird up --setup-key &lt;YOUR_SETUP_KEY&gt;</span><br></div></code></pre></div></div><p>Puede que veas una advertencia gRPC puntual mientras el socket del daemon aparece un instante después de
que el cliente lo llame — termina en <code>Connected</code>, así que es inofensiva:</p><div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">Salida</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">WARNING: ... dial unix /var/run/netbird.sock: connect: no such file or directory</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Connected</span><br></div></code></pre></div></div></div><div role="tabpanel" class="tabItem_yrzq" hidden=""><p>Descarga y ejecuta el <strong>instalador de NetBird para Windows</strong> desde
<a href="https://app.netbird.io/" target="_blank" rel="noopener noreferrer" class="">app.netbird.io</a> (Add Peer → Windows) o
<a href="https://pkgs.netbird.io/" target="_blank" rel="noopener noreferrer" class="">pkgs.netbird.io</a>. Instala una app de bandeja del sistema y la
CLI <code>netbird</code>. Luego, en <strong>PowerShell como Administrador</strong>, conéctate con tu key:</p><div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">netbird up --setup-key &lt;YOUR_SETUP_KEY&gt;</span><br></div></code></pre></div></div><p>(También puedes hacer clic en <strong>Connect</strong> desde el icono de la bandeja y autenticarte vía SSO, pero
la setup key es el mismo flujo de un solo paso que en las otras plataformas.)</p></div><div role="tabpanel" class="tabItem_yrzq" hidden=""><p>El mismo instalador de una línea que la VM, luego conéctate:</p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl -fsSL https://pkgs.netbird.io/install.sh | sh</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">sudo netbird up --setup-key &lt;YOUR_SETUP_KEY&gt;</span><br></div></code></pre></div></div></div></div></div>
<p>Verifica que los dos nodos se ven entre sí, y que el túnel lleva tráfico:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">netbird status</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">ping -c 3 100.87.239.229   # Windows: ping -n 3 100.87.239.229</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="netbird status en el Mac mostrando Peers count 1/1 Connected, más un ping exitoso a la IP de la mesh de la VM" src="https://wec.wiline.com/docs/es/assets/images/netbird-mac-status-857d291924930e23af4e6550b1845ec7.png" width="609" height="475" class="img_ev3q"></p>
<p><code>Peers count: 1/1</code> — ese peer es la VM. En el dashboard, ambas máquinas ahora
aparecen bajo <strong>Peers</strong> (como las inscribimos con una setup key en lugar de un
login SSO, aparecen bajo <strong>Servers</strong> en vez de <strong>User Devices</strong>):</p>
<p><img decoding="async" loading="lazy" alt="La VM y la laptop ambas conectadas como peers en el dashboard de NetBird" src="https://wec.wiline.com/docs/es/assets/images/netbird-peers-a54440db77e8715febacc8d410c24da5.png" width="1895" height="951" class="img_ev3q"></p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>¿Por qué ~200 ms?</div><div class="admonitionContent_BuS1"><p>Esta ruta pasa por un <strong>relay</strong> de NetBird (fíjate en <code>Interface type: Userspace</code>
en el Mac). Está bien para una UI web, y NetBird a menudo mejorará a un enlace
peer-to-peer directo cuando las redes lo permitan. La latencia hacia un peer con relay no es una
señal de alarma.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-4--reapunta-openclawlocal-a-la-ip-de-la-mesh">Paso 4 — Reapunta <code>openclaw.local</code> a la IP de la mesh<a href="https://wec.wiline.com/docs/es/tutorials/netbird-mesh-vpn-openclaw/#paso-4--reapunta-openclawlocal-a-la-ip-de-la-mesh" class="hash-link" aria-label="Enlace directo al paso-4--reapunta-openclawlocal-a-la-ip-de-la-mesh" title="Enlace directo al paso-4--reapunta-openclawlocal-a-la-ip-de-la-mesh" translate="no">​</a></h2>
<p>Desde el <a class="" href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/">Artículo 2</a>, tu archivo hosts mapea
<code>openclaw.local</code> a la IP de LAN de la VM (<code>10.80.4.212</code>). Cámbialo por la IP de la mesh
(<code>100.87.239.229</code>). La ruta del archivo y el comando de edición difieren por SO:</p>
<div class="theme-tabs-container tabs-container tabList_zI5C"><ul role="tablist" aria-orientation="horizontal" class="tabs"><li role="tab" tabindex="0" aria-selected="true" class="tabs__item tabItem_yeRP tabs__item--active">macOS</li><li role="tab" tabindex="-1" aria-selected="false" class="tabs__item tabItem_yeRP">Linux</li><li role="tab" tabindex="-1" aria-selected="false" class="tabs__item tabItem_yeRP">Windows</li></ul><div class="margin-top--md"><div role="tabpanel" class="tabItem_yrzq"><p><code>sed</code> en macOS necesita el <code>-i ''</code> vacío:</p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">grep openclaw /etc/hosts</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># 10.80.4.212 openclaw.local</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">sudo sed -i '' 's/^10\.80\.4\.212 openclaw\.local/100.87.239.229 openclaw.local/' /etc/hosts</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">grep openclaw /etc/hosts</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># 100.87.239.229 openclaw.local</span><br></div></code></pre></div></div></div><div role="tabpanel" class="tabItem_yrzq" hidden=""><p>El <code>sed</code> de GNU acepta <code>-i</code> sin argumento:</p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">grep openclaw /etc/hosts</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">sudo sed -i 's/^10\.80\.4\.212 openclaw\.local/100.87.239.229 openclaw.local/' /etc/hosts</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">grep openclaw /etc/hosts</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># 100.87.239.229 openclaw.local</span><br></div></code></pre></div></div></div><div role="tabpanel" class="tabItem_yrzq" hidden=""><p>El archivo hosts está en <code>C:\Windows\System32\drivers\etc\hosts</code> y necesita admin.
En <strong>PowerShell como Administrador</strong>:</p><div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">$h = "$env:WINDIR\System32\drivers\etc\hosts"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">(Get-Content $h) -replace '^10\.80\.4\.212 openclaw\.local','100.87.239.229 openclaw.local' | Set-Content $h</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Select-String openclaw $h</span><br></div></code></pre></div></div><p>(O abre ese archivo en <strong>Notepad ejecutado como Administrador</strong> y cambia la IP a mano.)</p></div></div></div>
<p>Prueba sobre la mesh — <em>antes</em> de tocar ningún puerto:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl -kI https://openclaw.local/</span><br></div></code></pre></div></div>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">Salida</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">HTTP/2 200</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">...</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">via: 1.1 Caddy</span><br></div></code></pre></div></div>
<p><code>via: 1.1 Caddy</code> sobre la IP de la mesh — el chat ahora fluye a través de NetBird. Carga
<code>https://openclaw.local/chat?session=main</code> en el navegador para confirmar que se comporta
exactamente como antes. El cert TLS sigue coincidiendo porque el hostname no cambió;
solo se movió la IP detrás de él.</p>
<p><img decoding="async" loading="lazy" alt="El chat de OpenClaw cargando sobre la mesh en https://openclaw.local" src="https://wec.wiline.com/docs/es/assets/images/netbird-chat-mesh-409321edd81ea8d59656891246bc0558.png" width="1900" height="993" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-5--cierra-los-puertos-públicos">Paso 5 — Cierra los puertos públicos<a href="https://wec.wiline.com/docs/es/tutorials/netbird-mesh-vpn-openclaw/#paso-5--cierra-los-puertos-p%C3%BAblicos" class="hash-link" aria-label="Enlace directo al Paso 5 — Cierra los puertos públicos" title="Enlace directo al Paso 5 — Cierra los puertos públicos" translate="no">​</a></h2>
<p>Aquí está el estado que estamos arreglando. En la VM:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">sudo ss -tlnp | grep -E ':(80|443)'</span><br></div></code></pre></div></div>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">Salida — escuchando en 0.0.0.0 (todas las interfaces)</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">LISTEN 0 4096  0.0.0.0:443  ...  docker-proxy</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">LISTEN 0 4096  0.0.0.0:80   ...  docker-proxy</span><br></div></code></pre></div></div>
<p>Caddy escucha en <strong><code>0.0.0.0</code></strong> — todas las interfaces, incluida la LAN (y cualquier NAT
delante de ella).</p>
<div class="theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"></path></svg></span>La trampa de ufw</div><div class="admonitionContent_BuS1"><p>Tu primer instinto es <code>ufw deny 443</code>. <strong>No funcionará.</strong> Los puertos de Caddy son
publicados por <strong>Docker</strong>, que escribe sus propias reglas de iptables que evitan la cadena
<code>INPUT</code> de ufw. Añadirías reglas de ufw, te sentirías seguro, y el 443 seguiría abierto de par en par. En
esta máquina <code>ufw</code> estaba incluso <code>inactive</code> — y activarlo no habría cambiado nada
para los puertos publicados por Docker.</p></div></div>
<p>El arreglo fiable es <strong>ligar los puertos publicados a la IP de la mesh</strong> en lugar de a
<code>0.0.0.0</code>, para que Caddy solo escuche en <code>wt0</code>. Edita el servicio de Caddy en
<code>docker-compose.override.yml</code> (el override del Artículo 2):</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">cd /home/ubuntu/openclaw</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">cp docker-compose.override.yml docker-compose.override.yml.bak</span><br></div></code></pre></div></div>
<p>Cambia el bloque de ports de:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"80:80"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"443:443"</span><br></div></code></pre></div></div>
<p>para ligarlo a la IP de la mesh:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"100.87.239.229:80:80"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"100.87.239.229:443:443"</span><br></div></code></pre></div></div>
<p>Recrea solo Caddy y vuelve a comprobar:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">docker compose up -d caddy</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">sudo ss -tlnp | grep -E ':(80|443)'</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="ss tras el cambio — Caddy ahora escuchando solo en la IP de la mesh 100.87.239.229, ya no en 0.0.0.0" src="https://wec.wiline.com/docs/es/assets/images/netbird-ss-after-0172e6fc62bc667a141b1f6528526e73.png" width="745" height="103" class="img_ev3q"></p>
<p>Los puertos ya no están en <code>0.0.0.0</code> — viven solo en la interfaz de la mesh.</p>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>Orden de arranque</div><div class="admonitionContent_BuS1"><p>Como el bind apunta a la IP de la mesh, <strong>NetBird debe estar arriba antes de que Caddy inicie</strong>
(p. ej. tras un reinicio, <code>wt0</code> necesita su IP antes de que Docker se ligue a ella). La
política <code>restart: unless-stopped</code> ya cubre esto — Caddy reintenta hasta que la
interfaz exista — pero conviene saberlo si alguna vez ves a Caddy en crash-loop
justo después de arrancar.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-6--demuestra-el-bloqueo">Paso 6 — Demuestra el bloqueo<a href="https://wec.wiline.com/docs/es/tutorials/netbird-mesh-vpn-openclaw/#paso-6--demuestra-el-bloqueo" class="hash-link" aria-label="Enlace directo al Paso 6 — Demuestra el bloqueo" title="Enlace directo al Paso 6 — Demuestra el bloqueo" translate="no">​</a></h2>
<p>Ambas direcciones, de la ejecución real.</p>
<p><strong>Aún alcanzable sobre la mesh</strong> (laptop):</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl -kI https://openclaw.local/</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># HTTP/2 200 ... via: 1.1 Caddy</span><br></div></code></pre></div></div>
<p><strong>Ya no alcanzable en la LAN</strong> (VM, golpeando su propia IP de LAN):</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl -kI --connect-timeout 5 --resolve openclaw.local:443:10.80.4.212 https://openclaw.local/</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="En la VM, curl a la antigua IP de LAN ahora devuelve Connection refused" src="https://wec.wiline.com/docs/es/assets/images/netbird-refused-0944b25666489470ae7b961e2d6243c8.png" width="617" height="55" class="img_ev3q"></p>
<p>Ese es el resultado: <strong>la misma URL, alcanzable solo a través de tu mesh, rechazada
en todas las demás partes.</strong></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="limpieza">Limpieza<a href="https://wec.wiline.com/docs/es/tutorials/netbird-mesh-vpn-openclaw/#limpieza" class="hash-link" aria-label="Enlace directo al Limpieza" title="Enlace directo al Limpieza" translate="no">​</a></h2>
<p>Elimina la setup key reutilizable ahora que ambos dispositivos están inscritos —
<strong>Setup Keys → delete</strong> en el dashboard. Tus peers siguen conectados; solo estás
cerrando la puerta que usaste para añadirlos. Para quitar un dispositivo más adelante, elimínalo bajo
<strong>Peers → User Devices</strong> y ejecuta <code>netbird down</code> en esa máquina.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="solución-de-problemas-real">Solución de problemas (real)<a href="https://wec.wiline.com/docs/es/tutorials/netbird-mesh-vpn-openclaw/#soluci%C3%B3n-de-problemas-real" class="hash-link" aria-label="Enlace directo al Solución de problemas (real)" title="Enlace directo al Solución de problemas (real)" translate="no">​</a></h2>
<ul>
<li class=""><strong>El diálogo <code>needrestart</code> no acepta <code>Tab</code>.</strong> Usa las flechas / Espacio / Enter. El paquete ya está instalado para cuando aparece la caja.</li>
<li class=""><strong>Advertencia gRPC de <code>netbird.sock</code> en el primer <code>up</code>.</strong> El socket del daemon va un instante por detrás del cliente. Si termina en <code>Connected</code>, ignórala.</li>
<li class=""><strong>Las reglas de <code>ufw</code> no bloquean el gateway.</strong> Los puertos publicados por Docker evitan ufw — liga el puerto a una IP específica (como arriba) o usa la propia cadena <code>DOCKER-USER</code> de Docker.</li>
<li class=""><strong>Caddy en crash-loop tras reiniciar.</strong> Arrancó antes de que <code>wt0</code> tuviera la IP de la mesh. <code>restart: unless-stopped</code> lo recupera; o ordena Docker después de <code>netbird.service</code>.</li>
<li class=""><strong>Ping alto hacia un peer.</strong> Estás con relay, no directo — funcional, solo más lento. Revisa el <code>Interface type</code> y el detalle del peer en NetBird para el tipo de conexión.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="qué-sigue">Qué sigue<a href="https://wec.wiline.com/docs/es/tutorials/netbird-mesh-vpn-openclaw/#qu%C3%A9-sigue" class="hash-link" aria-label="Enlace directo al Qué sigue" title="Enlace directo al Qué sigue" translate="no">​</a></h2>
<p>Eso cierra la serie <strong>Self-hosting OpenClaw</strong>:</p>
<ol>
<li class=""><a class="" href="https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/">Desplegar OpenClaw en una Instancia WEC con Docker Compose</a></li>
<li class=""><a class="" href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/">Asegurar OpenClaw con un proxy inverso Caddy + HTTPS</a></li>
<li class=""><a class="" href="https://wec.wiline.com/docs/es/tutorials/add-telegram-channel-openclaw/">Añadir un canal de Telegram a OpenClaw</a></li>
<li class=""><strong>Haz OpenClaw privado con una VPN mesh de NetBird</strong> ← estás aquí</li>
</ol>
<p>Ahora tienes un agente autoalojado que está cifrado en tránsito, emparejado con tus
dispositivos, accesible desde tu móvil e invisible para la internet pública — todo en
una sola Instancia WEC pequeña.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="continúa-el-viaje">Continúa el viaje<a href="https://wec.wiline.com/docs/es/tutorials/netbird-mesh-vpn-openclaw/#contin%C3%BAa-el-viaje" class="hash-link" aria-label="Enlace directo al Continúa el viaje" title="Enlace directo al Continúa el viaje" translate="no">​</a></h3>
<p>¿Listo para un agente que <em>recuerda</em>? Empieza la serie <strong>Self-hosting Hermes</strong> —
<strong><a class="" href="https://wec.wiline.com/docs/es/tutorials/self-host-hermes-agent/">Autoaloja el Hermes Agent con memoria persistente</a></strong> —
desplegado en esta misma Instancia WEC, con contexto que sobrevive a los reinicios.</p>]]></content>
        <author>
            <name>Rafael Fernandes</name>
            <uri>https://www.linkedin.com/in/rafaelmacariofernandes/</uri>
        </author>
        <category label="ai" term="ai"/>
        <category label="self-hosting" term="self-hosting"/>
        <category label="openclaw" term="openclaw"/>
        <category label="netbird" term="netbird"/>
        <category label="vpn" term="vpn"/>
        <category label="mesh" term="mesh"/>
        <category label="security" term="security"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Añade un canal de Telegram a OpenClaw]]></title>
        <id>https://wec.wiline.com/docs/es/tutorials/add-telegram-channel-openclaw/</id>
        <link href="https://wec.wiline.com/docs/es/tutorials/add-telegram-channel-openclaw/"/>
        <updated>2026-06-23T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Habla con tu agente OpenClaw autoalojado desde el móvil. Crea un bot de Telegram, conéctalo, supera la barrera de emparejamiento y chatea — capturado de un despliegue real.]]></summary>
        <content type="html"><![CDATA[<div class="tutorialHero"><img class="tutorialHero__brand" src="https://wec.wiline.com/docs/es/assets/images/openclaw-wordmark-003352a1a7f02afc3bd877ae6f2dc175.png" alt="OpenClaw"><span class="tutorialHero__plus">+</span><svg viewBox="0 0 24 24" fill="#26A5E4" aria-label="Telegram" class="tutorialHero__docker"><path d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z"></path></svg></div>
<p>Ya <a class="" href="https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/">desplegaste OpenClaw</a> y lo
<a class="" href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/">aseguraste detrás de HTTPS</a>. Ahora hazlo
<em>usable</em> — habla con tu agente desde el móvil vía Telegram. Creamos un bot, lo
conectamos, superamos la barrera de emparejamiento de OpenClaw y obtenemos una
respuesta real. Cada comando y tropiezo de abajo proviene de un despliegue real.</p>
<!-- -->
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>Reproducibilidad</div><div class="admonitionContent_BuS1"><p>Continúa de las Partes 1–2 en el mismo Instancia WEC — OpenClaw <strong>2026.6.8</strong>.
Telegram usa <strong>polling</strong> (el gateway consulta la API de Telegram), así que no se
requiere ningún webhook público ni dominio — esto funciona en una máquina privada.</p></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="lo-que-vas-a-construir">Lo que vas a construir<a href="https://wec.wiline.com/docs/es/tutorials/add-telegram-channel-openclaw/#lo-que-vas-a-construir" class="hash-link" aria-label="Enlace directo al Lo que vas a construir" title="Enlace directo al Lo que vas a construir" translate="no">​</a></h2>
<p>Tu móvil habla con un bot de Telegram; el gateway de OpenClaw consulta a Telegram en
busca de mensajes, los procesa con tu modelo y responde — sin necesidad de puertos
entrantes.</p>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="requisitos-previos">Requisitos previos<a href="https://wec.wiline.com/docs/es/tutorials/add-telegram-channel-openclaw/#requisitos-previos" class="hash-link" aria-label="Enlace directo al Requisitos previos" title="Enlace directo al Requisitos previos" translate="no">​</a></h2>
<ul>
<li class=""><strong><a class="" href="https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/">Parte 1</a></strong> + <strong><a class="" href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/">Parte 2</a></strong> completas — OpenClaw en marcha en <code>~/openclaw</code></li>
<li class="">Una <strong>cuenta de Telegram</strong> y un <strong>cliente de Telegram con sesión iniciada</strong> — la app del móvil/escritorio, o <code>web.telegram.org</code>. (No puedes crear un bot desde la página web pública de <code>t.me</code>; mira la <a href="https://wec.wiline.com/docs/es/tutorials/add-telegram-channel-openclaw/#troubleshooting-real-errors" class="">Solución de problemas</a>.)</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-1--crear-un-bot-de-telegram">Paso 1 — Crear un bot de Telegram<a href="https://wec.wiline.com/docs/es/tutorials/add-telegram-channel-openclaw/#paso-1--crear-un-bot-de-telegram" class="hash-link" aria-label="Enlace directo al Paso 1 — Crear un bot de Telegram" title="Enlace directo al Paso 1 — Crear un bot de Telegram" translate="no">​</a></h2>
<p>En la app de Telegram, toca <strong>buscar</strong> y escribe <strong><code>BotFather</code></strong>, luego abre la cuenta
oficial — la que tiene el <strong>tilde azul</strong>.</p>
<p><img decoding="async" loading="lazy" alt="Buscando BotFather en Telegram" src="https://wec.wiline.com/docs/es/assets/images/tg-step1a-search-922bcf2cacaf250ac10c1a03546f240b.png" width="1100" height="2381" class="img_ev3q"></p>
<p>Toca <strong>Start</strong> — BotFather responde con los comandos que entiende (verás opciones
como <code>/newbot</code>, <code>/mybots</code>, <code>/token</code> y más). Envía <strong><code>/newbot</code></strong> y luego sigue sus dos
indicaciones:</p>
<ol>
<li class=""><strong>Name</strong> — un nombre para mostrar, p. ej. <code>My OpenClaw Agent</code></li>
<li class=""><strong>Username</strong> — debe ser único y terminar en <code>bot</code>, p. ej. <code>wiline_openclaw_bot</code></li>
</ol>
<p>BotFather confirma con <em>"Done! Congratulations on your new bot"</em> y te da el <strong>token</strong>
— la línea después de <code>Use this token to access the HTTP API:</code>, como
<code>1234567890:AA...</code>.</p>
<p><img decoding="async" loading="lazy" alt="BotFather emitiendo el token del bot" src="https://wec.wiline.com/docs/es/assets/images/tg-step1c-token-0bec47ec554ad66500f6d053bd0a4810.png" width="756" height="1316" class="img_ev3q"></p>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>El token del bot es una credencial</div><div class="admonitionContent_BuS1"><p>Cualquiera que lo tenga controla tu bot. <strong>Difumínalo en la captura de arriba</strong> y
haz <code>/revoke</code> en BotFather si alguna vez se filtra.</p></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-2--conectar-el-bot-a-openclaw">Paso 2 — Conectar el bot a OpenClaw<a href="https://wec.wiline.com/docs/es/tutorials/add-telegram-channel-openclaw/#paso-2--conectar-el-bot-a-openclaw" class="hash-link" aria-label="Enlace directo al Paso 2 — Conectar el bot a OpenClaw" title="Enlace directo al Paso 2 — Conectar el bot a OpenClaw" translate="no">​</a></h2>
<p>En la <strong>VM</strong>, dentro de <code>~/openclaw</code>, registra el canal con tu token:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">docker compose run --rm openclaw-cli channels add --channel telegram --token "&lt;your-bot-token&gt;"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># Added Telegram account "default".</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="channels add ejecutándose con éxito" src="data:image/png;base64,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" width="641" height="242" class="img_ev3q"></p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-3--activar-y-verificar">Paso 3 — Activar y verificar<a href="https://wec.wiline.com/docs/es/tutorials/add-telegram-channel-openclaw/#paso-3--activar-y-verificar" class="hash-link" aria-label="Enlace directo al Paso 3 — Activar y verificar" title="Enlace directo al Paso 3 — Activar y verificar" translate="no">​</a></h2>
<p>Reinicia el gateway para que tome el canal, luego comprueba el estado:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">docker compose restart openclaw-gateway</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">docker compose run --rm openclaw-cli channels status</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># - Telegram default: enabled, configured, running, mode:polling, token:config</span><br></div></code></pre></div></div>
<p><code>mode:polling</code> significa que el gateway está consultando activamente a Telegram — sin
puerto entrante ni webhook.</p>
<p><img decoding="async" loading="lazy" alt="channels status mostrando Telegram en marcha en modo polling" src="https://wec.wiline.com/docs/es/assets/images/tg-step3-status-6393f1690ec9b270312c2f5e3a25f137.png" width="642" height="392" class="img_ev3q"></p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-4--enviar-un-mensaje-al-bot-barrera-de-emparejamiento">Paso 4 — Enviar un mensaje al bot (barrera de emparejamiento)<a href="https://wec.wiline.com/docs/es/tutorials/add-telegram-channel-openclaw/#paso-4--enviar-un-mensaje-al-bot-barrera-de-emparejamiento" class="hash-link" aria-label="Enlace directo al Paso 4 — Enviar un mensaje al bot (barrera de emparejamiento)" title="Enlace directo al Paso 4 — Enviar un mensaje al bot (barrera de emparejamiento)" translate="no">​</a></h2>
<p>Abre tu bot (<code>t.me/&lt;your-bot-username&gt;</code>), toca <strong>Start</strong> y envía un mensaje.</p>
<p>OpenClaw trata a los remitentes desconocidos como no confiables — así que en lugar de
responder, el bot devuelve un <strong>código de emparejamiento</strong> y tu id de usuario de
Telegram. Es el mismo patrón de seguridad que el emparejamiento de dispositivos de la
Parte 2.</p>
<p><img decoding="async" loading="lazy" alt="El bot respondiendo con el código de emparejamiento" src="https://wec.wiline.com/docs/es/assets/images/tg-step4-pairing-94e777f1790fbeadab0cd42be384b80b.png" width="1100" height="2381" class="img_ev3q"></p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-5--aprobar-el-emparejamiento">Paso 5 — Aprobar el emparejamiento<a href="https://wec.wiline.com/docs/es/tutorials/add-telegram-channel-openclaw/#paso-5--aprobar-el-emparejamiento" class="hash-link" aria-label="Enlace directo al Paso 5 — Aprobar el emparejamiento" title="Enlace directo al Paso 5 — Aprobar el emparejamiento" translate="no">​</a></h2>
<p>En la <strong>VM</strong>, aprueba el código del mensaje del bot:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">docker compose run --rm openclaw-cli pairing approve telegram &lt;pairing-code&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># Approved telegram sender &lt;id&gt;.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># Command owner configured telegram:&lt;id&gt; (commands.ownerAllowFrom was empty).</span><br></div></code></pre></div></div>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>nota</div><div class="admonitionContent_BuS1"><p>Aprobar al primer usuario como <strong>command owner</strong> cambia una configuración que
requiere reiniciar el gateway — OpenClaw lo hace <strong>automáticamente</strong> (verás
<code>config change requires gateway restart</code> y luego el reinicio del proveedor de
Telegram en los logs). Espera unos segundos a que vuelva antes de enviar otro
mensaje.</p></div></div>
<p><img decoding="async" loading="lazy" alt="Aprobando el emparejamiento en la VM — &amp;quot;Approved telegram sender …&amp;quot;" src="data:image/png;base64,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" width="639" height="240" class="img_ev3q"></p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-6--chatear-con-tu-agente">Paso 6 — Chatear con tu agente<a href="https://wec.wiline.com/docs/es/tutorials/add-telegram-channel-openclaw/#paso-6--chatear-con-tu-agente" class="hash-link" aria-label="Enlace directo al Paso 6 — Chatear con tu agente" title="Enlace directo al Paso 6 — Chatear con tu agente" translate="no">​</a></h2>
<p>De vuelta en Telegram, simplemente háblale — no hace falta más configuración;
OpenClaw con tu modelo responde de forma conversacional desde el primer momento.
Prueba con algo como:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">In one sentence, what's the point of putting a reverse proxy in front of my agent?</span><br></div></code></pre></div></div>
<p>Responde con una respuesta real — toda la cadena está activa: tu móvil → Telegram →
gateway → OpenAI → respuesta. Tu agente autoalojado ya cabe en tu bolsillo. (Darle
herramientas y skills llega en guías posteriores; el chat conversacional funciona de
inmediato.)</p>
<p><img decoding="async" loading="lazy" alt="El agente respondiendo de forma conversacional por Telegram" src="https://wec.wiline.com/docs/es/assets/images/tg-step6-working-7acac4effbd1c03d0657534125e3ea3b.png" width="1100" height="2381" class="img_ev3q"></p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="solución-de-problemas-errores-reales">Solución de problemas (errores reales)<a href="https://wec.wiline.com/docs/es/tutorials/add-telegram-channel-openclaw/#soluci%C3%B3n-de-problemas-errores-reales" class="hash-link" aria-label="Enlace directo al Solución de problemas (errores reales)" title="Enlace directo al Solución de problemas (errores reales)" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-el-botón-start-de-botfather-no-hace-nada-en-un-navegador">1. El botón "Start" de BotFather no hace nada en un navegador<a href="https://wec.wiline.com/docs/es/tutorials/add-telegram-channel-openclaw/#1-el-bot%C3%B3n-start-de-botfather-no-hace-nada-en-un-navegador" class="hash-link" aria-label="Enlace directo al 1. El botón &quot;Start&quot; de BotFather no hace nada en un navegador" title="Enlace directo al 1. El botón &quot;Start&quot; de BotFather no hace nada en un navegador" translate="no">​</a></h3>
<p>Abrir <code>t.me/BotFather</code> en un navegador web normal muestra un botón <strong>Start</strong> que no
hace nada — esa página es solo la tarjeta pública del bot y necesita un <strong>cliente de
Telegram con sesión iniciada</strong> al que delegar. Usa la app de escritorio/móvil, o
inicia sesión en <code>web.telegram.org</code> (vincula un dispositivo de escritorio escaneando
el QR desde tu móvil: Settings → Devices → Link Desktop Device), y luego escríbele a
BotFather ahí.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-el-bot-devuelve-un-código-de-emparejamiento-en-lugar-de-responder">2. El bot devuelve un código de emparejamiento en lugar de responder<a href="https://wec.wiline.com/docs/es/tutorials/add-telegram-channel-openclaw/#2-el-bot-devuelve-un-c%C3%B3digo-de-emparejamiento-en-lugar-de-responder" class="hash-link" aria-label="Enlace directo al 2. El bot devuelve un código de emparejamiento en lugar de responder" title="Enlace directo al 2. El bot devuelve un código de emparejamiento en lugar de responder" translate="no">​</a></h3>
<p>No es un error — es la <strong>barrera de emparejamiento</strong>. Los remitentes desconocidos
deben aprobarse primero (Paso 5). Tras la aprobación, el bot chatea con normalidad.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-proxy-headers-detected-from-untrusted-address">3. <code>Proxy headers detected from untrusted address</code><a href="https://wec.wiline.com/docs/es/tutorials/add-telegram-channel-openclaw/#3-proxy-headers-detected-from-untrusted-address" class="hash-link" aria-label="Enlace directo al 3-proxy-headers-detected-from-untrusted-address" title="Enlace directo al 3-proxy-headers-detected-from-untrusted-address" translate="no">​</a></h3>
<p>Si seguiste la Parte 2, el gateway registra esto porque Caddy está por delante de él.
Es una advertencia, no un fallo. Para silenciarla y restaurar la detección correcta
del cliente, establece <code>gateway.trustedProxies</code> con la red de tu proxy.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="notas-de-seguridad-y-dimensionamiento">Notas de seguridad y dimensionamiento<a href="https://wec.wiline.com/docs/es/tutorials/add-telegram-channel-openclaw/#notas-de-seguridad-y-dimensionamiento" class="hash-link" aria-label="Enlace directo al Notas de seguridad y dimensionamiento" title="Enlace directo al Notas de seguridad y dimensionamiento" translate="no">​</a></h2>
<ul>
<li class=""><strong>El emparejamiento es la barrera</strong> — solo los usuarios de Telegram aprobados
llegan al agente; el primer usuario aprobado se convierte en command owner.</li>
<li class=""><strong>Polling, no webhooks</strong> — no se abre ningún puerto entrante, lo que mantiene
intacto el endurecimiento de la Parte 2.</li>
<li class=""><strong>Dimensionamiento</strong> — en una máquina de 2 vCPU el gateway registró
<code>event loop degraded</code> bajo carga. Para un bot con mucho tráfico, dale más CPU/RAM.</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="qué-sigue">Qué sigue<a href="https://wec.wiline.com/docs/es/tutorials/add-telegram-channel-openclaw/#qu%C3%A9-sigue" class="hash-link" aria-label="Enlace directo al Qué sigue" title="Enlace directo al Qué sigue" translate="no">​</a></h2>
<p>Terminaste la <strong>Parte 3</strong> de la serie <strong>Self-hosting OpenClaw</strong>. Sigue:</p>
<ul>
<li class=""><strong>Parte 4 — <a class="" href="https://wec.wiline.com/docs/es/tutorials/netbird-mesh-vpn-openclaw/">Haz OpenClaw privado con una VPN mesh NetBird</a></strong> <em>(siguiente)</em> — pon tu agente en una mesh privada y cierra los puertos públicos.</li>
</ul>
<p>Después empieza la serie <strong>Self-hosting Hermes</strong> — <strong><a class="" href="https://wec.wiline.com/docs/es/tutorials/self-host-hermes-agent/">Autoaloja el Hermes Agent con memoria persistente</a></strong> — para un agente cuyo contexto sobrevive a los reinicios.</p>
<p>Los archivos complementarios de la serie viven en el repo de manifiestos de WiLine <em>(enlace pendiente con el repo)</em>.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="desmontaje">Desmontaje<a href="https://wec.wiline.com/docs/es/tutorials/add-telegram-channel-openclaw/#desmontaje" class="hash-link" aria-label="Enlace directo al Desmontaje" title="Enlace directo al Desmontaje" translate="no">​</a></h2>
<p>Elimina el canal de Telegram (mantiene en marcha el resto de OpenClaw):</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">docker compose run --rm openclaw-cli channels remove --channel telegram</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">docker compose restart openclaw-gateway</span><br></div></code></pre></div></div>
<p>Y haz <code>/revoke</code> del token del bot en BotFather si ya terminaste con él.</p>]]></content>
        <author>
            <name>Rafael Fernandes</name>
            <uri>https://www.linkedin.com/in/rafaelmacariofernandes/</uri>
        </author>
        <category label="ai" term="ai"/>
        <category label="self-hosting" term="self-hosting"/>
        <category label="openclaw" term="openclaw"/>
        <category label="telegram" term="telegram"/>
        <category label="chatbot" term="chatbot"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Asegura OpenClaw con un reverse proxy Caddy + HTTPS]]></title>
        <id>https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/</id>
        <link href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/"/>
        <updated>2026-06-23T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Pon Caddy delante de OpenClaw para tener HTTPS de verdad y auth con emparejamiento de dispositivos, luego cierra los puertos del gateway para que el proxy sea la única entrada — con todos los detalles a tener en cuenta.]]></summary>
        <content type="html"><![CDATA[<div class="tutorialHero"><img class="tutorialHero__brand" src="https://wec.wiline.com/docs/es/assets/images/openclaw-wordmark-003352a1a7f02afc3bd877ae6f2dc175.png" alt="OpenClaw"><span class="tutorialHero__plus">+</span><svg viewBox="0 0 24 24" fill="#1F88C0" aria-label="Caddy" class="tutorialHero__docker"><path d="M11.094.47c-.842 0-1.696.092-2.552.288a11.37 11.37 0 0 0-4.87 2.423 10.632 10.632 0 0 0-2.36 2.826A10.132 10.132 0 0 0 .305 8.582c-.398 1.62-.4 3.336-.043 5.048.085.405.183.809.31 1.212a11.85 11.85 0 0 0 1.662 3.729 3.273 3.273 0 0 0-.086.427 3.323 3.323 0 0 0 2.848 3.71 3.279 3.279 0 0 0 1.947-.346c1.045.51 2.17.864 3.339 1.04a11.66 11.66 0 0 0 4.285-.155 11.566 11.566 0 0 0 4.936-2.485 10.643 10.643 0 0 0 2.352-2.894 11.164 11.164 0 0 0 1.356-4.424 11.214 11.214 0 0 0-.498-4.335c.175-.077.338-.175.486-.293a.444.444 89.992 0 0 .001 0c.402-.322.693-.794.777-1.342a2.146 2.146 0 0 0-1.79-2.434 2.115 2.115 0 0 0-1.205.171c-.038-.043-.078-.086-.113-.13a11.693 11.693 0 0 0-3.476-2.93 13.348 13.348 0 0 0-1.76-.81 13.55 13.55 0 0 0-2.06-.613A12.121 12.121 0 0 0 11.093.47Zm.714.328c.345-.004.688.01 1.028.042a9.892 9.892 0 0 1 2.743.639c.984.39 1.89.958 2.707 1.632.803.662 1.502 1.45 2.091 2.328.026.039.048.08.07.12a2.12 2.12 0 0 0-.435 2.646c-.158.114-.97.692-1.634 1.183-.414.308-.733.557-.733.557l.581.68s.296-.276.665-.638c.572-.562 1.229-1.233 1.395-1.403a2.122 2.122 0 0 0 1.907.677 11.229 11.229 0 0 1-.013 4.046 11.41 11.41 0 0 1-1.475 3.897 12.343 12.343 0 0 1-2.079 2.587c-1.19 1.125-2.633 2.022-4.306 2.531a10.826 10.826 0 0 1-3.973.484 11.04 11.04 0 0 1-3.057-.652 3.304 3.304 0 0 0 1.417-2.294 3.275 3.275 0 0 0-.294-1.842c.18-.162.403-.363.656-.6 1.015-.955 2.353-2.303 2.353-2.303l-.47-.599s-1.63.972-2.801 1.728c-.307.198-.573.378-.777.517a3.273 3.273 0 0 0-1.516-.611c-1.507-.198-2.927.672-3.487 2.017a10.323 10.323 0 0 1-.695-1.078A10.92 10.92 0 0 1 .728 14.8a10.35 10.35 0 0 1-.2-1.212c-.164-1.653.103-3.258.629-4.754a12.95 12.95 0 0 1 1.087-2.288c.57-.968 1.248-1.872 2.069-2.656A11.013 11.013 0 0 1 11.808.797Zm-.147 3.257a3.838 3.838 0 0 0-3.82 3.82v2.36h-.94c-.751 0-1.377.625-1.377 1.377v3.8h1.46v-3.718h9.354v6.264H10.02v1.46h6.4c.751 0 1.377-.625 1.377-1.377v-6.43c0-.751-.626-1.377-1.377-1.377h-.94v-2.36a3.838 3.838 0 0 0-3.82-3.819zm0 1.46a2.371 2.371 0 0 1 2.36 2.36v2.36H9.3v-2.36a2.372 2.372 0 0 1 2.36-2.36zm10.141.392a1.253 1.253 0 0 1 1.296 1.434c-.049.319-.217.59-.453.78-.266.213-.61.318-.968.264a1.253 1.253 0 0 1-1.045-1.42 1.255 1.255 0 0 1 1.17-1.058zM5.384 17.425a2.02 2.02 0 0 1 1.917 1.298c.116.3.159.628.114.967a2.015 2.015 0 0 1-2.249 1.728 2.016 2.016 0 0 1-1.727-2.25 2.017 2.017 0 0 1 1.945-1.743z"></path></svg></div>
<p>En el <a class="" href="https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/">Artículo 1</a> pusimos OpenClaw en
marcha — pero solo sobre HTTP plano, con el truco de <code>allowInsecureAuth</code>. Aquí
ponemos <a href="https://caddyserver.com/" target="_blank" rel="noopener noreferrer" class="">Caddy</a> por delante como reverse proxy: HTTPS de
verdad, auth con emparejamiento de dispositivos y los puertos crudos del gateway
cerrados para que el proxy sea la única vía de entrada. Cada comando y error que
aparece abajo es del despliegue real.</p>
<!-- -->
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>Reproducibilidad</div><div class="admonitionContent_BuS1"><p>Continúa desde el Artículo 1 en el mismo Instancia WEC — <strong>Ubuntu 22.04.5 LTS</strong>,
OpenClaw <strong>2026.6.8</strong>. Usamos <strong>Caddy 2</strong> (<code>caddy:2</code>). Como esta máquina no tiene
dominio público, usamos la <strong>CA interna</strong> de Caddy (<code>tls internal</code>) para HTTPS
autofirmado; la sección <a href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/#pasar-a-produccion" class="">Pasar a producción</a> muestra el cambio
de una línea a certificados reales de Let's Encrypt una vez que tengas un dominio.</p></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="lo-que-vas-a-construir">Lo que vas a construir<a href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/#lo-que-vas-a-construir" class="hash-link" aria-label="Enlace directo al Lo que vas a construir" title="Enlace directo al Lo que vas a construir" translate="no">​</a></h2>
<p>Caddy termina el HTTPS y hace de reverse proxy hacia el gateway de OpenClaw por la
red interna de Docker. El gateway deja de publicar sus propios puertos — el único
punto de entrada pasa a ser Caddy en <code>:443</code>.</p>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="requisitos-previos">Requisitos previos<a href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/#requisitos-previos" class="hash-link" aria-label="Enlace directo al Requisitos previos" title="Enlace directo al Requisitos previos" translate="no">​</a></h2>
<ul>
<li class=""><strong><a class="" href="https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/">Artículo 1</a> completado</strong> — OpenClaw corriendo en <code>~/openclaw</code></li>
<li class="">Acceso por shell a la VM</li>
<li class="">Un hostname para el gateway. Con un dominio público, úsalo (y obtén certificados
reales de Let's Encrypt). En una máquina privada, usamos <code>openclaw.local</code> + una
entrada en el archivo hosts.</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-1--confirmar-el-punto-de-partida">Paso 1 — Confirmar el punto de partida<a href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/#paso-1--confirmar-el-punto-de-partida" class="hash-link" aria-label="Enlace directo al Paso 1 — Confirmar el punto de partida" title="Enlace directo al Paso 1 — Confirmar el punto de partida" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">cd ~/openclaw</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">docker compose ps</span><br></div></code></pre></div></div>
<p>Deberías ver <code>openclaw-gateway</code> <strong>Up (healthy)</strong>, publicando <code>18789/18790/3978</code>.
Ese es nuestro estado de partida: funcionando, pero solo HTTP.</p>
<p><img decoding="async" loading="lazy" alt="docker compose ps mostrando el gateway en marcha" src="data:image/png;base64,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" width="567" height="163" class="img_ev3q"></p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-2--escribir-el-caddyfile">Paso 2 — Escribir el Caddyfile<a href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/#paso-2--escribir-el-caddyfile" class="hash-link" aria-label="Enlace directo al Paso 2 — Escribir el Caddyfile" title="Enlace directo al Paso 2 — Escribir el Caddyfile" translate="no">​</a></h2>
<p>La config de Caddy: servir HTTPS y hacer proxy de todo hacia el gateway. <code>tls internal</code> usa la propia CA local de Caddy — no hace falta dominio público.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">cat &gt; Caddyfile &lt;&lt;'EOF'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># Caddy selects its certificate by SNI (the hostname in the TLS handshake).</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># A bare IP sends no SNI, so we use a hostname. With a public domain, put your</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># domain here and drop `tls internal` for automatic Let's Encrypt.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">openclaw.local {</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    tls internal</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    reverse_proxy openclaw-gateway:18789</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></div></code></pre></div></div>
<p>El destino del proxy es <code>openclaw-gateway:18789</code> — Caddy alcanza el gateway por su
<strong>nombre de servicio de Compose</strong> a través de la red compartida de Docker.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-3--añadir-caddy-a-compose">Paso 3 — Añadir Caddy a Compose<a href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/#paso-3--a%C3%B1adir-caddy-a-compose" class="hash-link" aria-label="Enlace directo al Paso 3 — Añadir Caddy a Compose" title="Enlace directo al Paso 3 — Añadir Caddy a Compose" translate="no">​</a></h2>
<p><code>docker compose</code> fusiona automáticamente un <code>docker-compose.override.yml</code>, así que
añadimos un servicio <code>caddy</code> sin tocar el archivo compose oficial de OpenClaw:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">cat &gt; docker-compose.override.yml &lt;&lt;'EOF'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">services:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  caddy:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    image: caddy:2</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    restart: unless-stopped</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    ports:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      - "80:80"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      - "443:443"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    volumes:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      - ./Caddyfile:/etc/caddy/Caddyfile:ro</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      - caddy_data:/data</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      - caddy_config:/config</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">volumes:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  caddy_data:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  caddy_config:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></div></code></pre></div></div>
<p><code>caddy_data</code> persiste la CA local + los certificados entre reinicios.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-4--iniciar-caddy">Paso 4 — Iniciar Caddy<a href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/#paso-4--iniciar-caddy" class="hash-link" aria-label="Enlace directo al Paso 4 — Iniciar Caddy" title="Enlace directo al Paso 4 — Iniciar Caddy" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">docker compose up -d</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">docker compose ps</span><br></div></code></pre></div></div>
<p>Caddy arranca junto al gateway con <code>443</code> publicado, y genera su CA interna + un
certificado en la primera ejecución.</p>
<p><img decoding="async" loading="lazy" alt="Contenedor Caddy en marcha con 443 publicado" src="https://wec.wiline.com/docs/es/assets/images/caddy-step4-up-d53a3b63c4e346428a8e733249b2049a.png" width="654" height="359" class="img_ev3q"></p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>nota</div><div class="admonitionContent_BuS1"><p><code>docker compose up -d</code> también inicia el helper de un solo uso <code>openclaw-cli</code>; que
quede en reposo aquí es inofensivo — lo invocamos con <code>docker compose run --rm</code>
cuando hace falta.</p></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-5--verificar-https">Paso 5 — Verificar HTTPS<a href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/#paso-5--verificar-https" class="hash-link" aria-label="Enlace directo al Paso 5 — Verificar HTTPS" title="Enlace directo al Paso 5 — Verificar HTTPS" translate="no">​</a></h2>
<p>Consulta el endpoint de salud del gateway <strong>a través de Caddy</strong>. <code>--resolve</code> hace
que curl envíe el hostname (SNI) mientras apunta a la máquina:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl -k --resolve openclaw.local:443:127.0.0.1 https://openclaw.local/healthz</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># {"ok":true,"status":"live"}</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="healthz respondiendo sobre HTTPS vía Caddy" src="data:image/png;base64,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" width="882" height="41" class="img_ev3q"></p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>¿Te topaste con un <code>tlsv1 alert internal error</code>?</div><div class="admonitionContent_BuS1"><p>Eso pasa si usaste una <strong>IP pelada</strong> como dirección del sitio — mira
<a href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/#1-tlsv1-alert-internal-error" class="">Solución de problemas #1</a>. Usa un hostname.</p></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-6--volver-a-asegurar-el-gateway">Paso 6 — Volver a asegurar el gateway<a href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/#paso-6--volver-a-asegurar-el-gateway" class="hash-link" aria-label="Enlace directo al Paso 6 — Volver a asegurar el gateway" title="Enlace directo al Paso 6 — Volver a asegurar el gateway" translate="no">​</a></h2>
<p>Ahora el tráfico llega por <code>https://openclaw.local</code>. Dos cambios de config:</p>
<ol>
<li class="">Añade el <strong>origen</strong> HTTPS a <code>allowedOrigins</code> (la Control UI rechaza orígenes desconocidos).</li>
<li class="">Vuelve a <strong>desactivar <code>allowInsecureAuth</code></strong> — HTTPS es un contexto seguro, así que
el truco del Artículo 1 ya no hace falta.</li>
</ol>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">docker compose run --rm --no-deps --entrypoint node openclaw-gateway \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  dist/index.js config set --batch-json '[{"path":"gateway.controlUi.allowedOrigins","value":["https://openclaw.local"]},{"path":"gateway.controlUi.allowInsecureAuth","value":false}]'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">docker compose restart openclaw-gateway</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-7--emparejar-tu-navegador">Paso 7 — Emparejar tu navegador<a href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/#paso-7--emparejar-tu-navegador" class="hash-link" aria-label="Enlace directo al Paso 7 — Emparejar tu navegador" title="Enlace directo al Paso 7 — Emparejar tu navegador" translate="no">​</a></h2>
<p>Abre <strong><code>https://openclaw.local/</code></strong> en tu navegador (en una máquina privada, primero
mapea <code>10.80.4.212 openclaw.local</code> en el archivo hosts de tu máquina — <code>/etc/hosts</code>
en macOS/Linux, o <code>C:\Windows\System32\drivers\etc\hosts</code> editado como Administrador
en Windows).</p>
<p><strong>Primero te toparás con una advertencia de certificado</strong> — <em>"Tu conexión no es
privada"</em> (<code>NET::ERR_CERT_AUTHORITY_INVALID</code>). Esto es esperado con <code>tls internal</code>:
el certificado está firmado por la <strong>CA local</strong> de Caddy, en la que tu sistema
operativo no confía. Haz clic en <strong>Avanzado → Continuar a openclaw.local (no
seguro)</strong> para seguir. (Con un dominio real + Let's Encrypt, el certificado tiene
confianza pública y esta advertencia nunca aparece — mira
<a href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/#pasar-a-produccion" class="">Pasar a producción</a>.)</p>
<p><img decoding="async" loading="lazy" alt="Advertencia de certificado del navegador — clic en Avanzado, luego Continuar" src="https://wec.wiline.com/docs/es/assets/images/caddy-step7-cert-warning-f6015d4f4498bdd7f495c01e08e91cf5.png" width="920" height="744" class="img_ev3q"></p>
<p>Ahora la página carga — pero con <code>allowInsecureAuth</code> desactivado, el gateway exige
<strong>emparejamiento de dispositivo</strong> antes de que este navegador pueda usar la Control
UI. Ese es el flujo seguro haciendo su trabajo. La pantalla muestra un id de
solicitud; apruébalo en el host:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">docker compose run --rm openclaw-cli devices approve &lt;request-id-from-the-screen&gt;</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="Pantalla de emparejamiento de dispositivo requerido" src="https://wec.wiline.com/docs/es/assets/images/caddy-step7-pairing-95202988ec2160fdef5dcd2e33fce67f.png" width="1100" height="508" class="img_ev3q"></p>
<p>De vuelta en el navegador, haz clic en <strong>Connect</strong> otra vez — empareja y carga el
dashboard.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-8--cerrar-el-acceso-directo-el-endurecimiento-de-verdad">Paso 8 — Cerrar el acceso directo (el endurecimiento de verdad)<a href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/#paso-8--cerrar-el-acceso-directo-el-endurecimiento-de-verdad" class="hash-link" aria-label="Enlace directo al Paso 8 — Cerrar el acceso directo (el endurecimiento de verdad)" title="Enlace directo al Paso 8 — Cerrar el acceso directo (el endurecimiento de verdad)" translate="no">​</a></h2>
<p>El gateway todavía publica <code>18789</code> en el host, así que <code>http://&lt;vm-ip&gt;:18789</code>
<em>sigue siendo alcanzable en HTTP plano</em>, saltándose a Caddy. Ciérralo: deja de
publicar los puertos del gateway para que Caddy en <code>:443</code> sea la única puerta. Caddy
sigue alcanzándolo por la red interna.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">cat &gt; docker-compose.override.yml &lt;&lt;'EOF'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">services:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  openclaw-gateway:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    # Unpublish the gateway's host ports — Caddy reaches it internally, so the</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    # only entry point is HTTPS on :443.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    ports: !reset []</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  caddy:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    image: caddy:2</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    restart: unless-stopped</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    ports:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      - "80:80"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      - "443:443"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    volumes:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      - ./Caddyfile:/etc/caddy/Caddyfile:ro</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      - caddy_data:/data</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      - caddy_config:/config</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">volumes:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  caddy_data:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  caddy_config:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">docker compose up -d</span><br></div></code></pre></div></div>
<p><code>ports: !reset []</code> limpia los puertos que publicaba el compose base.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-9--comprobar-que-está-cerrado">Paso 9 — Comprobar que está cerrado<a href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/#paso-9--comprobar-que-est%C3%A1-cerrado" class="hash-link" aria-label="Enlace directo al Paso 9 — Comprobar que está cerrado" title="Enlace directo al Paso 9 — Comprobar que está cerrado" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl -s -m 5 http://&lt;vm-ip&gt;:18789/healthz ; echo " &lt;- direct HTTP (should FAIL)"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">curl -sk --resolve openclaw.local:443:127.0.0.1 https://openclaw.local/healthz ; echo " &lt;- via Caddy (should work)"</span><br></div></code></pre></div></div>
<p>El primero no devuelve nada (conexión rechazada) — la puerta insegura desapareció.
El segundo todavía devuelve <code>{"ok":true,"status":"live"}</code>.</p>
<p><img decoding="async" loading="lazy" alt="Antes/después: puerto directo rechazado, HTTPS funciona" src="data:image/png;base64,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" width="1089" height="107" class="img_ev3q"></p>
<p>Tu OpenClaw ahora es alcanzable <strong>solo</strong> por HTTPS, con dispositivo emparejado.</p>
<p><img decoding="async" loading="lazy" alt="Control UI de OpenClaw sobre HTTPS" src="https://wec.wiline.com/docs/es/assets/images/caddy-control-ui-https-814c25fc8dd38568d2226856190421ba.png" width="1100" height="663" class="img_ev3q"></p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="solución-de-problemas-errores-reales">Solución de problemas (errores reales)<a href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/#soluci%C3%B3n-de-problemas-errores-reales" class="hash-link" aria-label="Enlace directo al Solución de problemas (errores reales)" title="Enlace directo al Solución de problemas (errores reales)" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-tlsv1-alert-internal-error">1. <code>tlsv1 alert internal error</code><a href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/#1-tlsv1-alert-internal-error" class="hash-link" aria-label="Enlace directo al 1-tlsv1-alert-internal-error" title="Enlace directo al 1-tlsv1-alert-internal-error" translate="no">​</a></h3>
<p>Aparece cuando la dirección del sitio en el Caddyfile era una IP pelada
(<code>https://10.80.4.212</code>):</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl: (35) error:0A000438:SSL routines::tlsv1 alert internal error</span><br></div></code></pre></div></div>
<p>Los logs de Caddy mostraban que el certificado se obtuvo bien. <strong>Causa:</strong> TLS elige
un certificado por <strong>SNI</strong> (el hostname en el handshake), pero las conexiones a una
IP pelada no envían SNI — así que Caddy no puede emparejar un certificado y aborta.
<strong>Solución:</strong> usa un hostname como dirección del sitio (Paso 2). Un lector con un
dominio real nunca se topa con esto — el dominio <em>es</em> el SNI.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-emparejamiento-de-dispositivo-requerido">2. Emparejamiento de dispositivo requerido<a href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/#2-emparejamiento-de-dispositivo-requerido" class="hash-link" aria-label="Enlace directo al 2. Emparejamiento de dispositivo requerido" title="Enlace directo al 2. Emparejamiento de dispositivo requerido" translate="no">​</a></h3>
<p>En la primera conexión HTTPS tras desactivar <code>allowInsecureAuth</code>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">Device pairing required — This browser needs one-time approval from the Gateway</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">host before it can use the Control UI.</span><br></div></code></pre></div></div>
<p><strong>Causa:</strong> este es el flujo seguro <em>correcto</em> — con la auth insegura desactivada,
cada navegador nuevo debe ser aprobado. <strong>Solución:</strong> <code>docker compose run --rm openclaw-cli devices approve &lt;request-id&gt;</code>. (La CLI puede registrar <code>scope upgrade pending approval … using local fallback</code> pero igual completa la aprobación.)</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-neterr_cert_authority_invalid">3. <code>NET::ERR_CERT_AUTHORITY_INVALID</code><a href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/#3-neterr_cert_authority_invalid" class="hash-link" aria-label="Enlace directo al 3-neterr_cert_authority_invalid" title="Enlace directo al 3-neterr_cert_authority_invalid" translate="no">​</a></h3>
<p>Esperado con <code>tls internal</code> — el certificado está firmado por la CA local de Caddy,
en la que tu SO no confía. Haz clic para continuar en una máquina privada. Con un
dominio público + Let's Encrypt (abajo), el certificado tiene confianza y no hay
advertencia.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="pasar-a-producción">Pasar a producción<a href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/#pasar-a-producci%C3%B3n" class="hash-link" aria-label="Enlace directo al Pasar a producción" title="Enlace directo al Pasar a producción" translate="no">​</a></h2>
<p>Con un dominio real, cambia dos líneas en el Caddyfile — apúntalo a tu dominio y
quita <code>tls internal</code>:</p>
<div class="language-diff codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-diff codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">- openclaw.local {</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">-     tls internal</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">+ openclaw.example.com {</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      reverse_proxy openclaw-gateway:18789</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></div></code></pre></div></div>
<p>Luego apunta el <strong>registro A</strong> del DNS del dominio a la IP pública de la VM y abre
<strong>80 + 443</strong> a internet. Caddy aprovisiona y renueva automáticamente un certificado
de <strong>Let's Encrypt</strong> — con confianza, sin advertencia, sin <code>--resolve</code> ni
<code>/etc/hosts</code>. Actualiza <code>allowedOrigins</code> a <code>https://openclaw.example.com</code>.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="notas-de-seguridad">Notas de seguridad<a href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/#notas-de-seguridad" class="hash-link" aria-label="Enlace directo al Notas de seguridad" title="Enlace directo al Notas de seguridad" translate="no">​</a></h2>
<ul>
<li class=""><strong>Punto de entrada único</strong> — solo el <code>:443</code> de Caddy queda expuesto; el gateway no
tiene puertos en el host (Paso 8).</li>
<li class=""><strong>Emparejamiento de dispositivo</strong> — cada navegador necesita aprobación de una sola
vez (<code>openclaw devices</code>).</li>
<li class=""><strong>Lista de orígenes permitidos</strong> — la Control UI solo acepta <code>https://openclaw.local</code>.</li>
<li class=""><strong>Rota los secretos</strong> — regenera <code>OPENCLAW_GATEWAY_TOKEN</code> si alguna vez se mostró
(capturas de pantalla, pantallas compartidas).</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="qué-sigue">Qué sigue<a href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/#qu%C3%A9-sigue" class="hash-link" aria-label="Enlace directo al Qué sigue" title="Enlace directo al Qué sigue" translate="no">​</a></h2>
<p>Terminaste la <strong>Parte 2</strong> de la serie <strong>Self-hosting OpenClaw</strong>. Sigue:</p>
<ul>
<li class=""><strong>Parte 3 — <a class="" href="https://wec.wiline.com/docs/es/tutorials/add-telegram-channel-openclaw/">Añade un canal de Telegram</a></strong> <em>(siguiente)</em> — habla con tu agente, ya asegurado, desde el móvil.</li>
<li class=""><strong>Parte 4 — <a class="" href="https://wec.wiline.com/docs/es/tutorials/netbird-mesh-vpn-openclaw/">Haz OpenClaw privado con una VPN mesh NetBird</a></strong> — ponlo en una mesh privada y cierra los puertos públicos.</li>
</ul>
<p>Los archivos complementarios (<code>Caddyfile</code>, <code>docker-compose.override.yml</code>) viven en
el repo de manifiestos de WiLine <em>(enlace pendiente con el repo)</em>.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="desmontaje">Desmontaje<a href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/#desmontaje" class="hash-link" aria-label="Enlace directo al Desmontaje" title="Enlace directo al Desmontaje" translate="no">​</a></h2>
<p>Para eliminar solo Caddy y reabrir los puertos directos, borra
<code>docker-compose.override.yml</code> y <code>Caddyfile</code>, luego <code>docker compose up -d</code>. Para
detener todo:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">docker compose down</span><br></div></code></pre></div></div>]]></content>
        <author>
            <name>Rafael Fernandes</name>
            <uri>https://www.linkedin.com/in/rafaelmacariofernandes/</uri>
        </author>
        <category label="ai" term="ai"/>
        <category label="self-hosting" term="self-hosting"/>
        <category label="docker" term="docker"/>
        <category label="openclaw" term="openclaw"/>
        <category label="caddy" term="caddy"/>
        <category label="https" term="https"/>
        <category label="security" term="security"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Desplegar OpenClaw en una Instancia WEC con Docker Compose]]></title>
        <id>https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/</id>
        <link href="https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/"/>
        <updated>2026-06-18T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[De una Instancia WEC a un agente OpenClaw autoalojado que de verdad responde — Docker Compose, tu propia clave de modelo y cada error real con su solución de un despliegue en vivo.]]></summary>
        <content type="html"><![CDATA[<div class="tutorialHero"><img class="tutorialHero__brand" src="https://wec.wiline.com/docs/es/assets/images/openclaw-wordmark-003352a1a7f02afc3bd877ae6f2dc175.png" alt="OpenClaw"><span class="tutorialHero__plus">+</span><svg viewBox="0 0 24 24" fill="#2496ED" aria-label="Docker" class="tutorialHero__docker"><path d="M13.983 11.078h2.119a.186.186 0 00.186-.185V9.006a.186.186 0 00-.186-.186h-2.119a.185.185 0 00-.185.185v1.888c0 .102.083.185.185.185m-2.954-5.43h2.118a.186.186 0 00.186-.186V3.574a.186.186 0 00-.186-.185h-2.118a.185.185 0 00-.185.185v1.888c0 .102.082.185.185.185m0 2.716h2.118a.187.187 0 00.186-.186V6.29a.186.186 0 00-.186-.185h-2.118a.185.185 0 00-.185.185v1.887c0 .102.082.185.185.186m-2.93 0h2.12a.186.186 0 00.184-.186V6.29a.185.185 0 00-.185-.185H8.1a.185.185 0 00-.185.185v1.887c0 .102.083.185.185.186m-2.964 0h2.119a.186.186 0 00.185-.186V6.29a.185.185 0 00-.185-.185H5.136a.186.186 0 00-.186.185v1.887c0 .102.084.185.186.186m5.893 2.715h2.118a.186.186 0 00.186-.185V9.006a.186.186 0 00-.186-.186h-2.118a.185.185 0 00-.185.185v1.888c0 .102.082.185.185.185m-2.93 0h2.12a.185.185 0 00.184-.185V9.006a.185.185 0 00-.184-.186h-2.12a.185.185 0 00-.184.185v1.888c0 .102.083.185.185.185m-2.964 0h2.119a.185.185 0 00.185-.185V9.006a.185.185 0 00-.184-.186h-2.12a.186.186 0 00-.186.186v1.887c0 .102.084.185.186.185m-2.92 0h2.12a.185.185 0 00.184-.185V9.006a.185.185 0 00-.184-.186h-2.12a.185.185 0 00-.184.185v1.888c0 .102.082.185.185.185M23.763 9.89c-.065-.051-.672-.51-1.954-.51-.338.001-.676.03-1.01.087-.248-1.7-1.653-2.53-1.716-2.566l-.344-.199-.226.327c-.284.438-.49.922-.612 1.43-.23.97-.09 1.882.403 2.661-.595.332-1.55.413-1.744.42H.751a.751.751 0 00-.75.748 11.376 11.376 0 00.692 4.062c.545 1.428 1.355 2.48 2.41 3.124 1.18.723 3.1 1.137 5.275 1.137.983.003 1.963-.086 2.93-.266a12.248 12.248 0 003.823-1.389c.98-.567 1.86-1.288 2.61-2.136 1.252-1.418 1.998-2.997 2.553-4.4h.221c1.372 0 2.215-.549 2.68-1.009.309-.293.55-.65.707-1.046l.098-.288Z"></path></svg><span class="tutorialHero__plus">+</span><svg viewBox="0 0 24 24" fill="#0a0a0a" aria-label="OpenAI" class="tutorialHero__openai"><path d="M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997z"></path></svg></div>
<p>Despliega un agente de IA autoalojado de <a href="https://openclaw.ai/" target="_blank" rel="noopener noreferrer" class="">OpenClaw</a> en una Instancia WEC
con Docker Compose — desde levantar la VM hasta un agente que de verdad responde,
usando tu propia clave de API del modelo. Cada comando, versión y error que aparece
abajo fue capturado de un despliegue real en un Instancia WEC.</p>
<!-- -->
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>Reproducibilidad</div><div class="admonitionContent_BuS1"><p>Tú aportas tu propia clave de API del modelo (OpenAI en esta guía); WiLine se
encarga del alojamiento. Esto se reprodujo en un Instancia WEC — <strong>Ubuntu 22.04.5
LTS, 8 vCPU, 15 GiB de RAM, 25 GB de disco libre</strong>. Versión de OpenClaw
<strong>2026.6.8</strong>, imagen <code>ghcr.io/openclaw/openclaw:latest</code>.</p></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="lo-que-vas-a-construir">Lo que vas a construir<a href="https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/#lo-que-vas-a-construir" class="hash-link" aria-label="Enlace directo al Lo que vas a construir" title="Enlace directo al Lo que vas a construir" translate="no">​</a></h2>
<p>Un único contenedor <strong>gateway</strong> de OpenClaw, configurado con tu proveedor de
modelo, accesible a través de su Control UI web y de la CLI.</p>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="requisitos-previos">Requisitos previos<a href="https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/#requisitos-previos" class="hash-link" aria-label="Enlace directo al Requisitos previos" title="Enlace directo al Requisitos previos" translate="no">​</a></h2>
<ul>
<li class="">Una cuenta de WiLine Edge Cloud</li>
<li class="">Una clave de API de un modelo — esta guía usa <strong>OpenAI</strong>; Anthropic y los modelos locales también funcionan</li>
<li class="">Un par de claves SSH en tu máquina (<code>~/.ssh/id_ed25519</code> o similar)</li>
<li class="">Unos 20 minutos</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-1--aprovisionar-la-vm-en-wiline">Paso 1 — Aprovisionar la VM en WiLine<a href="https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/#paso-1--aprovisionar-la-vm-en-wiline" class="hash-link" aria-label="Enlace directo al Paso 1 — Aprovisionar la VM en WiLine" title="Enlace directo al Paso 1 — Aprovisionar la VM en WiLine" translate="no">​</a></h2>
<p>Levanta una VM de WiLine que cumpla estos requisitos:</p>
<table><thead><tr><th>Requisito</th><th>Valor</th></tr></thead><tbody><tr><td>SO</td><td>Ubuntu 22.04 LTS (o 24.04 LTS)</td></tr><tr><td>Cómputo</td><td>≥ 2 vCPU / 4 GB de RAM — OpenClaw necesita ≥ 2 GB; la primera ejecución puede morir por falta de memoria (exit 137) en hosts de 1 GB</td></tr><tr><td>Disco</td><td>30 GB NVMe — 10 GB se llenan rápido en cuanto llegan las imágenes de Docker y los logs</td></tr><tr><td>Acceso</td><td>Tu <strong>clave pública SSH</strong> añadida al desplegar, para poder entrar sin contraseña</td></tr></tbody></table>
<p>El recorrido por el portal ya está documentado — sigue estos pasos y vuelve:</p>
<ul>
<li class=""><strong><a class="" href="https://wec.wiline.com/docs/es/cloud_portal/platform/compute/instances/compute_instance/">Desplegar una máquina virtual →</a></strong></li>
<li class=""><strong><a class="" href="https://wec.wiline.com/docs/es/cloud_portal/platform/compute/ssh_keys/">Configurar una clave SSH →</a></strong></li>
</ul>
<p>Cuando esté en marcha, conéctate por SSH:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">ssh ubuntu@&lt;ip-de-tu-vm&gt;</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-2--verificar-la-máquina">Paso 2 — Verificar la máquina<a href="https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/#paso-2--verificar-la-m%C3%A1quina" class="hash-link" aria-label="Enlace directo al Paso 2 — Verificar la máquina" title="Enlace directo al Paso 2 — Verificar la máquina" translate="no">​</a></h2>
<p>Antes de instalar nada, confirma con qué estás trabajando — la versión del SO y los
recursos disponibles. Anota estos números; son útiles cuando luego compares
rendimiento o abras un ticket de soporte.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">lsb_release -a</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">nproc</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">free -h</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">df -h /</span><br></div></code></pre></div></div>
<p>En nuestra Instancia WEC: <code>Ubuntu 22.04.5 LTS</code>, <code>8</code> vCPU, ~<code>15 GiB</code> de RAM, <code>25 GB</code> de disco libre.</p>
<p><img decoding="async" loading="lazy" alt="Terminal mostrando la versión del SO, CPU, RAM y disco" src="https://wec.wiline.com/docs/es/assets/images/step2-box-specs-c821280774bf4f5ec9205092f6646004.png" width="584" height="316" class="img_ev3q"></p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-3--instalar-docker--compose">Paso 3 — Instalar Docker + Compose<a href="https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/#paso-3--instalar-docker--compose" class="hash-link" aria-label="Enlace directo al Paso 3 — Instalar Docker + Compose" title="Enlace directo al Paso 3 — Instalar Docker + Compose" translate="no">​</a></h2>
<p>Comprueba si Docker ya está presente:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">docker --version</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">docker compose version</span><br></div></code></pre></div></div>
<p>Si no está instalado, sigue la guía oficial de Docker para tu distribución —
<strong><a href="https://docs.docker.com/engine/install/ubuntu/" target="_blank" rel="noopener noreferrer" class="">Instalar Docker Engine en Ubuntu</a></strong>.
La vía más rápida es el
<a href="https://docs.docker.com/engine/install/#install-using-the-convenience-script" target="_blank" rel="noopener noreferrer" class="">script de conveniencia</a>
de Docker, seguido de añadir tu usuario al grupo <code>docker</code> para poder ejecutarlo sin
<code>sudo</code>:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl -fsSL https://get.docker.com | sh</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">sudo usermod -aG docker $USER</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">newgrp docker</span><br></div></code></pre></div></div>
<p>Necesitas <strong>Docker Compose v2</strong> (el subcomando <code>docker compose</code>, no el antiguo
binario <code>docker-compose</code>).</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>nota</div><div class="admonitionContent_BuS1"><p>Si Docker ya está presente, el script <code>get.docker.com</code> lo detecta y avisa en lugar
de reinstalar — no lo fuerces en una máquina con contenedores en ejecución. En una
VM limpia instala desde cero. Este despliegue se ejecutó en un host donde Docker
<strong>29.1.3 / Compose v5.0.2</strong> ya estaba instalado.</p></div></div>
<p><img decoding="async" loading="lazy" alt="Salida de docker --version y docker compose version" src="data:image/png;base64,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" width="582" height="234" class="img_ev3q"></p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-4--obtener-el-archivo-compose">Paso 4 — Obtener el archivo Compose<a href="https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/#paso-4--obtener-el-archivo-compose" class="hash-link" aria-label="Enlace directo al Paso 4 — Obtener el archivo Compose" title="Enlace directo al Paso 4 — Obtener el archivo Compose" translate="no">​</a></h2>
<p>Crea un directorio para el proyecto y descarga el <code>docker-compose.yml</code> oficial de
OpenClaw:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">mkdir openclaw &amp;&amp; cd openclaw</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">curl -fsSL https://raw.githubusercontent.com/openclaw/openclaw/main/docker-compose.yml -o docker-compose.yml</span><br></div></code></pre></div></div>
<p>El archivo define dos servicios — <code>openclaw-gateway</code> (el gateway del agente de larga
duración) y <code>openclaw-cli</code> (una CLI de un solo uso que comparte la red del gateway).
Por defecto <strong>compila desde el código fuente</strong>; en su lugar lo apuntaremos a la
imagen precompilada.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-5--configurar-env">Paso 5 — Configurar <code>.env</code><a href="https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/#paso-5--configurar-env" class="hash-link" aria-label="Enlace directo al paso-5--configurar-env" title="Enlace directo al paso-5--configurar-env" translate="no">​</a></h2>
<p>Crea un <code>.env</code> junto al archivo compose. Fija la imagen precompilada y establece un
token para el panel:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">OPENCLAW_IMAGE=ghcr.io/openclaw/openclaw:latest</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">OPENCLAW_GATEWAY_TOKEN=&lt;pega la salida de: openssl rand -hex 32&gt;</span><br></div></code></pre></div></div>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>Mantén <code>.env</code> fuera de git</div><div class="admonitionContent_BuS1"><p>Contiene tu token del gateway (y cualquier clave). Añádelo a <code>.gitignore</code>. En su
lugar, sube un <code>.env.example</code> con valores de marcador.</p></div></div>
<p>Descarga la imagen:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">docker compose pull</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="docker compose pull terminando con la línea Pulled" src="data:image/png;base64,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" width="583" height="176" class="img_ev3q"></p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-6--crear-de-antemano-el-directorio-de-configuración">Paso 6 — Crear de antemano el directorio de configuración<a href="https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/#paso-6--crear-de-antemano-el-directorio-de-configuraci%C3%B3n" class="hash-link" aria-label="Enlace directo al Paso 6 — Crear de antemano el directorio de configuración" title="Enlace directo al Paso 6 — Crear de antemano el directorio de configuración" translate="no">​</a></h2>
<p>OpenClaw monta <code>~/.openclaw</code> dentro del contenedor mediante bind-mount. Créalo
<strong>primero con tu propio usuario</strong> — de lo contrario Docker lo crea como <code>root</code> y el
contenedor (que se ejecuta como el usuario <code>node</code>, uid 1000) no puede escribir en él:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">mkdir -p ~/.openclaw ~/.openclaw-auth-profile-secrets</span><br></div></code></pre></div></div>
<p>Si te lo saltas, te toparás con un error de permisos en la primera ejecución — mira
la <a href="https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/#soluci%C3%B3n-de-problemas-errores-reales" class="">Solución de problemas</a>.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-7--onboarding">Paso 7 — Onboarding<a href="https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/#paso-7--onboarding" class="hash-link" aria-label="Enlace directo al Paso 7 — Onboarding" title="Enlace directo al Paso 7 — Onboarding" translate="no">​</a></h2>
<p>Aquí es donde aportas tu propio modelo — el onboarding te pide tu proveedor y tu
clave de API. Esta guía usa <strong>OpenAI</strong>, pero Anthropic y los modelos locales
funcionan igual.</p>
<p>Ejecuta el onboarding de OpenClaw a través del contenedor gateway. Es interactivo —
te pregunta tu proveedor y clave de API y escribe la configuración:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">docker compose run --rm --no-deps --entrypoint node openclaw-gateway \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  dist/index.js onboard --mode local --no-install-daemon</span><br></div></code></pre></div></div>
<p>Avanza por las preguntas:</p>
<ul>
<li class=""><strong>Continuar</strong> tras el aviso de seguridad de uso personal → <strong>Yes</strong></li>
<li class=""><strong>Setup mode</strong> → <strong>QuickStart</strong></li>
<li class=""><strong>Provider</strong> → <strong>OpenAI</strong> (o tu proveedor), luego pega tu <strong>clave de API</strong></li>
<li class=""><strong>Default model</strong> → mantén el sugerido (<code>openai/gpt-5.5</code>)</li>
<li class=""><strong>Channel</strong> → busca <code>skip</code> → <strong>Skip for now</strong> (los canales de mensajería son una
guía posterior)</li>
<li class=""><strong>Skills / Hooks</strong> → <strong>No / Skip for now</strong> (configúralos luego con
<code>openclaw configure</code>)</li>
<li class=""><strong>Hatch your agent</strong> → <strong>Hatch later</strong> (a continuación iniciamos el gateway como servicio)</li>
</ul>
<p>Termina con <code>Onboarding complete.</code></p>
<p><img decoding="async" loading="lazy" alt="Onboarding — seleccionando el proveedor OpenAI" src="data:image/png;base64,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" width="626" height="391" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" alt="Onboarding — &amp;quot;Model configured: openai/gpt-5.5&amp;quot;" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAnMAAAGICAMAAAAd05Z7AAAAWlBMVEURIEAtN1JcYXCanKUAAABFQURMU2o7OjoiLkt3eYjb29xkaXv/elN5eHitra8BDSQbJkLCw8jCYE2Dh5VyQEc+Rl5XVVWYUUrocFGNjIz5uC4qxD8yXyRDfBN3msLgAAAACXBIWXMAAAsTAAALEwEAmpwYAAAc90lEQVR42u2dCYOquBKFIbkgi9IBRHRm3v//m6+qUkGCaHt7sRfPmbmNZKmE8FlJ0O5K2rbtk5c/L6beQdAnyhw2xFlCSofk5eXlcDhkGwj6RGXZwRhijal7edkcDhgS6AE6mI1AR8jBx0EPcnY0wbKbA3LQA6Ej5v5gYoUeOb0e2M1hHKBHQpe8FGAO+oxJ1O9V15j7M0seu+22G+MS4z+kRdp//5L++9we73af9C47L2YXL+7Vbre+/j3sdoeLJHNnr24uqc3u8NXDds818DM4+bmTE8Mdl57HI5Mlf6ZRyYg4Vje7/uwf1XxM/lV9KnNNc+3KTDzItrEns87GSlrTNJyc2aaxh/kL4zPukG0OV3p8kbG7z6ahTpwy370126fm8N5he4RjM4wY/TRC24HemjtKEvKiN82MOUbuSP9tu3P2P5MukftU6K4OnmnsnEBrdwezfmubJrtEzp6axjA39MJm5xen5r3MHaKO/Q1zByLONifhf9W2bTY/gDnxAGZyzYaBM/zG5xfrfm4U5ERjNLF6jdHE6vUJ0+vBnqiT9kCDtyOeaLwJCEMvTpbPMyN+aWeZGyu5B0P/072lQifLLutAB/IaXHDHTIoZrn7aMG8MrfBxokrTi+Z0mvjY+XYPbJEaO3lHqFlkZ+LCG2VPu9v4jmUED5emo+X3+WTTcL0sZLOZ02SVS9H18lUIc8as0MytGN8kXx23F66LW9J8Yc53iMdoJx174HQ7wSVOjyfbjFd25oqf6wS52NH988+lo/v33890dAqB4ZnKsuvhO2zICVg5Nwd2TJneJco48GzZEGY8L1kqkZG3oKNlz3Uyh7MZug9yR/jHjgyK1enFYc6HUrmTut5ayGnmvkiMGu87jXRM3GbDHosYtjOb0ruQbXxXz2bE8xpaWXPiYemg+R0VPLQfBu4fvSf1ujj9JPkHPyi+QzJo3AfzFY6OLvmQhZXvNebEyem/b8EcY2BnzBkhRObWwNzOsqc7sYuQwlzKFzhlMrcezmZ2YfprGNSdR2t6ofPgTpYeVHMn99k3xy35pbF3jWxSyonR7JAdOF2s0MfWB/FYzeGgVnxN6V3IPqljm5o7eGe78S91IcoOzWTmINT46zhJk2yZih+MXJcfFs43Jx620KGMlgz0/iDOH/nIf6KL/RxPr9lN5ibivglzstCZMeePS+bEWdFkq8w0u8m3KHPepkyIN/zcZLORm2hs4z2f9Me7JumMeKSDlhOjB/Z8gbmdnHjnSyV2kt5Ii7vNlH1hxqiz28zXc9bakJ/F12Eb3+h5eA66jpO3h3aILNJkbVaWmQ9wdNN6ztxirvNTK//70rm18fOMvHFnb+QL5g5CAr3hef1td8HP7XyB3eTnssld+lvK+SYs46YX6ucO8gUburnnVjxzGedsJgflyykAu8nPUZ7xKzNL36Lw1PqaYj1kn3gyP5sJ67nAXDY91ZEnDpnvQqZvPSmb8duSWwjDIhe7k7k1dIguu9k0ttk9kjmGTfathnqj+9brzOke4vi1ewgaNFmN6HGnC5Uzc7yQyYzs9IgEek1rZVn063qOh13WUryY4QnOm/HM0W3Y2Wnf2mSzF9EeU5aAEXPTeu4ULcSEudOuOTO3k2WVZX/r59bZDjZkH7yTOiz2rcpctvBM4obpina6nuN9CLfGG5xpWE5yZcqc7xCb3fht+kMd3eXzuavMybMSQe7uZyWfsVDw6DBzOx4zmSlOM+b8M6yTZPPJ6bz7O/mt3UEfuRl+wyux4QHH9edzEXM7wfCSOc442Zg52dVMc6v0/uD7YDYL5s7Zu5OZPXKZns+tMuernhrZp1q+br/1lTrTsPiHPTq3Soc2jTi75ks/h1jL+YbPhLNXzjfzD1XkEYWdnjgsq1w+NDssDKx+PPOODmc3u+1Td/xQMYLh1ucQ4Ylwtng4eH2cvvn3hGbM3f3ZV/aAz77+5muA5nD1+evhsSvoux9CNvc/gt5EcNrm5388HjG3+dlfQV27G+Z73iKjDxHuKnuIrjIDcxAE5iAwB0FgDgJzEJiDIDAH/SrmNn8g6JFK/uQJBH2u0mIuMAd9HXMZhiYWBgTMgTkwB+agz2Mu7937my2LDygylGUN5n4xc3lVtQ3f4r4arprr8+tnc5mFjZWS5kYzqrKtKjD3i5kzVV83lUmS4gYLVX/97BZQKyXvYI75B3O/mjmTuNYO9Geu+dTanv/edWr5Z+LopC0HU1lj0jDz6ZnhrMmML2mqummbIin4rI7rrRbJWznTQ2hWmcsb7RSY+33MJW2blk3F67mmqmxrk7bqmypPbGVrW9qKFZZYejZUbd9OiVrS0ERtq5KsNH3Vx/VWi+hZSNRmlTlisrRg7pcy15A7KZW5Omn6vGpymnSLqrk6t/bExpQfXpmqdZSdc4nwL7lVRM+mRGn2zNzr+w0w93P93MScTGaleCg7BGZWmLNcfJr6tKRhJ9X2sh1ZMrdaRM/0oM0G5gztJT5gMw3mviNzA99nZa7xm9l65pquMDckrmqXTmzwQKWEzTU/Ny+iZ3rIw0Qc9hBmNnuDuV/EnLUt3/KyMnlgzrUt/Q2imhddphY/mPNBJWd51da8BFP5khM7jRV/FdVbKxIzF5ol5qgvQ2OGnpkz1zfKYO7HPp8zfj5tA3PJQNNaWycFbSn4htf+oPJnPBHaaebzJXPPDu8zStqJxPXWiuSBQJ+ozRJz9IQupZOqcdwzC+ae4bOv1C/f3eD8Yb6a92cujVb4WlJru3nJm0VWm43LDw7M4fPWBJ+3QmAOzIE5MAfmMMRgDsyBOTAH5iAwB+bAHJiDwByYA3NgDsyBOTAH5sAcmANzEJgDc2AOzEFgDsyBOTAHgTkwB+bAHJiDMCBgDsyBOTAHgTkwB+bAHATmwByYA3NgDkO8kNtgDMDcY7UpMAafzhwUC6jAz0FgDoLAHATmIAjMQWAOAnMQBOYgMAdBYA4CcxAE5iAwB4E5MAeBOQjMQRCYg8AcBIE5CMxBYA7MQWAOAnMQBOYgMAdB72AOgsAcBOYg6AuY24dVX7bf70NisXdrZa8kryrr3LIF6BmZOx7pR7edk7PtAhrb7TYkdsclXONeityPzziVnVqAnpK57T4ptttijTnGZMJvv6y5Hf+SOZclYO7pmHP/c5fMdezPiv1xeySs3NhxStEdj+OMuYIpKcbj8bjfH4nPbp/tt91+XxCL3fY8Zx79WdeNR0LS2+w6n3MUl7poQUtCv5U597//LqA7Em/0L9seR3Z5hMxI1BAHHXu2wFxHjtBJ3rhnn8jZrD3heuwmaOis68TKlgqrzY5d4bErRj+Dxy34krhhv5U5Qi7ZLKEjYvx/+8QxJh3PfHQgPzZOzMlr+TExN82t5NZmzHXiEoWmcTzb3HuAqe1FC74kbthvZY6QS5Klp2NXtCVWmAeaOZkNmjTFiXWBOSezomwBLpnLok1HxpT56TRRm+wkO2/A6cLw3EIHH/ermRPcBLyIuZEJ6DwtzBUv1MKOwTPnN5wjwSZza8aHK8wVAlVgzhPI1sfA3KIFMPfL13ME3RI5Ys5lvBMgDIiMjLGT1dZ+38l0SE/oMs/UntdqwicVGdmD8fO7BXP7kffAXaiwF9rIkzphjp7LLVoAc7993/rfBXLJ0XsrN/JU53hNPx67JCNMjsIcPaELj+Z8nuODX99tvdfzNsIDvS6bvJfalGWezz0mixbA3K9/PncjBIcrhKxCl3tFcfm5hOaFIuQgFzaIQLdiM9ZKCxA+b32r9ltwBOYey1w2Oow9mMP3SiB8TxgCcxAE5qDfzpxLsfCHHsrcUJbl1S1GWg4EZclKMarQxzBXEFXpVaDyshZXCOKgj2NuYKiMSevC1Fwqr+shKeisNHRWm9KBOehjmctr+ZGWdZ6XRWLqdCiLgs4G4qwo2Q2COehDmTNGmaMide7KgYAbCjkbGEjJB3PQZ/g5nklzcnckYs4JcwThwC/BHPSR67kAG28nUppM/cZCmCtkwzqAOehDmXM0jdK+NS1dWte8aXBuSJU52V/Uhr6SVOIZHvRxz+doOhXsytKQjyvqsqwDc7KRpckVz+egT/gcIi2DI3PwaNBDPm+VPQQEPZC5At+wg/C9EgjMQRCYg8AcBObAHATmIDAHQWAOAnMQBOYgMAdBYA4CcxCYgyAwB4E5CAJzEJiDIPxtVwjMQWAOgr4Lc8X+RlAH53/l1b3yW693hEhfKYIQ6U/K3N5HMLyW2XH4SwnVeqXIHSHSrxVB4NbnZC6TQHDXgOo4QpfjSKvZNWd4R4j0a0XA3HPGRucI1BQ4OEQ19yHLMz1z5N8yAXMG5esh0ruOI8GG+OeLInEQdugJY6NLNN/uqFHNNWS5j3i+59iY4qPmzN0RIp28I51v1VhcJA7Cjjv1jLHRPXNbjWquIctDxPNum0iI6jlzd4RIJ9/JcV6nCOvzImMUhB136hljo3db9XMSU1pDlmskYCdrvSxm7o4Q6R3V3LOP1Ajr8yJxEHbcqWeMjT4G2CSquYYsVz48NOOSuVdDpE/M7Zd7CGZuHoQdd+oZY6PzBCgrOIlqriHLzz7JuePR7XkXUJwfn7wWIl2ZmyKsz4vEQdhxp54xNro+nwtRzX3Ico14fux0rRc9n3s9RHo3ui2t5KYI61GROAg79JSx0Qt+9HaOav56yPJXQ6TPihaXRRZB2KFn/bwVUc2hRzOHqOYQvlcC4XvCEATmIDAHgTnERod+S2x0SUwoiWIzxXkcos7V2LCAuY+Oje4TKUmCvs7zmLnaYPjB3EfHRveJnMQBhRfMmdoFKzlbSGvcDTD33tjomijZw5I5IwFgvRVxnwYzLZhL3hsbXRPLPK+XeRRkuJaw1mJFQr8iBieYe39sdE0sTZ4mF8ylPCWrFToWOaZWMPfu2OghMSQt1nO8qVUrvPKrB9wMMJe8MzZ6SFTUojyGLacMb0Wew+BegLl3x0afEnVmXT6fow2E81b4gQp2EGAueVBsdG8lLfGtPDD3yNjowwA3B+YeGxs9z/GcBMzheyUQmIPAHASBOQjMQRCYg8AcBObAHATmIDAHQWAOAnMQBOYgMAeBOTAHgTkIzEEQmIPwt10hCMxBYA6C3sDcq4HPk7dHUXez35iN8xAX/YmZk8BKWRzHPPmgKOpTIsVqWuRNgTehJ2Suy7LMJa+z8IYo6pLI9Sg88CIPzD1vbHSNRqjR0DWOuUY1pwCFFFN6fHMUdZ8oScfjIo/MZl0In95xHyTaOvQEsdE5vOWeo6BLNHSNY65RzTn4YHeOkPm3UdQ1kZMyP8lGzNFkm01WFEvoGWKj03rueOzOwc2VHB/VXLgb3xpFXRMzZpR9WMwcpWWhIUdz8H67x317jtjoOrdOQX+3o+4WeL/Qde+Joq6JGXnCsVjy6PcToaGRXSzCVDxJbPQrzO014HTyjijqmjglLfzcUcKj76eVHzYVzxIb3a/niok5iWMeoppHzP11FHVNVNTivPN6zodPZ5O4bc8SG93vGqbg5hLHPEQ1j5j76yjqmqjMXT6foxQXwqcXW8RJf6LY6AtpHPO1EOl/HUX9Hnkr4xafS+Dz1uSBUdS7Dm4OzCUPjaLedXtsWsEcBOF7whCYgyAwB4E5CMyBOQjMQWAOgh7AXG4Mh53GAYe3H/6SuZqDYeKAw3sOYA4HMIcDmANzOHwpc0VKwgGH9xzwrATC8zkIzEEQmIPwPWEIAnMQmIMgMAeBOQjMQRCYg34/c/hyIQ5v/ELmm5nDFyBweOOXRsAcDmAOBzAH5nD4Jszhy4U4vPELmXhWAuH5HASBOQjMQWAOgsAcBOYg6OHM1TcrDWVZv7eXsxby/pU/1q/tva3Z+aUMpe3Xx6x0V6uvNLscnis9W7uwoSxqc9eQXy35U5gzVVW1/d02WrtM6WdDUrZV9d5ezlroq+Eyf6W9tzU7a2igMWjXyarSq9VXml0Oz5WerV0YpVXNXUN+teTPYa6sm8q8nbmqj8fjA5krVpBbb69/H3O2vUbWLeZWml15S671bO3C6qq4rL1q82rJn8NcnuRVnaS2JXdn6WdJF2RaOiYNX1ndpj4vcX3bVtPF5g2VSQZTWWPSpKC8tg5DrBX0hqpNXyRv66ZtilCBbVJLay0MrfieUOFWe/GdtbZnazOb1IK/opAXNeQoz5hiUSTcX0q1Lm8pttS586F6dLWRTdNKPS2i1bUviwsL49kmtlx2V236FkqyMFCqlvzJzBkagyJpq76p8rZqeZoZaLptq9ryFEDj4/MSS6ln5uiWl5bSWHXSVE3PHsjfBa2gBb1NLUJzeWurMlRQm2stpGVTOZn8pcKt9mLmmqqy7cymrW2pVxTyooYGsWkWRQJz1PXKiLs7dz5Uj642sqn1tIhW1yKLC4sVdzcanqKlG9QWP38Pwes5QiuvmtxU9K5K6ZqrnoawqJqBLtdUtebx2czR0/uwmOa6nH9ODGiFqaDY1CKmah0d9ExtrreQlP7WSIVb7V0wR+D0arPwSx+9Is1bNtQ2F0UmdhpKtBNz0pep+vxqY5taL2ZuKhJdWDzrRt2Nh0feHHnyG5gr+f1Yynud3t8Fv6ktj4m4poJ+al7Jb9vzjTLkvXj7JaMmi+KJAa0wrUfEphbhuTxpez1Tm+sthFsjFW61t2SuTc6dGPxdna5I88wlc3GRiZ0hYk76MlWfX21sU+vFzE1FoguL96RRd+Ph4eQ2+RXM5eTIHS/plI+cmRsSR9dHbzBy/ppX0hjV1Wzxatj7BwbSpJ/5uTpaA4tNLWJkvu71TG1eaUFvzaC35mp7S+bEVahNdRzhinzesiFlbl5ktoeoaZtFeeXU+an6/Gpjm1pvYk6qT0UWF3bp57Qv8fBQS1XV/w7m+O45XkbbWvnIq7aWpQb5Flox+7yBl1DnRVBjhp7Hom3z2hCctvJ3weShQsycFtGR1jO1udqCuKM83Jpb7U2HOXOhE7TyM1zBX5HPWzYkzMVFJnZqXnDklaUKgbmp+vxqY5taT4to9anI/MIWNynqbjw8KS3m2tnkan4AgFeZc22VDrzQr9veMR8yX/Cuy79xfR6v3/tpjFJKqxopUsnquCr9bMMPpLSC3lC16YvkOtJagW3ySmWlBZm12NdKhZvthcOcuWCzaKSCXpHmLRoK3m9eZNoLVK2MEPdz1nmpHl1tZFPraRGtHq42urDlY5Sou2rTt8DOj7hz5+GxP/1ziHS+I3LRmebFaW5weuTk1F03FtLcxdmQpkUue7e1FmLdbO/KBc8qxle02pBbSy3cWueL5Pbw0Gx662pvKepusLna38Hh89a/V89zZPsLw1GvPkv+tVf7o5hLa2vL9BeO9bD2UfGvvVp8rwTC33aFwByYg8AcBOYgCMxBYA6CwBwE5iAIzEFgDgJzEATmIDAHQWAOAnMQBOYgMAeBOQgCcxCYg6BHMRf95lK31xf7ruteb2vfHanU/njPL9ZNpqEnZm67pR/H7fz3eLfHQMhWcl9Bbiuluu09OE2moadmbkyK7TpzTNKrTR23shku7vJgYO7pmHP/c5fMHdmfFSN5K5oiC/ZtxyQ7ylnMnOukjC+53/JZwfXGsWBnx6alejcyhlTmeOy4qBpT09BTMef+998FdAwO/SNohA/GY3t0cjYumDtuj7R005JM3nHbycy6HbPOM+erd8qcFJmM+Tzcm6dijpBLNkvoPBf0356n2D1DsT0SSfuRfdOcucID0/mSI0PGRXTJ1zFzWv3MHP3r1JjmqbHsG6nI8Kj8s5gj5JJk6elkiiT/w8jwi9FDs/Xz4Jy5vcy2iZYcec8gGM2Ym6oH5sJOZMuJ4zddz22STQFkPoc5wU3Ai5iTyZD9nEyCe2LkOG67yz1E4RHSkleYk+r+EJhTY5r3LYfLbYDMJ63nCLolcsRcsafVPU2VnV+ekVs68tw5HmVuHcf5NDx2496XvGRu3CdafdweZdvgs9SY5n3P8cLk+mn71v8ukEt0e+o3lS4s9GVr0C2e0BU+0Zf0zM3Xc/xi2oJsz34uGPvOewgw93nP527MIS6TRU1WhEX+Spli784lV7SnBbks+/ZxCW8s+76rJjD3Yz9v5ckzfr78QwTmfixzWUePgX/i/QNz+F7Jg1XgWQm+Jww/B+bAHATmwByYA3NgDsyBuS9hzjkwB+YeyVxaciwWMAfmHsZcakUpmANzj2Ku9MyVYA7MPYg5Z1UOzIE5MAfmfi9zDZgDc/BzYA7MQRgXMAfmfjZzDZgDc/BzYA7MQRgXMAfmsJ4Dc9Dn+rmhtP0ypazvabTO/7LCe5T34e+upMaBuR/FXF5VbTMjZKDzNi5StlUlL/rbvz7W2mWFVcVW+jf+SlpfDfqqaYsbLRQVq149A3Nfw5yp+rqpzHRu23TtBsuh6u9jbqqwqtjKKzavqgjI1ZcIzW2mla3rOl09A3Nfs54zxJsjWlLbtn3iTNsaUyRF37ZtnbMHaWtFaDCVNSbcL0vlS6pHFdqS/p4EVaiClQVzedOy6/R5sZVwplZCn+TMtHS0Tuvlbd20TejZ0AZnXLSNb70ttULcQlqV83GJzsDcV/k58nF0A9uqb6p8kLnHJE3V9OQAq9Q7DUHIRvNSW7U8Cw9V27eUaOlIzHkrC+aIkdImmhdb0bNgJUzvclazfeqLr2fozBIxvmdp2VROLaSJtq4V4hZSet2aZPUMzH0hc02bVw3dVsKL3QYt8nqGLWYunrPaluepqifCiqoZKobKTlYWzJUF29S8lblVrUwTs5zVlFBUwaapWkeF82Ch9Mzl7Li0da0QWqhLkqMDQTgt8OIzMPeVfq4U52CVOVmgv8KcLSpTV5ZvPVdnK3ayEjFnyCP2bspbYU6tTM5PzmrqBCGk9QyT0vbas8Cck0raulYILdCU3LbOL/nsfHsdnYG5r1nPDexNdDJS5tJE5taB7ud15nJmbkhcRczlfC8nK4s9BFFXT3mrzImViTk5Y+LrqtR6hmFj5qRngTm/e9XWtcLlviSNKEvB3Jf7OdoNVIPjvYOtp7m1oSmIELJ95ZkzuUySeW1i5nJig5ZZA6+yKjtZ0Qp+edaYoa/qcwszK3qmVqbHN3JGG1LaGqRaT5nTnolvy/2kynOrtK4V4haGPs95VZjIvD6dgbmvfj5HN2HgLQER0TS6tqdNqeNJ0TMnD9xokT65kLZ3zFzCUx9tLqlCz7tStaIVfF8orWrclDe3Es7USni+J2ec0+ahZ7lnTnsmZdrp0ZyVfoYKUQs5z8ylGLWzMzD3LT6HSOePVlM3PwS54fKPZjlfL9ROV/6slhvcPC+24s9cVE/OaGZ313vmgfIP6YY0LXJ2jKHCvAU35D5VMqczMIfPWy8lO5jkvs8kaMJt3f0VwBy+V7Kqob/3c9S05j9Y8BcVwByYS/C9EjAH5iB8fw7Mwc+BOQjMgTkwB+awnsPYgjn4OTAH5sAcBObAHNZzYA6CnwNzYA7MgTkwB+awngNz8HMQxgXMgTkwB+YgrOfAHPwcmIPAHJgDc2AO6zkIzMHPgTkw90RyG4wBmHusNgXG4PPWc9CqQAz8HATmIAjMQXg+B4G5oii9mysLMAc9iLnUM5eCOehRzBVpyX9+sgBz0MOYozWdkwOYgx7GnArMQWAOAnMQBOYgMAdBYA4CcxCYA3PQlzLnwBz0WOYGMAc9krmUmCvAHPRQ5l6KFMxBD2RuSF5edHIFc9AjmEtp0/pnSMEc9DDmhjxx5OgKMAc9iLk0f0kcregEOjAHfT5z6VAQc+ToiiF1YA76dObSIS9emDmC7mUY0sJhbKDP/H0IIi4n1hwzR9D9IV9HKRD0GaqNMXlO0+ofQs4zR2s6wg6CPlcvfl5NXoi3RP67qoQc43pOceNsLvfyidpca3Sz+QDrxQv0QUr0v/8Dc+NGlyP5FVUAAAAASUVORK5CYII=" width="627" height="392" class="img_ev3q"></p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-8--aplicar-la-configuración-del-gateway-e-iniciarlo">Paso 8 — Aplicar la configuración del gateway e iniciarlo<a href="https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/#paso-8--aplicar-la-configuraci%C3%B3n-del-gateway-e-iniciarlo" class="hash-link" aria-label="Enlace directo al Paso 8 — Aplicar la configuración del gateway e iniciarlo" title="Enlace directo al Paso 8 — Aplicar la configuración del gateway e iniciarlo" translate="no">​</a></h2>
<p>El onboarding escribió tu proveedor y modelo. Ahora establece las opciones de
ejecución del gateway — modo de ejecución, bind de red y los orígenes que aceptará
la Control UI — y luego inícialo como servicio en segundo plano.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">docker compose run --rm --no-deps --entrypoint node openclaw-gateway \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  dist/index.js config set --batch-json '[{"path":"gateway.mode","value":"local"},{"path":"gateway.bind","value":"lan"},{"path":"gateway.controlUi.allowedOrigins","value":["http://localhost:18789","http://127.0.0.1:18789"]}]'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">docker compose up -d openclaw-gateway</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="docker compose up -d creando el contenedor gateway" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAnUAAACpCAMAAAB6SjsbAAAApVBMVEURIECU3FWvrq+O01MPHT14d3fc3Nw7OjoiLksAAAACev4ZKUIwMC5GRURHT2Zpa3QbGxp7foiUlZqIipFeoOmAvVAwOlTMzcy7vMBbYnWlpqqenp4uRz1klEpzq05VVVQQfMpgX19QeUYFBQVKhdRGgxL/X1cpyED+vC5GeLMlRnQ/X0UaM10rV5AyWiA+Z6Cmwv/m7f8vrj3cV1KYXz7bpDAmJSTYPsBzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAXBklEQVR42u2dDX+iOBPAMRZ0n4gsoIKgUqy9u1ptb+/27vt/tGcmbySIrd2e2t3O/HZV8jIZyJ/JC2nwvn4cGdmS+6NRmIWjJIODOhs0AQwCing0ymr4kSSYNhqV9ajMWJ0zkSrGJOkID0bhALILjTl8xEwoGY3KFHOXowpUiyAdoD6kEVFey9gRK0YjP5fBYVaNRoMs1UcQHcHHIIukVSkTASIZli+MbKcM8yyLB6ogY4XUbYkITNGitBj5eDCAIuSZ6YuRjqJcHsO3sSSPc/gBJwza/SrFEhrLtcYmY5iXunQ0mcnidXxjAXyjdjgndZ1jecEgMqyEBpFnEDrVWOpqFOJ9/fdjUpeycJCA+UmRhiFLrAC8HiWcb57A1QEsBlgXMaTA85SXllWDGAJ9FRlmPiTN82hQZ76iLsqiMBKVU4ZhwawAi7pUkCoyFBZ1o5KFIRhmcVFlYF2m7oWYDUIGPxIwGct3qBMp83gUQeSgucmUFSJNzJrrIKoQsAHTI7ARkpX5ACypRjXcbPpiDOBi4FWC1Fo/3K9gYCnujZpBRsdyrdHKGObJqDFZmaDjdXqXOnWdjXl5hZlFnkReqySXtVZmSVjl+sJ6WN0I3t3dl7svH0ru7tCgO/0Dv0SA+6FjdSb5oQObb6XJpLHjWomtC2FluLMjnELdIkz0XZPuzrHOGONqsQo/OKNGUcvYDlObwu2TPSznUJF1Pe8OTrWVztRNZ6LmzHStfbn7N6z+VSgL6r7effk6qKLIJyE5l0RRFX69+/qv8XV3X+s6BBmQkJxNwrCq668CO6TublATcSQXAa8eIHYeQlcRcyQX4q5C7DxsXgk6kothB40sUPeFPB3JRbG7+9cjV0dycWfn3Y0IOpJzDByaz3YMUGdFfP/7t9/+/u4m+f7PH3/8893NvP7r99//Wp/X6Ng/U282NN3a1o9TxY+779PKT9sRVRydpvPlXk4dV1e+bCedA0zK1eITngSFMEEHhvt+JWNquwruvKZbFwJzKH9bVyAE5lD+scIWwBzKX4tzUseKY+cWuZe5ZEXaWbd48m2JGSuwXsKEsbK2f0QFO7HCSlZ1W8wOInwWn6IxKhmDx3twMqxTd8rq9162S3i3CJ4zVPgJ1sKcsB+Gfh3BRY38Q+pMt05BZ2OnoXOwU9ABdtegrmaJzWBRwJnFLO1IydgBdT4QWrAIWStTVoTNjxSYeR91NSs7yjuFuoqxtIRTCEvWTV3Bwp+AOizeuITIr/2ohrqBAGTPpq7+4n3R6b7/ZsQ0st//MGLC1r8bOUMjW5UAUFzWcPn8sgQOEqjLuowHaemXRRzWBfimyi+BnLKE//BMuQ6rCIiJyjgtEji7MC1wxQBUYRlBAr9ANZg9geqrBxErgRAALYFM5gdLU0NdLMutQGPCkgrANCigDQ11Qmnol6z0hb8qKyxb2VBCQ9xQh7qKUEejmtRoxZsGPGVVM3C4EBjVbSpLkSWJZJEFtuR1Ys6rwCY/LlEzUqcMwmvkC8Piy2EX+ZVGCzxcBI/B0IVDS2ufUmRR93dD3d86/p+Gun8OXN1ZnJ24wgmrsb0q0f3gfR5BtWAjxFhUJ+CuoCXysZ4AmsqHNpNVaQI1zEpobRG3BEAJwXulEbiRAnJGmB0wgCyYL/TROaJW86MexIY65LKC6gONSSG0liaGlUXjj4RSSA3+s5aGAaVQLtqQwk+LOjSz1NGRVKzVJNiAJiyCHjZSV7WddARaYtDOIF0pLEjwdgH8fX1ZwFdjfIXUKYPkRSshZ3QNZwc/q1D3cY5T95slOv4PS3TY75aclTrplhrqIsGIaGE1dX45KJEvcHZAEnhFSFWjhhjcAVZehXXrQxasIawq0fJWMdJQiypRP5RO7ApXsOQoRF6wODQBORWdZKjbWHglmU4qhfFJKN0mGFZVYSW8VlFVTGqROYV1OjqV5ms1wsFJV4s/wzTWfjWtw6gS3BTiPFJRJGoGSyq0TV0W1DaI0lr4OmlQCCXCDQk3wCUnKSIFGvo66OZFsv/2E1EnOjwWdZWoWIe6GKhEj1JFhfQrQJHxL5K6YiA+RLP4gq8zOqXjiBJRy7EoLpTuiWEN1qJfp9IJpRX2xjR1foEH6IBRatQgc0qrVPSBGunrFHWmN1cKzejfQmF5LSirRapSlqAvi4hX/TptEADJfBYZR31RZ1fLfl0kDz9+C1tghdTy5lWX2T+kLhZIRnjTQ3OSlrHwaFh3EX7jLad8HUKbqCGA069Ldb9OuhnRwtYgIZYhulIWdSHGDLSTUumEUnQrqaIOPTR4ygo5gCUWgjWZU1Cno8Fm6YWlGjybsFAGVmaSC4n2YepF+K/mPIQFUGKEBVjUYe9JtLDaoBja4hD6F/ElqRugi2uNYV+i7mOMJuDyQa9MUIfDzXggBpkWdTjgQ88Doz5csRuHcRLFiei5i5zYPsLIQLCb1JAas/uKOhjDxmIMW2IHC8ew+sfA6teJDpvvUmf6dVhuZY9mE5b6rKHOT6UN4HO1hzOjWR1dSUdVOWPYZKBbWGmPNThOsQEuheWlSFuCJQWoSsxlkfG1pE4bBBlFZ/Cy8yeH83UvUBd+jJkTOSjAyxcjXzA3gv1kTZ24/lCrgFeKjQo0jnDDhzIjVkckmzwcH+IMXCU62amZ7oCaOTJf51DnC878hiV99jH6VZc6NND06xB9DBU21M5owh9Y0XFaWxMwer6umzqRFUa/wnJwXYXoIqo8+rKoeNHCKoNCbL0jdtFu3SnPJhzqPsoscWOo+tVpuQ4UMyml9iZhWwV2E538zSOJ8KC8t1lnhXUdHFErQqMEhwI2Dq6NbtZE4Rlq0MPuEsJugz6eONR9qCdib+gJ1lF1dEa2umxf+lR0SzmMOE2cOeIjU9Q/lbSoG/zcaxmqrsD6Y9oanbzSxz2F+hdYIfQrUUdC1JGQEHUkRB0JUUdCQtSREHUkJP89dYMvJCSXlAFQN/ZISM4rs6Etc6KOhKgjIepISIg6kl+SusWSv65isX48sbDFenH45fFTsi7Xx2IeV9NVK2S5XHpvM+mYEcPnE6zbPjw8d4XnkXsctW/o6Ap3+BvKvBnHcXQF6pa9RVemqY3AtNfr9RcvJlGygpTT9tdiip+vytFEj1B677CcE0//NSM2kxOU7CeTjmTjMghyh9ksaSUJ4stTd2DECxJnQXBq2nR8bup6K7ve1utlv89fSGL46EPtLt2vhaz2H6du2mXi9ETqXjNiP9mepOfhkDqe5VkR/9zUAUsnU/emczlC3Rq9V3+57C37vT5XR+u+OHpc96br9UIF9tDXLXur/hrjPD6Fr6VJIgOn01W/twI+ODgm94uveb+pcPETGJ72UbGpQDhsjqQycE99SASm9aEgdeQt+j2Bu0Odk0Eo668aZB0j5pl77SYb6cmekb8JHvHNZPLgbfFoInDDSEUdxk2eJ/vhZO9NxgGzW1ie5lmQePM8y2M8yjKIhZoa58ybFRmWmyRx3pSvksgMSQKvKciSeT5nWcG1ljHkyz2dXXnYfIhNu07ZuhOkEUqkFp1SahmrI1W6om4I+eBVG/NZPuf52FIG+WXceB4U8/lMadEpZVzBsGHPZ69TJ5xcb7WEKun3lupoLWBZIWY9EwgJeG8N4SIl1ON0OW2SyEBgAcL7fYgFCp0vjZqmALqIQAVEWO0mHK4a6qQyLgJXj6KgtTqCL4B+3aLOyeAqaxsRB4Xrwri3m0z2m8ke2NtswPOJoy0S+Ay4bSYP2wdD3Way2U8eJg/Pkw2feNDAWggXQR4HyTjI0jyIvCQo4W8lgLpxlo15HqQsGHssCJLGE8kkKkMeQFuXZxF8sGCutWQZ/DWcp7OrblswQ7+jUraok0ZoPqUWlVJpmcNRAnYrAyV1nAUsDtIshcszDmaOMhWXBCiR0qJTyrgYTPXy3HsDdX1vARxp6sBvYY0Jf2Kom6566PFEygV8NS2syu4J8lbov9BZOl/Gwa1A+CMkFsGgZ9lTgWtR5FQdKWVrbUcfC1RHYOBa+EhJXVcGV1nLCKgKcA/8YQPt6oMHwO0RqB36vD06uslmB5ht4b84ehC+0LSwW/R6k4cN0AmuEBEOUuOCsLKzJAU6hgEbKrgDFmQziGNQ2ylQF3lMg6qSqAx5NguKKIgCBkelCgTq4qFnsreoEynNECIG4coI03gKLSql0jLHrmiQagMldWPUDjgVBYAVuMpUnGphlRaVUsVBXwMiordQ9+hxBEBRt/Y6qJN+TaZc6/6cSiICJRnoc/rCkdlfusL7KEgINJLQ3IH7BOpk4FIVq5P0VXfSok4dLYUphrquDK6ylhF6BCH8GhA00eMJ8HDia4PN7QSpQxQfdpNni7pnGfiArazIlTfeLkZnlCVFAFTn2ViFo3tAUFASj9nuQCVRGfKEB3OkbubxoFCB0B8ArrnO3qJOpDROLgfhygjjq4QWlVJpmcsk2kBJncgH/gxa5wB8lqNMxSnqlBaVUsfFwbzI+EnUPUI9rZCspYRvhdQ9KuqmTZL+dLlerxYqJTe+bqrAXGKr1leDXehF9d0vt8LBaWEXTFNnxiue1cvTyhZYujpUR+vesmM04WRwlXUa8bDzttiY7pEiAGqoqQNntpXDC0Wd6+uwPd5j+zrxRBfQy5sGGysgguqALx7k6KgEdVA9Y+MHGLOnbGQSlQGpG0vqoiBWgaIXF0SuG0FsYkld1O7gKyNMEy60qJRKi6JOGyipSyGJaCmDWWb8p1Km4iR5+lRUSh3Hs6zx+i+OJnpTqBHAbYkVo44MddCDXy5NkrW3xpoUKbHbJGbLRBIT2FcDxkc5eLW++BqbxbXdCeMt6h4Foi3q1jpQtbDiiGNfcrpEmmyVdgZXWduIuVVRHKgSgG12D5Pn/WQIfo4DT8Pd815TBx0+vt2KZngIKZE48IMTCH8e5zjvEBnXxdIA+3XQuYYyGPS9o7nu12XZfJ5ELnUqicpgqIuglz9TgTM2H0OfSWc3TTkUFKuUrbkcaYQ5ElpUSqVFUacNxGYYb4sikcZ7hblA5oxEHHbcxtFcaVEpTRxSeAp12PasZJPVX+sjQx0Gr1SgmK+beiqlSGqS6EBZ753zdQvRKPatuRokyaEOS5i2qROl409xqI8esclcyjlErzuDq6xlhD2a0FN1z9jgipEs+rkdfhvquGhv5VBWNM3QtmI4jHhn0L5aI2IYW0B3HK8/9MC9IfR74PbHMWyQ8TEOFdrUqSQyQ54q6iDh2FOBWEIA402VXd0r2KyL0USQH8ygKSOMt0ItOqXUMhbUpbp0oA5m7DgcxHkxA4CiZoSCyqCBl3F4w2J/QmpRKU3czB2kvTBft+AdX8YPPC6aQI7PJqC95TqO6yRN4EvPJk54gMBfDdRHi8XLWh4XC762JhNdI/i4UbrXU3V8y3H+ZKiidjvnSmwtM3aunYXd9M3UxZoND0qCyFmHvTKJziA7bWNHi1biZBcFmZSt2nYChZYmpWuEY+DsUNlsNuOi+6fj+HjWbYscuJzlOWznfPKRSeZrCs709Pr8zfn2kw9gfBTMrpiyPX/MitazlyNSsiA509P/xyk/MfC6slhO+6sfuBe2m+vbfjNO+fVStsGJYCfak4AtyjmnNSc/r9xcNSWtdCLxaH0dCQlRR/JzUjdMUPTMDv5+lUbTZZxh75HTtSV5K3WzIGNm2hJ2Tc0PFjC0FZVmfMyyoXVEQnI6dVHracrL1A0Ds3ILZqWtIxKSN1PHy1mbus6FgxyewUnOhllhHZGQ/AB16mGGRd2xhYOKs0ROdhN1JO+l7qaLOmfhoOZML80i6kh+kDomVullR3yds3BQccZz9WyOqCP5QerGsIA1nc9d6o4tHBScmeUFRB3Jf9mv61w4yMfjjI3HMzl0VUd0eUn+K+o6Fw4ORVucw1SddUSXl+SN1MUzs0BvOJs583XHFw6OZ3RJSd5BHYpKhEulD2aJf3Q5IAnJ0af/YxSdCH+3HduPLgckITlK3c1r6/1u6OKR0EonEqKOhISoI/l5qRvOXxu4/geDjHfuEXmuLSbd87pZnHyeb0j6qwh3/jR45/4N8Rup4+ovwr0Xdmk8+IvbH5C37BHpXWiLydY61af7+/unI0mf2pAdTfmryvb29nZvjm5Rfpw6BlPH8/HLuzQO/5MWOg0ulP3ULSZb61Sf7l9YpH+/+OTU8dstgGf8G/zY3e7eRh1/erI3YfP0Jo9qW0e9S6PaszEXa07GzgaQrV0o37lH5HW2mHTXqXIF1tPT4h5+8qd7EYAOcMEX90+wrYUT+PTZXB06uL0TAvtP7k3Yq9Txe3Prqq1V1CaPaltHvUuj3JdxFjPcFk3Fde5C+c49Iq+3xWRD3c3inut2FhiDrwU6vyfA7X4BHCJ8TuAno25/C//2VqO6A+fn3e53u+2J1Fk3aiy7bGqTR7Wto9mlUe1hFkvqRFznLpTv3CPy4ltMynzu2q2RoQ6wWvD7Jw7eTzhA/d8zgU+fr4Xd76FFtajbCR9327SyL1MHN6p1xeZyHYra5FFvdSYrzuzZqKgTcZ27UL5zj8iLbzEp87nUNS2s9HwoT8IBaurcwE/n6wCzhjouoLvZwZhiexp19/dWH9DZP7KhLm32c3Sp69yF8p17RF5vi0l7nariSHZ5ufR8i3v83/g6Fcg9/vn6ddCNa/p15hf4v90J1EGnjjvApONorDZ5NNSJXRqtfRlhlKviOnehfPcekVfZYrK1TvUJxxBcUQe9EM4X2MIuRH8EQiFOBz4t7j/fGHYvx7A7+bGFCTvO99wMZV/p17nzm9zeP1Jv6yh3aTT7Msa4mlPHde5C+c49Iq+zxWR7naocmSrquB4/iLGFtxBHrcBPOV+3xc+dmK/b8tvbU8ewNwf7R85a+0eawFnn9FXXLpTv2iPymltMHn/gwLnz9EId8pNfk/arPptwn0nsrv4c9p2LQq+yxSSt9PJ+7qf/N+97XnudLSZJfvY1JzdXzU5CK51+fbm9PXwETtQRdUQdUUfU0RvYPXoDu/e+N7ATdfQG9su/gZ2oozewX/4N7EQdvYH98m9gJ+o8j97Afuk3sBN1Hr2B/eJvYCfqPHoD+8XfwE7UefQG9ou/gZ2oozewX/4N7EQdvYH9Mm9gz6w3sBN19Ab2y7+BnaijN7Bf9A3sNyhEHb2B/WJvYL/Roqi7oWWBtObkzHJzc0gdcUfUXYi5ltC1IeouDh1hR9RdATrCjqg7J3PD583mf0Y2m+chcUfUnRm6Zws5Bd4zYUfUnZE6b3fAnOBuR9QRdWdzdd3QEXZE3Rmh845AB9h5nx47ou5M1D07oE1sBp+JOqLuLNANbcwmf3770x7LDj87dkTd+V0dMDf59m1Czo6oO/OsSePaNsLR4WcT9NlnT4i6M1OnvdzGamWJOqLurNRZrAF/G6KOqDsjddrVfUNRzH0zjSxRR9Sd42FYM2cCA1hJ3Z8TM6D47I/FiLrzULdxhrAo3zYHowmijqg7F3WbP6X8j6gj6s683OQl6p6JOqLuLCvrNsdb2M2QqCPqzuzsDqijNXZE3bmWrm+OtbAbWshO1J2LutfW1xF1RN0Z/kzn5bXERB1Rd56/Dnvp7yZozQlRd6a/STz+N2JEHVF3wT+FJeiIunO8lY+oI+o+rLPziDqi7tLYeUQdUXd56qiFJeoujZ1Hvo6ou/SYwiPqiLoL+zuPdhIj6i7r7mQcUUfUXY47jxwdUXd27jzHy5GjI+ouR57+R0LUkRB1JEQdCQlRR0LUkZAQdSREHQkJUUdC1JGQEHUkRB0JUUdCQtSREHUkJEQdCVFHQkLUkRB1JEQdUUdC1JEQdSQkRB0JUUdCQtSREHUkJEQdCVFHQtQRdSREHclnoe7/DBN0CR/Iyg8AAAAASUVORK5CYII=" width="629" height="169" class="img_ev3q"></p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="paso-9--comprobación-de-funcionamiento">Paso 9 — Comprobación de funcionamiento<a href="https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/#paso-9--comprobaci%C3%B3n-de-funcionamiento" class="hash-link" aria-label="Enlace directo al Paso 9 — Comprobación de funcionamiento" title="Enlace directo al Paso 9 — Comprobación de funcionamiento" translate="no">​</a></h2>
<p>Salud del contenedor:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">docker compose ps</span><br></div></code></pre></div></div>
<p>Quieres ver <code>Up ... (healthy)</code>. Luego consulta el endpoint de salud:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl http://127.0.0.1:18789/healthz</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># {"ok":true,"status":"live"}</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="docker compose ps en estado healthy y la respuesta JSON de healthz" src="https://wec.wiline.com/docs/es/assets/images/step9-health-2a25a28bc6b53dd20c385259373d72a5.png" width="626" height="217" class="img_ev3q"></p>
<p>Confirma que tu clave del proveedor es utilizable:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">docker compose run --rm openclaw-cli models status</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># openai ... api_key=1 ... status=usable</span><br></div></code></pre></div></div>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>Censura antes de capturar la pantalla</div><div class="admonitionContent_BuS1"><p><code>models status</code> imprime un prefijo de clave enmascarado (<code>sk-proj-…</code>). Recórtalo o
difumínalo antes de publicar la captura.</p></div></div>
<p>Ahora la prueba real de extremo a extremo. El comando <code>agent</code> necesita un destino,
así que primero lista los agentes y luego envía un mensaje al predeterminado
(<code>main</code>):</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">docker compose run --rm openclaw-cli agents list</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># - main (default)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">docker compose run --rm openclaw-cli agent --agent main \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  --message "Reply with exactly one word: working"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># working</span><br></div></code></pre></div></div>
<p>Si responde <code>working</code>, toda la cadena — gateway → OpenAI → respuesta — está activa.</p>
<p><img decoding="async" loading="lazy" alt="El agente respondiendo &amp;quot;working&amp;quot; — la captura que lo demuestra" src="https://wec.wiline.com/docs/es/assets/images/step9-agent-working-91514c6b42235714c1348d5ce9f6e9b9.png" width="628" height="391" class="img_ev3q"></p>
<p>La Control UI está en <code>http://&lt;ip-de-tu-instancia&gt;:18789/</code>; pega tu
<code>OPENCLAW_GATEWAY_TOKEN</code> en Ajustes para iniciar sesión.</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>Abrirla desde otra máquina</div><div class="admonitionContent_BuS1"><p>Los navegadores bloquean la Control UI sobre HTTP plano en una IP remota ("Secure
browser context required"). Accede mediante un túnel SSH para que se cargue como
<code>127.0.0.1</code> — mira la <a href="https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/#3-control-ui-secure-browser-context-required" class="">Solución de problemas #3</a>.</p></div></div>
<p><img decoding="async" loading="lazy" alt="Control UI de OpenClaw en el navegador, con sesión iniciada" src="https://wec.wiline.com/docs/es/assets/images/step9-control-ui-fe1df8b0971ad1a8192ff40d2ea6856e.png" width="1100" height="567" class="img_ev3q"></p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="solución-de-problemas-errores-reales">Solución de problemas (errores reales)<a href="https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/#soluci%C3%B3n-de-problemas-errores-reales" class="hash-link" aria-label="Enlace directo al Solución de problemas (errores reales)" title="Enlace directo al Solución de problemas (errores reales)" translate="no">​</a></h2>
<p>Estos son errores que realmente encontramos durante este despliegue — no son hipotéticos.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-eacces-permission-denied-mkdir-homenodeopenclawstate">1. <code>EACCES: permission denied, mkdir '/home/node/.openclaw/state'</code><a href="https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/#1-eacces-permission-denied-mkdir-homenodeopenclawstate" class="hash-link" aria-label="Enlace directo al 1-eacces-permission-denied-mkdir-homenodeopenclawstate" title="Enlace directo al 1-eacces-permission-denied-mkdir-homenodeopenclawstate" translate="no">​</a></h3>
<p>Apareció en la primera ejecución del onboarding cuando <code>~/.openclaw</code> no existía de antemano:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">[openclaw] The CLI command failed.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">[openclaw] Reason: Failed to open the plugin state database.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">| EACCES: permission denied, mkdir '/home/node/.openclaw/state' | EACCES</span><br></div></code></pre></div></div>
<p><strong>Causa:</strong> Docker creó automáticamente el origen del bind-mount <code>~/.openclaw</code> con
propietario <code>root</code> (<code>0:0</code>). El contenedor se ejecuta como el usuario <code>node</code> (uid
1000), por lo que no puede escribir dentro de ese directorio propiedad de root.</p>
<p><strong>Solución:</strong> asigna el directorio al uid 1000 (que también es tu usuario <code>ubuntu</code>):</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">sudo chown -R 1000:1000 ~/.openclaw</span><br></div></code></pre></div></div>
<p>Crear el directorio tú mismo de antemano (Paso 6) evita esto por completo.</p>
<p><img decoding="async" loading="lazy" alt="Error de permisos EACCES en la terminal" src="https://wec.wiline.com/docs/es/assets/images/error1-eacces-fbbf355224203ecfccd68552fcf0bcb3.png" width="968" height="246" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-no-target-session-selected">2. <code>No target session selected</code><a href="https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/#2-no-target-session-selected" class="hash-link" aria-label="Enlace directo al 2-no-target-session-selected" title="Enlace directo al 2-no-target-session-selected" translate="no">​</a></h3>
<p>La primera vez que ejecutamos <code>agent</code> sin especificar con quién hablar:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">Error: No target session selected. Use --agent &lt;id&gt;, --session-key &lt;key&gt;,</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">--session-id &lt;id&gt;, or --to &lt;E.164&gt;. Run openclaw agents list to see agents.</span><br></div></code></pre></div></div>
<p><strong>Causa:</strong> <code>openclaw agent</code> ejecuta un turno contra un agente/sesión específico; sin
ningún canal configurado no hay un destino implícito.</p>
<p><strong>Solución:</strong> lista los agentes y pasa el id explícitamente:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">docker compose run --rm openclaw-cli agents list          # muestra: main (default)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">docker compose run --rm openclaw-cli agent --agent main --message "..."</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="El error &amp;quot;No target session selected&amp;quot;" src="https://wec.wiline.com/docs/es/assets/images/error2-no-session-c405ca89ca87bd411080165c514046e5.png" width="839" height="241" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-control-ui-secure-browser-context-required">3. Control UI: "Secure browser context required"<a href="https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/#3-control-ui-secure-browser-context-required" class="hash-link" aria-label="Enlace directo al 3. Control UI: &quot;Secure browser context required&quot;" title="Enlace directo al 3. Control UI: &quot;Secure browser context required&quot;" translate="no">​</a></h3>
<p>Al abrir la Control UI desde otra máquina (p. ej. macOS) en la IP de la instancia sobre HTTP
plano, la página se niega a conectar:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">Secure browser context required</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">This page is running over plain HTTP, so the browser cannot create the</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">device identity the Gateway expects.</span><br></div></code></pre></div></div>
<p><strong>Causa:</strong> los navegadores solo exponen las APIs de cifrado que OpenClaw necesita en
un <em>contexto seguro</em> — HTTPS, o <code>localhost</code>/<code>127.0.0.1</code>. Un <code>http://&lt;ip&gt;:18789</code>
remoto no es ninguno, así que el <strong>navegador</strong> lo bloquea, independientemente del
flag del servidor <code>gateway.controlUi.allowInsecureAuth</code>.</p>
<p><strong>Solución:</strong> tuneliza el puerto por SSH para que el navegador hable con <code>127.0.0.1</code>
(un contexto seguro). En tu máquina local:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">ssh -L 18789:127.0.0.1:18789 ubuntu@&lt;ip-de-tu-vm&gt;</span><br></div></code></pre></div></div>
<p>Luego abre <code>http://127.0.0.1:18789/#token=&lt;tu-token&gt;</code> localmente. La solución
definitiva a largo plazo es HTTPS mediante un proxy inverso — la próxima guía.</p>
<p><img decoding="async" loading="lazy" alt="El mensaje rojo &amp;quot;Secure browser context required&amp;quot; en la Control UI" src="https://wec.wiline.com/docs/es/assets/images/error3-secure-context-3e5792a56fae6a2ea2a8f9e0f0aa4c3f.png" width="658" height="895" class="img_ev3q"></p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="notas-de-seguridad">Notas de seguridad<a href="https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/#notas-de-seguridad" class="hash-link" aria-label="Enlace directo al Notas de seguridad" title="Enlace directo al Notas de seguridad" translate="no">​</a></h2>
<p>Al arrancar, el gateway registró advertencias que conviene atender antes de cualquier
exposición pública:</p>
<ul>
<li class=""><strong>Bind a una dirección no-loopback</strong> — el gateway escucha en la LAN. No expongas el
puerto 18789 a la Internet pública sin autenticación por delante.</li>
<li class=""><strong><code>gateway.controlUi.allowInsecureAuth=true</code></strong> — marcado como peligroso. Ejecuta
<code>docker compose run --rm openclaw-cli security audit</code>.</li>
<li class=""><strong><code>plugins.allow</code> está vacío</strong> — pueden autocargarse plugins no incluidos (p. ej.
<code>codex</code>). Define una lista explícita de ids de plugins de confianza.</li>
</ul>
<p>Endurecer esto detrás de un proxy inverso con HTTPS es el tema de la próxima guía.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="lo-que-has-logrado">Lo que has logrado<a href="https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/#lo-que-has-logrado" class="hash-link" aria-label="Enlace directo al Lo que has logrado" title="Enlace directo al Lo que has logrado" translate="no">​</a></h2>
<p>Pasaste de una Instancia WEC a un agente de IA autoalojado y funcional:</p>
<ul>
<li class="">Aprovisionaste y verificaste una VM Ubuntu en WEC</li>
<li class="">Levantaste el gateway de OpenClaw con Docker Compose y la imagen precompilada oficial</li>
<li class="">Conectaste tu propio proveedor de modelo (OpenAI) y lo confirmaste de extremo a
extremo — el agente respondió por la CLI y por la Control UI</li>
<li class="">Te topaste con (y resolviste) los problemas reales de permisos, sesión y contexto
seguro por el camino</li>
</ul>
<p>El agente es tuyo: tu clave, tu máquina, tus datos. A partir de aquí se trata de
hacerlo seguro, accesible y útil.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="qué-puedes-hacer-con-ello">¿Qué puedes hacer con ello?<a href="https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/#qu%C3%A9-puedes-hacer-con-ello" class="hash-link" aria-label="Enlace directo al ¿Qué puedes hacer con ello?" title="Enlace directo al ¿Qué puedes hacer con ello?" translate="no">​</a></h2>
<p>OpenClaw no es un chatbot — es un <strong>agente autónomo que puede actuar en tu máquina</strong>.
Ahora que está en marcha, esto es lo que desbloquea (algunas funciones necesitan
configuración adicional, y cubrimos las principales más adelante en esta serie):</p>
<ul>
<li class=""><strong>Ejecuta trabajo real en la máquina.</strong> Puede ejecutar comandos de shell y leer/escribir
archivos — <em>"revisa qué está ocupando el disco,"</em> <em>"muestra los logs y resume los
errores"</em> — con sandboxing configurable para cuánto le permites tocar.</li>
<li class=""><strong>Háblale desde tu teléfono.</strong> OpenClaw se conecta a <strong>WhatsApp, Telegram, Discord,
Slack, Signal e iMessage</strong>, así que delegas tareas conversando, sin SSH. <em>(Conectamos
Telegram en la siguiente guía.)</em></li>
<li class=""><strong>Navega y actúa en la web.</strong> La automatización de navegador integrada le permite
navegar sitios, rellenar formularios y extraer datos de forma autónoma.</li>
<li class=""><strong>Escribe y ejecuta código.</strong> Se integra con <strong>Claude Code</strong> para programación
autónoma — escribir y modificar código, ejecutar pruebas e incluso abrir PRs.</li>
<li class=""><strong>Trabaja en segundo plano, de forma proactiva.</strong> Funciona 24/7, puede ejecutar tareas
en segundo plano y hace "latidos" (heartbeat) en vez de solo reaccionar cuando le
escribes.</li>
<li class=""><strong>Crece contigo.</strong> Una biblioteca comunitaria de habilidades (<strong>ClawHub</strong>), herramientas
autogeneradas y orquestación multiagente lo hacen más capaz con el tiempo —
respaldado por memoria persistente que mantiene el contexto entre conversaciones.</li>
</ul>
<p>Usos reales típicos: gestión de correo y calendario, procesamiento de documentos,
tareas de infraestructura en la máquina, pipelines de contenido y pruebas de código
autónomas.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="qué-sigue">Qué sigue<a href="https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/#qu%C3%A9-sigue" class="hash-link" aria-label="Enlace directo al Qué sigue" title="Enlace directo al Qué sigue" translate="no">​</a></h2>
<p>Esta es la <strong>Parte 1</strong> de la serie <strong>Self-hosting OpenClaw</strong> sobre ejecutar
infraestructura de IA de nivel producción en una Instancia WEC. Próximamente:</p>
<ul>
<li class=""><strong>Parte 2 — <a class="" href="https://wec.wiline.com/docs/es/tutorials/secure-openclaw-caddy-https/">Asegura OpenClaw con un proxy inverso Caddy + HTTPS</a></strong> <em>(siguiente)</em> — pon HTTPS por delante del gateway para eliminar el truco de <code>allowInsecureAuth</code> y acceder a la Control UI desde cualquier lugar, de forma segura.</li>
<li class=""><strong>Parte 3 — <a class="" href="https://wec.wiline.com/docs/es/tutorials/add-telegram-channel-openclaw/">Añade un canal de Telegram</a></strong> — habla con tu agente desde el móvil.</li>
<li class=""><strong>Parte 4 — <a class="" href="https://wec.wiline.com/docs/es/tutorials/netbird-mesh-vpn-openclaw/">Haz OpenClaw privado con una VPN mesh NetBird</a></strong> — ponlo en una mesh privada y cierra los puertos públicos.</li>
</ul>
<p>Los archivos complementarios de esta guía — <code>docker-compose.yml</code> y <code>.env.example</code> —
viven en el repo de manifiestos de WiLine <em>(enlace pendiente con el repo)</em>.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="desmontaje">Desmontaje<a href="https://wec.wiline.com/docs/es/tutorials/deploy-openclaw-docker-compose/#desmontaje" class="hash-link" aria-label="Enlace directo al Desmontaje" title="Enlace directo al Desmontaje" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">docker compose down              # detiene y elimina los contenedores</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">docker compose down --rmi all -v # también elimina la imagen y los volúmenes</span><br></div></code></pre></div></div>
<p>Para eliminar la configuración/estado del host:
<code>rm -rf ~/.openclaw ~/.openclaw-auth-profile-secrets</code>. Para detener la facturación
por completo, elimina la VM desde el portal de WiLine.</p>]]></content>
        <author>
            <name>Rafael Fernandes</name>
            <uri>https://www.linkedin.com/in/rafaelmacariofernandes/</uri>
        </author>
        <category label="ai" term="ai"/>
        <category label="self-hosting" term="self-hosting"/>
        <category label="docker" term="docker"/>
        <category label="openclaw" term="openclaw"/>
        <category label="vps" term="vps"/>
    </entry>
</feed>