<?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://www.bufio.cn/</link><description>Recent content on 随手记</description><generator>Hugo</generator><language>zh-cn</language><copyright>© 2026 &lt;a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener"&gt;苏ICP备2023022553号-1&lt;/a&gt;</copyright><lastBuildDate>Fri, 12 Jun 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://www.bufio.cn/index.xml" rel="self" type="application/rss+xml"/><item><title>macOS 上用 dd 制作 Windows 安装 U 盘后提示缺少驱动的原因</title><link>https://www.bufio.cn/posts/macos-dd-windows-installer-missing-driver/</link><pubDate>Fri, 12 Jun 2026 00:00:00 +0800</pubDate><guid>https://www.bufio.cn/posts/macos-dd-windows-installer-missing-driver/</guid><description>&lt;p&gt;最近在 macOS 上制作 Windows 安装 U 盘时踩了一个坑：用 &lt;code&gt;dd&lt;/code&gt; 把 Windows ISO 原样写进 U 盘后，机器可以从 U 盘启动，也能进入 Windows 安装界面，但安装器很快提示缺少驱动，或者在选择安装位置之前就卡在“Install Driver to Show Hardware / 缺少介质驱动程序”这一类界面。&lt;/p&gt;
&lt;p&gt;这个现象和 TaurusXin 记录的那篇文章很像。他遇到的是用 Etcher 写 Windows 安装盘后，安装器提示缺少驱动；我这里换成 macOS 下的 &lt;code&gt;dd&lt;/code&gt;，结果仍然类似。表面看像是 NVMe、RST、VMD、USB 控制器驱动问题，实际更可能是启动 U 盘的制作方式不适合 Windows 安装 ISO。&lt;/p&gt;
&lt;p&gt;后面实测可行的方案是：在 macOS 上改用 WinDiskWriter 重新制作 U 盘，并勾选 &lt;code&gt;Patch Installer Requirements&lt;/code&gt; 和 &lt;code&gt;Install Legacy BIOS Boot Sector&lt;/code&gt;，重新引导后就可以继续正常安装。&lt;/p&gt;
&lt;h2 id="现象能启动但安装器继续读不到安装文件"&gt;现象：能启动，但安装器继续读不到安装文件&lt;/h2&gt;
&lt;p&gt;典型过程是这样的：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 示例：不要照抄执行，diskN 必须先确认清楚&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo diskutil unmountDisk /dev/diskN
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dd &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Win11.iso &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/rdiskN &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4m &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;progress
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sync
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;写完之后，U 盘看起来已经“烧录成功”：&lt;/p&gt;</description></item><item><title>BiRefNet 深度解读：从图片分割到镜架边缘形态优化</title><link>https://www.bufio.cn/posts/birefnet-segmentation-decoder-morphology/</link><pubDate>Tue, 09 Jun 2026 00:00:00 +0800</pubDate><guid>https://www.bufio.cn/posts/birefnet-segmentation-decoder-morphology/</guid><description>&lt;h2 id="开头birefnet-到底在做什么"&gt;开头：BiRefNet 到底在做什么&lt;/h2&gt;
&lt;p&gt;BiRefNet 可以先粗略理解成一个高质量前景分割模型。它拿到一张图片之后，不是直接输出一张透明 PNG，而是先输出一张 mask：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;白色区域：要保留的主体
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;黑色区域：要删除的背景
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;灰色区域：边缘、不确定或半透明过渡
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果我们要做商品抠图，最终流程通常是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;原始图片
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;BiRefNet 预测 mask
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mask 作为 alpha 通道
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;得到透明背景 PNG
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;比如一副眼镜放在白色桌面上，BiRefNet 要判断每个像素属于眼镜还是背景。真正的难点不在于“整副眼镜在哪里”，而在于鼻托、镜腿、镜框内侧边缘、镜片孔洞这些细节是否干净、连续、圆润。&lt;/p&gt;
&lt;p&gt;这篇文章按一个完全不懂深度学习的人也能理解的方式，解释 BiRefNet 的工作链路，并重点讨论一个很实际的问题：如果模型整体已经抠得很准，但镜架鼻托边缘还有毛刺和不规整，下一步应该怎么修。&lt;/p&gt;
&lt;h2 id="总览birefnet-的基本分工"&gt;总览：BiRefNet 的基本分工&lt;/h2&gt;
&lt;p&gt;可以先把 BiRefNet 拆成两个大部分：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;图片
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;backbone：提取视觉特征
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;decoder / refinement：融合特征并生成 mask
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;输出前景 mask
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;更通俗一点：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;backbone = 看图并提取证据
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;decoder = 根据证据画出最终 mask
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;backbone 不直接输出“这个像素是前景、那个像素是背景”。它输出的是很多层特征图。decoder 才负责把这些特征图融合起来，变成最终可用的分割结果。&lt;/p&gt;
&lt;h2 id="backbone不是空模型而是视觉特征提取器"&gt;Backbone：不是空模型，而是视觉特征提取器&lt;/h2&gt;
&lt;p&gt;很多人第一次听到 backbone，会以为它是一个空权重模型。其实不是。&lt;/p&gt;
&lt;p&gt;backbone 是整个网络里的视觉主干，它负责把原始 RGB 像素转换成模型内部能理解的特征。以一张 &lt;code&gt;1024x1024&lt;/code&gt; 图片为例，原始输入大概是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[3, 1024, 1024]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里的 3 是 RGB 三个颜色通道。进入 backbone 后，它可能输出多层特征：&lt;/p&gt;</description></item><item><title>USB 转 SATA / NVMe 桥接芯片在 Linux 下的兼容性对比</title><link>https://www.bufio.cn/posts/usb-sata-nvme-bridge-linux-compatibility/</link><pubDate>Fri, 22 May 2026 00:00:00 +0800</pubDate><guid>https://www.bufio.cn/posts/usb-sata-nvme-bridge-linux-compatibility/</guid><description>&lt;p&gt;很多 2.5 寸硬盘盒、M.2 SATA 硬盘盒、M.2 NVMe 硬盘盒看起来只是“USB 外置硬盘”，但真正决定 Linux 体验的，经常是里面那颗 USB 桥接芯片和它的固件。&lt;/p&gt;
&lt;p&gt;同一块 SSD，换一个桥接芯片，可能会出现完全不同的结果：一个盒子能跑 UASP、能 &lt;code&gt;fstrim&lt;/code&gt;、能读 SMART；另一个盒子只跑 &lt;code&gt;usb-storage&lt;/code&gt;，或者大文件写入时反复 USB reset。本文把常见的 ASMedia ASM1153E、JMicron JMS 系列、Realtek RTL 系列放在一起，对 Linux 下的兼容性做一个偏实用的对比。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.bufio.cn/images/usb-storage-bridge-linux-matrix.svg" alt="USB 存储桥接芯片 Linux 兼容性矩阵"&gt;&lt;/p&gt;
&lt;h2 id="先给结论"&gt;先给结论&lt;/h2&gt;
&lt;p&gt;如果只是给 2.5 寸 SATA SSD/HDD 做 USB 外置盒，ASM1153E 和 JMS578 都很常见，也都支持 UASP。ASM1153E 的整体口碑偏稳，但 TRIM/SMART 仍然受固件影响；JMS578 功能完整、价格低、出货量大，但不同固件之间差异明显。&lt;/p&gt;
&lt;p&gt;如果是 NVMe 转 USB，JMS583 和 RTL9210/RTL9210B 是常见选择。JMS583 是早期大量普及的 USB 3.1 Gen2 转 NVMe 桥，规格漂亮，但旧固件、散热和某些主机组合下更容易遇到掉盘或重置；RTL9210/RTL9210B 后来在很多硬盘盒里成为更常见的“稳妥选择”，但也不是免疫，仍然要看固件版本和具体硬盘。&lt;/p&gt;
&lt;p&gt;Linux 下不要只看商品页写的“支持 UASP / 支持 TRIM”。更可靠的判断顺序是：&lt;/p&gt;</description></item><item><title>USB-SATA 桥接芯片的 UAS 兼容性问题排查</title><link>https://www.bufio.cn/posts/usb-sata-uas-compatibility/</link><pubDate>Wed, 20 May 2026 09:30:04 +0800</pubDate><guid>https://www.bufio.cn/posts/usb-sata-uas-compatibility/</guid><description>&lt;p&gt;通过 USB 外接硬盘时，如果遇到磁盘看起来一直很忙，但实际吞吐很低，问题不一定出在文件系统或硬盘本体上。一次比较典型的场景是：USB-SATA 桥接芯片声称支持 UAS，但它的固件实现不稳定，Linux 自动启用 UAS 后反而触发命令超时、USB 设备重置，最终表现成系统层面的 I/O 卡顿。&lt;/p&gt;
&lt;p&gt;这篇文章记录一次这类问题的现象、原因和处理方式。&lt;/p&gt;
&lt;h2 id="问题现象"&gt;问题现象&lt;/h2&gt;
&lt;p&gt;故障最直观的表现是：外接硬盘几乎不可用，但监控上又看不出正常的高吞吐。&lt;/p&gt;
&lt;p&gt;常见现象包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;iostat&lt;/code&gt; 中磁盘 &lt;code&gt;%util&lt;/code&gt; 长时间接近或等于 &lt;code&gt;100%&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;实际 &lt;code&gt;r/s&lt;/code&gt;、&lt;code&gt;w/s&lt;/code&gt;、&lt;code&gt;rkB/s&lt;/code&gt;、&lt;code&gt;wkB/s&lt;/code&gt; 并不高；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;await&lt;/code&gt;、&lt;code&gt;svctm&lt;/code&gt; 或应用侧读写延迟异常升高；&lt;/li&gt;
&lt;li&gt;文件复制、解压、数据库读写、备份任务间歇性卡住；&lt;/li&gt;
&lt;li&gt;偶尔出现 &lt;code&gt;Input/output error&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dmesg&lt;/code&gt; 或 &lt;code&gt;journalctl -k&lt;/code&gt; 中反复出现 UAS abort、SCSI timeout、USB reset。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;可以先用下面这些命令观察：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;iostat -xz &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dmesg -T &lt;span class="p"&gt;|&lt;/span&gt; grep -Ei &lt;span class="s1"&gt;&amp;#39;uas|usb|reset|abort|I/O error|blk_update_request&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;journalctl -k -b &lt;span class="p"&gt;|&lt;/span&gt; grep -Ei &lt;span class="s1"&gt;&amp;#39;uas|usb|reset|abort|I/O error&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;比较典型的内核日志类似这样：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uas_eh_abort_handler ...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;scsi host... uas_eh_device_reset_handler
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;usb ... reset SuperSpeed USB device number ...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;blk_update_request: I/O error, dev sdX, sector ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果这些日志和外接硬盘卡顿时间点高度重合，就要怀疑 USB 存储协议层的问题。&lt;/p&gt;</description></item><item><title>在 Docker 中部署 mihomo，并让本机与局域网电脑接入</title><link>https://www.bufio.cn/posts/mihomo-docker-lan-setup/</link><pubDate>Tue, 19 May 2026 00:00:00 +0800</pubDate><guid>https://www.bufio.cn/posts/mihomo-docker-lan-setup/</guid><description>&lt;p&gt;mihomo 是 Clash.Meta 的延续版本，常见用法是把它作为一个代理内核运行在一台长期在线的机器上，然后让本机、手机、Windows 电脑、Linux 电脑都通过这台机器访问代理服务。&lt;/p&gt;
&lt;p&gt;这篇文章记录一个最实用的部署方式：在 Docker 里运行 mihomo，宿主机负责提供端口，局域网里的其他设备只需要把 HTTP / SOCKS 代理指向这台机器即可。&lt;/p&gt;
&lt;h2 id="总览先看整体链路"&gt;总览：先看整体链路&lt;/h2&gt;
&lt;p&gt;假设运行 Docker 的机器局域网 IP 是 &lt;code&gt;192.168.1.10&lt;/code&gt;，mihomo 在这台机器上监听 &lt;code&gt;7890&lt;/code&gt; 端口：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Windows / Linux / 手机
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; | HTTP / SOCKS 代理：192.168.1.10:7890
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Docker 宿主机：192.168.1.10
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; | mihomo 容器
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;按规则直连或走代理节点
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;关键点只有三个：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;mihomo 配置里开启 &lt;code&gt;allow-lan: true&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;mihomo 监听地址使用 &lt;code&gt;bind-address: &amp;quot;*&amp;quot;&lt;/code&gt; 或宿主机局域网 IP。&lt;/li&gt;
&lt;li&gt;Docker 把 &lt;code&gt;7890&lt;/code&gt; 端口暴露给局域网。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="第一步准备目录"&gt;第一步：准备目录&lt;/h2&gt;
&lt;p&gt;在准备用作代理网关的机器上创建目录：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkdir -p ~/mihomo/config
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/mihomo
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;后面所有文件都放在 &lt;code&gt;~/mihomo&lt;/code&gt; 目录里：&lt;/p&gt;</description></item><item><title>客户端、Skill 与 MCP：大模型如何调用本地能力</title><link>https://www.bufio.cn/posts/llm-client-skill-mcp-interaction-flow/</link><pubDate>Sat, 16 May 2026 08:35:00 +0800</pubDate><guid>https://www.bufio.cn/posts/llm-client-skill-mcp-interaction-flow/</guid><description>&lt;p&gt;很多人第一次接触大模型工具调用时，容易把几个角色混在一起：大模型是不是直接读取了本地文件？Skill 是不是一个可执行插件？MCP Server 是不是直接连到了模型？&lt;/p&gt;
&lt;p&gt;实际链路更清晰，也更安全：&lt;strong&gt;模型只看见客户端发给它的上下文和工具定义；真正访问本地文件、数据库、命令行或服务的是客户端；MCP 是客户端调用本地能力的一种标准接口；调用结果再被客户端作为新消息发回模型。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.bufio.cn/images/llm-client-skill-mcp-loop-diagram.svg" alt="客户端、Skill 与 MCP 的交互闭环"&gt;&lt;/p&gt;
&lt;h2 id="总览先看完整链路"&gt;总览：先看完整链路&lt;/h2&gt;
&lt;p&gt;一次完整交互可以拆成四步：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;客户端读取用户问题，并加载本地 Skill、系统提示词、可用工具列表。&lt;/li&gt;
&lt;li&gt;客户端把这些信息整理成一次模型请求，发给大模型。&lt;/li&gt;
&lt;li&gt;大模型判断需要调用工具时，不直接执行本地动作，而是返回一个 &lt;code&gt;tool_call&lt;/code&gt; 请求。&lt;/li&gt;
&lt;li&gt;客户端根据 &lt;code&gt;tool_call&lt;/code&gt; 调用本地 MCP Server，把结果作为 &lt;code&gt;tool&lt;/code&gt; 消息回填给模型，模型再基于结果继续回答。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这里最重要的边界是：&lt;strong&gt;模型提出“我要调用什么”，客户端决定“能不能调用、怎么调用、结果如何回传”。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="skill-是什么给模型看的操作说明"&gt;Skill 是什么：给模型看的操作说明&lt;/h2&gt;
&lt;p&gt;Skill 可以理解成一份本地能力说明书。它通常不是让模型直接执行的程序，而是一组面向模型的规则、流程、约定和示例。&lt;/p&gt;
&lt;p&gt;例如，一个写博客的 Skill 可能告诉模型：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;# Blog Writing Skill
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; 写中文技术文章。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; 文件名必须是英文小写 slug。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; 创建文章前先读取已有标签。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; 图片必须通过 blog MCP 上传。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; 创建后再次读取文章确认 frontmatter 和图片引用。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;客户端在发起模型请求前，会把匹配到的 Skill 内容放进上下文。于是模型知道：当前任务不是随便写一段 Markdown，而是要按照这个博客系统的规范完成“查标签、写正文、上传图片、创建文章、验证结果”这些步骤。&lt;/p&gt;
&lt;p&gt;注意，Skill 本身不等于工具。它更像操作手册：告诉模型&lt;strong&gt;什么时候应该做什么、输出应该长什么样、有哪些约束不能违反&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="mcp-是什么给客户端调用的本地能力接口"&gt;MCP 是什么：给客户端调用的本地能力接口&lt;/h2&gt;
&lt;p&gt;MCP Server 则更接近真正的本地工具提供者。它把本地能力包装成结构化接口，比如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;blog.create_post&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;创建一篇 Hugo 博客文章&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;input_schema&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;object&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;properties&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;string&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;string&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;tags&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;array&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;items&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;string&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;draft&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;boolean&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;required&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;客户端启动或连接 MCP Server 后，会拿到这些工具定义。随后客户端可以把工具名、描述和参数结构告诉模型，让模型知道有哪些工具可用。&lt;/p&gt;</description></item><item><title>在 macOS 上打包 Windows EXE 的好办法：用 GitHub Actions 自动编译</title><link>https://www.bufio.cn/posts/macos-build-windows-exe-github-actions/</link><pubDate>Fri, 15 May 2026 12:00:00 +0800</pubDate><guid>https://www.bufio.cn/posts/macos-build-windows-exe-github-actions/</guid><description>&lt;p&gt;如果你平时在 macOS 上写 Python 脚本、桌面小工具或命令行工具，最后却要发给 Windows 用户使用，最容易遇到的问题就是：我能不能直接在 Mac 上打包出 &lt;code&gt;.exe&lt;/code&gt;？&lt;/p&gt;
&lt;p&gt;结论先说清楚：&lt;strong&gt;不建议在 macOS 本机强行交叉打包 Windows EXE。更稳的办法是把代码推到 GitHub，让 GitHub Actions 在 Windows 环境里自动打包。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这篇文章按使用教程来写，从准备项目、创建 workflow、触发构建、下载产物，到常见问题排查，完整走一遍。&lt;/p&gt;
&lt;h2 id="总览为什么要用-github-actions-打包-exe"&gt;总览：为什么要用 GitHub Actions 打包 EXE&lt;/h2&gt;
&lt;p&gt;PyInstaller 这类打包工具通常不是“万能交叉编译器”。也就是说：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;要打 Windows &lt;code&gt;.exe&lt;/code&gt;，最好在 Windows 上运行打包命令。&lt;/li&gt;
&lt;li&gt;要打 macOS 应用，就在 macOS 上打。&lt;/li&gt;
&lt;li&gt;要打 Linux 可执行文件，就在 Linux 上打。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这不是 GitHub Actions 的特殊限制，而是很多 Python 原生依赖、动态库、运行时文件和系统 API 都跟目标操作系统绑定。你在 macOS 上直接打包，得到的通常是 macOS 可执行文件，而不是 Windows 真正可用的 &lt;code&gt;.exe&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;GitHub Actions 的思路是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;macOS 本地开发
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; git push 到 GitHub
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; GitHub Actions 启动 Windows Runner
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 安装 Python 和依赖
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 执行 PyInstaller
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 上传 exe 产物
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 在 Actions 页面下载
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这样做的好处是：&lt;/p&gt;</description></item><item><title>从标量、向量到张量：深度学习里的“数字容器”到底是什么？</title><link>https://www.bufio.cn/posts/scalar-vector-tensor-deep-learning/</link><pubDate>Thu, 14 May 2026 09:55:14 +0800</pubDate><guid>https://www.bufio.cn/posts/scalar-vector-tensor-deep-learning/</guid><description>&lt;p&gt;刚接触深度学习时，很多人会被一堆词劝退：标量、向量、矩阵、张量、维度、形状、通道、批次……这些词看起来像数学课，其实它们背后的核心很简单：&lt;strong&gt;深度学习处理的东西，本质上是一堆数字；这些概念只是说明数字被怎样摆放。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;你可以先把它们理解成不同大小、不同结构的“数字容器”：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;标量：一个数字。&lt;/li&gt;
&lt;li&gt;向量：一排数字。&lt;/li&gt;
&lt;li&gt;矩阵：一张表格。&lt;/li&gt;
&lt;li&gt;张量：更通用的多维数字容器。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这篇文章不要求你有高等数学基础。我们只抓住一个目标：以后看到 &lt;code&gt;shape = (32, 3, 224, 224)&lt;/code&gt; 这种写法时，你知道它大概在说什么。&lt;/p&gt;
&lt;h2 id="先建立直觉模型只认识数字"&gt;先建立直觉：模型只认识数字&lt;/h2&gt;
&lt;p&gt;人看到一张猫的图片，会说“这是一只猫”。但神经网络不能直接理解“猫”这个字，也不能像人一样直接理解图像含义。它真正拿到的是数字。&lt;/p&gt;
&lt;p&gt;比如一张图片，在计算机里通常可以表示成很多像素点；每个像素点又可以用数字表示颜色。文本、声音、表格数据也一样，最后都要转成数字，模型才能计算。&lt;/p&gt;
&lt;p&gt;所以深度学习里的问题，经常会变成：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这些数字怎么组织？模型每一步拿到的数字长什么样？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这就是标量、向量、矩阵、张量这些概念的用处。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.bufio.cn/images/tensor-concepts.svg" alt="标量、向量、矩阵、张量的递进关系"&gt;&lt;/p&gt;
&lt;h2 id="标量一个单独的数字"&gt;标量：一个单独的数字&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;标量就是一个数。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;0.01
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-2.7
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在深度学习中，很多结果都是标量：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;学习率 &lt;code&gt;0.001&lt;/code&gt; 是一个标量。&lt;/li&gt;
&lt;li&gt;某次训练的损失值 &lt;code&gt;1.26&lt;/code&gt; 是一个标量。&lt;/li&gt;
&lt;li&gt;一个分类结果的概率 &lt;code&gt;0.87&lt;/code&gt; 也可以是一个标量。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;标量没有方向，也没有一排、一列、几行几列这些结构。它就是一个孤零零的数字。&lt;/p&gt;
&lt;p&gt;如果用“维度”来描述，标量通常可以理解为 &lt;strong&gt;0 维&lt;/strong&gt;。这里的 0 维不是说它不重要，而是说它没有展开成列表或表格。&lt;/p&gt;
&lt;h2 id="向量一排数字"&gt;向量：一排数字&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;向量是一组按顺序排好的数字。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[170, 65, 23]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你可以把它理解成一个人的三个特征：身高 170、体重 65、年龄 23。单个数字只能表达一个量，一排数字就可以一起描述一个对象。&lt;/p&gt;
&lt;p&gt;在机器学习里，一个样本经常会被表示成一个向量：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[面积, 卧室数量, 楼层, 距离地铁距离]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果我们要预测房价，就可以把每套房子变成这样一排数字。模型拿到的不是“这套房子不错”这种自然语言，而是一组可以计算的数。&lt;/p&gt;
&lt;p&gt;向量通常可以理解为 &lt;strong&gt;1 维&lt;/strong&gt;。这里的 1 维指的是它只有一个方向可以数：从左到右数第 1 个、第 2 个、第 3 个……&lt;/p&gt;</description></item><item><title>RapidOCR 入门：英文 OCR 识别、常规测评与使用注意事项</title><link>https://www.bufio.cn/posts/rapidocr-english-ocr-evaluation-usage-notes/</link><pubDate>Wed, 06 May 2026 11:17:28 +0800</pubDate><guid>https://www.bufio.cn/posts/rapidocr-english-ocr-evaluation-usage-notes/</guid><description>&lt;p&gt;OCR 这类工具最容易被误用：看起来只要把图片丢进去就能得到文字，但真正放到项目里时，还会遇到图片质量、方向、字体、语言、速度、批量处理和错误纠正等问题。本文以一个最小的 RapidOCR 英文识别项目为例，整理它适合做什么、怎么跑起来、怎么做常规测评，以及使用时需要注意哪些细节。&lt;/p&gt;
&lt;p&gt;本文不是完整排行榜式横向评测，而是偏工程落地的使用说明：先让脚本能稳定运行，再用一组可复现的指标判断它是否满足当前业务。&lt;/p&gt;
&lt;h2 id="rapidocr-是什么"&gt;RapidOCR 是什么&lt;/h2&gt;
&lt;p&gt;RapidOCR 可以理解为一个轻量级 OCR 推理方案。当前示例项目使用的是 &lt;code&gt;rapidocr-onnxruntime==1.2.3&lt;/code&gt;，底层通过 ONNX Runtime 执行 OCR 模型推理，典型链路包括：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;文本检测：先在图片中找出可能包含文字的区域。&lt;/li&gt;
&lt;li&gt;方向分类：判断文本是否需要旋转或方向修正。&lt;/li&gt;
&lt;li&gt;文本识别：对检测出来的文字区域逐行识别。&lt;/li&gt;
&lt;li&gt;结果整理：输出文本框、文字内容和置信度等结果。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;在这个项目里，我们只保留最常用的输出：把图片中的文字逐行打印出来。它适合做轻量脚本、批处理、小工具、后台任务，也适合在正式接入大系统前做 OCR 方案验证。&lt;/p&gt;
&lt;h2 id="适用场景"&gt;适用场景&lt;/h2&gt;
&lt;p&gt;RapidOCR 更适合这些场景：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;识别清晰图片里的英文、数字、短标签、票据字段、车牌周边信息等。&lt;/li&gt;
&lt;li&gt;在本地或服务器上离线运行，不想依赖外部 OCR API。&lt;/li&gt;
&lt;li&gt;希望用 Python 快速接入，先验证效果，再决定是否封装成服务。&lt;/li&gt;
&lt;li&gt;对部署体积、调用方式、推理依赖有一定要求，希望用 ONNX Runtime 这类通用推理后端。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;它不太适合直接裸用在这些场景：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;图片严重模糊、过曝、反光、压缩严重。&lt;/li&gt;
&lt;li&gt;版面复杂，需要结构化还原表格、段落、字段层级。&lt;/li&gt;
&lt;li&gt;需要极高准确率的财务、证件、合同等强校验场景。&lt;/li&gt;
&lt;li&gt;需要直接理解语义，而不只是把图片转成文字。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些场景不是不能做，而是要加预处理、后处理、规则校验，甚至引入专门模型或人工复核流程。&lt;/p&gt;
&lt;h2 id="最小项目结构"&gt;最小项目结构&lt;/h2&gt;
&lt;p&gt;当前项目可以保持很简单：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rapidOCR/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── README.md
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── ocr.py
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── IMG_20260506_110642.jpg
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── IMG_20260506_110656.jpg
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└── venv/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;核心脚本 &lt;code&gt;ocr.py&lt;/code&gt; 的职责很清楚：接收一张图片路径，调用 RapidOCR，然后把识别出的每行文字打印出来。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;argparse&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;rapidocr_onnxruntime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RapidOCR&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argparse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ArgumentParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;OCR text recognition using RapidOCR&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;--input&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Path to input image file&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RapidOCR&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;No text detected.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里的 &lt;code&gt;result&lt;/code&gt; 通常包含文本框坐标、识别文本、置信度等信息。示例脚本只打印 &lt;code&gt;line[1]&lt;/code&gt;，也就是文字内容。如果后续要做测评或业务校验，建议保留完整结果，不要过早只取纯文本。&lt;/p&gt;</description></item><item><title>Docker 日常维护、运行调试与排错教程：从 Compose 到 Swarm</title><link>https://www.bufio.cn/posts/docker-maintenance-debugging-compose-swarm/</link><pubDate>Wed, 29 Apr 2026 16:25:00 +0800</pubDate><guid>https://www.bufio.cn/posts/docker-maintenance-debugging-compose-swarm/</guid><description>&lt;p&gt;Docker 用久以后，问题通常不在“会不会启动一个容器”，而在日常维护和排错：容器为什么退出、日志在哪里看、端口为什么不通、磁盘为什么爆了、Compose 更新为什么没生效、Swarm 服务为什么一直 Pending。&lt;/p&gt;
&lt;p&gt;这篇文章按实际运维顺序整理：先看单机 Docker 的容器、镜像、日志、网络、存储和资源排查，再看 Compose 的项目级管理，最后看 Swarm 的集群级服务维护。&lt;/p&gt;
&lt;h2 id="总览docker-排错先分清三种运行层级"&gt;总览：Docker 排错先分清三种运行层级&lt;/h2&gt;
&lt;p&gt;Docker 相关问题先不要急着敲命令，先判断当前应用跑在哪一层：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;层级&lt;/th&gt;
 &lt;th&gt;常见命令&lt;/th&gt;
 &lt;th&gt;管理对象&lt;/th&gt;
 &lt;th&gt;适合场景&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;单机 Docker&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;docker run&lt;/code&gt;、&lt;code&gt;docker ps&lt;/code&gt;、&lt;code&gt;docker logs&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;container、image、volume、network&lt;/td&gt;
 &lt;td&gt;单机服务、临时调试、本地开发&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Docker Compose&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;docker compose up&lt;/code&gt;、&lt;code&gt;docker compose logs&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;一个项目里的多服务&lt;/td&gt;
 &lt;td&gt;开发环境、小型部署、依赖编排&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Docker Swarm&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;docker service&lt;/code&gt;、&lt;code&gt;docker stack&lt;/code&gt;、&lt;code&gt;docker node&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;service、task、stack、node&lt;/td&gt;
 &lt;td&gt;多节点集群、滚动更新、副本调度&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;排错时也按这个顺序看：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;服务不可用
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 容器是否存在、是否在运行
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 日志里有没有启动失败或业务异常
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 端口映射和网络是否正确
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; volume、配置、环境变量是否正确
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; CPU、内存、磁盘是否打满
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 如果是 Compose，看 project/service 维度
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 如果是 Swarm，看 node/service/task 维度
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="一日常巡检先看-docker-基本状态"&gt;一、日常巡检：先看 Docker 基本状态&lt;/h2&gt;
&lt;p&gt;先确认 Docker daemon 是否正常：&lt;/p&gt;</description></item><item><title>大模型周边概念速通：MCP、Skill、Agent、Tool 到底是什么</title><link>https://www.bufio.cn/posts/llm-mcp-skill-agent-concepts/</link><pubDate>Wed, 29 Apr 2026 16:05:00 +0800</pubDate><guid>https://www.bufio.cn/posts/llm-mcp-skill-agent-concepts/</guid><description>&lt;p&gt;大模型应用发展到现在，很多词会一起出现：MCP、Skill、Agent、Tool、Prompt、Resource、Memory、Guardrail、Handoff、Plugin、Connector。它们看起来都像“给 AI 增强能力”的东西，但实际所在的层级完全不同。&lt;/p&gt;
&lt;p&gt;如果把大模型应用想成一个软件系统：大模型负责理解和生成，Agent 负责决策和调度，Tool 负责执行动作，MCP 负责把外部能力标准化接进来，Skill 负责沉淀一套可复用的任务方法。理解这些边界之后，再看各种 AI IDE、智能客服、自动化助手、博客发布机器人，就不会被概念绕晕。&lt;/p&gt;
&lt;h2 id="总览先看一条完整链路"&gt;总览：先看一条完整链路&lt;/h2&gt;
&lt;p&gt;一次典型的大模型应用调用，可以拆成这条链路：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;用户请求
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 应用 / Agent
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 组装 Prompt、历史记录、上下文 Resource、Memory
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 调用大模型
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 大模型决定是否调用 Tool
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; Tool 可能来自本地代码、平台内置能力，或 MCP Server
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 工具返回结果
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; Agent 继续推理、校验、生成最终回答
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里最容易混淆的是三层：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;层级&lt;/th&gt;
 &lt;th&gt;解决的问题&lt;/th&gt;
 &lt;th&gt;典型概念&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;模型层&lt;/td&gt;
 &lt;td&gt;负责语言理解、推理、生成&lt;/td&gt;
 &lt;td&gt;LLM、大模型、多模态模型&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;编排层&lt;/td&gt;
 &lt;td&gt;决定怎么完成任务&lt;/td&gt;
 &lt;td&gt;Agent、Prompt、Memory、Guardrail、Handoff&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;能力接入层&lt;/td&gt;
 &lt;td&gt;连接外部数据和动作&lt;/td&gt;
 &lt;td&gt;Tool、MCP、Resource、Connector、Plugin、Skill&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;一句话概括：大模型是大脑，Agent 是工作流控制器，Tool 是手，MCP 是标准插口，Skill 是做事手册和工具包。&lt;/p&gt;
