<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://v2yy.com/</id>
  <title type="text">杨洋的小站</title>
  <subtitle type="text">慢慢来，比较快</subtitle>
  <updated>2026-09-28T19:30:00.000Z</updated>
  <author><name>杨洋</name></author>
  <link rel="alternate" href="https://v2yy.com/"/>
  <link rel="self" href="https://v2yy.com/atom.xml"/>
  <generator uri="https://github.com/CuteLeaf/Firefly">Firefly v6.16.8</generator>
    <entry>
      <id>https://v2yy.com/posts/hermes-pitfalls/</id>
      <title type="text">Hermes Agent 踩坑实录：从安装到多端消息、服务器与 Cloudflare 全记录</title>
      <published>2026-09-28T19:30:00.000Z</published>
      <updated>2026-09-28T19:30:00.000Z</updated>
      <author><name>杨洋</name></author>
      <link rel="alternate" href="https://v2yy.com/posts/hermes-pitfalls/"/>
      <summary type="text">把安装 Hermes Agent 以来真实踩过的坑按「安装环境 / 模型凭证 / 消息通道 / 浏览器工具 / 远程运维 / Pages 迁移」分类整理，每一条都带症状、根因和解法。</summary>
      <content type="html"><![CDATA[<p>这台机器上的 Hermes Agent 已经陪我跑了两个月，从 macOS 本机到 Vultr VPS，从 Telegram/企业微信/微信多通道到 Cloudflare Pages。中间踩的坑不少，有的查了一小时才发现是”错误提示在骗人”。这篇按分类整理成清单，症状 → 根因 → 解法，遇到同款问题时直接对号入座。</p>
<section><h2>一、安装与本机环境<a href="#一安装与本机环境"><span>#</span></a></h2><section><h3>1. 安装体积到底多大？<a href="#1-安装体积到底多大"><span>#</span></a></h3><p><code>~/.hermes</code> 实测 <strong>8.5G</strong>，看着吓人但大头都有出处：</p><ul>
<li><code>hermes-agent/</code> 5.6G：本体源码，其中 <code>.git</code> 2.5G（升级历史）、<code>node_modules</code> 1.0G（桌面端/TUI）、<code>venv</code> 927M（Python 运行时）</li>
<li><code>tools/</code> 972M：自动下载的 chromium、独立 node、cua-driver 等</li>
<li><code>state.db</code> 304M：<strong>会话数据库，删了整个聊天记录就没了</strong>，别当缓存清</li>
</ul><p>真正能安全回收的只有 <code>cache/scratch</code> 和各种日志，软件本体约 7.5G 属正常水平。</p></section><section><h3>2. npm 全局装包 EACCES<a href="#2-npm-全局装包-eacces"><span>#</span></a></h3><p>macOS 下往 <code>/usr/local</code> 写全局包会权限报错。统一改用独立 prefix：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npm</span><span> </span><span>i</span><span> </span><span>-g</span><span> </span><span>&lt;pkg&gt;</span><span> </span><span>--prefix</span><span> </span><span>~/.npm-global</span></div></div><div><div><div>2</div></div><div><span># PATH 里加 ~/.npm-global/bin（写在 ~/.zprofile）</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>另一个坑：有些 CLI 自带的 <code>xxx update</code> 不认你的 prefix（比如 chatlab-cli），升级仍要回到 <code>npm i -g --prefix ~/.npm-global &lt;pkg&gt;@latest</code>。</p></section><section><h3>3. Node 版本大一统<a href="#3-node-版本大一统"><span>#</span></a></h3><p>机器上同时躺着 brew node、nvm node、pkg 安装的 node 时，脚本会以谁先入 PATH 说了算，构建行为玄学。收敛方案：nvm 设 default 版本 + 全局 CLI 全部搬进 <code>~/.npm-global</code>，然后卸掉 brew 版 node。系统残留的 <code>/usr/local/bin/node</code>（pkg 装的）最后要么删掉要么确保 PATH 顺序。</p></section></section>
<section><h2>二、模型 Provider 与凭证<a href="#二模型-provider-与凭证"><span>#</span></a></h2><section><h3>1. “No usable credentials” 的假警报（经典陷阱）<a href="#1-no-usable-credentials-的假警报经典陷阱"><span>#</span></a></h3><p>症状：<code>Could not resolve credentials for provider X: No usable credentials found</code>，提示你去设环境变量。</p><p>真相：<strong>key 可能完全有效</strong>。凭证池 <code>~/.hermes/auth.json</code> 的 <code>credential_pool.&lt;provider&gt;</code> 每项带 <code>last_status</code> / <code>failure_reason</code> 状态字段，一次 402（billing）会把它标成 <code>exhausted</code>，之后即使账户恢复，Hermes 仍按标记判定”无可用凭证”。</p><p>处理流程：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 1. 先证伪：直接拿 key 调一次 provider API，200 就说明 key 是好的</span></div></div><div><div><div>2</div></div><div><span># 2. 备份后重置</span></div></div><div><div><div>3</div></div><div><span>cp</span><span> </span><span>~/.hermes/auth.json</span><span> </span><span>~/.hermes/auth.json.bak</span></div></div><div><div><div>4</div></div><div><span>hermes</span><span> </span><span>auth</span><span> </span><span>reset</span><span> </span><span>&lt;provider&gt;</span><span>   </span><span># 实测可能不彻底，必须回读文件确认</span></div></div><div><div><div>5</div></div><div><span># 3. 不彻底就手改 JSON：把 last_status / failure_reason / last_error_* 全部置 null，request_count 归零</span></div></div><div><div><div>6</div></div><div><span># 4. 验证：hermes auth status + 实跑一次，输出里没有 "Switched to fallback" 才算过</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>2. CLI 通了、gateway 还在报错<a href="#2-cli-通了gateway-还在报错"><span>#</span></a></h3><p>gateway 是常驻进程，凭证状态在内存里；CLI 每次新进程读磁盘。清完磁盘状态后如果 gateway 仍报错，需要 <code>hermes gateway restart</code>。</p><p>重启多 profile 时别用裸 <code>--replace</code>——它会误杀默认网关（web-ui 托管会自动拉起的那个），要按 pid 文件精确 kill 再启动对应 profile。</p></section><section><h3>3. 自定义 provider 的模型在 /model 里看不到<a href="#3-自定义-provider-的模型在-model-里看不到"><span>#</span></a></h3><p><code>hermes model</code> 选择器只管官方 portal，自定义模型用别名暴露：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>hermes</span><span> </span><span>config</span><span> </span><span>set</span><span> </span><span>model_aliases.&lt;name&gt;.model</span><span> </span><span>'&lt;model-id&gt;'</span></div></div><div><div><div>2</div></div><div><span>hermes</span><span> </span><span>config</span><span> </span><span>set</span><span> </span><span>model_aliases.&lt;name&gt;.provider</span><span> </span><span>'&lt;provider&gt;'</span></div></div><div><div><div>3</div></div><div><span># 之后 /model &lt;name&gt; 任意平台可切</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>坑：一次性命令 <code>hermes chat -m &lt;alias&gt;</code> 在已有默认 provider 时<strong>不会解析用户别名</strong>，会静默归一化到默认模型。走 <code>/model</code> 或环境变量 <code>HERMES_INFERENCE_PROVIDER</code> 才可靠。</p></section><section><h3>4. 图像生成：Web UI 端点没起来怎么办<a href="#4-图像生成web-ui-端点没起来怎么办"><span>#</span></a></h3><p>走 <code>apikey-image-gen</code> 那条路时如果本机 8648 端口压根没监听（Web UI 没跑），不要死等——直接用 provider 的 <strong>chat completions 多模态路由</strong>生成图像。注意百炼这类中转端的响应是非标准结构：图片 URL 藏在 <code>output.choices[0].message.content[0].image</code>，不是 OpenAI 标准的 <code>choices</code> 顶层。</p></section></section>
<section><h2>三、消息通道（Telegram / 企业微信 / 微信）<a href="#三消息通道telegram--企业微信--微信"><span>#</span></a></h2><section><h3>1. “通道显示已连接，但消息永远不回”——先查 home_channel<a href="#1-通道显示已连接但消息永远不回先查-home_channel"><span>#</span></a></h3><p>最高优先级的静默故障：profile 的 <code>home_channel</code> 缺 <code>platform</code> 键 → <code>load_gateway_config()</code> 抛 <code>KeyError: 'platform'</code> → <strong>该 profile 所有适配器直接不启动</strong>，但界面上看着一切正常。</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>hermes</span><span> </span><span>--profile</span><span> </span><span>&lt;name&gt;</span><span> </span><span>config</span><span> </span><span>set</span><span> </span><span>platforms.wecom.home_channel.platform</span><span> </span><span>wecom</span></div></div><div><div><div>2</div></div><div><span># 然后重启 gateway；修完一个 profile 记得扫一遍所有 profile，同病往往成串</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>2. 第二高频：发送者没配对<a href="#2-第二高频发送者没配对"><span>#</span></a></h3><p>日志里出现 <code>Unauthorized user ... on wecom</code> 且对方收到 “pairing code: XXXX”，批准即可：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>hermes</span><span> </span><span>-p</span><span> </span><span>&lt;profile&gt;</span><span> </span><span>pairing</span><span> </span><span>approve</span><span> </span><span>wecom</span><span> </span><span>&lt;CODE&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>3. 群消息永远不回 ≠ DM 配对问题<a href="#3-群消息永远不回--dm-配对问题"><span>#</span></a></h3><p><code>group_policy: pairing</code>（默认）会<strong>丢弃全部群流量</strong>。逐群放行：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>hermes</span><span> </span><span>config</span><span> </span><span>set</span><span> </span><span>platforms.wecom.group_policy</span><span> </span><span>allowlist</span></div></div><div><div><div>2</div></div><div><span>hermes</span><span> </span><span>config</span><span> </span><span>set</span><span> </span><span>platforms.wecom.group_allow_from</span><span> </span><span>'["&lt;chatid&gt;"]'</span></div></div><div><div><div>3</div></div><div><span># 改完要 gateway restart（这项在适配器构造期读取）</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>4. 白名单类环境变量必须放全局 .env<a href="#4-白名单类环境变量必须放全局-env"><span>#</span></a></h3><p><code>WECOM_ALLOW_ALL_USERS=1</code> 这类开关由 gateway 进程 <code>os.getenv()</code> 读取，而 <strong>profile 的 .env 永远不会合并进 os.environ</strong>。写在 profile <code>.env</code> 里静默无效，只有 <code>~/.hermes/.env</code> 生效。</p><p>验证也别看 <code>/proc/&lt;pid&gt;/environ</code>——那是 exec 时快照，dotenv 启动后的改动看不见。以行为或 gateway.log 为准。</p></section><section><h3>5. 企微/微信刷屏 ”💻 Running …”<a href="#5-企微微信刷屏--running-"><span>#</span></a></h3><p>这些平台不能编辑已发送消息，工具进度每条都会变成新气泡。顶层 <code>display.tool_progress: all</code> 在桌面端很香，但会压过平台默认的静默档：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>hermes</span><span> </span><span>config</span><span> </span><span>set</span><span> </span><span>display.platforms.wecom.tool_progress</span><span> </span><span>false</span></div></div><div><div><div>2</div></div><div><span># display 配置每回合解析，不用重启</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>6. 微信 iLink 通道连发丢消息<a href="#6-微信-ilink-通道连发丢消息"><span>#</span></a></h3><p>同账号 30 秒内连发多条消息/媒体会被冷却丢弃，批量交付时要<strong>间隔发送</strong>，不要一个回合甩十条。</p></section></section>
<section><h2>四、浏览器自动化与多媒体工具<a href="#四浏览器自动化与多媒体工具"><span>#</span></a></h2><section><h3>1. 连主人自己的 Chrome：走 CDP，别拷 profile<a href="#1-连主人自己的-chrome走-cdp别拷-profile"><span>#</span></a></h3><p>云浏览器拿不到登录态时，优先 <code>browser.cdp_url = http://127.0.0.1:9222</code> 直连本机开调试端口的 Chrome。<code>use_real_profile</code>（拷贝真实 profile）在 Chrome 运行中必然被写锁卡死。切标签的 <code>Target.activateTarget</code> 不可靠，用 <code>new_tab()</code> 开新标签自带登录态。</p></section><section><h3>2. macOS 图片处理两连坑<a href="#2-macos-图片处理两连坑"><span>#</span></a></h3><ul>
<li><code>sips -s format webp</code> 在部分系统版本直接报 <code>Can't write format</code> → 改走 ffmpeg 或 PIL</li>
<li>ffmpeg 想编码 <strong>avif</strong> 需要 <code>libaom/encoder</code>，常见发行版没带 → 目标 <code>.avif</code> 时先试，失败退回 PNG 交给构建工具优化</li>
</ul></section><section><h3>3. 二维码裁剪：OpenCV 有边界，视觉模型补位<a href="#3-二维码裁剪opencv-有边界视觉模型补位"><span>#</span></a></h3><p><code>cv2.QRCodeDetector</code> 能解标准方形 QR（裁完还能解码自证），但对<strong>微信赞赏码这种圆形太阳码</strong>完全无效——它根本不是 QR 格式。可行流程：</p><ol>
<li>标准 QR：<code>detect()</code> 拿四角坐标 → 裁方块 + 白边静区 → <code>detectAndDecode</code> 验证解出的链接和原图一致</li>
<li>太阳码：<code>vision_analyze</code> 让多模态模型估圆心半径 → 按几何裁剪 → 再用视觉复检”无切边、无残留文字”</li>
</ol><p>最后从<strong>线上 URL</strong> 拉回来做一次端到端解码/复检，防 CDN 给你旧图。</p></section><section><h3>4. giscus 评论报 “app is not installed on this repository”<a href="#4-giscus-评论报-app-is-not-installed-on-this-repository"><span>#</span></a></h3><p>仓库开了 Discussions 也会报——<strong>giscus 这个 GitHub App 必须单独安装授权</strong>：<code>github.com/apps/giscus</code> → 只选博客仓库 → Install and Authorize（会要求 passkey 二次验证）。装完不用改任何代码，刷新页面评论区就出来了。</p></section></section>
<section><h2>五、远程服务器运维（VPS / SSH）<a href="#五远程服务器运维vps--ssh"><span>#</span></a></h2><section><h3>1. 海外 VPS 的 SSH 玄学超时<a href="#1-海外-vps-的-ssh-玄学超时"><span>#</span></a></h3><p>Vultr 东京机从国内连经常 <code>Connection timed out during banner exchange</code>，但 443 端口 curl 秒通。结论：<strong>线路抖动，不是机器死了</strong>，别上机排查——套一层 2~3 次重试循环即可。</p></section><section><h3>2. 内联 ssh 命令是引号地狱，永远走脚本 + stdin<a href="#2-内联-ssh-命令是引号地狱永远走脚本--stdin"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>ssh</span><span> </span><span>host</span><span> </span><span>'bash -s'</span><span> &lt; </span><span>/tmp/probe.sh</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>内联字符串里出现中文括号、<code>%{...}</code>、嵌套引号、<code>python3 -c</code> 时必炸。长命令还有 <code>Argument list too long</code> 风险。</p></section><section><h3>3. 构建类长任务要完全脱离 ssh 会话<a href="#3-构建类长任务要完全脱离-ssh-会话"><span>#</span></a></h3><p>直接 <code>nohup ... &amp;</code> 挂在 ssh 里，ssh 断开仍可能带走子进程：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>ssh</span><span> </span><span>host</span><span> </span><span>'cd /opt/x &amp;&amp; (setsid nohup bash -c "pnpm run build &amp;&amp; ...; echo EXIT=\$?" &gt;&gt; build.log 2&gt;&amp;1 &lt; /dev/null &amp;); echo started'</span></div></div><div><div><div>2</div></div><div><span># 之后另开连接 tail build.log 轮询</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>反面教材：<code>pkill -f xxx</code> 的匹配串也会命中你 ssh 命令自己，把会话杀死——用字符类打断如 <code>pkill -f "kev[.]serve"</code>。</p></section><section><h3>4. nginx 的 403 不是 404<a href="#4-nginx-的-403-不是-404"><span>#</span></a></h3><p>静态站上「目录存在但没有 index.html」返回 403。写了 <code>error_page 403 404 /404.html;</code> 状态码<strong>仍然是 403</strong>（原始码透传），必须加等号强制改写：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>error_page </span><span>403</span><span> </span><span>404</span><span> </span><span>=404</span><span> /404.html;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>5. Cloudflare 橙云下的证书与缓存<a href="#5-cloudflare-橙云下的证书与缓存"><span>#</span></a></h3><ul>
<li>HTTP-01 验证可以穿过 CF 代理正常签 Let’s Encrypt，源站选 <strong>Full</strong> 模式；但代理会把静态资源边缘缓存 4h，<strong>换图后 URL 加版本号</strong>（<code>alipay.png?v=20260928</code>）是最省事的破缓存手段，比登录后台刷缓存快</li>
<li>域名 NS 在 CF、解析走橙云时，<code>dig</code> 到的是 CF 边缘 IP 而不是你的 VPS——这是特性不是解析失败</li>
</ul></section><section><h3>6. 本机 DNS 会骗人<a href="#6-本机-dns-会骗人"><span>#</span></a></h3><p>家庭网络装了分流代理时，<code>dig</code> 本机可能返回 <code>198.18.x.x</code> 这类假 IP。验证公网解析一律走 DoH：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>curl</span><span> </span><span>-s</span><span> </span><span>"https://dns.google/resolve?name=example.com&amp;type=A"</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h2>六、静态站迁 Cloudflare Pages（本博客就是例子）<a href="#六静态站迁-cloudflare-pages本博客就是例子"><span>#</span></a></h2><p>Astro 静态站从 VPS 搬 Pages 的完整拼图：</p><ol>
<li><strong>仓库先干净</strong>：内容全部 commit + push（Pages 只认 git）</li>
<li><strong>构建环境</strong>：Pages 里设环境变量 <code>NODE_VERSION=22</code>；用 corepack 的话再加 <code>COREPACK_ENABLE_DOWNLOAD_PROMPT=0</code>，否则 <code>pnpm install</code> 会卡在交互式提问等不到人</li>
<li><strong>域名绑定</strong>：项目 → Domains → 设置自定义域；DNS 本来就在 CF 的域会自动把 A 记录换成 Pages CNAME</li>
<li><strong>www/apex 跳转放在 zone 规则做，别放 <code>_redirects</code></strong>——这个坑我们踩过：站级 <code>_redirects</code> 会连子路径一起 301，把两个入口都当正式站访问时行为诡异；正确姿势是 zone 层 Redirect Rule：<code>https://www.v2yy.com/* → https://v2yy.com/${1}</code> 301</li>
<li><strong>Astro 内容集合的日期校验</strong>：frontmatter <code>published: 2026-09-28 17:00</code> 会报 <code>Expected type "date", received "string"</code>——<strong>必须写到秒</strong> <code>17:00:00</code></li>
</ol><p>迁移后 VPS 不必退场：留一个 nginx 块把老域名 301 到新域，双轨跑一段，老链接全部无缝。</p></section>
<section><h2>七、工具层的一个隐蔽坑（写给用 AI 助手运维的人）<a href="#七工具层的一个隐蔽坑写给用-ai-助手运维的人"><span>#</span></a></h2><p>自动化助手往文件里写配置时，如果内容里有 <code>XXX_KEY = "..."</code> 这种<strong>长得像凭据的行</strong>，某些脱敏管线会静默把值替换成 <code>***</code> 再落盘——语法合法但内容已废，排查半天。对策：含敏感字样的文件生成后<strong>回读断言</strong>没有 <code>***</code>；凭据用 argv/环境注入而不是内联字面量。</p><hr /></section>
<section><h2>附：一条通用心法<a href="#附一条通用心法"><span>#</span></a></h2><p>上面一半的坑，根因都是**“表面的成功信号是假的”**：通道显示 connected 但没启动、CLI 成功但 gateway 没恢复、构建 exit 0 但产物是旧文件、dig 有返回但是代理假 IP。所以给自己立的规矩是——<strong>状态必须回读验证，证据必须交叉来源</strong>：写完读文件、部署完 curl 线上、清凭证后实跑一次、验解析同时看 DoH 和权威 NS。</p><p>慢一点，但每个”完成”都是真的。</p><blockquote><p>环境：macOS + Hermes Agent、Vultr Ubuntu 24.04、Cloudflare（DNS/代理/Pages）、Astro 7 + pnpm</p></blockquote></section>]]></content>
    </entry>
    <entry>
      <id>https://v2yy.com/posts/hello-world/</id>
      <title type="text">Hello World！本站开张啦</title>
      <published>2026-09-28T00:00:00.000Z</published>
      <updated>2026-09-28T00:00:00.000Z</updated>
      <author><name>杨洋</name></author>
      <link rel="alternate" href="https://v2yy.com/posts/hello-world/"/>
      <summary type="text">博客搭建完成，这里是第一篇文章，记录一下开站的想法。</summary>
      <content type="html"><![CDATA[<section><h2>为什么要建这个博客<a href="#为什么要建这个博客"><span>#</span></a></h2><p>一直想有个自己的地方，写点技术笔记、记录生活和思考。域名是 v2yy.com，v2 是新版本的意思，yy 是我的名字缩写喵～</p></section>
<section><h2>本站的技术栈<a href="#本站的技术栈"><span>#</span></a></h2><ul>
<li>主题：<a href="https://github.com/CuteLeaf/Firefly" target="_blank">Firefly</a>（基于 Astro + Fuwari 的清新博客模板）</li>
<li>部署：Vultr VPS 上 Astro 静态构建，nginx 托管，Cloudflare 代理 + Let’s Encrypt 证书</li>
<li>更新方式：服务器上改内容后一键构建发布</li>
</ul></section>
<section><h2>慢慢来，比较快<a href="#慢慢来比较快"><span>#</span></a></h2><p>这是给自己的座右铭。不追求日更，但求每篇都有干货。</p><p>接下来计划写这些方向：</p><ol>
<li>后端与运维实战踩坑记录</li>
<li>自动化工具与效率实践</li>
<li>一些生活随想</li>
</ol><p>敬请期待喵～</p></section>]]></content>
    </entry>
</feed>
