<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>第七曜变异点</title><link>https://86c9071e.7luminaries.pages.dev/</link><description>Recent content on 第七曜变异点</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Tue, 06 Jan 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://86c9071e.7luminaries.pages.dev/index.xml" rel="self" type="application/rss+xml"/><item><title>SillyTavern PWA 功能折腾记录</title><link>https://86c9071e.7luminaries.pages.dev/p/sillytavern-pwa-%E5%8A%9F%E8%83%BD%E6%8A%98%E8%85%BE%E8%AE%B0%E5%BD%95/</link><pubDate>Tue, 06 Jan 2026 00:00:00 +0000</pubDate><guid>https://86c9071e.7luminaries.pages.dev/p/sillytavern-pwa-%E5%8A%9F%E8%83%BD%E6%8A%98%E8%85%BE%E8%AE%B0%E5%BD%95/</guid><description>&lt;p>一直很喜欢 L 站支持的 PWA 功能，然后想在 SillyTavern 中也使用它。昨天尝试了一下，发现 SillyTavern 本身就支持 PWA 功能。能够直接用 Chrome 或 Firefox “安装”到桌面。“安装”到桌面后就非常沉浸了，会全屏显示，隐藏地址栏。&lt;/p>
&lt;hr>
&lt;ul>
&lt;li>推荐使用 Chrome 或 Chromium 内核浏览器，对 PWA 支持比较好。&lt;/li>
&lt;li>不推荐 Firefox，除非你是使用的在线的服务。Firefox 对自签证书支持较差。&lt;/li>
&lt;li>Safari 没有测试，下面不会提及。&lt;/li>
&lt;li>测试使用的是 Windows 11 和 HyperOS 3（Android 16）。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="在线版-sillytavern">在线版 SillyTavern
&lt;/h2>&lt;p>如果使用的是在线的或者别人搭建的 SillyTavern 服务的话，如果是通过 &lt;code>https&lt;/code> 连接的，应该可以直接安装（Chrome、Firefox）。&lt;/p>
&lt;hr>
&lt;h2 id="本地运行的情况">本地运行的情况
&lt;/h2>&lt;p>本地运行的话，可以采用下面的方案之一（本地我个人只推荐用 Chrome，Firefox 对自签证书支持不太好）。
方案一比较麻烦，但相对安全一点。方案一安全的前提是妥善保管 CA 证书的私钥文件。方案一也可以不使用 OpenSSL，换成 mkcert，当然内容会不一样。
如果不介意 &lt;code>http&lt;/code> 的话，直接使用方案二更方便。&lt;/p>
&lt;h3 id="方案一https在本地创建自签证书">方案一（Https）：在本地创建自签证书
&lt;/h3>&lt;blockquote>
&lt;p>需要一个可以使用 OpenSSL 的环境。如果你安装了 Git，一般都会带着它。
需要在路由器的 DHCP 设置里面给 PC 分配静态 IP（按 MAC 绑定 IP），让 PC 始终拿到内网的同一个 IP。
大概逻辑是利用 OpenSSL 在本地生成一套自签 CA（1年有效期），并用它签发服务端证书（1年有效期），服务端证书绑定的 IP 是 PC 的IP。&lt;/p>&lt;/blockquote>
&lt;h4 id="pc-端配置">PC 端配置
&lt;/h4>&lt;h5 id="第一部分ca-证书">第一部分：CA 证书
&lt;/h5>&lt;ol>
&lt;li>进入安装 SillyTavern 的根目录。&lt;/li>
&lt;li>创建新的文件夹，重命名为 &lt;code>certs&lt;/code>。&lt;/li>
&lt;li>进入 &lt;code>certs&lt;/code> 文件夹，右键选择新建文本文档，重命名为 &lt;code>ca.conf&lt;/code>。&lt;/li>
&lt;li>将下面的内容复制到 &lt;code>ca.conf&lt;/code> 中，并保存。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="k">[req]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">distinguished_name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">dn&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">x509_extensions&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">v3_ca&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">no&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">[dn]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">C&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">CN&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">O&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">SillyTavern&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">OU&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">Home&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">CN&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">SillyTavern Home CA&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">[v3_ca]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">basicConstraints&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">critical, CA:TRUE, pathlen:0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">keyUsage&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">critical, keyCertSign, cRLSign&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">subjectKeyIdentifier&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">hash&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">authorityKeyIdentifier&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">keyid:always,issuer&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;ol start="5">
&lt;li>在 &lt;code>certs&lt;/code> 目录的空白处右键，选择 &lt;strong>Open Git Bash here&lt;/strong>。&lt;/li>
&lt;li>在弹出的窗口中输入下面的指令。这条指令会根据 &lt;code>ca.conf&lt;/code> 生成两个密钥文件。
&lt;ul>
&lt;li>公钥：&lt;code>ca.crt&lt;/code> 是包含公钥的 CA 证书，后续要把它导入到手机中&lt;/li>
&lt;li>私钥：&lt;strong>&lt;code>ca.key&lt;/code>&lt;/strong> &lt;strong>是私钥，务必妥善保存，不要把它导入到手机中。推荐执行下面完全部步骤后把它从电脑上直接删除。&lt;/strong>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">openssl genrsa -aes256 -out ca.key &lt;span class="m">4096&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">openssl req -x509 -new -days &lt;span class="m">365&lt;/span> -key ca.key -out ca.crt -config ca.conf -sha256
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;ol start="7">
&lt;li>执行上面的命令会提示 &lt;code>Enter PEM pass phrase:&lt;/code>，就是让你输密码，输入完成后需要回车。总共三次，输入、确认、再次输入。需要记住这个密码。&lt;/li>
&lt;/ol>
&lt;h5 id="第二部分服务端证书">第二部分：服务端证书
&lt;/h5>&lt;ol>
&lt;li>回到 &lt;code>certs&lt;/code> 文件夹，右键选择新建文本文档，重命名为 &lt;code>server.conf&lt;/code>。&lt;/li>
&lt;li>将下面的内容复制到 &lt;code>server.conf&lt;/code> 中，并保存。需要手动修改其中的 IP 地址。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="k">[req]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">distinguished_name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">dn&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">req_extensions&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">v3_server&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">no&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">[dn]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">C&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">CN&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">O&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">SillyTavern&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">OU&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">Server&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 请确保下面的 IP 替换为你的主机 IP。比如路由器给电脑分配的 IP 是 192.168.1.1，就修改为 192.168.1.1。&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">CN&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">192.168.x.xx&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">[v3_server]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">basicConstraints&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">critical, CA:FALSE&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">keyUsage&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">critical, digitalSignature, keyEncipherment&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">extendedKeyUsage&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">serverAuth&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">subjectAltName&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">@alt_names&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">[alt_names]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 请确保下面的 IP 替换为你的主机 IP。比如路由器给电脑分配的 IP 是 192.168.1.1，就修改为 192.168.1.1。&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">IP.1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">192.168.x.xx&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;ol start="3">
&lt;li>在 &lt;code>certs&lt;/code> 目录的空白处右键，选择 &lt;strong>Open Git Bash here&lt;/strong>。&lt;/li>
&lt;li>在弹出的窗口中输入下面的指令。这条指令会根据 &lt;code>server.conf&lt;/code> 生成两个密钥文件（专门给 SillyTavern 用的）。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">openssl genrsa -out server.key &lt;span class="m">2048&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">openssl req -new -key server.key -out server.csr -config server.conf
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;ol start="5">
&lt;li>然后执行下面的命令。执行后会提示 &lt;code>Enter PEM pass phrase:&lt;/code>，输入第 7 步设置的密码。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -out server.crt -days &lt;span class="m">365&lt;/span> -sha256 -extfile server.conf -extensions v3_server
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;ol start="6">
&lt;li>最后只需要保留 &lt;code>ca.crt&lt;/code>、&lt;code>server.crt&lt;/code> 和 &lt;code>server.key&lt;/code> 就行。&lt;strong>其他全部删除，尤其是 &lt;code>ca.key&lt;/code>，不建议联网的 PC 上保留这个文件。&lt;/strong>
&lt;ul>
&lt;li>我选择删除了 &lt;code>ca.key&lt;/code> 这个文件的话是为了降低误用风险，代价是后续 IP 变化/一年期限到了的话需要从头跑一遍上面的整个流程。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h4 id="在-sillytavern-中开启-ssl">在 SillyTavern 中开启 SSL：
&lt;/h4>&lt;ol>
&lt;li>进入安装 SillyTavern 的根目录。&lt;/li>
&lt;li>找到 &lt;code>config.yaml&lt;/code> 文件。&lt;/li>
&lt;li>打开 &lt;code>config.yaml&lt;/code> 文件，搜索 &lt;code>ssl&lt;/code>。&lt;/li>
&lt;li>找到跟下面一致的配置文件，然后把配置改成下面这样。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">ssl&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">enabled&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">certPath&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;certs/server.crt&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">keyPath&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;certs/server.key&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h4 id="手机端配置android">手机端配置（Android）：
&lt;/h4>&lt;ol>
&lt;li>关闭手机上的 Chrome，并确保 Chrome 的后台也被杀死了。&lt;/li>
&lt;li>将之前生成的 &lt;code>ca.crt&lt;/code> 文件导入到手机。&lt;/li>
&lt;li>进入手机的设置，直接搜索 &lt;strong>证书&lt;/strong>。&lt;/li>
&lt;li>找到 &lt;strong>安装证书&lt;/strong>，选择 &lt;strong>CA 证书&lt;/strong>。&lt;/li>
&lt;li>无视警告或者选择仍然安装（&lt;strong>对于这种局域网内自己操作的情况是相对安全的，如果是来路不明的证书不要安装&lt;/strong>）。&lt;/li>
&lt;li>然后选择之前导入的 &lt;code>ca.crt&lt;/code> 文件。&lt;/li>
&lt;li>打开 Chrome，现在再连接之前的 SillyTavern 监听地址应该就可以设置 PWA 了（选择 &lt;strong>添加到主屏幕&lt;/strong>）。
&lt;ul>
&lt;li>需要注意，之前是使用 &lt;code>http&lt;/code> 连接的，现在已经是 &lt;code>https&lt;/code> 了。例如之前是 &lt;code>http://192.168.1.1:8000&lt;/code>，现在就改为 &lt;code>https://192.168.1.1:8000&lt;/code>。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h3 id="方案二http">方案二（Http）：
&lt;/h3>&lt;blockquote>
&lt;p>此方案无需生成证书，通过浏览器 Flags 强制开启。这个选项可能会因为 Chrome 版本更新而失效。&lt;/p>&lt;/blockquote>
&lt;ol>
&lt;li>在地址栏输入 &lt;code>chrome://flags&lt;/code> （Chromium 内核的都一样）。&lt;/li>
&lt;li>搜索或找到其中的 &lt;code>Insecure origins treated as secure&lt;/code> 。&lt;/li>
&lt;li>在这个选项的空白框处输入你本地链接 SillyTavern 的地址。
&lt;ul>
&lt;li>例如：你手机连接的地址是 &lt;code>http://192.168.1.1:8000&lt;/code> ，就在这个空白框里面输入 &lt;code>http://192.168.1.1:8000&lt;/code> 。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>将边上的 &lt;strong>已停用&lt;/strong> 更改为 &lt;strong>已启动&lt;/strong>。&lt;/li>
&lt;li>重启浏览器。&lt;/li>
&lt;/ol></description></item><item><title>适合个人开发者的大善人</title><link>https://86c9071e.7luminaries.pages.dev/p/%E9%80%82%E5%90%88%E4%B8%AA%E4%BA%BA%E5%BC%80%E5%8F%91%E8%80%85%E7%9A%84%E5%A4%A7%E5%96%84%E4%BA%BA/</link><pubDate>Mon, 15 Dec 2025 00:00:00 +0000</pubDate><guid>https://86c9071e.7luminaries.pages.dev/p/%E9%80%82%E5%90%88%E4%B8%AA%E4%BA%BA%E5%BC%80%E5%8F%91%E8%80%85%E7%9A%84%E5%A4%A7%E5%96%84%E4%BA%BA/</guid><description>&lt;p>下面是一些适合个人开发者使用的不需要绑卡就能使用部分服务的平台。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>平台 / 方案&lt;/th>
&lt;th>类型 / 用途&lt;/th>
&lt;th>免费额度 / 限制&lt;/th>
&lt;th>绑卡要求&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Cloudflare Workers&lt;/td>
&lt;td>边缘 Serverless 计算&lt;/td>
&lt;td>100,000 请求/日；每次请求 10ms CPU；静态资源请求免费且不限量&lt;/td>
&lt;td>无需绑卡&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Cloudflare Pages&lt;/td>
&lt;td>静态网站托管、Jamstack、Pages Functions&lt;/td>
&lt;td>500 次构建/月；1 个并发构建；每项目 100 个自定义域名；静态请求和带宽不限量&lt;/td>
&lt;td>无需绑卡&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Netlify Free&lt;/td>
&lt;td>网站托管、函数计算、边缘函数&lt;/td>
&lt;td>300 credits/月；官方 Free 方案说明包含 100GB 带宽、300 分钟构建时间、125,000 次 Functions、100 万次 Edge Functions、10GB 存储&lt;/td>
&lt;td>无需绑卡&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Vercel Hobby&lt;/td>
&lt;td>前端托管、Serverless 函数&lt;/td>
&lt;td>100 万次函数调用；4 CPU 小时；360 GB 小时内存；100GB 快速数据传输；6,000 分钟构建时间&lt;/td>
&lt;td>无需绑卡&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GitHub Actions&lt;/td>
&lt;td>CI/CD 自动化构建与部署&lt;/td>
&lt;td>2,000 分钟/月；500MB Artifact 存储；10GB 缓存存储&lt;/td>
&lt;td>无需绑卡&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Supabase Free&lt;/td>
&lt;td>后端即服务、数据库、用户认证、对象存储、边缘函数&lt;/td>
&lt;td>500MB 数据库；50,000 月活跃用户；1GB 存储；10GB 总带宽；500,000 次 Edge Function 调用；最多 2 个活跃项目&lt;/td>
&lt;td>无需绑卡&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Neon Free&lt;/td>
&lt;td>Serverless PostgreSQL、用户认证&lt;/td>
&lt;td>100 个项目；每项目 100 CU-hours/月；每项目 0.5GB 存储；Neon Auth 支持 60,000 月活跃用户&lt;/td>
&lt;td>无需绑卡&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Turso Free&lt;/td>
&lt;td>云端 SQLite 数据库、边缘同步&lt;/td>
&lt;td>100 个数据库；5GB 总存储；每月 5 亿行读取；每月 1,000 万行写入；3GB 同步流量/月；1 天时间点恢复&lt;/td>
&lt;td>无需绑卡&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Upstash Redis Free&lt;/td>
&lt;td>Redis 缓存、限流&lt;/td>
&lt;td>1 个数据库；256MB 数据；500,000 条命令/月；10GB 月带宽&lt;/td>
&lt;td>无需绑卡&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Tailscale Personal&lt;/td>
&lt;td>VPN、零信任组网、内网访问&lt;/td>
&lt;td>6 个用户；用户设备不限量；50 个带标签资源；1,000 分钟/月临时资源&lt;/td>
&lt;td>无需绑卡&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>PostHog Free&lt;/td>
&lt;td>产品分析、用户行为分析&lt;/td>
&lt;td>1 个项目；产品分析 1,000,000 events/月；1 年数据保留；用量封顶在免费层&lt;/td>
&lt;td>无需绑卡&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Sentry Developer&lt;/td>
&lt;td>应用监控、错误追踪、链路追踪&lt;/td>
&lt;td>1 个用户；5,000 个错误事件；5GB 日志；5GB 应用指标；500 万 spans；50 个回放；1 个可用性监控；1 个定时任务监控&lt;/td>
&lt;td>无需绑卡&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table></description></item><item><title>Using AutoHotkey to Quickly Switch Between Multiple Input Languages</title><link>https://86c9071e.7luminaries.pages.dev/p/using-autohotkey-to-quickly-switch-between-multiple-input-languages/</link><pubDate>Mon, 10 Nov 2025 00:00:00 +0000</pubDate><guid>https://86c9071e.7luminaries.pages.dev/p/using-autohotkey-to-quickly-switch-between-multiple-input-languages/</guid><description>&lt;blockquote>
&lt;p>提示: 本文英文译文由 LLM（大语言模型）辅助翻译，可能存在不准确之处，请以中文原文为准。&lt;br>
Note: The English translation in this post was assisted by an LLM and may contain inaccuracies.&lt;br>&lt;/p>&lt;/blockquote>
&lt;hr>
&lt;h2 id="why-abandon-windows-native-switching">Why Abandon Windows&amp;rsquo; Native Switching?
&lt;/h2>&lt;p>Windows&amp;rsquo; default input language switching hotkeys become painfully inefficient once you have more than two languages. I regularly type in three languages, and using the default shortcut to cycle through them was frustrating. So I used AutoHotkey to repurpose a few spare keys on my keyboard to switch directly to a specific input language/keyboard layout. This turns sequential (cycling) switching into deterministic switching: press one key and land on the language you want.&lt;/p>
&lt;h2 id="my-personal-solution">My Personal Solution
&lt;/h2>&lt;p>My keyboard is a 108-key model with four built-in multimedia keys. I rarely use these keys, so I chose them as my language switching keys. With AutoHotkey, I mapped each key to switch directly to a specific input language/keyboard layout.&lt;/p>
&lt;p>My mappings are as follows:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Key&lt;/th>
&lt;th>Switch to&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>Mute&lt;/code>&lt;/td>
&lt;td>&lt;code>English Input&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>Volume -&lt;/code>&lt;/td>
&lt;td>&lt;code>Chinese Input&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>Volume +&lt;/code>&lt;/td>
&lt;td>&lt;code>Japanese Input&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="implementation-logic">Implementation Logic
&lt;/h3>&lt;p>I use the &lt;code>PostMessage&lt;/code> function to send a system input-language change request to the active window. The target language&amp;rsquo;s LCID is included in the parameters, which is what makes the switch deterministic.&lt;/p>
&lt;p>The specific code is as follows:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-ahk" data-lang="ahk">&lt;span class="line">&lt;span class="cl">&lt;span class="n">#Requires&lt;/span> &lt;span class="n">AutoHotkey&lt;/span> &lt;span class="n">v2&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="c1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; Force only one script instance to run.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; If the script is already running, launching it again will automatically replace the old instance.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; This is convenient when you tweak the script and want the changes to take effect immediately.&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">#SingleInstance&lt;/span> &lt;span class="n">Force&lt;/span>&lt;span class="c1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; --- Key Mapping Section ---
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; Logic: Intercept media keys -&amp;gt; Call switch function -&amp;gt; Pass the corresponding language ID (LCID)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; Note: The original media functions of these keys (mute / volume control) will be completely overridden.&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nl">Volume_Mute::&lt;/span>&lt;span class="n">SwitchToLang&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mh">0x0409&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="c1"> ; 0x0409 = English (United States)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nl">Volume_Down::&lt;/span>&lt;span class="n">SwitchToLang&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mh">0x0804&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="c1"> ; 0x0804 = Chinese (Mainland China)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nl">Volume_Up::&lt;/span>&lt;span class="n">SwitchToLang&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mh">0x0411&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="c1"> ; 0x0411 = Japanese
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; --- Core Function ---&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="n">SwitchToLang&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LangID&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="c1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"> ; PostMessage: Places a message into the target window&amp;#39;s message queue (returns immediately, fast)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"> ; Parameter details:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"> ; 1. 0x50 : Message code WM_INPUTLANGCHANGEREQUEST (request an input language change)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"> ; 2. 0 : wParam (system flag). 0 means &amp;#34;use default behavior&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"> ; 3. LangID : lParam (language identifier), e.g., 0x0409 passed in above
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"> ; 4. (empty): Control (control name). Left empty since we send to the whole window
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"> ; 5. &amp;#34;A&amp;#34; : The currently active window&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">PostMessage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mh">0x50&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">LangID&lt;/span>&lt;span class="p">,,&lt;/span> &lt;span class="s">&amp;#34;A&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="common-lcid-reference">Common LCID Reference
&lt;/h3>&lt;p>You can replace the LCIDs according to your needs.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Language/Layout&lt;/th>
&lt;th>LCID&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Chinese (Simplified)&lt;/td>
&lt;td>&lt;code>0x0804&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>English (United States)&lt;/td>
&lt;td>&lt;code>0x0409&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Japanese&lt;/td>
&lt;td>&lt;code>0x0411&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Korean&lt;/td>
&lt;td>&lt;code>0x0412&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Chinese (Traditional - Taiwan)&lt;/td>
&lt;td>&lt;code>0x0404&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Chinese (Traditional - Hong Kong)&lt;/td>
&lt;td>&lt;code>0x0C04&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="scope-of-application--limitations">Scope of Application &amp;amp; Limitations
&lt;/h3>&lt;p>This method switches the &lt;strong>input language / keyboard layout&lt;/strong>. It works best when each language maps to a single input method:&lt;/p>
&lt;ul>
&lt;li>✅ Suitable: One language → one IME/layout (e.g., Chinese only uses Microsoft Pinyin, Japanese only uses Microsoft Japanese, English only uses the US keyboard)&lt;/li>
&lt;li>❌ Not ideal: One language → multiple IMEs (e.g., Chinese has both Rime and Microsoft Pinyin). If you want to switch to a specific IME within the same language, this approach is usually not enough.&lt;/li>
&lt;/ul>
&lt;p>In the second case, you typically need to use &lt;strong>HKL&lt;/strong> to target a specific input method. I haven&amp;rsquo;t needed that, so I won&amp;rsquo;t expand on it here.&lt;/p>
&lt;h2 id="how-to-find-key-names-to-customize-your-mappings">How to Find Key Names to Customize Your Mappings?
&lt;/h2>&lt;p>Use AutoHotkey&amp;rsquo;s built-in &lt;strong>Key History and Script Info&lt;/strong> window.&lt;/p>
&lt;ol>
&lt;li>First run the script above (or any AutoHotkey script)&lt;/li>
&lt;li>Find the AutoHotkey icon in the taskbar&lt;/li>
&lt;li>Right-click and select &lt;code>Open&lt;/code> or &lt;code>Help&lt;/code> (may vary by version)&lt;/li>
&lt;li>In the window that opens, click &lt;code>View&lt;/code> at the top&lt;/li>
&lt;li>Select &lt;code>Key history and script info&lt;/code>&lt;/li>
&lt;li>Press the key you want to bind, then refresh the page (default: F5). You&amp;rsquo;ll see the corresponding name in the log.&lt;/li>
&lt;/ol>
&lt;p>Once you know the key name, you can modify the code above (like this):&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-ahk" data-lang="ahk">&lt;span class="line">&lt;span class="cl">&lt;span class="nl">YourKeyName::&lt;/span>&lt;span class="n">SwitchToLang&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mh">0x0409&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="how-to-set-up-auto-start-on-boot">How to Set Up Auto-Start on Boot?
&lt;/h2>&lt;p>There are two methods below: placing it in the Startup folder, or using Task Scheduler.&lt;/p>
&lt;h3 id="method-1-place-in-the-startup-folder">Method 1: Place in the Startup Folder
&lt;/h3>&lt;ol>
&lt;li>Press &lt;code>Win + R&lt;/code>&lt;/li>
&lt;li>Enter &lt;code>shell:startup&lt;/code>&lt;/li>
&lt;li>Drop a shortcut to your &lt;code>.ahk&lt;/code> script into that folder&lt;/li>
&lt;/ol>
&lt;p>This method is very simple. However, in some apps/windows, administrator privileges may be required for the script to work.&lt;/p>
&lt;h3 id="method-2-task-scheduler">Method 2: Task Scheduler
&lt;/h3>&lt;p>If you find that some apps/windows can&amp;rsquo;t use this script, you may need to run the AutoHotkey script with administrator privileges.&lt;/p>
&lt;p>General approach:&lt;/p>
&lt;ol>
&lt;li>Use Windows search to find &lt;strong>Task Scheduler&lt;/strong>, then open it&lt;/li>
&lt;li>Select &lt;strong>Create Task&lt;/strong> (not &lt;strong>Create Basic Task&lt;/strong>)&lt;/li>
&lt;li>Name it whatever you want&lt;/li>
&lt;li>In the &lt;strong>General&lt;/strong> tab → &lt;strong>Security options&lt;/strong>, check &lt;strong>Run only when user is logged on&lt;/strong>, and check &lt;strong>Run with highest privileges&lt;/strong>&lt;/li>
&lt;li>Switch to the &lt;strong>Actions&lt;/strong> tab, click &lt;strong>New&lt;/strong>, and set the program/script to your &lt;code>.ahk&lt;/code> file path&lt;/li>
&lt;/ol>
&lt;blockquote>
&lt;p>&lt;strong>Pro tip:&lt;/strong> There&amp;rsquo;s a &lt;strong>Hidden&lt;/strong> option in the &lt;strong>General&lt;/strong> tab. If you check it, Task Scheduler will start the task without popping up a UAC confirmation window.&lt;/p>&lt;/blockquote>
&lt;h2 id="potential-risks">Potential Risks
&lt;/h2>&lt;p>Tools like AutoHotkey may be flagged as cheating tools by anti-cheat systems in certain games. If your game uses aggressive anti-cheat, it&amp;rsquo;s safer not to use AutoHotkey scripts—or exit AutoHotkey while playing.&lt;/p></description></item><item><title>使用 AHK 来实现多语言输入法快捷切换</title><link>https://86c9071e.7luminaries.pages.dev/p/%E4%BD%BF%E7%94%A8-ahk-%E6%9D%A5%E5%AE%9E%E7%8E%B0%E5%A4%9A%E8%AF%AD%E8%A8%80%E8%BE%93%E5%85%A5%E6%B3%95%E5%BF%AB%E6%8D%B7%E5%88%87%E6%8D%A2/</link><pubDate>Wed, 05 Nov 2025 00:00:00 +0000</pubDate><guid>https://86c9071e.7luminaries.pages.dev/p/%E4%BD%BF%E7%94%A8-ahk-%E6%9D%A5%E5%AE%9E%E7%8E%B0%E5%A4%9A%E8%AF%AD%E8%A8%80%E8%BE%93%E5%85%A5%E6%B3%95%E5%BF%AB%E6%8D%B7%E5%88%87%E6%8D%A2/</guid><description>&lt;h2 id="为何抛弃-windows-原生切换">为何抛弃 Windows 原生切换？
&lt;/h2>&lt;p>Windows 默认的输入法切换键位在超过两种语言时，切换效率会变得极其低下。俺是有三种语言的输入需求，使用默认的切换键位痛苦不已。便借助 AutoHotKey 实现了使用键盘上多余的键位来进行输入法切换的功能。从“轮询切换”变为“确定性切换”。&lt;/p>
&lt;h2 id="个人的解决方法">个人的解决方法
&lt;/h2>&lt;p>俺的键盘是 108 键的，它自带了 4 个多媒体按键。这几个按键对于我来说是低频按键，因此我选择它们作为我的语言切换按键。俺使用了 AutoHotKey 将这几个键位映射为了不同输入法的切换键。&lt;/p>
&lt;p>俺的映射如下：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>按键&lt;/th>
&lt;th>切换到&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>静音&lt;/code>&lt;/td>
&lt;td>&lt;code>英文输入&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>音量 -&lt;/code>&lt;/td>
&lt;td>&lt;code>中文输入&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>音量 +&lt;/code>&lt;/td>
&lt;td>&lt;code>日语输入&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="实现逻辑">实现逻辑
&lt;/h3>&lt;p>使用 &lt;code>PostMessage&lt;/code> 函数，直接伪装成系统，向窗口发送一条切换输入语言的消息。并在参数里面附带目标语言的 LCID。这样就可以做到确定性的切换。&lt;/p>
&lt;p>具体代码如下。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-ahk" data-lang="ahk">&lt;span class="line">&lt;span class="cl">&lt;span class="n">#Requires&lt;/span> &lt;span class="n">AutoHotkey&lt;/span> &lt;span class="n">v2&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="c1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; 强制只运行一个脚本实例。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; 如果脚本已经在运行，再次启动会自动替换旧实例，方便修改代码后直接生效。&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">#SingleInstance&lt;/span> &lt;span class="n">Force&lt;/span>&lt;span class="c1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; --- 键位映射区域 ---
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; 逻辑：拦截媒体键 -&amp;gt; 调用切换函数 -&amp;gt; 传入对应的语言 ID (LCID)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; 注意：这些键的原始媒体功能（静音/调节音量）将被完全覆盖。&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nl">Volume_Mute::&lt;/span>&lt;span class="n">SwitchToLang&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mh">0x0409&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="c1"> ; 0x0409 = 英语 (美国)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nl">Volume_Down::&lt;/span>&lt;span class="n">SwitchToLang&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mh">0x0804&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="c1"> ; 0x0804 = 中文 (中国大陆)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nl">Volume_Up::&lt;/span>&lt;span class="n">SwitchToLang&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mh">0x0411&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="c1"> ; 0x0411 = 日语
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; --- 核心功能函数 ---&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="n">SwitchToLang&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LangID&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="c1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"> ; PostMessage: 将消息放入目标窗口的消息队列中（不等待结果直接返回，速度快）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"> ; 参数详解：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"> ; 1. 0x50 : 消息代码 WM_INPUTLANGCHANGEREQUEST (请求切换输入语言)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"> ; 2. 0 : wParam (系统标志位)，0 表示使用默认行为
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"> ; 3. LangID : lParam (语言标识符)，即上面传入的 0x0409 等
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"> ; 4. (空) : Control (控件名)，此处留空因为是对整个窗口发送
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"> ; 5. &amp;#34;A&amp;#34; : 当前活动窗口&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">PostMessage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mh">0x50&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">LangID&lt;/span>&lt;span class="p">,,&lt;/span> &lt;span class="s">&amp;#34;A&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="常用的-lcid-参考如下">常用的 LCID 参考如下
&lt;/h3>&lt;p>可以根据自己的需求替换其中的 LCID。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>语言/布局&lt;/th>
&lt;th>LCID&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>中文 (简体)&lt;/td>
&lt;td>&lt;code>0x0804&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>英文 (美国)&lt;/td>
&lt;td>&lt;code>0x0409&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>日文&lt;/td>
&lt;td>&lt;code>0x0411&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>韩文&lt;/td>
&lt;td>&lt;code>0x0412&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>中文 (繁体 - 台湾)&lt;/td>
&lt;td>&lt;code>0x0404&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>中文 (繁体 - 香港)&lt;/td>
&lt;td>&lt;code>0x0C04&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="适用范围--局限">适用范围 &amp;amp; 局限
&lt;/h3>&lt;p>这个方法切的是“语言/键盘布局”，所以在下面这种情况下体验最好：&lt;/p>
&lt;ul>
&lt;li>✅ 适合：一个语言对应一个输入法的情况（例如：中文只有微软拼音、日语只有微软日语、英文只有美式键盘）&lt;/li>
&lt;li>❌ 不太适合：同一种语言下有多个输入法的情况（例如：中文有小狼毫、微软拼音两种输入法。然后你想指定切换到小狼毫的话，这个方法就不太适合）&lt;/li>
&lt;/ul>
&lt;p>如果是上面第二种情况的话，通常需要用 HKL 去定位到具体的输入法。我没有遇到这个需求，所以也不展开讲了。&lt;/p>
&lt;h2 id="如何找按键名称方便自己该映射">如何找“按键名称”方便自己该映射？
&lt;/h2>&lt;p>用 AHK 自带的 Key history and script info。&lt;/p>
&lt;ol>
&lt;li>先运行上面的脚本（或者随便一个 AHK 的脚本）&lt;/li>
&lt;li>在任务栏中找到 AHK 图标&lt;/li>
&lt;li>右键选择 &lt;code>Open&lt;/code> 或 &lt;code>Help&lt;/code>（不同版本可能有差异）&lt;/li>
&lt;li>然后在打开的界面顶部选择 &lt;code>View&lt;/code>&lt;/li>
&lt;li>选择其中的 &lt;code>Key history and script info&lt;/code>&lt;/li>
&lt;li>然后按下你想绑定的按键，再刷新页面（默认是 F5）。就能看到界面日志里面对应的名称。&lt;/li>
&lt;/ol>
&lt;p>知道名称之后，你就可以修改上面的代码（像下面这样）：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-ahk" data-lang="ahk">&lt;span class="line">&lt;span class="cl">&lt;span class="nl">YourKeyName::&lt;/span>&lt;span class="n">SwitchToLang&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mh">0x0409&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="如何设置开机自启动">如何设置开机自启动？
&lt;/h2>&lt;p>下面有两种方式。一种是直接放入启动文件夹。另一种是利用任务计划程序。&lt;/p>
&lt;h3 id="方式-1放入启动文件夹">方式 1：放入启动文件夹
&lt;/h3>&lt;ol>
&lt;li>&lt;code>Win + R&lt;/code>&lt;/li>
&lt;li>弹出的窗口输入 &lt;code>shell:startup&lt;/code>&lt;/li>
&lt;li>把 &lt;code>.ahk&lt;/code> 脚本快捷方式丢进去就行&lt;/li>
&lt;/ol>
&lt;p>这个方法非常简单。但在有些程序的窗口下，可能需要管理员权限才能够使用 AHK 脚本。&lt;/p>
&lt;h3 id="方式-2任务计划程序">方式 2：任务计划程序
&lt;/h3>&lt;p>如果你发现有些软件或窗口无法使用这个脚本，可能需要以管理员权限运行 AHK 脚本。&lt;/p>
&lt;p>大致思路：&lt;/p>
&lt;ol>
&lt;li>直接用 Windows 自带的搜索，搜「任务计划程序」。然后打开它&lt;/li>
&lt;li>选择 &lt;strong>创建任务&lt;/strong>，不要选择 &lt;strong>创建基本任务&lt;/strong>&lt;/li>
&lt;li>名称随便取&lt;/li>
&lt;li>在「常规」页中找到「安全选项」，勾选其中的「只在用户登录时运行」，勾选「使用最高权限运行」&lt;/li>
&lt;li>将界面切到「操作」页，选择「新建」，将你的 AHK 脚本路径填进去&lt;/li>
&lt;/ol>
&lt;blockquote>
&lt;p>&lt;strong>PS：&lt;/strong>「常规」页中有一个「隐藏」的选项。如果勾选这个选项的话，启动是不会弹那个需要手动确认管理员权限的窗口。&lt;/p>&lt;/blockquote>
&lt;h2 id="可能的风险">可能的风险
&lt;/h2>&lt;p>AHK 这类工具在某些游戏环境里可能会被反作弊系统判定为作弊程序。如果你游玩的游戏反作弊比较激进，建议不要使用 AHK 脚本或在游戏时退出 AHK 程序。&lt;/p></description></item><item><title>如何使用 LLM 反思工作流提升译文质量？</title><link>https://86c9071e.7luminaries.pages.dev/p/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8-llm-%E5%8F%8D%E6%80%9D%E5%B7%A5%E4%BD%9C%E6%B5%81%E6%8F%90%E5%8D%87%E8%AF%91%E6%96%87%E8%B4%A8%E9%87%8F/</link><pubDate>Thu, 16 Oct 2025 00:00:00 +0000</pubDate><guid>https://86c9071e.7luminaries.pages.dev/p/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8-llm-%E5%8F%8D%E6%80%9D%E5%B7%A5%E4%BD%9C%E6%B5%81%E6%8F%90%E5%8D%87%E8%AF%91%E6%96%87%E8%B4%A8%E9%87%8F/</guid><description>&lt;h2 id="1-为什么单次-ai-翻译还不够稳定">1. 为什么单次 AI 翻译还不够稳定？
&lt;/h2>&lt;p>我平时会高频使用 LLM 来处理日语/英语翻译。最开始使用的方法很简单，跟传统的机械翻译一样：输入原文，让模型直接输出译文。这个方法很简单而且效率很高，我认识的人多数都会使用这个方法。&lt;/p>
&lt;p>在模型选择上，尤其是使用 Flash 模型，刚输入完原文过几秒译文就出现了。速度虽然很快，但产生的译文只能作为参考，如果要投入到生产中还需要大量的人工修改。&lt;/p>
&lt;p>单次翻译很难稳定解决漏译、术语一致性和语气控制等问题。后来我开始使用反思工作流，把翻译、审校和最终整合拆成几个环节，由不同节点各自处理。我发现通过这个思路构建的工作流产生的译文质量明显比单次翻译的好。&lt;/p>
&lt;h2 id="2-吴恩达的-translation-agent-思路">2. 吴恩达的 Translation Agent 思路
&lt;/h2>&lt;p>在后续的使用和学习中，我发现了吴恩达的 Translation Agent 这个项目。&lt;/p>
&lt;p>&lt;code>https://github.com/andrewyng/translation-agent&lt;/code>&lt;/p>
&lt;p>它的核心是三步：初步翻译 ➡️ 反思与评价 ➡️ 改进与润色。我借鉴了这个项目的思路，在 Dify 中构建了一个自己的反思工作流。&lt;/p>
&lt;h2 id="3-个人工作流总览">3. 个人工作流总览
&lt;/h2>&lt;p align="center">
&lt;img src="https://86c9071e.7luminaries.pages.dev/img/flowchart.png" alt="翻译反思工作流流程图" width="720" />
&lt;/p>
&lt;h2 id="4-每一阶段的设计思路">4. 每一阶段的设计思路
&lt;/h2>&lt;p>这个工作流就是把翻译任务拆分成不同阶段：输入、文本切片、知识检索、初译、反思、合并。每个阶段只负责一类明确的任务，这样可以减少长文本翻译中的各种问题。&lt;/p>
&lt;h3 id="41-输入阶段">4.1 输入阶段
&lt;/h3>&lt;p>工作流的起点，我会设置几个基础变量：&lt;/p>
&lt;ul>
&lt;li>&lt;code>source_doc&lt;/code>：需要翻译的文件&lt;/li>
&lt;li>&lt;code>source_text&lt;/code>：需要翻译的文本&lt;/li>
&lt;li>&lt;code>source_lang&lt;/code>：原始语言&lt;/li>
&lt;li>&lt;code>target_lang&lt;/code>：目标语言&lt;/li>
&lt;li>&lt;code>country&lt;/code>：目标语言的国家或使用场景&lt;/li>
&lt;/ul>
&lt;p>这个节点的变量是让后续所有节点知道“文本从哪里来、要翻译成什么语言、面向的国家或者使用场景是什么”。在翻译实践中，经常有一个误区就是：中文就是中文，英语就是英语，法语就是法语。但实际上不同国家或者地区的同一语言是有些许不同的。打个比方，同样的英语翻译为中文，面向中国大陆的中文、面向港澳台地区的中文以及面向新加坡的中文是不一样的，部分用词和语气可能都不同；同样的日语翻译为中文，面向商品资料、客服邮件、技术说明的不同情况时，表达方式也都是不一样的。这也是 LLM 相比于传统的机器翻译的优势之一：可以使用 Prompt 来自定义输出的风格。&lt;/p>
&lt;p>这个节点主要是确定翻译任务的各种条件，避免模型自由发挥。通过变量传递，更好地让模型遵守语言和地区的设定。&lt;/p>
&lt;h3 id="42-文档提取阶段">4.2 文档提取阶段
&lt;/h3>&lt;p>这一阶段主要是检测输入的是纯文本还是文档，如果检测到文档就使用特定的功能来提取文档内的内容。也就是把&lt;code>source_doc&lt;/code>转换为文本。如果输入的是&lt;code>source_text&lt;/code>，那就会直接跳过这一阶段。&lt;/p>
&lt;p>这一阶段没有任何 LLM 参与的内容，主要都是程序自动转换。需要注意的是，常规的翻译接触到的可能都是 Word、Excel 等带有复杂格式的文件，这类就不适合直接输入到工作流中让程序自动转换。这类最好使用 CAT 软件来提取文档内容，然后再输入到工作流之中。CAT 软件最大的优势就是格式保持。&lt;/p>
&lt;h3 id="43-变量聚合阶段">4.3 变量聚合阶段
&lt;/h3>&lt;p>文档或者文本经过上一个阶段后，会进入变量聚合器。它可以把文档中提取出来的文本或用户直接输入的文本统一成一个新的变量。使用这个新的变量将文本继续传递下去。这一目的是让后续的节点只需要接受一个统一的文本，而不需要关心这些文本是由文档输入的还是文本输入的。&lt;/p>
&lt;p>概括一下：&lt;/p>
&lt;p>如果用户上传了文档，则使用文档提取器提取文本。（上一节点）&lt;/p>
&lt;p>如果用户直接输入了文本，则直接使用用户输入的文本。&lt;/p>
&lt;p>将统一后的待翻译内容命名为&lt;code>source_content&lt;/code>&lt;/p>
&lt;p>这一步虽然很简单，但对于自动化工作流来说是很有必要的。它让整个流程可以兼容短文本、长文本和文档内容。&lt;/p>
&lt;h3 id="44-文本切片阶段">4.4 文本切片阶段
&lt;/h3>&lt;p>这是第一个代码执行节点。在这个节点会使用代码将上面输入的文本按照段落进行切片，我使用的是 Python 来处理这部分内容。这样做的原因是模型在处理长文本翻译时很容易出现这几个问题：上下文注意力分散，后半段质量暴跌，格式混乱，术语前后不一致等问题。虽然现在的 LLM 的上下文都很大了，但随着上下文内容的增多，模型更容易出现注意力分散和精度下降的情况。按照段落切片后，每一段都可以进入独立的翻译、反思和合并流程。每个小切片更容易让模型稳定处理，也方便后续逐段检查。&lt;/p>
&lt;p>切片的原则如下：&lt;/p>
&lt;ul>
&lt;li>优先按照正常的段落切分&lt;/li>
&lt;li>保留标题和正文的相邻关系&lt;/li>
&lt;li>控制每个切片的长度&lt;/li>
&lt;li>避免把同一句话或者同一个列表切散&lt;/li>
&lt;li>给每个切片保留顺序编号&lt;/li>
&lt;/ul>
&lt;h3 id="45-知识检索阶段">4.5 知识检索阶段
&lt;/h3>&lt;p>这个阶段使用的是迭代节点。切片会逐个经过知识检索节点。这个节点会根据当前段落的内容，从知识库中检索相关术语、固定译法、背景信息或者一些翻译规范，然后把这些信息作为备注一起输入给下一节点。&lt;/p>
&lt;p>利用 LLM 进行翻译很令人头痛的一点就是术语不统一。比如，A 在某个行业应该翻译为 B，但由于 A 在其他行业可以翻译为 C，所以 LLM 把这个 A 翻译为了 C，这就导致译文前后容易不一致。&lt;/p>
&lt;p>知识节点可以添加下面的文本内容：&lt;/p>
&lt;ul>
&lt;li>各类术语&lt;/li>
&lt;li>产品名称&lt;/li>
&lt;li>固定表达&lt;/li>
&lt;li>品牌名称&lt;/li>
&lt;li>etc.&lt;/li>
&lt;/ul>
&lt;p>这个节点涉及到了知识库维护的内容，需要使用 Embedding 模型。由于这篇文章主要分享的是工作流思路，故不涉及这部分的内容。&lt;/p>
&lt;h3 id="46-术语去重阶段">4.6 术语去重阶段
&lt;/h3>&lt;p>知识检索阶段过后，工作流会进入第二个代码执行节点。这部分我也使用的 Python 进行处理。主要是对上一个节点的内容做去重和整理。在长文本的情况下，知识检索很大概率会检索到多个相似条目，出现很多重复术语。直接把这些内容交给 LLM，更容易导致混乱而且增加不必要的 API 花费。&lt;/p>
&lt;p>这部分代码的主要工作内容如下：&lt;/p>
&lt;ul>
&lt;li>删除重复术语&lt;/li>
&lt;li>保留优先级更高的译法&lt;/li>
&lt;li>保留和当前段落最相关的术语&lt;/li>
&lt;li>控制术语参考的长度&lt;/li>
&lt;/ul>
&lt;h3 id="47-llm-1初译阶段">4.7 LLM 1：初译阶段
&lt;/h3>&lt;p>这一阶段终于开始进行翻译了，它负责生成第一版译文。这个阶段的目标是准确且完整地把原文翻译为目标语言。它不需要过度的润色，也不需要改动原文的结构。在我的实践中，第一版译文越是稳定，后面的反思和合并节点就越容易发挥作用。&lt;/p>
&lt;p>在模型的选择上，不是需要严谨表达的文章可以选择参数量比较小的模型以平衡成本，比如各类 Flash 模型。&lt;/p>
&lt;p>这一阶段的 Prompt 的重点如下：&lt;/p>
&lt;ul>
&lt;li>忠于原文&lt;/li>
&lt;li>保留全部信息&lt;/li>
&lt;li>参考传递的术语参考内容&lt;/li>
&lt;li>保留原文的格式&lt;/li>
&lt;li>根据目标地区调整用词&lt;/li>
&lt;/ul>
&lt;h3 id="48-llm-2反思阶段">4.8 LLM 2：反思阶段
&lt;/h3>&lt;p>这一阶段需要对上一阶段的翻译质量进行检查。这个节点需要同时读取原文、初译和输入的术语参考内容，然后从中发现问题并提出修改意见。&lt;/p>
&lt;p>这个阶段重点是发现问题，所以我会要求模型输出结构化的审校意见，而不是在初译的基础上重写译文。结构化的输出更容易被后面阶段利用，也方便人工审查时思考哪些意见值得采纳。在翻译实践中，很多译文看起来通顺，但很多问题在对照原文的情况下才更容易发现。&lt;/p>
&lt;p>在模型选择上，我建议选择性能最好的模型，这个阶段是最重要的。比如选择 Claude Opus、Gemini Pro、ChatGPT 带思考的系列。&lt;/p>
&lt;p>这一阶段的 Prompt 的重点如下：&lt;/p>
&lt;ul>
&lt;li>对照原文来检查初译&lt;/li>
&lt;li>找出其中的问题&lt;/li>
&lt;li>检查术语是否符合参考内容&lt;/li>
&lt;li>检查语气是否符合场景&lt;/li>
&lt;li>检查文章格式&lt;/li>
&lt;li>给出具体的修改建议以及理由&lt;/li>
&lt;/ul>
&lt;h3 id="49-llm-3合并阶段">4.9 LLM 3：合并阶段
&lt;/h3>&lt;p>这一阶段会根据原文、初译和反思节点给出的建议，生成最终的译文。这个阶段由 LLM 做取舍，采纳或舍弃建议。目标是输出一版准确、自然、更适合交付的译文。&lt;/p>
&lt;p>这一阶段的 Prompt 重点如下：&lt;/p>
&lt;ul>
&lt;li>以原文为准&lt;/li>
&lt;li>参考初译和反思意见&lt;/li>
&lt;li>采纳合理建议&lt;/li>
&lt;li>保留术语一致性&lt;/li>
&lt;li>输出最终译文&lt;/li>
&lt;li>避免引入新的信息&lt;/li>
&lt;/ul>
&lt;h3 id="410-输出阶段">4.10 输出阶段
&lt;/h3>&lt;p>每个切片完成直译、反思和合并后，工作流把所有切片按照原始顺序重新汇总，输出完整译文。这一阶段还是需要人工审查，避免长文本输出错误。我会着重检查顺序、否定句、条件句、专有名词、产品规格，这些内容出错的话会比表达错误产生的问题更大。&lt;/p>
&lt;h2 id="5-这个流程适合哪些翻译场景">5. 这个流程适合哪些翻译场景？
&lt;/h2>&lt;h3 id="51-技术文档与产品说明书">5.1 技术文档与产品说明书
&lt;/h3>&lt;p>这类文章通常包含大量行业专有名词、公司的内部表达、产品规格，单次翻译很容易出错。&lt;/p>
&lt;h3 id="52-跨境电商营销文案与独立站">5.2 跨境电商营销文案与独立站
&lt;/h3>&lt;p>可以通过这个工作流针对不同国家的消费者调整语气，既要说服力强，又要包含特定的 SEO 关键词。可以利用这个工作流将原文译为有本土吸引力的文案。RAG 可以确保品牌名和核心卖点不被错误翻译。&lt;/p>
&lt;h3 id="53-商品资料与本地化-qa">5.3 商品资料与本地化 QA
&lt;/h3>&lt;p>商品标题、规格参数、卖点描述和客服话术都对术语一致性有较高要求。通过这个思路也可以让译文的准确度、风格和品牌惯用表达上更加稳定。&lt;/p>
&lt;h2 id="6-成本限制和质量风险">6. 成本、限制和质量风险
&lt;/h2>&lt;p>这个工作流可以一定程度上提升译文的稳定性，但它会带来额外的成本。相比于单次翻译，即：「输入原文 ➡️ 直接输出译文」的方式，反思工作流至少会经历初译、反思、合并三个阶段。整个流程的 token 消耗、处理时间和调试成本都会明显增加。&lt;/p>
&lt;p>因此，这个流程更适合有一定质量要求的文本，例如上面提到的技术文档、产品说明书、商品资料、本地化内容等。对于非常短、风险很低、只需要快速浏览的文本，单次翻译通常是足够的。反思工作流的价值主要体现在「需要交付质量」的场景，而不是所有翻译场景。&lt;/p>
&lt;p>虽然我们都会在三个不同节点中通过 Prompt 来限制 LLM 的行为，但也有概率出现问题。比如 LLM 2 的反思节点过分挑刺，LLM 3 的节点产生了新的额外内容等情况。因此即使是这种自动化工作流，在面对要求非常严格的场景时，人工审查仍然是必要的。&lt;/p>
&lt;p>还有一点是切片容易带来的上下文断裂。上文的节点中其实可以在某个节点内添加「概括段落」的内容，然后一起输出给翻译节点，这样翻译节点会获得前后的上下文，上下文一致性会更好。切片的目的是降低模型的处理压力，提升精度，但切片切得太碎的话依旧会降低稳定性，使用时需要自己找到其中的平衡。&lt;/p>
&lt;p>这个工作流最核心的依旧是 LLM 自身的能力。如果你使用三个很差的模型，那产生的结果可能好不到哪里去。目前的顶尖模型在面对翻译的场景下通常表现是不错的，有效上下文也随着模型迭代不断增加。在我的实践中，我认为通常要保持 LLM 2、LLM 3 这两个使用了顶尖模型，这样这个工作流才会更稳定一些。&lt;/p>
&lt;p>对于 LLM 来说，面对不同语言情况选择不同模型也是有必要的。比如 Claude、Gemini、ChatGPT 这几个国外模型，它们训练时使用的中文语料相比国内的模型少很多，在面对中文相关翻译时表现可能不会很好。因此也需要使用者充分了解各个模型的优势，才能够让输出的译文更加稳定。&lt;/p>
&lt;h2 id="7-让-ai-翻译变为可控流程">7. 让 AI 翻译变为可控流程
&lt;/h2>&lt;p>对我来说，现在 LLM 的价值不只在于「翻译得更快」，更在于它可以参与到审校、改写、术语整理和质量检查等环节中。只要把任务拆分清楚，并在关键位置保留人工判断，AI 翻译就可以从一个临时工具，变成一个稳定的内容处理流程。都说未来 2026-2027 是 Agent 爆发增长的时期，相比于 OpenClaw 这类个人助手，我感觉类似的工作流可能会更好地帮助我们工作和生活。&lt;/p>
&lt;p>这个工作流也可以迁移到其他的场景中，背后的核心就是把复杂的文本任务拆分为明确的步骤，再通过工具和人工复核共同提高最终交付质量。&lt;/p></description></item><item><title>写在折腾完 Hugo 和 Astro 之后</title><link>https://86c9071e.7luminaries.pages.dev/p/%E5%86%99%E5%9C%A8%E6%8A%98%E8%85%BE%E5%AE%8C-hugo-%E5%92%8C-astro-%E4%B9%8B%E5%90%8E/</link><pubDate>Fri, 05 Sep 2025 00:00:00 +0000</pubDate><guid>https://86c9071e.7luminaries.pages.dev/p/%E5%86%99%E5%9C%A8%E6%8A%98%E8%85%BE%E5%AE%8C-hugo-%E5%92%8C-astro-%E4%B9%8B%E5%90%8E/</guid><description>&lt;p>写博客这件事情，是很简单的。写一篇文章，找一个平台，发布上去，别人打开就能够阅读。搭建博客的话，一般来说路径是两条，一条是传统博客（新浪、WordPress、Medium等），另一条是近些年新兴的静态博客（Hugo、Hexo、Jekyll、Astro 等）。&lt;/p>
&lt;h2 id="传统博客">传统博客
&lt;/h2>&lt;p>传统博客我们大家应该都非常熟悉了。就像早些年的新浪博客，哪怕现在的知乎，小红书其实都能算是博客。还有大量个人或公司网站都是依托 WordPress 搭建的。像新浪、知乎、小红书之类的就不提及了，它们是成套的方案，打开就能用。
随便讲一下 WordPress。这个安装好之后，每天面对的其实是网站后台，写文章就打开内置的编辑器，要插入图片就有相应的功能勾选。还有大量的成熟的第三方插件可供选择，丰俭由人。这个很适合多人写作、商业网站、内容站。管理员也可以给用户分配权限，设置文章经过管理员审核之后才能发布。它的优势是非常成熟，你遇到的问题基本上都有对应的解决方案。如果你想换主题、加功能，也通常能够找到现成的方案。对很多人来说，这种就非常省心。
不过，传统博客是非常「重」的。通常需要数据库、运行环境、后台登录系统，要的功能越多，整个博客也会越来越「重」。维护的成本也会越来越高。&lt;/p>
&lt;h2 id="静态博客">静态博客
&lt;/h2>&lt;p>静态博客就如名字一样，静态的意思是写完文章后利用工具直接把网页生成好。部署完静态博客后，访问者打开的就是已经生成好的 HTML 页面。
静态博客通常都是使用 Markdown 这样的纯文本来编辑、存储文章。这种一开始学习就会比较麻烦。要去看框架的目录结构，要知道对应的配置文件在哪里，还要学习构建和部署。很多问题都需要自己去一一排查。
当然度过新手阶段，一切就豁然开朗了。文章就是核心，后续维护什么的也很方便，写完文章之后推送到云端就行了，工具会自动生成对应的 HTML 页面。
静态博客的优势主要是速度、稳定性和安全性。对应的网站都是提前生成好的，少了很多步骤，访问就会更快。而且访问时对服务器的压力相比传统博客会小很多，像 Cloudflare Pages 对个人开发者提供的额度几乎是使用不完的。
传统博客还需要关注的安全问题，像登录、插件漏洞、主题漏洞 等等等等，对于个人开发者来说负担很大。像 WordPress 这种生态极其庞大的系统，插件和主题的选择会直接影响站点的安全性。
而静态博客的攻击面相对较小。页面都是静态文件，没有提供后台（可以自己选择加入后台），没有运行中的数据库。对于个人开发者来说，维护成本很低。&lt;/p>
&lt;h2 id="静态博客适合哪些人呢">静态博客适合哪些人呢？
&lt;/h2>&lt;p>拿我自己的感觉来看的话，静态博客最适合的其实还是喜欢折腾的人。刚开始学习起来还是很麻烦的，尤其如果没有编程的基础的话。但是搭建起来后的成就感是不一样的，哪怕你的博客其实没有什么人会看🤣。
静态博客有很多框架，而且这些框架都提供了很多很多的主题供你选择。但是这些主题都不是完美的，如果想自定义的话都需要小修小改，这个对于没有编程基础的人来说有点不便。
但现在已经是 AI 时代了，静态博客的小型代码，LLM 完全能够胜任。比如 Claude Code，配置好 API 后只需要提示词就能够让它自主修改。静态博客几乎都是前端代码，因此 LLM 修改起来很顺手。
很多人的静态博客也有一个毛病：装修地十分热闹，但文章总共没几篇（甚至装修笔记占了总数😂）。其实我自己也差不多，很多笔记没有公开放到博客上。
总结一下就是：&lt;/p>
&lt;ul>
&lt;li>喜欢折腾新技术的&lt;/li>
&lt;li>喜欢 Markdown 写作&lt;/li>
&lt;li>原意使用 Git 管理内容&lt;/li>
&lt;li>希望低成本部署个人网站的&lt;/li>
&lt;li>希望安全性更高一些的&lt;/li>
&lt;/ul>
&lt;h2 id="静态博客的框架">静态博客的框架
&lt;/h2>&lt;p>常见的静态博客的框架有 Hugo、Hexo、Jekyll、Astro、VitePress 等。这些框架的作用都是一样的：读取文章、主题和配置文件，然后生成可以直接部署的 HTML 网页。
我随便写一下我了解的 Hugo 和 Astro。
Hugo 应该是非常普及的框架了，我看过的很多博客都采用的这个框架。它的优点是构建速度快。文章数量越多，这个优势就会越大。Hugo 的主题也比较成熟。很多主题都会内置归档、标签、分类、目录、暗色模式、搜索、适配评论系统等等。当然还有一个坑就是不同的主题的配置可能差别很大。
Astro 可以实现非常现代的前端效果，适合那些想要炫酷效果、组件化和自定义要求很高的人。它其实像一个完整的网站项目，类似于很多企业的官网。除了常规的 Markdown 文章之外，它还可以使用组件、布局、数据集合和各自前端集合。我个人感觉它更适合作为作品集、产品展示。它可以结合 React、Vue、Svelte 等组件框架，也可以使用它自己的组件写页面。&lt;/p>
&lt;h2 id="写在最后">写在最后
&lt;/h2>&lt;p>我也按照惯例，祭出下面的这张梗图。&lt;/p>
&lt;p align="center">
&lt;img src="https://86c9071e.7luminaries.pages.dev/img/Blog.jpg" alt="建站如山倒，写作如抽丝" width="720" />
&lt;/p>
&lt;p>简单概括下就是：差生文具多，折腾博客系统代替了写博客🤣。
既然是博客，终究还是要回归文字内容的。我收藏了不少人的博客，但很多博客的文章都寥寥无几，只是页面的效果很酷炫（拿来当作参考）。我也希望我建完博客之后能稳定写一些文章吧，就当一个互联网的树洞。&lt;/p></description></item><item><title>归档 | Archives</title><link>https://86c9071e.7luminaries.pages.dev/archives/</link><pubDate>Sun, 06 Mar 2022 00:00:00 +0000</pubDate><guid>https://86c9071e.7luminaries.pages.dev/archives/</guid><description/></item><item><title>搜索 | Search</title><link>https://86c9071e.7luminaries.pages.dev/search/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://86c9071e.7luminaries.pages.dev/search/</guid><description/></item></channel></rss>