&lt;h2 id="大模型能力核心但不是完整应用"&gt;大模型：能力核心，但不是完整应用&lt;/h2&gt;
&lt;p&gt;大模型，也就是 LLM 或多模态模型，核心能力是根据上下文预测和生成内容。它可以读文字、写代码、解释日志、分析图片、生成计划，但它本身通常不直接拥有你的文件系统、数据库、浏览器、Git 仓库、公司内部系统权限。&lt;/p&gt;
&lt;p&gt;所以，大模型应用真正要解决的是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;给模型什么上下文。&lt;/li&gt;
&lt;li&gt;允许模型调用哪些能力。&lt;/li&gt;
&lt;li&gt;模型何时该自己回答，何时该查数据或执行动作。&lt;/li&gt;
&lt;li&gt;工具调用前后如何校验安全性和结果质量。&lt;/li&gt;
&lt;li&gt;多步骤任务失败时如何恢复。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这就是 Agent、Tool、MCP、Skill 等概念出现的原因。&lt;/p&gt;</description></item><item><title>Spring Boot 核心功能介绍：从启动应用到生产部署</title><link>https://www.bufio.cn/posts/spring-boot-core-features-introduction/</link><pubDate>Wed, 29 Apr 2026 15:40:00 +0800</pubDate><guid>https://www.bufio.cn/posts/spring-boot-core-features-introduction/</guid><description>&lt;p&gt;Spring Boot 的目标不是替代 Spring，而是让 Spring 应用更容易启动、配置、运行和交付。它把大量常见选择做成默认约定：依赖怎么选、Tomcat 怎么启动、JSON 怎么序列化、配置从哪里读取、健康检查怎么暴露、应用如何打成可执行 Jar。&lt;/p&gt;
&lt;p&gt;本文不做百科式穷举。Spring Boot 覆盖 Web、数据访问、消息、缓存、安全、可观测性、测试、容器镜像、AOT、原生镜像等很多方向，全部展开会变成官方手册。这里聚焦日常 Java 后端项目最常用、最值得先掌握的核心功能。&lt;/p&gt;
&lt;h2 id="总览spring-boot-解决什么问题"&gt;总览：Spring Boot 解决什么问题&lt;/h2&gt;
&lt;p&gt;一个传统 Spring 项目通常要先处理很多基础工作：引入一堆依赖、配置 DispatcherServlet、配置 JSON、配置数据源、配置事务、配置日志、配置打包方式。Spring Boot 把这些重复工作收敛成三件事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;用 Starter 组织依赖，例如 &lt;code&gt;spring-boot-starter-web&lt;/code&gt;、&lt;code&gt;spring-boot-starter-data-jpa&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;用自动配置根据 classpath 和配置文件创建合适的 Bean。&lt;/li&gt;
&lt;li&gt;用生产级能力补齐健康检查、指标、日志、外部化配置和可执行包。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;最小应用大概是这样：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;com.example.demo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;org.springframework.boot.SpringApplication&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;org.springframework.boot.autoconfigure.SpringBootApplication&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@SpringBootApplication&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DemoApplication&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SpringApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DemoApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;@SpringBootApplication&lt;/code&gt; 通常可以理解为三个能力的组合：组件扫描、配置类声明、自动配置入口。也就是说，项目启动后，Spring Boot 会扫描你的 Controller、Service、Repository，并按条件装配 Web、JSON、数据库、事务、日志等基础设施。&lt;/p&gt;
&lt;h2 id="1-starter把常用依赖打包成入口"&gt;1. Starter：把常用依赖打包成入口&lt;/h2&gt;
&lt;p&gt;Starter 是 Spring Boot 的第一层体验优化。你不需要自己逐个判断 Spring MVC、Jackson、Tomcat、Validation 等依赖的兼容版本，只要引入对应 Starter。&lt;/p&gt;</description></item><item><title>一次 HTTP 请求的完整旅程：从浏览器到 Nginx 再到后端服务</title><link>https://www.bufio.cn/posts/http-request-flow-browser-nginx-backend/</link><pubDate>Wed, 29 Apr 2026 15:05:00 +0800</pubDate><guid>https://www.bufio.cn/posts/http-request-flow-browser-nginx-backend/</guid><description>&lt;p&gt;我们在浏览器里输入一个网址，比如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;https://www.example.com/api/users
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;看起来只是按下回车，页面就返回了。但在这几百毫秒甚至几十毫秒里，浏览器、操作系统、DNS、TCP、TLS、Nginx、后端应用、数据库和缓存可能都参与了一次协作。&lt;/p&gt;
&lt;p&gt;这篇文章按时间顺序，把一次典型的 HTTPS 请求完整拆开：从浏览器发起请求，到 TCP 三次握手、TLS 密钥协商，再到 Nginx 接收连接、反向代理给后端服务，最后响应返回浏览器。&lt;/p&gt;
&lt;h2 id="总览请求链路长什么样"&gt;总览：请求链路长什么样&lt;/h2&gt;
&lt;p&gt;可以先看一个简化版流程图：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;用户输入 URL
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ▼
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;浏览器解析 URL
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ▼
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;DNS 解析域名 -&amp;gt; 得到服务器 IP
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ▼
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;TCP 三次握手 -&amp;gt; 建立可靠连接
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ▼
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;TLS 握手/密钥协商 -&amp;gt; 建立加密通道
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ▼
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;浏览器发送 HTTP 请求
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ▼
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Nginx 接收请求
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├─ 匹配 server_name
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├─ 匹配 location
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├─ 处理静态资源 / gzip / header / 日志
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ▼
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;proxy_pass 反向代理到后端应用
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ▼
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;后端应用处理业务逻辑
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ▼
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;后端返回 HTTP 响应
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ▼
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Nginx 返回响应给浏览器
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ▼
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;浏览器解析 HTML/CSS/JS 并渲染页面
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;实际生产环境里，中间还可能有 CDN、负载均衡、WAF、Service Mesh、网关、缓存、队列、数据库等组件。但核心路径大体就是上面这些层次。&lt;/p&gt;</description></item><item><title>MySQL B+ Tree 机制与算法详解</title><link>https://www.bufio.cn/posts/mysql-b-plus-tree-mechanisms-and-algorithms/</link><pubDate>Wed, 29 Apr 2026 00:00:00 +0800</pubDate><guid>https://www.bufio.cn/posts/mysql-b-plus-tree-mechanisms-and-algorithms/</guid><description>&lt;h2 id="为什么数据库普遍选择-b-tree"&gt;为什么数据库普遍选择 B+ Tree&lt;/h2&gt;
&lt;p&gt;如果只从算法课本看，查找一个有序集合有很多选择：数组可以二分，哈希表可以做到平均 O(1)，红黑树、AVL 树可以做到 O(logN)。但数据库的核心问题不是“CPU 内存里怎么找”，而是“磁盘或 SSD 上的数据怎么少读几次”。&lt;/p&gt;
&lt;p&gt;MySQL 的 InnoDB 存储引擎把表数据和索引组织成页。默认情况下，一个页通常是 16KB。一次查询如果每深入一层索引就需要读取一个页，那么树的高度越低，I/O 次数就越少。B+ Tree 的核心优势就在这里：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;每个节点能存放很多 key，树的分叉数很大，高度很低。&lt;/li&gt;
&lt;li&gt;所有真实记录都在叶子节点，非叶子节点只负责导航。&lt;/li&gt;
&lt;li&gt;叶子节点之间按 key 顺序连接，非常适合范围查询和排序扫描。&lt;/li&gt;
&lt;li&gt;节点大小天然适合数据库页，可以和磁盘读写单位对齐。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;所以，B+ Tree 不是为了在理论复杂度上压倒所有结构，而是为了适配数据库最昂贵的资源：随机 I/O。&lt;/p&gt;
&lt;h2 id="从二叉树到-b-tree"&gt;从二叉树到 B+ Tree&lt;/h2&gt;
&lt;p&gt;普通二叉搜索树每个节点最多两个孩子。即使它保持平衡，一千万条数据的高度也大约是二十多层。放在内存里问题不大，放在数据库里就意味着一次查找可能触发很多次随机页读取。&lt;/p&gt;
&lt;p&gt;B Tree 和 B+ Tree 解决的是“分叉太少”的问题。一个节点可以保存多个 key 和多个孩子指针。例如节点中有这些分隔 key：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[10 | 20 | 30]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;它可以把搜索空间分成四段：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;(-∞, 10) [10, 20) [20, 30) [30, +∞)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这样，一个节点不是二选一，而是多路分发。只要一个页中能放下足够多的 key 和指针，树的高度就会非常低。&lt;/p&gt;
&lt;p&gt;B+ Tree 相比 B Tree 又进一步做了一个重要取舍：非叶子节点不存放完整数据记录，只存放用于导航的 key 和子节点页号；所有完整记录都放在叶子节点。这样非叶子节点更小，能容纳更多 key，树也就更矮。&lt;/p&gt;
&lt;h2 id="b-tree-的基本结构"&gt;B+ Tree 的基本结构&lt;/h2&gt;
&lt;p&gt;一棵典型 B+ Tree 可以分成三类节点：&lt;/p&gt;</description></item><item><title>MySQL 各个模块与核心机制：从一条 SQL 理解数据库原理</title><link>https://www.bufio.cn/posts/mysql-architecture-and-core-mechanisms/</link><pubDate>Tue, 28 Apr 2026 18:05:26 +0800</pubDate><guid>https://www.bufio.cn/posts/mysql-architecture-and-core-mechanisms/</guid><description>&lt;p&gt;MySQL 是最常见的关系型数据库之一。很多人一开始使用 MySQL，关注的是如何写 &lt;code&gt;SELECT&lt;/code&gt;、&lt;code&gt;INSERT&lt;/code&gt;、&lt;code&gt;UPDATE&lt;/code&gt;、&lt;code&gt;DELETE&lt;/code&gt;，但如果想进一步理解性能优化、事务一致性、主从复制、锁等待、索引失效等问题，就需要了解 MySQL 内部由哪些模块组成，以及这些模块之间如何协作。&lt;/p&gt;
&lt;p&gt;本文会以科普的方式讲解 MySQL 的整体架构、核心模块和重要机制。你不需要先成为数据库内核开发者，也可以建立一套清晰的理解框架。&lt;/p&gt;
&lt;h2 id="1-mysql-的整体架构"&gt;1. MySQL 的整体架构&lt;/h2&gt;
&lt;p&gt;可以把 MySQL 大致分成两层：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;客户端
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;连接层
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;SQL 层：解析器、预处理器、优化器、执行器、权限检查、函数、视图、触发器等
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;存储引擎层：InnoDB、MyISAM、Memory 等
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;磁盘文件与操作系统
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中最重要的设计点是：MySQL 的 SQL 层和存储引擎层是分离的。&lt;/p&gt;
&lt;p&gt;SQL 层负责理解 SQL、检查权限、生成执行计划、调用存储引擎接口。存储引擎层负责真正的数据读写、索引组织、事务、锁、崩溃恢复等底层工作。&lt;/p&gt;
&lt;p&gt;这也是为什么 MySQL 可以支持不同存储引擎。最常用的是 InnoDB，它是现代 MySQL 默认存储引擎，支持事务、行级锁、MVCC、崩溃恢复等关键能力。&lt;/p&gt;
&lt;h2 id="2-连接层客户端如何连上-mysql"&gt;2. 连接层：客户端如何连上 MySQL&lt;/h2&gt;
&lt;p&gt;当应用程序连接 MySQL 时，首先经过连接层。&lt;/p&gt;
&lt;p&gt;连接层主要负责：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;建立连接
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;认证用户名和密码
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;检查客户端权限
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;维护连接状态
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;处理连接线程
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;管理连接超时
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;常见连接方式包括 TCP 连接和 Unix Socket 连接。&lt;/p&gt;
&lt;p&gt;例如应用程序使用如下连接信息：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;host=127.0.0.1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;port=3306
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;user=app_user
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;password=******
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;database=app_db
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;MySQL 会先验证账号密码，再根据权限表判断用户是否有访问某个库、表、列或执行某类操作的权限。&lt;/p&gt;</description></item><item><title>Linux 各个目录的作用：从根目录理解系统结构</title><link>https://www.bufio.cn/posts/linux-directory-structure-explained/</link><pubDate>Tue, 28 Apr 2026 17:53:07 +0800</pubDate><guid>https://www.bufio.cn/posts/linux-directory-structure-explained/</guid><description>&lt;p&gt;如果你刚开始接触 Linux，打开终端输入 &lt;code&gt;ls /&lt;/code&gt;，会看到一组看起来很固定的目录：&lt;code&gt;bin&lt;/code&gt;、&lt;code&gt;etc&lt;/code&gt;、&lt;code&gt;home&lt;/code&gt;、&lt;code&gt;usr&lt;/code&gt;、&lt;code&gt;var&lt;/code&gt;、&lt;code&gt;proc&lt;/code&gt;、&lt;code&gt;dev&lt;/code&gt; 等。它们不是随意命名的文件夹，而是 Linux 系统长期演化出来的一套目录分工。&lt;/p&gt;
&lt;p&gt;理解这些目录的作用，有助于你知道配置文件在哪里、日志在哪里、用户数据在哪里、程序通常安装在哪里，也能避免误删系统关键文件。本文会以 Linux 根目录 &lt;code&gt;/&lt;/code&gt; 为起点，逐一讲解常见目录的用途。&lt;/p&gt;
&lt;h2 id="1-根目录所有路径的起点"&gt;1. &lt;code&gt;/&lt;/code&gt;：根目录，所有路径的起点&lt;/h2&gt;
&lt;p&gt;Linux 的文件系统是一个单一的树状结构，最顶层就是 &lt;code&gt;/&lt;/code&gt;，也叫根目录。&lt;/p&gt;
&lt;p&gt;无论是系统文件、用户文件、磁盘分区、U 盘、网络挂载目录，最终都会挂载到这棵目录树的某个位置。和 Windows 常见的 &lt;code&gt;C:&lt;/code&gt;、&lt;code&gt;D:&lt;/code&gt; 盘符不同，Linux 更强调“从 &lt;code&gt;/&lt;/code&gt; 开始的一棵树”。&lt;/p&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/etc/ssh/sshd_config
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/home/alice/document.txt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/var/log/syslog
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这些路径都从 &lt;code&gt;/&lt;/code&gt; 开始。&lt;/p&gt;
&lt;p&gt;日常使用时，不建议直接在 &lt;code&gt;/&lt;/code&gt; 下随意创建文件或目录。根目录通常只放系统约定好的一级目录。&lt;/p&gt;
&lt;h2 id="2-bin基础命令目录"&gt;2. &lt;code&gt;/bin&lt;/code&gt;：基础命令目录&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;/bin&lt;/code&gt; 用来存放系统启动和基本维护所需的常用命令。&lt;/p&gt;
&lt;p&gt;常见命令包括：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ls
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cp
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mv
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rm
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cat
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkdir
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这些命令是系统最基础的工具，即使系统处在较小的维护环境中，也通常需要它们。&lt;/p&gt;
&lt;p&gt;在一些现代 Linux 发行版中，&lt;code&gt;/bin&lt;/code&gt; 可能只是一个指向 &lt;code&gt;/usr/bin&lt;/code&gt; 的符号链接。这是因为许多发行版已经采用了 &lt;code&gt;/usr&lt;/code&gt; 合并的目录布局，但从使用者角度看，&lt;code&gt;/bin&lt;/code&gt; 仍然可以理解为“基础命令所在的位置”。&lt;/p&gt;
&lt;h2 id="3-sbin系统管理命令目录"&gt;3. &lt;code&gt;/sbin&lt;/code&gt;：系统管理命令目录&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;/sbin&lt;/code&gt; 中通常存放系统管理相关命令，很多命令主要供 root 用户或系统管理员使用。&lt;/p&gt;
&lt;p&gt;常见命令包括：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ip
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;reboot
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;shutdown
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;fsck
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mount
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中不少命令和系统启动、磁盘检查、网络配置、关机重启有关。&lt;/p&gt;</description></item><item><title>Fail2Ban 工作原理与常用运维操作</title><link>https://www.bufio.cn/posts/fail2ban-principles-and-operations/</link><pubDate>Tue, 28 Apr 2026 17:35:00 +0800</pubDate><guid>https://www.bufio.cn/posts/fail2ban-principles-and-operations/</guid><description>&lt;p&gt;Fail2Ban 是 Linux 服务器上很常见的一类安全防护工具，主要用来应对 SSH、Web 登录、邮件服务等场景里的暴力破解和重复异常请求。它不会替代防火墙、密钥登录、最小权限这些基础安全措施，但非常适合做一层自动化封禁：发现某个 IP 在短时间内频繁失败，就把它临时加入黑名单，过一段时间后再自动释放。&lt;/p&gt;
&lt;p&gt;这篇记录一下 Fail2Ban 的工作原理、安装和开机启动方式、常用规则配置，以及误封后如何把某个 IP 从黑名单里移出来。&lt;/p&gt;
&lt;h2 id="fail2ban-的工作原理"&gt;Fail2Ban 的工作原理&lt;/h2&gt;
&lt;p&gt;Fail2Ban 的核心逻辑可以概括成四步：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;监听日志文件或 systemd journal。&lt;/li&gt;
&lt;li&gt;用 filter 里的正则表达式匹配失败行为。&lt;/li&gt;
&lt;li&gt;在指定时间窗口内统计同一个 IP 的失败次数。&lt;/li&gt;
&lt;li&gt;达到阈值后执行 action，把 IP 加入防火墙黑名单。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这里有几个关键概念。&lt;/p&gt;
&lt;h2 id="jail"&gt;jail&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;jail&lt;/code&gt; 是 Fail2Ban 的规则单元。一个 jail 通常对应一个服务，例如 SSH、Nginx、Postfix、Dovecot 等。每个 jail 会指定：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;监控哪个服务。&lt;/li&gt;
&lt;li&gt;读取哪个日志。&lt;/li&gt;
&lt;li&gt;使用哪个 filter。&lt;/li&gt;
&lt;li&gt;允许失败多少次。&lt;/li&gt;
&lt;li&gt;统计时间窗口是多少。&lt;/li&gt;
&lt;li&gt;封禁多久。&lt;/li&gt;
&lt;li&gt;使用什么 action 封禁 IP。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例如 SSH 的 jail 就是监听 SSH 登录日志，匹配登录失败记录，当同一个 IP 在一段时间内失败太多，就通过防火墙封禁。&lt;/p&gt;
&lt;h2 id="filter"&gt;filter&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;filter&lt;/code&gt; 负责识别“什么日志算失败”。它通常放在：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/etc/fail2ban/filter.d/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;例如 SSH 常见 filter 是：&lt;/p&gt;</description></item><item><title>用 MCP 管理 Hugo 博客：这个项目是怎么搭起来的</title><link>https://www.bufio.cn/posts/how-to-create-ai-blog-by-mcp/</link><pubDate>Tue, 28 Apr 2026 16:45:00 +0800</pubDate><guid>https://www.bufio.cn/posts/how-to-create-ai-blog-by-mcp/</guid><description>&lt;p&gt;这个仓库表面上是一个很轻量的 Hugo 博客，实际目标更明确：把博客内容、构建部署、以及 AI 写作入口放在同一个工程里。最终形成的工作流是：文章仍然以 Markdown 存在仓库中，Hugo 负责生成静态站点，仓库流水线负责构建部署，而 &lt;code&gt;mcp/&lt;/code&gt; 目录里的 MCP 服务负责给 AI 提供一组可控的读写工具。&lt;/p&gt;
&lt;h2 id="项目目标"&gt;项目目标&lt;/h2&gt;
&lt;p&gt;这个项目解决的是一个很具体的问题：写博客时不想每次都手动打开仓库、创建 frontmatter、提交文章、再等待部署。更理想的方式是直接和 AI 对话，让 AI 能够读取已有文章、理解站点配置、创建新文章、修改元数据，必要时还能上传图片。&lt;/p&gt;
&lt;p&gt;为了不把 AI 直接暴露给整个文件系统，项目没有设计成“随便执行命令”的形式，而是把能力收敛成 MCP 工具。每个工具只做一件事，例如列文章、读文章、创建文章、更新文章、上传图片。这样既方便使用，也比较容易控制边界。&lt;/p&gt;
&lt;h2 id="顶层结构"&gt;顶层结构&lt;/h2&gt;
&lt;p&gt;当前仓库大致分成几块：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── hugo.toml # Hugo 站点配置
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── content/ # 博客内容
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ├── about.md
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ └── posts/ # Markdown 文章目录
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── layouts/partials/ # 本站覆盖的局部模板
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── themes/terminal/ # Hugo terminal 主题
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── .github/workflows/ # 构建与部署流水线
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└── mcp/ # 自定义 blog MCP 服务
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;hugo.toml&lt;/code&gt; 定义站点基础信息，例如语言、标题、主题、分页和菜单。文章目录使用 Hugo 默认的 &lt;code&gt;content/posts&lt;/code&gt;。内容层仍然保持 Hugo 最朴素的方式：每篇文章是一个 Markdown 文件，文件头部用 TOML frontmatter 描述标题、日期、标签、摘要等信息。这样即使以后不使用 MCP，也不会被锁死在某个特殊系统里。&lt;/p&gt;</description></item><item><title>第一条随手记</title><link>https://www.bufio.cn/posts/hello/</link><pubDate>Tue, 28 Apr 2026 10:57:00 +0800</pubDate><guid>https://www.bufio.cn/posts/hello/</guid><description>&lt;p&gt;这是第一条随手写下的记录。&lt;/p&gt;</description></item><item><title>关于</title><link>https://www.bufio.cn/about/</link><pubDate>Tue, 28 Apr 2026 10:57:00 +0800</pubDate><guid>https://www.bufio.cn/about/</guid><description>&lt;p&gt;&lt;img src="https://www.bufio.cn/images/about-workspace.jpg" alt="程序员工作台"&gt;&lt;/p&gt;
&lt;p&gt;你好，我是一名程序员，日常主要围绕后端开发、基础设施和工程效率做一些实际工作。&lt;/p&gt;
&lt;p&gt;主力技术栈包括 PHP、Go、Java、JavaScript，也会长期接触 MySQL、Redis、Elasticsearch 等数据库与中间件，以及 Docker、Kubernetes 相关的基础设施、容器化和容器编排工作。日常也会关注自动化、CI/CD、部署发布和工程效率。最近也在持续关注大模型、MCP、RAG 等 AI 工具和工程化落地，尝试把它们接入日常开发、运维、写作和自动化流程里。&lt;/p&gt;
&lt;p&gt;这个博客主要用来记录一些随手写下的技术笔记、项目实践和问题排查过程。内容不追求完整教程，更偏向真实工作里遇到的问题、踩过的坑，以及当时觉得值得留下来的思考。&lt;/p&gt;
&lt;h2 id="技术方向"&gt;技术方向&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;技术开发：PHP / Go / Java / JavaScript&lt;/li&gt;
&lt;li&gt;数据库与中间件：MySQL / Redis / Elasticsearch&lt;/li&gt;
&lt;li&gt;基础设施与容器编排：Docker / Kubernetes&lt;/li&gt;
&lt;li&gt;工程实践：自动化 / CI/CD / 部署发布 / 问题排查&lt;/li&gt;
&lt;li&gt;AI 应用：大模型 / MCP / RAG / 开发辅助 / 内容生成 / 工作流集成&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="联系方式"&gt;联系方式&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Email: &lt;a href="mailto:yeh20200520@gmail.com"&gt;yeh20200520@gmail.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/gorilla-go"&gt;github.com/gorilla-go&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>