<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>三巧小站</title><link>https://3qiao.vip/</link><description>Recent content on 三巧小站</description><generator>Hugo</generator><language>zh-CN</language><lastBuildDate>Sun, 20 Apr 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://3qiao.vip/index.xml" rel="self" type="application/rss+xml"/><item><title>把 AI 接口接入个人工具：一次轻量化实践</title><link>https://3qiao.vip/posts/2025-ai-api-tooling/</link><pubDate>Sun, 20 Apr 2025 00:00:00 +0000</pubDate><guid>https://3qiao.vip/posts/2025-ai-api-tooling/</guid><description>用一个小型适配层统一模型接口，保留可替换、可观测和低成本。</description><content:encoded><![CDATA[<p>2025 年，模型接口越来越多，但个人工具并不需要同时绑定很多 SDK。我做了一次轻量化尝试：在业务代码和模型服务之间增加一个很薄的适配层，只约定输入、输出和错误格式。</p>
<h2 id="只保留真正需要的接口">只保留真正需要的接口</h2>
<p>适配层只做三件事：接收消息列表、选择模型、返回文本和用量信息。它不保存会话，也不把供应商特有的字段泄漏到业务代码中。这样更换服务商时，只需要替换适配层，而不是重写整个工具。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">ask</span><span class="p">(</span><span class="n">messages</span><span class="p">,</span> <span class="n">model</span><span class="o">=</span><span class="s2">&#34;small-fast&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">responses</span><span class="o">.</span><span class="n">create</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">model</span><span class="o">=</span><span class="n">model</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nb">input</span><span class="o">=</span><span class="n">messages</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">timeout</span><span class="o">=</span><span class="mi">30</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;text&#34;</span><span class="p">:</span> <span class="n">response</span><span class="o">.</span><span class="n">output_text</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;usage&#34;</span><span class="p">:</span> <span class="n">response</span><span class="o">.</span><span class="n">usage</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span></code></pre></div><p>真实项目里还需要处理超时、限流和重试。重试只针对明确的临时错误，并设置上限；如果请求已经产生费用，就不能无条件重复发送。</p>
<h2 id="把日志做成能回答问题的东西">把日志做成能回答问题的东西</h2>
<p>我只记录请求时间、模型名、耗时、状态码和 token 数，不记录完整提示词或敏感内容。发生问题时，这些字段足以判断是网络慢、服务拒绝，还是输入本身太大。日志的目标是帮助排错，而不是把所有数据都复制一份。</p>
<h2 id="低配服务器上的取舍">低配服务器上的取舍</h2>
<p>适配层可以用一个很小的 Python 或 Go 服务运行，也可以在静态站点之外单独部署。博客本身不需要调用 AI 才能展示，因此把两者拆开后，即使接口服务暂时停止，文章页面仍然可以正常访问。</p>
<p>这次实践让我更确定一件事：个人项目的“智能化”不应以堆叠组件为起点。先把边界、超时、日志和成本控制做好，剩下的能力才有稳定的落脚点。</p>
]]></content:encoded></item><item><title>用 Nginx 为静态站点加上缓存与安全响应头</title><link>https://3qiao.vip/posts/2023-nginx-static-cache/</link><pubDate>Tue, 12 Sep 2023 00:00:00 +0000</pubDate><guid>https://3qiao.vip/posts/2023-nginx-static-cache/</guid><description>从响应头和缓存策略入手，让一个静态站点更快、更稳。</description><content:encoded><![CDATA[<p>静态站点已经很快，但默认配置通常只完成了“能访问”。2023 年我给一个个人页面做优化时，没有引入额外的缓存服务，而是先把 Nginx 的基础能力用好。</p>
<h2 id="给不同类型的文件不同缓存时间">给不同类型的文件不同缓存时间</h2>
<p>带哈希文件名的 CSS、JavaScript 和图片可以长时间缓存；HTML 则应该短一些，方便发布后及时生效。下面是一段适合静态站点的配置：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nginx" data-lang="nginx"><span class="line"><span class="cl"><span class="k">location</span> <span class="p">~</span><span class="sr">*</span> <span class="s">\\.(css|js|png|jpg|jpeg|gif|svg|webp|woff2)</span>$ <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kn">expires</span> <span class="s">30d</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kn">add_header</span> <span class="s">Cache-Control</span> <span class="s">&#34;public,</span> <span class="s">max-age=2592000,</span> <span class="s">immutable&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">location</span> <span class="s">/</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kn">try_files</span> <span class="nv">$uri</span> <span class="nv">$uri/</span> <span class="s">/404.html</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kn">add_header</span> <span class="s">X-Content-Type-Options</span> <span class="s">&#34;nosniff&#34;</span> <span class="s">always</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kn">add_header</span> <span class="s">Referrer-Policy</span> <span class="s">&#34;strict-origin-when-cross-origin&#34;</span> <span class="s">always</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>immutable</code> 只适合文件内容一旦变化就会改名的资源。如果站点仍然使用固定的 <code>main.css</code>，缓存时间就不应设置得过长，否则更新样式后用户可能要手动刷新。</p>
<h2 id="压缩要适度">压缩要适度</h2>
<p>HTML、CSS、JavaScript 和 SVG 都适合启用 gzip。图片压缩则应该在构建阶段完成，不能指望 Nginx 在每次请求时临时处理大图片。对个人博客来说，少量清晰的图片比堆叠复杂的优化插件更容易维护。</p>
<h2 id="用响应头建立最低限度的安全边界">用响应头建立最低限度的安全边界</h2>
<p><code>X-Content-Type-Options</code> 可以避免浏览器猜测文件类型，<code>Referrer-Policy</code> 则减少从站点跳转时泄露完整路径。它们不能替代应用安全检查，却能以很低的成本补上基础防线。</p>
<p>配置变更后先检查语法，再平滑加载：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">nginx -t
</span></span><span class="line"><span class="cl">nginx -s reload
</span></span></code></pre></div><p>优化静态站点时，我通常先看真实请求的响应头和缓存命中情况，再决定是否需要更复杂的 CDN 或服务端缓存。简单的站点，简单的链路往往更稳定。</p>
]]></content:encoded></item><item><title>从脚本到服务：用 Docker Compose 管理一个小项目</title><link>https://3qiao.vip/posts/2021-lightweight-deployment/</link><pubDate>Fri, 18 Jun 2021 00:00:00 +0000</pubDate><guid>https://3qiao.vip/posts/2021-lightweight-deployment/</guid><description>记录一次把本地脚本整理成可重复部署服务的过程。</description><content:encoded><![CDATA[<p>很多个人项目最初只有一个脚本：在电脑上可以运行，换一台机器就要重新安装依赖、修改配置，再手动启动。2021 年我给一个小工具做整理时，决定把“运行环境”也作为项目的一部分保存下来。</p>
<h2 id="先把服务边界说清楚">先把服务边界说清楚</h2>
<p>我先把程序拆成三个部分：应用本身、持久化数据、配置文件。应用容器可以随时重建，数据目录需要长期保留，配置则通过环境变量注入。这样做的好处是，升级应用时不必碰数据，迁移服务器时也只需要打包数据目录和 compose 文件。</p>
<p>一个最小的 <code>docker-compose.yml</code> 可以这样写：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;3.8&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">app</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">example/app:1.0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">unless-stopped</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;8080:8080&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">env_file</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">.env</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">./data:/var/lib/app</span><span class="w">
</span></span></span></code></pre></div><p><code>restart: unless-stopped</code> 让机器重启后服务自动恢复；版本号固定则避免“今天能跑、明天突然变样”。开发阶段可以使用 <code>latest</code>，生产环境最好明确写版本。</p>
<h2 id="配置和数据分开">配置和数据分开</h2>
<p>密码、令牌等内容不应该提交到 Git。项目里只保留 <code>.env.example</code>，真正的 <code>.env</code> 放在服务器上，并限制文件权限：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cp .env.example .env
</span></span><span class="line"><span class="cl">chmod <span class="m">600</span> .env
</span></span><span class="line"><span class="cl">docker compose up -d
</span></span></code></pre></div><p>备份也围绕数据目录进行。对小项目来说，每天压缩一次数据并保留最近七份，已经比“只在出问题时想起来备份”可靠很多。</p>
<h2 id="让部署变成一个动作">让部署变成一个动作</h2>
<p>最后我把更新流程固定为：拉取代码、检查配置、构建新镜像、重新创建容器、查看日志。步骤固定之后，部署不再依赖某个人记得哪些命令，也更容易在低配服务器上控制资源。</p>
<p>这个实践让我意识到，轻量化不只是少装软件，更是让每个组件的职责足够单一。服务越小，备份、迁移和排错都越容易。</p>
]]></content:encoded></item><item><title>关于三巧小站</title><link>https://3qiao.vip/about/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://3qiao.vip/about/</guid><description>三巧小站的站点说明</description><content:encoded><![CDATA[<p>三巧小站是一个个人技术博客，主要记录开发、服务器运维和轻量工具方面的实践。内容以技术笔记和经验整理为主，方便日后检索，也希望能给遇到类似问题的人提供一点参考。</p>
<p>站点保持简洁，专注于文章阅读与技术内容分享。</p>
<p>如需联系站长，可在后续补充公开邮箱。</p>
]]></content:encoded></item></channel></rss>